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

306 lines
22 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 r2 (FI-ROCKO-4 dispositions)
Author: rocko (Claude Fable 5.1). Coordinator: darkwing. Task: FI-ROCKO-4.
Written 2026-09-06 18:35 UTC. Supersedes nothing: the original report
`docs/plans/reviews/2026-09-06_foundation-inspector-rocko-build.md` is preserved
unedited (sha256 `857470d97af6aeae4e7d7942c2a1d8455bfcda74ff68b8e1dc4ba8d066521fa4`,
unchanged since delivery); this revision records the admission findings, what
changed, and the new frozen candidate.
Inputs bound to this revision:
| Input | sha256 |
|---|---|
| Admission findings `2026-09-06_foundation-inspector-build-admission-findings.md` (as received, 18:04Z) | `59233c6126c2355df5a367f0e61df7798e6cccfaa682454992fcd0d0df39c82c` |
| Same file now (darkwing's "Transport (2026-09-06 18:17 UTC)" footnote appended; no other change) | `8d9f7aab3b3bdf79345a07ba3595b966982b7667714ac7e3f669d65e328ca3d5` |
| Charter candidate 3 `docs/plans/2026-09-06_foundation-inspector-charter.md` | `19b6721128a627a2032ffdb95ece2d50abe69a8f6d521e9eff8bbdaff22798b6` |
| `records.schema.json` / `check.py` / `records.fixtures.json` | `05774aaf…72af6` / `82564a7d…4607` / `d433d06d…da85` (full values re-verified by the oracle gate 1 and listed in the manifest) |
| Build request `2026-09-06_foundation-inspector-rocko-build-request.md` (now, transport footnote included) | `90a1f329e859152d4ce8e157cbd6c56e6bcced16b4ed757822d2309514fa9a15` |
| Integration HEAD (unchanged, nothing staged) | `9a5fbdbda74b16adf488fe28138b2ba69ea5e669` |
| Machine-generated manifest `2026-09-06_foundation-inspector-rocko-build-manifest-r2.json` | `2e8b5f1998a3be6cc7fcc3fba308724d2a29ca166c08f005e1bca88ddbc14196` |
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. Per-finding dispositions
### C1 — demo hash mismatch: transcription error, no drift
Disposition: **typo in the original report, not evidence drift.** Corrected;
no evidence was regenerated to make anything match.
- Advertised (report line 96): `…548fc023…`; measured then and now:
`f1c6bd523cdff161e51da156fd00cfaa652d193f41541686548cf023378fd8ab`.
The only difference is the transposition `fc``cf` at positions 5152; the
other 62 hex characters are identical.
- Evidence the file never changed: my scratch hash listing taken at 18:03:52 UTC
(before the report was written) already contains the measured value for
`demo/prop-message-is-not-authority.json`; the file's mtime is 17:52:58 UTC,
earlier than that listing; the demo copy is byte-identical to
`bundles/prop-message-is-not-authority.json` (same hash, 49,975 bytes; asserted
by `fixtures.test.mjs` "demo copy drifted"); and both aggregate manifests
reproduce the original report's values with the original method:
bundles 185 files 9,235,245 bytes `a804e4ab253839ba2bb8ad7475d27512b2ef88e70a9779f9093fee5478de4823`,
raw 19 files 1,336,985 bytes `508ad85208f4b69676f3d041faa4ff4cd4a2f8de74b7bde9b0b89123a8485cc4`.
- Root cause: the original §3 tables were hand-transcribed. The r2 manifest is
written by a script (`manifest-r2.json`: path, mode, size, sha256 for all 239
candidate files, aggregates, HEAD, toolchain, pinned inputs). No hash in this
report or in the manifest is typed by hand except where a value is quoted from
the original report to show the error.
### C2 — oracle divergences were waived: waiver removed, one class remains as a spec blocker
Disposition: **fixed for the calendar class; precise blocker for the pattern
class.** The oracle no longer has a `KNOWN_DIVERGENCES` list; every
schema-column disagreement is a problem and the oracle FAILS.
Calendar class (2 cases, fixed): the pinned checker refuses years 00010999 on
this platform because `strftime("%Y")` is unpadded below 1000, so its
strptime/strftime round-trip fails. The inspector now refuses them identically
(`isValidTime`: year < 1000 refused; charter §8 "calendar semantics from the
pinned checker"). Witness:
`python3 -c 'from datetime import datetime; print(datetime(999,1,1).strftime("%Y"))'``999`.
The oracle asserts this witness as gate 3 before comparing and fails with an
explicit "platform boundary" message on a platform that pads. Cases
`calendar-year-0001`, `calendar-year-0999`, `calendar-year-0100` now agree
(both refuse). `resolve.test.mjs` adds the three boundary vectors.
Pattern class (4 cases, blocker, unchanged verdicts): the pinned checker
evaluates `pattern` through Python `re.search`, whose `$` also matches
immediately before a trailing `\n`; ECMAScript `$` does not. Consequently the
pinned checker ACCEPTS `id`, `scope.projectId`, `authorizationRef` (runtimeId
pattern) and `policyRef.digest` values that carry a trailing newline, and the
inspector REFUSES them (`shape-pattern-mismatch`). Witnesses:
```
python3 -c 'import re; print(bool(re.search(r"^[a-z0-9][a-z0-9._-]{0,63}$", "abc\n")))' # True
node -e 'console.log(/^[a-z0-9][a-z0-9._-]{0,63}$/.test("abc\n"))' # false
```
Current oracle output (verbatim, exit 1):
```
platform witness: strftime('%Y') for year 999 -> '999' (pinned checker refuses years 0001..0999)
node v26.8.1; corpus 1432 records (38 pinned fixtures, 410 unique bundle records, 984 typeCase/mutation/lexical cases)
schema column: agree-valid 476, agree-invalid 915, DISAGREEMENTS 4; strict-only (parser-bound) cases: 27; unsupported-kind records not schema-assessed by the inspector: 10
profile column (schema-valid records only): profile-valid 476, profile-invalid 0
problem: DISAGREEMENT pattern-authorization-ref-trailing-newline: pinned-checker schema True / inspector schema False (inspector profile False, rule shape-pattern-mismatch)
problem: DISAGREEMENT pattern-digest-trailing-newline: pinned-checker schema True / inspector schema False (inspector profile False, rule shape-pattern-mismatch)
problem: DISAGREEMENT pattern-id-trailing-newline: pinned-checker schema True / inspector schema False (inspector profile False, rule shape-pattern-mismatch)
problem: DISAGREEMENT pattern-scope-project-id-trailing-newline: pinned-checker schema True / inspector schema False (inspector profile False, rule shape-pattern-mismatch)
FAIL: 4 differential problems
```
Why I did not resolve it myself: the findings forbid implementing a waiver or
changing the pinned schema/checker, and the charter forbids weakening safe
input handling. Making the inspector accept an identifier with an embedded
newline would relax input handling merely to turn the count green (the newline
would then be carried into `selection.projectId` and the text rendering, and a
`scope.projectId` of `p1\n` would be a distinct identity from `p1` that renders
almost identically). The schema-column comparison is therefore left honestly
red, and the suite reports `selftest: 36 passed, 1 failed` until this is ruled.
Proposed amendment (for darkwing/Jason; I implement nothing until ruled):
- (a) **Recommended.** Add to charter §8 (or the incorporated notes) a
"checker-dialect artifact" clause: "The pinned checker's `pattern` semantics
are Python `re.search`; its `$` accepts one trailing newline. This is a
dialect artifact, not an intended grammar. The inspector applies the pattern
with anchored (ECMAScript) semantics and refuses a trailing newline. The
differential oracle records the cases in this class by name, with the two
witnesses above, and reports them as an expected artifact-class difference
separately from agreement counts; any other disagreement fails." Under (a) the
oracle would carry a named, witnessed artifact class with exact direction
(pinned True / inspector False) and fail on any other direction or any other
case. This is the amendment I would implement; it needs a coordinator ruling
because it is a policy choice about which side is authoritative.
- (b) Against: relax the inspector to Python semantics. Rejected for the input
handling reason above and because §8 keeps "record-schema verdict distinct
from … refusal"; the inspector would have to accept an identifier the runtime
would never mint.
- (c) Against: drop the four mutations from the corpus. Rejected: it hides a
real, reproducible difference between the two implementations.
Until ruled, the candidate's differential gate is **red by design**, and this
report does not claim oracle PASS.
### C3 — `exit` outside the closed §7 fields: removed from the serialized result
Disposition: **fixed; no charter amendment needed.** Exit status is process
metadata derived from the emitted result (`exitFor(result)`), never a field
of it and never a text line.
- `resolve.mjs`: `RESULT_KEYS` is exactly the 12 charter §7 names in charter
order; `buildResult` builds no `exit` member and throws if the requested exit
is inconsistent with the derived result/reason; new export `exitFor(result)`
(allowed→0; refused/unresolved→3; invalid+io-failure→4; other invalid→2).
`evaluate()` returns `{ result, exit, detail }` with `exit` outside `result`.
- `foundation-inspect.mjs`: `renderText` ends with the `diagnostic:` line (no
`exit:` line); `run()` returns `{ json, result, exit }`; `main()` writes the
rendering and sets `process.exitCode = exit`.
- Tests whose expected keys come from the approved contract, not from the
implementation: `resolve.test.mjs` "closed result fields equal the pinned
charter §7 list" reads the charter file, asserts its sha256 is
`19b67211…798b6`, parses the twelve `- name:` bullets under "Closed result
fields, emitted on every outcome:", and asserts they equal both an
independent literal copy in the test and the implementation's `RESULT_KEYS`;
it also asserts `exit` is absent, checks `exitFor` on all four classes, and
that `buildResult` throws for four exit/reason inconsistencies.
`cli.test.mjs` and `fixtures.test.mjs` assert `!("exit" in parsed)` for every
spawned run and derive the expected process status from the parsed result;
text renderings are asserted to end with the diagnostic line and to contain
no `exit` line for all 204 cases. `test-foundation.sh` asserts the `--json`
key list equals the twelve names literally.
- Consequence: **the ten demo goldens (`*.expected.txt`, `*.expected.json`) were
regenerated** with the README loop because the contract fix removed one line
/ one member from each; the five `*.expected.exit` files are byte-identical
to before (hashes `9a271f2a…86aa` for 0 and `1121cfcc…02a2` for 3, as in the
original table). `index.json` is unchanged (its `expect.exit` was always the
process status). Demo tree total bytes 249,096 → 248,991 (105 = 5 × (8-byte
text line + 13-byte JSON member)).
### C4 — demo step 2 could not show unchanged bytes: corrected
Disposition: **fixed.** The BEFORE manifest is saved in a separate scratch
location first, the preview runs, then `sha256sum -c` verifies against that
SAME manifest (plus an inventory of path/type/size/mode/inode/mtime and a file
count). Corrected commands are in §4; the receipt from running them is in §3.
### C5 — report omitted O_NONBLOCK: prose corrected, flag kept
Disposition: **fixed in prose only; no code change.** The CLI opens the input
with `O_RDONLY | O_NOFOLLOW | O_NONBLOCK` (`REQUIRED_FLAGS` = those three
names) and refuses with `open-flags-unavailable` (exit 4) if any is not a
number on the platform. O_CLOEXEC is not used (charter §6). The original
report's lines 142 and 270 said `O_RDONLY|O_NOFOLLOW` only; that text was wrong
and is superseded by this paragraph. The FIFO test (`cli.test.mjs` "FIFO must be
refused after O_NONBLOCK open, not read") exercises the flag.
## 2. Changed files (old → new sha256; everything else byte-identical)
All under the FI-ROCKO-3 allowlist plus the two paths offered in the findings.
Nothing else in the working tree was touched; `git status` shows the same
pre-existing modified/untracked entries as before plus the two r2 documents.
| File | Lines | Original report sha256 | r2 sha256 |
|---|---|---|---|
| `scripts/foundation-inspect.mjs` | 239→244 | `bfa1b78007c772c54fe1dab90a53e19e6bce7e4888a5adb2de0b2cfe43c5cddd` | `dad369490ca8396d3b4a7bd4a1b7fa87b677a2b8cf991bf61672d446e5aa3c93` |
| `scripts/foundation/resolve.mjs` | 1625→1646 | `9ae8b4255b5dc1f1e9b0d36549865268c12dc53f010984eb1e5616b127d1d8a2` | `aa842898ebb0bb2a911406314c4c3a2c4079b46bfd674c4ffba0176a1e32766c` |
| `scripts/foundation/verify-schema.py` | 453→467 | `7e3098655b2e5f261391d788c2112a6ffb055895e357b427bc68e5f2fa7b52d7` | `b61031b6c2cea126133afda70894e7f2429b08bf2cd50fc766ec6b99f348c966` |
| `scripts/foundation/resolve.test.mjs` | 294→333 | `05f27aa6c8a1b8ff2ee45e5de03a9aea5ed13cf011b2464c5865022dc9781449` | `6bb6d40fa6ec90fb52db658f77ca9fd27cc3eb8673f040f52fc126e67983a571` |
| `scripts/foundation/cli.test.mjs` | 279→281 | `b7e9a13c11e3e6f487ada2779c892cdf848526de1ef8b21387d15d4a461ddb62` | `ccec06c266e03d878c1ecd9e8c20ee3f1fb82f478daead25efa4661b7090d83f` |
| `scripts/foundation/fixtures.test.mjs` | 93→98 | `2827de36214c8014471fed3478c42bf48eddb3fbdbff82cac9e27851646b15d2` | `94e7508717efcafd11585a288a5d791e1e48a993794e2d470c6097f904befdb3` |
| `scripts/test-foundation.sh` | 132 | `76fb7ba9da4345271b6ba93228a9da69c7e55c4e44b9f4033e84a8f4b430e8ec` | `fdfa13ce247bc9ad8f3617e248913aef384029198924685ada3431006ffdf114` |
| `fixtures/demo/demo-read-w1.expected.txt` | | `8bfff519ce8275cfe59562efd04647c242c328a09b301b91d70491f030cf1600` | `8ba4bbe92819ac8912c72e7c635730e99eac7916c0d924fc81b75115b5939b53` |
| `fixtures/demo/demo-read-w1.expected.json` | | `55e10c1693787cd8e39d3a66841d30e2336080d2ae4450a806a7ae8373db476b` | `42055bda71075ba5c710f011f83d470228f18097d93ac5585ba1b534cba0ef2a` |
| `fixtures/demo/demo-file-change-src.expected.txt` | | `f5387217ca0a41d437468af33b7d87d06c0c5535ab5490b568f9866a13b2732e` | `2cdd6d4e162fa904b03e8f8a88553a03065ac7fb646baabf21625d4f5962a79b` |
| `fixtures/demo/demo-file-change-src.expected.json` | | `88c7793c6af17ca255525451a499d9f8bb165155e50d2ee936bd68ba82a876a7` | `7ea412916404ec948c94e929cc7b2ce120b394944c66c7bfcc811d0721dd82db` |
| `fixtures/demo/demo-change-w1.expected.txt` | | `2a1090e5f5ad08e2175830943a7c81928de970470893e803b1e440843152c348` | `0e2807ab56d15670c51c8ad4a43f33b36c5672799937561f39e20c6e93be1dda` |
| `fixtures/demo/demo-change-w1.expected.json` | | `36c287cba327070f7ff3dd9846421bee3ebd0dd52e9c602b68e84fa620ef4fc5` | `ecf8313993ca27450d1753c6178be1a063fb0637f14be8f091de923b8b722516` |
| `fixtures/demo/adm-registration-revoked.expected.txt` | | `624c9e5991597d59fc3379343b16e7eab9cb6434f054da3a4df7518742e3b36c` | `c2fd118d44d21cbd709d30a2c2be2603740a81e628aa9d32f95d6eaf06d3a9a6` |
| `fixtures/demo/adm-registration-revoked.expected.json` | | `b966d939e0d2851ef731462a0fda932e75f53b9aac406216ad440f781e036d0f` | `c44b0935ed329c1a381f986997b1bc4be702fc4229166d8a0b49e4e80f9b2fa8` |
| `fixtures/demo/prop-message-is-not-authority.expected.txt` | | `e7b0e9fa64abe0c0f8c91fbc9aa565988ce76cb3967672bfacafab350e20d8be` | `388983ad6e4e31db6f5a688773ff2bf07d44d7ffe530eb7870e729a0f546278d` |
| `fixtures/demo/prop-message-is-not-authority.expected.json` | | `53884bfdea21185141b9a06d52aa1c0fe8e7bfae6627b465929403521f9bc4d8` | `742e479ddbb4cf2138d49ec0ac40cd038b6c20f8b9c0e2daffc5a859db9f4145` |
| `fixtures/demo/prop-message-is-not-authority.json` | | (report typo `…548fc023…`) | `f1c6bd523cdff161e51da156fd00cfaa652d193f41541686548cf023378fd8ab` (file unchanged) |
Unchanged (same hash as the original table): `strict-json.mjs`, `canonical.mjs`,
`validate-record.mjs`, `strict-json.test.mjs`, `canonical.test.mjs`,
`build-fixtures.mjs`, `index.json`, `fixtures/README.md`, all five
`*.expected.exit`, all five demo `*.json` bundles, all 185 bundles and 19 raw
fixtures (aggregates above). Full per-file listing with modes: `manifest-r2.json`.
Aggregates (r2 manifest method: sha256 of the LC_ALL=C sorted `sha256sum`
listing with repo-relative paths): `scripts/foundation/fixtures/` 227 files
`36d99b91065ed7af03f17b7b9cc309b3f7805ca5e3a337ddb5f3536f2ccaa563`;
`scripts/foundation/` 237 files `6c111ad1165e0da219d566f919e25005af47fa004257a11e7e2750a4010b9ecb`;
all 239 candidate files `e0fe89d9cc5245031ce484e9e0fe15b93319dd2cdf040800c52555a26fb1c1a6`.
Original-method demo aggregate (run in `scripts/foundation/fixtures/`,
`sha256sum demo/* | sha256sum`): `7000fdd9b2a5e92256429e148240541fab3f93d2ef009e47b08baddcbcef5216`.
Source changes in words: `resolve.mjs` (isValidTime year ≥ 1000; `exitFor`;
`RESULT_KEYS` without exit; `buildResult` consistency guard; `evaluate` returns
exit beside result). `foundation-inspect.mjs` (no exit line; `withExit`; exit via
`process.exitCode` only). `verify-schema.py` (waiver list and waiver comparison
deleted; gate 3 platform witness; trailing-newline cases renamed
`pattern-*-trailing-newline` and kept; calendar boundary cases; separate
schema/profile column summary; every disagreement is a problem).
`test-foundation.sh` (JSON key-list assertion; oracle summary grep lines).
Tests as described under C3 and C2. No new dependency, no new file outside the
allowlist, no `__pycache__` (verified absent after all runs).
## 3. Receipts (all run 2026-09-06 18:2x18:30 UTC at HEAD 9a5fbdbd)
| Check | Result |
|---|---|
| `node --check` on all 10 `.mjs` files; `ast.parse` on `verify-schema.py` | OK |
| `node --test scripts/foundation/` | 58 tests, 58 pass, 0 fail (was 57; +1 charter-derived field test) |
| `python3 scripts/foundation/verify-schema.py` | **FAIL, exit 1: exactly the 4 `pattern-*-trailing-newline` disagreements** (output quoted under C2); gates 13 PASS; 38 pinned fixtures agree |
| `NO_COLOR=1 scripts/test-foundation.sh` | **`selftest: 36 passed, 1 failed`**; the one failure is the oracle gate above; all other checks OK (syntax, fixture regeneration equality, node tests, demo exits 0/0/3/3/3, usage 2, I/O 4 ×3, oversize 2, disclaimer, JSON key list, golden byte-match, sandboxed inventory unchanged, canaries never printed) |
| `python3 docs/plans/foundation-v1-candidate/check.py` | exit 0 |
| `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 |
| `git rev-parse HEAD`; `git status` | `9a5fbdbd…`; nothing staged; only the pre-existing entries plus the two r2 documents |
| `scripts/foundation/__pycache__`, `docs/plans/foundation-v1-candidate/__pycache__` | absent |
Owner-demo receipt (corrected C4 procedure, verbatim outcome lines):
```
step 1 demo-read-w1.json → result: allowed … operation: work.read target null rc=0
step 2 before manifest: 227 files, sha256 cf2eed27c5e5b311d40e5346af89fbc5fdc7781488bf8ea9d663e66e7dd50d59
demo-file-change-src.json → result: allowed … operation: file.change target workspace:src/main.mjs rc=0
sha256sum -c against saved BEFORE manifest: all 227 OK
inventory (path/type/size/mode/inode/mtime) unchanged
file count unchanged: no file added
step 3 adm-workspace-registration-missing.json → refused / not-authorized / workspace-registration-missing, assignment null rc=3
step 4 demo-change-w1.json → unresolved / unknown-effects / runtime-reconciliation-required; selection keeps asg-a-w1 rev 1 rc=3
prop-all-pass-restricted-contexts.json → unresolved / unknown-effects / runtime-reconciliation-required rc=3
extra adm-registration-revoked → refused/registration-revoked rc=3; prop-message-is-not-authority → refused/message-is-not-authority rc=3
json keys = [disclaimer, preview, bundleVersion, authentication, declarations, result, reason, rule, selection, operation, proposal, diagnostic] rc=0
```
Every text rendering begins `SYNTHETIC PREVIEW — NO LIVE EFFECTS` and ends with
the `diagnostic:` line. These are bounded evidence (finite tests, inventory
comparison), not an OS-sandbox proof; the original report's §8 qualifications
stand.
## 4. Corrected owner-demo commands (A9; replaces original §9 step 2)
```sh
# 1. selected agent/project/workspace with a permitted read preview (exit 0)
node scripts/foundation-inspect.mjs scripts/foundation/fixtures/demo/demo-read-w1.json
# 2. assigned file.change preview (exit 0); prove no fixture file changed:
# save the BEFORE manifest elsewhere first, run, then verify against that SAME manifest
BEFORE="$(mktemp)"
( cd scripts/foundation/fixtures && find . -type f | LC_ALL=C sort | xargs sha256sum ) > "$BEFORE"
node scripts/foundation-inspect.mjs scripts/foundation/fixtures/demo/demo-file-change-src.json
( cd scripts/foundation/fixtures && sha256sum -c --quiet "$BEFORE" ) && echo "all $(wc -l < "$BEFORE") files unchanged"
[ "$(cd scripts/foundation/fixtures && find . -type f | wc -l)" = "$(wc -l < "$BEFORE")" ] && echo "no file added"
# 3. unregistered workspace selected: refusal without fallback (exit 3, assignment null)
node scripts/foundation-inspect.mjs scripts/foundation/fixtures/bundles/adm-workspace-registration-missing.json
# 4. cross-lane reassignment preview: original assignment stays selected; even fully
# supplied mock declarations cannot yield allowed (exit 3, unresolved)
node scripts/foundation-inspect.mjs scripts/foundation/fixtures/demo/demo-change-w1.json
node scripts/foundation-inspect.mjs scripts/foundation/fixtures/bundles/prop-all-pass-restricted-contexts.json
# machine-readable form; process exit status is the only exit signal
node scripts/foundation-inspect.mjs --json scripts/foundation/fixtures/demo/demo-read-w1.json; echo "exit $?"
```
Step 5 (Jason's answer on clarity and acceptance) is darkwing's to record; this
report claims no acceptance.
## 5. Open item and freeze
Open: the C2 pattern-class ruling (§1 C2, options a/b/c). Until ruled the
oracle and therefore `test-foundation.sh` are red on exactly those four named
cases; I have not weakened, waived or removed them.
Frozen at the r2 manifest (`manifest-r2.json`, sha256 `2e8b5f19…4196`; 239
files) for independent review. Rocko changes nothing under the allowlist until
darkwing returns a ruling or reconciled findings. No staging, commit,
checkout/reset, push, charter/schema/verdict/coordinator-record edit, live or
native/sync work, credential or `~/.mosaic` read, network access or engine
launch occurred. C1 (Archify rev 3) remains HELD; no lane action taken.