How it works
A heartbeat trigger has no event key: it declareskind: heartbeat with a cron expression and an optional timezone (default UTC). At apply time, auto creates one durable Temporal schedule per heartbeat trigger; each tick records a normal internal event and routes it like any other. Only the declaring agent’s heartbeat trigger can match its synthetic event key, and each tick is deduplicated by schedule and scheduled time, so missed-tick catch-ups never double-fire. See cron and webhooks.
The tick payload is small but load-bearing:
{{heartbeat.scheduledAt}} — the scheduled time, not the moment the session happened to start — so the window stays exact even when a run starts late.
The other design decision is that the reporter is read-only by construction: the mount grants contents: read, the GitHub tool surface is the read/search set, and the instructions forbid running tests or builds. A reporter that cannot write cannot break anything.
Install from the consolidated agent package
The packaged Ship Digest role is published in@auto/agents:
.auto/agents/ship-digest.yaml
slack connection name and #dev channel as optional: true wiring — apply skips it while no slack connection exists and activates it on the next apply once one does; until then the run report stands alone. Merge for GitHub Sync to apply.
The full configuration
.auto/fragments/environments/agent-runtime.yaml
.auto/agents/ship-digest.yaml
depth: 300on the mount. Analyzing a time window needs more git history than a default shallow clone; the prompt still teaches the agent togit fetch --shallow-sincewhen the window outruns the checkout.issues: writeexists only for the opt-in tracking-issue fallback described in the prompt; the default delivery writes nothing to GitHub. Tighten it toissues: readand dropissue_write/add_issue_commentif you never want that path available.- The mention trigger is a second, human entry point — an unscheduled digest on request. A manual or mention run has no
heartbeatpayload, so the request itself must define the window; the mention message tells the agent to ask for it when it is missing.
Walkthrough
1
Apply creates the schedule
On apply, auto reconciles one Temporal schedule for the
digest-heartbeat trigger. Edit the cron or timezone and re-apply, and the schedule updates; delete the trigger and the schedule goes with it.2
08:00 America/Los_Angeles: the tick fires
The schedule records a heartbeat event carrying
heartbeat.scheduledAt and routes it. The trigger’s routing: { kind: spawn } starts a fresh digest session for this tick. Duplicate ticks are impossible: the event’s dedup key is the schedule id plus the scheduled timestamp.3
The session gathers the window
The sandbox boots with
acme/widgets@main mounted (300 commits deep). The agent computes the 24-hour window ending at scheduledAt, then collects merged PRs (search_pull_requests), direct-to-main commits (git log --first-parent with explicit ISO bounds), per-PR bodies and diffs (pull_request_read), and CI runs on main (actions_list) to confirm the merges deployed.4
The digest lands twice
The session’s final message is the digest itself — readable later from the project’s sessions view. When Slack is connected, the agent also calls
chat.send once for the one-sentence summary in #dev, keeps the returned threadId, and threads the full digest as a single reply, so the channel stays scannable.Variations
- Pick your clock.
cronaccepts standard 5-field expressions (0 8 * * 1-5for weekdays) andtimezoneany IANA zone; the string is passed to the underlying scheduler verbatim. - Weekly instead of daily.
cron: 0 8 * * 1and change the prompt’s window arithmetic from 24 hours to 7 days — the window is defined entirely in the prompt. - Richer destinations. Add a hosted connection tool (Notion is a built-in provider) or any remote MCP server and publish the full digest there, letting the Slack message carry a link instead of a thread. See tools.
- Digest more than one repo. Add a second git mount and extend the prompt’s gather list; mounts merge by
mountPath, so imports and overrides compose cleanly. - Smoke test without waiting for the cron. Start a session directly from the web app with the message “Produce a digest for the last 24 hours ending now.” and confirm the report and the Slack thread. Manual runs have no
{{heartbeat.scheduledAt}}, so the message supplies the window explicitly.