I read this as an investigation - I will trace the r20 rehearsal, preseal, and live rollback evidence to identify the missing real surface and produce a recurrence-proof PASS gate.


I read this as an investigation - I traced r20's sealed controller, rehearsal, live authority chain, and rollback evidence without making changes.

## Decisive finding

The rejection was local and pre-network, not a Telegram provider failure.

r20 changed `task26-candidate-digest` to `3dab...5090`, but preserved a `task26-authority-pin.json` that pointed to the external authority chain whose current candidate remained the predecessor `6c9c...6a92`.

The adapter loads that pin during `connect()`, then rejects unless the external chain's `current_qualified_candidate` equals the credential candidate. The exact guard is in:

- `gateway/platforms/task26_runtime_authority.py:104-149` - reads paired `candidate-authority/registry.json` and `qualification-ledger.json`; rejects with `runtime candidate is not current or was revoked`.
- `gateway/platforms/telegram.py:2918-2958` - loads that authority before network connection and fails activation on any exception.

Read-only inspection of the pinned real authority showed:

- `current_qualified_candidate`: `6c9c...6a92` (the predecessor)
- r20 digest appears in events: `false`
- r20 digest is current: `false`
- r20 digest is invalidated: `false`

So the missing surface was **not merely a credential**. It was the complete external, append-only current-authority publication:

1. a paired `qualify` event/ledger row for the fresh candidate,
2. a matching authority pin with the new registry head, ledger head, and event count,
3. systemd credentials that reference that new pin and the same candidate digest.

`switch_postimages()` explicitly rewrote only `candidate-digest` and `task26-candidate-digest`; every other credential, including `task26-authority-pin.json`, was copied unchanged. See `scripts/nutricoach_v150_host_operations.py:127-143`.

## Why the passes were not contradictory

- The 13-case rehearsal was explicitly `loopback-only` and passed a `DisposableService`, not the live adapter against its external authority chain:
  - `.omo/evidence/nutricoach-v150-combined/task-v14r20-rehearsal/report.json`
  - `scripts/nutricoach_v150_sealed_target.py:43-74`
  - The rehearsal contains zero `candidate-authority` files; it only has copied runtime credentials.

- The sealed target authorized no external or Telegram customer events, and channel inbox was disabled:
  - `sealed-target.json` has `channel_inbox_authorized: false`, `external_events_authorized: 0`, `provider_events_authorized: 0`, and `telegram_customer_events_authorized: 0`.

- The independent audit was a pre-live closure audit, not a live-authority PASS gate. Its own output records:
  - `live_authorization_root_absent: true`
  - `live_execution_root_absent: true`
  - `live_successor_root_absent: true`
  - `authorization_state: AWAITING_FRESH_OWNER_INSTRUCTION`
  - Source: `.omo/evidence/nutricoach-v150-combined/task-v14r20-independent-audits.json`

- r20's candidate qualification also said `QUALIFIED_PENDING_LIVE_AUTHORIZATION`, not deployable.

The live run proves the gap: r20 reached `COMMITTED` with the successor service running, then manual adapter QA rejected authority and rollback restored the predecessor. No provider or customer event occurred.

- `.omo/evidence/nutricoach-v150-combined/task-v14r20-live-outcome.json`
- `/home/cube/.hermes/migrations/.../nutricoach-v150-v14-live-representative-59-3dab1a55/postcommit-rollback-receipt.json`

## False assumptions to remove

1. **"Rebinding the candidate credential makes it current."**
   False. Currentness is determined only by the external paired authority chain and its pin.

2. **"A loopback/service-state rehearsal exercises Telegram adapter startup."**
   False. The rehearsal tested controller stages and a filesystem service state machine, not `TelegramPlatform.connect()` against the pinned authority root.

3. **"Independent audit PASS is authorization to consume the live one-use ledger."**
   False. The audit explicitly excluded all live roots.

4. **"The immutable source closure covered the authority behavior."**
   False. r20's wheel contains `task26_candidate_authority.py` and `task26_runtime_authority.py`, but both are absent from its `source_inventory` and frozen `controller-source`. The source audit therefore could not independently inspect the code that rejected r20.

5. **"A postcommit manual adapter check is an acceptable first authority check."**
   False. It consumed r20's one-use ledger before finding the missing authority publication.

## Fresh-successor PASS gate

Do not authorize a successor unless every item is evidenced before ledger consumption:

- [ ] New digest and new one-use authorization. r20 is consumed (`CONSUMED/SUCCEEDED`) and must never be replayed.
- [ ] Full wheel-to-source closure: every executable wheel module, especially both Task26 authority modules, has a sealed source path and hash.
- [ ] A new external authority root exists outside profile, package, wheelhouse, candidate, and registry roots; it has required private ownership/modes.
- [ ] Its paired registry/ledger chains validate; the successor has an appended `qualify` entry and is the sole `current_qualified_candidate`.
- [ ] The new authority pin matches that root's source ID, genesis, registry head, ledger head, and event count.
- [ ] Installed successor credentials prove the same candidate digest in `task26-candidate-digest` and the new pin in `task26-authority-pin.json`.
- [ ] An offline, disposable **installed-wheel + real credential-surface** test invokes the actual authority loader and adapter activation path: it must accept the successor and reject a stale, missing, revoked, or pin-stale candidate. No `DisposableService`, direct field injection, or mocked authority source qualifies.
- [ ] Only if channel inbox is enabled: a separately authorized test-bot probe runs the adapter's actual three read-only Bot API checks (`getMe`, direct-message `getChat`, parent-channel `getChatMember`) and records exact bot, parent, privilege, and candidate bindings. Permit zero customer updates/messages.
- [ ] The controller performs the authority-loader check **before** marking the one-use ledger `SUCCEEDED`; failure leaves the ledger unconsumed and the predecessor untouched.

## Safe disposable-clone checks

The recorded regression command is safe but insufficient; it tests controller behavior only:

```bash
PYTHONDONTWRITEBYTECODE=1 PYTHONPATH='dualcoach/profile:.' \
uv run pytest -q \
  tests/test_nutricoach_v150_v14_safety.py \
  tests/test_nutricoach_v150_live_binding.py
```

Also safe, but mock-only, are:

```bash
PYTHONDONTWRITEBYTECODE=1 uv run pytest -q \
  tests/gateway/test_telegram_channel_inbox_adapter.py \
  tests/gateway/test_multi_customer_candidate_authority.py
```

The successor must add and pass the credential-surface authority test described above; neither existing command can authorize deployment.