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.
This commit is contained in:
2026-09-07 14:06:35 -05:00
parent 127a54fdff
commit 8ebddd6f93
423 changed files with 665972 additions and 0 deletions
+101
View File
@@ -0,0 +1,101 @@
# Foundation inspector fixtures
Synthetic, deterministic inputs for `scripts/foundation-inspect.mjs` and its tests.
Nothing here is live: identities, digests, times and paths are invented by
`build-fixtures.mjs` (SHA-256 of `synthetic:<label>` for every fake digest).
| Path | Content |
| --- | --- |
| `build-fixtures.mjs` | Generator. `node scripts/foundation/fixtures/build-fixtures.mjs <out-dir>` |
| `bundles/<case>.json` | One complete bundle per evaluation case (pretty JSON, trailing newline) |
| `raw/<case>.json` | Byte-exact lexical cases (duplicate keys, BOM, invalid UTF-8, oversize, ...) |
| `index.json` | Expected `exit/result/reason/rule/proposalRule[/byteOffset]` per case, sorted by name |
| `demo/<case>.json` | Owner-demo copies of five bundles, byte-identical to `bundles/` |
| `demo/<case>.expected.{txt,json,exit}` | Goldens: text output, `--json` output and exit code |
## Regeneration
```sh
node scripts/foundation/fixtures/build-fixtures.mjs /tmp/fx
diff -r /tmp/fx/bundles scripts/foundation/fixtures/bundles
diff -r /tmp/fx/raw scripts/foundation/fixtures/raw
diff /tmp/fx/index.json scripts/foundation/fixtures/index.json
```
`scripts/test-foundation.sh` performs exactly this comparison; a checked-in
fixture that differs from a fresh generation fails the suite. Goldens are
produced by running the inspector on the demo copies:
```sh
cd scripts/foundation/fixtures/demo
for f in *.json; do case "$f" in *.expected.*) continue;; esac
b="${f%.json}"
node ../../../foundation-inspect.mjs "$f" > "$b.expected.txt"; echo $? > "$b.expected.exit"
node ../../../foundation-inspect.mjs --json "$f" > "$b.expected.json"
done
```
## Case groups (see `index.json`)
positive, shape, profile, identity, structure, cycle, continuity, ownership,
operation, admission, registration-delegation, proposal, lexical (raw). Every
rule in the closed vocabulary except the CLI/I-O rules (covered by
`../cli.test.mjs` with a disposable sandbox) is exercised by at least one case;
`../resolve.test.mjs` enforces that.
The `profile-*` cases (addendum FI-C2-1) cover the strict typed-string profile:
one final LF on an `id`/`runtimeId`/`digest` value is schema-valid under the pinned
checker's `$` semantics and refused by the inspector as `profile-pattern-mismatch`
before any identity lookup (group `profile`); two final LFs, CRLF, CR, interior LF,
U+2028 and U+2029 fail the schema pattern itself (group `shape`); free-form text
with escaped newlines stays allowed (group `positive`).
The FI-FILBERT-6 correction cases (verdict findings F1F5) are the review's witness
recipes rebuilt from the frozen demo bundles, each with positive controls:
`adm-registry-declaration-*` and `adm-*-authorization-undeclared` (F1: every consulted
registry reference resolves by exact registry/id/revision/digest, rule
`registry-declaration-missing`; consulted dependency/mission/subject-task
authorizations must be declared), `adm-assigned-task-without-work-read*` and
`prop-task-only-*` (F2: a bound task is consulted work), `prop-issuer-*` (F3: the
issuer ceiling narrows both requester calculations and work access is rechecked),
`cycle-*versioned*` / `cycle-*-cross-revision` / `cycle-mission-parent-*revision*`
(F4: revision-exact cycle graphs), `shape-order-*` and `prop-message-*` /
`prop-delegation-missing-after-requester-admission` (F5: first failure independent of
record input order; message-is-not-authority after structural validation, before
requester admission). `../resolve.test.mjs` asserts each recipe's first failure and
permutation independence directly.
The FI-ROCKO-7 ordering cases extend F5 to the declared inventories (charter §10.4
applies beyond records): `shape-order-registries-*`, `shape-order-artifacts-*`,
`shape-order-authorizations-*`, `shape-order-delegation-inputs-*` (two different shape
errors per pair, forward and reversed, plus a "swapped" pair where the errors change
places and a malformed entry without a readable typed key, which sorts last),
`shape-order-family-precedence-*` (records, registries, artifacts, authorizations,
delegationInputs stay in that stage order), `profile-order-*` (strict-profile
violations follow the same key; any shape failure precedes them),
`shape-order-registry-digest-*` / `shape-order-profile-precedes-digest-registries` /
`shape-order-two-digest-mismatches-reversed` (content-digest traversal in
registry/id/revision/digest order after the profile stage), `dup-order-*` (identity
stage) and `struct-order-delegation-inputs-*` (structural stage). Typed keys:
registries `registry/id/revision/digest`, artifacts `runId/artifactId/digest`,
authorizations the runtime id, delegationInputs `decisionRef` kind/id/scope/revision;
equal keys tie-break on the entry's total ordering form (below). Semantic sequence arrays (instruction,
skill, endpoint, dependency, subject and evidence lists, paths, canonical content) are
never reordered. `../resolve.test.mjs` asserts the located first failure over every
permutation (all permutations for inventories of up to four entries, a fixed
deterministic family beyond that).
The FI-ROCKO-8 cases (FI-FILBERT-7 finding R5-1) fix the tie-break itself: r5 broke
ties on the ASCII-only mock-digest canonicalizer and mapped every non-ASCII entry to one
empty form, so two distinct Unicode entries with equal (or no) typed keys fell back to
input order. The inspector now orders ties by a total ordering form defined over the
whole strict-JSON input domain (sorted keys, array order kept, every string as a JSON
literal, no normalization), which is injective on distinct values and never a digest or
a validity judgement. `shape-order-unkeyed-unicode-<family>-{forward,reversed}` (two
unreadable non-ASCII entries per family: a string and an object, `shape-type-mismatch`
in both orders), `shape-order-equal-key-unicode-{records,registries}-{forward,reversed}`
(two copies with identical typed keys, a legitimate non-ASCII value and one different
shape error each: `shape-unknown-field` in both orders) and
`positive-unicode-display-name-allowed` (control). `../resolve.test.mjs` asserts the
form's totality and injectivity against `canonicalize()` refusals and the permutation
stability of every pair.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More