Repository navigation
Inherited failures: fail fast on a red Base branch, or fix it with a Base fix - #226
Merged
Merged
Conversation
A failed check that also failed, under the same name, on the Base branch commit the head last merged in is an Inherited failure (ADR 0008). The CI watcher now splits the failed checks into the branch's own and Inherited ones. A CI-fix Repair is given only the branch's own, with the Inherited ones listed as not to fix. When only Inherited failures are left, the Run merges the Base branch again if it moved, and otherwise fails naming the checks, the Base branch and its commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tively, test a still-red moved Base branch A check is an Inherited failure only if every check of its name on the Base branch commit failed. Commit statuses and a Base branch that moved but is still red are now covered through the binary. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Inherited failures fail the Run without a Repair
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ks, name the child Run's kind Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A Base fix, when the base-fix flag is given
base.fix in the User config lets every Run start a Base fix with no flag; no-base-fix forbids it for one Run, and contradicts base-fix. Setup asks about Base fixes only with every Run a Merge run, and always writes the setting. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… writes base.fix at its default with merging off, share the Spec run test setup Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Base fix in the User config and Setup
…ch directory at a time Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… left open, share the Launch directory lock Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
README.md: keep issue-221's wording that base-fix, no-base-fix or the User config's base.fix holds for each Ticket's Run, with issue-224's shared Base fix and its "One Base fix per Base branch" section. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Runs share one Base fix
…list and Run notification, share the check list and the Inherited failure cause Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
JacobStephens2
marked this pull request as ready for review
October 1, 2026 03:21
This was referenced Oct 1, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #221
Summary
A red check that is also red on the Base branch is an Inherited failure: thirdshift recognises it itself, before any Repair, from check names and conclusions only.
A Base fix is allowed by
base-fix, orbase.fix = truein the User config (Setup asks, when merging is on);no-base-fixforbids it for one Run. The Base fix itself sees no Inherited failures, starts no Base fix and sends no Run notification.The Spec review added one thing that fell between the Tickets: a Ticket's Run sends no Run notification, so its Base fix was reported nowhere once the Ticket landed. A Run now ends its progress lines with
Base fix: <issue URL> merged, the Spec run reads that back from a Ticket's Run, and the Ticket's line says so in the Tickets checklist and the Spec run's Run notification:It also shares what the Tickets had each written: one check list (
ci::check_list) for the Repair prompt and the Base fix issue, the Inherited failure cause written once,BaseFixAskinstead of a bareboolfor what a child Run is asked, and one name for thebase-fixword.Seam under test: the black-box one, the real binary with the fake
gh, fakeclaudeand the Resend stand-in (tests/support::Scenario).Evidence
a_ticket_that_landed_after_a_base_fix_says_so_in_the_notification_and_the_checklistfails: the notification's Tickets section holds only#21 landed with https://github.com/acme/widgets/pull/1.After: it passes: the line ends
, after Base fix https://github.com/acme/widgets/issues/22 merged, in the notification and in the Spec PR's checklist.a_ticket_whose_red_check_also_fails_on_the_spec_branch_shows_the_cause_in_the_checklist_and_the_notificationnow also reads the Spec run's Run notification for the Inherited failure cause (story 15).cargo test: every suite passes (503 tests, 70 intests/spec_run.rs, 15 intests/base_fix.rs, 20 intests/ci.rs).cargo clippy --all-targetsandcargo fmt --checkare clean.Merge Danger
Door: two-way
Default behavior changes only for a Run whose every red check is also red on the Base branch: it now fails at once, naming the checks, instead of spending a CI-fix Repair and ending as a Declined CI fix. Base fixes are off unless asked for. Reverting restores the old loop; a Base fix issue or a
thirdshift-base-fix-<n>.lockfile left behind is inert.Blast Radius: Runs
base-fixon, a Run writes an issue, adds thebase-fixandready-for-agentlabels to the repository if it lacks them, and merges a fix into the Base branch unattended.Base fix: <issue URL> …, before its outcome.Unaddressed findings
Standards
base_fix.rsre-fetches the Base branch's checks thatci::watchalready read (Duplicated Code): the issue needs the Base branch's own check URLs, whichFailedChecksdoes not carry; carrying them would widen the CI watcher's result for one caller that runs at most once per Run.BaseFix::fixonly to get its cause (locality, misleading name): the cause is now written once, but the decision stays inBaseFix, so the Repair loop asks one question and the cap, the ask and the cause stay behind one interface.base_fixnames an ask, aBaseFixand a report inmain.rsandnotification.rs(Mysterious Name): the child-Runboolis now aBaseFixAsk; the shadowing inmain.rsfollows howgoalandemailare resolved there.Kindmapping is inargs.rsand its inverse inchild_run.rs(Repeated Switches): two arms each, both on the constants inargs.rs; a shared table would be more code than it saves.child_run::Kindis imported bare, andEnded::Reachedshares a name withrun::Reached(Mysterious Name): readable at each use, and renaming touches four modules for no change in behavior.issue, worktree, base, goal, base_fix, logstravel together, with three newtoo_many_argumentsallows (Data Clumps): a real clump, but it predates the Spec (mainhad two); bundling it is its own refactor ofrun.rsandspec_run.rs.Taken { started, reached }is a pair of bools (Primitive Obsession): two independent facts, each read in one place.mark_filenames it once; the join with the common git directory is one line at each.checks_on_base/checks_on_origin/inherited_failurerepeat across test files (Duplicated Code): per-file helpers are the convention intests/, and their signatures differ with each file's scenario.worktree.rsandbranch.rs: fixed in the lines this Spec wrote; the rest is onmainalready, outside this diff.Spec
thirdshift merge <Base fix issue>) is not marked as a Base fix, so it sees its target check as an Inherited failure (story 27): the Spec defines the Base fix as the marked child Run, and the next Run to meet the open issue starts it again as one. Treating any issue labelledbase-fixas a Base fix is worth its own issue.gh label createfor labels the repository lacks was not asked for: without itgh issue create --labelfails on a repository that has never had a Base fix..gitafter a failed Base fix: deliberate, they are how the next Run tells the Base fix is no longer running; one is removed when its Base fix merges.gh issue list --search(Testing Decisions): the open issues are listed by label and their titles matched by thirdshift, because GitHub's search index lags behind an issue just written, which is when Tickets look.Tickets
🤖 Generated with Claude Code