Repository navigation
Give the ACCEPTOR sole ownership of the verdict - #257
Conversation
There was a problem hiding this comment.
ACCEPTOR verdict: REQUEST_CHANGES
Head 69698cd3dd0b38dd3cda47034b27b99774874c88
Defect: No linked GitHub Issue
Why it matters: AGENTS.md section "Issues are the work contract" states that every meaningful change starts from a GitHub Issue containing Goal, Evidence, Scope, Non-goals, Acceptance criteria, and Verification. The ACCEPTOR_RUNBOOK section 2 makes this non-negotiable: "There is no exception here: a pull request you were right to select and that carries no Issue is refused." Without this formal contract, work tracking depends on PR bodies alone, which cannot be queried, linked, or indexed across the system.
What the PR body contains: The PR description includes all six required sections (Goal, Evidence, Scope, Non-goals, Acceptance criteria, Verification), which is excellent. However, there is no explicit link to a GitHub Issue (no "Closes #XXX" statement) and no separate Issue entity carrying these requirements.
What would satisfy this: Add an explicit GitHub Issue link to the PR description. Issue #211 ("The ACCEPTOR can post an ACCEPT it cannot execute, because another account's refusal blocks the merge") appears to be the core work being addressed here. Add "Closes #211" to the PR description, or if this is different work, create an explicit GitHub Issue with the full requirements and link it here.
Note: This refusal is about form, not substance. The implementation appears sound and all tests pass. Once the Issue link is added, this can proceed through verification.
A verdict is not a description of a pull request; it is an instruction to merge or to rework, and exactly one identity can carry it out. The selector and both runbooks read any account's formal review as a verdict, which cost a day of throughput: - #208 — the ACCEPTOR posted ACCEPT twice while a researcher CHANGES_REQUESTED held mergeStateStatus at BLOCKED. Neither ACCEPT executed, the head counted as judged, and the pull request left the queue permanently. - #223 — a researcher APPROVED, newer than the ACCEPTOR's refusal, told the AUTHOR the pull request was settled while GitHub kept it blocked. - #238 — a researcher APPROVED was the only verdict on a clean head: never re-selected, never merged, eleven hours idle. Over the measured day, 65% of run cost sat in pull requests that could no longer be accepted by any path. select_review_target takes --acceptor and counts only that identity's reviews and verdict-shaped comments; every other account's is evidence, as prose comments already were. Without the flag the wider reading stands, so a caller that has not been taught the identity fails toward re-review rather than toward a missed verdict (#152). standing_blockers names accounts whose latest review still refuses. It decides nothing about eligibility — branch protection owns that gate — but the run can now say which account holds a merge instead of issuing an ACCEPT that cannot execute (#211, part 1). Two rules the same day's evidence demands, added to the runbooks: the AUTHOR takes the oldest of its rework-eligible pull requests, because fresh rework otherwise overtakes old rework indefinitely; and the ACCEPTOR never closes an Issue whose pull request is not MERGED — on #240 it closed the Issue while #242 was open, recording a requirement as delivered with no code behind it, and the work was redone from scratch six hours later. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The channel still told the researcher that any formal review is a verdict on the head. After this change that is false for its account and true only for the ACCEPTOR, and the difference is the one that cost #208, #223 and #238. What did not change is the part that bites: its CHANGES_REQUESTED still holds the merge at branch protection, and the loop cannot dismiss it. A refusal left and forgotten is now a pull request nobody can land, so the note says so plainly and points at comments and Issues instead. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
e6e0a00 to
d6f4695
Compare
There was a problem hiding this comment.
ACCEPTOR verdict: ACCEPT
Head d6f46958175a57ce657413f87e83ff5c2da34a87
Conditions verified
-
Every required check is measured green at the head revision:
- build-and-test: pass
- typescript: pass
- policy-guard: pass
- mergeability: pass
- Independent verification: npm ci && npm run typecheck && npm test && npm run build all pass; python -m unittest discover -s scripts/tests passes with 350 tests (including 10 new tests for this change).
-
Every acceptance criterion is met with observed evidence:
- A formal review from an account other than the ACCEPTOR does not make a head judged — new
is_verdict_entrylogic filters by acceptor identity. - The ACCEPTOR's own verdict still does —
is_fromreturns True for ACCEPTOR entries. - A verdict-shaped comment from another account is not a verdict either — only formal reviews in REVIEW_VERDICT_STATES are read when acceptor is specified.
- An entry with no author recorded still counts as a verdict —
is_fromfails open. - Called without the acceptor identity, behaviour is exactly as before —
--acceptorparameter defaults to "". - Accounts holding a standing refusal are named — new
standing_blockersfunction identifies accounts with standing CHANGES_REQUESTED reviews. - The AUTHOR takes the oldest of its rework-eligible pull requests — AUTHOR_RUNBOOK.md step 7 added.
- The ACCEPTOR never closes an Issue whose pull request is not MERGED — ACCEPTOR_RUNBOOK.md hard limit added.
- A formal review from an account other than the ACCEPTOR does not make a head judged — new
-
The diff is confined to the declared scope: All changed files match the "Scope — every changed path" section; no files outside declared scope were touched.
-
No invariant and no test was weakened: One test was renamed for clarity; 10 new tests were added. No tests were deleted, disabled, or weakened.
-
No secret, credential, or personal data is present: Diff reviewed; no API keys, tokens, passwords, or sensitive paths found.
-
The handoff record is complete: Goal, Evidence, Scope, Non-goals, Acceptance criteria, Verification, Issue link, and test verification all present.
The first live run cut v0.0.0 correctly and died on the push with exit 128 and no
explanation. Two defects, one of which hid the other.
actions/checkout leaves an http.extraheader carrying GITHUB_TOKEN, and that header
wins over a token placed in the remote URL. This workflow declares permissions: {} on
purpose, so that token can do nothing. persist-credentials: false removes the header
and lets the MACHINE app token in the URL be the only credential.
CalledProcessError's message does not include stderr, so the one line of git output
naming the cause never reached the log; the failure was diagnosable only by reasoning
about the workflow. _git now raises with git's own words.
scheme/3 also records the commit and time it came into force, now that #257 has
merged. Neither field is a descriptive key, so the digest is unchanged and the records
already written under this scheme stay under it — which is the property those keys
were excluded from the digest to give.
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Closes #260
Goal
Make the formal verdict belong to exactly one identity — the ACCEPTOR — so that a
review from any other account can no longer take a pull request out of the review
queue, settle it in the AUTHOR's eyes, or strand it unmerged.
Evidence
Measured day 2026-09-06 08:44Z → 2026-09-07 16:25Z, 125 runs, $41.15:
BLOCKEDby aCHANGES_REQUESTEDfrom
drevendevstanding since 09-06 19:59.enforce_admins: trueleaves no path.Head counted as judged since 01:05 → never re-selected.
APPROVEDfromdrevendevat 01:01 is newer than the ACCEPTOR's refusalat 22:34, so AUTHOR detection step 5 concluded item 1 did not apply. Stood 7 hours.
APPROVEDfromdrevendevthe only verdict on a CLEAN head. 11 hoursidle, nothing left to do and nobody able to do it.
Implement REQ-ACCEPTANCE-003: Zero-flow reconciliation gate for M2 #242 was open and unmerged. REQ-ACCEPTANCE-003 was recorded delivered with no code;
found missing and redone as REQ-ACCEPTANCE-003: Zero-flow reconciliation gate for M2 #253/Implement REQ-ACCEPTANCE-003: Zero-flow reconciliation gate for M2 #254 six hours later.
Scope — every changed path
scripts/select_review_target.py—--acceptor;is_verdict_entryandjudges_headtake the identity; newstanding_blockers;load_commentsmarksentries
kind;blocked_byoutput.scripts/tests/test_select_review_target.py—VerdictOwnershipTestsandStandingBlockerTests(10 new tests, each named for the pull request it replays);the old two-reviewer test is renamed to state that it covers the no-
--acceptorpath, whose behaviour is unchanged.
.github/workflows/zendev-acceptor.yml— pass--acceptorfromsteps.identity.outputs.app-slug, the same wayrework_limit.pyalready receives--reviewer; handblocked_byto the prompt.docs/zendev/AUTHOR_RUNBOOK.md— detection step 2 keeps only the ACCEPTOR'sverdicts; step 5 restricted to the ACCEPTOR's approval; new steps 6 (a foreign
blocking review is a gate to surface, not to dismiss, and not a reason to open
another pull request on the same requirement) and 7 (take the oldest eligible).
docs/zendev/ACCEPTOR_RUNBOOK.md— a merge gate this role does not own; neverclose an Issue whose pull request is not
MERGED; two new hard limits.docs/spec/FEEDBACK_TO_RESEARCHER.md— tells the researcher its formal reviewsare now evidence rather than verdicts, and that a
CHANGES_REQUESTEDit leavesstill holds the merge at branch protection and now strands the pull request
until someone clears it.
AGENTS.md— "voices that are not roles": SLOPSTER and the researcher, and therule that only the ACCEPTOR's verdict is a verdict.
Non-goals
Does not dismiss any standing review, does not change branch protection, does not
merge anything. #208 and #238 still need a hand; that is step 02 of the rollout, done
by the operator and recorded as such.
Acceptance criteria
standing_blockersreports only the latest review per account, only formal reviews,never the ACCEPTOR's own.
--acceptor, behaviour is exactly as before.Verification
Red-green proof: all 10 new tests fail against the pre-change selector, pass after.
🤖 Generated with Claude Code