← Files MightShapeARCHIVED FILE
scripts/create_persona.py
6.13 KB · Sep 30, 2026 · 23:14 UTC
#!/usr/bin/env python3
"""Assemble a synthetic participant only from a validated Reality Packet."""
from __future__ import annotations
import argparse
import json
import sys
from typing import Any
from dc_core import DesignCouncilError, json_output, load_json, now_utc, schema_validation
from validate_reality_packet import validate_reality_packet
TYPES = {"SYNTHETIC_USER", "SYNTHETIC_PRACTITIONER", "SYNTHETIC_EXPERT"}
def create_persona(spec: dict[str, Any], consequential: bool = True) -> dict[str, Any]:
if not isinstance(spec, dict):
raise DesignCouncilError("persona specification must be an object")
packet = spec.get("reality_packet")
if not isinstance(packet, dict):
raise DesignCouncilError("create_persona requires the complete reality_packet object")
packet_result = validate_reality_packet(packet, consequential=consequential)
if not packet_result["valid_for_persona"]:
raise DesignCouncilError("Reality Packet is insufficient: " + "; ".join(packet_result["errors"]))
participant_type = str(spec.get("participant_type", "")).upper()
if participant_type not in TYPES:
raise DesignCouncilError(f"participant_type must be one of {sorted(TYPES)}")
human_model = spec.get("human_model")
if not isinstance(human_model, dict):
raise DesignCouncilError("A full human_model is required; this script will not generate a shallow role stereotype")
model_validation = schema_validation(human_model, "human-model.schema.json")
if not model_validation["valid"]:
raise DesignCouncilError("Human Model is invalid: " + "; ".join(model_validation["errors"]))
variations = spec.get("variation_dimensions", {})
if not isinstance(variations, dict) or len(variations) < 2:
raise DesignCouncilError("variation_dimensions requires at least two behaviorally meaningful dimensions")
superficial = {"race", "ethnicity", "gender", "name", "age"}
if set(str(key).lower() for key in variations) <= superficial:
raise DesignCouncilError("variation must include behaviorally meaningful dimensions, not demographics alone")
facts = packet.get("supported_facts", [])
layers = spec.get("epistemic_layers", {})
domain_grounding = layers.get("DOMAIN_GROUNDING") if isinstance(layers, dict) else None
if domain_grounding is None:
domain_grounding = [{"detail": item["claim"], "source_ids": item["source_ids"]} for item in facts]
if not isinstance(domain_grounding, list) or not domain_grounding:
raise DesignCouncilError("DOMAIN_GROUNDING must contain traceable details")
valid_source_ids = {item["id"] for item in packet.get("sources", [])}
for index, detail in enumerate(domain_grounding):
if not isinstance(detail, dict) or not str(detail.get("detail", "")).strip():
raise DesignCouncilError(f"DOMAIN_GROUNDING item {index} requires a detail")
source_ids = detail.get("source_ids", [])
if not isinstance(source_ids, list) or not source_ids:
raise DesignCouncilError(f"DOMAIN_GROUNDING item {index} requires source_ids")
unknown_sources = sorted(set(source_ids) - valid_source_ids)
if unknown_sources:
raise DesignCouncilError(f"DOMAIN_GROUNDING item {index} references unknown source(s): {', '.join(unknown_sources)}")
inference = layers.get("REASONABLE_INFERENCE", packet.get("research_supported_inferences", [])) if isinstance(layers, dict) else packet.get("research_supported_inferences", [])
inference = [item.get("claim", "") if isinstance(item, dict) else str(item) for item in inference]
continuity = spec.get("constructed_continuity", layers.get("CONSTRUCTED_CONTINUITY", []) if isinstance(layers, dict) else [])
unknowns = layers.get("UNKNOWN", packet.get("unresolved_questions", [])) if isinstance(layers, dict) else packet.get("unresolved_questions", [])
if not isinstance(continuity, list):
raise DesignCouncilError("constructed_continuity must be an array")
supplied_limitations = spec.get("limitations", [])
if not isinstance(supplied_limitations, list) or not all(isinstance(item, str) for item in supplied_limitations):
raise DesignCouncilError("limitations must be an array of strings")
limitations = list(dict.fromkeys(supplied_limitations + [
"Cannot infer prevalence or population statistics from simulated experience.",
"Cannot know site-specific practice beyond the Reality Packet.",
"Must label intuition and defer outside the modeled knowledge boundary.",
"Constructed continuity is fictional and must never be promoted to evidence.",
]))
persona = {
"id": spec.get("id", "SP-001"),
"participant_type": participant_type,
"provenance": participant_type,
"reality_packet_id": packet["id"],
"variation_dimensions": variations,
"human_model": human_model,
"epistemic_layers": {
"DOMAIN_GROUNDING": domain_grounding,
"REASONABLE_INFERENCE": inference,
"CONSTRUCTED_CONTINUITY": [str(item) for item in continuity],
"UNKNOWN": [str(item) for item in unknowns],
},
"limitations": limitations,
"created_at": spec.get("created_at", now_utc()),
}
validation = schema_validation(persona, "synthetic-persona.schema.json")
if not validation["valid"]:
raise DesignCouncilError("Synthetic persona failed validation: " + "; ".join(validation["errors"]))
return persona
def main() -> int:
parser = argparse.ArgumentParser(description="Create a research-grounded synthetic participant")
parser.add_argument("input", nargs="?", help="Persona specification JSON; stdin when omitted")
parser.add_argument("--low-consequence", action="store_true", help="Allow valid FAST grounding")
args = parser.parse_args()
try:
spec = load_json(args.input) if args.input else json.load(sys.stdin)
json_output(create_persona(spec, consequential=not args.low_consequence))
except (DesignCouncilError, json.JSONDecodeError) as exc:
print(f"MightShape error: {exc}", file=sys.stderr)
return 2
return 0
if __name__ == "__main__":
raise SystemExit(main())
SHA-256: fb629294078411359768cc97b6db66ba28935d93cf3b110ab2117b0abfcd18fa