# Adaptive Nutrition Workflow — Consensus Revision 17

## Normative scope
This final revision supersedes conflicting details in stages 13–16; they remain normative otherwise. Planning only.

## Actual EventType adapter and root resolution
First, every event—regardless of event_type—follows `supersedes` to its root. A non-root correction inherits root classification. Exact current wire values:
- `morning_checkin` → MORNING
- `nutrition_checkin` → NUTRITION
- `workout_record` → WORKOUT
- `trainer_session_record` → TRAINER_SESSION
- `safety_audit` → SAFETY_AUDIT
- `check_in_validated` with accepted check_in payload → LEGACY_COMBINED; project its weight/sleep/readiness to morning namespace and calories/macros/meals/digestion to nutrition namespace from the single root, preserving one source ID; rejected/invalid payload is metadata only
- `history_imported` → LEGACY_COMBINED only when its typed import manifest supplies observation day/field namespace/source digest; otherwise excluded and policy-required data blocks candidate
- `correction` cannot be a root
- unknown roots excluded; policy-required unknown data blocks
Fixtures use every exact enum and every eligible writer.

## Orderable cutover and journal contracts
Canonical event stream adapter assigns a monotonic `append_sequence` in a separate strict sidecar `canonical-sequence.jsonl` under `.events.lock` and authority lock; existing legacy prefix is frozen at migration as `{legacy_event_count, legacy_prefix_sha256}`. Migration verifies and stores this prefix before enabling writers. Each post-cutover canonical append gets the next sequence through the intent protocol. Missing day mapping is legacy-derived only when the event belongs to the exact frozen prefix by index+digest. Any event beyond prefix without sequence/day mapping blocks and is recovered only from a valid intent. Event IDs are never used for ordering.

Strict journals:
- `source-day-intents.jsonl`: schema 1.0, intent_id, customer, session, flow, day, root_preimage_digest, state prepared|committed|abandoned, sequence, row_digest.
- `authority-mirror-intents.jsonl`: schema 1.0, intent_id, authority_kind, canonical_fact_id/digest, valid_from, state, adaptive_sequence, row_digest.
- global `runtime/config-epoch-journal.jsonl`: schema 1.0, epoch, config_digest, sorted customer keys, per-customer state map, prepared/committed time, row_digest.
Dedupe keys are intent_id/epoch; identical replay idempotent, conflict blocks. Row digest excludes row_digest only. `root_preimage_digest` is SHA-256 of canonical event bytes excluding event_id and append-time occurred_at, but including payload/customer/type/supersedes/session/day intent. Recovery state tables from stage 16 apply exactly.

Lock paths: customer `<root>/.authority-transition.lock`, registry `<profile>/customers/.registry.lock`, config `<profile>/runtime/.config.lock`, events `<customer>/.events.lock`, adaptive `<customer>/nutrition-plans/.adaptive.lock`. Global config fanout never holds multiple customer locks simultaneously: commit global prepared config under config lock, release it, then visit sorted customer keys one at a time and append mirror state; startup blocks adaptive actions until all current enabled customers are committed. This replaces any earlier simultaneous fanout lock interpretation and prevents inversion.

## Canonical collection ordering
ID collections sort canonical ID. Failed gates sort fixed gate-precedence then reason code. Safety reasons sort `(class,source_flow,matched_field,normalized_excerpt_digest)`. Meal slots sort slot index; foods sort food_id then serving grams. Training rows/daily targets sort KST date. Catalog candidates sort compiler score, food count, serving count, food-ID vector, serving vector. Operator notes/transcript preserve append order and are excluded from deterministic proposal digest unless explicitly typed input.

## Telegram final certainty
Telegram offers no idempotency-token absence lookup. Remove `message_absent` entirely. `confirmed_not_sent` exists only for pre-invocation local validation failure, which creates no attempt and needs no replacement reservation. Once provider invocation starts, every non-success/no-valid-single-receipt result is `delivery_unknown`, including cancellation; no automatic or operator-authorized replacement send exists in pilot. Human reconciliation may record an external receipt if found, or leave unknown permanently. Only a delivered receipt advances to sent audit.

## Signed calorie step table
Goal modes and direction:
- fat_loss: observed rate more negative than lower bound → +step; less negative/more positive than upper bound → -step.
- gain: below lower bound → +step; above upper bound → -step.
- maintain: below lower bound → +step; above upper bound → -step.
Inside inclusive band => maintain. Step sign is applied to current resolved daily target, then max-step/floor/ceiling/feasibility gates. Exact goal bands are user policy values.

## Meal-slot allocation
Policy stores ordered Meal-slot share integers summing exactly 10,000 basis points for each high/medium/low day category. Daily calories/macros are allocated by largest remainder: floor each target share, distribute residual units by descending fractional remainder then slot index. Per-slot compiler tolerance comes from policy fields `meal_slot_calorie_tolerance_percent` and `meal_slot_macro_tolerance_percent`; both use symmetric absolute deviation and inclusive boundary. Serving count formula is `(serving_grams - serving_min_grams) % serving_step_grams == 0`, within min/max; serving units are integer count relative to step. Compiler search/limits/tie order remain stage 16.

## Explicit crash/golden fixtures
- WizardStorage/session-finalization crash before intent, after intent, after canonical root, after sequence, after day mapping, after commit: expected exact journal/sequence/mapping rows and recovery result.
- check_in_validated accepted combined payload and history import manifest: expected namespace projection bytes.
- authority mirror prepared/fact/mirror/commit crashes and config fanout customer N interruption: adaptive actions blocked until recovery; exact row digests pinned.
- every signed-step row at lower/equal/upper/outside boundaries.
- Meal share remainder ties, serving min/max/step, tolerance equal/pass/fail, no-match.
- Telegram pre-call validation attempt_count=0; every post-call failure attempt_count=1,state=unknown; success attempt_count=1,one receipt,one sent audit.

## Approval boundary
Gate A remains blocked until these exact fixtures and stage 13–16 Gate A exits pass. Gates B–D additionally require reconciled user policy values and manual Telegram proof. No implementation begins before final plan approval.
