# Owner-Isolated Diagnostic Mode — Revision 26

## Status
Planning only. Supersedes Stage 25 in full.

## Decision summary
Implement an owner-authenticated, 60-minute maximum, current-boot diagnostic session that hot-attaches one marked isolated registry to the already-running ordinary gateway. Ordinary same-user/distinct-full-triple identity records remain legal and unchanged; only diagnostic authority, synthetic replay, isolated enabled-customer visibility and production exclusion require the session.

## Principles and options
Principles: no diagnostic inference; no production reach; bounded/read-only evidence; synthetic replay only; deployable changes exclude runtime state. Drivers: solo-owner usability, security isolation, deterministic incident handling. Chosen option is a Topic-59-authenticated hot-attached diagnostic runtime. Permanent master authority is rejected as unsafe; mandatory three accounts remains optional but is not required.

## 1. Non-ambient bootstrap and gateway admission

### Bootstrap authority
The existing running ordinary gateway and `AdaptiveOperatorService` remain the only ingress. Add explicit typed actions `diagnostic_prepare`, `diagnostic_activate`, `diagnostic_revalidate`, `diagnostic_close`; no natural-language parser and no config-only enablement.

A request enters only through the configured Topic-59 review triple and existing persisted callback/session mechanism. `AdaptiveOperatorService` refreshes the canonical owner and all live pins, then issues a one-use `DiagnosticBootstrapCapability` containing: schema/version, action, opaque capability ID/nonce digest, authenticated review triple/version digest, canonical owner triple/version digest, isolated profile digest, requested disposable-customer digest, test-bot digest, three role-destination digests, issue/expiry KST, gateway boot epoch, config/registry/consent/activation/artifact digests, predecessor and row digest. Maximum capability lifetime is 10 minutes. Raw triples/local CLI/config cannot mint it.

### Loader selection
`TelegramAdapter` starts only the ordinary coordinator. After authenticated `diagnostic_activate`, `AdaptiveOperatorService` passes the consumed bootstrap capability directly in memory to `NutritionCoachingCoordinator.attach_diagnostic_runtime(capability)`. That method calls `load_diagnostic_runtime_customer_registry`; no startup discovery/config/profile scanning can choose it. The hot-attached coordinator is keyed by diagnostic session digest and exact role destinations. Close/expiry/restart detaches it before disabling state. `nutrition_coaching_config.py` may define closed schema defaults but never contains an enable switch or authority.

### Private DM
Diagnostic mode disables legacy trainer private-DM inference. Only an exact pinned trainer `(chat_id, topic_id)` and active diagnostic role capability is accepted. Wrong DM text/callback/active binding terminates with zero downstream/provider work.

## 2. Distinct marked registry and ordinary-loader rejection

Add closed fields to the registry document schema: `registry_mode: ordinary_v1|diagnostic_isolated_v1` and optional `diagnostic_session_digest`. Existing documents default/migrate in memory to `ordinary_v1` without byte rewrite. Ordinary `load_runtime_customer_registry` and `load_committed_customer_registry` reject `diagnostic_isolated_v1`. Only `load_diagnostic_runtime_customer_registry(root, capability)` accepts it after current session/boot/spec validation.

The isolated profile has a physically separate private root and registry. During active diagnostics its one disposable customer is enabled so existing enabled-only workflow code can run. Production profile/registry is never modified. Production gateway cannot consume the marked registry even if misconfigured to its path.

Targets include `customer_coaching.py`, `customer_admin.py`, gateway constructor/service paths and exact registry domain tests.

## 3. Crash-recoverable activation/cleanup protocol

Create `checkin_cli/diagnostic_isolation.py` with append-only `data/diagnostic-isolation.jsonl` and `data/diagnostic-transitions.jsonl`. States: `prepared`, `activating`, `active`, `restart_unvalidated`, and terminal `expired|revoked|closed|recovery_required`. TTL max 60 minutes, exclusive expiry. Every transition validates schema/predecessor/digest under `profile_authority_lock`.

Atomic protocol under the authority lock:
1. append/fsync `transition_prepared` with intent, before-registry digest/bytes digest, disabled customer digest, target marked-registry digest, session/spec/boot digest;
2. atomically replace+fsync the isolated registry with marked diagnostic registry and enabled disposable customer;
3. append/fsync active session row;
4. append/fsync `transition_committed`.

Terminal cleanup reverses order safely: prepare cleanup intent with active registry digest and disabled target digest; detach gateway runtime; atomically replace+fsync registry to marked-but-disabled state; append terminal session; commit transition. It never touches production registry.

Recovery runs at gateway startup before any diagnostic attachment and on a 30-second host tick:
- prepared + registry-before => append abandoned, keep disabled;
- prepared + registry-target + missing active/commit => replace disabled registry, append `recovery_required`, provider zero;
- active from prior boot => detach, mark `restart_unvalidated`, disable registry; explicit Topic-59 `diagnostic_revalidate` creates a fresh current-boot activation transaction;
- expired active => detach, disable, append expired;
- cleanup partial => complete disable idempotently, terminalize; ambiguous digest => `recovery_required`, no attach/provider.

Every request and pre-provider boundary independently checks current time/session/boot/marked registry, so expiry is safe even if the tick is delayed.

## 4. Exact session APIs and state outcomes

Profile APIs: `prepare_diagnostic_isolation`, `activate_diagnostic_isolation`, `revalidate_diagnostic_isolation_after_restart`, `revoke_diagnostic_isolation`, `close_diagnostic_isolation`, `recover_diagnostic_isolation`, `load_active_diagnostic_isolation`.

Outcome matrix:
- no session/prepared: no diagnostic routes, zero lifecycle/provider rows;
- active current boot: exact three routes, operations allowed by capability;
- expired/revoked/closed/recovery_required: no routes, zero new lifecycle/provider rows;
- restart_unvalidated: no routes until fresh revalidation;
- stale/wrong action/owner/profile/customer/bot/destination/pin: reject before append, zero provider;
- mismatch after delivery reservation: preserve existing linked terminal unknown/provider-zero semantics;
- duplicate mutation: original typed duplicate result, no row/provider delta.

## 5. Preflight closed schema

Preserve ordinary `GateDPreflightReceipt` v1 exactly. Add `DiagnosticPreflightOptions` and `DiagnosticPreflightReceipt` schema `diagnostic_preflight_v1`.

Exact receipt fields: `schema_version`, `ready`, `reason_codes`, `checked_at_kst`, `epoch`, `checks`, `counts`, `digests`.

Exact checks: `owner_authenticated`, `same_owner_user`, `full_triples_distinct`, `destinations_distinct`, `review_space_disjoint`, `profile_isolated`, `data_root_private`, `disposable_customer_only`, `test_bot_separate`, `production_routes_absent`, `registry_marked`, `session_state_valid`, `boot_epoch_current`, `ttl_valid`, `customer_state_valid`, `feature_flags_safe`, `plan_window_current`, `schedule_fence_ready`, `artifacts_current`, `consent_current`, `activation_current`, `source_reconciled`, `reservation_baseline_clean`.

Exact counts: `customers`, `role_destinations`, `production_routes`, `reservations`, `schedule_rows`, `schedule_tombstones`, `artifact_kinds`, `session_rows`, `transition_rows`.

Exact digest keys: `owner`, `review_config`, `profile`, `registry`, `session`, `spec`, `test_bot`, `operator_destination`, `customer_destination`, `trainer_destination`, `registration`, `policy`, `catalog`, `constraints`, `source`, `consent`, `activation`, `feature_epoch`, `schedule_fence`.

Bounded reasons: `owner_invalid`, `identity_invalid`, `destination_collision`, `review_collision`, `profile_not_isolated`, `permissions_invalid`, `customer_invalid`, `test_bot_invalid`, `production_route_visible`, `registry_invalid`, `session_missing`, `session_stale`, `restart_revalidation_required`, `ttl_invalid`, `feature_flags_unsafe`, `plan_window_stale`, `schedule_invalid`, `artifact_invalid`, `consent_invalid`, `activation_invalid`, `source_invalid`, `reservation_not_clean`, `corrupt_state`.

The API is byte-for-byte read-only and exposes no raw identifier/path/text/token.

## 6. Coherent read-only incident snapshots

Add `profile_authority_read_lock` that requires the pre-existing 0600 authority lock and opens it read-only/shared without mkdir/create/chmod/write. Snapshot export additionally uses optimistic multi-ledger revision fencing, not an impossible global lock:
1. record canonical size/terminal row digest for every incident source ledger;
2. read bounded required rows through each ledger's existing validator/read lock;
3. record the same revisions again;
4. accept only if all match; otherwise retry up to 3 times then return `snapshot_unstable` without output.

No-mutation tests compare recursive file names, contents, modes, mtimes and sizes; atime is explicitly excluded because reads may update it.

Incident map:
- `incomplete_checkin`: wizard session/event ledger; bounded completed-step bitmap, expected no finalized event; fingerprint branch/state/provider=0.
- `low_adherence`: canonical observations + registration/policy; bounded adherence category/count window; fingerprint decision/operator-only/provider=0.
- `safety_hold`: canonical safety events; bounded reason enum/presence; fingerprint human-review/provider=0.
- `kst_rollover`: wizard/schedule/plan; two KST dates and logical clock boundary; fingerprint exactly one target day.
- `stale_callback|duplicate_callback`: operator session ledger + lifecycle; callback state/action/revision relation; fingerprint reject/duplicate and row/provider delta 0.
- `authority_change`: registry/consent/activation/config/session ledgers; mutation point enum and before/after digests; fingerprint no unauthorized append/provider.
- `provider_timeout_unknown|receipt_audit_pending`: delivery ledger; reservation/receipt/audit booleans and logical interruption point; fingerprint unknown or reconcile-with-provider-0.
- `reservation_race`: schedule/adaptive ledger; contender count bucket and barrier point enum; fingerprint one winner/provider=1.
- `disable_restart`: diagnostic/session/config/schedule ledgers; boot/disable relation; fingerprint detached/disabled/provider=0.

`DiagnosticAuditSnapshot` contains only generated opaque IDs, incident class, KST bounds, normalized state sequence, these bounded inputs/concurrency markers, revision/epoch/digests, booleans/counts and expected fingerprint. Export requires a fresh current-boot owner diagnostic capability. Unsupported/stale/unstable/forged requests create no file.

## 7. Synthetic replay enforcement

`materialize_diagnostic_replay` accepts only the strict snapshot and a digest-bound `DiagnosticReplayMapping`, writes only under the isolated root, rebases KST explicitly, and emits synthetic events with `provenance=diagnostic_synthetic_v1`.

Named enforcement targets:
- profile `models.py`: closed provenance enum/tag;
- `store.py`, `wizard_storage.py`/`wizard.py`, `adaptive_nutrition.py`, `customer_schedule.py`: ordinary loaders reject synthetic provenance; diagnostic replay loaders accept only with active session capability;
- `customer_coaching.py`: diagnostic registry/runtime gate;
- gateway `nutrition_coaching.py`/`telegram.py`: diagnostic context required for synthetic projections.

Materialize only the ledgers named in the incident map. Never copy source capabilities, operational row digests, approvals, destinations, receipt contents or customer text. Verify source expected fingerprint equals replay outcome fingerprint for the incident class.

## 8. Promotion manifest exact boundary

`build_diagnostic_promotion_manifest` is read-only and never deploys. Trust roots: profile source workspace and Hermes repository only. Allow repository-relative `.py`, test files, canonical docs, explicit migration source files, and code-side definitions for these schema keys only: `diagnostic_isolation.schema_version`, `max_ttl_minutes`, `allowed_incident_classes`, `require_disposable_customer`, `require_distinct_destinations`, `require_separate_test_bot`, `restart_requires_revalidation`.

Reject runtime config values, destinations/IDs, `.env`, auth/token/secret files, symlinks, traversal, customer/profile data, ledgers, snapshots, replay fixtures, capabilities, messages and generated artifacts. Manifest records file hashes, verification artifact references, required owner approval, health checks and rollback commands; deployment remains human-owned.

## 9. Files and sequencing

Profile: new `diagnostic_isolation.py`; `customer_admin.py`, `customer_coaching.py`, `models.py`, `store.py`, `wizard.py`, `wizard_storage.py`, `adaptive_nutrition.py`, `customer_schedule.py`, `__init__.py`; tests `test_customer_admin.py`, `test_customer_coaching_domain.py`, `test_customer_schedule.py`, `test_adaptive_nutrition.py`, and a new `test_diagnostic_isolation.py` for focused contracts.

Gateway: `nutrition_coaching_config.py`, `nutrition_coaching.py`, `telegram.py`; tests `test_adaptive_nutrition.py`, `test_nutrition_coaching.py`, `test_telegram_group_gating.py`, `test_telegram_physique_checkin.py`.

Docs: canonical profile `PILOT_RUNBOOK.md`, then hash-aligned pointer/HANDOFF/Korean guide/report only where affected.

One validation-coupled Ultragoal story; mandatory parallel executors by profile state/runtime, gateway bootstrap/routes, snapshot/replay/promotion, and tests/docs. Integrate before a single cleaner + verification + Architect + QA/red-team + terminal Critic gate.

## 10. Verification

Unit: transition/recovery crash points; TTL boundaries; boot fencing; ledger corruption; v1 ordinary receipt unchanged; exact diagnostic schema/reasons; ordinary loader rejects marked registry/synthetic provenance; promotion allowlist; full-tree non-mutation excluding atime.

Integration: Topic-59 bootstrap → hot attach → exact one disposable customer; config/auto-discovery cannot attach; same user at three distinct destinations; private-DM and wrong-route all-ingress provider zero; expiry tick/restart/owner/config/customer/bot/route mutations detach and disable; every partial activation/cleanup recovers fail closed.

Replay: every incident class sources exact ledgers, retries unstable reads, materializes only synthetic bounded rows and proves fingerprint equivalence.

E2E/red-team: all ingress kinds; stale/forwarded/duplicate callbacks; KST rollover; incomplete/low-adherence/safety; unknown/audit pending; thread/subprocess races; disable/restart. P2→P6 automated isolated fixture proves existing at-most-once/audited-only semantics and final flags false, delivery revoked, overlay rollback, disposable customer disabled, session closed and processes stopped. Actual Telegram/account/token/manual P2→P6/real deployment remain human-only.

Observability is typed return/local non-sensitive logs/metrics only; no Telegram diagnostic UI beyond explicit authenticated actions.

## Acceptance criteria
1. Ordinary loaders, same-user/distinct-full-triple legality and v1 preflight remain unchanged.
2. Diagnostic workflows and marked registry visibility—not identity sharing itself—require a current-boot active session and consumed owner-authenticated bootstrap capability.
3. Config alone, automatic discovery, ordinary loader, private DM or production capability can never attach diagnostics.
4. Every crash point converges to attached+active or detached+disabled; ambiguous state is recovery_required/provider zero.
5. Snapshot is coherent by revision fence, non-mutating, bounded and authorized; replay is synthetic and fingerprint-equivalent.
6. Promotion manifest contains only allowlisted reviewed changes and does not deploy.
7. Profile full/focused gateway tests and compile checks pass; unrelated failures are reported.
8. Cleaner zero blockers, Architect CLEAR/APPROVE, executor QA/red-team passed and terminal Critic OKAY.
9. Live Telegram, credentials, manual P2→P6 and real-customer deployment remain human-only.

## Intent reconciliation
The prior solo setup is valid because roles use distinct full triples/destinations even when one user owns them. The implementation adds safe diagnostic admission and isolation rather than changing ordinary identity semantics. Problems are reported and fixes requested in this development session; no natural-language Telegram diagnostic UI is added.

## ADR
Decision: Topic-59-authenticated hot-attached diagnostic runtime with marked registry, crash journal, current-boot fencing, coherent bounded snapshots, synthetic replay and allowlisted promotion manifest. Rejected: permanent master bypass and universal three-account requirement. Consequence: broader implementation/test surface, but no ordinary authorization weakening. Human owns live rehearsal and deployment.
