RELEASE
planning/2.8.5/audits/AUDIT_U6_api-split.md
sha256 ac1f8f3a5bc29f66 · 9810 bytes ·
original held in the private archive
# AUDIT U6 — `api.js` split (commit `775d8aa`, 2.8.5 U-A3)
**Auditor:** Independent (Protocol 8 stage 3). Diff-first — formed from the code, not the implementer's summary.
**Base:** `9274c75` → `775d8aa` on `dev`. Read-only. Nothing fixed, nothing committed.
**Date:** 2026-07-11.
## VERDICT: **ACCEPT**
Clean pure move. The AI directive is provably byte-unchanged and its golden-master genuinely re-derives from the split source. The gate was not weakened anywhere. `readGroup('api')` got *stronger-by-coverage*, not quieter. All 5 prior NOTE findings are closed at the root. No BLOCKER, no SHOULD-FIX. Two trivial NOTE-level observations below, neither requiring action.
I am reporting this as genuinely clean rather than manufacturing a finding to justify the stage.
---
## 1. The AI contract — provably unchanged ✅ (headline)
**Provably unchanged: YES.** Two independent proofs:
- **Content proof.** Reconstructed the original from the 4 files (headers stripped: 7+7+8 = 22 lines, which exactly accounts for the +22 line delta 2384→2406). `sort`-diff of original vs reconstruction is **empty** — the line multiset is byte-identical; no line of content was added, removed, or altered.
- **Order proof.** All 4 files are ordered subsequences of the original (verified with a greedy two-pointer match; the lone "failure" was a leading-blank-line artifact on `api-import.js` — its real first content line `/* global playQuest… */` was original line 1, correctly relocated). Order-preservation means the `_directive*` builder join order inside `getSystemDirective()` is unchanged.
**Golden-master still genuinely exercises the real assembly path: YES.** Suite 131 (131.5–131.13) does *not* compare stored strings. It:
1. sets `apiSrc131 = apiSource = readGroup('api')` — the concatenation of all 4 split files;
2. extracts each of the 8 `_directive*` builders **and** `getSystemDirective` as real source via `_declFull`/`extractFunctionBody` from that concatenation;
3. runs them in a `vm` sandbox with the **real** `GAME_DEFS` (loaded from `state.js`), calls `getSystemDirective()` across the 11-point state matrix, and asserts `sha256(output) === golden`.
If a builder body or the join order had shifted one byte, the hash would mismatch. If `readGroup` had returned only the hub, `_declFull` would fail to find the builders → `harnessErr131` set → every golden-master assert **fails** (not passes vacuously). So the test is fail-safe against exactly the "passes by not looking" class.
**The 10 golden hashes and all of Suite 131 are untouched in this commit** (`git show 775d8aa -- tests/…js | grep sha256|Suite 131|apiSource|readGroup` → empty). Combined with the byte-identical builder source, the hashes *must* still match — which is why the gate is green. Confirmed from two angles.
## 2. Pure move ✅
Proven byte-identical (see §1 content proof). The only additions are the 22 header-comment lines and the 5 eslint cross-file `globals` entries required because callers/callees now live in different files. Zero logic changes.
## 3. `readGroup('api')` / `Read-Group "api"` — NOT weaker ✅
Both runners glob `<stem>.js` OR `<stem>-*.js` across `JS_SUBFOLDERS`. Executed the Node matching logic: resolves to **4 files** (`api-directive.js`, `api-import.js`, `api-router.js`, `api.js`). PowerShell `Read-Group` uses the identical predicate (`-eq "$stem.js" -or -like "$stem-*.js"`); name-sort == path-sort here because all 4 share `js/services/`, so join order is identical to Node. The helper reads *more* source than before, not less — every test built on `apiSource` now sees the whole family.
## 4. Load order & global scope ✅
`index.html`: hub → `api-directive` → `api-import` → `api-router`, all before `cloud.js`. Verified the intra-group order is *correct*, not a lucky coincidence: the hub, `api-import`, and `api-router` have **zero** top-level executable statements (only function/const declarations); `api-directive`'s apparent top-level lines are all template-literal interior (directive text). No `const X = crossFileCall()` init-time dependency in any of the 4 files. Nothing executes until `window.onload`, so intra-group order genuinely doesn't matter.
`tests/test.html` correctly switched from loading `api.js` → `api-import.js` (the file that now owns the `autoImportState`/`sanitizeImportedContainer` it exercises). Confirmed `api-import.js` has **no real** runtime cross-file dependency: the only cross-file token flagged (`_routeNativeCommand`) is a **comment** on line 763, not a call. Loading it alone is functionally equivalent to the old `api.js` for test.html's paths.
## 5. The 5 prior NOTE closures — real at the root ✅
- **N1 (stale "Suite 220bis"):** no `220bis` refs remain anywhere in `tests/`/`scripts/`; the CSS comment now correctly points at `scripts/check-boot-chain.js` CHECK I/J/K. ✅
- **N2 (CSS-order third copy):** both runners now derive `CSS_SPLIT_FILES` from `index.html`'s own `<link>` tags. Ran the exact derivation regex against `index.html` → 12 files, equal to all 12 stylesheet links / all 12 `href=css/*.css` occurrences. Empty-result guard (`fail + exit`) prevents a silent empty list. ✅
- **N3 (header overclaim):** remaining "byte-identical" phrases all correctly describe *composed output / tested behavior* (golden-master-verified), not a file-level slice claim. ✅
- **N4 (stale flat-path comments):** `eslint.config.mjs` and `scripts/pre-commit` path comments updated (`js/state.js`→`js/core/state.js`, `api.js`→`api-import.js`/`api-router.js`, etc.); `ocr.js`/`ui-core-cmd.js`/`ui-render.js` pointer comments all corrected — comment-only, no logic touched. ✅
- **N5 (mojibake guard too narrow):** Suite 90 widened from a 12-file hand list to `allJsFiles()` — a glob over every non-vendor `js/**/*.js` (25 files) + 3 docs + CHANGELOG. Now covers the per-game data files (`db_nv`/`db_fo3`/`reg_nv`/`reg_fo3`) that Protocol 39 says legitimately carry non-ASCII and were the exact undetected gap. Vendor is excluded (not in `JS_SUBFOLDERS`), so no false positive from minified tesseract. The `MOJIBAKE90` assertion is unchanged — same pattern, just more files. ✅
## 6. Gate not weakened ✅
- Test count 2958 → 2971 (**+13**) is **entirely** Suite 90's widening: old 15 files + CHANGELOG = 16; new 25 js + 3 docs + CHANGELOG = 29; Δ = **+13, exactly the total delta**. Every new test is a real file getting a real mojibake check — no padding.
- No golden hash changed; no assertion literal loosened; no test deleted, skipped, or made vacuous. The Suite 90/96 label strings changed only to reflect the new file set. Suite 96.2 correctly re-points test.html's expected boot chain to `api-import`.
- Where the split *would* have broken a test (test.html loading a now-moved function), the **code** was corrected (load `api-import.js`), not the test softened.
## 7. Split-boundary quality ✅
Genuinely cohesive, not convenient:
- **hub (`api.js`, 530 ln)** = `_commGet`, `_validateTriNode`, `fetchAuthorizedModels`, `saveApiKeySilent`, `_resetTransmitUI`, `transmitMessage` — one theme: the AI network-request lifecycle + comm-config. A real hub, not a leftovers bin.
- **`api-directive` (244)** = prompt assembly. **`api-import` (807)** = AI→state. **`api-router` (825)** = deterministic/offline command routing (never calls AI). Each is one responsibility; `api-router` is broad but coherent.
- Coupling is a small, explicit seam: exactly 5 cross-file globals (`_commGet`, `getSystemDirective`, `submitCommandInput`, `_routeNativeCommand`, `_isPrecisePointer`), all declared in `eslint.config.mjs`. The split reduced coupling into 4 cohesive units rather than merely relocating it.
## 8. Docs / comments ✅
`library/CODE_MAP.md` (LIVE) fully updated for U-A3 — every api function mapped to its new file; spot-checked anchors all accurate (`getSystemDirective`:233, `transmitMessage`:191, `autoImportState`:195, `NATIVE_COMMAND_ROUTER`:13, `sanitizeImportedContainer`:16, inlineData:322). CLAUDE.md/ARCHITECTURE.md LOAD-ORDER-GUARD blocks list all 4 files in order (Suite 220 mechanically checks these vs index.html; gate green). CHANGELOG `[Unreleased]` header bumped to `2971/2971 | …-r8`, plain-English entry present. sw.js `-r7`→`-r8` with all 3 new files precached. Clean tree, no junk (Protocol 41).
## 9. Pre-existing flakiness
Not investigated per instructions. Nothing in this diff touches the a11y check path; the commit did not cause it.
---
## NOTE-level observations (no action required)
- **NOTE — CSS-derive regex is order-sensitive on attributes.** `_deriveCssSplitOrder()` / `Get-CssSplitOrder` require `rel` *before* `href` in the `<link>` tag. All 12 current tags are `rel`-first, so the list is complete today. If a future `index.html` tag were authored `href`-first, that tag would be silently dropped from the derived list — a *partial* list, which the empty-result guard would NOT catch. Risk is low (Prettier normalizes attribute order; `check-boot-chain.js` CHECK I/J/K independently guards the real order), but worth a one-line awareness note for whoever edits those tags.
- **NOTE — CHANGELOG `[Unreleased]` header was stale at `-r6` before this commit** (an intervening served-file commit reached `-r7` without syncing the header). This commit *corrected* it to `-r8`, so it is not a defect in `775d8aa` — noted only so the prior-commit sync gap is on record.
---
### Bottom line
The single most important thing in this commit — the bytes the model receives — is provably identical, and the test that guards it genuinely re-derives from the new files rather than passing incidentally. The gate is unweakened, `readGroup('api')` is complete, and the +13 tests are real coverage. **ACCEPT.**
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).