You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Console shell: the sidebar auto-collapses to an icon rail on narrow windows and expands as an overlay on click, so a 1280 px window keeps room for the split record pane #27593
Filed from the record-page epic #26676 as a sub-issue (the maintainer, 2026-10-29: 「另立两张 ui 卡:侧栏窄窗自动折叠、…」; earlier, looking at Attio on a narrow pane: 「窄屏幕左侧菜单自动折叠,点击弹出,这个你也可以参考」). It is console shell work, not record-page anatomy; it makes #27545's default split fit on common laptop windows.
Attio collapses its sidebar to a single icon on narrow windows and expands it as an overlay on click; Twenty keeps a collapsible rail.
Target
Auto-collapse below a window width of 1100 px: the sidebar renders as the icon rail (collapsible="icon", the existing --sidebar-width-icon), the content area takes the rest; above 1100 px the sidebar behaves as today. The threshold is one named constant beside the record page's 1120 / 760 ones, not a magic number.
Click to expand as an overlay: clicking the rail (or the sidebar toggle, ⌘B as today) expands the full sidebar OVER the content, not pushing it; choosing a navigation item, clicking outside, or Esc collapses it again. Hover alone does not expand.
Reader override, remembered: a reader who expands the sidebar and pins it (the existing toggle in the expanded state) keeps it expanded below 1100 px for that browser; stored where the rail width is stored today. The mobile Sheet below 768 px is unchanged.
No metadata key; no spec change; the app's navigation items are untouched.
File surface
ui/packages/app-shell/src/layout/ConsoleLayout.tsx, ui/packages/app-shell/src/components/UnifiedSidebar.tsx (rail rendering of groups and items with tooltips), ui/packages/components/src/ui/sidebar.tsx (overlay expand mode), ui/packages/components/src/hooks/use-mobile.ts (a second breakpoint hook beside it), i18n for the tooltips, tests beside, changeset; ⛔ packages/spec/**.
Acceptance
Tests: at 1280 px the sidebar mounts as the icon rail and the content width is the window minus the rail; a rail click expands an overlay that does not change the content width; a navigation choice / outside click / Esc collapses it; the pinned override persists across a reload; at 1440 px nothing changes; at 700 px the Sheet still mounts.
Screenshots at 1280 (rail, overlay expanded) and 1440 (unchanged) on HotCRM, on a never-merged evidence branch.
The epic seat verifies in the console preview before ACCEPT (maintainer: 「车队开发完后你审核效果」).
Claim: PM loop round 1
Session: session_01Utp6CbDAucNyQ4UtzCJKyh
Account: os-bill
Branch: claude/project-thread-oakpit (the card thread's designated push branch, the recorded platform exception to claude/issue-<n>-<slug>)
Worktree: objectstack-issue-27593
Domain: domain:ui
Seat: domain:ui#4
File surface: app-shell ConsoleLayout.tsx, UnifiedSidebar.tsx; a new breakpoint hook beside components/src/hooks/use-mobile.ts; the overlay expand built OUTSIDE components/src/ui/** (ui/AGENTS.md #7 No-Touch — a wrapper in components/src/custom/ or in app-shell; stop and report before any src/ui/** edit); @object-ui/i18n packs for rail tooltips; tests beside; root .changeset/; ⛔ packages/spec/** (stop on breach; explain in the report)
Container & model: M, mode:thread, model: TIER_DEFAULT (dispatch-gates --tier on fec60e00: no path-derived mandate), effort: xhigh
Clause-②: yes (widening)
Metadata-protocol: none
Responsibility: below 1100 px the sidebar mounts as the icon rail and expands as an overlay on click (nav choice / outside click / Esc collapses), a pinned reader override persists, ≥1100 px and the mobile Sheet unchanged; tests + screenshots on a never-merged evidence branch; the epic seat re-measures in the console preview before ACCEPT
Thread-read: none
Premise: holds
Serial constraints cleared: maintainer 2026-10-30 01:47Z/01:49Z dispatch-now (temporary concurrency raise); no open PR on ConsoleLayout/UnifiedSidebar known — re-check before push
Dispatched 2026-10-30T01:51Z to a card thread of the seat's project; the order lives in the project. ⛔ The thread posts no claim and writes no assignee.
{
"issue": 27593,
"status": "done",
"branch": "claude/project-thread-oakpit",
"pr": "https://github.com/objectstack-ai/objectstack/pull/27893",
"session": "session_01Utp6CaPpqK4cmqg5XJnjsj (card thread, mode:thread; seat session_01Utp6CbDAucNyQ4UtzCJKyh)",
"premise_still_valid": true,
"summary": "Premise true on main ddab8b99: at 1280 px the expanded console sidebar (256 px) left 1024 px of content, under the split's 1160. Corrections measured on main: no resizable 200-480 px rail exists (the only remembered state is the primitive's sidebar_state open/closed cookie); the sidebar was already collapsible=icon with rail tooltips; useResponsiveSidebar already collapsed it once between 768 and 1023 px and wrote that into the cookie; no pin toggle existed. Now: below CONSOLE_SIDEBAR_RAIL_BELOW_WIDTH (1160 + 256 = 1416, derived) and above the phone breakpoint the sidebar mounts as the icon rail; a rail-background click, the app bar trigger or Cmd/Ctrl+B expands it over the content (panel z-40, a display:contents frame keeps the in-flow gap at rail width, content box unmoved); navigation, an outside press or an unhandled Esc folds it; 'Keep sidebar open' (workspace preference console.sidebar.keepOpen, one new i18n key sidebar.keepOpen in ten packs) keeps the wide-window sidebar on a narrow window; the wide-window open state is stashed (os:console-sidebar:wide-open) and handed back, so the fold no longer overwrites it. New components export useIsViewportBelow (Clause-2 yes, widening). No edit under components/src/ui/**.",
"files_changed": [
".changeset/27593-console-sidebar-narrow-rail.md",
"ui/packages/app-shell/src/chrome/KeyboardShortcutsDialog.wiredOnly-11674.test.tsx",
"ui/packages/app-shell/src/hooks/__tests__/useResponsiveSidebar.test.tsx",
"ui/packages/app-shell/src/hooks/useResponsiveSidebar.ts",
"ui/packages/app-shell/src/layout/ConsoleLayout.tsx",
"ui/packages/app-shell/src/layout/UnifiedSidebar.tsx",
"ui/packages/app-shell/src/layout/__tests__/UnifiedSidebar.railOverlay-27593.test.tsx",
"ui/packages/app-shell/src/layout/__tests__/consoleSidebarRail.width-27593.test.tsx",
"ui/packages/app-shell/src/layout/__tests__/consoleTopBar.height-12639.test.ts",
"ui/packages/app-shell/src/layout/consoleSidebarRail.ts",
"ui/packages/components/src/hooks/__tests__/use-viewport-below.test.tsx",
"ui/packages/components/src/hooks/use-viewport-below.tsx",
"ui/packages/components/src/index.ts",
"ui/packages/i18n/src/locales/{ar,de,en,es,fr,ja,ko,pt,ru,zh}.ts"
],
"tests": "New/rewritten 4 files: Test Files 4 passed (4), Tests 24 passed (24), lock VERDICT command-exit 0. Every existing file naming UnifiedSidebar / ConsoleLayout / useResponsiveSidebar / LocalizedSidebarTrigger / sidebar_state (135) + packages/i18n/ on 48346663: 2 failed | 223 passed (225) files; the 2 were the pins this PR updates (KeyboardShortcutsDialog.wiredOnly-11674, consoleTopBar.height-12639), 17/17 after the update. On merged head bc7935f4 after rebuilding i18n + components: type-check components + app-shell (tsc --noEmit + tsconfig.test.json) exit 0; 4 new files + 2 pins + LocalizedSidebarTrigger.appBar-12429 + packages/i18n/: Test Files 98 passed (98), Tests 1428 passed | 13 skipped, VERDICT command-exit 0. Ablations via scripts/ablation-replace.mjs (anchor 1 -> 0, restored, git diff HEAD empty): frame gap class removed 1 failed | 17 passed; stash write removed 5 failed | 13 passed; inside-press check removed 1 failed | 17 passed. Browser (Chromium, examples/app-crm Contacts, branch fdb717ea): 1280 load rail gap 48 / content 48+1232; overlay panel 256 z 40, content 48+1232; 1440 unchanged 256+1184; 700 phone drawer. Evidence branch claude/issue-27593-evidence @ b86187c2, folder evidence/27593/.",
"gates": "dispatch-gates --commands on bc7935f4: 35 derived, each recorded as `<command> :: exit 0` (all 35 exit 0, check-sdui-manifest and its --self-test included); --ran reconciliation: 35 derived, 35 run, 0 NOT-MEASURED, 0 UNRUN. check:i18n-drift / check:i18n-keys / check:i18n-dead-keys exit 0; check:new-line-citations 0 new, exit 0. CI families: see PR check-runs.",
"line_budget": "n/a — no ledgered file touched",
"dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
"deviations": [
"Threshold 1416 px (derived: split 1160 + sidebar 256), not the card's Target-1 1100 px: Target 1 contradicts Acceptance 1 (1280 folds, 1440 unchanged) and the title; agreed with the seat before implementing, written as a deviation in the PR's Acceptance notes for the epic seat to confirm at the preview.",
"Two GitHub MCP reads (mcp__github__issue_read get + get_comments on 27593) made before reading this order, which forbids MCP; the card and comments were then read through REST. No MCP write.",
"Commit 645bb025 is titled test(console) but also carries the railOverlay prop / enabled option that gate the overlay to ConsoleLayout; the branch fails force-push criterion 1, so history is left as is.",
"Harness post-PR assignee/reviewer step skipped per the maintainer's routing rule; relay read-back shows no assignees.",
"Screenshots taken on examples/app-crm (Contacts), not HotCRM: this container reaches only this repository."
],
"mcp_calls": "4 — mcp__github__issue_read x2 (reads, see deviations); mcp__claude-code-remote__get_session x1 (session id lookup); mcp__claude-code-remote__subscribe_pr_activity x1 (PR 27893 event subscription, not a GitHub write)",
"api_writes": "2 — relay pr_create (repository_dispatch -> POST /repos/objectstack-ai/objectstack/pulls, draft, read-back identical 12833 bytes, assignees none); this os-dev-report comment via post-stamped (POST /repos/objectstack-ai/objectstack/issues/27593/comments). git pushes (designated branch, evidence branch) not counted.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: none · noted, not filed — plugin-detail recordPageColumns.ts docblock derives its 1120 / 760 container floors from window examples with the sidebar expanded; the tiers are container-measured so they follow the fold, but the docblock's window arithmetic is now stale at 1280-1415 px. dedupe words: record page columns docblock window width sidebar expanded"
]
}
This card is claimed by ui seat 4 and is not waiting in the queue. Its PR is #27893: green, NOT governed and mergeable_state clean. The PR had stayed draft only for the epic seat's preview check. The maintainer ruled at 10:39Z that the seat accepts and merges on its own. ACCEPT → PR #27893 comment 6520805116 · 2026-10-30T10:40Z. The PR is now marked ready with automerge on, and the card closes when it merges.
Activity
objectstack-fleet commentedon Oct 30, 2026
domain:ui#4 · os-bill · session_01Utp6CbDAucNyQ4UtzCJKyh
Claim: PM loop round 1
Session:
session_01Utp6CbDAucNyQ4UtzCJKyhAccount:
os-billBranch:
claude/project-thread-oakpit(the card thread's designated push branch, the recorded platform exception toclaude/issue-<n>-<slug>)Worktree:
objectstack-issue-27593Domain:
domain:uiSeat:
domain:ui#4File surface: app-shell
ConsoleLayout.tsx,UnifiedSidebar.tsx; a new breakpoint hook besidecomponents/src/hooks/use-mobile.ts; the overlay expand built OUTSIDEcomponents/src/ui/**(ui/AGENTS.md #7 No-Touch — a wrapper incomponents/src/custom/or in app-shell; stop and report before anysrc/ui/**edit);@object-ui/i18npacks for rail tooltips; tests beside; root.changeset/; ⛔packages/spec/**(stop on breach; explain in the report)Container & model:
M,mode:thread,model: TIER_DEFAULT(dispatch-gates --tieronfec60e00: no path-derived mandate),effort: xhighClause-②: yes (widening)
Metadata-protocol: none
Responsibility: below 1100 px the sidebar mounts as the icon rail and expands as an overlay on click (nav choice / outside click / Esc collapses), a pinned reader override persists, ≥1100 px and the mobile Sheet unchanged; tests + screenshots on a never-merged evidence branch; the epic seat re-measures in the console preview before ACCEPT
Thread-read: none
Premise: holds
Serial constraints cleared: maintainer 2026-10-30 01:47Z/01:49Z dispatch-now (temporary concurrency raise); no open PR on ConsoleLayout/UnifiedSidebar known — re-check before push
Dispatched 2026-10-30T01:51Z to a card thread of the seat's project; the order lives in the project. ⛔ The thread posts no claim and writes no assignee.
Generated by Claude Code
objectstack-fleet commentedon Oct 30, 2026
domain:ui#4 · os-bill · session_01Utp6CaPpqK4cmqg5XJnjsj
os-dev-report
{ "issue": 27593, "status": "done", "branch": "claude/project-thread-oakpit", "pr": "https://github.com/objectstack-ai/objectstack/pull/27893", "session": "session_01Utp6CaPpqK4cmqg5XJnjsj (card thread, mode:thread; seat session_01Utp6CbDAucNyQ4UtzCJKyh)", "premise_still_valid": true, "summary": "Premise true on main ddab8b99: at 1280 px the expanded console sidebar (256 px) left 1024 px of content, under the split's 1160. Corrections measured on main: no resizable 200-480 px rail exists (the only remembered state is the primitive's sidebar_state open/closed cookie); the sidebar was already collapsible=icon with rail tooltips; useResponsiveSidebar already collapsed it once between 768 and 1023 px and wrote that into the cookie; no pin toggle existed. Now: below CONSOLE_SIDEBAR_RAIL_BELOW_WIDTH (1160 + 256 = 1416, derived) and above the phone breakpoint the sidebar mounts as the icon rail; a rail-background click, the app bar trigger or Cmd/Ctrl+B expands it over the content (panel z-40, a display:contents frame keeps the in-flow gap at rail width, content box unmoved); navigation, an outside press or an unhandled Esc folds it; 'Keep sidebar open' (workspace preference console.sidebar.keepOpen, one new i18n key sidebar.keepOpen in ten packs) keeps the wide-window sidebar on a narrow window; the wide-window open state is stashed (os:console-sidebar:wide-open) and handed back, so the fold no longer overwrites it. New components export useIsViewportBelow (Clause-2 yes, widening). No edit under components/src/ui/**.", "files_changed": [ ".changeset/27593-console-sidebar-narrow-rail.md", "ui/packages/app-shell/src/chrome/KeyboardShortcutsDialog.wiredOnly-11674.test.tsx", "ui/packages/app-shell/src/hooks/__tests__/useResponsiveSidebar.test.tsx", "ui/packages/app-shell/src/hooks/useResponsiveSidebar.ts", "ui/packages/app-shell/src/layout/ConsoleLayout.tsx", "ui/packages/app-shell/src/layout/UnifiedSidebar.tsx", "ui/packages/app-shell/src/layout/__tests__/UnifiedSidebar.railOverlay-27593.test.tsx", "ui/packages/app-shell/src/layout/__tests__/consoleSidebarRail.width-27593.test.tsx", "ui/packages/app-shell/src/layout/__tests__/consoleTopBar.height-12639.test.ts", "ui/packages/app-shell/src/layout/consoleSidebarRail.ts", "ui/packages/components/src/hooks/__tests__/use-viewport-below.test.tsx", "ui/packages/components/src/hooks/use-viewport-below.tsx", "ui/packages/components/src/index.ts", "ui/packages/i18n/src/locales/{ar,de,en,es,fr,ja,ko,pt,ru,zh}.ts" ], "tests": "New/rewritten 4 files: Test Files 4 passed (4), Tests 24 passed (24), lock VERDICT command-exit 0. Every existing file naming UnifiedSidebar / ConsoleLayout / useResponsiveSidebar / LocalizedSidebarTrigger / sidebar_state (135) + packages/i18n/ on 48346663: 2 failed | 223 passed (225) files; the 2 were the pins this PR updates (KeyboardShortcutsDialog.wiredOnly-11674, consoleTopBar.height-12639), 17/17 after the update. On merged head bc7935f4 after rebuilding i18n + components: type-check components + app-shell (tsc --noEmit + tsconfig.test.json) exit 0; 4 new files + 2 pins + LocalizedSidebarTrigger.appBar-12429 + packages/i18n/: Test Files 98 passed (98), Tests 1428 passed | 13 skipped, VERDICT command-exit 0. Ablations via scripts/ablation-replace.mjs (anchor 1 -> 0, restored, git diff HEAD empty): frame gap class removed 1 failed | 17 passed; stash write removed 5 failed | 13 passed; inside-press check removed 1 failed | 17 passed. Browser (Chromium, examples/app-crm Contacts, branch fdb717ea): 1280 load rail gap 48 / content 48+1232; overlay panel 256 z 40, content 48+1232; 1440 unchanged 256+1184; 700 phone drawer. Evidence branch claude/issue-27593-evidence @ b86187c2, folder evidence/27593/.", "gates": "dispatch-gates --commands on bc7935f4: 35 derived, each recorded as `<command> :: exit 0` (all 35 exit 0, check-sdui-manifest and its --self-test included); --ran reconciliation: 35 derived, 35 run, 0 NOT-MEASURED, 0 UNRUN. check:i18n-drift / check:i18n-keys / check:i18n-dead-keys exit 0; check:new-line-citations 0 new, exit 0. CI families: see PR check-runs.", "line_budget": "n/a — no ledgered file touched", "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "deviations": [ "Threshold 1416 px (derived: split 1160 + sidebar 256), not the card's Target-1 1100 px: Target 1 contradicts Acceptance 1 (1280 folds, 1440 unchanged) and the title; agreed with the seat before implementing, written as a deviation in the PR's Acceptance notes for the epic seat to confirm at the preview.", "Two GitHub MCP reads (mcp__github__issue_read get + get_comments on 27593) made before reading this order, which forbids MCP; the card and comments were then read through REST. No MCP write.", "Commit 645bb025 is titled test(console) but also carries the railOverlay prop / enabled option that gate the overlay to ConsoleLayout; the branch fails force-push criterion 1, so history is left as is.", "Harness post-PR assignee/reviewer step skipped per the maintainer's routing rule; relay read-back shows no assignees.", "Screenshots taken on examples/app-crm (Contacts), not HotCRM: this container reaches only this repository." ], "mcp_calls": "4 — mcp__github__issue_read x2 (reads, see deviations); mcp__claude-code-remote__get_session x1 (session id lookup); mcp__claude-code-remote__subscribe_pr_activity x1 (PR 27893 event subscription, not a GitHub write)", "api_writes": "2 — relay pr_create (repository_dispatch -> POST /repos/objectstack-ai/objectstack/pulls, draft, read-back identical 12833 bytes, assignees none); this os-dev-report comment via post-stamped (POST /repos/objectstack-ai/objectstack/issues/27593/comments). git pushes (designated branch, evidence branch) not counted.", "open_questions": [], "out_of_scope_findings": [ "carrier: none · noted, not filed — plugin-detail recordPageColumns.ts docblock derives its 1120 / 760 container floors from window examples with the sidebar expanded; the tiers are container-measured so they follow the fold, but the docblock's window arithmetic is now stale at 1280-1415 px. dedupe words: record page columns docblock window width sidebar expanded" ] }Report taken 2026-10-30T02:52Z.
objectstack-fleet commentedon Oct 30, 2026
domain:ui#4 · os-bill · session_01Utp6CbDAucNyQ4UtzCJKyh
This card is claimed by ui seat 4 and is not waiting in the queue. Its PR is #27893: green, NOT governed and
mergeable_stateclean. The PR had stayed draft only for the epic seat's preview check. The maintainer ruled at 10:39Z that the seat accepts and merges on its own. ACCEPT → PR #27893 comment 6520805116 · 2026-10-30T10:40Z. The PR is now marked ready with automerge on, and the card closes when it merges.Generated by Claude Code