> 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-session02-ranking-and-presentation-selectors/spec.md).

# Session Specification

**Session ID**: `phase23-session02-ranking-and-presentation-selectors` **Phase**: 23 - Legion I - Scanner Camps **Status**: Not Started **Created**: 2026-07-05 **Base Commit**: ed83e7a7670c654c228db767abd018e7f4f39f63 **Package**: apps/web **Package Stack**: TypeScript

***

## 1. Session Overview

This session extends the pure Legion camp helper created in Session 01 with ranking, roster-size limits, selected-context priority, and presentation metadata for Champion Mode. The result is a deterministic selector layer that lets later battlefield rendering show a readable camp subset without changing the full camp census or Quest Board issue source.

It is next because Session 01 completed camp reconciliation and Phase 23 requires ranking before the camp layer can render solo, duo, squad, and army boards. Session 03 can independently fold scanner snapshots into the projection, but Session 06 depends on this selector work to render only the right camp count for the visible roster.

The plan stays inside `apps/web/src/lib/legionCamps.ts` and its focused tests. It does not add reducer wiring, persistence, Quest Board filtering, battlefield DOM, product copy, Golden Age presentation, rewards, protocol events, hosted state, or a new game authority.

***

## 2. Objectives

1. Add typed roster-mode and visible-limit selectors for zero-hero, solo, duo, squad, and army battlefield states.
2. Extend `rankVisibleLegionCamps` so selected-context sector matches rank first when a sector mapping is available.
3. Apply the documented deterministic ranking order: tier, HP, lower entrenchment, newest issue timestamp or snapshot fallback, and stable camp id.
4. Expose presentation metadata for muted or edge-placed entrenched camps while preserving `entrenchedScans` as the source of truth.

***

## 3. Prerequisites

### Required Sessions

* [x] `phase23-session01-pure-camp-reconciliation` - provides `apps/web/src/lib/legionCamps.ts`, camp ids, sector ids, safe sector labels, entrenchment counters, and base ranking metadata.
* [x] `phase22-session05-store-folding-and-selectors` - provides web projection selectors and the single `GameProjection` authority.

### Required Tools Or Knowledge

* TypeScript pure helper patterns in `apps/web/src/lib/legionCamps.ts`.
* Vitest pure-library test patterns in `apps/web/tests/legionCamps.test.ts`.
* Existing `CodebaseIssue` fields from `packages/protocol/src/suggestions.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 game layer can compute a roster mode from visible hero count - added as pure constants and helpers in `apps/web/src/lib/legionCamps.ts`.
* The web game layer can rank and trim visible camps without mutating or replacing the full camp census - implemented by extending `rankVisibleLegionCamps`.
* The web game layer can prioritize a selected sector when one is supplied or derivable from safe context - implemented as optional pure input with a fallback when no mapping exists.
* The web game layer can expose presentation metadata for entrenched camps so Session 06 can render them quieter or near the board edge.
* Focused tests cover limits, priority, fallback order, entrenchment treatment, timestamp fallback, and stable tie-breaking.

### Out Of Scope (Deferred)

* Projection reducer wiring for `suggestion_update` - Reason: Session 03 owns folding scanner snapshots into `GameProjection`.
* Store persistence, replay, mock-mode, reset, or local erasure assertions - Reason: Session 04 owns persistence boundaries after reducer folding exists.
* Quest Board inspection, filtering, or dry Banish - Reason: Session 05 owns issue-card focus and dismissal behavior.
* Battlefield camp rendering and singular champion copy - Reason: Session 06 owns `CampLayer` and product-surface presentation.
* Golden Age and scan lifecycle states - Reason: Session 07 owns scan-not-run, scan-failed, clean-scan, Dispatch Scouts, and Golden Age presentation.

***

## 5. Technical Approach

### Architecture

Keep `legionCamps.ts` as a pure module that imports only type contracts from protocol and `gameProjection.ts`. Extend the existing `RankVisibleLegionCampsInput` and `RankedLegionCamp` shapes rather than creating a second selector module. The selector continues to clone returned camp and sector records so callers do not mutate projection state by accident.

Add roster limit constants for zero, solo, duo, squad, and army modes. The ranking function will derive the mode from an optional `rosterSize` or `visibleHeroCount` input, trim the returned ranked list to the matching limit, and preserve the original `camps` record untouched for Quest Board inspection.

Selected-context priority should be optional and explicit. The input can carry a sector id, safe sector key, or file path; when the helper can match that context to an existing sector, matching camps sort first. When it cannot, the selector falls back to the documented general ordering without failing.

Presentation metadata is derived from existing aggregate fields. Entrenched camps are marked `muted` and `placement: "edge"` once they meet the existing entrenchment threshold; fresh camps use `placement: "field"`. These flags do not alter HP, tier, issue ids, scar state, or entrenchment counters.

### Design Patterns

* Pure selector: no React, store, browser storage, server, or network imports.
* Stable ordering: every comparator ends with `campId` so identical inputs are deterministic.
* Source preservation: ranking trims only the returned presentation list; the projection camp census remains the caller-owned input.
* Conservative context mapping: selected-sector priority applies only when the helper can match existing safe sector data.

***

## 6. Deliverables

### Files To Create

| File | Purpose                                                       | Est. Lines |
| ---- | ------------------------------------------------------------- | ---------- |
| None | Session 02 extends existing Session 01 helper and test files. | 0          |

### Files To Modify

| File                                 | Changes                                                                                                                           | Est. Lines |
| ------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------- | ---------- |
| `apps/web/src/lib/legionCamps.ts`    | Add roster limits, selected-context ranking, lower-entrenchment ordering, visible trimming, and presentation metadata.            | \~180      |
| `apps/web/tests/legionCamps.test.ts` | Add focused tests for roster modes, selected context, fallback ranking, entrenchment, timestamps, tie-breaking, and immutability. | \~220      |

***

## 7. Success Criteria

### Functional Requirements

* [ ] `rankVisibleLegionCamps` returns at most 3 camps for zero-hero and solo states, 4 for duo, 6 for squad, and 9 for army.
* [ ] Selected-context sector matches rank before non-matching camps when a sector mapping exists.
* [ ] When selected context cannot map to a sector, ranking falls back to tier, HP, lower entrenchment, newest issue timestamp or snapshot fallback, and stable camp id.
* [ ] Entrenched camps expose muted and edge-placement metadata without changing their underlying camp data.
* [ ] The full camp census remains available to Quest Board and future inspection callers because the selector trims only its returned list.

### Testing Requirements

* [ ] Unit tests cover zero-hero, solo, duo, squad, and army visible limits.
* [ ] Unit tests cover selected-context priority and fallback when context is absent or unmappable.
* [ ] Unit tests cover lower-entrenchment priority so stale muted camps stop dominating when comparable camps exist.
* [ ] Unit tests cover newest timestamp fallback to selector snapshot time and stable tie-breaking by camp id.
* [ ] Focused test command passes for `apps/web/tests/legionCamps.test.ts`.

### Non-Functional Requirements

* [ ] Selector output remains aggregate-only and does not copy raw issue messages, prompts, terminal output, provider payloads, file contents, absolute paths, environment values, or secrets.
* [ ] No new protocol event, new storage key, reducer side effect, hosted dependency, or trusted-erasure claim is introduced.
* [ ] Ranking is deterministic across identical inputs.

### 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 the selected session package as `apps/web`, and every deliverable targets the web pure game helper or its tests.
* The active Phase 23 PRD and docs/game-design backlog are the source-backed planning inputs: the master PRD references a missing ongoing-projects source file, but `.spec_system/PRD/phase_23/PRD_phase_23.md`, `.spec_system/PRD/phase_23/session_02_ranking_and_presentation_selectors.md`, and `docs/game-design/15-phases-yet-to-be-implemented.md` contain matching requirements and code anchors.
* Session 02 should extend the existing `rankVisibleLegionCamps` helper from Session 01 instead of adding a competing selector. Planning can proceed because no production caller depends on a separate selector name yet, and the current tests are focused on the same helper.
* Selected hero active-context mapping is not wired to the battlefield yet. The pure selector can accept optional sector context and safely fall back when no mapping exists; Session 06 can pass richer context when rendering owns the battlefield integration.

### Conflict Resolutions

* Session 01's stub mentioned a base `rankVisibleLegionCamps` helper while Session 02 owns ranking and presentation selectors. The chosen split is to keep Session 01's base helper and implement the full Champion Mode ranking behavior in that same helper during Session 02.
* The game-design backlog says every remaining phase should add pure-lib and component tests, while this session has no component or product surface. The chosen interpretation is focused pure-lib tests only, with component and browser coverage deferred to the battlefield rendering session.
* Zero-hero notes mention Golden Age when clean, but this session excludes scan lifecycle presentation. The chosen interpretation is that Session 02 only returns the zero-hero visible camp limit; Session 07 owns the clean Golden Age treatment.

### Key Considerations

* Keep `gameProjection.ts` as the only derived game-state authority.
* Preserve the Quest Board as the rich issue-detail source; selector output should carry ids and aggregate metadata only.
* Do not add copy, rendering, scan state, Banish behavior, rewards, or progress.

### Potential Challenges

* Comparator changes can make tests flaky if timestamps or camp ids are not normalized; end every ranking path with stable camp id ordering.
* Selected context can accidentally reintroduce unsafe file paths; normalize any path-derived input through existing safe sector helpers before comparing.
* Entrenched presentation flags can look like new state; derive them from `entrenchedScans` each call and never persist them into camp records.

### Relevant Considerations

* \[P22-apps/web] **Game projection is the single derived authority**: Ranking reads projection-shaped inputs and does not create another camp authority.
* \[P22-apps/web] **Derived projection storage is narrow**: The selector adds no storage and returns aggregate metadata only.
* \[P22-apps/web] **Do not add parallel game authorities**: Extending `legionCamps.ts` keeps camp selection in the existing pure helper.
* \[P22-apps/web] **Do not promote dormant camp links**: Ranking does not create mission links, camp progress, or rewards.
* \[P18-apps/server] **Quest Board state is durable local evidence**: Ranking preserves the full issue census for later Quest Board inspection.

### Behavioral Quality Focus

Checklist active: Yes Top behavioral risks for this session:

* Ranking input consumes external scanner-derived issue contracts, so selector types and tests must match declared protocol fields and handle unknown or missing context by falling back safely.
* Returned visible lists can accidentally hide the source census, so tests must prove the input camp record is not mutated or truncated.
* Entrenched camps can dominate small rosters, so lower-entrenchment priority and muted/edge metadata must be explicit and deterministic.

***

## 9. Testing Strategy

### Unit Tests

* Add `apps/web/tests/legionCamps.test.ts` cases for roster mode derivation and visible limits.
* Add selected-sector priority and unmappable-context fallback cases.
* Add comparator cases for tier, HP, lower entrenchment, newest timestamp, and camp id tie-breaking.
* Add presentation metadata and immutability assertions.

### Integration Tests

* No integration test is required in this session because no store, reducer, component, server, or protocol route changes are in scope.

### Runtime Verification

* Run the focused Vitest file for `apps/web/tests/legionCamps.test.ts`.
* Run the web package typecheck.

### Edge Cases

* Zero camps and zero heroes return an empty list with no error.
* Invalid or absent selected context does not change fallback ordering.
* Camps without matching issue timestamps use selector snapshot time.
* Entrenched citadels do not outrank comparable fresher camps solely because they are stale.

***

## 10. Dependencies

### Other Sessions

* Depends on: `phase23-session01-pure-camp-reconciliation`
* Depended by: `phase23-session06-battlefield-camp-layer`

***

## 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-session02-ranking-and-presentation-selectors/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.
