Skip to content

Give the ACCEPTOR sole ownership of the verdict - #257

Merged
zendev-acceptor[bot] merged 2 commits into
masterfrom
policy/verdict-ownership
Sep 7, 2026
Merged

zendev-acceptor[bot] merged 2 commits into
masterfrom
policy/verdict-ownership

Conversation

@drevendev

@drevendev drevendev commented Sep 7, 2026 •

Copy link
Copy Markdown
Owner

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:

spend in pull requests that can no longer be accepted $26.55 (65%)
spend in pull requests that merged $9.05 (22%)
unattributed $5.54 (13%)

Scope — every changed path

  • scripts/select_review_target.py — --acceptor; is_verdict_entry and
    judges_head take the identity; new standing_blockers; load_comments marks
    entries kind; blocked_by output.
  • scripts/tests/test_select_review_target.py — VerdictOwnershipTests and
    StandingBlockerTests (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---acceptor
    path, whose behaviour is unchanged.
  • .github/workflows/zendev-acceptor.yml — pass --acceptor from
    steps.identity.outputs.app-slug, the same way rework_limit.py already receives
    --reviewer; hand blocked_by to the prompt.
  • docs/zendev/AUTHOR_RUNBOOK.md — detection step 2 keeps only the ACCEPTOR's
    verdicts; 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; never
    close an Issue whose pull request is not MERGED; two new hard limits.
  • docs/spec/FEEDBACK_TO_RESEARCHER.md — tells the researcher its formal reviews
    are now evidence rather than verdicts, and that a CHANGES_REQUESTED it leaves
    still 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 the
    rule 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

  • A formal review from an account other than the ACCEPTOR does not make a head judged.
  • The ACCEPTOR's own verdict still does (The selector does not recognise the verdict heading the runbook prescribes #152 does not regress).
  • An entry with no author recorded still counts as a verdict.
  • standing_blockers reports only the latest review per account, only formal reviews,
    never the ACCEPTOR's own.
  • Called without --acceptor, behaviour is exactly as before.

Verification

Red-green proof: all 10 new tests fail against the pre-change selector, pass after.

python -m unittest discover -s scripts/tests   →  Ran 350 tests … OK

🤖 Generated with Claude Code

@zendev-acceptor zendev-acceptor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

drevendev and others added 2 commits September 7, 2026 20:58
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>
@drevendev
drevendev force-pushed the policy/verdict-ownership branch from e6e0a00 to d6f4695 Compare September 7, 2026 18:00

@zendev-acceptor zendev-acceptor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACCEPTOR verdict: ACCEPT

Head d6f46958175a57ce657413f87e83ff5c2da34a87

Conditions verified

  1. 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).
  2. 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_entry logic filters by acceptor identity.
    • The ACCEPTOR's own verdict still does — is_from returns 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_from fails open.
    • Called without the acceptor identity, behaviour is exactly as before — --acceptor parameter defaults to "".
    • Accounts holding a standing refusal are named — new standing_blockers function 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.
  3. 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.

  4. 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.

  5. No secret, credential, or personal data is present: Diff reviewed; no API keys, tokens, passwords, or sensitive paths found.

  6. The handoff record is complete: Goal, Evidence, Scope, Non-goals, Acceptance criteria, Verification, Issue link, and test verification all present.

@zendev-acceptor
zendev-acceptor Bot merged commit 39cca61 into master Sep 7, 2026
6 checks passed
@zendev-acceptor
zendev-acceptor Bot deleted the policy/verdict-ownership branch September 7, 2026 18:04
drevendev added a commit that referenced this pull request Sep 7, 2026
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>
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.

A review from any account is read as a verdict, but only the ACCEPTOR can execute one

1 participant