# Task 18 deterministic happy-path evidence

## Trainer-free v1 amendment, 2026-08-10

The retained happy-path node now proves that trainer launch authority is unavailable before customer check-in while owner authority remains the sole review route. The old table entry saying "trainer launcher accepted" is historical evidence for the superseded candidate and is not a v1 acceptance claim. The amended node passed in the focused canonical-package run and is bound to the current multi-root candidate manifest.

## Candidate binding

- Reopened candidate digest: `5f9a98e43386c90211fdb5d18ea6b59a8fe84b1558f40ecb034a360fc3764660` (89 paths).
- Synchronized candidate digest: `7224fe79147aec3c9827997b8be4f97775abf1950e37c1d4da4b179e6fd840ea` (89 paths).
- Changed candidate path: `tests/gateway/test_nutrition_coaching.py` only.
- Test source SHA-256: `136ec8b566a8a6c8c04ca6a4731c5dbd6afb83939766171a01771bbb8bf84605`.

## Failing-first validity receipt

The exact golden-path node failed before remediation (`exit 1`, `1 failed in 0.65s`) after the provider fake first attempted to parse its input as the request contract. The prior `build_draft_generation_request` monkeypatch supplied literal `grounded-input`, so the worker returned `generation_failed`. This demonstrates that the previous fake did not validate or consume a production-built request.

After removing that monkeypatch and validating inputs at the provider boundary, the exact node passed (`1 passed`).

## Retained happy-path scenario

Node: `tests/gateway/test_nutrition_coaching.py::test_dualcoach_golden_path`

| Boundary | Durable/visible state | Card state | Telegram delivery attempts |
|---|---|---|---:|
| preflight and committed onboarding | enabled; readiness present; processing allowed | trainer launcher accepted; owner route resolved | 0 |
| activation and check-in | customer route activated; 12 answers finalized | completion creates `generation_pending` | 0 |
| automatic draft | production request validated; provider called once; state `created` | `generating` -> `created` | 0 |
| approve | durable generation state `approved` | explicit send action available | 0 |
| explicit send and receipt | delivery `sent_audited`; receipt/message ID `7001` | sent result | 1 |
| pause and disable | paused; processing and transport denied | launcher denied | 1 |

This evidence claims only this deterministic happy-path lifecycle. Negative, restart, and concurrency matrices are explicitly deferred to Task 19.

## Provider request assertions

Before returning canonical fake output, the provider boundary now verifies:

- the production-built system prompt is non-empty, distinct from model input, and carries the `response_schema` and `untrusted_context` constraints without pinning prose;
- the model input is JSON with the exact `nutrition-coach-request-v2` top-level machine contract;
- `customer_key` is the current `client_001` and `revision_binding_digest` equals the coordinator's current finalized-check-in binding;
- `current_checkin.answers` exactly equals the durable finalized 12-answer snapshot and its KST day matches;
- current targets equal the production grounding targets;
- both top-level and fenced context trust are `untrusted_customer_data`;
- the response schema has the expected machine fields, `nutrition-coach-response-v2`, and exact recommendation target keys.

Only after these assertions does the fake return canonical response JSON bound to the production grounding.

## Verification

- Retained command: `.venv/bin/python -W error -m pytest tests/gateway/test_nutrition_coaching.py::test_dualcoach_golden_path --junitxml=<evidence>/dualcoach-task-18-junit.xml -q`
- Retained result: `1 test, 0 failures, 0 errors, 0 skipped in 0.429s`.
- JUnit SHA-256: `c014ef451b86230c17b5dfbba8eb1cf82d25c578d588e567e2eef32726c2610d`.
- Owning suites, independent and warnings-as-errors: nutrition coaching `152 passed`; physique check-in `123 passed`; room bootstrap activation `18 passed`.
- Ruff, compileall, and `git diff --check`: pass.
- Exact owned-symbol ty (`test_dualcoach_golden_path`, `_profile_registry`, `_generation_worker_response`): zero diagnostics.
- Privacy and owned-symbol no-sleep/no-poll scans: pass.
- Candidate replay: all 89 raw-byte records and aggregate NUL-framed digest verified.

## Non-touch and cleanup

No product file, plan, ledger, or checkbox was edited. Existing unrelated dirty work was preserved. All IDs and payloads are synthetic; network access is limited to the ephemeral `127.0.0.1` fake. PTB and `aiohttp` are shut down in the scenario, pytest owns the temporary profile, and verification scratch files are removed.
