Repository navigation
feat(manager): WhatsApp Cloud API channel - #149
Conversation
Repository-contract fixtures and SQL conversation-creation helpers created aiosqlite engines without disposing them, causing intermittent worker-thread exceptions. Each test now owns and disposes its engines. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An opt-in idempotency key claims a durable receipt together with the user message, so a retried preparation recovers the same turn instead of appending another. Adds saved-response recovery for channel workers and migration 0007. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Provider-independent inbox, sender bindings, execution claims, webhook receipts, ordered outbox and delivery events, worker ownership, and the channel identity port, with migrations 0008-0010. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Opt-in authenticated Cloud API intake, a supervised channel turn worker with conservative recovery, provider delivery with text controls, and health reporting for a stopped worker. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Integration plan, ADRs 0007-0010, implementation tasks 0014-0017 and 0019-0020, review records, and index entries. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The worker logs one `channel status:` line per configured interval with queue counts, the oldest unfinished and unsent ages, and unknown, failed, and blocked reply counts. The read is bounded to five seconds so monitoring cannot stall scheduling or a graceful drain. Every rejected webhook request logs a fixed reason. No line contains message text, sender IDs, tokens, or provider IDs. Adds EXTRA_WHATSAPP_STATUS_LOG_SECONDS (default 60). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The runbook covers preflight, deployment, staged enablement and pausing, health and status logs, restart recovery, rollback, retention, and the real-number smoke-test checklist. The example runs agent-manager on durable storage with a tool-free assistant and an allowlist identity resolver; its port is bound to loopback because the web conversation API is anonymous by default. A fake-provider rehearsal proves that paused intake survives restarts, is answered and sent exactly once, projects a delivered callback, and is not resent after a further restart. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…dget' into codex/whatsapp-integration
…dget' into codex/whatsapp-integration
…dget' into codex/review-whatsapp-149 # Conflicts: # tasks/README.md
# Conflicts: # tasks/README.md
Asaf-prog
left a comment
There was a problem hiding this comment.
1. Summary
Reviewed webhook intake, identity, preparation, execution, delivery, controls, persistence and lifecycle. Synced the latest #148 correction and resolved tasks/README.md while preserving both task lists. One confirmed blocker remains.
2. Blocking issues
Sender-disabled composition bypasses suppression and controls; see the inline finding. In the documented worker-on/sender-off state, /stop becomes failed and a subsequent text invokes the engine and becomes answered. /new also cannot reset in this state.
3. Non-blocking issues
No additional confirmed finding in the inspected paths. Retention and operator reconciliation remain documented follow-ups.
4. Architecture concerns
Manager/provider boundaries and request-scoped identity are preserved; ADRs 0006-0010 document the added contracts. Control enforcement must run independently of outbound transport activation.
5. Security concerns
The reproduced gap permits processing after opt-out, including potential model/tool work. The reproduction used a fake engine and no provider sends. Outbound delivery still has its separate suppression guard.
6. Testing gaps
make check passed on this head: formatting, lint, mypy, 1,432 tests / 27 warnings, and generated-stub checks. A separate real-app TestClient + file-backed SQLite reproduction produced [('stop', 'failed'), ('text', 'answered')] and one engine invocation after /stop. Existing control tests instantiate ChannelControlService directly, missing its absence in sender-disabled composition. Add app-level coverage for suppression and reset with sender disabled, including transition back to live.
7. Suggested improvements
Keep suppression and state-changing controls active whenever the worker runs. Preserve startup without outbound credentials when sender is disabled.
8. Final recommendation
Request changes for the confirmed defect. This is a COMMENT review because the connected account is the PR author and cannot approve its own PR. main also requires independent approval. No main merge was performed.
| organization_id=options.organization_id, | ||
| render=render_text, | ||
| ) | ||
| controls = ChannelControlService( |
There was a problem hiding this comment.
[P1] Keep suppression and controls active while sending is paused. ChannelControlService is constructed only inside extra_whatsapp_sender_enabled, so the supported worker-on/sender-off state passes controls=None to ChannelTurnProcessor. Reproduction through create_app/TestClient with file-backed SQLite: POST a signed /stop followed by a signed text; /stop becomes failed and the text becomes answered with one engine invocation. /new also fails to reset. Enforce suppression and state-changing controls whenever the worker runs, independently of transport credentials, and add composition-level regression coverage.
feat(manager): WhatsApp pilot packaging and operations (WA-06)
cb71fd1
into
codex/fix-140-cancelled-token-budget
Adds an opt-in WhatsApp Cloud API channel for one organization-owned number, following the integration plan (WA-01 to WA-05).
Commits
test(manager): close SQL engines owned by repository tests. Fixes intermittent aiosqlite worker-thread exceptions in the full gate (task 0018).feat(manager): prepare conversation turns idempotently. An opt-in idempotency key claims a durable receipt with the user message, so a retried preparation recovers the same turn. Migration0007, ADR 0006.feat(manager): add durable channel storage. Provider-independent inbox, sender bindings, execution claims, webhook receipts, ordered outbox and delivery events, worker ownership, and the channel identity port. Migrations0008–0010.feat(manager): connect WhatsApp webhook, worker, and delivery. Authenticated webhook intake, a supervised turn worker with conservative recovery, provider delivery with text controls, and/healthreturning 503 when the worker has stopped. Adds theregexdependency (used by text rendering).docs. ADRs 0007–0010, tasks, and review records.Rollout
Everything is off by default. Three flags turn it on in order, and settings validation rejects a flag without its prerequisites:
EXTRA_WHATSAPP_ENABLED: webhook intake.EXTRA_WHATSAPP_WORKER_ENABLED: turn worker. Requires intake, an organization id, and an identity resolver plugin.EXTRA_WHATSAPP_SENDER_ENABLED: outbound delivery. Requires the worker.No real account or number has been activated yet. Customer Google/JWT account linking and pilot packaging remain planned.
Validation
make checkpasses on the final commit: 1423 tests, plus ruff and mypy.0007–0010need to run on deploy.🤖 Generated with Claude Code