Skip to content

Releasing a Claim, and removing it when thirdshift closes the issue - #294

Merged
JacobStephens2 merged 3 commits into
issue-212from
issue-281
Oct 1, 2026
Merged

JacobStephens2 merged 3 commits into
issue-212from
issue-281

Conversation

@JacobStephens2

Copy link
Copy Markdown
Owner

Summary

What becomes of a Claim when the Run or Spec run that made it ends, decided in one place (run in src/run.rs), with the label work in src/claim.rs:

 run
   preflight, pick the branches
   claim::make                        # in-progress, in place of ready-for-agent
-  create the worktree … take the issue to its goal
+  run_in_worktree                    # create the worktree … take the issue to its goal
+  when it ends
+    merged                → claim.remove_if_closed
+    ready for review      → the Claim stays
+    failed                → claim.release_if_nothing_on_origin
release_if_nothing_on_origin(branch)        # Issue branch, or Spec branch
  if branch is on origin, or a pull request comes from it → keep the Claim
  if the issue is no longer in-progress                    → leave it
  take in-progress off if the Claim added it,
  put ready-for-agent back if the Claim took it off        # one request, other labels kept

remove_if_closed                             # after a Self-merge
  if the issue is closed and in-progress → take in-progress off

Neither can fail the run: a failure is a warning: line naming the command to run by hand, and an interrupt doesn't stop them. A Ticket's Run and a Base fix make no Claim, so they release and remove nothing.

Seam tested: the thirdshift binary against the fake gh and fake claude (the existing one). The fake gh gains gh api --method DELETE repos/<repo>/issues/<n>/labels/<label>.

Supporting moves: warn → progress::warn and retry_if_interrupted → interrupt::retry_if_interrupted, now that the Claim uses them too; Git::on_origin shared with Worktree::delete_from_origin.

Evidence

  • Before: a_failed_run_that_pushed_nothing_releases_its_claim
    run on #7 labelled [bug, ready-for-agent, architecture]; the session exits 1 with no changes
    assert labels == [bug, architecture, ready-for-agent]
      left:  ["bug", "architecture", "in-progress"]      FAILED
    
    a_merge_run_ends_with_the_issue_closed_and_its_claim_removed
    merge run on #7 labelled [bug, ready-for-agent]
    assert labels == [bug]
      left:  ["bug", "in-progress"]                      FAILED
    
    After: both pass, with the rest of the new cases: cargo test → 655 passed, 0 failed; cargo clippy --all-targets -- -D warnings and cargo fmt --check clean.
Acceptance criterion Test
Failed session, nothing pushed: labels as before failed_run: a_failed_run_that_pushed_nothing_releases_its_claim
No ready-for-agent before, none gained failed_run: a_released_claim_gives_no_ready_for_agent_to_an_issue_that_had_none
Interrupted, nothing on origin: released failed_run: an_interrupted_run_that_left_nothing_on_origin_releases_its_claim
Failed run that pushed keeps the Claim failed_run: a_failed_run_that_pushed_its_issue_branch_keeps_its_claim
Ready for review keeps the Claim fresh_run: a_run_that_ends_with_its_pr_ready_for_review_keeps_its_claim
Spec run: released with neither Spec branch nor Spec PR, kept with either spec_run: a_spec_run_that_left_no_spec_branch_on_origin_and_no_spec_pr_releases_the_specs_claim, a_failed_spec_run_whose_spec_branch_is_on_origin_keeps_the_specs_claim, a_failed_spec_run_that_left_a_spec_pr_keeps_the_specs_claim
Merge run: closed, no in-progress merge_run: a_merge_run_ends_with_the_issue_closed_and_its_claim_removed
Merge run of a Spec: the same spec_run: a_merge_run_of_a_spec_ends_with_the_spec_closed_and_its_claim_removed
Removal fails: still merged, warning: with the command merge_run: a_failed_claim_removal_still_exits_as_merged_with_a_warning
README "When the Claim ends", under "The Claim"

Merge Danger

Door: two-way

Reverting restores "the label stays, whatever the outcome". Labels already put back or removed on real issues stay as they are, and can be relabelled by hand.

Blast Radius: labels

Every Run and Spec run that makes a Claim now makes one to three more gh calls when it ends merged or failed. A wrong release would make an issue ready-for-agent again, which matters once a Pickup run exists to take it; until then the label is only read by people.

Three choices worth a look in review:

  • A release undoes what the Claim changed on the issue's labels as they are now, rather than writing back the list read before the Claim, so a label added during the run (say needs-info) survives. An issue whose in-progress someone took off during the run is left alone.
  • in-progress is removed after a Self-merge whether thirdshift or GitHub closed the issue, and stays if the issue is still open because closing it failed.
  • A Failed run whose push failed has nothing on origin, so its Claim is released while its work sits in the kept worktree. That is the rule as written; the README says so, and that the local Issue branch stops the next Run on that machine at the pre-flight checks.

Unaddressed findings

Standards

  • Possible Repeated Switches / Primitive Obsession: an enum for what making the Claim changed (src/claim.rs). The two facts are independent, each with its own undo (take in-progress off; put ready-for-agent back). An enum of their four combinations would need the same matches in the same three places.
  • add_label_command has no function counterpart, so the by-hand command can drift from the code. The code releases with one PUT of every label, which a by-hand command can't spell without knowing the issue's other labels; POST of the one label is the by-hand equivalent. The path is now shared (labels_path), and the doc comment says why they differ.
  • Possible Data Clumps: run_in_worktree takes nine parameters. It follows the five other too_many_arguments sites around it; bundling issue, base, goal, base_fix, logs is a refactor of implement and spec_run::run too, beyond this Ticket.

Spec

  • Removal also happens when GitHub, not thirdshift, closed the issue. Kept: the criterion is "a Merge run ends with the issue closed and without in-progress", and a merge into the default branch is the case where GitHub closes it.
  • Labels added during the run are kept, and an issue whose in-progress was taken off is left alone, beyond "puts the labels back as they were". Kept: writing back the old list would wipe a needs-info someone added during a Spec run that lasts hours, and make the issue ready-for-agent again.
  • A failed release is a warning: naming commands, which the issue asks for only on removal after a merge. Kept: without it a release that failed would be silent, and the issue stranded in-progress with no hint.
  • Help text updated, though Releasing a Claim, and removing it when thirdshift closes the issue #281 names only the README. Kept: the help already describes the Claim, and would have been wrong by omission; Pickup runs: thirdshift takes ready-for-agent issues on its own #212 story 50 covers it.
  • No new Spec run test named for "ready for review keeps the Claim". a_spec_run_claims_the_spec_and_its_tickets_runs_change_no_label already ends ready for review and asserts the Spec is in-progress; a Run now has its own named case.

Closes #281

🤖 Generated with Claude Code

JacobStephens2 and others added 3 commits October 1, 2026 18:36
…ue closed

A Run or a Spec run that fails with nothing on origin to take over, no
Issue branch or Spec branch and no pull request, puts its issue's labels
back as they were before the Claim. A Merge run takes in-progress off once
the issue is closed. Either failing is a warning naming the command to run
by hand.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… labels path, clearer names in the Claim, more release cases, the Self-merge's glossary entry

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
src/claim.rs: the Claim a run makes and releases, from this branch, beside
is_on, which the Pickup run asks whether an issue carries a Claim. The two
branches each lifted the same label test out of label_in_progress; it stays
one function under this branch's name, has_label, and is_on is what asks
about in-progress.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@JacobStephens2
JacobStephens2 merged commit f583031 into issue-212 Oct 1, 2026
13 checks passed
@JacobStephens2
JacobStephens2 deleted the issue-281 branch October 1, 2026 19:06
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.

1 participant