I read this as an advisory architecture decision - I’ll inspect the QA evidence, production plan, and relevant workflow boundaries, then return one explicit workflow with state machine and release gates.


message: WebSocket error

stop_reason: error


I read this as a definitive architecture decision - I’ll provide the selected workflow, durable lifecycle, recovery rules, migration, and release gates now.

# DualCoach decision memo

## Decision

Choose:

1. **Automatic asynchronous drafting after each eligible finalized check-in**
2. **Separate `Approve` and `Send to customer` actions**

There is no normal-path `Generate` button and no `Approve and send` composite action.

Automatic drafting never implies approval or delivery. Customer transport remains impossible until a distinct durable operator approval exists for the exact latest draft revision.

## Architecture boundary

```text
Canonical check-in EventStore
  -> durable generation request
  -> asynchronous Coach/Polish worker
  -> immutable operator draft revision
  -> Telegram review card
  -> durable Approve
  -> separate durable Send intent
  -> reservation-first Telegram transport
  -> provider receipt
  -> sent audit
```

Telegram is a command and projection surface, not authoritative state. Use the existing private ledgers, append-only events, locks, and startup draining; do not add a broker or general workflow engine.

## Viable alternatives considered

### A. Automatic draft + separate Approve/Send — recommended

- **Coupling:** Provider latency is removed from the check-in and operator callback paths.
- **Testability:** Generation, approval, and transport faults can be injected independently.
- **Migration:** Reuses the existing `nc2` created/edited/approved delivery lifecycle; adaptive composite actions must be retired.
- **Failure mode:** Automatic generation may become blocked or backlogged, but the customer check-in remains safely saved and nothing is delivered.

### B. Click-to-generate + Approve-and-send

- **Benefit:** Fewer unused provider calls and fewer final operator clicks.
- **Coupling:** A Telegram callback initiates provider work, while the final composite chains approval, activation, delivery enablement, provider I/O, receipt persistence, and publication.
- **Failure mode:** This recreates the observed callback-timeout ambiguity, orphaned generation claims, and partial composite transitions. A failed callback can conceal a committed approval or provider attempt.
- **Migration:** Closer to the current adaptive UI, but preserves two competing lifecycle models and is harder to prove safe.

The observed failures decide against B.

## Durable state machine

Each transition is append-only and idempotent by immutable check-in, revision, approval, and delivery identifiers.

```text
CHECKIN_FINALIZED
  -> GENERATION_QUEUED
  -> GENERATING
  -> DRAFT_READY(revision N)
  -> APPROVED(revision N)
  -> DELIVERY_RESERVED
  -> PROVIDER_IN_FLIGHT
  -> RECEIPT_RECORDED
  -> SENT_AUDITED
```

Alternate transitions:

```text
GENERATION_QUEUED/GENERATING
  -> GENERATION_BLOCKED_AUTH
  -> GENERATION_QUEUED after credential repair

GENERATING
  -> GENERATION_FAILED
  -> GENERATION_QUEUED by Retry generation

DRAFT_READY
  -> SUPERSEDED + DRAFT_READY(revision N+1)       # edit
  -> SUPERSEDED + GENERATION_QUEUED(revision N+1) # regenerate
  -> HELD

APPROVED
  -> SUPERSEDED + unapproved revision             # later edit/regenerate

DELIVERY_RESERVED
  -> APPROVED                                     # proven no provider invocation

PROVIDER_IN_FLIGHT
  -> DELIVERY_UNKNOWN                             # no durable receipt; terminal, no resend

RECEIPT_RECORDED
  -> AUDIT_PENDING                                # audit failure; reconcile only
  -> SENT_AUDITED
```

### Invariants

- One initial generation request per canonical finalized check-in event.
- Safety-held, disabled, stale-consent, or ineligible check-ins create a hold notice, not a customer draft.
- Approval binds operator identity, approval event, draft revision, body digest, source check-in, and current authority.
- Editing or regenerating invalidates all prior approval and send capabilities.
- Send requires the latest matching approval event. This check applies to Telegram and canonical service APIs


type: cancelled

previous_status: running

reason: No terminal memo after repeated checks and explicit final-deliverable steer; closing inconclusive and resolving from existing evidence.