ADVERSARIAL owns epistemic refinement. QUALIFY owns implementation-readiness. - ADVERSARIAL_DISPOSITIONS enum: SURVIVES / SURVIVES_WITH_MITIGATIONS / EVIDENCE_DEFICIENT / APPLICABILITY_DEFICIENT / INNOVATION_WARRANTED / NO_VIABLE_CANDIDATE. Only SURVIVES* unlock SYNTHESIS; every other disposition routes targeted refinement to the earliest repairable stage (EVIDENCE_DEFICIENT/NO_VIABLE_CANDIDATE → OUTWARD, APPLICABILITY_DEFICIENT → APPLICABILITY, INNOVATION_WARRANTED → INNOVATION) - advance() REQUIRES a valid adversarial disposition — an adversarial pass without a disposition cannot unlock anything - synthesis_readiness() deterministic invariant (§7): disposition unlocks AND ≥1 surviving candidate AND mitigations represented (for SURVIVES_WITH_MITIGATIONS) AND blocking unknowns = 0 - Falsification tests + required mitigations are first-class capsule fields — they reach SYNTHESIS as mandatory inputs, not optional prose (§5) - QUALIFY narrowed (§8–10): outcomes PROCEED / REFINE_SYNTHESIS (routes to SYNTHESIS only) / REOPEN_EARLIER_STAGE (exceptional; requires reason, return_to, new_blocking_fact, why_previous_gate_could_not_reasonably_ have_caught_it). Generic MORE_RESEARCH removed as a QUALIFY outcome. - Fake worker + all chain tests updated to the disposition/outcome contract Acceptance §18: 18/18 PASS (test_adversarial_gated_refinement.py). 148 tests pass, ruff clean.
221 lines
9.5 KiB
Python
221 lines
9.5 KiB
Python
#!/usr/bin/env python3
|
|
"""fake_worker.py — deterministic fake MiMo for pipeline integration testing.
|
|
|
|
NOT the production research worker. This worker produces known, structured
|
|
artifacts for each stage so the pipeline plumbing (persistence, handoff,
|
|
gating, retry) can be tested in seconds instead of 20-minute real MiMo runs.
|
|
|
|
Canonical pipeline (RP2, handoff 2026-09-08):
|
|
ISSUE → INWARD → OUTWARD → APPLICABILITY
|
|
→ optional INNOVATION → ADVERSARIAL → SYNTHESIS → QUALIFY
|
|
|
|
Per the authoritative build prompt §18:
|
|
"DETERMINISTIC TESTS PROVE THE PIPE. REAL MIMO DOES THE RESEARCH."
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
sys.path.insert(0, str(Path(__file__).parent))
|
|
from research_task_tree import ResearchTaskTree, STAGES
|
|
|
|
def fake_issue_gate(tree: ResearchTaskTree) -> dict:
|
|
"""ISSUE is deterministic: the Gitea issue content is the DEFINE replacement.
|
|
The fake worker emits the compiled gate artifact directly."""
|
|
issue = tree.issue
|
|
return {
|
|
"goal": f"Capability from issue #{issue}",
|
|
"why": "recorded in issue body",
|
|
"constraints": ["must not regress existing behavior"],
|
|
"acceptance": ["issue acceptance criteria"],
|
|
"quality": "BUILDABLE",
|
|
}
|
|
|
|
def fake_inward(tree: ResearchTaskTree) -> dict:
|
|
# Assert ISSUE-gate artifact was actually supplied
|
|
prior = tree.get_prior_artifacts("INWARD")
|
|
issue_gate = prior.get("ISSUE", {})
|
|
assert issue_gate.get("goal"), "INWARD: ISSUE-gate artifact missing — evidence handoff broken"
|
|
return {
|
|
"claims": [
|
|
{"id": "I1", "claim": "memory_store uses FTS5", "evidence": ["src/rune/memory_store.py:86"], "status": "CURRENT"},
|
|
{"id": "I2", "claim": "shard routing is present", "evidence": ["src/rune/shard_manager.py:114"], "status": "CURRENT"},
|
|
],
|
|
}
|
|
|
|
def fake_outward(tree: ResearchTaskTree) -> dict:
|
|
"""Unified OUTWARD (RP3): one research task, discovery + reading as
|
|
SUBSECTIONS of one artifact — not separate stages."""
|
|
prior = tree.get_prior_artifacts("OUTWARD")
|
|
inward = prior.get("INWARD", {})
|
|
claims = inward.get("claims", [])
|
|
assert len(claims) >= 2, f"OUTWARD: expected ≥2 INWARD claims, got {len(claims)}"
|
|
return {
|
|
"discovery": {
|
|
"queries": ["fts5 hybrid search", "sqlite-vec alternatives"],
|
|
"tool_receipts": [{"tool": "webfetch", "query": "fts5 hybrid search", "status": "completed"}],
|
|
"sources": [
|
|
{"id": "O1", "url": "https://sqlite.org/fts5.html", "title": "FTS5", "source_class": "software"},
|
|
{"id": "O2", "url": "https://github.com/asg017/sqlite-vec", "title": "sqlite-vec", "source_class": "software"},
|
|
],
|
|
},
|
|
"reading": {
|
|
"evidence": [
|
|
{"id": "E1", "source_id": "O1", "url": "https://sqlite.org/fts5.html",
|
|
"claim": "FTS5 supports external content tables", "limitations": ["token porter only"]},
|
|
{"id": "E2", "source_id": "O2", "url": "https://github.com/asg017/sqlite-vec",
|
|
"claim": "vec0 virtual tables for embedding search", "limitations": []},
|
|
],
|
|
},
|
|
}
|
|
|
|
def fake_applicability(tree: ResearchTaskTree) -> dict:
|
|
"""APPLICABILITY (RP4): classify researched mechanisms against Rune's reality."""
|
|
prior = tree.get_prior_artifacts("APPLICABILITY")
|
|
outward = prior.get("OUTWARD", {})
|
|
sources = outward.get("discovery", {}).get("sources", [])
|
|
assert len(sources) >= 2, f"APPLICABILITY: expected ≥2 OUTWARD sources, got {len(sources)}"
|
|
return {
|
|
"route": "INNOVATION_WARRANTED",
|
|
"candidates": [
|
|
{
|
|
"id": "M1",
|
|
"source_ids": ["O1", "O2"],
|
|
"classification": "APPLICABLE_WITH_ADAPTATION",
|
|
"integration_point": "memory_store.recall",
|
|
"benefit": "hybrid recall",
|
|
"cost": "low",
|
|
"architectural_fit": "fits memory authority",
|
|
"authority_implications": "none",
|
|
"risks": ["extension availability"],
|
|
"required_adaptation": ["fallback when extension missing"],
|
|
"evidence": ["E1", "E2"],
|
|
},
|
|
],
|
|
}
|
|
|
|
def fake_adversarial(tree: ResearchTaskTree) -> dict:
|
|
prior = tree.get_prior_artifacts("ADVERSARIAL")
|
|
outward = prior.get("OUTWARD", {})
|
|
evidence = outward.get("reading", {}).get("evidence", [])
|
|
assert len(evidence) >= 2, f"ADVERSARIAL: expected ≥2 evidence items, got {len(evidence)}"
|
|
return {
|
|
"disposition": "SURVIVES_WITH_MITIGATIONS",
|
|
"counter_evidence": [
|
|
{"id": "A1", "source_id": "O1", "claim": "FTS5 porter tokenizer may not handle all languages",
|
|
"derived_from": ["E1"], "limitations": ["minor"]},
|
|
],
|
|
"surviving_candidates": ["M1"],
|
|
"rejected_candidates": [],
|
|
"required_mitigations": ["mode labeling"],
|
|
"falsification_tests": ["FT1: harness run with embedding backend unreachable"],
|
|
"attacks": [
|
|
{"id": "A1", "target_candidate": "M1", "dimension": "failure modes",
|
|
"finding": "tokenizer coverage", "evidence": ["E1"], "severity": "minor"},
|
|
],
|
|
}
|
|
|
|
def fake_innovation(tree: ResearchTaskTree) -> dict:
|
|
prior = tree.get_prior_artifacts("INNOVATION")
|
|
inward = prior.get("INWARD", {})
|
|
outward = prior.get("OUTWARD", {})
|
|
applicability = prior.get("APPLICABILITY", {})
|
|
inward_claims = [c["id"] for c in inward.get("claims", [])]
|
|
evidence_ids = [e["id"] for e in outward.get("reading", {}).get("evidence", [])]
|
|
applicable = [c["id"] for c in applicability.get("candidates", [])]
|
|
assert inward_claims, "INNOVATION: no INWARD claims received"
|
|
assert evidence_ids, "INNOVATION: no OUTWARD evidence received"
|
|
assert applicable, "INNOVATION: no APPLICABILITY candidates received"
|
|
return {
|
|
"conclusion": "ADOPT",
|
|
"candidates": [
|
|
{"id": "C1", "category": "ADAPT", "proposal": "hybrid FTS+vec recall",
|
|
"derived_from": inward_claims[:1] + evidence_ids[:1] + applicable[:1],
|
|
"composition_of": [], "novelty": "adapted",
|
|
"expected_gain": "better recall", "risks": ["latency"],
|
|
"kill_test": "recall returns >0 rows"},
|
|
],
|
|
}
|
|
|
|
def fake_synthesis(tree: ResearchTaskTree) -> dict:
|
|
prior = tree.get_prior_artifacts("SYNTHESIS")
|
|
innovation = prior.get("INNOVATION", {})
|
|
candidates = innovation.get("candidates", [])
|
|
assert candidates, "SYNTHESIS: no INNOVATION candidates received"
|
|
return {
|
|
"selected_design": "hybrid FTS+vec recall",
|
|
"rejected_alternatives": [{"design": "pure FTS", "reason": "E2 shows vec adds value"}],
|
|
"evidence_used": ["I1", "E1", "E2", "A1", "M1", "C1"],
|
|
"architecture_implications": ["memory_store.recall modified"],
|
|
"files_affected": ["src/rune/memory_store.py"],
|
|
"contracts": ["recall returns thresholded rows"],
|
|
"tests_required": ["test_recall_hybrid"],
|
|
"failure_modes": ["sqlite-vec not installed"],
|
|
"kill_conditions": ["recall returns 0 rows on seeded store"],
|
|
"build_instructions": ["modify recall() in memory_store.py", "add tests"],
|
|
}
|
|
|
|
def fake_qualify(tree: ResearchTaskTree) -> dict:
|
|
prior = tree.get_prior_artifacts("QUALIFY")
|
|
synthesis = prior.get("SYNTHESIS", {})
|
|
assert synthesis.get("selected_design"), "QUALIFY: no synthesis received"
|
|
# First QUALIFY pass: build instructions incomplete → REFINE_SYNTHESIS
|
|
# (narrowed semantics: synthesis-only refinement, no research repeat).
|
|
# Record the refine round in the tree so the workflow records a 2nd round.
|
|
if tree.state.get("qualify_outcome") != "REFINE_SYNTHESIS":
|
|
return {
|
|
"outcome": "REFINE_SYNTHESIS",
|
|
"reason": "rollback/recovery not specified",
|
|
"build_instructions": synthesis.get("build_instructions", []),
|
|
}
|
|
# After synthesis refinement: PROCEED
|
|
return {
|
|
"outcome": "PROCEED",
|
|
"selected_design": synthesis.get("selected_design", ""),
|
|
"evidence_used": synthesis.get("evidence_used", []),
|
|
}
|
|
|
|
FAKE_WORKERS = {
|
|
"ISSUE": fake_issue_gate,
|
|
"INWARD": fake_inward,
|
|
"OUTWARD": fake_outward,
|
|
"APPLICABILITY": fake_applicability,
|
|
"ADVERSARIAL": fake_adversarial,
|
|
"INNOVATION": fake_innovation,
|
|
"SYNTHESIS": fake_synthesis,
|
|
"QUALIFY": fake_qualify,
|
|
}
|
|
|
|
def run_fake_stage(tree: ResearchTaskTree) -> dict:
|
|
stage = tree.current_stage
|
|
worker = FAKE_WORKERS.get(stage)
|
|
if not worker:
|
|
raise ValueError(f"no fake worker for stage {stage}")
|
|
return worker(tree)
|
|
|
|
def main_with(argv: list[str]) -> int:
|
|
import argparse
|
|
parser = argparse.ArgumentParser(description="Fake stage worker for pipeline testing")
|
|
parser.add_argument("--issue", type=int, required=True)
|
|
parser.add_argument("--round", type=int, default=1)
|
|
parser.add_argument("--artifact-dir", required=True)
|
|
parser.add_argument("--out-dir", required=True)
|
|
args = parser.parse_args(argv[1:])
|
|
|
|
tree = ResearchTaskTree(issue=args.issue, round_no=args.round, artifact_dir=args.artifact_dir)
|
|
if tree.is_complete:
|
|
print("research tree already complete")
|
|
return 0
|
|
artifact = run_fake_stage(tree)
|
|
out_dir = Path(args.out_dir)
|
|
out_dir.mkdir(parents=True, exist_ok=True)
|
|
stage = tree.current_stage
|
|
(out_dir / f"stage_{stage}.json").write_text(json.dumps(artifact, indent=2))
|
|
print(f"fake stage {stage} complete")
|
|
return 0
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main_with(sys.argv))
|