I read this as a Task23 implementation/audit - I’ll inspect the governing evidence and cutover topology read-only before deciding whether a minimal repair is required.


I read this as a topology audit with a potential narrow repair - the evidence confirms the prior blocker, and I’m now validating the approved Task23 contract against the actual activation implementation and its history.


I read this as confirming an implementation gap - the canonical customer-admin transaction already supports a sole disabled customer, while the gateway only exposes it through replacement cutover; I’m mapping the Task23 journal and test seams before adding the failing exact-topology test.


I read this as a narrow Task23 repair assessment - the live state confirms the zero-current topology and no downstream authority; I’m now comparing the existing activation transaction with the generic cutover invariants to choose the least-invasive supported entrypoint.


I read this as a supported-path implementation - hypotheses A (generic replacement), B (fixture-only), and C (direct canonical activation) resolve decisively: only C can preserve the authenticated sole disabled Task23 target without inventing or mutating a current customer. I’m extracting the reusable validation and completion boundaries for a one-purpose Task23 command.


I read this as test-first implementation - the repair boundary is now defined: exact Task23 journal approval, one disabled trainer-free synthetic registry row, canonical activation, then normal committed completion; I’m adding an isolated regression fixture that proves this route and its replay behavior before changing production code.


I read this as completing the isolated test fixture - the live registry confirms the intended target is trainer-free and already carries readiness artifacts, so the regression fixture will model that exact topology without touching live authority.


I read this as a justified narrow repair test - I’m applying only the regression fixture and assertions now; it should fail because no exact no-current Task23 activation API exists yet.


I read this as continuing the test-first repair - the workstation lacks the `apply_patch` helper, so I’m using a shell-local `apply_patch` wrapper around Git’s patch applicator; no files were changed by the failed helper lookup.


I read this as applying the same test-only patch correctly - Git rejected the hand-written hunk counts before changing anything, so I’m rerunning it with automatic hunk recounting.


I read this as correcting patch mechanics, not scope - the prior patch also contained shorthand hunk markers that Git cannot parse. I’m generating a normal unified diff from a temporary copy and applying that diff to the source.


I read this as locking the required behavior before implementation - the new tests cover read-only exact-topology preflight, single canonical activation plus idempotent replay, stale generation rejection, and rejection of a second disabled customer.


I read this as running the newly added regression test - the patch applied, but the shell exited before invoking pytest, so I’m executing the intended failing test now.


I read this as implementing the minimal supported repair - I’m adding a dedicated Task23 method to the existing coordinator, preserving generic cutover behavior while requiring the HMAC-bound successor, exactly one disabled trainer-free target, and canonical activation/completion replay.


I read this as closing the replay invariant - committed bootstrap completion increments the successor generation, so I’m updating the Task23 verifier to accept only that exact one-step ACTIVE transition while retaining the pinned awaiting-generation check.


I read this as verifying the implementation slice - the source changes applied; I’m now running the focused regression tests, which were again placed after the patch command’s shell exit and therefore have not yet run.


I read this as fixing the first replay defect exposed by the test - ACTIVE is terminal in the bootstrap model, so the Task23 successor uniqueness check was excluding its own completed successor; I’m allowing exactly ACTIVE successors and aligning the durable-readiness generation check with that one-step completion transition.


I read this as rerunning the focused Task23 regression - the replay-specific verifier and readiness transition are corrected; I’m validating the exact activation and topology cases again.


I read this as exposing the approved repair through the existing Task23 CLI surface - I’m first extending its parser test for an explicit, default-read-only `task23-supersession activate` action.


I read this as validating the CLI contract before wiring it - the parser should now fail because the exact Task23 activation subcommand has not yet been registered.


I read this as completing the explicit operator entrypoint - I’m adding `task23-supersession activate` with no user-supplied current customer, session, or customer key; it derives the sole HMAC-bound successor and remains read-only without `--execute`.


I read this as verifying the CLI boundary - the action is wired to the dedicated path; I’m running its parser regression before broader behavioral tests.


I read this as validation of the completed repair - the focused tests pass; I’m checking changed-file diagnostics and running the full Task23 and cutover suites in parallel to catch integration and replay regressions.


I read this as resolving static-validation coverage - both related suites and compilation/diff checks passed; the LSP service rejected the sibling repository path, so I’m retrying via the working-directory-relative form and checking the project’s configured lint/type commands.


I read this as finishing static checks - the LSP cannot inspect a sibling repository from this session’s fixed workspace, while the command smoke test reached the expected profile-root validation; I’m using the project’s configured Ruff and Ty tooling and re-running affected suites after the CLI addition.


I read this as fixing diagnostics introduced by the new test support - Ruff’s format check reports a pre-existing unformatted baseline across all four untracked files; Ty correctly found the test’s new static imports cannot resolve the profile-local package. I’m replacing those imports with the same runtime module discovery used by the production coordinator and making the observation construction statically explicit.


I read this as checking that the diagnostic repair is clean - I removed the new static profile-package imports without changing behavior; I’m rerunning lint, type checking, and the focused Task23 tests.


I read this as final V3 verification - lint and type checks are clean, and the focused cases pass; I’m re-reading the changed activation/CLI boundaries, running both related suites once more, building to a temporary output directory, and exercising the CLI help without touching the live profile.


I read this as hardening a replay edge found during final review - Task23’s post-activation phases must require the adaptive feature epoch just like generic post-activation phases. I’m adding the failing regression before extending that shared requirement.


I read this as confirming the replay hardening test - it should presently fail because the Task23-complete phase does not yet mark the feature epoch required.


I read this as applying the confirmed replay fix - the new test failed exactly as expected, so I’m extending the existing post-activation feature-epoch requirement to the two Task23 post-activation phases.


I read this as final verification after the replay hardening - I’m rerunning static checks, all related tests, the package build, CLI help, and the previously pinned live-authority hashes; no activation command will be executed against the live profile.


I read this as implementing the justified narrow Task23 repair - completed and verified without live activation.

- Added exact no-current Task23 activation and read-only-by-default CLI action.
- Requires the HMAC-bound, freshly approved successor; exactly one disabled trainer-free synthetic target; canonical readiness and activation.
- Preserves generic cutover behavior and supports idempotent ACTIVE replay.
- Verified live registry/journal/outbox hashes remained unchanged.

Verified: 53 related tests passed, Ruff/Ty passed, and package build succeeded. Ruff format check remains baseline-wide noncompliant in pre-existing untracked files; unrelated formatting was left untouched.


I read this as an evidence-only audit follow-up - I will make no edits and no live-profile access; I’m collecting the existing test, source, and prior-seal artifacts needed to distinguish the verified repair from the now-stale Task23 seal.


I read this as completing the evidence gap analysis - the prior seal is conclusively stale after the four source changes; I’m now checking only local source/tests for the requested disposable E2E stages and the wheel-receipt metadata, without executing or accessing live systems.


I read this as an evidence-only audit - I’m finishing the local state-machine/CLI and test-coverage review, then will report the stale-seal status and validated results without edits or live access.


I read this as an evidence-only audit - the source review confirms the requested activation boundary; I’m extracting the exact CLI dispatch and historical validation commands, with no mutation or live-profile access.


I read this as an evidence-only audit - I’m checking the exact named phases and parser contract once more so the final report distinguishes tested fail-closed behavior from the remaining disposable-E2E gap.


I read this as an evidence-only audit - no files were edited and no live profile, Telegram, or service command was run.

**Scope**
- Task23 delta: `telegram_nutrition_onboarding_task23_supersession.py`, `telegram_room_bootstrap_cutover.py`, `scripts/nutrition-room-bootstrap`, and `test_task23_supersession.py`.
- The repository has extensive unrelated dirty baseline; it was not touched.

**State machine and CLI**
- Supersession: `PREPARING -> PREPARED -> CUSTOMER_BOUND -> FRESH_OWNER_APPROVED`.
- Activation: `task23_ready -> task23_activated -> task23_complete`; a replay of `task23_complete` reconciles without another activation.
- Fail-closed guards require the authenticated successor, exact generation, one trainer-free disabled target, no enabled/current replacement customer, disabled feature flags, valid current-KST checklist, readiness authority, and a valid post-activation feature epoch.
- `task23-supersession preflight` is read-only. `prepare` and `replay` require `--execute`.
- `activate` selects `preflight_task23_synthetic` unless `--execute` is supplied, then selects `execute_task23_synthetic`.
- Activation argv contract:
  ```text
  nutrition-room-bootstrap task23-supersession --profile-root <profile> activate
    --data-root <path> --g1-g5-checklist <path> --kst-date <YYYY-MM-DD>
    --customer-user-id <id> --provider-membership-receipt <path>
    [--package-root <path>] [--execute]
  ```

**Tests**
- No separate failing/RED run is evidenced.
- Recorded GREEN command:
  ```bash
  pytest tests/gateway/test_task23_supersession.py \
    tests/gateway/test_telegram_room_bootstrap_cutover.py --no-header -q
  ```
  Result: `53 passed in 1.25s`.
- The files contain 18 Task23 test functions and 20 cutover test functions; parametrization accounts for the 53 collected cases.
- Negative-path coverage includes stale historical authority, old callback rejection, stale generation, no-current topology, missing feature epoch on replay, and second-customer rejection.

**Recorded validation**
- Scoped Ruff and Ty: `All checks passed!`
- `compileall` and `git diff --check`: passed.
- `uv build --quiet --out-dir "$out"` produced an sdist and `hermes_agent-0.17.0-py3-none-any.whl`.
- Scoped `ruff format --check` remains baseline-noncompliant for pre-existing untracked files; no broad formatting was applied. (`ruff` is available as `.venv/bin/ruff`, not on PATH.)

**Seal status: stale**
- Prior seal: candidate `a778ee4f...`; manifest SHA `fea342f6...`; 11 leaves; status snapshot 1,003 entries / 46,725 bytes.
- Wheel: SHA `17a69181...`, 8,556,461 bytes, 994 members, index `e1f4edf7...`.
- Wheel receipt file SHA `9b46528e...`; receipt digest `2d85b013...`.
- Freeze SHA `4ab72d62...`; freeze receipt digest `8b59b0f9...`.
- Verifier SHA `899f3361...`.
- All four current Task23 files mismatch their sealed leaf hashes. The seal must not be treated as evidence for the current source.

**E2E coverage**
- Covered with isolated fixtures: customer `/start`, fresh owner card/callback, no onboarding-service entry, canonical activation, replay, and topology rejection.
- Gap: no disposable-profile E2E runs the CLI through activation and then a full question-12 -> durable generation-job -> delivery-disabled flow. The reviewed Task23 tests do not cover that end-to-end chain.

**Live operations**
- None. No live preflight, activation, profile read, Telegram call, or service command was issued in this audit.


I read this as an offline implementation and verification request - I’ll add a deterministic disposable-profile E2E regression through the production CLI contracts, capture a failing baseline first, fix only the exposed gaps, then reseal and verify entirely against local source and temporary fixtures.


I read this as offline implementation work - the existing Task23 fixture already creates a temporary authority but does not traverse the production CLI into post-activation coaching, so I’m tracing the established durable check-in test harness before adding the smallest end-to-end bridge.


I read this as an offline continuation with stricter diagnostic provenance - I’ll isolate the four Task23-owned files from the unrelated dirty baseline, run Ruff/Ty only through their import closure, and fold the disposable CLI E2E into that owned scope before resealing.


I read this as offline E2E construction and provenance control - the owned four are all untracked Task23 files with clean Ruff/Ty lint/type results, while formatting is confined to those four; I’m now deriving the real check-in contract and activation-to-runtime handoff before creating the required RED test.


I read this as offline E2E implementation - the runtime path is a real canonical registry reload plus `NutritionCoachingCoordinator` and its durable bridge; I’m pinning its provider-receipt and 12-step contracts now, then I’ll add the failing CLI-to-runtime test without touching unrelated baseline files.


I read this as the requested RED-first offline E2E - I found a concrete Q7 boundary defect: direct model actions currently accept any non-empty sleep-quality value even though the production callback contract permits only 1–5. I’m adding the disposable CLI-to-runtime regression first so that defect is recorded before the minimal runtime fix.


I read this as a correction to the RED setup - the first patch was rejected before any source change, so I’m applying the same isolated test through a generated unified patch and then running only that test to obtain the actual baseline failure.


I read this as a real RED result from the new offline path - activation reaches canonical customer enablement but completion is passed the pre-enable bootstrap generation, so it deterministically fails stale before Q7. I’m tracing that generation boundary and will repair it before rerunning the full chain.


I read this as focused offline diagnosis - the stale boundary is not caused by canonical activation itself, so I’m instrumenting the disposable fixture around the exact readiness/completion call to identify which local store operation changes the generation before applying a minimal fix.


I read this as keeping the E2E deterministic - the first RED was a fixture-clock mismatch: the production CLI reopened the temporary 30-minute store using wall clock and correctly expired it. I’m pinning only the disposable store clock, then the same test will expose the real Q7 control defect.


I read this as completing the owned-scope cleanup - the deterministic E2E is green after correcting only its temporary clock seam; I’m now applying Ruff’s scoped diff exclusively to the four Task23-owned files, preserving every unrelated dirty file and hunk.


I read this as completing scoped formatting without scope creep - Ruff emitted its patch on stderr, so the first application was a no-op; I’m re-capturing that exact formatter patch and applying it only to the four owned files.


I read this as a patch-path correction - the formatter patch is valid but Ruff labels paths without `a/`/`b/`, so `git apply` stripped one directory; I’m applying the unchanged scoped patch with zero path stripping.


I read this as fixing E2E-introduced type errors only - Ruff is now clean for all four owned files; Ty found four diagnostics solely in the new test, so I’m correcting the fixed-clock override and narrowing the optional request token before rerunning the scoped checks.


I read this as offline verification before resealing - the new E2E and all four Task23-owned files are now Ruff-format/Ruff-check/Ty clean, so I’m running the Task23, canonical cutover, activation, predecessor, and coaching regression suites once against isolated temporary fixtures.


I read this as preparing the offline reseal - all relevant suites are green (165 Task23/cutover/activation/predecessor and 133 coaching), so I’m inspecting the existing deterministic seal tooling and source-leaf inventory before replacing the stale evidence with a current, verifier-bound seal.


I read this as final offline build/seal preparation - the current manifest’s 11 leaves already include the production launcher and all four current Task23 files; I’m running the standard package build and confirming Ty’s strict invocation before atomically replacing the stale code-only seal.


I read this as final pre-seal verification - the standard build completed in a temporary directory and was removed; I’m now rerunning the formatted suite and Ty with all diagnostics promoted to errors, then will create and independently verify the current two-epoch seal.


I read this as a strict-type correction confined to the E2E test - the behavioral suites are green, but strict Ty correctly sees a loop-local final result as potentially unbound, so I’m making the twelfth, save-producing Specification23 action explicit.


I read this as completing the corrected strict verification before sealing - the disposable production-CLI E2E passes in isolation; I’m rerunning its full affected regression set and all scoped static gates once more, then producing the deterministic offline seal.


I read this as producing the requested code-only receipt - the final affected suites are green (165 + 133) and strict scoped static checks pass, so I’m replacing only the stale Task23 evidence artifacts and immediately validating their source, status, receipt, and two-epoch wheel bindings offline.


I read this as completing the scoped Task23 audit, E2E, and offline reseal - all owned checks are clean and unrelated baseline remains untouched.

- Added/fixed disposable-profile production-CLI E2E through activation, reload, 12-question Specification23 flow, durable generation, restart, replay, and no pre-Task24 delivery.
- Fixed only E2E-introduced typing/clock seams.
- Verified:
  - 165 Task23/cutover/activation/Task22 tests passed.
  - 133 nutrition-coaching tests passed.
  - Scoped Ruff format/check, strict Ty, compileall, and diff check passed.
  - Standard package build and deterministic two-epoch wheel build passed.
- Replaced and independently verified the offline seal:
  - Candidate: `47a69e94d0847cc9db179064dd428172f825701fb824c6954291321eff31e01d`
  - Wheel: `9c2b57fc5c517bb3ffbdd925946e34e4015bf0986f91c93998cc70f00761f60d`
  - Manifest: `d639558a344a37cbb3b9c811fe14501199b05af6126ba97b3077c017a461be3f`
- No live profile, service, Telegram, or authority was accessed.

Unrelated dirty baseline remains excluded: 999 non-Task23 status entries (44 modified, 955 untracked). Previously observed broad baseline failures were left untouched.


I read this as an evidence-only audit request - I will read only the current sealed artifacts and local source/test evidence, then report exact hashes, commands, outcomes, and the fail-closed operating procedure without changing or accessing live state.


I read this as completing the evidence record - the seal supplies all 11 leaf hashes and receipts; I’m reading only the command dispatch and fail-closed activation definitions to distinguish exact verified behavior from values that cannot be derived without opening a live profile.


I read this as resolving the remaining procedure details from code, not execution - I’m locating the exact activation preconditions and receipt transitions so the requested runbook identifies every fail-closed boundary without inventing live paths or identifiers.


I read this as finishing the documented activation state machine - the fixture and readiness receipt are now exact; I’m reading the two cutover methods and plan validation to capture the expected preflight/execute/replay transitions and required checklist/package bindings.


I read this as a final evidence-only clarification of package resolution - the activation and readiness transitions are established; I’m reading only the cutover constructor/module loader to state whether `--package-root` is required or merely an explicit package pin.


I read this as validating the final unsealed verifier reference before reporting - I’m performing one read-only hash/stat lookup of the verifier itself; no source, evidence, profile, service, or live authority will be changed or opened.


I read this as an evidence-only Task23 handoff - below is the current sealed inventory, verified test evidence, and fail-closed procedure; no edits or live access occurred.

## Current seal

- Candidate digest: `47a69e94d0847cc9db179064dd428172f825701fb824c6954291321eff31e01d`
- Candidate diff digest: `2fa1172e384f81ec8ba225f5b31c2c895a9d9628e20db4984e4c98d5f328ebf4`
- Leaves: 11; source bytes: 1,085,888; patch payload bytes: 629,182.
- Manifest: SHA-256 `d639558a344a37cbb3b9c811fe14501199b05af6126ba97b3077c017a461be3f`, 6,007 B, mode `0600`.
- Freeze: SHA-256 `4c918ea46ca706885da917294c7f931a56710ea42bf82c250a1b330d77ea2d65`, 680 B, mode `0600`; receipt digest `55283bea0a65c327ca6b36c1d4ae8171ee7a653d26e0ddb2a7848e5e653bddc1`.
- Verifier: SHA-256 `899f33615bc8d9262477d4a70af0f7a82722874b34913d0e59ea4ea17a8f93a5`, 8,928 B, mode `0700`.
- Launcher: SHA-256 `9eb9cf494c1d219fd716fbe87148f7a87a039a9a639e2a62ec137770d7b1dc4e`, 25,272 B.
- Wheel: SHA-256 `9c2b57fc5c517bb3ffbdd925946e34e4015bf0986f91c93998cc70f00761f60d`, 8,557,357 B, 994 members; member-index SHA-256 `2a4e7238c38222c2c6dd5cd5e2482e81bcd8dd8b53fb3b8a1ff770cc4ebc81f1`.
- Wheel receipt: file SHA-256 `5b525b8794aa4288f91d7a286a646ed1dfd56ee282aaa35b27e2ade5c9cec6c6`, 842 B; receipt digest `d2991e1b3dc07415b20f5fb7c4e53aa994b8ed23ec4dbafc9aad6c2bf5bcfa5f`.
- Status snapshot: SHA-256 `a18fd0152d64ed4de88e5494074c77ea40e112e2e108721f5e3a3762aea9e70f`, 46,725 B, 1,003 entries.
- Profile-package provenance: predecessor digest `3090dc7ca9e93236394dd4da88647b100ea70b22f00080950a24e1d254f575e6`; `accessed: false`. No profile-package byte count exists because it was deliberately not opened.
- Two reproducibility epochs: `1700000000`, `1800000000`; exact wheel equality asserted. Builder SHA-256: `284e0b428f96851241c8b3a2531d2227d38dda8e4ca1ed30fd4375cc67cefb39`.

### All sealed leaves

`*` marks the four Task23-owned topology/E2E files.

| Leaf | Source bytes / SHA-256 | Diff category, patch bytes / SHA-256 |
|---|---|---|
| `gateway/platforms/telegram.py` | 695,369 / `ce35a2e77af14994949aa5a9de12c0b742bd87fcd73cfa48f1e3b7c5d39320c6` | tracked, 225,867 / `1e9feb540a4243093d89bdead81507d5057aa7a1106c19da1d86526528ccebb4` |
| `gateway/platforms/telegram_nutrition_onboarding_runtime.py` | 34,987 / `e1f15fa2f1e04ccc42da582db1b7032abb24a4ff4c78de318e2307323e68c1d5` | untracked, 36,137 / `332b6aef91e3f8836567e2a7526136bcc489401432958bdff64e4c7aa898ca9f` |
| `gateway/platforms/telegram_nutrition_onboarding_runtime_authority.py` | 7,480 / `75bd8c9217c34874ed3788cab88ab2e098048bfe58c3e98c21bd9d7f5283a7ca` | untracked, 8,010 / `cc276b378d87b4b9dbf8b36973911d3e4475421aecad759d0cebf8beceadf9a5` |
| `gateway/platforms/telegram_nutrition_onboarding_runtime_callback.py` | 19,026 / `5e13ff75efc63c4b326bc4c45b952aea11663f5aa765a4a3aa07a2f7f33eab8f` | untracked, 19,860 / `adf92b39af8d27e66a9ff8f549aeee06e4b5cf50543adb135c57325092790590` |
| `* gateway/platforms/telegram_nutrition_onboarding_task23_supersession.py` | 58,342 / `8d8f5d40ff1f228abcc02c38ec0d3e8b9cd840ed43b36941509ee845f52d9fc2` | untracked, 60,077 / `1b75a559c06258e513360fa41b1fb40cc5a7a0cedf2b6573f3a0f23732312ea1` |
| `gateway/platforms/telegram_room_bootstrap.py` | 103,078 / `a715d39aaa0bc1a8ba5d7c5a6aa0450bca340b7489971cf82a91d23f56980039` | untracked, 105,899 / `e314fdd3fb35a4601ed482f1aa72717aba3c3ce1c484b32a4ed0390dde0012d9` |
| `gateway/platforms/telegram_room_bootstrap_activation.py` | 14,966 / `49a3c0c9d3efdc6ff5b95e96216d88eaf184c171a4ff4993acbd939069f46203` | untracked, 15,635 / `6986069720b25afa13e47c3db1a51086f776047dccf1df2a87065b697f5fd9af` |
| `* gateway/platforms/telegram_room_bootstrap_cutover.py` | 45,503 / `8407f8c06aa67d13dcdee95fc363c75ea9551133d84a95fd97994125c87c08c5` | untracked, 46,948 / `9aabdf8046d0dcc730384c5b00fe2cf3b0303fe0947572b665d15891d9002de8` |
| `* scripts/nutrition-room-bootstrap` | 25,272 / `9eb9cf494c1d219fd716fbe87148f7a87a039a9a639e2a62ec137770d7b1dc4e` | untracked, 26,128 / `fcdefe9dba55fda486898d935ec209f5f47ce75a4b615cfd34a9924df2b9397f` |
| `* tests/gateway/test_task23_supersession.py` | 60,115 / `8ae4aadfed2d74af9b064e720073bd396eda314f89c437f6b3981e3e8dcec424` | untracked, 62,025 / `6bee73b1c5b442d49a1cba04744612258055f67fd3f776446042f3b042e956c3` |
| `tests/gateway/test_telegram_room_bootstrap.py` | 21,750 / `9089b2e34def7ea4e87c3bb41178d2df50575e8cfe7c93a3ab0dcad7c8fa150e` | untracked, 22,596 / `884755ac9444f55014cf7da5716f1af5ea2ceb5590a2da790535ce9f08b24867` |

## Exact verification commands and outputs

Working directory: `/home/cube/projects/richard/hermes-agent`.

```bash
PYTHONDONTWRITEBYTECODE=1 .venv/bin/pytest -p no:cacheprovider --no-header -q \
  tests/gateway/test_task23_supersession.py \
  tests/gateway/test_telegram_room_bootstrap_cutover.py \
  tests/gateway/test_telegram_room_bootstrap_activation.py \
  tests/gateway/test_task22_supplemental_owner_review.py
```

Output: `165 passed in 3.72s`.

```bash
PYTHONDONTWRITEBYTECODE=1 .venv/bin/pytest -p no:cacheprovider --no-header -q \
  tests/gateway/test_nutrition_coaching.py
```

Output: `133 passed in 2.19s`.

```bash
.venv/bin/ruff format --check \
  gateway/platforms/telegram_nutrition_onboarding_task23_supersession.py \
  gateway/platforms/telegram_room_bootstrap_cutover.py \
  scripts/nutrition-room-bootstrap \
  tests/gateway/test_task23_supersession.py

.venv/bin/ruff check \
  gateway/platforms/telegram_nutrition_onboarding_task23_supersession.py \
  gateway/platforms/telegram_room_bootstrap_cutover.py \
  scripts/nutrition-room-bootstrap \
  tests/gateway/test_task23_supersession.py

.venv/bin/ty check --project . --python .venv --error all --error-on-warning \
  gateway/platforms/telegram_nutrition_onboarding_task23_supersession.py \
  gateway/platforms/telegram_room_bootstrap_cutover.py \
  scripts/nutrition-room-bootstrap \
  tests/gateway/test_task23_supersession.py

PYTHONDONTWRITEBYTECODE=1 .venv/bin/python -m compileall -q \
  gateway/platforms/telegram_nutrition_onboarding_task23_supersession.py \
  gateway/platforms/telegram_room_bootstrap_cutover.py \
  scripts/nutrition-room-bootstrap \
  tests/gateway/test_task23_supersession.py

git diff --check -- \
  gateway/platforms/telegram_nutrition_onboarding_task23_supersession.py \
  gateway/platforms/telegram_room_bootstrap_cutover.py \
  scripts/nutrition-room-bootstrap \
  tests/gateway/test_task23_supersession.py
```

Output, in order:

```text
4 files already formatted
All checks passed!
All checks passed!
```

`compileall` and `git diff --check` succeeded silently.

```bash
out=$(mktemp -d /tmp/task23-uv-build.XXXXXX)
trap 'rm -rf "$out"' EXIT
uv build --quiet --out-dir "$out"
find "$out" -maxdepth 1 -type f -printf '%f %s\n' | sort
```

Output:

```text
.gitignore 1
hermes_agent-0.17.0-py3-none-any.whl 8557357
hermes_agent-0.17.0.tar.gz 12045117
```

```bash
cd '/home/cube/projects/richard/traning coach'
PYTHONDONTWRITEBYTECODE=1 python .omo/evidence/reseal-task23-expiry-supersession.py --replace
```

Output:

```json
{"candidate":"47a69e94d0847cc9db179064dd428172f825701fb824c6954291321eff31e01d","freeze":"55283bea0a65c327ca6b36c1d4ae8171ee7a653d26e0ddb2a7848e5e653bddc1","manifest":"d639558a344a37cbb3b9c811fe14501199b05af6126ba97b3077c017a461be3f","mode":"offline-code-only-task23-reseal","paths":11,"wheel":"9c2b57fc5c517bb3ffbdd925946e34e4015bf0986f91c93998cc70f00761f60d","wheel_receipt":"d2991e1b3dc07415b20f5fb7c4e53aa994b8ed23ec4dbafc9aad6c2bf5bcfa5f"}
```

Independent verifier output:

```json
{"candidate":"47a69e94d0847cc9db179064dd428172f825701fb824c6954291321eff31e01d","freeze":"55283bea0a65c327ca6b36c1d4ae8171ee7a653d26e0ddb2a7848e5e653bddc1","manifest":"d639558a344a37cbb3b9c811fe14501199b05af6126ba97b3077c017a461be3f","mode":"offline-code-only-task23-verification","paths":11,"wheel":"9c2b57fc5c517bb3ffbdd925946e34e4015bf0986f91c93998cc70f00761f60d","wheel_receipt":"d2991e1b3dc07415b20f5fb7c4e53aa994b8ed23ec4dbafc9aad6c2bf5bcfa5f"}
```

## Disposable CLI E2E RED/GREEN

Test:

```text
test_task23_disposable_cli_activation_reloads_into_specification23_checkin
```

- RED: the first run reopened the disposable bootstrap store using wall-clock time, making the fixed-time fixture successor expired. The failure was corrected only by pinning that test-store clock to `NOW`. No production authority behavior changed.
- GREEN command:

```bash
PYTHONDONTWRITEBYTECODE=1 .venv/bin/pytest -p no:cacheprovider --no-header -q \
  tests/gateway/test_task23_supersession.py::test_task23_disposable_cli_activation_reloads_into_specification23_checkin
```

Output: `1 passed in 0.35s`. It is also included in the 165-test GREEN run.

Exact disposable outcomes:

- Invalid `--customer-user-id 999`: `SystemExit`; complete disposable profile tree byte-for-byte unchanged.
- Valid no-`--execute` invocation: `mode == "preflight"`, `phase == "task23_ready"`; tree unchanged.
- First `--execute`: `phase == "task23_complete"`; bootstrap successor is `ACTIVE`; enabled registry keys equal only `[task22_dm_rehearsal]`.
- Identical second `--execute`: `phase == "task23_complete"` and `reconciled == true`.
- A direct preflight using `active.generation - 1` fails with `ActivationCutoverError` matching `generation`.

The 12-question flow is exact:

1. `bodyweight`: `value "70"`
2. `calories`: `value "2300"`
3. `macros`: `value "150 280 65"`
4. `meals`: `value "계획대로 3식"`
5. `water`: `value "2.5"`
6. `sleep_duration`: `value "7"`
7. `sleep_quality`: `select "4"`
8. `digestion`: `select "normal"`
9. `condition`: `select "4"`
10. `appetite_stress`: `value "식욕 3/5, 스트레스 2/5"`
11. `training_summary`: `value "하체 70분"`
12. `optional_note`: `select "skip"`

Q7 rejects `select "6"` with `accepted == false` and does not advance the bound step.

Generation job evidence:

- Identity is the dynamically issued `completed.completion.request_token`; no literal token is fixed or emitted by the test.
- The exact asserted identity contract is non-null token plus `draft_generation_history(token) == ["generation_pending"]`.
- Count is exactly one before restart and exactly one after restart/duplicate callback.
- Restart reloads the bootstrap as `ACTIVE`, reloads canonical registry and coordinator, retains the same one `generation_pending` record, and rejects duplicate save callback with `accepted == false`.
- Delivery count is zero: both `data/scheduled-deliveries.jsonl` and `data/owner-actions/draft-deliveries.json` are absent before and after restart.

## Active-safe preflight argv and controlled procedure

No literal live values can be supplied without reading a live profile, which was not done. This is the exact invocation shape; it omits `--execute` and must not be run under the current no-live-access constraint:

```bash
/home/cube/projects/richard/hermes-agent/scripts/nutrition-room-bootstrap \
  task23-supersession \
  --profile-root "$PROFILE_ROOT" \
  activate \
  --package-root "$PACKAGE_ROOT" \
  --data-root "$PROFILE_ROOT/data/customers/$TASK23_CUSTOMER" \
  --g1-g5-checklist "$G1_G5_CHECKLIST" \
  --kst-date "$CURRENT_KST_DATE" \
  --customer-user-id "$CANONICAL_CUSTOMER_USER_ID" \
  --provider-membership-receipt "$MEMBERSHIP_RECEIPT"
```

Required inputs and resolution:

- `--profile-root`: required, existing non-symlink directory. It resolves bootstrap state, canonical registry, Task23 HMAC journal, and profile-local package.
- `--package-root`: parser-optional, but should be explicitly supplied for a controlled run. If omitted, it resolves to `$PROFILE_ROOT/workspace/checkin_cli`; it must be a safe directory.
- `--data-root`: required safe directory and must resolve exactly to `$PROFILE_ROOT/data/customers/$TASK23_CUSTOMER`.
- `--g1-g5-checklist`: required safe regular file. It must bind the exact customer key, profile root, data root, canonical registry path, current KST date, consent version, and affirmative G1-G5 items.
- `--kst-date`: required ISO date and must equal the cutover process's current `Asia/Seoul` date.
- `--customer-user-id`: required, but is accepted only when it matches the Task23 successor customer claim, canonical private-DM address, and membership receipt.
- `--provider-membership-receipt`: required non-symlink file. It must contain exactly the v2 `telegram.getChatMember` observation fields and canonical `receipt_digest`; be complete, fresh within five minutes, bind the customer private DM and owner, cover exactly the owner staff route, and show the customer is not an active member of that staff chat.

Authentication and fail-closed gates:

1. The CLI first invokes Task23 supersession preflight and takes the successor session ID and generation from its authenticated journal; callers cannot supply a different session or generation.
2. The journal validates its HMAC, old-ledger/session pins, Task22 terminal evidence, answer provenance, exact sole successor, customer binding, and fresh owner callback.
3. The target is fixed to `TASK23_CUSTOMER`; the generic `activate-cutover` path is not applicable because Task23 has no current enabled customer.
4. The canonical registry must contain exactly one trainer-free target. Preflight accepts only:
   - target disabled and zero enabled customers: `task23_ready`, or
   - target as the sole enabled customer: recovery state.
5. Readiness validates consent, private customer DM, staff review route, provider observation, and customer/staff separation.
6. Delivery and activation config/feature-epoch flags must remain false through Task23.

Expected transitions:

```text
Supersession journal:
PREPARING -> PREPARED -> CUSTOMER_BOUND -> FRESH_OWNER_APPROVED

Successor bootstrap:
PREPARED -> REGISTERING -> AWAITING_ACTIVATION -> ACTIVE

Task23 cutover receipt:
task23_ready -> task23_activated -> task23_complete

Generation:
no request -> one generation_pending request_token -> one generation_pending request_token after restart
```

Controlled execution, only after an independently authorized live preflight returns `task23_ready`:

1. Append `--execute` to the same argv. It persists readiness, initializes the target data root, activates the sole target, then completes the authenticated bootstrap.
2. Expected result: `task23_complete`, sole enabled Task23 target, successor `ACTIVE`.
3. Re-run the identical execute argv once only as reconciliation: expected `task23_complete` and `reconciled: true`.
4. Start coaching only through the committed customer DM launcher and its bound callback contract; complete the 12 responses above, then save.
5. Treat returned `completion.request_token` and its durable `generation_pending` ledger record as the exact job signal. Do not sleep or poll; do not create delivery artifacts or invoke Task24 delivery.
6. Restart normally, reload canonical registry/coordinator, verify the same token has exactly one pending record, and reject a duplicate save callback.

No source, seal, profile, Telegram, service, registry, or live authority was changed in this follow-up.