# Diagnostic Isolation — Constructor Closure Plan

## Scope
Incorporates Stage 33 and changes only three construction contracts. User approved Ultragoal execution after consensus.

## RALPLAN-DR
Principles: durable identity differs from process identity; registered adaptive paths must be impossible to construct without paired canonical persistence; dormant control must not require an enabled coordinator. Drivers: restart safety, constructor-enforced invariants, testability. Chosen option is explicit typed factories and sealed bindings. Duck typing, ambient globals and optional production fallbacks are rejected.

## 1. Canonical transaction factory and enforcement
Add `CanonicalEventTransaction.for_customer_runtime(runtime: CustomerRuntime)` in `store.py`. `CustomerRuntime` exposes canonical resolved `customer_root`, `wizard_root`, and `nutrition_plans_root`; factory derives:
- events: `wizard_root/events.jsonl`
- sequence: `nutrition_plans_root/canonical-sequence.jsonl`
- lock: `wizard_root/.events.lock`
It validates containment/privacy/no symlinks and returns the only registered-customer transaction.

`WizardService.__init__(wizard_root, *, canonical_transaction=None, adaptive_registered=False)` enforces `adaptive_registered => canonical_transaction is not None` before creating `EventStore`. `NutritionCoachingCoordinator._configure_registry` builds the transaction from each enabled `CustomerRuntime` and passes both arguments. Customer admin activation/reconciliation and every adaptive wizard construction use the same factory. `EventStore` receives the transaction and registered finalization calls paired transaction append; if absent in a registered/adaptive flow it rejects before event bytes. Standalone nonregistered `WizardService`/`EventStore` retains event-only behavior.

Tests: registered customer cannot construct/finalize without transaction; exact paths/lock; coordinator/admin/reconcile all use factory; standalone unchanged; batch/single append paired; no nested flock.

## 2. Dormant delivery authority and transport factory
Add `DiagnosticDeliveryAuthority` independent of any coordinator. Constructor takes verified spec, boot epoch, profile session store, registry/artifact loaders and immutable adapter/test-bot identity. Methods:
- `verify_reservation(session_digest, generation, reservation_id, destination_digest, deadline)`
- `verify_provider_start(...)`
- `terminalize_cancel_or_timeout(...)`
Every method reloads current session/spec/owner/registry/bot/pins under the established admission/profile locks.

Add `TelegramCustomerTransport.for_diagnostic(adapter, verified_spec, authority)`. It requires the dormant adapter’s exact verified bot identity, exact customer destination from spec, sealed diagnostic method version, and native Telegram async sender. It does not require `NutritionCoachingCoordinator`. It returns a transport whose only diagnostic method is:
`async send_diagnostic_customer(body, *, reservation_id, session_digest, generation, deadline_monotonic, destination_digest)`.
The method delegates authority verification then calls the exact adapter bot with bounded connect/pool/write/read timeouts. Generic send compatibility is unavailable on this object.

Construction order: validate roots/spec/bot → create session store and `DiagnosticDeliveryAuthority` → create sealed diagnostic transport → create `DiagnosticHost` → create dormant `DiagnosticControlService` → register Topic-59 only. Activation later creates the child coordinator and injects the already frozen transport explicitly. No circular coordinator dependency.

## 3. Durable transport binding digest
Define canonical preimage:
`{schema_version:"diagnostic_transport_binding_v1", adapter_kind:"telegram-test-bot", test_bot_digest, customer_destination_digest, max_provider_timeout_seconds, method_version:"send_diagnostic_customer_v1", authority_spec_digest}`.
`diagnostic_transport_binding_digest = sha256(canonical_json(preimage))`.

The digest is computed before session prepare from verified durable inputs and stored in spec approval/config projection, every diagnostic session/transition/capability/reservation row, and preflight digest map. It never contains Python object identity. On restart the factory reconstructs authority/transport from the same verified spec and adapter identity and requires exact digest equality before Topic-59 prepare/revalidate. Changed bot/destination/timeout/method/spec invalidates old sessions.

`DiagnosticHost` additionally freezes the process-local transport reference and checks `transport is self._transport` under admission lock from reservation through provider completion. This is an extra runtime TOCTOU check only. Coordinator setters reject while diagnostic state is nonterminal.

Tests: digest vectors; same-value restart rebind succeeds only current boot revalidation; any preimage mutation rejects; object swap with same marker/digest rejects by identity; old process object is never serialized; dormant factory requires no coordinator; setter race provider zero.

## Pre-mortem and verification
- Missing transaction writes unpaired event: constructor/finalization rejection.
- Dormant transport accidentally needs coordinator: constructor test with no coordinator.
- Restart trusts object identity: digest-vector/reconstruction tests.
- Swap after validation: frozen-reference admission test.

Run full profile pytest+compileall; focused gateway tests+py_compile including diagnostic sibling; full gateway truthfully; wheel/policy and read-lock/race tests from Stage 33; cleaner/Architect/QA/Critic. No live Telegram, credentials, manual P2–P6 or deployment.

## Acceptance
All registered adaptive wizard events are paired by factory construction; dormant host builds without coordinator; durable binding is canonical/restartable and process identity is only an additional check; all Stage-33 contracts remain unchanged.

## Intent reconciliation
No new user choice: this only makes the previously approved isolated diagnostic design constructible and restart-safe.

## ADR
Use typed factories and a durable binding digest; reject optional production fallbacks and process-object persistence.
