Skip to content

A Ticket fails as CI red when its Repair already re-ran the failed checks and they passed #508

Closed

Description

@JacobStephens2

What happened

In the Spec run for #486 last night, Ticket #489 failed with "CI red on 7a2e53a and the Repair found nothing to fix on the branch", although CI on 7a2e53a was green by then.

From the command log (~/.thirdshift/logs/JacobStephens2/thirdshift/commands/pickup/486-20261007T013901-0400.log, times EDT):

PR #498 is now green and mergeable, but the Spec run had already stopped. Because issue-486 and PR #496 exist, #486 is no longer a Ready issue, so no Pickup run resumes it.

Expected

When gh run rerun --failed is refused, thirdshift should read the checks on the head commit again before calling the Ticket failed. Here the run had already been re-run, its new attempt passed, and the Ticket should have gone on to merge.

Where

src/ci.rs, rerun_through: if rerun_failed_jobs fails, it returns Ok(None) without looking at the checks' current state. The failed checks it acts on were read before the Repair session ran, so they predate any re-run the session made.

Notes

  • A Repair session re-running CI is reasonable: its prompt lets it, and here it was the right fix for a flaky test.
  • Either thirdshift picks up re-runs it didn't start, or the Repair prompt tells the session to leave CI re-runs to thirdshift. The first is more robust.

Activity

  1. added
    bugSomething isn't working
    needs-triageMaintainer needs to evaluate this issue
    on Oct 7, 2026
  2. JacobStephens2 commented on Oct 7, 2026

    @JacobStephens2
    OwnerAuthor

    This was generated by AI during triage.

    Agent Brief

    Category: bug
    Summary: A refused Check re-run must refresh CI on the unchanged head before a Run is failed as a Declined CI fix.

    Current behavior:
    The Repair loop retains the failed checks it read before starting a CI-fix Repair. When the Repair leaves the head unchanged, the Check re-run uses that snapshot. If GitHub refuses gh run rerun --failed, rerun_through returns None immediately, and the Repair loop fails the Run as CI red. A Repair can already have re-run those checks and watched them pass, so the retained failures need not describe current CI.

    Verification:

    • The original Command log records Repair re-running workflow run 37581771582 at 02:41:56 EDT, then thirdshift attempting the same re-run at 02:49:25, receiving a refusal at 02:49:26, and failing Ticket Reviewers list the files they read #489 at 02:49:29.
    • GitHub's attempt 2 confirms success on head 7a2e53afdf7e6933df8bf790b211c40694041466, completed by 02:44:54 EDT. Attempt 1 failed on that same head.
    • Independently reproduced against checkout f03ac2b7358f2380940bd45c03eb65b5c67d091d using the existing Scenario harness, real git, and fake GitHub/Harness executables: the implement session creates a failing Actions check whose re-run passes; session 2 runs gh run rerun 900 --failed --repo acme/widgets and commits nothing. The fake GitHub then refuses thirdshift's second request because no failed job remains. Assertions confirmed the check was successful, the Run nevertheless exited unsuccessfully with the Declined CI fix cause, two re-run requests were made, and only one Repair ran. cargo test --test triage_508_repro -- --nocapture passed its assertions of the broken behavior. The temporary reproduction test was removed after verification; no fix was implemented during triage.

    Desired behavior:
    When a Check re-run request fails, determine the outcome from freshly read checks on the same head instead of immediately treating the earlier failure snapshot as current. If those checks have passed, continue normal Delivery and, for a Merge run, Self-merge. If they are pending, watch them finish using the existing interruption behavior. If the branch's own checks are still red, preserve the Declined CI fix outcome without spending another Repair or issuing another re-run for that head. Preserve the existing treatment of Inherited failures and the protection against reading an earlier attempt as the result of a successful re-run request.

    The refusal remains visible in progress output, but its error text is not evidence of CI's current state. A failed checks read or an empty listing after previously known failures must not become an unverified successful outcome. When several workflow runs are involved, assess the whole head's CI, including any re-run already started before a later request was refused.

    Key interfaces:

    • rerun / rerun_through and their Result<Option<Ci>> contract: a request refusal alone must not mean there is no current CI outcome to observe.
    • The CI Outside boundary, checks_on, and watch behavior: reuse deterministic head-commit checks and polling, including current pending checks and Inherited failure classification.
    • The Repair loop's unchanged-head path: preserve its one-Repair/one-Check-re-run budget and normal delivery/merge gates.
    • The existing InMemory and Scenario test boundaries can simulate a refused request with checks already green or pending.
    • ADR-0010 currently says a refused re-run unconditionally fails the Run. Amend that consequence to reflect fresh-check reconciliation; align the domain documentation where necessary.

    Acceptance criteria:

    • An unchanged-head Repair that re-runs failed Actions checks to green is followed by a successful Delivery, even if thirdshift's subsequent re-run request is refused. A regression test exercises this through the Run boundary.
    • A Merge run in that case reaches Self-merge on the same verified head, rather than failing its Ticket as CI red.
    • A refused request with current pending checks waits for completion; pending-to-green continues and pending-to-own-red ends as a Declined CI fix. Interruption still stops the watch.
    • A refused request with current own failures still fails without another Repair or re-run, and reports the refusal.
    • Fresh checks-read errors and an empty listing after known failures do not produce a successful delivery or merge.
    • Reconciliation considers all checks on the head when one workflow request succeeded before another was refused; remaining failures and pending checks cannot be ignored.
    • Existing successful-re-run freshness checks, non-Actions refusal behavior, Inherited failure handling, and merge head verification remain covered and correct.
    • ADR-0010 and any affected domain wording describe the revised refusal behavior consistently.

    Out of scope:

  3. added
    ready-for-agentFully specified, ready for an AFK agent
    in-progressA Claim: thirdshift has taken this issue
    and removed
    needs-triageMaintainer needs to evaluate this issue
    ready-for-agentFully specified, ready for an AFK agent
    on Oct 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions