> 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/specs/phase24-session03-live-enemy-presentation/spec.md).

# Session Specification

**Session ID**: `phase24-session03-live-enemy-presentation` **Phase**: 24 - Legion II - Live Tier And Combat Playback **Status**: Not Started **Created**: 2026-07-08 **Package**: apps/web **Package Stack**: TypeScript

***

## 1. Session Overview

This session renders the live enemy tier that the projection already folds: skirmishers (`test_failure`), the ram (`build_error`/`build_fix`), gremlin pressure (`ts_errors`/`lint_error`), stacked per-hero wraiths, and siege state. The data has existed since Phase 22 (`legion.skirmishers`, `legion.ram`, `legion.gremlins`, `legion.wraiths`, `legion.siege`) but nothing renders it - the board only shows scanner camps.

The work follows the shipped Phase 23 layer conventions exactly: `CampLayer.tsx` demonstrates the pattern (thin selector-fed component, CSS custom-property placement, native buttons with accessible names, sr-only summaries, `data-*` attributes for tests, reduced-motion twins via `data-reduced-motion` plus the `battlefield2d-reduce-motion` frame class). A new pure presentation module `liveLegion.ts` derives the bounded visible model (skirmisher cap with overflow count, board-wide wraith cap of 3 by stack, gremlin pressure total, keep-gate anchor constants), and a new `LiveLegionLayer.tsx` renders it between the camp layer and hero tokens.

Design constraints from the phase PRD: wraiths stack and never multiply (one per hero, cap 3 visible); skirmisher tooltips show the safe test path; the ram rocks at a stable keep-gate anchor with a cracked-gate static chip; gremlins are a count chip with jitter-free static twin; siege renders its level. No urgency pulses ship here - the single `alertFocus` pulse is Session 05 - and no strikes, kills, HP drain, or audio.

***

## 2. Objectives

1. Derive a bounded, safe live-enemy presentation model as a pure `apps/web/src/lib/liveLegion.ts` module with focused tests.
2. Render skirmishers, ram, gremlins, wraiths, and siege in a new `LiveLegionLayer` board-internal component ordered below hero tokens, with native-control accessibility and complete static twins.
3. Place wraiths beside their hero tokens (click selects the hero) and skirmishers at the board edge with safe test-path labels.
4. Prove event-to-marker flow through the normal store path: a real `test_failure` produces a visible skirmisher and a pass clears it.

***

## 3. Prerequisites

### Required Sessions

* [x] `phase24-session01-pure-strike-and-combo-model` - shared presentation constants and safe-label conventions.

### Required Tools/Knowledge

* `GameProjectionLegion` shapes in `gameProjection.ts` (verified: `skirmishers: Record<path, {path, sinceAt}>`, `ram: {detail, sinceAt} | null`, `gremlins: {tsErrors, tsFiles, lint}`, `wraiths: Record<HeroId, {stack, sinceAt}>`, `siege: {level, stalledTaskIds} | null`).
* `CampLayer.tsx` conventions (placement style, test ids, sr-only summaries, accessible names) and `Battlefield.tsx` mount order (camp layer -> scanner layer -> hero tokens at z-20).
* `mapHeroPositionToBattlefield` + `worldLimits` for wraith placement.
* `useSettingsStore` `reduceMotion` flag.

### Environment Requirements

* Node 26.2.0+, npm 11.16.0, workspace install present.

***

## 4. Scope

### In Scope (MVP)

* A viewer sees failing tests as enemies: red edge markers with count badge; visible marker cap (`LIVE_LEGION_SKIRMISHER_VISIBLE_LIMIT`) with an overflow count chip; safe test-path label on each marker (title + accessible name); green-check pass state is implicit (marker clears when the projection removes the skirmisher).
* A viewer sees a broken build as the ram: marker at a stable keep-gate anchor (new constants in the layer), slow-rock motion variant, cracked chip static twin; clears when `build_fix` removes it from the projection.
* A viewer sees type/lint pressure as gremlins: a count chip near the keep anchor showing total pressure with subtle jitter (motion) or no jitter (static); hidden at zero pressure.
* A viewer sees environment trouble as wraiths: one translucent wisp per hero with a stack-count badge and darkening-by-stack styling, capped at 3 visible board-wide (highest stacks win); positioned beside the hero's token; clicking the wisp selects that hero.
* A viewer sees stall escalation as siege state: a siege banner with level chip when `siege` is non-null.
* Layer-level sr-only summary counting live threats; every interactive marker is a native button with an accessible name; non-interactive markers carry sr-only text.
* Reduced-motion static twins for every visual, at design time.
* Store-path integration test: `test_failure` event -> marker; pass event -> cleared.

### Out of Scope (Deferred)

* Strike playback, HP drain, camp kills, counterattacks - *Session 04.*
* Scrying posture and the `alertFocus` pulse - *Session 05.*
* Audio cues - *Session 06.*
* Replay/mock persistence regressions beyond existing coverage - *Session 07.*

***

## 5. Technical Approach

### Architecture

Pure model first: `liveLegion.ts` exports `liveLegionPresentation(legion)` returning bounded arrays/records (visible skirmishers + overflow, wraith entries capped and ordered, gremlin total, ram/siege pass-throughs) plus safe-label helpers reusing the `legionCamps.ts` sanitizers. `LiveLegionLayer.tsx` is selector-fed from `Battlefield.tsx` (which already subscribes to the projection via `selectGameLegion`-derived camp data) and renders inside the board div after `ScannerStateLayer`, before the hero token layer.

### Design Patterns

* Presentation-only battlefield layers (P23 lesson): no state, no timers, no game rules in the layer.
* Pure derivation with caps as named constants (Session 01 convention).
* CampLayer accessibility conventions: native buttons, sr-only summaries, `data-testid` helpers exported for tests.

### Technology Stack

* TypeScript, React, clsx, Vitest + React Testing Library (happy-dom).

***

## 6. Deliverables

### Files to Create

| File                                                      | Purpose                                                                     | Est. Lines |
| --------------------------------------------------------- | --------------------------------------------------------------------------- | ---------- |
| `apps/web/src/lib/liveLegion.ts`                          | Pure presentation model: caps, ordering, safe labels, keep anchor constants | \~160      |
| `apps/web/src/components/battlefield/LiveLegionLayer.tsx` | Board-internal live enemy layer                                             | \~260      |
| `apps/web/tests/liveLegion.test.ts`                       | Pure model tests                                                            | \~180      |
| `apps/web/tests/LiveLegionLayer.test.tsx`                 | Component tests incl. reduced motion and caps                               | \~260      |

### Files to Modify

| File                                                  | Changes                                                                        | Est. Lines |
| ----------------------------------------------------- | ------------------------------------------------------------------------------ | ---------- |
| `apps/web/src/components/battlefield/Battlefield.tsx` | Mount LiveLegionLayer with legion selector + hero coordinates + select handler | \~25       |
| `apps/web/src/styles` or component classes            | Motion variants (flicker/rock/jitter) with reduce-motion gating                | \~40       |
| `apps/web/tests/Battlefield.test.tsx`                 | Store-path integration: test\_failure -> marker -> clear                       | \~50       |

***

## 7. Success Criteria

### Functional Requirements

* [ ] A `test_failure` event through `applyEvent` renders a skirmisher marker; the corresponding pass clears it (integration test).
* [ ] Skirmisher rendering caps at the visible limit with an accurate overflow chip; labels are safe relative paths.
* [ ] Ram renders at the keep anchor when `ram` is non-null with motion and cracked-chip static variants; absent otherwise.
* [ ] Gremlin chip shows total pressure, hides at zero, and has a jitter-free static twin.
* [ ] At most 3 wraiths render regardless of afflicted hero count; highest stacks win; stack badge and darkening reflect stack; clicking selects the hero; wraiths for off-board heroes are counted in the summary but not positioned.
* [ ] Siege banner renders its level when siege is active.
* [ ] Layer summary announces live threat counts to screen readers.
* [ ] No raw output, prompts, absolute paths, or secrets in any rendered string (privacy test over the model and rendered DOM).

### Testing Requirements

* [ ] Pure model tests for caps, ordering, labels, zero states.
* [ ] Component tests across motion/static modes and common board sizes (desktop/mobile widths as in existing battlefield tests).
* [ ] Integration test through the real store event path.

### Non-Functional Requirements

* [ ] Layer holds no state and starts no timers; ordering below hero tokens (camps z-15, live legion z-16/17, heroes z-20).
* [ ] All caps and anchors are named exported constants.

### Quality Gates

* [ ] All files ASCII-encoded, Unix LF line endings.
* [ ] Biome clean; web typecheck passes; full web project tests pass from repo root.

***

## 8. Implementation Notes

### Key Considerations

* Skirmisher `path` values in the projection come from `test_failure` events; treat them as untrusted display input and sanitize through the `legionCamps.ts` segment helpers (same posture as camp sector labels).
* Wraith placement needs hero coordinates: pass a prepared `Record<HeroId, {leftPct, topPct}>` from `Battlefield.tsx` (it already computes these for tokens) instead of recomputing in the layer.
* The keep-gate anchor is a new constant pair (bottom-center of the board, inside the vignette inset); document it for Session 04 (ram-break effects) and Session 09 (bosses).
* Marker z-index: keep below hero tokens so heroes read as the foreground army (section 13A layer order).

### Potential Challenges

* Board clutter on busy repos: mitigated by the skirmisher cap + overflow chip and the wraith cap; verify visually via component tests with many entries.
* Hero not visible (over the 12-hero cap): wraith entries whose hero has no coordinates render in the summary only - do not invent positions.

### Relevant Considerations

* \[P23-apps/web] **Presentation-only battlefield layers**: no new state authority; the layer reads the projection presentation model only.
* \[P23-apps/web] **Do not move scan configuration into the battlefield**: ram `detail` and siege `stalledTaskIds` stay bounded (counts/ids only, no raw command output).
* \[P07] **Redaction is boundary-specific**: skirmisher/ram labels are a new display boundary - sanitize and test.
* \[P22-apps/web] **Do not add parallel game authorities**: caps and ordering derive in the pure model, not stored anywhere.

### Behavioral Quality Focus

Checklist active: Yes Top behavioral risks for this session:

* Unbounded marker rendering under event storms (caps must be enforced in the model, not the component).
* Raw event detail leaking into tooltips/labels.
* Accessibility regressions: markers stealing focus order from hero tokens or missing accessible names.

***

## 9. Testing Strategy

### Unit Tests

* `liveLegionPresentation`: empty legion (all-zero states), caps and overflow math, wraith ordering by stack then heroId, gremlin totals, label sanitization (absolute paths, hostile strings, non-ASCII).

### Integration Tests

* Store path: `applyEvent({type:"test_failure", ...})` renders a marker; the corresponding clearing event removes it (verify against actual reducer event shapes).

### Manual Testing

* Not required beyond component tests; visual polish lands with Session 04 effects.

### Edge Cases

* Zero heroes with wraith records (stale hero ids): summary counts only.
* Ram present and siege active simultaneously: both render without overlap (siege banner offset from keep anchor).
* 50 skirmishers: cap + "+N more" chip.

***

## 10. Dependencies

### External Libraries

* None new.

### Other Sessions

* **Depends on**: `phase24-session01-pure-strike-and-combo-model`.
* **Depended by**: Sessions 04, 05.

***

## Next Steps

Run `/implement` to begin AI-led 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/specs/phase24-session03-live-enemy-presentation/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.
