Skip to content

The draft Spec PR and its Tickets checklist - #169

Merged
JacobStephens2 merged 9 commits into
mainfrom
issue-147
Sep 30, 2026
Merged

JacobStephens2 merged 9 commits into
mainfrom
issue-147

Conversation

@JacobStephens2

@JacobStephens2 JacobStephens2 commented Sep 30, 2026 •

Copy link
Copy Markdown
Owner

Summary

The Spec PR now exists from the first landed Ticket onward, as a draft carrying a Tickets checklist.

 spec_run::run
+  draft_spec_pr               # an already-open Spec PR, back to draft
   land_tickets                # up to parallel <n> Tickets at once
     start ready Tickets
+      checklist: "#n running"               (if the Spec PR is open)
     a Ticket's Run ends       # a Run that could not be followed to its end → failed
       re-read the graph
+      Spec PR open?  rewrite the checklist between its markers
+      else landed?   open the Spec PR as a draft: title, Closes #<spec>, checklist
+                     (even if the re-read failed, so a Failed spec run still has its URL)
-  open_spec_pr                # once every Ticket was done
+  open_spec_pr                # only if nothing landed this run (all already closed)
   review_and_deliver
     Spec review session (/pr rewrites the body)
     push
+    put the checklist back: between the markers, or appended
     mark ready
     Repair loop, Self-merge when asked
+  failed spec run → FailedRun { pr_url: Spec PR }  → URL on stdout, exit 1
+  failure from the Spec review on → checklist put back, then the Failed run path

The checklist, in the body between <!-- thirdshift:tickets --> markers:

## Tickets

- [x] #21 landed with https://github.com/acme/widgets/pull/1
- [ ] #22 failed: claude exited 1
- [ ] #23 blocked by #22
- [ ] #24 running
- [ ] #25 unready: labelled needs-info
- [x] #26 done
  • standing() gives each Ticket's line. The stderr summary and the Run notification's Ticket lines (One Run notification per Spec run #148) use it too, so the wording is the same everywhere.
  • With parallel Tickets (Parallel Tickets: spec.parallel and parallel <n> #143), the checklist is written once for each batch of Tickets started, and again whenever one ends. Endings are handled one at a time on the Spec run's thread, so checklist writes and opening the Spec PR never race.
  • gh pr edit breaks on gh 2.45 (the Projects classic sunset), so github::set_pr_body sets the body through the REST API. The fake gh now handles that PATCH, and pr view by number.
  • If a checklist update fails while Tickets run, thirdshift only warns and keeps going. Putting the checklist back before the PR is marked ready is still required.
  • The Spec review prompt now says to keep the checklist between its markers or leave it out.
  • The help text, README and CONTEXT.md (new term: Tickets checklist) are updated.
  • origin/main is merged in, including parallel Tickets, the Spec PR's Repair loop and Self-merge, and one Run notification per Spec run. Tests that need Numbered Issue branches after a merged or closed PR #21 to land before CI watching, CI-fix Repair and the Repair cap #22 starts run with parallel 1. Tests now find the Spec PR by its head branch, because it is no longer the last PR opened.

Evidence

  • Before: the Spec PR opened only after every Ticket landed, so a Failed spec run left no PR and printed nothing on stdout. There was no checklist.
    After: cargo fmt --check, cargo clippy --all-targets -D warnings and cargo test all pass. New or reworked tests in tests/spec_run.rs:
    • the_spec_pr_opens_as_a_draft_once_the_first_ticket_lands_with_the_checklist: Numbered Issue branches after a merged or closed PR #21's session sees no Spec PR. CI watching, CI-fix Repair and the Repair cap #22's sees a draft with Closes #20, where #21 landed with …/pull/1 is ticked and #22 running is not.
    • the_spec_review_writes_the_spec_pr_body_and_the_checklist_is_put_back_after_it: the body is rewritten without markers, the checklist is appended, and the PR is ready.
    • a_spec_review_that_keeps_the_markers_has_the_checklist_replaced_between_them: the text outside the markers stays byte for byte.
    • a_ticket_that_fails_once_the_spec_pr_is_open_is_shown_failed_in_its_checklist, plus each unready, blocked, cycle and outside-blocker test: exit 1, a draft Spec PR, its URL on stdout, and the exact checklist.
    • a_ticket_that_lands_just_before_github_stops_answering_still_gets_the_draft_spec_pr: failed before this fix (no Spec PR), passes after it.
    • a_failed_spec_review_that_rewrote_the_body_has_the_checklist_put_back_in_the_draft
    • a_checklist_update_github_refuses_is_only_a_warning
    • Unit tests for checklist and with_checklist: replacing, appending when the markers are gone, appending when only one marker is left.

Merge Danger

Door: two-way

Revert the commits to undo it. The only lasting effect is on Spec PRs opened while this is deployed: their bodies keep a marked checklist section.

Blast Radius: Spec runs

Only a Spec run changes. The ordinary single-issue Run's code paths are untouched: pr_view only had its argument renamed, and pr_body/set_pr_body are new. Each batch of Tickets started and each Ticket that ends costs one more gh read and one PATCH.

Unaddressed findings

Standards

  • land_tickets returns the checklist, fills an out-param &mut Option<PullRequest>, and owns the Spec PR's lifecycle (Divergent Change; "return results, don't produce side effects"). Kept. The Spec PR must stay visible to the caller on the failure path, so it can go in FailedRun.pr_url. A SpecPr type is worth doing on its own once Rerunning a Spec #149 reshapes reruns.
  • #[allow(clippy::too_many_arguments)] on review_and_deliver: spec, spec_pr and checklist form a Data Clump. Kept, for the same reason: that same SpecPr type would absorb them.
  • Feature Envy: write_checklist belongs beside pr_body/set_pr_body. Kept. The markers are a Spec run concept, and github.rs stays a thin gh layer.
  • The checklist is a bare String, and pr_view takes a head branch or a number as a &str (Primitive Obsession). Kept. The checklist is produced by one pure function and consumed by one. pr_view is private, and gh pr view itself takes either form.
  • draft_spec_pr has the same shape as failed_run::open_pr_url. Kept. They differ in keep_ready and in what they return, and sharing them would couple the Failed run path to Spec runs.
  • write_checklist_or_warn(spec, pr, &checklist(…)) appears twice in the loop. Kept. One is after Tickets start and one after a Ticket ends, and a helper would hide which moment is which.
  • draft_spec_pr sounds like it creates a draft. Kept. Its doc says it finds the open Spec PR and turns it back into a draft, and the name matches convert_to_draft.

Spec

  • A failure in the Self-merge that is a policy refusal leaves the Spec PR ready, not a draft. Kept. This is the Failed run rule main already has for every Run, and the README documents it. "A Failed spec run", where Tickets are missing, always leaves a draft.
  • If gh pr create succeeds but the lookup right after it fails, the run fails without a URL, and a later landing would try to create the PR again. Kept. The first error is still reported and the run fails. It needs GitHub to fail between two back-to-back calls.
  • Tickets closed before this Spec run show as done without their PR. Finding each Ticket's PR belongs with rerunning a Spec (Rerunning a Spec #149).
  • draft_spec_pr turns an already-open ready Spec PR back to a draft (beyond The draft Spec PR and its Tickets checklist #147). Kept. It is what makes "A Failed spec run leaves the Spec PR a draft" hold when a Spec PR already exists.
  • The checklist is also written when Tickets start, and has extra states: not started, in a cycle, interrupted. Kept. "Running" can only show if the checklist is written when a Ticket starts. The extra states name real Ticket states that the spec's list doesn't cover.
  • Only the first marker pair is replaced if a body holds two. Kept. thirdshift writes one pair, and the Spec review prompt asks for it to be kept as it is.

Closes #147

🤖 Generated with Claude Code

JacobStephens2 and others added 4 commits September 30, 2026 02:30
…ts checklist

The checklist sits between fixed markers in the Spec PR's body, one line per
Ticket, and is rewritten as each Ticket starts and ends. After the Spec review
rewrites the body, thirdshift puts it back before marking the PR ready. A
Failed spec run leaves the Spec PR a draft and prints its URL.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…iled update only warns

Also defines the Tickets checklist in CONTEXT.md, passes the Spec PR to the
Spec review instead of looking it up again, and names pr_view's argument for
what it takes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Both sides taught the fake gh's pr_view to look a PR up by number;
kept main's version, which also documents its --jq support.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30T03:08:06Z, host tracewake. Uncommitted work at the time of failure is included in this commit.
@JacobStephens2
JacobStephens2 marked this pull request as draft September 30, 2026 03:08
JacobStephens2 and others added 5 commits September 30, 2026 12:20
Parallel Tickets (#143) and the Spec PR's Repair loop and Self-merge
landed on main. The Tickets checklist now follows the parallel loop: it
is written once per batch of Tickets started and whenever one ends, and
the Spec PR opens after the first Ticket lands. Tests that need #21 to
land before #22 starts run with parallel 1, and the Spec PR is found by
its head rather than by its index.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…st line

A Ticket whose Run could not be followed to its end is shown failed rather
than running, and a Ticket that lands just before GitHub stops answering
the graph read still opens the draft Spec PR, so the Failed spec run
prints its URL. Also fix the run doc (an open Spec PR is turned back into
a draft) and list every checklist state in CONTEXT.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
One Run notification per Spec run (#148) landed on main. Its Ticket lines
and the Tickets checklist now come from the same standing(), with main's
"not started" in place of "to do", and the Spec PR is opened and kept
up to date inside run_ready_tickets. The notification tests expect the
Spec PR at pull/2, now that it opens once the first Ticket lands.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@JacobStephens2
JacobStephens2 marked this pull request as ready for review September 30, 2026 13:20
@JacobStephens2
JacobStephens2 merged commit f4fe0e7 into main Sep 30, 2026
13 checks passed
@JacobStephens2
JacobStephens2 deleted the issue-147 branch September 30, 2026 13:26
@JacobStephens2 JacobStephens2 mentioned this pull request Sep 30, 2026
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.

The draft Spec PR and its Tickets checklist

1 participant