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

AUDIT_karma.md



RELEASE

planning/2.8.5/audits/AUDIT_karma.md

sha256 0ea9fafe883a292a · 11552 bytes · original held in the private archive

# AUDIT — FO3 Karma Engine rebuild (Protocol 8 Stage 3, independent) > **Auditor:** Opus, Stage-3 independent audit. **Branch:** `dev` @ `5ed8ba2` (clean). > **Method:** started from the diff + running app + live fallout.wiki cross-check; > reproduced the citation guard red-then-green by hand; rendered the Karma Center > with Playwright directly at 360/412/desktop. The implementer's report was **not** > read. Snapshot as of 2026-07-15. > **Commits audited:** `5946e41` (karma engine) + `5ed8ba2` (2.9.0→2.8.0 version revert). ## VERDICT: ✅ PASS The unit does exactly what it set out to do. The fabricated "ENCLAVE HIT SQUAD" is gone; the replacement data is real, wiki-cited, and independently verified; the citation guard genuinely prevents recurrence (I broke it and watched it catch the break); the engine is deterministic/offline; NV is untouched; the version revert is complete and the golden-master hashes are correct. Two **minor, non-blocking** findings (doc-count drift; a pre-existing app-wide portrait font-size gap the unit inherited) are listed at the end. Neither warrants a return to Stage 2. --- ## 1. IS THE DATA CORRECT AND ACTUALLY CITED? — ✅ YES ### Enclave hit squad is GONE (grepped the whole codebase, not just the karma file) Every surviving `enclave` reference is legitimate, not the fabrication: - `state.js:805` — the **faction-reputation registry** entry `{ key:'enclave', name:'Enclave', tier:'major' }`. The Enclave is a real FO3 reputation faction; unrelated to karma hit squads. - `state.js:1661` — the karma **action** `destroy_enclave_crawler` ("Destroyed the Enclave Mobile Command crawler", +500, cited to Broken Steel's *Who Dares Wins*). A **real** karma event, correctly cited. - `state.js:1812` — the AI faction-directive text (lists Enclave as a rep faction). Legit. - Comments in `state.js` / `ui-render-factions.js` that document the removal. The hit-squad logic (`getKarmaHitSquad`, `GAME_DEFS.FO3.karma.hitSquads`) contains **no Enclave**. Rendered proof (412px, good karma): the only "Enclave" on screen is the cited action label; the hit-squad line reads `[!] TALON COMPANY RISK — PRIVATE CONTRACT`. `fabricatedPattern: false`. ### Corrected mechanic — both directions, neither at Neutral ✅ `hitSquads`: **Regulators @ −250 (evil)**, **Talon Company @ +250 (good)**. `getKarmaHitSquad` returns Regulators at `karma ≤ −250`, Talon at `karma ≥ +250`, `null` between. Behaviorally boundary-exact (Suite 230.9, and my own render: −750 → REGULATORS, +750 → TALON, 0 → NO BOUNTY ACTIVE). ### 90-title table — spot-checked against LIVE fallout.wiki (WebFetch 403-blocked as the build documented; verified via WebSearch across fallout.wiki + fandom mirror + Neoseeker) ✅ | Level | Good | Neutral | Bad | Verified | |---|---|---|---|---| | 1 | Vault Guardian | Vault Dweller | Vault Delinquent | ✅ | | 15 | Urban Legend | Urban Myth | Urban Superstition | ✅ | | 30 | Messiah | True Mortal | Devil | ✅ | Also rendered live: L20 good = "Last, Best Hope of Humanity" ✅, L25 bad = "Stuff of Nightmares" ✅, L12 neutral = "Keeper" ✅. The L11 "Capital" (not "Capitol") spelling is an intentional, documented correction consistent with "Capital Wasteland". No invented-looking titles found. ### Companions — exactly 8, correct gates, each cited ✅ Verified against fallout.wiki: **none** = Dogmeat, Charon; **good** = Fawkes, Star Paladin Cross; **neutral** = Butch DeLoria, Sergeant RL-3; **evil** = Clover, Jericho. Every row carries a `src`. Rendered proof: Dogmeat & Charon appear at every alignment; Butch (previously missing entirely) now appears at neutral; Clover/Jericho at evil; Fawkes/Cross at good. ### UNVERIFIED values ship as `null` — none guessed ✅ - `project_purity_self` → `delta: null`, `verificationStatus:'conflict'` (+600 vs +1000 source conflict, both pages cited). - `good_quest_act` / `evil_quest_act` → `delta: null`, `unverified` ("at least ±50", no exact figure). - Talon `squadSize: null`, `spawnFrequency: null`; Regulators `spawnFrequency: null` (squadSize `3` is genuinely cited on the Regulator page — verified: "typically operate in three-member squads"). ### Every constant carries a `fallout.wiki` src ✅ (spot-checked hitSquads, companions, titlesSrc, all 27 events). --- ## 2. THE CITATION GUARD (Suite 230) — ✅ REAL, and I proved it red-then-green Read the validator (`_validateKarmaCitations`): it flags (a) any hitSquad/companion/event/titlesSrc lacking a `fallout.wiki` src, (b) an `enclave` faction name, (c) an UNVERIFIED/conflict event with non-null `delta`, (d) an UNVERIFIED hitSquad with non-null spawnFrequency/squadSize, (e) Regulators not at −250 or Talon not at +250. It runs against a corrupted fixture (230.2, RED) **and** the real shipped data (230.3, GREEN). **I reproduced red-then-green against the REAL data myself** (not the self-contained fixture): - Replaced the real Regulators entry in `state.js` with a fabricated `{ faction:'Enclave Hit Squad', src:'', spawnFrequency:'daily', … }`. - Ran the runner → **3 failures**, each naming the exact problem: - `230.3 ✗ … ["…(Enclave Hit Squad) missing fallout.wiki src","…names an Enclave faction","Regulators missing or not at threshold -250"]` - `230.4 ✗ [anti-regression]` - `230.9 ✗ [behavioral] getKarmaHitSquad` - Reverted → **ALL 3236 PASS**. The guard is wired to the actual data, not just a green-only fixture. Recurrence is genuinely blocked. Behavioral coverage (230.8–230.11, 230.14) exercises the real engine functions against the real data end-to-end. --- ## 3. DETERMINISTIC / NATIVE / OFFLINE, and NV survived — ✅ - **Zero AI at runtime.** All six engine functions (`getKarmaTier`, `getTitleAlignment`, `getKarmaTitle`, `getKarmaHitSquad`, `getKarmaCompanions`, `applyKarmaEvent`) are pure over `state.karma`/`state.lvl` + `_activeDef().karma`. No `fetch`/`transmit` path. - **Level clamp correct.** `getKarmaTitle` = `Math.max(1, Math.min(lvl, 30)) - 1`, falling back to the tier label if the array is absent. L31 and L50 both resolve to the L30 title (Messiah/Devil) — no crash, no `undefined`. Verified in Suite 230.8 and by direct render (L50 → Messiah). - **NV untouched.** `GAME_DEFS.FNV` has **no** `karma` block (Suite 230.5 + my render check: `hasKarmaData:false`). `usesKarmaCenter:false` ⇒ the FO3 Karma Center block stays hidden and the NV swing-needle stays visible; the FO3 data never leaks (Protocol 38). NV slider still writes state (value 500 confirmed). No game literals in `getKarmaHitSquad`/`getKarmaCompanions` — data-driven via `_activeDef()`. - **Redundancy fix.** The duplicate tier label is gone — "NEUTRAL" appears exactly **1×** on the board (was the F9 double-print). The `stat_karma` slider is never hidden and still overrides manually: setting it to −300 fired `input` and drove `state.karma = -300`, `disabled:false`, `readOnly:false` — edit survives, nothing became read-only. The action picker and the slider sync through one path (`applyKarmaEvent` writes state + slider + `updateKarmaUI` + `renderKarmaCenter`); applying "water_to_beggar" moved 0 → +50 and synced the slider; the null-delta UNVERIFIED event was a correct no-op. --- ## ALSO VERIFIED - **Version fix complete.** `APP_VERSION = '2.8.0'`; `CACHE_NAME = 'robco-terminal-v2.8.0-r30'`. The only `2.9.0` strings are (a) the `v2.9.0 ENCOUNTER` roadmap refs in `db_*.js`/`ARCHITECTURE.md` (fine) and (b) the Suite 228 `_applySlotEnvelope` mock fixture `APP_VERSION:'2.9.0'` — introduced by unrelated commit `2210b57`, a pre-existing version-comparison fixture (fine). Changelog entries sit under `## [Unreleased]`. - **Golden-master hashes recomputed correctly.** Diffing the pre-karma commit (`eecf825`) → HEAD, **exactly 2** sha256 rows changed, **both `ctx:'FO3'`** — the 2 rows carrying the corrected `irreversibleTriggers` content (Enclave/Outcast fabrication removed). The 9 FNV rows are byte-identical to pre-existing. Suite 131 is green in my full run, which is definitive proof the committed hashes match the live directive text at `APP_VERSION=2.8.0` (the directive embeds `${APP_VERSION}`). - **Nothing weakened.** The removed Suite 187 assertions (187.9d/e/f, which tested the *old* flat `karmaCompanions` structure) were **replaced with equal-or-stronger** versions: the new 187.9d checks *both* `getKarmaCompanions` and `renderKarmaCenter` bodies for hardcoded companion literals and **adds Butch DeLoria** to the forbidden set the old test omitted. The eslint change is a single `_KARMA_TIERS: 'readonly'` globals declaration (Protocol 22 cross-file reuse), not a rule relaxation. No assertion was softened. - **Protocol 22** (extended `renderKarmaCenter` in place — no `renderKarmaCenterNew`/fork), **1** (cache r30), **13** (Suites 230 + 187 are the guarding tests, same commit), **41** (working tree clean; Suite 98 green). - **CHANGELOG accuracy.** All six user-facing entries are true against the running app (title live-updates, tappable cited actions with UNVERIFIED marked, Regulators/Talon fix, 3 companion-gate fixes + Butch restored, double-print fix, citation-guard note). Plain-English, correct category grouping (Protocol 21). - **Full gate:** `ALL 3236 TESTS PASSED` on a clean tree. --- ## FINDINGS (minor — non-blocking; not grounds for Stage-2 return) **F-1 — Doc test-count drift (LOW, Protocol 2a incompleteness, introduced by this unit).** The count bump 3222→3236 updated the canonical Protocol 2a locations, but **4 prose mentions still read the old count**: - `CLAUDE.md:205` ("3222-test gate"), `CLAUDE.md:233` ("3222-test pre-commit gate"), `CLAUDE.md:572` ("**229 suites, 3222 tests**"), `README.md:331` ("3222-test count"). These are hits on Protocol 2a's own mandated `Select-String` drift scan and should show `3236` (and `230 suites` for line 572). Cosmetic/internal-doc only; no functional impact. Recommend fixing in a follow-up doc-sync commit. **F-2 — New karma filter input is sub-16px in PORTRAIT mobile (LOW, pre-existing app-wide, inherited via Protocol 22).** The new `#karmaEventFilter` (`type="search"`) renders at **13.33px** at 360/412px **portrait** (the owner's primary Android/OnePlus orientation), tripping Protocol 17's ≥16px focus-zoom guard, and shows as a plain white UA box. Root cause: the FO3 16px restyle (`css/60-fo3-pipboy.css:1768`) lives inside `@media (orientation: landscape)`, so portrait falls back to the unstyled base rule. **However, this is NOT a regression this unit introduced** — `invDrawerSearch`, `questSearch`, `perkSearch`, and `notesSearch` all render at 13.33px in portrait too. The unit correctly reused the established `.tray-head input[type='search']` primitive (Protocol 22) and inherited the pre-existing portrait gap. Page-level overflow is clean at 360/412 (verified). Recommend an **app-wide** portrait fix (add a base or `@media(orientation:portrait)` 16px rule covering all `.tray-head` search filters) as a separate task, rather than singling out the karma filter — and a static Protocol-20 guard so it can't regress. --- ## Evidence artifacts - `planning/mockups/fo3/audit-karma-412-good.png` (Talon + good companions + "Last, Best Hope of Humanity") - `planning/mockups/fo3/audit-karma-412-evil.png` (Regulators + evil companions + "Stuff of Nightmares") - `planning/mockups/fo3/audit-karma-412-neutral.png` (NO BOUNTY + Butch/RL-3 + "Keeper") - `planning/mockups/fo3/audit-karma-360-good.png` / `audit-karma-desktop-*.png`
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).