# Foundation inspector — rocko build report r5 (FI-ROCKO-7: §10.4 stable traversal of the declared inventories) Author: rocko (Claude Fable 5.1). Coordinator: darkwing. Reviewer: filbert. Task: FI-ROCKO-7. Written 2026-09-07 15:03 UTC. Supersedes nothing: the original report, the r2/r3/r4 reports and manifests, the charter, the addendum, both verdicts, the corrections request, the ordering follow-up and the demo test-gate decision are preserved unedited. This revision closes the F5 remainder disclosed in r4 §3(b) (declared inventories were still shape-validated in input order; the registry digest traversal at r4 `resolve.mjs:831` used input order), records what changed r4 → r5, the per-family permutation evidence, the owner's demo test-gate decision, and the frozen candidate for independent re-review. ## 0. Admission, compatibility and blockers FI-ROCKO-7 is **admitted and compatible** with the standing chain, the FI-ROCKO-3 build request, the corrections request, the ordering follow-up and the demo test-gate decision; the code/test work is complete under the original allowlist plus the two r5 receipt paths named in the follow-up. C1 (Archify rev 3) remains HELD with no lane action. **No open blocker.** The r4 §7 integration-gate blocker (`scripts/test-task.sh` and `scripts/test-release.sh` not runnable here without executing Docker/live branches) is now governed by the owner decision recorded in `docs/plans/reviews/2026-09-07_foundation-inspector-demo-test-gate-decision.md` (sha256 `62cea74b…4898`, 2026-09-07 14:47 UTC): both suites are **NOT RUN / DEFERRED** for this offline demo (§7). They are not reported green and no live test authority is claimed. The decision changes only the offline-demo test prerequisite; full deployment/integration testing remains a later gate. Inputs bound to this revision (all hashes machine-read; full list in the r5 manifest `pinnedInputs`): | Input | sha256 | |---|---| | charter `docs/plans/2026-09-06_foundation-inspector-charter.md` | `19b6721128a627a2032ffdb95ece2d50abe69a8f6d521e9eff8bbdaff22798b6` | | addendum FI-C2-1 | `afe2980be2f91e701dae5af3018831ac5c300474f52bcc06e740ce5b5cc68ca5` | | code verdict FI-FILBERT-6 | `e4cc5970aab20240e6ea3a9a1513ef942ff167fefbb03356b9a32c89999adb3d` | | corrections request | `35c237f5c001832a0cfed23b03ff7530ffa78443612ffe11130fe456b968bd61` | | r4 report | `dff9f676ad0a97f6333a59d527e02ed55e7bcdca986fe9c1488280b84d4e677f` | | r4 manifest (331 files) | `2a2e885f9df3dd626a09fe5db6e28af67c289e54660872031c18f8aa40cb0826` | | ordering follow-up `…_foundation-inspector-ordering-followup.md` | `e93a65b94fd4df8bd6e74c2f38afaf02e0f86b36632ff5a820fd8ac464c22101` | | demo test-gate decision `…_foundation-inspector-demo-test-gate-decision.md` | `62cea74bea59e69f29c8a29a9c0311fdc6feffa2c337bc6a141db55149204898` | | pinned schema / checker / fixtures | `05774aaf…2af6` / `82564a7d…4607` / `d433d06d…da85` | Git: HEAD `9a5fbdbda74b16adf488fe28138b2ba69ea5e669` (main), unchanged throughout; nothing staged; the only tracked modifications are the pre-existing ones (`BUILD-LOG.md`, `docs/SESSIONS.md`, `docs/plans/CURRENT.md`, not touched by rocko). 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 - r4 report and manifest: unedited, hashes above re-verified after the r5 build. - r3 report (`ec0444c9…5508`) and manifest (`3c2253b6…8547`): unedited. **The r4 §3(g) correction stands and is repeated here so it stays visible in history:** the r3 `test-task.sh` 90/0 receipt did execute the suite's Docker and live branches (Docker was reachable), contradicting r3 §6 "no engine launch". r3 is not relabeled; those actions were not repeated in r4 or r5 (§7). - Charter, addendum, verdicts, corrections request, follow-up, decision doc, pinned schema/checker/fixtures: unedited (manifest `pinnedInputs`). - Every pre-existing fixture bundle, raw case, demo copy and golden is byte-identical to r4 (manifest diff: 5 files changed, 38 bundles added, 0 removed; §4). Every prior `index.json` expectation is unchanged; only 38 cases were appended. ## 2. Closure of the ordering follow-up ### 2.1 What §10.4 requires and what r4 still did Charter §10.4: "Within a stage use stable kind/id/scope/revision and field ordering, not input order." r4 applied this to `bundle.records` only. Registries, artifacts, authorizations and delegationInputs were traversed in input order at the shape stage (`checkArray` per family), at the profile stage (the profile sink is filled in validation order, so it inherited input order for these families), at the content-digest check (r4 `resolve.mjs:831`, `b.registries.forEach`), at the identity stage (duplicate checks per family) and at the structural stage (delegationInputs `decisionRef` existence). The first failure within any of those traversals therefore depended on input position. ### 2.2 Implementation (`scripts/foundation/resolve.mjs`) - `stableOrder(items, keyOf)` (exported, r5 lines 730–767): the generic stable traversal. `keyOf` returns a typed key (array of `string | integer | null` components) or `null` when the item has no readable identity. Order: keyed items before unkeyed; keys component-wise (a `null` component after every readable value; strings by code unit; integers numerically); equal keys tie-break on canonical content (`canonicalize`, `CanonicalError` → empty string); then input index. Returns input indices, so every locator path keeps the input index (privacy and locator semantics unchanged; `detail` remains internal). - `ORDER_KEYS` (exported, 801–807), the typed identity key per family: - records: `[kind, id, scopeKey, revision]` (tolerant, exactly the r4 rule: unreadable kind/id/scope kind → unkeyed; non-integer revision → `null` component); `shapeValidationOrder` now delegates to `stableOrder`, behaviour-preserving (all r4 F5 tests and fixtures unchanged); - registries: `[registry, id, revision, digest]`; unkeyed unless `registry` and `id` are strings; - artifacts: `[runId, artifactId, digest]`; unkeyed unless `runId`/`artifactId` are strings; - authorizations: `[runtimeId]`; unkeyed unless a string; - delegationInputs: the `decisionRef` kind/id/scope/revision key; unkeyed unless the ref is readable. - `checkInventory(v, path, itemCheck, keyOf)` (813–818): bounds first (`shape-type-mismatch`, `shape-bound-exceeded` at the array path, as before), then each item in stable order with its input-index path. Used for the four declared inventories in `validateBundleShapeScoped` (821–823 and 857–864). Family precedence is unchanged: records → registries → artifacts → authorizations → selection → operation → proposal → delegationInputs. - Profile stage: unchanged code; `profileSink[0]` is now the first violation in stable order for every family because the sink is filled in traversal order. - Digest stage (871–884): content-digest traversal iterates `stableOrder(b.registries, ORDER_KEYS.registries)`; rules, reasons and paths unchanged. - `Model` constructor (1084–1089): computes `this.order` (stable index lists for the four families) once; `indexIdentities` (stage 3) walks each family's duplicate check in that order; `checkStructuralReferences` (stage 4, 1181) walks delegationInputs in that order. Lookup-based consumers (`requireRegistryDeclaration`, `registryContent`, `requireArtifacts`, the authorization `Set`, `delegationInputs.find`) are order-independent once duplicates are rejected and are untouched. - **Semantic sequence arrays are not reordered.** `checkArray` is unchanged and still used, in input order, for `operations`, `readPaths`, `writePaths`, `endpointRefs`, instruction/skill/context-content lists, `dependencies`, `subjectRefs`, `evidenceRefs` and every other ordered array; canonical content is never reordered; `evaluate` does not mutate or reorder the input bundle (asserted). - Stage/field precedence preserved: every shape failure before any profile violation, every profile violation before any digest failure, identity before structural; within an entry the field order of the existing checkers is unchanged. - No new rule, reason or exit; the closed vocabulary is unchanged. No input file is mutated; no exception is invented for declaration lists. ### 2.3 Per-family evidence (fixtures in `build-fixtures.mjs` 1076–1230; tests in `resolve.test.mjs` 548–712) Each fixture pair carries two *different* failures on two entries; the expected first rule is determined by the typed key, and the "swapped" pairs move the errors between the same two entries so the expected rule changes with the key, not the position. `assertPermutationStable` re-evaluates every permutation of the family (all permutations for inventories of ≤ 4 entries; for larger inventories a fixed deterministic family: identity, reverse, descending sort, odd/even interleave, every rotation and twelve seeded Fisher–Yates shuffles) and asserts the identical located first failure (rule, reason, exit, proposal step, and the locator resolved to the entry's typed key plus field tail). Expected keys are asserted explicitly, so the tests demonstrate intended first-rule stability, not merely a consistent nonzero exit. | Family / stage | Fixtures (group) | Expected first rule and locator | |---|---|---| | registries, shape | `shape-order-registries-{forward,reversed}` (shape) | `shape-missing-field` at agent-policy/ap-full (missing `digest`) before `shape-unknown-field` at project-policy/pp-p1 | | registries, shape, swapped | `shape-order-registries-swapped-{forward,reversed}` | `shape-unknown-field` at ap-full (errors exchanged) | | registries, shape, malformed | `shape-order-registries-malformed-sorts-last`, `-only-malformed` | `{registry: 7}` (unkeyed) sorts last → `shape-enum-mismatch` at the keyed bad entry `.registry`; a lone `"not-an-entry"` → `shape-type-mismatch` | | registries, profile | `profile-order-registries-{forward,reversed}` (profile) | `profile-pattern-mismatch` at agent-policy/ap-reader `.id` before scope-role/role-reader | | registries, shape > profile | `profile-order-shape-precedes-profile-registries` (shape) | `shape-unknown-field` at project-policy/pp-p2 although ap-reader carries the (earlier-key) profile violation | | registries, digest | `shape-order-registry-digest-{forward,reversed}` (profile) | `mock-content-unsupported` (unsupported-capability) at ap-full `.content` before `registry-digest-mismatch` at role-writer | | registries, digest, swapped | `shape-order-registry-digest-swapped-{forward,reversed}` | `registry-digest-mismatch` at ap-full `.digest` | | registries, profile > digest | `shape-order-profile-precedes-digest-registries` | `profile-pattern-mismatch` at pp-p2 `.id` although ap-full carries the digest mismatch | | registries, two digest mismatches | `shape-order-two-digest-mismatches-reversed` | `registry-digest-mismatch` at scope-role/role-reader (lower key) in every order | | artifacts, shape | `shape-order-artifacts-{forward,reversed}`, `-swapped-{forward,reversed}`, `-malformed-sorts-last` | `shape-pattern-mismatch` at run-1/art-1 `.digest` before `shape-unknown-field` at art-2; swapped → `shape-unknown-field` at art-1; `{runId: 1}` sorts last | | artifacts, profile | `profile-order-artifacts-reversed` | `profile-pattern-mismatch` at run-1/art-2 `.artifactId` (art-1's violation is on `.runId`, a higher key after the LF) | | authorizations, shape | `shape-order-authorizations-{forward,reversed}`, `-malformed-sorts-last` | `shape-pattern-mismatch` at the lower runtime id `…-1000-…0002` in every order; a non-string entry sorts last | | authorizations, profile and shape > profile | `profile-order-authorizations-reversed`, `profile-order-shape-precedes-profile-authorizations` | `profile-pattern-mismatch` at `…0001\n`; a pattern failure on the higher id precedes a profile violation on the lower one | | delegationInputs, shape | `shape-order-delegation-inputs-{forward,reversed}`, `-swapped-{forward,reversed}`, `-malformed-sorts-last` | `shape-enum-mismatch` at decision/d-deleg-b `.mode` before `shape-missing-field` at d-deleg-reg; swapped → `shape-missing-field` at d-deleg-b; `{decisionRef: null}` sorts last | | delegationInputs, structural | `struct-order-delegation-inputs-{forward,reversed}` (structure) | `record-reference-missing` (missing-state) at d-deleg-b revision 9 `.decisionRef` in every order | | delegationInputs, identity | `dup-order-delegation-inputs-reversed` (identity) | `duplicate-delegation-input` located at the d-deleg-reg duplicate although a valid lower-key input is listed after it | | family precedence | `shape-order-family-precedence-{registries-before-artifacts,artifacts-before-authorizations,authorizations-before-delegation-inputs}` | the earlier family's failure wins regardless of positions | | identity precedence | `dup-order-registries-precede-artifacts` (identity) | `duplicate-registry-identity` before `duplicate-artifact-identity` | | identity, differing digests (unit only) | `resolve.test.mjs` "family precedence and identity-stage order" | duplicate plain-registry and artifact identities with differing digests: the higher digest is the reported entry in every order | Unit test `stableOrder: typed keys …` pins the helper itself on hand-built inventories (integer-before-null revision, digest tie-break, duplicate index tie-break, unkeyed items ordered by canonical content, records/artifacts/authorizations/delegationInputs keys) and asserts `evaluate` leaves the bundle byte-identical. Mutation witness (scratchpad only, discarded): with `stableOrder` short-circuited to input order for the four families, the seven new r5 tests fail (`stableOrder`, the five per-family tests and the precedence test) while all r4 tests still pass; the r5 tests therefore detect the r4 behaviour. ## 3. Disclosures for the reviewer - (a) **Tie-break for byte-identical duplicates.** Two entries with equal typed key and equal canonical content are indistinguishable; the duplicate reported at the identity stage is then the higher input index. This is position-dependent only in the degenerate case where the two positions hold identical bytes, so no observable result changes. Entries with equal key but different content (e.g. duplicate registry identity with a different digest) are ordered by content and are permutation-stable (asserted). - (b) **`registry-digest-mismatch` / `mock-content-unsupported` share one traversal.** Both are raised in a single stable pass over registries after the profile stage; an unsupported content on a lower key precedes a mismatch on a higher key and vice versa. This is the intended first-rule order (key order, not rule severity). - (c) **`registry-declaration-missing` semantics.** Unchanged from r4 (accepted by darkwing as diagnostic naming only); the reviewer's semantic confirmation is still open and nothing in r5 alters that path. - (d) **Permutation family for large inventories** is a fixed deterministic subset (§2.3), not all n! orders; the base bundle carries ten registries. All-permutation coverage applies to the artifact, authorization and delegation-input inventories (2–3 entries). The helper's own ordering is asserted directly on hand-built lists. - (e) **Locator (`detail`) remains internal.** The tests read it through `evaluate` only; `inspect`/CLI output still exposes no path or value (fixtures.test and the suite's privacy checks unchanged and green). - (f) **r3 live-branch correction** (r4 §3(g)) remains in force and unrelabeled (§1, §7). ## 4. Changed files (r4 → r5 sha256; everything else byte-identical, 38 fixtures added, 0 removed) | File | r5 sha256 | Change | |---|---|---| | `scripts/foundation/resolve.mjs` | `892f95ab8a971d93060353e43d53cb6988e7e61f2a5bae71f6f2aaf2b7c1f09f` | `stableOrder`, `ORDER_KEYS`, `checkInventory`; stable traversal at shape/profile/digest/identity/structural stages (§2.2) | | `scripts/foundation/resolve.test.mjs` | `0fdcbf80edfc35e877132566b9af5b1256d5efd3e69d4fc809eb0ca259a5bba3` | seven FI-ROCKO-7 tests, `firstLocated`, `permutations`, `assertPermutationStable` | | `scripts/foundation/fixtures/build-fixtures.mjs` | `273f3f12d1911aaa067a1046bbea20c5eb27b5b80c9913a833c0863b8e06ab7c` | 38 ordering cases (§2.3) | | `scripts/foundation/fixtures/index.json` | `31c6fab4d27f3e9c3e26cdcf343171f9c744404d359d042c80dc5a8566a57ea0` | 334 cases (was 296) | | `scripts/foundation/fixtures/README.md` | `f3d548908f8bde629525ac0be90df40bbfaf01faff79980474ef4164f306ec7e` | FI-ROCKO-7 paragraph | | `scripts/foundation/fixtures/bundles/*` | 38 added (manifest) | none modified | Group counts (`index.json`): admission 62, continuity 2, cycle 6, identity 7 (+2), lexical 19, operation 7, ownership 10, positive 21, profile 47 (+10), proposal 48, registration-delegation 13, shape 85 (+24), structure 7 (+2). ## 5. Receipts (run 2026-09-07 15:0x 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 (syntax, fixture regeneration equality for bundles/raw/index/demo, node tests, oracle, demo exits, goldens, sandbox inventory incl. uid/gid, canaries) | | `node --test scripts/foundation/` | 77 tests, 77 pass, 0 fail (was 70) | | `python3 scripts/foundation/verify-schema.py` (jsonschema 4.26.0, pinned hashes) | PASS; corpus 1563 records; schema column agree-valid 539, agree-invalid 987, DISAGREEMENTS 0; profile refusals asserted 30 | | `python3 docs/plans/foundation-v1-candidate/check.py` | PASS (155 shape fixtures, 5 obligations, 35 model cases) | | `scripts/test-config.sh` | 24 passed, 0 failed | | `scripts/test-conductor.sh` | 17 passed, 0 failed (disposable test copies only) | | `scripts/test-auth.sh` | 15 passed, 0 failed | | `scripts/test-task.sh` | **NOT RUN / DEFERRED** (owner decision, §7) | | `scripts/test-release.sh` | **NOT RUN / DEFERRED** (owner decision, §7) | | every new fixture through the CLI (`env -i`-style PATH/HOME) | 38/38 match `index.json`; every pre-existing case unchanged | | git | HEAD unchanged, nothing staged, no candidate file tracked; no `__pycache__` created (the one present under `.pi/evidence/goal56/` predates this work, 2026-09-06) | ## 6. Constraints honoured No edit to charter, addendum, verdicts, corrections request, follow-up, decision doc, pinned schema/checker/fixtures, prior reports or manifests, CURRENT, shared logs, root/package files, policies or runtime data. 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 launch; no 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, recorded verbatim in scope) Per `2026-09-07_foundation-inspector-demo-test-gate-decision.md` (Jason, 2026-09-07 14:47 UTC, after clarification): `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. The deferral changes only the test prerequisite for the bounded offline demo; it authorizes no live model calls, deployment actions, weaker inspector behaviour or permanent removal of tests. Still required and delivered here: inspector unit/CLI/privacy/non-effect/differential checks, the foundation checker and the authorized isolated non-live integration checks (§5). 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 before dependent live runtime work. The r3 execution of the live branches (r4 §3(g)) is neither authorized nor relabeled by this decision. ## 8. Freeze Frozen at the r5 manifest (`docs/plans/reviews/2026-09-07_foundation-inspector-rocko-build-manifest-r5.json`, sha256 `a63bb103e4cfe51bbd8a42cd95545f935ac83c1da51cf469298375cf94ced135`; 369 files: source 6, tests 5, suite 1, fixtures-meta 3, bundles 315, raw 19, demo 20; aggregate `scripts/foundation/` 367 files `126b05e4…82ae`) 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.