> 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-session03-projection-folding/implementation-notes.md).

# Implementation Notes

**Session ID**: `phase23-session03-projection-folding` **Package**: apps/web **Started**: 2026-07-05 18:03 **Last Updated**: 2026-07-05 18:13

***

## Session Progress

| Metric              | Value     |
| ------------------- | --------- |
| Tasks Completed     | 20 / 20   |
| Estimated Remaining | 0 minutes |
| Blockers            | 0         |

***

## Task Log

### 2026-07-05 - Session Start

**Environment verified**:

* [x] Prerequisites confirmed for `.spec_system/` and `apps/web`
* [x] Tools available: npm 11.16.0, git 2.43.0, jq 1.7
* [x] Directory structure ready
* [x] Required Node 26.2.0 runtime available through `npx node@26.2.0`

***

### Task T001 - Verify reducer no-op, alert focus helpers, and reconciliation exports

**Started**: 2026-07-05 18:02 **Completed**: 2026-07-05 18:04 **Duration**: 2 minutes

**Notes**:

* Confirmed `reduceSuggestionUpdate` currently returns the original state and is dispatched for `suggestion_update`.
* Confirmed alert focus is finalized through `finalizeProjectionUpdate` and `recomputeAlertFocus`, with `newCamp` lower priority than flares, gates, siege, ram, and skirmishers.
* Confirmed `reconcileLegionCamps` exports aggregate camps, sectors, new camp ids, and changed camp ids.

**Files Changed**:

* `.spec_system/specs/phase23-session03-projection-folding/implementation-notes.md` - recorded setup evidence.

**Verification**:

* Command/check: `rg -n "suggestion_update|reduceSuggestion|alertFocus|finalizeProjectionUpdate|recomputeAlertFocus|legion" apps/web/src/lib/gameProjection.ts`
  * Result: PASS - found the current reducer no-op and focus helper flow.
  * Evidence: `reduceSuggestionUpdate` is a no-op; dispatch routes `suggestion_update` to it.
* Command/check: `sed -n '1,620p' apps/web/src/lib/legionCamps.ts`
  * Result: PASS - inspected Session 01 helper exports and aggregate-only result shape.
  * Evidence: `reconcileLegionCamps` accepts issues, previous camps, previous sectors, and timestamp.
* UI product-surface check: N/A - reducer/test verification only.
* UI craft check: N/A - no UI surface changed.

***

### Task T002 - Verify suggestion snapshot factories and no-progress regression assertions

**Started**: 2026-07-05 18:02 **Completed**: 2026-07-05 18:04 **Duration**: 2 minutes

**Notes**:

* Confirmed `makeCodebaseIssue`, `makeSuggestionUpdate`, and reconnect snapshot helpers are available in `gameProjection.test.ts`.
* Confirmed the existing no-progress assertion currently expects camp no-op behavior and must be narrowed to links/rewards/privacy while camps reconcile.

**Files Changed**:

* `.spec_system/specs/phase23-session03-projection-folding/implementation-notes.md` - recorded setup evidence.

**Verification**:

* Command/check: `rg -n "suggestion_update|SuggestionUpdate|codebaseIssues|camp|replay|mock|lastEventAt|pendingLinks|campLinks" apps/web/tests/gameProjection.test.ts`
  * Result: PASS - located snapshot factories, persistence guard tests, and camp/link assertions.
  * Evidence: existing snapshot test covers links, camps, rewards, and sensitive prompt exclusion.
* Command/check: `sed -n '430,530p' apps/web/tests/gameProjection.test.ts` and `sed -n '1360,1490p' apps/web/tests/gameProjection.test.ts`
  * Result: PASS - inspected helper definitions and current regression body.
  * Evidence: `makeSuggestionUpdate` defaults `codebaseIssues` to an empty snapshot and sets `updatedAt`.
* UI product-surface check: N/A - test verification only.
* UI craft check: N/A - no UI surface changed.

***

### Task T003 - Verify focused web test and typecheck commands

**Started**: 2026-07-05 18:03 **Completed**: 2026-07-05 18:04 **Duration**: 1 minute

**Notes**:

* Confirmed root Vitest config includes web tests under the `web` project with `happy-dom`.
* Confirmed `apps/web/package.json` has `typecheck` but no package-local `test` script; the focused test command is `npm --workspace @factionos/web exec -- vitest run tests/gameProjection.test.ts`.
* Confirmed an ephemeral Node 26.2.0 runtime is available for final gates despite the shell default being Node 24.14.0.

**Files Changed**:

* `.spec_system/specs/phase23-session03-projection-folding/implementation-notes.md` - recorded setup evidence.

**Verification**:

* Command/check: `sed -n '1,220p' package.json`, `sed -n '1,220p' apps/web/package.json`, and `sed -n '1,220p' vitest.config.ts`
  * Result: PASS - verified root test script, web typecheck script, and focused web test inclusion.
  * Evidence: root `npm test` maps to Vitest; web package exposes `typecheck`.
* Command/check: `npm --workspace @factionos/web exec -- vitest run tests/gameProjection.test.ts`
  * Result: PASS - baseline focused test passed before implementation.
  * Evidence: 1 test file passed, 26 tests passed.
* Command/check: `npx -y node@26.2.0 -v`
  * Result: PASS - required Node runtime can be invoked for gates.
  * Evidence: output was `v26.2.0`.
* UI product-surface check: N/A - command verification only.
* UI craft check: N/A - no UI surface changed.

***

### Task T004 - Import reconciliation helper and update suggestion reducer signature

**Started**: 2026-07-05 18:04 **Completed**: 2026-07-05 18:06 **Duration**: 2 minutes

**Notes**:

* Imported `reconcileLegionCamps` into `gameProjection.ts`.
* Updated `reduceSuggestionUpdate` to receive the typed event and reduce context, with an explicit replay guard before reconciliation.

**Files Changed**:

* `apps/web/src/lib/gameProjection.ts` - added helper import, event/context parameters, replay guard, and dispatch update.

**Verification**:

* Command/check: `npx -y -p node@26.2.0 -p npm@11.16.0 npm --workspace @factionos/web run typecheck`
  * Result: PASS - web package TypeScript compilation passed.
  * Evidence: `tsc -b --noEmit` exited 0.
* UI product-surface check: N/A - reducer-only change.
* UI craft check: N/A - no UI surface changed.

***

### Task T005 - Add reducer-local camp record equality helpers

**Started**: 2026-07-05 18:04 **Completed**: 2026-07-05 18:06 **Duration**: 2 minutes

**Notes**:

* Added camp equality for hp, maxHp, tier, entrenchment, scar percentage, and sorted issue ids.
* Added camp record equality so fresh helper outputs do not force projection updates when aggregate camp records are unchanged.

**Files Changed**:

* `apps/web/src/lib/gameProjection.ts` - added `areCampsEqual` and `areCampRecordsEqual`.

**Verification**:

* Command/check: `npx -y -p node@26.2.0 -p npm@11.16.0 npm --workspace @factionos/web run typecheck`
  * Result: PASS - helper types compile under the required runtime.
  * Evidence: `tsc -b --noEmit` exited 0.
* UI product-surface check: N/A - reducer-only change.
* UI craft check: N/A - no UI surface changed.

***

### Task T006 - Add reducer-local sector record equality helpers

**Started**: 2026-07-05 18:04 **Completed**: 2026-07-05 18:06 **Duration**: 2 minutes

**Notes**:

* Added sector equality for safe labels, cells, fog percentages, and touch timestamps.
* Added sector record equality so sector-only changes are detected and unchanged sector records preserve projection identity.

**Files Changed**:

* `apps/web/src/lib/gameProjection.ts` - added `areSectorsEqual` and `areSectorRecordsEqual`.

**Verification**:

* Command/check: `npx -y -p node@26.2.0 -p npm@11.16.0 npm --workspace @factionos/web run typecheck`
  * Result: PASS - helper types compile under the required runtime.
  * Evidence: `tsc -b --noEmit` exited 0.
* UI product-surface check: N/A - reducer-only change.
* UI craft check: N/A - no UI surface changed.

***

### Task T007 - Add scanner snapshot test helpers

**Started**: 2026-07-05 18:06 **Completed**: 2026-07-05 18:11 **Duration**: 5 minutes

**Notes**:

* Added helpers for scanner issues, scanner snapshots, camp lookup by safe sector label, and large issue sets.

**Files Changed**:

* `apps/web/tests/gameProjection.test.ts` - added reusable scanner-camp test helpers.

**Verification**:

* Command/check: `npm --workspace @factionos/web exec -- vitest run tests/gameProjection.test.ts`
  * Result: PASS - helper-backed focused tests compile and run.
  * Evidence: 1 test file passed, 31 tests passed.
* UI product-surface check: N/A - test helper change only.
* UI craft check: N/A - no UI surface changed.

***

### Task T008 - Replace suggestion update no-op with camp reconciliation

**Started**: 2026-07-05 18:04 **Completed**: 2026-07-05 18:11 **Duration**: 7 minutes

**Notes**:

* `suggestion_update` now passes current scanner issues, prior camps, prior sectors, and normalized snapshot timestamp into `reconcileLegionCamps`.
* Invalid `event.updatedAt` falls back to reducer context time through existing timestamp helpers.

**Files Changed**:

* `apps/web/src/lib/gameProjection.ts` - replaced camp no-op with reconciliation.

**Verification**:

* Command/check: `npm --workspace @factionos/web exec -- vitest run tests/gameProjection.test.ts`
  * Result: PASS - scanner folding tests passed.
  * Evidence: 31 tests passed, including aggregate camp creation.
* UI product-surface check: N/A - reducer-only change.
* UI craft check: N/A - no UI surface changed.

***

### Task T009 - Apply reconciled camps and sectors only on aggregate differences

**Started**: 2026-07-05 18:04 **Completed**: 2026-07-05 18:11 **Duration**: 7 minutes

**Notes**:

* Added record equality checks before returning a new projection.
* Empty no-op snapshots preserve object identity and unchanged aggregate records do not update projection state.

**Files Changed**:

* `apps/web/src/lib/gameProjection.ts` - guarded projection updates with camp and sector record equality.
* `apps/web/tests/gameProjection.test.ts` - asserted empty snapshot identity and deterministic aggregate folding.

**Verification**:

* Command/check: `npm --workspace @factionos/web exec -- vitest run tests/gameProjection.test.ts`
  * Result: PASS - no-op and deterministic folding assertions passed.
  * Evidence: 31 tests passed.
* UI product-surface check: N/A - reducer/test change only.
* UI craft check: N/A - no UI surface changed.

***

### Task T010 - Update lastEventAt only on scanner aggregate changes

**Started**: 2026-07-05 18:04 **Completed**: 2026-07-05 18:11 **Duration**: 7 minutes

**Notes**:

* `lastEventAt` updates to the normalized snapshot timestamp only inside the changed-record branch.
* Replay and no-op snapshots return the original state before timestamp mutation.

**Files Changed**:

* `apps/web/src/lib/gameProjection.ts` - scoped `lastEventAt` update to camp/sector changes.
* `apps/web/tests/gameProjection.test.ts` - asserted changed scanner fold timestamp and empty snapshot identity.

**Verification**:

* Command/check: `npm --workspace @factionos/web exec -- vitest run tests/gameProjection.test.ts`
  * Result: PASS - timestamp and no-op coverage passed.
  * Evidence: 31 tests passed.
* UI product-surface check: N/A - reducer/test change only.
* UI craft check: N/A - no UI surface changed.

***

### Task T011 - Recompute alert focus through existing priority helpers

**Started**: 2026-07-05 18:04 **Completed**: 2026-07-05 18:11 **Duration**: 7 minutes

**Notes**:

* Scanner camp updates flow through `finalizeProjectionUpdate`.
* New camp focus stays below flares, gates, siege, ram, and skirmishers.

**Files Changed**:

* `apps/web/src/lib/gameProjection.ts` - used existing projection finalization path after scanner reconciliation.
* `apps/web/tests/gameProjection.test.ts` - added scanner-specific alert focus priority coverage.

**Verification**:

* Command/check: `npm --workspace @factionos/web exec -- vitest run tests/gameProjection.test.ts`
  * Result: PASS - alert priority assertions passed.
  * Evidence: 31 tests passed.
* UI product-surface check: N/A - reducer/test change only.
* UI craft check: N/A - no UI surface changed.

***

### Task T012 - Preserve unrelated projection fields during scanner reconciliation

**Started**: 2026-07-05 18:04 **Completed**: 2026-07-05 18:11 **Duration**: 7 minutes

**Notes**:

* Scanner reconciliation replaces only `sectors`, `legion.camps`, and `lastEventAt`.
* Pending links, camp links, heroes, essence, buildings, War Tide, council, nemeses, season, attention, flare ceremony, and non-camp Legion records stay isolated.

**Files Changed**:

* `apps/web/src/lib/gameProjection.ts` - preserved non-camp projection branches by spreading existing state and Legion fields.
* `apps/web/tests/gameProjection.test.ts` - added field isolation regression coverage.

**Verification**:

* Command/check: `npm --workspace @factionos/web exec -- vitest run tests/gameProjection.test.ts`
  * Result: PASS - unrelated-field isolation assertions passed.
  * Evidence: 31 tests passed.
* UI product-surface check: N/A - reducer/test change only.
* UI craft check: N/A - no UI surface changed.

***

### Task T013 - Keep replayed suggestion updates camp-inert

**Started**: 2026-07-05 18:04 **Completed**: 2026-07-05 18:11 **Duration**: 7 minutes

**Notes**:

* Added an explicit `ctx.isReplaying` guard before scanner reconciliation.
* Replayed scanner snapshots return the same projection object.

**Files Changed**:

* `apps/web/src/lib/gameProjection.ts` - added replay rejection guard.
* `apps/web/tests/gameProjection.test.ts` - added replay camp-inert coverage.

**Verification**:

* Command/check: `npm --workspace @factionos/web exec -- vitest run tests/gameProjection.test.ts`
  * Result: PASS - replay snapshot assertions passed.
  * Evidence: 31 tests passed.
* UI product-surface check: N/A - reducer/test change only.
* UI craft check: N/A - no UI surface changed.

***

### Task T014 - Allow mock-enabled non-replay snapshots to fold in memory

**Started**: 2026-07-05 18:04 **Completed**: 2026-07-05 18:11 **Duration**: 7 minutes

**Notes**:

* Mock mode is not rejected by the reducer, so non-replay scanner snapshots can fold camps in memory.
* Persistence guard semantics remain unchanged through `shouldPersistGameProjection`.

**Files Changed**:

* `apps/web/src/lib/gameProjection.ts` - kept mock mode out of the scanner replay guard.
* `apps/web/tests/gameProjection.test.ts` - added mock in-memory fold and persistence guard assertions.

**Verification**:

* Command/check: `npm --workspace @factionos/web exec -- vitest run tests/gameProjection.test.ts`
  * Result: PASS - mock fold and persistence guard assertions passed.
  * Evidence: 31 tests passed.
* UI product-surface check: N/A - reducer/test change only.
* UI craft check: N/A - no UI surface changed.

***

### Task T015 - Add creation, HP, tier, sector stability, and sorted issue id tests

**Started**: 2026-07-05 18:06 **Completed**: 2026-07-05 18:11 **Duration**: 5 minutes

**Notes**:

* Added a three-issue, two-sector scanner snapshot test.
* Covered severity HP math, fort/camp tiers, stable safe labels, stable cells, deterministic output, and sorted issue ids.

**Files Changed**:

* `apps/web/tests/gameProjection.test.ts` - added scanner camp creation coverage.

**Verification**:

* Command/check: `npm --workspace @factionos/web exec -- vitest run tests/gameProjection.test.ts`
  * Result: PASS - creation and stability assertions passed.
  * Evidence: 31 tests passed.
* UI product-surface check: N/A - reducer test only.
* UI craft check: N/A - no UI surface changed.

***

### Task T016 - Add coalescing, entrenchment, membership, and removal tests

**Started**: 2026-07-05 18:06 **Completed**: 2026-07-05 18:11 **Duration**: 5 minutes

**Notes**:

* Added coverage for budgeted large snapshot coalescing into bounded camps plus Borderlands.
* Added repeated-snapshot entrenchment increments, changed-membership reset, and empty-snapshot camp cleanup coverage.

**Files Changed**:

* `apps/web/tests/gameProjection.test.ts` - added coalescing and reconciliation lifecycle tests.

**Verification**:

* Command/check: `npm --workspace @factionos/web exec -- vitest run tests/gameProjection.test.ts`
  * Result: PASS - coalescing, entrenchment, membership, and removal assertions passed.
  * Evidence: 31 tests passed.
* UI product-surface check: N/A - reducer test only.
* UI craft check: N/A - no UI surface changed.

***

### Task T017 - Add replay and mock snapshot tests

**Started**: 2026-07-05 18:06 **Completed**: 2026-07-05 18:11 **Duration**: 5 minutes

**Notes**:

* Added tests proving replayed snapshots preserve object identity and do not mutate camps.
* Added tests proving mock-enabled non-replay snapshots fold camps in memory while persistence remains denied.

**Files Changed**:

* `apps/web/tests/gameProjection.test.ts` - added replay and mock scanner coverage.

**Verification**:

* Command/check: `npm --workspace @factionos/web exec -- vitest run tests/gameProjection.test.ts`
  * Result: PASS - replay/mock assertions passed.
  * Evidence: 31 tests passed.
* UI product-surface check: N/A - reducer test only.
* UI craft check: N/A - no UI surface changed.

***

### Task T018 - Add rewards, links, and unrelated field isolation tests

**Started**: 2026-07-05 18:06 **Completed**: 2026-07-05 18:11 **Duration**: 5 minutes

**Notes**:

* Added regression coverage proving scanner reconciliation does not mutate progress rewards, pending links, camp links, camp scar damage, or unrelated projection branches.

**Files Changed**:

* `apps/web/tests/gameProjection.test.ts` - added aggregate-only field isolation test.

**Verification**:

* Command/check: `npm --workspace @factionos/web exec -- vitest run tests/gameProjection.test.ts`
  * Result: PASS - reward/link/isolation assertions passed.
  * Evidence: 31 tests passed.
* UI product-surface check: N/A - reducer test only.
* UI craft check: N/A - no UI surface changed.

***

### Task T019 - Add alert priority and scanner privacy tests

**Started**: 2026-07-05 18:06 **Completed**: 2026-07-05 18:11 **Duration**: 5 minutes

**Notes**:

* Added scanner-specific alert priority assertions for new camp focus and higher-priority focus preservation.
* Added privacy assertions excluding raw issue prompts, messages, absolute paths, event bodies, terminal output, provider payloads, file contents, environment values, and secrets from projection JSON.

**Files Changed**:

* `apps/web/tests/gameProjection.test.ts` - added alert priority and privacy coverage.

**Verification**:

* Command/check: `npm --workspace @factionos/web exec -- vitest run tests/gameProjection.test.ts`
  * Result: PASS - alert and privacy assertions passed.
  * Evidence: 31 tests passed.
* UI product-surface check: N/A - reducer test only.
* UI craft check: N/A - no UI surface changed.

***

## Checkpoints

### Checkpoint 1 - Before Final Gates

**Recorded**: 2026-07-05 18:11

* Test/build command: `npm --workspace @factionos/web exec -- vitest run tests/gameProjection.test.ts` - PASS, 31 tests.
* Typecheck command: `npx -y -p node@26.2.0 -p npm@11.16.0 npm --workspace @factionos/web run typecheck` - PASS, `tsc -b --noEmit`.
* Scope check: re-read Session 03 objectives and confirmed changes remain limited to reducer folding and focused tests.
* Next task: T020 final focused Vitest, web typecheck, and ASCII/LF validation.

***

### Task T020 - Run focused Vitest, web typecheck, and ASCII/LF validation

**Started**: 2026-07-05 18:12 **Completed**: 2026-07-05 18:13 **Duration**: 1 minute

**Notes**:

* Ran formatter before final gates so checks covered the final edited code shape.
* Final gates passed for scanner reducer tests, web typecheck, ASCII-only content, LF line endings, and whitespace.

**Files Changed**:

* `apps/web/src/lib/gameProjection.ts` - final formatted reducer implementation.
* `apps/web/tests/gameProjection.test.ts` - final formatted scanner test coverage.
* `.spec_system/specs/phase23-session03-projection-folding/implementation-notes.md` - final task evidence.
* `.spec_system/specs/phase23-session03-projection-folding/tasks.md` - final checklist status.

**Verification**:

* Command/check: `npx biome format --write apps/web/src/lib/gameProjection.ts apps/web/tests/gameProjection.test.ts`
  * Result: PASS - formatter completed.
  * Evidence: formatted 2 files and fixed 1 file.
* Command/check: `npm --workspace @factionos/web exec -- vitest run tests/gameProjection.test.ts`
  * Result: PASS - focused scanner projection tests passed.
  * Evidence: 1 test file passed, 31 tests passed.
* Command/check: `npx -y -p node@26.2.0 -p npm@11.16.0 npm --workspace @factionos/web run typecheck`
  * Result: PASS - web package typecheck passed under required Node 26.2.0 runtime.
  * Evidence: `tsc -b --noEmit` exited 0.
* Command/check: `perl -ne 'print "$ARGV:$.:$_" if /[^\x00-\x7F]/' apps/web/src/lib/gameProjection.ts apps/web/tests/gameProjection.test.ts .spec_system/specs/phase23-session03-projection-folding/implementation-notes.md .spec_system/specs/phase23-session03-projection-folding/tasks.md`
  * Result: PASS - no non-ASCII output.
  * Evidence: command exited 0 with empty output.
* Command/check: `perl -ne 'print "$ARGV:$.:CRLF\n" if /\r/' apps/web/src/lib/gameProjection.ts apps/web/tests/gameProjection.test.ts .spec_system/specs/phase23-session03-projection-folding/implementation-notes.md .spec_system/specs/phase23-session03-projection-folding/tasks.md`
  * Result: PASS - no CRLF output.
  * Evidence: command exited 0 with empty output.
* Command/check: `git diff --check`
  * Result: PASS - no whitespace errors.
  * Evidence: command exited 0 with empty output.
* UI product-surface check: N/A - no user-facing UI changed.
* UI craft check: N/A - no UI surface changed.

***


---

# 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-session03-projection-folding/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.
