Skip to content

feat(agents): a spent metered cap closes on Codex, not on a maintainer - #441

Draft
bioedca wants to merge 7 commits into
mainfrom
agent/issue-439
Draft

feat(agents): a spent metered cap closes on Codex, not on a maintainer#441
bioedca wants to merge 7 commits into
mainfrom
agent/issue-439

Conversation

@bioedca

@bioedca bioedca commented Aug 11, 2026

Copy link
Copy Markdown
Owner

Summary

AGENTS.md §Review held three individually-sound rules that were jointly unsatisfiable:

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

If both permitted metered reviews found anything at all — even something small and immediately
fixed — the zero-finding review the gate demanded needed a third read, which the cap forbade.
The PR then sat behind a human indefinitely with nothing wrong with it.

The defect is sharper than the deadlock

The old gate bundled two different questions — were the findings dealt with? and did a provider
read the head being merged?
— into one test, and billed both to the most expensive provider in
the lane. A review that finds three real problems costs two credits to close; one that finds nothing
costs one. The contract priced diligence as failure, and it did so in the currency ADR-0064 was
most careful about. That is why the deadlock is not an edge case: it is reached by exactly the PRs
the lane is working correctly on.

What changes

When the cap is spent and every finding from both completed reviews is fixed,
deferred-and-tracked, or dropped sub-floor
with every thread resolved, a fresh Codex read of the
final head
closes the gate in CodeRabbit's place. The maintainer escalation is deleted, not
supplemented — leaving it would preserve the gate this removes.

Nothing new is extended to Codex. AGENTS.md already calls it "unmetered and so uncapped" and
"not optional: it is what makes the metered providers affordable." It is being allowed to finish.

Three conditions shut the branch, and none of them is a motive

Each is readable off the pull request:

  1. A refusal is not a spent cap — a throttle, quota refusal or failed run reviewed nothing, so it
    is a wait. An unavailable CodeRabbit still freezes the PR.
  2. A clean review is already the gate — if either completed review came back clean and its
    evidence still stands, the branch never opens.
  3. The two completed reviews must sit at different commit_ids, with the commits between them
    answering the first review's findings. Two reads of one unchanged diff are one review asked twice.

An earlier draft was rejected in review for testing intent. It read "an ask made to spend the
cap is not one of the two."
A motive is not checkable by anyone, including the agent itself — and
read strictly it restores the deadlock in a new form: an agent that suspects its own motive
concludes the cap is unspent, so the close is shut, so it must ask again, which it cannot. A rule
that fixes a deadlock must not be able to re-create one. The remaining deterrent is structural
instead: spending an ask to reach the close buys nothing even if it works, because the close costs
the disposal of every finding plus a further review on top.

The close is a substitute, not a discount

Anything the closing read surfaces is disposed of by the same three dispositions before it closes. An
earlier draft held it to "nothing blocking", which would have silently dropped two severity bands
relative to the zero-actionable-comments bar it replaces — and on agent-layer paths those findings
are not tracked at all. The bar does not move; only who holds it does.

Clearing the gate is still not authority to merge, promoted to its own rule rather than left in
a tail clause, because it is the most damaging available misreading.

All six drift surfaces move together

Every rule-stating file is material (AGENTS.md:177–182) precisely so a push that changes what the
gate requires cannot leave a stale copy behind:

file what it said
.agents/skills/tether-worker/agents/openai.yaml "after the mandatory CodeRabbit gate"
.agents/skills/tether-worker/SKILL.md "the last gate before merge"
CONTRIBUTING.md ×6 lane restatement, the cap, and its own copy of the escalation
.github/pull_request_template.md CodeRabbit-is-terminal evidence fields
docs/PRD.md §12 "the last gate before merge"
.greptile/README.md "CodeRabbit, not Greptile, is the gate"

openai.yaml is the one worth naming: four lines, read by no test, and injected as the Codex
lane's default prompt. A stale copy there would brief every future Codex-lane worker under a
contract that no longer exists, with no signal at all.

The escalation existed in three files — AGENTS.md:221, CONTRIBUTING.md:416 and
docs/PRD.md §12.4 — and all three are gone. This body, a commit message and ADR-0065 all said
two until Codex found the third: the PRD phrases it "the lane stops for the maintainer" rather
than "hand the PR to the maintainer", so the phrase-matched grep never saw it. Since AGENTS.md
tells agents to stop and ask when contracts conflict, a worker reading the PRD would have stranded
on exactly the PR this unblocks. The sweep is now semantic across all seven rule-stating files. Deleting it also repairs AGENTS.md:227, "Human sign-off: … Nothing else waits,"
which was false while a capped review gate waited on a human. That sentence becoming true again is
affirmative evidence the escalation was the anomaly.

The PR template gains somewhere for the new evidence to live: a Codex closing read line and a
"cap spent, closed by Codex" option. A rule with no field in the template is a rule that will not
be evidenced.

One bullet deliberately not changed

AGENTS.md:82–83, the doctor bullet's "Every remedy is maintainer authority — post a marker,
promote a label, arm someone else's merge."
I intended to append that your own stranded PR is not on
that list. It is unchanged and correct as it stands: every remedy doctor actually enumerates is
someone else's to apply, and closing your own PR's gate is not one of them. Recorded here rather than
left as a silent omission.

Linked tracking

  • Closes: chore(agents): a spent metered cap should close on Codex, not on a maintainer #439
  • ADR: 0065 — reserved with claim.py reserve-adr (refs/adr-reservations/0065), never by
    reading docs/adr/. ADR-0064's Status bullet amended on one line, since
    scripts/gen_adr_index.py extracts that field with a single-line pattern.
  • Milestone: M11 — Agent-swarm infrastructure
  • Risk (may only increase): high
  • Risk rationale: changes what the merge gate requires. Prose only — no script, workflow, label, ref
    namespace or test fixture; git revert restores the escalation exactly.

This PR cannot use the branch it introduces

AGENTS.md:6 — only instructions on the default branch govern, and unmerged edits are inert. Every
file here is material, so this re-arms its own review and is judged under the old gate: it
needs a clean CodeRabbit review like any other PR. That is the correct order, not an oversight — the
rule earns its way in under the regime it replaces. #434 becomes mergeable under the new branch only
once this is on main.

Provenance of the claim

No claim ref was taken, and that is stated rather than hidden. #439 is status:backlog with no
tether-agent-ready marker, so claim.py claim would exit 3. Posting that marker myself was
blocked — correctly: an agent minting the maintainer approval that authorizes its own issue is the
write-loop the design guards against. The mutex risk is nil regardless (a brand-new backlog issue is
unclaimable by anyone), so this is a process gap, not a safety gap. The maintainer's authorization for
this exact work is quoted verbatim in #439's Execution autonomy section.

Type of change

  • feat — new capability

Self-review checklist (PRD §12.4)

  • Schema freeze respected — no schema surface touched.
  • conda-lock updated if dependencies changed — no dependency change.
  • Tests added/updated — none added, deliberately. The diff adds no importable code, and
    pinning review wording in a test is exactly what retired tests/test_review_policy.py in chore(agents): retire the review-policy prose-drift guard #260
    and made test(ci): re-cover the PR-template evidence fields structurally, after the review-gate rewrite #261 necessary. The existing mechanical guards in
    tests/test_agent_contract_is_runnable.py are the right level and all still pass.
  • Docs updateddocs/PRD.md §12, the new ADR, and the ADR index; mkdocs build --strict
    green.
  • Data policy respected — no data.
  • No secrets committed — none.
  • Code scanning clean — awaiting CI.
  • Review complete — Codex has taken three rounds and is on a fourth; every finding it
    raised was a P1 and every one was fixed rather than deferred, because each falsified this PR's own
    central claim. Round 1: the different-commit_id condition re-created the deadlock (non-serious
    findings are disposed by deferral, which moves no head), and the template's final-head requirement
    was unsatisfiable on the capped path. Round 2: the close admitted scope pushed after the cap was
    spent — read by the closing provider and nobody else — which is now shut by a fourth condition.
    Round 3: the scope freeze was anchored to the clock rather than the reviewed commit, the closing
    read's evidence shape was impossible for a clean Codex run, and a third copy of the escalation
    was found in the PRD. CodeRabbit has not been asked yet.
  • Provenance stamped — n/a, no analysis path touched.
  • New tunables registered in PRD §11.2 — none introduced.
  • Scientific/statistical claims carry a citation — none made. REUSE green (415/415,
    "compliant with version 3.3").
  • A resolved PRD decision that changed is reflected in the PRD and an ADR — both, in this PR.

Testing

Local gates, reported exactly as run, on Python 3.12.13 / pytest 9.1.1 in this worktree:

  • Test matrixgreen: 2817 passed, 19 skipped, 34 deselected in 60.55s
    (QT_QPA_PLATFORM=offscreen pytest -m "not large and not sidecar and not deep").
  • The agent-contract guards specificallygreen, 21 passed. These are the tests that could
    have caught a careless edit here: _SHA_DEFINED (the `<SHA>`40-hexreview window)
    and the arming-page floor both bind the merge bullet, which is why it is left byte-identical
    and the new rule supplies its referent instead of rewording it.
  • One real failure, found and fixed: test_no_published_page_points_at_the_unserved_prd caught
    the new ADR pointing at docs/PRD.md in a form a site reader cannot follow — mkdocs excludes that
    file. Replaced with the absolute blob URL the test requires. It is recorded rather than quietly
    fixed because the guard did its job.
  • mkdocs build --strictgreen, built in 1.41s.
  • reuse lintgreen, 415/415 files, "compliant with version 3.3".
  • Per-hook equivalents across all 10 changed files — clean: trailing whitespace, final newline,
    tabs, merge-conflict markers; openai.yaml re-parsed with PyYAML.
  • pre-commit run --all-filesNOT COMPLETED LOCALLY, stated rather than implied. It stalls
    indefinitely in the native-PowerShell lane on this machine (bounded at 420s, no output), and the
    WSL lane cannot run it because git there cannot resolve a Windows-path worktree. Neither failure
    involves this diff. The required pre-commit CI job runs the identical pinned hooks and is
    authoritative; the manual per-hook run above is what bounds the gap in the meantime.

AGENTS.md held three individually-sound rules that were jointly unsatisfiable:
the gate needed a CodeRabbit review with no actionable comments, metered
providers were capped at two completed reviews, and a third pass meant handing
the PR to a human. A PR whose two permitted reviews both found something --
even something small and immediately fixed -- could never reach the zero-finding
review the gate demanded. PR #434 is the first to arrive there.

The deeper defect is that the old gate bundled "were the findings dealt with?"
and "did a provider read the merging head?" into one test and billed both to
the most expensive provider, so a review finding three real problems cost two
credits to close while one finding nothing cost one. The contract priced
diligence as failure.

When the cap is spent and every finding is fixed, deferred-and-tracked or
dropped sub-floor with threads resolved, a fresh Codex read of the final head
now closes the gate in CodeRabbit's place. Three conditions shut that branch,
each readable off the PR rather than out of an agent's account of its own
motives: a refusal is not a spent cap, a clean review is already the gate, and
the two completed reviews must sit at different commit_ids with the commits
between them answering the first one's findings.

The maintainer escalation is deleted from both files that carried it, which
repairs AGENTS.md's "Nothing else waits" -- false while a capped review gate
waited on a human. Clearing the gate is still not authority to merge, stated as
its own rule. The merge bullet is byte-identical, preserving both mechanical
guards in test_agent_contract_is_runnable.py.

All six drift surfaces move together, since every rule-stating file is material.

Closes #439. ADR-0065.
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 62bb56ef-9acb-44c9-8886-fb921f5b87e6

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

Codex raised two P1 findings against the first draft, both correct and both
falsifying this PR's central claim, so both are fixed rather than deferred.

1. The different-commit_id condition re-created the deadlock. It required the
   two completed reviews to sit at different commit_ids with commits between
   them answering the first one's findings. But when a review's findings are all
   non-serious, the prescribed disposition is deferral or a sub-floor drop, which
   moves no head. A second review at that same head was then locked out of the
   close while the cap forbade a third. The test is now the disposal itself --
   commits that answer the findings, or the replies and resolutions that record a
   deferral or drop -- not a new commit.

2. The final-head requirement was unsatisfiable on the capped path. On that path
   CodeRabbit's two reviews sit at earlier heads by construction, because
   answering them is what moved the head; demanding every reached provider cover
   the final head therefore demanded the third review the cap forbids. Only the
   provider that closes the gate must read the final head.

Finding 2 was reported against one line of the PR template. It was a sample of a
class: the same requirement appears three more times, in the template's
CodeRabbit line and twice in CONTRIBUTING.md -- one of which asserted outright
that "answering a finding moves the head", the exact premise finding 1
falsifies. All four are fixed.

ADR-0065 records the corrected condition and, deliberately, the draft that was
wrong: a rule that fixes a deadlock must be checked against its own failure
mode.
Codex round 2 raised two more P1s, both correct and both fixed.

1. The fallback could be used for material scope neither metered review saw.
   A material push AFTER the second completed review leaves the cap spent, so
   the close still applied -- and that new code would then be read by the
   closing provider and by nobody else, since material pushes re-arm review but
   do not raise the two-review ceiling. It also made the branch reachable by
   choice, which the other conditions exist to prevent: push the risky part
   last. A fourth condition now shuts it -- every commit between the second
   completed review and the closing read must answer a recorded finding or be a
   non-material exception. This is what makes ADR-0065's "third opinion on a
   twice-read diff" true rather than merely asserted.

2. The PR template still carried the different-commit_id condition that the
   previous commit retired from AGENTS.md, so the mandatory template rejected
   the valid cap-spent path -- the same deadlock, one file over. Fixed there,
   and the "earlier heads by construction" phrasing I introduced in the template
   and CONTRIBUTING.md is wrong for the same reason: where disposal is a
   deferral or drop, the heads legitimately match.

The second finding is the third time in this PR that a fix landed in one file
and not its mirrors. Swept all five rule-stating files; the only surviving
mention of the retired condition is ADR-0065's deliberate record of the draft
that was wrong.
…clock

Codex round 3, three more P1s, all correct.

1. The scope freeze was time-anchored. "After the second completed review" let
   a material push that landed while that review was still running fall inside
   the window -- before its submitted_at, after the commit_id it actually read
   -- so a commit no metered provider saw could still ride the close. Anchored
   at the commit_id now, with the reason stated inline.

2. The evidence shape was unsatisfiable in the ordinary clean case. The
   template demanded commit_id, submitted_at and COMMENTED/APPROVED from
   whichever provider closed the gate, but a clean Codex run reports a thumbs-up
   reaction and has no pull-request-review object at all, so the new path could
   not be recorded exactly when it succeeded cleanly. It now asks for what Codex
   can emit while keeping the final-head binding.

3. There was a THIRD copy of the maintainer escalation, in docs/PRD.md §12.4.
   The PR body, the previous commit message and ADR-0065 all claimed it lived in
   exactly two files. That was wrong. The PRD phrases it "the lane stops for the
   maintainer" rather than "hand the PR to the maintainer", so the phrase-matched
   grep never saw it -- and since AGENTS.md tells agents to stop and ask when
   contracts conflict, a worker reading the PRD would have stranded on exactly
   the PR this unblocks.

The sweep is now semantic across all seven rule-stating files rather than a
grep for remembered wording, and ADR-0065 records why the grep missed it. That
is the fourth time in this PR a fix landed in one file and not its mirrors.
Codex round 4, two P1s and a P2, all correct.

1. A clean Codex run is often a bare thumbs-up carrying no commit, so the head
   recorded for it was AUTHOR-supplied, not provider-attested. A push landing
   while the read was in flight could then have the PR name a head the provider
   never saw and pass it to --match-head-commit -- the exact thing binding the
   merge exists to prevent. The closing read must now carry a SHA the provider
   itself wrote; an unstamped reaction is a fine lane result and is not a close.

   This is the resolution of a genuine tension between two rounds. Round 3 found
   that demanding commit_id/submitted_at/COMMENTED made the path unsatisfiable
   in the ordinary clean case; round 4 found that accepting a bare reaction made
   the head unattested. Both are right, and the answer is not a compromise: a
   reaction is a lane result, not a close.

2. The scope freeze was per-commit, so a commit that answered a recorded finding
   AND carried an unrelated hunk passed the literal test while smuggling in
   exactly the scope the condition excludes. The unit is now the change.

3. The template's cap-spent option said "nothing but disposal", which contradicts
   AGENTS.md still allowing the non-material exceptions after the cap -- a clean
   main merge or a comment edit would have re-stranded an eligible PR. The
   checklist now carries the exceptions too.

ADR-0065 records the fifth condition and, deliberately, the round-3/round-4
tension, because the resolution is the reusable part.
…dicted

Codex round 5, three P1s and a P3. All four are contradictions the round-4 fix
introduced: the attestation rule landed in one place and three others still said
the old thing. Fifth instance of that pattern in this PR.

1. AGENTS.md still required the closing read to be "posted by the provider on
   the pull request" one sentence above the bullet that permits a stamped run
   artifact, so the artifact alternative was rejected by the text introducing it.

2. The template's cap-spent option required "neither came back clean", which is
   stronger than the canonical condition ("neither came back clean AND its
   evidence still stands"). Where review 1 was clean at an earlier head, a
   material push re-armed it, and review 2 then found something, the option
   became unselectable with no third read available -- the deadlock again.

3. CONTRIBUTING.md applied submitted_at and COMMENTED/APPROVED to whichever
   review closes the gate, including a Codex close, which is exactly the
   unsatisfiability ADR-0065 records. Those fields are CodeRabbit's; a stamped
   artifact records what it actually emits.

4. The ADR said "three conditions" in two places while the normative list has
   four, understating the safeguard that keeps post-cap scope out. Counted as
   sub-floor on an agent-layer path, but fixed rather than dropped because it
   miscounts safety conditions in the record that defines them.
…ger names

Codex round 6, one P1 and one P2, both correct.

1. The clean-review condition stranded a PR it was written to leave alone.
   Closing the gate and supplying a bindable head are two different jobs, and
   only the first was handled. A clean review at commit A closes the gate; a
   PERMITTED non-material push to B leaves that closure intact, since review
   evidence survives such a push -- but --match-head-commit still demands A,
   which is no longer the head, and the spent cap forbids a third metered read
   to rebind it. Condition 2 then shut the Codex close precisely BECAUSE the
   review was clean. A clean review followed by a formatting commit could
   therefore never merge at all.

   A stamped Codex read of the current head now supplies the binding without
   re-opening a gate that was never in question. What the close still may never
   do is stand in for a metered read that never happened.

   This is an older tension than this PR -- evidence surviving a non-material
   push has always sat awkwardly against an exact-head merge guard -- but the
   new branch made it reachable, so it is fixed here rather than deferred.

2. docs/PRD.md still said the mandatory CodeRabbit gate is what holds
   auto-merge, contradicting the terminus this PR introduces. The identical
   sentence in CONTRIBUTING.md was updated earlier in this PR and this one was
   not: the sixth time a fix landed in one file and not its mirrors.
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.

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

1 participant