Files
stack/docs/plans/reviews/2026-09-07_foundation-inspector-rocko-build-r6.md
T
jason.woltje 8ebddd6f93 feat(foundation): offline synthetic scope/permission inspector (FI-FILBERT-8 APPROVED r6)
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.
2026-09-07 14:06:35 -05:00

226 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 r2r5
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 719771)
- `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` 12311266; tests in `resolve.test.mjs` 723801)
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.