Skip to content

The Spec run owns its lifecycle to its Delivery, tested in-process behind its seam - #420

Merged
JacobStephens2 merged 2 commits into
mainfrom
issue-419
Oct 5, 2026
Merged

JacobStephens2 merged 2 commits into
mainfrom
issue-419

Conversation

@JacobStephens2

Copy link
Copy Markdown
Owner

Summary

The Spec run's whole lifecycle, from the Ticket loop to its ending, now lives in one function, run_through. It reaches everything outside itself through the Spec run's seam.

 spec_run::run(tickets, worktree, delivery, parallel)
   SpecPr::resume                      # failed resume: no lines, as before
-  land_tickets(ChildRunsAndGitHub)
-  spec_pr.landed → Delivery::deliver(worktree, …, || spec_pr.put_back)
-  FailedRun::from(error)              # read the process-wide interrupt
+  ChildRunsAndGitHub::new(worktree, delivery, spec_pr)   # owns all three
+  run_through(outside, spec, base, branch, tickets, parallel)
+    land_tickets
+    outside.landed(checklist) → Spec PR URL
+    outside.deliver(Spec review Opening, |o| o.put_back(checklist))
+    on a failed Delivery: outside.show(checklist)
+    on a failed landing: FailedRun built explicitly, outside.interrupted(), outside.spec_pr_url()
+    Ticket lines on Reached and Failed spec run

The seam (Outside) gains landed -> String, spec_pr_url, put_back and deliver(opening, before_ready: FnOnce(&mut Self)). When the Delivery starts, the adapter takes the worktree and the Delivery out of their Options and lends itself to the put-back.

Evidence

  • Before: only the end-to-end Spec run tests checked the Spec review's Opening, the put-back before ready, the checklist shown again after a failed Delivery, and the Spec PR's URL on a failed landing.
    After: these in-process tests use the scripted fake:

    • every Ticket lands, then the Spec PR is told so, then the Delivery runs (Spec review Opening, put-back, Ready), and the Reached carries the Ticket lines
    • a failed landing means no Delivery; it carries the Spec PR's URL (or none if it isn't open) and the Ticket lines
    • a failed landing with an interrupt ends as interrupted
    • if the Spec PR can't be opened after the last landing, there is no Delivery
    • a failed Delivery shows the checklist again and keeps its PR URL, log and Ticket lines
    • a Delivery that fails in the Spec review shows the checklist again without a put-back
    • a put-back that fails fails the Delivery before ready
  • Two end-to-end tests are deleted because their assertions are now covered in-process:

    • the_spec_review_writes_the_spec_pr_body_and_the_checklist_is_put_back_after_it
    • a_failed_spec_review_that_rewrote_the_body_has_the_checklist_put_back_in_the_draft

    The exact final body is still asserted end-to-end by a_spec_review_whose_resume_ends_with_killed_background_work_still_delivers_the_spec_pr.

  • cargo test passes in full and cargo clippy --all-targets is clean.

Merge Danger

Door: two-way

A refactor with no behaviour change: stderr, the Spec PR, the notification and exit codes are unchanged.

Blast Radius: Spec runs

Unaddressed findings

Standards

  • Hand-built FailedRun duplicates From<anyhow::Error>: skipped. The spec asks for the Failed spec run to be built explicitly, reading the interrupt through the seam, not through that conversion.
  • Data clump spec, base, branch: skipped. The spec's decision is that the lifecycle takes these as plain arguments.
  • Wide seam, and a Middle Man adapter forwarding to SpecPr: skipped. The spec decides on one seam carrying exactly these methods, and landed returning the URL.
  • Worktree and Delivery held in Options with expect, and spec/base_fix/harness copied out of the stored Delivery: skipped. The Option hand-over is the spec's chosen design. The copies avoid borrowing the Delivery the adapter must later give away.
  • The fake's deliver builds Reached and FailedRun with the same constants: skipped. The duplication is two lines of test data and reads clearly.

Spec

None skipped.

Closes #419

🤖 Generated with Claude Code

JacobStephens2 and others added 2 commits October 5, 2026 06:03
…hind its seam (#419)

The Spec run's seam grows the Spec PR's last landing, which returns its
URL, the Spec PR's URL if open, the put-back of the Tickets checklist and
the Delivery, given its Opening and a step before ready that takes the
seam. run_through holds every rule from the Ticket loop to the ending: the
Spec review's Opening, the put-back before ready, the checklist shown
again after a failed Delivery, and the Failed spec run of a failed
landing, which now asks the seam whether it was interrupted. The real
adapter owns the worktree, the Delivery and the Spec PR, and hands the
first two over when the Delivery starts. The entry point only resumes the
Spec PR, builds the adapter and calls run_through.

The two end-to-end tests on the checklist being put back are replaced by
in-process ones.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…landing, and a Delivery failing before the put-back

Telling the Spec PR the last Ticket landed is part of the landing, so if
it fails, the Failed spec run is built explicitly too, asking the seam
whether it was interrupted. The scripted Delivery can fail in its opening
session, before the put-back, and a test shows the checklist again then.
The Opening's kind in tests is SPEC_REVIEW, and a shorter test name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@JacobStephens2
JacobStephens2 merged commit 23fcc29 into main Oct 5, 2026
13 checks passed
@JacobStephens2
JacobStephens2 deleted the issue-419 branch October 5, 2026 10:16
@JacobStephens2 JacobStephens2 mentioned this pull request Oct 5, 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 Spec run owns its lifecycle to its Delivery, tested in-process behind its seam

1 participant