Skip to content

feat: shell rail v2 with light and dark themes - #687

Merged
pedronauck merged 79 commits into
mainfrom
shell-rail
Oct 1, 2026
Merged

pedronauck merged 79 commits into
mainfrom
shell-rail

Conversation

@pedronauck

@pedronauck pedronauck commented Sep 30, 2026 •

Copy link
Copy Markdown
Member

What & why

CompozyOS gets the shell rail v2 redesign from docs/design/opendesign/shell-rail/ (board shell-rail-v2.html, DESIGN-NOTES.md, brand-spec.md, IMPLEMENTATION-PLAN.md), implemented across the daemon, @compozy/ui, the web app, the desktop shell, the docs and the official skill. The UI has a light and a dark theme. The rail and the topbar form one chrome surface around a rounded content panel, windows tile edge to edge inside it, and new windows open as tabs by default.

Polish round 2 (this update) answers the review of the first cut:

  1. Buttons, type sizes, icon sizes and colour hierarchy now follow one ladder everywhere (every web surface was swept).
  2. Cards finally lift off the pane: black chrome → dark panel → lighter cards with a hairline → popovers above them.
  3. Topbar and rail are one surface; the work area is a panel with a rounded corner instead of hairlines between the bars.
  4. The whole type scale moved down (body 14.5 → 13.5px, headings 17 → 15px, KPIs 24 → 20px).
  5. New windows open as a tab in the focused window. ⌥-click a rail icon for a split, ⇧-click for a new desktop, right-click for every destination.
  6. The composer reads clearly in both themes (the root cause was a disabled Send button fading the whole plate to 50%).
  7. Sessions are full width again, as on main.
  8. Tool calls are back to main's clean rows (no dark box).
  9. The session list sidebar is open by default, including for existing users.
  10. An empty desktop is a ChatGPT-style start: "What should we work on?" above the real session composer; sending starts a session there.

Screenshots (1600×1000, dark · light)

Dark Light
Empty desktop, dark Empty desktop, light
Empty desktop with a prompt, dark Empty desktop with a prompt, light
Session, full width with the list open, dark Session, full width with the list open, light
Agent detail, dark Agent detail, light
Tabs plus an Option-click split, dark Tabs plus an Option-click split, light
Rail right-click menu, dark Rail right-click menu, light
Command palette, dark Command palette, light
All desktops, dark All desktops, light
Settings as a tab, dark Settings as a tab, light
Home as a tab, dark Home as a tab, light

What changes for users

Shell

  • The dock is a 60px rail on the left. Its foot holds the profile switcher, the theme toggle and Settings. Below 960px it becomes a bottom tab bar with the same controls. Magnification and the dock's New session button are gone.
  • The topbar is flat, 52px tall and runs the full width. It shares one surface with the rail — no hairline between them. In the Electron app on macOS it reserves 84px for the traffic lights and centres them. The tray holds the desktop dots, All desktops, the bell and ⌘K.
  • The work area is a panel inset into that chrome: rounded top-left corner, a hairline on its top and left edges, flush with the window's right and bottom edges. Tiled windows clip to the corner.
  • Opening windows: a new window opens as a tab in the focused window; an empty desktop gets one full pane. On the rail, click focuses an app's window or opens it as a tab, ⌥-click opens it split beside the focused window, ⇧-click opens it on a new desktop, and right-click offers Open in new tab · Open in split · Open in new window (floating) · Open in new desktop · Go to tab. Sessions gets the same menu. Continue/Fork "New window" still opens as a split so the source stays visible.
  • Empty desktop: "What should we work on?" above the real session composer with an agent picker (the project's default agent). Enter starts a session on that desktop with the prompt as its first message; a failed start keeps the words. The unsent prompt is kept per project. In Global scope the composer asks you to pick a project.
  • All desktops covers only the desk, with live layout thumbnails lifted as cards, keycaps, needs-you dots and a dashed New desktop card.
  • Gutterless tiling: windows tile flush with a 1px hairline (9px hit area, orange while hovered, dragged or focused). Floating windows keep a hairline and an elevated shadow.
  • Browser-style tabs sit on a recessed deck; minimize, zoom and close are quiet icons at the end of the head. Window heads use one grammar and never carry an inverted primary: head actions are small secondary buttons and status is a dot plus text.
  • Window › Arrange offers Main and stack, Columns, Grid and Balance sizes; Window › Move window to lists every other desktop.

Sessions

  • The transcript and composer use the full pane width again, as on main.
  • Tool calls render as main's minimal rows.
  • The session list sidebar is open by default. Earlier saved state is migrated once, and an explicit close still wins across reloads.
  • The composer is a raised card with a readable placeholder, 28px toolbar controls and an inverted Send that only dims itself when empty.

Theme

  • Light, Dark or System in Settings › Appearance or from the rail; Dark is the default. Stored per browser (compozy.theme) and applied before first paint. The Electron shell follows the theme with no flash.

Design system (@compozy/ui, tokens.css, DESIGN.md)

  • Surface ramp (dark / light): chrome #0a0a0a / #f2f2f3 · pane #131313 / #fff · card #1a1a1a / #fff + hairline + shadow · popover #1c1c1c / #fff · sunken #0e0e0e / #f7f7f8 · hover #232323 / #f4f4f5 · chrome plates rail-hover / rail-selected. shadow-card and shadow-pop start with a 1px line ring, so cards, menus, dialogs, sheets and toasts read one step above what they cover.
  • Type: Inter 425/500/600 and Geist Mono. Ladder 11 · 12 · 12.5 · 13 · 13.5 body · 14 titles · 15 headings · 18 page H1 · 20 KPI · 24 hero. No Tailwind default sizes or raw px left in web/src or packages/ui/src.
  • Controls share one height ladder: buttons xs 22 · sm 26 · default 30 · lg 34 · cta 36; inputs and selects 30, compact controls, search and toolbars 26; icon buttons 22/26/28/32 with 12/14/16px glyphs. One inverted primary per view; head actions secondary sm; text links link; pressed states are a plate, never a ring; dialog footers use the default size.
  • Icons: lucide at one 1.75 stroke set once in UIProvider (the hand-drawn rail glyphs match it); 2 only for the send arrow and small check marks.
  • Status readouts are Pill form="plain" (dot + text). The Switch has a white knob on a mid-grey track in both themes.
  • Readable greys clear WCAG AA on every surface, including the new card, popover and chrome plates; the contrast suites run per theme.
  • Primitives: StateGlyph, the KindIcon identity well, the composer InputGroup, pop menus, two-level Card/Surface (sunken), quiet tables, container-query layout primitives. ToolCallRow.Group is deleted (tool rows are back to main).

App surfaces. Sessions, Tasks, Loops (runs, editor, DAG), Automation, Scheduler, Home, Settings, Onboarding, Agents, Workspaces, Profiles, Marketplace, Extensions, Knowledge, Vault, Terminal, the command palette, the bell, toasts and dialogs were swept onto the ladder in both themes: cards on bg-card, one primary per view, plain status, aligned control heights, no ad-hoc sizes.

Marketing site (packages/site) keeps its own warm theme and fonts (D9).

Bugs fixed along the way (each with a regression test)

  • Main and stack / Columns failed with "no room" on a tabbed layout. They now keep decks whole through the additive keep_frames option.
  • Balance sizes sent a leaf id and did nothing.
  • Switching to an empty desktop bounced back to the previous one.
  • The Sessions dock item did nothing in Global scope.
  • The window-manager status flashed a false "Can't save window layout" pill at every startup.
  • Reading window-manager settings with an unregistered client id returned 500.
  • Agent terminals floated over the tiled session. Floating windows opened exactly on top of each other. beside_focus fell back to floating when focus was stale.
  • A seam could lock on adaptive islands.
  • The palette's initial highlight could sit below the fold.
  • The vault's Save button went dead after 15s, and deleting the inspected secret reopened it as "not found".
  • The tasks dashboard drew an invented 24h queue history.
  • Suspended windows claimed that records were missing.
  • Wire codes leaked into the inbox reasons.
  • Clicking the dock item of a window minimized from zoom did not restore it.
  • The terminal head drew a doubled rule.
  • Snapping a tile stored 0.50036 instead of 0.5 whenever the desk width was odd.
  • After ⌘W on a deck's active tab, focus fell back to a hidden tab, so a second ⌘W closed the wrong tab.
  • The composer faded to 50% whenever Send was disabled (the shared InputGroup dimmed on any disabled child).
  • The "Save the current layout" tile rendered as a stadium; settings rows built from fragments lost their spacing ("dev● Unsupported").

How you verified it

  • make gate: all affected lanes pass (see the checklist below).
  • Web E2E: the full non-nightly suite ran on the integrated tree under the machine verify lock — 272 passed, 3 skipped (environment-dependent gateway legs), 11 failed on the first pass. Every failure was triaged: 3 product regressions fixed with Go/unit regression tests (tile fraction, hidden-tab focus, Continue/Fork placement) and stale selectors from intentional contract changes (the removed empty-desktop button, tab-default closing). The final rerun on fresh builds passed 82/82, plus a new journey on the shipped tab default (rail click → one frame, two tabs; ⌥-click → split).
  • Real-app walks on isolated QA labs (daemon from this branch + demo seed), dark and light: empty desktop → session creation, rail click/⌥/⇧/right-click, session width/sidebar/tool rows/composer, agent detail, settings, palette, overview, dashboard.
  • Independent review: Claude Code (Fable 5.1, high effort) via herdr reviewed the whole diff against the 10 asks — 0 high, 9 medium, 18 low plus a coverage list; all fixed, and a confirmation round verified every item (10/10 asks delivered, nothing regressed). The first cut was also reviewed three times by Codex (GPT 6.1 Sol, xhigh).
  • Unit and contract coverage: per-theme token contracts (AA text floors on every surface incl. chrome plates, focus rings, 3:1 indicators), the tab policy and floating open in the window-manager reducers, the sidebar-state migration, the empty-desktop composer (per-project drafts, send, failure, Global scope), rail gestures and menu, the terminal bridge and the routing coordinator.

Impact

  • Public config defaults ([window_manager], SD-013 public regime, no key renames): gaps.* 8/10 → 0, new_window_policy floating → tab (new value; beside_focus and floating remain), bindings.bottom_center reserved → zoom. Explicit user values win. The release note carries the migration block with the old values.
  • Additive public surface: new_window_policy = "tab"; an optional floating flag on window.open (HTTP/UDS DTO, OpenAPI, generated web types, compozy__window_open, and compozy window open --floating); the main_stack arrangement and the optional keep_frames flag on layout.arrange. New palette ids layout.arrange.main-stack, layout.arrange.columns, window.move_to_desktop.1..9, all unbound.
  • User state: layouts reflow once from normalized rects; the session sidebar preference migrates once (the incidental open:false is dropped, collapsed threads are kept); the old global empty-desktop draft key is discarded.
  • Docs: config, CLI and window-management pages on the site, skills/compozy/references/{window-management,native-tools}.md, DESIGN.md, the DS reference, COPY.md, the glossary, docs/_memory/change-impact.md, the release note and the affected QA scenarios.
  • Internal (deleted, no aliases): dock magnification, the bottom dock band, traffic-light chrome, glass/dock/glaze tokens, ToolCallRow.Group, --container-transcript, --font-weight-display, --size-deck-add, QueueHealthSparkline, LoopStatusPill.

Follow-ups found during QA (out of scope, pre-existing)

  • internal/loop/timeline.go:381 — loop timeline headline leaks the wire status token ("Run is now needs-approval"). Pre-existing, daemon copy, unrelated to the shell redesign.
  • packages/site docs page-action menus ("Open with AI"/view options) and "Copy as Markdown" toast don't open under headless automation, even on live compozy.com — needs a manual check.
  • Tasks status facet counts are computed after the status filter (backend); the UI only shows exact counts.
  • New session in Global scope (Session menu / ⌘N) only toasts "Select an active workspace…" — product decision on Global session creation. Shell surfaces now show a truthful disabled state.
  • Adaptive tiling islands have no free edges by design (group-projection NO_EDGES_RESIZABLE).
  • Daemon: a steer that interrupts a turn while it waits on a permission leaves the interaction 'pending' forever (Allow once fails, CLI approve says 'pending permission not found', phantom needs-you badges). Repro: ask pending → session prompt <same session> '<text>' (interrupt_fallback). Evidence in the QA walk report. Pre-existing, session runtime.
  • ⌘T New tab → "Open in this tab" records a closed entry for the blank new-tab placeholder (pickDestination closes it with a normal tab close → reducer_window_close pushClosedEntry), so ⌘⇧T walks back through blank placeholders. Pre-existing since feat: integrated terminal — runtime-owned shells for people and agents #490; proper fix needs a discard flag on CloseWindowCommand (wire change + codegen).
  • Daemon notification titles fall back to the raw session id for untitled sessions (internal/api/core/attention_notification_sources.go:139); the web now titles them "New session", but the daemon source should stop emitting ids as titles.
  • Seed/read-model: tasks dashboard status breakdown ("In progress 1") disagrees with the list (0 in_progress rows).
  • Product call: a no-op single-round Loop renders "Rounds 1 / 1" in danger (the at/over-cap rule reads alarming).
  • Daemon static server (internal/api/httpapi/static.go) serves index.html without Cache-Control and with Last-Modified = daemon start time, so browsers heuristically cache the SPA shell; after a bundle swap the cached index points at deleted hashed chunks → blank app. Suggested: index.html 'Cache-Control: no-cache', hashed /assets immutable. Pre-existing.
  • CLI config set on quoted keys like window_manager.shortcuts."palette.open" fails with a duplicate-key error once the key exists; config set + config reload doesn't push to open web clients.
  • A launcher click during a daemon outage is dropped silently.
  • Window-manager clients are never pruned (48 stale registrations in the lab); switching to a long-gone client id succeeds silently. Backend decision.
  • A workspace .compozy/config.toml that pins the whole [window_manager] section silently shadows user-scope changes from the CLI/Settings.
  • Workspace switch: the outgoing desktop's windows re-render against the incoming runtime workspace before the desk swaps, so e.g. a Loops run window fetches the run under the wrong workspace (404 "Couldn't open this run"). Window apps should read their window's own workspace (stamp ?workspace= on deep-link routes). Pre-existing (reproduced on main a15f541).
  • Same-tab full navigation leaves the previous document in Chrome's back/forward cache holding its SSE sockets, starving the 6-per-host HTTP/1.1 pool so deep-link window.open stalls. Close/reopen EventSources on pagehide/pageshow (web/src/lib/ticketed-event-source.ts). Pre-existing.

⚠️ Needs a human

  • The formal /qa-execution verdict pass over the changed scenarios (user-invoked only). Agents walked the scenarios and fixed what they found, but did not record verdicts.
  • One look at the terminal on a real retina display (headless Chromium paints the xterm WebGL canvas blank at DPR 2).

How this was built

The controller (Claude Opus 5.5) orchestrated Claude Code Opus 5.5 workers on this branch. Round 1: ten herdr workers (tokens, theme, go-wm, shell, frame, prims and three surface sweeps) plus three Codex reviews. Round 2: a written polish contract and six Opus workers — foundation, shell, window-open, session, empty-desktop and a settings sweep — in three waves (fixes, surface sweep, review fixes), the full web E2E suite, and an independent Claude Code Fable 5.1 (high) review with a confirmation round. The controller verified every slice in a real lab and committed.


  • make gate passes locally; this PR is delivered only after its required CI checks are green
  • New or changed behavior is covered by tests, or I explained above why not
  • If an agent wrote or co-wrote this, I named it above and verified the result myself

🤖 Generated with Claude Code

@vercel

vercel Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
compozy-site Ready Ready Preview Oct 1, 2026 12:33am UTC

Request Review

@coderabbitai

coderabbitai Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Walkthrough

Warning

Review details and warnings were omitted to fit the comment limit.

@github-actions

github-actions Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

React Doctor found no new issues. 🎉

Reviewed by React Doctor for commit 55c2b7f.

@greptile-apps

greptile-apps Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 2/5

[High risk] Rebuilds desktop shell UI and window management with theme support.

The PR does not appear safe to merge until delivery recovery and Sessions dock activation handle these failure and timing cases.

Findings

  1. P1 Failed Read Blocks Session Launch ▶
  2. P1 Session opens on wrong desktop ▶
  3. P2 Failed launch leaves empty desktop ▶
Summary

The PR redesigns the desktop shell and themes and, since the previous review, adds substantial session-catalog, managed-delivery, recovery, and desktop integration work.

  • Session catalog reads now support bounded pages, facets, and a fresh lookup for the Sessions dock.
  • Managed worktree delivery adds reviewed commit scope, durable journals, and restart recovery.
  • The remaining concerns are recovery of an unreadable journal and two Sessions dock launch states.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart LR
  Dock[Sessions dock click] --> Catalog[Catalog lookup]
  Catalog --> Jump[Session workspace jump]
  Journal[Delivery journal inventory] --> Recovery[Recovery and receipt repair]
  Recovery --> Delivery[Commit, push, draft PR]
Loading

Reviews (5) · Last reviewed commit: "fix: keep launches where they started an..."

Comment thread internal/windowmanager/reducer_layout_arrange.go Outdated
Comment thread desktop/src/window/window-theme.ts Outdated
Comment thread web/src/systems/os/components/desktop-layout-thumbnail.tsx
Comment thread internal/windowmanager/reducer_window_open.go Outdated
Comment on lines +58 to +63
onCreated: session =>
jumpToSession({
sessionId: session.id,
agentName: session.agent_name,
workspaceId: session.workspace_id,
}),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Session opens on wrong desktop If someone sends a prompt from an empty desktop and switches desktops while the session is being created, this callback opens the session without the originating desktop ID. The window manager then uses the currently active desktop, so the new session appears somewhere other than where the prompt was sent.

Comment on lines +69 to +70
ports.manager.switchDesktop(desktopId);
return ports.coordinator.userOpen({ ...fresh, desktopId });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Failed launch leaves empty desktop “Open in new desktop” switches to the newly created desktop before checking whether the window opened. If the open fails, the operator remains on an empty desktop with no window, rollback, or launch-failure message.

@greptile-apps

greptile-apps Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

Comments Outside Diff

These findings could not be posted inline.

  • P1 Delayed Launch Reverses Workspace Switch web/src/systems/os/hooks/use-desktop-dock.ts:155 ▶

    If someone clicks the Sessions dock item and switches projects while the catalog request is pending, the callback can still jump to a session in the project they left. The jump handler switches the active workspace back, overriding the user's newer choice.

  • P2 Running status is not announced web/src/components/assistant-ui/session-live-tool-row.tsx:38 ▶

    The new live-tool line is plain text with no status live region, replacing a tool row whose status icon announced “Running.” Screen-reader users no longer receive that announcement when a tool starts running; the visual shimmer does not convey it to them.

    Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

pedronauck and others added 17 commits September 30, 2026 17:15
Re-values the surface ramp, hairlines, text, signals and shadows to the v2 brand spec, adds a [data-theme=light] token set, the new sunken/surface-2/selected/well roles, inverted primary, card/elevated/pop shadows and the rail shell geometry.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
window_manager.gaps default to 0, new_window_policy defaults to beside_focus, and bindings.bottom_center defaults to zoom now that the dock no longer owns the bottom edge. Explicit user values keep winning.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Tiled frames drop radius, border and shadow; floating frames keep a hairline and elevated shadow. Traffic lights become minimize/zoom/close icon buttons at the trailing edge, and root window heads show the identity well.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
The shell becomes a grid with the topbar across the top and the rail beside the desk. The dock band reservation, dock magnification, dock separators and the dock New Session control are removed; the pager moves into the topbar tray.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…tables

Cards and surfaces sit on canvas with the card shadow, Surface gains a sunken variant, menus and popovers use the pop shadow with 32px items, the table header is a muted label row, and the link variant stops using the needs-you orange.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Swaps Geist/JetBrains Mono for Inter and Geist Mono, moves the weight ladder to 425/500/600 and the body to 14.5px, retunes radii and control heights, lifts muted greys to WCAG AA, routes shadows through theme adapters so the light theme reaches shadow utilities, and themes Storybook by data-theme. The marketing site pins its previous type baseline.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Adds the main_stack arrangement to layout.arrange (first window takes a 60% main column, the rest stack in the side column), the unbound layout.arrange.main-stack palette action, tool schema, OpenAPI, CLI help, docs and the official skill reference.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
The deck becomes a sunken strip whose active tab is a surface plate with concave feet fused to the head; inactive tabs are split by hairlines that hide next to hover and the active tab. Snap defaults mirror the flush daemon defaults and deck gestures wait for the measured, rail-offset origin.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
The menubar becomes a flat 52px topbar that reserves the macOS traffic-light space in Electron, drops the Settings control, and hosts the pager dots (with an orange dot for an off-screen desktop that needs you) next to an All desktops button that toggles the overview.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
The DESIGN.md generator emits dark and light columns and resolves theme adapters; contrast suites run per theme; the glaze and focus-ring lint rules reject theme-blind white or black ink; the marketing site keeps its orange CTA.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Adds the InputGroup composer variant with tool and send buttons, quiet and segment Button options, pill filter chips, StateGlyph failed/stopped/still, ToolCallRow.Group inset panels with a mint running ring, the approval-card Dock anatomy, KindIcon glyph nodes, Panel on the card surface, RouteNav as pill segments and 15px count badges.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
The renderer reports its theme preference to the desktop shell, which persists it and sets nativeTheme.themeSource before any window exists so every page, including boot, paints the right theme with no flash. Toasts, the loop editor canvas, the cron builder, brand marks and the running terminal follow the resolved theme.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…o-level surfaces

Bordered canvas-soft panels become canvas cards with the card shadow, insets move to the sunken surface, selection turns neutral instead of orange, onboarding drops the orange progress rules, and agent/worktree states render through StateGlyph.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
pedronauck and others added 19 commits September 30, 2026 17:21
…l anchors

layout.arrange keeps treating every named window as its own participant; the shell's deck-preserving arrangement is an additive keep_frames option co-shipped through the contract, OpenAPI, generated types, CLI --keep-frames, the compozy__layout_arrange tool, docs and the official skill. Agent terminal windows anchored to a session inside a floating frame take the floating fallback instead of failing the open.

Found by the second Codex review.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…d task windows

ListingToolbar search collapses to an icon toggle that opens the real field instead of shrinking to a clipped input, and task list/dashboard windows whose live data is suspended read as loading instead of 'No tasks yet'.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
The dock no longer takes the minimize branch for a target the topology already reports as minimized when the client frame still lists it as focused, so the hollow-ring click restores the window (VC-06).

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
The manager's shared resource/inline-field conflict guard now rejects keep_frames with resource_id, so HTTP/UDS, the tool and the CLI agree and execute matches preview. Found by the third Codex review.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
The collapsible search keeps the field while it is focused or holds a query (read from the live value, controlled or uncontrolled), remounts an uncontrolled field from its live value, and forwards disabled to the toggle. Found by the third Codex review.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Fixes the product-language check: design notes, DESIGN.md prose, token comments, the lint rule message, the site stylesheet comment and QA scenarios now use CompozyOS or drop the brand where the sentence is about the accent colour.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Extracts sub-components, pure helpers and sub-hooks from the 38 functions React Doctor flagged as too complex on this branch (useTasksPage and useTaskDetailPage split into focused hooks with identical view-models), returns a fragment from the session work-entry list, and exposes aria-pressed instead of aria-selected on session rows only while a selection is open. Also carries the remaining CompozyOS wording fixes in TS sources.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
E2E-030 measured the Marketplace head's drag spacer before the route published its Refresh/Add actions, so under CI load the pointer landed on the Refresh tooltip trigger and no drag started. The spec now waits for the source head to settle and grips a midpoint that is stable across frames.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
… spawn wake

TestUDSTransportWindowManagerMatchesHTTP now sets new_window_policy=floating before its journey, since its floating-stack premise no longer holds under the beside_focus default. E2E-006 waits for the needs-attention wake instead of the first wake; the 'completed' child wake from #685 arrives first, which kept this test red on main.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
… lands on a window

A single arrange participant returns its own frame under every arrangement, so a lone deck keeps its stack identity with keep_frames. Floating opens walk distinct cascade candidates that restart on the next diagonal before clamping at the desktop edge, so a new window never sits exactly over an existing one. Found by Greptile.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
… show floating decks

The desktop shell serializes theme preference writes latest-wins so desktop-theme.json always ends on the last reported choice, and All desktops thumbnails render floating tab decks as floating tiles instead of dropping them. Found by Greptile.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
New windows now join the focused window as a tab (new default
`new_window_policy = "tab"`); an empty desktop still gets one full pane.
The rail keeps focus-first clicks and adds Option-click (split beside
focus), Shift-click (new desktop) and a right-click menu with every
destination, including an explicit floating window (`floating` on
window.open, `compozy window open --floating`, `compozy__window_open`).

Fixes found by the E2E pass on the new default: tile snaps store the
exact zone fraction on odd desk widths, fallback focus after a close
lands on the tab the frame shows, and Continue/Fork "New window" keeps
opening as a split so the source stays visible.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
An empty desktop now shows "What should we work on?" above the real
session composer, with an agent picker that defaults to the project's
agent. Sending creates the session with the prompt as its first
message and opens it on that desktop; a failed start keeps the words,
an unresolved agent hands off to the New session dialog, and Global
scope asks for a project. The unsent prompt is kept per project, and
the old global draft slot is discarded.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Answers the review of the first cut of the shell rail redesign:

- Surfaces: black chrome, a darker pane, cards one step lighter with a
  hairline, popovers/dialogs/sheets above them; chrome gets its own
  hover and selected plates.
- Shell: topbar and rail are one surface around a rounded inset panel.
- Type moves down one step (body 13.5, headings 15, KPIs 20) and every
  web surface uses the semantic ladder.
- Controls share one height ladder (buttons 22/26/30/34/36, inputs 30,
  compact and toolbars 26, composer row 28), one inverted primary per
  view, plain dot-and-text status, plate-only pressed states and one
  1.75 icon stroke.
- Sessions: full-width transcript and main's tool rows again, the
  session list open by default (older saved state migrates once), and
  a composer that no longer fades when Send is disabled.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
React Doctor flagged five functions whose spans the sweep edited
(SecretField, SplitPane, AgentHeartbeatOps, SettingsInlineSaveControls,
WorktreeRefSelect) for high control-flow complexity. Their branches
move into small subcomponents and pure helpers; props, copy, data-slots
and test ids are unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Main moved useOsAttention onto useSessionCatalog; the shell-rail test
for untitled session titles still mocked useSessions.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Review findings on #687:
- An empty-desktop prompt now opens its session on the desktop it was
  sent from, even if the operator switches desktops while the session
  is created (`useAttentionJump` accepts an explicit `desktopId`).
- "Open in new desktop" returns to the previous desktop and removes the
  new one when the open fails, and says so.
- The live tool row announces "Running …" as a polite status; the
  parallel count is announced from a sibling status outside its button.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
});
return {
sessions: query.data?.pages[0]?.sessions ?? [],
ready: workspaceId !== null && !query.isLoading,
ready: !pending && !query.isLoading,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Failed read blocks session launch After an initial catalog request fails in Global scope, ready becomes true even though no sessions were fetched. The dock treats the empty result as “no sessions” and asks the user to pick a project before its fresh lookup can run. Existing sessions therefore cannot be opened through that dock click.

@pedronauck
pedronauck merged commit f420e57 into main Oct 1, 2026
48 of 50 checks passed
@pedronauck
pedronauck deleted the shell-rail branch October 1, 2026 03:58
This was referenced Oct 9, 2026

This branch was successfully deployed

1 active deployment
Preview — 55c2b7fc Deployed Oct 1, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant