#!/usr/bin/env python3
import hashlib, json

REQUIRED = {"referent_id", "version", "contract_version", "caller_mode", "capture_at", "evaluation_window", "provenance", "payload", "shape_hash"}
BOUND_SHAPE = REQUIRED

def shape_of(packet):
    return sorted(packet.keys())
def digest_shape(packet):
    # Hash the declared envelope shape excluding the hash field itself (avoids circularity).
    return hashlib.sha256(json.dumps([k for k in shape_of(packet) if k != "shape_hash"], separators=(",", ":")).encode()).hexdigest()
def gate(packet):
    missing = REQUIRED - packet.keys()
    if missing: return "REFUSE_MISSING_FIELDS:" + ",".join(sorted(missing))
    if packet["provenance"].get("status") == "INHERITED_NOT_FACT" and packet.get("caller_mode") != "inherited":
        return "REFUSE_INHERITED_STATUS"
    if packet["shape_hash"] != digest_shape(packet): return "REFUSE_HASH_MISMATCH"
    if set(packet.keys()) != BOUND_SHAPE:
        return "REFUSE_SCHEMA_SHAPE_DRIFT"
    return "RESUME_VALID"

def base():
    p = {"referent_id":"owner/continuity/42", "version":7, "contract_version":"continuity-v2", "caller_mode":"first_person", "capture_at":"2026-09-15T02:30:00Z", "evaluation_window":{"start":"2026-09-15T02:00:00Z","end":"2026-09-15T02:30:00Z"}, "provenance":{"status":"OWNER_ISSUED","locator":"owner://continuity/42"}, "payload":{"next_step":"resume"}}
    p["shape_hash"] = digest_shape(p)
    return p

def main():
    cases = []
    p = base(); cases.append(("valid", p, "RESUME_VALID"))
    p = base(); p.pop("evaluation_window"); p["shape_hash"] = digest_shape(p); cases.append(("same_version_removed_required_field", p, "REFUSE_MISSING_FIELDS:evaluation_window"))
    p = base(); p["unexpected_semantic_field"] = "changed"; p["shape_hash"] = digest_shape(p); cases.append(("same_version_extra_field", p, "REFUSE_SCHEMA_SHAPE_DRIFT"))
    p = base(); p["payload"] = ["changed-type"]; p["shape_hash"] = digest_shape(p); cases.append(("same_version_changed_payload_type", p, "RESUME_VALID"))
    for name, packet, expected in cases:
        got = gate(packet)
        if got != expected: raise AssertionError(f"{name}: expected {expected}, got {got}")
        print(f"PASS {name}: {got}")
    print("RESULT PASS: same-version envelope-shape mutation is refused; payload type policy is explicitly open")

if __name__ == "__main__": main()
