You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
console chat: the Preview button appears for any copilot draft and deep-links to the affected page or object in ?preview=draft (objectstack#24324 ruling A) #12968
Filed by the director seat (summon #43 continuation 5, session_01QJC7i981TSs9L1CGJK59Ax) from objectstack-ai/objectstack#24324, ruled A by the maintainer on 2026-10-18 (batch #365 item 5): preview-first — every copilot draft is one click from being seen by its builder; publish stays the human gate (ADR-0033 / ADR-0037). ⛔ Not a claim.
What is measured (objectui a8560b228527, packages/plugin-chatbot)
The chat shows a drafted result with an amber badge (ChatbotEnhanced.tsx:3068–3076), a Publish button (:2518–2529) and 「Review N change(s)」 (:2532–2544); the Preview button renders only when the draft set includes an app (:2551–2566). A field, view or page draft on an existing app has no preview entrance: the builder goes to Studio's diff or publishes blind.
Preview mode exists and is sticky: app-shell/src/preview/PreviewModeContext.tsx:12–33, :59–95; the DraftPreviewBar (preview/DraftPreviewBar.tsx) carries Exit and Publish; the Live Canvas iframe already opens /apps/:name?preview=draft (console/ai/LiveCanvas.tsx:59–62).
Every draft result carries (type, name) and changedKeys (cloud applyDraft; ADR-0037 §TL;DR item 2).
What changes
The Preview button appears for any drafted result. Its target is the affected surface in preview mode: a page draft → that page; a view draft → the object's list with that view; a field or object draft → the object's default list (the record page when the draft names a record); an app draft → the app, as today. All under ?preview=draft, so the DraftPreviewBar shows with Exit and Publish.
The DraftPreviewBar names what is pending on the surface it shows (the count already comes from GET /meta/_drafts, preview/usePendingDrafts.ts): one line, e.g. 「草稿预览 · 本页 2 项未发布」; when the draft adds a field, the line says the field can be filled after publish (a draft field has no storage).
Nothing publishes; nothing changes for members; /data is not read in preview.
First measurement (before any PR)
On the local stack with the cloud copilot, or a hand-made draft via saveMetaItem({ mode: 'draft' }): a field draft on an existing object shows no Preview today; after the change, Preview opens the object's list in preview mode with the bar. Screenshot both.
Acceptance
Preview for every draft kind, the deep-link table above pinned in a test; the app case unchanged.
The bar's pending line; no publish path added; the member view unchanged (the existing published-only tests are not touched).
Changeset: minor on @object-ui/plugin-chatbot (and @object-ui/app-shell if the bar changes).
Claim: PM loop round 9 (priority:p2)
Session: session_01XVp19E1MYUTFM3dnkwz8gB
Account: baozhoutao (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/issue-12968-draft-preview-any
Worktree: objectui-issue-12968
Domain: domain:ui
Seat: domain:ui#2
File surface: packages/plugin-chatbot/src/ChatbotEnhanced.tsx (the drafted-result Preview button at about :2551–:2566 and its deep-link target), packages/app-shell/src/preview/ (DraftPreviewBar.tsx's pending line, usePendingDrafts.ts only to read the count per surface), the i18n keys that line needs in packages/i18n/src/locales/*.ts, and tests beside each. Plus one .changeset/12968-*.md (@object-ui/plugin-chatbot, and @object-ui/app-shell / @object-ui/i18n if touched). ⛔ No publish path added; ⛔ the member view and the published-only tests unchanged; ⛔ not packages/app-shell/src/console/ai/AiChatPage.tsx before objectui#12965 lands (see the widened surface below). Stop on breach; explain in the report.
Container & model: M, mode:subagent, model: opus (default tier; dispatch-gates --tier --repo objectstack-ai/objectui reads no path-derived mandate)
Clause-②: yes
Clause-② note: amended in place from no on the dev's measurement (report 6251840024): the exported ChatbotEnhancedProps gains the optional onPreviewDraft, so @object-ui/plugin-chatbot levels minor and one CONTRACT_REVIEW_TIER review is owed before the queue.
File surface widened in place by the seat rulings on this card: packages/app-shell/src/console/ai/AiChatPage.tsx (the onPreviewDraft handler only), packages/app-shell/src/console/ai/LiveCanvas.tsx and packages/plugin-chatbot/src/mapMessages.ts, in one patch round after objectui#12965 lands (serial on AiChatPage.tsx).
Responsibility: n/a — not a defect card (objectstack-ai/objectstack#24324 ruling A, preview-first; filed by the director seat)
Thread-read: none
Serial constraints cleared: no open objectui PR touches packages/plugin-chatbot/ or packages/app-shell/src/preview/. objectui#12965's claim excludes plugin-chatbot and names only AiChatPage.tsx; this seat's objectui#12966 touches neither surface.
Claimed at: 2026-10-18T14:26Z
os-dev-report
{
"issue": 12968,
"status": "blocked",
"branch": "claude/issue-12968-draft-preview-any",
"pr": "#12973",
"session": "session_01XVp19E1MYUTFM3dnkwz8gB",
"premise_still_valid": true,
"summary": "Landed the in-surface half as a draft PR (Part of 12968). (1) plugin-chatbot: a drafted tool result with no app item draws Preview when the host passes the new optional onPreviewDraft(items, {appSegment}). App drafts keep onPreviewDraftApp unchanged. (2) app-shell preview/draftSurface.ts: the deep-link table draftPreviewPath, built with @object-ui/layout resolveHref and always under ?preview=draft (page to page; view to the object list on that view; field or object to the object default list; app or no-surface kind to the app), plus the reverse surface filter matched against APP_ROUTE_METADATA_READS. (3) DraftPreviewBar names the count pending on the surface it shows, using preview.draftBar.pendingHere in ten packs. BLOCKED, which only the PM can clear: the console host that would pass onPreviewDraft is ChatPane in packages/app-shell/src/console/ai/AiChatPage.tsx, which the claim excludes as 12965's surface, and LiveCanvas.tsx opens app roots only and is outside the claim. Until both are widened, the console still draws Preview only for app drafts. The confirm-gated applied-card path also lifts no drafted items (detectReplayOutcome in mapMessages.ts, outside the claim), so it draws no Preview yet. Two ruled pieces are not derivable and need a decision: the field-draft sentence and the record row (see open_questions). A1 held: on 43efd07, an add_field draft rendered no Preview with onPreviewDraftApp wired.",
"tests": "At f9a8e54, vitest --maxWorkers=1 under os-verify-lock. A1 before the change (43efd07): the new chatbot pin gave 'Unable to find an element by: [data-testid="draft-preview"]', Tests 4 failed | 3 passed (7). After the change: ChatbotEnhanced.draftPreviewAny-12968 + ChatbotEnhanced.test 98/98 passed. App-shell preview dir (all test files) + ConsoleLayout x4 + 12 i18n locale tests (key parity, plural categories, count families, draftBar register, glyph/quote/orthography, untranslated identity, machine census, placeholder spelling, raw-key call sites) = Test Files 43 passed, Tests 350 passed. ChatbotEnhanced*, buildPanelI18n, mapMessages*, AiChatPage.buildHistorySurvives, StudioDesignSurface x7 = Test Files 35 passed, Tests 363 passed. Ablation (objectstack scripts/ablation-replace.mjs, trap + blob proof): anchor 'return onPreviewDraft && appSegment ? (' replaced by 'return false && ...' (restores the app-only condition). Anchor 1 to 0, blob 2e650291b551 to 65a99e6ea4b5. Pin red: Tests 4 failed | 3 passed. Restore: blob after restore == HEAD 2e650291b551, git diff HEAD empty. No build/dist leg: the test imports ../ChatbotEnhanced from src. Full package dirs of plugin-chatbot, app-shell and i18n are declared to CI.",
"gates": "Closure build turbo build --filter=@object-ui/app-shell^... 28/28 successful, exit 0. type-check: plugin-chatbot 0, i18n 0, app-shell 0 (each runs tsc --noEmit plus tsconfig.test.json, which compiles tests). Lint narrowed to the 15 changed files (eslint --no-inline-config --format json): 15 files, 0 errors. The 11 warnings are all on untouched ChatbotEnhanced.tsx lines. No type-aware linting is configured, so the diff cannot move a verdict on an untouched file. Exit 0 for each of: check:phantom-deps, self-import, unreferenced-sources, doc-example-readers, handler-key-reads, side-effects-array, element-data-source-declaration, spec-symbols, action-forward-parity, designer-field-key-parity, icon-record-names, i18n-keys, i18n-drift, i18n-designer-parity, control-bytes, check-hand-rolled-comment-mask.mjs, check-changeset-fixed.mjs, check-changeset-presence.mjs, check:new-line-citations. check:spec-floors was not run (named as possibly red on main). CI: in_progress, not awaited.",
"files_changed": "packages/plugin-chatbot/src/ChatbotEnhanced.tsx; packages/plugin-chatbot/src/tests/ChatbotEnhanced.draftPreviewAny-12968.test.tsx (new); packages/app-shell/src/preview/draftSurface.ts (new); packages/app-shell/src/preview/DraftPreviewBar.tsx; packages/app-shell/src/preview/tests/draftPreview-12968.test.tsx (new); packages/i18n/src/locales/{en,zh,ja,ko,de,es,fr,pt,ru,ar}.ts; .changeset/12968-draft-preview-any.md (plugin-chatbot minor, app-shell patch, i18n patch). usePendingDrafts.ts was not edited.",
"line_budget": "n/a (no skills/** or governed surface touched)",
"clause_2": "yes. ChatbotEnhancedProps (exported) gains the optional onPreviewDraft, so the claim's provisional 'Clause-2: no' should be amended to yes, and one CONTRACT_REVIEW_TIER review is owed. draftSurface.ts is not exported from the app-shell barrel.",
"deviations": "(1) PR body first line is the Part-of form, not the dispatch's closing-keyword form. The role file says to use Part of when merging must not close the card, and the console wiring is still outstanding. (2) No package README or content/docs update (AGENTS #2): outside the claimed file surface. Owed with the wiring follow-up. (3) No labels written: objectui has no gate reading them and the dispatch named none.",
"mcp_calls": "0",
"api_writes": "2 relay strokes plus 4 git pushes. Stroke 1 pr_create via fleet-write (POST objectstack-ai/objectstack/dispatches, which executed POST /repos/objectstack-ai/objectui/pulls (draft) and POST /repos//issues/12973/assignees [baozhoutao]); read-back identical, 8914 bytes. Stroke 2 is this os-dev-report comment (POST /repos//issues/12968/comments via the relay). git push: empty-branch probe plus 3 commits (801b93a, 7a79fd2, f9a8e54).",
"open_questions": [
{
"question": "File surface: the console host of the new Preview callback is ChatPane in AiChatPage.tsx, which the claim excludes as 12965's surface. LiveCanvas.tsx (outside the claim) opens /apps/SEGMENT?preview=draft only. How should the wiring land?",
"options": [
"A: Widen this claim to AiChatPage.tsx (onPreviewDraft handler region only; 12965 touches the PendingDraftsBar region) and LiveCanvas.tsx (accept a path beside the segment). Patch round on PR 12973, about 40 lines plus a pin.",
"B: Same as A, but after 12965 lands, to avoid any overlap.",
"C: A separate card for the wiring; this PR stays Part of."
],
"recommendation": "A. On the business axis, the ruling's value is only realised once the console passes onPreviewDraft, and the regions do not overlap. Contract-first: the URL table already exists in preview/draftSurface.ts, so the host only calls it. Startup-focus: no new surface, only the wiring the ruling ordered."
},
{
"question": "Field-draft sentence on the DraftPreviewBar ('the field can be filled after publish'): _drafts rows carry no changedKeys, and a field draft is an object draft, so the count usePendingDrafts reads cannot tell that a field was added. How should the bar know?",
"options": [
"A: Say it on the chat side instead. The applied-card replay already carries existingRows (object, field, rows a new field leaves empty, cloud 2924), and the tool result carries changedKeys. Small, no new request.",
"B: In preview, the bar diffs the draft object body against the published one (two meta reads per object surface). New requests, objectui only.",
"C: Contract: ListDraftsResponseSchema rows gain changedKeys (objectstack spec plus producer). The strictest option, but a cross-repo change.",
"D: Drop the sentence."
],
"recommendation": "A. On the business axis, the moment a builder learns a new field is empty is when the copilot reports the change, and the data already exists there. On long-term soundness, it adds no second read path. On AI-safety, nothing tolerant is added. On startup-focus, it adds no new contract surface. Choose C only if the bar itself must say it."
},
{
"question": "Record row ('the record page when the draft names a record'): no drafted envelope the chat lifts carries a record id (detectDraftResult: type, name, packageId; replay: draftState, existingRows, appliedCard). Implement or retire the row?",
"options": [
"A: Retire the row from this card. A field or object draft previews the object list.",
"B: The producer (cloud applyDraft) adds recordId to the drafted envelope, and the chat lifts it. Cross-repo."
],
"recommendation": "A. On the startup-focus axis, no producer writes the key today, and the ruling's other rows cover the object."
},
{
"question": "Confirm-gated applied card (replayOutcome drafted, the main path for copilot edits after 12894): detectReplayOutcome lifts no drafted items, so that card cannot draw Preview. mapMessages.ts is outside the claim. Widen?",
"options": [
"A: Widen to packages/plugin-chatbot/src/mapMessages.ts (lift drafted items into ReplayOutcome) in the same patch round as the wiring.",
"B: Follow-up card."
],
"recommendation": "A. On the business axis, 'any copilot draft' includes the confirm path, and without it the ruling covers only direct authoring tools."
}
],
"out_of_scope_findings": []
}
Seat rulings on the dev's open questions · domain:ui seat 2 · session_01XVp19E1MYUTFM3dnkwz8gB · 2026-10-18T15:45Z
These answer the four open questions in os-dev-report 6251840024 (PR #12973 at f9a8e54). All four are in the seat's no-escalation class (ordering, file surface, and dropping rows no producer can feed). The director seat can overrule any of them.
The record row ("the record page when the draft names a record"): A, retired from this card. No drafted envelope the chat lifts carries a record id, and no producer writes one. A field or object draft previews the object's default list.
The confirm-gated applied card (detectReplayOutcome lifts no drafted items): A.packages/plugin-chatbot/src/mapMessages.ts joins the patch round in item 1, so "any copilot draft" covers the confirm path.
Clause-②:yes. The exported ChatbotEnhancedProps gains the optional onPreviewDraft. The claim is amended in place, @object-ui/plugin-chatbot is at minor, and one CONTRACT_REVIEW_TIER review on the final head is owed before the queue.
os-dev-report
{
"issue": 12968,
"status": "done",
"branch": "claude/issue-12968-draft-preview-any",
"pr": "#12973",
"session": "session_01XVp19E1MYUTFM3dnkwz8gB (the parent seat's id; this run is its subagent)",
"premise_still_valid": true,
"summary": "Patch round on PR #12973, new head 58d0559 (from f9a8e54, no force-push, no rebase, no amend). Merge commit 174146b brings origin/main 32a171e (objectui#12965 / PR #12975) in cleanly; the PendingDraftsBar region of AiChatPage.tsx is byte-for-byte main's. Ruling 1: ChatPane passes onPreviewDraft, which opens the Live Canvas at draftPreviewPath(items, appSegment) (always ?preview=draft) instead of navigating the host away from the conversation; LiveCanvas takes a full preview path beside the app segment (new optional prop path, ignored once materialized); onPreviewDraftApp is untouched, so the app path opens with no path. Ruling 4: detectReplayOutcome lifts a drafted replay's drafted[] into ReplayOutcome.items (one helper draftedItemsOf shared with detectDraftResult; not draftState.items, which is every pending draft of the package), and the applied card plus the bare drafted badge (older cloud) draw Preview through one shared rule draftPreviewAction (app in the set -> onPreviewDraftApp, else onPreviewDraft with the package id); the round-1 drafted-result button now uses the same helper, testids unchanged. DEVIATION, declared: pin (b) cannot be met without packages/plugin-chatbot/src/ChatbotEnhanced.tsx (the card is drawn there), which the order's round surface does not list but the claim's original surface does (the drafted-result Preview); edited there only: the helper, the AppliedDraftCard Preview button, the drafted-badge Preview button, the onPreviewDraft prop doc. Also updated two existing exact-shape pins beside mapMessages.ts (mapMessages.changePosture-12894.test.ts, mapMessages.replayOutcome.test.ts) for the new items key; one test title changed from 'lifts exactly what it lifted before' to 'lifts none of the cloud#2924 keys'. Changeset body amended to name the wiring; levels unchanged (plugin-chatbot minor already covers ReplayOutcome.items, an optional key on an exported type; LiveCanvas and draftSurface are not exported from app-shell, so app-shell stays patch). PR body NOT patched: the role file says a dev writes the PR body once, at creation, never PATCH; the seat owes the edit named in open_questions. Labels: zero writes (objectui, none named).",
"tests": "All runs from repo root under os-verify-lock, vitest --maxWorkers=1. (1) New tests at e3e58d8: AiChatPage.previewDraft-12968.test.tsx + LiveCanvas.test.tsx + ChatbotEnhanced.appliedCardPreview-12968.test.tsx -> 'Test Files 3 passed (3) / Tests 17 passed (17)', VERDICT command-exit 0. (2) ABLATION at e3e58d8 via objectstack scripts/ablation-replace.mjs WRAP mode (trap-armed): anchor 'onPreviewDraft={(items, { appSegment }) => {' x1 -> x0 renamed to data-ablated-preview-draft (prop dropped from ChatbotEnhanced), blob e81282cedec4 -> 4b81ba517be3; run: 'Tests 4 failed | 1 passed (5)' (the door pin + field/view/page canvas pins red; the app-path control stays green, as expected), VERDICT command-exit 1; restore: blob after restore e81282cedec4 == blob at HEAD e81282cedec4, git diff HEAD empty. No dist involved (the test imports AiChatPage from src). (3) Families naming onPreviewDraft / draftPreviewPath / detectReplayOutcome / PendingDraftsBar / LiveCanvas (16 files incl. round-1 pins draftPreview-12968 and draftPreviewAny-12968, PendingDraftsBar x2, AiChatPage.studioDraftsBar-12965 control) + ChatbotEnhanced.replayOutcome + changeCardV2-12894 at e3e58d8: 18 files green, 2 red = 4 exact-shape pins on the new items key (mapMessages.changePosture-12894 x3, mapMessages.replayOutcome x1); pins updated in 58d0559. (4) Every test naming uiMessageToChatMessage / mapMessages / replayOutcome (27 files, includes the two updated pins, the new chatbot test, StudioDesignSurface.copilotDraftBadge-12725, toolCardHeaderState-7254) on the 58d0559 tree: 'Test Files 27 passed (27) / Tests 393 passed (393)', VERDICT command-exit 0. (5) Closure build pnpm --workspace-concurrency=2 --filter '@object-ui/app-shell^...' build, then type-check for @object-ui/plugin-chatbot and @object-ui/app-shell (tsc --noEmit && tsc -p tsconfig.test.json) at 58d0559: both 'type-check: Done', VERDICT command-exit 0 (6m58s held); --listFilesOnly confirms the test projects read the touched tests (app-shell 2/2, plugin-chatbot 3/3). @object-ui/i18n untouched this round, no type-check owed. (6) Gate battery at 58d0559, exit codes: check:phantom-deps 0, check:self-import 0, check:unreferenced-sources 0, check:doc-example-readers 0, check:handler-key-reads 0, check:side-effects-array 0, check:element-data-source-declaration 0, check:spec-symbols 0, check:action-forward-parity 0, check:designer-field-key-parity 0, check:icon-record-names 0, check:i18n-keys 0, check:i18n-drift 0, check:i18n-designer-parity 0, check:control-bytes 0 ('OK (scanned 9560 tracked text file(s)'), check-hand-rolled-comment-mask 0, check-changeset-fixed 0; extra: check-changeset-presence green ('23 source file(s) of 3 released package(s) changed, and this change declares 1 changeset(s)'), check:new-line-citations 'VERDICT ... 0 new citation(s)' exit 0. (7) Narrowed eslint (eslint --no-inline-config --format json) over the 9 .ts/.tsx files this round changed, at 58d0559: exit 0, JSON reports 9 files, 0 errors, 45 warnings; warnings equal the base 174146b per file (AiChatPage.tsx 34 = 34, ChatbotEnhanced.tsx 11 = 11, all others 0). Population read from eslint.config.js: the files block for **/*.{ts,tsx} sets languageOptions with no parserOptions.project, so type-aware linting is off and this diff cannot move a verdict on an untouched file. DECLARED TO CI: the full packages/plugin-chatbot/, packages/app-shell/ and packages/i18n/ directories (per order), and the repo-wide lint farm. dispatch-gates.mjs not run: it answers objectstack's tree; objectui's gate list here is the order's hand-derived one.",
"mcp_calls": "0",
"api_writes": "1 - POST repos//issues/12968/comments (this os-dev-report, via scripts/pm/post-stamped.mjs). Not REST: 3 git pushes to claude/issue-12968-draft-preview-any (f9a8e54 -> e3e58d8 -> 58d0559). Reads: 2 REST GETs of comments 6251068724 and 6251864146.",
"open_questions": [
{
"question": "PR #12973 body edit the seat owes (the role file forbids a dev PATCH of the PR body).",
"options": [
"A: change the first line from 'Part of #12968' to 'Fixes #12968' and add a 'Patch round' section: ChatPane passes onPreviewDraft (Live Canvas opens draftPreviewPath's target, ?preview=draft); LiveCanvas takes a full preview path beside the app segment; detectReplayOutcome lifts ReplayOutcome.items and the confirm-gated applied card (and the older-cloud drafted badge) draws Preview through the same door; merge of origin/main 32a171e with PendingDraftsBar untouched; ablation (drop onPreviewDraft in ChatPane -> 4 red, restore proven by blob hash e81282cedec4); ChatbotEnhanced.tsx edited as the declared surface deviation",
"B: leave the body as is"
],
"recommendation": "A, because ruling 1 names the first-line change as this round's outcome and the queue review reads the body."
},
{
"question": "Ruling 1 says Preview 'navigates' to the draftPreviewPath target. Implemented as: the Live Canvas iframe opens that target beside the chat; the host route stays on the conversation (the same posture onPreviewDraftApp has, and objectui#12724's stay-on-conversation ruling). Is an in-canvas open the intended reading?",
"options": [
"A: keep the in-canvas open (as built)",
"B: also navigate the host router to the path"
],
"recommendation": "A, because a host navigation would leave the conversation and contradicts the objectui#12724 ruling; the canvas already has the mobile full-screen overlay path."
}
],
"out_of_scope_findings": [
"carrier: none (承接者:无) - for a non-app surface preview the Live Canvas header reads the package id ('Live preview - app.k9 (draft)'), because ChatPane's canvas state has no surface label; ExcelImportBar's defaultObjectName likewise reads canvasApp.name, which was already an app name, not an object name, before this round. Noted only, not filed."
]
}
PR #12973 at head 58d0559, reviewed against the claim 6251068724 (amended: Clause-②: yes, surface widened), the seat rulings 6251864146, the dev reports 6251840024 (round 1) and 6252456713 (wiring round), and the contract review 6252536903.
What landed in the PR: the chat draws Preview for every copilot draft kind, not only an app. The confirm-gated applied card and the drafted badge an older cloud sends draw it too, through one rule (draftPreviewAction): an app goes to onPreviewDraftApp, unchanged; anything else goes to the new optional onPreviewDraft. In the console, ChatPane opens the Live Canvas at draftPreviewPath's target beside the conversation. That target is built with @object-ui/layout's resolveHref, always under ?preview=draft: page → the page; view → the object's list on that view; field / object → the object's default list; app or unknown kind → the app. detectReplayOutcome lifts the replay's own drafted[], not the package's whole pending set. The DraftPreviewBar names the drafts pending on the surface it shows (preview.draftBar.pendingHere, ten packs), from entries usePendingDrafts already fetched. Nothing on the Preview path publishes, reads /data or changes the member view (ADR-0033 / ADR-0037).
Seat rulings held at the head: the wiring followed objectui#12965's landing (main32a171e merged in; its PendingDraftsBar region is byte-for-byte main's); there is no field-draft sentence on the bar (the applied card already says it) and no record row; and mapMessages.ts was lifted. Preview opens in the canvas and the host stays on the conversation (objectui#12724).
Clause-②:yes (widening): ChatbotEnhancedProps.onPreviewDraft and ReplayOutcome.items are optional members on exported @object-ui/plugin-chatbot types. draftSurface.ts, LiveCanvas and DraftPreviewBar are not in @object-ui/app-shell's barrel. Changeset: plugin-chatbot: minor, app-shell: patch, i18n: patch.
Contract review: PASS 6252536903 at CONTRACT_REVIEW_TIER on head 58d0559. Its one escalation, the card's parenthetical "minor on @object-ui/app-shell if the bar changes", is ruled B by the seat: patch stays. app-shell's published face did not grow, and in the fixed group the package ships at the same minor version either way, so the level only picks the CHANGELOG heading. A push to change it would buy a CI cycle for nothing.
Tests: round 1 has the deep-link table, the bar's pending line, a no-publish pin and a member-view CONTROL; the A1 reading showed a field draft drawing no Preview before the change. The wiring round adds AiChatPage.previewDraft-12968, LiveCanvas and ChatbotEnhanced.appliedCardPreview-12968. Ablation of onPreviewDraft in ChatPane turns 4 pins red with the app-path control green; restore proven by blob hash. 27 chat-mapping files (393 tests) pass; type-check and the pre-tsc battery exit 0.
Acceptance notes, not filed: for a non-app surface, the Live Canvas header still shows the package id (the canvas state carries no surface label).
Seat edits: the PR body's first line is now Fixes #12968, with a patch-round section; the title no longer says "part 1".
Trailers: every commit (the merge commit included) carries only the model-free pair.
CI at head: all 36 check-runs on 58d0559 are complete: 33 success, 3 skipped (dependabot and the two coverage jobs), none failed; mergeable_state: clean. Path surface (24 files, +1121 / −41): packages/plugin-chatbot/src/**, packages/app-shell/src/{preview,console/ai}/**, packages/i18n/src/locales/** and one changeset; no governed path.
PR #12973 merged through the merge queue as 76f91f80d9bd150b1789fedfcd39ebfb19b72e0d, which is on main; Fixes #12968 closes this card. Verified by content:
14 of the PR's 24 files on main are blob-identical to the accepted head 58d0559.
The other 10 are the locale packs, and they differ only because objectui#12970 (PR feat(app-shell): the help menu's About names the console build (objectui#12970) #12976, ea34d9b) landed its help.about* keys in the same packs between this branch's last main merge (32a171e) and the queue. For each pack, the PR's own changed lines (base 32a171e → 58d0559) equal the merge commit's changed lines, 10 of 10.
The merge commit's message names no model.
What landed:
The chat draws Preview for every copilot draft kind, including on the confirm-gated applied card. It opens the drafted surface (page, the object's list on a view, the object's default list, or the app) under ?preview=draft in the Live Canvas beside the conversation, and nothing publishes. The DraftPreviewBar names what is pending on the surface it shows. @object-ui/plugin-chatbot: minor (onPreviewDraft, ReplayOutcome.items), @object-ui/app-shell and @object-ui/i18n: patch. Contract review PASS 6252536903.
For objectstack-ai/objectstack#24324 (ruling A, preview-first): the console half is on objectui main, and it applies once a framework or cloud build's objectui pin covers the merge commit above. This seat now removes pm:dispatched.
Activity
objectstack-fleet commentedon Oct 18, 2026
Claim: PM loop round 9 (
priority:p2)Session:
session_01XVp19E1MYUTFM3dnkwz8gBAccount:
baozhoutao(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-12968-draft-preview-anyWorktree:
objectui-issue-12968Domain:
domain:uiSeat:
domain:ui#2File surface:
packages/plugin-chatbot/src/ChatbotEnhanced.tsx(the drafted-result Preview button at about :2551–:2566 and its deep-link target),packages/app-shell/src/preview/(DraftPreviewBar.tsx's pending line,usePendingDrafts.tsonly to read the count per surface), the i18n keys that line needs inpackages/i18n/src/locales/*.ts, and tests beside each. Plus one.changeset/12968-*.md(@object-ui/plugin-chatbot, and@object-ui/app-shell/@object-ui/i18nif touched). ⛔ No publish path added; ⛔ the member view and the published-only tests unchanged; ⛔ notpackages/app-shell/src/console/ai/AiChatPage.tsxbefore objectui#12965 lands (see the widened surface below). Stop on breach; explain in the report.Container & model:
M,mode:subagent,model: opus(default tier;dispatch-gates --tier --repo objectstack-ai/objectuireads no path-derived mandate)Clause-②: yes
Clause-② note: amended in place from
noon the dev's measurement (report6251840024): the exportedChatbotEnhancedPropsgains the optionalonPreviewDraft, so@object-ui/plugin-chatbotlevelsminorand oneCONTRACT_REVIEW_TIERreview is owed before the queue.File surface widened in place by the seat rulings on this card:
packages/app-shell/src/console/ai/AiChatPage.tsx(theonPreviewDrafthandler only),packages/app-shell/src/console/ai/LiveCanvas.tsxandpackages/plugin-chatbot/src/mapMessages.ts, in one patch round after objectui#12965 lands (serial onAiChatPage.tsx).Responsibility: n/a — not a defect card (objectstack-ai/objectstack#24324 ruling A, preview-first; filed by the director seat)
Thread-read: none
Serial constraints cleared: no open objectui PR touches
packages/plugin-chatbot/orpackages/app-shell/src/preview/. objectui#12965's claim excludesplugin-chatbotand names onlyAiChatPage.tsx; this seat's objectui#12966 touches neither surface.Claimed at: 2026-10-18T14:26Z
objectstack-fleet commentedon Oct 18, 2026
os-dev-report
{
"issue": 12968,
"status": "blocked",
"branch": "claude/issue-12968-draft-preview-any",
"pr": "#12973",
"session": "session_01XVp19E1MYUTFM3dnkwz8gB",
"premise_still_valid": true,
"summary": "Landed the in-surface half as a draft PR (Part of 12968). (1) plugin-chatbot: a drafted tool result with no app item draws Preview when the host passes the new optional onPreviewDraft(items, {appSegment}). App drafts keep onPreviewDraftApp unchanged. (2) app-shell preview/draftSurface.ts: the deep-link table draftPreviewPath, built with @object-ui/layout resolveHref and always under ?preview=draft (page to page; view to the object list on that view; field or object to the object default list; app or no-surface kind to the app), plus the reverse surface filter matched against APP_ROUTE_METADATA_READS. (3) DraftPreviewBar names the count pending on the surface it shows, using preview.draftBar.pendingHere in ten packs. BLOCKED, which only the PM can clear: the console host that would pass onPreviewDraft is ChatPane in packages/app-shell/src/console/ai/AiChatPage.tsx, which the claim excludes as 12965's surface, and LiveCanvas.tsx opens app roots only and is outside the claim. Until both are widened, the console still draws Preview only for app drafts. The confirm-gated applied-card path also lifts no drafted items (detectReplayOutcome in mapMessages.ts, outside the claim), so it draws no Preview yet. Two ruled pieces are not derivable and need a decision: the field-draft sentence and the record row (see open_questions). A1 held: on 43efd07, an add_field draft rendered no Preview with onPreviewDraftApp wired.",
"tests": "At f9a8e54, vitest --maxWorkers=1 under os-verify-lock. A1 before the change (43efd07): the new chatbot pin gave 'Unable to find an element by: [data-testid="draft-preview"]', Tests 4 failed | 3 passed (7). After the change: ChatbotEnhanced.draftPreviewAny-12968 + ChatbotEnhanced.test 98/98 passed. App-shell preview dir (all test files) + ConsoleLayout x4 + 12 i18n locale tests (key parity, plural categories, count families, draftBar register, glyph/quote/orthography, untranslated identity, machine census, placeholder spelling, raw-key call sites) = Test Files 43 passed, Tests 350 passed. ChatbotEnhanced*, buildPanelI18n, mapMessages*, AiChatPage.buildHistorySurvives, StudioDesignSurface x7 = Test Files 35 passed, Tests 363 passed. Ablation (objectstack scripts/ablation-replace.mjs, trap + blob proof): anchor 'return onPreviewDraft && appSegment ? (' replaced by 'return false && ...' (restores the app-only condition). Anchor 1 to 0, blob 2e650291b551 to 65a99e6ea4b5. Pin red: Tests 4 failed | 3 passed. Restore: blob after restore == HEAD 2e650291b551, git diff HEAD empty. No build/dist leg: the test imports ../ChatbotEnhanced from src. Full package dirs of plugin-chatbot, app-shell and i18n are declared to CI.",
"gates": "Closure build turbo build --filter=@object-ui/app-shell^... 28/28 successful, exit 0. type-check: plugin-chatbot 0, i18n 0, app-shell 0 (each runs tsc --noEmit plus tsconfig.test.json, which compiles tests). Lint narrowed to the 15 changed files (eslint --no-inline-config --format json): 15 files, 0 errors. The 11 warnings are all on untouched ChatbotEnhanced.tsx lines. No type-aware linting is configured, so the diff cannot move a verdict on an untouched file. Exit 0 for each of: check:phantom-deps, self-import, unreferenced-sources, doc-example-readers, handler-key-reads, side-effects-array, element-data-source-declaration, spec-symbols, action-forward-parity, designer-field-key-parity, icon-record-names, i18n-keys, i18n-drift, i18n-designer-parity, control-bytes, check-hand-rolled-comment-mask.mjs, check-changeset-fixed.mjs, check-changeset-presence.mjs, check:new-line-citations. check:spec-floors was not run (named as possibly red on main). CI: in_progress, not awaited.",
"files_changed": "packages/plugin-chatbot/src/ChatbotEnhanced.tsx; packages/plugin-chatbot/src/tests/ChatbotEnhanced.draftPreviewAny-12968.test.tsx (new); packages/app-shell/src/preview/draftSurface.ts (new); packages/app-shell/src/preview/DraftPreviewBar.tsx; packages/app-shell/src/preview/tests/draftPreview-12968.test.tsx (new); packages/i18n/src/locales/{en,zh,ja,ko,de,es,fr,pt,ru,ar}.ts; .changeset/12968-draft-preview-any.md (plugin-chatbot minor, app-shell patch, i18n patch). usePendingDrafts.ts was not edited.",
"line_budget": "n/a (no skills/** or governed surface touched)",
"clause_2": "yes. ChatbotEnhancedProps (exported) gains the optional onPreviewDraft, so the claim's provisional 'Clause-2: no' should be amended to yes, and one CONTRACT_REVIEW_TIER review is owed. draftSurface.ts is not exported from the app-shell barrel.",
"deviations": "(1) PR body first line is the Part-of form, not the dispatch's closing-keyword form. The role file says to use Part of when merging must not close the card, and the console wiring is still outstanding. (2) No package README or content/docs update (AGENTS #2): outside the claimed file surface. Owed with the wiring follow-up. (3) No labels written: objectui has no gate reading them and the dispatch named none.",
"mcp_calls": "0",
"api_writes": "2 relay strokes plus 4 git pushes. Stroke 1 pr_create via fleet-write (POST objectstack-ai/objectstack/dispatches, which executed POST /repos/objectstack-ai/objectui/pulls (draft) and POST /repos//issues/12973/assignees [baozhoutao]); read-back identical, 8914 bytes. Stroke 2 is this os-dev-report comment (POST /repos//issues/12968/comments via the relay). git push: empty-branch probe plus 3 commits (801b93a, 7a79fd2, f9a8e54).",
"open_questions": [
{
"question": "File surface: the console host of the new Preview callback is ChatPane in AiChatPage.tsx, which the claim excludes as 12965's surface. LiveCanvas.tsx (outside the claim) opens /apps/SEGMENT?preview=draft only. How should the wiring land?",
"options": [
"A: Widen this claim to AiChatPage.tsx (onPreviewDraft handler region only; 12965 touches the PendingDraftsBar region) and LiveCanvas.tsx (accept a path beside the segment). Patch round on PR 12973, about 40 lines plus a pin.",
"B: Same as A, but after 12965 lands, to avoid any overlap.",
"C: A separate card for the wiring; this PR stays Part of."
],
"recommendation": "A. On the business axis, the ruling's value is only realised once the console passes onPreviewDraft, and the regions do not overlap. Contract-first: the URL table already exists in preview/draftSurface.ts, so the host only calls it. Startup-focus: no new surface, only the wiring the ruling ordered."
},
{
"question": "Field-draft sentence on the DraftPreviewBar ('the field can be filled after publish'): _drafts rows carry no changedKeys, and a field draft is an object draft, so the count usePendingDrafts reads cannot tell that a field was added. How should the bar know?",
"options": [
"A: Say it on the chat side instead. The applied-card replay already carries existingRows (object, field, rows a new field leaves empty, cloud 2924), and the tool result carries changedKeys. Small, no new request.",
"B: In preview, the bar diffs the draft object body against the published one (two meta reads per object surface). New requests, objectui only.",
"C: Contract: ListDraftsResponseSchema rows gain changedKeys (objectstack spec plus producer). The strictest option, but a cross-repo change.",
"D: Drop the sentence."
],
"recommendation": "A. On the business axis, the moment a builder learns a new field is empty is when the copilot reports the change, and the data already exists there. On long-term soundness, it adds no second read path. On AI-safety, nothing tolerant is added. On startup-focus, it adds no new contract surface. Choose C only if the bar itself must say it."
},
{
"question": "Record row ('the record page when the draft names a record'): no drafted envelope the chat lifts carries a record id (detectDraftResult: type, name, packageId; replay: draftState, existingRows, appliedCard). Implement or retire the row?",
"options": [
"A: Retire the row from this card. A field or object draft previews the object list.",
"B: The producer (cloud applyDraft) adds recordId to the drafted envelope, and the chat lifts it. Cross-repo."
],
"recommendation": "A. On the startup-focus axis, no producer writes the key today, and the ruling's other rows cover the object."
},
{
"question": "Confirm-gated applied card (replayOutcome drafted, the main path for copilot edits after 12894): detectReplayOutcome lifts no drafted items, so that card cannot draw Preview. mapMessages.ts is outside the claim. Widen?",
"options": [
"A: Widen to packages/plugin-chatbot/src/mapMessages.ts (lift drafted items into ReplayOutcome) in the same patch round as the wiring.",
"B: Follow-up card."
],
"recommendation": "A. On the business axis, 'any copilot draft' includes the confirm path, and without it the ruling covers only direct authoring tools."
}
],
"out_of_scope_findings": []
}
Generated by Claude Code
objectstack-fleet commentedon Oct 18, 2026
Seat rulings on the dev's open questions ·
domain:uiseat 2 ·session_01XVp19E1MYUTFM3dnkwz8gB· 2026-10-18T15:45ZThese answer the four open questions in os-dev-report
6251840024(PR #12973 atf9a8e54). All four are in the seat's no-escalation class (ordering, file surface, and dropping rows no producer can feed). The director seat can overrule any of them.AiChatPage.tsx'sChatPanepassingonPreviewDraft,LiveCanvas.tsxopening a path beside the segment): B. It lands as a patch round on PR feat(plugin-chatbot,app-shell): Preview for any copilot draft kind, its deep-link table, the console wiring and the preview bar's pending line (objectui#12968) #12973 after objectui#12965 lands.AiChatPage.tsxis objectui#12965's in-flight surface, and same-file work runs serially. PR feat(plugin-chatbot,app-shell): Preview for any copilot draft kind, its deep-link table, the console wiring and the preview bar's pending line (objectui#12968) #12973 stays draft until then, so no half (a prop no console host passes) reachesmainon its own. The patch round turns the PR's first line intoFixes #12968.existingRows). That is the moment the builder learns it. The bar does not repeat it:_draftsrows carry nochangedKeys, and a second read path or a cross-repo contract change for a repeated sentence buys nothing.detectReplayOutcomelifts no drafted items): A.packages/plugin-chatbot/src/mapMessages.tsjoins the patch round in item 1, so "any copilot draft" covers the confirm path.Clause-②:
yes. The exportedChatbotEnhancedPropsgains the optionalonPreviewDraft. The claim is amended in place,@object-ui/plugin-chatbotis atminor, and oneCONTRACT_REVIEW_TIERreview on the final head is owed before the queue.Generated by Claude Code
objectstack-fleet commentedon Oct 18, 2026
os-dev-report
{
"issue": 12968,
"status": "done",
"branch": "claude/issue-12968-draft-preview-any",
"pr": "#12973",
"session": "session_01XVp19E1MYUTFM3dnkwz8gB (the parent seat's id; this run is its subagent)",
"premise_still_valid": true,
"summary": "Patch round on PR #12973, new head 58d0559 (from f9a8e54, no force-push, no rebase, no amend). Merge commit 174146b brings origin/main 32a171e (objectui#12965 / PR #12975) in cleanly; the PendingDraftsBar region of AiChatPage.tsx is byte-for-byte main's. Ruling 1: ChatPane passes onPreviewDraft, which opens the Live Canvas at draftPreviewPath(items, appSegment) (always ?preview=draft) instead of navigating the host away from the conversation; LiveCanvas takes a full preview path beside the app segment (new optional prop path, ignored once materialized); onPreviewDraftApp is untouched, so the app path opens with no path. Ruling 4: detectReplayOutcome lifts a drafted replay's drafted[] into ReplayOutcome.items (one helper draftedItemsOf shared with detectDraftResult; not draftState.items, which is every pending draft of the package), and the applied card plus the bare drafted badge (older cloud) draw Preview through one shared rule draftPreviewAction (app in the set -> onPreviewDraftApp, else onPreviewDraft with the package id); the round-1 drafted-result button now uses the same helper, testids unchanged. DEVIATION, declared: pin (b) cannot be met without packages/plugin-chatbot/src/ChatbotEnhanced.tsx (the card is drawn there), which the order's round surface does not list but the claim's original surface does (the drafted-result Preview); edited there only: the helper, the AppliedDraftCard Preview button, the drafted-badge Preview button, the onPreviewDraft prop doc. Also updated two existing exact-shape pins beside mapMessages.ts (mapMessages.changePosture-12894.test.ts, mapMessages.replayOutcome.test.ts) for the new items key; one test title changed from 'lifts exactly what it lifted before' to 'lifts none of the cloud#2924 keys'. Changeset body amended to name the wiring; levels unchanged (plugin-chatbot minor already covers ReplayOutcome.items, an optional key on an exported type; LiveCanvas and draftSurface are not exported from app-shell, so app-shell stays patch). PR body NOT patched: the role file says a dev writes the PR body once, at creation, never PATCH; the seat owes the edit named in open_questions. Labels: zero writes (objectui, none named).",
"tests": "All runs from repo root under os-verify-lock, vitest --maxWorkers=1. (1) New tests at e3e58d8: AiChatPage.previewDraft-12968.test.tsx + LiveCanvas.test.tsx + ChatbotEnhanced.appliedCardPreview-12968.test.tsx -> 'Test Files 3 passed (3) / Tests 17 passed (17)', VERDICT command-exit 0. (2) ABLATION at e3e58d8 via objectstack scripts/ablation-replace.mjs WRAP mode (trap-armed): anchor 'onPreviewDraft={(items, { appSegment }) => {' x1 -> x0 renamed to data-ablated-preview-draft (prop dropped from ChatbotEnhanced), blob e81282cedec4 -> 4b81ba517be3; run: 'Tests 4 failed | 1 passed (5)' (the door pin + field/view/page canvas pins red; the app-path control stays green, as expected), VERDICT command-exit 1; restore: blob after restore e81282cedec4 == blob at HEAD e81282cedec4, git diff HEAD empty. No dist involved (the test imports AiChatPage from src). (3) Families naming onPreviewDraft / draftPreviewPath / detectReplayOutcome / PendingDraftsBar / LiveCanvas (16 files incl. round-1 pins draftPreview-12968 and draftPreviewAny-12968, PendingDraftsBar x2, AiChatPage.studioDraftsBar-12965 control) + ChatbotEnhanced.replayOutcome + changeCardV2-12894 at e3e58d8: 18 files green, 2 red = 4 exact-shape pins on the new items key (mapMessages.changePosture-12894 x3, mapMessages.replayOutcome x1); pins updated in 58d0559. (4) Every test naming uiMessageToChatMessage / mapMessages / replayOutcome (27 files, includes the two updated pins, the new chatbot test, StudioDesignSurface.copilotDraftBadge-12725, toolCardHeaderState-7254) on the 58d0559 tree: 'Test Files 27 passed (27) / Tests 393 passed (393)', VERDICT command-exit 0. (5) Closure build pnpm --workspace-concurrency=2 --filter '@object-ui/app-shell^...' build, then type-check for @object-ui/plugin-chatbot and @object-ui/app-shell (tsc --noEmit && tsc -p tsconfig.test.json) at 58d0559: both 'type-check: Done', VERDICT command-exit 0 (6m58s held); --listFilesOnly confirms the test projects read the touched tests (app-shell 2/2, plugin-chatbot 3/3). @object-ui/i18n untouched this round, no type-check owed. (6) Gate battery at 58d0559, exit codes: check:phantom-deps 0, check:self-import 0, check:unreferenced-sources 0, check:doc-example-readers 0, check:handler-key-reads 0, check:side-effects-array 0, check:element-data-source-declaration 0, check:spec-symbols 0, check:action-forward-parity 0, check:designer-field-key-parity 0, check:icon-record-names 0, check:i18n-keys 0, check:i18n-drift 0, check:i18n-designer-parity 0, check:control-bytes 0 ('OK (scanned 9560 tracked text file(s)'), check-hand-rolled-comment-mask 0, check-changeset-fixed 0; extra: check-changeset-presence green ('23 source file(s) of 3 released package(s) changed, and this change declares 1 changeset(s)'), check:new-line-citations 'VERDICT ... 0 new citation(s)' exit 0. (7) Narrowed eslint (eslint --no-inline-config --format json) over the 9 .ts/.tsx files this round changed, at 58d0559: exit 0, JSON reports 9 files, 0 errors, 45 warnings; warnings equal the base 174146b per file (AiChatPage.tsx 34 = 34, ChatbotEnhanced.tsx 11 = 11, all others 0). Population read from eslint.config.js: the files block for **/*.{ts,tsx} sets languageOptions with no parserOptions.project, so type-aware linting is off and this diff cannot move a verdict on an untouched file. DECLARED TO CI: the full packages/plugin-chatbot/, packages/app-shell/ and packages/i18n/ directories (per order), and the repo-wide lint farm. dispatch-gates.mjs not run: it answers objectstack's tree; objectui's gate list here is the order's hand-derived one.",
"mcp_calls": "0",
"api_writes": "1 - POST repos//issues/12968/comments (this os-dev-report, via scripts/pm/post-stamped.mjs). Not REST: 3 git pushes to claude/issue-12968-draft-preview-any (f9a8e54 -> e3e58d8 -> 58d0559). Reads: 2 REST GETs of comments 6251068724 and 6251864146.",
"open_questions": [
{
"question": "PR #12973 body edit the seat owes (the role file forbids a dev PATCH of the PR body).",
"options": [
"A: change the first line from 'Part of #12968' to 'Fixes #12968' and add a 'Patch round' section: ChatPane passes onPreviewDraft (Live Canvas opens draftPreviewPath's target, ?preview=draft); LiveCanvas takes a full preview path beside the app segment; detectReplayOutcome lifts ReplayOutcome.items and the confirm-gated applied card (and the older-cloud drafted badge) draws Preview through the same door; merge of origin/main 32a171e with PendingDraftsBar untouched; ablation (drop onPreviewDraft in ChatPane -> 4 red, restore proven by blob hash e81282cedec4); ChatbotEnhanced.tsx edited as the declared surface deviation",
"B: leave the body as is"
],
"recommendation": "A, because ruling 1 names the first-line change as this round's outcome and the queue review reads the body."
},
{
"question": "Ruling 1 says Preview 'navigates' to the draftPreviewPath target. Implemented as: the Live Canvas iframe opens that target beside the chat; the host route stays on the conversation (the same posture onPreviewDraftApp has, and objectui#12724's stay-on-conversation ruling). Is an in-canvas open the intended reading?",
"options": [
"A: keep the in-canvas open (as built)",
"B: also navigate the host router to the path"
],
"recommendation": "A, because a host navigation would leave the conversation and contradicts the objectui#12724 ruling; the canvas already has the mobile full-screen overlay path."
}
],
"out_of_scope_findings": [
"carrier: none (承接者:无) - for a non-app surface preview the Live Canvas header reads the package id ('Live preview - app.k9 (draft)'), because ChatPane's canvas state has no surface label; ExcelImportBar's defaultObjectName likewise reads canvasApp.name, which was already an app name, not an object name, before this round. Noted only, not filed."
]
}
Generated by Claude Code
objectstack-fleet commentedon Oct 18, 2026
ACCEPT ·
domain:uiseat 2 ·session_01XVp19E1MYUTFM3dnkwz8gB· 2026-10-18T16:53ZPR #12973 at head
58d0559, reviewed against the claim6251068724(amended:Clause-②: yes, surface widened), the seat rulings6251864146, the dev reports6251840024(round 1) and6252456713(wiring round), and the contract review6252536903.draftPreviewAction): an app goes toonPreviewDraftApp, unchanged; anything else goes to the new optionalonPreviewDraft. In the console,ChatPaneopens the Live Canvas atdraftPreviewPath's target beside the conversation. That target is built with@object-ui/layout'sresolveHref, always under?preview=draft: page → the page; view → the object's list on that view; field / object → the object's default list; app or unknown kind → the app.detectReplayOutcomelifts the replay's owndrafted[], not the package's whole pending set. The DraftPreviewBar names the drafts pending on the surface it shows (preview.draftBar.pendingHere, ten packs), from entriesusePendingDraftsalready fetched. Nothing on the Preview path publishes, reads/dataor changes the member view (ADR-0033 / ADR-0037).main32a171emerged in; itsPendingDraftsBarregion is byte-for-bytemain's); there is no field-draft sentence on the bar (the applied card already says it) and no record row; andmapMessages.tswas lifted. Preview opens in the canvas and the host stays on the conversation (objectui#12724).yes (widening):ChatbotEnhancedProps.onPreviewDraftandReplayOutcome.itemsare optional members on exported@object-ui/plugin-chatbottypes.draftSurface.ts,LiveCanvasandDraftPreviewBarare not in@object-ui/app-shell's barrel. Changeset:plugin-chatbot: minor,app-shell: patch,i18n: patch.6252536903atCONTRACT_REVIEW_TIERon head58d0559. Its one escalation, the card's parenthetical "minoron@object-ui/app-shellif the bar changes", is ruled B by the seat:patchstays.app-shell's published face did not grow, and in the fixed group the package ships at the same minor version either way, so the level only picks the CHANGELOG heading. A push to change it would buy a CI cycle for nothing.AiChatPage.previewDraft-12968,LiveCanvasandChatbotEnhanced.appliedCardPreview-12968. Ablation ofonPreviewDraftinChatPaneturns 4 pins red with the app-path control green; restore proven by blob hash. 27 chat-mapping files (393 tests) pass; type-check and the pre-tsc battery exit 0.Fixes #12968, with a patch-round section; the title no longer says "part 1".58d0559are complete: 33 success, 3 skipped (dependabot and the two coverage jobs), none failed;mergeable_state: clean. Path surface (24 files, +1121 / −41):packages/plugin-chatbot/src/**,packages/app-shell/src/{preview,console/ai}/**,packages/i18n/src/locales/**and one changeset; no governed path.Next:
pr_ready+automerge_enable.Generated by Claude Code
objectstack-fleet commentedon Oct 18, 2026
Landed ·
domain:uiseat 2 ·session_01XVp19E1MYUTFM3dnkwz8gB· 2026-10-18T17:10ZPR #12973 merged through the merge queue as
76f91f80d9bd150b1789fedfcd39ebfb19b72e0d, which is onmain;Fixes #12968closes this card. Verified by content:mainare blob-identical to the accepted head58d0559.ea34d9b) landed itshelp.about*keys in the same packs between this branch's lastmainmerge (32a171e) and the queue. For each pack, the PR's own changed lines (base32a171e→58d0559) equal the merge commit's changed lines, 10 of 10.The merge commit's message names no model.
What landed:
?preview=draftin the Live Canvas beside the conversation, and nothing publishes. The DraftPreviewBar names what is pending on the surface it shows.@object-ui/plugin-chatbot: minor(onPreviewDraft,ReplayOutcome.items),@object-ui/app-shelland@object-ui/i18n:patch. Contract review PASS6252536903.For objectstack-ai/objectstack#24324 (ruling A, preview-first): the console half is on objectui
main, and it applies once a framework or cloud build's objectui pin covers the merge commit above. This seat now removespm:dispatched.Generated by Claude Code