I read this as a read-only failure forensics task - I’ll derive all 59 classifications from the supplied JUnit XML, then trace each cluster into fixtures, tests, production symbols, and relevant history without rerunning anything.


The XML now partitions cleanly into deterministic signatures; I’m tracing the remaining Telegram-only failures against the uncommitted v1.1.1 diff so the final labels separate current regressions from inherited v1.1.0 test debt.


I read this as a read-only failure forensics task - I classified all 59 failures from the supplied XML and traced each to its owning fixture, assertion, or production symbol.

## Result

`59 = 2 + 21 + 1 + 1 + 16 + 2 + 1 + 1 + 1 + 1 + 1 + 1 + 6 + 1 + 1 + 1 + 1`

No failure is unaccounted for or assigned twice.

### 1. Live profile package outgrew activation fixture — **invalid fixture** — 2

**Nodeids**

- `test_nutrition_startup_accepts_committed_receipt_and_canonical_path`
- `test_nutrition_live_reload_keeps_disable_and_revoke_live`

**Observed state**

Both call `activate_customer()` through the absolute live import:

`/home/cube/.hermes/profiles/physique-coach/workspace/checkin_cli`

The live package rejects activation with:

`nutrition readiness failed: adjustment_policy_missing,baseline_missing,calculation_missing,customer_restrictions_unresolved,readiness_receipt_missing,restriction_kb_missing`

**Owner**

- `tests/gateway/test_nutrition_coaching.py:94-174::_profile_registry`
- Live gate: `checkin_cli/customer_admin.py:823,893-895`
- Validation: `checkin_cli/nutrition_readiness.py:192-199`

**Smallest RED→GREEN action**

Stop constructing the obsolete partial activation layout. Reuse the live package’s canonical readiness/activation fixture or build all six required readiness artifacts before calling `activate_customer()`. Do not bypass readiness.

---

### 2. Trainer surface was specified but never implemented — **product defect** — 21

**Nodeids/patterns**

- 7 AC21 trainer cases:
  - `test_gateway_ac21_safety_matrix[SF-S6-T-SUMMARY]`
  - `[SF-S1-T]` through `[SF-S6-T]`
- `test_trainer_route_is_exactly_isolated_from_customer_and_reserved_spaces`
- `test_private_trainer_menu_uses_live_assignment_and_opaque_customer_callback`
- `test_private_trainer_selection_rejects_group_and_wrong_trainer`
- 9 `test_trainer_launcher_accepts_human_friendly_commands_and_shows_customer_date[...]` cases
- `test_registry_trainer_bridge_opens_scoped_trainer_session`
- `TestPhysiqueCheckinBridge::test_trainer_bridge_start_to_save_scopes_event_to_registered_customer`

**Observed state**

Deterministic `AttributeError`s for absent APIs:

- `open_trainer_launcher` — 8 occurrences across coordinator/bridge
- `resolve_trainer`
- `trainer_private_menu` — 2
- `handle_trainer_text` — 9
- `trainer_for`

Repository search found no definitions for any expected trainer API.

**Owner**

- `gateway/platforms/nutrition_coaching.py:2190::NutritionCoachingCoordinator`
- `gateway/platforms/nutrition_coaching.py:2251::_configure_registry`
- `gateway/platforms/physique_checkin.py:153::PhysiqueCheckinBridge`

**Smallest RED→GREEN action**

Implement the tested trainer route and bridge seam once: construct trainer-scoped bridges during `_configure_registry`, then expose exact routing, launcher, callback, private-menu, and text-command methods. Keep customer/trainer address isolation and opaque callbacks intact.

---

### 3. Obsolete weekly direct-send/prose contract — **stale assertion** — 1

**Nodeid**

- `test_due_tick_sends_weekly_summary_only_to_owner_during_pilot`

**Observed state**

`SendResult(success=False, error='client_001:2026-07-06:weekly')`.

The current weekly path requires a latest finalized coaching snapshot and creates an owner-reviewed draft via `create_weekly_review_draft`; the old fixture supplies neither. The test also pins an entire Korean report paragraph-by-paragraph, which is a prohibited prose assertion.

**Owner**

- Test: `tests/gateway/test_nutrition_coaching.py:634-674`
- Current contract: `gateway/platforms/nutrition_coaching.py:3831::create_weekly_review_draft`
- Dispatch: `gateway/platforms/telegram.py:10565-10620`

**Smallest RED→GREEN action**

Rewrite around the current reviewed-draft lifecycle: seed a finalized snapshot, assert destination and typed lifecycle state, and replace exact prose equality with structural/behavioral assertions.

---

### 4. Edit creates a child draft but test approves the parent — **stale assertion** — 1

**Nodeid**

- `test_owner_draft_lifecycle_is_canonical_and_preapproval_send_is_rejected`

**Observed state**

`edit_draft("draft-001", ...)` succeeds with child ID `ed601598207d9eec`; the test then approves old ID `draft-001`, yielding `draft_not_approvable`.

This is separate from `coach_v2_review_required`.

**Owner**

- Test: `tests/gateway/test_nutrition_coaching.py:808-825`
- Production: `gateway/platforms/nutrition_coaching.py:5514::edit_draft`

**Smallest RED→GREEN action**

Carry `edited.draft_id` into approval and send, then assert parent/child lineage rather than assuming in-place mutation.

---

### 5. Legacy draft fixtures lack mandatory Coach v2 evidence — **invalid fixture** — 16

**Nodeids**

- `test_delivery_intent_is_pending_before_transport_and_retry_never_resends`
- `test_delivered_receipt_retries_audit_without_duplicate_transport`
- `test_pending_delivery_requires_explicit_receipt_reconciliation`
- `test_corrupt_delivery_ledger_fails_closed_without_transport`
- `test_approved_edit_creates_child_revision_and_requires_reapproval`
- `test_sent_audit_write_failure_leaves_receipt_for_retry_without_resend`
- `test_corrupt_request_ledger_blocks_delivery_preparation`
- `test_production_console_factory_wires_canonical_lifecycle_and_receipt_transport`
- `test_consent_revocation_is_rechecked_at_draft_lifecycle_boundaries`
- `test_safety_hold_on_existing_draft_blocks_delivery_reservation`
- `test_approved_revision_and_canonical_evidence_are_required`
- `test_concurrent_delivery_reservation_allows_only_one_transport`
- Two accepted UTF-16 cases in `test_telegram_utf16_limit_is_checked_before_outbox_reservation`
- Two `test_console_transport_revalidates_after_reservation[...]` cases

**Observed state**

Fifteen directly or indirectly receive:

`DraftAction(... accepted=False, error='coach_v2_review_required')`

The console test receives HTTP `409` from the same approval rejection.

The shared fixture calls `create_draft(text)` and immediately `approve_draft()` without `coach_review` or `revision_binding_digest`.

**Owner**

- Fixture: `tests/gateway/test_nutrition_coaching.py:770::_approved_durable_draft`
- Gate: `gateway/platforms/nutrition_coaching.py:6195-6264::_approve_draft_locked`

**Smallest RED→GREEN action**

Upgrade the shared fixture to create a genuine Coach-v2-reviewed, revision-bound draft through the production generation seam. Do not relax `coach_v2_review_required`.

---

### 6. Customer-card tests depend on wall-clock plan eligibility — **nondeterministic test defect** — 2

**Nodeids**

- `test_manual_customer_aliases_render_one_reusable_card_with_stable_callback`
- `test_customer_start_callback_click_binds_clicked_card_and_resumes_open_draft`

**Observed state**

- Manual aliases send zero cards.
- Callback answers but edits zero messages.

Their registry plan starts `2026-07-01`; production checks the current KST date against the 28-day plan window. The XML run occurred on 2026-08-22, so transport is correctly denied.

**Owner**

- Tests: `tests/gateway/test_nutrition_coaching.py:1437-1478,1520-1569`
- Gate: `gateway/platforms/nutrition_coaching.py:2631::customer_transport_allowed`

**Smallest RED→GREEN action**

Inject a fixed in-window `kst_date_provider` into the coordinator or pass an explicit in-window day through the card/callback fixture. Never rely on wall time.

---

### 7. Scheduled card moved from flat buttons to rows — **stale assertion** — 1

**Nodeid**

- `test_scheduled_customer_card_reuses_stable_callback_without_opening_session`

**Observed state**

`prompt.buttons == ()`, while the expected callback is stored in `prompt.button_rows`.

**Owner**

- Test: `tests/gateway/test_nutrition_coaching.py:1481-1517`
- Builder: `gateway/platforms/telegram.py:6289::_nutrition_customer_card_prompt`

**Smallest RED→GREEN action**

Assert the first `button_rows` entry and callback identity. Preserve the separate assertion that no wizard draft opens.

---

### 8. `object.__new__` fixture omits initialized service state — **invalid fixture** — 1

**Nodeid**

- `test_approve_and_send_uses_fresh_action_capabilities_and_stops_on_failure`

**Observed state**

`AttributeError: 'AdaptiveOperatorService' object has no attribute 'profile_root'`

The fixture bypasses `AdaptiveOperatorService.__init__`, which initializes `profile_root`, `_session_lock`, authority paths, and replay state.

**Owner**

- Test fixture near `tests/gateway/test_nutrition_coaching.py:1940-2005`
- Constructor: `gateway/platforms/nutrition_coaching.py:9897`
- Failure: `gateway/platforms/nutrition_coaching.py:10735::_authority_session_lock`

**Smallest RED→GREEN action**

Construct `AdaptiveOperatorService` normally with a temporary profile root and minimal valid coordinator, then replace only the narrow capability collaborators needed by the test.

---

### 9. Sleep prompt test pins natural-language prose — **stale assertion** — 1

**Nodeid**

- `TestPhysiqueCheckinBridge::test_model_can_rewrite_the_current_sleep_quality_prompt_without_advancing_the_draft`

**Observed state**

Expected one old Korean sentence; current prompt renders equivalent guidance over two lines.

**Owner**

- Test: `tests/gateway/test_telegram_physique_checkin.py:379-395`
- Prompt: `gateway/platforms/physique_checkin_prompts.py:51::build_wizard_prompt`

**Smallest RED→GREEN action**

Delete exact prose equality. Assert unchanged step/version, five choices, opaque callbacks, active message binding, and no answer mutation.

---

### 10. Completion test mocks retired direct-send seam — **stale assertion** — 1

**Nodeid**

- `TestPhysiqueTelegramAdapterIngress::test_completed_customer_checkin_sends_draft_request_only_to_operator_dm`

**Observed state**

`_send_nutrition_topic` awaited zero times.

Current completion handling requires an explicit generation-card route and publishes through `_publish_nutrition_generation_card`; the fixture configures neither and asserts the old transport.

**Owner**

- Test: `tests/gateway/test_telegram_physique_checkin.py:546-580`
- Production: `gateway/platforms/telegram.py:6575::_render_nutrition_completion`

**Smallest RED→GREEN action**

Provide a valid operator-card route and assert generation-card publication to that route, not the retired direct topic send.

---

### 11. Keyboard fixture patches symbols no longer consumed — **invalid fixture** — 1

**Nodeid**

- `TestPhysiqueTelegramAdapterIngress::test_summary_keyboard_keeps_three_actions_in_one_telegram_row`

**Observed state**

Rendered keyboard rows are empty under the fake. `_physique_markup` imports Telegram button/markup classes inside the method, while the test patches module globals.

**Owner**

- Test: `tests/gateway/test_telegram_physique_checkin.py:777-799`
- Production: `gateway/platforms/telegram.py:8143::_physique_markup`

**Smallest RED→GREEN action**

Use the installed Telegram markup object or patch the actual imported `telegram.InlineKeyboardButton/InlineKeyboardMarkup` seam, then assert one row with three callback-bearing buttons.

---

### 12. Inline model-feedback expectation is obsolete — **stale assertion** — 1

**Nodeid**

- `TestPhysiqueTelegramAdapterIngress::test_saved_checkin_adds_profile_gated_model_feedback_without_generic_ingress`

**Observed state**

`_humanize_korean_copy` awaited zero times.

Current callback publication deliberately renders only finalized canonical facts; provider coaching moved to the out-of-band worker seam.

**Owner**

- Test: `tests/gateway/test_telegram_physique_checkin.py:1265-1310`
- Production: `gateway/platforms/telegram.py:8172::_render_physique_callback_prompt`

**Smallest RED→GREEN action**

Assert canonical finalized publication and no inline provider call. Test provider-backed feedback separately through the worker boundary.

---

### 13. Schedule fixture lacks newer schedule authority — **invalid fixture** — 6

**Nodeids**

- `TestNutritionScheduleDelivery::test_tick_success_persists_audited_receipt`
- `...::test_provider_timeout_is_unknown_and_never_retried`
- `...::test_delivered_receipt_restarts_into_audit_without_provider`
- `...::test_duplicate_tick_has_one_provider_delivery`
- `...::test_concurrent_ticks_share_one_durable_provider_authority`
- `...::test_daily_customer_and_weekly_owner_destinations_are_pinned`

**Observed state**

- Daily/weekly `SendResult` errors are task keys.
- Provider awaits remain zero.
- No schedule rows exist.
- The concurrency timeout occurs because the provider’s `entered` event is never reached; it is deterministic fallout, not a flaky timing failure.

`_ScheduleCoordinator.spec` lacks `schedule.daily_time`, so `_nutrition_daily_task_is_current()` rejects all daily work. Weekly publication also lacks the finalized snapshot required by `create_weekly_review_draft`.

**Owner**

- Fixture: `tests/gateway/test_telegram_physique_checkin.py:1494::_ScheduleCoordinator`
- Adapter fixture: `:1537::_schedule_adapter`
- Readiness gate: `gateway/platforms/telegram.py:10221::_nutrition_daily_task_is_current`
- Weekly gate: `gateway/platforms/nutrition_coaching.py:3831::create_weekly_review_draft`

**Smallest RED→GREEN action**

Give `_ScheduleCoordinator` a valid enabled schedule and finalized snapshot, and initialize the durable schedule fence in setup. Keep event-based concurrency synchronization; once provider entry is reachable, remove the incidental one-second failure mode in favor of a bounded event wait around the actual provider call.

---

### 14. Provider client is misclassified as Codex from configured default — **product defect** — 1

**Nodeid**

- `test_coaching_stage_uses_resolved_provider_token_parameter`

**Observed state**

The client returns `{"slots":[]}`, but `_request_physique_coaching_stage()` returns an empty `ProviderGenerationResult` with missing-metadata semantics.

The method ignores the test’s configured general provider and defaults to `physique_coach.draft_provider="openai-codex"`. `provider_result()` then requires Codex metadata that the resolved OpenAI-compatible client did not return.

**Owner**

- Test: `tests/gateway/test_telegram_physique_checkin.py:2127-2169`
- Production: `gateway/platforms/telegram.py:9586::_request_physique_coaching_stage`
- Classification branch: `gateway/platforms/telegram.py:9633::provider_result`

**Smallest RED→GREEN action**

Make provider classification follow the actual resolved client/provider contract rather than an unrelated default label. Preserve token-parameter selection and add a RED case where requested and resolved provider identities differ.

---

### 15. Daily publication does not recheck finalized revision — **product defect** — 1

**Nodeid**

- `TestStrictCoachingClosure::test_daily_publication_rechecks_revision_before_edit`

**Observed state**

`query.edit_message_text` is awaited once even though the bridge’s second snapshot differs (`80.0` → `81.0`). Production reads the snapshot only once and publishes it.

**Owner**

- Test: `tests/gateway/test_telegram_physique_checkin.py:2301-2337`
- Production: `gateway/platforms/telegram.py:8172::_render_physique_callback_prompt`

**Smallest RED→GREEN action**

Immediately before editing, reread the finalized snapshot and compare its canonical revision/digest with the frozen input. Abort publication on drift.

---

### 16. Adaptive rejection fixture uses non-awaitable `MagicMock` — **invalid fixture** — 1

**Nodeid**

- `TestStrictCoachingClosure::test_adaptive_publication_rechecks_revision_before_reservation`

**Observed state**

The intended stale-revision branch is reached, then failure handling calls:

`await query.message.reply_text(...)`

but `reply_text` is a `MagicMock`, producing:

`TypeError: object MagicMock can't be used in 'await' expression`

**Owner**

- Test: `tests/gateway/test_telegram_physique_checkin.py:2339-2372`
- Transport: `gateway/platforms/telegram.py:5960::_send_adaptive_operator_notice`

**Smallest RED→GREEN action**

Make `query.message.reply_text` an `AsyncMock`, then retain assertions that no reservation/edit occurs and a rejection notice is sent.

---

### 17. Test expects an explicitly omitted `None` keyword — **stale assertion** — 1

**Nodeid**

- `test_topic_59_dual_coach_drain_coexists_with_adaptive_operator`

**Observed state**

Expected:

`reply_text("적응형 검토 확인", reply_markup=None)`

Actual:

`reply_text("적응형 검토 확인")`

Both are equivalent for Telegram; `_send_adaptive_operator_notice` intentionally omits the keyword when it is `None`.

**Owner**

- Test: `tests/gateway/test_telegram_physique_checkin.py:2396-2426`
- Production: `gateway/platforms/telegram.py:5960-5971::_send_adaptive_operator_notice`

**Smallest RED→GREEN action**

Assert the response text and absence of a non-null markup independently; do not pin an optional `None` keyword.

## Provenance

All 59 are inherited rather than introduced by the current uncommitted v1.1.1 changes:

- HEAD is tagged `dualcoach-v1.1.0`.
- The failing test files and `nutrition_coaching.py` are unchanged from HEAD.
- The current `telegram.py` diff affects production preflight/menu registration and nutrition text edit retry handling, not the failing symbols above.
- Commit `d7425705ee` added the Coach-v2 approval, schedule-readiness, provider-metadata, and publication changes after most fixtures were introduced in `4305027f67`.
- The absolute live physique package is mutable outside this repository, so its exact upgrade commit cannot be established here; the XML proves the package loaded successfully and enforced newer readiness gates.

## Fix order

1. **Product defects:** trainer APIs (21), provider classification (1), daily revision recheck (1).
2. **Shared authority fixtures:** Coach-v2 reviewed draft helper (16), live activation readiness (2), schedule fixture (6).
3. **Determinism:** inject KST date in customer-card tests (2).
4. **Remaining fixture mismatches:** initialized adaptive service, Telegram markup seam, async rejection transport (3).
5. **Stale assertions:** weekly lifecycle/prose, child draft ID, button rows, prompt prose, generation-card publication, out-of-band feedback, omitted `None` keyword (7).

No missing-dependency or unrelated-pre-existing-defect classification is needed: imports resolved, and every failure maps to the clusters above. No files were edited and no tests were rerun.