Skip to content

feat(slack): outbound notifier for approvals, digests and readiness

isidro requested to merge claude/slack-outbound-notifications into main

Closes the operator loop: instead of asking the system with /cmo, the system tells the marketing channel when something needs attention.

  • slack_notifier.py:
    • NotifierState persisted at runtime/slack_notifications.json (notified approval ids, last digest id, last readiness status).
    • notify_pending_approvals: posts any new pending approval with a rich block (action, platform, risk, cost, id, resolve hint).
    • notify_latest_digest: posts the most recent shadow-mode digest once, with a 260-char summary.
    • notify_readiness_change: fires only when overall status transitions bucket (ready attention blocked) with per-check detail.
    • run_notifier: orchestrates the three, saves state atomically, idempotent across re-runs.
  • CLI: slack notify [--approvals --digest --readiness --all] for cron / ad-hoc runs, and slack notify-loop --interval N used by the scheduler service.
  • docker-compose now runs a dedicated notifier container that loops every CMO_NOTIFIER_INTERVAL_SECONDS (default 300s). Sleeps gracefully when Slack env vars are missing so it's safe to ship pre-provisioning.
  • 11 new tests cover state roundtrip, formatter outputs, dedup behaviour (approvals/digests/readiness), failure-path state preservation, and end-to-end run_notifier idempotence.

Verified in production: the notifier service posted the first red readiness alert to #cmoagent immediately after deploy (Slack returned ok=true, ts preserved in state). 178/178 suite green.

Merge request reports