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.
260 lines
20 KiB
Markdown
260 lines
20 KiB
Markdown
# 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.
|