RELEASE
planning/2.8.5/plans/KARMA_ENGINE_PLAN.md
sha256 8de6b0f06f00dca0 · 25210 bytes ·
original held in the private archive
# KARMA ENGINE REBUILD (FO3) — Protocol 8 Stage 1 Plan
> **Snapshot as of 2026-07-15 — Opus diagnose+plan stage. No code, no commits.**
> Branch `dev` @ `eecf825`. Target version **2.9.0** (MINOR — new user-facing feature).
> Deterministic, native, offline karma engine for Fallout 3. **Zero AI at runtime.**
> Fixes a live Protocol 3 violation in shipped code AND introduces the citation
> guard that makes the class of violation impossible to recur.
---
## 0. TL;DR
The FO3 Karma Center currently (a) invents a faction that does not exist in the game
("ENCLAVE HIT SQUAD"), at the wrong threshold, and misses that **good** karma also gets
you hunted; (b) has three wrong companion gates; (c) models zero of the 90 level-scaled
karma **titles** that are the single most FO3-specific karma fact; (d) makes the slider the
primary interface when the owner wants **action-driven** deterministic karma; (e) prints
"NEUTRAL" twice on one board.
This unit: moves the corrected, **fallout.wiki-cited** dataset into `GAME_DEFS.FO3` as the
source of record; adds five deterministic engine functions; rebuilds `renderKarmaCenter()`
into one coherent board (action picker → title → number → what it unlocks) with the slider
kept as a manual override; strips the duplicate label; and adds **Suite (citation guard)**
that fails the build if any karma constant lacks a `fallout.wiki` source — the chokepoint
that makes the invented-faction class un-recurrable.
---
## 1. CORRECTED-FACT TABLE — shipped-wrong → correct
Every row is verified against the real shipped code (cited by `file:line`) and the
authoritative extraction. **The build transcribes exact values from the cited fallout.wiki
pages; the AI is a typist (Protocol 3).**
| # | Fact | SHIPPED TODAY (wrong) | CORRECT (fallout.wiki) | Evidence |
|---|------|----------------------|------------------------|----------|
| **F1** | **Hunter faction** | `[!] ENCLAVE HIT SQUAD RISK` — a faction that **does not exist** in FO3 | **REGULATORS** (Bounty Notice) hunt at **EVIL**; **TALON COMPANY** (Private Contract) hunt at **GOOD** | `js/ui/ui-render-factions.js:242` |
| **F2** | **Hunt threshold** | Only `karma <= -750` (Very Evil) triggers a hunter | Regulators at **≤ −250** (all Evil, not just Very Evil); Talon at **≥ +250** | `ui-render-factions.js:211,241` |
| **F3** | **Good karma is also hunted** | Not modeled at all — good players are never warned | **Talon Company hunts good/very-good players** | missing entirely |
| **F4** | **Karma titles** | 5 fixed band labels; **no level input** | **90 titles = level (1–30) × alignment (Good/Neutral/Bad)**. e.g. L1 Good = "Vault Guardian", L30 Good = "Messiah". L21–30 need Broken Steel. | no title machinery exists |
| **F5** | **Companion — Dogmeat** | Listed under **good** (≥ +250 gated) | **NO karma requirement** | `state.js:1314` |
| **F6** | **Companion — Charon** | Listed under **neutral** (gated) | **NO karma requirement** | `state.js:1316` |
| **F7** | **Companion — Butch DeLoria** | **Missing entirely** | **Neutral** (−249…+249) companion | absent |
| **F8** | **Companion count** | 7 names in 3 buckets | **8 companions** in 4 gate-classes (none/good/neutral/evil) | `state.js:1313–1317` |
| **F9** | **Redundant NEUTRAL** | Slider readout AND Karma Center both print the tier word | One coherent board; strip the slider's duplicate label | `index.html:2659` + `ui-render-factions.js:235` |
| **F10** | **Primary interface** | Slider is the only way to change karma | **Action-driven**: tap "stole from container" (−5) etc.; slider becomes manual override | whole panel |
### ⚠ DO NOT "FIX" — already correct, keep byte-for-byte
- **The 5 numeric bands** (`ui-core-cmd.js:939–945` `_KARMA_TIERS` and `ui-render-factions.js:211–231`): ≤−750 Very Evil · −749…−250 Evil · **−249…+249 Neutral** · +250…+749 Good · +750+ Very Good. **These are correct.** A prior model wrongly claimed they were off by reading a paraphrase — verified against code, they match the wiki. Reuse `_KARMA_TIERS`, do not redefine (Protocol 22).
- **`state.karma` range** ±1000, clamp on the slider (`index.html:2670–2671`). Keep.
### Observed adjacent quirk — FLAG, do not fix here (out of scope)
`_KARMA_TIERS[4].label === 'Messiah'` (`ui-core-cmd.js:944`) — the **NV** swing-needle
readout's top band is labelled "Messiah", which is actually an FO3 level-30 *title*, not a
band name. This drives the NV needle only (FO3 hides it). It is a separate mislabel outside
this unit's FO3 scope and the "don't touch the bands" instruction. Note it in the plan
report for a future NV pass; **do not change it in this unit.**
---
## 2. STATE-FIELD REALITY CHECK (correction to the brief)
- The player level is **`state.lvl`** (default `1`), **not** `state.level`. Input id
`stat_lvl`, handler `onLvlInputChanged()` (`ui-core-cmd.js:226`), clamped to
**`MAX_PLAYER_LEVEL = 50`** (`ui-core-cmd.js:268`). The title function must read
`state.lvl`.
- **App level cap (50) > FO3 title table cap (30).** `getKarmaTitle(karma, lvl)` must
**clamp the level to the table's max (30)** so levels 31–50 resolve to the level-30 title
rather than `undefined`. This is a mandatory enumerated edge case (§7).
- **No new numeric state field is required** — `state.karma` already exists and already
migrates/imports/saves. See §6 for the event-log persistence decision.
---
## 3. DATA STRUCTURES — the source of record in `GAME_DEFS.FO3`
All karma data is **per-game DATA in `GAME_DEFS.FO3`** (Protocol 38), read via
`_activeDef()`, gated behind the existing `usesKarmaCenter` flag so NV never reaches it.
No game literals in feature code. **Every entry carries a structured `src` field citing
fallout.wiki** — this is what the §5 guard enforces.
The whole karma dataset lives inside explicit guard markers so both the structural
per-entry check and a coarse region grep can target it:
```js
// ── KARMA-DATA-GUARD:BEGIN — every constant below cites fallout.wiki (Protocol 3, Suite <N>) ──
karma: {
// §3a — hunter factions (replaces the invented ENCLAVE HIT SQUAD)
hitSquads: [
{ faction: 'Regulators', alignment: 'evil', threshold: -250, contract: 'Bounty Notice',
src: 'fallout.wiki "Regulators (Fallout 3)" / "Karma (Fallout 3)"',
spawnFrequency: null, squadSize: null, verificationStatus: 'unverified' },
{ faction: 'Talon Company', alignment: 'good', threshold: 250, contract: 'Private Contract',
src: 'fallout.wiki "Talon Company" / "Karma (Fallout 3)"',
spawnFrequency: null, squadSize: null, verificationStatus: 'unverified' },
],
// §3b — 8 companions, 4 gate-classes. karmaReq ∈ none|good|neutral|evil.
// otherGate is transcribed cited prose (caps cost / quest / Barter check).
companions: [
{ name: 'Dogmeat', karmaReq: 'none', otherGate: '<transcribe>', src: 'fallout.wiki "Dogmeat (Fallout 3)"' },
{ name: 'Charon', karmaReq: 'none', otherGate: '<transcribe>', src: 'fallout.wiki "Charon"' },
{ name: 'Fawkes', karmaReq: 'good', otherGate: '<transcribe>', src: 'fallout.wiki "Fawkes"' },
{ name: 'Star Paladin Cross',karmaReq: 'good', otherGate: '<transcribe>', src: 'fallout.wiki "Star Paladin Cross"' },
{ name: 'Butch DeLoria', karmaReq: 'neutral', otherGate: '<transcribe>', src: 'fallout.wiki "Butch DeLoria"' },
{ name: 'Sergeant RL-3', karmaReq: 'neutral', otherGate: '<transcribe>', src: 'fallout.wiki "Sergeant RL-3"' },
{ name: 'Clover', karmaReq: 'evil', otherGate: '<transcribe>', src: 'fallout.wiki "Clover"' },
{ name: 'Jericho', karmaReq: 'evil', otherGate: '<transcribe>', src: 'fallout.wiki "Jericho"' },
],
// §3c — 90 titles: level 1..30 × 3 alignment columns.
// ONE table-level citation (single wiki page). Values transcribed exactly.
titlesSrc: 'fallout.wiki "Karma (Fallout 3)" (Karmic titles table)',
titleMaxLevel: 30, // app allows lvl 50; clamp to this for lookup
titles: {
good: [ /* index 0 = L1 "Vault Guardian" … index 29 = L30 "Messiah" */ ],
neutral: [ /* L1..L30 */ ],
bad: [ /* L1..L30 */ ],
},
// §3d — event-action table. Player picks one → applyKarmaEvent(delta).
// repeatable=true for grindable actions; false for one-off story beats.
// UNVERIFIED / SOURCE-CONFLICT values ship as null + verificationStatus.
events: [
{ id: 'steal_container', label: 'Stole from a container', delta: -5, repeatable: true, src: 'fallout.wiki "Karma (Fallout 3)"', verificationStatus: 'verified' },
{ id: 'water_beggar', label: 'Gave purified water to a beggar', delta: 50, repeatable: true, src: 'fallout.wiki "Karma (Fallout 3)"', verificationStatus: 'verified' },
// … full cited table transcribed by the build from the extraction …
{ id: 'project_purity', label: 'Activated Project Purity yourself', delta: null, repeatable: false,
src: 'fallout.wiki "Take it Back!" — SOURCE CONFLICT +600 vs +1000', verificationStatus: 'conflict' },
],
}
// ── KARMA-DATA-GUARD:END ──
```
### Source-of-record mechanism (how the build consumes cited data without re-deriving)
The **full cited dataset is captured in a companion data doc**, not left for the build model
to re-derive from memory (hallucination risk):
> **`planning/KARMA_DATA.md`** (Stage-2 deliverable, authored from the ChatGPT extraction +
> spot-checked against the cited wiki pages) holds: the complete 90-title table, all 8
> companions with exact `otherGate` prose, the full event-action list with exact deltas and
> repeatability, and the hit-squad rows. Every line carries its `fallout.wiki` page. **The
> build's only job is transcription** from this doc into `GAME_DEFS.FO3.karma` — it never
> invents a value. Any cell the doc marks UNVERIFIED/CONFLICT ships as `null` +
> `verificationStatus`, never a guessed number.
(This plan intentionally does **not** transcribe the 90 titles / full event list inline —
that belongs in the data doc so there is exactly one source of record, per the DRY/no-second-
copy-of-truth discipline in CLAUDE.md's doc model.)
---
## 4. ENGINE FUNCTIONS — deterministic, offline, zero AI
All live in `js/ui/ui-render-factions.js` alongside `renderKarmaCenter()` (Protocol 22 —
extend the karma renderer's home, no new file/service). All are **pure over
`(karma, lvl)` + `_activeDef().karma`** — game-agnostic by construction; NV (`usesKarmaCenter:
false`) never calls the title/hit-squad machinery.
| Function | Signature | Behavior |
|----------|-----------|----------|
| `getKarmaTier(karma)` | → `{label, ...}` | Reuse existing `_KARMA_TIERS` (5-band). **No new breakpoints** (Protocol 22). |
| `getTitleAlignment(karma)` | → `'good'\|'neutral'\|'bad'` | **3-way** collapse: Very Good+Good→`good`; Neutral→`neutral`; Very Evil+Evil→`bad`. ⚠ Distinct from the 5-band tier — do not conflate. |
| `getKarmaTitle(karma, lvl)` | → `string` | `align = getTitleAlignment(karma)`; `idx = Math.min(lvl, titleMaxLevel) - 1` (clamp 50→30); `titles[align][idx]`. Fallback to tier label if table cell empty. |
| `getKarmaHitSquad(karma)` | → hit-squad row \| `null` | `karma <= -250` → Regulators row; `karma >= 250` → Talon row; else `null`. Data-driven from `hitSquads[]`, never a literal. |
| `applyKarmaEvent(delta)` | mutates | `state.karma = Math.max(-1000, Math.min(1000, (state.karma\|\|0) + delta))`; `saveState()`; sync the slider (`#stat_karma`) + `updateKarmaUI()` + `renderKarmaCenter()`. **Guards `delta == null`** (UNVERIFIED events are non-applicable — see §7). |
`getKarmaCompanions(karma)` (or inline in render): partition `companions[]` by
`karmaReq` against the current karma — `none` always available; `good`/`neutral`/`evil`
available when karma is in that band.
---
## 5. ⭐ THE PROTOCOL 3 CITATION GUARD — the chokepoint that makes F1 un-recurrable
**New Node-runner suite (`tests/robco-diagnostics.js`)** — a static, greppable-axiom guard.
The invented ENCLAVE HIT SQUAD survived because Protocol 3 is honour-system. Per the
escape-ratchet (Protocol 36b): the class of defect that escaped gets a gate guard at the
layer it escaped from.
**What it checks (scoped to THIS unit's karma data):**
1. **Region present:** the `KARMA-DATA-GUARD:BEGIN … END` markers exist in `state.js`.
2. **Per-entry `src`:** every object in `hitSquads[]`, `companions[]`, and `events[]`, plus
`titlesSrc`, has a non-empty `src`/`*Src` string **matching `/fallout\.wiki/`**. A new
entry cannot be added without citing the wiki → adding a fake faction now **fails the
build**.
3. **No un-cited numeric karma value:** every `delta`/`threshold` that is a number lives on
an entry that carries a `fallout.wiki` `src`. A numeric value whose entry lacks a citation
fails.
4. **UNVERIFIED ⇒ null:** any entry with `verificationStatus` of `'unverified'` or
`'conflict'` must have its numeric field (`delta`/`spawnFrequency`/`squadSize`) `=== null`
— a guessed number on an unverified entry fails (locks the "ship as null, never a guess"
rule).
5. **Anti-regression for F1:** assert the karma dataset contains **no** `/enclave/i`
hit-squad faction and **does** contain `Regulators` + `Talon Company` at thresholds
`-250` / `+250`. This directly locks the corrected fact so the exact invented string can
never come back.
**Why the structural `src` field (not comment-proximity grep):** a required field on every
entry is machine-exact and false-positive-free; a "is there a `// fallout.wiki` comment near
this line" heuristic is fragile. The `src` field IS the axiom — greppable, un-removable
without a test failure.
**How it generalizes (note only — DO NOT build here):** the same `src`-field-or-region-guard
discipline is the seed of a future **data-provenance sweep** across all `GAME_DEFS` gameplay
data (barter/vats/rads already carry citation *comments*; the sweep would migrate them to the
structured `src` form + one broad guard). That is a separate unit — this plan scopes the
guard to the karma dataset only and flags the generalization.
---
## 6. UI — the event-log board + redundancy fix (Protocol 22, 17, 25)
`renderKarmaCenter()` is **extended, not replaced** (Protocol 22). One coherent board, top to
bottom: **ACTION PICKER → TITLE → NUMBER/BAND → WHAT IT UNLOCKS**.
- **Action picker (the "event log"):** a filterable list of the cited `events[]`, reusing the
existing **tracker-row pattern** (Protocol UI-3: `div.tracker-row` + a `<button>` per
action, `aria-label` literal) and the **`notesSearch`-style filter input** already in the
DOM (Protocol 22). Each row: label + delta badge (`+50` / `−5`) + an apply `<button>` that
calls `applyKarmaEvent(delta)`. **UNVERIFIED events (`delta === null`)** render disabled
with a "value unverified" note — visible but non-applicable (never a guessed apply).
- **Title line:** `getKarmaTitle(state.karma, state.lvl)` — the level-scaled title (the new
FO3-specific readout). Updates live when karma OR level changes.
- **Number/band:** the existing tier label + `KARMA ±n` readout (reuse). This is the **only**
place the tier word appears on the board now.
- **Unlocks:** hit-squad warning driven by `getKarmaHitSquad()` (correct faction + contract,
or "no bounty active"); companion availability from `getKarmaCompanions()` (the corrected
8, grouped by gate-class, with `otherGate` shown as tracker-meta).
- **Redundancy fix (F9):** strip the duplicate tier word from the slider side (`karma_label`
keeps driving the **needle readout for NV**, which FO3 hides; on FO3 the tier word appears
once, in the Karma Center). The slider (`#stat_karma`) **remains the manual override**
(Protocol 22, 25) — `applyKarmaEvent` and the slider both write `state.karma` and both
re-render through one path so they can never drift.
- **Mobile (Protocol 17):** action buttons ≥28px tap target, filter input ≥16px font, no
360px overflow. Verified with **Playwright directly** (`tests/render-check.mjs` at 360/412),
never a dev server (Protocol 10). Primary devices Android/OnePlus.
---
## 6a. PERSISTENCE DECISION (Protocol 4 implication — decided)
**Decision: the event log is STATELESS over `state.karma`. No new durable state field.**
- Applying an action mutates the **existing** `state.karma` (already defaulted, migrated,
imported, sanitized, saved). Titles/tier/companions/hit-squad are all **computed live** from
`state.karma` + `state.lvl` on every render — nothing new to persist.
- **Protocol 4 cost: zero.** No `state.js` default, no `migrateState()`, no `autoImportState()`,
no sanitizer entry, no cloud-sync path — `state.karma` already rides the serialized-whole
save (Protocol 34).
- This is the **tightest reading** of the brief (matches the owner's stated preference for the
narrowest scope). The phrase "event log" here means the **action picker + live computation**,
not a persisted history.
- **Deferred alternative (flag for owner, NOT in this unit):** a durable
`state.karmaHistory[]` (timestamped applied events) WOULD be a new state field triggering the
**full Protocol 4 4-file chain** + a sanitize entry + a cloud round-trip test. Recommend
against for v1 — it adds real surface for a feature the owner framed as "compute karma," not
"audit karma." Revisit only if the owner explicitly wants a reviewable ledger.
---
## 7. ENUMERATED EDGE CASES (Protocol 8 plan-audit / Protocol 26)
Every path the change touches, with intended behavior:
1. **Level 31–50** (app cap 50 > title cap 30): `getKarmaTitle` clamps `idx` to 29 → shows the
L30 title. Never `undefined`.
2. **Level 1**: `idx = 0` → the L1 title (e.g. "Vault Guardian").
3. **Broken Steel titles (L21–30)**: present in the table; app already allows those levels, so
no gating needed — they simply resolve.
4. **Karma exactly at a boundary** (−250, +250, −750, +750): reuse `_KARMA_TIERS` (`<=`/`<`)
exactly — boundary behavior identical to today's shipped bands. Hit-squad uses the same
`-250`/`+250` edges (`<=`/`>=`).
5. **Karma clamp at ±1000**: `applyKarmaEvent` clamps; a +50 at +990 lands at +1000; a −5 at
−1000 stays −1000.
6. **UNVERIFIED event (`delta === null`)**: row disabled, apply is a no-op, "value unverified"
note shown. No guessed number ever applied.
7. **NV context (`usesKarmaCenter: false`)**: title/hit-squad/companion machinery never runs;
needle readout shown; Karma Center hidden. **Suite 187.9/187.9a–f must stay green** (the
mutual-exclusivity + companion-data behavioral tests).
8. **Save from before this unit**: `state.karma` already present → loads unchanged; new board
computes from it. No migration needed.
9. **Slider override still works**: dragging `#stat_karma` updates state + re-renders the board
(title/companions/hit-squad recompute). Slider and action picker converge on one value.
10. **Level changed via `stat_lvl`** while Karma Center open: title line must recompute — hook
`renderKarmaCenter()` into the level-change path (or the existing `updateMath()`/dirty-check
at `ui-core.js:2036`) so the level-scaled title updates live.
11. **Desktop ≥1000px** and **portrait/landscape FO3 Pip-Boy**: board renders without overflow;
desktop unchanged (Protocol 10).
---
## 8. ACCEPTANCE CRITERIA (Definition of Done — Protocol 26)
The unit is done only when **every** item is verified on the real artifact (rendered/run),
never assumed:
- [ ] **AC1 (F1/F2/F3):** No "ENCLAVE HIT SQUAD" string anywhere. Karma ≤ −250 shows a
**Regulators/Bounty Notice** warning; karma ≥ +250 shows a **Talon Company/Private Contract**
warning; neutral shows neither. (Behavioral test + render check.)
- [ ] **AC2 (F4):** `getKarmaTitle` returns the correct level-scaled title for a spread of
`(karma, lvl)` pairs incl. L1, L30, and L50-clamped-to-30, across all 3 alignments. Board
shows the title live.
- [ ] **AC3 (F5–F8):** Dogmeat + Charon show as **no-karma-requirement**; Fawkes + Star Paladin
Cross under good; **Butch DeLoria** + Sergeant RL-3 under neutral; Clover + Jericho under evil.
8 companions total. Behavioral test asserts the partition from real `GAME_DEFS.FO3` data.
- [ ] **AC4 (event engine):** Tapping an action changes `state.karma` by the exact cited delta,
clamped ±1000, and re-renders title/tier/companions/hit-squad. Slider stays a working override.
- [ ] **AC5 (F9):** The tier word appears exactly **once** on the FO3 board (no slider-side
duplicate).
- [ ] **AC6 (citation guard):** The new suite fails a synthetic branch where a hit-squad entry
lacks `src`/cites something other than fallout.wiki, and where an `unverified` entry carries a
non-null number. Passes on the real data. Anti-regression assert for the F1 strings.
- [ ] **AC7 (UNVERIFIED policy):** Project-Purity self-activation (source conflict), hit-squad
spawn frequency, and Regulator squad size all ship as `null` + `verificationStatus`, and render
as "unverified", never a number.
- [ ] **AC8 (Protocol 38):** No FNV/FO3 game literal in the new engine functions or render code;
all karma data read via `_activeDef().karma`. NV unaffected (Suite 187.x green).
- [ ] **AC9 (Protocol 17/10):** No 360px overflow; inputs ≥16px, tap targets ≥28px; desktop
unchanged — verified via Playwright `render-check.mjs` at 360/412 + a ≥1000px check.
- [ ] **AC10 (gate):** Full gate green; test count synced everywhere (Protocol 2a); regression
test present in the same commit (Protocol 13); docs updated (Protocol 2); `CACHE_NAME` bumped
(served files change — Protocol 1); version → 2.9.0.
---
## 9. UNVERIFIED VALUES — ship as `null`, never guessed (Protocol 3)
| Value | Status | Ship as |
|-------|--------|---------|
| Project Purity self-activation delta | **SOURCE CONFLICT** (+600 vs +1000 on the wiki) | `delta: null`, `verificationStatus: 'conflict'` |
| Hit-squad spawn frequency | **UNVERIFIED** on the wiki | `spawnFrequency: null, verificationStatus: 'unverified'` |
| Regulator / Talon squad size | **UNVERIFIED** | `squadSize: null, verificationStatus: 'unverified'` |
| Any event delta not found verbatim on a cited page | **UNVERIFIED** | `delta: null` + status; row disabled in UI |
The citation guard (§5 check 4) enforces that every one of these has its number `=== null`.
## 9a. Kill-penalty modeling note (Protocol 3 trap)
Kill karma penalties are **NOT derivable from an NPC's alignment** — the wiki explicitly warns
the penalty is programmed faction data. If kill-based events are included, model an **explicit
cited `killKarmaDelta`** per event; **never** an `if (npc.karma >= 250) …` inference. (Flagged so
the build does not "compute" a value the data must supply.)
---
## 10. FILE-TOUCH MAP (for Stage 2)
| File | Change |
|------|--------|
| `js/core/state.js` | Add `GAME_DEFS.FO3.karma { hitSquads, companions, titles+titlesSrc+titleMaxLevel, events }` inside `KARMA-DATA-GUARD` markers. Remove the old flat `karmaCompanions` string buckets (or keep as a derived convenience — prefer replace, Protocol 22). |
| `js/ui/ui-render-factions.js` | Add 5 engine fns; rebuild `renderKarmaCenter()` into the action-board; keep `usesKarmaCenter` gating + the mutual-exclusive needle hide. |
| `index.html` | Karma Center markup for the action picker + filter input; strip the duplicate slider-side tier label. Bump nothing structural for NV. |
| `css/` (curio/fo3 karma styles) | Tracker-row + action-badge styles; mobile tap targets. |
| `sw.js` | Bump `CACHE_NAME` (served files change — Protocol 1). |
| `tests/robco-diagnostics.js` | New citation-guard suite + engine behavioral tests + F1 anti-regression + updated 187.9x for the new companion data shape. Sync counts (Protocol 2a). |
| `tests/test.html` | Update if the import/companion contract shape changes (Protocol 40). |
| `planning/KARMA_DATA.md` | **New** — the full cited dataset the build transcribes from. |
| `ARCHITECTURE.md` / `CHANGELOG.md` / `README.md` | Protocol 2 docs; version → 2.9.0. |
| `QUEUE.md` / `library/BRAIN_DUMP.md` / `library/CODE_MAP.md` | LIVE-doc updates if structure/protocol changes (new citation-guard suite). |
---
## 11. HARD CONSTRAINTS (restated for Stage 2)
- **Protocol 3** — fallout.wiki only; every constant cited via `src`; UNVERIFIED ships `null`.
- **Protocol 38** — all karma data in `GAME_DEFS`; no game literals; NV unaffected.
- **Protocol 22** — extend `renderKarmaCenter` + reuse `_KARMA_TIERS` / tracker-row / filter-input
patterns; no parallel karma renderer, no second tier-breakpoint definition.
- **Player-control** — deterministic, offline, native; zero AI at runtime; slider override survives.
- **Protocol 17/10** — mobile-safe; verify with Playwright directly, never a dev server.
- **Protocol 13** — regression test (the F1 anti-regression + engine tests) in the same commit.
- **Protocol 26 / 8** — every AC verified on the real rendered/run artifact before "done".
STAMP · generated for RELEASE v2.8.5 commit 06e5180 (06e51801b38a) · archive input-tree hash c07fbfbdd2e1ddeb · 754 files · no wall-clock timestamp (regenerates identically when nothing changed).