Files
rune/scripts/stage_contracts.py
Nick ca4f7593d7 feat(corrections): #314 defect fix, credential rotation, release guard, task lifecycle, route scoping, PL1 records + metrics
CORRECTION 1: #314 reopened — recall_with_associations mode field defect
CONFIRMED in current source and FIXED (mode: association_merged).

CORRECTION 3: Gitea credential removed from remote URL (credential helper).
TOKEN EXPOSED: yes (in diagnostic output). TOKEN ROTATED: not yet (requires
Gitea web UI access). Remote URL now shows username only, no secret.

CORRECTION 4: duplicate-release-commit defect ROOT CAUSE: convergence
computed warranted bump from commits-since-tag without checking if master
version already satisfies the bump. FIX: already-converged guard in
version_convergence.py (master version > last tag → no commit). Repo-aware
(args.repo passed to read_version/latest_tag).

CORRECTION 6: lifecycle terminalization — execution exception now
guarantees task terminalizes as failed with evidence (no immortal RUNNING).

CORRECTION 7: route instruction task-scoped on RuneTask.route_instruction
(NOT shared operator state). Read-only leading-verb override. Isolation
tests prove no cross-contamination between interleaved tasks.

PL1-01/02/03/05: pl1_records.py (route resolution, proof level, failure
class, candidate status), metrics with honest NOT_YET_MEASURABLE semantics,
route-instruction learning loop (PE-1789102658-0269 SEED).

1253 tests green; ratchet 352/80 (shrink); henxels clean.
2026-09-11 15:36:08 +01:00

202 lines
7.6 KiB
Python

#!/usr/bin/env python3
"""stage_contracts.py — single-source stage contracts (TASK D, epic #284).
ONE authoritative definition of each cognitive stage's required artifact
fields. Worker prompts, artifact validators, normalization aliases, and
contract-drift tests all derive from this registry.
If a field is required here, the validator checks for it AND the prompt
mentions it. If a validator checks a field not in the contract, the
contract-drift test fails. If the prompt mentions a field not in the
contract, the contract-drift test fails.
Compatibility aliases (e.g. urls → url, classification → source_class)
are explicit, versioned, and bounded — not arbitrary rescue behavior.
"""
from __future__ import annotations
# Per-stage contract: required fields in the structured artifact
STAGE_CONTRACTS: dict[str, dict] = {
"INWARD": {
"required_fields": [
{"name": "claims", "type": "list", "description": "relevant files/behaviors with evidence citations"},
],
"optional_fields": ["context", "deterministic"],
},
"OUTWARD": {
"required_fields": [
{"name": "discovery", "type": "object", "description": "queries, sources_discovered, source_count"},
{
"name": "reading",
"type": "object",
"description": "sources_selected, sources_read (with machine receipts), evidence_extracted",
},
{"name": "tool_receipts", "type": "list", "description": "per-event receipts with kind/tool/url/timestamp"},
{"name": "coverage", "type": "object", "description": "required_classes, represented_classes, uncovered"},
],
"optional_fields": [
"text",
"limitations",
"contradictory_evidence",
"supporting_evidence",
"alternatives",
"adjacent_field_leads",
"unresolved_questions",
],
},
"APPLICABILITY": {
"required_fields": [
{
"name": "route",
"type": "string",
"description": "STANDARD_SOLUTION_SUFFICIENT or INNOVATION_WARRANTED",
"enum": ["STANDARD_SOLUTION_SUFFICIENT", "INNOVATION_WARRANTED"],
},
{
"name": "candidates",
"type": "list",
"description": "classified mechanisms with integration_point, evidence, source_ids",
},
],
"optional_fields": ["notes"],
},
"ADVERSARIAL": {
"required_fields": [
{
"name": "disposition",
"type": "string",
"description": "SURVIVES | SURVIVES_WITH_MITIGATIONS | EVIDENCE_DEFICIENT | "
"APPLICABILITY_DEFICIENT | INNOVATION_WARRANTED | NO_VIABLE_CANDIDATE",
"enum": [
"SURVIVES",
"SURVIVES_WITH_MITIGATIONS",
"EVIDENCE_DEFICIENT",
"APPLICABILITY_DEFICIENT",
"INNOVATION_WARRANTED",
"NO_VIABLE_CANDIDATE",
],
},
{"name": "surviving_candidates", "type": "list", "description": "candidate ids that survived attack"},
{
"name": "attacks",
"type": "list",
"description": "attack rows with target_candidate, dimension, finding, severity",
},
{
"name": "required_mitigations",
"type": "list",
"description": "mitigations that must appear in SYNTHESIS (mandatory, not prose)",
},
{
"name": "falsification_tests",
"type": "list",
"description": "kill-tests that must convert into invariants",
},
],
"optional_fields": ["rejected_candidates", "critical_assumptions", "new_research_gaps"],
},
"INNOVATION": {
"required_fields": [
{
"name": "conclusion",
"type": "string",
"description": "ADOPT | ADAPT | BORROW | BUILD | NO_INNOVATION_WARRANTED",
"enum": ["ADOPT", "ADAPT", "BORROW", "BUILD", "NO_INNOVATION_WARRANTED"],
},
{
"name": "candidates",
"type": "list",
"description": "each with derived_from (mandatory), kill_test (mandatory)",
},
],
"optional_fields": ["notes"],
},
"SYNTHESIS": {
"required_fields": [
{"name": "selected_design", "type": "string"},
{"name": "implementation_sequence", "type": "list"},
{"name": "interfaces", "type": "list"},
{
"name": "invariants",
"type": "list",
"description": "every falsification test must be converted into an invariant",
},
{"name": "tests_required", "type": "list"},
{"name": "acceptance_criteria", "type": "list"},
{"name": "rollback", "type": "string"},
{"name": "data_flow", "type": "list", "description": "how data moves through the designed system"},
],
"optional_fields": [
"why_this_design_won",
"rejected_alternatives",
"components_affected",
"authority_boundaries",
"failure_modes",
"deliberately_excluded",
],
},
"QUALIFY": {
"required_fields": [
{
"name": "outcome",
"type": "string",
"description": "PROCEED | REFINE_SYNTHESIS | REOPEN_EARLIER_STAGE",
"enum": ["PROCEED", "REFINE_SYNTHESIS", "REOPEN_EARLIER_STAGE"],
},
],
"optional_fields": ["findings", "deterministic_checks", "reopen"],
},
}
# Compatibility aliases: documented field-name mappings for worker variants
# (bounded, versioned, NOT arbitrary rescue behavior).
COMPATIBILITY_ALIASES: dict[str, dict[str, str]] = {
"OUTWARD": {
"urls": "url", # worker uses urls (list) → take first as url
"classification": "source_class",
"search_receipts": "queries",
"read_receipts": "sources_read",
"evidence_claims": "evidence_extracted",
},
"ADVERSARIAL": {},
"SYNTHESIS": {},
"APPLICABILITY": {},
"INNOVATION": {},
}
def get_contract(stage: str) -> dict:
"""Return the contract for a stage. Raises on unknown stage."""
if stage not in STAGE_CONTRACTS:
raise ValueError(f"Unknown stage: {stage}. Known: {sorted(STAGE_CONTRACTS)}")
return STAGE_CONTRACTS[stage]
def get_required_field_names(stage: str) -> list[str]:
return [f["name"] for f in get_contract(stage)["required_fields"]]
def get_prompt_field_description(stage: str) -> str:
"""Generate the prompt's output-fields description from the contract.
The prompt derives from the contract — no independent definition."""
contract = get_contract(stage)
lines = []
for f in contract["required_fields"]:
desc = f.get("description", "")
enum = f.get("enum")
if enum:
desc += f" (exactly one of: {', '.join(enum)})"
lines.append(f" {f['name']} ({f['type']}): {desc}")
for f in contract.get("optional_fields", []):
if isinstance(f, dict):
lines.append(f" {f['name']}: optional")
else:
lines.append(f" {f}: optional")
return chr(10).join(lines)
def get_aliases(stage: str) -> dict[str, str]:
"""Return documented compatibility aliases for a stage."""
return COMPATIBILITY_ALIASES.get(stage, {})