> For the complete documentation index, see [llms.txt](https://faction-os.gitbook.io/faction-os-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://faction-os.gitbook.io/faction-os-docs/.spec_system/archive/sessions/phase21-session02-shared-expansion-host/implementation-notes.md).

# Implementation Notes

**Session ID**: `phase21-session02-shared-expansion-host` **Package**: apps/web **Started**: 2026-06-29 17:02 **Last Updated**: 2026-06-29 17:19

***

## Session Progress

| Metric              | Value   |
| ------------------- | ------- |
| Tasks Completed     | 22 / 22 |
| Estimated Remaining | 0 hours |
| Blockers            | 0       |

***

## Task Log

### 2026-06-29 - Session Start

**Environment verified**:

* [x] Prerequisites confirmed by `.spec_system/scripts/check-prereqs.sh --json --env --package apps/web`
* [x] Tools checked by `.spec_system/scripts/check-prereqs.sh --json --tools "node,npm" --package apps/web`
* [x] Directory structure ready

**Environment notes**:

* The prerequisite script returned `overall: pass` for package `apps/web`.
* Tool check reported `node v24.14.0` and `npm 11.16.0`; the spec text names Node 26.2.0+ as the target runtime, so final verification will record any command impact explicitly.

***

### Task T001 - Verify analyzer state, Session 01 completion, and Session 02 scope

**Started**: 2026-06-29 17:02 **Completed**: 2026-06-29 17:02 **Duration**: 1 minute

**Notes**:

* Confirmed analyzer state points at `phase21-session02-shared-expansion-host` in phase 21.
* Confirmed Session 01 is marked complete in the phase stub and analyzer completed sessions.
* Confirmed Session 02 package scope is `apps/web`, with the shared bottom-rail host as the in-scope deliverable.

**Files Changed**:

* `.spec_system/specs/phase21-session02-shared-expansion-host/implementation-notes.md` - Created session implementation log and recorded T001 evidence.
* `.spec_system/specs/phase21-session02-shared-expansion-host/tasks.md` - Marked T001 complete and initialized progress summary.

**Verification**:

* Command/check: `if [ -d ".spec_system/scripts" ]; then bash .spec_system/scripts/analyze-project.sh --json; else bash /home/aiwithapex/.codex/plugins/cache/apex-spec-system/apex-spec/2.1.3-codex/skills/apex-spec/scripts/analyze-project.sh --json; fi`
  * Result: PASS - current session resolved.
  * Evidence: JSON reported `current_phase: 21`, `current_session: "phase21-session02-shared-expansion-host"`, `current_session_dir_exists: true`, `monorepo: true`, and Session 01 in `completed_sessions`.
* Command/check: `sed -n '1,180p' .spec_system/PRD/phase_21/session_01_expansion_state_foundation.md`
  * Result: PASS - prerequisite session complete.
  * Evidence: Session 01 stub has `Status: Complete` and checked success criteria for transient expansion state.
* Command/check: `sed -n '1,220p' .spec_system/PRD/phase_21/session_02_shared_expansion_host.md`
  * Result: PASS - scope reviewed.
  * Evidence: Session 02 stub scopes work to `apps/web`, shared host, one-at-a-time expansion, guarded nested controls, focus restore, and no duplicate panel tree.
* UI product-surface check: N/A - audit-only task.
* UI craft check: N/A - audit-only task.

***

### Task T002 - Audit current bottom-rail App wiring and shell note behavior

**Started**: 2026-06-29 17:02 **Completed**: 2026-06-29 17:03 **Duration**: 1 minute

**Notes**:

* Reviewed the current `bottomRail` layout in `apps/web/src/App.tsx`.
* Confirmed Quest Board, Orchestration, and War Room are currently direct `ShellSurface` children in a three-column grid.
* Confirmed `SurfaceStateNote` is emitted inside `ShellSurface` before the panel content, so the host wiring must preserve that same wrapper path instead of duplicating state note rendering.

**Files Changed**:

* `.spec_system/specs/phase21-session02-shared-expansion-host/implementation-notes.md` - Recorded T002 audit evidence.
* `.spec_system/specs/phase21-session02-shared-expansion-host/tasks.md` - Marked T002 complete and updated progress summary.

**Verification**:

* Command/check: `sed -n '1,260p' apps/web/src/App.tsx`
  * Result: PASS - bottom-rail wiring and shell-note behavior reviewed.
  * Evidence: `bottomRail` renders direct `ShellSurface` wrappers for `quest-board`, `orchestration`, and `war-room`; `ShellSurface` renders `SurfaceStateNote` and then a single child content wrapper.
* UI product-surface check: N/A - audit-only task.
* UI craft check: N/A - audit-only task.

***

### Task T003 - Audit existing expansion store API and panel-local detail controls

**Started**: 2026-06-29 17:03 **Completed**: 2026-06-29 17:04 **Duration**: 1 minute

**Notes**:

* Confirmed `useSettingsStore` exports `BottomRailSurfaceId`, `expandedBottomRailSurfaceId`, and `setExpandedBottomRailSurfaceId`.
* Confirmed the expansion field is part of `DEFAULT_TRANSIENT_SETTINGS` and excluded from persisted snapshots.
* Reviewed current local detail controls: Quest Board and War Room use local `detailsOpen` dialogs; Orchestration opens `OrchestrationDetailPortal` from `OrchestrationHeader`.
* Preserved scope boundary: this session will add shell-level focused expansion without renaming or deleting panel-specific detail portals.

**Files Changed**:

* `.spec_system/specs/phase21-session02-shared-expansion-host/implementation-notes.md` - Recorded T003 audit evidence.
* `.spec_system/specs/phase21-session02-shared-expansion-host/tasks.md` - Marked T003 complete and updated progress summary.

**Verification**:

* Command/check: `sed -n '1,560p' apps/web/src/store/useSettingsStore.ts`
  * Result: PASS - expansion state API is available and transient.
  * Evidence: Found `BottomRailSurfaceId = "quest-board" | "orchestration" | "war-room"`, `expandedBottomRailSurfaceId: BottomRailSurfaceId | null`, setter, and default `null` transient state.
* Command/check: `sed -n '1,260p' apps/web/src/components/QuestBoard.tsx`
  * Result: PASS - Quest Board detail control reviewed.
  * Evidence: Header button opens `quest-board-detail-modal` through local `detailsOpen`; Escape listener is local to that detail modal.
* Command/check: `sed -n '1,260p' apps/web/src/components/WarRoomPanel.tsx`
  * Result: PASS - War Room detail control reviewed.
  * Evidence: Header button opens `war-room-detail-modal` through local `detailsOpen`; Escape listener is local to that detail modal.
* Command/check: `sed -n '1,220p' apps/web/src/components/orchestration/OrchestrationHeader.tsx` and `sed -n '2120,2310p' apps/web/src/components/orchestration/OrchestrationShell.tsx`
  * Result: PASS - Orchestration detail control reviewed.
  * Evidence: Header button calls `onOpenDetails`, and `OrchestrationShell` conditionally renders `OrchestrationDetailPortal`.
* UI product-surface check: N/A - audit-only task.
* UI craft check: N/A - audit-only task.

***

### Task T004 - Create BottomRailExpansionHost props and typed surface-id contract

**Started**: 2026-06-29 17:04 **Completed**: 2026-06-29 17:06 **Duration**: 2 minutes

**Notes**:

* Created `BottomRailExpansionHost` in the web component package.
* Bound `surfaceId` to the existing `BottomRailSurfaceId` union from `useSettingsStore` instead of duplicating ids locally.
* Added a minimal pass-through wrapper so later behavior can be layered without changing the child panel instance contract.

**Files Changed**:

* `apps/web/src/components/BottomRailExpansionHost.tsx` - Added typed host props and initial pass-through component.
* `.spec_system/specs/phase21-session02-shared-expansion-host/implementation-notes.md` - Recorded T004 evidence.
* `.spec_system/specs/phase21-session02-shared-expansion-host/tasks.md` - Marked T004 complete and updated progress summary.

**Verification**:

* Command/check: `sed -n '1,120p' apps/web/src/components/BottomRailExpansionHost.tsx`
  * Result: PASS - typed contract present.
  * Evidence: `BottomRailExpansionHostProps.surfaceId` is typed as `BottomRailSurfaceId`, with `title`, `className`, and `children` props.
* UI product-surface check: N/A - component is not wired into the product surface yet.
* UI craft check: N/A - component is not wired into the product surface yet.

***

### Task T005 - Implement the interactive descendant guard selector

**Started**: 2026-06-29 17:06 **Completed**: 2026-06-29 17:07 **Duration**: 1 minute

**Notes**:

* Added a single exported guard selector for controls that must not trigger background expansion.
* Added a helper that checks `closest(...)` from the event target and handles non-DOM/SSR contexts without throwing.

**Files Changed**:

* `apps/web/src/components/BottomRailExpansionHost.tsx` - Added `BOTTOM_RAIL_EXPANSION_INTERACTIVE_SELECTOR` and `isBottomRailExpansionInteractiveTarget`.
* `.spec_system/specs/phase21-session02-shared-expansion-host/implementation-notes.md` - Recorded T005 evidence.
* `.spec_system/specs/phase21-session02-shared-expansion-host/tasks.md` - Marked T005 complete and updated progress summary.

**Verification**:

* Command/check: `sed -n '1,160p' apps/web/src/components/BottomRailExpansionHost.tsx`
  * Result: PASS - guard selector includes required descendants.
  * Evidence: Selector includes `button`, `a[href]`, `input`, `textarea`, `select`, `[role='button']`, `[role='tab']`, `[contenteditable]`, and `[data-no-panel-expand]`; helper returns false when `Element` is unavailable.
* UI product-surface check: N/A - component is not wired into the product surface yet.
* UI craft check: N/A - component is not wired into the product surface yet.

***

### Task T006 - Implement compact and expanded wrapper structure with same-instance child promotion

**Started**: 2026-06-29 17:07 **Completed**: 2026-06-29 17:09 **Duration**: 2 minutes

**Notes**:

* Added store-backed compact versus focused layout selection using `expandedBottomRailSurfaceId`.
* Added stable DOM ids for host, title, panel, backdrop, expanded marker, expand control, and close control.
* Kept `children` in one stable content slot so expansion changes wrapper layout instead of rendering a duplicate child tree.

**Files Changed**:

* `apps/web/src/components/BottomRailExpansionHost.tsx` - Added store-backed compact/focused wrapper structure and id helper.
* `.spec_system/specs/phase21-session02-shared-expansion-host/implementation-notes.md` - Recorded T006 evidence.
* `.spec_system/specs/phase21-session02-shared-expansion-host/tasks.md` - Marked T006 complete and updated progress summary.

**Verification**:

* Command/check: `sed -n '1,220p' apps/web/src/components/BottomRailExpansionHost.tsx`
  * Result: PASS - same-instance wrapper structure present.
  * Evidence: Host reads `expandedBottomRailSurfaceId`, compares it to `surfaceId`, switches classes between compact `h-full` and fixed focused layout, and renders `{children}` once inside `id={ids.panel}`.
* UI product-surface check: N/A - component is not wired into the product surface yet.
* UI craft check: N/A - component is not wired into the product surface yet.

**BQC Fixes**:

* Contract alignment: The host reuses the existing `BottomRailSurfaceId` store contract and keeps a single child content slot (`apps/web/src/components/BottomRailExpansionHost.tsx`).

***

### Task T007 - Implement dialog ARIA state, labelled title wiring, aria-controls, and stable test-id helpers

**Started**: 2026-06-29 17:09 **Completed**: 2026-06-29 17:10 **Duration**: 1 minute

**Notes**:

* Added focused-only dialog semantics so compact rail cells remain normal cockpit regions.
* Wired `aria-labelledby` to the host title and `aria-controls` to the stable panel slot.
* Added stable test ids for focused surfaces, panels, and backdrops through the shared id helper.

**Files Changed**:

* `apps/web/src/components/BottomRailExpansionHost.tsx` - Added focused dialog ARIA attributes and stable test ids.
* `.spec_system/specs/phase21-session02-shared-expansion-host/implementation-notes.md` - Recorded T007 evidence.
* `.spec_system/specs/phase21-session02-shared-expansion-host/tasks.md` - Marked T007 complete and updated progress summary.

**Verification**:

* Command/check: `sed -n '40,140p' apps/web/src/components/BottomRailExpansionHost.tsx`
  * Result: PASS - focused dialog semantics and test ids present.
  * Evidence: Expanded host uses `role="dialog"`, `aria-modal="true"`, `aria-labelledby={ids.title}`, `aria-controls={ids.panel}`, `data-testid={ids.expanded}`, and backdrop `data-testid={ids.backdrop}`.
* UI product-surface check: N/A - component is not wired into the product surface yet.
* UI craft check: N/A - component is not wired into the product surface yet.

**BQC Fixes**:

* Accessibility and platform compliance: Focused mode now participates in the platform accessibility model as a labelled dialog (`apps/web/src/components/BottomRailExpansionHost.tsx`).

***

### Task T008 - Implement product-facing explicit open affordance and focused close affordance

**Started**: 2026-06-29 17:10 **Completed**: 2026-06-29 17:12 **Duration**: 2 minutes

**Notes**:

* Added visible shell-level `Focus` controls using native buttons for keyboard, pointer, and screen-reader input.
* Added focused-mode `Close` controls that clear the shared expansion state.
* Added focus restoration to the recorded opener for intentional close actions, with `isConnected` guarding for unmounted openers.
* Kept repeated open clicks idempotent while the surface is already focused.

**Files Changed**:

* `apps/web/src/components/BottomRailExpansionHost.tsx` - Added open/close controls, labels, state reflection, and focus restore.
* `.spec_system/specs/phase21-session02-shared-expansion-host/implementation-notes.md` - Recorded T008 evidence.
* `.spec_system/specs/phase21-session02-shared-expansion-host/tasks.md` - Marked T008 complete and updated progress summary.

**Verification**:

* Command/check: `sed -n '1,240p' apps/web/src/components/BottomRailExpansionHost.tsx`
  * Result: PASS - explicit controls and focus management present.
  * Evidence: Host renders `data-testid={ids.expand}` button with `aria-controls`, `aria-expanded`, `aria-pressed`, product-facing label, focused close button with `data-testid={ids.close}`, and effect-based focus restoration to a connected opener.
* UI product-surface check: N/A - component is not wired into the product surface yet.
* UI craft check: N/A - component is not wired into the product surface yet.

**BQC Fixes**:

* Duplicate action prevention: Opening an already focused surface returns without mutating state again (`apps/web/src/components/BottomRailExpansionHost.tsx`).
* Accessibility and platform compliance: Open and close affordances are native buttons with accessible names and state (`apps/web/src/components/BottomRailExpansionHost.tsx`).

***

### Task T009 - Wire Quest Board through the shared host

**Started**: 2026-06-29 17:12 **Completed**: 2026-06-29 17:13 **Duration**: 1 minute

**Notes**:

* Imported `BottomRailExpansionHost` into `App.tsx`.
* Wrapped the Quest Board rail cell with `surfaceId="quest-board"` while keeping the existing `ShellSurface` and `QuestBoard` instance inside the host.
* Moved the rail min-height constraint to the host and made `ShellSurface` flex inside the host so state notes remain on the same path.

**Files Changed**:

* `apps/web/src/App.tsx` - Wired Quest Board through `BottomRailExpansionHost`.
* `.spec_system/specs/phase21-session02-shared-expansion-host/implementation-notes.md` - Recorded T009 evidence.
* `.spec_system/specs/phase21-session02-shared-expansion-host/tasks.md` - Marked T009 complete and updated progress summary.

**Verification**:

* Command/check: `sed -n '1,180p' apps/web/src/App.tsx`
  * Result: PASS - Quest Board host wiring reviewed.
  * Evidence: `BottomRailExpansionHost surfaceId="quest-board"` wraps one `ShellSurface surface={surface("quest-board")}` containing one `<QuestBoard />`.
* UI product-surface check: PASS - code inspection confirms the new shell control copy is product-facing (`Focus`) and no debug/runtime/scaffolding copy was added to Quest Board.
* UI craft check: PASS - Quest Board keeps the existing `ShellSurface` and panel layout path; formal viewport polish remains Session 06 scope.

**BQC Fixes**:

* Contract alignment: Quest Board uses the existing shared host and existing shell-state note contract without duplicating panel state (`apps/web/src/App.tsx`).

***

### Task T010 - Wire Orchestration through the shared host

**Started**: 2026-06-29 17:13 **Completed**: 2026-06-29 17:14 **Duration**: 1 minute

**Notes**:

* Wrapped the Orchestration rail cell with `surfaceId="orchestration"` while preserving a single `OrchestrationPanel` child.
* Kept `ShellSurface` inside the host so loading, offline, empty, and disconnected notes stay in the existing shell path.

**Files Changed**:

* `apps/web/src/App.tsx` - Wired Orchestration through `BottomRailExpansionHost`.
* `.spec_system/specs/phase21-session02-shared-expansion-host/implementation-notes.md` - Recorded T010 evidence.
* `.spec_system/specs/phase21-session02-shared-expansion-host/tasks.md` - Marked T010 complete and updated progress summary.

**Verification**:

* Command/check: `sed -n '150,180p' apps/web/src/App.tsx`
  * Result: PASS - Orchestration host wiring reviewed.
  * Evidence: `BottomRailExpansionHost surfaceId="orchestration"` wraps one `ShellSurface surface={surface("orchestration")}` containing one `<OrchestrationPanel />`.
* UI product-surface check: PASS - code inspection confirms the shared shell control adds product-facing focus copy only and leaves Orchestration detail semantics unchanged.
* UI craft check: PASS - Orchestration keeps the existing panel and shell-note path; formal viewport polish remains Session 06 scope.

**BQC Fixes**:

* Contract alignment: Orchestration uses the shared host without adding a parallel panel tree or new local expansion boolean (`apps/web/src/App.tsx`).

***

### Task T011 - Wire War Room through the shared host

**Started**: 2026-06-29 17:14 **Completed**: 2026-06-29 17:15 **Duration**: 1 minute

**Notes**:

* Wrapped the War Room rail cell with `surfaceId="war-room"` while preserving a single `WarRoomPanel` child.
* Kept `ShellSurface` inside the host so optional Worker lifecycle notes and shell-state notes stay in the existing layout path.

**Files Changed**:

* `apps/web/src/App.tsx` - Wired War Room through `BottomRailExpansionHost`.
* `.spec_system/specs/phase21-session02-shared-expansion-host/implementation-notes.md` - Recorded T011 evidence.
* `.spec_system/specs/phase21-session02-shared-expansion-host/tasks.md` - Marked T011 complete and updated progress summary.

**Verification**:

* Command/check: `sed -n '156,190p' apps/web/src/App.tsx`
  * Result: PASS - all three bottom rail hosts reviewed.
  * Evidence: Quest Board, Orchestration, and War Room are each wrapped by `BottomRailExpansionHost`; War Room wraps one `ShellSurface surface={surface("war-room")}` containing one `<WarRoomPanel />`.
* UI product-surface check: PASS - code inspection confirms the shared shell control adds product-facing focus copy only and leaves War Room optional Worker copy unchanged.
* UI craft check: PASS - War Room keeps the existing panel and shell-note path; formal viewport polish remains Session 06 scope.

**BQC Fixes**:

* Contract alignment: War Room uses the shared host without adding a parallel panel tree or new local expansion boolean (`apps/web/src/App.tsx`).

***

### Task T012 - Connect host open and close behavior to expandedBottomRailSurfaceId

**Started**: 2026-06-29 17:15 **Completed**: 2026-06-29 17:16 **Duration**: 1 minute

**Notes**:

* Confirmed host open behavior writes the current `surfaceId` through `setExpandedBottomRailSurfaceId`.
* Confirmed host close behavior clears the shared state to `null`.
* Because the store has one `expandedBottomRailSurfaceId`, opening Quest Board, Orchestration, or War Room replaces any previously expanded bottom surface.

**Files Changed**:

* `apps/web/src/components/BottomRailExpansionHost.tsx` - Uses the shared settings-store expansion setter for open and close behavior.
* `.spec_system/specs/phase21-session02-shared-expansion-host/implementation-notes.md` - Recorded T012 evidence.
* `.spec_system/specs/phase21-session02-shared-expansion-host/tasks.md` - Marked T012 complete and updated progress summary.

**Verification**:

* Command/check: `sed -n '1,240p' apps/web/src/components/BottomRailExpansionHost.tsx`
  * Result: PASS - store-backed singleton open/close behavior reviewed.
  * Evidence: `openSurface` calls `setExpandedBottomRailSurfaceId(surfaceId)`, `closeSurface` calls `setExpandedBottomRailSurfaceId(null)`, and `isExpanded` derives from `expandedBottomRailSurfaceId === surfaceId`.
* UI product-surface check: PASS - no debug or diagnostic copy added; focused controls remain product-facing.
* UI craft check: PASS - state replacement happens through the host without introducing extra layout variants or duplicate panels.

**BQC Fixes**:

* State freshness on re-entry: Closing clears the shared focused surface id to `null` before a later re-open (`apps/web/src/components/BottomRailExpansionHost.tsx`).
* Contract alignment: The host uses Session 01's transient store field rather than introducing parallel state (`apps/web/src/components/BottomRailExpansionHost.tsx`).

***

### Task T013 - Implement guarded panel-background click expansion

**Started**: 2026-06-29 17:16 **Completed**: 2026-06-29 17:17 **Duration**: 1 minute

**Notes**:

* Added compact-mode panel click handling that opens the host through the shared `openSurface` path.
* Guarded interactive descendants with the shared selector so nested controls keep their own click behavior.
* Used the explicit Focus button as the restoration target for background launches.

**Files Changed**:

* `apps/web/src/components/BottomRailExpansionHost.tsx` - Added guarded panel click handler.
* `.spec_system/specs/phase21-session02-shared-expansion-host/implementation-notes.md` - Recorded T013 evidence.
* `.spec_system/specs/phase21-session02-shared-expansion-host/tasks.md` - Marked T013 complete and updated progress summary.

**Verification**:

* Command/check: `sed -n '1,260p' apps/web/src/components/BottomRailExpansionHost.tsx`
  * Result: PASS - guarded panel click behavior present.
  * Evidence: `handlePanelClick` returns when expanded or when `isBottomRailExpansionInteractiveTarget(event.target)` is true, otherwise opens the surface through `openSurface(...)`.
* UI product-surface check: PASS - no diagnostic copy added; background open behavior is invisible until user interaction.
* UI craft check: PASS - native nested controls remain protected by the guard; formal pointer/viewport evidence is covered by focused tests and Session 06.

**BQC Fixes**:

* Accessibility and platform compliance: Nested controls are protected from shell click hijacking (`apps/web/src/components/BottomRailExpansionHost.tsx`).

***

### Task T014 - Implement Escape close listener with cleanup

**Started**: 2026-06-29 17:17 **Completed**: 2026-06-29 17:18 **Duration**: 1 minute

**Notes**:

* Added a `keydown` listener only while the current host is expanded.
* Escape uses the same close path as the close button, including focus restoration.
* Listener cleanup runs when the host closes or unmounts.

**Files Changed**:

* `apps/web/src/components/BottomRailExpansionHost.tsx` - Added expanded-only Escape listener with cleanup.
* `.spec_system/specs/phase21-session02-shared-expansion-host/implementation-notes.md` - Recorded T014 evidence.
* `.spec_system/specs/phase21-session02-shared-expansion-host/tasks.md` - Marked T014 complete and updated progress summary.

**Verification**:

* Command/check: `sed -n '56,115p' apps/web/src/components/BottomRailExpansionHost.tsx`
  * Result: PASS - Escape listener lifecycle reviewed.
  * Evidence: Effect returns early when not expanded, registers `window.addEventListener("keydown", onKeyDown)`, closes on unprevented Escape, and removes the listener in cleanup.
* UI product-surface check: PASS - Escape behavior adds no visible debug or diagnostic UI.
* UI craft check: PASS - Escape close is consistent with existing modal controls; nested overlay ordering remains deferred to Session 04 as specified.

**BQC Fixes**:

* Resource cleanup: The Escape listener is only acquired while expanded and is removed in the effect cleanup (`apps/web/src/components/BottomRailExpansionHost.tsx`).

***

### Task T015 - Implement backdrop click close and inner-panel click containment

**Started**: 2026-06-29 17:18 **Completed**: 2026-06-29 17:19 **Duration**: 1 minute

**Notes**:

* Backdrop clicks now use the same close path as the close button and Escape.
* The focused panel container stops click propagation while expanded, keeping nested content interactions separate from backdrop recovery.

**Files Changed**:

* `apps/web/src/components/BottomRailExpansionHost.tsx` - Added backdrop click close and expanded panel click containment.
* `.spec_system/specs/phase21-session02-shared-expansion-host/implementation-notes.md` - Recorded T015 evidence.
* `.spec_system/specs/phase21-session02-shared-expansion-host/tasks.md` - Marked T015 complete and updated progress summary.

**Verification**:

* Command/check: `sed -n '96,170p' apps/web/src/components/BottomRailExpansionHost.tsx`
  * Result: PASS - backdrop close and content containment reviewed.
  * Evidence: Backdrop renders with `data-testid={ids.backdrop}` and `onClick={closeSurface}`; focused panel container calls `event.stopPropagation()` when expanded.
* UI product-surface check: PASS - backdrop and containment add no debug or diagnostic UI.
* UI craft check: PASS - focused surface has a conventional dimmed backdrop and click recovery path consistent with existing overlay behavior.

**BQC Fixes**:

* Failure path completeness: Focused surfaces now recover through backdrop close in addition to close button and Escape (`apps/web/src/components/BottomRailExpansionHost.tsx`).

***

### Task T016 - Add App-level stable test ids for bottom-rail controls

**Started**: 2026-06-29 17:19 **Completed**: 2026-06-29 17:19 **Duration**: less than 1 minute

**Notes**:

* Confirmed the shared id helper emits `bottom-rail-expand-*`, `bottom-rail-expanded-*`, `bottom-rail-close-*`, and `bottom-rail-backdrop-*`.
* Confirmed all three bottom rail surfaces are wired through `BottomRailExpansionHost`, so these ids are available from the real App shell.

**Files Changed**:

* `apps/web/src/components/BottomRailExpansionHost.tsx` - Provides stable test ids through `bottomRailExpansionIds`.
* `apps/web/src/App.tsx` - Wires all three bottom rail surfaces through the host.
* `.spec_system/specs/phase21-session02-shared-expansion-host/implementation-notes.md` - Recorded T016 evidence.
* `.spec_system/specs/phase21-session02-shared-expansion-host/tasks.md` - Marked T016 complete and updated progress summary.

**Verification**:

* Command/check: `sed -n '1,260p' apps/web/src/components/BottomRailExpansionHost.tsx`
  * Result: PASS - stable id helper and control bindings reviewed.
  * Evidence: `bottomRailExpansionIds` returns `expand`, `expanded`, `close`, and `backdrop` ids; open, close, expanded host, and backdrop elements use those ids.
* Command/check: `sed -n '156,190p' apps/web/src/App.tsx`
  * Result: PASS - App-level host wiring reviewed.
  * Evidence: Quest Board, Orchestration, and War Room are all wrapped by `BottomRailExpansionHost`, so App renders the stable ids for all three surfaces.
* UI product-surface check: PASS - stable ids do not expose visible implementation telemetry.
* UI craft check: PASS - controls share one consistent shell-level treatment across all three surfaces.

***

### Task T017 - Update cockpit shell assertions to prove no duplicate bottom-panel trees

**Started**: 2026-06-29 17:19 **Completed**: 2026-06-29 17:19 **Duration**: less than 1 minute

**Notes**:

* Added a Cockpit shell test helper that asserts exactly one Quest Board, one Orchestration panel, and one War Room panel tree are present.
* Reused the existing mocked Orchestration panel in the boundary test while keeping Quest Board and War Room as real panel implementations.

**Files Changed**:

* `apps/web/tests/CockpitShell.test.tsx` - Added `expectSingleBottomPanelTrees` and used it in existing War Room shell coverage.
* `.spec_system/specs/phase21-session02-shared-expansion-host/implementation-notes.md` - Recorded T017 evidence.
* `.spec_system/specs/phase21-session02-shared-expansion-host/tasks.md` - Marked T017 complete and updated progress summary.

**Verification**:

* Command/check: `sed -n '1,190p' apps/web/tests/CockpitShell.test.tsx`
  * Result: PASS - single-tree assertions added.
  * Evidence: `expectSingleBottomPanelTrees` asserts `quest-board`, `orchestration-panel`, and `war-room-panel` each render exactly once; the War Room shell test calls the helper.
* UI product-surface check: N/A - test-only task.
* UI craft check: N/A - test-only task.

***

### Task T018 - Add host tests for explicit open, close, backdrop, Escape, focus restore, and listener cleanup

**Started**: 2026-06-29 17:19 **Completed**: 2026-06-29 17:19 **Duration**: less than 1 minute

**Notes**:

* Added `BottomRailExpansionHost.test.tsx`.
* Covered explicit open, close button, backdrop close, Escape close, focus restoration, Escape listener cleanup, singleton replacement, and same-instance child state preservation.
* Removed a React contentEditable fixture warning before recording the passing run.

**Files Changed**:

* `apps/web/tests/BottomRailExpansionHost.test.tsx` - Added host behavior tests.
* `.spec_system/specs/phase21-session02-shared-expansion-host/implementation-notes.md` - Recorded T018 evidence.
* `.spec_system/specs/phase21-session02-shared-expansion-host/tasks.md` - Marked T018 complete and updated progress summary.

**Verification**:

* Command/check: `npm test -- apps/web/tests/BottomRailExpansionHost.test.tsx`
  * Result: PASS - focused host test file passed.
  * Evidence: Vitest reported `apps/web/tests/BottomRailExpansionHost.test.tsx (15 tests)` and `Test Files 1 passed (1)`, `Tests 15 passed (15)`.
* UI product-surface check: N/A - test-only task.
* UI craft check: N/A - test-only task.

***

### Task T019 - Add host tests for guarded interactive descendants

**Started**: 2026-06-29 17:19 **Completed**: 2026-06-29 17:19 **Duration**: less than 1 minute

**Notes**:

* Added a guarded-click matrix for button, link, input, textarea, select, role button, role tab, contenteditable, and `[data-no-panel-expand]` descendants.
* Added a positive background-click case to prove non-interactive panel content can still open the focused surface.

**Files Changed**:

* `apps/web/tests/BottomRailExpansionHost.test.tsx` - Added guarded descendant tests.
* `.spec_system/specs/phase21-session02-shared-expansion-host/implementation-notes.md` - Recorded T019 evidence.
* `.spec_system/specs/phase21-session02-shared-expansion-host/tasks.md` - Marked T019 complete and updated progress summary.

**Verification**:

* Command/check: `npm test -- apps/web/tests/BottomRailExpansionHost.test.tsx`
  * Result: PASS - guard tests passed as part of the focused host run.
  * Evidence: Vitest reported `apps/web/tests/BottomRailExpansionHost.test.tsx (15 tests)` and `Tests 15 passed (15)`, including guarded descendant cases.
* UI product-surface check: N/A - test-only task.
* UI craft check: N/A - test-only task.

***

### Task T020 - Add App shell tests for expanded surface ids, replacement, shell notes, and single mounted panel instances

**Started**: 2026-06-29 17:19 **Completed**: 2026-06-29 17:19 **Duration**: less than 1 minute

**Notes**:

* Added an App-level bottom rail expansion test in `CockpitShell.test.tsx`.
* Covered Quest Board, Orchestration, and War Room expanded test ids, one-at-a-time replacement, close recovery with focus restore, shell state-note preservation, and single mounted panel assertions.

**Files Changed**:

* `apps/web/tests/CockpitShell.test.tsx` - Added App-level bottom rail expansion host coverage.
* `.spec_system/specs/phase21-session02-shared-expansion-host/implementation-notes.md` - Recorded T020 evidence.
* `.spec_system/specs/phase21-session02-shared-expansion-host/tasks.md` - Marked T020 complete and updated progress summary.

**Verification**:

* Command/check: `npm test -- apps/web/tests/CockpitShell.test.tsx`
  * Result: PASS - Cockpit shell test file passed.
  * Evidence: Vitest reported `apps/web/tests/CockpitShell.test.tsx (8 tests)` and `Tests 8 passed (8)`.
* UI product-surface check: PASS - App-level assertions verify product-facing dialogs named `Quest Board`, `Orchestration`, and `War Room`; no debug/scaffolding copy was introduced.
* UI craft check: PASS - Tests preserve shell notes and one mounted panel instance per bottom rail surface.

***

### Task T021 - Run focused host and shell tests

**Started**: 2026-06-29 17:19 **Completed**: 2026-06-29 17:19 **Duration**: less than 1 minute

**Notes**:

* Ran the exact focused test set from the task checklist.
* Confirmed the new host tests, App shell tests, and existing mobile accessibility coverage pass together.

**Files Changed**:

* `.spec_system/specs/phase21-session02-shared-expansion-host/implementation-notes.md` - Recorded T021 evidence.
* `.spec_system/specs/phase21-session02-shared-expansion-host/tasks.md` - Marked T021 complete and updated progress summary.

**Verification**:

* Command/check: `npm test -- apps/web/tests/BottomRailExpansionHost.test.tsx apps/web/tests/CockpitShell.test.tsx apps/web/tests/MobileAccessibility.test.tsx`
  * Result: PASS - focused test set passed.
  * Evidence: Vitest reported `Test Files 3 passed (3)` and `Tests 31 passed (31)`.
* UI product-surface check: PASS - Mobile Accessibility and Cockpit Shell coverage passed with the new host wrapper.
* UI craft check: PASS - Focused tests include close/focus recovery, shell notes, and single mounted panel checks.

***

### Task T022 - Run web typecheck and ASCII/LF validation

**Started**: 2026-06-29 17:19 **Completed**: 2026-06-29 17:19 **Duration**: less than 1 minute

**Notes**:

* Ran the web workspace typecheck successfully.
* Ran Biome formatting on changed TSX files and reran focused tests and typecheck afterward.
* Validated ASCII-only content and absence of CRLF characters across changed source, test, and session artifact files.

**Files Changed**:

* `.spec_system/specs/phase21-session02-shared-expansion-host/implementation-notes.md` - Recorded T022 evidence.
* `.spec_system/specs/phase21-session02-shared-expansion-host/tasks.md` - Marked T022 complete, updated progress summary, and completed the checklist.

**Verification**:

* Command/check: `npm run typecheck --workspace @factionos/web`
  * Result: PASS - web TypeScript project typechecked.
  * Evidence: `tsc -b --noEmit` exited successfully.
* Command/check: `npx biome format --write apps/web/src/components/BottomRailExpansionHost.tsx apps/web/src/App.tsx apps/web/tests/BottomRailExpansionHost.test.tsx apps/web/tests/CockpitShell.test.tsx`
  * Result: PASS - formatting completed.
  * Evidence: Biome reported `Formatted 4 files in 5ms. Fixed 2 files.`
* Command/check: `npm test -- apps/web/tests/BottomRailExpansionHost.test.tsx apps/web/tests/CockpitShell.test.tsx apps/web/tests/MobileAccessibility.test.tsx`
  * Result: PASS - focused test set still passed after formatting.
  * Evidence: Vitest reported `Test Files 3 passed (3)` and `Tests 31 passed (31)`.
* Command/check: `perl -ne 'if (/[^\\x00-\\x7F]/) { print "$ARGV:$.: non-ascii\\n"; $bad=1 } END { exit($bad ? 1 : 0) }' apps/web/src/components/BottomRailExpansionHost.tsx apps/web/src/App.tsx apps/web/tests/BottomRailExpansionHost.test.tsx apps/web/tests/CockpitShell.test.tsx .spec_system/specs/phase21-session02-shared-expansion-host/tasks.md .spec_system/specs/phase21-session02-shared-expansion-host/implementation-notes.md`
  * Result: PASS - ASCII-only validation passed.
  * Evidence: Command exited 0 with no output.
* Command/check: `grep -n $'\\r' apps/web/src/components/BottomRailExpansionHost.tsx apps/web/src/App.tsx apps/web/tests/BottomRailExpansionHost.test.tsx apps/web/tests/CockpitShell.test.tsx .spec_system/specs/phase21-session02-shared-expansion-host/tasks.md .spec_system/specs/phase21-session02-shared-expansion-host/implementation-notes.md`
  * Result: PASS - no CRLF characters found.
  * Evidence: Command produced no matches; grep exited 1 as expected for no matching CR characters.
* UI product-surface check: PASS - focused tests and code inspection confirm product-facing Focus/Close controls only, with no debug/scaffolding copy added.
* UI craft check: PASS - focused tests cover dialog semantics, close recovery, focus restore, state notes, and single mounted panel instances.

***


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://faction-os.gitbook.io/faction-os-docs/.spec_system/archive/sessions/phase21-session02-shared-expansion-host/implementation-notes.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
