> 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/phase23-session04-store-persistence-boundaries/spec.md).

# Session Specification

**Session ID**: `phase23-session04-store-persistence-boundaries` **Phase**: 23 - Legion I - Scanner Camps **Status**: Not Started **Created**: 2026-07-05 **Base Commit**: 83924f9b9b19aaa775fc91b6a9ea8cafef7b0e96 **Package**: apps/web **Package Stack**: TypeScript

***

## 1. Session Overview

This session proves that scanner camp state follows the existing browser-local projection persistence path in `apps/web`. Camp aggregates must persist only through `localStorage["factionos-game-v1"]`, fold through the normal `useGameStore.applyEvent` path, and remain covered by the existing reset and browser-local erasure behavior.

It is next because Phase 23 Sessions 01-03 have completed pure camp reconciliation, ranking selectors, and reducer folding. The Phase 23 dependency table marks Session 04 as executable after Session 03, and the next sessions depend on this persistence proof before they expose inspection, Banish, battlefield camps, or scan-state presentation.

The work is intentionally regression-focused. It should add store-level tests and only the minimal production wiring required if those tests expose a gap. It must not add a new storage authority, hosted state, trusted-erasure claim, protocol event, Quest Board camp focus, battlefield rendering, or Banish UI.

***

## 2. Objectives

1. Prove real `suggestion_update` snapshots reconcile camps through the normal store event path and persist through the existing projection key.
2. Prove replay and mock-mode paths cannot persist replayed or drill camp state.
3. Prove reset and browser-local erasure still clear the same projection key without creating a new storage key or claim boundary.
4. Extend focused web tests around persistence, storage inventory, hydration, and privacy-safe aggregate camp serialization.

***

## 3. Prerequisites

### Required Sessions

* [x] `phase23-session01-pure-camp-reconciliation` - provides aggregate camp coalescing, safe sector labels, HP, tiers, entrenchment, and stable cells.
* [x] `phase23-session03-projection-folding` - folds `suggestion_update` snapshots into `GameProjection.legion.camps` and `GameProjection.sectors`.
* [x] `phase22-session06-persistence-lifecycle` - provides projection storage key, debounced persistence, lifecycle flushing, replay and mock guards.
* [x] `phase22-session08-erasure-docs-and-validation` - provides reset and browser-local erasure coverage for `factionos-game-v1`.

### Required Tools Or Knowledge

* Zustand store event folding in `apps/web/src/store/useGameStore.ts`.
* Projection persistence helpers in `apps/web/src/lib/gameProjection.ts`.
* Browser-local erasure helpers in `apps/web/src/lib/localErasure.ts`.
* Vitest patterns in `apps/web/tests/gameProjectionStore.test.ts` and `apps/web/tests/localErasure.test.ts`.

### Environment Requirements

* Node 26.2.0 or newer and npm 11.16.0.
* Root workspace dependencies installed.
* Commands run from the repository root.

***

## 4. Scope

### In Scope (MVP)

* The web store can apply a real scanner `suggestion_update` through `applyEvent`, update `gameProjection.legion.camps`, and write the aggregate camp state to `localStorage["factionos-game-v1"]` only after the existing debounce, max-latency, or explicit flush paths.
* Persisted camp state contains bounded aggregate fields only: issue ids, safe sector labels, HP, tier, entrenchment counters, scar percentage, cells, and timestamps.
* Replay dispatch and explicit `replayingSinceMs` paths do not mutate or persist camp state.
* Mock-enabled scanner snapshots may fold drill camps in memory but do not change persisted projection storage.
* Reset and browser-local erasure continue to remove the existing projection key and do not broaden trusted-erasure wording.
* Tests prove no new camp-specific storage key is introduced.
* Server hydration snapshots after boot can reconcile camps through the normal non-replay store path.

### Out Of Scope (Deferred)

* Quest Board camp filter, inspection, or Banish behavior - Reason: Session 05 owns issue-card focus and existing dismiss action plumbing.
* Battlefield camp layer, Champion Mode rendering, or camp marker copy - Reason: Session 06 owns user-facing camp rendering.
* Dispatch Scouts, Golden Age, scan-not-run, scan-failed, or mock-drill UI labels - Reason: Session 07 owns scan lifecycle and clean-state presentation.
* Public-demo synthetic fronts - Reason: Phase 23 explicitly excludes this behavior and Session 04 only preserves the non-persistence boundary needed by any later drill surface.
* Hosted state, cross-device erasure, unified trusted erasure, or production hosted claims - Reason: current security posture keeps these as open no-claims.
* New protocol events, storage keys, server routes, XP, loot, banners, achievements, camp damage, or combat playback - Reason: Phase 23 guardrails forbid these expansions.

***

## 5. Technical Approach

### Architecture

Keep `apps/web/src/lib/gameProjection.ts` as the single derived game-state authority and `apps/web/src/store/useGameStore.ts` as the only web event folding path. Session 04 should first extend `apps/web/tests/gameProjectionStore.test.ts` with store-level camp scenarios using the existing fresh-module import helpers, fixed fake timers, `makeSuggestionUpdate`, `__flushGameProjectionPersist`, and `dispatchReplayEntries`.

The happy path should apply a `suggestion_update` with real codebase issues, assert that the store projection contains aggregate camps, flush pending projection writes, and assert the persisted JSON exists only under `GAME_PROJECTION_STORAGE_KEY`. Replay and mock tests should keep the current storage value unchanged while proving the in-memory behavior is honest: replay stays camp-inert, and mock mode may show drill camps in memory without writing them.

Reset and erasure coverage should remain anchored to the existing local browser cleanup path. `apps/web/tests/localErasure.test.ts` already proves `executeBrowserErasure` removes `GAME_PROJECTION_STORAGE_KEY`; add storage inventory assertions only if needed to prove there is no new camp-specific key. If tests reveal missing production behavior, make the smallest change in `useGameStore.ts`, `gameProjection.ts`, or `localErasure.ts` and keep it within the existing persistence and erasure contracts.

### Design Patterns

* Store-level regression: exercise `useGameStore.applyEvent` rather than duplicating reducer-only assertions already covered by Session 03.
* Existing storage authority: keep all camp state under `GAME_PROJECTION_STORAGE_KEY`.
* Progress-inert replay and mock paths: mutate neither durable camp state nor rewards when events are replayed or mock-enabled.
* Boundary inventory: prove local reset and browser erasure clear the same key without adding hosted or trusted-erasure language.

***

## 6. Deliverables

### Files To Create

| File | Purpose                                                      | Est. Lines |
| ---- | ------------------------------------------------------------ | ---------- |
| None | This session should modify existing app and test files only. | 0          |

### Files To Modify

| File                                         | Changes                                                                                                                    | Est. Lines |
| -------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | ---------- |
| `apps/web/tests/gameProjectionStore.test.ts` | Add store-level camp persistence, replay, mock, hydration, storage-key, reset, and aggregate-privacy regressions.          | \~260      |
| `apps/web/tests/localErasure.test.ts`        | Add or strengthen browser-local erasure inventory checks for the projection key and absence of camp-specific storage keys. | \~40       |
| `apps/web/src/store/useGameStore.ts`         | Minimal wiring only if store-level tests expose a persistence, flush, reset, or hydration gap.                             | \~40       |
| `apps/web/src/lib/gameProjection.ts`         | Minimal helper or parser adjustment only if persisted camp aggregate validation exposes a gap.                             | \~30       |
| `apps/web/src/lib/localErasure.ts`           | Minimal inventory adjustment only if erasure tests expose a missing projection-key cleanup path.                           | \~20       |

***

## 7. Success Criteria

### Functional Requirements

* [ ] Store `applyEvent` reconciles real scanner issues into camp aggregates through a normal non-replay `suggestion_update`.
* [ ] Real camp aggregates persist through `localStorage["factionos-game-v1"]` after existing projection flush behavior.
* [ ] Persisted projection JSON contains aggregate camp and sector fields but not raw issue messages, prompts, terminal output, provider payloads, file contents, absolute paths, environment values, or secrets.
* [ ] Replay dispatch and `replayingSinceMs` do not mutate or persist camps.
* [ ] Mock mode can fold drill camps in memory without changing persisted projection storage.
* [ ] `resetToSeed()` clears pending projection writes, resets in-memory camps, and removes the existing projection key.
* [ ] Browser-local erasure removes the existing projection key and does not claim hosted, cross-device, or unified trusted erasure.
* [ ] No new camp-specific storage key is introduced.
* [ ] A post-boot server hydration snapshot reconciles camps through the non-replay path.

### Testing Requirements

* [ ] Focused Vitest coverage is added for camp persistence, replay guard, mock guard, reset, erasure, no-new-storage-key, hydration, and aggregate-only privacy.
* [ ] Existing projection persistence tests still pass.
* [ ] Focused test command passes for `apps/web/tests/gameProjectionStore.test.ts` and `apps/web/tests/localErasure.test.ts`.

### Non-Functional Requirements

* [ ] No new protocol event, storage key, hosted dependency, or trusted-erasure claim is introduced.
* [ ] Existing debounced and lifecycle flush behavior remains intact.
* [ ] Store changes, if any, remain deterministic and local-browser scoped.

### Quality Gates

* [ ] All files ASCII-encoded.
* [ ] Unix LF line endings.
* [ ] Code follows project conventions.
* [ ] `npm --workspace @factionos/web run typecheck` passes.

***

## 8. Implementation Notes

### Working Assumptions

* Package context is `apps/web`: the analysis script reports Session 04 as an `apps/web` candidate, the Phase 23 PRD scopes the work to the web cockpit game layer, and all deliverables target web store, projection, erasure, and tests.
* Session 04 is executable now: the analysis script lists Sessions 01-03 as complete and the Phase 23 dependency table says Session 04 depends on Session 03.
* The existing `GAME_PROJECTION_STORAGE_KEY` constant is the authoritative storage key for camps: `apps/web/README_web.md`, `CONSIDERATIONS.md`, and `SECURITY-COMPLIANCE.md` all identify `factionos-game-v1` as the projection boundary.
* `gameProjectionStore.test.ts` is the right primary test file: it already owns hydration, applyEvent folding, replay, mock mode, persistence scheduling, lifecycle flush, and reset coverage.
* `localErasure.test.ts` is the right erasure test file: it already proves browser-local erasure removes `GAME_PROJECTION_STORAGE_KEY` and avoids raw value leakage.
* Minimal production changes may not be needed. Planning can proceed because the session objective is to add regression proof first and only repair production gaps found by those tests.

### Conflict Resolutions

* The master PRD planned-phase table still labels Phase 23 as Not Started, while the analysis script, live Phase 23 PRD, and state history show Phase 23 is in progress with Sessions 01-03 complete. The chosen interpretation is to trust the analysis script and live Phase 23 artifacts for workflow state.
* Session 04 asks for store persistence boundaries, while Sessions 05-07 define UI behavior for inspection, Banish, battlefield camps, scan states, and Golden Age. The chosen interpretation is to keep this session to store, persistence, erasure, and privacy regressions only.

### Key Considerations

* `gameProjection.ts` remains the single derived game-state authority.
* Camp persistence stays browser-local, aggregate-only, and under `factionos-game-v1`.
* Replay and mock mode stay progress-inert and must not mint durable camp progress.
* Browser-local erasure is not trusted unified erasure and must not close open hosted or erasure findings.

### Potential Challenges

* Pending debounced writes can mask failures: focused tests should use `__flushGameProjectionPersist()` and timer advancement consistently.
* Store module state can leak between tests: continue using fresh module imports, fake timers, and after-each cleanup patterns already present in `gameProjectionStore.test.ts`.
* Existing reducer tests already cover many camp semantics: avoid duplicating pure reducer coverage unless needed to prove store-level persistence.

### Relevant Considerations

* \[P22-apps/web] **Derived projection storage is narrow**: Camp persistence must stay inside the existing `factionos-game-v1` aggregate boundary and reuse reset/browser-local erasure behavior.
* \[P22-apps/web] **Game projection is the single derived authority**: Store-level camp tests should assert event folding through `gameProjection` selectors instead of adding parallel state.
* \[P22-apps/web] **Do not persist replay or drill progress**: Replay and mock scanner snapshots must not mint durable progress, rewards, kills, titles, essence, banners, War Tide, or persisted drill camps.
* \[P08] **Full trusted erasure remains no-claim**: Browser-local cleanup coverage must not broaden erasure claims across archives, logs, backups, hosted surfaces, or future integrations.
* \[P07] **Redaction is boundary-specific**: Persisted projection, tests, and evidence must keep raw commands, terminal output, file contents, provider payloads, secrets, broad absolute paths, and raw issue text out of broad surfaces.

### Behavioral Quality Focus

Checklist active: Yes Top behavioral risks for this session:

* A real scanner snapshot persists outside the existing projection key or creates a second storage authority.
* Replay or mock-mode traffic writes durable camps and makes drill state look like progress.
* Reset or browser-local erasure leaves pending camp writes behind or broadens trusted-erasure claims without evidence.

***

## 9. Testing Strategy

### Unit Tests

* Add `gameProjectionStore.test.ts` coverage for real scanner camp persistence, replay no-persist behavior, mock no-persist behavior, storage-key inventory, hydration, reset cleanup, and aggregate-only serialization.
* Add or strengthen `localErasure.test.ts` coverage for projection-key cleanup and absence of camp-specific storage keys.

### Integration Tests

* Use `useGameStore.applyEvent` with typed `suggestion_update` snapshots so tests exercise the real store reducer folding and persistence scheduling path.
* Use `dispatchReplayEntries` for replay scenarios so replay flags match the shared replay helper behavior.

### Runtime Verification

* Run `npm --workspace @factionos/web test -- gameProjectionStore.test.ts localErasure.test.ts`.
* Run `npm --workspace @factionos/web run typecheck`.

### Edge Cases

* Empty or clean scanner snapshots should not create fake camps.
* Replayed snapshots should not mutate camps even when they contain newer issue ids.
* Mock-enabled snapshots may create in-memory drill camps but must leave any persisted projection byte-for-byte unchanged.
* Reset should clear pending writes before timers can persist stale camp state.
* Browser-local erasure should tolerate duplicate idempotency keys and disabled storage without leaking raw projection values.

***

## 10. Dependencies

### Other Sessions

* Depends on: `phase23-session03-projection-folding`.
* Depended by: `phase23-session08-validation-and-documentation`.
* Related later UI sessions: `phase23-session05-quest-board-camp-focus`, `phase23-session06-battlefield-camp-layer`, and `phase23-session07-scanner-and-golden-age-states`.

***

## 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/phase23-session04-store-persistence-boundaries/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.
