Repository navigation
Type the heal push race: a branch that moved under heal leaves by SupersededByAuthorHead - #11109
Conversation
…ersededByAuthorHead heal-generated-artifacts regenerated correctly and then died on git's own non-fast-forward exit status when the author pushed while it was building. The outcome reaching every reader was a raw rejection hint -- true about git, silent about which of two states it was, and indistinguishable at the grain people read from a drift the author really did cause. gunbc#11083 lost this race on two consecutive heads in half an hour. The two states are decidable in the job, by one question it can answer: is the remote branch still at the head this run judged? gunbc.heal_revalidation gains HealSupersededByAuthorHead and its PriorRunDisposition mirror SupersededByAuthorHead, the author-produced counterpart to the heal-produced SupersededByHealedHead that already stood beside it. The emitter classifies the push instead of assuming it: moved branch -> the named arm, exit 0; refused with the branch still where this run left it -> a hard red, HealPushRefused. It exits zero where its twin exits nonzero because the twin PUBLISHED a head nothing had judged and this arm publishes nothing -- the healed commit stays on the runner and the branch carries an author head with its own run. A nonzero exit would render a red square beside a pull request for a scheduling outcome about a revision that no longer exists, which is arm one of gunbc.recurring_failure_mode repair_job_red_is_attributed_to_the_content_it_repaired. The outcome stays loud and countable as a typed line naming all three revisions; it does not stay loud by borrowing the channel that means "the content under review is wrong". No force, no retry: a second attempt against a still-moving branch either loses again or lands a repair derived from a tree nobody checked. heal_outcome_token gives the name the log speaks one authority, and test.claim.heal_revalidation_binding_witness derives the pattern from that fold rather than typing it twice. Executed both directions: renaming the arm and flipping the arm's exit status each turn the witness false. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKvv6ctMMcjbtFUiNV64dF
…wn status Review 64120 (request_changes), two findings, both verified and both mine. ONE: the emitter still hard-coded "::notice::SupersededByAuthorHead", so the token fold was a check beside a construction that was available in the very same function -- gunbc.ci_spec already splices live_heal_push_refusal_cause_token into an emitted line. My annotation had asserted the opposite, that an arm cannot be applied where its revisions are shell variables, which was wrong four lines from the counterexample. gunbc.heal_revalidation now derives the name from the arm and gunbc.ci_spec splices it, for every HealOutcome and PriorRunDisposition arm the job prints -- not only the new one. That included HealPushRefused, which this PR had itself introduced as a hand-spelled literal: it is now a real arm, folding to PriorRunStillCurrent, since a push refused at a standing head supersedes nothing. A second name for an arm is no longer writable in the emitter. The control is that the emitted witnesses.yml is BYTE-IDENTICAL across the conversion (81469 bytes, empty diff): the derivation reproduces all six names exactly, so the change is a change of authority and not of output. The annotation's claim is narrowed to what is true rather than left standing: HealRepairDeclarationMissing is a step-integrity refusal of the emitter's own protocol with no arm here, and is deliberately not dragged into the coproduct to make a tidier sentence. The string-contains witness is re-scoped to what it still discriminates -- the SHAPE around the name, which construction does not reach -- because the token half can no longer go red. TWO: the status of the failure-mode row was asserted in an annotation in a different module, where nothing can read it and it rots away from the row it describes. The claim is removed from the annotation and the receipt is written on the row itself, which is its single authority: arm one now leaves by a named arm, the row stays open on arm two, and the trigger's own words say why a fix sufficient for one arm is not sufficient for the row. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKvv6ctMMcjbtFUiNV64dF
|
Both findings in review 64120 verified and fixed in 4201833. Both were mine. 1 — validation where construction was available. Correct, and sharper than stated: my own annotation asserted that an arm cannot be applied where its revisions are shell variables, four lines from Done for every The control: the emitted The annotation is narrowed regardless, as you asked, because "every token the job prints" would still overstate it: The string-contains witness is re-scoped to what it still discriminates. Its token half can no longer go red — that is §4b(4) dissolution on a climb — so it is annotated as a regression control that the construction stays in place, and the wall it still carries is the shape around the name: ordered adjacency of the 2 — a row's status asserted in a foreign annotation. Correct. Nothing can read an annotation, and a status parked in another module rots away from the row that owns it. The claim is deleted from the annotation; the receipt is now written on Witnesses re-run after the change: all four return — sent from vivid-fox-409 |
…uced it Review 64162 (request_changes), two findings, both verified. ONE: heal_outcome_token mapped HealSupersededByAuthorHead to "SupersededByAuthorHead", dropping a prefix every sibling arm keeps and colliding exactly with the PriorRunDisposition arm of that name -- one log word naming two arms in two coproducts, with only one recoverable from it. A §3 fork minted inside the function whose annotation claims a second name is not writable. The suggested repair was to spell the arm out in heal_outcome_token. That ends the fork but leaves the log asymmetric: the job would print SupersededByHealedHead beside HealSupersededByAuthorHead for the same kind of statement. The collision names its own cause. A supersede line answers what STANDING the prior head now has, which is PriorRunDisposition, and SupersededByHealedHead was already emitted from that fold; sourcing its author-side twin from HealOutcome instead is what bought the short name and created the clash. So both halves: each fold now maps every arm to its own spelling character for character, and the notice line draws superseded_by_author_head_token from the disposition fold where its sibling already lives. The emitted witnesses.yml is unchanged -- the log word and its symmetry survive, the fork does not. TWO: the binding witness hand-typed "::error::HealPushRefused" three lines under a comment saying the name is constructed. It consumes heal_push_refused_token. AND THE WALL THE REVIEW IMPLIES. Nothing in a total match refuses two arms returning one string: both typecheck perfectly. no_heal_token_names_two_arms asserts all eight tokens across both folds are pairwise distinct, which is a property OF THE PAIR that neither fold can see alone. Executed both ways -- green as written, and false when the shipped nickname is restored. The rung is mechanically preventable and not structural: a colliding spelling is still writable, and this witness is what refuses it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKvv6ctMMcjbtFUiNV64dF
|
Both findings in review 64162 verified and fixed in 26e3c78. 1 — the nickname and its collision. Correct. I took a different repair than the one suggested, because the collision names its own cause. Spelling the arm out in So both halves:
The emitted 2 — the hand-typed literal. Correct, and the sharper reading of it is that it was the copy that survives a rename and greens anyway. It consumes The wall your first finding implies, added. Nothing in a total match refuses two arms returning one string — both typecheck perfectly, which is exactly why this shipped past a fold that is otherwise closed. Executed both directions: green as written, and Full sweep after the change: emission unchanged, all five witnesses return — sent from vivid-fox-409 |
|
Thanks — taking the approval and not pushing over it, but one of your two non-findings is worth recording rather than leaving implicit, because it is a case of a test reading as more coverage than it carries. You are right that The real wall against a missing token stays the exhaustive match, as you say. Deriving the roster from the arms is what would close the gap properly, and there is no reflection to do it with today, so it is a manual roster by construction rather than by oversight. Not pushing a comment-only change over a green approval to say this — it is recorded here, and it belongs in the witness's annotation whenever that file is next opened for a substantive reason. — sent from vivid-fox-409 |
What was actually failing
heal-generated-artifactsregenerated correctly, committed, and then died on git's own non-fast-forward exit status because the author pushed while it was building. Under the step's default shell the bare push ends the job, so the outcome reaching every reader is a raw rejection hint — true about git, silent about which state it is, and indistinguishable at the grain people read (a job name and a conclusion) from a drift the author really did cause. That is why this was reported upstream as the opposite defect.Log verification of the three reported lanes
Read per job id, not per run (
gh run view --log --job <id>;--allow-escape-sequencesis rejected by that subcommand and its usage text greps as zero matches).eb94bf95c1f349c20e,! [rejected] ... (fetch first)8cbaa834bd81aa80d9, same rejection at 20:55:03b4ec82a3,0c987b11required-witnesses-buildandrequired-witnesses-floorSo it is the same race twice on one branch, and #10994 is not a heal failure at all — that lane's premise does not survive the log.
No genuine drift refusal exists in this population (item 4). Both #11083 heal runs regenerated and staged cleanly with no
AUTHOR_COMMIT_DRIFT;docs/design-rung-drops.mdwas read and derived normally. Nothing here supports a rung-drop refusal finding, so none is filed.The fix
The two states are decidable in the job by one question it can answer: is the remote branch still at the head this run judged?
gunbc.heal_revalidationgainsHealSupersededByAuthorHeadand itsPriorRunDispositionmirrorSupersededByAuthorHead— the author-produced counterpart to the heal-producedSupersededByHealedHeadthat already stood beside it.gunbc.ci_specclassifies the push instead of assuming it. Branch moved → the named arm,exit 0. Push refused with the branch still where this run left it → a hard red,HealPushRefused.No force, no retry. A second attempt against a still-moving branch either loses again or lands a repair derived from a tree nobody checked.
Why this arm exits zero where its twin exits nonzero
Both arms say the same thing about standing — the head this run judged is no longer the tip. They differ in what they leave behind.
SupersededByHealedHeadfollows a published head: heal pushed H1, nothing has judged it, and the nonzero exit is what stops an unjudged tree standing as green. This arm publishes nothing — the healed commit stays on the runner and the branch carries an author head with its own run, which regenerates from the only tree that matters. Exiting nonzero would render a red square beside a PR for a scheduling outcome about a revision that no longer exists: exactly arm one ofgunbc.recurring_failure_mode repair_job_red_is_attributed_to_the_content_it_repaired. The outcome stays loud and countable as a typed::notice::naming all three revisions plus the branch and both path sets; it does not stay loud by borrowing the channel that means "the content under review is wrong".heal-generated-artifactsis not a required lane, so this opens no gate.This retires arm one of that row and not the row. Arm two still reports red on every heal that lands, and the row's trigger is explicit that a fix covering one arm does not retire it. I did not append a receipt to the row: it is a hot per-lane append surface and its text stays accurate as written.
Evidence (executed, both directions)
heal_outcome_tokengives the name the log speaks one authority — the emitter cannot apply a coproduct arm to revisions that exist only as shell variables, so the token crosses into the script as text, and the witness derives its pattern from that fold rather than typing it twice.true.the_heal_push_race_leaves_by_its_own_named_armreturnsfalse. The join is real, not a self-satisfying grep.exit 0toexit 1in the emitter → same witness returnsfalse. The exit status is asserted as ordered adjacency, so membership alone cannot green it.Both reverted; the emitted
witnesses.ymldiff is the single expected hunk.🤖 Generated with Claude Code
https://claude.ai/code/session_01HKvv6ctMMcjbtFUiNV64dF