Pattern: completion notifications without a webhook server
June 3, 2026 · View on GitHub
Status: Active Captured from: epic soc-yjzp (Anthropic Managed Agents parity, May 2026) Applies to: off-API users who want "outcome complete → notify external system" behavior without standing up a webhook server.
Anthropic's 2026-05-06 Managed Agents launch shipped webhooks as a first-class primitive. Off-API users running AgentOps locally don't need a webhook server. Three patterns cover the common cases using infrastructure you already have.
Pattern A — GitHub Actions issue creation
Use when: the work is being run inside a GitHub Actions workflow (nightly dream cycle, scheduled out-of-session run, release proof harness).
The repo's .github/workflows/nightly.yml already does this for the dream-cycle
proof job — when the run completes, a step opens or updates a GitHub issue
with the result summary and links to the run logs. Same shape works for any
outcome: a job step that calls gh issue create or gh issue comment with
the structured result.
Skeleton:
- name: Notify on completion
if: always() # fires on success and failure
run: |
gh issue create \
--title "Outcome: ${{ github.workflow }} #${{ github.run_number }}" \
--body "Status: ${{ job.status }}
Result: $(cat .agents/rpi/last-result.json | jq -c)
Run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" \
--label outcome
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Trade-off: GitHub-only, asynchronous (you watch the issue), but you already have the auth and infrastructure.
Pattern B — git post-commit hook → curl
Use when: the work writes a tracked artifact (a learnings file, a
.agents/rpi/execution-packet.json, a release note) and you want to fan out
to Slack / Discord / Mattermost / a custom endpoint when that artifact lands
on a specific branch.
Skeleton (.git/hooks/post-commit, marked executable):
#!/usr/bin/env bash
set -euo pipefail
branch=$(git rev-parse --abbrev-ref HEAD)
[[ "$branch" == "main" || "$branch" == "crank/"* ]] || exit 0
# Only fire when the watched artifact actually changed in this commit.
git diff-tree --no-commit-id --name-only -r HEAD | grep -q '^\.agents/rpi/' || exit 0
payload=$(jq -nc \
--arg branch "$branch" \
--arg sha "$(git rev-parse --short HEAD)" \
--arg subj "$(git log -1 --format=%s)" \
'{text: "✅ \($branch) @ \($sha): \($subj)"}')
curl -sS -X POST -H 'Content-Type: application/json' \
-d "$payload" "$SLACK_WEBHOOK_URL" >/dev/null || true
Trade-off: scoped to the local clone (only fires on the developer / runner that ran the commit), but reaches any HTTP endpoint and runs synchronously on commit so debugging is easy.
Pattern C — substrate event tailing
Use when: the work runs unattended out of session on an orchestration
substrate (the loop dispatched on an NTM swarm, MCP, or managed-agents — a
scheduled evolve run, a recurring ao goals measure job, a wiki-forge job) and
you want a downstream consumer to react to specific job events. AgentOps ships no
daemon of its own; out-of-session execution is delegated to the substrate (see
ADR-0009), and the
substrate is where the event stream lives.
The substrate writes one JSON line per job event to its event log. A consumer process (systemd unit, tmux pane, container sidecar) tails that log and forwards the events it cares about.
Skeleton:
# Consumer running alongside the out-of-session substrate.
# Point the tail at your substrate's event log (path varies by deployment).
tail -F "$SUBSTRATE_EVENT_LOG" \
| jq -c --unbuffered 'select(.event == "job.completed" and .job.kind == "dream.run")' \
| while read -r line; do
url=$(echo "$line" | jq -r '.job.report_url // empty')
curl -sS -X POST -H 'Content-Type: application/json' \
-d "{\"text\": \"Dream cycle complete: $url\"}" \
"$SLACK_WEBHOOK_URL" >/dev/null || true
done
Trade-off: needs a long-running consumer process and the substrate to be running, but is the closest analog to a real webhook stream — every job event is visible, including failures and partial runs, with no GitHub or commit dependency.
Decision matrix
| Situation | Pattern |
|---|---|
| Work runs in GitHub Actions; you watch issues | A — GitHub Actions issue creation |
| Work writes a tracked artifact; you have a chat webhook URL | B — git post-commit → curl |
| Work runs unattended on the substrate (NTM / MCP / managed-agents); you want real-time events | C — substrate event tailing |
| You think you need a webhook server | You probably don't — start with A or B; reach for C only if you genuinely need event streaming |
What this is NOT
- Not a managed cloud service. Nothing leaves your infrastructure.
- Not a replacement for the Anthropic Managed Agents webhook primitive — if you're on the Claude Platform, use that. This pattern is for the off-API case where standing up a webhook server is more infrastructure than the problem warrants.
- Not strongly-ordered. None of the three patterns guarantees delivery; if you need that, you need a queue, not a webhook.