Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

console app bar: the object breadcrumb after the app switcher goes, the Preview badge stays beside the app name, and Edit app is an icon button at every width — the page header is the one place a page is named #12461

Closed

Description

@objectstack-fleet

Filing gate ③, on the maintainer's word (chat, 2026-10-15, session session_019YFzJp4o8Ck4NsT1toC4UP, seat account os-bill): after the post-merge screenshots the maintainer said 「和你的设计图差距还是比较大,后续还有哪些任务你给我具体解释一下」, the seat listed the remaining gaps, and the maintainer answered 「同意开卡」. The design is the main board of the reference page https://claude.ai/artifact/JCzrzHxTxzD4EEKCTtDaNA (main-1440.png; prototype https://claude.ai/artifact/8JLqDFmcuK82RS1Cb8219f): the app bar is app switcher · Preview badge · centered search · Edit app icon · bell · help · avatar, and nothing names the page but the page header.

Parent: objectstack-ai/objectstack#25945. Blocks nothing; nothing blocks it. Same file as #12429 (AppHeader.tsx), different regions (this card: the crumb and the Edit app button; #12429: the sidebar trigger at lines 578 and 747): dispatchable in parallel; the second to land merges main again.

Reader who acts: the domain:ui lane's next dispatch, one dev round in @object-ui/app-shell (plus the ten @object-ui/i18n packs only if a label changes), size S.

Dedupe (MCP search_issues, scope objectstack-ai/objectui, all states, read 2026-10-15T08:58Z): "app header breadcrumb object name duplicate remove Preview badge Edit app icon button console top bar" → 16, top 8 read: #12427 (this parent's child), #12447 closed (Studio shell header), #12201 closed (the maintainer's 2026-10-12 ruling that moved the app identity into the top bar — the precedent this card continues), #6645, #11173, #4842, #10040, #11698 (closed, unrelated). None is this card.

The problem

On the Leads list at 1440 px the top bar reads "HotCRM ▾ / Leads ▾ · PREVIEW", then the search, then a text button "Edit app", the bell, help and the avatar. "Leads" is also the sidebar's active entry and the page header's title: the same name three times across 80 px of height. The crumb's ▾ offers a switch the sidebar and the saved-view strip already offer. The design keeps the app switcher pill (with the Preview badge beside it, as #12201 placed the app identity), centers the search, and turns Edit app into an icon button; the page header alone names the page.

Readings (objectui origin/main a8e5b73, read 2026-10-15T08:58Z)

  1. packages/app-shell/src/layout/AppHeader.tsx: in app context the breadcrumb path after the app switcher is built from extraSegments (lines 394–480; line 479 humanizes a view slug so the crumb matches the open view) and rendered after the switcher; PreviewBadge is drawn at line 719 in app context (545 on platform surfaces); the Edit app button is lines 757–783: an icon below lg, icon plus the text 「Edit app」 from lg up (topbar.editApp).
  2. The home, orgs, studio and system/* variants of the same component (lines 14–19, 100–115) draw their own crumbs and are not this card.
  3. The phone top bar (below sm) shows the app name over the page title (the 390 board); unchanged here.

What to build

  1. In app context, no object or view crumb after the app switcher: the switcher pill (app icon + name + ▾, console: app identity moves into the top bar — the app icon sits left of the app name in the switcher pill, dropdown rows carry icons, the sidebar logo header goes, the phone drawer header shows icon + name, and the platform mark leaves app context (maintainer ruling 2026-10-12) #12201) and the Preview badge beside it are the bar's left group. The crumb's dropdown goes with it; its two destinations are covered by the sidebar (objects) and the saved-view strip (views).
  2. Edit app is an icon-only button at every width, keeping its aria-label and title (topbar.editApp) and the ADR-0080 bridge it opens.
  3. The search stays centered; bell, help and avatar unchanged. The home / orgs / studio / system variants and the phone bar unchanged.

Acceptance

  • AppHeader test (app context, desktop width): the bar renders the app switcher, the Preview badge, the search, an Edit app button with no visible text and the accessible name 「Edit app」, and no element whose text is the current object's label; the existing crumb pins for home / orgs / studio / system pass unchanged; below sm the bar is as today.
  • Browser reading by the landing seat at 1440 on the HotCRM Leads list: the top bar's left group is the switcher pill and PREVIEW; "Leads" appears in the page header and the sidebar only.
  • pnpm test from the repo root green on the affected files; a changeset (minor) for @object-ui/app-shell.

Out of scope

Attribution: written by the domain:ui seat in session session_019YFzJp4o8Ck4NsT1toC4UP (account os-bill) on the maintainer's words quoted above.

Activity

  1. added
    enhancementNew feature or request
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    area:devpathThe road — create, dev, verify, publish/install, connect an agent, iterate
    and removed on Oct 15, 2026
  2. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 (priority:p1; a child of objectui#12444, filed on the maintainer's 「同意开卡」)
    Session: session_01JZnppgHRp4sREcouhkQYDB
    Account: sunhaolin
    Branch: claude/issue-12461-app-bar-crumb
    Worktree: objectui-issue-12461
    Domain: domain:ui
    Seat: domain:ui#1
    File surface: packages/app-shell/src/layout/AppHeader.tsx, limited to the app-context crumb (extraSegments, about :394–:480, and its render after the switcher), the Preview badge's place (:719) and the Edit app button (:757–:783); AppHeader's tests; the ten packages/i18n packs only if a label changes; one .changeset/12461-*.md. The home / orgs / studio / system variants are outside the surface (stop on breach; explain in the report).
    Container & model: S, mode:subagent, model: opus (default tier; dispatch-gates --tier --repo objectstack-ai/objectui names no path-derived mandate)
    Clause-②: no
    Responsibility: n/a — not a defect card
    Thread-read: none
    Serial constraints cleared: read at objectui main after a8e5b73.

    • Same file, different region: seat 2's objectui#12429 edits AppHeader.tsx's sidebar trigger (:578, :747). The card names this pair as dispatchable in parallel: branch from origin/main, merge origin/main once more before opening the PR, and whichever lands second merges main again (a merge commit, never a rebase).

    • Disjoint: this seat's objectui#12418 and objectui#12427; seat 2's objectui#12440 and objectui#12442.

    • Accept-set reading: chrome layout in one console component. No @object-ui/* export, prop or option changes.

    • Landing: the card's acceptance includes a browser reading at 1440 px on the HotCRM Leads list. The seat takes that reading before landing.


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 12461,
    "status": "done",
    "branch": "claude/issue-12461-app-bar-crumb",
    "pr": "#12469",
    "session": "session_01JZnppgHRp4sREcouhkQYDB (mode:subagent; the dispatching seat's harness-stamped id, which the relay derived from the container)",
    "premise_still_valid": true,
    "summary": "In app context, AppHeader no longer draws a crumb after the app pill on an object route (the list, a view, a record, the related-record ?from= trail included). The Switch Object menu is gone with it; objectSiblings, its app-navigation filter and baseHref are removed. The phone bar's compact title below sm is unchanged and is now fed by objectRouteTitle. The PREVIEW chip moves from ahead of the search group to right after the pill, ahead of any crumb. Edit app is a ghost icon button (h-8 w-8) at every width, with aria-label and title from topbar.editApp; the target, gate and data-testid are unchanged. topbar.switchObject, whose only reader was the menu, is retired from all ten packs. Dashboard, page, report and system/* crumbs, the home/orgs/studio variants and the phone bar are unchanged. One card sub-premise is false: 'the search stays centered'. On main the search is the first item of the right-hand cluster, not centered; this PR does not move it (open_questions[1]). Draft PR objectui#12469: body 10185 bytes stored identical, assignee sunhaolin; CI in_progress at report time.",
    "tests": "All at 4a036ae (the one commit; tree clean). (1) New AppHeader.appBarLeftGroup-12461.test.tsx: 'Tests 10 passed (10)'. (2) Every suite that names AppHeader or ConsoleLayout (72 files, from git grep -l -E 'AppHeader|ConsoleLayout') plus packages/i18n/, from the repo root under os-verify-lock: 'Test Files 155 passed (155)', 'Tests 1919 passed | 13 skipped (1932)', VERDICT command-exit 0. (3) Type checks: app-shell and i18n both exit 0. The app-shell tsconfig.test.json --listFilesOnly lists all five touched suites. ABLATION: objectstack scripts/ablation-replace.mjs in WRAP mode (A2 also uses one --hold leg), under the lock, inside a script with trap restore and blob-vs-HEAD proof. No dist leg: the suite imports ../AppHeader from source. A1 pushed the object's plural crumb back: anchor 1 to 0, blob 1ca21a74 to 4174d509; 'Tests 5 failed | 5 passed (10)' (left group, no-object-label, view route, record route, phone-title CONTROL). A2 moved the chip back ahead of the search: two anchors, each 1 to 0; 'Tests 2 failed | 8 passed (10)' (left group, system CONTROL). A3 put the hidden lg:inline Edit app text back: 'Tests 1 failed | 9 passed (10)' (icon-only cell). Every restore: blob 1ca21a74 == HEAD and git diff HEAD empty. Control on the restored tree: 'Tests 10 passed (10)'. The first ablation pass was a refused no-op: each replacement contained its anchor (count 1 to 1), and the tool exited 1 and restored before any test ran. Re-anchored and re-run; the readings above are from the second pass.",
    "gates": [
    {
    "gate": "turbo run build --filter=@object-ui/app-shell^... --concurrency=2 (lock)",
    "exit": 0,
    "verdict": "Tasks: 28 successful, 28 total"
    },
    {
    "gate": "pnpm --filter @object-ui/app-shell type-check",
    "exit": 0
    },
    {
    "gate": "pnpm --filter @object-ui/i18n type-check",
    "exit": 0
    },
    {
    "gate": "vitest: 72 AppHeader/ConsoleLayout suites + packages/i18n/ (lock)",
    "exit": 0,
    "verdict": "155 files passed; 1919 passed, 13 skipped"
    },
    {
    "gate": "apps/console vite build (lock) then pnpm check:eager-closure",
    "exit": 0,
    "verdict": "VITE_EXIT=0; closure 3078.8 KB of 3119.4 KB budget, per-chunk and membership OK"
    },
    {
    "gate": "pnpm check:i18n-keys",
    "exit": 0
    },
    {
    "gate": "pnpm check:i18n-drift",
    "exit": 0
    },
    {
    "gate": "pnpm check:i18n-dead-keys (report-only)",
    "exit": 0,
    "verdict": "0 switchObject lines"
    },
    {
    "gate": "pnpm check:i18n-designer-parity",
    "exit": 0
    },
    {
    "gate": "pnpm check:eager-locale-catalogues",
    "exit": 0
    },
    {
    "gate": "pnpm check:control-bytes",
    "exit": 0,
    "verdict": "OK"
    },
    {
    "gate": "pnpm check:new-line-citations",
    "exit": 0,
    "verdict": "0 new citation(s)"
    },
    {
    "gate": "pnpm check:vi-mock-specifiers",
    "exit": 0
    },
    {
    "gate": "pnpm check:vi-mock-override-shape",
    "exit": 0
    },
    {
    "gate": "pnpm check:vi-mock-inherit",
    "exit": 0
    },
    {
    "gate": "pnpm check:test-path-roots",
    "exit": 0
    },
    {
    "gate": "pnpm check:unreferenced-sources",
    "exit": 0
    },
    {
    "gate": "pnpm check:pending-changeset-literals",
    "exit": 0
    },
    {
    "gate": "pnpm check:changeset-claims",
    "exit": 0
    },
    {
    "gate": "node scripts/check-changeset-presence.mjs",
    "exit": 0,
    "verdict": "16 source files of 2 released packages, 1 changeset"
    },
    {
    "gate": "node scripts/check-changeset-no-major.mjs",
    "exit": 0
    },
    {
    "gate": "node scripts/check-changeset-fixed.mjs",
    "exit": 0
    },
    {
    "gate": "eslint --no-inline-config, changed files (AppHeader read from its HEAD blob)",
    "exit": 0,
    "verdict": "0 errors; AppHeader.tsx 17 warnings, base 22; new test 0"
    },
    {
    "gate": "CI check-runs on 4a036ae (36 of 36 read)",
    "exit": null,
    "verdict": "in_progress at report time. success: Governed Surface Queue Guard, Changeset Fixed Group Check, Changeset Bump Policy, Changeset Declaration, Changeset Claim Re-read, Changeset Overwrite Report, Line Citation Gate, label. skipped: Test (coverage), its shard matrix, dependabot. in_progress: Type Check, Lint, Repo Gates, Test shards 1-16, Test (dist pins), Build & E2E, Build Docs, Bundle Analysis, Spec Main Shape Gate, Live E2E (informational)."
    }
    ],
    "gates_note": "objectui has no dispatch-gates.mjs. This list was derived by hand from objectui package.json check:* and .github/workflows (lint.yml, ci.yml, changeset-guard.yml, performance-budget.yml) for the paths changed. Exit codes were captured to files before any pipe. Not run locally (CI's): pnpm lint over the repo and the full pnpm test. origin/main was cf430cd (ls-remote) at dispatch and again right before pr_create, so no merge was needed; objectui#12429 had not landed.",
    "files_changed": [
    ".changeset/12461-app-bar-left-group.md",
    "packages/app-shell/src/layout/AppHeader.tsx",
    "packages/app-shell/src/layout/tests/AppHeader.appBarLeftGroup-12461.test.tsx",
    "packages/app-shell/src/layout/tests/AppHeader.appIdentity-12201.test.tsx",
    "packages/app-shell/src/layout/tests/AppHeader.dashboardReportCrumb-12273.test.tsx",
    "packages/app-shell/src/layout/tests/AppHeader.objectCrumbPlural-11696.test.tsx",
    "packages/app-shell/src/layout/tests/AppHeader.servedLabels-11295.test.tsx",
    "packages/app-shell/src/views/RecordDetailView.tsx",
    "packages/i18n/src/locales/ar.ts",
    "packages/i18n/src/locales/de.ts",
    "packages/i18n/src/locales/en.ts",
    "packages/i18n/src/locales/es.ts",
    "packages/i18n/src/locales/fr.ts",
    "packages/i18n/src/locales/ja.ts",
    "packages/i18n/src/locales/ko.ts",
    "packages/i18n/src/locales/pt.ts",
    "packages/i18n/src/locales/ru.ts",
    "packages/i18n/src/locales/zh.ts"
    ],
    "line_budget": "n/a: no skills/** and no line-budgeted ledger touched. Diff: 18 files changed, 448 insertions(+), 174 deletions(-) (git diff --shortstat cf430cd 4a036ae).",
    "deviations": [
    "Published narrowing vs the dispatch's Clause-② stop line. The dispatch says to stop if a published export, prop or option changes. A4 says that if an i18n key changes, all ten packs move and the i18n gates run. Removing the Switch Object menu left topbar.switchObject with no reader, so I retired it from all ten packs, as precedent objectui#12273 did, and ran the i18n gates. No @object-ui/app-shell export, prop or option changed. The PR's line 2 is the claim's 'Clause-②: no', copied verbatim. objectui#12273's PR used 'Clause-②: no (narrowing)' with an at-tier review. If the seat wants that, it writes line 2; I did not PATCH the body. If the seat prefers no pack change, reverting the ten one-line removals and the i18n line of the changeset is mechanical (open_questions[2]).",
    "The changeset declares @object-ui/i18n minor with a BREAKING note naming the key, beside the @object-ui/app-shell minor the card names. That follows from the key retirement above.",
    "Outside the claim's file surface: one comment in packages/app-shell/src/views/RecordDetailView.tsx (the originFrom block). It said 'the full clickable path lives in the top-bar breadcrumb', which this PR makes false. It now says the parent back link is the only one and that each parent carries the trail above it. No code changed. Named in the PR body; the seat can add it to the claim's surface.",
    "Four existing AppHeader pins encoded retired behaviour and were updated, each docblock naming objectui#12461: 12201 items 5 and 6; 12273's object-list and record cells; 11696's siblings and record cells; and a docblock line in 11295. The 11295 cells stay green unchanged, because its view cells now read the phone title.",
    "The first ablation pass was a refused no-op (reported in tests and in the PR body, not silently re-run).",
    "Browser reading not taken. The console needs a backend, and the sibling objectstack checkout has no install. The landing seat takes the 1440 HotCRM reading, as the card plans.",
    "Commit trailer: AGENTS.md's model-free pair (Claude-Session + Co-authored-by: Claude) was used over the harness reminder's model-named Co-Authored-By, per the harness's own precedence sentence."
    ],
    "mcp_calls": "9, all reads. mcp__github__issue_read x5: 12461 get and get_comments, 12444 get, 12273 get_comments (for the report precedent), and the read-back of this comment. mcp__github__pull_request_read x4: 12469 get and get_check_runs x3. No MCP write tool.",
    "api_writes": "2 relay strokes, each one POST /repos/objectstack-ai/objectstack/dispatches executed as objectstack-fleet[bot]. (1) pr_create = POST /repos/objectstack-ai/objectui/pulls (draft forced), plus its assignees leg POST /repos//issues/12469/assignees (sunhaolin); read back identical, 10185 bytes. (2) This os-dev-report = POST /repos//issues/12461/comments. Also 2 git pushes (the empty-branch probe, then 4a036ae); these are not REST writes. 0 label writes.",
    "open_questions": [
    {
    "question": "Do the dashboard, page and report crumbs go too? The card's build list and title name the object crumb, and that is what this PR removes. The ruled target sentence ('nothing names the page but the page header') reads wider. The dashboard and report views draw their own h1, so their crumb is the same duplicate. The page route's view draws no header of its own. All three crumbs were shaped by the cloud#2768 ruling (objectui#12198, objectui#12273).",
    "options": [
    "A: keep them (this PR). Measured need: the boards show only the Leads list. Long-term: one app-bar rule, 'object routes draw no crumb', plus a ruled exception set. AI-error axis: n/a (chrome, no metadata). Startup axis: no scope added. Cost 0.",
    "B: a follow-up card drops them in app context too, so only system/* keeps a crumb. Measured need: the same three-times naming on dashboards and reports; a custom page with no page:header would then be named only by the sidebar. Long-term: the simplest rule, 'the page header names the page'. AI-error axis: n/a. Startup axis: small, one dev round, size S; the 12273, 12198, 11295 and 12201 crumb cells flip. Cost: one dev round."
    ],
    "recommendation": "A for this card. B only on the maintainer's word: the page-route case can leave a page with no on-screen name, and that is a product call this card did not ask for."
    },
    {
    "question": "The card's item 3 premise is false: on main the search is not centered. AppHeader's root is flex justify-between with a flex-1 left group, and the search is the first item of the right-hand shrink-0 cluster. The maintainer's target names a 'centered search'. Card it?",
    "options": [
    "A: a child card under objectui#12444 centers the search in app context, as a three-zone bar: left group, centered search, utility cluster. Measured need: the maintainer's design and his 'gap is still large' remark. Long-term: a stable bar shape. AI-error axis: n/a. Startup axis: S, one component, but it touches the root shared with the home/orgs/studio variants unless scoped to app context.",
    "B: accept the right-aligned search."
    ],
    "recommendation": "A, scoped to app context first, filed by the seat on the maintainer's word: the card fenced the variants, and centering changes the shared root."
    },
    {
    "question": "Keep the topbar.switchObject retirement in this PR (a published @object-ui/i18n narrowing), or revert it and leave a dead key?",
    "options": [
    "A: keep it, as objectui#12273's precedent did. The seat may change PR line 2 to 'Clause-②: no (narrowing)' and run the at-tier review. Long-term: no dead pack key. Startup axis: retire immediately. Cost: one review.",
    "B: revert the ten one-line pack edits and the i18n line of the changeset. The key stays with no reader, and check:i18n-dead-keys (report-only) lists it as a candidate. Cost: a dead published key."
    ],
    "recommendation": "A: the key's only reader is gone, and a startup-phase retirement is immediate by the frame's own rule."
    }
    ],
    "out_of_scope_findings": [
    "carrier: the domain:ui seat (open_questions[1]) · the search is not centered on main, a design gap against the card's target. Noted in the PR's Acceptance notes, not filed. Dedupe words: app bar search centered, AppHeader search position, top bar three zones.",
    "carrier: the maintainer via the seat (open_questions[0]) · dashboard, page and report crumbs still name the page in app context. Noted in the PR's Acceptance notes, not filed.",
    "carrier: 承接者:无 · the one-click jump to a grandparent in a related-record trail went with the record crumb. Each ancestor is still reachable step by step through RecordDetailView's parent back link. Noted in the PR body, not filed."
    ]
    }


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · domain:ui seat 1 · session_01JZnppgHRp4sREcouhkQYDB · 2026-10-15T11:25Z

    • Merged: PR feat(app-shell,i18n): the console app bar's left group is the app pill and its PREVIEW chip; an object route draws no crumb; Edit app is an icon button (objectui#12461) #12469 landed through the merge queue as main d9d9d98684 at 2026-10-15T11:24Z. It has one parent, d25585cffb, and is an ancestor of origin/main.
    • Verified by content: all 18 files the PR changes are blob-identical on d9d9d98684 to the accepted head 021cf7cda1. That head is the merge of the reviewed 4a036ae8d4 with main 5c4deb56ab, made through the relay's pr_update_branch as the card's same-file rule requires. objectui#12429 landed first.
    • Mis-close scan: the PR body's only closing keyword is Fixes objectstack-ai/objectui#12461. The only issue closed with the merge is this card, as completed.
    • What now holds:
      • Inside an app, the console top bar's left group is the app pill and its PREVIEW chip. An object route (list, view, record) draws no crumb, and the 「Switch Object」 menu is gone.
      • 「Edit app」 is an icon button at every width, named 「Edit app」.
      • topbar.switchObject has left all ten packs (@object-ui/i18n minor, BREAKING note).
      • The browser reading at 1440 and 390 passed every claim (ACCEPT 6190450442).
    • The chain: claim 6189226851, report 6190092384, isolated contract review 6190374575 (FAIL on the Clause-② line alone, which the seat patched to Clause-②: no (narrowing)), seat PASS record 6190438949, ACCEPT 6190450442.
    • Carried on:
      • objectui#12472, the centered search: its Blocked-by: objectstack-ai/objectui#12461 is now met, and the claiming seat dispatches it next.
      • The dashboard, page and report crumbs: kept, option A. Dropping them (B) waits on the maintainer's word, and is raised in the seat's report to the maintainer.
    • Label: pm:dispatched is removed in the same act as this note. The assignee stays, as the record of who drove it.

    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:devpathThe road — create, dev, verify, publish/install, connect an agent, iteratedomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatenhancementNew feature or requestpriority:p1

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions