Skip to content

--email sends a Run notification whenever a Run ends - #121

Merged
JacobStephens2 merged 2 commits into
mainfrom
issue-114
Sep 29, 2026
Merged

JacobStephens2 merged 2 commits into
mainfrom
issue-114

Conversation

@JacobStephens2

Copy link
Copy Markdown
Owner

Summary

email / --email [<address>] makes a Run send one Run notification through Resend when it ends, whatever the outcome.

 main
   args::parse                 # email/--email, anywhere; next token is the address only if it has @ and isn't https://
   UserConfig::load
+  interrupt::install          # now before the notification checks, so no signal slips between them
+  RunNotification::new        # same local checks as email-test (address, RESEND_API_KEY); reads the issue title
   run::run  -> Reached { pr_url, log } | FailedRun { …, interrupted }
   print outcome (stderr) and PR URL (stdout)   # write errors ignored, so a closed terminal can't stop the send
+  notification.send(&ended, goal)
+    subject  [thirdshift] acme/widgets#7 <title>: ready for review | merged | failed | interrupted
+    body     Pull request / Cause / Session log / Host / Took
+    a failed send -> "warning: could not send the Run notification: <Resend's error>"

Help and README document --email, with a new "Run notifications" section.

Evidence

  • Before: thirdshift <Issue URL> --email me@example.com → exit 2, unexpected argument after the Issue URL: --email
    After: tests/run_notification.rs (11 tests, all passing) covers:
    • each of the four outcomes, including INT/TERM/HUP
    • bare --email falling back to email.to
    • the flag in every position, never taking the Issue URL as the address
    • preflight stopping before any work with no gh calls and no send
    • no flag, no send
    • 401/500 replies leaving the exit code and stdout unchanged, with a warning: line
  • tests/command_surface.rs: a repeated --email / email exits 2; help lists --email.
  • Full cargo test: all suites pass. cargo clippy --all-targets: clean.

Merge Danger

Door: two-way

Without --email, a Run behaves as before. Three changes affect every Run:

  • The SIGINT/SIGTERM/SIGHUP handler is installed a moment earlier, right after the User config loads.
  • progress::step ignores write errors instead of panicking.
  • A successful Run's stderr line is built from Goal::outcome(), which now returns the bare phrase. The text is unchanged.

Blast Radius: small

With --email, a Run makes one extra read-only gh issue view --json title call at the start.

Unaddressed findings

Standards

  • Data Clumps (pr_url + log in Reached, FailedRun, Body): Body has to hold them for rendering anyway, and it went away as a symptom once the outcome became a single interrupted field. Merging Reached and FailedRun into one type would reshape the Failed run path well beyond this issue.
  • Full Ended enum from run::run: I only partly fixed this. FailedRun now records interrupted when the Run fails, so the outcome is decided once. A new enum would add churn for no behaviour change.
  • Primitive Obsession (email: Option<Option<String>>): email.always and --no-email #115 adds no-email, which makes this three-state. The right type is clearer to choose there.
  • Hostname fallback differs ("unknown" in the failure commit, "unknown host" in email): they read differently in context: "host unknown" in a sentence, a labelled field in email, and the email wording comes from the spec.
  • CONTEXT.md says "ready" where the code says "ready for review": the issue gives the subject outcome as ready for review word for word.

Spec

  • The title is read from GitHub rather than reused from the Run's preflight: I kept this. The title is now read once at the start of the Run, not at the end, which also removes the risk of the send being blocked by a gh call that hangs. If it can't be read, it is left out, as the spec allows.

Closes #114

🤖 Generated with Claude Code

JacobStephens2 and others added 2 commits September 29, 2026 19:56
`email` / `--email`, optionally followed by an address, makes a Run send
one email through Resend however it ends: ready for review, merged,
failed or interrupted. The address and RESEND_API_KEY are checked before
any work, with the same checks as email-test; a failed send is only a
warning.

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

The interrupt handler goes in before the notification is checked, the
FailedRun records whether the Run was interrupted when it failed, and the
issue title is read at the start of the Run. Writes to a closed terminal
no longer panic before the notification is sent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@JacobStephens2
JacobStephens2 merged commit a77dcce into main Sep 29, 2026
13 checks passed
@JacobStephens2
JacobStephens2 deleted the issue-114 branch September 29, 2026 20: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.

--email sends a Run notification whenever a Run ends

1 participant