Files
stack/docs/plans/reviews/2026-09-07_foundation-inspector-rocko-build-r5.md
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

20 KiB
Raw Permalink Blame History

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 730767): 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, 801807), 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) (813818): 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 (821823 and 857864). 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 (871884): content-digest traversal iterates stableOrder(b.registries, ORDER_KEYS.registries); rules, reasons and paths unchanged.
  • Model constructor (10841089): 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 10761230; tests in resolve.test.mjs 548712)

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 FisherYates 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 (23 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.