Skip to content

feat: send a deterministic email digest after each pipeline run - #59

Merged
loadinglucian merged 3 commits into
mainfrom
feat/autorelease-email-digest
Aug 4, 2026
Merged

feat: send a deterministic email digest after each pipeline run#59
loadinglucian merged 3 commits into
mainfrom
feat/autorelease-email-digest

Conversation

@loadinglucian

@loadinglucian loadinglucian commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

What

Adds an end-of-pipeline TL;DR email: one fixed-template message per completed PHP autorelease watcher or Autorelease publish transaction run, delivered through Resend.

  • autorelease/_state.py: new pure email_digest function — a deterministic template table keyed on run conclusion, watch decision, admitted plan action, and release transaction state. Every interpolated value is validated against the existing shape rules (ACTION_KEY_RE, SHA256_RE, STABLE_VERSION_RE), so no model-authored prose can reach the outbound channel; an outcome with no template is rejected instead of guessed at.
  • autorelease/control.py: exposes it as the email-digest subcommand.
  • .github/workflows/autorelease-email.yml: new workflow_run-triggered workflow. Downloads the triggering run's retained artifact (investigation state or transaction state), renders the digest, and posts it to the Resend API. Skips quietly while RESEND_API_KEY, AUTORELEASE_EMAIL_FROM, or AUTORELEASE_EMAIL_TO is unset, so it can merge ahead of provisioning.
  • tests/test_autorelease.py: data-driven coverage of every template plus rejection of unroutable or unvalidated run state.
  • AUTORELEASE.md, docs/repository-settings.md: document the channel and the required secret/variables.

Why

Actions failure email and the deduplicated issues only speak up when something goes wrong. For an unattended system, silence is ambiguous between "healthy, no change" and "the schedule silently stopped" — the daily digest turns the quiet days into a positive heartbeat.

Verification

  • ./scripts/test.sh passes locally (36 tests, public-language check included).
  • Rendered today's real watcher run (30889304134) through the CLI: selects no_change_reviewed with the correct manifest digest and run link.

Protected controls will be red by design (owner-authored protected-path change); judge by Script checks.

Summary by CodeRabbit

  • New Features

    • Added automatic email digests after watcher and publish runs, including quiet runs.
    • Digests use consistent templates with outcomes, relevant details, and links.
    • Added safe fallback summaries for rejected or unrecognized run results.
    • Notifications are skipped safely when required email settings are incomplete.
  • Documentation

    • Added setup instructions for the sender, recipient, API key, and verified sending domain.
  • Bug Fixes

    • Added validation to prevent malformed or unsupported run results from generating misleading notifications.

One fixed-template TL;DR email per completed watcher or publish run,
rendered by the new email-digest control subcommand from retained run
state only and delivered through Resend. The workflow skips quietly
until the RESEND_API_KEY secret and the email variables exist.
@cursor

cursor Bot commented Aug 4, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_50f9b20f-1422-47a8-b4cd-a72ca868e8fd)

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1573adf0-60e4-48ec-8ceb-fc0e312d5f50

📥 Commits

Reviewing files that changed from the base of the PR and between e2f1f34 and 194e274.

📒 Files selected for processing (3)
  • .github/workflows/autorelease-email.yml
  • autorelease/_state.py
  • tests/test_autorelease.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • .github/workflows/autorelease-email.yml
  • tests/test_autorelease.py
  • autorelease/_state.py

📝 Walkthrough

Walkthrough

Adds deterministic email digests for completed watcher and publish runs. The CLI renders validated fixed templates from retained state. A GitHub Actions workflow sends the digest through Resend when configuration is complete.

Changes

Email digest delivery

Layer / File(s) Summary
Digest templates and validation
autorelease/_state.py, tests/test_autorelease.py
Adds fixed templates for watcher and publish outcomes. Validates workflow metadata, URLs, repository names, versions, decisions, actions, and transactions. Tests cover supported and rejected inputs.
Digest CLI command
autorelease/control.py, tests/test_autorelease.py
Adds the email-digest command. It loads available state files, renders the digest, and prints JSON output. Control errors produce sanitized fallback JSON.
Workflow delivery and configuration
.github/workflows/autorelease-email.yml, AUTORELEASE.md, docs/repository-settings.md
Adds completion triggers, retained-state retrieval, configuration gating, digest rendering, and Resend delivery. Documents the required repository settings and skip behavior.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Run as Watcher or publish workflow
  participant Email as autorelease-email workflow
  participant CLI as email-digest
  participant Resend as Resend API
  Run->>Email: Complete with workflow metadata
  Email->>Email: Retrieve retained state
  Email->>CLI: Render fixed-template digest
  CLI-->>Email: Return subject and body
  Email->>Resend: Send email digest
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: sending deterministic email digests after pipeline runs.
Description check ✅ Passed The description explains the change, rationale, implementation, and test results, but omits the Security and licensing section and several verification checklist items.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/autorelease-email-digest

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/autorelease-email.yml:
- Around line 104-108: Update the curl invocation that POSTs to
https://api.resend.com/emails to include bounded --connect-timeout and
--max-time options, ensuring stalled connections or requests fail promptly while
preserving the existing authentication, payload, and error-reporting flags.

In `@autorelease/_state.py`:
- Around line 236-248: In the decision handling around the report decision,
validate decision["modelCall"] with isinstance(..., bool) before the
quiet_day/template selection; reject missing or non-boolean values rather than
relying on truthiness, while preserving the existing behavior for explicit True
and False values.
- Around line 251-254: Update the plan validation around action and action_key
so each permitted action requires its corresponding actionKey prefix before
deriving version; do not rely solely on ACTION_KEY_RE.fullmatch. Preserve the
special original-action key contract for reconcile_partial, and reject
mismatched combinations such as new_patch with a repair key.
- Around line 195-225: Update the transaction-state handling around the
released/conclusion branches to require a non-null transaction when conclusion
is "success", validate that transaction["released"] is an exact boolean, and
require it to be True for successful runs. Reject inconsistent successful state
instead of falling through to the publish_failed email, while preserving the
existing handling for non-success conclusions.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1b8a66c2-2c65-48cc-a2c0-3a911b1d4f2e

📥 Commits

Reviewing files that changed from the base of the PR and between caf8eca and 7e06443.

📒 Files selected for processing (6)
  • .github/workflows/autorelease-email.yml
  • AUTORELEASE.md
  • autorelease/_state.py
  • autorelease/control.py
  • docs/repository-settings.md
  • tests/test_autorelease.py

Comment thread .github/workflows/autorelease-email.yml
Comment thread autorelease/_state.py
Comment thread autorelease/_state.py
Comment thread autorelease/_state.py
Run state that matches no template, including a corrupt retained
artifact, now emails a fallback summary naming the exact rejection
reason. Only revalidated values are interpolated; everything else is
replaced with 'unknown'.
@cursor

cursor Bot commented Aug 4, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_a05ca6d6-7cb0-4cb1-96ac-f7f25d73e0c7)

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
autorelease/control.py (1)

185-195: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Reject missing publish state as unexpected.

The state paths are optional for failed runs, but a successful publish run cannot be classified when --transaction is absent or contains JSON null. Lines 275-279 pass that case as None; email_digest then defaults released to False and emits publish_failed. The email can state that nothing was published although the state is unknown. Require a valid transaction and boolean released for successful publish runs, then raise ControlError so email_fallback handles the case.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@autorelease/control.py` around lines 185 - 195, Update email_digest to
validate successful publish runs before classification: require a present,
non-null transaction state containing a boolean released value, and raise
ControlError when validation fails so email_fallback handles the unknown state.
Preserve the existing optional-state behavior for failed runs and avoid
defaulting missing released data to False.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@autorelease/control.py`:
- Around line 271-280: Update email_digest to validate decision.manifestDigest,
plan.actionKey, and transaction.version as strings before passing them to regex
fullmatch; raise ControlError for null or non-string values so the existing
handler emits the fallback JSON digest. Preserve normal matching behavior for
valid string fields.

---

Outside diff comments:
In `@autorelease/control.py`:
- Around line 185-195: Update email_digest to validate successful publish runs
before classification: require a present, non-null transaction state containing
a boolean released value, and raise ControlError when validation fails so
email_fallback handles the unknown state. Preserve the existing optional-state
behavior for failed runs and avoid defaulting missing released data to False.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c998e06a-5311-474c-adfe-5cde13730f97

📥 Commits

Reviewing files that changed from the base of the PR and between 7e06443 and e2f1f34.

📒 Files selected for processing (4)
  • AUTORELEASE.md
  • autorelease/_state.py
  • autorelease/control.py
  • tests/test_autorelease.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • AUTORELEASE.md

Comment thread autorelease/control.py
Reject a green publish run without released transaction state instead
of emailing a false failure, require exact booleans for released and
modelCall, pin each action to its action-key family, validate field
types before regex matching so corrupt artifacts reach the fallback
instead of raising TypeError, and bound the Resend call with connect
and request timeouts.
@cursor

cursor Bot commented Aug 4, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_4e051878-7902-4c99-9312-5eca687c8ad7)

@loadinglucian

Copy link
Copy Markdown
Contributor Author

Review round complete. All five CodeRabbit findings were valid and are fixed in 194e274: bounded timeouts on the Resend call, a green publish run without released transaction state now rejects into the fallback digest instead of emailing a false failure, modelCall and released require exact booleans, each action is pinned to its action-key family, and malformed artifact fields raise ControlError so the fallback path survives corrupt state. All threads are replied to and resolved, Script checks is green, and the re-review reported nothing new. Protected controls stays red by design pending owner approval.

@loadinglucian
loadinglucian merged commit 8921801 into main Aug 4, 2026
4 of 5 checks passed
@loadinglucian
loadinglucian deleted the feat/autorelease-email-digest branch August 4, 2026 11:24
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