AMG-4 — the records auto-merge workflow judges a PR by the PR's OWN copy of the guard, so its "can never widen itself" claim is circular
status: open · raised panel run 12 (2026-08-21) · migrated 2026-08-24 (body verbatim below) · class: loudness & safety (governance guard) · judgement: YES · effort 0.25 pd + one owner scope-check · re-verified STILL LIVE and aggravated by panel run 13 (2026-08-24)
17. AMG-4 — the records auto-merge workflow judges a PR by the PR's OWN copy
of the guard script, so its "can never widen itself" claim is circular: a
same-repo PR that edits scripts/automerge_guard.py to always-ALLOW is
graded by the edited guard. · judgement: YES · effort 0.25 pd (fix) +
one owner scope-check (shared with the OBX-1 question below) · horizon: live
now, conditional on the pushing credential's reach
Mechanism, code-verified. .github/workflows/auto-merge-records.yml:44 is
actions/checkout@v5 with no ref (on a pull_request event → the PR merge
commit), and :82 runs python3 scripts/automerge_guard.py from that
checkout. The ^scripts/ deny pattern (automerge_guard.py:55) therefore
lives in the very file a PR replaces. Editing scripts/ (unlike
.github/workflows/) needs no workflow scope on the pushing credential,
so any routine/session with contents-write can open a records-classed PR that
rewrites the guard to ALLOW and edits the guard's own tests in the same PR;
the only remaining gate is required CI under --auto (:103). The workflow
comment :20-21 and the guard docstring :17-18 both assert self-widening is
impossible — false for scripts/. This is the second firing of the 08-11
"cannot-widen-itself" valve (queue :2119), which pre-registered mint as
its own row; AMG-2 was the first firing of the same valve, so the precedent
for a separate row is in the file.
Data plan. None — static code + workflow semantics. Owned. One fact is
not repo-verifiable and is the shared owner-check below: whether the
.github/-flank is even reachable depends on whether the routines' GitHub
credential holds workflow scope.
Test design + kill criterion — pre-registered, HARMLESS-ON-FAILURE (this is
the sharp part). Fix: execute the guard from the base ref, not the merge
ref — the workflow already has the exact primitive (git show "origin/$BASE:$Q" at :75 materialises a base-ref file). Verification probe:
a PR that edits the guard to unconditionally ALLOW must draw a DENY
end-to-end. Critical constraint on the probe, or the test IS the incident it
tests for: a naive probe against the real base branch, if the fix regressed,
would let its own edited guard ALLOW, arm gh pr merge --auto, and merge an
always-ALLOW guard onto main. The probe MUST be harmless on failure — target
a scratch base branch (the pull_request trigger has no base filter, so the
workflow still fires; a wrongful merge lands on the scratch branch, not main),
or carry a deliberately-failing required check so --auto can never complete.
Specify this in the PR before touching code. Kill criterion: with the fix
in place, the always-ALLOW probe PR draws DENY from the base-ref guard;
reverting the fix → the probe's edited guard ALLOWs (against the scratch base
only). Recorded residual: the workflow file itself is still evaluated
from the merge ref, shielded only by the workflow-scope requirement — whose
status is the very unknown the OBX-1 owner-check resolves. If that scope is
held by the routines' credential, this fix is necessary-but-not-sufficient and
the residual must be recorded, not silently assumed closed.
Triage note — 2026-08-24 (annotation only; the body above is untouched)
Re-verified STILL LIVE this week, and aggravated. Panel run 13 records
that 05895ab ("gates: merge exactly the SHA the guard evaluated") fixes the
TOCTOU leg only — which SHA gets merged — not who supplies the evaluator.
.github/workflows/auto-merge-records.yml:~44 still checks out the PR merge
ref and runs the PR's own copy of the guard. For same-repo pull_request
events the workflow file itself also runs from the merge ref with a
contents: write token, and this plan has no branch protection (05895ab's own
commit message states it), so base-ref-guarding the script is
necessary-but-not-sufficient. See the cross-reference section of
research/queue/open/amg-5-sanctioned-move-fail-open.md, which states plainly
that AMG-5 does not subsume AMG-4.
Escalated by the daily audit as well. The 2026-08-21 daily-audit report
(branch outbox/daily-audit) raised this as escalation E1, asking for either
the base-ref checkout fix or a temporary disable of the records fast path
before the 08-26 implementer window. That ask was not actioned.
BUILT — 2026-09-02
Outcome: shipped. auto-merge-records.yml now materialises the guard from
origin/$BASE (git show origin/$BASE:scripts/automerge_guard.py) and grades
the PR with THAT copy; an unreadable base-ref guard exits 1 (deny), never
falling back to the PR's copy.
Route — recorded honestly: built on the owner's direct instruction in an
interactive session (2026-09-02 deep self-audit, research/2026-09-02_self_audit.md
finding M3), NOT via queue/approved/ and the Wednesday implementer — the
EXQ-1 precedent. The guard correctly denies this open→built move, so a human
merges.
Verification. Two lint tests in tests/test_execution/test_automerge_guard.py:
the base-ref materialisation must be present and no python3 scripts/automerge_guard.py
invocation may remain; the read failure must fail closed. Negative control:
both tests were written first and ran RED against the unfixed workflow (2 failed),
then GREEN after the edit. The row's end-to-end probe (an always-ALLOW PR against
a scratch base branch) was NOT run — it needs a scratch branch and a live
workflow run, which is an owner-run step; recorded as not done, not as done.
Residual, recorded as the row required: for same-repo PRs the workflow file
itself still runs from the merge ref (shielded only by the workflow scope
requirement on the pushing credential, still unverified — the OBX-1 check).
The main-push watcher (separate PR, same audit) covers the other bypass: a
commit landing on main with no PR at all.
Provenance recorded by triage 2026-09-07 (annotation only; the record above is untouched): shipped in PR #127 (8c0f731, merged 2026-09-03). The residual OBX-1 probe named in this file — does the routines's GitHub credential hold workflow scope? — is still unanswered and is carried in the 2026-09-07 triage report.