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

181 lines
14 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 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.