> 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-session05-quest-board-camp-focus/spec.md).

# Session Specification

**Session ID**: `phase23-session05-quest-board-camp-focus` **Phase**: 23 - Legion I - Scanner Camps **Status**: Not Started **Created**: 2026-07-05 **Base Commit**: 78e16703ab4c97c10b39a375e2bd3390c1395d5d **Package**: apps/web **Package Stack**: TypeScript

***

## 1. Session Overview

This session connects projection camps to the existing Quest Board work path in `apps/web`. A focused camp issue set should open the shared Quest Board focused surface, show the real issue cards behind that camp, and provide a dry Banish action that dismisses current issue ids through the existing Quest Board issue dismissal route.

It is next because Phase 23 Sessions 01-04 have completed camp reconciliation, ranking selectors, projection folding, and store persistence boundaries. The Phase 23 dependency table marks Session 05 as executable after Session 03, and Session 06 needs the inspection and Banish callbacks before rendering camp buttons on the battlefield.

The implementation stays inside existing web store, Quest Board, action, and test surfaces. It does not add server routes, protocol events, battlefield camp rendering, rewards, XP, loot, banners, achievements, camp kill credit, combat playback, target flags, mission links, hosted state, or trusted-erasure claims.

***

## 2. Objectives

1. Add Quest Board camp focus state that records an active camp id and bounded current issue ids.
2. Open the existing Quest Board focused bottom-rail surface from camp focus without duplicating the Quest Board tree.
3. Filter or emphasize Quest Board cards whose `dismissIssue.issueId` belongs to the active camp and provide a clear empty focused state.
4. Implement dry Banish by batching current camp issue dismissals through the existing issue dismiss path with duplicate-trigger prevention, rollback, and partial-failure feedback.

***

## 3. Prerequisites

### Required Sessions

* [x] `phase23-session01-pure-camp-reconciliation` - provides camp ids, issue ids, safe sectors, HP, tiers, and stable camp aggregates.
* [x] `phase23-session02-ranking-and-presentation-selectors` - provides presentation-ready camp ranking for future battlefield callbacks.
* [x] `phase23-session03-projection-folding` - folds scanner snapshots into `GameProjection.legion.camps` with current camp issue ids.
* [x] `phase23-session04-store-persistence-boundaries` - proves camp aggregates stay under the existing browser-local projection persistence boundary.
* [x] `phase21-session05-quest-board-and-war-room-parity` - provides the shared focused bottom-rail surface pattern used by Quest Board.
* [x] `phase18-session09-web-quest-board-typed-cards` - provides typed Quest Board cards and issue dismissal actions.

### Required Tools Or Knowledge

* Zustand store patterns in `apps/web/src/store/useGameStore.ts`.
* Focused bottom-rail surface state in `apps/web/src/store/useSettingsStore.ts`.
* Quest Board component and card rendering in `apps/web/src/components/QuestBoard.tsx`.
* Existing issue dismissal client in `apps/web/src/lib/questBoardActions.ts`.
* Vitest and React Testing Library patterns in `apps/web/tests/QuestBoard.test.tsx`, `apps/web/tests/questBoardStore.test.ts`, and `apps/web/tests/questBoardActions.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 set and clear a camp focus containing `campId`, bounded `issueIds`, and optional safe display fields needed by Quest Board copy.
* Setting camp focus opens the existing `quest-board` focused bottom-rail surface through `useSettingsStore`, preserving one mounted Quest Board implementation.
* The Quest Board can filter or strongly emphasize cards whose `actions.dismissIssue.issueId` is in the active camp.
* The focused empty state says that the camp is already reconciled or its issues are no longer present.
* Dry Banish dismisses only current issue cards through `dismissQuestBoardIssue`, reusing existing request timeout, validation, and error mapping.
* Dry Banish prevents duplicate triggers while issue dismissals are in flight, uses existing optimistic removal and rollback patterns, leaves failed issue cards visible, and reports success, failure, and partial-failure feedback through existing Quest Board feedback UI.
* Quest acceptance, hero assignment, scan triggers, normal card dismissal, and focused surface ergonomics continue to work.
* Tests cover focus state, focused surface opening, filtering, clear behavior, empty focused state, Banish success, failure, partial failure, pending state, and no reward or projection-progress side effects.

### Out Of Scope (Deferred)

* Battlefield camp buttons or `CampLayer` rendering - Reason: Session 06 owns camp marker rendering, accessible battlefield buttons, and callbacks into this session's Quest Board focus actions.
* Dispatch Scouts, Golden Age, scan-not-run, scan-failed, and mock-drill battlefield states - Reason: Session 07 owns scan lifecycle presentation.
* New server routes or protocol events - Reason: Phase 23 requires existing Quest Board issue dismissal paths and existing `suggestion_update` snapshots.
* Progress rewards, XP, loot, banners, achievements, camp kill credit, camp damage, live combat playback, target flags, class advantage, mission links, or focus-battle dimming - Reason: Phase 23 explicitly forbids these expansions for scanner camps.
* Rich issue detail copied into projection state - Reason: Quest Board already owns titles, messages, file labels, and dismiss actions.
* Hosted state, cross-device deletion, unified trusted erasure, or production hosted claims - Reason: current security posture keeps these as no-claim boundaries.

***

## 5. Technical Approach

### Architecture

Keep Quest Board as the real issue inspection surface. Add a small camp-focus slice to `apps/web/src/store/useGameStore.ts` instead of copying issue details into projection state or adding a second Quest Board instance. The slice should store only a camp id plus normalized, de-duplicated, bounded issue ids and optional safe labels for display.

Expose store actions such as `focusQuestBoardCamp`, `clearQuestBoardCampFocus`, and `banishQuestBoardCamp`. `focusQuestBoardCamp` should validate and bound the issue ids, update the store, and call `useSettingsStore.getState().setExpandedBottomRailSurfaceId("quest-board")` so future battlefield buttons can open the same shared focused surface. It should not change projection progress, rewards, selected heroes, scan configuration, or storage keys.

In `QuestBoard.tsx`, derive `visibleCards` from the active focus. A card belongs to the focus when `card.actions.dismissIssue?.issueId` is in the active `issueIds` set. Render a compact camp-focus banner with clear and Banish controls, the current matching issue count, and product-facing copy only. When focus is active and no cards match, render the focused empty state instead of the normal overall empty state.

Implement dry Banish in the store using the existing `dismissQuestBoardIssue` client and existing Quest Board mutation helpers: `beginQuestAction`, `clearQuestAction`, `removeQuestCardOptimistically`, `rollbackQuestCard`, `clearOptimisticQuestCard`, `runBounded`, and `setQuestFeedback`. The action should target currently visible focused issue cards, not stale ids that no longer have dismissable cards. Successful issue dismissals should remove the cards optimistically and wait for the normal scanner snapshot reconciliation to remove camps from the projection. Failed issue dismissals should roll back the affected cards and keep the camp focus visible with normal error feedback.

### Design Patterns

* Same-instance focused surface: reuse `BottomRailExpansionHost` and `expandedBottomRailSurfaceId` so Quest Board state does not fork.
* Bounded focus state: normalize and cap issue ids before storing them.
* Existing action path: route Banish through `dismissQuestBoardIssue` rather than adding backend or protocol surface area.
* Scoped optimistic updates: remove cards optimistically for successful dry Banish attempts and roll back only failed cards.
* Projection honesty: Banish never writes projection progress; the next real `suggestion_update` snapshot owns camp removal.

***

## 6. Deliverables

### Files To Create

| File | Purpose                                                        | Est. Lines |
| ---- | -------------------------------------------------------------- | ---------- |
| None | This session modifies existing web source and test files only. | 0          |

### Files To Modify

| File                                       | Changes                                                                                                                                                                              | Est. Lines |
| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------- |
| `apps/web/src/store/useGameStore.ts`       | Add camp-focus state, selectors, focus/clear actions, dry Banish batching, duplicate-trigger prevention, optimistic success handling, rollback, and partial-failure feedback.        | \~180      |
| `apps/web/src/components/QuestBoard.tsx`   | Render active camp focus banner, filter or emphasize focused issue cards, add clear and Banish controls, and show focused empty state.                                               | \~180      |
| `apps/web/tests/questBoardStore.test.ts`   | Add store tests for focus normalization, focused-surface opening, clear behavior, Banish success, failure, partial failure, pending state, and no reward or projection side effects. | \~260      |
| `apps/web/tests/QuestBoard.test.tsx`       | Add component tests for focused card filtering, clear control, empty focused state, Banish UI states, accessibility labels, and existing Quest Board behavior under focus.           | \~240      |
| `apps/web/tests/questBoardActions.test.ts` | Add helper-level issue-dismiss regressions for Banish-safe invalid issue ids, request body shape, and existing route use.                                                            | \~40       |

***

## 7. Success Criteria

### Functional Requirements

* [ ] Calling the camp-focus store action records the active camp id and a normalized bounded set of issue ids.
* [ ] Calling the camp-focus store action opens the existing Quest Board focused bottom-rail surface.
* [ ] Active camp focus filters or emphasizes only cards whose `actions.dismissIssue.issueId` belongs to the camp.
* [ ] Clearing focus restores the normal Quest Board card list and summary.
* [ ] A focused camp with no matching current issue cards shows a clear already-reconciled or no-longer-present empty state.
* [ ] Dry Banish calls the existing issue dismissal route for current focused issue ids only.
* [ ] Dry Banish success removes issue cards optimistically but creates no reward, progress, camp kill credit, projection mutation, or storage key.
* [ ] Dry Banish failure rolls back failed cards and leaves the camp visible.
* [ ] Dry Banish partial failure leaves remaining failed issue cards visible and reports normal Quest Board action errors.
* [ ] Quest acceptance, assignment, scan triggers, and normal dismissal still work with and without active camp focus.

### Testing Requirements

* [ ] Focused store tests cover focus state, focus opening, clear behavior, Banish success, failure, partial failure, duplicate-trigger prevention, pending key cleanup, and no projection-progress side effects.
* [ ] Focused component tests cover filtered cards, empty focused state, clear control, Banish control, accessibility labels, pending disabled state, and existing actions under focus.
* [ ] Existing action tests still prove `dismissQuestBoardIssue` posts to the current issue dismissal route and rejects invalid issue ids before network calls.
* [ ] Focused test command passes for `apps/web/tests/questBoardStore.test.ts`, `apps/web/tests/QuestBoard.test.tsx`, and `apps/web/tests/questBoardActions.test.ts`.

### Non-Functional Requirements

* [ ] No new server route, protocol event, storage key, hosted dependency, or trusted-erasure claim is introduced.
* [ ] Focus state stores only ids and safe display labels, not raw issue messages, prompts, terminal output, provider payloads, file contents, absolute paths, environment values, secrets, or copied projection payloads.
* [ ] Duplicate Banish triggers are rejected while any camp issue dismissal is in flight.
* [ ] UI copy is product-facing and does not expose diagnostics as gameplay.

### Quality Gates

* [ ] All files ASCII-encoded.
* [ ] Unix LF line endings.
* [ ] Code follows project conventions.
* [ ] Primary user-facing surfaces contain product-facing copy only.
* [ ] `npm --workspace @factionos/web run typecheck` passes.

***

## 8. Implementation Notes

### Working Assumptions

* Package context is `apps/web`: the analysis script reports Session 05 as an `apps/web` candidate, the Phase 23 PRD scopes the work to the web cockpit game layer, and every deliverable targets web store, Quest Board, actions, or web tests.
* The existing focused Quest Board surface is the correct inspection target: `App.tsx` mounts Quest Board inside `BottomRailExpansionHost` with surface id `quest-board`, and `useSettingsStore` already owns `expandedBottomRailSurfaceId`.
* Camp issue ids map to current Quest Board issue cards through `card.actions.dismissIssue?.issueId`: `QuestBoard.tsx` already uses that capability for codebase issue dismissal, and Session 05 only needs current dismissable issue cards for dry Banish.
* Session 05 can expose store callbacks before battlefield camps render: Session 06 depends on this callback shape, and component/store tests can call the focus action directly without a battlefield camp button.

### Conflict Resolutions

* The master PRD planned-phase table still labels Phase 23 as Not Started, while the analyzer, Phase 23 PRD, and completed session records show Phase 23 is in progress with Sessions 01-04 complete. The chosen interpretation is to trust the analyzer and live Phase 23 PRD for workflow state.
* Session 05 asks camp inspection to open from a clicked camp, but Session 06 owns actual battlefield camp buttons. The chosen interpretation is to build the Quest Board focus action and UI now, then let Session 06 call that action from `CampLayer`.
* The Phase 23 source says Banish waits for normal snapshot reconciliation before removing the camp, while existing Quest Board actions remove cards optimistically. The chosen interpretation is to optimistically remove issue cards only after successful issue dismissal requests while leaving projection camp removal to the next real `suggestion_update` snapshot.

### Key Considerations

* Dry Banish must read as dismissal of current issue cards, not combat victory.
* Missing issue ids are a reconciliation state, not progress.
* Existing scan configuration and diagnostics remain in utility surfaces.
* The implementation must keep Quest Board as the rich issue-detail owner and keep projection state aggregate-only.

### Potential Challenges

* Partial Banish failures can leave filtered focus with fewer cards: roll back failed cards, keep the focus active, and report a warning that counts successes and failures.
* Opening the focused surface from a store action can fight focus management: rely on the existing `BottomRailExpansionHost` focus behavior and avoid a second imperative focus path unless tests prove it is required.
* Camp focus can become stale after a scanner snapshot removes issue cards: keep the focused empty state explicit and allow the user to clear focus.

### Relevant Considerations

* \[P22-apps/web] **Game projection is the single derived authority**: Quest Board focus must not copy camp progress or create a second game-state authority.
* \[P22-apps/web] **Derived projection storage is narrow**: Focus and Banish must not add a new storage key or persist rich issue details.
* \[P22-apps/web] **Do not add parallel game authorities**: Quest Board can inspect and dismiss issue cards, but camp removal remains projection reconciliation through snapshots.
* \[P22-apps/web] **Do not persist replay or drill progress**: Banish cannot mint XP, essence, banners, kills, titles, or War Tide.
* \[P18-apps/server] **Quest Board state is durable local evidence**: Dry Banish must use existing issue dismissal semantics and avoid trusted-erasure claims.
* \[P07] **Redaction is boundary-specific**: New UI, store state, tests, and feedback must keep raw commands, terminal output, file contents, provider payloads, secrets, and broad absolute paths out of broad surfaces.
* \[P21-apps/web] **Same-instance focused surfaces**: Quest Board focus should promote the existing bottom-rail implementation instead of duplicating a second tree.

### Behavioral Quality Focus

Checklist active: Yes Top behavioral risks for this session:

* Duplicate dry Banish triggers can send repeated dismiss requests unless in-flight issue ids are locked.
* Partial failures can hide remaining work unless failed issue cards roll back into the focused list.
* Stale camp issue ids can produce fake progress unless the empty focused state explains that the camp has already reconciled or its issues are gone.

***

## 9. Testing Strategy

### Unit Tests

* Store tests in `apps/web/tests/questBoardStore.test.ts` should cover focus normalization, clear behavior, focused-surface opening, Banish batching, duplicate-trigger prevention, success, failure, partial failure, pending key cleanup, optimistic rollback, and no projection or reward mutation.
* Action tests in `apps/web/tests/questBoardActions.test.ts` should continue to cover `dismissQuestBoardIssue` request path, body validation, malformed input, offline, timeout, conflict, and malformed response handling.

### Integration Tests

* Component tests in `apps/web/tests/QuestBoard.test.tsx` should render active camp focus with mixed Quest Board cards, verify only current camp issue cards are visible or emphasized, verify clear and Banish controls, and verify existing accept, assign, dismiss, scan trigger, summary, and reply-target behavior still works.

### Runtime Verification

* Run `npm --workspace @factionos/web test -- QuestBoard.test.tsx questBoardStore.test.ts questBoardActions.test.ts`.
* Run `npm --workspace @factionos/web run typecheck`.
* Validate ASCII and LF for changed files.

### Edge Cases

* Camp focus with duplicate, blank, malformed, or excessive issue ids.
* Camp focus whose issue ids no longer appear in current Quest Board cards.
* Banish while one or more issue ids are already pending dismissal.
* Banish where every request fails.
* Banish where some requests succeed and some fail.
* Existing non-codebase Quest Board cards while camp focus is active.
* Camp focus remains active while a later `suggestion_update` removes all matching issue cards.

***

## 10. Dependencies

### Other Sessions

* Depends on: `phase23-session03-projection-folding`, `phase23-session04-store-persistence-boundaries`
* Depended by: `phase23-session06-battlefield-camp-layer`, `phase23-session08-validation-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/phase23-session05-quest-board-camp-focus/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.
