#!/usr/bin/env python3
"""Lint the Task26 v6 Golden Path and Recovery Runbook pair (runbook-rebind task st_01a0054e).

v6 linter: carries all v3 checks forward (v1-v3 runbooks are preserved byte-identical and checked by
their own linters) and adds the v4 groups: path-existence (on-disk hash/mode/ownership verification),
no-secret (raw token and unapproved IDs absent), obsolete-path-negative (session-file references only
inside prohibition statements), and archive-CLI (exact verifier command with the actual seal path).
"""
from __future__ import annotations

import hashlib
import json
import os
import re
import stat
import sys
from pathlib import Path

BASE = Path(__file__).resolve().parent
REPO = BASE.parents[3]
GP = BASE / "task26-golden-path-2e0894ea-v6.md"
RC = BASE / "task26-recovery-runbook-2e0894ea-v6.md"
RECEIPT = BASE / "runbook-binding-receipt-v6.json"

CANDIDATE = "2e0894eac92bc396cc4723bf1f18ebc653b95018dd41574df435941c235da925"
WHEEL = "af4a9d0a1ffffb6eb7551c1d6dc2b32853ca6d024332a4f8f5702bbf992f141b"
PLAN = "7ace03c6dad33d2fc3ef223621cbca68a150fde8429932138e252fb8498ac582"
ACTOR = "8527916639"

# v3 pins (unchanged evidence) plus the v4 G14 pins.
PINS = [
    "b852482350129dd025f0c4c79539e04a71c4758c9ec336e2c5bcda44676791f6",
    "cd9e080ab886d503a205e7a7335eb23942b161daeaa2028a6e8f63492a99ba68",
    "8278dde4efa8bcd366fce873ccc07f651818275238b67452b9e6cb93c67afd6d",
    "6aeaaab42e0030b0f626037d7b9ef6661ec93fb57fd991c843967c1fc695905c",
    "16acf1b355715379ad526433803353e1f34c418e9560fc1466f3f77f951df919",
    "7d163cb01708869d617efeadd852f7e6ec4f963a94c50d19b104cc849483c171",
    "f9a46172386333a0067f43695fb1429a043a606f04baceb06e8c59b90c36235c",
    "0e9b4b1f0449d8351602598fead3ef01b8616e230c147e44db79667284ceae49",
    "83c95abb4b07f30fae0d996a7148c92b2db4600f6f3da2aadaf50a7ff62c71fc",
    "847548932b42210416fcfc302574e523778f12aecd2614ac28a95af6602c8dcc",
    "c7fc477141259087e8e9dd6520b2066eda3bab20dc6e2f625942431b6830c187",
    "c9a6bb0fea893d90956249264277481039a522021001495f07a2a28ec6280e0d",
    "8732bbac3bccb5b235597b7841190c31bf39f8644cd70c79a5dbad2334fd8994",
    "23845bda574c085e16b6d316e5e84e54b3bc6db3a8803c6b3b93ed7c4b86b049",
    "145515d5e110dcebb94fcaa554bcee29544b3a75dcfe058fae944ea3b70042b2",
    "8b03fa714304b34a7f19dc9b077d8fbd54e7f7bc46232bf8d5bc65ae7b873ff5",
    "7b6a1aa54b58d6e7e6733a963772e220a903ffae5a3e0e6d089283af13e097db",
    "cb06310270868da5dfc3c1d292b0ccedd5f3021d0f211576449c96649fa4730b",
    "90fcb04be789fd519d6bb83f9ba659927721e9d272594de84329c42c3529ed18",
    "d8d818cdfd2a22b088064b216136bfdee050b096a5b7fdcc9fb6f5958312dd05",
    "fab6322753cccca614e1ee83bf5221013185662a74c86f5d19563e38f0fc0d2b",
    "769db2bd5c39e405176ecb438d05ad0f10c538ee53c6c7849e809123f91ecb88",
    "78d7474df421f26ec4d106fa378270f60e57178553c3423fad717698c0fd5b22",
    "e6888d62915b4b57b22b2769ebf46a7a1ee57d0d4ad7c4d5c1502bffc895ebdf",
    "bd051dda8666ce9e014ec79c58acd4d7b8df2c27fad01a7a78c76e46c3388baf",
    "4128cece087f3f4eb84c3917207299fae6bd1ac9971d7e4e8676549106bb7c20",
    "8a290b11cac5b6957c772366abe875c7f635b8a3e7956a665471ffaa90b6c495",
    "2fc323f8b00c18f21598f717fb6a19a7e342b7786909e59adcf4bc3955bcef99",
    "776e90301a63dce8912bf8a4110cd037f833c3a0e875a3ddd1289c0c9ee2ac9e",
    "12b97aa72e2df86719dbb7b80b477237d590aaaea81a9f812f321a1566936528",
    "29db87f2855c4804dbbc69244e68d9fb9f51808f7f7759fb592ed68f5f090185",
    "28ff72fdb7925cbdcd8e7dd7e8c058bcca42cab3377c34078164766197ed6968",
    "597a37e4912d729b3b5f6022bccff6dc12a73b024128805980f5ab1ade4fb83d",
    "d2879b128573a9d75a55958d2fa3e1a24be1636f0a7a20affe8f2ef9b94dfd33",
    "a8191eed20ca75abcb03c33ac0c0b915cd810249466e816c6a1a55ba154af188",
    "f5d51008f1e8c8ee102df930fa68c7945a3276a249187adc7b7f6ba4b1e8e134",
    "3eca06827bfd62a4f07498bf7fed54e3b2b47a9f62e1f5b81701d1453e88806e",
    "cc0e4b697633150f3626daa8cf5bb4825bc96243f1a3dfadb6c9f540cad9906c",
    "6ab725042cbed536d8a8f3faee29e431470617f21d83c795e19574a27fcd032f",
    "1f55b8f967c6564113574d41bf1c15ce977d35c38b5687f1623ba37f235a7bdb",
    "f4f2d2347b4b68a080b9c437a813accbef8c4bcc471cbf21d67ffff31e56f89b",
    "633d5c25ce70cb9ab5d19c434122b14b46fb7108962ab6e453ce6ab3ede1848f",
    "6024c26ccae3cad7fcf46a77bca8607d73293a92ddb3dcf8fa5a278361869f1d",
    "ff230719eb5a69ae821bb0a4d58356d59b2803c490d692659a197e22083793c7",  # v3 GP
    "e62691493d2a75c312dd248a07bbc52f7f1d98d4acd701b824bbaf5edf8bdd3a",  # v3 RC
    "32a379d855c6e5af978bd9886e3bf49c616c7c20f1c1d5f100c19d8adfc5eb4a",
    "ae2f5f9046c06f8f0b42693be5aa0d9c31cada8024ae1e5ab8ba19a3cf10f6fc",
    "f93106b16643227e2ef9dec67a5bbd497e1d353e779da62287898a087071af87",
    "0b46e887fc12c45f12814f4ef025c10101ed36e5df0ac35f70abb1862c0c1db2",
    "28420d4aa8fc1ee1298c9fe93a69e47aa187c0d2777ca672fb603cfbebc569cf",
    "9eb1b5ae", "7004dac9", "0bd0afb9", "68de185a",
    # v4 G14 additions
    "154f588c89758c6ca6a1e06cc39ce9489d5c3ff8ace94e10e71cce919252bcca",  # .env
    "d0aacf0f4bdbb7c04e12769947c498ee869248972b36f099800a3d3c27c0a3a8",  # token sha256 (no-secret proof)
    "b41060dea28eb3bbb83217068f5218d5df2c879dba00e73c9ca4e577a6049dad",  # telegram.py
    "849791bc3b31145a691b8ef4defacba72aeb5de0fca12427988ec1ddd4e9fffa",  # nutrition_coaching_config.py
    "b48e2b49f0939316e7e4368c940492d1b8a94863da7e5c8141f7f67a1fe08efb",  # telegram_nutrition_addresses.py
    # v5 G13 sealed lifecycle observer pins
    "58ed20c6f12887c10d6bf4fb388dded7c1eec9b30ec7f6bffa7c67e7e237b2c8",
    "68d98bca346296eacbe4c2cef9a105072314ba542728b043bbff319b6b19b466",
    "7d637a2587079fc94c28e2866d4f337e97fea102e50f37542299e9611c226489",
    "763aa448fc0220a237fe3a29ebfbd01c6d9ec2855c3db90ac12f157a7a0f2e0d",
    "b9bd1a93af4969692d23a3e4632825d0c72c02842732bad0447d9b8ce8ce3fdb",
    "6fe4e7375bf1ff51ca6fc0850f9adcb6d8baef2b9e6bbce8116ef1044a9ecf06",
    "23e673309b4d7cdd1eed77ae8126e58100270f9e4d13ca1093b01c4b1667543e",
    "a6991e5203ac84f91d8a1c46d32de51c9ac56d63b2906e86583d33c946756bb0",
    "3ecf6cf7f3384a3a03701f4f565edf6a571a82b53fabd3e8de36ea8648189221",
    "26219f277a95d6f001d1055de72287c60bbf3605602d7725ae571aceb436bad1",
    "6281af7400d166acc4fae18af7fbaa401890eb7ef49607967398118d488f09b4",
    "475b72cb29ec28f085efdd1af5382f61b70db460aec861c52bc350c43dbe175b",
    "6ca2fcdbe1c516a68281be941ebe7a2101f0b70fbf95bdff38b7817e1630cee1",
    "d128cb14884551bb9f21562c5111e3685ddb40c0bf023af85f7427855ee70d5c",
]

STALE_PATTERNS = [
    r"python -m hermes_cli\.nutrition_readiness --customer",
    r"request-generation --customer",
    r"record-generation",
    r"customer_admin --registry \S+ create",
    r"customer_admin --registry \S+ reset",
    r"customer_admin --registry \S+ delete",
    r"customer_admin --registry \S+ set-next-checkin",
    r"disable <[^>]*> --reason",
    r"/home/cube/\.hermes/profiles/dualcoachtest/\.venv",
    r"/etc/systemd/system/hermes-gateway-dualcoachtest",
    r"dry-run, verify, execute",
    r"gateway/cron/scheduler\.py",
    r"telegram_customer_onboarding\.py",
    r"eff71467",
    r"exit `?2`?,? stdout JSON `?status`? == `?PASS",
    r"(?<!retained-)archive-verifier-permission-seal",   # stale seal filename (v3 error)
    r"Telegram auth files under",                        # deleted G14 assertion form (v1-v3)
    r"api_id",                                           # unapproved raw ID form
    r"telegram_nutrition_onboarding_e2e\.py",          # missing workspace watcher
    r"scripts/telegram_nutrition_onboarding_e2e",         # missing workspace watcher path
    r"task26-invite-harness-st_01a0056a/",               # superseded v1 harness root
    r"invite_harness\.py",                              # superseded v1 harness executable
]

# On-disk path-existence contracts (read-only verification).
PATH_CHECKS = [
    (REPO / ".omo/evidence/task26/retained-archive-verifier-permission-seal.json",
     "28ff72fdb7925cbdcd8e7dd7e8c058bcca42cab3377c34078164766197ed6968", 0o400),
    (REPO / ".omo/evidence/task26/verify_retained_archives.py",
     "12b97aa72e2df86719dbb7b80b477237d590aaaea81a9f812f321a1566936528", 0o500),
    (REPO / ".omo/evidence/task26/retained-archive-schema-inventory.json",
     "29db87f2855c4804dbbc69244e68d9fb9f51808f7f7759fb592ed68f5f090185", 0o400),
    (Path("/home/cube/.hermes/profiles/dualcoachtest/.env"),
     "154f588c89758c6ca6a1e06cc39ce9489d5c3ff8ace94e10e71cce919252bcca", 0o600),
    (Path("/home/cube/.hermes/profiles/dualcoachtest/config.yaml"),
     "f93106b16643227e2ef9dec67a5bbd497e1d353e779da62287898a087071af87", 0o600),
    (Path("/home/cube/.config/systemd/user/hermes-gateway-dualcoachtest.service"),
     "0b46e887fc12c45f12814f4ef025c10101ed36e5df0ac35f70abb1862c0c1db2", None),
]

OBSERVER_ROOT = REPO / ".omo/evidence/task26/task26-lifecycle-observer-st_01a005b4"
OBSERVER_FILES = {
    "lifecycle_observer.py": ("58ed20c6f12887c10d6bf4fb388dded7c1eec9b30ec7f6bffa7c67e7e237b2c8", 0o500),
    "permission-seal.json": ("68d98bca346296eacbe4c2cef9a105072314ba542728b043bbff319b6b19b466", 0o600),
    "manifest.schema.json": ("7d637a2587079fc94c28e2866d4f337e97fea102e50f37542299e9611c226489", 0o600),
    "live-arm-manifest.json": ("763aa448fc0220a237fe3a29ebfbd01c6d9ec2855c3db90ac12f157a7a0f2e0d", 0o600),
    "verification-receipt.redacted.json": ("b9bd1a93af4969692d23a3e4632825d0c72c02842732bad0447d9b8ce8ce3fdb", 0o600),
    "artifact-hashes.sha256": ("6fe4e7375bf1ff51ca6fc0850f9adcb6d8baef2b9e6bbce8116ef1044a9ecf06", 0o600),
    "EXACT_COMMANDS.md": ("23e673309b4d7cdd1eed77ae8126e58100270f9e4d13ca1093b01c4b1667543e", 0o600),
    "runtime-manifest.template.json": ("a6991e5203ac84f91d8a1c46d32de51c9ac56d63b2906e86583d33c946756bb0", 0o600),
    "test_lifecycle_observer.py": ("3ecf6cf7f3384a3a03701f4f565edf6a571a82b53fabd3e8de36ea8648189221", 0o600),
    "test-results.txt": ("26219f277a95d6f001d1055de72287c60bbf3605602d7725ae571aceb436bad1", 0o600),
    "ruff.txt": ("6281af7400d166acc4fae18af7fbaa401890eb7ef49607967398118d488f09b4", 0o600),
    "compile.txt": ("475b72cb29ec28f085efdd1af5382f61b70db460aec861c52bc350c43dbe175b", 0o600),
    "live-arm-only.redacted.json": ("6ca2fcdbe1c516a68281be941ebe7a2101f0b70fbf95bdff38b7817e1630cee1", 0o600),
}
INVITE_ROOT = REPO / ".omo/evidence/task26/task26-invite-harness-st_01a0056a-v2"
INVITE_FILES = {
    "invite_harness_v2.py": ("83c95abb4b07f30fae0d996a7148c92b2db4600f6f3da2aadaf50a7ff62c71fc", 0o500),
    "independent_verify_v2.py": ("bb3ae896f8dd1a51d2f9df425f3afdc2bf72f73877163b6941a2bba2ee44b4ad", 0o500),
    "permission-seal-v2.json": ("847548932b42210416fcfc302574e523778f12aecd2614ac28a95af6602c8dcc", 0o600),
    "schema-v2.json": ("6a4249d263db04bea1432ad2393150ed77d3243a1365ed8dbe1eed5c0261df9e", 0o600),
    "OPERATIONS-v2.md": ("82099d8a3658a494c3cf13558e43fe7846e7e826cfcdec6c10515f05a697752d", 0o600),
    "hashes-v2.json": ("ad6cd01cfb85aa4183cea5067fab4a507cbae2a8418223c4f160b9253cf388e0", 0o600),
    "verification-receipt-v2.redacted.json": ("c9a6bb0fea893d90956249264277481039a522021001495f07a2a28ec6280e0d", 0o600),
    "test_invite_harness_v2.py": ("8732bbac3bccb5b235597b7841190c31bf39f8644cd70c79a5dbad2334fd8994", 0o600),
}
OBSOLETE_NEEDLES = ("auth_39664143", ".session-journal", ".local/share/hermes/telegram")
PROHIBITION_MARKERS = ("never", "obsolete", "prohibit", "forbidden", "absent", "deleted")

results: list[dict[str, object]] = []


def check(name: str, ok: bool, detail: str = "") -> None:
    results.append({"check": name, "ok": bool(ok), "detail": detail})


def sha256_of(path: Path) -> str:
    return hashlib.sha256(path.read_bytes()).hexdigest()


def main() -> int:
    check("file-pair-exists", GP.exists() and RC.exists(), f"{GP.name}, {RC.name}")
    if not (GP.exists() and RC.exists()):
        return report()
    gp = GP.read_text(encoding="utf-8")
    rc = RC.read_text(encoding="utf-8")

    # --- binding ---------------------------------------------------------------
    for label, text in (("gp", gp), ("rc", rc)):
        check(f"{label}-candidate-full-hash", CANDIDATE in text)
        check(f"{label}-wheel-hash", WHEEL in text)
        check(f"{label}-plan-hash", PLAN in text)
        check(f"{label}-actor-id", ACTOR in text)
        check(f"{label}-candidate-mentions>=8", text.count(CANDIDATE) + text.count("2e0894ea") >= 8,
              f"{text.count(CANDIDATE) + text.count('2e0894ea')}")
        check(f"{label}-version-marker", "v6" in text and "2026-08-15" in text)

    for pin in PINS:
        check(f"pin-{pin[:8]}", pin in gp or pin in rc, pin)

    # --- stale/incorrect content must be absent --------------------------------
    for pat in STALE_PATTERNS:
        for label, text in (("gp", gp), ("rc", rc)):
            m = re.search(pat, text)
            check(f"{label}-stale-absent:{pat[:36]}", m is None, m.group(0)[:80] if m else "")

    # --- stale candidate + forbidden reused identifiers -------------------------
    stale_candidate = "19ed0e6047f0a4d7650c47a7413ee0298a243f768a63bcb8fb9cffe44d140a1a"
    check("gp-stale-candidate-superseded", gp.count(stale_candidate) >= 1 and "supersede" in gp.lower())
    check("rc-stale-candidate-superseded", rc.count(stale_candidate) >= 1)
    forbidden_ids = ["task22_dm_rehearsal", "task26_synthetic_rehearsal", "task26_same_actor_rehearsal",
                     "cb_2NQV5sbkN-M6awycJH7X5g", "cb_6S6RABpDgZ165V02A7qEzw",
                     "cb_9yTz0oNwdU8s8hradru8BA", "cb_rmDnfrqA6gkmjoQEdwxu0g",
                     "wizard_995f04a3b8bc256fa13ff407", "3f44a18ea620d963"]
    for fid in forbidden_ids:
        check(f"gp-forbidden-id-{fid[:24]}", fid in gp)
    overlap = [f for f in forbidden_ids if f in rc]
    check("rc-forbidden-id-overlap-scan", not overlap, f"overlap: {overlap}")
    for word in ["<link>", "<token>", "<nonce>", "TBD", "placeholder", "FIXME"]:
        check(f"gp-no-placeholder-{word}", word not in gp)
        check(f"rc-no-placeholder-{word}", word not in rc)

    # --- structural -------------------------------------------------------------
    for label, text in (("gp", gp), ("rc", rc)):
        check(f"{label}-heading", text.lstrip().startswith("# "))
        check(f"{label}-has-verdict-language", "PASS" in text and "FAIL" in text)
        check(f"{label}-private-evidence-modes", "600" in text and "700" in text)
    check("gp-evidence-pins-verifier-exit0", "Exit `0`" in gp and "verifier-input.json" in gp)
    check("gp-deadlines-table", "120 seconds" in gp and "600 seconds" in gp and "90 seconds" in gp)
    check("rc-supported-command-table", "| Interface | Supported command shape |" in rc)
    check("rc-void-interface-section", "Void interfaces" in rc)
    check("rc-abort-matrix", "Abort decision matrix" in rc)

    # --- semantic deadlock / correctness checks ---------------------------------
    g8_line = next((ln for ln in gp.splitlines() if ln.startswith("| G8 |")), "")
    check("gp-g8-row-found", bool(g8_line))
    check("gp-g8-no-absence-demand", "absent" not in g8_line.lower())
    check("gp-g8-holder-check", "flock" in g8_line and "no active holder" in g8_line.lower())
    check("gp-g8-scope-check", "approved_clear_scopes" in g8_line)
    g15_line = next((ln for ln in gp.splitlines() if ln.startswith("| G15 |")), "")
    check("gp-g15-row-found", bool(g15_line))
    check("gp-g15-lock-absent", "absent" in g15_line.lower() and "gateway.lock" in g15_line)
    for label, text in (("gp", gp), ("rc", rc)):
        check(f"{label}-no-manual-lock-deletion", "manual `gateway.lock` deletion" in text
              and re.search(r"\brm [^\n]*gateway\.lock|delete (the )?lock file by hand", text) is None)
    check("rc-lock-disposal-rule", "Any other deletion of the lock file is forbidden" in rc
          and "sealed pre-reset" in rc)
    chain = ["Pre-reset arm", "Pre-reset execute and verify", "Invite harness `dry-run` and `verify`",
             "Exact-wheel deployment", "Observe before prepare/Start", "Lifecycle:",
             "Disable and service stop", "Cleanup execute and verify"]
    positions = [gp.find(c) for c in chain]
    check("gp-ordering-chain-complete", all(p > 0 for p in positions), f"positions: {positions}")
    check("gp-ordering-chain-ordered", positions == sorted(positions), f"positions: {positions}")
    check("gp-mode-order-cleanup", "`dry-run` -> `execute` -> `verify`" in rc)
    check("gp-approval-literal-phrase", "'TASK26_ARCHIVE_FIRST_PROFILE_RESET_APPROVED'" in gp)
    check("gp-cleanup-approval-phrase", "TASK26_POST_LIFECYCLE_ARCHIVE_CLEANUP_APPROVED" in gp and
          "TASK26_POST_LIFECYCLE_ARCHIVE_CLEANUP_APPROVED" in rc)
    check("gp-one-prepare", "invite_prepare_maximum` is 1" in gp or "one-prepare maximum" in rc)
    check("gp-one-start", "accepted_start_claim_maximum: 1" in gp)
    check("gp-one-delivery", "exactly one customer DM" in gp and "exactly one `adaptive_plan_delivered`" in gp)
    check("rc-no-recovery-shortcuts", "recovery shortcut" in rc)
    check("rc-expire-mode", "invite_harness_v2.py expire" in rc)
    check("gp-no-open-gates", [m.start() for m in re.finditer(r"\bOPEN\b", gp)] ==
          [gp.find("No gate is OPEN") + len("No gate is ")])
    check("gp-b3-closed", "(B3 closed)" in gp)
    check("gp-g12-closed", "(G12 closed)" in gp)
    check("gp-b6-closed", "(B6 closed)" in gp)
    check("gp-decision-complete", "No gate is OPEN" in gp)
    check("rc-matrix-decision-complete", "no row is blocked" in rc)
    check("gp-readiness-cli-correct", "checkin_cli.readiness_cli --profile-root" in gp and "--customer-key" in gp)
    check("gp-activate-cli-correct", "customer_admin --registry" in gp and "--checklist-evidence" in gp)
    check("gp-disable-cli-correct", "disable <customer_key>" in gp)
    check("gp-venv-correct", "/home/cube/projects/richard/hermes-agent/.venv" in gp)
    check("gp-unit-path-correct", "/home/cube/.config/systemd/user/hermes-gateway-dualcoachtest.service" in gp)
    check("gp-generation-event-driven", "no generation CLI" in gp)
    check("gp-hermes-home-binding", "HERMES_HOME=/home/cube/.hermes/profiles/dualcoachtest" in gp)
    check("gp-digest-domain-clarification", "different digest domain" in gp or "different domain" in gp)
    check("gp-pre-reset-command", "reset_controller.py' execute --profile" in gp.replace("\n", " "))
    check("gp-cleanup-commands", "cleanup_controller.py' dry-run" in gp and "cleanup_controller.py' verify" in gp)
    check("gp-harness-commands", '"$PY" -B "$D/invite_harness_v2.py" dry-run' in gp)

    # --- v4: path-existence checks (read-only, on disk) --------------------------
    for path, want_sha, want_mode in PATH_CHECKS:
        name = f"path-{path.name[:40]}"
        if not path.exists():
            check(name, False, "missing")
            continue
        st = path.lstat()
        ok = (not stat.S_ISLNK(st.st_mode)) and stat.S_ISREG(st.st_mode) and sha256_of(path) == want_sha
        if want_mode is not None:
            ok = ok and (stat.S_IMODE(st.st_mode) == want_mode)
        ok = ok and (st.st_uid == os.getuid())
        check(name, ok, f"mode {oct(stat.S_IMODE(st.st_mode))}")
    unit_text = (Path("/home/cube/.config/systemd/user/hermes-gateway-dualcoachtest.service")
                 .read_text(encoding="utf-8"))
    check("path-unit-execstart-profile", "--profile dualcoachtest" in unit_text)
    check("path-unit-hermes-home-exact", 'Environment="HERMES_HOME=/home/cube/.hermes/profiles/dualcoachtest"'
          in unit_text)
    check("path-unit-no-token-env", "TELEGRAM_BOT_TOKEN" not in unit_text)

    # --- v4: no-secret checks -----------------------------------------------------
    token_pattern = re.compile(r"\b\d{8,12}:[A-Za-z0-9_-]{35}\b")
    check("gp-no-secret-token-pattern", token_pattern.search(gp) is None)
    check("rc-no-secret-token-pattern", token_pattern.search(rc) is None)
    env_raw = Path("/home/cube/.hermes/profiles/dualcoachtest/.env").read_text(encoding="utf-8")
    m = re.findall(r"(?m)^\s*TELEGRAM_BOT_TOKEN\s*=\s*(\S+)\s*$", env_raw)
    check("env-exactly-one-token", len(m) == 1 and len(m[0]) == 46 and
          hashlib.sha256(m[0].encode()).hexdigest() ==
          "d0aacf0f4bdbb7c04e12769947c498ee869248972b36f099800a3d3c27c0a3a8")
    if m:
        check("gp-raw-token-absent", m[0] not in gp)
        check("rc-raw-token-absent", m[0] not in rc)
    for label, text in (("gp", gp), ("rc", rc)):
        bad_lines = [ln for ln in text.splitlines()
                     if "39664143" in ln and not any(k in ln.lower() for k in PROHIBITION_MARKERS)]
        check(f"{label}-no-unapproved-api-id", not bad_lines, f"{len(bad_lines)} lines")

    # --- v4: obsolete-path-negative checks ----------------------------------------
    for label, text in (("gp", gp), ("rc", rc)):
        bad = [ln for ln in text.splitlines()
               if any(n in ln for n in OBSOLETE_NEEDLES)
               and not any(k in ln.lower() for k in PROHIBITION_MARKERS)]
        check(f"{label}-obsolete-only-in-prohibition", not bad, f"{len(bad)} lines")
    check("gp-g14-prohibits-session-files", "never create, copy, restore, or require the obsolete session files"
          in gp)

    # --- v4: archive CLI checks -----------------------------------------------------
    exact_cli = ("verify_retained_archives.py .omo/evidence/task26/retained-archive-schema-inventory.json "
                 "--permission-seal .omo/evidence/task26/retained-archive-verifier-permission-seal.json")
    check("rc-archive-cli-exact", exact_cli in rc)
    check("rc-archive-cli-exitcodes", "exit 0 = PASS, exit 1 = FAIL" in rc)
    check("gp-archive-seal-pins", "28ff72fd" in gp and "12b97aa7" in gp and "29db87f2" in gp
          and "597a37e4" in gp)

    # --- v4: G14 predicate content --------------------------------------------------
    check("gp-g14-row-bot-api", "| G14 | Bot API authority" in gp)
    check("gp-g14-section-12", "### 1.2 Bot API authority predicates" in gp)
    check("gp-g14-token-proof", "exactly one nonempty `TELEGRAM_BOT_TOKEN`" in gp and "length 46" in gp)
    check("gp-g14-loaded-source-equality", "Loaded/source equality" in gp or "loaded/source equality" in gp)
    check("gp-g14-topic-zero", "topic_id `0`" in gp)
    check("gp-g14-review-topic-59", "topic_id `59`" in gp)
    check("gp-g14-five-modules", all(h in gp for h in [
        "a8191eed20ca75abcb03c33ac0c0b915cd810249466e816c6a1a55ba154af188",
        "f5d51008f1e8c8ee102df930fa68c7945a3276a249187adc7b7f6ba4b1e8e134",
        "b41060dea28eb3bbb83217068f5218d5df2c879dba00e73c9ca4e577a6049dad",
        "849791bc3b31145a691b8ef4defacba72aeb5de0fca12427988ec1ddd4e9fffa",
        "b48e2b49f0939316e7e4368c940492d1b8a94863da7e5c8141f7f67a1fe08efb"]))
    check("gp-g14-zero-obsolete-scan", "Zero obsolete-path references" in gp)
    check("rc-g14-matrix-row", "| Bot API authority G14 | all Golden Path Section 1.2 predicates hold |" in rc)
    check("rc-g14-prohibition", "never create, copy, restore, or require the obsolete session files" in rc)

    # --- v5 G13 sealed lifecycle observer checks -------------------------------
    observer_files_ok = OBSERVER_ROOT.is_dir() and (stat.S_IMODE(OBSERVER_ROOT.stat().st_mode) == 0o700)
    observed_modes: dict[str, str] = {}
    for name, (want_sha, want_mode) in OBSERVER_FILES.items():
        path = OBSERVER_ROOT / name
        if not path.exists():
            observer_files_ok = False
            continue
        info = path.lstat()
        observed_modes[name] = oct(stat.S_IMODE(info.st_mode))
        observer_files_ok = observer_files_ok and stat.S_ISREG(info.st_mode) and not stat.S_ISLNK(info.st_mode)
        observer_files_ok = observer_files_ok and info.st_uid == os.getuid() and info.st_nlink == 1
        observer_files_ok = observer_files_ok and stat.S_IMODE(info.st_mode) == want_mode and sha256_of(path) == want_sha
    check("g13-observer-files-exist-hash-mode", observer_files_ok, str(observed_modes))
    check("g13-root-exact", str(OBSERVER_ROOT.relative_to(REPO)) == ".omo/evidence/task26/task26-lifecycle-observer-st_01a005b4")
    check("g13-harness-pin", "58ed20c6f12887c10d6bf4fb388dded7c1eec9b30ec7f6bffa7c67e7e237b2c8" in gp)
    check("g13-permission-schema-manifest-verification-inventory", all(item in gp for item in [
        "68d98bca346296eacbe4c2cef9a105072314ba542728b043bbff319b6b19b466",
        "7d637a2587079fc94c28e2866d4f337e97fea102e50f37542299e9611c226489",
        "763aa448fc0220a237fe3a29ebfbd01c6d9ec2855c3db90ac12f157a7a0f2e0d",
        "b9bd1a93af4969692d23a3e4632825d0c72c02842732bad0447d9b8ce8ce3fdb",
        "6fe4e7375bf1ff51ca6fc0850f9adcb6d8baef2b9e6bbce8116ef1044a9ecf06",
    ]))
    check("g13-exact-commands-pin", "23e673309b4d7cdd1eed77ae8126e58100270f9e4d13ca1093b01c4b1667543e" in gp and
          "EXACT_COMMANDS.md" in gp)
    check("g13-three-modes", all(mode in gp for mode in ["subscribe-outbox", "audit-tail", "watch-deliveries"]))
    check("g13-arm-and-barrier-cli", "arm-only --profile" in gp and "multi-observer --profile" in gp and "--ready-fd" in gp)
    check("g13-exact-timeouts", "--timeout 120" in gp and "--timeout 600" in gp and "bounded by a monotonic timeout" in gp)
    check("g13-ready-before-trigger", "before any trigger" in gp and "status: READY" in gp and "names all three observers" in gp)
    check("g13-event-driven", "inotify" in gp and "event-driven" in gp and "transition.revision_after" in gp)
    check("g13-negative-no-sleep-poll", "never sleeps, polls" in gp and "no sleep, polling" in gp)
    check("g13-negative-no-telegram-cursor-drop-replay", all(item in gp for item in ["raw Telegram", "cursors", "drops updates", "replays events"]))
    check("g13-runtime-manifest-pins", all(item in gp for item in ["observer-manifest.json", "new session", "generation", "customer", "transaction", "draft", "delivery IDs"]))
    check("g13-receipts-private", "owner-held 0600 receipts" in gp and "0700 run evidence directory" in gp)
    check("g13-arm-only-unchanged", "live arm-only proof is PASS" in gp and "identical" in gp and "d128cb14884551bb9f21562c5111e3685ddb40c0bf023af85f7427855ee70d5c" in gp)
    check("g13-quality-results", "tests9" in gp and "Ruff" in gp and "LSP" in gp and "compile" in gp and "live-arm-only" in gp)
    check("g13-no-missing-watcher", "telegram_nutrition_onboarding_e2e.py" not in gp and "telegram_nutrition_onboarding_e2e.py" not in rc)
    check("g13-no-legacy-subscription-command", "scripts/telegram_nutrition_onboarding_e2e" not in gp and "scripts/telegram_nutrition_onboarding_e2e" not in rc)
    check("g13-rc-root-and-order", ".omo/evidence/task26/task26-lifecycle-observer-st_01a005b4" in rc and
          "multi-observer READY barrier before any trigger" in rc and "re-arm from `transition.revision_after`" in rc)
    check("g13-rc-negative-paths", all(item in rc for item in ["no sleep", "poll", "raw Telegram", "cursor", "drop", "replay"]))

    # --- v6 G12 sealed invite harness v2 and absent-ledger checks -------------
    invite_ok = INVITE_ROOT.is_dir() and stat.S_IMODE(INVITE_ROOT.stat().st_mode) == 0o700
    invite_modes: dict[str, str] = {}
    for name, (want_sha, want_mode) in INVITE_FILES.items():
        path = INVITE_ROOT / name
        if not path.exists():
            invite_ok = False
            continue
        info = path.lstat(); invite_modes[name] = oct(stat.S_IMODE(info.st_mode))
        invite_ok = invite_ok and stat.S_ISREG(info.st_mode) and not stat.S_ISLNK(info.st_mode)
        invite_ok = invite_ok and info.st_uid == os.getuid() and info.st_nlink == 1
        invite_ok = invite_ok and stat.S_IMODE(info.st_mode) == want_mode and sha256_of(path) == want_sha
    check("g12-v2-files-exist-hash-mode", invite_ok, str(invite_modes))
    check("g12-v2-root-exact", str(INVITE_ROOT.relative_to(REPO)) == ".omo/evidence/task26/task26-invite-harness-st_01a0056a-v2")
    check("g12-v2-pins", all(item in gp for item in [
        "83c95abb4b07f30fae0d996a7148c92b2db4600f6f3da2aadaf50a7ff62c71fc",
        "847548932b42210416fcfc302574e523778f12aecd2614ac28a95af6602c8dcc",
        "6a4249d263db04bea1432ad2393150ed77d3243a1365ed8dbe1eed5c0261df9e",
        "82099d8a3658a494c3cf13558e43fe7846e7e826cfcdec6c10515f05a697752d",
        "bb3ae896f8dd1a51d2f9df425f3afdc2bf72f73877163b6941a2bba2ee44b4ad",
        "c9a6bb0fea893d90956249264277481039a522021001495f07a2a28ec6280e0d",
        "ad6cd01cfb85aa4183cea5067fab4a507cbae2a8418223c4f160b9253cf388e0",
    ]))
    check("g12-v2-exact-operations", "OPERATIONS-v2.md" in gp and
          '"$PY" -B "$D/invite_harness_v2.py" dry-run' in gp and
          '"$PY" -B "$D/invite_harness_v2.py" verify' in gp and
          '"$PY" -B "$D/invite_harness_v2.py" prepare' in gp and
          '"$PY" -B "$D/invite_harness_v2.py" observe' in gp and
          '"$PY" -B "$D/invite_harness_v2.py" expire' in gp)
    check("g12-v2-absent-ledger-baseline", "RESET_BOUND_LEDGER_ABSENT" in gp and
          "bootstrap ledger and `data/onboarding` are absent" in gp)
    check("g12-v2-candidate-initializer", "RoomBootstrapStore.__init__" in gp and
          "candidate wheel's" in gp and "never manually initialize an empty ledger" in gp)
    check("g12-v2-stable-parent-race", "stable owner-held 0700 `data` parent" in gp and
          "remains absent at the watch boundary" in gp and "race" in rc)
    check("g12-v2-one-create", "exactly one candidate initializer creation" in gp and
          "second creation event" in gp)
    check("g12-v2-one-generation1-prepared", "exactly one generation `1`, state `PREPARED`" in gp and
          "exactly generation `1` and state `PREPARED`" in rc)
    check("g12-v2-no-manual-init", "never manually initialize an empty ledger" in gp and
          "manually initializes an empty ledger" in rc)
    check("g12-v2-quality", "14 harness tests PASS" in gp and "Ruff PASS" in gp and
          "compile PASS" in gp and "LSP errors 0" in gp and "10/10 PASS" in gp)
    check("g12-v2-live-dry-verify", "Both must report `PASS`" in gp and
          "`RESET_BOUND_LEDGER_ABSENT`" in gp and "`mutations: 0`" in gp and
          "`ready_for_one_invite: true`" in gp)
    check("g12-v2-live-profile-unchanged", "4ea2ee60dcc6725c3b8aeca2f4893aa73a7a137ec0d274b813d0383d97787e3e" in gp and
          "before and after" in gp)
    check("g12-v2-wheel-before-prepare", 0 < gp.find("Exact-wheel deployment") < gp.find("### 5.1") < gp.find("## 6. One invite preparation"))
    check("g12-v2-no-old-root", "task26-invite-harness-st_01a0056a/" not in gp and "task26-invite-harness-st_01a0056a/" not in rc)
    check("g12-v2-no-old-executable", "invite_harness.py" not in gp and "invite_harness.py" not in rc)
    check("g12-v2-rc-baseline", "RESET_BOUND_LEDGER_ABSENT" in rc and "Invite harness v2" in rc and
          "v2 root only" in rc)
    check("g12-v2-rc-negative", "no manual empty-ledger initialization" in rc and "stable `data` parent watch" in rc)

    # --- receipt (v6) ------------------------------------------------------------
    if RECEIPT.exists():
        try:
            data = json.loads(RECEIPT.read_text(encoding="utf-8"))
        except json.JSONDecodeError as exc:
            data = None
            check("receipt-valid-json", False, str(exc))
        if data is not None:
            check("receipt-valid-json", True)
            check("receipt-schema", data.get("schema") == "task26-runbook-binding-receipt-v6")
            check("receipt-candidate", data.get("candidate") == CANDIDATE)
            check("receipt-wheel", data.get("wheel_sha256") == WHEEL)
            check("receipt-plan", data.get("plan_sha256") == PLAN)
            check("receipt-actor", data.get("actor") == ACTOR)
            dumped = json.dumps(data)
            check("receipt-supersedes-v5", "3c6a0f24124142fc1b03c350a357d5530aa1a561c4b332e2e6c29a0de74c87e2" in dumped and
                  "e2fbff811e5b0caf4551010c1a47d640bba17febffb951f0525b6ff4dccd71d3" in dumped and
                  "2fb0019a8d9e46cc197f72685a89fc54ca5fb98a52840f41ade5d51f76d86736" in dumped)
            check("receipt-supersedes-v4", "e0979931fb863d957403f71305d33470697813c5e740622b6b05a23a65c76359" in dumped and
                  "f539460f91096cfe32140dd6d5b5a940ff38a13ad67bf6d83302f6732aa719a1" in dumped and
                  "2a00bb6d8103086e729ac5c36041d0e7d602a1c660c21eda20463ea8de1b78e7" in dumped)
            check("receipt-supersedes-v3", "ff230719eb5a69ae821bb0a4d58356d59b2803c490d692659a197e22083793c7" in dumped)
            check("receipt-supersedes-v2", "633d5c25ce70cb9ab5d19c434122b14b46fb7108962ab6e453ce6ab3ede1848f" in dumped)
            check("receipt-self-hash-present", bool(data.get("self_sha256")))
            check("receipt-verdict", data.get("verdict", {}).get("task_verdict") == "PASS")
            check("receipt-ready-token", data.get("verdict", {}).get("readiness_token") == "READY_FOR_ONE_INVITE_EXECUTION")
            check("receipt-no-open-blockers", not data.get("verdict", {}).get("open_blockers"))
    else:
        check("receipt-present", True, "skip: receipt written after lint")

    return report()


def report() -> int:
    failed = [r for r in results if not r["ok"]]
    for r in results:
        if not r["ok"]:
            print(f"FAIL {r['check']}: {r['detail']}")
    out = BASE / "lint-results-v6.json"
    out.write_text(json.dumps({"checks": results, "total": len(results),
                               "failed": len(failed)}, indent=2) + "\n", encoding="utf-8")
    print(f"{'PASS' if not failed else 'FAIL'} ({len(results) - len(failed)}/{len(results)} checks) -> {out.name}")
    return 0 if not failed else 1


if __name__ == "__main__":
    sys.exit(main())
