aws-samples / aws-samples/sample-autonomous-cloud-coding-agents
Add Slack-button approvals for Cedar HITL approval gates
- Langage dominant
- TypeScript
- Étoiles
- 146
- Forks
- 46
- Merge moyen
- 3 j 10 h
- PR mergées (30 j)
- 24
Description
> **Follow-up from [PR #88](https://github.com/aws-samples/sample-autonomous-cloud-coding-agents/pull/88)** — Cedar HITL approvals work via CLI today; Slack interactivity is wired but not yet used for approvals.
## Functional description
PR #88 ships a complete Cedar HITL approval flow: when an agent attempts a sensitive operation (force push, write to .env, etc.), the task pauses in `AWAITING_APPROVAL` status and an operator must run `bgagent approve ` (or `deny`) to resume or fail it. The CLI works well for individual operators, but **for team workflows (CI bots, on-call rotations, mobile approvals) the natural surface is Slack**, not a terminal.
ABCA already has a working `SlackIntegration` construct (`cdk/src/constructs/slack-integration.ts`) with `InteractionsFn` handling `block_actions` callbacks, plus `SlackNotifyFn` posting task lifecycle events. The plumbing for "user clicks a button in a Slack message → Lambda processes the action → state update" already works for `link_account_action` and a few others. **Extending it with `approve_action:` / `deny_action:` block_actions would close the HITL loop end-to-end without any new infrastructure.**
**User-visible impact (what's missing today):**
- Operator on phone gets a Slack message: "Task abc-123 is awaiting approval (force push)." They have to switch to a terminal, run `bgagent login`, run `bgagent approve abc-123 req-456`. Friction is high enough that approvals stall.
- CI bots can't approve via Slack workflow steps (they could call the API directly, but the Slack-button surface would be the cleanest integration).
- Any team using the dashboard for visibility but the CLI for action lives in two contexts.
**With this issue resolved:**
- Slack message gains "✅ Approve" / "❌ Deny" buttons.
- Click triggers `block_actions` callback, Lambda extracts the user identity (already mapped via `SlackUserMappingTable`), calls the same `approve-task` / `deny-task` handlers the CLI uses.
- Operator never leaves Slack.
## Technical context
**Existing plumbing:**
- `cdk/src/handlers/slack-interactions.ts` — handles incoming `block_actions` payloads. Already validates Slack signatures, extracts the action_id, maps the Slack user to an ABCA user via `SlackUserMappingTable`. Adding new action_ids is a switch-case.
- `cdk/src/handlers/shared/slack-blocks.ts` — Block Kit template helpers. Has `taskCreated`, `taskCompleted`, etc.; needs an `approvalRequest` template that includes the buttons.
- `cdk/src/handlers/slack-notify.ts` — currently called by FanOutConsumer when an `approval_requested` event is dispatched. Today it posts plain text; would need to switch to the Block Kit template.
- `cdk/src/handlers/approve-task.ts` / `deny-task.ts` — the existing handlers. Their interfaces (`POST /tasks/{id}/approve` etc.) are reusable; the Slack interactions Lambda would call into the same shared business logic via `cdk/src/handlers/shared/approval-action.ts` (need to extract the core from the HTTP handler into a shared module).
**Net new code:**
- `slackApprovalRequestBlocks(taskId, requestId, ruleIds)` in `slack-blocks.ts` (~50 LOC).
- `handleApproveAction` / `handleDenyAction` in `slack-interactions.ts` (~80 LOC each).
- Refactor approve-task.ts / deny-task.ts to extract a `processApprovalDecision(...)` shared function that both the HTTP handler and the Slack handler call (~30 LOC refactor + tests).
- Wiring: `slack-notify.ts` switches to the Block Kit template when `event.event_type === 'approval_requested'`.
**Estimated effort:** ~1 day for a single dev including tests + E2E validation against the dev stack.
## Proposed options
**Recommended path:**
1. Refactor the approve/deny handler core into `cdk/src/handlers/shared/approval-decision.ts` (no behavior change; passes existing tests).
2. Add Block Kit template + new action_id branches in `slack-interactions.ts`.
3. Update `slack-notify.ts` to use the Block Kit template for `approval_requested` events.
4. Add CDK + agent-side E2E test that simulates the Slack interaction flow end-to-end (the existing pattern from `cdk/test/handlers/slack-interactions.test.ts` extends naturally).
**Alternative considered:** building a separate Slack approval handler that bypasses the HTTP path. **Rejected** because it would diverge the auth/audit trail — both surfaces should land in the same `ApprovalsTable` row with the same fields.
## Acceptance criteria
- [ ] Operator who is logged into Slack via the existing `link account` flow can approve / deny a pending task by clicking a button in the FanOut-dispatched Slack message
- [ ] Approval row in `TaskApprovalsTable` shows `decided_by_channel: "slack"` (new optional field — extends the existing schema additively)
- [ ] Slack button click is rejected (with a friendly Slack ephemeral message) if the clicker is not the task owner — same 403 logic as the HTTP handler
- [ ] CDK unit tests cover both new action_ids; CDK construct tests verify the FanOut config
- [ ] Updated user docs section in `docs/guides/USER_GUIDE.md` showing the Slack approval flow
## Out of scope
- iOS / Android push notifications.
- Mobile-friendly approval landing page (web-form fallback for users not on Slack).
- Reassigning a stuck approval to a different user (separate issue if needed).
- Bulk approve/deny.
## References
- `cdk/src/constructs/slack-integration.ts`
- `cdk/src/handlers/slack-interactions.ts` (existing block_actions handler)
- `cdk/src/handlers/shared/slack-blocks.ts` (Block Kit template helpers)
- `cdk/src/handlers/approve-task.ts` / `deny-task.ts` (existing approval handlers)
- `docs/design/CEDAR_HITL_GATES.md` §6.5 (approval flow design)
- Slack Block Kit reference:
Guide de contribution
Ouvrir le guide de contribution
Piste de recherche
Commencez par cdk/src/handlers/slack-interactions.ts, shared/slack-blocks.ts, slack-notify.ts et approve-task.ts/deny-task.ts ; exécutez cdk/test/handlers/slack-interactions.test.ts pour comprendre la couverture existante de block_actions. Suivez le flux d’approbation actuel et le comportement partagé d’autorisation avant d’ajouter le chemin Slack. C’est terminé lorsque les deux IDs d’action, les blocs de notification Slack, l’attribution du canal, le rejet par le propriétaire, la couverture du construct, la validation E2E et la section de USER_GUIDE.md sont couverts.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Évaluation
- Stack technique
- aws, typescript
- Domaine
- api, backend, devops, documentation, testing
- Type d'issue
- Fonctionnalité
- Difficulté
- 4/5
- Temps estimé
- 3-5 jours
- Activité
- Calme
- Clarté
- Clairement spécifiée
- Accessibilité débutants
- 48/100