GENERATED VIEW · PRIVATE ARCHIVE · DO NOT SERVE
RobCo Industries · Archive Museum

KARMA_ENGINE_PLAN.md



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).