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.
277 lines
10 KiB
Python
277 lines
10 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))
|