297 lines
11 KiB
Python
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())
|