# First-Customer Nutrition Onboarding Implementation Plan

Status: approved for execution
Tier: HEAVY — health-data schemas, private persistence, Telegram session handling, authority/activation boundaries, and cross-repository deployment are in scope.
Planner evidence: `.omo/senpi-task/completion-results/st_019fb874.txt`

## Outcome

Deliver a remote, self-service Telegram workflow that keeps room bootstrap in
`AWAITING_ACTIVATION` until all nutrition prerequisites are complete:

1. `privacy-v1` consent.
2. Customer baseline and restriction questionnaire.
3. Customer summary attestation.
4. Deterministic restriction reconciliation against a private, approved,
   versioned global safety knowledge base.
5. Scoped clinical clearance for medical/medication answers or safety holds.
6. Deterministic BMR/TDEE and 12-week calorie/macronutrient trajectory.
7. Registered trainer review.
8. Canonical owner approval.
9. Atomic disabled-customer registry projection.
10. Digest-pinned readiness receipt.
11. Existing canonical activation and cutover authority.

`READY` permits the existing activation card; it never activates a customer,
enables adaptive nutrition, enables delivery, or sends coaching.

## Source and deployment ownership

- Canonical profile source:
  `/home/cube/.hermes/profiles/physique-coach/workspace/checkin_cli`
- Deployed test profile copy:
  `/home/cube/.hermes/profiles/dualcoachtest/workspace/checkin_cli`
- Gateway source:
  `/home/cube/projects/richard/hermes-agent`
- Preserve all unrelated dirty changes. Never reset, clean, stash, or overwrite
  whole dirty files.
- Implement in the canonical profile first. Deploy only an explicit allowlist
  byte-for-byte to `dualcoachtest`, then verify `cmp`/SHA-256 parity.
- Do not commit unless the user separately authorizes it.

## Contracts

### Profile domain

Add:

- `checkin_cli/nutrition_onboarding_models.py`
  - strict frozen Pydantic models, `extra="forbid"`
  - session states, message evidence, KB, baseline, reconciliation, initial
    plan, adjustment policy, clinical/trainer/owner/readiness receipts
  - canonical decimal values encoded as JSON strings
- `checkin_cli/nutrition_onboarding_calculations.py`
  - pure normalization, restriction reconciliation, initial-plan calculation,
    adjustment-policy construction
- `checkin_cli/nutrition_onboarding_store.py`
  - private `0700` directories and `0600` files/locks
  - symlink/hardlink/owner rejection
  - flock + generation CAS + atomic replace + file/directory fsync
  - prepared/committed/uncertain publication protocol
- `checkin_cli/nutrition_onboarding.py`
  - typed service operations for start/resume, answer submission, attestation,
    clinical review, trainer review, owner review, finalization, status,
    cancellation
  - re-read registry, consent, routes, owner, feature flags, and enabled state
    before every mutation
- `checkin_cli/nutrition_onboarding_cli.py`
  - operator status/recovery, clinical review, purge, legacy migration, and KB
    seed commands
- `checkin_cli/policies/nutrition-restriction-kb-template-v1.json`
  - unapproved cited source template; runtime owner approval is separate

Modify:

- readiness modules to select `readiness-current.json`, recompute every digest,
  validate strict artifacts, and preserve the six baseline missing reason codes
- `customer_admin.py` for disabled-only idempotent projection, prepared/committed
  journal, activation receipt v2, and bounded legacy migration authority
- `adaptive_nutrition.py` only through a narrow policy adapter; reuse existing
  snapshot/proposal engines
- `__init__.py` for stable API/version exports only

### Telegram adapter

Add `gateway/platforms/telegram_nutrition_onboarding.py` as a thin adapter:

- Load the configured profile package and reject source containment/API-version
  mismatches.
- Encode callbacks as opaque `non1:` values under 64 bytes with no health data.
- Validate exact SID/generation/actor/chat/topic/message/action/membership.
- Call the profile service around provider operations.
- Use prepared/committed/uncertain publication state; never retry an unknown
  send automatically.
- Reserve onboarding callbacks and exact prompt replies before generic/LLM and
  bootstrap catch-all routing.
- Consume stale, forwarded, wrong-topic, wrong-actor, media, edited, anonymous,
  and unknown onboarding updates without mutation.

Modify the existing bootstrap/activation/cutover/nutrition modules only at the
orchestration boundaries described in the planner artifact. Preserve all
existing room/bootstrap states, `privacy-v1`, activation receipts, explicit
`--execute`, and `delivery=false`.

## Questionnaire and validation

Customer sequence after consent:

1. DOB or adult age.
2. Mifflin equation constant basis: male, female, or decline.
3. Height cm.
4. Weight kg.
5. Activity category.
6. Activity rationale/training pattern.
7. Goal: lose, maintain, gain.
8. Target weight/date or maintenance intent.
9. Allergies.
10. Intolerances.
11. Dietary/religious/ethical exclusions.
12. Disliked foods.
13. Preferred foods.
14. Medical conditions.
15. Medications and supplements.
16. Pregnancy/breastfeeding.
17. Eating-disorder risk/history.
18. Cooking access.
19. Budget band.
20. Meal count.
21. Schedule constraints.
22. Full summary and customer attestation.

Rules:

- Structured `none` is required; blank never means none.
- Age 18–120; raw DOB is discarded after deriving age and source digest.
- Height 120–230 cm; weight 30–300 kg; meal count 2–6.
- Goal date must align with the canonical plan start and supported horizon.
- Free text is NFC-normalized, whitespace-collapsed, bounded, and deduplicated.
- Underage and equation-basis decline cannot be owner-overridden.
- Unknown restrictions stay unresolved.
- Medical conditions or medications require scoped external clinical review.
- Pregnancy/breastfeeding, eating-disorder risk, or uncertainty creates a
  safety hold.
- The system records only binary nonmedical coaching clearance plus an opaque
  external reference; it never diagnoses, prescribes, or changes medication.

## Calculation and adjustment policy

- Decimal arithmetic with `ROUND_HALF_UP`.
- Mifflin-St Jeor:
  `BMR = 10*kg + 6.25*cm - 5*age + equation_constant`.
- Versioned activity factors:
  sedentary 1.200, light 1.375, moderate 1.550, very_active 1.725,
  extra_active 1.900.
- Versioned energy-density constant: 7700 kcal/kg.
- Loss <= 1.0% projected bodyweight/week; gain <= 0.5%/week.
- Reject unsafe goals instead of silently clamping them.
- Exactly 12 weekly rows; longer goals receive a 12-week partial trajectory.
- Calories round to 20 kcal and stay within 1500–4500.
- Protein 120–250 g; fat 40–150 g; macros exactly reconcile to calories.
- No food recommendation is generated during calculation.
- Weekly adjustment remains proposal-only and reuses existing deterministic
  snapshot/proposal logic:
  - two non-overlapping seven-day mean-weight windows
  - >=4 current samples, >=10 total samples, >=5 adherent days
  - no contradictory adherence evidence
  - safety holds force human review
  - 100 kcal step, seven-day cooldown, existing bounds
  - no registry replacement, activation, delivery, or Telegram send

## State and authority

Bootstrap remains:

`AWAITING_CONSENT -> AWAITING_ACTIVATION -> ACTIVE`

Independent onboarding state:

`COLLECTING -> CUSTOMER_ATTESTATION -> RECONCILING -> [SAFETY_HOLD] -> TRAINER_REVIEW -> OWNER_REVIEW -> FINALIZING -> READY`

Terminal states:

`CANCELLED`, `EXPIRED`, `CONSENT_REVOKED`, `UNSUPPORTED_MINOR`.

Any change to consent, owner, role routes, KB, baseline, calculation method,
registry projection, review scope, or safety flags makes readiness stale.

## Execution waves

### Wave 0 — Safety and source normalization

1. Capture canonical-profile, deployed-profile, gateway Git status and protected
   live-authority digests.
2. Port existing readiness modules semantically into the canonical profile.
3. Pin `NUTRITION_ONBOARDING_API_VERSION = "1.0"`.
4. Confirm the current six-reason readiness baseline is unchanged.

### Wave 1 — Failing tests in parallel

1. Schema/calculation RED:
   strict models, normalization, golden calculation vectors, unsafe goals.
2. Store/recovery RED:
   permissions, symlinks, atomicity, CAS, multiprocess race, publication
   uncertainty and restart recovery.
3. Admin/readiness RED:
   disabled-only projection, activation v2, legacy migration pinning, stale
   authority, exact registry isolation.
4. Gateway RED:
   callback codec, exact actor/chat/topic/message/generation checks, no generic
   fallback, restart and provider-unknown behavior.
5. Adaptive RED:
   readiness-policy adapter and proposal-only behavior.

Each RED command must fail for the intended missing contract, not import,
syntax, fixture, or unrelated baseline failures.

### Wave 2 — Contract GREEN

Implement strict immutable models and artifact schemas first. Run focused model
tests, Ruff, basedpyright/available LSP diagnostics, and compile checks.

### Wave 3 — Domain GREEN in parallel

1. Pure calculations/reconciliation/KB validation.
2. Store/service/recovery.
3. Customer-admin projection, activation v2, legacy migration.
4. Telegram callback codec and Korean prompt renderer against fakes.
5. KB template and operator/customer/trainer documentation.

### Wave 4 — Integration GREEN

1. Consent handoff into onboarding.
2. Exact text/callback ingress reservation.
3. Finalization into disabled registry projection.
4. Readiness-gated activation card and cutover.
5. Readiness policy into existing adaptive proposal construction.
6. End-to-end test from consent through readiness and cutover preflight.

### Wave 5 — Deployment

1. Run full canonical profile test/lint/type/compile suite.
2. Run gateway focused and full gateway suite/build.
3. Deploy the explicit feature allowlist to `dualcoachtest`.
4. Verify byte parity.
5. Stop gateway writers, create bounded legacy migration manifest, seed and
   owner-approve the global KB, enable explicit onboarding config, and restart
   exactly one systemd-managed gateway.
6. Verify protected live authority changed only by the explicitly approved
   migration/seed/config actions; no real customer data is created.

### Wave 6 — Real-surface QA

Use an isolated/disposable customer and Telegram routes, never the real or
currently enabled customer:

1. Customer completes every prompt remotely, resumes after restart, corrects an
   answer, attests, and reaches trainer review.
2. Wrong actor/topic/message, stale callback, forwarded card, duplicate update,
   media, edited update, and concurrent callbacks produce no mutation.
3. Safety-hold answers block calculation until a scoped clinical clearance.
4. Trainer changes invalidate prior attestation/reviews correctly.
5. Owner approval finalizes immutable artifacts and disabled registry projection.
6. Readiness CLI returns exit 0 only with all artifacts and `delivery=false`.
7. Activation card appears only after readiness.
8. Restart preserves readiness and activation authority.
9. Customer and trainer complete the flow without operator intervention;
   copy/step friction found in QA is fixed RED→GREEN and rerun.
10. Remove disposable state and verify no browser/server/temp/process residue.

## Success criteria

1. Happy path: a fully valid disposable customer reaches `READY`; evidence is
   profile service output, readiness CLI exit 0, Telegram transcript/screenshots,
   immutable artifact digests, and disabled delivery/activation flags.
2. Safety edges: underage, missing/declined equation basis, unknown restriction,
   unsafe goal, medical/medication input without clearance, stale KB, stale
   review, and changed consent/route all fail closed with no activation card.
3. Authority/adversarial: wrong actor/chat/topic/message/generation, duplicate
   and concurrent updates, forwarded/stale cards, unknown callbacks, and
   provider-unknown sends cannot mutate or fall through.
4. Recovery: process death at publication and registry projection boundaries
   produces exact idempotent recovery or a durable operator-required state,
   never automatic duplicate send.
5. Regression: existing bootstrap, consent, activation, cutover, adaptive,
   coaching, and delivery-disabled suites stay green.
6. UX: customer and trainer can complete the remote workflow without the owner
   coaching them through routine steps.

## Stop condition

Stop immediately when all six success criteria are green with captured RED,
GREEN, real-surface, cleanup, parity, live-safety, and self-review evidence; all
todos are reconciled; no child or QA resource remains; and no real-customer
state or Telegram delivery has been mutated.
