# Dual-coach model rerun — Revision 2

## Summary
Implement the smallest customer-isolated handoff from a trainer’s first agreed session to adaptive nutrition: capture a dual-confirmed schedule reference, start a restrictive schedule-unconfirmed baseline and six-field morning check-ins, then derive a confirmed seven-day strategy only after a typed Topic-59 operator confirmation. Resolve the archived-history projection defect before fixture or synthetic-history work. Canonical events remain source facts; the existing adaptive journal remains the only strategy/review lifecycle authority. One policy-approved static operational reminder is allowed; AI/coaching content is never auto-sent.

**Reviewed immutable inputs:** planner `88a5f24a882329852fff159644ae1a5abd096a6ae0e184bd20bc99d4a01dc968`, architect `fc2d8f3161f5d9b0b1f776bb2a060afd2a7cac2d595da59a4ed3a6a7f7193036`, critic `43d73653ff9d3e7ef2a02e5834cf8b93109a6c166992e199935622976646e905`. Repository evidence: `history_imports._baseline_event()` writes `history_imported` as `archived` with KST occurrence time and no manifest; `project_canonical_events()` currently admits only `accepted|unsafe` then requires a manifest; `_reconciliation_event_plan()` already derives the KST day from `occurred_at_kst`. `AdaptiveEventStore` already journals revisions/approval/delivery; gateway `AdaptiveOperatorService` has persisted, pinned, single-use capabilities but no `schedule_confirm` action.

## Intent Diff
- **Add canonical source facts only:** immutable schedule-reference/correction and exact-reference operator-confirmation facts; six-field morning check-in facts; no canonical strategy or review-item lifecycle events.
- **Add adaptive derived lifecycle only:** baseline/confirmed strategy revisions and deterministic review candidates, each pinned to canonical source IDs/digests and policy version/digest.
- **Repair compatibility:** project validated archived schema-1 history with its KST occurrence day; introduce schema-2 typed provenance for future imports without rewriting legacy rows.
- **Preserve:** append-only correction, customer isolation, current revisions/feature epoch, approved artifacts, explicit approval/send lifecycle, and Topic-59 review ingress.

## Decision Drivers
1. Existing immutable archived rows are evidence, not malformed data: `archived` status and absent manifest are both intentional legacy shape characteristics.
2. A strategy/proposal has exactly one lifecycle ledger: `AdaptiveEventStore`; Canonical EventStore is limited to externally observed source facts and confirmations.
3. The interview requires one automatic reminder before non-response review, but only a bounded static operational template—not autonomous AI advice or strategy delivery.
4. Topic-59 authenticates review ingress; the refreshed canonical owner remains a distinct lifecycle/audit identity as required by the canonical runbook.

## Options
- **Chosen — compatible legacy reader + schema-2 future writer.** Schema-1 `history_imported` remains immutable and derives its observation day from the parsed KST `occurred_at_kst`; schema-2 adds a typed `import_manifest.observation_kst_day` that must equal that day. The reconciled source-day mapping must match the same date. This fixes both current skip conditions without a history rewrite.
- **Rejected — require manifests for all rows.** It loses immutable archived data and still fails the status gate.
- **Chosen — canonical source → adaptive derived projection.** Canonical stores schedule/check-in/confirmation facts; adaptive owns baseline/confirmed/revision/review/approval/activation/delivery lifecycle. Host sessions publish and authorize cards only.
- **Rejected — duplicate strategy/review events in Canonical EventStore.** It creates split authority with the established adaptive journal.
- **Chosen — one fenced static reminder.** A pre-approved, versioned static template is scheduler-eligible exactly once per missing-check-in window; no other automatic customer message is allowed.

## In scope / out of scope
**In:** first agreed-session reference (KST date/start time, customer and trainer confirmations, one current last-change note); schedule correction; restrictive baseline; confirmed seven-day strategy; body weight/sleep/fatigue/pain/exercise-feasibility/meal-deviation check-in; deterministic risk, safety, reminder, non-response review; typed host confirmation and Topic-59 evidence.

**Out:** trainer availability, recurring calendar, booking/sync, detailed schedule audit or raw conversation retention, direct JSONL/registry edits, synthetic history before repair, production activation, autonomous medical advice, AI/customer auto-send, and any retry after unknown delivery.

## File-level changes
- `checkin_cli/models.py`, `store.py`, and `contracts/checkin-event.schema.json`: add schema-2 typed `HistoryImportManifest`, `ScheduleReference`, and `ScheduleConfirmation` payloads/builders/discriminators. A schedule reference contains customer key, KST session date/time, dual confirmations, bounded note, predecessor/reference digest and deterministic key; confirmation contains only exact reference event ID/digest plus actor/pin digest. Corrections supersede references. Do not add canonical strategy/review payloads.
- `history_imports.py`, `adaptive_nutrition.py`, `customer_admin.py`: implement the explicit history reader/migration gate below and preserve canonical event bytes, event IDs, existing revision, and feature epoch.
- `wizard_models.py`, `wizard.py`, `customer_coaching.py`: capture the reference and six validated morning fields using registered exact customer/trainer context; bridge only a confirmed current reference to seven-day strategy input, never `CustomerSpec.schedule` reminder cadence.
- `adaptive_nutrition.py`: add source-to-adaptive projection metadata and event validation for restrictive baseline, confirmed strategy, and review candidate. Pin customer key, source event/revision digest, source-day mapping digest, policy version/digest, feature epoch, parent/revision digest, and confirmation ID where applicable.
- `customer_schedule.py`: add the sole operational-reminder policy/evaluator and receipt-fenced non-response candidate lookup; reuse its durable schedule ledger rather than a new send path.
- `gateway/platforms/nutrition_coaching.py`, `telegram.py`, `nutrition_coaching_config.py`: add typed `schedule_confirm` to `_ADAPTIVE_ACTIONS`, callback grammar, persisted capability/session schema, card rendering, and all-update-kind Topic-59 reservation. Keep `AdaptiveOperatorService` as the host authority boundary.
- Tests: extend `test_history_imports.py`, `test_adaptive_nutrition.py`, `test_event_contract.py`, `test_wizard_domain.py`, `test_customer_admin.py`, `test_customer_schedule.py`, `test_operator_console.py`; gateway `test_adaptive_nutrition.py`, `test_nutrition_coaching.py`, `test_telegram_physique_checkin.py`, and `test_telegram_group_gating.py`.

## Sequencing and dependencies
1. **Compatibility gate first.** In `project_canonical_events()` determine root type before status exclusion. Admit a terminal row when it is `accepted|unsafe`, plus an unchanged terminal schema-1 root `history_imported` only when status is `archived`, provenance source type is `historical_markdown`, its RFC3339 KST occurrence parses, and the check-in validates. For a correction rooted in that valid legacy event, use the terminal correction payload but the root day. For schema-2 history, require the typed manifest day and require it to equal the parsed root KST occurrence date. Any malformed timestamp/manifest, root/terminal conflict, correction fork, or source-day disagreement raises/fails closed; it is never silently skipped.
   Reconcile by appending only missing idempotent source-day mappings from `_reconciliation_event_plan()`’s existing KST derivation, keyed by root-event ID/preimage and current unchanged epoch. Compare mapping day and root-derived day before projection. Never edit/archive/replay canonical events, bump epoch, or generate history. This gate blocks every fixture/synthetic-history/projection follow-on.
2. **Establish aggregate ownership and recovery.** Canonical transaction appends schedule-reference/correction/confirmation source facts; adaptive creates baseline/confirmed strategy revisions and review candidates. The schedule-confirm action validates and appends the canonical confirmation first, then idempotently projects its exact source tuple into `AdaptiveEventStore`. The adaptive dedupe preimage is `(customer, source-reference-id, source-reference-digest, confirmation-id, source-day-mapping-digest, policy-version/digest, epoch, parent-digest)`. On crash after canonical append, reconciliation projects the missing adaptive row once; on crash after adaptive append, it returns that matching row. Neither ledger resolves disagreement by recency: mismatch stops and reports reconciliation required. Use runbook lock order registry/authority → adaptive lifecycle → adaptive store; revalidate pins immediately before each append.
3. **Capture and confirmation contract.** Require KST date/time, both confirmations, and bounded note before a submitted schedule reference. Only a current terminal reference can be confirmed. `schedule_confirm` is distinct from `approve`: it carries exact schedule event ID/digest (not a proposal digest), authenticated review triple/version, refreshed canonical-owner snapshot/version, config/registry/consent/activation/registration/source/epoch pins, origin message/topic, issue/expiry and nonce digest. It is one-use and is revoked on any pin change, expiry, duplicate claim, forwarded/wrong topic/user, or reference supersession. A correction invalidates all prior confirmation capabilities and confirmed-strategy eligibility.
4. **Derive baseline, risk, and strategy.** Baseline is an adaptive revision derived from readiness and contains only meal principles, hydration, protein, regularity and review state—no workout/rest/session-timed target. A valid exact confirmation projects a new immutable adaptive confirmed revision with seven categories, exception behavior, last-change note, and predecessor. Define `dual-coach-risk-v1` as an approved policy artifact whose canonical JSON digest is persisted: normalize each required morning field to `0|1|2`: weight change vs prior valid check-in `<=2%, >2–4%, >4%`; sleep `>=7h, 5–<7h, <5h`; fatigue `low, moderate, high`; pain `none, present, severe`; exercise `possible, limited, impossible`; meal deviation `none, partial, material`. Score is the integer sum; `score >= 4` (inclusive) creates `risk_score_threshold`. Pain present/severe creates `pain_override`; impossible creates `exercise_impossible_override`; both override score and hold normal strategy. Missing/invalid/ambiguous predecessor, mapping, policy artifact, or normalization creates exactly one `risk_evidence_unavailable` review and no adaptive activation/delivery. Review-candidate dedupe preimage is `(customer, evaluation KST day, reason code, policy version/digest, terminal check-in ID/digest, source strategy digest, epoch)`; candidates are adaptive rows, while host cards/sessions are presentation authority only.
5. **Bounded reminder and host wiring.** Define `dual-coach-reminder-v1`: a fixed, human-approved static Korean reminder template with digest, no free text/AI interpolation, expected check-in KST window, response correlation to a valid terminal check-in, ordinal `1`, and expiry. At window close, only this policy may use `reserve_customer_task_delivery → sending → delivered → sent_audited`; its reservation key is `(customer, expected window, template-policy digest, ordinal=1)`. Require enabled/consented eligible customer, current registry/config/authority pins, startup fence ready, and no correlated check-in immediately before reservation and provider call. A durable delivered/sent-audited receipt is the sole `reminded` evidence. `unknown`, `audit_pending`, torn/corrupt/fenced, expired, or absent receipt is terminal non-reminded: no retry, no inferred reminder, no non-response review. After delivered/sent-audited and the response deadline, create once `(customer, window, reminder fact ID, reason-version)` non-response candidate only if no correlated check-in exists. All other scheduler/baseline/strategy/risk/review paths make zero provider calls.
6. **Gateway routing and verification.** Reserve configured Topic-59 `(chat_id, topic_id)` before generic dispatch for every update kind in `telegram.py`; wrong users reject with no fallthrough. The opener remains `적응형 영양 검토`/`/adaptive_review`; no generic route or raw destination/input is added. Wire registered trainer input to only its mapped customer and publish bounded evidence cards. Automated evidence never substitutes for manual Gate-D or enables a real customer.

## Acceptance criteria
1. Existing schema-1 archived `history_imported` rows with valid KST occurrence/check-in project exactly once; malformed or disagreement cases fail closed; canonical events, current revisions, and epoch remain unchanged.
2. New schema-2 history requires matching typed manifest/root-occurrence day; source-day mapping is append-only/idempotent and agrees before projection.
3. Canonical records contain source facts only; only adaptive rows decide baseline/confirmed/revision/review/approval/activation/delivery. Crash/restart reconciliation produces one matching adaptive projection or a stop, never a recency winner.
4. A schedule is confirmed only through a typed one-use `schedule_confirm` capability bound to the exact current reference and all required authority/config/customer/epoch pins; a superseding correction invalidates it.
5. The baseline never emits workout/rest/session timing. Confirmed strategy is a later immutable adaptive revision with seven categories and the bounded note.
6. `dual-coach-risk-v1` evaluates the stated six-field table and inclusive `>=4` threshold deterministically; pain and exercise-impossible always hold/review; absent evidence fails closed; each candidate dedupes by the stated preimage.
7. Exactly one static reminder can be sent per eligible missing-check-in window through the existing fenced ledger. Unknown/audit-pending/receipt absence creates no non-response candidate and never retries. A delivered-and-audited reminder plus elapsed response deadline and no correlated check-in creates one candidate.
8. Topic-59 accepts only its configured review triple and rejects every other update/callback without generic/model/media/provider fallthrough. Customer/trainer/review triples remain isolated; provider calls remain zero until the one allowed reminder or explicit operator `send`.

## Verification
No commands, tests, formatters, implementation, fixtures, migration, activation, or provider actions are run in this planning pass. After implementation, run the canonical focused profile tests and the runbook’s gateway checks: `pytest tests/test_history_imports.py tests/test_adaptive_nutrition.py tests/test_event_contract.py tests/test_wizard_domain.py tests/test_customer_admin.py tests/test_customer_schedule.py tests/test_operator_console.py -q`; then `pytest -q`; in `/home/cube/projects/richard/hermes-agent`, `pytest -q tests/gateway/test_adaptive_nutrition.py tests/gateway/test_nutrition_coaching.py tests/gateway/test_telegram_physique_checkin.py tests/gateway/test_telegram_group_gating.py`, then `pytest -q tests/gateway` and compile the three runbook gateway modules. Add process-race/restart cases for the compatibility mapping, confirmation projection, reminder reservation/provider/receipt boundaries, unknown terminality, and A/B isolation.

## Escalation/Risk Gate
Stop before fixture generation, migration execution, host enablement, or rollout if legacy day/mapping evidence disagrees; if any schema cannot encode the typed contracts; if canonical/adaptive recovery cannot prove a unique projection; if a capability/pin/review-space check fails; or if the reminder would use unapproved dynamic text, infer delivery, retry unknown, or route through a new provider path. Escalate architecture only for a contradiction with the pinned runbook contract; hand implementation slices to executors after the compatibility gate; send the resulting immutable revision to Architect then Critic before any delivery-adjacent enablement.

## Verification Plan
| Layer | Proof |
|---|---|
| History | legacy archived, schema-2, correction, malformed, mapping disagreement, 32-process idempotency, restart; event bytes/revision/epoch unchanged |
| Ownership | crash before/after canonical confirmation and adaptive append; one adaptive revision or fail-closed reconciliation |
| Capability | wrong/expired/duplicate/forwarded/superseded/rotated pins reject with no source/adaptive/provider row |
| Risk | every normalized boundary, score 3/4, overrides, missing evidence, policy-digest change, dedupe |
| Reminder | no response before window, one delivered receipt, audit pending/unknown/restart/corrupt fence, response race, provider counts |
| Gateway | all Topic-59 update kinds, wrong user, exact trainer mapping, A/B isolation, zero calls before permitted send |

## Risks and mitigations
- **Legacy observations disappear or mutate.** Narrow schema-1 admission, root-day derivation, mapping agreement, and append-only reconciliation preserve evidence.
- **Split authority/recovery ambiguity.** Explicit canonical-source/adaptive-derived ownership, fixed lock order, source tuple dedupe, and stop-on-disagreement remove a recency fallback.
- **Confirmation authorizes stale schedule.** Exact-reference capability, complete pins, single consumption, and correction invalidation prevent it.
- **Reminder becomes an AI/send bypass.** Fixed approved template, one reservation key, existing ledger/fence, and terminal unknown semantics bound it.
- **Risk policy drift or under-triage.** Versioned artifact/digest, fixed table/formula/threshold, hard overrides, and evidence-unavailable hold make tests and operations deterministic.
- **Host route leakage.** Existing Topic-59 first-match reservation and gateway focused tests prevent generic fallthrough.
