feat(TASK-I): PL0 passive Experience Ledger — 10 seed Process Experiences #322

Merged
conductor merged 1 commits from pl0-experience-ledger into master 2026-09-10 02:25:02 +01:00
2 changed files with 112 additions and 0 deletions

No files matched your search

+10
View File
@@ -0,0 +1,10 @@
{"experience_id": "PE-1789003210-0001", "task_run_stage": "OUTWARD/164", "result_type": "FAILURE", "symptom": "Worker wrote schema-variant artifact (search_receipts/sources at top level instead of discovery/reading subsections); gate rejected with '0 sources'", "evidence_references": ["research/issue-164/outward.json (pre-normalization)", "stage_OUTWARD.events.jsonl"], "root_cause_layer": "SCHEMA", "confidence": 0.9, "human_intervention": false, "pipeline_sha": "2ef2d55", "correction_applied": "Schema-variant normalization in _normalize_outward_artifact", "recurrence_class": "worker-output-format", "wall_cost_s": 415, "model_cost_s": 0, "tool_cost_s": 0, "quality_outcome": "", "reusable_lesson": true, "candidate_status": "SEED", "recorded_at": "2026-09-10T01:20:10.388757+00:00"}
{"experience_id": "PE-1789003210-0002", "task_run_stage": "OUTWARD/164", "result_type": "FAILURE", "symptom": "Worker used 'urls' (list) instead of 'url' (string) and 'classification' instead of 'source_class'", "evidence_references": ["research/issue-164/outward.json source entries"], "root_cause_layer": "SCHEMA", "confidence": 0.95, "human_intervention": false, "pipeline_sha": "2ef2d55", "correction_applied": "Compatibility aliases: urls\u2192url, classification\u2192source_class", "recurrence_class": "field-naming", "wall_cost_s": 0, "model_cost_s": 0, "tool_cost_s": 0, "quality_outcome": "", "reusable_lesson": true, "candidate_status": "SEED", "recorded_at": "2026-09-10T01:20:10.388913+00:00"}
{"experience_id": "PE-1789003210-0003", "task_run_stage": "ADVERSARIAL/164", "result_type": "FAILURE", "symptom": "Worker omitted the mandatory disposition field; pipeline raised ValueError on advance", "evidence_references": ["research/issue-164/adversarial.json (disposition: None)"], "root_cause_layer": "PROMPT_CONTRACT", "confidence": 0.9, "human_intervention": false, "pipeline_sha": "2ef2d55", "correction_applied": "ADVERSARIAL prompt now lists disposition as first required key", "recurrence_class": "missing-mandatory-field", "wall_cost_s": 300, "model_cost_s": 0, "tool_cost_s": 0, "quality_outcome": "", "reusable_lesson": true, "candidate_status": "SEED", "recorded_at": "2026-09-10T01:20:10.389003+00:00"}
{"experience_id": "PE-1789003210-0004", "task_run_stage": "ADVERSARIAL/164", "result_type": "INEFFICIENCY", "symptom": "Attack with dimension 'duplicate_authority / Rune constraint violations' addressed 2 dimensions but the gate only counted 1 (break-after-first-match)", "evidence_references": ["scripts/research_stage_executor.py _validate_adversarial"], "root_cause_layer": "VALIDATOR", "confidence": 0.9, "human_intervention": false, "pipeline_sha": "2ef2d55", "correction_applied": "Multi-dimension matching (removed break)", "recurrence_class": "validator-semantics", "wall_cost_s": 0, "model_cost_s": 0, "tool_cost_s": 0, "quality_outcome": "", "reusable_lesson": true, "candidate_status": "SEED", "recorded_at": "2026-09-10T01:20:10.389077+00:00"}
{"experience_id": "PE-1789003210-0005", "task_run_stage": "SYNTHESIS/164", "result_type": "SUCCESS", "symptom": "Gate correctly rejected synthesis v1 for missing binding mitigation M1 (integration surface not decided)", "evidence_references": ["research/issue-164/synthesis.json (no 'integration surface' in invariants)"], "root_cause_layer": "VALIDATOR", "confidence": 1.0, "human_intervention": false, "pipeline_sha": "2ef2d55", "correction_applied": "Worker must address M1 in synthesis v2", "recurrence_class": "mitigation-enforcement", "wall_cost_s": 0, "model_cost_s": 0, "tool_cost_s": 0, "quality_outcome": "gate blocked incomplete design", "reusable_lesson": true, "candidate_status": "SEED", "recorded_at": "2026-09-10T01:20:10.389163+00:00"}
{"experience_id": "PE-1789003210-0006", "task_run_stage": "APPLICABILITY/164", "result_type": "SUCCESS", "symptom": "Route STANDARD_SOLUTION_SUFFICIENT correctly determined \u2014 INNOVATION skipped on merit (existing warm-neuron propagation sufficient)", "evidence_references": ["research/issue-164/applicability.json"], "root_cause_layer": "STAGE_ARCHITECTURE", "confidence": 0.95, "human_intervention": false, "pipeline_sha": "2ef2d55", "correction_applied": "", "recurrence_class": "conditional-routing", "wall_cost_s": 0, "model_cost_s": 0, "tool_cost_s": 0, "quality_outcome": "correct skip", "reusable_lesson": true, "candidate_status": "SEED", "recorded_at": "2026-09-10T01:20:10.389244+00:00"}
{"experience_id": "PE-1789003210-0007", "task_run_stage": "OUTWARD/164", "result_type": "SUCCESS", "symptom": "Real external HippoRAG 2 evidence found (13 sources, 6 read from arxiv.org) that grounded the evaluation", "evidence_references": ["research/issue-164/outward.json"], "root_cause_layer": "MODEL_BEHAVIOR", "confidence": 0.95, "human_intervention": false, "pipeline_sha": "2ef2d55", "correction_applied": "", "recurrence_class": "external-research", "wall_cost_s": 415, "model_cost_s": 0, "tool_cost_s": 0, "quality_outcome": "", "reusable_lesson": true, "candidate_status": "SEED", "recorded_at": "2026-09-10T01:20:10.389330+00:00"}
{"experience_id": "PE-1789003210-0008", "task_run_stage": "OUTWARD/B1-v2", "result_type": "INEFFICIENCY", "symptom": "384s cognition on a LOCAL_FIX-class task with zero external evidence surface; worker even attempted to fix the bug during research", "evidence_references": ["research/issue-314/telemetry/attempts.jsonl", "#310 contamination record"], "root_cause_layer": "ROUTING", "confidence": 1.0, "human_intervention": false, "pipeline_sha": "2ef2d55", "correction_applied": "D1 disposition: LOCAL_FIX and STANDARD_CHANGE bypass OUTWARD (ADAPT-2/3/4)", "recurrence_class": "fixed-depth-routing", "wall_cost_s": 384, "model_cost_s": 0, "tool_cost_s": 0, "quality_outcome": "", "reusable_lesson": true, "candidate_status": "SEED", "recorded_at": "2026-09-10T01:20:10.389413+00:00"}
{"experience_id": "PE-1789003210-0009", "task_run_stage": "D5/mutation_boundary", "result_type": "FAILURE", "symptom": "Porcelain-line comparison couldn't detect mutation of an already-dirty file", "evidence_references": ["tests/test_adapt9_artifact_authority.py fingerprint test"], "root_cause_layer": "DETERMINISTIC_RUNTIME", "confidence": 1.0, "human_intervention": false, "pipeline_sha": "2ef2d55", "correction_applied": "D5.1: content fingerprints replace porcelain comparison", "recurrence_class": "detection-gap", "wall_cost_s": 0, "model_cost_s": 0, "tool_cost_s": 0, "quality_outcome": "", "reusable_lesson": true, "candidate_status": "SEED", "recorded_at": "2026-09-10T01:20:10.389500+00:00"}
{"experience_id": "PE-1789003210-0010", "task_run_stage": "ADVERSARIAL+OUTWARD/contracts", "result_type": "FAILURE", "symptom": "Stage-output requirements defined in two places (template + inline prompt) that drifted independently", "evidence_references": ["prompts/research-stages/adversarial.md (had disposition)", "scripts/research_stage_executor.py pp list (lacked disposition)"], "root_cause_layer": "STAGE_ARCHITECTURE", "confidence": 1.0, "human_intervention": false, "pipeline_sha": "2ef2d55", "correction_applied": "TASK D: single-source stage_contracts.py \u2014 prompts, validators, aliases all derive from one registry", "recurrence_class": "duplicate-authority", "wall_cost_s": 0, "model_cost_s": 0, "tool_cost_s": 0, "quality_outcome": "", "reusable_lesson": true, "candidate_status": "SEED", "recorded_at": "2026-09-10T01:20:10.389590+00:00"}
+102
View File
@@ -0,0 +1,102 @@
#!/usr/bin/env python3
"""process_experience.py — PL0 passive Experience Ledger (TASK I, epic #284).
Observation and evidence capture only. No autonomous self-editing.
Records Process Experience entries from pipeline runs: failures, successes,
inefficiencies — with root-cause attribution, evidence references, and
confidence. These are EVIDENCE RECORDS, not permanent rules.
Root-cause layers:
MODEL_BEHAVIOR — the model did something unexpected
PROMPT_CONTRACT — the prompt didn't communicate requirements clearly
SCHEMA — field names/structure mismatch
NORMALIZER — normalization was missing or incorrect
TOOL_INTERFACE — tool behavior differed from expectation
ARTIFACT_ASSEMBLY — artifact file composition issue
VALIDATOR — gate checked the wrong thing or missed something
ROUTING — the stage shouldn't have run for this task class
STAGE_ARCHITECTURE — the stage design itself is the problem
DETERMINISTIC_RUNTIME — deterministic code issue (not model)
EXTERNAL_DEPENDENCY — external service behaved unexpectedly
UNKNOWN — attribution uncertain
"""
from __future__ import annotations
import json
import os
import time
from datetime import datetime, timezone
ROOT_CAUSE_LAYERS = {
"MODEL_BEHAVIOR", "PROMPT_CONTRACT", "SCHEMA", "NORMALIZER",
"TOOL_INTERFACE", "ARTIFACT_ASSEMBLY", "VALIDATOR", "ROUTING",
"STAGE_ARCHITECTURE", "DETERMINISTIC_RUNTIME", "EXTERNAL_DEPENDENCY",
"UNKNOWN",
}
RESULT_TYPES = {"SUCCESS", "FAILURE", "INEFFICIENCY"}
CANDIDATE_STATUSES = {"SEED", "EVALUATED", "PROMOTED", "REJECTED"}
class ExperienceLedger:
"""Append-only ledger of Process Experience records."""
def __init__(self, path: str):
self.path = path
os.makedirs(os.path.dirname(path), exist_ok=True)
def record(self, task_run_stage: str, result_type: str, symptom: str,
evidence: list[str], root_cause: str, confidence: float,
human_intervention: bool = False, pipeline_sha: str = "UNKNOWN",
correction_applied: str = "", recurrence_class: str = "",
wall_cost_s: float | str = 0, model_cost_s: float | str = 0,
tool_cost_s: float | str = 0, quality_outcome: str = "",
reusable: bool = False, candidate_status: str = "SEED") -> dict:
assert result_type in RESULT_TYPES, f"invalid result_type: {result_type}"
assert root_cause in ROOT_CAUSE_LAYERS, f"invalid root_cause: {root_cause}"
assert candidate_status in CANDIDATE_STATUSES, f"invalid status: {candidate_status}"
entry = {
"experience_id": f"PE-{int(time.time())}-{len(self.load_all()) + 1:04d}",
"task_run_stage": task_run_stage,
"result_type": result_type,
"symptom": symptom,
"evidence_references": evidence,
"root_cause_layer": root_cause,
"confidence": confidence,
"human_intervention": human_intervention,
"pipeline_sha": pipeline_sha,
"correction_applied": correction_applied,
"recurrence_class": recurrence_class,
"wall_cost_s": wall_cost_s,
"model_cost_s": model_cost_s,
"tool_cost_s": tool_cost_s,
"quality_outcome": quality_outcome,
"reusable_lesson": reusable,
"candidate_status": candidate_status,
"recorded_at": datetime.now(timezone.utc).isoformat(),
}
with open(self.path, "a") as f:
f.write(json.dumps(entry) + "\n")
return entry
def load_all(self) -> list[dict]:
if not os.path.exists(self.path):
return []
out = []
with open(self.path) as f:
for line in f:
try: out.append(json.loads(line))
except json.JSONDecodeError: continue
return out
def by_root_cause(self, layer: str) -> list[dict]:
return [e for e in self.load_all() if e["root_cause_layer"] == layer]
def successes(self) -> list[dict]:
return [e for e in self.load_all() if e["result_type"] == "SUCCESS"]
def failures(self) -> list[dict]:
return [e for e in self.load_all() if e["result_type"] == "FAILURE"]
def reusable_lessons(self) -> list[dict]:
return [e for e in self.load_all() if e["reusable_lesson"]]