I read this as a read-only design investigation - I’ll trace the activation checklist, its tests and fixtures, credential/profile safeguards, probe receipts, and candidate packaging, then return exact failing-first cases and a minimal compatible contract without editing or executing tests.


The source package is profile-local rather than tracked in the Hermes tree; I found an immutable candidate-v3 snapshot containing the same runtime, tests, and shipped-wheel tree, so I’ll use that snapshot rather than read the live profile.


I read this as a read-only design investigation - report the smallest fail-closed contract, tests, seam, compatibility rules, and packaging impact.

## Finding

The current gate is not evidence-based:

- `_REQUIRED_CHECKLIST_ITEMS` includes `token_rotated`.
- `_read_activation_checklist()` accepts it only when the value is exactly `True`.
- Activation fixtures therefore assert rotation regardless of whether rotation was necessary.
- The candidate-v3 Telegram probe already establishes most required facts, but its emitted v3 receipt omits:
  - `env_sha256`
  - returned username and bot ID
  - before/after authority digest maps

The clean change is to preserve `token_rotated: true` as the legacy branch and add a strict evidence branch for `token_rotated: false`. Activation transaction, journal, audit, and nutrition behavior should remain untouched.

## Proposed checklist contract

```json
{
  "checklist": {
    "token_rotated": false,
    "credential_hygiene": {
      "schema": "activation-credential-hygiene-evidence-v1",
      "rotation_required": false,
      "reason": "existing_profile_credential_verified",
      "receipt_path": "/absolute/profile/data/activation-evidence/telegram-getme.json",
      "receipt_sha256": "<64 lowercase hex>"
    },
    "missend_test_passed": true,
    "provider_terms_checked": {
      "checked": true,
      "version": "<CONSENT_VERSION>"
    },
    "withdrawal_deletion_doc": true,
    "retention_backup_doc": true,
    "manual_fallback_doc": true
  }
}
```

The accepted union is:

```text
token_rotated == true
OR
token_rotated == false AND credential_hygiene is valid
```

Missing, null, string, object-valued `token_rotated`, or evidence accompanying any value other than exact `false` must fail closed.

## Typed receipt contract

```json
{
  "schema": "activation-telegram-credential-hygiene-v1",
  "status": "READY_TELEGRAM_CREDENTIAL_HYGIENE",
  "profile_root": "/canonical/profile/path",
  "config_sha256": "<64 lowercase hex>",
  "env_sha256": "<64 lowercase hex>",
  "configured_username": "bound_bot",
  "expected_bot_id": 123456,
  "get_me": {
    "method": "Bot.get_me",
    "username": "bound_bot",
    "bot_id": 123456
  },
  "consumes_updates": false,
  "mutates_cursor": false,
  "writes_state": false,
  "authority_before_sha256": {
    "config.yaml": "<sha256>",
    ".env": "<sha256>",
    "customers/registry.json": "<sha256>"
  },
  "authority_after_sha256": {
    "config.yaml": "<sha256>",
    ".env": "<sha256>",
    "customers/registry.json": "<sha256>"
  }
}
```

Use exact-key allowlists at every level; reject extra keys. Require lowercase SHA-256 strings and a positive integer bot ID, excluding booleans.

The receipt should include all authority files selected by the probe. Activation should require at least `config.yaml`, `.env`, and the resolved registry path, rather than accepting empty or caller-selected maps.

## Required validators

The structured branch should validate, in order:

1. **Checklist evidence file**
   - Non-symlink regular file.
   - Single hard link.
   - Owned by the effective UID.
   - No group/other permission bits.

2. **Receipt location and integrity**
   - Canonically contained under the resolved profile root.
   - Non-symlink regular file, one link, effective-UID owned, owner-private.
   - SHA-256 of exact receipt bytes equals `receipt_sha256` before JSON is trusted.

3. **Profile binding**
   - `profile_root` equals the canonical activation profile root.
   - Current owner-private `config.yaml` digest equals `config_sha256`.
   - Current owner-private `.env` digest equals `env_sha256`.
   - Do not parse or load `.env` during activation.

4. **Bot binding**
   - `configured_username == get_me.username`.
   - `expected_bot_id == get_me.bot_id`.
   - Probe generation derives `configured_username` from the bound config and `expected_bot_id` from the token prefix.
   - Config/env digest equality proves those authorities have not changed since probing.

5. **Non-consuming behavior**
   - Method exactly `"Bot.get_me"`.
   - `consumes_updates`, `mutates_cursor`, and `writes_state` are each exactly `false`; integers and null must not be accepted as booleans.

6. **Non-writing behavior**
   - Before and after maps have identical keys and values.
   - Required authority paths are present.
   - Paths are normalized profile-relative paths without `..`, absolute paths, or aliases.
   - Current config/env hashes still match their receipt values.

7. **Token non-exposure**
   - Strict receipt key allowlists: no `token`, `secret`, `authorization`, headers, environment dump, or arbitrary metadata fields.
   - Reject forbidden key names recursively.
   - Reject serialized token-shaped content, for example:
     ```regex
     (?<![A-Za-z0-9_])[1-9][0-9]{4,}:[A-Za-z0-9_-]{10,}(?![A-Za-z0-9_])
     ```
   - Probe tests must additionally assert that the exact injected test token is absent from serialized output and object representations.
   - Activation must never load the token merely to perform this check.

Use descriptor-based reads (`O_NOFOLLOW`, then `fstat`) where available so validation and reading do not have a symlink-swap gap. The probe’s current `secure_file()` checks metadata before a later path read and is therefore weaker than this requirement.

## Minimal production seam

Only modify checklist validation in `checkin_cli/customer_admin.py`:

```python
token_rotated = evidence.get("token_rotated")
if token_rotated is True:
    pass
elif token_rotated is False:
    _validate_credential_hygiene_evidence(
        evidence.get("credential_hygiene"),
        profile_root=profile_root,
        registry_path=registry_path,
        checklist_path=path,
    )
else:
    raise CustomerAdminError(
        "G5 token rotation requires rotation or verified credential hygiene"
    )
```

Add narrowly scoped private helpers:

```python
_read_owner_private_bytes(path, *, label) -> bytes
_sha256_bytes(value) -> str
_require_sha256(value, *, label) -> str
_resolve_profile_evidence_path(profile_root, value) -> Path
_validate_credential_hygiene_evidence(...)
_validate_credential_hygiene_receipt(...)
_reject_credential_material(payload, raw_bytes)
```

Do not change:

- `activate_customer()` or `_activate_customer_locked()`
- activation journal generation or recovery
- audit writing
- nutrition readiness or receipt generation
- provider-terms validation
- the remaining boolean checklist gates

The probe artifact needs a new receipt version because changing the meaning of the existing v3 schema in place would make historical v3 receipts ambiguous.

## Exact failing-first activation tests

Add near `_activation_fixture()`:

```python
def _write_credential_hygiene_receipt(
    profile_root: Path,
    registry_path: Path,
    *,
    overrides: dict[str, object] | None = None,
) -> tuple[Path, str]:
    ...
```

It should create private `config.yaml`, `.env`, registry, and receipt files, with deterministic content and no network use.

Add these tests to `tests/test_customer_admin.py`:

```text
test_activation_accepts_verified_existing_profile_credential_without_rotation
test_activation_preserves_legacy_token_rotated_true
test_activation_rejects_false_token_rotation_without_hygiene_evidence
test_activation_rejects_unknown_credential_hygiene_schema
test_activation_rejects_credential_hygiene_receipt_digest_mismatch
test_activation_rejects_credential_hygiene_receipt_outside_profile
test_activation_rejects_symlinked_credential_hygiene_receipt
test_activation_rejects_non_private_credential_hygiene_receipt
test_activation_rejects_non_private_structured_checklist
test_activation_rejects_receipt_bound_to_another_profile
test_activation_rejects_changed_profile_config_after_probe
test_activation_rejects_changed_profile_env_after_probe
test_activation_rejects_configured_and_getme_username_mismatch
test_activation_rejects_expected_and_getme_bot_id_mismatch
test_activation_rejects_consuming_or_mutating_probe_claim
test_activation_rejects_changed_authority_digest_map
test_activation_rejects_empty_or_incomplete_authority_digest_map
test_activation_rejects_credential_hygiene_receipt_with_extra_fields
test_activation_rejects_receipt_containing_forbidden_credential_key
test_activation_rejects_receipt_containing_token_shaped_content
```

Use parameterization for the three side-effect flags and permission cases. Every rejection test should also assert:

```python
assert json.loads(registry_path.read_text())["customers"][0]["enabled"] is False
assert not (profile_root / "data/customer-activation-journal.json").exists()
```

That proves evidence rejection occurs before activation writes.

## Exact failing-first probe tests

Extend `test_network_probe_v3.py`, preferably against a new v4 module/schema:

```text
test_receipt_binds_profile_config_and_env_digests
test_receipt_records_returned_username_and_bot_id
test_receipt_records_identical_before_and_after_authority_maps
test_receipt_requires_private_regular_profile_config_and_env
test_probe_calls_only_get_me
test_probe_does_not_consume_updates_or_write_profile_state
test_serialized_receipt_and_repr_do_not_expose_token
test_receipt_contract_has_no_unapproved_keys
test_wrong_returned_bot_username_or_id_produces_no_ready_receipt
```

Replace the existing timeout test’s fixed `0.01` timing dependency. Subscribe to an explicit cancellation signal inside the fake `get_me`, invoke the bounded timeout, and assert cancellation plus exact call history. The current test works by elapsed-time behavior and violates deterministic async-test discipline.

## Backward compatibility

Must remain valid:

- Existing checklists containing exact `token_rotated: true`.
- Existing gateway fixture writers using that legacy form; retaining at least one is useful compatibility coverage.
- Structured provider-terms validation and current consent-version behavior.
- Every other exact-true checklist item.
- Activation journal versions 1 and 2.
- Existing journal digest semantics.
- Existing activation audit record shape and checklist path binding.
- Existing committed activation receipt validation.

Do not reinterpret historical `false` or missing values as acceptable.

One limitation remains: the audit stores only the checklist path, not checklist or receipt digests. Therefore this seam verifies evidence at activation time but does not make it independently tamper-evident afterward. Changing that would require a journal/audit contract migration and is outside the minimal compatible seam.

## Packaging impact

`customer_admin.py` is bound in three production representations:

- `snapshot/profile-package/checkin_cli/customer_admin.py`
- `snapshot/package-runtime-closure/checkin_cli/customer_admin.py`
- `snapshot/shipped-wheel-tree/checkin_cli/customer_admin.py`

The production wheel member is also bound to the source snapshot through `wheel_member_source_map`. Tests, wheel bytes, `RECORD`, runtime closure, manifest entries, source digests, quality artifacts, and `candidate_digest` are all sealed by `candidate-manifest.json`.

Implementation therefore requires rebuilding and resealing the candidate rather than patching one snapshot. `verify_candidate.py` itself needs no semantic change unless the new probe receipt is added to candidate inventory; its existing regular-file, mode, inventory, digest, wheel/source, and `RECORD` checks already cover the packaging invariants.

No files were edited, tests were not run, and no live profile, network, or Git operation was accessed.


I read this as a tests-only failing-first implementation - inspect the target test module, patch only it with deterministic fixtures and focused cases, then run the smallest new-test pytest selection and report the expected RED.


The target module matches the candidate-era fixture and has no hygiene contract coverage. I’ll add one deterministic profile-bound receipt fixture plus narrowly parameterized acceptance/rejection tests, leaving the existing activation fixture and tests unchanged.


`apply_patch` is not installed on this workstation (`command not found`), and no file changed. I’ll locate an available patch helper before touching the test file so the requested edit mechanism remains satisfied.


No helper exists under the accessible system, home, or temporary paths, and the target checksum is unchanged. I’ll use a shell-local `apply_patch` wrapper over the system `patch` utility; it creates no helper file and will persist changes only to the requested test module.


I read this as an immediate stop and restoration check - make no edits, confirm the target still matches its exact pre-task hash, and report status.


I read this as an immediate stop and restoration check - no further implementation.

No edits were applied, so no revert was necessary.

- Pre-task and current SHA-256: `0870962e2c42be238e9ddda4a309d7c527e1fd5e2a13f44c3f64858a68a4f72e`
- File size and timestamp remain unchanged.
- No proposed hygiene fixture or tests are present.
- No tests, Git, production, live services, network, or evidence roots were touched.


I read this as checkpoint 1 only - add waiver-contract tests in the single authorized test file, run one focused pytest selection to capture RED, then stop before any production edit.


The production parser confirms the intended RED: it still requires every non-provider checklist item, including `token_rotated`, to be exactly `True`. I’ll now patch only waiver tests, preserving the file’s owner-read-only mode after the edit.


Checkpoint 1 test patch is applied only to `tests/test_customer_admin.py`; directory and file modes are restored to `0500/0400`. I’ll perform static diagnostics, then execute the new waiver-focused pytest selection exactly once.


I read this as checkpoint 1 complete - report RED and stop before production changes.

Patched only `tests/test_customer_admin.py` using `apply_patch`.

Command run exactly once:

```bash
/home/cube/miniconda3/bin/pytest -q tests/test_customer_admin.py -k token_rotation_waiver
```

Result:

- **1 failed**
- **42 passed**
- **85 deselected**
- Expected RED: valid bounded-pilot waiver rejected because production still requires `token_rotated is True`.
- Two non-test warnings: pytest could not write `.pytest_cache` due workspace permissions.

Current test-file SHA-256:

```text
76e9e10435901bb20f9bfe2e44c0cd88a397471d3a74fbb922e12e288a64ed2b
```

File mode restored to `0400`; directories restored to `0500`. No production, service, network, Git, live-profile authority, or evidence-root changes were made. Awaiting checkpoint 2 authorization.


I read this as checkpoint 2 implementation - add a small strict pure policy module, connect only the checklist parser, run the focused waiver tests once to GREEN, then run changed-file static checks and report modes, LOC, and hashes.


The minimum seam is clear: a 95-line policy module can own strict waiver parsing and mandatory binding checks, while `customer_admin.py` only imports it and delegates the `token_rotated` item. No transaction or audit code needs modification.


Production is patched: the new policy module is 80 physical lines, strict/frozen/extra-forbid, and `customer_admin.py` has only the import plus one delegated checklist branch. I’ll now run the exact checkpoint-1 focused selection once.


The focused run exposed one strictness gap: Pydantic’s `Literal[True]` accepts integer `1` despite model strict mode. I’ll patch explicit pre-validation requiring object identity with `True`, then rerun the focused selection because GREEN is the controlling checkpoint requirement.


The corrective patch did apply; the nonzero shell status came from `diff`’s normal “files differ” status under `pipefail`, not from `patch`. The module is now 87 lines with restrictive modes restored. I’ll rerun the focused selection to establish GREEN.


The waiver selection is GREEN: 43 passed and 85 deselected. I’ll now locate an already-installed formatter/type checker without fetching anything; if none exists, I’ll use Python compilation against only the three changed files with bytecode redirected outside the workspace.


An existing cached Ruff binary is available, so no install or network access is needed. I’ll run lint and format-check against exactly the three changed files.


Ruff found no diagnostics in the new module; the lint failures are existing whole-file issues in the already-unformatted 5k-line admin module and test module. Format-check also identifies the new module, so I’ll inspect only its formatting diff and patch that file without broad reformatting.


The type checker likewise reports existing errors across the large test/admin files, while the focused module needs only three line wraps. I’ll apply those formatting-only wraps, then validate the new module independently with Ruff and basedpyright; broad-file findings will be reported as pre-existing rather than changed.


Basedpyright found only local typing issues in the new module: repeated mapping indexing was not narrowed, the validated model result was unused, and `model_config` lacked `ClassVar`. I’ll patch those annotations/local bindings without changing behavior.


I read this as checkpoint 2 complete - report implementation and verification.

Implemented:

- Added strict frozen Pydantic v2 policy module.
- Legacy exact `token_rotated: true` remains accepted.
- Exact `false` requires the exact waiver contract and all four matching top-level bindings.
- Missing, extra, coerced, or incorrect values fail before activation writes.
- No non-exposure inference or proof claim added.

Verification:

- Focused tests: **43 passed, 85 deselected**.
- Initial GREEN run attempt found `Literal[True]` coercing integer `1`; explicit identity validation fixed it, requiring a second focused run.
- New module Ruff lint: passed.
- New module Ruff format: passed.
- New module basedpyright: `0 errors, 0 warnings`.
- All three changed files compile successfully.
- Broader Ruff/basedpyright checks surfaced existing whole-file issues in the large admin/test modules; none were suppressed or broadly refactored.

New module size:

- 97 physical lines
- 79 nonblank/non-comment lines
- Below 250 LOC

Hashes:

```text
40547c2934a3b2fb4f855443d110e2cde8340b14e4e26f23e0641d78520bf763  checkin_cli/customer_admin.py
235a7ecdc881ea0bf216e2c77b8ae7457fc300323d6f4a152ae51e53e814a01e  checkin_cli/activation_token_rotation_policy.py
76e9e10435901bb20f9bfe2e44c0cd88a397471d3a74fbb922e12e288a64ed2b  tests/test_customer_admin.py
```

Restrictive modes restored: files `0400`, directories `0500`. No service, network, live authority, Git, or evidence changes.