> 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-session01-expansion-state-foundation/spec.md).

# Session Specification

**Session ID**: `phase21-session01-expansion-state-foundation` **Phase**: 21 - Bottom Rail Focused Surface Expansion **Status**: Not Started **Created**: 2026-06-29 **Package**: apps/web **Package Stack**: TypeScript

***

## 1. Session Overview

This session establishes the canonical transient state model for the focused bottom-rail expansion work in Phase 21. It adds one typed surface identifier for Quest Board, Orchestration, and War Room, then stores the currently expanded bottom-rail surface in the existing web settings store alongside the other transient overlay booleans.

This is the first executable Phase 21 session because every later expansion, shortcut, overlay, accessibility, and regression session depends on one shared source of truth for opening, replacing, and clearing the focused bottom-rail surface. The user-facing panel expansion is intentionally deferred; the proved outcome here is the state foundation and tests that prevent later sessions from inventing parallel booleans or persisted preferences.

The implementation must keep expansion state out of the durable settings snapshot and update reset helpers so the state cannot leak between tests or browser erasure flows.

***

## 2. Objectives

1. Define a typed bottom-rail surface id limited to Quest Board, Orchestration, and War Room.
2. Add `expandedBottomRailSurfaceId` plus a setter to `apps/web/src/store/useSettingsStore.ts`.
3. Preserve the durable settings snapshot by excluding expansion state from persistence and hydration.
4. Add focused tests for default, set, clear, reset, hydration, and persistence behavior.

***

## 3. Prerequisites

### Required Sessions

* [x] `phase20-session11-release-evidence-and-event-privacy` - Completed the prior phase release evidence and no-overclaim boundaries that Phase 21 must preserve.

### Required Tools Or Knowledge

* Node 26.2.0+ and npm 11.16.0 workspace commands.
* Zustand store semantics, especially partial `set(...)` merge behavior.
* Vitest and Testing Library reset patterns used by `apps/web/tests`.

### Environment Requirements

* Local repository checkout with npm dependencies installed.
* Browser `localStorage` stubs available in the existing web test setup.

***

## 4. Scope

### In Scope (MVP)

* Web shell users can rely on one canonical bottom-rail expansion state for Quest Board, Orchestration, and War Room - implemented as transient Zustand state.
* Future Phase 21 surfaces can open, replace, and clear the expanded bottom-rail surface through a typed setter - implemented in `useSettingsStore`.
* Settings persistence remains limited to durable user preferences - expansion state is excluded from `factionos-settings-v1`.
* Focused store and reset-helper tests prove expansion state defaults, set/clear behavior, reset behavior, stale snapshot exclusion, and test isolation.

### Out Of Scope (Deferred)

* Rendering the shared expanded shell host - Reason: owned by Session 02 after the state source exists.
* Changing Orchestration labels, shortcuts, or detail portal behavior - Reason: owned by Sessions 03 and 04.
* Quest Board or War Room panel behavior changes - Reason: owned by Session 05.
* Browser viewport accessibility proof for expanded panels - Reason: owned by Session 06 after the UI exists.

***

## 5. Technical Approach

### Architecture

Use `apps/web/src/store/useSettingsStore.ts` as the single source for transient bottom-rail expansion state. The store already combines durable settings with non-persisted overlay booleans such as `replayPanelOpen`, `commandPaletteOpen`, `settingsDrawerOpen`, `heatmapOpen`, `standingsOpen`, `leaderboardOpen`, `toolUsageOpen`, and `complexityOverlayOpen`; the new field belongs in that transient group.

Keep durable defaults and transient defaults distinct enough that `loadPersistedSettings()` and `persist(...)` cannot accidentally write or hydrate `expandedBottomRailSurfaceId`. Because Zustand merges partial state by default, update `resetSettings()` and direct test reset helpers so transient fields are explicitly restored, including the new expansion state.

### Design Patterns

* Typed union state: Limits valid expansion ids to `quest-board`, `orchestration`, `war-room`, or `null`.
* Durable/transient separation: Keeps localStorage snapshots limited to durable preferences while still colocating shell overlay state.
* Focused regression tests: Uses real store execution and localStorage snapshots to prove behavior rather than relying on type declarations alone.

***

## 6. Deliverables

### Files To Create

| File | Purpose                                                           | Est. Lines |
| ---- | ----------------------------------------------------------------- | ---------- |
| None | This session modifies the existing settings store and tests only. | 0          |

### Files To Modify

| File                                             | Changes                                                                                           | Est. Lines |
| ------------------------------------------------ | ------------------------------------------------------------------------------------------------- | ---------- |
| `apps/web/src/store/useSettingsStore.ts`         | Add bottom-rail surface type, transient state, setter, reset handling, and persistence exclusion. | \~55       |
| `apps/web/src/components/LocalErasurePanel.tsx`  | Route browser erasure settings reset through the canonical reset path or transient defaults.      | \~10       |
| `apps/web/tests/useSettingsStore.test.ts`        | Add focused default, set, clear, reset, hydration, and persistence exclusion tests.               | \~80       |
| `apps/web/tests/settingsNotifications.test.ts`   | Update settings reset setup so expansion state cannot leak.                                       | \~5        |
| `apps/web/tests/SettingsAudio.test.tsx`          | Update settings reset setup so expansion state cannot leak.                                       | \~8        |
| `apps/web/tests/CockpitShell.test.tsx`           | Update cockpit reset setup so expansion state cannot leak into later shell tests.                 | \~5        |
| `apps/web/tests/MobileAccessibility.test.tsx`    | Update mobile reset setup so expansion state cannot leak.                                         | \~5        |
| `apps/web/tests/KeyboardShortcutsModal.test.tsx` | Update shortcut modal reset setup so expansion state cannot leak.                                 | \~5        |
| `apps/web/tests/CommandPalette.test.tsx`         | Update command palette reset setup so expansion state cannot leak.                                | \~5        |
| `apps/web/tests/keyboardShortcuts.test.ts`       | Update keyboard shortcut reset setup so expansion state cannot leak.                              | \~5        |
| `apps/web/tests/NoticeBoard.test.tsx`            | Update notice board reset setup so expansion state cannot leak.                                   | \~5        |
| `apps/web/tests/HeroRosterSort.test.tsx`         | Update roster reset setup so expansion state cannot leak.                                         | \~5        |
| `apps/web/tests/useNotifications.test.tsx`       | Update notification reset setup so expansion state cannot leak.                                   | \~5        |

***

## 7. Success Criteria

### Functional Requirements

* [ ] `expandedBottomRailSurfaceId` defaults to `null`.
* [ ] `setExpandedBottomRailSurfaceId(...)` accepts only `quest-board`, `orchestration`, `war-room`, or `null` at the TypeScript boundary.
* [ ] Setting one id replaces any previous id and setting `null` clears expansion state.
* [ ] `resetSettings()` clears expansion state without persisting it.
* [ ] `loadPersistedSettings()` ignores stale or malformed expansion fields in localStorage.

### Testing Requirements

* [ ] Unit tests written and passing for the settings store default, set, clear, reset, hydration, and persistence behavior.
* [ ] Reset-helper consumer tests updated so expansion state cannot leak between tests.
* [ ] Focused web tests and web typecheck completed.

### Non-Functional Requirements

* [ ] Expansion state remains transient UI state and is not stored in `factionos-settings-v1` or the theme mirror.
* [ ] No new hosted-service, execution, erasure, or formal accessibility claims are introduced.

### Quality Gates

* [ ] All files ASCII-encoded.
* [ ] Unix LF line endings.
* [ ] Code follows project conventions.
* [ ] Primary user-facing surfaces remain unchanged in this session.

***

## 8. Implementation Notes

### Working Assumptions

* Bottom-rail expansion state belongs in `useSettingsStore`: Phase 21 names `apps/web/src/store/useSettingsStore.ts`, and the file already owns transient shell overlay booleans, so planning can proceed without a new store.
* Session 01 should not render UI: The stub says the user-facing outcome is not visible yet and later sessions own the host and panel behavior, so this session should stop at state plus tests.

### Conflict Resolutions

* Store documentation calls the module a persistent user-preference store, but the same module already contains non-persisted overlay state. The chosen interpretation is to add expansion beside the existing transient fields while keeping durable snapshot typing and persistence limited to preferences.

### Key Considerations

* Zustand `set(defaults)` merges by default, so reset paths must explicitly clear transient fields instead of assuming omitted fields reset.
* `DEFAULT_SETTINGS` currently represents durable defaults. If transient defaults are introduced, they must not cause `loadPersistedSettings()` to return transient fields as durable settings.
* Direct test setup using `{ ...DEFAULT_SETTINGS }` must be updated where it can leave stale overlay state behind.

### Potential Challenges

* Test modules that import the store once may carry state between tests: use explicit reset setup and focused assertions.
* Stale localStorage snapshots may contain unknown fields: persistence tests should assert those fields are ignored and not re-written.
* Local erasure reset must clear UI shell state without broadening trusted-erasure claims.

### Relevant Considerations

* \[P20] **Release evidence is the execution claim source**: This session must not claim panel expansion is shipped until later UI sessions prove visible behavior.
* \[P20] **Broad privacy gates are release-critical**: The persisted settings snapshot must stay bounded and avoid adding transient shell state to durable browser storage.
* \[P03] **Stable docs are the current contract**: No stable docs change is needed here because no user-facing interaction path changes yet.

### Behavioral Quality Focus

Checklist active: Yes Top behavioral risks for this session:

* Transient state leaks into persisted localStorage snapshots.
* Reset paths leave stale expansion state that changes later shell tests or user flows.
* Invalid or stale persisted snapshots influence runtime expansion state.

***

## 9. Testing Strategy

### Unit Tests

* Add `useSettingsStore` tests for default `null`, valid id set, replacement, clear to `null`, reset to `null`, and persistence exclusion.
* Add hydration tests proving stale `expandedBottomRailSurfaceId` values in localStorage are ignored.

### Integration Tests

* Update existing settings, cockpit, command palette, keyboard, notice, roster, notification, and mobile reset-helper tests so they explicitly reset the new transient state.

### Runtime Verification

* No browser runtime verification is required in this session because no visible expanded surface is rendered.

### Edge Cases

* `resetSettings()` after an expanded id is set must clear the id even though durable defaults omit transient fields.
* A persisted snapshot containing `expandedBottomRailSurfaceId` must not hydrate the field or survive the next write.
* Direct test setup with `{ ...DEFAULT_SETTINGS }` must not leave a previous expanded id active.

***

## 10. Dependencies

### Other Sessions

* Depends on: Phase 20 complete.
* Depended by: `phase21-session02-shared-expansion-host`, `phase21-session03-orchestration-command-center-expansion`, `phase21-session04-shortcut-and-overlay-layering`, `phase21-session05-quest-board-and-war-room-parity`, `phase21-session06-responsive-layout-and-accessibility-pass`, `phase21-session07-final-regression-and-documentation`.

***

## Next Steps

Run the `implement` workflow step to begin implementation.


---

# 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-session01-expansion-state-foundation/spec.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.
