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

14 KiB
Raw Permalink Blame History

Foundation inspector — rocko build report r3 (FI-ROCKO-5: C2 correction under addendum FI-C2-1)

Author: rocko (Claude Fable 5.1). Coordinator: darkwing. Task: FI-ROCKO-5. Written 2026-09-06 21:25 UTC. Supersedes nothing: the original report (…-rocko-build.md, sha256 857470d97af6aeae4e7d7942c2a1d8455bfcda74ff68b8e1dc4ba8d066521fa4) and the r2 report/manifest (…-rocko-build-r2.md 0dabd82fffdc3a56ebf9a1d8b851832c3acee2cb9332892bde9dbb13151d5e19, …-rocko-build-manifest-r2.json 2e8b5f1998a3be6cc7fcc3fba308724d2a29ca166c08f005e1bca88ddbc14196) are preserved unedited. This revision records the C2 correction only, what changed, and the new frozen candidate.

0. Compatibility statement

FI-ROCKO-5 is compatible with the standing chain and with the FI-ROCKO-3 build request; no blocker. The open item of r2 §5 (the C2 pattern-class ruling) is closed by darkwing's written addendum FI-C2-1 as approved by filbert (FI-FILBERT-5). Nothing outside the original implementation/test allowlist was touched; C1 (Archify rev 3) remains HELD with no lane action.

Inputs bound to this revision:

Input sha256
Charter candidate 3 docs/plans/2026-09-06_foundation-inspector-charter.md (unchanged) 19b6721128a627a2032ffdb95ece2d50abe69a8f6d521e9eff8bbdaff22798b6
Addendum FI-C2-1 2026-09-06_foundation-inspector-pattern-profile-addendum.md (exact bytes) afe2980be2f91e701dae5af3018831ac5c300474f52bcc06e740ce5b5cc68ca5
Filbert verdict 2026-09-06_foundation-inspector-pattern-profile-verdict.md 03c979b77cc6b03b6685ba51ed1ce24c3f1d7b274ce8f772263a103670050da7
records.schema.json / check.py / records.fixtures.json (pinned, unchanged) 05774aaf…72af6 / 82564a7d…4607 / d433d06d…da85 (full values re-verified by oracle gate 1 and listed in the manifest)
Build request 2026-09-06_foundation-inspector-rocko-build-request.md 90a1f329e859152d4ce8e157cbd6c56e6bcced16b4ed757822d2309514fa9a15
Original report / r2 report / r2 manifest (preserved) 857470d9…21fa4 / 0dabd82f…1e19 / 2e8b5f19…4196
Integration HEAD (unchanged, nothing staged) 9a5fbdbda74b16adf488fe28138b2ba69ea5e669
Machine-generated manifest 2026-09-06_foundation-inspector-rocko-build-manifest-r3.json 3c2253b6c9f31e448c77aaac53977d41baecfb30e15845dcfffd8dc694428547

The charter, addendum and verdict hashes above are also asserted by a unit test (resolve.test.mjs, "the unchanged charter, the exact addendum and its verdict are bound"); a byte change to any of the three fails the suite.

Toolchain measured: Node v26.8.1, CPython 3.12.8, jsonschema 4.26.0, Linux 7.2.2-1-cachyos x86_64, glibc 2.44 (strftime("%Y") for year 999 → 999).

1. Design of the correction (addendum FI-C2-1, point by point)

Schema column (genuine pinned compatibility). The pinned checker validates pattern with Python re.search, whose $ matches at end of string or before exactly one final \n. resolve.mjs now exports PINNED_PATTERNS (id, runtimeId, digest), pinnedPatternMatches(re, v) = re.test(v) || (v.endsWith("\n") && re.test(v.slice(0, -1))), and strictPatternMatches(re, v) = re.test(v). The shape gate (checkPattern) uses the pinned semantics for shape-pattern-mismatch, so the four r2 witnesses become schema agreements and the schema column has zero disagreements with no named exceptions (r2's KNOWN_DIVERGENCES set is gone).

Strict production profile. After the pinned check, checkPattern applies the strict check on every occurrence of the three typed families (records, scope ids, actor ids, supersedes, dependency refs, registry refs, payload ids, unselected records, registry entries, artifacts, authorization, selection, proposal and delegation inputs). A violation is recorded into a per-call profileSink (path only; never the value). validateRecordShape returns { verdict: "valid", profileViolations: [...] } so the schema and profile verdicts are independent. validateBundleShape collects violations from every record and wrapper field and, after shape validation succeeds and before any identity index or registry-digest profile, refuses with exit 2, invalid-request, rule profile-pattern-mismatch, path of the first violation in validation order. Closed §7 fields are unchanged; selection and operation are withheld (null); the offending value is never echoed.

No trimming, normalization or waiver. The original string is preserved; no identity index is built from a profile-invalid value; no case name is whitelisted anywhere. Precedence: profile refusal wins over a dangling reference on the same field, so a profile-invalid ref is distinguishable from a missing one (unit test with ref-missing-dependency vs profile-record-dependency-ref-id-dangling-one-final-lf).

Free-form text, time and path keep their semantics. Escaped newlines in mission.objective / task.purpose stay schema- and profile-valid (profile-escaped-newline-free-text-allowed, exit 0). time with a final LF fails shape-pattern-mismatch (strptime refuses it in the pinned checker too); relativePath with a final LF fails path-invalid. Neither is in the profile.

Bridge. validate-record.mjs now reports schemaValid and profileValid independently, with rule: "profile-pattern-mismatch" and the first violating path when the record is schema-valid but profile-invalid.

Oracle. verify-schema.py builds an in-memory strict variant of the pinned schema (only the three $defs patterns, trailing $\Z; check_schema passes; pinned bytes untouched) and computes the profile column with the real jsonschema validator, never by name. The four original witnesses are preserved by name as declared probes (schema True / profile False); a newline matrix (valid, one final LF, two final LF, CRLF, CR, interior LF, U+2028, U+2029) runs across 8 sites of the three families with declared schema/profile outcomes; every strict-only refusal must carry the fixed rule and a path; a run with no refusals or an unobserved declared refusal is a problem.

One incidental fix in the allowlist. The eight payload-level scope re-checks previously re-validated rec.scope under the bare path scope, which would have double-counted a profile violation; they now use requireScopeKind (kind-only check with the proper record.scope.kind path). No verdict of the 204 pre-existing fixtures changed (index.json gained 55 entries and nothing else; demo goldens byte-identical).

2. Filbert's FI-FILBERT-5 matrix, as implemented

Per family (id, runtimeId, digest): Python / Node schema / strict.

Value Python (pinned) Node schema column Strict profile Inspector verdict
valid T T T proceeds
one final LF T T F exit 2 invalid-request profile-pattern-mismatch
two final LF, CRLF, CR, interior LF, U+2028, U+2029 F F F exit 2 invalid-request shape-pattern-mismatch

Asserted three ways: unit test on the exported predicates, 55 CLI fixtures, and the oracle's declared probes against the real Python validators.

3. Changed files (r2 → r3 sha256; everything else byte-identical)

File Lines r2 sha256 r3 sha256
scripts/foundation/resolve.mjs 1646→1713 aa842898ebb0bb2a911406314c4c3a2c4079b46bfd674c4ffba0176a1e32766c eaa4a419618328915cd470ba192a88b8d313ee8fa48f600289ffe39df15280b2
scripts/foundation/validate-record.mjs →71 e4e70f60cce68e535ef98b0627647c440d620432cf8ddaed074df4e252ecde58 dcff28b53501b68fe7368adcbf02db0526ab4273d1c1c42e47df9a78a0bd9b4b
scripts/foundation/verify-schema.py 467→575 b61031b6c2cea126133afda70894e7f2429b08bf2cd50fc766ec6b99f348c966 e5fce462abd0e8c5be4e4543fd31146a35aba41a653d2aa105c28ca5a20deb67
scripts/foundation/resolve.test.mjs 333→425 6bb6d40fa6ec90fb52db658f77ca9fd27cc3eb8673f040f52fc126e67983a571 d7de059015edcf0bded7c4da901a7302394a42bf1e38b86bd403fbb01f5327ff
scripts/foundation/cli.test.mjs 281→317 ccec06c266e03d878c1ecd9e8c20ee3f1fb82f478daead25efa4661b7090d83f a0bda7779739021da7cbe03b46209e370f7443100f6ea56a512468003d60b74c
scripts/foundation/fixtures/build-fixtures.mjs →979 094ecc811b85465cc0748a86b76b75c1784a2e0296f242b6ff2dbd1660b426a9 0386276f0e0ba8fbb3f4126ffabadd6afec90fc3a92749fe314783d26300aa5c
scripts/foundation/fixtures/index.json 204→259 cases 74988a1dcd4b48b0be2243d3fefff3b6de5d90a947094be201c3486142ca12a2 c013090406bb6bbb7b490b952eb4020065b4c732e4aecb0e6cf5c5c8037a6663
scripts/foundation/fixtures/README.md →51 aba48ba054d09247b048a8955507b87a275cd585870f1f658b7c6b826fc892bd ed0376589b203b1bcea6e1d82322311ec843eb7537756762bf917b8552d29523
scripts/test-foundation.sh 132→143 fdfa13ce247bc9ad8f3617e248913aef384029198924685ada3431006ffdf114 73417979d1f9320603b0244350f9665d2bdf1485084c0928002ba033929b3c19

Added: 55 bundle fixtures scripts/foundation/fixtures/bundles/profile-*.json (generator output; listed individually in the manifest). Removed: none. Unchanged: scripts/foundation-inspect.mjs, strict-json.mjs, canonical.mjs, fixtures.test.mjs, canonical.test.mjs, strict-json.test.mjs, all 19 raw fixtures, all 20 demo files and goldens (demo aggregate 6a32862b…8956 and raw aggregate dbc620a7…60d3 identical to r2).

Manifest r3 aggregates (sha256 of the LC_ALL=C sorted sha256sum listing): fixtures/bundles/ 240 files aa4836a4a4f989ecc70acf8df770b894c6ef77e85c4e913c24bb422b1aef0afa; fixtures/ 282 3de4c896d63041db0b6b5beb961d9dfa6486f7681dd16f91abb5d8ef48c16bfb; scripts/foundation/ 292 4fbdcfb8d71612d4ac21dc0b7f5b60064847d7087bfbe8ec215a9f1abdbafb9a; scripts/ 294 e5f9039cf4614e8acc3f6c6a71f0a03dbc1c29002703029180d1ccef5757979c.

4. New fixture inventory (55 cases)

Group Rule Count Cases
profile profile-pattern-mismatch 34 one final LF at 3 family sites (id-record-id, runtime-id-authorization-ref, digest-registry-ref) plus 31 further occurrence sites: record scope project/workspace ids, actor principal/execution ids, supersedes, dangling dependency ref, registry ref id, agent type, file root id, registration/assignment agent ids, criterion id, decision subject ref, unselected record id and its scope, registry id and plain/content-bearing digests, artifact run/id/digest, authorization, selection agent/project/workspace/assignment-ref ids and assignment-ref scope, proposal requester/target/context ids, delegation input decision ref
shape shape-pattern-mismatch 19 two-final-lf, crlf, cr, interior-lf, u2028, u2029 at each of the 3 family sites (18) + profile-time-one-final-lf
shape path-invalid 1 profile-path-one-final-lf
positive — (exit 0) 1 profile-escaped-newline-free-text-allowed

Every profile case is asserted to return selection: null, operation: null, and no \n inside any result string (loop in resolve.test.mjs).

5. Receipts (all run 2026-09-06 21:0x21:23 UTC at HEAD 9a5fbdbd)

Check Result
node --check on all 10 .mjs files; ast.parse on verify-schema.py OK
node --test scripts/foundation/ 63 tests, 63 pass, 0 fail (was 58; +5: binding, matrix, independence/order, precedence, CLI negative control)
python3 scripts/foundation/verify-schema.py PASS, exit 0. Corpus 1539 records (38 pinned fixtures, 449 unique bundle records, 1052 typeCase/mutation/lexical). Schema column: agree-valid 519, agree-invalid 983, DISAGREEMENTS 0; strict-only (parser-bound) 27; unsupported-kind not assessed 10. Profile column: profile-valid 489, profile-invalid 30. profile refusals asserted: 30 … 12 declared by name; 73 named probes verified
NO_COLOR=1 scripts/test-foundation.sh selftest: 43 passed, 0 failed (was 36/1); new checks: zero schema disagreements, refusals counted, profile exits 2/2/0, JSON refusal shape with value never echoed
CLI negative controls selection agentId + one LF → status 2, invalid-request, profile-pattern-mismatch, selection/operation null, agent-a absent from JSON and text; two LFs → shape-pattern-mismatch; escaped newlines in free text → exit 0
python3 docs/plans/foundation-v1-candidate/check.py exit 0, PASS: 35
scripts/test-config.sh / test-task.sh / test-release.sh / test-conductor.sh / test-auth.sh 24/0, 90/0, 14/0, 17/0, 15/0 passed/failed
Fixture regeneration build-fixtures.mjs into a sandbox equals checked-in bundles/, raw/, index.json, demo bundles
git rev-parse HEAD; git status 9a5fbdbd…; nothing staged; only the pre-existing tracked modifications (BUILD-LOG.md, docs/SESSIONS.md, docs/plans/CURRENT.md) plus untracked candidate/review files
scripts/foundation/__pycache__, docs/plans/foundation-v1-candidate/__pycache__ absent

6. Constraints honoured and freeze

No edit to charter, addendum, verdict, 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. Tests spawn the inspector; the inspector spawns nothing. Finite tests and static flags are evidence, not sandbox proof.

Frozen at the r3 manifest (manifest-r3.json, sha256 3c2253b6…8547; 294 files) for filbert's full code review. Rocko changes nothing under the allowlist until darkwing returns reconciled findings. C1 (Archify rev 3) remains HELD; no lane action taken.