← Files MightShapeARCHIVED FILE

scripts/validate_reality_packet.py

5.93 KB · Sep 30, 2026 · 23:14 UTC

↓ Download file

#!/usr/bin/env python3
"""Validate Reality Packet shape, traceability, and grounding sufficiency."""

from __future__ import annotations

import argparse
import json
import sys
from typing import Any

from dc_core import DesignCouncilError, json_output, load_json, schema_validation


THRESHOLDS = {
    "FAST": {"sources": 1, "facts": 2, "workflows": 1, "key_detail": 1},
    "RESEARCHED": {"sources": 3, "facts": 5, "workflows": 2, "key_detail": 1},
    "DEEP": {"sources": 5, "facts": 10, "workflows": 3, "key_detail": 2},
}
KEY_FIELDS = (
    "responsibilities", "working_environment", "decision_rights", "terminology", "tools_and_systems",
    "dependencies", "organizational_relationships", "incentives", "constraints", "performance_pressures",
    "failure_modes", "workarounds", "common_variations", "cultural_context", "unresolved_questions", "local_variation",
)


def validate_reality_packet(packet: dict[str, Any], consequential: bool = False) -> dict[str, Any]:
    if not isinstance(packet, dict):
        raise DesignCouncilError("Reality Packet must be a JSON object")
    schema = schema_validation(packet, "reality-packet.schema.json")
    errors = list(schema["errors"])
    warnings: list[str] = []
    grounding = str(packet.get("grounding_level", "FAST")).upper()
    threshold = THRESHOLDS.get(grounding, THRESHOLDS["FAST"])
    sources = packet.get("sources", []) if isinstance(packet.get("sources"), list) else []
    facts = packet.get("supported_facts", []) if isinstance(packet.get("supported_facts"), list) else []
    source_ids = {item.get("id") for item in sources if isinstance(item, dict)}
    duplicate_ids = len(source_ids) != len(sources)
    if duplicate_ids:
        errors.append("sources: source IDs must be unique")
    for index, fact in enumerate(facts):
        for source_id in fact.get("source_ids", []) if isinstance(fact, dict) else []:
            if source_id not in source_ids:
                errors.append(f"supported_facts.{index}: unknown source ID {source_id}")
    for index, inference in enumerate(packet.get("research_supported_inferences", [])):
        for source_id in inference.get("based_on", []) if isinstance(inference, dict) else []:
            if source_id not in source_ids:
                errors.append(f"research_supported_inferences.{index}: unknown source ID {source_id}")
    normalized_claims = [str(item.get("claim", "")).strip().lower() for item in facts if isinstance(item, dict)]
    if len(normalized_claims) != len(set(normalized_claims)):
        warnings.append("Duplicate supported claims do not create independent grounding")
    if len(sources) < threshold["sources"]:
        errors.append(f"{grounding} grounding requires at least {threshold['sources']} source(s); found {len(sources)}")
    if len(facts) < threshold["facts"]:
        errors.append(f"{grounding} grounding requires at least {threshold['facts']} traceable fact(s); found {len(facts)}")
    if len(packet.get("workflows", [])) < threshold["workflows"]:
        errors.append(f"{grounding} grounding requires at least {threshold['workflows']} workflow detail(s)")
    for field in KEY_FIELDS:
        if len(packet.get(field, [])) < threshold["key_detail"]:
            errors.append(f"{field}: insufficient detail for {grounding} grounding")
    regulations = packet.get("regulations", [])
    if not regulations and packet.get("regulations_not_applicable") is not True:
        warnings.append("Regulations are empty without regulations_not_applicable=true; verify rather than assume none apply")
    authority = {item.get("authority_type") for item in sources if isinstance(item, dict)}
    authoritative_count = sum(1 for item in sources if item.get("authority_type") in {"primary", "official", "peer_reviewed", "professional_authority"})
    if grounding in {"RESEARCHED", "DEEP"} and authoritative_count < max(2, threshold["sources"] - 1):
        errors.append(f"{grounding} grounding requires multiple primary or authoritative sources")
    if len({item.get("publisher") for item in sources if isinstance(item, dict)}) < min(2, len(sources)) and len(sources) > 1:
        warnings.append("Most sources share one publisher; inspect source independence")
    if consequential and grounding == "FAST":
        errors.append("FAST grounding is insufficient for a consequential synthetic participant")
    if consequential and not packet.get("local_variation"):
        errors.append("Consequential grounding must identify local variation rather than imply universal practice")
    if not packet.get("unresolved_questions"):
        warnings.append("No unresolved questions are recorded; this may indicate false completeness")
    valid = not errors
    return {
        "valid": valid,
        "valid_for_persona": valid and (not consequential or grounding in {"RESEARCHED", "DEEP"}),
        "grounding_level": grounding,
        "consequential": consequential,
        "schema_validator": schema["validator"],
        "counts": {"sources": len(sources), "authoritative_sources": authoritative_count, "supported_facts": len(facts), "workflows": len(packet.get("workflows", []))},
        "errors": errors,
        "warnings": warnings,
        "unknowns_preserved": len(packet.get("unresolved_questions", [])),
        "local_variation_preserved": bool(packet.get("local_variation")),
    }


def main() -> int:
    parser = argparse.ArgumentParser(description="Validate a MightShape Reality Packet")
    parser.add_argument("input", nargs="?", help="Reality Packet JSON; stdin when omitted")
    parser.add_argument("--consequential", action="store_true")
    args = parser.parse_args()
    try:
        packet = load_json(args.input) if args.input else json.load(sys.stdin)
        result = validate_reality_packet(packet, args.consequential)
        json_output(result)
        return 0 if result["valid"] else 1
    except (DesignCouncilError, json.JSONDecodeError) as exc:
        print(f"MightShape error: {exc}", file=sys.stderr)
        return 2


if __name__ == "__main__":
    raise SystemExit(main())

SHA-256: cbc2c4d36ddd3d07abe77a03246dafdcc0faad636dd0881ca2d1cf581a9ff5d9