# Foundation inspector — rocko build report r6 (FI-ROCKO-8: R5-1 total tie-break over the strict-JSON domain) Author: rocko (Claude Fable 5.1). Coordinator: darkwing. Reviewer: filbert. Task: FI-ROCKO-8. Written 2026-09-07 15:35 UTC. Supersedes nothing: the original report, the r2–r5 reports and manifests, the charter, the addendum, all verdicts, the corrections request, the ordering follow-up and the demo test-gate decision are preserved unedited. This revision corrects exactly the FI-FILBERT-7 finding R5-1 (verdict `docs/plans/reviews/2026-09-07_foundation-inspector-code-verdict-r5.md`, sha256 `154e7b5d804c059d6fec4c936c78e4ae33bb7fc273ba3a5eec7b00d0a9ce1e59`), records what changed r5 → r6, the new Unicode permutation regressions, and the frozen candidate for independent re-review. ## 0. Admission, compatibility and blockers FI-ROCKO-8 is **admitted and compatible** with the standing chain, the FI-ROCKO-3 build request, the corrections request, the ordering follow-up, the demo test-gate decision and the FI-FILBERT-7 verdict; the work stays inside the original source/test allowlist plus the two r6 receipt paths named in the task. C1 (Archify rev 3) remains HELD with no lane action. **No open blocker.** `scripts/test-task.sh` and `scripts/test-release.sh` remain **NOT RUN / DEFERRED** under the owner decision (§7); no live test authority is claimed. Inputs bound to this revision (all hashes machine-read; full list in the r6 manifest `pinnedInputs`, which adds the r5 report, the r5 manifest and the FI-FILBERT-7 verdict to the r5 list): | Input | sha256 | |---|---| | charter `docs/plans/2026-09-06_foundation-inspector-charter.md` | `19b6721128a627a2032ffdb95ece2d50abe69a8f6d521e9eff8bbdaff22798b6` | | addendum FI-C2-1 | `afe2980be2f91e701dae5af3018831ac5c300474f52bcc06e740ce5b5cc68ca5` | | code verdict FI-FILBERT-6 | `e4cc5970aab20240e6ea3a9a1513ef942ff167fefbb03356b9a32c89999adb3d` | | code verdict FI-FILBERT-7 (r5, NOT APPROVED, R5-1) | `154e7b5d804c059d6fec4c936c78e4ae33bb7fc273ba3a5eec7b00d0a9ce1e59` | | corrections request | `35c237f5c001832a0cfed23b03ff7530ffa78443612ffe11130fe456b968bd61` | | ordering follow-up | `e93a65b94fd4df8bd6e74c2f38afaf02e0f86b36632ff5a820fd8ac464c22101` | | demo test-gate decision | `62cea74bea59e69f29c8a29a9c0311fdc6feffa2c337bc6a141db55149204898` | | r5 report | `17c67427dfdf1bc5dac491c4d0223928e87fdfc67a26175ac239eb2b2f7b7dec` | | r5 manifest (369 files) | `a63bb103e4cfe51bbd8a42cd95545f935ac83c1da51cf469298375cf94ced135` | | pinned schema / checker / fixtures | `05774aaf…2af6` / `82564a7d…4607` / `d433d06d…da85` | Git: HEAD `9a5fbdbda74b16adf488fe28138b2ba69ea5e669` (main), unchanged throughout; nothing staged. Tracked modifications present in the worktree: the pre-existing `BUILD-LOG.md`, `docs/SESSIONS.md`, `docs/plans/CURRENT.md`, plus `docs/TOOLS.md` and `scripts/agent.sh`, which were modified by another session at 15:04 UTC (outside the allowlist; not touched, not inspected beyond `git status`, reported here only). All candidate files remain untracked. Toolchain: node v26.8.1, Python 3.12.8, jsonschema 4.26.0, Linux 7.2.2-1-cachyos. ## 1. Evidence preservation - r5 report and manifest: unedited, hashes above re-verified after the r6 build. The r5 §3(a) claim that "two entries with equal typed key and equal canonical content are indistinguishable" was **wrong** for non-ASCII entries (R5-1); it is corrected here (§2), not by editing r5. - r4 report/manifest, r3 report (`ec0444c9…5508`) and manifest (`3c2253b6…8547`): unedited. **The r4 §3(g) correction stands and is repeated so it stays visible:** the r3 `test-task.sh` 90/0 receipt did execute the suite's Docker and live branches, contradicting r3 §6 "no engine launch". r3 is not relabeled; those actions were not repeated in r4, r5 or r6 (§7). - Charter, addendum, both verdicts, corrections request, follow-up, decision doc, pinned schema/checker/fixtures, `canonical.mjs` (`a7760882…0172`, the mock-digest canonicalizer): unedited. - Every pre-existing fixture bundle, raw case, demo copy and golden is byte-identical to r5 (manifest diff r5 → r6: 5 files changed, 13 bundles added, 0 removed; §4). Every prior `index.json` expectation is unchanged; only 13 cases were appended. ## 2. Correction of R5-1 ### 2.1 The defect r5 `stableOrder` broke equal-key and unkeyed ties on `canonicalize()`, the ASCII-only mock content-digest canonicalizer, and mapped its `CanonicalError` to the empty string. Every entry containing a non-ASCII string therefore had the same tie-break form, and two *distinct* such entries with equal typed keys (duplicates) or no readable key fell through to the input index, restoring input-order-dependent first failures — exactly filbert's six reversed-pair witnesses. The defect was a wrong choice of domain: the mock digest domain is deliberately narrower than the strict-JSON input domain (Unicode record values and logical paths are permitted input), so a digest canonicalizer can never be a total order over inputs. ### 2.2 Implementation (`scripts/foundation/resolve.mjs`, r6 lines 719–771) - `orderingForm(value)` (exported, new): a total ordering form over the strict-JSON parser's output domain (`null`, booleans, safe integers, any well-formed Unicode string, arrays, objects): literals as JSON literals, strings and keys as `JSON.stringify` literals, arrays in order, objects with keys sorted by UTF-16 code unit, compact separators, no normalization or case folding, no length or depth limit beyond the parser's own bounds. Every parser-producible value has exactly one form and distinct values have distinct forms (injective), so the index fallback in `stableOrder` now decides only between byte-identical entries. It throws a plain `Error` only for non-JSON values, which `parseStrict` never yields; there is no `CanonicalError` path. It is **not** a digest, is never emitted or compared with a declared digest, and is not a validity judgement: `canonical.mjs` and the `inspector-content-digest/1` domain are untouched and `mock-content-unsupported` semantics are unchanged. - `stableOrder(items, keyOf)`: unchanged except that the memoized tie-break calls `orderingForm(items[i])` instead of the `canonicalize`/`CanonicalError` fallback. Typed keys, keyed-before-unkeyed, component comparison and the input-index locator are exactly r5. `ORDER_KEYS`, `checkInventory`, `shapeValidationOrder`, the digest/identity/structural traversals and `Model.order` are unchanged. - Nothing else changed: no blanket Unicode rejection (the Unicode positive controls stay `allowed`), semantic sequence arrays are still never reordered, family and stage precedence are unchanged, `detail` remains internal and no value is echoed, the closed rule/reason/exit vocabulary is unchanged, no new dependency. ### 2.3 Regressions (fixtures in `build-fixtures.mjs` 1231–1266; tests in `resolve.test.mjs` 723–801) The fixtures rebuild filbert's witnesses from the base bundle. Each pair carries two different shape failures on two distinct entries with a legitimate non-ASCII value, forward and reversed; the expected first rule is the same in both orders and in every permutation (`assertPermutationStable`, all permutations for ≤ 4 entries, the fixed deterministic family beyond that). | Case | Content | Expected first rule (both orders) | |---|---|---| | `shape-order-unkeyed-unicode-{records,registries,artifacts,delegation-inputs}-{forward,reversed}` (shape) | `[{"extra":"é"}, "é"]` as the whole inventory: two unreadable-key entries | `shape-type-mismatch` at the string entry (form `"…` sorts before `{…`); r5 flipped to `shape-unknown-field`/`shape-missing-field` in one order | | `shape-order-equal-key-unicode-records-{forward,reversed}` (shape) | two `agent-definition/agent-a/system/1` copies, both `displayName: "Café"`, one `status: "invalid-status"`, the other an unknown `payload.extra` | `shape-unknown-field` at `.payload.extra` (the copy with the extra key sorts first: `"extra"` < `"harnessRef"`); removing the extra key yields `shape-enum-mismatch` (the pair really carries two errors) | | `shape-order-equal-key-unicode-registries-{forward,reversed}` (shape) | two `agent-policy/ap-full/1/` copies, `readPaths[0].path: "café"`, one `network: "everything"`, the other an unknown `restrictions.extra` | `shape-unknown-field` at `.content.restrictions.extra`; removing the extra key yields `shape-enum-mismatch` | | `positive-unicode-display-name-allowed` (positive) | `displayName: "Café 😀"` on agent-a | `allowed` | Unit tests: "R5-1: the ordering form is total and injective…" (37 hand-built values covering every parser type, NFC/NFD and case-distinct strings, NBSP/U+2028, escapes, nested arrays/objects, Unicode keys and values: 37 distinct forms; 12 of them (test bound ≥ 12) are refused by `canonicalize()` with `CanonicalError`, proving the domains differ; key order independent of insertion order and by code unit; array order semantic; string form equals `JSON.stringify`; `stableOrder` on the unkeyed and equal-key Unicode pairs in both input orders for all four families; only a `structuredClone` duplicate falls back to the index), "R5-1: unkeyed Unicode entries…" (per family: permutation-stable `shape-type-mismatch` with a null locator key, the two fixtures are exact reversals, and the located entry is the string in both), "R5-1: equal-key Unicode entries…" (permutation-stable `shape-unknown-field` with the asserted agent-a key and `.payload.extra` / `.content.restrictions.extra` tails, the sibling enum error is reachable, the Unicode positive stays allowed). The existing `stableOrder` unit test keeps its r5 expectations (`"not-an-entry"` still precedes `{registry: 7}`). Filbert's own witness files (`/tmp/fi-filbert-7.239u2bco-evidence/witnesses/`, read only) through the r6 inspector: all six Unicode pairs now report the same rule in both orders (`shape-type-mismatch` for the four unkeyed pairs, `shape-unknown-field` for the two equal-key pairs). Mutation witness (scratchpad copy only, discarded): with the r5 tie-break (`canonicalize` + `CanonicalError → ""`) restored in the r6 tree, the three R5-1 unit tests and the "evaluate over every bundle fixture" index check fail (the 13 new `index.json` expectations flip), while all 34 other resolve tests pass; the r6 regressions therefore detect the r5 behaviour. ## 3. Disclosures for the reviewer - (a) **Index fallback.** `a.i - b.i` remains as the final comparator and now decides only between byte-identical entries (equal forms ⇒ equal values). The r5 §3(a) wording was wrong for non-ASCII entries; the corrected claim is asserted by the injectivity test and the equal-key fixtures. - (b) **Ordering form vs. canonical digest.** Two different serializations now exist in `resolve.mjs`/`canonical.mjs` on purpose: the digest form is the frozen mock domain (ASCII, `CanonicalError` for everything else, unchanged); the ordering form is total. They agree on the shared ASCII domain up to representation but are never compared with each other. The ordering form is never written to output. - (c) **Cost.** The form is computed at most once per entry per `stableOrder` call (memoized) and only for ties; inputs are already bounded by the strict parser (MAX_ITEMS/MAX_DEPTH/MAX_STRING_BYTES), so no new resource bound is introduced. - (d) `registry-declaration-missing` semantics: unchanged; the FI-FILBERT-7 verdict confirms them as consistent. - (e) Permutation family for large inventories remains the fixed deterministic subset (r5 §3(d)); the new unkeyed pairs have two entries (all permutations); the equal-key records pair sits in the 36-record inventory (35 base records plus the copy) (deterministic family). - (f) r3 live-branch correction (r4 §3(g)) remains in force and unrelabeled (§1, §7). ## 4. Changed files (r5 → r6 sha256; everything else byte-identical, 13 fixtures added, 0 removed) | File | r6 sha256 | Change | |---|---|---| | `scripts/foundation/resolve.mjs` | `c4951014828080caf9a5038845104afdac02465af10e94a2fddd3558b29621a3` | `orderingForm`; `stableOrder` tie-break (§2.2) | | `scripts/foundation/resolve.test.mjs` | `dca397f268cfd39feffae3839f57afc61fe74ca324645dd1d916219f38d4b15b` | three R5-1 tests; `orderingForm`/`canonicalize` imports; two comment/title words | | `scripts/foundation/fixtures/build-fixtures.mjs` | `ea5a54028b75cc6f3fbce1666f6fad04b3df774a137b0fd281da0a1abb0d0cab` | 13 FI-ROCKO-8 cases (§2.3) | | `scripts/foundation/fixtures/index.json` | `e5b2cf8d6ad0bab6aa232edf80492ca58dae260940f52cf87e34525b166b459a` | 347 cases (was 334) | | `scripts/foundation/fixtures/README.md` | `c69c64a15b0a20105c46d6b8e04e32a80d8c9b025f9f47aa9e18679a69da8ed2` | tie-break wording; FI-ROCKO-8 paragraph | | `scripts/foundation/fixtures/bundles/*` | 13 added (manifest) | none modified | Group counts (`index.json`): admission 62, continuity 2, cycle 6, identity 7, lexical 19, operation 7, ownership 10, positive 22 (+1), profile 47, proposal 48, registration-delegation 13, shape 97 (+12), structure 7. ## 5. Receipts (run 2026-09-07 15:2x UTC at HEAD 9a5fbdbd; logs kept in the session scratchpad) | Check | Result | |---|---| | `NO_COLOR=1 PYTHONDONTWRITEBYTECODE=1 scripts/test-foundation.sh` | 43 passed, 0 failed | | `node --test scripts/foundation/` | 80 tests, 80 pass, 0 fail (was 77) | | `python3 scripts/foundation/verify-schema.py` (jsonschema 4.26.0, pinned hashes) | PASS; corpus 1568 records; schema column agree-valid 540, agree-invalid 991, DISAGREEMENTS 0; profile refusals asserted 30 | | `python3 docs/plans/foundation-v1-candidate/check.py` | PASS | | `scripts/test-config.sh` / `test-conductor.sh` / `test-auth.sh` | 24/0, 17/0, 15/0 | | `scripts/test-task.sh`, `scripts/test-release.sh` | **NOT RUN / DEFERRED** (owner decision, §7) | | every new fixture through the CLI (`env -i`, `PATH=/nonexistent`, `HOME=/nonexistent`) | 13/13 match `index.json`; every pre-existing case unchanged; fresh generation equals the checked-in tree | | filbert's six Unicode witness pairs through the r6 CLI | same rule in both orders for all six (§2.3) | | git | HEAD unchanged, nothing staged, no candidate file tracked; no `__pycache__` created | ## 6. Constraints honoured No edit to charter, addendum, verdicts, corrections request, follow-up, decision doc, pinned schema/checker/fixtures, `canonical.mjs`, prior reports or manifests, CURRENT, shared logs, root/package files, policies or runtime data. No blanket Unicode rejection. No new dependency. No staging, commit, checkout/reset or push (Dewey's index untouched). No live, native/sync, credential or `~/.mosaic` work; no network; no engine or container launch. Tests spawn the inspector; the inspector spawns nothing. Finite tests and static flags are evidence, not sandbox proof. No fake full-suite pass, no new waiver. ## 7. Deferred coverage (owner decision, unchanged) Per `2026-09-07_foundation-inspector-demo-test-gate-decision.md` (Jason, 2026-09-07 14:47 UTC): `scripts/test-task.sh` and `scripts/test-release.sh` are deferred for this offline inspector demo and are reported **NOT RUN / DEFERRED**, never passed or fully covered; no live model calls, deployment actions, weaker inspector behaviour or permanent removal of tests are authorized. Still required and not rocko's to grant: filbert's independent code approval and Jason's separate synthetic demo acceptance. Full deployment/integration testing remains a later gate. The r3 execution of the live branches (r4 §3(g)) is neither authorized nor relabeled. ## 8. Freeze Frozen at the r6 manifest (`docs/plans/reviews/2026-09-07_foundation-inspector-rocko-build-manifest-r6.json`, sha256 `a4a4493000aff5905337a643886ca36e7c5377d52deed77b8aeab7174ca73dcf`; 382 files: source 6, tests 5, suite 1, fixtures-meta 3, bundles 328, raw 19, demo 20; aggregate `scripts/foundation/` 380 files `e9d406b3…3dff`) for filbert's independent re-review. Rocko changes nothing under the allowlist until darkwing returns reconciled findings. C1 (Archify rev 3) remains HELD; no lane action taken.