Skip to content

chore(agents): a spent metered cap should close on Codex, not on a maintainer #439

Description

@bioedca

Maintenance kind

governance

Work requested

A pull request that passes review can become permanently unmergeable, and the only exit the
contract offers is a human.
PR #434 reached that
state on 2026-08-10 and is sitting in it now. This asks to remove the human from that exit and give
it to Codex.

The deadlock, exactly

Three sentences in AGENTS.md §Review are individually reasonable and jointly unsatisfiable:

line rule
AGENTS.md:188 "CodeRabbit is the last gate: at least one review with no actionable comments"
AGENTS.md:211 "Two completed reviews per METERED provider, then stop."
AGENTS.md:221 "If a third pass would be needed, hand the PR to the maintainer with a comment saying why."

If both permitted metered reviews find something — even something small, even something immediately
fixed — the zero-finding review the gate demands requires a third read, which the cap forbids.
The PR is then stuck behind a human, forever, with nothing actually wrong with it.

It is not hypothetical; here is the reproduction

PR #434, head 02fb081a834a566f3b563bdfcb6302b5f545bd41:

  • 15/15 required checks green, mergeStateStatus: CLEAN, every commit signed.
  • Codex reviewed five times and converged with nothing blocking outstanding.
  • CodeRabbit review 1 at 454eebfActionable comments posted: 2. Both fixed. On the replies
    CodeRabbit itself confirmed one and withdrew the other ("I withdraw the finding").
  • CodeRabbit review 2 at 359675eActionable comments posted: 1, a 🟡 Minor. Fixed in
    02fb081, which is a three-line comment edit — squarely on the non-material exception list.
  • All three review threads resolved. Nothing is outstanding.

Two completed metered reviews, every finding disposed, and the PR cannot merge. The cap is spent, so
no third review may be asked. AGENTS.md says hand it to a human.

The rule prices diligence as failure

This is the part worth fixing rather than patching. The current gate bundles two different questions
were the findings dealt with? and did a provider read the head being merged? — into one test,
and assigns both to the most expensive provider in the lane. The arithmetic that falls out is
backwards: a review that finds three real problems costs two credits to close, while a review that
finds nothing costs one. A PR is punished for having been reviewed usefully. A provider that
finds something and has it fixed is a better outcome than one that finds nothing, and the contract
currently treats it as the worse one.

Two more places the same gap shows

  • claim.py doctor already has an unarmed section — "open pull requests that are finished and
    that nothing will merge"
    — so the tooling detects this class. But doctor reports and never
    writes, and AGENTS.md says every remedy it implies is maintainer authority: "post a marker,
    promote a label, arm someone else's merge."
    Detection without an agent-reachable remedy is what
    produces a stranded PR. (docs: make the GitHub wiki a lightweight index of the docs site #434 was inside doctor's 45-minute grace at the time of writing and had
    not yet surfaced there.)
  • ADR-0064 §Consequences already listed "automatic resumption of a stranded pull request" among
    what the cut gave up, on the evidence that the empty refs/lane-advances/ namespace showed it had
    never occurred. It has now occurred.

There is already a precedent for the fix in the same section

AGENTS.md:183–187 handles an exhausted metered provider correctly and says so in words:
"if the seat is empty record 'Greptile: no credits this month' and move on; exhaustion never
blocks
."
A spent CodeRabbit cap is the same situation, and today it does block. The change asked
for here makes CodeRabbit consistent with Greptile rather than inventing a new principle.

What should change

When the metered cap is genuinely spent and every finding is genuinely disposed, one Codex review
of the final head closes the gate.
Codex is already the right instrument and the contract already
says why: it is "unmetered and so uncapped" (AGENTS.md:165) and "not optional: it is what makes
the metered providers affordable"
(:168). It can be re-run until it converges at no cost. Nothing
about the safety property changes — AGENTS.md:156, "You are never the only reviewer of your own
diff"
, still holds, because Codex is an external provider and author-side output still satisfies
nothing.

The branch must be narrow enough that it cannot become the preferred path. The design constraint is
that an agent must never be able to burn two metered asks on purpose to unlock a cheaper route.

Acceptance criteria

  • AGENTS.md §Review gains an explicit, mechanical branch: when the metered cap is spent, every
    finding from every completed metered review is fixed / deferred-and-tracked / dropped-sub-floor
    with all threads resolved, and no new scope has landed since, then one Codex review of
    the final head with nothing actionable outstanding closes the gate
    .
  • The branch is unreachable by choice. At minimum: the two completed metered reviews must
    sit at different commit_ids, with the commits between them answering the first review's
    findings — so a deliberately-burned second ask does not open it; and if either completed review
    came back clean and its evidence still stands, that review is the gate and the branch never
    opens. Reachability must be decidable from the PR record, never from an agent's account of its
    own motives.
  • AGENTS.md:221's "hand the PR to the maintainer" escalation is deleted, not merely
    supplemented — leaving it in place preserves the human gate this issue exists to remove.
  • Clearing the gate is not authority to merge. The two must stay separate and it must be
    stated as its own rule, not buried in a tail clause.
  • --match-head-commit still names a head some review actually read — say which, given the
    closing read is now Codex's.
  • AGENTS.md:224's "Two is the ceiling every provider shares" is scoped to metered
    providers, resolving a pre-existing contradiction with :212–213 ("Codex is uncapped") that
    would otherwise be cited to refuse the closing read.
  • Every drift surface lands in the same PR. All six restate the retired rule and all six are
    on the material list (AGENTS.md:177–182):
    .agents/skills/tether-worker/SKILL.md:85;
    .agents/skills/tether-worker/agents/openai.yaml:4 ("after the mandatory CodeRabbit gate");
    CONTRIBUTING.md:15, 262, 288, 316 (the lane restatement), :408 (the cap) and :416
    (its own copy of the maintainer escalation — the escalation lives in exactly two files, this
    and AGENTS.md:221, and both must go);
    .github/pull_request_template.md:38, 61;
    docs/PRD.md:1065, 1123;
    .greptile/README.md:101 ("CodeRabbit, not Greptile, is the gate").
    openai.yaml is the highest-risk omission: four lines, read by no test, and injected as the
    Codex lane's default prompt — forgetting it silently instructs every Codex-lane worker
    under the deleted contract.
  • .github/pull_request_template.md gains somewhere to record the new evidence: line 38's
    CodeRabbit options need a "cap spent, closed by Codex" verdict, and the Codex line needs a
    closing-read field. A rule with no field in the template is a rule that will not be evidenced.
  • An ADR lands in the same PR. This supersedes part of a decision ADR-0064 made explicitly
    ("one CodeRabbit review with no actionable comments is the last gate before merge",
    ADR-0064:311–312), so it cannot be a silent edit. Reserve the number with
    <py> .agents/bin/claim.py reserve-adrnever by reading docs/adr/ — and amend
    ADR-0064's Status bullet on one line, since scripts/gen_adr_index.py extracts that
    field with a single-line pattern.
  • pre-commit run --all-files and the test matrix stay green. The mechanical guards in
    tests/test_agent_contract_is_runnable.py must still pass — in particular _SHA_DEFINED
    (:108), the --match-head-commit presence check (:111) and the arming-page floor (:836).

Scope and non-goals

In scope: AGENTS.md §Review and the six drift surfaces above; the PR template's evidence
fields; one new ADR plus a one-line Status amendment to ADR-0064.

Out of scope, deliberately:

  • Any change to what counts as serious (AGENTS.md:197) or to the sub-floor drop rule (:203).
  • Any change to the Greptile budget controls or .greptile/config.json.
  • Any weakening of AGENTS.md:156 — the authoring agent is still never the only reviewer, and
    author-side or local output still satisfies nothing.
  • Reframing the gate for every PR. A rewrite that replaces "no actionable comments" with "every
    finding disposed" as the primary rule was considered and rejected: it swaps a third-party
    attestation for a self-attestation on the default path, and it collides with CONTRIBUTING.md's
    rule that a credit-funded review must not be answerable entirely by deferral. The fix is a narrow
    branch that opens only when the cap is spent, not a new primary rule.
  • Restoring any of the machinery ADR-0064 removed — no round ledger, no label state machine, no
    launcher. The branch must be prose the agent evaluates, consistent with ADR-0064's finding that
    review state has no concurrency content.

Size estimate

M — up to 400 added lines

Risk level

high — the lane, and a Greptile credit is worth spending if the seat has budget

This changes what the merge gate requires. AGENTS.md puts rule-stating files on the material list
for exactly this reason, so the implementing PR re-arms its own review and is read under the old
rule.

Does this need an architecture decision record?

yes — a decision gets made here

Dependencies / blockers

none

Sequencing note rather than a blocker: the implementing PR cannot clear itself. AGENTS.md:6
says only agent instructions on the default branch govern and unmerged edits are inert, and
:177–182 makes a rule-stating push material. So the fix PR is judged under the current gate and
needs a clean CodeRabbit review of its own; the new branch only becomes available to other PRs once
it is on main. #434 is therefore unblocked after this merges, not by it being written.

Execution autonomy

agent-can-do-alone

Authorization, recorded because ADR-0064 requires it. AGENTS.md:208–210 and ADR-0064:334–336
say agent-layer paths accept bug fixes and safety fixes only, and that "a capability change requires
a maintainer-opened issue and may not originate in a review finding."
This is a capability change
on those paths. It originates from the maintainer, in session, on 2026-08-10, verbatim:

Raise an issue using the repo's template about the situation that didn't allow you to merge #434,
we want to get rid of human gates for merge. In a situation like this, codex should be able to
clear the gate.

This issue is that maintainer-opened issue. It did not originate in a review finding: it
originates in a merge that could not happen. Note also that #434 is explicitly not the issue for
this work — absorbing it there would have been the scope breach AGENTS.md §Outcome and authority
forbids.

Related work / file overlap

#434 is the reproduction and is blocked pending this. No file overlap with it: #434 touches
wiki/Home.md and .github/workflows/wiki.yml only.

#261 is related and must not be tripped over. It asks to re-cover
.github/pull_request_template.md's review-evidence fields with a structural test, after the last
review-gate rewrite replaced them. This issue edits those same fields again. Whoever takes #261
should write the assertion against the fields as they stand after this lands, and whoever takes
this should not add a prose-pinning test — pinning wording is exactly what retired
tests/test_review_policy.py in #260 and made #261 necessary.

Supersedes part of ADR-0064; ADR-0062 and ADR-0057 also state the retired rule but are historical
records and are not edited. Closed #399 ("the mandatory CodeRabbit gate and the…") is prior art
on the same bullet and is superseded by ADR-0064, not by this.

Duplicate search run against open and closed issues for review gate, CodeRabbit, deadlock,
stranded and last gate. Nothing proposes removing the escalation or letting an unmetered provider
close the gate; the nearest hits are #261 (test coverage, related above) and the closed agent-layer
fixes #399 / #409 / #415, all of which tightened the old gate rather than providing an exit from it.

Target area

CI / repository governance

Milestone

none / cross-cutting

Required safety attestations

  • I searched existing issues and this is not a duplicate.
  • I am not reporting a vulnerability publicly; security reports use the private advisory flow.
  • I included no secrets or private, raw, unlicensed, user, or lab data.

Metadata

Metadata

Assignees

No one assigned

    Labels

    priority:P1Highrisk:highDeclared risk: high. Routes nothing - every PR walks the same lane. May only increase.size:MDiff budget <=400 changed lines (excl. lockfiles/generated)status:in-progressIn progresstype:ciCI/CD

    Projects

    Status
    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions