> 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/phase22-session04-camp-link-boundaries/spec.md).

# Session Specification

**Session ID**: `phase22-session04-camp-link-boundaries` **Phase**: 22 - Projection Foundation **Status**: Not Started **Created**: 2026-07-05 **Base Commit**: b2353754d7fd4bfe81642428f043a565e5794b6f **Package**: apps/web **Package Stack**: TypeScript

***

## 1. Session Overview

This session adds dormant camp-to-mission linkage readiness to the Phase 22 game projection reducer. The result lets a future real camp accept action provide a `campId`, `heroId`, and verbatim prompt, then bind the next matching `mission_start` to a camp without adding UI, protocol fields, or fake Quest Board accepts.

It is next because Sessions 01-03 established the v1 projection shape, attention reducer, and live enemy reducer slices. Session 04 completes the remaining reducer-only boundary before store folding, persistence lifecycle, honesty regression, and erasure validation can rely on a stable camp-link contract.

The session must preserve the Phase 22 honesty posture: no camp HP burn-down, XP, banners, kills, War Tide gain/loss, loot, ceremony, or progress-like counters are minted from ordinary suggestions, unrelated missions, replay traffic, or snapshot reconciliation.

***

## 2. Objectives

1. Add an explicit reducer-owned way to record pending camp links only from real `campId`, `heroId`, `prompt`, and `expiresAt` inputs.
2. Bind `mission_start` to `campLinks` by exact `heroId + prompt` match, with the documented same-hero fallback inside the expiry window.
3. Clear mission camp links on `mission_complete` and terminal failed or cancelled `mission_update` events without touching unrelated links.
4. Make `suggestion_update` snapshot handling explicit as no-progress reconciliation for this session, with tests proving it cannot fabricate camp progress.

***

## 3. Prerequisites

### Required Sessions

* [x] `phase22-session01-projection-contract` - Provides the v1 `GameProjection` shape, persistence parser, and neutral aggregate.
* [x] `phase22-session02-hero-attention-reducer` - Provides hero, flare, and gate reducer patterns.
* [x] `phase22-session03-legion-enemy-reducer` - Provides live enemy reducer slices, alert focus priority, and privacy-safe bounded refs.

### Required Tools Or Knowledge

* npm workspace commands from `.spec_system/CONVENTIONS.md`.
* Vitest pure reducer test style in `apps/web/tests/gameProjection.test.ts`.
* Protocol event contracts in `packages/protocol/src/events.ts` and mission prompt contract in `packages/protocol/src/missions.ts`.

### Environment Requirements

* Node 26.2.0+ and npm 11.16.0+ per project conventions.
* Browser storage and store integration are not required for this reducer-only session.
* No database, migration, hosted service, Worker, or credential dependency applies.

***

## 4. Scope

### In Scope (MVP)

* Future camp accept callers can record a pending link only when they supply real `campId`, `heroId`, verbatim `prompt`, and `expiresAt` values - add a pure helper in `apps/web/src/lib/gameProjection.ts`.
* The projection can bind a `mission_start` to `campLinks` by `heroId + prompt`, then by first same-hero pending link inside the expiry window - keep matching deterministic and consume only the chosen pending link.
* Mission completion and failed or cancelled mission updates clear only the relevant mission camp link - leave unrelated pending links and camp links intact.
* `suggestion_update` is handled as explicit no-progress reconciliation for this session - if camp coalescing is still deferred, the code and tests make the no-op deliberate.
* Ordinary idle-suggestion accepts and current codebase issue cards do not create camp links - no Quest Board action path is wired without a real camp caller.
* Focused tests cover exact prompt binding, fallback binding, expiry behavior, link clearing, unrelated-link preservation, and snapshot no-progress behavior.

### Out Of Scope (Deferred)

* Quest Board UI changes - Reason: current issue cards expose dismissal, not real camp accept/assign.
* Camp marker buttons, Quest Board filtering, and battlefield rendering - Reason: Phase 22 creates state authority only; presentation belongs to later game phases.
* Camp HP burn-down, verified camp kills, XP, banners, loot, essence, War Tide, combat strikes, SFX, or ceremony - Reason: Session 04 only proves linkage readiness.
* Protocol event changes or new REST routes - Reason: current mission and suggestion contracts are sufficient for the dormant reducer boundary.
* Store folding, local persistence, lifecycle flushes, local-erasure inventory, and hosted persistence - Reason: Sessions 05, 06, and 08 own those surfaces.

***

## 5. Technical Approach

### Architecture

Keep all camp-link logic inside `apps/web/src/lib/gameProjection.ts`. Add pure helpers beside the existing copy-on-write reducer utilities, then wire only the existing `mission_start`, `mission_complete`, `mission_update`, and `suggestion_update` protocol events into `reduceGameProjection`.

The reducer will treat `pendingLinks` and `campLinks` as bounded aggregate state, not an event log. Matching uses normalized timestamps, real expiry checks, deterministic ordering, and copy-on-write updates that return the original projection reference for unchanged inputs.

### Design Patterns

* Pure reducer with copy-on-write no-ops: Matches existing projection reducer patterns and keeps tests deterministic.
* Explicit contract no-op: A `suggestion_update` reducer case documents that camp reconciliation is intentionally deferred and cannot mint progress in this session.
* Narrow exported helper: A `recordProjectionPendingLink` helper allows a future real caller to add a link without exposing store or UI dependencies now.
* Privacy-bounded prompt handling: Store the verbatim prompt only because the v1 contract needs exact matching, keep it out of UI/protocol/export paths, and remove it when consumed or expired.

***

## 6. Deliverables

### Files To Create

| File | Purpose                                   | Est. Lines |
| ---- | ----------------------------------------- | ---------- |
| none | No new source or test files are required. | 0          |

### Files To Modify

| File                                    | Changes                                                                                                                                                 | Est. Lines |
| --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
| `apps/web/src/lib/gameProjection.ts`    | Add pending-link validation, recording, mission binding, terminal clearing, explicit `suggestion_update` no-progress reducer, and reducer switch cases. | \~160      |
| `apps/web/tests/gameProjection.test.ts` | Add typed mission/suggestion builders and focused reducer tests for camp-link recording, binding, expiry, clearing, and snapshot no-progress behavior.  | \~220      |

***

## 7. Success Criteria

### Functional Requirements

* [ ] A pending camp link is recorded only when `campId`, `heroId`, `prompt`, and `expiresAt` are all present and usable.
* [ ] `mission_start` links by exact `heroId + prompt` before same-hero fallback, and only inside the expiry window.
* [ ] Matched pending links are consumed deterministically without deleting unrelated pending links.
* [ ] `mission_complete` and terminal failed or cancelled `mission_update` clear only the matching `campLinks` entry.
* [ ] `suggestion_update` cannot create links, lower camp HP, kill camps, or change XP, banners, kills, essence, or War Tide.
* [ ] Ordinary idle-suggestion and codebase-issue snapshots remain inert for camp-link creation.

### Testing Requirements

* [ ] `apps/web/tests/gameProjection.test.ts` covers exact prompt binding, fallback binding, expiry behavior, deterministic clearing, and `suggestion_update` no-progress behavior.
* [ ] `npm test -- apps/web/tests/gameProjection.test.ts` passes.
* [ ] `npm --workspace @factionos/web run typecheck` passes.

### Non-Functional Requirements

* [ ] Projection reducer remains pure, deterministic, serializable, and bounded.
* [ ] No new UI, protocol, server, hosted persistence, audio, or second game-state authority is introduced.
* [ ] Sensitive prompt handling is limited to the required short-lived pending-link matching field.

### Quality Gates

* [ ] All files ASCII-encoded.
* [ ] Unix LF line endings.
* [ ] Code follows project conventions.
* [ ] Biome check passes for changed source and test files.
* [ ] UI product surface is N/A because no rendered user-facing file changes.

***

## 8. Implementation Notes

### Working Assumptions

* Reducer-only scope: The current repo has `apps/web/src/lib/gameProjection.ts` and `apps/web/tests/gameProjection.test.ts` from Sessions 01-03, while `useGameStore.ts` is not yet wired to projection. Session 05 owns store folding, so Session 04 can proceed with pure reducer and helper work only.
* No real camp caller exists yet: The Phase 22 PRD says current codebase issue cards expose dismissal rather than accept/assign and `/suggestions/:suggestionId/accept` handles idle suggestions. Planning can proceed by adding a dormant helper and tests without wiring current Quest Board actions.

### Conflict Resolutions

* Exact prompt matching vs broad privacy guidance: The Phase 22 v1 shape and Session 04 stub require pending links to store the verbatim prompt for `heroId + prompt` matching, while carry-forward privacy guidance warns against broad raw prompt exposure. The chosen interpretation is to keep the prompt only inside pending-link reducer state, consume or expire it deterministically, avoid UI/protocol/export exposure, and defer persistence lifecycle guardrails to Session 06.

### Key Considerations

* `missionTags.ts` is not a valid linkage mechanism because `Mission` has no tags field.
* `suggestion_update` is canonical Quest Board snapshot traffic but is not a camp accept signal in this session.
* Future camp UI can call the helper only after it has a real camp id, hero id, and exact prompt.

### Potential Challenges

* Prompt matching can accidentally bind unrelated same-hero missions if the fallback is too broad: apply expiry checks, deterministic ordering, and exact prompt priority before fallback.
* Snapshot events can be mistaken for progress: add explicit no-progress tests against progress-like fields and camp state.
* Verbatim prompt storage is sensitive: keep it local to reducer aggregate state and avoid adding UI, protocol, docs examples, export, or diagnostic exposure.

### Relevant Considerations

* \[P03] **Stable docs are the current contract**: Use live PRD, phase stub, package README, and docs as source of truth.
* \[P18-apps/server+apps/web] **`suggestion_update` is canonical**: Treat it as snapshot reconciliation and avoid parallel event authority.
* \[P18-apps/server] **Quest Board state is durable local evidence**: Do not create camp links from current suggestion persistence or ordinary idle accepts.
* \[P20] **Broad privacy gates are release-critical**: Keep raw prompts, terminal output, file contents, tokens, secrets, and broad paths out of UI, protocol events, diagnostics, and tests beyond bounded synthetic fixtures.
* \[P08] **Full trusted erasure remains no-claim**: Browser-local cleanup and future projection persistence cannot imply broad trusted erasure.

### Behavioral Quality Focus

Checklist active: Yes Top behavioral risks for this session:

* External event handling must keep schema-aligned input checks and explicit no-op behavior for unsupported snapshots.
* State-mutating reducer helpers must avoid duplicate pending-link creation and preserve unrelated links during bind or clear operations.
* Write-like future linkage must remain idempotent and bounded so replay, snapshots, and ordinary suggestions cannot fabricate progress.

***

## 9. Testing Strategy

### Unit Tests

* Test `recordProjectionPendingLink` for valid input, missing/blank fields, expired input, duplicate input, deterministic ordering, and privacy-safe serialization expectations.
* Test `mission_start` exact `heroId + prompt` binding, same-hero fallback binding, expired-link rejection, and pending-link consumption.
* Test `mission_complete` and failed or cancelled `mission_update` clearing without deleting unrelated links.
* Test `suggestion_update` snapshots with idle and codebase issue content as explicit no-progress no-ops.

### Integration Tests

* No store or component integration tests are required in this session.
* Session 05 will add store folding tests after the reducer contract is complete.

### Runtime Verification

* Run `npm test -- apps/web/tests/gameProjection.test.ts`.
* Run `npm --workspace @factionos/web run typecheck`.
* Run `npm exec -- biome check apps/web/src/lib/gameProjection.ts apps/web/tests/gameProjection.test.ts`.

### Edge Cases

* Expired pending links do not bind and can be pruned without changing unrelated valid links.
* Multiple pending links for one hero prefer exact prompt match over fallback.
* Same prompt on different heroes does not bind across heroes.
* Terminal mission updates clear the linked mission once and redundant clears return the same state reference.
* Snapshot traffic cannot mutate progress-like counters or remove camps.

***

## 10. Dependencies

### Other Sessions

* Depends on: `phase22-session03-legion-enemy-reducer`
* Depended by: `phase22-session05-store-folding-and-selectors`, `phase22-session06-persistence-lifecycle`, `phase22-session07-honesty-regression-tests`, `phase22-session08-erasure-docs-and-validation`

***

## 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/phase22-session04-camp-link-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.
