I read this as a diagnostics task on a `SOURCE_GOLDEN_PATH_FAIL` where "customer quiescence is unavailable" after an isolated production journey and archive-v2 cleanup. I'll investigate the source_golden_path driver, verifier, CustomerServiceStateStore pause contract, withdraw_customer registry/consent behavior, cleanup, inventory, and native tests - then propose a RED test and smallest fix without editing.

Let me begin with parallel exploration.


The primary repo is `/home/cube/projects/richard/hermes-agent/`. Let me read the verifier and driver fully and hunt for the quiescence predicate and related production contracts.


I have the full verifier and driver. The failing predicate is at `verify_source_golden_path.py:510` — it requires a live `paused: true` row in `data/owner-actions/customer-service-state.json` after cleanup. Now the crux: does `withdraw_customer` clear the pause, and does cleanup prune it? Let me inspect the pause contract, withdraw behavior, and cleanup.


Key finding emerging: the verifier's quiescence predicate (`verify_source_golden_path.py:505-510`) reads `customer-service-state.json` expecting a `customers` list with `customer_key`/`paused` bool rows, but the native `CustomerServiceStateStore` schema is `{"schema":"customer-service-state-v1","states":{key:{"state":"paused",...}},"payload_digest":...}` — a `states` dict, no `paused` bool, no `customers` key. Let me confirm withdraw_customer behavior and whether cleanup prunes that live file


Let me read the full inventory logic and the cleanup archive/prune flow to determine whether `customer-service-state.json` survives live or is archived/pruned.


The schema mismatch is now fully confirmed. Let me verify the native pause contract tests and the verifier's own test harness so I can propose an exact RED test.


I have the full diagnosis confirmed. Let me quickly pinpoint a native pause-contract test to anchor the RED test.


I read this as confirmation-and-finalization of the quiescence diagnosis. I've independently traced the native service-state schema, digest, private-mode, and lifecycle history. Here is the final report.

<analysis>
**Literal Request**: Independently confirm the quiescence diagnosis — native service-state schema, digest, private-mode, and pause/withdrawal/cleanup history — and finalize with exact RED test + smallest fix.

**Actual Need**: Decide truthfully whether the failure is a verifier read-schema bug, a driver miss, or a weak verifier contract, and hand back a precise BLOCK/PASS verdict with actionable file/line evidence and a ready-to-write test.

**Success Looks Like**: A finalized verdict naming the exact wrong predicate, the correct native predicate, proof that the driver performed pause before withdraw and that cleanup keeps the pause live, plus an exact RED test and a minimal verifier-only fix.
</analysis>

## Verdict: BLOCK on verifier contract (verifier-only bug). Driver and native stores are correct.

The failing predicate is at `verify_source_golden_path.py:505-510`, and it reads the **wrong schema**. It searches for `service.get("customers", [])` rows carrying a boolean `paused`, but the native `CustomerServiceStateStore` ledger has **no `customers` key, no top-level `customer_key`, and no `paused` boolean**. Therefore `service.get("customers", [])` always yields `[]`, the `any(...)` is always `False`, and the verifier **always** raises `customer quiescence is unavailable` — regardless of the real pause state. This is the same class of read-schema defect previously found in the bootstrap `customer_key` and check-in-cardinality predicates (prior children st_01a013b7, st_01a013be).

## Exact failing predicate
`scripts/verify_source_golden_path.py:504-510`:
```python
service_path = root / "data/owner-actions/customer-service-state.json"
private(service_path)
service = read_json(service_path, "service state")
if not any(isinstance(row, dict) and row.get("customer_key") == customer and row.get("paused") is True for row in service.get("customers", [])):
    raise ValueError("customer quiescence is unavailable")
```

## Correct native contract (source of truth)

### Schema — `gateway/platforms/nutrition_service_state.py`
- File: `data/owner-actions/customer-service-state.json` (`_SCHEMA = "customer-service-state-v1"`).
- Shape (lines 88-97, `_empty`):
  ```json
  {
    "schema": "customer-service-state-v1",
    "states": { "<customer_key>": {"state": "paused", "revision": 1, "updated_on": "2026-08-18"} },
    "payload_digest": "<sha256-hex>"
  }
  ```
- `set_paused` writes `states[key]["state"] = "paused"|"active"` (lines 70-100). `is_paused` is `row.get("state") == "paused"` (lines 50-55). There is **no** `customers` list and **no** boolean `paused`.

### Digest — `nutrition_service_state.py:38-46, 156-187`
`payload_digest = sha256( json.dumps({"schema": ..., "states": {...}}, ensure_ascii=False, sort_keys=True, separators=(",", ":")) )` computed over exactly the `{"schema","states"}` fields. `_read` rejects any file whose digest does not match.

### Private mode — `nutrition_service_state.py:146-155, 182-189`
The ledger is written via `mkstemp`+`fchmod(0o600)`+atomic `os.replace`; `_read` enforces mode `0o600`, regular non-symlink, `st_nlink == 1`. The verifier's `private(service_path)` at line 505 therefore passes (the non-frozen 0o600 allowance), confirming this is not a file-mode problem — the file exists and is well-formed; only the verifier's field lookup is wrong.

## Lifecycle history: pause survives withdrawal and cleanup (live, not pruned)
- **Pause is committed before withdraw**: `scripts/source_golden_path.py` calls `paused = coordinator.handle_text(address, "코칭 일시중지")`, asserts `paused.reply.accepted`, then calls `withdrawn = coordinator.withdraw_customer(address)`. Pause is durable before withdrawal.
- `handle_text("코칭 일시중지")` → `_set_customer_paused(paused=True)` → `CustomerServiceStateStore.set_paused(...)` writes `{state:"paused", revision:1,...}` (`nutrition_coaching.py:3143-3193, 3529-3530`).
- **Withdrawal never touches service state**: `NutritionCoachingCoordinator.withdraw_customer` (`nutrition_coaching.py:3348-3410`) delegates to `checkin_cli.customer_admin.withdraw_customer`, which flips registry `enabled=False` + `consent.granted=False` only. The pause row stays in `customer-service-state.json`.
- **Cleanup does not prune it**: `archive_customer_cleanup` is invoked with only `SharedLedgerProjection.DRAFT_GENERATIONS` (`source_golden_path.py`). `post_cleanup_authority_inventory` counts only files whose relative path contains the customer key (`customer_cleanup_inventory.py:187-253`); `data/owner-actions/customer-service-state.json` is a shared per-customer-agnostic ledger (path lacks `client_001`), so it is neither counted as `active` nor in the pruned candidate set. The file remains **live with the paused row intact** after cleanup.
- The observed error being the quiescence string (not a file-not-found from `private`) is independent proof the file is present; only the predicate's field lookup fails.

## Decision among the three candidate options
1. **Require pause receipt before withdrawal** — Unnecessary. Pause is already committed (and cryptographically digest-protected) in the service-state ledger before withdraw; tightening the driver adds nothing.
2. **Derive quiescence from withdrawn registry + terminal cleanup inventory** — Rejected. That would conflate operational "paused" with consent/disablement, discarding real pause evidence the driver emits, and it would be redundant: the registry-withdrawal authority is already asserted separately (`verify_native`, line ~474, `terminal registry authority is not withdrawn`).
3. **Driver missed a pause action** — False. The driver performs pause then withdraw exactly as intended; native tests prove the same `handle_text("코칭 일시중지")` commits a paused row (`tests/gateway/test_nutrition_coaching.py:1227-1232` asserts accepted + `coaching_processing_allowed is False`; `:496-499` reads it back via `is_paused is True`).

**Correct terminal quiescence authority** is the native service-state ledger itself: live file `data/owner-actions/customer-service-state.json` with `states[<customer>]["state"] == "paused"`, digest-recomputed.

## Smallest correct fix (verifier-only)
Replace lines 504-510 of `verify_source_golden_path.py` with:
```python
service_path = root / "data/owner-actions/customer-service-state.json"
private(service_path)
service = read_json(service_path, "service state")
states_value = service.get("states")
row = states_value.get(customer) if isinstance(states_value, dict) else None
if (
    service.get("schema") != "customer-service-state-v1"
    or not isinstance(row, dict)
    or row.get("state") != "paused"
    or service.get("payload_digest")
    != hashlib.sha256(canonical(
        {"schema": service.get("schema"), "states": states_value}
    )).hexdigest()
):
    raise ValueError("customer quiescence is unavailable")
```
No driver change, no production change. Add a reusable `verify_customer_quiescence(path, customer)` helper (mirroring `verify_bootstrap_active_lineage`) if you want the test to exercise it directly.

## Exact RED test (add to `tests/gateway/test_source_golden_path_verifier.py`)
Import the native store (`gateway.platforms.nutrition_service_state.CustomerServiceStateStore`), exercise the real contract, and assert the verifier currently fails the native shape:
```python
def test_customer_quiescence_reads_native_service_state(tmp_path: Path) -> None:
    from gateway.platforms.nutrition_service_state import CustomerServiceStateStore
    store = CustomerServiceStateStore(
        tmp_path / "data/owner-actions/customer-service-state.json"
    )
    store.ensure()
    assert store.set_paused("client_001", paused=True, updated_on="2026-08-18") is True
    assert verifier.verify_customer_quiescence(
        store.path, "client_001"
    ) is True  # fails today: got empty-customers predicate
```
Today this raises `customer quiescence is unavailable` (RED). Companion parametrized rejects: wrong customer, `state=="active"`, missing file, tampered `payload_digest` — all reading the native shape, not a placeholdered format.

## Privacy / digest / schema / stale / duplicate notes
- **Schema/digest**: The store already protects integrity via `payload_digest` over `{schema, states}`. The fix must recompute it (as the verifier already does for the readiness `baseline/readiness/receipt` digests) so a tampered quiescence row cannot pass. `canonical()` in the verifier uses `sort_keys`/`ensure_ascii=False` by default, matching the store's `ensure_ascii=False` — use that form, not the ASCII variant.
- **Private**: `private(service_path)` non-frozen 0o600 gate already matches the store's write mode; keep it.
- **Stale**: no freshness required — quiescence is a terminal proof; the predicate is `state == "paused"` only. Do not pin `revision`/`updated_on` (they are audit metadata, not authority).
- **Duplicate**: the `states` dict is keyed by the exact customer key, so `states.get(customer)` returns exactly one row — no multi-row ambiguity (unlike the JSONL/regex cases). Single customer binds cleanly.

## PASS/BLOCK
**BLOCK** on the current verifier contract: `verify_source_golden_path.py:504-510` cannot pass by design. The production driver + native `CustomerServiceStateStore` + cleanup are correct; pause is committed before withdrawal and stays live after cleanup. Apply the verifier-only fix above (no driver, no production change), add the RED test, and the golden path will advance past `QUIESCED` instead of failing.

<results>
<files>
- /home/cube/projects/richard/hermes-agent/scripts/verify_source_golden_path.py - The failing predicate at lines 504-510 reads the wrong schema; fix here (verifier-only). The 0o600 private-mode check at line 505 is already correct; only the field lookup is wrong.
- /home/cube/projects/richard/hermes-agent/gateway/platforms/nutrition_service_state.py - Source of truth for the native quiescence contract: schema "customer-service-state-v1", states dict keyed by customer_key with {state:"paused", revision, updated_on}, payload_digest sha256 over {schema,states}, file mode 0o600 (lines 38-46, 88-100, 146-187).
- /home/cube/projects/richard/hermes-agent/gateway/platforms/nutrition_coaching.py - Pause/withdraw flow: handle_text("코칭 일시중지") -> _set_customer_paused -> set_paused (3143-3193, 3529-3530); withdraw_customer (3348-3410) only flips registry, does not clear pause; store bound at data/owner-actions/customer-service-state.json (2199-2202).
- /home/cube/projects/richard/hermes-agent/scripts/source_golden_path.py - Driver proves pause committed before withdraw (paused=handle_text(...일시중지), then withdrawn=withdraw_customer), and cleanup runs with only DRAFT_GENERATIONS shared projection so the pause ledger stays live.
- /home/cube/.cache/task26-strict-successor-1786976146/src-p/checkin_cli/customer_cleanup_inventory.py - post_cleanup_authority_inventory only counts files whose path embeds the customer key (187-253); data/owner-actions/customer-service-state.json is not in the pruned candidate set, confirming the pause ledger remains live after cleanup.
- /home/cube/projects/richard/hermes-agent/tests/gateway/test_source_golden_path_verifier.py - Where the RED test belongs; existing pattern (bootstrap lineage / canonical journey events) shows the native-contract accept+reject style to copy.
- /home/cube/projects/richard/hermes-agent/tests/gateway/test_nutrition_coaching.py - Native pause-contract evidence: handle_text("코칭 일시중지") commits paused (1227-1232), read-back via is_paused True (496-499); paused rows are state=="paused", not a customers list.
</files>

<answer>
Verdict: BLOCK on the verifier contract — verifier-only bug, driver and native stores are correct.

The exact failing predicate is `verify_source_golden_path.py:504-510`. It checks `service.get("customers", [])` rows with a boolean `paused`, but the native `CustomerServiceStateStore` ledger (`gateway/platforms/nutrition_service_state.py`) is `{"schema":"customer-service-state-v1","states":{customer_key:{"state":"paused","revision":1,"updated_on":ISO}},"payload_digest":sha256}`. There is never a `customers` key, so the lookup returns `[]`, the `any(...)` is always False, and `customer quiescence is unavailable` is raised unconditionally. The file exists and is 0o600-valid (the observed error is the predicate string, not a private-mode/file-not-found error) — only the field lookup is wrong.

Lifecycle fact check: the driver commits pause (`handle_text(address, "코칭 일시중지")` -> `set_paused(paused=True)`) BEFORE `withdraw_customer`; withdrawal flips registry enabled/consent only and never touches service state; cleanup (DRAFT_GENERATIONS single shared projection) does not prune the shared per-customer-agnostic ledger, so the paused row remains live. Therefore option 3 (driver miss) is false and option 1 (require pause receipt before withdraw) is unnecessary; option 2 (re-derive from registry) is redundant with the already-checked "terminal registry isn't withdrawn" and would discard real pause evidence. The correct terminal quiescence authority is the live native service-state row `states[<customer>]["state"] == "paused"` with digest recomputed.

Smallest fix: edit only `verify_source_golden_path.py:504-510` to read `states.get(customer)` and require `state == "paused"` plus recompute `payload_digest` over `{"schema","states"}`. No driver or production change. RED test in `tests/gateway/test_source_golden_path_verifier.py` that drives a real `CustomerServiceStateStore.set_paused(...)` ledger and calls the verifier predicate — fails today, passes after the fix. Privacy/digest/schema/private are satisfied by recomputing the store's own digest and keeping the existing 0o600 private gate; no freshness pin; single row per customer (no duplicate ambiguity).
</answer>

<next_steps>
1. Apply the verifier-only fix at `verify_source_golden_path.py:504-510` (schema "customer-service-state-v1", `states.get(customer)["state"]=="paused"`, recompute `payload_digest`). Optionally extract `verify_customer_quiescence(path, customer)`.
2. Add the RED test in `tests/gateway/test_source_golden_path_verifier.py` using a real `CustomerServiceStateStore.set_paused(...)` native ledger (accept paused; reject wrong-customer, active, missing, tampered digest).
3. Re-run `source_golden_path.py` + `verify_source_golden_path.py`; the journey should advance past QUIESCED. No driver, cleanup, or production edits needed.
</next_steps>
</results>