# Adaptive Nutrition Workflow — Consensus Revision 15

## Normative plan set
This revision, Revision 14, and Revision 13 form the normative implementation plan in that precedence order. Revision 15 supersedes only clauses it explicitly replaces. Revision 14 supplies source fold, wire/arithmetic, resolver, revision/lifecycle, transport, config/privacy, and verification details. Revision 13 remains normative for alternatives, complete meal-catalog/compiler contract, exact file/symbol map, release gates/exits, migration/rollback, observability, premortem, and acceptance not replaced here. Gate A emits shadow observations only; every personalized candidate numeric field is null and no proposal/effective/delivery event is enabled.

## Canonical source adapter
Use existing `Event.supersedes` as the sole canonical correction edge. Do not add `correction_of`. A correction's logical root is reached by following `supersedes`; its flow and observation day are inherited from that root and cannot change.

Do not add `observation_kst_day` to the strict legacy event envelope. Instead create a separate additive `nutrition-plans/source-days.jsonl` sidecar keyed by immutable root event ID with `{root_event_id, customer_key, flow, observation_kst_day, recorded_at_kst, source}`. New wizard finalization writes the canonical event first and then idempotently appends the sidecar mapping under the customer transition lock. Legacy roots without sidecar derive the date from root `occurred_at` in KST and are marked derived. Corrections always use root mapping. This preserves old-reader compatibility; no old wire changes or rollback-floor problem.

Flow is the canonical root `source_flow`. Existing generic corrections inherit root flow. MORNING is authoritative for bodyweight/sleep/readiness/general appetite/stress and morning digestion. NUTRITION is authoritative for calories/macros/Meals and nutrition-specific appetite/digestion. Namespaced projection fields preserve both (`morning.appetite`, `nutrition.appetite`, etc.); policy must name which one controls each gate and may not silently merge. Bristol/digestion concern is the union with source attribution. Observed-energy consistency uses required policy field `observed_energy_tolerance_percent`; absent/invalid disables nutrition completeness.

## Canonical bytes specification
Canonical documents are NFC-normalized Unicode and UTF-8 encoded with non-ASCII preserved (`ensure_ascii=false`), sorted object keys, compact separators, no insignificant whitespace, arrays kept in contract-defined order, LF only. Input JSON numeric tokens are parsed directly with Decimal (`parse_float=Decimal`, `parse_int=Decimal`), never through binary float.

Scales: weight kg 0.01; percentages/rates/adherence 0.01; sleep hours 0.01; calories integer kcal; macros/servings integer grams; g/kg 0.001; confidence component ratios 0.0001. Wire decimals are fixed-scale strings; integers are JSON integers. Arithmetic uses unrounded Decimal and ROUND_HALF_UP only at the named output boundary.

Identity digests exclude self ID/digest/signature and nondeterministic wall-clock fields (`created_at_kst`, `generated_at_kst`) but include effective/observation KST dates and all version/source digests. Audit event IDs add a cryptographic random nonce so repeated identical actions remain distinct; deterministic artifact IDs do not. Byte-identical acceptance applies to rebuilt `TrendSnapshotV1`, `PolicyDecisionV1`, compiled proposal payloads, and `current.json`, not append-time audit envelopes.

## Repeated-adjustment and macro decision rules
For each of seven KST dates, `resolve_active_nutrition_target(customer,date,as_of)` supplies the input target: active overlay if valid on that date, otherwise immutable baseline. A new adjustment starts from the target effective on its proposed `effective_from`; adjustments therefore accumulate only through explicit effective overlays, never from a stale baseline.

Weekly target is the sum of seven resolved daily calorie targets plus `7 * configured_daily_step`, subject to bounds and exact integer feasibility. Category objective minimizes the sum of absolute calorie deviation from policy-configured high/medium/low daily multipliers times the weekly mean, then macro deviation, with ties by category priority and date. Multipliers and category counts are policy fields and must reconcile to seven dated rows.

`calorie_adjustment_candidate` occurs only when observed weight rate is outside goal band and all confidence/adherence/cooldown gates pass. `macro_redistribution_candidate` occurs only when weight rate is inside band (so weekly calories are unchanged), training schedule coverage is complete, macro adherence meets threshold, and policy permits carb cycling; it redistributes carbs/fat within exact weekly energy while protein stays fixed. If both could apply, calorie adjustment wins and redistribution is deferred. Infeasible lattice/search yields human_review.

## Bitemporal resolver
Every overlay has valid-time `[effective_from,effective_through]` and transaction-time event sequence. Resolver accepts `kst_day` and `as_of_event_sequence`.
- Historical query evaluates authority and lifecycle as known at `as_of` and returns the overlay effective on `kst_day`; later rollback does not rewrite historical results.
- Current/future query uses latest transaction state. A rollback/deactivation event has `valid_from`; dates before it retain the historical overlay, dates at/after return baseline.
- Consent/safety/activation failure at current query returns `blocked`, not baseline, because processing itself is unauthorized/unsafe.
- Normal expiry/supersession/rollback with valid authority returns baseline.
- Multiple overlays, graph/digest/config mismatch returns blocked.
All reporting/adherence records pin both valid day and as-of sequence.

## Customer-wide transition serialization
Introduce a per-customer interprocess `authority-transition.lock` at the canonical customer root. Lock order is: authority-transition → registry/config → canonical events (`.events.lock`) → source-day/adaptive stream → adaptive delivery cache. Registry activation/disable, consent writes, canonical correction/safety writes, adaptive mutation, config epoch cutover for that customer, and delivery all participate.

The authorization linearization point is the strict provider call while this lock is held: immediately before the call, reload and revalidate registry/consent/safety/correction heads/source digest/policy/catalog/epoch/destination/revision. Changes committed before the lock are observed; authority changes wait until the call returns. The contract does not claim revocation can cancel an external call already linearized. Race tests use independent real writers and prove this ordering/deadlock freedom.

## Adaptive-only strict transport
Do not call generic `TelegramAdapter.send`. Add an adaptive-only `send_adaptive_once_strict_topic(request) -> AdaptiveTransportResult` that makes exactly one `_send_message_strict_topic` provider invocation, requires configured topic 59, never retries, never removes `message_thread_id`, and returns exactly one typed receipt.

Result taxonomy: `delivered(receipt)` only on provider-confirmed success; `confirmed_not_sent(code)` only for pre-call validation or explicit provider response proving no message accepted; `unknown(code)` for timeout, connection loss, flood-control, unclassified exception, malformed/multiple receipt, or any exception after invocation begins. Missing/multiple/fallback receipt is unknown. Replacement reservation is allowed only after `confirmed_not_sent` or explicit provider receipt lookup proving not sent; unknown never auto-resends.

Adaptive stream remains delivery authority. The cache is rebuildable execution state. Approved/effective authority loss appends `plan_held` plus `plan_superseded` when permanently invalid, and resolver blocks it.

## Feature config transaction
Add parser/model changes in `gateway/platforms/nutrition_coaching_config.py`; exact active profile config and `activation/telegram-config.template.yaml` receive typed `operator_review` fields. Implications are enforced: `delivery => activation => operator_candidates => analytics_shadow`. Invalid combination fails startup.

Cutover writes a complete temp config with incremented monotonic `feature_epoch`, fsyncs, atomically replaces config, then appends matching adaptive epoch event under authority-transition lock. Startup compares config epoch and latest epoch event. If config is ahead, append the missing epoch event after validating config; if stream is ahead or payload/digest differs, fail closed and require operator recovery. Rollback is another forward atomic replacement with higher epoch and flags disabled; epochs never decrement.

## File/order and release gates
Normatively retain Revision 13's full file map and meal compiler. Add `nutrition_coaching_config.py`, `source-days.jsonl` writer/reader, and authority-transition lock participation in `checkin_cli/store.py`, consent/admin writers, gateway coordinator, and config writer. Gate A cannot start until old/new reader fixture proves canonical event compatibility because source-day metadata is external. Gate A exit requires source sidecar idempotency, ordinary multi-flow projection, deterministic rebuild bytes, all integrity/boundary fixtures, and zero candidate fields. Gates B–D retain Revision 13 exits and remain blocked until intent reconciliation.

## Additional acceptance fixtures
- Existing `supersedes` chains with root-flow inheritance, sidecar present/absent, delayed save, old binary reading canonical events unchanged.
- Cross-flow namespaced conflicts and controlling-field policy.
- Unicode NFC/NFD, Korean text, Decimal source tokens, fixed scales, excluded timestamps, deterministic artifact digests.
- Repeated overlay adjustment, historical as-of before/after rollback, expiry/supersession/blocked authority.
- Macro redistribution eligibility and priority against calorie adjustment.
- Flag implication matrix, interrupted config/epoch transaction, monotonic rollback recovery.
- Independent safety/correction/consent writer races at transport linearization point.
- Strict-topic one-call spy proving one invocation, no retry/fallback, and typed confirmed/unknown branches.

## Intent reconciliation required
Before Gates B–D: target weekly rate by goal; adherence/tolerance; calorie step/bounds/cooldown/emergency bound; protein/fat settings; carb categories/multipliers/schedule authority; meal constraints/catalog owner; D+29–84 policy. These are not executor defaults.
