# DualCoach Task 9 Evidence

## Verdict

PASS — the generation worker claims with a lease, validates provider/revision/input,
allows at most one correction, persists one claim-bound draft, updates the staff card,
and resumes or terminates safely after failure and restart.

## Reused behavior and exact gap

Reused:

- Task 7 generation states, CAS transitions, retry budget, receipts, and digest journal;
- Task 8 atomic `generation_pending` enqueue and restart reconciliation;
- grounded input builder, schema/semantic validation, draft persistence, and
  callback/provider separation.

Confirmed gap:

- no lease-bearing worker orchestration existed;
- a crashed `generating` claim could remain stranded;
- provider readiness, bounded correction, retryable auth failure, card update, and
  privacy-safe worker logging were not owned by one resumable worker.

Rejected extras:

- startup-wide authentication preflight, which belongs to Task 10;
- new card UX or delivery behavior;
- a new worker framework;
- customer-facing features, speculative abstraction, future scaffolding, broad
  refactoring, suppressions, and manual receipt cleanup.

## Failing-first and implementation

Executor `st_019fe21e` first observed collection failure because
`DraftGenerationWorker` did not exist, then added the minimal worker and deterministic
lease clock.

The first adversarial review found a real HIGH race: after lease expiry and reclaim, a
stale worker could persist its provider result. A deterministic failing regression
reproduced stale text persistence.

The repair passes the exact typed `DraftGenerationClaim` through
`run_once -> create_draft -> _create_draft_locked -> _ensure_created_generation_locked`.
Under the owning delivery lock, persistence now checks:

- claim type and token;
- exact active claim identity and attempt;
- unexpired lease;
- finalized event and current revision.

A stale rejection neither transitions nor releases the fresh claim.

## Acceptance evidence

- Successful worker path: one provider result, one draft revision, one staff-card update.
- Invalid model output: at most one correction; second invalid result creates no draft.
- Authentication failure: `generation_failed`, gate `provider_authentication`, retryable
  action retained.
- First expired lease: safely reclaimed after restart.
- Second expiry: terminal according to the retry cap.
- Cancellation: records `worker_cancelled`.
- Stale revision and stale claim: fail closed.
- Card-update failure is bounded and logged by gate.
- Claim and receipt history remain durable; no manual deletion is required.
- Logs contain identifiers and gate names; raw draft/customer text is absent.
- Customer delivery remains zero.

## HIGH race closure

Final independent reviewer `st_019fe22f`: CONFIRMED.

- claim 1 attempt 1 expired;
- claim 2 reclaimed attempt 2;
- stale claim 1 draft write and completion failed;
- claim 2 remained `generating` and unpoisoned;
- fresh claim 2 persisted exactly one draft and completed;
- existing-draft replay with stale claim also failed.

Independent manual QA `st_019fe22e` observed the same behavior with fake providers,
one card update, zero HTTP/socket/Telegram calls, and zero customer delivery.

## Automated and type verification

- New stale-worker race: failed before repair, passed after repair.
- Focused worker suite: 9 passed.
- Full owning domain: 168 passed.
- Final manual regression: CONFIRMED.
- Ruff: PASS.
- `py_compile`: PASS.
- `git diff --check`: PASS.
- Symbol-level Task 9 type audit: 0 actionable diagnostics.
- Task 9 added no `Any`, cast, ignore, or suppression.

The broad automated verifier initially counted unrelated dirty-worktree functions as
Task 9-owned. Its range-based verdict was rejected. The final symbol-level review
covered only the owned worker, claim, lease, request-builder, and claim-forwarding/CAS
expressions.

## Candidate identity

- `gateway/platforms/nutrition_coaching.py`:
  `72a7b770266d41944fd4c699c5dac1b3c18173182f9b8d62d926f6c9c41cab58`
- `tests/gateway/test_nutrition_coaching.py`:
  `a41413cdfa6b341f8496458f34800738b6b2aaf6d6c97d42e24ffdd668909add`
- 67-path candidate digest:
  `4e7b57ef1bdee807471251486be24744efb689665d7fc6849cb09ccf2fc624fe`
- JSON manifest SHA-256:
  `233cc884a02cb2abaa79759f14ed904812c98f0565f9068ef8a72b074d32474f`
- Markdown manifest SHA-256:
  `a24dc2fd647d24db8d2661987ba541894fb6573ff5bcef9ffc92a1e4b4539993`

Stored and recomputed manifest digests agree.

## Cleanup and non-touch

- Fake providers only; no real provider, network, Telegram, activation, customer action,
  or delivery.
- Temporary manual roots and type-check output were removed.
- No runtime process or QA artifact retained.
- No commit, push, reset, stash, or clean.
- Unrelated dirty-worktree changes were preserved.

