Rocko-authored, Filbert-reviewed inspector (r6 manifest a4a44930...) with full review/build/verdict evidence under docs/plans/reviews. 43/0 selftests, oracle zero-disagreement, foundation checker PASS. Owner A9 acceptance recorded separately.
226 lines
16 KiB
Markdown
226 lines
16 KiB
Markdown
# 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/<same digest>` 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.
|