#!/usr/bin/env python3
"""Independent mutation probe against governance/validate_orgdef.py.

Filed as evidence for the independent strategist review of the drafter's
response to findings F1-F6, memo
  memos/2026-11-07-1810--thingalog-strategist--principal--review-of-drafter-response
pinned to commit c09c404f1f65fcf745c6723ab3ca78046242012b.

This is EVIDENCE, not a gate. It is deliberately not placed under
governance/tests/ and is not collected by `unittest discover`, so it cannot
turn CI red. Whether the survivors below become enforced guards is finding
R1, and that is the principal's disposition to make.

Each case removes something the response memo claims as a fix and asks the
drafter's own validate() whether it notices.

    KILLED   - a guard fired; the claimed fix is enforced.
    SURVIVED - the validator accepts the file without it; the claimed fix is
               present in the artifact but nothing prevents its removal.

Run from the repository root:

    python governance/reviews/mutation_probe_2026-11-07.py

Exit code is 0 regardless of outcome; read the table.
"""
import copy
import json
import sys
from pathlib import Path

GOV = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(GOV))

from validate_orgdef import ValidationError, validate  # noqa: E402

BASE = json.loads((GOV / "orgdef.json").read_text(encoding="utf-8"))


def drop_validates_for(src, dst):
    def mutate(org):
        org["relationships"] = [
            e for e in org["relationships"]
            if not (e.get("type") == "validates_for"
                    and e.get("from") == src and e.get("to") == dst)
        ]
    return mutate


CASES = [
    ("R1/F4", "delete limitations[] entirely",
     "limitations[] added to the canonical orgdef",
     lambda o: o.pop("limitations", None)),

    ("R1/F4", "blank every limitation string",
     "limitations[] added to the canonical orgdef",
     lambda o: o.update(limitations=["  "] * 4)),

    ("R1/F6", "delete publication_policy",
     "publication_policy with authorship classes and handling constraints",
     lambda o: o.pop("publication_policy", None)),

    ("R1/F6", "empty publication_policy.authorship_classes",
     "authorship classes recorded for authorial-integrity",
     lambda o: o.get("publication_policy", {}).update(authorship_classes=[])),

    ("R1", "delete the top-level records block",
     "record contracts documented",
     lambda o: o.pop("records", None)),

    ("F5", "drop labs-qa validates_for labs-release",
     "labs-qa validates_for labs-release for release scrutiny",
     drop_validates_for("labs-qa", "labs-release")),

    ("F5", "drop institute-reviewer validates_for labs-implementer",
     "institute-reviewer validates_for labs-implementer",
     drop_validates_for("institute-reviewer", "labs-implementer")),
]


def probe(mutate):
    org = copy.deepcopy(BASE)
    mutate(org)
    try:
        validate(org, GOV)
    except ValidationError as exc:
        return "KILLED", str(exc)
    except Exception as exc:                       # noqa: BLE001 - report, don't mask
        return "ERROR", f"{type(exc).__name__}: {exc}"
    return "SURVIVED", "validator accepts the file without it"


def main():
    print(f"orgdef {BASE.get('version')} / status {BASE.get('status')}")
    print(f"baseline: {validate(copy.deepcopy(BASE), GOV)}\n")

    killed = survived = errored = 0
    for finding, label, claim, mutate in CASES:
        verdict, detail = probe(mutate)
        killed += verdict == "KILLED"
        survived += verdict == "SURVIVED"
        errored += verdict == "ERROR"
        print(f"{verdict:<9}[{finding}] {label}")
        print(f"          claimed: {claim}")
        print(f"          {detail}\n")

    print(f"{killed} killed, {survived} survived, {errored} errored, "
          f"{len(CASES)} total")
    if survived:
        print("\nEvery survivor is a field present in the artifact that nothing "
              "prevents being removed or blanked. See finding R1.")


if __name__ == "__main__":
    main()
