# Task26 Golden Path, candidate 2e0894ea (v5)

Version: v5 (2026-08-15, supersedes v4, v3, v2, and v1 rebind runbooks). v5 makes exactly one correction to G13, replacing the missing workspace event watcher with the sealed lifecycle observer root and preserving every v4 gate, order, and command otherwise.
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-v4.md` sha256 `e0979931fb863d957403f71305d33470697813c5e740622b6b05a23a65c76359` and `task26-recovery-runbook-2e0894ea-v4.md` sha256 `f539460f91096cfe32140dd6d5b5a940ff38a13ad67bf6d83302f6732aa719a1` plus receipt `runbook-binding-receipt-v4.json` sha256 `2a00bb6d8103086e729ac5c36041d0e7d602a1c660c21eda20463ea8de1b78e7` (v4; only G13 is corrected here).
- Supersedes `task26-golden-path-2e0894ea-v3.md` sha256 `ff230719eb5a69ae821bb0a4d58356d59b2803c490d692659a197e22083793c7` and `task26-recovery-runbook-2e0894ea-v3.md` sha256 `e62691493d2a75c312dd248a07bbc52f7f1d98d4acd701b824bbaf5edf8bdd3a` plus receipt `runbook-binding-receipt-v3.json` sha256 `fd77a4d387b9bab7978c84cb51b1ab3200d52a126dea411db98492f6be67fb88` (v3; only the G11 seal path and the G14 authority predicates were stale there).
- 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` (Bot API authority only; no local session files exist or may be created)
- 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 v5 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 | Sealed lifecycle observer root `.omo/evidence/task26/task26-lifecycle-observer-st_01a005b4`; harness sha256 `58ed20c6f12887c10d6bf4fb388dded7c1eec9b30ec7f6bffa7c67e7e237b2c8`; permission `68d98bca...`; schema `7d637a25...`; live-arm manifest `763aa448...`; verification receipt `b9bd1a93...`; hash inventory `6fe4e737...`; tests9, Ruff, LSP, compile, and live-arm-only all PASS; observer manifests pin candidate, wheel, plan, v4 runbooks, receipts, and new lifecycle IDs | Before any trigger, all three modes are armed behind the multi-observer readiness barrier; event-driven inotify with bounded monotonic timeout; no sleep, polling, raw Telegram, cursor, drop, or replay; receipts 0600; arm-only authority before and after identical | PASS at authoring; recheck at run time |
| G14 | Bot API authority (predicates in Section 1.2; replaces the deleted session-file assertions): unit file regular, owner-held, sha256 `0b46e887fc12c45f12814f4ef025c10101ed36e5df0ac35f70abb1862c0c1db2` with `--profile dualcoachtest` and exact `HERMES_HOME=/home/cube/.hermes/profiles/dualcoachtest`; profile `.env` sha256 `154f588c89758c6ca6a1e06cc39ce9489d5c3ff8ace94e10e71cce919252bcca` and `config.yaml` sha256 `f93106b16643227e2ef9dec67a5bbd497e1d353e779da62287898a087071af87` both regular, non-symlink, `cube:cube`, mode 600; no-secret token proof (exactly one nonempty token, length 46, sha256 `d0aacf0f4bdbb7c04e12769947c498ee869248972b36f099800a3d3c27c0a3a8`, loaded/source equality); chat bindings equal (owner DM `8693203710`, review group `-1004484458766` topic `59`, operator card to owner private DM topic `0`); five-module candidate/runtime byte equality; zero obsolete-path references | All predicates in Section 1.2 hold | 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 v5 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).

### 1.2 Bot API authority predicates (G14; adjudicated; hash-only, no-secret)

The old session-file assertions are deleted: the gateway's Telegram authority is the Bot API token, not any local session store. Every predicate below is verified read-only and without exposing secret material; receipts record hashes, lengths, and counts only.

1. Unit authority: `/home/cube/.config/systemd/user/hermes-gateway-dualcoachtest.service` is a regular file owned by `cube`, sha256 `0b46e887fc12c45f12814f4ef025c10101ed36e5df0ac35f70abb1862c0c1db2`; its `ExecStart` contains `--profile dualcoachtest` and its `Environment` sets exactly `HERMES_HOME=/home/cube/.hermes/profiles/dualcoachtest` (and no token variable).
2. Source files: `/home/cube/.hermes/profiles/dualcoachtest/.env` sha256 `154f588c89758c6ca6a1e06cc39ce9489d5c3ff8ace94e10e71cce919252bcca` and `/home/cube/.hermes/profiles/dualcoachtest/config.yaml` sha256 `f93106b16643227e2ef9dec67a5bbd497e1d353e779da62287898a087071af87` are both regular files, not symlinks, owned `cube:cube`, mode 0600.
3. No-secret token proof: `.env` holds exactly one nonempty `TELEGRAM_BOT_TOKEN` assignment; the token has length 46 and sha256 `d0aacf0f4bdbb7c04e12769947c498ee869248972b36f099800a3d3c27c0a3a8`. Loaded/source equality holds by construction and is re-verified read-only: the unit sets no `TELEGRAM_BOT_TOKEN`; `hermes_cli.env_loader.load_hermes_dotenv` loads `<HERMES_HOME>/.env` with precedence; `gateway/config.py` resolves the token via `os.getenv("TELEGRAM_BOT_TOKEN")`; and `config.yaml` contains no bot-token field. The raw token is never printed, copied, stored, or included in any receipt.
4. Chat bindings (exactly one owner DM and one staff/review group; no other chats): `.env` `TELEGRAM_ALLOWED_USERS=8693203710`, `TELEGRAM_GROUP_ALLOWED_USERS=8693203710`, `TELEGRAM_GROUP_ALLOWED_CHATS=-1004484458766`; `config.yaml` `allow_from: [8693203710]`, `group_allow_from: [8693203710]`, `group_allowed_chats: [-1004484458766]`; configured owner/review chat equality: `operator_review` chat_id `-1004484458766` topic_id `59` (staff review; preview topic `219`); `operator_card_route` user_id `8693203710`, chat_id `8693203710` (owner private DM), topic_id `0`.
5. Candidate/runtime byte equality for the five authority modules (installed venv files equal the sealed wheel members): `gateway/config.py` `a8191eed20ca75abcb03c33ac0c0b915cd810249466e816c6a1a55ba154af188`, `gateway/run.py` `f5d51008f1e8c8ee102df930fa68c7945a3276a249187adc7b7f6ba4b1e8e134`, `gateway/platforms/telegram.py` `b41060dea28eb3bbb83217068f5218d5df2c879dba00e73c9ca4e577a6049dad`, `gateway/platforms/nutrition_coaching_config.py` `849791bc3b31145a691b8ef4defacba72aeb5de0fca12427988ec1ddd4e9fffa`, `gateway/platforms/telegram_nutrition_addresses.py` `b48e2b49f0939316e7e4368c940492d1b8a94863da7e5c8141f7f67a1fe08efb`.
6. Zero obsolete-path references: the candidate wheel and the runtime gateway package contain no reference to `auth_39664143`, `.session-journal`, `~/.local/share/hermes/telegram/`, or Telethon (scanned and zero at authoring). Those session files are obsolete under Bot API authority: never create, copy, restore, or require them; their absence is the correct state.
7. No raw tokens or new identifiers appear in evidence beyond the already-approved actor/owner/chat bindings (`8527916639`, `8693203710`, `-1004484458766`, topics `59`/`219`/`0`); token proof is hash-and-length only.

## 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: the sealed lifecycle observer's outbox, audit, and delivery modes are armed from `.omo/evidence/task26/task26-lifecycle-observer-st_01a005b4`, and the `multi-observer` readiness FD reports all three READY before any invite, service, handset, or lifecycle trigger; the harness's own ledger subscription still 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 Sealed lifecycle observer barrier (G13, armed before any trigger)

The only lifecycle observer is `.omo/evidence/task26/task26-lifecycle-observer-st_01a005b4/lifecycle_observer.py`, harness sha256 `58ed20c6f12887c10d6bf4fb388dded7c1eec9b30ec7f6bffa7c67e7e237b2c8`, permission seal `68d98bca346296eacbe4c2cef9a105072314ba542728b043bbff319b6b19b466`, manifest schema `7d637a2587079fc94c28e2866d4f337e97fea102e50f37542299e9611c226489`, live-arm manifest `763aa448fc0220a237fe3a29ebfbd01c6d9ec2855c3db90ac12f157a7a0f2e0d`, verification receipt `b9bd1a93af4969692d23a3e4632825d0c72c02842732bad0447d9b8ce8ce3fdb`, and artifact hash inventory `6fe4e7375bf1ff51ca6fc0850f9adcb6d8baef2b9e6bbce8116ef1044a9ecf06`. The sealed `EXACT_COMMANDS.md` sha256 is `23e673309b4d7cdd1eed77ae8126e58100270f9e4d13ca1093b01c4b1667543e`, and the runtime manifest template sha256 is `a6991e5203ac84f91d8a1c46d32de51c9ac56d63b2906e86583d33c946756bb0`. The test file sha256 is `3ecf6cf7f3384a3a03701f4f565edf6a571a82b53fabd3e8de36ea8648189221`, the tests result sha256 is `26219f277a95d6f001d1055de72287c60bbf3605602d7725ae571aceb436bad1`, the Ruff result sha256 is `6281af7400d166acc4fae18af7fbaa401890eb7ef49607967398118d488f09b4`, the compile result sha256 is `475b72cb29ec28f085efdd1af5382f61b70db460aec861c52bc350c43dbe175b`, and the live-arm-only receipt sha256 is `6ca2fcdbe1c516a68281be941ebe7a2101f0b70fbf95bdff38b7817e1630cee1`.

Create a new 0700 run evidence directory and a 0600 `observer-manifest.json` from the sealed template. Replace every runtime marker with the new lifecycle IDs and receipt SHA-256 values. The manifest must retain the candidate `2e0894eac92bc396cc4723bf1f18ebc653b95018dd41574df435941c235da925`, wheel `af4a9d0a1ffffb6eb7551c1d6dc2b32853ca6d024332a4f8f5702bbf992f141b`, plan `7ace03c6dad33d2fc3ef223621cbca68a150fde8429932138e252fb8498ac582`, and sealed v4 runbook hashes `e0979931fb863d957403f71305d33470697813c5e740622b6b05a23a65c76359` and `f539460f91096cfe32140dd6d5b5a940ff38a13ad67bf6d83302f6732aa719a1`. It must pin the new session, generation, customer, transaction, draft, and delivery IDs, plus reset, invite, provider, and cleanup receipt hashes. No output may pre-exist.

From the repository root, use the exact commands in `EXACT_COMMANDS.md`:

```bash
H='.omo/evidence/task26/task26-lifecycle-observer-st_01a005b4/lifecycle_observer.py'
P='/home/cube/.hermes/profiles/dualcoachtest'
M='<0700-run-evidence>/observer-manifest.json'

python3 -B "$H" subscribe-outbox --profile "$P" --manifest "$M" --after-seq 0 --timeout 120 --output '<0700-run-evidence>/outbox-transition.redacted.json'
python3 -B "$H" audit-tail --profile "$P" --manifest "$M" --after-seq 0 --timeout 120 --output '<0700-run-evidence>/audit-transition.redacted.json'
python3 -B "$H" watch-deliveries --profile "$P" --manifest "$M" --after-seq 0 --timeout 120 --output '<0700-run-evidence>/delivery-transition.redacted.json'
python3 -B "$H" arm-only --profile "$P" --manifest "$M" --receipt '<0700-run-evidence>/arm-only.redacted.json'
python3 -B "$H" multi-observer --profile "$P" --manifest "$M" --timeout 600 --ready-fd "$READY_WRITE_FD" --receipt '<0700-run-evidence>/multi-observer.redacted.json'
```

The parent action controller must block every service, invite, handset, or lifecycle trigger until one readiness message with `status: READY` names all three observers. `multi-observer` arms all three inotify subscriptions before writing READY. Each mode is event-driven and bounded by a monotonic timeout. For every re-arm, use the prior receipt's `transition.revision_after` as both the manifest `after_seq` and CLI `--after-seq`, and set the expected next state. A mismatch fails closed. The observer never sleeps, polls, calls Telegram/provider/network, changes cursors, drops updates, replays events, forces publication, or mutates profile authority. Outputs are owner-held 0600 receipts. The live arm-only proof is PASS with before and after authority hash `d128cb14884551bb9f21562c5111e3685ddb40c0bf023af85f7427855ee70d5c` identical.

### 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.
- Lifecycle observer receipts: `outbox-transition.redacted.json`, `audit-transition.redacted.json`, `delivery-transition.redacted.json`, and `multi-observer.redacted.json`, all 0600 under the run evidence root.
- 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 v5, 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 v5 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; never create, copy, restore, or require the obsolete session files named in Section 1.2 (they are obsolete under Bot API authority, and their absence is correct by design).
