Skip to content

feat(delivery): onComplete=deliver fans notifications to webhook + Slack#258

Merged
amcheste-ai-agent[bot] merged 1 commit into
developfrom
feat/delivery
Jun 2, 2026
Merged

feat(delivery): onComplete=deliver fans notifications to webhook + Slack#258
amcheste-ai-agent[bot] merged 1 commit into
developfrom
feat/delivery

Conversation

@amcheste-ai-agent
Copy link
Copy Markdown
Contributor

Summary

Adds a new lifecycle terminal action — onComplete: deliver — that routes
the team's completion signal to one or more configured destinations. Two
production senders are wired in this PR (webhook + Slack); the API also
defines email and google-drive so the surface stays stable for future
backends, but their senders return a clean ErrNotImplemented recorded
on status.delivery[] rather than panicking.

Per-target success and failure are independent: a Slack outage does not
roll the team back to Failed if the webhook succeeded. This matches
the design's best-effort posture for notifications — operators read
status.delivery[] to see exactly what landed.

API

  • LifecycleSpec.onComplete: deliver (new enum value)
  • LifecycleSpec.delivery[] — typed targets with discriminator + shared
    credentialsSecret / artifactPath / message fields
  • AgentTeamStatus.delivery[] — per-target outcome with timestamp

Scope note

The operator pod does not mount team output PVCs, so the MVP delivers a
metadata envelope (artifact list + identifiers) rather than file
payloads. Downstream systems are expected to fetch the actual artifacts
from persisted storage. A Job-based file-transfer pattern is sketched
in the design doc but deferred — the discriminator + status shape is
already there to accommodate it without a future breaking change.

Sender details

Webhook — HTTPS POST of {event, team, namespace, phase, message, artifacts} as application/json. Non-2xx is recorded as failure.

Slack — the operator loads the incoming-webhook URL from the
configured credentialsSecret (key: slack-webhook-url) at dispatch
time
, posts a formatted message with the artifact list, never holds
the URL at rest.

Tests

  • internal/delivery89% coverage. Dispatcher routing, all three
    ErrNotImplemented branches, webhook payload + non-2xx, Slack secret
    loading + missing-key + non-2xx + message formatting.
  • internal/controllerexecuteDelivery records per-target status
    (mixed success/failure), no-ops on empty / nil lifecycle,
    executeOnComplete routes the deliver case end-to-end.

Sample

config/samples/delivery-team.yaml — webhook + Slack targets, with
the Secret setup command in the leading comment.

Test plan

  • CI green (lint + unit + integration)
  • kubectl apply -f config/samples/delivery-team.yaml parses cleanly
  • make docs-api clean (regenerated)

🤖 Generated with Claude Code

…slack

Adds a new lifecycle terminal action that fans the team's completion
signal out to one or more configured destinations. Per-target success
and failure are recorded on status.delivery[] independently — a Slack
outage does not roll the team back to Failed if the webhook succeeded.

API
- LifecycleSpec.onComplete: deliver
- LifecycleSpec.delivery[] of typed targets (webhook | slack | email |
  google-drive) with per-type fields and a shared credentialsSecret /
  artifactPath / message
- AgentTeamStatus.delivery[] records per-target outcome with timestamp

Senders
- internal/delivery: Dispatcher selects a Sender per target.Type
- WebhookSender: HTTP POST of {event, team, namespace, phase, message,
  artifacts} as application/json; non-2xx → failure
- SlackSender: loads the incoming-webhook URL from credentialsSecret
  (key: "slack-webhook-url") at dispatch time and posts a formatted
  message; the operator never holds the URL at rest
- email and google-drive are declared in the API but their senders
  return ErrNotImplemented — surfaces as a clean per-target failure
  rather than a runtime panic, and unblocks API stability for future
  backends

Scope note: the operator pod does not mount team output PVCs, so the
MVP delivers a metadata envelope (artifact list + identifiers), not
file payloads. Downstream systems are expected to fetch the actual
artifacts from persisted storage. A Job-based file-transfer pattern
is sketched in the design doc but deferred.

Tests
- delivery package: 89% coverage — dispatcher routing, all three
  ErrNotImplemented branches, webhook payload + non-2xx, slack secret
  loading + missing-key + non-2xx + message formatting
- controller: executeDelivery records per-target status (mixed
  success/failure), no-ops on empty / nil lifecycle, executeOnComplete
  routes the deliver case end-to-end

Sample
- config/samples/delivery-team.yaml — webhook + Slack targets with the
  Secret setup command in the leading comment

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: amcheste <13696614+amcheste@users.noreply.github.com>
@amcheste-ai-agent amcheste-ai-agent Bot requested a review from amcheste as a code owner June 2, 2026 14:23
@github-actions github-actions Bot added the docs label Jun 2, 2026
@amcheste-ai-agent amcheste-ai-agent Bot merged commit 1abadf3 into develop Jun 2, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant