Skip to content

feat: parent and child sessions - #327

Merged
pedronauck merged 2 commits into
mainfrom
child-sessions
Aug 6, 2026
Merged

pedronauck merged 2 commits into
mainfrom
child-sessions

Conversation

@pedronauck

@pedronauck pedronauck commented Aug 6, 2026 •

Copy link
Copy Markdown
Member

Session sidebar + parent-session provenance

Implements docs/design/opendesign/session/session-sidebar.html: the session window grows an in-window sessions rail hosting the same catalog body the dock's Sessions surface uses, and sessions created from inside another session now record that origin as queryable lineage — rendered as nested "provenance threads" in both the sidebar and the dock modal.

The prototype proposed a new session.parent_id backend field. Recon showed the field already ships as part of the safe-spawn lineage record (lineage.parent_session_id / root_session_id / spawn_depth, migrated and indexed in sessions since the safe-spawn work), so this PR adds no schema migration. The real gaps were: (1) the everyday agent path (compozy__session_create) dropped the caller's identity on the floor, (2) validation forbade a parent on non-spawned sessions, (3) no read-side filters existed, and (4) nothing in the web rendered the hierarchy.

Demo

session-sidebar-demo.mp4

Product decisions (locked before implementation)

Decision Choice
Parent semantics Provenance-light: a session created from inside another session records parent_session_id/root_session_id/spawn_depth but stays type: "user" — no TTL, no auto-stop, no spawn budget, no permission narrowing. Governed children still come exclusively from compozy spawn / POST /api/agent/spawn, whose semantics are untouched.
Recording paths Auto + explicit: the native tool always links the server-minted caller; CLI/HTTP accept an explicit parent; HTTP/UDS calls carrying validated agent-identity headers infer the caller when no explicit parent is given.
Window behavior on row click In-place switch with dedup: the current window retargets to the clicked session; if that session already owns a window, that window is focused instead (preserves the ≤1-window-per-session invariant).
Sidebar default Closed (transcript stays full-bleed); topbar toggle mirroring the inspector pattern, preference persisted in localStorage compozy:session:sidebar:v1.
Read surface Full filters: parent= / root= on the catalog across HTTP, UDS, CLI, and the native tool.

Invariants resolved during implementation: provenance links never cross the workspace boundary (explicit cross-workspace parent → validation error; native-tool cross-workspace create stays unlinked); the spawn reaper never touches user-type sessions regardless of lineage; depth chains server-side (depth = parent.depth + 1, root inherited); lineage is immutable post-create, so cycles are impossible.

Backend

Provenance lineage on the create path

  • internal/session/manager_start_storage.go — normalizeCreateLineage now accepts a parent on user sessions (rule was "only spawned may have a parent"; now "spawned or user"). New prepareProvenanceLineage resolves the parent once, enforces same-workspace, and fills server-owned root_session_id/spawn_depth from the parent's tree — caller-supplied root/depth are overwritten, so they can't be forged. validateProvenanceLineageShape rejects any governance field (TTL, auto-stop, spawn role, budget, permission policy) on a provenance link with ErrValidation.
  • Missing parent propagates ErrSessionNotFound (→ 404); shape/workspace violations map to 400. Coordinator/system/dream sessions still cannot carry a parent.

Creation surfaces

  • Native tool (internal/daemon/native_session_mutation.go): compozy__session_create links scope.SessionID — the hosted-MCP-bound, nonce+PID-validated caller — as parent whenever the target workspace equals the caller's. Not a tool input, so an agent can neither forge nor suppress it. Cross-workspace creates (reachable only under approve-all permission mode; the registry denies them by default) stay unlinked rather than failing, preserving the existing capability.
  • HTTP/UDS (internal/api/contract, internal/api/core/handlers.go): CreateSessionRequest.parent_session_id; when absent and X-Compozy-Session-ID/X-Compozy-Agent headers resolve to a valid active caller, the caller becomes the parent. Invalid claimed identity fails the create (401/403) instead of silently proceeding.
  • CLI (internal/cli/session_create.go): compozy session new --parent <id>.

Catalog read side

  • parent= (direct children) and root= (whole tree, root row included — roots store root_session_id = own id) filters threaded through session.ListQuery → store.SessionCatalogPageQuery → SQL clauses, plus the active-row matcher, with both filters bound into the list-cursor fingerprint so cursors can't be replayed across queries.
  • Exposed on GET /api/sessions (HTTP+UDS, OpenAPI spec updated), compozy session list --parent/--root, and the compozy__session_list input schema (descriptor digest regenerated). The CLI human/toon table gains a Parent column; -o json/-o jsonl already carried full lineage.

Window-manager retarget (daemon)

  • NavigateWindowCommand gains an optional instance_key (internal/windowmanager/commands.go, contract payload + strict decode). The reducer applies it on replace-mode only (push+retarget is ErrInvalidCommand), re-keys the window, and resets the nav stack — a stale cross-instance back-stack would otherwise let a breadcrumb pop resurrect a duplicate window for a session that moved elsewhere.

Web

Shared session list with provenance threads

  • SessionsModalBody (previously module-private inside sessions-modal.tsx) is extracted into web/src/systems/session/components/session-list/ — one file per responsibility (session-list.tsx, -row, -thread, -group, -archived) — and the dock modal becomes a thin Dialog wrapper around it. The modal and the new sidebar render the exact same body, so nesting shipped in both for free.
  • lib/session-hierarchy.ts (ported from the tasks subtask tree): buildSessionTree nests a session under its loaded parent and keeps orphans as roots so nothing vanishes behind pagination; collectThreadSessions flattens deep chains into their root's thread (one visual nesting level, per the design grammar); childSessionSignalTone surfaces the most urgent child state (failed > waiting > running) on a collapsed thread's toggle so escalations never hide behind the fold.
  • Thread anatomy per the prototype: children nested behind a hairline connector, parent chip with count + rotating chevron + collapsed-state urgency dot, current session marked with the accent left bar + aria-current. One deliberate delta: the chip lives in the row's trailing action cluster rather than inside the row button — the prototype nested a role="button" inside a <button>, which violates the accessibility floor.
  • Filter keeps thread integrity: a child matching the query keeps its parent visible. The "Recent" back affordance moved into the All pane (as in the prototype) so both hosts share it.

The sidebar

  • SessionSidebar (session system, presentational): 264 px left rail on the window's rail surface, filter + Recent ⇄ All panes + agent groups + "New session" footer (wired to the existing create flow). Width-animated open/close honoring motion-reduce, inert while closed so keyboard order skips hidden content.
  • Closed by default; toggle in the session topbar actions using the dock's sessions icon (SquareTerminal) so it doesn't read as a twin of the inspector's PanelRight. Open preference is one global flag; per-thread collapse is shared with the dock modal (both from the persisted @xstate/store at compozy:session:sidebar:v1, cross-tab synced — same pattern as the inspector store).
  • Data rides the shell's existing catalog query key (useSessions with the same filters), so the rail is cache-warm on first open and live through the existing session_catalog_changed stream — no new SSE connection.

In-place switch with dedup

  • RoutingCoordinator.userRetarget (the only URL↔WM bridge): if the target instance already owns a live window → userActivateWindow (focus, one history push); otherwise WindowManagerRuntime.retargetWindow dispatches the extended window.navigate and one history entry follows. SessionWindow derives its session from the window route, so the retargeted window re-renders onto the new session with the prompt runtime remounting via the existing key={id}.

Cross-surface

  • Official skill (skills/compozy/references/): runtime-operations.md documents provenance semantics + parent/root catalog queries; native-tools.md documents the auto-link on session_create and the new session_list inputs.
  • Site docs: packages/site/content/docs/sessions/lifecycle.mdx gains a "Creation provenance" section (provenance vs governed spawn, all three recording paths, filters); CLI reference pages regenerated via make codegen.
  • Open Design: session/ added to the opendesign README index; SESSION-REDESIGN-SPEC.md gains a §12 addendum recording shipped decisions and authorized deltas vs the prototype.
  • QA tracker: new content-addressed scenarios RT-session-parent-provenance and ET-web-session-sidebar-threads; ET-web-sessions-catalog-modal, ET-web-session-thread-full-bleed, ET-web-session-inspector-toggle reset and re-walked.

Compozy Impact Audit

  • Native tools: compozy__session_list input schema gains parent/root (descriptor digest + native-tool-catalog.json regenerated); compozy__session_create behavior documented (auto-link, not an input — schema unchanged); catalog testdata co-shipped.
  • Extensibility and hooks: no new hook events; spawn hook taxonomy untouched. Extension host API sessions.create keeps SessionTypeSystem with no caller session — checked, no impact. WM command protocol extended (window.navigate.instance_key), strict-decoded.
  • Workspace data isolation: provenance links are same-workspace by construction (explicit cross-workspace → error; native-tool cross-workspace → unlinked); parent/root filters compose with the existing workspace scoping; list-cursor fingerprints bind the new filters. Payloads already carried lineage — no new cross-workspace read path.
  • Official Compozy skill: updated (runtime-operations.md, native-tools.md).

Verification

  • Go: new/extended cases in the canonical suites — manager_lineage_test.go (provenance rules, governance rejection, cross-workspace, chaining), query_test.go + global_db_session_index_test.go (lineage filters, active + durable layers), spawn_reaper_test.go (provenance sessions never reaped), native_tools_test.go (bound-scope auto-link, operator stays unlinked), lifecycle_test.go (retarget re-keys + resets nav stack; push+retarget rejected), plus a provenance leg added to the UDS↔HTTP transport-parity integration suite (explicit + identity-inferred create, filter parity across transports). Touched packages green under -race (~7.3k tests).
  • Web: session-hierarchy unit suite; sessions-modal suite extended with nesting + thread-integrity-filter cases; routing-coordinator suite extended with retarget + dedup cases; full web lane green (538 files / 4366 tests), make bun-lint + typecheck clean; new Storybook story systems/session/components/SessionSidebar (threads + collapsed-signal variants).
  • Codegen: make codegen co-shipped (openapi/compozy.json, web/src/generated/compozy-openapi.d.ts, tool catalog, CLI docs); make codegen-check clean.
  • Live QA (isolated lab, eng-qa-bootstrap, teardown clean: true): RT-session-parent-provenance walked over real CLI/HTTP — explicit parent, depth chaining, filters, Parent column, missing-parent and cross-workspace rejections, identity-header inference. ET-web-session-sidebar-threads + the three reset web scenarios walked in the browser against the lab daemon — closed default, 264 px rail, thread chip with 5 nested children, collapse/expand, in-place switch (URL moved, one window), dedup focus (no duplicate window), persistence across reload, modal sharing the same threads, prototype-vs-implementation capture bundle with zero unresolved structural mismatches. All five scenarios recorded pass with evidence in the lab journey log.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added session provenance, including parent and root session tracking.
    • Added CLI and API filters for parent and root sessions.
    • Added hierarchical session views with collapsible threads, grouping, archived sessions, and sidebar navigation.
    • Added session creation options for assigning a parent session.
    • Added window retargeting to switch application instances and routes.
  • Bug Fixes
    • Improved session lineage validation and workspace safeguards.
    • Prevented provenance-only sessions from being incorrectly stopped during cleanup.

@pedronauck pedronauck self-assigned this Aug 6, 2026
@vercel

vercel Bot commented Aug 6, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
compozy-site Ready Ready Preview Aug 6, 2026 11:13pm

Request Review

@coderabbitai

coderabbitai Bot commented Aug 6, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 5c4676cb-589e-4b2e-968c-7f20c7a9a344

📥 Commits

Reviewing files that changed from the base of the PR and between 31c4b7b and 8a64e8b.

⛔ Files ignored due to path filters (5)
  • docs/qa/charters/CH-session-catalog-navigation.md is excluded by !**/*.md
  • docs/qa/charters/CH-session-sidebar-navigation.md is excluded by !**/*.md
  • docs/qa/reports/2026-08-06-pr-327-coderabbit.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-web-session-sidebar-threads.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-web-sessions-catalog-modal.md is excluded by !**/*.md
📒 Files selected for processing (13)
  • internal/api/udsapi/transport_parity_integration_test.go
  • internal/windowmanager/lifecycle_test.go
  • internal/windowmanager/reducer_window_navigate.go
  • web/src/systems/os/components/__tests__/sessions-modal.test.tsx
  • web/src/systems/os/lib/__tests__/routing-coordinator.test.ts
  • web/src/systems/os/lib/routing-coordinator.ts
  • web/src/systems/session/components/session-list/session-list-group.tsx
  • web/src/systems/session/components/session-list/session-list-thread.tsx
  • web/src/systems/session/components/session-list/session-list.tsx
  • web/src/systems/session/hooks/__tests__/use-session-topbar-slot.test.tsx
  • web/src/systems/session/hooks/use-session-topbar-slot.tsx
  • web/src/systems/session/lib/__tests__/session-hierarchy.test.ts
  • web/src/systems/session/lib/session-hierarchy.ts

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.


Walkthrough

The PR adds session provenance and parent/root filters, window instance retargeting, and a shared hierarchical session sidebar. It updates API, storage, CLI, daemon, window-manager, and web session components with tests.

Changes

Session lineage and catalog filtering

Layer / File(s) Summary
Lineage contracts and creation entry points
internal/api/..., internal/cli/..., internal/daemon/..., internal/tools/builtin/sessions.go
Session creation accepts parent provenance. Session listing supports parent and root filters across API, CLI, daemon, and tool inputs.
Lineage normalization and catalog storage
internal/session/..., internal/store/...
User-session provenance validates parent relationships, derives root and spawn depth, persists lineage, and applies filters to active and durable catalog queries.
Native wiring and validation
internal/daemon/..., internal/api/udsapi/..., internal/session/*_test.go, internal/store/globaldb/*_test.go
Tests cover lineage persistence, validation, filtering, transport parity, workspace isolation, and reaper behavior.

Window instance retargeting

Layer / File(s) Summary
Navigation retargeting flow
internal/api/contract/windowmanager_command_payloads.go, internal/windowmanager/...
Replace navigation accepts an instance key, rejects it in push and pop modes, updates the target instance, and clears navigation history.
Routing and runtime retargeting
web/src/systems/os/lib/..., web/src/systems/os/runtime/...
Routing reuses matching instances, focuses existing target windows, or retargets the current window through a window command.
Retargeting validation
internal/windowmanager/lifecycle_test.go, web/src/systems/os/**/__tests__/*, web/src/systems/os/components/stories/_shell-fixture.tsx
Tests cover in-place retargeting, existing-window focus, route reconciliation, history navigation, and command fixtures.

Hierarchical session navigation UI

Layer / File(s) Summary
Sidebar state and topbar control
web/src/systems/session/hooks/...
Sidebar visibility and collapsed threads persist. The session topbar provides an accessible toggle.
Hierarchy and shared session list
web/src/systems/session/lib/..., web/src/systems/session/components/session-list/..., web/src/systems/session/components/session-sidebar.tsx
Shared components render session rows, grouped threads, archived sessions, filtering, collapse controls, and child-status signals.
Session-window integration
web/src/systems/os/apps/session/...
The session window passes its window ID, renders the sidebar and delete dialog, loads session data, and retargets the window on session selection.
Modal integration and UI tests
web/src/systems/os/components/sessions-modal.tsx, web/src/systems/os/components/__tests__/sessions-modal.test.tsx
The modal uses the shared list. Tests cover nested rendering, collapse behavior, ancestor-preserving filtering, and persisted group collapse.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

  • compozy/compozy#266: Shares agent-identity resolution used to infer session provenance.
  • compozy/compozy#280: Shares window-manager navigation command changes used by instance-key retargeting.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.52% which is insufficient. The required threshold is 80.00%. 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 identifies the main change: support for parent and child sessions.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch child-sessions

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 Aug 6, 2026 •

Copy link
Copy Markdown

Greptile Summary

The PR adds parent-session provenance across creation and catalog surfaces, then exposes the resulting hierarchy through shared session-list UI in the dock and session sidebar.

  • Records and validates provenance lineage for user sessions across native-tool, HTTP/UDS, and CLI creation paths.
  • Adds parent/root catalog filters and propagates them through storage, APIs, CLI, native tools, and generated contracts.
  • Adds window retargeting with deduplication and navigation-stack reset.
  • Introduces shared threaded session-list components and a persistent in-window session sidebar.

Confidence Score: 5/5

The PR appears safe to merge because no blocking failure remains.

No blocking failure remains.

Important Files Changed

Filename Overview
internal/session/manager_start_storage.go Extends create-lineage normalization to support provenance-light user sessions while deriving and validating server-owned lineage.
internal/store/globaldb/global_db_session_page.go Applies parent and root lineage filters to durable session catalog queries.
internal/windowmanager/reducer_window_navigate.go Supports replace-mode instance rekeying and resets navigation state during session-window retargeting.
web/src/systems/os/lib/routing-coordinator.ts Coordinates in-place session retargeting while focusing an existing owner window to preserve deduplication.
web/src/systems/session/lib/session-hierarchy.ts Builds provenance trees, retains pagination orphans, flattens thread descendants, and derives collapsed-thread status signals.
web/src/systems/session/components/session-sidebar.tsx Adds the in-window session catalog rail using the shared threaded session-list body.

Sequence Diagram

sequenceDiagram
  participant Caller
  participant API as CLI / HTTP / Native Tool
  participant Manager as Session Manager
  participant Store as Session Store
  participant Catalog as Session Catalog
  participant Web as Dock / Sidebar

  Caller->>API: Create session with explicit or inferred parent
  API->>Manager: Create request + parent session ID
  Manager->>Store: Resolve parent in target workspace
  Store-->>Manager: Parent lineage
  Manager->>Manager: Derive root ID and spawn depth
  Manager->>Store: Persist user session with provenance
  Caller->>Catalog: List by parent or root
  Catalog->>Store: Apply workspace and lineage filters
  Store-->>Catalog: Matching session tree rows
  Catalog-->>Web: Session catalog
  Web->>Web: Build provenance threads
  Web-->>Caller: Render dock modal or session sidebar
Loading

Reviews (2): Last reviewed commit: "fix: review round" | Re-trigger Greptile

<body>
<div class="wrap" data-od-id="session-index">
<h1>Session domain</h1>
<p class="sub">Final pages and labs for the session surface. Vocabulary lives in <code>session.css</code>; spec in <code>SESSION-REDESIGN-SPEC.md</code>.</p>

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 Redundant session index subtitle

The paragraph repeats what the self-explanatory heading and linked sections already convey, adding unnecessary descriptive copy to the new index.

Suggested change
<p class="sub">Final pages and labs for the session surface. Vocabulary lives in <code>session.css</code>; spec in <code>SESSION-REDESIGN-SPEC.md</code>.</p>

Context Used: CLAUDE.md (source)

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!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🧹 Nitpick comments (2)
web/src/systems/session/hooks/__tests__/use-session-topbar-slot.test.tsx (1)

32-34: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cover the sidebar-toggle contract.

These fixtures hard-code sidebarOpen to false and discard onSidebarToggle. Add a test that supplies both open states, asserts the accessible pressed state, and verifies that clicking session-sidebar-toggle calls the callback.

As per coding guidelines, “Before adding a Vitest or Playwright test, identify the invariant, owning layer, and canonical suite; update existing route, hook, component, story, or E2E suites first.”

Also applies to: 98-99, 139-140

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/systems/session/hooks/__tests__/use-session-topbar-slot.test.tsx`
around lines 32 - 34, Extend the canonical use-session-topbar-slot test suite to
cover the sidebar-toggle contract: parameterize or add cases for both
sidebarOpen states, assert the session-sidebar-toggle control’s accessible
pressed state, and retain an onSidebarToggle spy to verify it is called on
click. Update the existing fixture and relevant tests rather than creating a new
suite.

Source: Coding guidelines

web/src/systems/session/components/session-list/session-list-thread.tsx (1)

76-76: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use a tokenized connector offset.

before:left-[11px] introduces an ad-hoc spacing value. Replace it with an exported spacing or layout token.

As per coding guidelines, “Use tokens from packages/ui/src/tokens.css and DESIGN.md for all color, type, radius, spacing, and motion values; do not invent design values.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/systems/session/components/session-list/session-list-thread.tsx` at
line 76, Replace the hardcoded before:left-[11px] offset in the session-list
thread connector with the appropriate exported spacing or layout token from the
established UI tokens, while preserving the connector’s current positioning and
appearance.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/api/udsapi/transport_parity_integration_test.go`:
- Around line 1507-1515: Extend the rootQuery assertions in the transport parity
test to issue the same request through HTTP as well as UDS. Compare the HTTP
session catalog IDs and total with the UDS result, preserving the existing
expected whole-tree IDs and using the established transport helper and assertion
patterns from the parent filter checks.

In `@internal/session/manager_start_storage.go`:
- Around line 150-159: The lineage creation flow around
validateCreateLineageReferences must load the derived root session after
resolving rootID and reject it when its WorkspaceID differs from workspaceID.
Preserve the existing parent validation and only persist filled.RootSessionID
after confirming the root belongs to the requested workspace.

In `@internal/windowmanager/reducer_window_navigate.go`:
- Around line 23-25: Move the InstanceKey validation in the window navigation
reducer ahead of the NavigatePop branch so pop commands cannot ignore it. Ensure
any command with InstanceKey set and Mode other than NavigateReplace returns the
existing ErrInvalidCommand, while preserving normal pop handling and
replace-mode behavior.

In `@web/src/systems/os/lib/routing-coordinator.ts`:
- Around line 244-256: The existing-window branch in the routing coordinator
must compare existing.route with target.route before returning from
userActivateWindow. When the routes differ, activate or restore the existing
window, navigate it to target.route, await completion, then call
pushRoute(target.route) exactly once; preserve the current activation-only
behavior for matching routes. Add a regression test covering the same instance
key with different routes.

In `@web/src/systems/session/components/session-list/session-list-thread.tsx`:
- Around line 70-87: Set the child-content container in session-list-thread.tsx
(lines 70-87) to inert whenever collapsed is true, and apply the same change to
the group-content container in session-list-group.tsx (lines 62-82), preventing
keyboard focus while preserving normal interaction when expanded.

In `@web/src/systems/session/components/session-list/session-list.tsx`:
- Around line 77-84: Update the descendant filtering logic in the session-list
thread construction so a matching descendant retains all intermediate ancestors
through the thread root, even when those ancestors do not match the filter.
Preserve the existing root match behavior and ensure childSessions contains the
complete ancestor path rather than only sessions passing matchesFilter.

In `@web/src/systems/session/lib/session-hierarchy.ts`:
- Around line 20-30: Update the session hierarchy construction around the
parentId and childrenByParent logic to detect multi-session lineage cycles, not
just self-parenting. Before nesting a session, identify cycles such as A → B → A
and place every participant in the cycle into roots so SessionList can render
them; preserve normal parent-child nesting for acyclic sessions. Add coverage
for a two-session cycle.

---

Nitpick comments:
In `@web/src/systems/session/components/session-list/session-list-thread.tsx`:
- Line 76: Replace the hardcoded before:left-[11px] offset in the session-list
thread connector with the appropriate exported spacing or layout token from the
established UI tokens, while preserving the connector’s current positioning and
appearance.

In `@web/src/systems/session/hooks/__tests__/use-session-topbar-slot.test.tsx`:
- Around line 32-34: Extend the canonical use-session-topbar-slot test suite to
cover the sidebar-toggle contract: parameterize or add cases for both
sidebarOpen states, assert the session-sidebar-toggle control’s accessible
pressed state, and retain an onSidebarToggle spy to verify it is called on
click. Update the existing fixture and relevant tests rather than creating a new
suite.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: a57881ed-ec3d-4c31-bdc8-61a3dd3e4dc7

📥 Commits

Reviewing files that changed from the base of the PR and between c1e9ba5 and 31c4b7b.

⛔ Files ignored due to path filters (20)
  • docs/design/opendesign/README.md is excluded by !docs/design/**/*, !**/*.md
  • docs/design/opendesign/session/SESSION-REDESIGN-SPEC.md is excluded by !docs/design/**/*, !**/*.md
  • docs/design/opendesign/session/index.html is excluded by !docs/design/**/*
  • docs/design/opendesign/session/index.html.artifact.json is excluded by !docs/design/**/*, !**/*.json
  • docs/design/opendesign/session/session-sidebar.html is excluded by !docs/design/**/*
  • docs/design/opendesign/session/session-sidebar.html.artifact.json is excluded by !docs/design/**/*, !**/*.json
  • docs/qa/scenarios/ET-web-session-inspector-toggle.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-web-session-sidebar-threads.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-web-session-thread-full-bleed.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-web-sessions-catalog-modal.md is excluded by !**/*.md
  • docs/qa/scenarios/RT-session-parent-provenance.md is excluded by !**/*.md
  • internal/tools/builtin/testdata/native-tool-catalog.json is excluded by !**/*.json
  • openapi/compozy.json is excluded by !**/*.json
  • packages/site/content/docs/cli/session/list.mdx is excluded by !**/*.mdx
  • packages/site/content/docs/cli/session/new.mdx is excluded by !**/*.mdx
  • packages/site/content/docs/sessions/lifecycle.mdx is excluded by !**/*.mdx
  • skills/compozy/references/native-tools.md is excluded by !**/*.md
  • skills/compozy/references/runtime-operations.md is excluded by !**/*.md
  • web/src/generated/compozy-openapi.d.ts is excluded by !**/generated/**, !**/generated/**, !**/*.d.ts
  • web/src/systems/session/components/stories/session-sidebar.stories.tsx is excluded by !**/*.stories.tsx
📒 Files selected for processing (53)
  • internal/api/contract/session_runtime_payloads.go
  • internal/api/contract/windowmanager_command_payloads.go
  • internal/api/core/agent_identity.go
  • internal/api/core/handlers.go
  • internal/api/core/session_catalog.go
  • internal/api/spec/session_catalog.go
  • internal/api/udsapi/transport_parity_integration_test.go
  • internal/cli/client_test.go
  • internal/cli/session_catalog_client.go
  • internal/cli/session_create.go
  • internal/cli/session_list.go
  • internal/daemon/native_session_list.go
  • internal/daemon/native_session_mutation.go
  • internal/daemon/native_tools_test.go
  • internal/daemon/spawn_reaper_test.go
  • internal/session/catalog_page.go
  • internal/session/manager_create_prepare.go
  • internal/session/manager_lineage_test.go
  • internal/session/manager_start_storage.go
  • internal/session/query_test.go
  • internal/store/globaldb/global_db_session_index_test.go
  • internal/store/globaldb/global_db_session_page.go
  • internal/store/session_catalog_page.go
  • internal/tools/builtin/sessions.go
  • internal/windowmanager/commands.go
  • internal/windowmanager/lifecycle_test.go
  • internal/windowmanager/reducer_window_navigate.go
  • web/src/systems/os/apps/session/session-window-content.tsx
  • web/src/systems/os/apps/session/session-window-view.tsx
  • web/src/systems/os/apps/session/session-window.tsx
  • web/src/systems/os/apps/session/use-session-window-controller.tsx
  • web/src/systems/os/apps/session/use-session-window-sidebar.ts
  • web/src/systems/os/components/__tests__/sessions-modal.test.tsx
  • web/src/systems/os/components/sessions-modal.tsx
  • web/src/systems/os/components/stories/_shell-fixture.tsx
  • web/src/systems/os/hooks/__tests__/os-interaction-hooks.test.tsx
  • web/src/systems/os/hooks/__tests__/use-os-window-deck.test.tsx
  • web/src/systems/os/lib/__tests__/routing-coordinator.test.ts
  • web/src/systems/os/lib/os-types.ts
  • web/src/systems/os/lib/routing-coordinator.ts
  • web/src/systems/os/runtime/window-manager-tab-commands.ts
  • web/src/systems/session/components/session-list/session-list-archived.tsx
  • web/src/systems/session/components/session-list/session-list-group.tsx
  • web/src/systems/session/components/session-list/session-list-row.tsx
  • web/src/systems/session/components/session-list/session-list-thread.tsx
  • web/src/systems/session/components/session-list/session-list.tsx
  • web/src/systems/session/components/session-sidebar.tsx
  • web/src/systems/session/hooks/__tests__/use-session-topbar-slot.test.tsx
  • web/src/systems/session/hooks/use-session-sidebar-state.ts
  • web/src/systems/session/hooks/use-session-topbar-slot.tsx
  • web/src/systems/session/index.ts
  • web/src/systems/session/lib/__tests__/session-hierarchy.test.ts
  • web/src/systems/session/lib/session-hierarchy.ts

Comment thread internal/api/udsapi/transport_parity_integration_test.go
Comment on lines +150 to +159
parentLineage := store.NormalizeSessionLineage(parent.ID, parent.Lineage)
rootID := strings.TrimSpace(parentLineage.RootSessionID)
if rootID == "" {
rootID = parent.ID
}
filled := *lineage
filled.ParentSessionID = parentID
filled.RootSessionID = rootID
filled.SpawnDepth = parentLineage.SpawnDepth + 1
return &filled, nil

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Validate the derived root workspace before persisting lineage.

The code verifies that parent belongs to workspaceID, but it does not verify the derived root. validateCreateLineageReferences only verifies that the root exists.

If a same-workspace parent has a root reference in another workspace, this code persists that foreign root on the new session. Load the root and reject it when its WorkspaceID differs from workspaceID.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/session/manager_start_storage.go` around lines 150 - 159, The
lineage creation flow around validateCreateLineageReferences must load the
derived root session after resolving rootID and reject it when its WorkspaceID
differs from workspaceID. Preserve the existing parent validation and only
persist filled.RootSessionID after confirming the root belongs to the requested
workspace.

Comment thread internal/windowmanager/reducer_window_navigate.go Outdated
Comment thread web/src/systems/os/lib/routing-coordinator.ts
Comment thread web/src/systems/session/components/session-list/session-list.tsx
Comment on lines +20 to +30
const parentId = session.lineage?.parent_session_id ?? "";
if (parentId !== "" && parentId !== session.id && byId.has(parentId)) {
const siblings = childrenByParent.get(parentId);
if (siblings) {
siblings.push(session);
} else {
childrenByParent.set(parentId, [session]);
}
continue;
}
roots.push(session);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Handle multi-session lineage cycles.

The self-parent check does not handle A -> B -> A cycles. Both sessions enter childrenByParent, roots stays empty, and SessionList renders neither session.

Detect parent cycles before nesting. Keep each cycle participant visible as a root. Add coverage for a two-session cycle.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/systems/session/lib/session-hierarchy.ts` around lines 20 - 30,
Update the session hierarchy construction around the parentId and
childrenByParent logic to detect multi-session lineage cycles, not just
self-parenting. Before nesting a session, identify cycles such as A → B → A and
place every participant in the cycle into roots so SessionList can render them;
preserve normal parent-child nesting for acyclic sessions. Add coverage for a
two-session cycle.

@pedronauck
pedronauck merged commit 62d7e97 into main Aug 6, 2026
2 of 4 checks passed
@pedronauck
pedronauck deleted the child-sessions branch August 6, 2026 23:11
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

React Doctor found no new issues. 🎉

Reviewed by React Doctor for commit 8a64e8b.

This branch was successfully deployed

1 active deployment
Preview — 8a64e8b4 Deployed Aug 6, 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