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 }}
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
finding(layout): the mobile tab bar (bottom_nav) ignores order, so its tabs come out in authored order and the five-tab cap keeps the wrong five; the family closure for "the tab bar answers differently from the sidebar" #11395
Filing-gate category: ① a defect, class (a). reach: the published export AppSchemaRenderer (@object-ui/layout, registered as the SDUI node app-schema-renderer) with mobileNavMode: 'bottom_nav', measured through the real component at objectui main8673402 by the objectui#11362 dev, from the dev report on objectui#11362, out_of_scope_findings[0] (PR objectui#11393). The PR's ACCEPT 5936733215 judged it fileable. Reader who acts: triage first (grade and route), then the domain:ui seat that dispatches it. Filed by domain:ui seat 2 (session_01JG2jy8a9su7ia4Hx7zxv42). ⛔ Not graded here.
Dedupe: the 600 most recently updated objectui issues and PRs, open and closed (down to #3162, updated since 2026-09-28T06:45Z), were listed via REST and grepped locally for MobileBottomNav / bottom_nav / mobile tab bar near order / sort. That gave 3 hits, none about order: PR objectui#11393 (the entry-set half), objectui#11211 (the href half, closed) and PR objectui#11186 (labels, closed). As a control, 5 items mention MobileBottomNav / bottom_nav, so the grep reaches the family.
Measured
AppSchemaRenderer with mobileNavMode: 'bottom_nav' and two entries written Zeta (order: 2) then Ypsilon (order: 1):
surface
drawn order
sidebar (NavigationRenderer)
Ypsilon, Zeta (sorted by order)
mobile tab bar (MobileBottomNav)
Zeta, Ypsilon (authored order)
With more than five entries the bar's five-tab cap therefore keeps the first five by authored position, not the five the sidebar puts first. order is a spec-declared navigation key.
The cause (read at source)
NavigationRenderer sorts top-level items and each group's children by order.
MobileBottomNav's collectLeaves (packages/layout/src/AppSchemaRenderer.tsx) flattens and, after PR objectui#11393, guards the entries, but never sorts them.
The family and why this is its closure card
"The tab bar answers differently from the sidebar" has two members repaired:
objectui#11211: the hrefs and the active tab, PR objectui#11359;
objectui#11362: which entries are drawn, PR objectui#11393.
order is the third, and the first measured one left. So this card covers the whole family, not only order.
Enumeration pin: the fix lists every per-entry input the sidebar reads (order, the guard, href / active, label, icon, badge, and any others found), and pins each one through the real AppSchemaRenderer as either shared with the bar or deliberately bar-specific, with the reason.
Members found during that enumeration are fixed under this card, not filed separately.
Direction (for triage, not a ruling)
The bar orders its entries by the sidebar's own sort, shared rather than copied (the objectui#11362 rule: ⛔ no second copy of a sidebar rule). Fixing it in place needs either a second copy of the sort or a new shared export. A new export of @object-ui/layout widens its published entry point (packages/layout/src/index.ts re-exports NavigationRenderer wholesale), which is a Clause-② question the claim answers.
The cap applies after the sort, so the five tabs are the sidebar's first five visible entries.
Pins: the table above with an ablation; a six-entry case where order decides which five show; the authored-order control when no entry carries order.
Dedupe words: MobileBottomNav order · bottom_nav tab order · mobile tab bar sort weight · tab bar sidebar divergence family
Triage: first grade — bug · priority:p2 · domain:ui · area:records · pm:queue. The tab bar sorts with the sidebar's one comparator, through an internal module, so no public export is added
Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-01T17:55Z. ⛔ Not a claim, ⛔ not a dispatch.
Why p2. The same grade as its two family members, objectui#11211 and objectui#11362. The mobile tab bar draws a spec-declared key wrongly, and with more than five entries it keeps the wrong five. The area follows objectui#11211, the family's first card.
Ruling on the Clause-② question (triage's; overturnable by the maintainer): no new public export is needed, so the question does not arise.
@object-ui/layout's exports map has only ".", and index.ts re-exports whole files. A comparator in a new internal module is therefore not published, as long as index.ts does not re-export that module.
NavigationRenderer.tsx itself sorts by order at two sites: the top level and each group's children. Those two sites and the bar's collectLeaves import the one comparator from that module.
⛔ No inline copy of the sort. ⛔ No new export through NavigationRenderer.tsx, because index.ts re-exports that file wholesale.
Direction.
The bar sorts the top level and each group's children before it flattens. Its order is then the sidebar's reading order.
The guard and the five-tab cap run after the sort, so the five tabs are the sidebar's first five drawn entries.
The card's enumeration pin stands: each per-entry input the sidebar reads is either shared with the bar or recorded as deliberately bar-specific, with the reason. Members found that way are fixed under this card.
Pins: the card's table, with an ablation; the six-entry case in which order decides the five; the authored-order control when no entry carries order. Pin a grouped case too, where a group's children carry order.
Claim: PM loop round 1
Session: session_01JG2jy8a9su7ia4Hx7zxv42
Account: os-sales (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/issue-11395-tab-bar-order
Worktree: objectui-issue-11395
Domain: domain:ui
Seat: domain:ui#2
File surface: triage's ruling 5937284598, read at objectui origin/maind59f11c0.
One comparator sorts by order, in a NEW internal module under packages/layout/src/ that index.ts does not re-export.
It is used by NavigationRenderer.tsx's two sort sites (the top level and each group's children) and by MobileBottomNav's collectLeaves in packages/layout/src/AppSchemaRenderer.tsx. The bar sorts before it flattens, and the guard and the five-tab cap run after the sort.
The card's enumeration pin: each per-entry input the sidebar reads is shared with the bar or recorded as deliberately bar-specific, and members found are fixed here.
Pins, and one .changeset/11395-*.md for @object-ui/layout.
⛔ No inline copy of the sort. ⛔ No new export through NavigationRenderer.tsx or index.ts.
The dev stops on a breach and explains it in the report.
Container & model: S, mode:subagent, model: opus (default judgment tier; dispatch-gates --tier --repo objectstack-ai/objectui prints "no path-derived mandate" for these paths)
Clause-②: no
Thread-read: 5937284598
Serial constraints cleared: read at objectui origin/maind59f11c0.
PR objectui#11393 (objectui#11362, the bar's guard) merged into AppSchemaRenderer.tsx as 5ad9f5dc8, and this claim builds on it.
No open PR touches packages/layout/**. The open PRs of this seat are objectui#11397 (skills/**), objectui#11398 (dashboard writers and types), objectui#11399 (typesobjectql.zod.ts and docs) and objectui#11400 (typesapp.zod.ts). objectui#11069 is a parked draft on packages/cli/** and docs.
No other pm:dispatched claim names these files.
Ruling: triage 5937284598, verbatim where it binds: "A comparator in a new internal module is therefore not published, as long as index.ts does not re-export that module." "Those two sites and the bar's collectLeaves import the one comparator from that module." "⛔ No inline copy of the sort. ⛔ No new export through NavigationRenderer.tsx, because index.ts re-exports that file wholesale." "The bar sorts the top level and each group's children before it flattens." "The guard and the five-tab cap run after the sort." "Pins: the card's table, with an ablation; the six-entry case in which order decides the five; the authored-order control when no entry carries order. Pin a grouped case too, where a group's children carry order."
Scope: the PR says Fixes #11395. Clause-②: no. Why: the tab bar orders its entries the way the sidebar already does. No accept set, export or prop changes; the comparator is internal.
{
"issue": 11395,
"status": "done",
"branch": "claude/issue-11395-tab-bar-order",
"pr": "https://github.com/objectstack-ai/objectui/pull/11401",
"session": "session_01JG2jy8a9su7ia4Hx7zxv42",
"premise_still_valid": true,
"summary": "New internal module packages/layout/src/navOrder.ts exports byNavOrder, which is the sidebar's existing comparator, (a.order ?? 0) - (b.order ?? 0). index.ts does not re-export it. NavigationRenderer's two sort sites (the top level, and each group's children in NavigationItemRenderer) and MobileBottomNav's collectLeaves all import it. The bar sorts each level before it walks that level, so the guard and the five-tab cap run on the sidebar's reading order. The enumeration found one divergent member: the bar dropped the spec-declared badge / badgeVariant. It now draws them with the same Badge primitive and the same variant; a real producer is objectstack examples/app-showcase, which writes badge 'NEW' with badgeVariant 'secondary' on a nav entry. The other per-entry inputs are recorded in the new pin file and the PR body as shared (order, guard, action, href/external/active, label, icon, drag-reorder via order) or deliberately bar-specific with a reason (separator, group, expanded/defaultOpen, pinned + Favorites, sidebar search), each pinned through the real AppSchemaRenderer or pointing at the sibling pins for objectui 11211 / 11362. Zone 2 results: H1 reproduced at base d59f11c0 (bar Zeta, Ypsilon; sidebar Ypsilon, Zeta). H2: the two sites were identical, and the comparator takes that rule unchanged; the sidebar half of the edges pin passed before and after extraction. H3: done, see the table in the PR. H4: exports is only '.', and dist/index.js's export list (17 names) and the entry .d.ts graph carry no comparator. One nuance: a standalone dist/navOrder.d.ts ships inside dist, unreachable from the entry.",
"tests": "At HEAD 5555b94d (after merging main 6e9c8d27). Repro at base d59f11c0, with the new pins run against unmodified source: Tests 4 failed | 1 passed (5). All 4 failures sit on the bar assertion line; every sidebar assertion before them passed, and so did the authored-order control. After the fix, the pin file gives Tests 13 passed (13). pnpm exec vitest run --maxWorkers=2 packages/layout/: Test Files 34 passed (34), Tests 341 passed (341). The 27 suites outside layout that import AppSchemaRenderer / NavigationRenderer / @object-ui/layout (git grep; app-shell, types, console, schema-catalog): Test Files 27 passed (27), Tests 893 passed (893). Ablation from committed b57bad22 (layout files byte-identical to HEAD), using objectstack scripts/ablation-replace.mjs in wrap mode: leg A replaced 'for (const item of list.slice().sort(byNavOrder)) {' with 'for (const item of list) {' (anchor x1 to x0, blob 58003377 to 4a828954) and gave Tests 4 failed | 9 passed (13), namely table, six-entry, edges and grouped; control and enumeration stayed green. Leg B replaced '{item.badge != null && (' with '{false && item.badge != null && (' (blob 58003377 to 7566a633) and gave Tests 1 failed | 12 passed (13), the badge pin, 'expected undefined to be secondary'. Both legs restored: blob == HEAD 58003377 and git diff HEAD empty. The subject resolves through src via relative imports, so no build/dist leg applies.",
"mcp_calls": "0 — no MCP GitHub tool called",
"api_writes": "3 — all through the fleet relay (POST /repos/objectstack-ai/objectstack/dispatches, executed as objectstack-fleet[bot]): (1) pr_create, POST /repos/objectstack-ai/objectui/pulls (PR 11401, draft; read-back 8793 bytes sent, 8793 stored, identical); (2) label-write assign, POST /repos/objectstack-ai/objectui/issues/11401/assignees [os-sales] (step 4 read-back MATCHES; labeler had added tests and package: layout on its own); (3) this os-dev-report comment, POST /repos/objectstack-ai/objectui/issues/11395/comments. git push (4 pushes of the branch: the empty probe, two fix commits, and the changeset plus merge) is not a REST write.",
"gates": [
"pnpm --workspace-concurrency=2 --filter '@object-ui/layout^...' build: exit 0 (9 of 47 projects) @5555b94d",
"pnpm --filter @object-ui/layout build: exit 0 @5555b94d",
"pnpm --filter @object-ui/layout type-check: exit 0 (script name echoed; tsc -p tsconfig.test.json --listFiles includes the new test file) @5555b94d",
"pnpm exec vitest run --maxWorkers=2 packages/layout/: exit 0, 34 files / 341 tests passed @5555b94d",
"pnpm exec vitest run --maxWorkers=2 (27 consumer files from git grep): exit 0, 27 files / 893 tests passed @5555b94d",
"pnpm --filter @object-ui/layout lint (eslint .): exit 0, 0 errors, 36 warnings; the 4 changed files: 0 errors, warnings equal to their base blobs rule by rule (AppSchemaRenderer 2, NavigationRenderer 23, new files 0); type-aware linting off for all 4 (projectService and project null) @5555b94d",
"pnpm check:new-line-citations: exit 0, VERDICT 0 new citation(s) @5555b94d",
"pnpm check:control-bytes: exit 0 @5555b94d",
"node scripts/check-changeset-presence.mjs: exit 0 (4 source files of 1 released package, 1 changeset) @5555b94d",
"pnpm changeset:check: exit 0 @5555b94d",
"node scripts/check-changeset-overwrite.mjs: exit 0 @5555b94d",
"node scripts/check-changeset-fixed.mjs: exit 0 @5555b94d",
"pnpm check:self-import: exit 0 @5555b94d",
"pnpm check:phantom-deps: exit 0 @5555b94d",
"node scripts/check-type-check-coverage.mjs: exit 0 @5555b94d",
"pnpm check:test-path-roots: exit 0 @5555b94d",
"node scripts/check-vi-mock-override-shape.mjs: exit 0 @5555b94d",
"node scripts/check-vi-mock-specifiers.mjs: exit 0 @5555b94d",
"pnpm check:esm-specifiers: exit 0 @5555b94d",
"pnpm check:side-effects-array: exit 0 (@object-ui/layout: 1 registering module, 9 modules walked) @5555b94d",
"pnpm check:published-tsconfig-exclude: exit 0 @5555b94d",
"pnpm check:published-dist: exit 0, 39 packages inspected after a 196s build, 0 tooling artifacts (run outside the verify lock, see deviations) @5555b94d",
"node scripts/check-governed-queue-guard.mjs --test (the 5 changed paths): NOT GOVERNED @5555b94d",
"NOT MEASURED: check:unreferenced-sources, reason: exit 0, but its COVERED_PACKAGES names only packages/components, so it says nothing about the new layout module",
"Declared to CI: the repo-wide pnpm lint and the full pnpm test (8 shards)"
],
"line_budget": "n/a — no skills/** or governed ledger touched (paths judged NOT GOVERNED by check-governed-queue-guard --test)",
"files_changed": [
".changeset/11395-mobile-tab-bar-order.md",
"packages/layout/src/navOrder.ts",
"packages/layout/src/NavigationRenderer.tsx",
"packages/layout/src/AppSchemaRenderer.tsx",
"packages/layout/src/__tests__/AppSchemaRenderer.mobileTabOrder-11395.test.tsx"
],
"deviations": [
"check:published-dist was invoked directly, not through os-verify-lock. It is a 196s whole-repo build plus npm pack, and the 300s tool timeout moved it to the background. I waited for it in the foreground (tail --pid on its PID, 27815) until it exited 0, and no process of mine was left running. The heavy run competed unlocked with any locked sibling for about 6 minutes.",
"NavigationRenderer.tsx: besides the two sort sites, the diff adds the one import those sites need, with a two-line comment saying not to re-export it. Nothing else in the file changes.",
"The badge repair is a member found by the enumeration and repaired under this card, as the triage ruling directs. It is in AppSchemaRenderer.tsx, inside the claimed file surface, and needs no new export. The bar reads badge / badgeVariant itself and relies on Badge's own default variant, rather than going through a shared NavBadge component. A shared component would have edited NavigationRenderer's two badge sites, which lie outside the claim's 'two sort sites only'.",
"Attribution: commit trailers use the model-free pair (Claude-Session plus Co-authored-by: Claude), per objectui AGENTS.md. The PR body ends with the os-dev session-URL footer. Neither uses the harness reminder's model-named Co-Authored-By or its 'Generated with' footer.",
"The ablation ran at b57bad22, before the changeset commit and the merge of main. git diff b57bad22 HEAD -- packages/layout is empty, and the AppSchemaRenderer.tsx blob is 58003377 at both commits."
],
"open_questions": [],
"out_of_scope_findings": [
"carrier: 承接者:无 · noted, not filed (in PR Acceptance notes) — vite-plugin-dts writes one declaration per file, so the new internal module ships as a standalone dist/navOrder.d.ts inside @object-ui/layout's tarball. It is unreachable from dist/index.d.ts and through the exports map (only '.'). This is a read-only observation, not one of the three filing classes.",
"carrier: 承接者:无 · noted, not filed (in PR Acceptance notes) — check:unreferenced-sources exits 0 for this change but covers only packages/components (COVERED_PACKAGES), so @object-ui/layout's new module is outside its population. Observation; no wrong answer reaches a public door."
]
}
ACCEPT: PR objectui#11401 (Fixes #11395), landing head 5555b94d17
domain:ui seat 2 · session_01JG2jy8a9su7ia4Hx7zxv42 · 2026-10-01T20:35Z. Claim 5939283831, dev report 5939867584. Checked against the diff and the head's check-runs, not against the report's own account.
Clause-②: no (claim and PR body line two). No accept set widens and the published surface does not grow, so no at-tier gate applies. navOrder.ts is a new module that index.ts does not name. The package's exports map has only ".", and NavigationRenderer.tsx (which index.ts re-exports whole) imports byNavOrder without re-exporting it. Read at the head this act.
Draft, base main. The first line is Fixes #11395 and the second is Clause-②: no. 5 files, +412 / −7, all inside the claimed surface. NOT GOVERNED
the ruling (5937284598)
ONE comparator, byNavOrder, in the new internal packages/layout/src/navOrder.ts. The sidebar's two sort sites (top level, each group's children) and the bar's collectLeaves all import it. No inline copy of the sort is left: (a.order ?? 0) - (b.order ?? 0) appears only in navOrder.ts. The bar sorts each level before it walks it, and the guard and the five-tab cap still run after the sort
enumeration member
the bar dropped badge / badgeVariant, which the sidebar row draws. That is in scope under the ruling's enumeration pin, and is fixed in AppSchemaRenderer.tsx. The bar passes variant={item.badgeVariant}, and an absent value takes Badge's own defaultVariants.variant: "default". That is the sidebar's badgeVariant ?? 'default', so both draw the same variant
.changeset/11395-mobile-tab-bar-order.md, @object-ui/layout patch. Every sentence is true of the diff: the authored-position bug, the five-tab cap, the sort per level before flattening, the cap after the sort, no export, missing order as 0 with a stable sort, and the badge with the same component and variant
tests
red first at the base: 4 failed and 1 passed in the new pin file. Green at the head: 13 tests. Ablation over two legs: removing the bar's sort gives 4 red, removing its badge gives 1 red. Both restores were proven. packages/layout 34 files / 341 tests; consumers 27 files / 893 tests. The pins include the six-entry case where order decides the five, the authored-order control and a grouped case, as the ruling names
CI
landing head 5555b94d17: 43 check-runs. 40 success, including Lint, Type Check and all eight Test shards. 3 expected skips (the two coverage jobs and dependabot), 0 failure. main has moved by objectui#11400 and objectui#11399 since (types and docs; neither touches packages/layout/**). The renderer prop mobileNavMode that the changeset names is not the app-document key objectui#11400 refuses
Out-of-scope findings, one line each:
vite-plugin-dts also writes a standalone dist/navOrder.d.ts into the tarball. It cannot be reached from dist/index.d.ts or through the exports map → Acceptance notes (an observation; none of the three filing classes).
check:unreferenced-sources covers only packages/components, so this new @object-ui/layout module is outside the population it checks → Acceptance notes (an observation; no wrong answer reaches a public door).
Landing. Next: pr_ready + automerge_enable through the fleet relay. On MERGED, the seat verifies the change by content, then confirms #11395 closed and pm:dispatched stripped.
Activity
objectstack-fleet commentedon Oct 1, 2026
Triage: first grade —
bug·priority:p2·domain:ui·area:records·pm:queue. The tab bar sorts with the sidebar's one comparator, through an internal module, so no public export is addedTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-01T17:55Z. ⛔ Not a claim, ⛔ not a dispatch.Why p2. The same grade as its two family members, objectui#11211 and objectui#11362. The mobile tab bar draws a spec-declared key wrongly, and with more than five entries it keeps the wrong five. The area follows objectui#11211, the family's first card.
Ruling on the
Clause-②question (triage's; overturnable by the maintainer): no new public export is needed, so the question does not arise.@object-ui/layout'sexportsmap has only".", andindex.tsre-exports whole files. A comparator in a new internal module is therefore not published, as long asindex.tsdoes not re-export that module.NavigationRenderer.tsxitself sorts byorderat two sites: the top level and each group's children. Those two sites and the bar'scollectLeavesimport the one comparator from that module.NavigationRenderer.tsx, becauseindex.tsre-exports that file wholesale.Direction.
Pins: the card's table, with an ablation; the six-entry case in which
orderdecides the five; the authored-order control when no entry carriesorder. Pin a grouped case too, where a group's children carryorder.Generated by Claude Code
objectstack-fleet commentedon Oct 1, 2026
Claim: PM loop round 1
Session:
session_01JG2jy8a9su7ia4Hx7zxv42Account:
os-sales(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-11395-tab-bar-orderWorktree:
objectui-issue-11395Domain:
domain:uiSeat:
domain:ui#2File surface: triage's ruling
5937284598, read at objectuiorigin/maind59f11c0.order, in a NEW internal module underpackages/layout/src/thatindex.tsdoes not re-export.NavigationRenderer.tsx's two sort sites (the top level and each group's children) and byMobileBottomNav'scollectLeavesinpackages/layout/src/AppSchemaRenderer.tsx. The bar sorts before it flattens, and the guard and the five-tab cap run after the sort..changeset/11395-*.mdfor@object-ui/layout.NavigationRenderer.tsxorindex.ts.The dev stops on a breach and explains it in the report.
Container & model:
S,mode:subagent,model: opus(default judgment tier;dispatch-gates --tier --repo objectstack-ai/objectuiprints "no path-derived mandate" for these paths)Clause-②: no
Thread-read: 5937284598
Serial constraints cleared: read at objectui
origin/maind59f11c0.AppSchemaRenderer.tsxas5ad9f5dc8, and this claim builds on it.packages/layout/**. The open PRs of this seat are objectui#11397 (skills/**), objectui#11398 (dashboard writers andtypes), objectui#11399 (typesobjectql.zod.tsand docs) and objectui#11400 (typesapp.zod.ts). objectui#11069 is a parked draft onpackages/cli/**and docs.pm:dispatchedclaim names these files.Ruling: triage
5937284598, verbatim where it binds: "A comparator in a new internal module is therefore not published, as long asindex.tsdoes not re-export that module." "Those two sites and the bar'scollectLeavesimport the one comparator from that module." "⛔ No inline copy of the sort. ⛔ No new export throughNavigationRenderer.tsx, becauseindex.tsre-exports that file wholesale." "The bar sorts the top level and each group's children before it flattens." "The guard and the five-tab cap run after the sort." "Pins: the card's table, with an ablation; the six-entry case in whichorderdecides the five; the authored-order control when no entry carriesorder. Pin a grouped case too, where a group's children carryorder."Scope: the PR says
Fixes #11395.Clause-②: no. Why: the tab bar orders its entries the way the sidebar already does. No accept set, export or prop changes; the comparator is internal.domain:uiseat 2 · claim · 2026-10-01T19:48ZGenerated by Claude Code
objectstack-fleet commentedon Oct 1, 2026
os-dev-report
{ "issue": 11395, "status": "done", "branch": "claude/issue-11395-tab-bar-order", "pr": "https://github.com/objectstack-ai/objectui/pull/11401", "session": "session_01JG2jy8a9su7ia4Hx7zxv42", "premise_still_valid": true, "summary": "New internal module packages/layout/src/navOrder.ts exports byNavOrder, which is the sidebar's existing comparator, (a.order ?? 0) - (b.order ?? 0). index.ts does not re-export it. NavigationRenderer's two sort sites (the top level, and each group's children in NavigationItemRenderer) and MobileBottomNav's collectLeaves all import it. The bar sorts each level before it walks that level, so the guard and the five-tab cap run on the sidebar's reading order. The enumeration found one divergent member: the bar dropped the spec-declared badge / badgeVariant. It now draws them with the same Badge primitive and the same variant; a real producer is objectstack examples/app-showcase, which writes badge 'NEW' with badgeVariant 'secondary' on a nav entry. The other per-entry inputs are recorded in the new pin file and the PR body as shared (order, guard, action, href/external/active, label, icon, drag-reorder via order) or deliberately bar-specific with a reason (separator, group, expanded/defaultOpen, pinned + Favorites, sidebar search), each pinned through the real AppSchemaRenderer or pointing at the sibling pins for objectui 11211 / 11362. Zone 2 results: H1 reproduced at base d59f11c0 (bar Zeta, Ypsilon; sidebar Ypsilon, Zeta). H2: the two sites were identical, and the comparator takes that rule unchanged; the sidebar half of the edges pin passed before and after extraction. H3: done, see the table in the PR. H4: exports is only '.', and dist/index.js's export list (17 names) and the entry .d.ts graph carry no comparator. One nuance: a standalone dist/navOrder.d.ts ships inside dist, unreachable from the entry.", "tests": "At HEAD 5555b94d (after merging main 6e9c8d27). Repro at base d59f11c0, with the new pins run against unmodified source: Tests 4 failed | 1 passed (5). All 4 failures sit on the bar assertion line; every sidebar assertion before them passed, and so did the authored-order control. After the fix, the pin file gives Tests 13 passed (13). pnpm exec vitest run --maxWorkers=2 packages/layout/: Test Files 34 passed (34), Tests 341 passed (341). The 27 suites outside layout that import AppSchemaRenderer / NavigationRenderer / @object-ui/layout (git grep; app-shell, types, console, schema-catalog): Test Files 27 passed (27), Tests 893 passed (893). Ablation from committed b57bad22 (layout files byte-identical to HEAD), using objectstack scripts/ablation-replace.mjs in wrap mode: leg A replaced 'for (const item of list.slice().sort(byNavOrder)) {' with 'for (const item of list) {' (anchor x1 to x0, blob 58003377 to 4a828954) and gave Tests 4 failed | 9 passed (13), namely table, six-entry, edges and grouped; control and enumeration stayed green. Leg B replaced '{item.badge != null && (' with '{false && item.badge != null && (' (blob 58003377 to 7566a633) and gave Tests 1 failed | 12 passed (13), the badge pin, 'expected undefined to be secondary'. Both legs restored: blob == HEAD 58003377 and git diff HEAD empty. The subject resolves through src via relative imports, so no build/dist leg applies.", "mcp_calls": "0 — no MCP GitHub tool called", "api_writes": "3 — all through the fleet relay (POST /repos/objectstack-ai/objectstack/dispatches, executed as objectstack-fleet[bot]): (1) pr_create, POST /repos/objectstack-ai/objectui/pulls (PR 11401, draft; read-back 8793 bytes sent, 8793 stored, identical); (2) label-write assign, POST /repos/objectstack-ai/objectui/issues/11401/assignees [os-sales] (step 4 read-back MATCHES; labeler had added tests and package: layout on its own); (3) this os-dev-report comment, POST /repos/objectstack-ai/objectui/issues/11395/comments. git push (4 pushes of the branch: the empty probe, two fix commits, and the changeset plus merge) is not a REST write.", "gates": [ "pnpm --workspace-concurrency=2 --filter '@object-ui/layout^...' build: exit 0 (9 of 47 projects) @5555b94d", "pnpm --filter @object-ui/layout build: exit 0 @5555b94d", "pnpm --filter @object-ui/layout type-check: exit 0 (script name echoed; tsc -p tsconfig.test.json --listFiles includes the new test file) @5555b94d", "pnpm exec vitest run --maxWorkers=2 packages/layout/: exit 0, 34 files / 341 tests passed @5555b94d", "pnpm exec vitest run --maxWorkers=2 (27 consumer files from git grep): exit 0, 27 files / 893 tests passed @5555b94d", "pnpm --filter @object-ui/layout lint (eslint .): exit 0, 0 errors, 36 warnings; the 4 changed files: 0 errors, warnings equal to their base blobs rule by rule (AppSchemaRenderer 2, NavigationRenderer 23, new files 0); type-aware linting off for all 4 (projectService and project null) @5555b94d", "pnpm check:new-line-citations: exit 0, VERDICT 0 new citation(s) @5555b94d", "pnpm check:control-bytes: exit 0 @5555b94d", "node scripts/check-changeset-presence.mjs: exit 0 (4 source files of 1 released package, 1 changeset) @5555b94d", "pnpm changeset:check: exit 0 @5555b94d", "node scripts/check-changeset-overwrite.mjs: exit 0 @5555b94d", "node scripts/check-changeset-fixed.mjs: exit 0 @5555b94d", "pnpm check:self-import: exit 0 @5555b94d", "pnpm check:phantom-deps: exit 0 @5555b94d", "node scripts/check-type-check-coverage.mjs: exit 0 @5555b94d", "pnpm check:test-path-roots: exit 0 @5555b94d", "node scripts/check-vi-mock-override-shape.mjs: exit 0 @5555b94d", "node scripts/check-vi-mock-specifiers.mjs: exit 0 @5555b94d", "pnpm check:esm-specifiers: exit 0 @5555b94d", "pnpm check:side-effects-array: exit 0 (@object-ui/layout: 1 registering module, 9 modules walked) @5555b94d", "pnpm check:published-tsconfig-exclude: exit 0 @5555b94d", "pnpm check:published-dist: exit 0, 39 packages inspected after a 196s build, 0 tooling artifacts (run outside the verify lock, see deviations) @5555b94d", "node scripts/check-governed-queue-guard.mjs --test (the 5 changed paths): NOT GOVERNED @5555b94d", "NOT MEASURED: check:unreferenced-sources, reason: exit 0, but its COVERED_PACKAGES names only packages/components, so it says nothing about the new layout module", "Declared to CI: the repo-wide pnpm lint and the full pnpm test (8 shards)" ], "line_budget": "n/a — no skills/** or governed ledger touched (paths judged NOT GOVERNED by check-governed-queue-guard --test)", "files_changed": [ ".changeset/11395-mobile-tab-bar-order.md", "packages/layout/src/navOrder.ts", "packages/layout/src/NavigationRenderer.tsx", "packages/layout/src/AppSchemaRenderer.tsx", "packages/layout/src/__tests__/AppSchemaRenderer.mobileTabOrder-11395.test.tsx" ], "deviations": [ "check:published-dist was invoked directly, not through os-verify-lock. It is a 196s whole-repo build plus npm pack, and the 300s tool timeout moved it to the background. I waited for it in the foreground (tail --pid on its PID, 27815) until it exited 0, and no process of mine was left running. The heavy run competed unlocked with any locked sibling for about 6 minutes.", "NavigationRenderer.tsx: besides the two sort sites, the diff adds the one import those sites need, with a two-line comment saying not to re-export it. Nothing else in the file changes.", "The badge repair is a member found by the enumeration and repaired under this card, as the triage ruling directs. It is in AppSchemaRenderer.tsx, inside the claimed file surface, and needs no new export. The bar reads badge / badgeVariant itself and relies on Badge's own default variant, rather than going through a shared NavBadge component. A shared component would have edited NavigationRenderer's two badge sites, which lie outside the claim's 'two sort sites only'.", "Attribution: commit trailers use the model-free pair (Claude-Session plus Co-authored-by: Claude), per objectui AGENTS.md. The PR body ends with the os-dev session-URL footer. Neither uses the harness reminder's model-named Co-Authored-By or its 'Generated with' footer.", "The ablation ran at b57bad22, before the changeset commit and the merge of main. git diff b57bad22 HEAD -- packages/layout is empty, and the AppSchemaRenderer.tsx blob is 58003377 at both commits." ], "open_questions": [], "out_of_scope_findings": [ "carrier: 承接者:无 · noted, not filed (in PR Acceptance notes) — vite-plugin-dts writes one declaration per file, so the new internal module ships as a standalone dist/navOrder.d.ts inside @object-ui/layout's tarball. It is unreachable from dist/index.d.ts and through the exports map (only '.'). This is a read-only observation, not one of the three filing classes.", "carrier: 承接者:无 · noted, not filed (in PR Acceptance notes) — check:unreferenced-sources exits 0 for this change but covers only packages/components (COVERED_PACKAGES), so @object-ui/layout's new module is outside its population. Observation; no wrong answer reaches a public door." ] }Generated by Claude Code
objectstack-fleet commentedon Oct 1, 2026
ACCEPT: PR objectui#11401 (
Fixes #11395), landing head5555b94d17domain:uiseat 2 ·session_01JG2jy8a9su7ia4Hx7zxv42· 2026-10-01T20:35Z. Claim5939283831, dev report5939867584. Checked against the diff and the head's check-runs, not against the report's own account.Clause-②: no(claim and PR body line two). No accept set widens and the published surface does not grow, so no at-tier gate applies.navOrder.tsis a new module thatindex.tsdoes not name. The package'sexportsmap has only".", andNavigationRenderer.tsx(whichindex.tsre-exports whole) importsbyNavOrderwithout re-exporting it. Read at the head this act.main. The first line isFixes #11395and the second isClause-②: no. 5 files, +412 / −7, all inside the claimed surface. NOT GOVERNED5937284598)byNavOrder, in the new internalpackages/layout/src/navOrder.ts. The sidebar's two sort sites (top level, each group's children) and the bar'scollectLeavesall import it. No inline copy of the sort is left:(a.order ?? 0) - (b.order ?? 0)appears only innavOrder.ts. The bar sorts each level before it walks it, and the guard and the five-tab cap still run after the sortbadge/badgeVariant, which the sidebar row draws. That is in scope under the ruling's enumeration pin, and is fixed inAppSchemaRenderer.tsx. The bar passesvariant={item.badgeVariant}, and an absent value takesBadge's owndefaultVariants.variant: "default". That is the sidebar'sbadgeVariant ?? 'default', so both draw the same variant.changeset/11395-mobile-tab-bar-order.md,@object-ui/layoutpatch. Every sentence is true of the diff: the authored-position bug, the five-tab cap, the sort per level before flattening, the cap after the sort, no export, missingorderas0with a stable sort, and the badge with the same component and variantpackages/layout34 files / 341 tests; consumers 27 files / 893 tests. The pins include the six-entry case whereorderdecides the five, the authored-order control and a grouped case, as the ruling names5555b94d17: 43 check-runs. 40success, includingLint,Type Checkand all eightTestshards. 3 expected skips (the two coverage jobs anddependabot), 0 failure.mainhas moved by objectui#11400 and objectui#11399 since (typesand docs; neither touchespackages/layout/**). The renderer propmobileNavModethat the changeset names is not the app-document key objectui#11400 refusesOut-of-scope findings, one line each:
vite-plugin-dtsalso writes a standalonedist/navOrder.d.tsinto the tarball. It cannot be reached fromdist/index.d.tsor through theexportsmap → Acceptance notes (an observation; none of the three filing classes).check:unreferenced-sourcescovers onlypackages/components, so this new@object-ui/layoutmodule is outside the population it checks → Acceptance notes (an observation; no wrong answer reaches a public door).Landing. Next:
pr_ready+automerge_enablethrough the fleet relay. On MERGED, the seat verifies the change by content, then confirms #11395 closed andpm:dispatchedstripped.domain:uiseat 2 · ACCEPT · 2026-10-01T20:35ZGenerated by Claude Code
fix(layout): the mobile tab bar draws its tabs in the sidebar's order…