Skip to content

One Run notification per Spec run - #170

Merged
JacobStephens2 merged 8 commits into
mainfrom
issue-148
Sep 30, 2026
Merged

JacobStephens2 merged 8 commits into
mainfrom
issue-148

Conversation

@JacobStephens2

@JacobStephens2 JacobStephens2 commented Sep 30, 2026 •

Copy link
Copy Markdown
Owner

Summary

A Spec run now sends one Run notification for the whole Spec, with a line on each Ticket, as in the stderr summary. The checks (address, RESEND_API_KEY) already ran once in main before any Ticket, and Ticket Runs already skipped notifications. The missing piece was the per-Ticket lines. They now travel out of the Spec run on Reached / FailedRun, and they work with Tickets running in parallel (merged in from main):

 spec_run::run
-  land_tickets                  # bails; per-Ticket lines only printed to stderr on failure
+  (ticket_lines, landed) = land_tickets
+    run_ready_tickets           # up to `parallel` at once; an interrupted Ticket is now an outcome, not an error
+    summarize                   # same lines as the stderr summary
+      ready but never started → "not started"
   open_and_review               # Spec PR + Spec review
+  attach ticket_lines to Reached / FailedRun
 main
   notification.send(&ended)
+    Body: ... Took, then "Tickets:" and one line per Ticket

Example body of a Failed spec run:

Cause:        Tickets not done: #21, #22
Host:         droplet-1
Took:         21s

Tickets:
#21 failed: claude exited 1 (session log: …)
#22 blocked by #21
#23 landed with https://github.com/acme/widgets/pull/1

README, help and the Run notification glossary entry in CONTEXT.md now say that a Spec run sends one notification for the whole Spec.

Evidence

  • Before: there were new tests in tests/spec_run.rs for ready, failed and interrupted Spec runs. The subject and outcome were already right, but the body had no Ticket lines, so all three failed, e.g. expected "#21 landed with https://github.com/acme/widgets/pull/1\n" in: Pull request: …/pull/3 … Took: 29s.
    After: all 51 spec_run tests pass, including:
    • ready → one email, …#20 Widgets, all of them: ready for review, with #21 landed with …/pull/1 and #22 landed with …/pull/2
    • failed → one email, : failed, with each Ticket's outcome
    • interrupted → one email, : interrupted, with #21 interrupted and #22 blocked by #21
    • --no-email with email.always sends none
    • a missing address or RESEND_API_KEY stops the run before any Ticket starts
    • a Resend 401 leaves exit 0 and stdout alone, and adds a warning: line
  • Before: in review, with parallel 1 and two independent Tickets, an interrupt during Numbered Issue branches after a merged or closed PR #21 gave #22 blocked by (no blockers) in the email.
    After: a_ready_ticket_an_interrupt_kept_from_starting_is_not_started_in_the_notification checks for #22 not started, and it passes.
  • After merging main (parallel Tickets), the full suite passes, and cargo clippy --all-targets and cargo fmt --check are clean.

Merge Danger

Door: two-way

Blast Radius: notifications

This changes the email body for Spec runs, and how an interrupt flows inside the Spec run's Ticket loop. Stderr, stdout and exit codes stay the same.

Unaddressed findings

Standards

  • ticket_lines on Reached and FailedRun widens a Run's interface for a field only a Spec run uses (Shotgun Surgery / Refused Bequest). Skipped: main only gets the lines through run::run's result, and a separate return type would ripple through every caller with no change in behaviour.
  • ticket_lines: Vec::new() in four constructors, and the struct-update pair in spec_run::run (Duplicated Code). Skipped: this follows from the choice above, and each one is a one-line default.
  • land_tickets returns a (Vec<String>, Result<()>) tuple. Skipped: the lines are needed on success and on failure alike. A struct for two fields used in one place adds nothing.
  • Primitive Obsession: the lines are pre-formatted Strings. Skipped: they are only ever displayed, and sharing the text with the stderr summary is the point.
  • Mysterious Name: landed / landing hold Result<()>. Skipped: they read as "whether the Tickets landed", which is what they hold.
  • Repeated Switches: the if-cascade in summarize. Skipped: the cascade was already there. This branch only adds arms to it, and reshaping it is a separate refactor.
  • Test helpers run_emailing / the_one_notification are not used by the older ticket_runs_send_no_run_notification. Skipped: that test asserts no request at all, so it doesn't fit the_one_notification, and it's out of scope here.

Spec

  • Tickets closed before the Spec run get no line. Skipped: the spec asks for lines "as in the stderr summary", and that summary leaves them out too.
  • On an interrupt, or on success, the Ticket lines are not printed on stderr. Skipped: the spec doesn't ask for it, and stderr stays as it was.
  • A Spec run that fails before any Ticket runs (e.g. the Spec branch push fails) sends no Ticket lines. Skipped: there are no Ticket outcomes to report yet.
  • A Ticket whose Run could not be waited on (an error rather than an outcome) shows as not started. Skipped: the Spec run's cause names the error, and this path is an internal failure, not a Ticket outcome.

Closes #148

🤖 Generated with Claude Code

JacobStephens2 and others added 5 commits September 30, 2026 02:17
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ossary

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30T03:13:03Z, 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:13
JacobStephens2 and others added 2 commits September 30, 2026 11:57
Parallel Tickets from main now carry each Ticket's outcome, Interrupted
included, out of the Spec run for its notification.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t started"

Its line read "blocked by " with no blockers. Also name the notification in
the Run notification glossary entry, and say any error, not only an
interrupt, keeps the Ticket lines off stderr.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@JacobStephens2
JacobStephens2 marked this pull request as ready for review September 30, 2026 12:21
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.

One Run notification per Spec run

1 participant