> 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-session01-pure-strike-and-combo-model/implementation-notes.md).

# Implementation Notes

**Session ID**: `phase24-session01-pure-strike-and-combo-model` **Package**: apps/web **Started**: 2026-07-08 05:29 **Last Updated**: 2026-07-08 05:29

***

## Session Progress

| Metric              | Value   |
| ------------------- | ------- |
| Tasks Completed     | 18 / 18 |
| Estimated Remaining | 0 hours |
| Blockers            | 0       |

***

## Task Log

### 2026-07-08 - Session Start

**Environment verified**:

* [x] Prerequisites confirmed (check-prereqs pass, npm 11.16.0, apps/web registered)
* [x] Tools available (jq 1.7, git 2.43.0)
* [x] Directory structure ready

### Task T001 - Verify prerequisites

**Completed**: 2026-07-08 05:29

**Notes**:

* `npx tsc -b --noEmit` in apps/web passes clean at baseline.
* `ToolName` union verified in `packages/protocol/src/missions.ts` (13 named tools plus `(string & {})` custom MCP escape hatch). `ToolUseEvent` / `ToolResultEvent` verified in `packages/protocol/src/events.ts` (`tool_result` carries `ok: boolean` and optional bounded `preview`).
* `toolCategoryFor` verified in `apps/web/src/lib/toolUsage.ts`; its category split does not match the strike vocabulary one-to-one, so the strike mapping will be explicit and local per the spec.
* `legionCamps.ts` exports `normalizeLegionIssuePathSegments` and `sanitizeLegionSectorLabel` - reusable as the safe-path substrate for `safeStrikeLabel` (absolute-root scoping, sensitive-segment redaction, ASCII normalization already proven there).

### Tasks T002-T013 - combatPlayback.ts module

**Completed**: 2026-07-08 05:35

**Notes**:

* Created `apps/web/src/lib/combatPlayback.ts` (\~300 lines) with the full pure model: strike classification, counterattack derivation, combo merging, stage props, SFX rate limiting, and safe strike labels.
* Constant values landed: `STRIKE_MAX_DURATION_MS` 400, `COMBO_WINDOW_MS` 4000, `COMBO_MAX_STAGE` 3, `STAGE_PROP_MILESTONES_MS` \[5min, 15min, 30min], `SFX_RATE_WINDOW_MS` 60000, `SFX_RATE_LIMIT_PER_MINUTE` 6, `STRIKE_LABEL_MAX_LENGTH` 64, `COMBO_STATE_TTL_MS` = combo window. All named exports for the design Phase 12 tuning sweep.
* Strike mapping: hammer (Edit/MultiEdit/Write/NotebookEdit/apply\_patch), fire (Bash), scout (Read/Glob/Grep/WebFetch/WebSearch), peon (Task), generic strike fallback (planning tools, custom MCP, empty/invalid).
* Safe labels reuse `normalizeLegionIssuePathSegments` + `sanitizeLegionSectorLabel` so strike tooltips inherit the proven Phase 23 redaction posture; tool-name component is alphanumeric-bounded with a "Strike" fallback.

**Files Changed**:

* `apps/web/src/lib/combatPlayback.ts` - new pure module.

### Tasks T014-T018 - tests and gates

**Completed**: 2026-07-08 05:36

**Notes**:

* `apps/web/tests/combatPlayback.test.ts`: 27 tests across classification, labels/privacy, combo merging, stage props, rate limiting, a simulated 45-minute stream (1140 events, bounded emissions/cues/state), and a purity-contract source scan (comment-stripped) rejecting store/React/ audio/DOM/clock/RNG usage.
* All gates pass: focused vitest 27/27, `tsc -b --noEmit` clean, `biome check` clean, both files ASCII with LF endings.

**Files Changed**:

* `apps/web/tests/combatPlayback.test.ts` - new focused test file.

**BQC Fixes**:

* Resource/memory bounds: combo accumulator prunes idle entries on every advance and cue histories are pruned+capped, verified by the long-stream test (`apps/web/src/lib/combatPlayback.ts`).
* Trust boundary: classification and labels never throw on non-string, empty, hostile, or non-ASCII input; sensitive path segments redact.

***

## Design Decisions

### Decision 1: shouldPlayCue drops the redundant category parameter

**Context**: The task text sketched `shouldPlayCue(category, historyMs, nowMs)`, but histories are already threaded per category by the caller. **Options Considered**:

1. Keep the category parameter - matches the sketch but is dead surface the function never reads.
2. `shouldPlayCue(historyMs, nowMs)` - smaller contract, category independence guaranteed structurally by caller-keyed histories.

**Chosen**: Option 2. **Rationale**: The requirement is per-category independence, which the history-per-category shape enforces; an unused parameter invites the false belief that the function coordinates across categories. Tests cover category independence explicitly.

### Decision 2: Out-of-order strike timestamps clamp forward

**Context**: WebSocket/replay delivery can reorder events slightly. **Chosen**: `advanceComboState` clamps an earlier timestamp up to the last seen time for that key, so stages never regress and pruning stays monotonic. The ordering contract is documented on the function.

### Decision 3: Only allowed cue plays are recorded in rate-limit history

**Context**: Recording denied attempts would grow history unboundedly under event storms. **Chosen**: `shouldPlayCue` records only allowed plays and returns a pruned, capped history, so caller state is bounded by the per-minute limit.


---

# 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-session01-pure-strike-and-combo-model/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.
