I read this as a deep code-quality review - I’ll map the state machine and CLI seams, trace representative and edge-case flows through implementation and tests, then return a severity-ranked PASS/FAIL report without edits.


The state and persistence map is complete. I’m now validating the highest-risk restart/cutover paths against the real CLI order and focused tests, with particular attention to whether tests exercise the composed operator flow rather than isolated stores.


I read this as a deep code-quality review - I traced the composed lifecycle, restart paths, CLI contracts, and focused tests without editing files.

# Verdict: FAIL

`blocker_count: 4`

## Severity-ranked findings

### 1. CRITICAL — `customer activate` is not replay-safe and can rebind activation to different membership evidence

- `gateway/platforms/dualcoach_admin.py:1082-1094` always performs a fresh Bot API preflight and overwrites `--staff-membership-evidence` before calling cutover.
- The preflight requires exact generation equality at `dualcoach_admin.py:944-954`.
- Cutover itself explicitly accepts the previous generation after activation at `dualcoach_activation_cutover.py:230-249`, so the two CLI contracts disagree.
- On retry after successful activation:
  - Original generation fails before cutover because the bootstrap is now one generation ahead.
  - Supplying the new generation creates new evidence, but an existing activation binding then conflicts at `dualcoach_activation_cutover.py:392-416`.
- If the first process crashes after registry activation but before bootstrap activation, retry can overwrite the evidence file and bind a different digest than the evidence which authorized the registry transaction.

The public operator command therefore cannot reliably reconcile its own partially committed operation. No test invokes `activate_customer_cutover` or `staff_membership_preflight` through the composed CLI.

### 2. CRITICAL — owner finalization is not durably replayable after registry projection

- Finalization writes five timestamp-dependent artifacts and mutates the registry before writing `ready.json`: `nutrition_onboarding_finalization.py:181-348`.
- Only afterward does the facade write `ready.json` and delete the mutable session: `nutrition_onboarding_finalize_service.py:35-58`.
- Runtime restart retries `finalizing` using a new `datetime.now(...)`, changing every timestamp-dependent digest.
- If the crash occurred after registry projection but before `ready.json`, retry computes a different artifact bundle and encounters the committed projection as a conflict rather than converging.

The finalization test at `dualcoach/profile/tests/test_nutrition_onboarding.py:593-736` only tests a clean one-shot completion; it injects no failure after projection.

### 3. HIGH — workflow transitions have no CAS or lock, allowing lost answers and replay evidence

- `submit_answer` reads the mutable workflow, advances the cursor, and writes it without acquiring `NutritionOnboardingStore.locked()`: `nutrition_onboarding.py:189-215`.
- Replay detection is only a check against the previously read document: `nutrition_onboarding_session.py:47-77`.
- Two concurrent updates can both read cursor N, both pass duplicate checks, and both write cursor N+1. Last writer wins; the first answer and consumed update disappear.
- Attestation, owner review, cancellation, and most reconciliation transitions use the same unguarded pattern.
- The concurrency test at `test_nutrition_onboarding_store.py:89-121` tests only the unrelated publication-generation CAS, not workflow mutations.

### 4. HIGH — `UNCERTAIN` customer publications are terminal with no customer/operator recovery seam

- Any transport exception or missing authoritative message ID marks the publication uncertain: `telegram_nutrition_onboarding_runtime_publication_transport.py:418-431`.
- Re-publication returns immediately for `UNCERTAIN`: lines `332-350`.
- Restart receipt recovery can only recover an existing Telegram receipt; a `DISPATCHING` record without one is also converted to uncertain at lines `378-387`.
- Operator recovery creates replacement generations only for owner-attention states, not collecting/customer cards.

Thus a transient timeout where Telegram did not commit can strand onboarding permanently. The test at `test_nutrition_onboarding_store.py:147-173` merely asserts “no retry,” effectively pinning the blocker without asserting an operational resolution path.

### 5. MEDIUM — customer-visible and CLI errors obscure the actionable failure

- After consuming an invite, package-resolution, registry, and registration failures are all reported as “invalid or expired link”: `telegram.py:12018-12036`.
- This hides a server-side outage after the token has already transitioned to `REGISTERING`.
- `dualcoach_admin.main` has no domain-error boundary around invite, activation, or membership commands: `dualcoach_admin.py:1049-1206`; malformed operator input produces a traceback rather than concise JSON/stderr.
- This contrasts with `nutrition_onboarding_cli.py:149-202`, which at least handles missing state cleanly.

### 6. MEDIUM — tests cover components but mock away the composed blockers

- Invite tests stop at `PREPARED`: `test_dualcoach_admin_customer_invite.py:67-97`.
- TTL tests stop at claim/expiry: `test_telegram_customer_bootstrap_invite_ttl.py:30-83`.
- Owner callback tests replace publication authority, membership presence, service, finalization, and routing with mocks: `test_telegram_operator_notification_recovery.py:450-527,530-632`.
- No test found for the real sequence:
  `invite -> Telegram claim -> disabled registration -> consent -> onboarding -> readiness projection -> membership preflight -> activation cutover -> ACTIVE`.
- No activation crash/restart test exists despite the implementation exposing reconciliation semantics.

## Representative scenario traces

1. **Clean happy path**
   - Invite creates `PREPARED`.
   - Private-DM claim creates `REGISTERING`.
   - Disabled registry registration advances to `AWAITING_CONSENT`.
   - Consent publication/reconciliation advances to `AWAITING_ACTIVATION`.
   - Nutrition answers, reconciliation, owner review, finalization produce readiness artifacts.
   - Membership preflight and cutover enable the registry and advance bootstrap to `ACTIVE`.
   - Individual transitions are coherent when every operation succeeds once.

2. **Restart after claim or disabled registration**
   - Durable bootstrap ledger retains `REGISTERING`/`AWAITING_CONSENT`.
   - Startup recovery at `telegram.py:5755-5823` revalidates exact disabled registration and republishes consent.
   - This path is reasonably durable and idempotent.

3. **Restart during finalization/activation**
   - Finalization crash after projection cannot replay with a new timestamp-derived artifact bundle.
   - Activation replay through the documented CLI fails generation equality or conflicts after fresh evidence overwrites.
   - This full scenario does not converge and is release-blocking.

## Five edge cases traced

1. Unclaimed invite expires exactly at 24 hours and becomes `EXPIRED`.
2. Claimed invite replay is rejected as single-use.
3. Group-chat claim and owner self-claim are rejected.
4. Stale reply/callback generation is rejected against publication authority.
5. Concurrent answers or provider-unknown publication can strand or lose valid customer progress.

## Verification

Focused tests were run in their correct package roots:

- Gateway: **41 passed**
- Profile onboarding: **33 passed**
- Total: **74 passed**

The passing tests do not exercise the four blocker paths above. No files were edited.