I read this as a read-only forensic investigation of H4 and the failed `dualcoachtest` readiness probe - I’ll trace the receipt/checker contract, inventory and hash all resulting artifacts, compare state snapshots, and report the minimal safe receipt-write allowance without exposing secrets.


I read this as a read-only H4 forensic report - I traced the checker, reconstructed its snapshot scope, inventoried the probe’s writes, and defined a minimal candidate-bound receipt contract.

## Finding

**H4 is refuted.** `result=profile_mutated` was not caused by the receipt directory being inside the snapshot.

The failed command omitted profile binding:

```text
PYTHONDONTWRITEBYTECODE=1 .../dualcoach_admin provider-auth check \
  --json \
  --receipt-dir .../profiles/dualcoachtest/data/dualcoach-provider-auth \
  --allow-billable-active-probe
```

`--receipt-dir` controls only receipt output; it does not select the Hermes profile. Consequently, `get_hermes_home()` resolved the default `/home/cube/.hermes` root.

The snapshot algorithm hashes exactly seven declared paths under that root, including `auth.json`, but excluding the receipt directory. It requires existing files to be regular, non-symlinked, mode `0600`, and hashes canonical JSON records containing path, byte count, and SHA-256.

The probe’s hashes were:

- Pre: `a52417e5c6f2ecac51ba1eb9207d7849c58a0026eced14da826230d4b1204d14`
- Post: `8e89b79a57feac69f0e90f8c8e40e9d77f1077331d66e231b6130e64cec6dd98`

Recomputing the algorithm over `/home/cube/.hermes` currently produces the exact post hash. The actual `dualcoachtest` snapshot currently produces a different hash, `7d163cb0...`, confirming it was not the snapshotted root.

## Exact probe filesystem effects

The command interval contains exactly these three file effects:

| Operation | Path | Mode | Size | Current SHA-256 |
|---|---|---:|---:|---|
| Changed | `/home/cube/.hermes/auth.json` | `0600` | 4610 | `7051b1c2a2cf278d12002cf73308013a15e8e7efcb8cb73862ab794f8bbfd90d` |
| Created | `/home/cube/.hermes/profiles/dualcoachtest/data/dualcoach-provider-auth/receipts/20260815T123448Z-63e65f3106c342be975ca1c4ea67a2f1.json` | `0600` | 1329 | `5b71f2b5b93b789e446e2b249ecac2adfed7b9428b54b585eb707dfdd19dec18` |
| Replaced | `/home/cube/.hermes/profiles/dualcoachtest/data/dualcoach-provider-auth/index.json` | `0600` | 297 | `0449bcbc720ba134a41ca9584ef0f5a2fc6b84766e4b24acdb0e09db802cddd0` |

The reconstructed pre-probe index was also 297 bytes, expected mode `0600`, SHA-256:

```text
b65af48f39be0e0179e8bf5e606053cdd8b08032862f9655ed0c433ab561c40b
```

The global auth mutation was credential-pool bookkeeping: the Codex pool entry ended marked `exhausted` with a command-time status timestamp, after selection attempted to refresh an expiring credential and found no usable runtime token. No credential values were inspected or disclosed.

Therefore, the stronger claim that “no other runtime state changed” is false: **global auth runtime state changed**. The checker correctly detected that protected mutation, although `profile_mutated` masks the underlying `credential_missing` result.

## No model, service, Telegram, or dualcoach operational effect

Verified evidence:

- `request_attempts: null`
- `billable: false`
- `prompt_length`, `request_contract`, `usage`, and response fields: null
- No model generation path was reached.
- A credential refresh may have been attempted; that is not a model request.
- Receipt effects report:
  - delivery actions: `0`
  - registry mutations: `0`
  - service actions: `0`
  - Telegram actions: `0`
- `hermes-gateway-dualcoachtest.service` remains `inactive/dead`, `MainPID=0`; it exited hours before this probe.
- No dualcoach registry, gateway state, outbox ledger, service unit, Telegram ledger, database, workspace, or runtime file changed during the probe interval.
- The only non-receipt mutation found was `/home/cube/.hermes/auth.json`.

The required candidate wheel contains the same snapshot implementation byte-for-byte as the invoked module, but the invoked runtime reported candidate digest `f9a461...`, not required candidate `2e0894ea...`; therefore this receipt is not candidate-bound readiness evidence.

## Minimal safe candidate-bound contract

1. **Bind execution**
   - Verify wheel SHA-256 `af4a9d0...`.
   - Verify candidate digest `2e0894ea...`.
   - Require explicit canonical profile root rather than inferring it from `--receipt-dir`.

2. **Declare exact allowed writes before probing**
   - One newly generated `receipts/<timestamp>-<nonce>.json`.
   - One atomic replacement of `index.json`.
   - Temporary files only as same-directory atomic-write intermediates, with no residue.
   - Directory mtime changes are metadata consequences, not additional authority.

3. **Snapshot all protected state**
   - Selected profile surfaces.
   - Global/shared auth store.
   - Service state and unit identity.
   - Runtime databases, registries, delivery/outbox and Telegram ledgers.
   - Capture before resolution and after client close, before receipt publication.

4. **Fail on any undeclared mutation**
   - Credential refresh or pool-status writes remain forbidden under this strict probe contract.
   - Preserve the underlying result separately, e.g. `probe_result=credential_missing`, while setting `contract_result=protected_state_mutated`.
   - Never retry after a model or credential-network attempt.

5. **Validate publication separately**
   - After writing, compare the final filesystem delta to the two declared outputs.
   - Record operation, path, mode, size, pre/post hashes, payload digest, candidate digest, and protected-state pre/post hashes.
   - Require both output files to be regular, non-symlinked `0600` files.

No files were modified or deleted during this investigation.