Skip to content

fix(bin): supervision and record integrity, plus watcher arm-guard allow-list - #1493

Closed
coreldh wants to merge 4 commits into
kunchenguid:mainfrom
coreldh:fm/cm31r2-fm-guard-land
Closed

coreldh wants to merge 4 commits into
kunchenguid:mainfrom
coreldh:fm/cm31r2-fm-guard-land

Conversation

@coreldh

@coreldh coreldh commented Aug 2, 2026 •

Copy link
Copy Markdown
Contributor

Correction (by the author, before review): this PR was opened with a title and description
that named only the arm-guard change. That was wrong — it understated the scope by a wide margin.
The description below is the corrected, measured one. Nothing in the branch changed; only this
text did.

Summary

Firstmate supervision and record integrity, plus a hardening of the watcher arm guard.
43 files, +2658/−104, in four commits. This is not a small change and should not be reviewed as one.

What is actually in here

1. fix: reconcile retained supervision state — 43 files, +2522/−104. The bulk of the PR. Adds
six new scripts and their suites:

new purpose
bin/fm-record-reconcile.sh reconcile durable records against live state
bin/fm-custody-lib.sh custody checks for retained work
bin/fm-model-capacity-hold.sh + -lib.sh capacity-based dispatch holds
bin/fm-bearings-report.sh bearings generation
bin/fm-process-progress.sh progress accounting

and touches ~20 existing bin/ scripts, largest being bin/fm-decision-hold.sh (+473/−25) and
bin/fm-watch.sh (+166/−1).

2. fix: reconcile terminal wakes immediately — 7 files, +94/−16.

3. fix: fail closed on watcher syntax checks — 3 files, +78/−24. The arm-guard allow-list.

4. docs: disclose bash -s watcher guard gap — 1 file, +5/−1.

The load-bearing design choice (commit 3)

The watcher syntax-check exception is an allow-list, not patterns bolted onto a deny-list. Three
holes have been found in this guard — --rcfile, --init-file, -s — all argument-consuming or
payload-hiding invocation forms. The class is demonstrably not exhausted, so the structure must fail
closed on the fourth form nobody has found yet. A deny-list would not.

One pre-existing gap is deliberately NOT closed

bash -s with an operand hides a heredoc/here-string payload from shell-invocation analysis and can
execute bin/fm-watch.sh end to end. It is pre-existing on main, not introduced here. Tracked
as #1489; docs/arm-pretool-check.md now names it and limits the coverage claim to statically
recognized shell invocations rather than overstating what the guard catches.

Verification

Rebased onto 8c21b10; origin/main is an ancestor, so this fast-forwards. git range-diff
accounts for all four commits — none squashed or dropped; three patch-identical, one
integration-adjusted where an AGENTS.md conflict retained both this branch's correction and new
main's process-event clause.

Thirteen-case arm-guard matrix across Codex, Claude, Grok, OpenCode and Pi transports: 12/13.
A13 is expected red and is not this branch's defect — attribution was controlled by running it
against origin/main at 8c21b10, which returns the identical deny watcher-bundled. Tracked
separately.

Security-property matrix: bash -n / bash --noexec allowed; nested execution, --rcfile,
--init-file and all three redirection forms denied.

Suites green: fm-arm-pretool-check, fm-procevent, fm-turnend-guard, fm-supervision-events,
fm-watch-triage; fm-lint with pinned ShellCheck 0.11.0; documentation-audience check; git diff --check.

Not independently verified: the guard matrix was produced by the authoring agent. It could not be
re-run by the supervising agent, because commands naming the watcher script are themselves denied by
the guard under test.

Review note

Opened for @kunchenguid. Merge authority is not ours.

@coreldh coreldh changed the title fix(bin): fail closed on watcher syntax checks via allow-list fix(bin): supervision and record integrity, plus watcher arm-guard allow-list Aug 2, 2026
@coreldh
coreldh marked this pull request as draft August 2, 2026 16:26
@coreldh

coreldh commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by #1631, which folds this work back into one self-contained PR per the repository owner's ruling and drops the contract change he rejected. Closing this in favour of #1631 — no work is lost.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant