diff --git a/SCOPE.md b/SCOPE.md index 0d68f39..1e0a862 100644 --- a/SCOPE.md +++ b/SCOPE.md @@ -919,6 +919,7 @@ operator decides whether each item is added to scope. | 48 | **Bot tier-ladder doc-drift sweep (FID-20260915-006a, `implemented`):** census-flagged T7 drift generalized — resource multiplier comments claimed 0.75→3.0× while the formula always computed 0.75→**2.25×** (T5–T7 overstated); defense ladder quotes (150→9600) were pre-scale values vs the function's real **15→2880** (10× family error); boss block inherited it (claimed 192,000 total; code builds 2,880×20 = 57,600). Corrected: botService header + getResourceRange + getBotDefenseForTier + getDefenseMultiplier range doc, attack-route garrison comment, BASE_RAID_BALANCE.md worked example (historical banner). Repo-wide sweep: no admin panels or tests quote the wrong numbers. Code truth verified by executing the formulas (comment==formula: YES); behavior unchanged; gates green | 2026-09-15 (SESSION-2026-09-15-006) | Closed (commit 2681571, canonical PR #49) | | 49 | **Vault-economy Monte Carlo re-measured against SHIPPED code (audit row 44 closure):** sim rewritten to run the real engine functions (regenerateBotResources/applyGrowthPattern/nextGrownVault/getVaultCap — no mirror), per-axis vaults, FID-005 bookkeeping, declared-resource raid mix. Caught + fixed 2 defects on the way: the harness's `v===0` guard silently re-created absorbing zero (day-30 loot 0, contradicting the live acceptance gate — removed), and the TS2308 barrel collision exposed a stale async `regenerateBotResources` duplicate in botService (zero callers, pre-linear curve, stale cap) — deleted, its Boss 0.02 rate carried into the engine table (which lacked Boss → latent NaN) with a `?? 0.10` NaN guard. **Band confirmed:** declared single-axis 2.73M/day flat over 30 days (= audit 2.24M × 1.22 hoarder-3× exactly); undeclared 5.48M/day; legacy counterfactual still dies (0 at day 30). Live check: 54 bots, 0 dead-both. Gates: tsc 0 (full) · lint 0 · vitest 815/1 | 2026-09-15 (SESSION-2026-09-15-006) | Complete (commit 2681571, canonical PR #49) — re-measurement archived | | 50 | **Ladder-truth CI gate (FID-20260915-006a follow-through):** the doc-drift lesson made executable — `scripts/ladderTruth.ts` parses the documented tier tables out of botService source comments (resource multipliers ×2 sites, base defense ×2 sites, player brackets) and `__tests__/lib/ladderTruth.test.ts` asserts every documented value equals live function output (getResourceRange / getBotDefenseForTier / getPlayerLevelBonus); comment-only edits that drift now fail CI, site-count guards fail loudly on deleted tables, truth spot-pins keep the scanner honest. Drill-verified: corrupted comment value → 2 failing tests → restored clean. getBotDefenseForTier exported for the gate. Gates: tsc 0 · lint 0 · vitest 821/1 skipped | 2026-09-15 (SESSION-2026-09-15-006) | Closed (commit d8eb098, canonical PR #51) | +| 51 | **Header numeric-claim drift sweep (`dev/audits/HEADER-DRIFT-SWEEP-2026-09-15.md`):** machine sweep of every lib/ file header for numeric claims without test/runtime validation (~200 claims / ~40 files), top candidates adjudicated claim-vs-code-vs-tests. Ranked five: (1) factoryUpgradeService header curve already contradicts the CI-pinned factoryCurves curve (wrong slots/regen/costs/defense); (2) battleService header documents superseded combat semantics (pre-FID-20260915-001/-002 formula line, pre-FID-005 theft rule; capture 10-15% still true); (3) botGrowthEngine build-rate/age block true today but zero-pinned and probabilistic; (4) botSummoningService 1.5x/168h player-facing claims enforced by nothing; (5) rankingService duplicated band table (code-safe via powerMultiplier passthrough, docs-drift + overlap-ambiguity risk). Fix path: extend the row-50 ladder-truth gate pattern; #1/#2 are live falsehoods needing doc correction first | 2026-09-15 (SESSION-2026-09-15-006) | Audit complete — fixes open for operator | | 30 | **Gate-baseline divergence:** repo gates no longer match the ledger's 2026-09-06 "tsc 0 / eslint clean / 341 green" — live: tsc 1 error (`__tests__/lib/flagHolderSurvival.test.ts`, committed `4674b73`), eslint **460 errors / 3 warnings** (incl. re-appeared `any`s in friends suites previously burn-downed), vitest **12 failed** (all `lib/__tests__/redis.test.ts`). The tree also carries an uncommitted parallel session's WIP (20 modified files: clan panels, friends/messaging tests, messagingService, websocket handlers; untracked `scripts/nn-fixany.mjs`, `scripts/nn-lintreport.mjs`, `lib/errorMessage.ts`, `docs/llms-*`; `MONGODB_TO_MARIADB_SCHEMA_MAPPING.md` deleted). Attribution and disposition were operator decisions — session 2026-09-07 (001) touched nothing beyond its approved 2-line fix **→ RESOLVED 2026-09-07 (SESSION-2026-09-07-002): operator reviewed the WIP and chose fix+commit+gitignore — defect repaired, WIP committed as 4 path-scoped commits (`0e82eb5`, `8be0bde`, `de914fa`, `8051813`), scraped llms docs gitignored (kept local), nn-*.mjs codemod scripts left untracked pending operator call** | 2026-09-07 (sessions 001–002) | Closed (resolved) | --- diff --git a/dev/audits/HEADER-DRIFT-SWEEP-2026-09-15.md b/dev/audits/HEADER-DRIFT-SWEEP-2026-09-15.md new file mode 100644 index 0000000..1418037 --- /dev/null +++ b/dev/audits/HEADER-DRIFT-SWEEP-2026-09-15.md @@ -0,0 +1,87 @@ +# Header numeric-claim drift sweep — lib/ (2026-09-15) + +**Task source:** operator directive — "Sweep every lib/ file header for the same drift +class: numeric claims in comments that no test or runtime check validates, and rank the +riskiest five." Follows FID-20260915-006a (tier-ladder comments drifted for months) and +the ladder-truth CI gate (SCOPE row 50) that now guards that one file. + +## Method (and its limits) + +Machine sweep of the **file-header comment block only** (up to first code line, ≤140 +lines) of every `lib/**/*.ts`: regex extraction of percentages, multipliers, numeric +ranges, comma-grouped magnitudes, and time constants (noise-filtered: FIDs, dates, +version strings). ~200 claims across ~40 files. Each top candidate then adjudicated by +hand: claim ↔ code ↔ test-coverage check (import-level grep of `__tests__/`, not raw +substring count). + +Limits: function-level comments are out of scope here (botService's are already +CI-guarded by `scripts/ladderTruth.ts`); test-coverage probes are greps, so a test that +pins a value without the literal is counted as uncovered (conservative — that's the +right bias for a risk ranking). + +## The ranked five + +### 1. `lib/factoryUpgradeService.ts` — header curve contradicts CI-pinned truth (HIGH) +Header documents: slots `5000 + (level−1)×500` (L1 = 5,000 … L10 = 9,500), regen +`416.67/hr`, costs `1000/500 × 1.5^level`, defense ladder 1,500 / 11,391 / 76,699 / +169,000. The live engine (pinned by `__tests__/lib/factoryCurves.test.ts`, green in +CI) is a **different design**: slots 400 + 150/level (L1 = 400 … L10 = 1,750), regen +120/hr, costs base 2,500/1,250 × 1.5^target, defense (L−1)²×12,500 with the L1 cliff +smoothed. This is not drift-in-waiting — **the header is already false**, in the exact +file family (`factory*`) that shipped the auction build-shape regression, and no test +reads the header. Any engineer tuning factories from the header tunes the wrong game. +Fix: rewrite header to the pinned curve; ideally re-point the doc at the test. + +### 2. `lib/battleService.ts` — header documents superseded combat semantics (HIGH) +Header (L14–18): counter formula `DefenderDEF − AttackerSTR/2`, "HP loss translates to +unit casualties", "Winners capture 10–15%", "Base attacks allow 20% resource theft". +Reality: the capture constants (L140–141: 0.10/0.15) are true, but the theft line +predates FID-038's declared-resource rule and FID-20260915-004/-005's vault caps and +preserve-axis bookkeeping, and the formula line describes the pre-FID-20260915-001/002 +engine — the exact algebra whose garrison mis-wire produced the annihilation incident. +Combat is the highest-blast-radius system in the game and this header is the first doc +any engineer reads. Fix: restate the live rules; keep capture (still true). + +### 3. `lib/botGrowthEngine.ts` — build-rate/age block is true today, unguarded, and probabilistic (MEDIUM-HIGH) +Header build rates (Fortress 1/2h, Raider 1/h, Hoarder 1/4h, Ghost 1/1.5h, Balanced +1/h) match `BUILD_RATES` (L76–82: 0.5/1.0/0.25/0.67/1.0) — verified this sweep. But +zero tests import the table, builds are probabilistic (`Math.random() < rate`), and the +header's age multipliers (1× <7d, 1.5× 7–30d, 2× >30d) describe `getAgeMultiplier` +steps that nothing pins. This file just underwent its largest rewrite (FID-005/006); +the ladder-truth precedent shows how fast these tables rot. Fix: extend the ladder-truth +gate to `BUILD_RATES` + age steps + `REGENERATION_RATES`. + +### 4. `lib/botSummoningService.ts` — feature claims with zero coverage (MEDIUM) +Header promises 1.5× summoned-bot resources and a 168h (7-day) cooldown. Both are real +today (`RESOURCE_MULTIPLIER` applied at L118–119, `COOLDOWN_HOURS: 168`) but no test +touches the service — the claims are enforced by nothing. These are *player-facing +promises*: if the multiplier is dropped during a refactor, summoning silently stops +delivering what the UI/tech-tree says it does. Fix: two unit pins (multiplier applied; +cooldown math). + +### 5. `lib/rankingService.ts` — overlapping band table, docs-only risk (MEDIUM) +Header restates the four balance bands (0.5×/0.8×/1.0×/1.1×) with boundaries that +overlap in the text (Balanced 0.85–1.15 vs Optimal 0.95–1.05) without saying which wins. +Code drift is *currently impossible* — ranking consumes `balanceEffects.powerMultiplier` +from balanceService, single source — but the duplicated table can drift (it must be +edited in lockstep with balanceService's), no direct `rankingService` test exists, and +the overlap ambiguity invites a future wrong fix. Fix: one line ("band resolution lives +in balanceService.getBalanceEffects; values below are a mirror") + a parity test +mirroring the ladder-truth pattern. + +## Honorable mentions + +- `concentrationZoneService.ts`: "70%" (×2 in header) — concentration-yield claim, no test. +- `harvestEstimate.ts`: header's "wrong base ranges / fabricated blocks" narrative is + *correctly labeled as history* and the module has a dedicated test — the model citizen. +- `terrainCodec.ts`: 22,500-tile payload math is a grid-size fact; codec tested, the + magnitude claim isn't (cosmetic risk). +- Migration files (`migrations/*.ts`) carry one-off historical numbers — out of scope; + they describe completed, non-recurring work. + +## Recommendation + +The riskiest five share one property: **numeric truth that lives in prose**. The +ladder-truth gate (row 50) already proves the pattern works; extending it costs ~30 +lines per table. Suggested order: #1 and #2 are *already false* (doc corrections, not +just guards); #3–#5 are guard additions before they rot.