the-mindscape-of-bro-tsong/scripts/init_round05_context.py

297 lines
11 KiB
Python

import json
import sys
from datetime import date
from pathlib import Path
OUTPUT_RELATIVE_PATH = (
Path("ccra_review_bundle")
/ "round-05_init_selector-calibration-policy"
/ "00_ROUND05_NEW_SESSION_CONTEXT.md"
)
REQUIRED_SOURCE_FILES = [
"reports/Round04_closeout_note_2026-06-18.md",
"reports/Round04_blind_routing_evaluation_report_2026-06-18.md",
"reports/Round04_1_post_patch_routing_verification_report_2026-06-18.md",
"selector/round04_blind_inputs.json",
"selector/selector_rules.json",
"selector/selector_calibration_inputs.json",
"tests/qpi.regression.json",
"tests/intellectual_archaeology.regression.json",
"tests/regression_cases.json",
"docs/DECISIONS.md",
"docs/WORKFLOW.md",
]
SECTION_TITLES = [
"Project State",
"Round 04 / 04.1 Closure Summary",
"Round 05: Selector Calibration Policy Review",
"Round 05 Core Questions",
"Files To Read in New Conversation",
"Round 05 Non-Goals",
"Suggested Round 05 Deliverables",
"Missing Files",
]
MODEL_FILES = {
"QPI": Path("models") / "qpi.model.json",
"Intellectual Archaeology": Path("models") / "intellectual_archaeology.model.json",
}
REGRESSION_FILES = {
"QPI": Path("tests") / "qpi.regression.json",
"Intellectual Archaeology": Path("tests") / "intellectual_archaeology.regression.json",
"Aggregate": Path("tests") / "regression_cases.json",
}
def read_json(path):
return json.loads(path.read_text(encoding="utf-8"))
def collect_missing_files(root):
return [
rel_path
for rel_path in REQUIRED_SOURCE_FILES
if not (root / Path(rel_path)).is_file()
]
def collect_model_states(root):
states = []
for display_name, rel_path in MODEL_FILES.items():
path = root / rel_path
if not path.is_file():
states.append({
"display_name": display_name,
"status": "missing",
"stability_level": "missing",
"regression_status": "missing",
})
continue
data = read_json(path)
states.append({
"display_name": display_name,
"status": data.get("status", "unknown"),
"stability_level": data.get("stability_profile", {}).get("stability_level", "unknown"),
"regression_status": data.get("regression_status", "unknown"),
})
return states
def collect_regression_counts(root):
counts = {}
for display_name, rel_path in REGRESSION_FILES.items():
path = root / rel_path
if not path.is_file():
counts[display_name] = "missing"
continue
data = read_json(path)
counts[display_name] = len(data.get("regression_cases", []))
return counts
def collect_round04_evidence(root):
closeout = root / "reports" / "Round04_closeout_note_2026-06-18.md"
post_patch = root / "reports" / "Round04_1_post_patch_routing_verification_report_2026-06-18.md"
evidence = {
"closeout_note_present": closeout.is_file(),
"post_patch_report_present": post_patch.is_file(),
"targeted_cases_checked": "unknown",
"targeted_failures": "unknown",
"behavior_changes_recorded": "unknown",
"r04_bi_022_recorded": False,
}
if post_patch.is_file():
text = post_patch.read_text(encoding="utf-8")
evidence["targeted_cases_checked"] = extract_report_value(text, "Targeted cases checked")
evidence["targeted_failures"] = extract_report_value(text, "Targeted failures")
evidence["behavior_changes_recorded"] = extract_report_value(text, "Behavior changes recorded")
evidence["r04_bi_022_recorded"] = "R04-BI-022" in text
return evidence
def extract_report_value(text, label):
prefix = f"{label}: "
for line in text.splitlines():
if line.startswith(prefix):
return line[len(prefix):].strip()
return "unknown"
def build_markdown_list(items):
return "\n".join(f"- {item}" for item in items)
def build_file_readiness_lines(root):
lines = []
for rel_path in REQUIRED_SOURCE_FILES:
status = "present" if (root / Path(rel_path)).is_file() else "missing"
lines.append(f"- `{rel_path}` ({status})")
return lines
def build_context_document(root):
missing_files = collect_missing_files(root)
model_states = collect_model_states(root)
regression_counts = collect_regression_counts(root)
evidence = collect_round04_evidence(root)
model_state_lines = [
f"- {item['display_name']}: status `{item['status']}`, "
f"stability_level `{item['stability_level']}`, "
f"regression_status `{item['regression_status']}`"
for item in model_states
]
regression_lines = [
f"- {name}: {count}"
for name, count in regression_counts.items()
]
missing_lines = ["- None"] if not missing_files else [f"- `{path}`" for path in missing_files]
lines = [
"# Round 05 New Session Context",
"",
f"Generated: {date.today().isoformat()}",
"",
"Generated by `python scripts/init_round05_context.py`.",
"",
"This file is a read-only startup context for a new Round 05 conversation. The script does not call an LLM and does not modify selector rules, model cards, model specs, or regression cases.",
"",
"## Project State",
"",
"- Current phase: `model_library_mvp`.",
"- Current goal: file-first cognitive model library MVP.",
"- Current sample models: QPI and Intellectual Archaeology.",
"- Current selector: rule-based, backed by `selector/selector_rules.json`.",
"- No LLM selector, RAG, vector database, frontend, backend, or service layer is in scope.",
"",
"Current model lifecycle state:",
"",
*model_state_lines,
"",
"Current regression case counts:",
"",
*regression_lines,
"",
"## Round 04 / 04.1 Closure Summary",
"",
"- Round 04 was a blind routing evaluation.",
"- The original Round 04 blind routing report is preserved and was not rewritten.",
"- Round 04.1 was a selector no-call, hard exclusion, and depth-limiting patch plus post-patch verification.",
"- Round 04.1 is not a second blind test.",
"- Four targeted cases passed post-patch verification.",
f"- Targeted cases checked: {evidence['targeted_cases_checked']}.",
f"- Targeted failures: {evidence['targeted_failures']}.",
f"- Behavior changes recorded: {evidence['behavior_changes_recorded']}.",
"- No model lifecycle upgrade follows from Round 04 / 04.1.",
"- No LLM selector is introduced.",
"- QPI and Intellectual Archaeology remain `draft / B / pending`.",
"",
"Round 04.1 targeted fixes:",
"",
"- `R04-BI-002`: translation payload must not trigger QPI.",
"- `R04-BI-024`: explicit IA refusal must hard-exclude Intellectual Archaeology while allowing lightweight QPI.",
"- `R04-BI-035`: depth-limiting plus dominant-scarcity judgment should allow QPI.",
"- `R04-BI-036`: depth-limiting plus information / path / consensus scarcity judgment should allow QPI.",
"",
"`R04-BI-022` closeout note:",
"",
"- Original result: hard no-call.",
"- After patch: QPI selected, IA rejected.",
"- Judgment: accepted collateral behavior change from the depth-limiting QPI override.",
"- This is not treated as a new failure.",
"- It is already represented in QPI regression as `case_qpi_round04_depth_limited_assumption_scarcity_001`.",
"- Round 05 should discuss whether it remains regression, also enters calibration, or becomes calibration-only policy material later.",
"- Do not open a new patch round only for `R04-BI-022`.",
"",
"## Round 05: Selector Calibration Policy Review",
"",
"Round 05 should review selector calibration policy before changing rules.",
"",
"Round 05 should not directly modify selector rules unless Owner / CCRA explicitly approves a follow-up Round 05.1 patch.",
"",
"Round 05 should not continue treating Round 04.1 as an unfinished patch round. The policy review starts from the accepted Round 04 / 04.1 closure evidence.",
"",
"## Round 05 Core Questions",
"",
"- Should governance / responsibility / consensus inputs more easily enter QPI?",
"- For capacity / resource / path inputs, which should remain no-call and which should enter QPI?",
"- Should likely Intellectual Archaeology natural-language inputs allow direct IA entry, or must they be QPI-first?",
"- When the user says \"QPI already judged...\", should the selector still select QPI together with IA, or select IA only?",
"- How should ambiguous low-context inputs be handled?",
"- Is a before/after behavior diff checker needed to list every non-target behavior change automatically?",
"- Should `R04-BI-022` stay in regression, move into calibration discussion, or both?",
"",
"## Files To Read in New Conversation",
"",
*build_file_readiness_lines(root),
"",
"## Round 05 Non-Goals",
"",
"- Do not add a third model.",
"- Do not upgrade QPI.",
"- Do not upgrade Intellectual Archaeology.",
"- Do not introduce an LLM selector.",
"- Do not introduce RAG, vector database, frontend, or backend.",
"- Do not expand all organization problems into QPI recall.",
"- Do not convert the full Round 04 blind pool directly into regression.",
"",
"## Suggested Round 05 Deliverables",
"",
"- `01_ROUND05_SCOPE_AND_POLICY_QUESTIONS.md`",
"- `02_ROUND04_BEHAVIOR_DIFF_FOR_CALIBRATION.md`",
"- `03_CANDIDATE_CALIBRATION_CASES.md`",
"- `04_CANDIDATE_REGRESSION_CASES.md`",
"- `05_CCRA_REVIEW_QUESTIONS_ROUND05.md`",
"",
"## Missing Files",
"",
*missing_lines,
"",
]
summary = {
"output_path": str(OUTPUT_RELATIVE_PATH).replace("\\", "/"),
"success": True,
"missing_files": missing_files,
"sections": SECTION_TITLES,
}
return "\n".join(lines), summary
def write_context_document(root):
document, summary = build_context_document(root)
output_path = root / OUTPUT_RELATIVE_PATH
output_path.parent.mkdir(parents=True, exist_ok=True)
output_path.write_text(document, encoding="utf-8")
return output_path, summary
def main():
root = Path(__file__).resolve().parents[1]
output_path, summary = write_context_document(root)
print(f"Generated file: {output_path}")
print("Success: yes")
if summary["missing_files"]:
print("Missing files:")
for path in summary["missing_files"]:
print(f"- {path}")
else:
print("Missing files: none")
print("Main sections:")
for section in summary["sections"]:
print(f"- {section}")
print("Selector rules modified: no")
return 0
if __name__ == "__main__":
sys.exit(main())