# Task 19 narrow coverage and artifact-integrity remediation

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

The historical 22-node matrix remains evidence for crash, stale-render, duplicate-send, and restart controls, but trainer-route expectations are superseded. Current v1 coverage rejects trainer callbacks and private trainer access before authority or domain mutation, routes owner review only, and retains the same deterministic event-based race tests. Candidate provenance is now the multi-root manifest covering both gateway source and the canonical loaded profile package.

## DoneClaim

Task/Specification 19 remediation is complete. The input candidate was the verified 89-path digest `7224fe79147aec3c9827997b8be4f97775abf1950e37c1d4da4b179e6fd840ea`. One regression-capable production-handler test was added, the real stale-provider race was selected, the Task 7 receipt-crash test source was admitted to the authoritative candidate, and JSON/JUnit surfaces were made exactly identical. The resulting candidate has 90 paths and digest `412a7dd5aba7da0cc7e839d520b3d91591ae201fa5e1aef0996715a2ab77d7f4`.

Machine-readable matrix: `dualcoach-task-19-matrix.json`  
Retained JUnit: `dualcoach-task-19-junit.xml`

## Final verifier reports read first

| Verifier | Verdict and finding |
|---|---|
| `st_019fe58a` automated | needs-fix: JUnit contained the unmapped approval-idempotency node; Task 7 receipt-crash source was absent from the 89-path manifest. |
| `st_019fe58b` manual | confirmed the prior 20-test runtime behavior and counters, but did not close artifact-membership/surface defects. |
| `st_019fe58c` adversarial | needs-fix: crash row omitted the real stale-provider write race; stale row did not directly exercise render-identity mismatch. |

## Before/after node table

| Matrix case | Before | After |
|---|---|---|
| `stale_revision` | `test_n3_callback_acknowledges_before_rejecting_stale_revision_or_render` exercised stale generation only. | Retained that node and added `tests/gateway/test_telegram_physique_checkin.py::TestPhysiqueTelegramAdapterIngress::test_n3_callback_rejects_stale_render_identity_before_business_mutation`. The new test sends a callback rendered for message `0x65` against clicked message `0x66`, asserts ACK occurs before stale replacement, and proves the generation reader, address/authority resolution, regenerate/approve/hold/send methods, and generation scheduler are never called. |
| `generation_worker_crash` | `test_generation_worker_restart_reclaims_only_an_expired_lease` covered expiry/reclaim but not a provider return racing a reclaimed claim. | Retained expiry/reclaim and added `tests/gateway/test_nutrition_coaching.py::test_generation_worker_stale_provider_return_cannot_persist_over_reclaimed_claim`. Four Events are installed before worker triggers and awaited with one-second bounds; stale attempt 1 cannot persist and only fresh attempt 2 creates the draft. |
| `duplicate_explicit_send` | JUnit included `test_approval_receipt_is_immutable_duplicate_idempotent_and_zero_delivery`, but matrix JSON did not map it. | JSON maps both the duplicate explicit-send/no-resend node and approval-idempotency/zero-delivery node. |
| `provider_success_before_receipt_persistence` | Real CAS/evidence node referenced `test_task7_generation_hardening.py`, which was outside the candidate. | Node retained without weakening; its source is now in both `candidate_paths` and `candidate_files`. |
| expanded restart surface | JSON used one unexpanded parametrized node while JUnit contained eight test cases. | JSON lists all eight exact parameter IDs plus the waiting-state recovery-routing node. |

## Exact final matrix surface

The ordered `exact_nodes` array in `dualcoach-task-19-matrix.json` is byte-for-byte equivalent to the ordered JUnit testcase surface. It contains 22 unique nodes:

- wrong actor/chat/topic: 3;
- stale generation and stale render identity: 2;
- duplicate callback/callback expiry shared deterministic node: 1;
- provider authentication and invalid output: 2;
- worker expiry/reclaim and real stale-provider CAS race: 2;
- eight expanded durable restart phases plus recovery routing: 9;
- provider success before receipt persistence: 1;
- duplicate explicit send plus immutable duplicate approval: 2.

Retained JUnit result: `22 passed in 0.39s`; `tests=22`, `failures=0`, `errors=0`, `skipped=0`.

## Candidate provenance

- Before: 89 paths, `7224fe79147aec3c9827997b8be4f97775abf1950e37c1d4da4b179e6fd840ea`.
- Added candidate path: `tests/gateway/test_task7_generation_hardening.py`, 45,807 bytes, SHA-256 `b6c90c3784be7cd729ea4717087d5371c187df2374e277b608a62bf09565a447`.
- Changed candidate path: `tests/gateway/test_telegram_physique_checkin.py`, 178,662 bytes, SHA-256 `2c576ced6480d5f9c732d244b4ffb4c4a0b5a0df68181938bf1efce2ff964b0a`.
- After: 90 paths, `412a7dd5aba7da0cc7e839d520b3d91591ae201fa5e1aef0996715a2ab77d7f4`.
- Raw-byte verification: all 90 JSON inventory records matched path, byte count, and SHA-256; sorted path/NUL/raw-byte/NUL recomputation matched the aggregate digest.
- JSON/Markdown manifest identity: 90 ordered raw-file rows matched exactly.

## Verification receipts

- New stale-render node before evidence rebuild: `1 passed` with warnings as errors.
- Complete exact matrix, run once from JSON `exact_nodes`: `22 passed` with warnings as errors.
- Owning suites, warnings as errors:
  - generation/delivery: `189 passed`;
  - callback: `124 passed`;
  - restart/bootstrap: `105 passed`;
  - total: `418 passed`.
- Ruff on all five owning matrix test files: pass.
- Compileall on owning production/test modules: pass.
- `git diff --check`: pass.
- Exact changed-symbol `ty` for `test_n3_callback_rejects_stale_render_identity_before_business_mutation`: zero diagnostic intersections.
- No-sleep/no-poll scan over 13 unique selected function bodies: zero hits.
- Event discipline: the only concurrent selected node is the stale-provider race; it creates four exact Events before trigger and uses four bounded `asyncio.wait_for` calls. The new render test directly awaits the handler and asserts order synchronously, requiring no timing wait.
- Privacy/forbidden-token/suppression scan: pass; no OpenAI key, Telegram token, bearer header, `Any`, cast, ignore, noqa, or pyright suppression was introduced by the new symbol.
- Manual socket-denied QA: the new render mismatch and real stale-provider race nodes passed `2 passed` while AF_INET/AF_INET6 `connect` and `connect_ex` were hard-denied.

## Artifact hashes

- JUnit SHA-256: `385ef1cb8a233e86d69c344ec56531e2cddcf8bdff8e87df78421f5e70f73aa8`.
- Matrix JSON SHA-256: `e43f6ed3b269f5aaf4324e2294170e8690c042d632b9a91efeac02ce5b576799`.
- Candidate JSON manifest SHA-256: `a47aedc2d540339fb80bba07497883f1a29b75049d3eac3e0ae7cf3fc8c8159c`.
- Candidate Markdown manifest SHA-256: `503d8b46df6324e7fbcb7e1995f0dcf65213f4edc8ab59a5ef22796e42f416b5`.

## Non-touch, cleanup, and scope deferral

- Changed only `tests/gateway/test_telegram_physique_checkin.py` plus Task 19 evidence and candidate manifests.
- No product source defect was exposed; no product source changed.
- Did not edit the plan, ledger, checkbox, Task 18 artifacts, customer configuration, or runtime state.
- No commit, push, reset, checkout, stash, clean, index operation, real network, Telegram, provider, customer, activation, or delivery action occurred.
- All unrelated dirty work was preserved. Repository status remains 119 entries with status digest `e673f0b45318d605ef07226f8fdb57fc0bc07532c10620a8f553f1854551930c`.
- Task-owned temporary command logs and socket-denial shim were removed after evidence consolidation.
- Deferred as outside this remediation: product refactors, broader type-debt cleanup, new customer behavior, additional callback framework, provider-level exactly-once claims, and Tasks 20-27 release/rehearsal work.
