| name | event-provenance-hardening |
| description | Enforce strict source-boundary labeling in simulations that mix canon/source-grounded content with generated behavior |
| source | auto-skill |
| extracted_at | 2026-05-30T05:08:24.432Z |
| updated_at | 2026-05-30T05:33:00.000Z |
Event Provenance Hardening
When building simulations that reference sacred texts, historical sources, or any canonical material alongside AI-generated behavior, enforce a strict labeling system to prevent generated content from being mistaken for source truth.
Core Rule
CANON_ANCHOR is only allowed when source_anchor is non-null. All other events default to SIMULATION_EVENT.
Event Schema
Every event must include:
{
"label": "CANON_ANCHOR | INTERPRETIVE_BRIDGE | SIMULATION_EVENT",
"source_anchor": "string or null",
"canon_claim": "string or null",
"simulation_note": "string or null"
}
Labeling Rules
| Label | When to use | Required fields |
|---|
CANON_ANCHOR | Event directly corresponds to a documented source passage | source_anchor (non-null), canon_claim |
INTERPRETIVE_BRIDGE | Relates to canon themes but infers beyond explicit text | source_anchor (optional), simulation_note |
SIMULATION_EVENT | Default — all generated tick behavior, thoughts, minor actions | simulation_note |
Two-Layer Validation
Provenance is enforced at two points:
Layer 1: Provider Output Validation
Before provider output enters the simulation, validate it:
class ProviderOutputValidator:
def validate_and_parse(self, raw_output, provider_name, agent_name, tick):
parsed = try_parse_json(raw_output)
if parsed is None:
quarantine(provider_name, agent_name, tick, raw_output, "Invalid JSON")
return {"valid": False}
if not parsed.get("thought") or not parsed.get("action"):
quarantine(provider_name, agent_name, tick, raw_output, "Missing fields")
return {"valid": False}
if parsed.get("label") == "CANON_ANCHOR" and not parsed.get("source_anchor"):
quarantine(provider_name, agent_name, tick, raw_output, "Boundary violation")
parsed["label"] = "SIMULATION_EVENT"
return {"valid": True, **parsed}
Layer 2: Event Log Validation
When events are appended to the log:
def _validate(self, event: dict) -> bool:
if event.get("label") == "CANON_ANCHOR" and not event.get("source_anchor"):
event["quarantine_reason"] = "CANON_ANCHOR without source_anchor"
return False
return True
Quarantine Files
| File | Contents |
|---|
data/quarantine_events.jsonl | Events with invalid provenance |
data/quarantine_provider_outputs.jsonl | Invalid provider responses (bad JSON, missing fields, boundary violations) |
Both files should exist (even if empty) so tests can check known paths. During normal mock operation, both should stay empty.
Implementation Pattern
def _classify_event(self, action_text: str) -> tuple[str, str | None, str | None, str | None]:
"""Return (label, source_anchor, canon_claim, simulation_note)."""
canon_ref = self.scripture.get_canon_reference(action_text)
if canon_ref and keyword_matches(action_text, canon_ref, threshold=3):
return CANON_ANCHOR, canon_ref["reference"], canon_ref["summary"], None
elif canon_ref:
return INTERPRETIVE_BRIDGE, canon_ref["reference"], canon_ref["summary"], "Relates to canon but not direct match."
return SIMULATION_EVENT, None, None, "Generated tick behavior — not grounded in source text."
Key Lessons from Genesis Kernel
- Default to SIMULATION_EVENT — routine generated actions (walking, tending, observing) should NOT be CANON_ANCHOR even if they use canon-adjacent keywords
- Require 3+ keyword matches for CANON_ANCHOR classification — prevents loose keyword matching from over-classifying
- Two-layer validation — validate provider output BEFORE it enters the simulation, AND validate events BEFORE they enter the log
- Quarantine, don't crash — invalid events and provider outputs go to separate files for audit; the simulation continues
- UI must show provenance — event cards display Source reference and Type (Canon/Interpretation/Simulation) visibly
- Fallback actions must avoid canon keywords — generic fallback text ("walks forward", "observes surroundings") prevents accidental CANON_ANCHOR classification
- Separate the three layers in code —
scripture_source.py holds canon references, consequence_engine.py classifies, event_log.py validates, providers/validation.py sanitizes provider output
Anti-Patterns
- ❌ Labeling generated tick actions as CANON_ANCHOR without a source reference
- ❌ Removing labels from output
- ❌ Letting agents or providers declare new scripture
- ❌ Using canon-adjacent keywords in fallback actions (triggers misclassification)
- ✅ Always default to SIMULATION_EVENT when uncertain
- ✅ Verify CANON_ANCHOR events against the source reference registry
- ✅ Quarantine invalid provider outputs with reason codes
- ✅ Override provider-forced CANON_ANCHOR to SIMULATION_EVENT