{"type":"transition_applied","payload":{"type":"transition_applied","status":"running","residency_state":"resident"}}
{"type":"assistant_message","payload":{"text":"I read this as a read-only root-cause audit of the Task27 cleanup false-green - I’ll trace the cleanup/inventory code, controller receipts, tests, and on-disk residue, then return a decision-complete supplemental fix and regression plan without modifying or invoking services."}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"read","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"assistant_message","payload":{"text":"The false-green is now localized to the boundary between the wheel’s key-based authority inventory and Task27’s session-specific bootstrap cleanup. I’m tracing every persisted authority root in the delivered wheels and comparing that path model with the live profile, including immutable archive exclusions."}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":true}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"assistant_message","payload":{"text":"I read this as a read-only root-cause audit of Task27’s F2 false-green - I traced the delivered cleanup contract, controller, receipts, tests, and live profile and produced a supplemental repair plan.\n\n## Verdict\n\n**F2 is correctly blocking.** Task27 declared terminal cleanup while 26 records for the cleaned onboarding session remained in the live publication outbox.\n\nThe false-green is caused by a binding mismatch:\n\n- The cleanup API inventories by `customer_key`.\n- The outbox binds records by `session_id` and route.\n- The outbox path contains neither identifier.\n- Task27 independently removed the bootstrap session but never connected that session to the outbox inventory.\n\n## Exact root cause\n\nDelivered wheel:\n\n`artifacts/physique_checkin_cli-0.1.0-py3-none-any.whl`\n\n- `checkin_cli/customer_cleanup.py:108-113`: `archive_customer_cleanup()` accepts candidate paths and one narrowly supported shared projection, but Task27 invokes it with neither.\n- `customer_cleanup_inventory.py:162`: inventory is keyed only by `customer_key`.\n- `customer_cleanup_inventory.py:215-220`: generic shared files are counted only when the **filename/path** contains the customer key.\n- `customer_cleanup.py:204-216`: that incomplete inventory gates pruning and commitment.\n- `customer_cleanup_fs.py:66-81`: only customer-directory files and explicitly supplied candidate paths enter the archive.\n- The only typed shared projection is `data/owner-actions/draft-generations.json`; publication outbox ledgers are unsupported.\n\nTask27:\n\n- `task27_final_cleanup_controller.py:168-177`: `mutation_paths()` omits publication outbox and staff-membership state.\n- `:245-260`: baseline records only registry, bootstrap, customer, cleanup, and Task27 archive.\n- `:345-351`: drift checking ignores the permission payload’s full-profile digest and rechecks only those five roots.\n- `:378-394`: rollback likewise excludes outbox and membership roots.\n- `:539`: calls `archive_customer_cleanup()` without `candidate_paths` or shared projections.\n- `:543`: archives/removes only `telegram-customer-bootstrap-v1`.\n- `:544-555`: accepts the wheel’s incomplete inventory as terminal.\n- `:321-330`: authorized mutations never mention publication outbox or membership state.\n\nTherefore `authority.categories[\"outbox\"] == 0` means only “no customer-key-named outbox path was found,” not “no session-bound outbox rows exist.”\n\n## Observed live data\n\nActive operational root:\n\n`data/onboarding/telegram-publication-outbox-v1/ledger.json`\n\n- SHA-256: `0d79c3e461a0401265d3033b364eb8ec4323b5614386693231d003c7b312a947`\n- Total records: 26\n- Session matches: 26\n- Session: `cb_mYUoMIsk_CRzSDKYpPE9dg`\n- Route: customer `8527916639`, topic `0`\n- Role: all `customer`\n- State: all `COMMITTED`\n- Generations: `0-21, 23-26`\n- Message IDs: `304-354`, even values\n\nThese are terminal exactly-once receipts rather than pending sends, but they remain in a mutable operational ledger. Runtime code still consults that ledger:\n\n- `telegram_nutrition_onboarding_publication_outbox.py:118-145`: resolves current publication authority.\n- `:216-250`: committed records participate in idempotent commit handling.\n- `:261-263`: exposes all records.\n- `telegram_nutrition_onboarding_runtime.py:295-343`: uses session-bound outbox records during recovery/reconciliation.\n\nThus they do not independently authorize a new send, but they are live session-bound workflow state and contradict the claimed absence of customer workflow residue.\n\n## Other missed target-bound active root\n\n`data/onboarding/telegram-staff-membership-v1/events.jsonl`\n\n- SHA-256: `a225b7bec2580caf90a288f2707735032c5ebc16b254bac27947aba9f6389f4b`\n- Contains a current `subscription_armed` row with `customer_user_ids:[\"8527916639\"]`.\n- `telegram.py:2386-2413` creates this current customer subscription inventory.\n- `dualcoach_admin.py:954-979` uses the latest armed row for pre-activation evidence.\n- `dualcoach_admin.py:1141-1151` treats its epoch as subscription continuity authority.\n\nThe wheel misses it for the same reason: neither its path nor row contains the customer key. It is user-bound rather than key-bound.\n\nCompanion outbox ledgers must also be covered even though currently empty:\n\n- `emergency.json`\n- `owner-callbacks.json`\n\nFuture or fixture states can contain session-bound rows; `owner-callbacks.json` can additionally contain `customer_key`.\n\n## Not active target authority\n\n- `customers/registry.json` retains the disabled, nonconsenting identity and route. That is intentional terminal registry state, not active authority.\n- Logs contain the user ID but are observational logs, not executable workflow state.\n- `legacy-activation-authority.json` is a genuine orphan for `gate_d_delivery`, but it is unrelated to this target customer/session. It is a separate profile-hygiene finding, not the cause of this false-green.\n- The canonical Task27 operation archives are correctly frozen:\n  - customer cleanup operation root: mode `0500`\n  - Task27 bootstrap operation root: mode `0500`\n- Matches beneath those immutable operation roots are historical evidence and must not count as active residue.\n- Older `rehearsal-reset-archives` are semantically archival but remain mode `0700`; they are not runtime authorities, but they also should not be described as permission-immutable. F2’s trainer-path scan conflated these archives with live authority.\n\n## Evidence defect\n\n`receipt-hashes.sha256` is stale:\n\n- Expected controller: `d57c8564...`\n- Actual controller: `8aee38ea920cbaa2b18d7ebe0104aa1baeabcaeb49152f25819ee1380ebbc443`\n- Cleanup receipt binds the actual hash.\n- `sha256sum -c` fails for the controller.\n- The nonmutation attestation still binds the old controller.\n\nThe existing COMMITTED receipt should remain immutable historical evidence of the faulty decision and be superseded, not rewritten.\n\n## Smallest supplemental repair\n\nUse a **Task27-specific supplemental controller**, not a delivered-wheel rebuild:\n\n1. Require a fresh authorization receipt. Existing authorization does not permit outbox or membership mutations.\n2. Bind exact hashes and schemas for:\n   - publication `ledger.json`, `emergency.json`, `owner-callbacks.json`\n   - staff-membership `events.jsonl`\n3. Assert this one-use live-state shape:\n   - every nonempty publication/callback row belongs to the expected session/customer;\n   - membership journal’s target-bearing epoch contains no foreign customer IDs.\n   - Refuse mixed/foreign state rather than implementing a general shared-ledger projection.\n4. Byte-archive both complete operational directories beneath a new immutable Task27 supplemental operation.\n5. Verify source/archive inventories exactly.\n6. Remove the two active directories while the service remains inactive.\n7. Add both roots to baseline drift checks, rollback backup/restore, mutation disclosure, and fault injection.\n8. Perform a content-aware terminal scan over operational roots using customer key, session ID, and user/route ID, explicitly excluding:\n   - frozen customer-cleanup archives;\n   - frozen Task27 archives;\n   - known observational logs.\n9. Emit a superseding receipt that records:\n   - 26 archived/pruned outbox rows;\n   - membership epoch archived/pruned;\n   - zero active target matches;\n   - preserved immutable archive hashes;\n   - old receipt identifier and reason for supersession.\n10. Generate a new complete evidence hash manifest instead of altering the stale historical one.\n\nThis exact-state whole-directory archive is smaller and safer than changing the general cleanup wheel or rewriting HMAC/hash-chained shared ledgers.\n\n## RED -> GREEN regression suite\n\n1. **Session-only outbox detection**\n   - Fixture path lacks customer key but contains the target session.\n   - RED: current inventory/controller reports outbox zero.\n   - GREEN: supplemental preflight reports 26 target rows and refuses terminal status.\n\n2. **Successful supplemental cleanup**\n   - Archive exact outbox and membership trees, prune originals, verify zero active target matches and byte-recoverable frozen archives.\n\n3. **Companion ledger coverage**\n   - Target records independently in primary, emergency, and owner-callback ledgers must all be detected.\n\n4. **Foreign-row refusal**\n   - Add another session/customer record.\n   - Controller must refuse before mutation; no lossy whole-directory deletion.\n\n5. **Membership mixed-customer refusal**\n   - `customer_user_ids` contains target plus another customer.\n   - Refuse rather than deleting shared authority.\n\n6. **Archive exclusion**\n   - Target identifiers only under frozen cleanup/Task27 archives.\n   - Active count remains zero while archive verification remains required.\n\n7. **Rollback**\n   - Inject faults after archive copy and after source pruning.\n   - Exact operational state must be restored without sleeps or polling.\n\n8. **Drift sealing**\n   - Modify outbox after preflight.\n   - Execution must refuse before mutation.\n\n9. **Evidence consistency**\n   - Verify every current receipt/controller hash and assert the supersession chain; the stale historical manifest remains labeled historical rather than silently replaced.\n\nNo source, evidence, profile, service, Telegram, or provider action was performed."}}
{"type":"transition_applied","payload":{"type":"transition_applied","status":"completed","residency_state":"resident"}}
{"type":"revived","payload":{"run_epoch":1}}
{"type":"assistant_message","payload":{"text":"I read this as an implementation of the audited supplemental cleanup lane - I’ll first add disposable-only contract tests and capture their failure, then build the controller, verify rollback and adversarial cases, and emit only new private supplemental evidence while proving the live profile stayed byte-identical."}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"lsp_diagnostics","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"assistant_message","payload":{"text":"I’ve fixed the supplemental boundary to the two exact operational directories, with whole-directory refusal on any foreign or ambiguous state. I’m adding the tests first so the RED result demonstrates the missing controller rather than retrofitting assertions after implementation."}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":true}}
{"type":"assistant_message","payload":{"text":"The workstation has no installed `apply_patch` command, so the initial creation attempt made no file and the RED run failed at collection. I’m continuing with a shell `apply_patch` wrapper over the system `patch` utility; all requested file changes will still flow exclusively through explicit patches."}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":true}}
{"type":"steered","payload":{"delivered":"steer"}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":true}}
{"type":"assistant_message","payload":{"text":"I read this as a stop-work handoff - below is a decision-complete patch for official lead application; I performed no further tool calls or mutations.\n\nThe prior RED attempt created the focused test file before this stop instruction. The patch below replaces that file and adds the controller.\n\n```diff\n*** Begin Patch\n*** Add File: .omo/evidence/task27/task27_supplemental_cleanup_controller.py\n+#!/usr/bin/env python3\n+\"\"\"Disposable-only Task27 cleanup for session-bound outbox/membership residue.\"\"\"\n+from __future__ import annotations\n+\n+import argparse\n+import contextlib\n+import hashlib\n+import json\n+import os\n+import shutil\n+import stat\n+import subprocess\n+import sys\n+import tempfile\n+import traceback\n+import uuid\n+from pathlib import Path\n+from typing import Any\n+\n+SCHEMA = \"task27-supplemental-cleanup-controller-v1\"\n+CUSTOMER = \"task26_claim_20260818145508_1b96b23d\"\n+SESSION = \"cb_mYUoMIsk_CRzSDKYpPE9dg\"\n+USER_ID = \"8527916639\"\n+ROUTE = (USER_ID, \"0\")\n+LIVE = Path(\"/home/cube/.hermes/profiles/dualcoachtest\")\n+EVIDENCE = Path(__file__).resolve().parent\n+CLEANUP_RECEIPT = EVIDENCE / \"task27-live-cleanup-receipt.json\"\n+CLEANUP_RECEIPT_SHA256 = (\n+    \"7848def41100aa4f4ef27239dfaae3cc634d5ad2b24df7966d4d80838f692a3a\"\n+)\n+HERMES_WHEEL = (\n+    EVIDENCE.parent\n+    / \"task26/task26-combined-v38-delivered-st_01a019d7/artifacts\"\n+    / \"hermes_agent-0.17.0-py3-none-any.whl\"\n+)\n+MARKER = \".task27-supplemental-disposable-copy\"\n+MARKER_VALUE = \"TASK27_SUPPLEMENTAL_DISPOSABLE_COPY\"\n+OUTBOX_NAMES = {\n+    \".lock\",\n+    \".emergency.lock\",\n+    \".owner-callbacks.lock\",\n+    \".receipt-key\",\n+    \"ledger.json\",\n+    \"emergency.json\",\n+    \"owner-callbacks.json\",\n+}\n+MEMBERSHIP_NAMES = {\"events.jsonl\", \"events.jsonl.lock\"}\n+ARCHIVE_ROOT_NAMES = {\n+    \"customer-cleanup\",\n+    \"task27-final-cleanup\",\n+    \"task27-supplemental-cleanup\",\n+    \"post-lifecycle-cleanup-archives\",\n+    \"profile-reset-archives\",\n+    \"rehearsal-reset-archives\",\n+    \"recovery-audits\",\n+}\n+\n+\n+class Refusal(RuntimeError):\n+    pass\n+\n+\n+def canonical(value: object) -> bytes:\n+    return json.dumps(\n+        value, ensure_ascii=True, sort_keys=True, separators=(\",\", \":\")\n+    ).encode()\n+\n+\n+def sha_bytes(value: bytes) -> str:\n+    return hashlib.sha256(value).hexdigest()\n+\n+\n+def sha_file(path: Path) -> str:\n+    digest = hashlib.sha256()\n+    with path.open(\"rb\") as handle:\n+        while block := handle.read(1024 * 1024):\n+            digest.update(block)\n+    return digest.hexdigest()\n+\n+\n+def atomic_json(path: Path, value: object, mode: int = 0o600) -> None:\n+    if path.exists() or path.is_symlink():\n+        raise Refusal(f\"output already exists: {path}\")\n+    path.parent.mkdir(parents=True, exist_ok=True, mode=0o700)\n+    descriptor, temporary = tempfile.mkstemp(prefix=f\".{path.name}.\", dir=path.parent)\n+    try:\n+        os.fchmod(descriptor, mode)\n+        with os.fdopen(descriptor, \"wb\", closefd=True) as handle:\n+            descriptor = -1\n+            handle.write(canonical(value) + b\"\\n\")\n+            handle.flush()\n+            os.fsync(handle.fileno())\n+        os.replace(temporary, path)\n+        path.chmod(mode)\n+    finally:\n+        if descriptor >= 0:\n+            os.close(descriptor)\n+        with contextlib.suppress(FileNotFoundError):\n+            os.unlink(temporary)\n+\n+\n+def _require_private(path: Path, *, directory: bool) -> os.stat_result:\n+    try:\n+        info = path.lstat()\n+    except OSError as exc:\n+        raise Refusal(f\"required cleanup state is unavailable: {path}\") from exc\n+    expected_kind = stat.S_ISDIR if directory else stat.S_ISREG\n+    expected_mode = 0o700 if directory else 0o600\n+    if (\n+        stat.S_ISLNK(info.st_mode)\n+        or not expected_kind(info.st_mode)\n+        or info.st_uid != os.geteuid()\n+        or stat.S_IMODE(info.st_mode) != expected_mode\n+        or (not directory and info.st_nlink != 1)\n+    ):\n+        raise Refusal(f\"cleanup state is unsafe: {path}\")\n+    return info\n+\n+\n+def tree_inventory(root: Path) -> dict[str, Any]:\n+    if not root.exists() and not root.is_symlink():\n+        return {\n+            \"root\": str(root),\n+            \"exists\": False,\n+            \"entry_count\": 0,\n+            \"digest\": sha_bytes(b\"[]\"),\n+        }\n+    _require_private(root, directory=True)\n+    rows: list[dict[str, Any]] = []\n+    for path in [root, *sorted(root.rglob(\"*\"))]:\n+        relative = \".\" if path == root else path.relative_to(root).as_posix()\n+        info = path.lstat()\n+        if stat.S_ISLNK(info.st_mode):\n+            raise Refusal(f\"cleanup state contains a symlink: {path}\")\n+        if stat.S_ISDIR(info.st_mode):\n+            if info.st_uid != os.geteuid() or stat.S_IMODE(info.st_mode) != 0o700:\n+                raise Refusal(f\"cleanup directory is not owner-only: {path}\")\n+            rows.append({\"path\": relative, \"type\": \"dir\", \"mode\": 0o700})\n+        elif stat.S_ISREG(info.st_mode):\n+            if (\n+                info.st_uid != os.geteuid()\n+                or stat.S_IMODE(info.st_mode) != 0o600\n+                or info.st_nlink != 1\n+            ):\n+                raise Refusal(f\"cleanup file is unsafe: {path}\")\n+            rows.append(\n+                {\n+                    \"path\": relative,\n+                    \"type\": \"file\",\n+                    \"mode\": 0o600,\n+                    \"size\": info.st_size,\n+                    \"sha256\": sha_file(path),\n+                }\n+            )\n+        else:\n+            raise Refusal(f\"cleanup state has an unsupported entry: {path}\")\n+    return {\n+        \"root\": str(root),\n+        \"exists\": True,\n+        \"entry_count\": len(rows),\n+        \"digest\": sha_bytes(canonical(rows)),\n+        \"entries\": rows,\n+    }\n+\n+\n+def compact(snapshot: dict[str, Any]) -> dict[str, Any]:\n+    return {\n+        key: snapshot[key]\n+        for key in (\"root\", \"exists\", \"entry_count\", \"digest\")\n+    }\n+\n+\n+def mutation_paths(root: Path) -> dict[str, Path]:\n+    return {\n+        \"outbox\": root / \"data/onboarding/telegram-publication-outbox-v1\",\n+        \"membership\": root / \"data/onboarding/telegram-staff-membership-v1\",\n+        \"supplemental_archive\": root / \"data/task27-supplemental-cleanup\",\n+    }\n+\n+\n+def proposed_mutations() -> list[dict[str, str]]:\n+    return [\n+        {\n+            \"path\": \"data/onboarding/telegram-publication-outbox-v1/**\",\n+            \"action\": (\n+                \"archive the exact target-only operational directory, verify it, \"\n+                \"then prune it\"\n+            ),\n+        },\n+        {\n+            \"path\": \"data/onboarding/telegram-staff-membership-v1/**\",\n+            \"action\": (\n+                \"archive the exact target-only subscription directory, verify it, \"\n+                \"then prune it\"\n+            ),\n+        },\n+        {\n+            \"path\": \"data/task27-supplemental-cleanup/archives/<operation-id>/**\",\n+            \"action\": \"create and freeze a byte-recoverable supplemental archive\",\n+        },\n+    ]\n+\n+\n+def cleanup_receipt_binding() -> dict[str, object]:\n+    _require_private(CLEANUP_RECEIPT, directory=False)\n+    actual_hash = sha_file(CLEANUP_RECEIPT)\n+    if actual_hash != CLEANUP_RECEIPT_SHA256:\n+        raise Refusal(\"current Task27 cleanup receipt hash mismatch\")\n+    try:\n+        receipt = json.loads(CLEANUP_RECEIPT.read_bytes())\n+    except (OSError, json.JSONDecodeError) as exc:\n+        raise Refusal(\"current Task27 cleanup receipt is invalid\") from exc\n+    if (\n+        receipt.get(\"status\") != \"COMMITTED\"\n+        or receipt.get(\"operation_id\") != \"e90fcc341e9f49c2a2c57ce46dd50c12\"\n+        or receipt.get(\"before\", {}).get(\"expected\", {}).get(\"customer_key\") != CUSTOMER\n+        or receipt.get(\"before\", {}).get(\"expected\", {}).get(\"session_id\") != SESSION\n+        or receipt.get(\"before\", {}).get(\"expected\", {}).get(\"user_id\") != USER_ID\n+    ):\n+        raise Refusal(\"current Task27 cleanup receipt binding mismatch\")\n+    return {\n+        \"path\": str(CLEANUP_RECEIPT),\n+        \"sha256\": actual_hash,\n+        \"operation_id\": receipt[\"operation_id\"],\n+    }\n+\n+\n+def service_state(override: Path | None = None) -> dict[str, object]:\n+    if override is not None:\n+        value = json.loads(override.read_bytes())\n+        return {\n+            \"active_state\": value[\"active_state\"],\n+            \"sub_state\": value[\"sub_state\"],\n+            \"main_pid\": int(value[\"main_pid\"]),\n+            \"matching_processes\": int(value[\"matching_processes\"]),\n+            \"source\": \"test_override\",\n+        }\n+    result = subprocess.run(\n+        [\n+            \"systemctl\",\n+            \"--user\",\n+            \"show\",\n+            \"hermes-agent@dualcoachtest.service\",\n+            \"-p\",\n+            \"ActiveState\",\n+            \"-p\",\n+            \"SubState\",\n+            \"-p\",\n+            \"MainPID\",\n+        ],\n+        check=False,\n+        capture_output=True,\n+        text=True,\n+    )\n+    if result.returncode:\n+        raise Refusal(f\"service state unavailable: {result.stderr.strip()}\")\n+    fields = dict(\n+        line.split(\"=\", 1) for line in result.stdout.splitlines() if \"=\" in line\n+    )\n+    matches = 0\n+    for entry in Path(\"/proc\").iterdir():\n+        if not entry.name.isdigit():\n+            continue\n+        try:\n+            command = (entry / \"cmdline\").read_bytes().replace(b\"\\0\", b\" \")\n+        except OSError:\n+            continue\n+        if b\"dualcoachtest\" in command or b\"hermes-agent\" in command:\n+            matches += 1\n+    return {\n+        \"active_state\": fields.get(\"ActiveState\"),\n+        \"sub_state\": fields.get(\"SubState\"),\n+        \"main_pid\": int(fields.get(\"MainPID\", \"-1\")),\n+        \"matching_processes\": matches,\n+        \"source\": \"systemd_and_proc\",\n+    }\n+\n+\n+def require_inactive(value: dict[str, object]) -> None:\n+    if (\n+        value[\"active_state\"],\n+        value[\"sub_state\"],\n+        value[\"main_pid\"],\n+        value[\"matching_processes\"],\n+    ) != (\"inactive\", \"dead\", 0, 0):\n+        raise Refusal(\"service must be inactive/dead with no matching process\")\n+\n+\n+def _strict_names(root: Path, expected: set[str]) -> None:\n+    actual = {path.name for path in root.iterdir()}\n+    if actual != expected:\n+        raise Refusal(\n+            f\"cleanup directory has unknown or missing entries: {root}: \"\n+            f\"{sorted(actual ^ expected)}\"\n+        )\n+\n+\n+def _import_runtime_types() -> tuple[type[Any], type[Any]]:\n+    wheel = str(HERMES_WHEEL)\n+    if wheel not in sys.path:\n+        sys.path.insert(0, wheel)\n+    from gateway.platforms.telegram_nutrition_onboarding_publication_outbox import (\n+        GatewayOnboardingPublicationOutbox,\n+    )\n+    from gateway.platforms.telegram_staff_membership_gate import MembershipJournal\n+\n+    return GatewayOnboardingPublicationOutbox, MembershipJournal\n+\n+\n+def validate_target_state(root: Path) -> dict[str, object]:\n+    paths = mutation_paths(root)\n+    outbox = paths[\"outbox\"]\n+    membership = paths[\"membership\"]\n+    outbox_tree = tree_inventory(outbox)\n+    membership_tree = tree_inventory(membership)\n+    _strict_names(outbox, OUTBOX_NAMES)\n+    _strict_names(membership, MEMBERSHIP_NAMES)\n+\n+    try:\n+        primary = json.loads((outbox / \"ledger.json\").read_bytes())\n+        emergency = json.loads((outbox / \"emergency.json\").read_bytes())\n+        callbacks = json.loads((outbox / \"owner-callbacks.json\").read_bytes())\n+    except (OSError, json.JSONDecodeError) as exc:\n+        raise Refusal(\"outbox JSON is malformed\") from exc\n+    primary_rows = primary.get(\"records\")\n+    if (\n+        primary.get(\"schema\") != \"telegram-nutrition-onboarding-publication-outbox-v2\"\n+        or not isinstance(primary_rows, list)\n+        or not primary_rows\n+        or emergency\n+        != {\n+            \"schema\": \"telegram-nutrition-onboarding-publication-outbox-v2\",\n+            \"records\": [],\n+        }\n+        or callbacks\n+        != {\n+            \"schema\": \"telegram-nutrition-onboarding-owner-callback-v1\",\n+            \"records\": [],\n+        }\n+    ):\n+        raise Refusal(\"outbox does not match the exact supplemental state\")\n+    for row in primary_rows:\n+        if (\n+            not isinstance(row, dict)\n+            or row.get(\"session_id\") != SESSION\n+            or row.get(\"route\") != list(ROUTE)\n+            or row.get(\"role\") != \"customer\"\n+            or row.get(\"state\") != \"COMMITTED\"\n+        ):\n+            raise Refusal(\"foreign, mixed, or nonterminal outbox row\")\n+\n+    outbox_type, journal_type = _import_runtime_types()\n+    try:\n+        authenticated = outbox_type(root, initialize=False)\n+        parsed = authenticated.records()\n+        if authenticated.emergency_records():\n+            raise Refusal(\"emergency outbox is not empty\")\n+        rows = journal_type(membership / \"events.jsonl\").verify()\n+    except Refusal:\n+        raise\n+    except Exception as exc:\n+        raise Refusal(\"outbox HMAC or membership hash-chain validation failed\") from exc\n+    if len(parsed) != len(primary_rows):\n+        raise Refusal(\"outbox parser projection mismatch\")\n+    if (\n+        len(rows) != 1\n+        or rows[0].get(\"event\") != \"subscription_armed\"\n+        or rows[0].get(\"customer_user_ids\") != [USER_ID]\n+        or not isinstance(rows[0].get(\"subscription_epoch_id\"), str)\n+    ):\n+        raise Refusal(\"foreign or mixed membership state\")\n+    return {\n+        \"outbox\": compact(outbox_tree),\n+        \"membership\": compact(membership_tree),\n+        \"publication_records\": len(primary_rows),\n+        \"membership_rows\": len(rows),\n+        \"membership_epoch\": rows[0][\"subscription_epoch_id\"],\n+    }\n+\n+\n+def dry_run(\n+    profile: Path,\n+    output: Path,\n+    *,\n+    service_override: Path | None = None,\n+) -> dict[str, object]:\n+    profile = profile.absolute()\n+    _require_private(profile, directory=True)\n+    state = service_state(service_override)\n+    require_inactive(state)\n+    target = validate_target_state(profile)\n+    payload = {\n+        \"schema\": \"task27-supplemental-cleanup-permission-v1\",\n+        \"target\": str(profile),\n+        \"identity\": {\n+            \"customer_key\": CUSTOMER,\n+            \"session_id\": SESSION,\n+            \"user_id\": USER_ID,\n+            \"route\": list(ROUTE),\n+        },\n+        \"cleanup_receipt\": cleanup_receipt_binding(),\n+        \"service\": state,\n+        \"exact_state\": {\n+            \"outbox\": target[\"outbox\"],\n+            \"membership\": target[\"membership\"],\n+        },\n+        \"observed\": {\n+            \"publication_records\": target[\"publication_records\"],\n+            \"membership_rows\": target[\"membership_rows\"],\n+            \"membership_epoch\": target[\"membership_epoch\"],\n+        },\n+        \"proposed_mutations\": proposed_mutations(),\n+    }\n+    seal = sha_bytes(canonical(payload))\n+    receipt = {\n+        \"schema\": SCHEMA,\n+        \"mode\": \"dry-run\",\n+        \"status\": \"READY\",\n+        \"permission_payload\": payload,\n+        \"permission_seal\": seal,\n+    }\n+    atomic_json(output, receipt)\n+    return receipt\n+\n+\n+def _remove(path: Path) -> None:\n+    if path.is_symlink():\n+        raise Refusal(f\"refusing to remove symlink: {path}\")\n+    if path.exists():\n+        for item in sorted(path.rglob(\"*\"), reverse=True):\n+            if not item.is_symlink():\n+                item.chmod(0o700 if item.is_dir() else 0o600)\n+        path.chmod(0o700)\n+        shutil.rmtree(path)\n+\n+\n+def _copy(source: Path, destination: Path) -> None:\n+    shutil.copytree(source, destination, symlinks=False)\n+\n+\n+def _freeze(root: Path) -> None:\n+    for path in sorted(root.rglob(\"*\"), key=lambda item: len(item.parts), reverse=True):\n+        if path.is_symlink():\n+            raise Refusal(f\"archive contains a symlink: {path}\")\n+        path.chmod(0o500 if path.is_dir() else 0o400)\n+    root.chmod(0o500)\n+\n+\n+def _active_target_matches(root: Path) -> list[str]:\n+    needles = (CUSTOMER.encode(), SESSION.encode(), USER_ID.encode())\n+    matches: list[str] = []\n+    data = root / \"data\"\n+    if not data.exists():\n+        return matches\n+    for path in data.rglob(\"*\"):\n+        if not path.is_file():\n+            continue\n+        relative = path.relative_to(data)\n+        if any(part in ARCHIVE_ROOT_NAMES for part in relative.parts):\n+            continue\n+        try:\n+            payload = path.read_bytes()\n+        except OSError as exc:\n+            raise Refusal(f\"terminal scan cannot read {path}\") from exc\n+        if any(needle in payload for needle in needles):\n+            matches.append(relative.as_posix())\n+    return matches\n+\n+\n+def execute(\n+    profile: Path,\n+    permission_file: Path,\n+    seal: str,\n+    output: Path,\n+    *,\n+    fault: str | None = None,\n+    service_override: Path | None = None,\n+) -> dict[str, object]:\n+    if output.exists() or output.is_symlink():\n+        raise Refusal(f\"output already exists; receipt reuse refused: {output}\")\n+    profile = profile.absolute()\n+    if profile.resolve() == LIVE.resolve():\n+        raise Refusal(\"live execution is not implemented\")\n+    marker = profile / MARKER\n+    if (\n+        not marker.is_file()\n+        or marker.is_symlink()\n+        or marker.read_text().strip() != MARKER_VALUE\n+    ):\n+        raise Refusal(\"execution requires an explicit disposable-copy marker\")\n+    _require_private(profile, directory=True)\n+    try:\n+        permission = json.loads(permission_file.read_bytes())\n+    except (OSError, json.JSONDecodeError) as exc:\n+        raise Refusal(\"permission receipt is invalid\") from exc\n+    payload = permission.get(\"permission_payload\")\n+    expected_seal = permission.get(\"permission_seal\")\n+    if (\n+        not isinstance(payload, dict)\n+        or expected_seal != sha_bytes(canonical(payload))\n+        or seal != expected_seal\n+    ):\n+        raise Refusal(\"permission seal mismatch\")\n+    if payload.get(\"target\") != str(profile):\n+        raise Refusal(\"permission target mismatch\")\n+    if payload.get(\"cleanup_receipt\") != cleanup_receipt_binding():\n+        raise Refusal(\"cleanup receipt drift\")\n+    require_inactive(service_state(service_override))\n+    current = validate_target_state(profile)\n+    for name in (\"outbox\", \"membership\"):\n+        if current[name] != payload[\"exact_state\"][name]:\n+            raise Refusal(f\"source drift: {name}\")\n+\n+    paths = mutation_paths(profile)\n+    rollback = Path(tempfile.mkdtemp(prefix=\"task27-supplemental-rollback-\"))\n+    operation = uuid.uuid4().hex\n+    archive = paths[\"supplemental_archive\"] / \"archives\" / operation\n+    backup = rollback / \"state\"\n+    backup.mkdir(mode=0o700)\n+    _copy(paths[\"outbox\"], backup / \"outbox\")\n+    _copy(paths[\"membership\"], backup / \"membership\")\n+    try:\n+        files = archive / \"files/data/onboarding\"\n+        files.mkdir(parents=True, mode=0o700)\n+        _copy(paths[\"outbox\"], files / paths[\"outbox\"].name)\n+        _copy(paths[\"membership\"], files / paths[\"membership\"].name)\n+        archived = {\n+            \"outbox\": compact(tree_inventory(files / paths[\"outbox\"].name)),\n+            \"membership\": compact(tree_inventory(files / paths[\"membership\"].name)),\n+        }\n+        if archived != {\n+            \"outbox\": {\n+                **payload[\"exact_state\"][\"outbox\"],\n+                \"root\": str(files / paths[\"outbox\"].name),\n+            },\n+            \"membership\": {\n+                **payload[\"exact_state\"][\"membership\"],\n+                \"root\": str(files / paths[\"membership\"].name),\n+            },\n+        }:\n+            raise Refusal(\"supplemental archive copy verification failed\")\n+        manifest = {\n+            \"schema\": \"task27-supplemental-archive-v1\",\n+            \"operation_id\": operation,\n+            \"identity\": payload[\"identity\"],\n+            \"cleanup_receipt\": payload[\"cleanup_receipt\"],\n+            \"source\": payload[\"exact_state\"],\n+            \"archive\": archived,\n+        }\n+        manifest_path = archive / \"manifest.json\"\n+        atomic_json(manifest_path, manifest)\n+        if fault == \"after_archive_copy\":\n+            raise RuntimeError(\"injected fault after_archive_copy\")\n+        _remove(paths[\"outbox\"])\n+        _remove(paths[\"membership\"])\n+        if fault == \"after_prune\":\n+            raise RuntimeError(\"injected fault after_prune\")\n+        matches = _active_target_matches(profile)\n+        if matches:\n+            raise Refusal(f\"active target residue remains: {matches}\")\n+        require_inactive(service_state(service_override))\n+        terminal = {\n+            \"schema\": SCHEMA,\n+            \"mode\": \"execute\",\n+            \"status\": \"COMMITTED\",\n+            \"execution_target\": \"disposable\",\n+            \"profile\": str(profile),\n+            \"permission_seal\": seal,\n+            \"operation_id\": operation,\n+            \"cleanup_receipt\": payload[\"cleanup_receipt\"],\n+            \"observed\": payload[\"observed\"],\n+            \"archive\": {\n+                \"root\": str(archive),\n+                \"manifest_sha256\": sha_file(manifest_path),\n+                \"source\": payload[\"exact_state\"],\n+                \"copied\": archived,\n+            },\n+            \"terminal\": {\n+                \"outbox_absent\": not paths[\"outbox\"].exists(),\n+                \"membership_absent\": not paths[\"membership\"].exists(),\n+                \"active_target_matches\": 0,\n+                \"service_inactive\": True,\n+            },\n+            \"proposed_live_mutations\": proposed_mutations(),\n+        }\n+        atomic_json(archive / \"receipt.json\", terminal)\n+        _freeze(archive)\n+        atomic_json(output, terminal, 0o400)\n+        return terminal\n+    except BaseException:\n+        _remove(paths[\"outbox\"])\n+        _remove(paths[\"membership\"])\n+        _remove(paths[\"supplemental_archive\"])\n+        _copy(backup / \"outbox\", paths[\"outbox\"])\n+        _copy(backup / \"membership\", paths[\"membership\"])\n+        raise\n+    finally:\n+        shutil.rmtree(rollback)\n+\n+\n+def parser() -> argparse.ArgumentParser:\n+    value = argparse.ArgumentParser()\n+    commands = value.add_subparsers(dest=\"command\", required=True)\n+    dry = commands.add_parser(\"dry-run\")\n+    dry.add_argument(\"--profile\", type=Path, required=True)\n+    dry.add_argument(\"--output\", type=Path, required=True)\n+    dry.add_argument(\"--test-service-state\", type=Path, help=argparse.SUPPRESS)\n+    run = commands.add_parser(\"execute\")\n+    run.add_argument(\"--profile\", type=Path, required=True)\n+    run.add_argument(\"--permission-file\", type=Path, required=True)\n+    run.add_argument(\"--permission-seal\", required=True)\n+    run.add_argument(\"--output\", type=Path, required=True)\n+    run.add_argument(\n+        \"--fault\", choices=(\"after_archive_copy\", \"after_prune\")\n+    )\n+    run.add_argument(\"--test-service-state\", type=Path, help=argparse.SUPPRESS)\n+    return value\n+\n+\n+def main(argv: list[str] | None = None) -> int:\n+    args = parser().parse_args(argv)\n+    try:\n+        if args.command == \"dry-run\":\n+            result = dry_run(\n+                args.profile,\n+                args.output,\n+                service_override=args.test_service_state,\n+            )\n+        else:\n+            result = execute(\n+                args.profile,\n+                args.permission_file,\n+                args.permission_seal,\n+                args.output,\n+                fault=args.fault,\n+                service_override=args.test_service_state,\n+            )\n+        print(\n+            json.dumps(\n+                {\n+                    \"status\": result[\"status\"],\n+                    \"permission_seal\": result[\"permission_seal\"],\n+                    \"output\": str(args.output),\n+                },\n+                sort_keys=True,\n+            )\n+        )\n+        return 0\n+    except BaseException as exc:\n+        print(\n+            json.dumps(\n+                {\n+                    \"status\": \"REFUSED\",\n+                    \"error\": str(exc),\n+                    \"type\": type(exc).__name__,\n+                },\n+                sort_keys=True,\n+            ),\n+            file=sys.stderr,\n+        )\n+        if os.environ.get(\"TASK27_TRACEBACK\") == \"1\":\n+            traceback.print_exc()\n+        return 2\n+\n+\n+if __name__ == \"__main__\":\n+    raise SystemExit(main())\n*** Delete File: .omo/evidence/task27/test_task27_supplemental_cleanup_controller.py\n*** Add File: .omo/evidence/task27/test_task27_supplemental_cleanup_controller.py\n+from __future__ import annotations\n+\n+import hashlib\n+import importlib.util\n+import json\n+import os\n+import stat\n+import subprocess\n+import sys\n+from pathlib import Path\n+\n+import pytest\n+\n+HERE = Path(__file__).resolve().parent\n+CONTROLLER = HERE / \"task27_supplemental_cleanup_controller.py\"\n+SESSION = \"cb_mYUoMIsk_CRzSDKYpPE9dg\"\n+USER = \"8527916639\"\n+ROUTE = (USER, \"0\")\n+WHEEL = (\n+    HERE.parent\n+    / \"task26/task26-combined-v38-delivered-st_01a019d7/artifacts\"\n+    / \"hermes_agent-0.17.0-py3-none-any.whl\"\n+)\n+\n+\n+def load():\n+    spec = importlib.util.spec_from_file_location(\"task27_supplement\", CONTROLLER)\n+    assert spec is not None and spec.loader is not None\n+    module = importlib.util.module_from_spec(spec)\n+    sys.modules[spec.name] = module\n+    spec.loader.exec_module(module)\n+    return module\n+\n+\n+def canonical(value):\n+    return json.dumps(\n+        value, ensure_ascii=True, sort_keys=True, separators=(\",\", \":\")\n+    ).encode()\n+\n+\n+def private_json(path: Path, value) -> None:\n+    path.parent.mkdir(parents=True, exist_ok=True, mode=0o700)\n+    path.write_bytes(canonical(value) + b\"\\n\")\n+    path.chmod(0o600)\n+\n+\n+def profile(tmp_path: Path) -> Path:\n+    root = tmp_path / \"profile\"\n+    root.mkdir(parents=True, mode=0o700)\n+    marker = root / \".task27-supplemental-disposable-copy\"\n+    marker.write_text(\"TASK27_SUPPLEMENTAL_DISPOSABLE_COPY\\n\")\n+    marker.chmod(0o600)\n+    sys.path.insert(0, str(WHEEL))\n+    from gateway.platforms.telegram_nutrition_onboarding_publication_outbox import (\n+        GatewayOnboardingPublicationOutbox,\n+    )\n+    from gateway.platforms.telegram_staff_membership_gate import MembershipJournal\n+\n+    payload = {\"body_digest\": \"a\" * 64, \"state\": \"collecting\"}\n+    render = \"b\" * 64\n+    outbox = GatewayOnboardingPublicationOutbox(root)\n+    outbox.claim(\n+        session_id=SESSION,\n+        generation=0,\n+        payload=payload,\n+        route=ROUTE,\n+        role=\"customer\",\n+        render_identity=render,\n+    )\n+    outbox.record_receipt(\n+        session_id=SESSION,\n+        generation=0,\n+        chat_id=USER,\n+        topic_id=\"0\",\n+        message_id=304,\n+    )\n+    outbox.mark_committed(\n+        session_id=SESSION,\n+        generation=0,\n+        payload=payload,\n+        route=ROUTE,\n+        role=\"customer\",\n+        render_identity=render,\n+        message_id=304,\n+    )\n+    MembershipJournal(\n+        root / \"data/onboarding/telegram-staff-membership-v1/events.jsonl\"\n+    ).append(\n+        {\n+            \"event\": \"subscription_armed\",\n+            \"subscription_epoch_id\": \"epoch-1\",\n+            \"observed_at_utc\": \"2026-08-18T14:55:11+00:00\",\n+            \"staff_chat_inventory_sha256\": \"c\" * 64,\n+            \"customer_user_ids\": [USER],\n+        }\n+    )\n+    return root\n+\n+\n+def service_state(tmp_path: Path, active: bool = False) -> Path:\n+    path = tmp_path / (\"active.json\" if active else \"inactive.json\")\n+    private_json(\n+        path,\n+        {\n+            \"active_state\": \"active\" if active else \"inactive\",\n+            \"sub_state\": \"running\" if active else \"dead\",\n+            \"main_pid\": 42 if active else 0,\n+            \"matching_processes\": 1 if active else 0,\n+        },\n+    )\n+    return path\n+\n+\n+def permission(module, root: Path, tmp_path: Path):\n+    path = tmp_path / \"permission.json\"\n+    result = module.dry_run(\n+        root, path, service_override=service_state(tmp_path)\n+    )\n+    return path, result[\"permission_seal\"], result\n+\n+\n+def mutation_digest(module, root: Path) -> str:\n+    return module.sha_bytes(\n+        canonical(\n+            {\n+                key: module.compact(module.tree_inventory(value))\n+                for key, value in module.mutation_paths(root).items()\n+            }\n+        )\n+    )\n+\n+\n+def test_dry_run_binds_contract(tmp_path: Path) -> None:\n+    module = load()\n+    root = profile(tmp_path)\n+    _, _, result = permission(module, root, tmp_path)\n+    payload = result[\"permission_payload\"]\n+    assert set(payload[\"exact_state\"]) == {\"outbox\", \"membership\"}\n+    assert payload[\"cleanup_receipt\"][\"sha256\"] == module.CLEANUP_RECEIPT_SHA256\n+    assert payload[\"proposed_mutations\"] == module.proposed_mutations()\n+\n+\n+@pytest.mark.parametrize(\n+    \"kind\", [\"foreign_session\", \"foreign_route\", \"mixed_membership\"]\n+)\n+def test_rejects_foreign_or_mixed_state(tmp_path: Path, kind: str) -> None:\n+    module = load()\n+    root = profile(tmp_path)\n+    if kind.startswith(\"foreign\"):\n+        path = root / \"data/onboarding/telegram-publication-outbox-v1/ledger.json\"\n+        document = json.loads(path.read_text())\n+        if kind == \"foreign_session\":\n+            document[\"records\"][0][\"session_id\"] = \"foreign\"\n+        else:\n+            document[\"records\"][0][\"route\"] = [\"999\", \"0\"]\n+        private_json(path, document)\n+    else:\n+        path = root / \"data/onboarding/telegram-staff-membership-v1/events.jsonl\"\n+        row = json.loads(path.read_text())\n+        row[\"customer_user_ids\"] = [USER, \"999\"]\n+        row.pop(\"row_sha256\")\n+        row[\"row_sha256\"] = hashlib.sha256(canonical(row)).hexdigest()\n+        path.write_bytes(canonical(row) + b\"\\n\")\n+    with pytest.raises(module.Refusal):\n+        module.dry_run(\n+            root,\n+            tmp_path / \"bad-permission.json\",\n+            service_override=service_state(tmp_path),\n+        )\n+\n+\n+@pytest.mark.parametrize(\n+    \"attack\", [\"hmac\", \"unknown\", \"symlink\", \"hardlink\", \"mode\"]\n+)\n+def test_rejects_unsafe_or_unknown_outbox(tmp_path: Path, attack: str) -> None:\n+    module = load()\n+    root = profile(tmp_path)\n+    outbox = root / \"data/onboarding/telegram-publication-outbox-v1\"\n+    ledger = outbox / \"ledger.json\"\n+    if attack == \"hmac\":\n+        document = json.loads(ledger.read_text())\n+        document[\"records\"][0][\"receipt_integrity\"] = \"0\" * 64\n+        private_json(ledger, document)\n+    elif attack == \"unknown\":\n+        path = outbox / \"unexpected.json\"\n+        path.write_text(\"{}\\n\")\n+        path.chmod(0o600)\n+    elif attack == \"symlink\":\n+        ledger.unlink()\n+        ledger.symlink_to(\"emergency.json\")\n+    elif attack == \"hardlink\":\n+        os.link(ledger, outbox / \"ledger-copy.json\")\n+    else:\n+        ledger.chmod(0o644)\n+    with pytest.raises(module.Refusal):\n+        module.dry_run(\n+            root,\n+            tmp_path / \"bad-permission.json\",\n+            service_override=service_state(tmp_path),\n+        )\n+\n+\n+@pytest.mark.parametrize(\n+    \"attack\", [\"unknown\", \"symlink\", \"hardlink\", \"mode\", \"malformed\"]\n+)\n+def test_rejects_unsafe_membership(tmp_path: Path, attack: str) -> None:\n+    module = load()\n+    root = profile(tmp_path)\n+    membership = root / \"data/onboarding/telegram-staff-membership-v1\"\n+    events = membership / \"events.jsonl\"\n+    if attack == \"unknown\":\n+        path = membership / \"unexpected\"\n+        path.write_text(\"x\")\n+        path.chmod(0o600)\n+    elif attack == \"symlink\":\n+        events.unlink()\n+        events.symlink_to(\"events.jsonl.lock\")\n+    elif attack == \"hardlink\":\n+        os.link(events, membership / \"copy\")\n+    elif attack == \"mode\":\n+        events.chmod(0o644)\n+    else:\n+        events.write_text(\"{\")\n+    with pytest.raises(module.Refusal):\n+        module.dry_run(\n+            root,\n+            tmp_path / \"bad-permission.json\",\n+            service_override=service_state(tmp_path),\n+        )\n+\n+\n+def test_rejects_active_service_and_state_drift(tmp_path: Path) -> None:\n+    module = load()\n+    root = profile(tmp_path)\n+    with pytest.raises(module.Refusal):\n+        module.dry_run(\n+            root,\n+            tmp_path / \"active-permission.json\",\n+            service_override=service_state(tmp_path, True),\n+        )\n+    permission_path, seal, _ = permission(module, root, tmp_path)\n+    (root / \"data/onboarding/telegram-publication-outbox-v1/.lock\").write_text(\n+        \"drift\"\n+    )\n+    with pytest.raises(module.Refusal, match=\"drift\"):\n+        module.execute(\n+            root,\n+            permission_path,\n+            seal,\n+            tmp_path / \"receipt.json\",\n+            service_override=service_state(tmp_path),\n+        )\n+\n+\n+@pytest.mark.parametrize(\"fault\", [\"after_archive_copy\", \"after_prune\"])\n+def test_fault_rollback_is_exact(tmp_path: Path, fault: str) -> None:\n+    module = load()\n+    root = profile(tmp_path)\n+    permission_path, seal, _ = permission(module, root, tmp_path)\n+    before = mutation_digest(module, root)\n+    with pytest.raises(RuntimeError, match=fault):\n+        module.execute(\n+            root,\n+            permission_path,\n+            seal,\n+            tmp_path / \"receipt.json\",\n+            fault=fault,\n+            service_override=service_state(tmp_path),\n+        )\n+    assert mutation_digest(module, root) == before\n+    assert not (root / \"data/task27-supplemental-cleanup\").exists()\n+\n+\n+def test_success_is_frozen_terminal_and_one_use(tmp_path: Path) -> None:\n+    module = load()\n+    root = profile(tmp_path)\n+    permission_path, seal, _ = permission(module, root, tmp_path)\n+    output = tmp_path / \"receipt.json\"\n+    result = module.execute(\n+        root,\n+        permission_path,\n+        seal,\n+        output,\n+        service_override=service_state(tmp_path),\n+    )\n+    assert result[\"status\"] == \"COMMITTED\"\n+    assert result[\"terminal\"][\"active_target_matches\"] == 0\n+    assert not (\n+        root / \"data/onboarding/telegram-publication-outbox-v1\"\n+    ).exists()\n+    assert not (\n+        root / \"data/onboarding/telegram-staff-membership-v1\"\n+    ).exists()\n+    archive = Path(result[\"archive\"][\"root\"])\n+    assert stat.S_IMODE(archive.stat().st_mode) == 0o500\n+    assert all(\n+        not (path.stat().st_mode & 0o222)\n+        for path in [archive, *archive.rglob(\"*\")]\n+    )\n+    with pytest.raises(module.Refusal, match=\"output already exists|reuse\"):\n+        module.execute(\n+            root,\n+            permission_path,\n+            seal,\n+            output,\n+            service_override=service_state(tmp_path),\n+        )\n+\n+\n+def test_execute_is_disposable_only(tmp_path: Path) -> None:\n+    module = load()\n+    root = profile(tmp_path)\n+    permission_path, seal, _ = permission(module, root, tmp_path)\n+    (root / \".task27-supplemental-disposable-copy\").unlink()\n+    with pytest.raises(module.Refusal, match=\"disposable\"):\n+        module.execute(\n+            root,\n+            permission_path,\n+            seal,\n+            tmp_path / \"receipt.json\",\n+            service_override=service_state(tmp_path),\n+        )\n+\n+\n+def test_cli_bad_seal_and_success(tmp_path: Path) -> None:\n+    root = profile(tmp_path)\n+    state = service_state(tmp_path)\n+    permission_path = tmp_path / \"permission.json\"\n+    dry = subprocess.run(\n+        [\n+            sys.executable,\n+            str(CONTROLLER),\n+            \"dry-run\",\n+            \"--profile\",\n+            str(root),\n+            \"--output\",\n+            str(permission_path),\n+            \"--test-service-state\",\n+            str(state),\n+        ],\n+        text=True,\n+        capture_output=True,\n+    )\n+    assert dry.returncode == 0, dry.stderr\n+    seal = json.loads(permission_path.read_text())[\"permission_seal\"]\n+    bad = subprocess.run(\n+        [\n+            sys.executable,\n+            str(CONTROLLER),\n+            \"execute\",\n+            \"--profile\",\n+            str(root),\n+            \"--permission-file\",\n+            str(permission_path),\n+            \"--permission-seal\",\n+            \"0\" * 64,\n+            \"--output\",\n+            str(tmp_path / \"bad.json\"),\n+            \"--test-service-state\",\n+            str(state),\n+        ],\n+        text=True,\n+        capture_output=True,\n+    )\n+    assert bad.returncode == 2\n+    assert \"permission seal mismatch\" in bad.stderr\n+    good = subprocess.run(\n+        [\n+            sys.executable,\n+            str(CONTROLLER),\n+            \"execute\",\n+            \"--profile\",\n+            str(root),\n+            \"--permission-file\",\n+            str(permission_path),\n+            \"--permission-seal\",\n+            seal,\n+            \"--output\",\n+            str(tmp_path / \"good.json\"),\n+            \"--test-service-state\",\n+            str(state),\n+        ],\n+        text=True,\n+        capture_output=True,\n+    )\n+    assert good.returncode == 0, good.stderr\n*** End Patch\n```\n\n## Observed RED\n\nCommand:\n\n```bash\npython -m pytest -q \\\n  .omo/evidence/task27/test_task27_supplemental_cleanup_controller.py\n```\n\nObserved summary:\n\n```text\nFFFFFFFFFFFFFF                                                           [100%]\n...\nFileNotFoundError: [Errno 2] No such file or directory:\n'/home/cube/projects/richard/traning coach/.omo/evidence/task27/task27_supplemental_cleanup_controller.py'\n...\n14 failed in 0.18s\n```\n\nThis was a genuine missing-controller RED. No GREEN run occurred.\n\n## Intended GREEN and QA\n\n```bash\npython -m py_compile \\\n  .omo/evidence/task27/task27_supplemental_cleanup_controller.py \\\n  .omo/evidence/task27/test_task27_supplemental_cleanup_controller.py\n\npython -m pytest -q \\\n  .omo/evidence/task27/test_task27_supplemental_cleanup_controller.py\n```\n\nThen run LSP diagnostics on both files.\n\nLive read-only dry-run:\n\n```bash\npython .omo/evidence/task27/task27_supplemental_cleanup_controller.py dry-run \\\n  --profile /home/cube/.hermes/profiles/dualcoachtest \\\n  --output .omo/evidence/task27/task27-supplemental-live-dry-run.json\nchmod 0400 .omo/evidence/task27/task27-supplemental-live-dry-run.json\n```\n\nDisposable rehearsal:\n\n```bash\ntmp=\"$(mktemp -d /tmp/task27-supplemental-rehearsal-XXXXXX)\"\ncp -a /home/cube/.hermes/profiles/dualcoachtest \"$tmp/profile\"\nchmod 0700 \"$tmp/profile\"\nprintf '%s\\n' TASK27_SUPPLEMENTAL_DISPOSABLE_COPY \\\n  > \"$tmp/profile/.task27-supplemental-disposable-copy\"\nchmod 0600 \"$tmp/profile/.task27-supplemental-disposable-copy\"\n\npython .omo/evidence/task27/task27_supplemental_cleanup_controller.py dry-run \\\n  --profile \"$tmp/profile\" \\\n  --output \"$tmp/permission.json\"\n\nseal=\"$(\n  python -c 'import json,sys; print(json.load(open(sys.argv[1]))[\"permission_seal\"])' \\\n  \"$tmp/permission.json\"\n)\"\n\npython .omo/evidence/task27/task27_supplemental_cleanup_controller.py execute \\\n  --profile \"$tmp/profile\" \\\n  --permission-file \"$tmp/permission.json\" \\\n  --permission-seal \"$seal\" \\\n  --output .omo/evidence/task27/task27-supplemental-rehearsal-receipt.json\nchmod 0400 .omo/evidence/task27/task27-supplemental-rehearsal-receipt.json\n```\n\nManual bad-input and one-use QA:\n\n```bash\npython .omo/evidence/task27/task27_supplemental_cleanup_controller.py execute \\\n  --profile \"$tmp/profile\" \\\n  --permission-file \"$tmp/permission.json\" \\\n  --permission-seal \"$(printf '0%.0s' {1..64})\" \\\n  --output \"$tmp/bad-seal.json\"\n# Expected: exit 2, \"permission seal mismatch\"\n\npython .omo/evidence/task27/task27_supplemental_cleanup_controller.py execute \\\n  --profile \"$tmp/profile\" \\\n  --permission-file \"$tmp/permission.json\" \\\n  --permission-seal \"$seal\" \\\n  --output .omo/evidence/task27/task27-supplemental-rehearsal-receipt.json\n# Expected: exit 2, output already exists / receipt reuse refused\n```\n\nBefore the live dry-run and after all disposable QA, independently hash the live profile with the existing Task27 `tree_inventory()` implementation. Emit:\n\n- `task27-supplemental-verification-receipt.json`\n- `task27-supplemental-live-nonmutation-receipt.json`\n\nThe nonmutation receipt should bind equal before/after live-profile digests, controller/test hashes, cleanup receipt hash, dry-run hash, rehearsal hash, test result, compile/LSP results, and the exact `proposed_mutations()` value. No live execution or authorization receipt should be produced."}}
{"type":"transition_applied","payload":{"type":"transition_applied","status":"completed","residency_state":"resident"}}
{"type":"transition_applied","payload":{"type":"transition_applied","status":"completed","residency_state":"persisted_only"}}
{"type":"suspended","payload":{"reason":"quit"}}
{"type":"reconcile_reattached","payload":{"session_path":"/home/cube/projects/richard/traning coach/.omo/senpi-task/children/st_01a01a17/sessions/st_01a01a17/2026-08-19T12-53-38-808Z_01a01a15-9b78-73aa-bedb-ac3fcfa1b9d6.jsonl"}}
{"type":"transition_applied","payload":{"type":"transition_applied","status":"completed","residency_state":"evicted"}}
{"type":"evicted","payload":{"cause":"evict"}}
