# Owner-Isolated Diagnostic Mode — Revision 25

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

## Goal
Reuse the existing canonical-owner Telegram user, test bot and test group for solo rehearsal and future incident reproduction without creating a permanent master bypass. Existing ordinary registry semantics remain unchanged: roles must have distinct full Telegram triples and distinct chat/topic spaces; sharing a `user_id` is already legal when destinations differ. Diagnostic mode adds explicit isolation, TTL, restart fencing, disposable-customer visibility, bounded evidence and promotion controls—not a generalized identity-validator relaxation.

## RALPLAN-DR

### Principles
1. Ordinary production loaders and authorization remain unchanged and never infer diagnostics.
2. Diagnostic authority is an explicit, owner-authenticated, time-bounded session tied to one isolated profile, test bot, disposable customer and three distinct destinations.
3. A diagnostic session carries less reach than production: no production root, customer, bot, route, credential, capability or runtime artifact is visible or promotable.
4. Evidence export is non-mutating, bounded, de-identified and reproducible only through typed synthetic incident classes.
5. Only reviewed code, allowlisted migrations and non-secret/non-route config schema changes may become a deployment candidate.

### Drivers
Solo-owner usability; zero production bypass; deterministic incident reproduction and rollback.

### Options
- **A — Explicit isolated session and diagnostic runtime overlay (chosen):** compatible with one user and distinct destinations, strongly fenced, reusable during incidents; adds a small state machine.
- **B — Permanent master identity:** easy but unsafe and unauditable; rejected.
- **C — Always require three accounts:** strongest separation but blocks the established solo workflow; retained only as an optional realistic test topology, not required by ordinary current semantics.

## Exact architecture

### A. Diagnostic profile module
Create `checkin_cli/diagnostic_isolation.py`; export only typed high-level APIs from `checkin_cli/__init__.py`. The module owns:

- `DiagnosticIsolationSpec`: isolated profile digest, disposable customer digest, canonical-owner digest/version, test-bot digest, exact operator/customer/trainer role destinations, allowed incident classes/operations, issued/expires KST, boot epoch.
- `DiagnosticIsolationSession`: opaque ID, state, predecessor/row digest and immutable spec digest.
- states: `prepared -> active -> restart_unvalidated -> active`, and terminal `expired|revoked|closed`; no terminal state reopens.
- APIs: `prepare_diagnostic_isolation`, `activate_diagnostic_isolation`, `revalidate_diagnostic_isolation_after_restart`, `revoke_diagnostic_isolation`, `close_diagnostic_isolation`, `load_active_diagnostic_isolation`.
- maximum TTL: 60 minutes; expiry is exclusive (`now >= expires` rejects). Calls are idempotent by request/spec digest; concurrent activation has one winner.
- append-only `data/diagnostic-isolation.jsonl`, schema/transition/predecessor/digest validation under `profile_authority_lock` for writes.

Every gateway boot generates an in-memory cryptographically random boot epoch. A persisted active session from another boot is returned only as `restart_unvalidated`; it authorizes nothing until exact owner/spec revalidation records a new active revision for the current boot. No config flag alone enables diagnostics.

### B. Registry/runtime visibility
Update `customer_coaching.py` with a typed diagnostic runtime loader/context; do not weaken `CustomerSpec`, `RegistryDocument` or ordinary `load_runtime_customer_registry`.

The disposable customer is enabled only in the **isolated profile registry** during an active session so existing enabled-only coordinator/menu logic can operate. Its profile root, data root, bot and all destinations must be disjoint from production roots/routes. It is disabled before prepare, enabled transactionally only after activation checks, and disabled on expire/revoke/close/cleanup. Production registry is never edited or overlaid.

`load_diagnostic_runtime_customer_registry(profile_root, session_capability)` first validates the active current-boot session under authority lock and returns only its exact disposable customer and role routes. Gateway construction chooses this loader only from a verified diagnostic session context; ordinary startup remains unchanged. Production lifecycle APIs reject diagnostic contexts, and diagnostic contexts reject any customer/root/route not pinned by the session.

### C. Role ingress and capability
Extend `AdaptiveOperatorCapability` or add one typed `DiagnosticRoleCapability` nested in the existing capability contract with: mode, session/spec/boot digests, role, destination digest, disposable customer, owner/config/registry/consent/activation/source/registration/policy/catalog/constraints/epoch pins, nonce, issue/expiry.

Operator actions still enter through configured Topic 59 and `AdaptiveOperatorService`. Customer/trainer ingress is authorized by a session-bound coordinator context and exact role destination; no raw master tuple is accepted. The legacy private trainer-DM bridge is disabled in diagnostic mode unless the private DM is exactly the pinned trainer destination; DM text/callback/active binding outside it terminates with downstream/provider zero. Every lifecycle append, reservation and provider call revalidates current boot/session/owner/role destination/customer/test-bot/live pins.

### D. Preflight compatibility
Preserve `GateDPreflightReceipt` v1 byte/field semantics for ordinary calls. Add typed `DiagnosticPreflightOptions` and separately versioned `DiagnosticPreflightReceipt` (`diagnostic_preflight_v1`) or a closed tagged result union—never widen v1 maps.

`audit_diagnostic_isolation_preflight(profile_root, customer_key, options)` is byte-for-byte read-only and checks: current canonical owner, same owner user across all three roles, pairwise-distinct full triples and destination pairs, no reserved-space collision, isolated private profile/data root, enabled state consistent with session phase, disposable customer only, test bot distinct from production bot, zero production routes/credentials, feature/delivery false before activation, current KST window, schedule fence, artifacts/consent/activation/source/registration, session TTL/boot/state and clean reservation baseline. Receipt exposes only schema/mode, booleans, bounded counts/reasons/digests/epoch/time—no identifiers or paths.

### E. Non-mutating audit snapshot
Add `DiagnosticIncidentClass` enum with bounded classes: `incomplete_checkin`, `low_adherence`, `safety_hold`, `kst_rollover`, `stale_callback`, `duplicate_callback`, `authority_change`, `provider_timeout_unknown`, `receipt_audit_pending`, `reservation_race`, `disable_restart`.

Add `export_diagnostic_audit_snapshot(profile_root, customer_key, *, incident_class, window, capability)`. Authorization requires a freshly validated current-boot owner diagnostic capability, not caller text. Reads use a new `profile_authority_read_lock` that requires the existing 0600 lock file/directory and opens it read-only with shared `flock`; it must not mkdir/create/chmod/write. Missing/malformed lock fails closed. Tests snapshot the entire tree before/after success and rejection.

The strict snapshot schema contains generated opaque IDs; KST bounds; normalized state/event sequence; bounded branch inputs needed by the selected incident class (enums, booleans, bounded numeric buckets—not customer text); logical concurrency points; revision/epoch/digests; provider-attempt/receipt booleans; expected outcome fingerprint. Forbidden: names, raw customer keys, free text, destinations, Telegram IDs, paths, tokens, receipt contents, medical text, capabilities and operational rows.

### F. Synthetic replay
Create `materialize_diagnostic_replay(snapshot, isolated_root, *, mapping, approved_by_capability)` in `diagnostic_isolation.py`. `DiagnosticReplayMapping` is stored in the isolated profile with its own digest and binds only opaque source roles to disposable customer/test destinations. It cannot contain production IDs/paths.

Replay accepts only supported snapshot classes and freshness, rebases time explicitly to a supplied KST evaluation window, creates synthetic events/provenance only under a validated isolated root, and never copies source capabilities, ledgers, approvals, receipt values, destinations or text. Production loaders reject `provenance=diagnostic_synthetic`; only the diagnostic runtime loader accepts it. Verification compares an incident-class-specific outcome fingerprint (decision/state sequence/provider-count/terminal status) rather than raw digests.

### G. Promotion manifest, not deployment engine
Add `build_diagnostic_promotion_manifest(...)` to the profile module. Exact trust roots are the current profile source workspace and `/home/cube/projects/richard/hermes-agent`. Allow only repository-relative Python/test/doc paths, explicit migration files and a closed list of non-secret config **schema keys**. Reject symlinks, path traversal, runtime/customer/data/artifact/ledger/snapshot files, `.env`/auth/token/secret files, raw destinations/IDs, test customers and generated runtime state. The manifest is read-only and includes hashes, test evidence and rollback requirements. It does not deploy.

Issue reporting remains in this development session. Observability is limited to typed returns, local non-sensitive logs/metrics and test artifacts; no Telegram diagnostic command/UI is added.

## Files and sequencing
1. Profile state/runtime: `checkin_cli/diagnostic_isolation.py` (new), `customer_admin.py`, `customer_coaching.py`, `__init__.py`; tests `test_customer_admin.py`, `test_customer_coaching.py`, new focused diagnostic test only if existing files become unwieldy.
2. Gateway config/capabilities/routes: `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` as directly affected.
3. Snapshot/replay/promotion in the profile diagnostic module with strict property/adversarial tests.
4. Canonical profile `PILOT_RUNBOOK.md`; update root pointer/HANDOFF/Korean guide/report only where directly affected; recompute canonical hash and pointers.

Implementation is validation-coupled and should be one Ultragoal story with parallel executor slices for profile, gateway, snapshot/replay and tests/docs, followed by one integrated verification/review boundary.

## Migration and compatibility
- No session means exact existing behavior.
- Existing ordinary v1 preflight remains stable.
- Existing same-user/distinct-full-triple registry records remain valid; no new ordinary identity rule.
- Existing production capabilities never become diagnostic capabilities.
- Diagnostic ledger is new, versioned and fail-closed; no credential/customer migration.
- Isolated customer enablement occurs only in the isolated registry and is forcibly reversed during cleanup.

## Deliberate pre-mortem
1. **Diagnostic route reaches production:** prevent with root/bot/customer/route/spec pins at load, mint, append, reservation and provider boundaries; injection test provider zero.
2. **Restart revives authority:** boot epoch forces `restart_unvalidated`; stale callback/provider zero until owner revalidation.
3. **Snapshot leaks data:** positive schema allowlist, strict canonical serialization and recursive forbidden-value/property tests.
4. **Private trainer DM bypasses role route:** diagnostic mode disables legacy DM except exact pinned destination; all-ingress negatives.
5. **Replay merely resembles incident:** class-specific bounded inputs, concurrency markers and outcome fingerprints establish equivalence.
6. **Config promotion carries secrets/routes:** schema-key allowlist plus content/path/symlink scanners reject manifest.

## Verification

### Unit
- Session transition table, 60-minute TTL/exact boundary, idempotency, single-winner activation, restart suspension/revalidation and terminal irreversibility.
- Ordinary preflight v1 serialization unchanged; diagnostic receipt closed schema/reason allowlist and forbidden-value scan.
- Same user + three distinct full triples passes only diagnostic context; wrong owner or aliased destination rejects.
- Disabled-before-prepare, isolated enable during active, forced disable on every terminal/failed cleanup.
- Session/ledger deletion, reordering, malformed/re-digested rows fail closed.
- Read lock and snapshot success/failure leave full filesystem bytes/metadata unchanged.
- Each incident schema supports deterministic fingerprint; stale/unsupported/malformed snapshots reject without output.
- Promotion rejects every runtime/secret/route/config-value class.

### Integration
- Existing single-owner test environment can pass diagnostic preflight after disposable customer setup while ordinary production startup remains unchanged.
- Isolated runtime sees exactly one disposable customer; production runtime sees none of its routes/state.
- Operator/customer/trainer same user at exact distinct destinations completes the workflow; wrong destination/private DM/generic ingress provider zero.
- Owner/session/TTL/boot/config/customer/bot/route/pin changes before append produce zero rows/provider; after reservation preserve existing linked terminal semantics.
- Snapshot read-only export → synthetic replay → expected fingerprint for every incident class.

### E2E/red-team
- all Telegram ingress kinds at all three role destinations; stale/forwarded/duplicate callbacks; private-DM bridge; KST rollover; incomplete check-in; low adherence; safety; provider unknown/audit pending; reservation race; disable/restart.
- concurrent activation and reservation have one winner.
- P2→P6 in isolated mode preserves at-most-once/audited-only success and final cleanup: flags false, delivery revoked, overlay rolled back, disposable customer disabled, session closed, processes stopped.
- live Telegram sends, tokens/accounts, manual P2→P6 and real deployment remain human-only and are not claimed by automation.

### Observability
Opaque session/snapshot IDs, state/mode/role, bounded reason code, KST, digests and counters only. Health checks are typed/local and detect restart-unvalidated sessions, expiry, isolation violations and incomplete cleanup.

## Acceptance criteria
1. Default ordinary behavior and v1 receipt remain unchanged.
2. Solo-owner role sharing works only through an active current-boot diagnostic session, three distinct destinations, isolated enabled disposable customer and test bot/profile.
3. No production runtime/capability/route/data is readable, callable, replayable or promotable from diagnostics.
4. Expiry/restart/owner/config/route/customer/bot mutation fails closed before provider work.
5. Snapshot is authorized, read-only, bounded and de-identified; replay is synthetic and fingerprint-equivalent.
6. Promotion manifest includes only allowlisted reviewed changes and never performs deployment.
7. Profile full tests/compileall and focused gateway tests/py_compile pass; broader unrelated failures are reported honestly.
8. Cleaner, Architect CLEAR/APPROVE, executor QA/red-team passed and terminal Critic OKAY are required for completion.
9. Human-only live Telegram/account/token/manual P2→P6/real-customer deployment remains untouched.

## Intent reconciliation
The established single-user practice is supported without asserting a false existing three-user production requirement. User identity may be shared because roles remain distinct full triples/destinations. The new value is the explicit isolated session, disposable runtime, restart/TTL fence, audit/replay and promotion boundary. Problems continue to be raised in this development session; no Telegram natural-language diagnostic UI is created.

## ADR
**Decision:** implement a canonical-owner-only current-boot diagnostic session with isolated runtime loader, v2 diagnostic preflight, authorized non-mutating snapshots, synthetic incident replay and allowlisted promotion manifests.

**Drivers:** solo-owner usability, production isolation, deterministic incident handling.

**Rejected:** permanent master bypass; universal three-account requirement.

**Consequences:** additional explicit state and tests, but no ordinary compatibility break or operational control-plane expansion.

**Human follow-ups:** reuse/provision test Telegram destinations, perform live P2→P6, approve any deployment and own tokens/accounts.
