Skip to content

fix: wake creator on child turn completion - #685

Merged
pedronauck merged 3 commits into
compozy:mainfrom
carlosedsousa-dev:fix/spawn-wake-on-turn-complete
Sep 30, 2026
Merged

pedronauck merged 3 commits into
compozy:mainfrom
carlosedsousa-dev:fix/spawn-wake-on-turn-complete

Conversation

@carlosedsousa-dev

@carlosedsousa-dev carlosedsousa-dev commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Problem

When a spawned child session finishes its turn, it transitions to BadgeIdle and remains there — awaiting the next session_prompt — until the TTL expires (default 30min). In that state the creator is never notified.

dispatchSpawnWake (internal/session/spawn_wake.go:121) wakes the creator only for three badges:

  • BadgeStopped
  • BadgeFailed
  • BadgeWaitingForAuth, BadgeWaitingForInput, BadgeNeedsAttention

BadgeIdle falls through to default and returns false. BadgeDone — defined in internal/session/badge.go as idleEligibleBadgeInput(input) && input.Unseen, i.e. "child is idle and the creator has not yet read the result" — is also not in the list.

This breaks the contract dispatchSpawnWake was written for: it covers failure, hang, and attention requests, but not normal completion.

Reproduction

  1. Create an orchestrator agent with toolsets: [compozy__sessions].
  2. In a session, call compozy__session_spawn then compozy__session_prompt <id> "<briefing>" without wait: true.
  3. End the creator's turn.
  4. Observe: the child processes its turn and goes active/idle. The creator stays active/idle.
  5. grep session.spawn_wake ~/.compozy/logs/compozy.log — no session.spawn_wake.delivered line for that child.

Confirmed empirically: the only delivered lines in the log are for children that were explicitly stopped (session_stop), whose final badge is stopped. A child that finishes its turn naturally produces nothing.

Impact

Without a wake on normal completion, the async delegation pattern — the reason session_spawn exists — is not usable in practice. The parent is forced into one of two bad workarounds:

  • Block with session_prompt wait: true or session_wait, serializing what should run in parallel.
  • Poll session_list/session_events repeatedly, recreating the problem the wake exists to solve.

session_wait also has a 30-minute ceiling (WaitTimeoutMax), so long turns overflow and the parent loses the child.

Proposed fix

Add BadgeDone to spawnWakeReasonForBadge with a new SpawnWakeReasonCompleted:

// SpawnWakeReasonCompleted wakes the creator when a child finishes its turn normally.
SpawnWakeReasonCompleted SpawnWakeReason = "completed"

func spawnWakeReasonForBadge(badge Badge) (SpawnWakeReason, bool) {
	switch badge {
	case BadgeStopped:
		return SpawnWakeReasonStopped, true
	case BadgeFailed:
		return SpawnWakeReasonFailed, true
	case BadgeDone:
		return SpawnWakeReasonCompleted, true
	case BadgeWaitingForAuth, BadgeWaitingForInput, BadgeNeedsAttention:
		return SpawnWakeReasonNeedsAttention, true
	default:
		return "", false
	}
}

Why BadgeDone and not BadgeIdle

BadgeDone is the semantically correct candidate: in internal/session/badge.go it is derived from idleEligibleBadgeInput(input) && input.Unseen — "idle and the creator has not read the result yet". That is literally the signal the parent needs.

Plain BadgeIdle is more aggressive: it fires on any transition to idle, including after the parent has already read the result. BadgeDone as default; BadgeIdle only if there is a reason for redundant wakes.

Alternative considered and rejected — turn.end hook

I considered using the extension turn.end hook to detect completion and act. It is not viable, for structural reasons:

  • The hook executor is an ephemeral subprocess (internal/hooks/executor_subprocess.go): raw exec.Cmd, no HostAPI, no session, env allowlist of 16 vars.
  • The only output is stdout, captured by the daemon and capped at 1 MiB — nobody reads that channel.
  • Hooks are for patch, not wake: the pipeline is pipeline[P, R] with apply func(P, R) P — the handler can only mutate the payload it received, never inject a prompt into another session.

So the turn.end event does reach the extension (verified at internal/session/manager_hooks_prompt.go:108 → internal/daemon/hooks_bridge_agent_dispatch.go:74), but there is no channel for the extension to wake the parent. The fix has to come from the host.

Optional: opt-in flag

If preserving current behavior for extensions that do not want the completion wake is preferred, add a flag on SpawnOpts (e.g. NotifyOnTurnEnd) or extend the existing NotifyCreator (internal/store/session_lineage.go:17). Today NotifyCreator is a single boolean that toggles the whole wake — it does not distinguish "wake on failure" from "wake on completion". Happy to implement either form; flagging the trade-off explicitly.

References

  • internal/session/spawn_wake.go:108-119 — spawnWakeReasonForBadge, the root
  • internal/session/spawn_wake.go:121 — dispatchSpawnWake
  • internal/daemon/session_wake_bridge.go:53 — delivery via PromptSynthetic
  • internal/session/badge.go — BadgeDone and BadgeIdle definitions
  • internal/session/spawn.go:88 — Manager.Spawn
  • internal/hooks/executor_subprocess.go — hook executor (why the alternative does not work)

Environment

  • CompozyOS v0.3.0-beta.28, Linux
  • Subprocess extension over github.com/compozy/compozy/sdk/go v0.3.0-beta.28
  • Workspace with active daemon, orchestrator agent with toolsets: [compozy__sessions]

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

Summary by CodeRabbit

  • Bug Fixes
    • Completed child sessions now produce a valid completion wake reason, and session status badges map to their corresponding wake reasons. Attention transitions also deliver a wake. Canceled child turns do not emit a completion wake, while normal completed turns do. Idle sessions continue not to trigger a wake.

A spawned child session goes to the BadgeIdle state when its turn ends,
and stays there until the next prompt or TTL expiry. BadgeIdle is not in
spawnWakeReasonForBadge, so the creator is never woken when a child
finishes its turn normally — only on stopped, failed, or needs_attention.

BadgeDone is derived from `idle && Unseen` (internal/session/badge.go) —
exactly the signal "child finished, creator has not read the result yet".
Mapping it to a new SpawnWakeReasonCompleted closes the gap and makes the
documented async delegation contract work: parent spawns N children, ends
its turn, and is resumed as each child completes.
@vercel

vercel Bot commented Sep 29, 2026

Copy link
Copy Markdown

@carlosedsousa-dev is attempting to deploy a commit to the Compozy Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (2)
  • docs/_memory/change-impact.md is excluded by !**/*.md
  • docs/qa/scenarios/RT-session-spawn-wake.md is excluded by !**/*.md

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

⚙️ Run configuration

Configuration used: Repository: compozy/compozy/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 1da1d6db-51aa-4cf0-aad7-72e3faeecb05

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: compozy/compozy/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 53ca2401-dd2d-4ad8-b058-0cc606b49b1c

📥 Commits

Reviewing files that changed from the base of the PR and between f819e3b and f4458e5.

📒 Files selected for processing (3)
  • internal/session/manager_prompt_pump.go
  • internal/session/manager_transition_attention.go
  • internal/session/spawn_wake_test.go

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 6 remain after this review.


Walkthrough

The change adds a completed-session wake reason and maps BadgeDone to it. The prompt-pump transition suppresses completed wakes when the finishing turn remains marked canceled. Tests cover badge mappings, dispatch, and canceled and normal turns.

Changes

Completed Session Wake Delivery

Layer / File(s) Summary
Define and map the completed wake reason
internal/session/spawn_wake.go, internal/session/spawn_wake_test.go
SpawnWakeReasonCompleted is valid, and BadgeDone maps to it. Tests cover badge mappings and dispatch for a done badge.
Suppress completed wakes for canceled turns
internal/session/manager_prompt_pump.go, internal/session/manager_transition_attention.go, internal/session/spawn_wake_test.go
The prompt-pump transition checks whether the finishing turn was canceled. The attention-transition helper skips a completed wake when delivery is disabled and the resulting badge is BadgeDone. Tests compare canceled and normal turns.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Merge Risk: ⚪ Minimal · up to f4458

No merge-blocking issue is identified with completed child-session wakes or canceled-turn suppression.

Security Architecture Review

Security architecture risk: 🔵 Low · up to f4458

Completed child turns now wake their creator through an existing channel. No new external access path was established, but a failed delivery may leave the creator unaware of completion.

Retained concerns

  • Low · reliability · inferred: New completion notifications inherit an at-most-once reservation made before delivery succeeds. A transient notifier or bridge failure can leave the creator without a completion wake and prevent a same-event retry while the reservation remains cached.
Security review details

Security Blast Radius

  • inferred — The newly reachable effect is a synthetic prompt to the recorded, active creator session. The reviewed path does not establish a new externally callable endpoint or broader recipient selection.

Trust Boundaries and Controls

  • observed — The daemon bridge revalidates the event, admits delivery only while its workers are running, checks that the creator is active, and sends a synthetic prompt directing the creator to inspect the child.
  • observed — A completed wake carries no child-result detail from the badge-specific detail function. Wake text is redacted and length-bounded before inclusion in the synthetic prompt.

Resilience and Maintainability Implications

  • inferred — The existing deduplication prevents immediate duplicate dispatch, but its pre-delivery reservation also limits recovery from a failed completion wake. The reviewed tests cover normal and canceled completion, not successful retry after a delivery failure.

Hardening Proposals

  • proposed — If completion delivery is intended to survive transient failures, define a bounded retry or reconciliation policy together with downstream duplicate protection.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 8 functions across 4 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: notifying the creator when a child turn completes.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

[Medium risk] Adds a new wake signal when child sessions complete normally.

The PR appears safe to merge based on the reviewed changes.

Findings

  1. P2 Spawn-wake QA scenario is outdated ▶
Summary

The PR wakes a spawned child’s creator when a normal turn reaches BadgeDone and suppresses that completion wake for a canceled turn.

  • Adds completion delivery and cancellation regression tests.
  • Records cross-surface impact and updates the spawn-wake QA scenario.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Child turn settles] --> B[Publish catalog and attention edge]
  B --> C{BadgeDone?}
  C -- No --> D[Existing badge-based wake handling]
  C -- Yes --> E{Turn canceled?}
  E -- Yes --> F[Suppress completed wake]
  E -- No --> G[Dispatch completed wake to creator]
Loading

Reviews (3) · Last reviewed commit: "docs: record PR 685 impact and update sp..."

Comment thread internal/session/spawn_wake.go
Comment thread internal/session/spawn_wake_test.go
coderabbitai[bot]
coderabbitai Bot previously approved these changes Sep 29, 2026
Comment thread internal/session/manager_transition_attention.go
Comment thread internal/session/spawn_wake_test.go
coderabbitai[bot]
coderabbitai Bot previously approved these changes Sep 29, 2026
@pedronauck
pedronauck merged commit a15f541 into compozy:main Sep 30, 2026
42 of 45 checks passed
@pedronauck pedronauck mentioned this pull request Sep 30, 2026
pedronauck added a commit that referenced this pull request Sep 30, 2026
… 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]>
pedronauck added a commit that referenced this pull request Oct 1, 2026
… 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]>
pedronauck added a commit that referenced this pull request Oct 1, 2026
* docs: add shell rail v2 design board and implementation plan

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

* feat: light and dark token sets for the shell rail design system

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]>

* feat: pill actions, inverted primary, kbd and tooltip restyle

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

* feat: flush tiling defaults for the window manager

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]>

* feat: flat window frames with quiet trailing controls

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]>

* feat: persisted light, dark and system theme with a theme toggle

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

* feat: left rail replaces the bottom dock

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]>

* feat: two-level surfaces, pop menus, pill menubar triggers and quiet 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]>

* feat: Inter and Geist Mono type system with theme-scoped shadows

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]>

* feat: main and stack window arrangement

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]>

* feat: StateGlyph primitive and identity-well KindIcon tone

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

* feat: browser-style window tabs on a recessed deck

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]>

* feat: flat topbar with desktop dots and an All desktops button

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]>

* feat: per-theme DESIGN.md, contrast floors and theme-safe lint rules

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]>

* feat: composer card, pill filters, approval card and state glyph states

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]>

* feat: theme-aware toasts, loop editor, terminal and desktop windows

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]>

* feat: settings, onboarding, agents, workspaces and catalogs on the two-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]>

* feat: theme-aware identity ink, live token showcase and light wallpapers

Identity plates compute their ink against the active theme's surface, the design-system showcase reads token values live from the document, light wallpapers get proper washes, a flat desk wallpaper is added, and transcript, window-toolbar and count tokens land.

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

* feat: rail foot, compact tab bar, flat desk and empty-desktop card

The profile switcher, theme toggle and Settings move to the rail foot (and the compact tab bar's trailing edge); the desk defaults to a flat wallpaper with an empty-desktop card offering New session; the macOS title bar grows to 52px with centred traffic lights and Linux title-bar overlays follow the theme; the workspace overview drops its glass surfaces.

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

* feat: session transcript, composer and approvals in the shell rail language

The transcript and composer share a centred 760px column, consecutive tool calls sit in one sunken panel with a mint running ring that holds still while paused, the composer is the card variant with a round inverted send, approvals use the well + sunken command + pill decision anatomy, and session, terminal and todo states render through StateGlyph.

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

* feat: hairline seams, arrange presets and move window to desktop

Seams become a 9px hit strip around a 1px hairline that turns accent on hover, drag and focus, with Shift for 10% keyboard steps and a 220px pane floor. Window › Arrange offers Main and stack, Columns, Grid and Balance sizes; Move window to lists the other desktops by name. Tab states render through StateGlyph and root heads compose the KindIcon well. Also fixes island seams locked by split minimums that can no longer fit.

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

* feat: theme-following boot page and theme QA scenarios

The desktop boot page drops its dark-only literals for boot tokens that follow the theme; theme chrome colors are pinned to the rail token; new QA scenarios cover toggle persistence, system following the OS, light readability and native no-flash, and the native window controls scenario reflects the 52px topbar.

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

* feat: every primitive reads right in both themes

Sweeps all packages/ui stories in dark and light: form fields sit on canvas with hairlines, checked controls use the inverted primary instead of orange, insets move to sunken, skeletons, sparklines and chart tooltips stay visible in light, prose drops to the 14.5px body with neutral links, code sits on code-bg, the palette input gets a quiet variant, count badges use the count type, and the DayStackedBars flake is fixed at its root (recharts cold import).

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

* docs: design-system reference, glossary and copy for the shell rail

Rewrites the OS shell chapter and shell/core DS styles for the rail, flat topbar, tray desktops, gutterless tiling, browser tabs and both themes; updates GUIDE, PARITY and fonts across DS pages; adds Rail, Theme and Main and stack to the glossary and the theme, empty-desk and arrange copy.

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

* feat: command palette, bell, toasts and OS dialogs on the new surfaces

The palette gets a quiet query field, plain row glyphs, Kbd chord caps and a pop argument dropdown; toasts and the bell float on the pop surface; OS dialogs use sunken insets and pill actions; skill exposure health stops using orange.

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

* docs: QA scenarios, change impact and release note for the shell rail

Rewrites the shell, dock, pager, tiling and tab scenarios for the shipped shell, adds scenarios for the dock foot and compact tab bar, empty desktop, Inter type ramp, arrange presets, move to desktop and keyboard seam resize, records the change impact, and adds the release highlight with the SD-013 migration block for the new window-manager defaults.

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

* feat: tasks, loops, automation and home in the shell rail language

The task list becomes a real table with status groups, StateGlyph status, owner chips and container-query column drops; view switch and quick status pills are pill segments; kanban, inbox and dashboard move to card and sunken surfaces; loops, runs, the loop editor, job forms and the cron builder drop bordered canvas-soft boxes and orange selection; every task, loop and automation state renders through StateGlyph.

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

* feat: desktops overview on the desk with live layout thumbnails

The All desktops overview covers only the desk, with live layout thumbnails, keycaps for the desktop switch chords, needs-you dots, a New desktop card, per-card menus and grid keyboard navigation.

Also fixes two pre-existing bugs surfaced by the new shell: the window-manager status no longer flashes a false 'Can't save window layout' pill before the first socket attempt (new idle status), and the Sessions dock item works in Global scope by reading every workspace's sessions.

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

* feat: queue, session lists, terminal window and approvals on the new surfaces

The prompt queue becomes a sunken inset above the composer card, session rows and sidebar toggles use neutral selection, inspector and delete-dialog lists become sunken insets, attachments sit on surface-2, untitled terminals are named after their shell, terminal input requests use identity wells with an inverted Send, and the approval grants list is a card.

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

* feat: secondary pills everywhere, symbol key labels and a truthful palette highlight

Outline buttons become surface-2 secondary pills across settings, agents, workspaces, catalogs and the palette; shortcut bindings render as Kbd caps with a quiet override marker; every named key code gets its glyph in labels and aria-keyshortcuts; runtime speed switch and reasoning slider follow the inverted-primary controls; Settings heads become a root head.

Fixes a palette bug where the initial highlight could sit on a row far below the fold: the automatic highlight now follows the top result until the operator moves it, and restarts on open and on query change.

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

* feat: one head grammar for root, document and drill-in windows

The window head puts the overflow menu right after the title, drill-ins get a quiet back button with muted crumbs, master-detail apps keep their root head, snap previews and merge targets use a flat accent hairline with an accent pill, and the zoom menu's arrange row mirrors Window › Arrange.

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

* refactor: retire legacy surface roles from session and terminal surfaces

Outline buttons become secondary pills, the composer stop becomes a round secondary disc, fg-strong ink and badge/input/row/btn fills map onto the new ramp, and session stories frame components in the card grammar.

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

* fix: keep the marketing site's warm theme under the new primitives

Pins the site's previous palette, card/pop shadows, button label size, type roles and JetBrains Mono smoothing, and composes site-local classes for the header kbd, docs page-action menus, the provider tooltip and toasts, so the site renders as before (pixel diff ≤0.06% per page) apart from the accepted pill buttons.

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

* fix: no dead New session in Global scope and no bounce when switching to an empty desktop

Home, the empty-desktop card and the Sessions dock item share one New session control that is disabled with its reason in Global scope, and the dock item opens the workspace switcher instead of toasting. A bare '/' route now only reuses a dashboard on the active desktop, so switching to an empty desktop no longer bounces back. The compact tab bar gets roving focus.

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

* feat: session head with agent chip, bound provider and last activity

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

* feat: deliberate disabled buttons, neutral intensity meter and a solid idle terminal cursor

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

* refactor: retire the glass, dock and traffic-light token families

Deletes the shell glass, dock, traffic-light, blur/saturate and dead height tokens from both themes now that nothing consumes them, renames the workspaces edge fade onto the popover surface, and regenerates DESIGN.md and the font-size classes.

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

* feat: identity-well dialog heads, quiet close buttons and popup-first dialog focus

Entity dialog heads use the mint identity well with a quiet eyebrow through a shared dialog tone model (semantic danger/warning wells stay), close buttons use the quiet icon-button grammar, Dialog and Sheet focus the popup unless a consumer names an initial focus target (ConfirmDialog keeps the least-destructive action per the alertdialog pattern), and cn() merges the custom radius utilities.

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

* fix: truthful queue health and the rest of tasks, loops and automation in the new language

The tasks dashboard stops drawing an invented 24-hour queue history (the read model carries no series) and reports the current queue snapshot instead; LoopStatusPill becomes LoopStatusMark; outline buttons become secondary or quiet pills; running bands, priorities, live indicators, scheduler panels and inspect sheets drop non-attention orange; the window-head count only shows when it is exact.

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

* docs: loops catalog scenario names LoopStatusMark

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

* refactor: orange only for needs-you and no legacy surface roles across the web app

Maps every remaining legacy role (elevated, canvas-soft/tint, row/glaze/button/badge/input fills, fg-strong, highlight shadows) onto the new ramp across settings, workspaces, marketplace, runtime selector, onboarding, profiles, vault and the showcase, and reduces accent usage to needs-you, highlights and the brand mark; stories stage floating windows with the floating frame finish.

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

* refactor: delete dead tokens and the unused queue sparkline

Removes the button-default fills and canvas-tint, moves the window radius/shadow the marketing hero still uses into the site's own theme, points the hover alias at surface-2, and deletes QueueHealthSparkline now that no surface draws an invented queue history.

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

* refactor: finish the legacy-role migration and delete the last glaze tokens

Row hover/selected, surface glaze, input and badge fills move to surface-2, selected, sunken and the canvas field grammar across tasks, loops, automation, Home, the workspaces menus and the topbar, and the emptied token families leave both themes, the generator and the lint allowlists.

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

* fix: single rule before the terminal head actions

The OS head no longer draws a doubled rule before Stop: the status fragment only renders when it has chips, and the actions lead with their own rule only when chips precede them.

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

* fix: keep Save secret enabled while the vault create editor stays open

The create editor refetches the idle, stale inventory so collision protection stays fresh instead of disabling Save 15 seconds after the inventory loaded.

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

* fix: title untitled sessions as New session in the bell and toasts

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

* fix: plain-language inbox reasons, suspended windows no longer claim records are missing

Fixes found in the tasks/loops verification walk: inbox blocking reasons print words instead of wire codes, the failed band keeps the runtime error apart from the retry guidance, suspended task/loop/diff windows show loading instead of 'not found', approvals read as needs-you, and unreported metrics render an em dash. Scenario notes record the deliberate contract changes.

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

* test: type the untitled-session fixture; note the entity modal shell contract change

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

* feat: one filter representation per width, truthful loop stats and quieter rail values

Quick status pills and the Filter chip never show the same filter twice; loops with no runs read '— · No runs this month' without wrapping; the task rail's duration drops the mono id voice.

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

* feat: pane-width layouts, calm popover focus, neutral catalog glyphs and a quiet search clear

Popovers opened by pointer focus the popup instead of ringing their first control; catalog card glyphs default to neutral; SearchInput replaces the native blue cancel button with a quiet clear button; empty-disclosure rows, metric grids, section heads and run cards switch layout on their pane width (container queries) so narrow tiles on wide screens stay readable; listing toolbars never wrap inside the window toolbar strip; cn() merges named sizing tokens.

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

* fix: namespace-aware bare token utilities and no dead sizing classes

The prefer-bare-token-utility lint rule only suggests a bare utility when the token lives in the namespace that utility reads, and every dead named-size class it produced is made live with the right token or removed, restoring the 232px submenu floor.

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

* fix: head titles win over meta and typed confirms focus their input

Window heads shrink trailing meta and drop time, archived and origin before the title truncates (also keeps the task run overflow reachable at 375px); typed ConfirmDialogs open on the confirmation input; the symbol picker grid fills the profile dialogs, which gain the quiet close; stale scenario texts are corrected.

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

* refactor: drop the dead textarea min-height class

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

* fix: explain disabled arrange and move items, and make Balance sizes work

Arrange presets and Balance sizes are disabled with 'needs two windows on this desktop' when there is one window, Move window to stays visible and disabled with 'needs another desktop' on a single desktop, and Balance sizes now balances the focused group instead of sending a leaf node the daemon rejects.

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

* fix: desktop boot tokens follow the retired surface roles

The desktop main build read --color-surface-glaze and --color-btn-default-hover, which the token cleanup removed; boot surfaces now map to surface-2 and selected, with the card shadow on hover like the secondary button.

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

* fix: arrange whole tab frames and list every desktop in Move window to

Main and stack and Columns now arrange complete tab frames, keeping each deck's stack id, members and active tab (floating decks tile whole) instead of failing with 'no room' on an ordinary tabbed layout. Move window to lists every other desktop by name; desktops past the nine numbered slots move by id.

Found by the Codex review.

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

* fix: indicator contrast, light count legibility and a stable session list toolbar

Adds an indicator role so inactive desktop dots and the queued glyph clear 3:1 in both themes, tunes the light accent so white counts clear 4.5:1, strengthens light identity-well ink, gives chord separators and the search key cap the key type, and makes the session list toolbar and bulk selection bar share one explicit frame (the cn() sizing merge had shrunk the toolbar). Found by the Codex review and the QA walks.

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

* fix: tile agent terminals, cascade floating opens and read settings after registration

Agent-materialized terminal windows tile beside their session under beside_focus instead of floating over its controls; floating opens cascade off an identical rect instead of stacking exactly; an empty desktop tiles full frame when focus sits on another desktop; the window-manager settings read ignores an unattached client id instead of returning 500 (and logs 5xx reads), and the web waits for client registration before reading.

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

* test: update web and desktop e2e for the shell rail

Specs follow the rail foot Settings opener, Session-menu New session, the tray pager and desktop overview, quiet window controls, the 52px title bar, accessible names for the theme toggle and profile switcher, and pin new_window_policy only where a spec's invariant is floating; the dock magnification spec is retired. Also fixes the compact workspaces overview, whose hanging worktree menu sat under the dismiss band at 390px.

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

* refactor: name the zoom binding constant

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

* fix: keep layout.arrange's public meaning and tile-safe agent terminal 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]>

* fix: collapsible toolbar search and no false empty state in background 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]>

* fix: a minimized window's dock item always restores it

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]>

* test: format the arrange compatibility cases

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

* fix: reject keep_frames together with a layout resource

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]>

* fix: an engaged or disabled toolbar search never folds away wrongly

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]>

* docs: say CompozyOS, not Compozy, in design docs, tokens and scenarios

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]>

* refactor: split complex React functions flagged by React Doctor

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]>

* test: tasks responsive journey opens the folded search from its toggle

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

* test: grip windows only after their head actions settle

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]>

* test: pin floating policy for transport parity and wait for the right 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]>

* fix: lone arrange participants keep their shape and the cascade never 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]>

* fix: theme writes converge on the last choice and overview thumbnails 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]>

* feat: open new windows as tabs and add rail placement gestures

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]>

* feat: start a session from the empty desktop composer

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]>

* fix: rebalance surfaces, type and controls across the shell rail UI

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]>

* refactor: split complex React functions the polish sweep touched

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]>

* test: read the untitled bell session through the session catalog mock

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]>

* fix: keep launches where they started and announce running tools

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]>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
This was referenced Oct 9, 2026
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.

2 participants