Repository navigation
The draft status chip sits in the same place in every pillar's item header #12524
Description
Source
Measured on objectui main d4bc81eab, 2026-10-15, in a writable package. Data pillar: "Unpublished draft" is the chip right after the api-name chip, beside the title. Automations: the same, beside the title. Interfaces: "Unpublished draft" sits at the far right of the item header, next to the Design / Run toggle. Access: "This package's objects · saved as draft" sits beside the title. Four headers, two positions.
Target
- The status chip is the item header's second chip in every pillar: after the api-name or kind chip, before the meta slot.
- The right slot holds actions only (Design / Run, the Enabled switch, Explain access, the saved-at meta).
StudioItemHeadertakes the chip throughchips, so the pillars stop placing it themselves.
File surface
packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx (ifHeaderEl and the Access header), StudioItemHeader.tsx, tests beside.
Acceptance
- A test renders the four headers with a draft item and asserts the chip's position (the same parent, the same index) in all four.
- Screenshots of the four headers at 1280 px, light and dark.
pnpm --filter @object-ui/app-shell testgreen.
Constraints
- Coordinate with Interfaces pillar: the rail is the app's navigation tree (groups, metadata icons, kind tags), "+" offers the ten nav kinds, and the canvas follows the kind: page regions designer, object runtime list preview, dashboard canvas #12455 (Interfaces header) and Access pillar: the "Permission matrix" band retires into a permission-set header; Objects / System capabilities / Row-level security / Tab visibility / Delegated admin become tabs; the object matrix gets OWD chips, 18 px checkboxes and hover-only bulk actions #12456 (Access header): whichever lands first, the other rebases.
- Existing
data-testids stay.
Filed by the epic PM session session_cdb7a717-def7-4e71-9988-f089fbaa18ff from the 2026-10-15 Studio test pass: objectui main d4bc81eab, HotCRM 63db83e2, the vite console on :5180 against objectstack serve --dev on :4001, viewport 1482 × 1119, dark theme with a light-theme check.
Evidence
Screenshots from the test pass, kept on the temporary branch claude/studio-test-pass-evidence-20261015 of objectstack-ai/objectui (commit 72115588c, never merged: the maintainer ruled that screenshots do not enter main, and asked for a temporary branch referenced from the card). The branch's docs/design/studio-redesign/findings/2026-10-15/README.md maps every file to its card. Pane captures, 800 × 604 of a 1482 × 1119 viewport, dark unless named light.

docs/design/studio-redesign/findings/2026-10-15/12524-automations-header-chip-left.jpg — automations header chip left.

docs/design/studio-redesign/findings/2026-10-15/12524-data-header-chip-left.jpg — data header chip left.

docs/design/studio-redesign/findings/2026-10-15/12524-interfaces-header-chip-right.jpg — interfaces header chip right.
Parent: #12446
Body refreshed 2026-10-15T15:19Z by the epic PM session: an Evidence section with the test-pass screenshots added.
Activity
objectstack-fleet commentedon Oct 15, 2026
Claim: PM loop round 1 (domain:ui lane queue; an objectui#12446 test-pass card)
Session:
session_01SJn1GEkrbbBQkhGC26wEDwAccount:
os-sales(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-12524-header-status-chipWorktree:
objectui-issue-12524(in the cloud session's own container)Domain:
domain:uiSeat:
domain:ui#4File surface:
packages/app-shell/src/views/studio-design/StudioItemHeader.tsx(a status slot and the shared chip),packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx(only thechips/actions/ status props of the four item headers:ifHeaderEl, theDataPillarandAutomationsPillarheaders,accessHeaderEl),metadata-admin/i18n.ts(new keys only, if any), new tests beside them, one.changeset/12524-*.md(stop on breach; explain in the report)Container & model:
S,mode:cloud,model: opus(cloud on the maintainer's stance 「可以的话,ui 后续开发尽量用云 session,并且要求截图给我看」; lane scope on 「你也可以接ui车道的卡」; batch 5 on 「上限提到 5,提速」, this session's chat, 2026-10-15)Clause-②: no
Responsibility:
n/a — not a defect cardThread-read: none
Serial constraints cleared: objectui#12522 (the Interfaces header's Design / Run switch,
ifHeaderEl) landed as PR objectui#12582 (830eb1f), and objectui#12514 (the Automations and Access empty branches) landed as PR objectui#12581 (f23eb01). The card's coordination with objectui#12455 and objectui#12456 is moot: both landed. Still in flight onStudioDesignSurface.tsx, this seat: objectui#12516 (theDataPillaradd-field handler, not its header). Any textual overlap is resolved by mergingorigin/main.objectstack-fleet commentedon Oct 16, 2026
os-dev-report
{
"issue": 12524,
"status": "done",
"branch": "claude/issue-12524-header-status-chip",
"pr": null,
"session": "session_01GzRy6FiHuy1vpBecH2KRYM — this run's harness-stamped id (Claude-Session trailer on every commit)",
"premise_still_valid": true,
"summary": "Measured on f23eb01: the PM's assumption 1 holds. Data and Automations pass the amber 'Unpublished draft' span through
chips, and Interfaces (ifHeaderEl) put it first inactions; that header was the only one with the chip in the wrong slot. I chose shape (a): StudioItemHeader takes a typedstatusprop ({label, title}) and draws the one shared amber chip (StudioStatusChip) after the identity and before the meta. The api-name and kind chips sit in an always-renderedcontentswrapper (data-slot identity-chips), so the chip is child index 3 of the title row in all four pillars, whatever identity chips a pillar shows, and the layout does not change. Interfaces' right slot now holds actions only; Access's writable 'saved as draft' note goes throughstatus, and its read-only link stays inactions. Branch head d9376cc, base f23eb01. origin/main has since moved 3 commits (1b3b5b1, daeb77f, 3e09c1e); none touches the header props or StudioItemHeader, and the merge is clean, so I did not merge.","tests": "At HEAD d9376cc. New pin StudioItemHeader.statusPlace-12524.test.tsx: 4 passed (the four real pillars against one server double; with a draft, every chip is a child of the title row at one shared index, after the h1 and the identity-chips wrapper, outside the actions; the Interfaces actions keep their order (canvas-mode-toggle, then if-properties-toggle) with no chip among them; CONTROL with no draft: Data, Automations and Interfaces draw no chip; CONTROL with a read-only package: Access draws no status and its read-only link sits in the ml-auto actions container). Full app-shell suite (the file set of
pnpm --filter @object-ui/app-shell test), run aspnpm exec vitest run packages/app-shell/ --shard=K/8for K = 1 to 8, each under os-verify-lock, all VERDICT command-exit 0: 1420 files passed and 1 skipped (1421); 13538 tests passed and 9 skipped. The unsharded run could not finish inside the 10-minute foreground cap: one attempt was lost when the container restarted, and --shard=1/4 timed out at 590 s with no result, so both read as NOT MEASURED. Shard 1/8 on 81ec63c found 2 real failures in the existing pin StudioDesignSurface.flowCanvasChip-11665: it reads the title's parentElement as the header row, and my first wrapper enclosed the h1. d9376cc fixes this by wrapping only the identity chips, which leaves the h1 a direct child of the row; that pin is green, untouched. Ablation (source path; the test imports ./StudioDesignSurface relatively, so there is no dist step): I moved the Interfaces chip back intoactions(mutation landed: grep count 1 for the injected chip, 0 for the removed status line), and the pin went red, 2 failed and 2 passed ('Interfaces: expected … to be …' and 'expected span to be null'); aftergit checkout HEAD -- StudioDesignSurface.tsx,git diff HEADis 0 bytes.","gates": "Local, at d9376cc, exit codes captured before any pipe:
pnpm --filter @object-ui/app-shell type-checkexit 0 (tsc --noEmit and tsc -p tsconfig.test.json; dependency closure built first withturbo run build --filter=@object-ui/app-shell^... --concurrency=2, 28/28 tasks, VERDICT 0);pnpm --filter @object-ui/app-shell buildVERDICT 0; eslint on the 3 touched .tsx files exit 0 (0 errors; the 14 warnings are existing react-hooks warnings, none on edited lines);pnpm check:i18n-keysexit 0;pnpm check:i18n-driftexit 0 (0 keys added);node scripts/check-changeset-presence.mjsexit 0;pnpm check:new-line-citationsexit 0 (0 new). Not applicable: check:i18n-designer-parity (no keys added), check:i18n-dead-keys (none retired); no new Intl.* call. objectui has no scripts/pm/dispatch-gates.mjs, so there is no --ran reconciliation; the list above is derived by hand from the dispatch. CI: no PR yet, so no check-runs to cite.","mcp_calls": "5 — claude-code-remote add_repo ×2 (objectstack, hotcrm, read; answered read_available, nothing attached); github issue_read ×3 (get #12524, get_comments #12524, get_comments #12446). No write tool.",
"api_writes": "0 REST writes to GitHub. git push only: claude/issue-12524-header-status-chip (empty branch, then 81ec63c, then d9376cc) and claude/issue-12524-evidence (57d29e5, then 33ef46f). Local backend only (not GitHub): POST /api/v1/packages plus 4 draft PUTs on my own --fresh HotCRM instance on :4123, since stopped.",
"files_changed": [
"packages/app-shell/src/views/studio-design/StudioItemHeader.tsx",
"packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx (only the status, chips and actions props of ifHeaderEl, the DataPillar and AutomationsPillar headers, accessHeaderEl, and the accessNoteEl they feed)",
"packages/app-shell/src/views/studio-design/StudioItemHeader.statusPlace-12524.test.tsx (new)",
".changeset/12524-header-status-chip.md (new)"
],
"screenshots": [
"https://github.com/objectstack-ai/objectui/blob/33ef46f53/docs/design/studio-redesign/findings/12524/12524-data-header-light.png?raw=true",
"https://github.com/objectstack-ai/objectui/blob/33ef46f53/docs/design/studio-redesign/findings/12524/12524-data-header-dark.png?raw=true",
"https://github.com/objectstack-ai/objectui/blob/33ef46f53/docs/design/studio-redesign/findings/12524/12524-automations-header-light.png?raw=true",
"https://github.com/objectstack-ai/objectui/blob/33ef46f53/docs/design/studio-redesign/findings/12524/12524-automations-header-dark.png?raw=true",
"https://github.com/objectstack-ai/objectui/blob/33ef46f53/docs/design/studio-redesign/findings/12524/12524-interfaces-header-light.png?raw=true",
"https://github.com/objectstack-ai/objectui/blob/33ef46f53/docs/design/studio-redesign/findings/12524/12524-interfaces-header-dark.png?raw=true",
"https://github.com/objectstack-ai/objectui/blob/33ef46f53/docs/design/studio-redesign/findings/12524/12524-access-header-light.png?raw=true",
"https://github.com/objectstack-ai/objectui/blob/33ef46f53/docs/design/studio-redesign/findings/12524/12524-access-header-dark.png?raw=true",
"https://github.com/objectstack-ai/objectui/blob/33ef46f53/docs/design/studio-redesign/findings/12524/12524-interfaces-full-light.png?raw=true",
"https://github.com/objectstack-ai/objectui/blob/33ef46f53/docs/design/studio-redesign/findings/12524/12524-interfaces-full-dark.png?raw=true"
],
"deviations": [
"Target 3 says the header takes the chip 'through
chips'. I used a dedicated typedstatusprop (shape a): the header owns both where the chip sits and how it looks, and a pillar cannot pass markup or a link into it. That is the card's intent ('the pillars stop placing it themselves') with a stricter contract than its wording.","The status chip gained
shrink-0 whitespace-nowrap(shared StudioStatusChip). Measured at 1280 px in the Automations header: with main's classes the chip wrapped to two lines (37 px tall) and the title still truncated (117 of 163 px); with the new classes the chip stays on one line (21 px) and the title takes 110 px. The title truncation exists either way; it comes from that pane's width.","Access open-set header (PermissionMatrixEditor.tsx, outside the claimed file surface) is unchanged: there the note still sits at the tab row's right end, as objectui#12456 placed it to stop the title row truncating the set's label. Its markup now comes from the shared StudioStatusChip, through accessNoteEl.",
"The full app-shell suite was run as 8 shards over the identical file set, not as one
pnpm --filter @object-ui/app-shell testinvocation, because of the 10-minute foreground cap."],
"line_budget": "n/a",
"open_questions": [
{
"question": "Status slot shape (PM assumption 2): a dedicated slot (a), or keep it in
chips(b)?","options": [
"A: typed
statusprop {label, title}, drawn by the header after chips and before meta (built)","B: keep it in
chipswith one shared chip component"],
"recommendation": "A, built. Business need: the measured defect was a pillar putting the chip in the wrong slot, and A makes that impossible. Long-term: the header owns position and look (contract-first). AI-error axis: the typed prop refuses JSX or a link, so a mistake is caught at compile time rather than silently tolerated. Startup axis: one optional prop and one component, no new capability."
},
{
"question": "Is Access's 'This package's objects · saved as draft' note target 1's status chip (PM assumption 3)?",
"options": [
"A: yes in the writable posture: it goes through
status; read-only shows StudioReadOnlyLink, an action, inactions(built)","B: no: it stays a package note in
chips, and Access has no status chip"],
"recommendation": "A, built. Measured: writable shows the amber note (title = engine.studio.access.bannerTitle); read-only shows the link (title = engine.studio.access.bannerTitleReadOnly), unchanged in place. It is the only draft signal the Access pillar shows, the card's Source counts it as Access's chip, and the right slot stays actions only. Caveat: the note is package-level, shown whether or not a draft exists; Access has no per-set draft status (see out_of_scope_findings)."
},
{
"question": "Contract conflict: os-dev.md says to post the report on the card and open the draft PR; this dispatch's degraded channel says not to, because the fleet-write relay answers 403 from this container.",
"options": [
"A: follow the degraded channel: pr null, the PR body in pr_body_draft, the PM posts and opens (done)",
"B: attempt the relay writes"
],
"recommendation": "A. os-dev.md's write path goes through fleet-write, which the dispatch measured as refused here, so the file's step is unexecutable rather than overridden. Two smaller notes: the commit trailers follow objectui AGENTS.md's model-free pair (Co-authored-by: Claude), which takes precedence over the harness reminder; and AGENTS.md names the lock at /home/user/objectstack/scripts/pm/os-verify-lock.sh, while this container's read-only clone has it under /home/user/objectstack-ai/objectstack, which is the path I used."
}
],
"out_of_scope_findings": [
"observation (not filed; none of classes a/b/c): permission sets show no item-level draft status. The Access open-set header (PermissionMatrixEditor renderStudio) has no hasDraft state, and its 'saved as draft' note is a constant package note shown with or without a pending draft · reach: read-only inference from the source plus the evidence package · carrier: none · Acceptance notes only · dedupe words: permission set draft status, Unpublished draft Access, matrix draft chip"
],
"pr_title_draft": "feat(app-shell): the draft status chip sits in one place in every Studio item header (objectui#12524)",
"pr_body_draft": "(elided by the seat: opened verbatim as the body of PR #12589, read back identical)"
}
Relayed verbatim by seat
domain:ui#4(sessionsession_01SJn1GEkrbbBQkhGC26wEDw) from the cloud session's final message, the degraded write channel. The seat opened the draft PR #12589 frompr_title_draftandpr_body_draft, with assigneeos-sales."pr": nullabove is the session's own value at report time.Generated by Claude Code
objectstack-fleet commentedon Oct 16, 2026
Landed · PR #12589 merged as
ae923econmain· read 2026-10-16T01:19Zorigin/mainae923ecexport function StudioStatusChipinStudioItemHeader.tsxstatus={inStudioDesignSurface.tsxStudioItemHeader.statusPlace-12524.test.tsx,.changeset/12524-header-status-chip.mdState change: the card closed as completed through
Fixes #12524, andpm:dispatchedis removed in this act. The cloud sessionsession_01GzRy6FiHuy1vpBecH2KRYMis archived. #12590 (objectui#12513), which was queued behind this PR, now mergesmainand resolves the shared import line in its follow-up round.Generated by Claude Code