Repository navigation
fix(claim-check): refuse a LIVE pull request, not a merged predecessor (CLOUD-520) - #509
Conversation
CLOUD-369 The second matrix is bought unconditionally: a successor is admitted behind a holder whose CI has not answered, and whose base is known to conflict
Why Rescoped 2026-08-13, the second time. The first rescope (2026-08-12) cut this issue down to what the landing lease did not do; PR #369 merged that day and the board moved it to In Review. Most of that scope did land — the warm queue and speculative linearization, This issue is labelled landed while its own §7 obligation is unmet. That is the shape CLOUD-472 exists to gate: the Ready block is checked for shape, the code for correctness, and nothing compares the two. Verified against
Why the first two cost money rather than tidiness. The second matrix is a favourable bet because it is conditioned: a holder that is green and holding the lease will almost certainly fast-forward, so the successor's run overlaps a merge that is about to happen. Unconditioned, the same run is bought behind a holder whose CI has not answered and may come back red — in which case the merge never happens, the successor's run is voided, and the mechanism has spent an extra matrix to save nothing. That is the waste this issue exists to remove, reappearing inside its own fix. The conflict arm is the same argument reached from the other side and measured on 2026-08-13 (comment above): the holder landed CLOUD-515, the admitted successor's run on The third is diagnosis: Acceptance
Explicitly rejected
Refinement — Ready Refinement gate: Definition of Ready & Done. This body carries only its specializations.
Done
CLOUD-520 `claim-check`'s has-pr reads a MERGED predecessor as a live competitor, so an issue released back to Todo cannot be re-pulled
Measured 2026-08-13, pulling CLOUD-479. CLOUD-479 is
PR #376 is Why the rule cannot tell
The URL carries no state. The rule's stated purpose is narrower than what it implements — its header says it "catches one that published before the column moved", which is a claim about an open PR. A merged one is the opposite signal: it is evidence that work finished, not that it is in flight. This is The tension this sits in, stated rather than hand-waved
So the fix is on the payload side, not the network side: the caller already fetches Cost, so it is not overstatedOne issue, once. The workaround is to take it over deliberately — which the gate's own message invites — but there is no bypass for it: Not this issue's subject. The receipt being branch-keyed and going stale after a merge is CLOUD-516. Whether a merged PR should complete its issue at all is CLOUD-468. This is only about The questions that blocked Ready, answeredBoth are settled in the Ready block below: the payload does not expose PR state (measured), and the caller supplies it — the Filed while pulling CLOUD-479 rather than fixed in passing: narrowing this rule needs a decision about the payload contract that CLOUD-431's author is better placed to make, and widening Refinement — Ready (narrow Refinement gate: Definition of Ready & Done. This body carries only specializations. Open question 1 is answered, with evidence. Linear's Open question 2 is answered by the precedent one gate over.
|
|
Warning Review limit reached
Next review available in: 34 minutes Limit details: You’ve used all 3 included reviews currently available. Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits within each organization. For paid Pro and Pro+ reviews, CodeRabbit uses a developer's included PR review attempts over the past 7 days to set the current hourly allowance. At typical activity levels, the full plan allowance applies. Higher sustained activity can lower the allowance until earlier attempts leave the 7-day window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (22)
Comment |
…mmand Refs: CLOUD-121
Refs: CLOUD-121
…s the length lint Refs: CLOUD-121
… was out The rebase onto 228c29d crossed four landed changes, each resolved rather than absorbed: - `-y --yes` became a GLOBAL flag when CLOUD-46 discharged CLOUD-42's G11, so this branch's per-command copy is dropped; `capture prune` reads the global. The destructive-row test is main's, plus the anti-vacuity counter this branch added — its predecessor asserted the set was EMPTY, so emptiness was the pass, and the successor asserts a property OF the set, where the same emptiness would be vacuous. - `tests/pointer_only.rs`'s census requires every leaf verb to declare what it may emit. `capture show` is `Passthrough`: handing back the bytes the caller's own command wrote is the whole job, since the other way to see them is running that command again. `list` and `prune` are `PointerOnly`. The row states what the sweep does NOT exercise — its corpus holds no capture — rather than letting a 0-against-0 count read as proof. - The `run` dispatcher was one line over the length lint once it gained two arms; the `exec` arm becomes `run_exec`. - Completions and man pages are derived, so they are regenerated rather than hand-merged. BREAKING CHANGE: the library API changes in three ways, all introduced by the capture-navigation work earlier on this branch and declared here for the range. `exec::run_with` and `exec::run_in_with` take an `exec::Mode` argument, so a caller choosing between teeing the child's streams and capturing them must say which it wants. There is no default parameter to fall back on deliberately: `--capture-only` changes what every wrapped command's caller sees, and a signature that let it be selected implicitly is the shape CLOUD-285's transparency contract exists to prevent. `Command::Exec` gains a `capture_only` field and the `Command` enum gains a `Capture` variant, both of which a caller matching exhaustively must handle. Refs: CLOUD-121
CLOUD-619 landed the helper and the census that enforces it while this branch was out. Setting `XDG_DATA_HOME` by hand redirects nothing on Windows — `etcetera` reads the roaming known folder there — so the child writes to the real user profile and every assertion about the capture store is about someone else's. Refs: CLOUD-121
473a7f5 to
148b685
Compare
`has-pr`'s stated purpose is narrower than what it implemented. Its own header says it "catches one that published before the column moved" — a claim about an OPEN pull request. A merged one is the opposite signal: evidence that work finished, not that it is in flight. So an issue released back to Todo could never be pulled again. Measured on CLOUD-479 — Todo, unassigned, its body explicitly inviting the next taker — refused on PR #376, which had merged the day before. The same shape blocked CLOUD-369 today, whose two attached PRs are both merged. The state cannot be looked up here, and that is the constraint rather than an oversight: this gate is a pure function of stdin — no tracker credential, no network, so it cannot hang or rate-limit and its suite runs unconditionally — and the tracker's attachment objects carry `id`, `title`, `subtitle` and `url`, with no state at all. So the caller supplies it, the shape `claimed-keys` already uses for the facts it cannot fetch. ABSENT REFUSES, and that default is what makes this safe: a caller supplying nothing gets exactly today's behaviour, so the narrowing can only ever turn a false refusal into a pull, never a real competitor into a silent pass. Open refuses, malformed refuses — a parse failure must not become a pass — and only an explicit merged/closed reading stands down. The refusal now names the remedy, since the alternative a caller finds on its own is skipping the gate entirely. Nine cases, and the load-bearing ones are the pairs: a merged PR is pullable while the SAME payload without the state still refuses, and an open PR still refuses — a narrowing that also stopped refusing live competitors would pass every positive case and be worthless. The `#MUTANT` row took two corrections, both worth naming. It first reported `names-no-case`: `mutant` passes the case name to `bats --filter`, which reads it as a REGEX, so a name containing `(a)` matched nothing and proved nothing. Renamed to `clause a`. It then SURVIVED: the row neutered the `merged` boolean while the `state` string check still filtered merged PRs out, so the case passed on deliberately broken code. It now neuters the state comparison, and 42 of 42 declared mutations are caught. Refs: CLOUD-520
148b685 to
983ff63
Compare
|
|
/fast-forward |



Why
has-pr's stated purpose is narrower than what it implemented. Its own header says it "catches one that published before the column moved" — a claim about an open pull request. A merged one is the opposite signal: evidence that work finished, not that it is in flight.So an issue released back to Todo could never be pulled again. Measured on CLOUD-479 — Todo, unassigned, its body explicitly inviting the next taker — refused on PR #376, which had merged the day before. The same shape blocked CLOUD-369 today, whose two attached PRs are both merged.
The constraint that shapes the fix
The state cannot be looked up here. This gate is a pure function of stdin — no tracker credential, no network, so it cannot hang or rate-limit and its suite runs unconditionally — and the tracker's attachment objects carry
id,title,subtitleandurl, with no state at all (measured on CLOUD-369's payload: two attachments, both merged, neither saying so).So the caller supplies it, the shape
claimed-keysalready uses for facts about a PR this checkout did not author. That preserves agents-fetch-gates-decide rather than trading it away, and it is why the rule is not simply demoted to a warning — one that can still refuse a live competitor is worth more than one that only advises.Absent refuses, and that default is what makes this safe. A caller supplying nothing gets exactly today's behaviour, so the narrowing can only ever turn a false refusal into a pull — never a real competitor into a silent pass. Open refuses, malformed refuses (a parse failure must not become a pass), and only an explicit merged/closed reading stands down. The refusal now names the remedy, since the alternative a caller finds on its own is skipping the gate entirely.
Tests
Nine cases, 44/44 in the suite. The load-bearing ones are the pairs:
The mutation row took two corrections, both worth naming
It first reported
names-no-case:mutantpasses the declared case name tobats --filter, which reads it as a regex, so a name containing(a)matched nothing and the row proved nothing. Renamed toclause a.It then SURVIVED: the row neutered the
mergedboolean while thestatestring check still filtered merged PRs out, so the case passed on deliberately broken code. It now neuters the state comparison.mise run mutant: 42 of 42 declared mutations caught.That regex trap is worth knowing beyond this PR — clause-reference names like
(b1)are the natural way to bind a test to a §7 obligation, and they are exactly the names that silently fail to match. Recorded on CLOUD-472.Closes CLOUD-520