# Task26 Golden Path, candidate 2e0894ea (v3)

Version: v3 (2026-08-15, supersedes the v2 and v1 rebind runbooks listed below)
Status: READY. Every external gate open at v2 (B3, G12, B6) is now closed with sealed, independently re-verified evidence. Execute only through the order in Section 2; do not execute partially.

Supersession (append-only; all prior documents remain byte-identical):

- Supersedes `task26-golden-path-2e0894ea-v2.md` sha256 `633d5c25ce70cb9ab5d19c434122b14b46fb7108962ab6e453ce6ab3ede1848f` and `task26-recovery-runbook-2e0894ea-v2.md` sha256 `6024c26ccae3cad7fcf46a77bca8607d73293a92ddb3dcf8fa5a278361869f1d` (v2; stale venv/unit paths, wrong module pins, invented lifecycle CLIs fixed here).
- Supersedes `task26-golden-path-2e0894ea.md` sha256 `1f55b8f967c6564113574d41bf1c15ce977d35c38b5687f1623ba37f235a7bdb` and `task26-recovery-runbook-2e0894ea.md` sha256 `f4f2d2347b4b68a080b9c437a813accbef8c4bcc471cbf21d67ffff31e56f89b` (v1). These two v1 files must remain byte-identical in place: the sealed invite harness (G12) hash-binds them and refuses to run if they change.
- Still supersedes `.omo/evidence/dualcoach-golden-path-contract.md` sha256 `32a379d855c6e5af978bd9886e3bf49c616c7c20f1c1d5f100c19d8adfc5eb4a` and `.omo/evidence/dualcoach-recovery-runbook.md` sha256 `ae2f5f9046c06f8f0b42693be5aa0d9c31cada8024ae1e5ab8ba19a3cf10f6fc` (stale candidate `19ed0e6047f0a4d7650c47a7413ee0298a243f768a63bcb8fb9cffe44d140a1a`; void).

Binding (exact; short forms are the first 8 hex chars):

- Candidate full digest: `2e0894eac92bc396cc4723bf1f18ebc653b95018dd41574df435941c235da925` (`2e0894ea`)
- Wheel sha256: `af4a9d0a1ffffb6eb7551c1d6dc2b32853ca6d024332a4f8f5702bbf992f141b` (`af4a9d0a`)
- Amended plan sha256: `7ace03c6dad33d2fc3ef223621cbca68a150fde8429932138e252fb8498ac582` (`7ace03c6`)
- Authorized actor: Telegram user id `8527916639` (same physical account as prior rehearsals; new logical customer lifecycle only)
- Profile under test: `dualcoachtest` at `/home/cube/.hermes/profiles/dualcoachtest`
- Runtime venv (single runtime for service and all CLIs): `/home/cube/projects/richard/hermes-agent/.venv` — there is no profile-local `.venv`; v1/v2 references to one were stale
- Bot: `@dual_coach_pilot_test_bot` (api_id `39664143`)
- Config: `/home/cube/.hermes/profiles/dualcoachtest/config.yaml`, sha256 `f93106b16643227e2ef9dec67a5bbd497e1d353e779da62287898a087071af87` at authoring, `adaptive_nutrition` provider `openai-codex`
- systemd unit: `hermes-gateway-dualcoachtest.service`, unit file `/home/cube/.config/systemd/user/hermes-gateway-dualcoachtest.service` sha256 `0b46e887fc12c45f12814f4ef025c10101ed36e5df0ac35f70abb1862c0c1db2`, `Type=simple`, `ExecStart=/home/cube/projects/richard/hermes-agent/.venv/bin/python -m hermes_cli.main --profile dualcoachtest gateway run`, `HERMES_HOME=/home/cube/.hermes/profiles/dualcoachtest`, `Restart=always`, `RestartSec=5`, `RestartForceExitStatus=75`, `TimeoutStopSec=210`

Freshness boundary: only the Telegram actor identity and DM history are reused. Every customer key, session id, invite link, token, bootstrap/consent/onboarding/readiness/check-in/generation/delivery state, reset run id, and evidence directory is new. Forbidden reused values are listed in Section 7.

## 0. Authorization and non-negotiable constraints

Authority: the `same-actor-live-rehearsal-authorized` entry, recorded at 2026-08-15T11:55:24Z, row 128 of the plan ledger `.omo/start-work/ledger.jsonl` (the plan/authorization ledger, not the profile customer-bootstrap ledger). Authorized actions: authenticated archive-first pre-reset; controlled exact-candidate wheel deployment and service rebind; exactly one new canonical bootstrap invite; one private-DM `/start` by actor `8527916639`; synthetic provider and owner/operator actions through one uninterrupted normal lifecycle; exactly one synthetic customer DM delivery after an explicit send decision; final disable; authenticated archive-first cleanup reset.

Forbidden always: archive restore or prepopulation, direct durable-state mutation, raw Telegram listener or cursor edits, service replay, forced publication, any recovery shortcut, any change to the untouched other profiles (`physique-coach`, `quarantine`), candidate rebuilds or rerollouts inside the window, token or secret mutation, bypassing the review/owner-action lane, implicit sends, additional Telegram customer DMs or deliveries beyond the one authorized send, and any manual deletion of `gateway.lock` (the sealed pre-reset disposes of it; nothing else may).

## 1. Fail-closed preflight (every gate must PASS; any FAIL or UNKNOWN stops the rehearsal)

Every gate writes a receipt into this run's new private evidence directory (mode 700; files 600), for example `.omo/evidence/task26/task26-same-actor-live-rehearsal-<UTC timestamp>/`. Commands run from the repository root unless stated otherwise.

| Gate | Check | Pass condition | Status at v3 authoring |
|---|---|---|---|
| G1 | `python3 ".omo/evidence/task26/task26-repaired-archive-successor-2e0894eac92bc396cc4723bf1f18ebc653b95018dd41574df435941c235da925/verify_candidate.py" ".omo/evidence/task26/task26-repaired-archive-successor-2e0894eac92bc396cc4723bf1f18ebc653b95018dd41574df435941c235da925/verifier-input.json"` | Exit `0`, stdout JSON `status` == `PASS`. Verifier sha256 `b852482350129dd025f0c4c79539e04a71c4758c9ec336e2c5bcda44676791f6`, verifier-input sha256 `cd9e080ab886d503a205e7a7335eb23942b161daeaa2028a6e8f63492a99ba68` | PASS (evidenced) |
| G2 | `sha256sum` of the candidate wheel | Equals `af4a9d0a...` | PASS (evidenced) |
| G3 | `.omo/start-work/ledger.jsonl` row 128 holds `same-actor-live-rehearsal-authorized` with this candidate, wheel, plan, actor | Present and matching | PASS (evidenced) |
| G4 | `sha256sum` of `config.yaml`; file modes of auth material (`.env`, `auth.json`) | Config equals `f93106b1...`; auth material private | PASS at authoring; recheck at run time |
| G5 | Extract the wheel into a new private temp dir; sha256 of the runtime modules: `gateway/config.py` `a8191eed20ca75abcb03c33ac0c0b915cd810249466e816c6a1a55ba154af188`, `gateway/run.py` `f5d51008f1e8c8ee102df930fa68c7945a3276a249187adc7b7f6ba4b1e8e134`, `cron/scheduler.py` `3eca06827bfd62a4f07498bf7fed54e3b2b47a9f62e1f5b81701d1453e88806e`, `gateway/platforms/telegram_customer_bootstrap.py` `145515d5e110dcebb94fcaa554bcee29544b3a75dcfe058fae944ea3b70042b2`, `gateway/platforms/telegram_customer_bootstrap_registration.py` `cc0e4b697633150f3626daa8cf5bb4825bc96243f1a3dfadb6c9f540cad9906c`, `gateway/platforms/telegram_nutrition_onboarding.py` `6ab725042cbed536d8a8f3faee29e431470617f21d83c795e19574a27fcd032f`, `gateway/platforms/dualcoach_admin.py` `0e9b4b1f0449d8351602598fead3ef01b8616e230c147e44db79667284ceae49` | All seven equal (this corrects the v1/v2 module list, which named three non-existent paths and stale digests) | PASS at authoring; recheck at run time |
| G6 | Current authority snapshot equals the sealed pre-reset dry-run manifest (receipt sha256 `2fc323f8b00c18f21598f717fb6a19a7e342b7786909e59adcf4bc3955bcef99`, manifest `evidence_digest` `d2879b128573a9d75a55958d2fa3e1a24be1636f0a7a20affe8f2ef9b94dfd33`): registry `9eb1b5ae...` (empty), bootstrap ledger `7004dac9...` (three EXPIRED claim-free sessions `cb_6S6RABpDgZ165V02A7qEzw`, `cb_9yTz0oNwdU8s8hradru8BA`, `cb_rmDnfrqA6gkmjoQEdwxu0g`), `sessions/sessions.json` `0bd0afb9...`, `state.db` `68de185a...` mode 600 | All digests equal the manifest entries | PASS at authoring; recheck at run time |
| G7 | Provider readiness (blocker B3, CLOSED): READY receipt raw file sha256 `8278dde4efa8bcd366fce873ccc07f651818275238b67452b9e6cb93c67afd6d` at `data/dualcoach-provider-auth/receipts/20260815T124422Z-72c8a29e63504cf5a66c75966e4ae266.json`, payload `receipt_sha256` `6aeaaab42e0030b0f626037d7b9ef6661ec93fb57fd991c843967c1fc695905c`, index sha256 `16acf1b355715379ad526433803353e1f34c418e9560fc1466f3f77f951df919`, `result: ready`, `success: true`, exit 0, `request_attempts: 1`, `billable: true`, `store: false`, 26 total tokens (17 in, 9 out), response `completed`, pre/post profile snapshot identical at `7d163cb01708869d617efeadd852f7e6ec4f963a94c50d19b104cc849483c171` (`profile_snapshot_unchanged: true`), zero delivery/registry/service/Telegram effects | Receipt digests and fields match | PASS (B3 closed) |
| G8 | Stale lock disposition (pre-reset): `flock -n /home/cube/.hermes/profiles/dualcoachtest/gateway.lock true` succeeds (no active holder); record the lock sha256/stat (at authoring `28420d4aa8fc1ee1298c9fe93a69e47aa187c0d2777ca672fb603cfbebc569cf`, mode 600, stale pid `4091167`); `gateway.lock` is inside the sealed pre-reset contract `approved_clear_scopes`, so the sealed reset disposes of it as reset input. The stale lock's presence is permitted ONLY as that sealed reset input; hand removal is forbidden everywhere | No active holder AND lock in contract scope | PASS at authoring |
| G9 | `sha256sum` of `/home/cube/.config/systemd/user/hermes-gateway-dualcoachtest.service` and `systemctl --user cat hermes-gateway-dualcoachtest.service` | Equals `0b46e887...` (this corrects the v1/v2 `/etc/systemd/system` path) | PASS at authoring; recheck at run time |
| G10 | Pre-reset controller sealed evidence (B2, CLOSED): controller `reset-controller-st_01a0054d/reset_controller.py` sha256 `bd051dda8666ce9e014ec79c58acd4d7b8df2c27fad01a7a78c76e46c3388baf`, contract `4128cece087f3f4eb84c3917207299fae6bd1ac9971d7e4e8676549106bb7c20`, permission `8a290b11cac5b6957c772366abe875c7f635b8a3e7956a665471ffaa90b6c495` (`execute_allowed: true`, phrase `TASK26_ARCHIVE_FIRST_PROFILE_RESET_APPROVED`), dry-run receipt `2fc323f8b00c18f21598f717fb6a19a7e342b7786909e59adcf4bc3955bcef99` (PASS, mutations 0), readiness `776e90301a63dce8912bf8a4110cd037f833c3a0e875a3ddd1289c0c9ee2ac9e` (`ready_for_live_reset: true`) | All five pins equal | PASS (B2 closed) |
| G11 | Retained-archive verifier evidence (B5, CLOSED): verifier `.omo/evidence/task26/verify_retained_archives.py` sha256 `12b97aa72e2df86719dbb7b80b477237d590aaaea81a9f812f321a1566936528`, inventory `29db87f2855c4804dbbc69244e68d9fb9f51808f7f7759fb592ed68f5f090185`, seal `28ff72fdb7925cbdcd8e7dd7e8c058bcca42cab3377c34078164766197ed6968`, verification receipt `597a37e4912d729b3b5f6022bccff6dc12a73b024128805980f5ab1ade4fb83d` (PASS, five archives, four schemas), tests `1b92ac7250d1af013204d8074176b971307211eddee91606d4cdd0fcd05efd79` (14/14 PASS, LSP clean) | All pins equal, receipt PASS | PASS (B5 closed) |
| G12 | Invite harness sealed evidence (B4, CLOSED): harness `.omo/evidence/task26/task26-invite-harness-st_01a0056a/invite_harness.py` sha256 `2826b1baf97918829f80d41722ec6534f933e57efa52345353cdb366cac314fb` (mode 500), permission seal `c4e857894ebd8b2e96289ea70d7cdcdd38cdad1c72cc7b1c86f33a5d2de7d171`, approval receipt `c7fc477141259087e8e9dd6520b2066eda3bab20dc6e2f625942431b6830c187` (binds ledger row 128), verification receipt `c2a6393f2ccf67ab6872f83e03793af72ea21b7df2e616c7cc2256c566c6b2c0` (`PASS_HARNESS_FAIL_LIVE_PREFLIGHT`: harness PASS, live preflight blocked only by the lock the pre-reset removes); sealed tests `68f6f72f773265a3e889b5ab2024425caa7eb1fd0cf4f612379fd7203293185b` pass 6/6 with Ruff, LSP, and py_compile clean (reproduced at v3 authoring) | All pins equal | PASS (G12 closed) |
| G13 | Subscription harnesses staged from the trusted workspace source checkout (never the runtime venv), syntax-checked | `python3 -m py_compile` clean | Prepared at run time |
| G14 | Telegram auth files under `~/.local/share/hermes/telegram/` (`auth_39664143.session`, `auth_39664143.session-journal`, `auth_39664143.json`) mode 600 | All private | PASS at authoring; recheck at run time |
| G15 | Post-reset lock gate (Section 3.1): after pre-reset `verify` passes, `test ! -e /home/cube/.hermes/profiles/dualcoachtest/gateway.lock` | `gateway.lock` absent after reset | Checked in Section 3.1 |
| G16 | Cleanup controller sealed evidence (B6, CLOSED): controller `reset-controller-st_01a0054d/post-lifecycle-cleanup-v2/cleanup_controller.py` sha256 `8b03fa714304b34a7f19dc9b077d8fbd54e7f7bc46232bf8d5bc65ae7b873ff5`, contract v2 `7b6a1aa54b58d6e7e6733a963772e220a903ffae5a3e0e6d089283af13e097db`, permission seal v2 `cb06310270868da5dfc3c1d292b0ccedd5f3021d0f211576449c96649fa4730b` (phrase `TASK26_POST_LIFECYCLE_ARCHIVE_CLEANUP_APPROVED`), test receipt `90fcb04be789fd519d6bb83f9ba659927721e9d272594de84329c42c3529ed18` (21/21 PASS, Ruff/compile/LSP clean, reproduced at v3 authoring), independent verification receipt `d8d818cdfd2a22b088064b216136bfdee050b096a5b7fdcc9fb6f5958312dd05`, readiness `fab6322753cccca614e1ee83bf5221013185662a74c86f5d19563e38f0fc0d2b` (`ready_for_post_lifecycle_cleanup: true`), live pre-rehearsal dry-run rejection `769db2bd5c39e405176ecb438d05ad0f10c538ee53c6c7849e809123f91ecb88` (`EXPECTED_REJECTION_PASS`: zero authorized ACTIVE lifecycle today) | All pins equal | PASS (B6 closed) |

UNKNOWN equals FAIL. No gate is OPEN at v3 authoring.

### 1.1 Provider digest-domain clarification (permanent)

The provider-auth receipt field `candidate_digest` `f9a46172386333a0067f43695fb1429a043a606f04baceb06e8c59b90c36235c` is the provider COMMAND digest, deterministically emitted by module `gateway/platforms/dualcoach_admin.py` (sha256 `0e9b4b1f0449d8351602598fead3ef01b8616e230c147e44db79667284ceae49`, byte-identical in the sealed wheel and the runtime venv). It is a different digest domain from the full candidate envelope digest `2e0894ea...`. Never compare across domains and never flag `f9a461...` as candidate drift. The provider command must run with the profile-bound environment: `HERMES_HOME=/home/cube/.hermes/profiles/dualcoachtest` (the earlier `probe_unknown`/missing-credential failures came from resolving the default `/home/cube/.hermes` instead).

## 2. Execution order (mandatory chain; no step may be skipped or reordered)

1. Pre-reset arm: sealed reset `dry-run` PASS (Section 3, Step A).
2. Pre-reset execute and verify (Section 3, Steps B and C; gate G15).
3. Invite harness `dry-run` and `verify` — both must exit 0 with `ready_for_one_invite: true`; this is only possible after step 2 because the harness refuses to run while `gateway.lock` exists (Section 5.1).
4. Exact-wheel deployment and loaded-byte proof (Section 4).
5. Observe before prepare/Start: outbox/audit/delivery subscriptions armed (Section 5.2); the harness's own ledger subscription precedes its one prepare call; the claim observer must be READY before the human opens the handoff and presses Start (Sections 6-8).
6. Lifecycle: claim, consent, onboarding, owner review, readiness, activation, check-in, generation, review, exactly-once delivery (Sections 8-12).
7. Disable and service stop (Section 13).
8. Cleanup execute and verify (Section 14; strict ACTIVE lifecycle gates).

## 3. Archive-first pre-reset (sealed reset controller st_01a0054d)

Exact sealed command shape (from `reset-controller-st_01a0054d/exact-execute-command.txt`; run from the repository root; display-wrapped here):

```bash
python3 '.omo/evidence/task26/reset-controller-st_01a0054d/reset_controller.py' execute --profile '/home/cube/.hermes/profiles/dualcoachtest' --other-profile '/home/cube/.hermes/profiles/physique-coach' --archive-root '/home/cube/.hermes/profiles/dualcoachtest/data/profile-reset-archives' --contract '.omo/evidence/task26/reset-controller-st_01a0054d/schema-contract.json' --permission '.omo/evidence/task26/reset-controller-st_01a0054d/permission-receipt.json' --candidate '2e0894eac92bc396cc4723bf1f18ebc653b95018dd41574df435941c235da925' --wheel-sha256 'af4a9d0a1ffffb6eb7551c1d6dc2b32853ca6d024332a4f8f5702bbf992f141b' --plan-sha256 '7ace03c6dad33d2fc3ef223621cbca68a150fde8429932138e252fb8498ac582' --approval 'TASK26_ARCHIVE_FIRST_PROFILE_RESET_APPROVED' --run-id 'task26-live-reset-2e0894ea'
```

Semantics (from the sealed source): the new archive lands at `<archive-root>/<run-id>` via atomic rename from `.pending-<run-id>` (the rollback boundary); prior archives are read and hash-pinned from both `data/rehearsal-reset-archives` and the archive root and never written; cleared scopes are the contract's `approved_clear_scopes` (including `sessions`, `state.db`, and `gateway.lock`) plus dynamic patterns; protected roots include `dualcoach-provider-auth`, both archive roots, and the gate checklist files; every bootstrap session must be terminal and claim-free (the three current EXPIRED sessions qualify); `--approval` takes the literal phrase presented by the human; run ids are one-use.

- Step A (arm): stop the service (`systemctl --user stop hermes-gateway-dualcoachtest.service`, confirm `inactive`; execute refuses while profile processes exist). Run the command with mode `dry-run`. Require `status: PASS`, `mutations: 0`, `bootstrap_ledger_covered: true`, `bootstrap_session_count: 3`. Save stdout as the arm receipt.
- Step B (execute): after the human presents the approval phrase, run the exact execute command. Require `status: PASS`, `post_reset_empty_baseline: true`, `other_profiles_unchanged: true`.
- Step C (verify): run with mode `verify` (post-execute only; it reads the sealed manifest/receipt inside the new archive and asserts the live authority set is empty). Require `status: PASS`.

### 3.1 Post-reset verification

- Gate G15: `test ! -e /home/cube/.hermes/profiles/dualcoachtest/gateway.lock` (lock absent; controller verify already failed otherwise).
- Spot-check: `customers/`, `data/onboarding/`, `data/customers/`, `sessions/`, `state.db` absent; `config.yaml` still matches G4; `data/rehearsal-reset-archives/` still holds exactly the five historical archives.
- Runtime state (`cron/jobs.json`, sessions, ledgers) is regenerated by the canonical runtime at service start in Section 5.3, never by hand. Confirm no timers exist first (`systemctl --user list-timers`).

## 4. Exact-wheel deployment and loaded-byte proof

1. Service is already stopped (Section 3). Record the pre-change `direct_url.json` digest under `/home/cube/projects/richard/hermes-agent/.venv/lib/python3.12/site-packages/hermes_agent-0.17.0.dist-info/`.
2. Deploy: `/home/cube/projects/richard/hermes-agent/.venv/bin/python -m pip install --force-reinstall --no-deps "/home/cube/projects/richard/traning coach/.omo/evidence/task26/task26-repaired-archive-successor-2e0894eac92bc396cc4723bf1f18ebc653b95018dd41574df435941c235da925/artifacts/hermes_agent-0.17.0-py3-none-any.whl"` (path quoted; it contains a space). No candidate rebuild in this window.
3. Loaded-byte proof:
   - The installed `hermes_agent-0.17.0.dist-info/direct_url.json` references the candidate wheel path above.
   - sha256 of the seven installed modules listed in G5 (under the venv's `site-packages/`) equals the G5 wheel digests, including `gateway/platforms/dualcoach_admin.py` `0e9b4b1f...` (the provider command module) and `gateway/platforms/telegram_customer_bootstrap.py` `145515d5...` (the module the invite harness byte-proves at every run).
   - The editable `checkin_cli` install (`__editable__.physique_checkin_cli-0.1.0.pth`) still resolves to `/home/cube/.hermes/profiles/dualcoachtest/workspace/checkin_cli/checkin_cli`.

## 5. Invite harness gates and observers (before any actor-visible action)

### 5.1 Harness dry-run and verify (post-reset, pre-prepare)

With `D='.omo/evidence/task26/task26-invite-harness-st_01a0056a'`, `PY='/home/cube/projects/richard/hermes-agent/.venv/bin/python'`, `PROFILE='/home/cube/.hermes/profiles/dualcoachtest'`, run from the repository root:

```bash
"$PY" -B "$D/invite_harness.py" dry-run --profile "$PROFILE" --permission "$D/permission-seal.json" --receipt "$D/<run-id>-dry-run.redacted.json"
"$PY" -B "$D/invite_harness.py" verify  --profile "$PROFILE" --permission "$D/permission-seal.json" --receipt "$D/<run-id>-verify.redacted.json"
```

Both must exit 0 with `ready_for_one_invite: true`. The harness hard-refuses while `gateway.lock` exists (verified: the current live dry-run fails with blocker `gateway.lock must be absent`), so these gates can pass only after Section 3. The harness also re-verifies the v1 runbook digests (`1f55b8f9...` / `f4f2d234...`), the approval event (`23845bda574c085e16b6d316e5e84e54b3bc6db3a8803c6b3b93ed7c4b86b049`, bound to ledger row 128), the wheel, the plan, and the loaded bootstrap module bytes on every invocation.

### 5.2 Event subscriptions (armed before prepare)

From the trusted workspace source checkout, writing into this run's evidence directory:

- Outbox: `python3 scripts/telegram_nutrition_onboarding_e2e.py subscribe-outbox --profile /home/cube/.hermes/profiles/dualcoachtest --after-seq 0 --timeout 120 --output <evidence>/outbox-events.jsonl`
- Audit: `python3 scripts/telegram_nutrition_onboarding_e2e.py audit-tail --profile /home/cube/.hermes/profiles/dualcoachtest --after-seq 0 --timeout 120 --output <evidence>/audit-events.jsonl`
- Delivery: `python3 scripts/telegram_nutrition_onboarding_e2e.py watch-deliveries --profile /home/cube/.hermes/profiles/dualcoachtest --after-seq 0 --timeout 120 --output <evidence>/delivery-events.jsonl`

Re-arm each subscription after every firing with `--after-seq` advanced past the observed `seq`. No raw Telethon listener, ever.

### 5.3 Service start

Start the service (`systemctl --user start hermes-gateway-dualcoachtest.service`). Readiness: `journalctl --user -u hermes-gateway-dualcoachtest.service -n 30 --no-pager` shows the gateway connected as `@dual_coach_pilot_test_bot` without `RuntimeError`, within 90 seconds.

Bounded deadlines (operator policy; an exceeded bound is a STOP to the recovery runbook, never a silent retry): watch windows 120 seconds; service connect 90 seconds; claim after `/start` 120 seconds; consent-to-owner-review 300 seconds; check-in-complete to staff-review card 600 seconds; send decision to exactly one DM 300 seconds; CLI exits 60 seconds.

## 6. One invite preparation (sealed harness, exactly once)

Only after Sections 3-5 pass:

```bash
"$PY" -B "$D/invite_harness.py" prepare --profile "$PROFILE" --permission "$D/permission-seal.json" --draft "$D/<run-id>-customer-draft.private.json" --handoff "$D/<run-id>-invite-handoff.private.json" --receipt "$D/<run-id>-preparation.redacted.json" --evidence-root "$D"
```

The harness subscribes to `ledger.json` before calling `RoomBootstrapStore.prepare_rehearsal_customer_invite` exactly once. The 0600 handoff is the only raw-token location; the redacted receipt carries session/token/SID hashes. `invite_prepare_maximum` is 1 in the permission seal: a second prepare is sealed off. If the invite expires unclaimed, the only path is the harness `expire` mode (recovery procedure R3), then one new sealed preparation.

## 7. One Start (new logical lifecycle only)

1. Start the exact claim observer before the human acts: `"$PY" -B "$D/invite_harness.py" observe --profile "$PROFILE" --permission "$D/permission-seal.json" --session-id '<session-id>' --sid-hash '<sid-hash>' --ready "$D/<run-id>-claim-watch-ready.redacted.json" --receipt "$D/<run-id>-claim-observed.redacted.json" --timeout-seconds 120` (ids from the redacted preparation receipt). The observer uses bounded inotify events: no Telegram calls, no getUpdates, no cursor edits, no sleeps, no polling.
2. Only after the READY receipt exists: actor `8527916639` opens the 0600 handoff privately, opens the invite link once, and presses Start once (`accepted_start_claim_maximum: 1`). Never a bare `/start`.
3. Claim verification within 120 seconds: exactly one accepted claim for the prepared session, and exactly one `room_bootstrap_role_swap_blocked` audit event (the account still holds the owner role) with no `role_conflict` client message.

All-new values (all prior values forbidden): customer key `cust_...` (forbidden: `task22_dm_rehearsal`, `task26_synthetic_rehearsal`, `task26_same_actor_rehearsal`); bootstrap session `cb_...` (forbidden: `cb_2NQV5sbkN-M6awycJH7X5g`, `cb_6S6RABpDgZ165V02A7qEzw`, `cb_9yTz0oNwdU8s8hradru8BA`, `cb_rmDnfrqA6gkmjoQEdwxu0g`); one fresh 64-hex invite nonce; onboarding/check-in session id (forbidden: `wizard_995f04a3b8bc256fa13ff407`); generation idempotency token (forbidden: `3f44a18ea620d963`); reset run ids `task26-live-reset-2e0894ea` (pre-reset) and `task26-post-lifecycle-cleanup-2e0894ea` (cleanup); a new evidence directory.

## 8. Consent and onboarding (handset)

Accept the consent card in DM (`canonical_customer_consent_accept` / `..._committed`, version `v1` in audit). Complete the onboarding questions. States in order: `collecting`, `customer_attestation`, `reconciling`, then `owner_review` or `safety_hold`. `ready` before owner review is a defect; stop and capture evidence. No free-form goal text outside the scripted questions.

## 9. Owner review (handset, owner role)

The owner review card arrives in the owner review group `-1004484458766`, topic `59` (staff review) or `219` (preview); operator card in owner DM `8693203710`. From `safety_hold`, use the card's `safety_hold` action first. Then `review_questions`, edit answers as needed, `review_targets` (calories 2900 / protein 210 / carbs 320 / fat 90), `review_done`. One action at a time; the visible card is the authority; no parallel mutating actions.

## 10. Readiness and activation (corrected CLIs)

1. Handset: `readiness_check`, upload the checklist photo plus weight 93 kg, submit.
2. Readiness audit (read-only): `/home/cube/projects/richard/hermes-agent/.venv/bin/python -m checkin_cli.readiness_cli --profile-root /home/cube/.hermes/profiles --customer-key <key>` (this corrects the v1/v2 `hermes_cli.nutrition_readiness --customer` invocation, which does not exist). Require exit 0 with all items satisfied.
3. Activation (one-use): `/home/cube/projects/richard/hermes-agent/.venv/bin/python -m checkin_cli.customer_admin --registry /home/cube/.hermes/profiles/dualcoachtest/customers/registry.json activate --profile-root /home/cube/.hermes/profiles --data-root /home/cube/.hermes/profiles/dualcoachtest/data --checklist-evidence <readiness receipt path> <customer_id>`. Exactly one run; a second attempt fails closed.
4. Verify within 120 seconds: registry `service_state: "active"` for the new key; onboarding ledger `ready`; activation-completion notice in the outbox/audit trail.

## 11. Synthetic weekly check-in (12 questions)

DM `checkin`, `Begin check-in`, answer all 12 questions in order (Q7: one of choices `1` to `5`), one message per answer, waiting for each prompt (120-second per-prompt bound). Completing the check-in atomically enqueues the generation job in the same transaction (durable state is the authority; there is no generation CLI — the v1/v2 `hermes_cli.nutrition_review_service request-generation` command does not exist and is void).

## 12. Generation, review, and exactly-once delivery

1. The generation worker claims the lease and calls the provider with the immutable input revision; the synthetic provider configuration produces the plan. The staff-review card arrives in the review group within 600 seconds of check-in completion.
2. Only after the checklist shows all items satisfied: press the card's approval action once, then the separate authorized `Send to customer` delivery action once. Approval never auto-sends.
3. Exactly-once proof: exactly one customer DM with the plan; exactly one `adaptive_plan_delivered` audit event for this generation; the delivery watcher shows exactly one delivered record with one attempt and one receipt. A second DM, a second audit event, an implicit send before the explicit decision, or a duplicate after a service restart are all defects: stop and capture evidence.

## 13. Disable and stop

- Disable: `/home/cube/projects/richard/hermes-agent/.venv/bin/python -m checkin_cli.customer_admin --registry /home/cube/.hermes/profiles/dualcoachtest/customers/registry.json disable <customer_key>` (no reason flag exists; v1/v2's `--reason` was wrong). Expect exit 0 and registry `service_state: "disabled"`.
- Stop the service and confirm `inactive` (the cleanup controller refuses to run while the profile has processes, and checks the unit's MainPID itself).

## 14. Post-lifecycle cleanup (sealed cleanup controller, strict ACTIVE lifecycle gates)

The sealed post-lifecycle cleanup controller (`8b03fa71...`) solves the former B6 gap by archiving the full bootstrap ledger BYTE-EXACT, preserving the ACTIVE row, rather than forcing an ACTIVE-to-terminal mutation (forbidden). Its contract v2 (`7b6a1aa5...`) requires `active_state: ACTIVE` handling with terminal states `EXPIRED/CANCELLED/FAILED/COMPLETED` for all prior sessions.

Strict lifecycle gates (all enforced by the sealed controller; any failure is a fail-closed rejection): exactly one authorized ACTIVE bootstrap lifecycle derived from the live ledger; all prior sessions terminal; exactly one disabled customer and owner-terminal state; exactly one sent-and-audited delivery with a terminal provider receipt; generation/draft/card/outbox reconciled; no jobs, claims, leases, attempt locks, or pending/unknown outcomes; the service inactive (dead MainPID 0, no profile process, no gateway lock held); archive committed before any clear; rollback before a terminal receipt on any failure; historical archives and the other profile hash-stable. Today's live state rejects as expected (zero ACTIVE lifecycle, receipt `769db2bd...`); after Section 13 the same dry-run must PASS.

Exact commands (from `exact-execute-command.txt` sha256 `78d7474df421f26ec4d106fa378270f60e57178553c3423fad717698c0fd5b22` and `exact-verify-command.txt` sha256 `e6888d62915b4b57b22b2769ebf46a7a1ee57d0d4ad7c4d5c1502bffc895ebdf`; run from the repository root; display-wrapped):

```bash
python3 '.omo/evidence/task26/reset-controller-st_01a0054d/post-lifecycle-cleanup-v2/cleanup_controller.py' dry-run --profile '/home/cube/.hermes/profiles/dualcoachtest' --other-profile '/home/cube/.hermes/profiles/physique-coach' --archive-root '/home/cube/.hermes/profiles/dualcoachtest/data/post-lifecycle-cleanup-archives' --contract '.omo/evidence/task26/reset-controller-st_01a0054d/post-lifecycle-cleanup-v2/schema-contract-v2.json' --permission '.omo/evidence/task26/reset-controller-st_01a0054d/post-lifecycle-cleanup-v2/permission-seal-v2.json' --candidate '2e0894eac92bc396cc4723bf1f18ebc653b95018dd41574df435941c235da925' --wheel-sha256 'af4a9d0a1ffffb6eb7551c1d6dc2b32853ca6d024332a4f8f5702bbf992f141b' --plan-sha256 '7ace03c6dad33d2fc3ef223621cbca68a150fde8429932138e252fb8498ac582' --approval 'TASK26_POST_LIFECYCLE_ARCHIVE_CLEANUP_APPROVED' --actor-id '8527916639' --run-id 'task26-post-lifecycle-cleanup-2e0894ea'
```

Then, after the human presents the cleanup approval phrase, the same command with `execute`, then:

```bash
python3 '.omo/evidence/task26/reset-controller-st_01a0054d/post-lifecycle-cleanup-v2/cleanup_controller.py' verify --profile '/home/cube/.hermes/profiles/dualcoachtest' --other-profile '/home/cube/.hermes/profiles/physique-coach' --archive-root '/home/cube/.hermes/profiles/dualcoachtest/data/post-lifecycle-cleanup-archives' --archive '/home/cube/.hermes/profiles/dualcoachtest/data/post-lifecycle-cleanup-archives/task26-post-lifecycle-cleanup-2e0894ea' --contract '.omo/evidence/task26/reset-controller-st_01a0054d/post-lifecycle-cleanup-v2/schema-contract-v2.json' --permission '.omo/evidence/task26/reset-controller-st_01a0054d/post-lifecycle-cleanup-v2/permission-seal-v2.json' --candidate '2e0894eac92bc396cc4723bf1f18ebc653b95018dd41574df435941c235da925' --wheel-sha256 'af4a9d0a1ffffb6eb7551c1d6dc2b32853ca6d024332a4f8f5702bbf992f141b' --plan-sha256 '7ace03c6dad33d2fc3ef223621cbca68a150fde8429932138e252fb8498ac582' --approval 'TASK26_POST_LIFECYCLE_ARCHIVE_CLEANUP_APPROVED' --actor-id '8527916639' --run-id 'task26-post-lifecycle-cleanup-2e0894ea'
```

Require dry-run PASS, execute PASS, verify PASS with `post_reset_empty_baseline: true`, then confirm `gateway.lock` absent (G15 form) and the other profiles untouched. No restore or prepopulation from any archive at any point.

## 15. Evidence and receipt inventory (all mode 600, in the run's evidence directory)

- Preflight gate receipts G1-G16 with outcomes and digests.
- Pre-reset receipts: dry-run stdout, archive `manifest.json`/`receipt.json` digests, verify stdout, G15 lock-absence record.
- Invite harness receipts: dry-run, verify, preparation (redacted), claim-watch-ready, claim-observed, and expiry (only if used).
- Deployment receipt: pre/post `direct_url.json` digests and the seven loaded-module digests.
- Subscription transcripts: `outbox-events.jsonl`, `audit-events.jsonl`, `delivery-events.jsonl`.
- Handset step log: actor, action, visible card/result, UTC time, per step.
- Readiness CLI output, activation receipt, disable receipt.
- Exactly-once proof bundle: the single DM evidence, the single audit event, the single delivery attempt/receipt record.
- Cleanup receipts: dry-run, archive manifest/receipt digests, verify stdout, lock-absence record.
- Final summary receipt binding every digest to candidate `2e0894ea`, wheel `af4a9d0a`, plan `7ace03c6`, actor `8527916639`, runbook version v3, the pre-reset pins (`bd051dda`/`4128cece`/`8a290b11`), the cleanup pins (`8b03fa71`/`7b6a1aa5`/`cb063102`), and the harness pins (`2826b1ba`/`c4e85789`/`c7fc4771`).

## 16. Prohibitions (restated)

No invented commands beyond those listed here or in the v3 recovery runbook; no profile state hand-edits; no bootstrap-ledger edits; no manual `gateway.lock` deletion (the sealed pre-reset is its only disposal path; the sealed invite harness refuses to run while it exists); no restore/prepopulate from archives; no raw Telegram listeners or cursor edits; no service replay; no forced publication; no recovery shortcuts; no candidate rebuild or rerollout inside the window; no token/secret mutation; no second invite preparation (sealed maximum 1); no second Start claim (sealed maximum 1); no second delivery; no extra customer DMs; no changes to `physique-coach` or `quarantine`; no git mutations as part of the rehearsal.
