Repository navigation
Studio copilot: one right slot that shows either the properties or the copilot — properties follow the selection and mark the copilot's drafts; the copilot folds to a strip while properties show; change card v2; the 变更 badge in sync; no 源码 tab; pillar labels kept #12725
Description
Source
Walked on 2026-10-16 at 1010 × 1119 in Studio › app.hcpl › Interfaces with the AI 副驾 open: the panel takes about 330 px on the right, the properties fold into a 画布 / 属性 tab pair in the canvas, and the record-list preview is left with one and a half columns. StudioAiCopilot.tsx mounts the panel; StudioDesignSurface.tsx line 1758 hides the pillar labels below xl (sr-only xl:not-sr-only).
Rulings (2026-10-16, the epic PM session, verbatim)
- 「studio 中其他的设计面板属性都是右侧打开的,你换到 tab 会不会改动太大,是否考虑浮出」 and 「属性好需要考虑选中每个元素的时候也要显示。」 — the properties stay on the right and follow the selection.
- 「ai 副驾或者属性面板是否能切换隐藏,这个宽屏的界面好乱。还有我在和ai 讨论的时候估计也不会看属性吧?」 — the floating panel beside a permanent rail was rejected; one right slot shows either the properties or the copilot.
- 「源码的选项卡有必要保留吗?未来还有人看吗」 — the canvas has no 源码 tab.
- 「按「右栏二选一」定稿」.
Target (StudioRailCopilot and StudioRailProps artboards)
- One right slot (360 px at ≥ 1280, 300 px at 1010) with a segmented header 属性 | AI 副驾 (the copilot segment carries a count badge of pending items).
⌘.toggles the slot between the two; the top bar's right-most icon collapses the slot entirely. No floating panel, no overlay dock, no fourth column at any width. - Switching by intent: clicking any canvas element (column, card, field, button) switches the slot to 属性 for that element; Esc or a click on empty canvas returns to the item's own properties. While 属性 shows, the copilot is a 40 px strip at the slot's foot 「AI 副驾 · N 项待确认 · 回到对话 →」; while the copilot shows, the canvas hint reads 「点元素 → 右栏切到属性」.
- Properties follow the selection in every pillar; a field or view setting the copilot changed carries a 草稿 chip and a 来源 line (「由 AI 副驾在 2 分钟前作为草稿加入」). The canvas's own tabs go: no 画布 / 源码 / 属性 tabs; one status line above the canvas (「运行态预览 · 含未发布改动」). The item's definition is reachable from its ⋯ menu (查看定义 · 复制 JSON · 重命名 · 删除) and as a diff in the 变更 drawer.
- Change card v2 inside the copilot: 「修改方案 · N 项」 with one row per change (+ / ✎ symbol, kind, object · field, machine name and parameters in small type), the amber posture note for a live app with data, 存为草稿 (primary) / 调整; the applied card 「已存为草稿 · N 项」 with 查看变更并发布 / 撤销; the composer with the 「借阅看板 ×」 context chip and the placeholder 「问问数据,或让我改改这个应用…」. Data from objectstack-ai/cloud#2924.
- The top bar's 变更 badge counts the copilot's drafts the moment they land; the item header shows 「草稿 · N 项未发布」.
- The pillar switcher keeps its labels (数据 / 自动化 / 界面 / 权限) from 1024 px up (
lg:not-sr-onlyinstead ofxl:); below that, icons with tooltips.
File surface
packages/app-shell/src/views/studio-design/StudioAiCopilot.tsx, StudioDesignSurface.tsx (the right slot, the pillar labels, the canvas status line, the item ⋯ menu), packages/app-shell/src/layout/ChatDock.tsx, the Interfaces preview's selection → properties wiring, tests beside.
Acceptance
- Tests: the slot shows the copilot with no properties; selecting a card switches it to that card's properties with the copilot strip at the foot; Esc returns to the view's properties; the 变更 badge equals the draft count; no 源码 tab; pillar labels visible at 1024.
- Screenshots at 1010 and 1280 px in both slot modes.
pnpm --filter @object-ui/app-shell testgreen.
Design
Artboards on the temporary branch claude/console-home-login-design-20261016 of objectstack-ai/objectui, commit 98aab506a (never merged; *.preview.html opens in a browser, *.dc.html is the canvas source): StudioRailCopilot (source) · StudioRailProps (source). The rejected alternative (the floating panel) stays beside them as StudioCopilot for the record. The README beside them records the rulings.
Evidence
Screenshots on the same branch and commit: ai-studio-books-21-rows-1010px.jpg · ai-studio-kanban-preview.jpg · ai-copilot-modify-plan.jpg.
Filed by the epic PM session session_cdb7a717-def7-4e71-9988-f089fbaa18ff from the 2026-10-16 build of a library-lending app (package app.hcpl) on the test1016 tenant at https://os-4e4qa4.objectos.app (staging, zh-CN, the maintainer's browser pane at 1010 × 1119). The maintainer named the repositories, verbatim: 「部分前端功能代码在 objectui 后端在 onjectstack」.
Body refreshed 2026-10-16T15:56Z: the floating-panel design this card was filed with was replaced by the right-slot design per the maintainer's rulings above; title, Target, File surface, Acceptance and Design rewritten; Source and Evidence unchanged.
Activity
[-]Studio copilot: a floating panel over the canvas that never takes the properties rail; the rail follows the selection and marks the copilot's draft changes; change card v2; the 变更 badge in sync; pillar labels kept[/-][+]Studio copilot: one right slot that shows either the properties or the copilot — properties follow the selection and mark the copilot's drafts; the copilot folds to a strip while properties show; change card v2; the 变更 badge in sync; no 源码 tab; pillar labels kept[/+]objectstack-fleet commentedon Oct 17, 2026
Claim: PM loop round 11, serial · a partial claim,
Part of #12725(priority:p2; filed with its rulings by the epic PM session, no triage comment on the card)Session:
session_011K9g23W67dy2zwnyzaoB6oAccount:
os-teslaBranch:
claude/issue-12725-studio-right-slotWorktree:
objectui-issue-12725(in the cloud session's own container)Domain:
domain:uiSeat:
domain:ui#1File surface: the Studio right slot, items 1, 2, 3 (the canvas half) and 6 of the card's Target. Line numbers are on
main4b3d6ed.packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx:⌘.to toggle the two, and the top bar's collapse.inspectorTab,:3216,:4591);sr-only xl:not-sr-only→lg:,:1787).packages/app-shell/src/views/studio-design/StudioAiCopilot.tsxandpackages/app-shell/src/layout/ChatDock.tsx, only as far as the copilot moves into the shared slot and gains its folded strip. The ChatDock's other hosts keep their behaviour.metadata-admin/i18n.ts, en and zh rows).StudioDesignSurface.*andStudioAiCopilot.*families..changeset/12725-*.mdfor@object-ui/app-shell.Not this claim; the card stays open for them,
Part of #12725(they need the copilot's draft provenance and objectstack-ai/cloud#2924's payload, which this seat cannot read):The PR carries
Part of #12725, notFixes. On merge the seat releases the card back topm:queuewith these items named.Outside the surface:
A route outside the surface stops and reports (stop on breach; explain in the report).
Container & model:
L,mode:cloud,model: opus(default tier; no path-derived mandate; sized L for an 8.5k-line surface file and two hosts)Clause-②: no
Clause-② reason: Studio's own chrome is rearranged. No spec key, exported prop, export or language-pack key changes; the new copy goes in the metadata-admin designer table, and
StudioDesignSurface/StudioChatDockkeep their exported signatures.Responsibility: n/a — a ruled design change (the maintainer's 「按「右栏二选一」定稿」, quoted in the card)
Thread-read: none
Serial constraints cleared: read at objectui
main4b3d6ed. No open non-bot PR exists, and the bot PRs touch no source file. The in-flightdomain:uicards held by other seats (objectui#12843, #12816, #12809, #12795) name console Home, the Approvals inbox, the splash and the console sidebar, not Studio's design surface or the ChatDock. The last changes toStudioDesignSurface.tsxare this seat's PR #12849 (objectui#12718) and PR #12792 (objectui#12707), both merged. The seat holds no other card in flight (serial mode; objectui#12726 was released topm:retriageas a duplicate of objectui#11815).Generated by Claude Code
objectstack-fleet commentedon Oct 17, 2026
os-dev-report
{ "issue": 12725, "status": "done", "branch": "claude/issue-12725-studio-right-slot", "pr": "https://github.com/objectstack-ai/objectui/pull/12863", "session": "session_01RHosmNCrzgLTBGywhN5wSv", "premise_still_valid": true, "summary": "Partial claim delivered (items 1, 2, 6 and the canvas half of item 3); the PR body opens with 'Part of #12725'. Studio now has one right slot (StudioRightSlot): it shows 属性 or AI 副驾, 300 px below 1280 and 360 px from 1280. It has a segmented header, ⌘./Ctrl+. switches the two halves, and the top bar's right-most icon hides it, remembered under the existing Studio sessionStorage key. While 属性 shows, the copilot folds to a 40 px strip at the slot's foot. The Interfaces pillar portals its inspector into the slot. Selecting a canvas element (a dashboard widget or page block through onSelectionChange, or a nav entry in nav editing) switches the slot to 属性. Esc or a click on empty canvas returns to the item's own properties. The canvas lost its 画布/源码/属性 tabs (the folded center tabs and the source page's Source/Properties tabs) for one status line. The item's ⋯ menu opens 查看定义 (a source page's code editor, or any other item's document as JSON) and 复制 JSON; a new page opens on its definition. Pillar names show from lg (1024 px). The copilot's conversation, agent, scope and requests are unchanged: StudioChatDock keeps its name and props and becomes the slot's body; it reports {available, planPending} up to the slot.", "tests": "Full package on merged head bd0e3d5 (merge of origin/main 024d446): `pnpm --filter @object-ui/app-shell test` → EXIT=0, 'Test Files 1534 passed | 1 skipped (1535) · Tests 14511 passed | 9 skipped (14520)'. Earlier on 8c3773b+tests: `vitest run packages/app-shell/src/views/studio-design packages/app-shell/src/layout` → 302 passed / 2134 tests. Baseline before any edit at 4b3d6ed: 303 files / 2131 tests green. New test files: StudioDesignSurface.rightSlot-12725 (12 tests: copilot with no properties; selection → 属性 with the strip at the foot; Esc and an empty-canvas click return to the item's own properties; ⌘./Ctrl+. and the top-bar toggle under the Studio key only; the plan-pending marker; no canvas tabs plus the ⋯ definition; lg pillar labels; controls for an own-aside pillar, no copilot served, and an injected aiSlot); __tests__/studioRightSlot-12725 (8 tests); __tests__/studioCopilotSlotBody-12725 (3 tests). Ablation (the tests import src, so there was no dist step): each mutant was applied to committed HEAD by an anchored replace that refuses unless the anchor hits exactly once, and restored with `git checkout HEAD -- PATH`. Each restore was proved by blob == HEAD blob 712c7d0 and an empty `git diff HEAD`. Results: A1 no showProps → 5 red; A2 Esc off → 1 red; A3 empty-click off → 1 red; A4 lg→xl → 2 red (rightSlot + narrowHeader); A5 default mode props → 2 red; A6 ⌘. off → 1 red; A7 ⋯ menu removed → 3 red; A8 a tab injected into the source-page properties → 1 red. The A1 restore leg first failed: a script bug after `shift` restored the wrong path. The blob guard reported RESTORE FAILED; I restored by hand, verified blob 712c7d0 == HEAD, fixed the script, and the restore legs for A2–A8 passed. Re-pointed pins, each with its reason in a comment: InterfacesPillar.foldedToggle-12611 → renamed slotGutter-12611 (the dot pin becomes 'a selection calls showProps and the inspector is in the slot'; the 16 px gutter moves with the slot; CONTROL classic 24 px). selectionLeafScope (the folded center-tab reading → the canvas status line stops naming a selection the new leaf lacks). sourcePage-12522, interfacesReadOnly-11136, interfacesCreatePage-11823 (the editor moved from the Source tab to the definition sheet; the 'folded layout' variants became 'properties in the right slot'). narrowHeader-12509 (xl → lg). autosaveSwitch-11232 (no Properties tab to open). accessSegment-12452, createAppAccessibleName, createAppOpensInterfaces-11794 (findByRole('banner') → the shell header's test id, because testing-library reads the aside's header as a second banner). navDraftLabels-11862 (the fake client gains withPreviewDrafts; the inspector is now always mounted). studioChatDockPersistence → renamed studioCopilotSlotBody-12725. Deleted, with where each pin went: centerTab.ts and __tests__/centerTab.test.ts pinned nextCenterTab (a target appearing → Properties, a target clearing → Canvas, steady state keeps the user's tab). That rule is gone with the folded tabs: the appear edge is now showProps on the selection's identity (rightSlot 'selecting an element…' and slotGutter 'a selection asks…'); the clear edge is Esc and empty-click returning to the item's own properties (rightSlot); the leaf-change half lives in selectionLeafScope. The steady-state 'never fight the user' rule holds by construction: showProps fires only when the selection identity changes, and rightSlot's strip test shows the copilot stays chosen while the selection stands. __tests__/wideViewport.test.tsx pinned WIDE_VIEWPORT_BREAKPOINT = 1280 and the inclusive boundary of useIsWideViewport, which decided canvas-beside-properties versus tabs. No window width decides between them any more, so that rule no longer applies. The 1280 line survives as the slot's width step (xl:w-[360px]), pinned in studioRightSlot-12725 'is 300 px wide, 360 px from 1280'. useIsNarrowerThan and AUTOMATIONS_CONFIG_FOLD_WIDTH stay in wideViewport.ts, pinned by AutomationsPillar.narrowRow-11795 (green).", "gates": "Local, captured before any pipe, on bd0e3d5: `pnpm --filter '@object-ui/app-shell^...' build` VERDICT command-exit 0; `pnpm --filter @object-ui/app-shell type-check` EXIT=0 (0 'error TS'); check:i18n-keys 0; check:i18n-drift 0; check:i18n-dead-keys 0 (report-only; the one Studio candidate, engine.studio.if.previewIsRuntime, predates this change); check:i18n-designer-parity 0; `vitest run packages/i18n/src/__tests__` 0 (83 files, 1321 passed); check:changeset-claims 0 ('No pending changeset names a file this change touches'); changeset:check 0; check:test-path-roots 0; check:spec-symbols 0; check:new-line-citations 0 ('0 new citation(s)'); `node scripts/check-changeset-presence.mjs` 0 ('25 source file(s) of 1 released package(s) changed … declares 1 changeset'); eslint --no-inline-config on the changed .ts/.tsx files 0 errors (warnings: the same rule set as the base file, one exhaustive-deps warning removed). `node scripts/check-nul-bytes.mjs` → MODULE_NOT_FOUND: that script exists only in objectstack, so this is not a gate reading. I ran the equivalent scan instead (`grep -naP` over control bytes on the changed files): clean. `dispatch-gates.mjs --commands/--ran` is not present in objectui; the list above was derived by hand from package.json and ci.yml per the brief. The CI farm is not yet read: no PR exists.", "mcp_calls": "0", "api_writes": "0 — git push only: claude/issue-12725-studio-right-slot (8c3773b, then bd0e3d5) and the evidence branch claude/issue-12725-evidence (72d4957, evidence/12725/ with 26 PNGs and a README). No PR, comment, label or MCP write.", "open_questions": [ { "question": "Item 3 says properties follow the selection 'in every pillar'. This claim wires Interfaces only; Data (data-field-inspector), Automations (auto-config-aside / auto-config-drawer) and Access keep their own asides, so beside them the slot shows the copilot alone (a 4th column when both are open, as before). Move them into the slot in a follow-up?", "options": [ "A: follow-up card: each pillar claims the slot (useStudioRightSlot) and portals its StudioInspector, the same seam this PR adds", "B: leave them; Interfaces only" ], "recommendation": "A, because the seam is already built and generic: claimProps plus a portal target. The ruling 「studio 中其他的设计面板属性都是右侧打开的」 asks for it, and Data and Automations each mount one StudioInspector, so each pillar is a small, bounded edit." }, { "question": "Clicking a card, column or field in an object/view leaf's canvas (the maintainer's 借阅看板) cannot select anything: StudioCanvasPreviewProps, a published face exported from the package, carries no onSelectionChange by contract. Such a leaf keeps its own properties (ObjectListViewInspector) in the slot.", "options": [ "A: a separate card adds an optional selection channel to StudioCanvasPreviewProps (Clause-② yes)", "B: accept view-level properties only" ], "recommendation": "A, because the card's own example (a kanban card selected) needs it. It changes a published prop, so it is outside this Clause-② no claim." }, { "question": "The 'N 项待确认' count has no source in objectui: the only signal is the boolean usePlanApprovalPending (objectui#11666). The strip and the segment show a marker ('方案待确认') instead of a number, which follows the brief's H5 fallback. Keep it until objectstack-ai/cloud#2924's payload supplies a count?", "options": [ "A: keep the boolean marker now; the count lands with item 4/5", "B: hide the marker until a count exists" ], "recommendation": "A, because it is a real, already-published signal. A count needs cloud#2924's draft provenance." } ], "out_of_scope_findings": [ "class: none (observation) · ChatDockLauncher (packages/app-shell/src/layout/ChatDock.tsx) has no shipped host after this move (the console retired it in P3b; Studio was its last). Retiring it would orphan the language-pack key console.ai.dock.open (Clause-② yes), which this claim rules out, so it stays with a comment saying no surface mounts it. ChatDockPanel's children override and useChatDockState's persistWidthKey option likewise lost their only consumer. · carrier: Acceptance notes · dedupe words: ChatDockLauncher zero consumer, console.ai.dock.open", "class: none (observation) · packages/app-shell/src/views/ObjectView.tsx:1360 has a doc comment naming useIsWideViewport, which this change retired; comment only, outside the surface · carrier: Acceptance notes · dedupe words: useIsWideViewport stale comment", "class: none (observation, not a regression) · at 1010 px the Interfaces item header crushes the title (only the kind chip shows, and the Design/Run toggle clips) because the canvas column is about 470 px; before, with the 340 px dock, it was narrower still (evidence before-copilot-1010-dashboard.png vs after-copilot-1010-dashboard.png) · carrier: Acceptance notes · dedupe words: Studio item header 1010 truncation", "class: none (observation) · after Esc, the clicked dashboard widget keeps a blue ring (focus, not selection: the slot already shows the dashboard's own properties); live reading after-copilot-1010-esc.png · carrier: Acceptance notes · dedupe words: dashboard widget focus ring after deselect" ] }Posted by the
domain:uiseat 1 PM (session_011K9g23W67dy2zwnyzaoB6o) verbatim from the cloud session's final message, because a cloud session cannot comment from its container. The seat filledprwith the draft PR it opened, #12863.The seat's answers:
StudioCanvasPreviewProps. That isClause-②: yes, so the next claim on Studio copilot: one right slot that shows either the properties or the copilot — properties follow the selection and mark the copilot's drafts; the copilot folds to a strip while properties show; change card v2; the 变更 badge in sync; no 源码 tab; pillar labels kept #12725 declares it and owes oneCONTRACT_REVIEW_TIERreview.bd0e3d5: 28 files, +1644/−915.studio-design/,layout/ChatDock.tsx,layout/chatDockState.ts,metadata-admin/i18n.tsor the changeset.index.tsandpackage.jsonhave a zero-line diff.ChatDock.tsxis a comment.bd0e3d5brings onlymain, and the head merges cleanly with currentmain.Clause-②: nostands.bd0e3d5, then the ACCEPT.Generated by Claude Code
objectstack-fleet commentedon Oct 17, 2026
Landed (partial) ·
domain:uiseat 1 ·session_011K9g23W67dy2zwnyzaoB6o· 2026-10-17T06:48Zmain34354a4b6d, with one parent,1aa622a. It is an ancestor oforigin/main.024d446to the reviewed headbd0e3d5, changed line for changed line in each of the 28 files (+1644/−915).packages/app-shell/src/views/studio-design/StudioRightSlot.tsxexists, and the pillar labels inStudioDesignSurface.tsxreadlg:not-sr-only.Part of #12725, so the card stays open.Release:
session_011K9g23W67dy2zwnyzaoB6o(seatdomain:ui#1, accountos-tesla) releases claim6229552663. Cause: a partial landing. Destination:pm:queue, assignee cleared, for the remainder below. Re-claim from the queue; a claim on the remainder starts frommain.The remainder of #12725:
useStudioRightSlot/claimPropsplus a portal target) instead of its own aside.Clause-②: no; no external dependency.StudioCanvasPreviewProps. That isClause-②: yesand owes oneCONTRACT_REVIEW_TIERreview.Recorded, not filed (the PR's Acceptance notes):
ChatDockLauncherhas no shipped host;useIsWideViewportcomment inObjectView.tsx;The cloud session is archived.
Generated by Claude Code
65 remaining items
objectstack-fleet commentedon Oct 20, 2026
ACCEPT — PR #13100 @
3667f86c72f12af8e4b0e31d1de5d2df183aa3e2·domain:uiseat 2 (session_01XVp19E1MYUTFM3dnkwz8gB) ·Part of #127256280220194.2ec6827962): longest prefix,[]governs all, absent means not recorded.DraftChangeis derived from the spec'sGetMetaItemResponse.surfaceoption is'studio' | 'api', sent once, withcopilotunsendable at the type level, and Omitted on delete.views/studio-design/**carrysurface: 'studio'(census on the head).by: 'copilot', with the time computed fromat.mergeDraftChanges, which drops every entry at or under a changed path, so the feared mis-mark cannot occur. Governing-only would leave the commonest copilot edit (one property of a published field) unmarked. The spec pointer6254786917reads the same way.metadata-admin/**.@object-ui/data-objectstackminor,@object-ui/app-shellminor. Clause-②: yes.PermissionMatrixEditPage,views/metadata-admin/PermissionMatrixEditor.tsx) sends nosurface. It should state'studio'only under the Studio host (embedded+packageId), because the same component serves metadata-admin;@object-ui/data-objectstack'sgetDraftplus the three readers, and thesurfacesave option;field-multi/FieldsListEditorrows (metadata-admin widgets);Next: ready + auto-merge; landing is verified by content on
main, then the card is released topm:queuewith the remainder.Generated by Claude Code
objectstack-fleet commentedon Oct 20, 2026
Landed (partial) ·
domain:uiseat 2 ·session_01XVp19E1MYUTFM3dnkwz8gBmain3bcf314e79(Part of #12725).3bcf314e79as at the accepted head3667f86c72. The 21st,metadata-admin/i18n.ts, also carries objectui#13096 (633ef99a3f), which landed onmainfirst. Its sorted changed lines from merge-base to head equal those of the merge commit against its first parent, so the PR's hunks landed exactly. The merge message carries no model identifier.@object-ui/data-objectstackreadsdraftChanges(readDraftChanges,governingDraftChange,draftChangesUnder) and sends?surface=studioon Studio's draft saves (all 15 underviews/studio-design/**);ObjectListViewInspectordraw the 草稿 chip and 「由 AI 副驾在 N 分钟前作为草稿加入」 for the copilot's drafted settings, and mark the array elements the copilot added.Release:
session_01XVp19E1MYUTFM3dnkwz8gB(seatdomain:ui#2, accountbaozhoutao) releases claim6279105929. Cause: a partial landing. Destination:pm:queue, assignee cleared. Re-claim from the queue; a claim on the remainder starts frommain.The remainder of #12725:
PermissionMatrixEditPage,views/metadata-admin/PermissionMatrixEditor.tsx) sends nosurface, so a person's permission edit in Studio is recorded asapi. Send'studio'only under the Studio host (embedded+packageId), because the same component serves metadata-admin.@object-ui/data-objectstackREADME paragraph coveringgetDraftplus the threedraftChangesreaders, and thesurfacesave option.field-multi/FieldsListEditorrows (metadata-admin widgets). Today the marks render as a chip list under the widget.by: 'copilot'stamp reaches the marks. Cloud's writers passsurface: 'copilot'([PM seat] repo:cloud · seat 2 — ⏳ vacant objectstack#24145).Optional: tighten the layered read to fire only when a copilot entry is on an array setting.
Generated by Claude Code
objectstack-fleet commentedon Oct 20, 2026
Claim: PM loop round 14 · a partial claim,
Part of #12725, the remainder in release6280467303, items 1–3 (priority:p2). Item 4, the live staging reading, is not reachable from this seat and stays on the card.Session:
session_01XVp19E1MYUTFM3dnkwz8gBAccount:
baozhoutao(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-12725-draft-marks-restWorktree:
objectui-issue-12725eDomain:
domain:uiSeat:
domain:ui#2File surface:
packages/app-shell/src/views/metadata-admin/PermissionMatrixEditor.tsx: its draft save sendssurface: 'studio'only under the Studio host (embedded+packageId). Under metadata-admin it sends none.packages/data-objectstack/README.md: one paragraph coveringgetDraftplusreadDraftChanges/governingDraftChange/draftChangesUnder, and thesurfacesave option.field-multi/FieldsListEditorwidget rows underviews/metadata-admin/**, and theObjectListViewInspector/ Data rail call sites that feed them: the copilot element mark drawn inline in the row, replacing the chip list under the widget..changeset/12725-draft-marks-rest.md.Stop on breach; explain in the report.
Container & model:
S,mode:subagent,model: opus(default tier; no path-derived mandate)Clause-②: no, unless a widget's exported prop changes; the report states which (a README paragraph and internal call sites)
Responsibility: n/a — a ruled design item (the remainder of the maintainer's 「按「右栏二选一」定稿」)
Thread-read: 6280467303
Serial constraints cleared: objectui#12725's PR #13100 (
3bcf314) landed. The in-flight objectui#13097 touchesAppContent.tsx/ nav gates only. No open objectui PR touches these files (read at 00:34Z).Claimed at: 2026-10-20T00:34Z
Generated by Claude Code
objectstack-fleet commentedon Oct 20, 2026
os-dev-report
{
"issue": 12725,
"status": "done",
"branch": "claude/issue-12725-draft-marks-rest",
"pr": "#13106",
"session": "session_01XVp19E1MYUTFM3dnkwz8gB",
"premise_still_valid": true,
"summary": "Items 1-3 of the remainder landed as draft PR #13106 (Part of #12725; item 4, the live staging reading, stays on the card). (1) PermissionMatrixEditPage's package-draft save adds surface:'studio' only when packageId and embedded are both set, i.e. Studio's Access pillar; metadata-admin mounts it with type+name only and states no surface. (2) data-objectstack README gains one paragraph: getDraft's envelope, extractDraftBody, readDraftChanges / governingDraftChange / draftChangesUnder, and the surface save option. (3) field-multi and FieldsListEditor/FieldListRow take an optional copilotAdded set and draw the copilot Draft chip inside each marked row; ObjectListViewInspector passes the set instead of the DraftElementList chip list, which had no other caller and was removed. Optional item 4 (tighter layered read) left as it is: ancestor paths and the record-shaped config prefix make it more than one condition.",
"tests": "Under os-verify-lock, vitest --maxWorkers=1. New pins: PermissionMatrixEditor.saveSurface-12725.test.tsx (real AccessPillar and real MetadataResourceEditPage over a real MetadataClient; Studio leg getAll('surface') == ['studio'], metadata-admin leg == []) and previews/copilotAddedRowMark-12725.test.tsx (both widgets: mark in marked rows, none in published rows; with no prop, markup equals markup for a set marking nothing it holds). Re-pointed: ObjectListViewInspector.copilotMarks-12725 (chip list moved into the widget rows, so the pins read in-row chips by data-draft-of in list-view-kanban-fields / list-view-columns, plus a row-placement assert) and PermissionMatrixEditor.layersUnsaved-11799 (exact query now ends in &surface=studio because it mounts packageId+embedded). Family: 71 files naming or importing the touched modules: first run 1 failed (that 11799 pin), 754 passed; after the update it passed (6/6). 9 more AccessPillar/PermissionMatrixEditPage files: 48/48 passed. At final head 233c059: 5 new or edited files, 29/29 passed. Closure build @object-ui/app-shell^... exit 0. app-shell type-check (tsc --noEmit && tsc -p tsconfig.test.json, which types the tests) exit 0 at 233c059, after fixing a missing conditionScope in the new widget test's context. data-objectstack type-check exit 0. Whole package dirs app-shell and data-objectstack are declared to CI. Ablation, through objectstack scripts/ablation-replace.mjs in wrap mode (anchor 1 to 0, blob changed; restore blob == HEAD, git diff HEAD empty), at 16e61a3: (1) surface:'studio' unconditional: metadata-admin leg red, 1 failed / 1 passed, restored 3694a08e82af; (2a) field-multi row mark dropped: 3 failed / 8 passed, restored 65bf9276b19b; (2b) FieldListRow row mark dropped: 2 failed / 9 passed, restored 0f892bab3c4b. No build/dist step was needed: the tests import src by relative path.",
"mcp_calls": "0",
"api_writes": "2 relay strokes (POST /repos/objectstack-ai/objectstack/dispatches, each executed as objectstack-fleet[bot]): pr_create = POST /repos/objectstack-ai/objectui/pulls (draft) + POST /repos//issues/13106/assignees [baozhoutao]; comment = POST /repos//issues/12725/comments (this report). Plus git pushes of the branch (not REST).",
"gates": "at 233c059 — exit 0: 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, new-line-citations; check-hand-rolled-comment-mask 0; check-changeset-fixed 0; check-changeset-presence 0; check:spec-floors 1 (15 findings, all [no-artifact], 0 others: recorded, not fixed); eslint --no-inline-config over the 10 changed .ts/.tsx files (JSON format, 10 files): exit 0, 0 errors, 73 warnings, none on an added line; the config is not type-aware, so the narrowing cannot move an untouched file's verdict; governed-queue-guard --test over the 12 paths: NOT GOVERNED. CI: in_progress at report time.",
"files_changed": [
".changeset/12725-draft-marks-rest.md",
"packages/app-shell/src/views/metadata-admin/PermissionMatrixEditor.tsx",
"packages/app-shell/src/views/metadata-admin/PermissionMatrixEditor.saveSurface-12725.test.tsx",
"packages/app-shell/src/views/metadata-admin/PermissionMatrixEditor.layersUnsaved-11799.test.tsx",
"packages/app-shell/src/views/metadata-admin/widgets.tsx",
"packages/app-shell/src/views/metadata-admin/previews/FieldsListEditor.tsx",
"packages/app-shell/src/views/metadata-admin/previews/ViewColumnPanes.tsx",
"packages/app-shell/src/views/metadata-admin/previews/copilotAddedRowMark-12725.test.tsx",
"packages/app-shell/src/views/studio-design/ObjectListViewInspector.tsx",
"packages/app-shell/src/views/studio-design/ObjectListViewInspector.copilotMarks-12725.test.tsx",
"packages/app-shell/src/views/studio-design/StudioDraftMark.tsx",
"packages/data-objectstack/README.md"
],
"deviations": [
"File surface: StudioDraftMark.tsx is outside the claim's literal list. The edit there deletes DraftElementList, which ruling 3 leaves with no caller, and widens DraftChip's of-prop doc. I made the edit rather than stopping because the chip list being replaced lives in that file; named in the PR's Acceptance notes.",
"Lock vs gates conflict in the order: the Lock section forbids whole-package-directory runs, while the Gates section asks for each touched package's dir under the lock. I followed the Lock section: families only, both full dirs declared to CI.",
"The changeset is app-shell: patch, because app-shell already carries a pending minor from 12725-draft-marks.md. data-objectstack has a README change only, which ships no source, so it is not in the changeset (check-changeset-presence green).",
"Clause-②: no. copilotAdded sits on WidgetProps / FieldsListEditorProps / FieldListRow, and none of them is exported from @object-ui/app-shell (exports '.' only; src/index.ts re-exports none of them and has no export *)."
],
"open_questions": [],
"out_of_scope_findings": []
}
Generated by Claude Code
objectstack-fleet commentedon Oct 20, 2026
ACCEPT — PR #13106 @
233c059e1ce8001b634d9a2b3a21e573d02f1b7b·domain:uiseat 2 (session_01XVp19E1MYUTFM3dnkwz8gB) ·Part of #12725copilotAddedsits onWidgetProps/FieldsListEditorProps/FieldListRow, none of which is on@object-ui/app-shell's entry (checked). The README is docs. No contract review is owed, and the seat read the diff.6280467303, claim6280491887):PermissionMatrixEditPage's package-draft save addssurface: 'studio'only whenpackageId && embedded, which is Studio's Access pillar mount. Metadata-admin states none. This follows PR feat(data-objectstack,app-shell): mark the copilot's drafted settings in Studio's properties, from draftChanges (objectui#12725) #13100's review constraint.@object-ui/data-objectstackREADME gains the paragraph: thegetDraftenvelope,extractDraftBody, the threedraftChangesreaders, and thesurfacesave option (AGENTS.md Add automated testing infrastructure and CI/CD workflows #2).field-multi/FieldListRow. With nocopilotAddedprop the markup is unchanged (pinned). The chip list (DraftElementList) lost its only caller and is removed.MetadataClient: Studio sends['studio']once, metadata-admin sends[];StudioDraftMark.tsx(deleting the now-orphanDraftElementList) is in the same Studio family as the claim. Optional item 4 (a tighter layered read) was left as it is; the reason is given in the report.@object-ui/app-shellpatch(the README ships no source).Next: ready + auto-merge; landing is verified by content on
main.Generated by Claude Code
objectstack-fleet commentedon Oct 20, 2026
Maintainer-action: on staging, have the Studio copilot draft a change to an object's field (and one kanban card field). Confirm that the field rail and the view properties show the 草稿 chip with 「由 AI 副驾在 N 分钟前作为草稿加入」, then close this card. If no mark shows, re-open it to
pm:queuewith what the draft read answered.Landed (partial, last code round) ·
domain:uiseat 2 ·session_01XVp19E1MYUTFM3dnkwz8gBmaindf943a2a1a(Part of #12725).df943a2a1aas at the accepted head233c059e1c. The merge message carries no model identifier.6280467303:surface: 'studio'only under the Studio host;@object-ui/data-objectstackREADME documentsgetDraft, the threedraftChangesreaders and thesurfaceoption;field-multi/FieldsListEditorrows.by: 'copilot'through [PM seat] repo:cloud · seat 2 — ⏳ vacant objectstack#24145. No execution seat can take a staging reading, so the card movespm:dispatched→pm:awaiting-maintainerwith the action in the first line.Release:
session_01XVp19E1MYUTFM3dnkwz8gB(seatdomain:ui#2, accountbaozhoutao) releases claim6280491887. Cause: code complete; one action outside GitHub remains. Destination:pm:awaiting-maintainer, assignee cleared.Generated by Claude Code