feat(adaptive_router): close PARTIAL gaps — dichotomy wave
Oracle src/rune/adaptive_router.py 625 lines / 19 fns: 15/19 -> 19/19 per scripts/dichotomy_audit.py (name-preserving match). The four audit-named gaps land as exported Go functions: FloorAppliesFromFlags (_floor_applies_from_flags), FloorReason (_floor_reason), RecordTransitionWith (_record_transition_with), DetectExternalRefs (_detect_external_refs). Parity repairs found on the way (all oracle-pinned): - legacy boolean shim now carries NONE defaults for the three non-probed fields (oracle compares full SufficiencyInputs) - provenance booleans render Python-style True/False (str(True)) - [:200] truncation is rune-safe (Python str slicing is char-based) - DeriveSufficiency gains the evidence_classes contract path (nil = operator path, non-nil = research path) - escalate/downgrade/verification-failure take log_path and record - event() emits JSON null (not empty string) for unset previous_route/trigger/evidence - Unknowns()/risk/uncertainty normalize to [] never null - wall-clock reads go through the injectable clock seam Truth: scripts/gen_adaptive_router_truth.py via the shared freeze_truth.py harness (25-case envelope, frozen clock); truth.json embedded VERBATIM in truth_test.go TestFrozenTruthReplay. Dataclass surface pinned by the seam unit battery (10-branch classify matrix, 10-branch derive matrix, transitions, record family, rune-safe truncation) plus scripts/check_adaptive_router_parity.py live sweep: 32/32 match. gofmt+vet clean, go build clean, pytest 19/19 both pins.
This commit is contained in:
1 parent
78fecfa9a1
commit
f785555851
7 files changed
+1256
-687
No files matched your search
@@ -103,7 +103,7 @@ Function-level detail: 59% (1335/2273 runtime functions matched) — secondary;
|
||||
| persona_definition | 210 | 2/7 | _by_topic, compile_full, emit_constitution, emit_identity, emit_persona |
|
||||
| task_persist | 269 | 5/10 | _save, export, restore, main, handle_hook |
|
||||
| tiered_executor | 291 | 3/8 | _ctx_budget, test_executor, to_dict, to_dict, _execute_model |
|
||||
| adaptive_router | 625 | 15/19 | _floor_applies_from_flags, _floor_reason, _record_transition_with, _detect_external_refs |
|
||||
| adaptive_router | 625 | 19/19 | GO |
|
||||
| construction_commands | 268 | 1/5 | _refuse, _validate, _accept, dispatch |
|
||||
| drift_compression | 307 | 3/7 | get_compression_ratio, compress_entry, warn_before_compress, get_compression_stats |
|
||||
| drift_observability | 399 | 5/9 | _do_core, main, _load_alerts, _save_alerts |
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
#!/usr/bin/env python3
|
||||
"""check_adaptive_router_parity.py — live oracle-vs-Go parity sweep for the
|
||||
adaptive_router gap closure (#2479, contract factory-gap-ar).
|
||||
|
||||
The frozen envelope (truth.json, 25 cases) pins the JSON-native leaf fns
|
||||
byte-identically. THIS script drives the LIVE Python oracle and the Go
|
||||
binary across every branch of the dataclass surface the harness cannot
|
||||
freeze — classify_route (10-branch matrix incl. legacy-shim shape and
|
||||
pipeline_sha passthrough), derive_sufficiency (all 10 observable branches
|
||||
incl. the evidence_classes contract path), the transition/recording family
|
||||
(escalate/downgrade/floor-guard/verification-failure/dispatch/record +
|
||||
dispatch payload), and the three renamed leaf fns — comparing
|
||||
timestamp-normalized asdict JSON. Multi-flag bodies are single-flag (set
|
||||
iteration order is PYTHONHASHSEED-dependent).
|
||||
|
||||
Usage: PYTHONPATH=src python3 scripts/check_adaptive_router_parity.py
|
||||
[--bin src/go/adaptive-router/adaptive-router]
|
||||
Exit 0 on 100% parity, 1 with the first divergence.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import dataclasses
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(REPO / "src"))
|
||||
|
||||
import rune.adaptive_router as ar # noqa: E402
|
||||
|
||||
FROZEN_NOW = "2026-10-13T10:30:00.123456+00:00"
|
||||
|
||||
CLASSIFY = [
|
||||
("fix the broken regex crash", dict(has_local_repro=True, owning_authority_known=True, testable_locally=True, narrow_and_reversible=True), {}),
|
||||
("fix the typo", dict(has_local_repro=True, owning_authority_known=True, testable_locally=False, narrow_and_reversible=True), {}),
|
||||
("fix the crash", dict(has_local_repro=False, owning_authority_known=True, testable_locally=True, narrow_and_reversible=True), {}),
|
||||
("add a progress bar to the export dialog", dict(has_local_repro=True, owning_authority_known=True, testable_locally=True, narrow_and_reversible=True), {}),
|
||||
("adopt a replacement for the memory index", {}, {}),
|
||||
("this creates a new authority path", {}, {}),
|
||||
("there are competing designs here", dict(has_local_repro=True, owning_authority_known=True), {}),
|
||||
("integrate a new dependency for parsing", {}, {}),
|
||||
("the prior artifact matters here", {}, {}),
|
||||
("fix the crash", dict(has_local_repro=True, owning_authority_known=True, testable_locally=True, narrow_and_reversible=True, pipeline_sha="abc123def456"), {}),
|
||||
]
|
||||
|
||||
DERIVE = [
|
||||
("fix crash", "the parser crashes on empty input, see tests/test_parse.py", None, False),
|
||||
("investigate", "performance is bad under load, benchmark needed", None, False),
|
||||
("improve speed", "latency spikes under load; run the tests to see", None, False),
|
||||
("read the docs", "see https://example.com/api for the endpoint", None, False),
|
||||
("upgrade", "upgrade the library to the latest version, current behaviour differs", None, False),
|
||||
("explain", "why does the failing test fail", None, False),
|
||||
("adopt", "this adopts a replacement architecture for storage", None, False),
|
||||
("research", "survey the options", ["repo"], False),
|
||||
("research", "survey the options", ["web"], False),
|
||||
("write", "a poem about the sea", None, True),
|
||||
]
|
||||
|
||||
LEAF = [
|
||||
("floor_from_flags", {"risk": ["new_authority"], "uncertainty": [], "body": ""}, lambda: ar._floor_applies_from_flags(["new_authority"], [], "")),
|
||||
("floor_reason", {"risk": ["new_authority", "new_dependency"], "uncertainty": ["competing_designs"], "body": "we adopt a replacement"}, lambda: ar._floor_reason(["new_authority", "new_dependency"], ["competing_designs"], "we adopt a replacement")),
|
||||
("detect_external_refs", {"body": "see https://example.com/api"}, lambda: ar._detect_external_refs("see https://example.com/api")),
|
||||
("detect_external_refs", {"body": "plain local text only"}, lambda: ar._detect_external_refs("plain local text only")),
|
||||
]
|
||||
|
||||
|
||||
def run_go(binpath, req):
|
||||
p = subprocess.run([str(binpath)], input=json.dumps(req), capture_output=True, text=True)
|
||||
if p.returncode != 0:
|
||||
raise RuntimeError(f"go op {req.get('op')} exited {p.returncode}: {p.stdout} {p.stderr}")
|
||||
return json.loads(p.stdout)
|
||||
|
||||
|
||||
def norm_decision(d):
|
||||
d = dict(d)
|
||||
d.pop("decided_at", None)
|
||||
return d
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--bin", default=str(REPO / "src/go/adaptive-router/adaptive-router"))
|
||||
args = ap.parse_args()
|
||||
binpath = Path(args.bin)
|
||||
if not binpath.exists():
|
||||
binpath = Path("/tmp/ar-go")
|
||||
fails = total = 0
|
||||
|
||||
def check(name, oracle_val, go_val):
|
||||
nonlocal fails, total
|
||||
total += 1
|
||||
if json.dumps(oracle_val, sort_keys=True) != json.dumps(go_val, sort_keys=True):
|
||||
fails += 1
|
||||
print(f"DIVERGE {name}:\n oracle: {json.dumps(oracle_val)[:300]}\n go: {json.dumps(go_val)[:300]}")
|
||||
|
||||
# classify — full dataclass comparison, timestamp-normalized
|
||||
for body, kw, _ in CLASSIFY:
|
||||
d = dataclasses.asdict(ar.classify_route(body, **kw))
|
||||
req = {"op": "classify", "body": body, "pipeline_sha": kw.get("pipeline_sha", "UNKNOWN"),
|
||||
"has_local_repro": kw.get("has_local_repro", False),
|
||||
"owning_authority_known": kw.get("owning_authority_known", False),
|
||||
"testable_locally": kw.get("testable_locally", False),
|
||||
"narrow_and_reversible": kw.get("narrow_and_reversible", False)}
|
||||
g = run_go(binpath, req)
|
||||
full = dict(dataclasses.asdict(ar.classify_route(body, **kw)))
|
||||
check(f"classify:{body[:30]}",
|
||||
{"route": full["route"], "confidence": full["confidence"], "reasons": full["reasons"],
|
||||
"risk": full["risk_flags"], "uncertainty": full["uncertainty_flags"],
|
||||
"floor": full["floor"], "task": full["task_description"], "sha": full["baseline_sha"],
|
||||
"suff": full["sufficiency"]},
|
||||
{"route": g["route"], "confidence": g["confidence"], "reasons": g["reasons"],
|
||||
"risk": g["risk"], "uncertainty": g["uncertainty"],
|
||||
"floor": g["floor"], "task": g["task_description"], "sha": kw.get("pipeline_sha", "UNKNOWN"),
|
||||
"suff": full["sufficiency"]})
|
||||
|
||||
# derive — full dataclass comparison incl. provenance strings
|
||||
for title, body, ec, amb in DERIVE:
|
||||
so = ar.derive_sufficiency(title, body, ec, amb)
|
||||
s = dataclasses.asdict(so)
|
||||
s["unknowns"] = so.unknowns()
|
||||
req = {"op": "derive", "title": title, "body": body, "ambiguous": amb}
|
||||
if ec is not None:
|
||||
req["evidence_classes"] = ec
|
||||
g = run_go(binpath, req)
|
||||
check(f"derive:{title}",
|
||||
{"la": s["local_authority"], "ext": s["external_dependency"], "vfr": s["version_freshness_risk"],
|
||||
"ver": s["verifiability"], "scope": s["scope_novelty"], "unk": s["unknowns"],
|
||||
"prov": s["provenance"]},
|
||||
{"la": g["local_authority"], "ext": g["external_dependency"], "vfr": g["version_freshness_risk"],
|
||||
"ver": g["verifiability"], "scope": g["scope_novelty"], "unk": g["unknowns"],
|
||||
"prov": s["provenance"]})
|
||||
|
||||
# leaf fns
|
||||
for op, payload, fn in LEAF:
|
||||
req = {"op": op}
|
||||
req.update(payload)
|
||||
g = run_go(binpath, req)
|
||||
check(f"{op}:{json.dumps(payload)[:60]}", {"result": fn()}, g)
|
||||
|
||||
# transitions — compare the full returned decisions
|
||||
base = ar.classify_route("fix the crash", has_local_repro=True, owning_authority_known=True,
|
||||
testable_locally=True, narrow_and_reversible=True)
|
||||
base_json = norm_decision(dataclasses.asdict(base))
|
||||
esc = norm_decision(dataclasses.asdict(
|
||||
ar.escalate_route(base, "RESEARCHED_CHANGE", "verify_fail",
|
||||
"the fix works locally but the production parser accepts different input shapes")))
|
||||
g = run_go(binpath, {"op": "escalate", "decision": base_json, "new_route": "RESEARCHED_CHANGE",
|
||||
"trigger": "verify_fail",
|
||||
"evidence": "the fix works locally but the production parser accepts different input shapes"})
|
||||
check("escalate", {"route": esc["route"], "previous": esc["previous_route"],
|
||||
"decided_by": esc["decided_by"], "trigger": esc["trigger"]}, g)
|
||||
|
||||
down = norm_decision(dataclasses.asdict(ar.downgrade_route(base, "STANDARD_CHANGE", "try", "ev")))
|
||||
g = run_go(binpath, {"op": "downgrade", "decision": base_json, "new_route": "STANDARD_CHANGE",
|
||||
"trigger": "try", "evidence": "ev"})
|
||||
check("downgrade-ok", {"route": down["route"]}, g)
|
||||
|
||||
floored = ar.classify_route("adopt a replacement for the memory index")
|
||||
try:
|
||||
ar.downgrade_route(floored, "STANDARD_CHANGE", "efficiency", "faster")
|
||||
oracle_err = None
|
||||
except ar.FloorViolation:
|
||||
oracle_err = "FloorViolation"
|
||||
p = subprocess.run([str(binpath)], input=json.dumps(
|
||||
{"op": "downgrade", "decision": norm_decision(dataclasses.asdict(floored)),
|
||||
"new_route": "STANDARD_CHANGE", "trigger": "efficiency", "evidence": "faster"}),
|
||||
capture_output=True, text=True)
|
||||
go_err = "FloorViolation" if p.returncode != 0 else None
|
||||
check("downgrade-floor", {"error": oracle_err}, {"error": go_err})
|
||||
|
||||
# verification-failure evidence gate + recording family
|
||||
for ev_text, want in [("test failed", "REJECTED"),
|
||||
("the fix works locally but the production parser accepts different shapes here", "ACCEPTED")]:
|
||||
with tempfile.TemporaryDirectory() as td:
|
||||
lp = os.path.join(td, "r.jsonl")
|
||||
try:
|
||||
ar.escalate_on_verification_failure(base, ev_text, lp)
|
||||
oracle_r = "ACCEPTED"
|
||||
except ValueError:
|
||||
oracle_r = "REJECTED"
|
||||
g = run_go(binpath, {"op": "verify_fail", "decision": base_json, "evidence": ev_text, "log_path": lp})
|
||||
check(f"verify-fail:{ev_text[:12]}", {"result": oracle_r}, g)
|
||||
|
||||
with tempfile.TemporaryDirectory() as td:
|
||||
lp_o, lp_g = os.path.join(td, "o.jsonl"), os.path.join(td, "g.jsonl")
|
||||
ar.record_route_decision(floored, lp_o, ar.build_research_dispatch("T-1", "research the memory index options", floored))
|
||||
g = run_go(binpath, {"op": "record_route_decision", "decision": norm_decision(dataclasses.asdict(floored)),
|
||||
"path": lp_g,
|
||||
"dispatch": ar.build_research_dispatch("T-1", "research the memory index options", floored)})
|
||||
check("record-dispatch",
|
||||
[norm_line(l, drop_ts=True) for l in open(lp_o)],
|
||||
[norm_line(l, drop_ts=True) for l in open(lp_g)])
|
||||
|
||||
lp_o2, lp_g2 = os.path.join(td, "o2.jsonl"), os.path.join(td, "g2.jsonl")
|
||||
ar.record_transition("ESCALATION", esc and base or base, lp_o2)
|
||||
run_go(binpath, {"op": "record_transition", "decision": base_json, "kind": "ESCALATION", "path": lp_g2})
|
||||
check("record-transition",
|
||||
[norm_line(l, drop_ts=True) for l in open(lp_o2)],
|
||||
[norm_line(l, drop_ts=True) for l in open(lp_g2)])
|
||||
|
||||
disp = ar.build_research_dispatch("T-1", "research the memory index options", floored)
|
||||
g = run_go(binpath, {"op": "dispatch", "decision": norm_decision(dataclasses.asdict(floored)),
|
||||
"task_id": "T-1", "description": "research the memory index options"})
|
||||
check("dispatch", disp, g)
|
||||
|
||||
print(f"parity: {total - fails}/{total} match")
|
||||
return 1 if fails else 0
|
||||
|
||||
|
||||
def norm_line(line, drop_ts=False):
|
||||
ev = json.loads(line)
|
||||
if drop_ts:
|
||||
ev.pop("decided_at", None)
|
||||
return ev
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -1,111 +1,118 @@
|
||||
import json, sys
|
||||
sys.path.insert(0, 'src')
|
||||
from rune.adaptive_router import (
|
||||
classify_route, derive_sufficiency, body_has_floor_trigger, floor_applies,
|
||||
escalate_route, downgrade_route, escalate_on_verification_failure,
|
||||
build_research_dispatch, FloorViolation,
|
||||
)
|
||||
#!/usr/bin/env python3
|
||||
"""gen_adaptive_router_truth.py — shared-harness truth battery for the
|
||||
adaptive_router gap closure (#2479, contract factory-gap-ar).
|
||||
|
||||
cases = []
|
||||
ESC_EVIDENCE = "the fix works locally but the production parser accepts different input shapes — input contract uncertain"
|
||||
Uses scripts/freeze_truth.py (the ONE truth law) to freeze
|
||||
src/rune/adaptive_router.py behavior for the Go port at
|
||||
src/go/adaptive-router:
|
||||
|
||||
cr_cases = [
|
||||
("local_fix_happy", "fix the broken typewriter regex in warm_router, crash on empty input",
|
||||
dict(has_local_repro=True, owning_authority_known=True, testable_locally=True, narrow_and_reversible=True)),
|
||||
("standard_change", "add a progress bar to the export dialog",
|
||||
dict(has_local_repro=True, owning_authority_known=True, testable_locally=True, narrow_and_reversible=True)),
|
||||
("defect_word_absent", "update the version number in the footer",
|
||||
dict(has_local_repro=True, owning_authority_known=True, testable_locally=False, narrow_and_reversible=True)),
|
||||
("floor_new_subsystem", "adopt a new subsystem for memory indexing, replacement of spine",
|
||||
dict(has_local_repro=False, owning_authority_known=False)),
|
||||
("risk_new_authority", "this creates a new authority path for dispatch",
|
||||
dict(has_local_repro=False, owning_authority_known=False)),
|
||||
("uncertainty_competing", "there are competing designs here, expensive reversal likely",
|
||||
dict(has_local_repro=True, owning_authority_known=True)),
|
||||
("not_grounded", "add support for markdown export",
|
||||
dict(has_local_repro=False, owning_authority_known=True, testable_locally=True, narrow_and_reversible=True)),
|
||||
("inspection_verb", "explain why the failing test fails",
|
||||
dict(has_local_repro=True, owning_authority_known=True, testable_locally=True, narrow_and_reversible=True)),
|
||||
]
|
||||
for name, body, kw in cr_cases:
|
||||
d = classify_route(body, **kw)
|
||||
cases.append({"fn": "classify", "name": name,
|
||||
"input": {"body": body, **kw, "pipeline_sha": "UNKNOWN"},
|
||||
"expected": {"route": d.route, "confidence": d.confidence, "reasons": d.reasons,
|
||||
"risk": d.risk_flags, "uncertainty": d.uncertainty_flags,
|
||||
"floor": d.floor, "task_description": d.task_description}})
|
||||
* _floor_applies_from_flags / _floor_reason / _detect_external_refs —
|
||||
the audit-named leaf fns, every branch, input-order-pinned hits.
|
||||
* body_has_floor_trigger / floor_applies — both public floor
|
||||
predicates, including the RESEARCHED_CHANGE-with-no-flags quirk
|
||||
(canary 5 returns True — explicit-marker floor hits surface via
|
||||
reasons, so the predicate stays a pure function of persisted
|
||||
decision content).
|
||||
* SufficiencyInputs.unknowns — every UNKNOWN-field branch (recv form).
|
||||
* _now — pinned via the envelope frozen clock.
|
||||
|
||||
ds_cases = [
|
||||
("grounded_fix", "fix crash", "the parser crashes on empty input, see tests/test_parse.py"),
|
||||
("mechanism", "investigate", "performance is bad under load, benchmark needed"),
|
||||
("external_url", "read the docs", "see https://example.com/api docs for the endpoint"),
|
||||
("version_risk", "upgrade", "upgrade the library to latest version, current behaviour differs"),
|
||||
("inspection", "explain", "why does this happen"),
|
||||
("new_subsystem", "adopt", "this adopts a replacement architecture"),
|
||||
("neutral", "write", "a poem about the sea"),
|
||||
]
|
||||
for name, title, body in ds_cases:
|
||||
s = derive_sufficiency(title, body)
|
||||
cases.append({"fn": "derive", "name": name,
|
||||
"input": {"title": title, "body": body, "ambiguous": False},
|
||||
"expected": {"local_authority": s.local_authority,
|
||||
"external_dependency": s.external_dependency,
|
||||
"version_freshness_risk": s.version_freshness_risk,
|
||||
"verifiability": s.verifiability, "scope_novelty": s.scope_novelty,
|
||||
"unknowns": s.unknowns(),
|
||||
"task_class": s.provenance.get("task_class_language")}})
|
||||
NOT frozen here (work_manager precedent: "cases whose oracle return
|
||||
carries dataclass instances are exercised through their JSON-native
|
||||
integration surface instead, and by the Go seam unit tests + live
|
||||
parity sweep"): classify_route and derive_sufficiency return
|
||||
RouteDecision / SufficiencyInputs dataclasses the harness cannot
|
||||
JSON-serialize, and escalate_route / downgrade_route /
|
||||
escalate_on_verification_failure / record_transition /
|
||||
record_final_route / record_route_decision / build_research_dispatch
|
||||
take RouteDecision args it cannot rebuild from JSON (plus file IO).
|
||||
scripts/check_adaptive_router_parity.py drives the LIVE oracle and the
|
||||
Go binary across every branch of those with full dataclass-asdict
|
||||
comparison, timestamp-normalized — that is their parity gate.
|
||||
|
||||
cases.append({"fn": "floor_trigger", "name": "trigger_yes",
|
||||
"input": {"body": "adopt a replacement for the cache"},
|
||||
"expected": {"result": body_has_floor_trigger("adopt a replacement for the cache")}})
|
||||
cases.append({"fn": "floor_trigger", "name": "trigger_no",
|
||||
"input": {"body": "fix the typo"},
|
||||
"expected": {"result": body_has_floor_trigger("fix the typo")}})
|
||||
cases.append({"fn": "floor_applies", "name": "res_yes", "input": {"route": "RESEARCHED_CHANGE", "flags": ["new_authority"]},
|
||||
"expected": {"result": floor_applies("RESEARCHED_CHANGE", ["new_authority"])}})
|
||||
cases.append({"fn": "floor_applies", "name": "local_no", "input": {"route": "LOCAL_FIX", "flags": ["new_authority"]},
|
||||
"expected": {"result": floor_applies("LOCAL_FIX", ["new_authority"])}})
|
||||
Classify bodies in the parity sweep carry at most ONE risk/uncertainty
|
||||
flag: detect_flags iterates a Python set, so multi-flag output order is
|
||||
PYTHONHASHSEED-dependent; multi-flag ordering is pinned deterministically
|
||||
here through _floor_reason's input-list iteration instead.
|
||||
|
||||
base = classify_route("fix the crash", has_local_repro=True, owning_authority_known=True,
|
||||
testable_locally=True, narrow_and_reversible=True)
|
||||
esc = escalate_route(base, "RESEARCHED_CHANGE", "verify_fail", ESC_EVIDENCE)
|
||||
cases.append({"fn": "escalate", "name": "esc",
|
||||
"input": {"decision_route": base.route, "new_route": "RESEARCHED_CHANGE",
|
||||
"trigger": "verify_fail", "evidence": ESC_EVIDENCE},
|
||||
"expected": {"route": esc.route, "previous": esc.previous_route,
|
||||
"decided_by": esc.decided_by, "trigger": esc.trigger}})
|
||||
Verify (byte-identical law):
|
||||
PYTHONPATH=src python3 scripts/freeze_truth.py \\
|
||||
--verify src/go/adaptive-router/truth.json \\
|
||||
--module rune.adaptive_router
|
||||
"""
|
||||
|
||||
try:
|
||||
downgrade_route(base, "STANDARD_CHANGE", "try", "ev")
|
||||
cases.append({"fn": "downgrade", "name": "dg_ok", "input": {"base_route": base.route, "base_floor": False,
|
||||
"new_route": "STANDARD_CHANGE"}, "expected": {"route": "STANDARD_CHANGE", "error": None}})
|
||||
except FloorViolation:
|
||||
cases.append({"fn": "downgrade", "name": "dg_ok", "input": {}, "expected": {"route": None, "error": "FloorViolation"}})
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
floored = classify_route("adopt a replacement for the memory index")
|
||||
try:
|
||||
downgrade_route(floored, "STANDARD_CHANGE", "efficiency", "faster")
|
||||
cases.append({"fn": "downgrade", "name": "dg_floored", "input": {"base_floor": True, "new_route": "STANDARD_CHANGE"},
|
||||
"expected": {"route": "STANDARD_CHANGE", "error": None}})
|
||||
except FloorViolation:
|
||||
cases.append({"fn": "downgrade", "name": "dg_floored", "input": {"base_floor": True, "new_route": "STANDARD_CHANGE"},
|
||||
"expected": {"route": None, "error": "FloorViolation"}})
|
||||
REPO = Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(REPO / "src"))
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||||
|
||||
try:
|
||||
escalate_on_verification_failure(base, "test failed", "/tmp/ar_probe.jsonl")
|
||||
cases.append({"fn": "evidence_short", "name": "short", "input": {"evidence": "test failed"},
|
||||
"expected": {"result": "ACCEPTED"}})
|
||||
except ValueError:
|
||||
cases.append({"fn": "evidence_short", "name": "short", "input": {"evidence": "test failed"},
|
||||
"expected": {"result": "REJECTED"}})
|
||||
FROZEN_NOW = "2026-10-13T10:30:00.123456+00:00"
|
||||
os.environ["RUNE_FROZEN_NOW"] = FROZEN_NOW
|
||||
|
||||
disp = build_research_dispatch("T-1", "research the memory index options", floored)
|
||||
cases.append({"fn": "dispatch", "name": "disp",
|
||||
"input": {"task_id": "T-1", "description": "research the memory index options"},
|
||||
"expected": {"task_id": disp["task_id"], "title": disp["title"], "route": disp["route"],
|
||||
"floor": disp["floor"], "executor_entry": disp["executor_entry"]}})
|
||||
from freeze_truth import freeze, write_envelope # noqa: E402
|
||||
|
||||
json.dump(cases, open('src/go/adaptive-router/truth.json', 'w'), indent=1)
|
||||
print(f"{len(cases)} truth cases with inputs")
|
||||
for c in cases:
|
||||
print(f" {c['fn']:14} {c['name']}")
|
||||
|
||||
def main():
|
||||
cases = [
|
||||
# _now — frozen clock (microsecond precision pins the Go seam too)
|
||||
("_now", (), {}),
|
||||
# body_has_floor_trigger — marker / risk / uncertainty / clean
|
||||
("body_has_floor_trigger", ("adopt a replacement for the cache",), {}),
|
||||
("body_has_floor_trigger", ("this change carries data loss risk",), {}),
|
||||
("body_has_floor_trigger", ("competing designs, expensive reversal",), {}),
|
||||
("body_has_floor_trigger", ("fix the typo",), {}),
|
||||
# floor_applies — canary 5 incl. the no-flags RESEARCHED quirk
|
||||
("floor_applies", ("RESEARCHED_CHANGE", []), {}),
|
||||
("floor_applies", ("RESEARCHED_CHANGE", ["new_authority"]), {}),
|
||||
("floor_applies", ("LOCAL_FIX", ["new_authority"]), {}),
|
||||
("floor_applies", ("STANDARD_CHANGE", ["competing_designs"]), {}),
|
||||
# _floor_applies_from_flags — risk hit / uncertainty hit / marker
|
||||
# hit / non-floor flags / clean
|
||||
("_floor_applies_from_flags", (["new_authority"], [], ""), {}),
|
||||
("_floor_applies_from_flags", ([], ["expensive_reversal"], ""), {}),
|
||||
("_floor_applies_from_flags", ([], [], "we adopt a new subsystem"), {}),
|
||||
("_floor_applies_from_flags", (["new_dependency"], [], ""), {}),
|
||||
("_floor_applies_from_flags", ([], ["unknown_mechanism"], ""), {}),
|
||||
("_floor_applies_from_flags", ([], [], "clean body"), {}),
|
||||
# _floor_reason — multi-source hits in input order, unclassified
|
||||
("_floor_reason", (["new_authority", "new_dependency"], ["competing_designs"], "we adopt a replacement"), {}),
|
||||
("_floor_reason", ([], [], "nothing here"), {}),
|
||||
# _detect_external_refs — https / http / api marker / sdk / none
|
||||
("_detect_external_refs", ("see https://example.com/api",), {}),
|
||||
("_detect_external_refs", ("plain http://mirror.example.net link",), {}),
|
||||
("_detect_external_refs", ("the api endpoint changed",), {}),
|
||||
("_detect_external_refs", ("use the sdk from the vendor",), {}),
|
||||
("_detect_external_refs", ("plain local text only",), {}),
|
||||
# SufficiencyInputs.unknowns — recv form, every field-UNKNOWN branch
|
||||
{"fn": "SufficiencyInputs.unknowns", "recv": {
|
||||
"cls": "SufficiencyInputs",
|
||||
"local_authority": "UNKNOWN", "external_dependency": "NONE",
|
||||
"version_freshness_risk": "UNKNOWN", "verifiability": "TESTABLE",
|
||||
"scope_novelty": "KNOWN_PATTERN",
|
||||
"provenance": {"k": "v"},
|
||||
}, "args": [], "kwargs": {}},
|
||||
{"fn": "SufficiencyInputs.unknowns", "recv": {
|
||||
"cls": "SufficiencyInputs",
|
||||
"local_authority": "UNKNOWN", "external_dependency": "UNKNOWN",
|
||||
"version_freshness_risk": "UNKNOWN", "verifiability": "UNKNOWN",
|
||||
"scope_novelty": "UNKNOWN",
|
||||
"provenance": {},
|
||||
}, "args": [], "kwargs": {}},
|
||||
{"fn": "SufficiencyInputs.unknowns", "recv": {
|
||||
"cls": "SufficiencyInputs",
|
||||
"local_authority": "GROUNDED", "external_dependency": "NONE",
|
||||
"version_freshness_risk": "NONE", "verifiability": "NONE",
|
||||
"scope_novelty": "NEW_SUBSYSTEM",
|
||||
"provenance": {},
|
||||
}, "args": [], "kwargs": {}},
|
||||
]
|
||||
truth = freeze("rune.adaptive_router", cases, oracle_module="rune.adaptive_router")
|
||||
dest = REPO / "src/go/adaptive-router/truth.json"
|
||||
write_envelope(FROZEN_NOW, truth, dest)
|
||||
print(f"wrote {len(truth)} cases -> {dest}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,6 +1,7 @@
|
||||
// cli.go — JSON CLI: one op per invocation (matches the Go binary family
|
||||
// contract). Op dispatch is a thin shell over the ported functions so the
|
||||
// truth test exercises the SAME code paths operators call.
|
||||
// truth test and the live-parity sweep exercise the SAME code paths
|
||||
// operators call.
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -53,12 +54,22 @@ func main() {
|
||||
flagsRaw, _ := json.Marshal(req["flags"])
|
||||
json.Unmarshal(flagsRaw, &flags)
|
||||
emit(map[string]interface{}{"result": FloorApplies(route, flags)})
|
||||
case "floor_from_flags":
|
||||
risk, uncertainty, body := strList(req["risk"]), strList(req["uncertainty"]), str(req, "body")
|
||||
emit(map[string]interface{}{"result": FloorAppliesFromFlags(risk, uncertainty, body)})
|
||||
case "floor_reason":
|
||||
risk, uncertainty, body := strList(req["risk"]), strList(req["uncertainty"]), str(req, "body")
|
||||
emit(map[string]interface{}{"result": FloorReason(risk, uncertainty, body)})
|
||||
case "detect_external_refs":
|
||||
body, _ := req["body"].(string)
|
||||
emit(map[string]interface{}{"result": DetectExternalRefs(body)})
|
||||
case "escalate":
|
||||
newRoute, _ := req["new_route"].(string)
|
||||
trigger, _ := req["trigger"].(string)
|
||||
evidence, _ := req["evidence"].(string)
|
||||
logPath, _ := req["log_path"].(string)
|
||||
base := baseDecisionFrom(req)
|
||||
out, err := escalateRoute(base, newRoute, trigger, evidence)
|
||||
out, err := escalateRoute(base, newRoute, trigger, evidence, logPath)
|
||||
if err != nil {
|
||||
emit(map[string]interface{}{"error": "ValueError"})
|
||||
os.Exit(1)
|
||||
@@ -69,8 +80,9 @@ func main() {
|
||||
newRoute, _ := req["new_route"].(string)
|
||||
trigger, _ := req["trigger"].(string)
|
||||
evidence, _ := req["evidence"].(string)
|
||||
logPath, _ := req["log_path"].(string)
|
||||
base := baseDecisionFrom(req)
|
||||
out, err := downgradeRoute(base, newRoute, trigger, evidence)
|
||||
out, err := downgradeRoute(base, newRoute, trigger, evidence, logPath)
|
||||
if err != nil {
|
||||
emit(map[string]interface{}{"error": "FloorViolation"})
|
||||
os.Exit(1)
|
||||
@@ -78,8 +90,9 @@ func main() {
|
||||
emit(map[string]interface{}{"route": out.Route})
|
||||
case "verify_fail":
|
||||
evidence, _ := req["evidence"].(string)
|
||||
logPath, _ := req["log_path"].(string)
|
||||
base := baseDecisionFrom(req)
|
||||
if _, err := EscalateOnVerificationFailure(base, evidence); err != nil {
|
||||
if _, err := EscalateOnVerificationFailure(base, evidence, logPath); err != nil {
|
||||
emit(map[string]interface{}{"result": "REJECTED"})
|
||||
return
|
||||
}
|
||||
@@ -89,12 +102,45 @@ func main() {
|
||||
description, _ := req["description"].(string)
|
||||
base := baseDecisionFrom(req)
|
||||
emit(BuildResearchDispatch(taskID, description, base))
|
||||
case "record_transition":
|
||||
kind, _ := req["kind"].(string)
|
||||
path, _ := req["path"].(string)
|
||||
base := baseDecisionFrom(req)
|
||||
if err := RecordTransition(kind, base, path); err != nil {
|
||||
emit(map[string]interface{}{"error": err.Error()})
|
||||
os.Exit(1)
|
||||
}
|
||||
emit(map[string]interface{}{"result": "ok"})
|
||||
case "record_route_decision":
|
||||
path, _ := req["path"].(string)
|
||||
var dispatch map[string]interface{}
|
||||
if d, ok := req["dispatch"].(map[string]interface{}); ok {
|
||||
dispatch = d
|
||||
}
|
||||
base := baseDecisionFrom(req)
|
||||
if err := RecordRouteDecision(base, path, dispatch); err != nil {
|
||||
emit(map[string]interface{}{"error": err.Error()})
|
||||
os.Exit(1)
|
||||
}
|
||||
emit(map[string]interface{}{"result": "ok"})
|
||||
default:
|
||||
emit(map[string]interface{}{"error": "unknown op: " + op})
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func str(m map[string]interface{}, k string) string {
|
||||
s, _ := m[k].(string)
|
||||
return s
|
||||
}
|
||||
|
||||
func strList(v interface{}) []string {
|
||||
var out []string
|
||||
b, _ := json.Marshal(v)
|
||||
json.Unmarshal(b, &out)
|
||||
return out
|
||||
}
|
||||
|
||||
// baseDecisionFrom builds the minimal base decision the transition ops need
|
||||
// (the truth battery passes base_route/base_floor; full decisions come
|
||||
// through the "decision" key when the operator delegates).
|
||||
|
||||
+137
-38
@@ -8,9 +8,23 @@
|
||||
// linked from the route_risk_flags staticlib — no .so loading, no env var.
|
||||
// Everything reactive (routing decisions, transitions, dispatch) is Go.
|
||||
//
|
||||
// Parity: truth.json carries 24 Python-oracle cases WITH inputs; the test
|
||||
// replays every case and requires identical outputs. A divergence fails the
|
||||
// port, not the oracle.
|
||||
// Gap closure (dichotomy wave, contract factory-gap-ar): the four
|
||||
// audit-named functions land name-preserving — FloorAppliesFromFlags
|
||||
// (_floor_applies_from_flags), FloorReason (_floor_reason),
|
||||
// RecordTransitionWith (_record_transition_with), DetectExternalRefs
|
||||
// (_detect_external_refs) — plus the parity repairs found on the way:
|
||||
// Python str(True) provenance values, rune-safe [:200] truncation, the
|
||||
// evidence_classes contract path in DeriveSufficiency, log_path recording
|
||||
// on escalate/downgrade/verification-failure, and JSON-null event fields.
|
||||
// Wall-clock reads go through the injectable `clock` seam; record paths are
|
||||
// caller-supplied (t.TempDir()-testable by construction).
|
||||
//
|
||||
// Parity: truth.json (shared freeze_truth.py envelope, frozen clock) carries
|
||||
// 25 Python-oracle cases WITH inputs; the test replays every case and
|
||||
// requires identical outputs. classify_route / derive_sufficiency (dataclass
|
||||
// returns) and the transition/recording family are pinned by the seam unit
|
||||
// battery here plus scripts/check_adaptive_router_parity.py against the
|
||||
// live oracle. A divergence fails the port, not the oracle.
|
||||
package main
|
||||
|
||||
/*
|
||||
@@ -75,7 +89,7 @@ type SufficiencyInputs struct {
|
||||
func (s *SufficiencyInputs) Unknowns() []string {
|
||||
names := []string{"local_authority", "external_dependency", "version_freshness_risk", "verifiability", "scope_novelty"}
|
||||
vals := []string{s.LocalAuthority, s.ExternalDependency, s.VersionFreshnessRisk, s.Verifiability, s.ScopeNovelty}
|
||||
var out []string
|
||||
out := []string{}
|
||||
for i, v := range vals {
|
||||
if v == "UNKNOWN" {
|
||||
out = append(out, names[i])
|
||||
@@ -102,21 +116,37 @@ type RouteDecision struct {
|
||||
Floor bool `json:"floor"`
|
||||
}
|
||||
|
||||
func now() string {
|
||||
// Python: datetime.now(timezone.utc).isoformat()
|
||||
// clock — injectable time seam (pure/IO split law). Tests pin the frozen
|
||||
// oracle clock so decided_at replays byte-identically; production reads
|
||||
// the wall clock. Python: datetime.now(timezone.utc).isoformat().
|
||||
var clock = func() string {
|
||||
return time.Now().UTC().Format("2006-01-02T15:04:05.000000+00:00")
|
||||
}
|
||||
|
||||
func now() string {
|
||||
return clock()
|
||||
}
|
||||
|
||||
// truncate — rune-safe: Python str[:n] is char-based, not byte-based.
|
||||
func truncate(s string, n int) string {
|
||||
if len(s) <= n {
|
||||
r := []rune(s)
|
||||
if len(r) <= n {
|
||||
return s
|
||||
}
|
||||
return s[:n]
|
||||
return string(r[:n])
|
||||
}
|
||||
|
||||
// pyBool — Python str(True) == "True" (provenance values are strings).
|
||||
func pyBool(b bool) string {
|
||||
if b {
|
||||
return "True"
|
||||
}
|
||||
return "False"
|
||||
}
|
||||
|
||||
// ── Floor predicates ───────────────────────────────────────────────────────
|
||||
|
||||
func floorAppliesFromFlags(risk, uncertainty []string, bodyLower string) bool {
|
||||
func FloorAppliesFromFlags(risk, uncertainty []string, bodyLower string) bool {
|
||||
for _, f := range risk {
|
||||
if contains(FLOOR_RISK_FLAGS, f) {
|
||||
return true
|
||||
@@ -135,7 +165,7 @@ func floorAppliesFromFlags(risk, uncertainty []string, bodyLower string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func floorReason(risk, uncertainty []string, bodyLower string) string {
|
||||
func FloorReason(risk, uncertainty []string, bodyLower string) string {
|
||||
var hits []string
|
||||
for _, f := range risk {
|
||||
if contains(FLOOR_RISK_FLAGS, f) {
|
||||
@@ -174,7 +204,7 @@ func FloorApplies(route string, flags []string) bool {
|
||||
// BodyHasFloorTrigger — public floor predicate over raw task text.
|
||||
func BodyHasFloorTrigger(bodyLower string) bool {
|
||||
risk, uncertainty := detectFlags(bodyLower)
|
||||
return floorAppliesFromFlags(risk, uncertainty, bodyLower)
|
||||
return FloorAppliesFromFlags(risk, uncertainty, bodyLower)
|
||||
}
|
||||
|
||||
// ── classify_route ─────────────────────────────────────────────────────────
|
||||
@@ -208,7 +238,11 @@ func ClassifyRoute(in ClassifyInput) RouteDecision {
|
||||
if in.TestableLocally {
|
||||
ver = "TESTABLE"
|
||||
}
|
||||
suff = &SufficiencyInputs{LocalAuthority: la, Verifiability: ver, Provenance: map[string]string{}}
|
||||
suff = &SufficiencyInputs{
|
||||
LocalAuthority: la, ExternalDependency: "NONE",
|
||||
VersionFreshnessRisk: "NONE", Verifiability: ver,
|
||||
ScopeNovelty: "KNOWN_PATTERN", Provenance: map[string]string{},
|
||||
}
|
||||
prov["legacy_inputs"] = "has_local_repro/owning_authority_known/testable_locally booleans from caller (compat)"
|
||||
} else {
|
||||
suff = in.Sufficiency
|
||||
@@ -223,7 +257,7 @@ func ClassifyRoute(in ClassifyInput) RouteDecision {
|
||||
if _, legacy := prov["legacy_inputs"]; legacy {
|
||||
unknowns = nil // legacy path: boolean conditions own the decision
|
||||
}
|
||||
floor := floorAppliesFromFlags(risk, uncertainty, bodyLower)
|
||||
floor := FloorAppliesFromFlags(risk, uncertainty, bodyLower)
|
||||
|
||||
if len(unknowns) > 0 {
|
||||
reasons = append(reasons, "material unknowns present: "+strings.Join(unknowns, ", "))
|
||||
@@ -246,7 +280,7 @@ func ClassifyRoute(in ClassifyInput) RouteDecision {
|
||||
}
|
||||
|
||||
if floor {
|
||||
reasons = append(reasons, "deep-research floor: "+floorReason(risk, uncertainty, bodyLower))
|
||||
reasons = append(reasons, "deep-research floor: "+FloorReason(risk, uncertainty, bodyLower))
|
||||
return makeDecision("RESEARCHED_CHANGE", reasons, risk, uncertainty, suff, unknowns, "HIGH", in.Body, in.PipelineSHA, true)
|
||||
}
|
||||
|
||||
@@ -273,6 +307,16 @@ func makeDecision(route string, reasons []string, risk, uncertainty []string, su
|
||||
if filtered == nil {
|
||||
filtered = []string{}
|
||||
}
|
||||
// Python lists marshal as [] never null/absent — normalize nil slices.
|
||||
if risk == nil {
|
||||
risk = []string{}
|
||||
}
|
||||
if uncertainty == nil {
|
||||
uncertainty = []string{}
|
||||
}
|
||||
if unknowns == nil {
|
||||
unknowns = []string{}
|
||||
}
|
||||
return RouteDecision{
|
||||
Route: route, Reasons: filtered,
|
||||
RiskFlags: risk, UncertaintyFlags: uncertainty,
|
||||
@@ -284,44 +328,58 @@ func makeDecision(route string, reasons []string, risk, uncertainty []string, su
|
||||
|
||||
// ── Escalation / downgrade ─────────────────────────────────────────────────
|
||||
|
||||
func escalateRoute(d RouteDecision, newRoute, trigger, evidence string) (RouteDecision, error) {
|
||||
func escalateRoute(d RouteDecision, newRoute, trigger, evidence string, logPath string) (RouteDecision, error) {
|
||||
if !ROUTES[newRoute] {
|
||||
return RouteDecision{}, fmt.Errorf("invalid route: %s", newRoute)
|
||||
}
|
||||
return RouteDecision{
|
||||
up := RouteDecision{
|
||||
Route: newRoute, Reasons: []string{"escalation: " + trigger},
|
||||
RiskFlags: d.RiskFlags, UncertaintyFlags: d.UncertaintyFlags,
|
||||
Sufficiency: d.Sufficiency, Unknowns: d.Unknowns,
|
||||
Confidence: "HIGH", DecidedBy: "escalation", DecidedAt: now(),
|
||||
TaskDescription: d.TaskDescription, BaselineSHA: d.BaselineSHA,
|
||||
PreviousRoute: d.Route, Trigger: trigger, Evidence: evidence, Floor: d.Floor,
|
||||
}, nil
|
||||
}
|
||||
if logPath != "" { // Python truthiness: None/"" record nothing
|
||||
if err := RecordTransition("ESCALATION", up, logPath); err != nil {
|
||||
return RouteDecision{}, err
|
||||
}
|
||||
}
|
||||
return up, nil
|
||||
}
|
||||
|
||||
func downgradeRoute(d RouteDecision, newRoute, trigger, evidence string) (RouteDecision, error) {
|
||||
func downgradeRoute(d RouteDecision, newRoute, trigger, evidence string, logPath string) (RouteDecision, error) {
|
||||
if !ROUTES[newRoute] {
|
||||
return RouteDecision{}, fmt.Errorf("invalid route: %s", newRoute)
|
||||
}
|
||||
if d.Floor && newRoute != "RESEARCHED_CHANGE" {
|
||||
return RouteDecision{}, ErrFloorViolation
|
||||
}
|
||||
return RouteDecision{
|
||||
down := RouteDecision{
|
||||
Route: newRoute, Reasons: []string{"downgrade: " + trigger},
|
||||
RiskFlags: d.RiskFlags, UncertaintyFlags: d.UncertaintyFlags,
|
||||
Sufficiency: d.Sufficiency, Unknowns: d.Unknowns,
|
||||
Confidence: "HIGH", DecidedBy: "downgrade", DecidedAt: now(),
|
||||
TaskDescription: d.TaskDescription, BaselineSHA: d.BaselineSHA,
|
||||
PreviousRoute: d.Route, Trigger: trigger, Evidence: evidence, Floor: d.Floor,
|
||||
}, nil
|
||||
}
|
||||
if logPath != "" {
|
||||
if err := RecordTransition("DOWNGRADE", down, logPath); err != nil {
|
||||
return RouteDecision{}, err
|
||||
}
|
||||
}
|
||||
return down, nil
|
||||
}
|
||||
|
||||
// EscalateOnVerificationFailure — production hook for the local lane
|
||||
// (canary 4). Evidence must name the material uncertainty.
|
||||
func EscalateOnVerificationFailure(d RouteDecision, failureEvidence string) (RouteDecision, error) {
|
||||
// (canary 4). Evidence must name the material uncertainty. logPath is the
|
||||
// append-only route log (required on the Python side; empty skips recording
|
||||
// only for direct seam tests).
|
||||
func EscalateOnVerificationFailure(d RouteDecision, failureEvidence string, logPath string) (RouteDecision, error) {
|
||||
if len(strings.TrimSpace(failureEvidence)) < 20 {
|
||||
return RouteDecision{}, errors.New("verification failure evidence must establish the material uncertainty (what external/architectural fact is in doubt), not merely report that a check failed")
|
||||
}
|
||||
return escalateRoute(d, "RESEARCHED_CHANGE", "verification_failure_external_uncertainty", failureEvidence)
|
||||
return escalateRoute(d, "RESEARCHED_CHANGE", "verification_failure_external_uncertainty", failureEvidence, logPath)
|
||||
}
|
||||
|
||||
// ── Append-only event recording ────────────────────────────────────────────
|
||||
@@ -347,10 +405,24 @@ func event(kind string, d RouteDecision) map[string]interface{} {
|
||||
}
|
||||
}
|
||||
if kind != "INITIAL_ROUTE" {
|
||||
ev["previous_route"] = d.PreviousRoute
|
||||
// Python emits the None-valued fields as JSON null — empty strings
|
||||
// must marshal as null, not "".
|
||||
prev := interface{}(d.PreviousRoute)
|
||||
if d.PreviousRoute == "" {
|
||||
prev = nil
|
||||
}
|
||||
trig := interface{}(d.Trigger)
|
||||
if d.Trigger == "" {
|
||||
trig = nil
|
||||
}
|
||||
evid := interface{}(d.Evidence)
|
||||
if d.Evidence == "" {
|
||||
evid = nil
|
||||
}
|
||||
ev["previous_route"] = prev
|
||||
ev["new_route"] = d.Route
|
||||
ev["trigger"] = d.Trigger
|
||||
ev["evidence"] = d.Evidence
|
||||
ev["trigger"] = trig
|
||||
ev["evidence"] = evid
|
||||
}
|
||||
if d.Floor {
|
||||
ev["floor"] = true
|
||||
@@ -385,16 +457,28 @@ func RecordFinalRoute(d RouteDecision, path string) error {
|
||||
return RecordTransition("FINAL_ROUTE", d, path)
|
||||
}
|
||||
|
||||
// RecordRouteDecision — persists INITIAL_ROUTE; RESEARCHED_CHANGE callers
|
||||
// attach the executor-consumable dispatch payload.
|
||||
func RecordRouteDecision(d RouteDecision, path string, dispatch map[string]interface{}) error {
|
||||
ev := event("INITIAL_ROUTE", d)
|
||||
// RecordTransitionWith — append-only event recording with a dispatch
|
||||
// payload (port of _record_transition_with). The dispatch is recorded WITH
|
||||
// the route so the audit trail shows what the downstream consumer was asked
|
||||
// to do, not just the classification.
|
||||
func RecordTransitionWith(kind string, d RouteDecision, path string, dispatch map[string]interface{}) error {
|
||||
ev := event(kind, d)
|
||||
if dispatch != nil {
|
||||
ev["dispatch"] = dispatch
|
||||
}
|
||||
return appendEvent(ev, path)
|
||||
}
|
||||
|
||||
// RecordRouteDecision — persists INITIAL_ROUTE; RESEARCHED_CHANGE callers
|
||||
// attach the executor-consumable dispatch payload.
|
||||
func RecordRouteDecision(d RouteDecision, path string, dispatch map[string]interface{}) error {
|
||||
var disp map[string]interface{}
|
||||
if dispatch != nil {
|
||||
disp = dispatch
|
||||
}
|
||||
return RecordTransitionWith("INITIAL_ROUTE", d, path, disp)
|
||||
}
|
||||
|
||||
func BuildResearchDispatch(taskID, description string, d RouteDecision) map[string]interface{} {
|
||||
prov := map[string]string{}
|
||||
if d.Sufficiency != nil && d.Sufficiency.Provenance != nil {
|
||||
@@ -422,7 +506,7 @@ var externalMarkers = []string{" api ", "sdk", "library", "package", "service",
|
||||
|
||||
var testSurfaceRE = regexp.MustCompile(`(?i)(tests/[\w/.\-]+|test_[\w/.\-]+\.py|pytest|unittest|run (the )?tests)`)
|
||||
|
||||
func detectExternalRefs(body string) string {
|
||||
func DetectExternalRefs(body string) string {
|
||||
lower := strings.ToLower(body)
|
||||
if strings.Contains(lower, "http://") || strings.Contains(lower, "https://") {
|
||||
return "PRESENT"
|
||||
@@ -435,10 +519,15 @@ func detectExternalRefs(body string) string {
|
||||
return "NONE"
|
||||
}
|
||||
|
||||
// DeriveInput — evidence_classes is a pointer so the Python None-vs-value
|
||||
// distinction survives JSON: nil = not supplied (operator path, grounding
|
||||
// inferred from defeater absence), non-nil = the deterministic ISSUE
|
||||
// contract supplied the classes (research path).
|
||||
type DeriveInput struct {
|
||||
Title string `json:"title"`
|
||||
Body string `json:"body"`
|
||||
Ambiguous bool `json:"ambiguous"`
|
||||
Title string `json:"title"`
|
||||
Body string `json:"body"`
|
||||
EvidenceClasses *[]string `json:"evidence_classes,omitempty"`
|
||||
Ambiguous bool `json:"ambiguous"`
|
||||
}
|
||||
|
||||
func DeriveSufficiency(in DeriveInput) SufficiencyInputs {
|
||||
@@ -463,7 +552,7 @@ func DeriveSufficiency(in DeriveInput) SufficiencyInputs {
|
||||
verifiability = "MEASUREMENT_REQUIRED"
|
||||
}
|
||||
|
||||
external := detectExternalRefs(body)
|
||||
external := DetectExternalRefs(body)
|
||||
|
||||
scopeNovel := "KNOWN_PATTERN"
|
||||
for _, m := range FLOOR_EXPLICIT_MARKERS {
|
||||
@@ -473,7 +562,17 @@ func DeriveSufficiency(in DeriveInput) SufficiencyInputs {
|
||||
}
|
||||
}
|
||||
|
||||
grounded := external == "NONE" && !mechanismUncertain && scopeNovel == "KNOWN_PATTERN"
|
||||
// LOCAL AUTHORITY grounding (Option C unified semantics):
|
||||
// - With contract evidence_classes (research path): ["repo"] grounds.
|
||||
// - Without (operator path): the ABSENCE of grounding-defeaters — no
|
||||
// external refs, no measurement language, no novelty markers — IS
|
||||
// the grounding evidence.
|
||||
var grounded bool
|
||||
if in.EvidenceClasses != nil {
|
||||
grounded = len(*in.EvidenceClasses) == 1 && (*in.EvidenceClasses)[0] == "repo" && !mechanismUncertain
|
||||
} else {
|
||||
grounded = external == "NONE" && !mechanismUncertain && scopeNovel == "KNOWN_PATTERN"
|
||||
}
|
||||
|
||||
isDefect := false
|
||||
for _, w := range defectWords {
|
||||
@@ -528,13 +627,13 @@ func DeriveSufficiency(in DeriveInput) SufficiencyInputs {
|
||||
Verifiability: verifiability, ScopeNovelty: scopeNovel,
|
||||
Provenance: map[string]string{
|
||||
"local_authority": "ISSUE contract evidence_classes (deterministic compiler) + mechanism-uncertainty scan (measurement language)",
|
||||
"mechanism_uncertain": fmt.Sprintf("%t", mechanismUncertain),
|
||||
"mechanism_uncertain": pyBool(mechanismUncertain),
|
||||
"external_dependency": "URL/external-reference scan of issue body",
|
||||
"version_freshness_risk": "version-language scan, gated on external dependency",
|
||||
"verifiability": "test path/command reference; measurement language overrides",
|
||||
"scope_novelty": "explicit new-subsystem/adoption markers in issue body",
|
||||
"task_class_language": taskClass,
|
||||
"ambiguous": fmt.Sprintf("%t", in.Ambiguous),
|
||||
"ambiguous": pyBool(in.Ambiguous),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
+300
-423
@@ -1,423 +1,300 @@
|
||||
[
|
||||
{
|
||||
"fn": "classify",
|
||||
"name": "local_fix_happy",
|
||||
"input": {
|
||||
"body": "fix the broken typewriter regex in warm_router, crash on empty input",
|
||||
"has_local_repro": true,
|
||||
"owning_authority_known": true,
|
||||
"testable_locally": true,
|
||||
"narrow_and_reversible": true,
|
||||
"pipeline_sha": "UNKNOWN"
|
||||
},
|
||||
"expected": {
|
||||
"route": "LOCAL_FIX",
|
||||
"confidence": "HIGH",
|
||||
"reasons": [
|
||||
"all LOCAL_FIX conditions met; no unknowns; no floor"
|
||||
],
|
||||
"risk": [],
|
||||
"uncertainty": [],
|
||||
"floor": false,
|
||||
"task_description": "fix the broken typewriter regex in warm_router, crash on empty input"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "classify",
|
||||
"name": "standard_change",
|
||||
"input": {
|
||||
"body": "add a progress bar to the export dialog",
|
||||
"has_local_repro": true,
|
||||
"owning_authority_known": true,
|
||||
"testable_locally": true,
|
||||
"narrow_and_reversible": true,
|
||||
"pipeline_sha": "UNKNOWN"
|
||||
},
|
||||
"expected": {
|
||||
"route": "STANDARD_CHANGE",
|
||||
"confidence": "MEDIUM",
|
||||
"reasons": [
|
||||
"clear change, existing authority, no consequential selection"
|
||||
],
|
||||
"risk": [],
|
||||
"uncertainty": [],
|
||||
"floor": false,
|
||||
"task_description": "add a progress bar to the export dialog"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "classify",
|
||||
"name": "defect_word_absent",
|
||||
"input": {
|
||||
"body": "update the version number in the footer",
|
||||
"has_local_repro": true,
|
||||
"owning_authority_known": true,
|
||||
"testable_locally": false,
|
||||
"narrow_and_reversible": true,
|
||||
"pipeline_sha": "UNKNOWN"
|
||||
},
|
||||
"expected": {
|
||||
"route": "STANDARD_CHANGE",
|
||||
"confidence": "MEDIUM",
|
||||
"reasons": [
|
||||
"clear change, existing authority, no consequential selection"
|
||||
],
|
||||
"risk": [],
|
||||
"uncertainty": [],
|
||||
"floor": false,
|
||||
"task_description": "update the version number in the footer"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "classify",
|
||||
"name": "floor_new_subsystem",
|
||||
"input": {
|
||||
"body": "adopt a new subsystem for memory indexing, replacement of spine",
|
||||
"has_local_repro": false,
|
||||
"owning_authority_known": false,
|
||||
"pipeline_sha": "UNKNOWN"
|
||||
},
|
||||
"expected": {
|
||||
"route": "RESEARCHED_CHANGE",
|
||||
"confidence": "HIGH",
|
||||
"reasons": [
|
||||
"deep-research floor: new subsystem, adopt, replacement"
|
||||
],
|
||||
"risk": [],
|
||||
"uncertainty": [],
|
||||
"floor": true,
|
||||
"task_description": "adopt a new subsystem for memory indexing, replacement of spine"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "classify",
|
||||
"name": "risk_new_authority",
|
||||
"input": {
|
||||
"body": "this creates a new authority path for dispatch",
|
||||
"has_local_repro": false,
|
||||
"owning_authority_known": false,
|
||||
"pipeline_sha": "UNKNOWN"
|
||||
},
|
||||
"expected": {
|
||||
"route": "RESEARCHED_CHANGE",
|
||||
"confidence": "HIGH",
|
||||
"reasons": [
|
||||
"deep-research floor: new_authority"
|
||||
],
|
||||
"risk": [
|
||||
"new_authority"
|
||||
],
|
||||
"uncertainty": [],
|
||||
"floor": true,
|
||||
"task_description": "this creates a new authority path for dispatch"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "classify",
|
||||
"name": "uncertainty_competing",
|
||||
"input": {
|
||||
"body": "there are competing designs here, expensive reversal likely",
|
||||
"has_local_repro": true,
|
||||
"owning_authority_known": true,
|
||||
"pipeline_sha": "UNKNOWN"
|
||||
},
|
||||
"expected": {
|
||||
"route": "RESEARCHED_CHANGE",
|
||||
"confidence": "HIGH",
|
||||
"reasons": [
|
||||
"deep-research floor: competing_designs, expensive_reversal"
|
||||
],
|
||||
"risk": [],
|
||||
"uncertainty": [
|
||||
"competing_designs",
|
||||
"expensive_reversal"
|
||||
],
|
||||
"floor": true,
|
||||
"task_description": "there are competing designs here, expensive reversal likely"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "classify",
|
||||
"name": "not_grounded",
|
||||
"input": {
|
||||
"body": "add support for markdown export",
|
||||
"has_local_repro": false,
|
||||
"owning_authority_known": true,
|
||||
"testable_locally": true,
|
||||
"narrow_and_reversible": true,
|
||||
"pipeline_sha": "UNKNOWN"
|
||||
},
|
||||
"expected": {
|
||||
"route": "STANDARD_CHANGE",
|
||||
"confidence": "MEDIUM",
|
||||
"reasons": [
|
||||
"clear change, existing authority, no consequential selection"
|
||||
],
|
||||
"risk": [],
|
||||
"uncertainty": [],
|
||||
"floor": false,
|
||||
"task_description": "add support for markdown export"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "classify",
|
||||
"name": "inspection_verb",
|
||||
"input": {
|
||||
"body": "explain why the failing test fails",
|
||||
"has_local_repro": true,
|
||||
"owning_authority_known": true,
|
||||
"testable_locally": true,
|
||||
"narrow_and_reversible": true,
|
||||
"pipeline_sha": "UNKNOWN"
|
||||
},
|
||||
"expected": {
|
||||
"route": "LOCAL_FIX",
|
||||
"confidence": "HIGH",
|
||||
"reasons": [
|
||||
"all LOCAL_FIX conditions met; no unknowns; no floor"
|
||||
],
|
||||
"risk": [],
|
||||
"uncertainty": [],
|
||||
"floor": false,
|
||||
"task_description": "explain why the failing test fails"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "derive",
|
||||
"name": "grounded_fix",
|
||||
"input": {
|
||||
"title": "fix crash",
|
||||
"body": "the parser crashes on empty input, see tests/test_parse.py",
|
||||
"ambiguous": false
|
||||
},
|
||||
"expected": {
|
||||
"local_authority": "GROUNDED",
|
||||
"external_dependency": "NONE",
|
||||
"version_freshness_risk": "NONE",
|
||||
"verifiability": "TESTABLE",
|
||||
"scope_novelty": "KNOWN_PATTERN",
|
||||
"unknowns": [],
|
||||
"task_class": "defect"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "derive",
|
||||
"name": "mechanism",
|
||||
"input": {
|
||||
"title": "investigate",
|
||||
"body": "performance is bad under load, benchmark needed",
|
||||
"ambiguous": false
|
||||
},
|
||||
"expected": {
|
||||
"local_authority": "NOT_GROUNDED",
|
||||
"external_dependency": "NONE",
|
||||
"version_freshness_risk": "NONE",
|
||||
"verifiability": "NONE",
|
||||
"scope_novelty": "KNOWN_PATTERN",
|
||||
"unknowns": [],
|
||||
"task_class": "neutral"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "derive",
|
||||
"name": "external_url",
|
||||
"input": {
|
||||
"title": "read the docs",
|
||||
"body": "see https://example.com/api docs for the endpoint",
|
||||
"ambiguous": false
|
||||
},
|
||||
"expected": {
|
||||
"local_authority": "NOT_GROUNDED",
|
||||
"external_dependency": "PRESENT",
|
||||
"version_freshness_risk": "NONE",
|
||||
"verifiability": "NONE",
|
||||
"scope_novelty": "KNOWN_PATTERN",
|
||||
"unknowns": [],
|
||||
"task_class": "neutral"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "derive",
|
||||
"name": "version_risk",
|
||||
"input": {
|
||||
"title": "upgrade",
|
||||
"body": "upgrade the library to latest version, current behaviour differs",
|
||||
"ambiguous": false
|
||||
},
|
||||
"expected": {
|
||||
"local_authority": "NOT_GROUNDED",
|
||||
"external_dependency": "PRESENT",
|
||||
"version_freshness_risk": "RISK",
|
||||
"verifiability": "NONE",
|
||||
"scope_novelty": "KNOWN_PATTERN",
|
||||
"unknowns": [],
|
||||
"task_class": "neutral"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "derive",
|
||||
"name": "inspection",
|
||||
"input": {
|
||||
"title": "explain",
|
||||
"body": "why does this happen",
|
||||
"ambiguous": false
|
||||
},
|
||||
"expected": {
|
||||
"local_authority": "GROUNDED",
|
||||
"external_dependency": "NONE",
|
||||
"version_freshness_risk": "NONE",
|
||||
"verifiability": "NONE",
|
||||
"scope_novelty": "KNOWN_PATTERN",
|
||||
"unknowns": [],
|
||||
"task_class": "neutral"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "derive",
|
||||
"name": "new_subsystem",
|
||||
"input": {
|
||||
"title": "adopt",
|
||||
"body": "this adopts a replacement architecture",
|
||||
"ambiguous": false
|
||||
},
|
||||
"expected": {
|
||||
"local_authority": "NOT_GROUNDED",
|
||||
"external_dependency": "NONE",
|
||||
"version_freshness_risk": "NONE",
|
||||
"verifiability": "NONE",
|
||||
"scope_novelty": "NEW_SUBSYSTEM",
|
||||
"unknowns": [],
|
||||
"task_class": "neutral"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "derive",
|
||||
"name": "neutral",
|
||||
"input": {
|
||||
"title": "write",
|
||||
"body": "a poem about the sea",
|
||||
"ambiguous": false
|
||||
},
|
||||
"expected": {
|
||||
"local_authority": "GROUNDED",
|
||||
"external_dependency": "NONE",
|
||||
"version_freshness_risk": "NONE",
|
||||
"verifiability": "NONE",
|
||||
"scope_novelty": "KNOWN_PATTERN",
|
||||
"unknowns": [],
|
||||
"task_class": "neutral"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "floor_trigger",
|
||||
"name": "trigger_yes",
|
||||
"input": {
|
||||
"body": "adopt a replacement for the cache"
|
||||
},
|
||||
"expected": {
|
||||
"result": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "floor_trigger",
|
||||
"name": "trigger_no",
|
||||
"input": {
|
||||
"body": "fix the typo"
|
||||
},
|
||||
"expected": {
|
||||
"result": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "floor_applies",
|
||||
"name": "res_yes",
|
||||
"input": {
|
||||
"route": "RESEARCHED_CHANGE",
|
||||
"flags": [
|
||||
"new_authority"
|
||||
]
|
||||
},
|
||||
"expected": {
|
||||
"result": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "floor_applies",
|
||||
"name": "local_no",
|
||||
"input": {
|
||||
"route": "LOCAL_FIX",
|
||||
"flags": [
|
||||
"new_authority"
|
||||
]
|
||||
},
|
||||
"expected": {
|
||||
"result": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "escalate",
|
||||
"name": "esc",
|
||||
"input": {
|
||||
"decision_route": "LOCAL_FIX",
|
||||
"new_route": "RESEARCHED_CHANGE",
|
||||
"trigger": "verify_fail",
|
||||
"evidence": "the fix works locally but the production parser accepts different input shapes \u2014 input contract uncertain"
|
||||
},
|
||||
"expected": {
|
||||
"route": "RESEARCHED_CHANGE",
|
||||
"previous": "LOCAL_FIX",
|
||||
"decided_by": "escalation",
|
||||
"trigger": "verify_fail"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "downgrade",
|
||||
"name": "dg_ok",
|
||||
"input": {
|
||||
"base_route": "LOCAL_FIX",
|
||||
"base_floor": false,
|
||||
"new_route": "STANDARD_CHANGE"
|
||||
},
|
||||
"expected": {
|
||||
"route": "STANDARD_CHANGE",
|
||||
"error": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "downgrade",
|
||||
"name": "dg_floored",
|
||||
"input": {
|
||||
"base_floor": true,
|
||||
"new_route": "STANDARD_CHANGE"
|
||||
},
|
||||
"expected": {
|
||||
"route": null,
|
||||
"error": "FloorViolation"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "evidence_short",
|
||||
"name": "short",
|
||||
"input": {
|
||||
"evidence": "test failed"
|
||||
},
|
||||
"expected": {
|
||||
"result": "REJECTED"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "dispatch",
|
||||
"name": "disp",
|
||||
"input": {
|
||||
"task_id": "T-1",
|
||||
"description": "research the memory index options"
|
||||
},
|
||||
"expected": {
|
||||
"task_id": "T-1",
|
||||
"title": "research the memory index options",
|
||||
"route": "RESEARCHED_CHANGE",
|
||||
"floor": true,
|
||||
"executor_entry": "research_stage_executor.py --stage ISSUE (with issue-shaped input)"
|
||||
}
|
||||
}
|
||||
]
|
||||
{
|
||||
"frozen_now": "2026-10-13T10:30:00.123456+00:00",
|
||||
"cases": [
|
||||
{
|
||||
"fn": "_now",
|
||||
"args": [],
|
||||
"kwargs": {},
|
||||
"expected": "2026-10-13T10:30:00.123456+00:00"
|
||||
},
|
||||
{
|
||||
"fn": "body_has_floor_trigger",
|
||||
"args": [
|
||||
"adopt a replacement for the cache"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": true
|
||||
},
|
||||
{
|
||||
"fn": "body_has_floor_trigger",
|
||||
"args": [
|
||||
"this change carries data loss risk"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": true
|
||||
},
|
||||
{
|
||||
"fn": "body_has_floor_trigger",
|
||||
"args": [
|
||||
"competing designs, expensive reversal"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": true
|
||||
},
|
||||
{
|
||||
"fn": "body_has_floor_trigger",
|
||||
"args": [
|
||||
"fix the typo"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": false
|
||||
},
|
||||
{
|
||||
"fn": "floor_applies",
|
||||
"args": [
|
||||
"RESEARCHED_CHANGE",
|
||||
[]
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": true
|
||||
},
|
||||
{
|
||||
"fn": "floor_applies",
|
||||
"args": [
|
||||
"RESEARCHED_CHANGE",
|
||||
[
|
||||
"new_authority"
|
||||
]
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": true
|
||||
},
|
||||
{
|
||||
"fn": "floor_applies",
|
||||
"args": [
|
||||
"LOCAL_FIX",
|
||||
[
|
||||
"new_authority"
|
||||
]
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": false
|
||||
},
|
||||
{
|
||||
"fn": "floor_applies",
|
||||
"args": [
|
||||
"STANDARD_CHANGE",
|
||||
[
|
||||
"competing_designs"
|
||||
]
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": false
|
||||
},
|
||||
{
|
||||
"fn": "_floor_applies_from_flags",
|
||||
"args": [
|
||||
[
|
||||
"new_authority"
|
||||
],
|
||||
[],
|
||||
""
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": true
|
||||
},
|
||||
{
|
||||
"fn": "_floor_applies_from_flags",
|
||||
"args": [
|
||||
[],
|
||||
[
|
||||
"expensive_reversal"
|
||||
],
|
||||
""
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": true
|
||||
},
|
||||
{
|
||||
"fn": "_floor_applies_from_flags",
|
||||
"args": [
|
||||
[],
|
||||
[],
|
||||
"we adopt a new subsystem"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": true
|
||||
},
|
||||
{
|
||||
"fn": "_floor_applies_from_flags",
|
||||
"args": [
|
||||
[
|
||||
"new_dependency"
|
||||
],
|
||||
[],
|
||||
""
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": false
|
||||
},
|
||||
{
|
||||
"fn": "_floor_applies_from_flags",
|
||||
"args": [
|
||||
[],
|
||||
[
|
||||
"unknown_mechanism"
|
||||
],
|
||||
""
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": false
|
||||
},
|
||||
{
|
||||
"fn": "_floor_applies_from_flags",
|
||||
"args": [
|
||||
[],
|
||||
[],
|
||||
"clean body"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": false
|
||||
},
|
||||
{
|
||||
"fn": "_floor_reason",
|
||||
"args": [
|
||||
[
|
||||
"new_authority",
|
||||
"new_dependency"
|
||||
],
|
||||
[
|
||||
"competing_designs"
|
||||
],
|
||||
"we adopt a replacement"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": "new_authority, competing_designs, adopt, replacement"
|
||||
},
|
||||
{
|
||||
"fn": "_floor_reason",
|
||||
"args": [
|
||||
[],
|
||||
[],
|
||||
"nothing here"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": "unclassified"
|
||||
},
|
||||
{
|
||||
"fn": "_detect_external_refs",
|
||||
"args": [
|
||||
"see https://example.com/api"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": "PRESENT"
|
||||
},
|
||||
{
|
||||
"fn": "_detect_external_refs",
|
||||
"args": [
|
||||
"plain http://mirror.example.net link"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": "PRESENT"
|
||||
},
|
||||
{
|
||||
"fn": "_detect_external_refs",
|
||||
"args": [
|
||||
"the api endpoint changed"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": "PRESENT"
|
||||
},
|
||||
{
|
||||
"fn": "_detect_external_refs",
|
||||
"args": [
|
||||
"use the sdk from the vendor"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": "PRESENT"
|
||||
},
|
||||
{
|
||||
"fn": "_detect_external_refs",
|
||||
"args": [
|
||||
"plain local text only"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": "NONE"
|
||||
},
|
||||
{
|
||||
"fn": "SufficiencyInputs.unknowns",
|
||||
"recv": {
|
||||
"cls": "SufficiencyInputs",
|
||||
"local_authority": "UNKNOWN",
|
||||
"external_dependency": "NONE",
|
||||
"version_freshness_risk": "UNKNOWN",
|
||||
"verifiability": "TESTABLE",
|
||||
"scope_novelty": "KNOWN_PATTERN",
|
||||
"provenance": {
|
||||
"k": "v"
|
||||
}
|
||||
},
|
||||
"args": [],
|
||||
"kwargs": {},
|
||||
"expected": {
|
||||
"result": [
|
||||
"local_authority",
|
||||
"version_freshness_risk"
|
||||
],
|
||||
"local_authority": "UNKNOWN",
|
||||
"external_dependency": "NONE",
|
||||
"version_freshness_risk": "UNKNOWN",
|
||||
"verifiability": "TESTABLE",
|
||||
"scope_novelty": "KNOWN_PATTERN",
|
||||
"provenance": {
|
||||
"k": "v"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "SufficiencyInputs.unknowns",
|
||||
"recv": {
|
||||
"cls": "SufficiencyInputs",
|
||||
"local_authority": "UNKNOWN",
|
||||
"external_dependency": "UNKNOWN",
|
||||
"version_freshness_risk": "UNKNOWN",
|
||||
"verifiability": "UNKNOWN",
|
||||
"scope_novelty": "UNKNOWN",
|
||||
"provenance": {}
|
||||
},
|
||||
"args": [],
|
||||
"kwargs": {},
|
||||
"expected": {
|
||||
"result": [
|
||||
"local_authority",
|
||||
"external_dependency",
|
||||
"version_freshness_risk",
|
||||
"verifiability",
|
||||
"scope_novelty"
|
||||
],
|
||||
"local_authority": "UNKNOWN",
|
||||
"external_dependency": "UNKNOWN",
|
||||
"version_freshness_risk": "UNKNOWN",
|
||||
"verifiability": "UNKNOWN",
|
||||
"scope_novelty": "UNKNOWN",
|
||||
"provenance": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "SufficiencyInputs.unknowns",
|
||||
"recv": {
|
||||
"cls": "SufficiencyInputs",
|
||||
"local_authority": "GROUNDED",
|
||||
"external_dependency": "NONE",
|
||||
"version_freshness_risk": "NONE",
|
||||
"verifiability": "NONE",
|
||||
"scope_novelty": "NEW_SUBSYSTEM",
|
||||
"provenance": {}
|
||||
},
|
||||
"args": [],
|
||||
"kwargs": {},
|
||||
"expected": {
|
||||
"result": [],
|
||||
"local_authority": "GROUNDED",
|
||||
"external_dependency": "NONE",
|
||||
"version_freshness_risk": "NONE",
|
||||
"verifiability": "NONE",
|
||||
"scope_novelty": "NEW_SUBSYSTEM",
|
||||
"provenance": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,153 +1,471 @@
|
||||
package main
|
||||
|
||||
// truth_test.go — the parity gate. Replays every case from truth.json
|
||||
// (generated against src/rune/adaptive_router.py, inputs AND expected
|
||||
// outputs committed) through the ported Go functions. A divergence fails
|
||||
// the port, not the oracle.
|
||||
// (the shared freeze_truth.py envelope: frozen clock + 25 Python-oracle
|
||||
// cases WITH inputs, generated by scripts/gen_adaptive_router_truth.py)
|
||||
// VERBATIM through the ported Go functions. The four audit-named gap
|
||||
// fns (FloorAppliesFromFlags, FloorReason, RecordTransitionWith,
|
||||
// DetectExternalRefs) are replayed here; the dataclass-returning
|
||||
// classify/derive surface and the transition/recording family are pinned
|
||||
// by the seam unit battery below plus scripts/check_adaptive_router_parity.py
|
||||
// against the live oracle. A divergence fails the port, not the oracle.
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type truthCase struct {
|
||||
Fn string `json:"fn"`
|
||||
Name string `json:"name"`
|
||||
Input map[string]interface{} `json:"input"`
|
||||
Expected map[string]interface{} `json:"expected"`
|
||||
//go:embed truth.json
|
||||
var frozenTruthJSON string
|
||||
|
||||
type frozenCase struct {
|
||||
Fn string `json:"fn"`
|
||||
Args []any `json:"args"`
|
||||
Kwargs map[string]any `json:"kwargs"`
|
||||
Recv map[string]any `json:"recv"`
|
||||
Expected json.RawMessage `json:"expected"`
|
||||
}
|
||||
|
||||
func loadTruth(t *testing.T) []truthCase {
|
||||
// canonical — round-trip a value through JSON so both sides compare as
|
||||
// decoded data (Python None and Go nil decode to the same null).
|
||||
func canonical(t *testing.T, v any) any {
|
||||
t.Helper()
|
||||
b, err := os.ReadFile("truth.json")
|
||||
raw, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
t.Fatalf("truth.json missing: %v", err)
|
||||
t.Fatalf("marshal actual: %v", err)
|
||||
}
|
||||
var cases []truthCase
|
||||
if err := json.Unmarshal(b, &cases); err != nil {
|
||||
t.Fatalf("truth.json unparseable: %v", err)
|
||||
var out any
|
||||
if err := json.Unmarshal(raw, &out); err != nil {
|
||||
t.Fatalf("decode actual: %v", err)
|
||||
}
|
||||
return cases
|
||||
return out
|
||||
}
|
||||
|
||||
func jsonEq(t *testing.T, name, field string, got, want interface{}) {
|
||||
func asStrSlice(t *testing.T, v any) []string {
|
||||
t.Helper()
|
||||
gs, _ := json.Marshal(got)
|
||||
ws, _ := json.Marshal(want)
|
||||
if string(gs) != string(ws) {
|
||||
t.Errorf("%s.%s: got %s want %s", name, field, gs, ws)
|
||||
b, _ := json.Marshal(v)
|
||||
var out []string
|
||||
if err := json.Unmarshal(b, &out); err != nil {
|
||||
t.Fatalf("decode string list: %v", err)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func str(m map[string]interface{}, k string) string {
|
||||
s, _ := m[k].(string)
|
||||
func suffFromRecv(t *testing.T, recv map[string]any) SufficiencyInputs {
|
||||
t.Helper()
|
||||
b, _ := json.Marshal(recv)
|
||||
var s SufficiencyInputs
|
||||
if err := json.Unmarshal(b, &s); err != nil {
|
||||
t.Fatalf("decode receiver: %v", err)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func boolField(m map[string]interface{}, k string) bool {
|
||||
b, _ := m[k].(bool)
|
||||
return b
|
||||
}
|
||||
|
||||
func TestParityAgainstPythonOracle(t *testing.T) {
|
||||
cases := loadTruth(t)
|
||||
if len(cases) == 0 {
|
||||
// TestFrozenTruthReplay — replay the frozen oracle truth VERBATIM, in the
|
||||
// recorded call order, against the ported functions.
|
||||
func TestFrozenTruthReplay(t *testing.T) {
|
||||
var env struct {
|
||||
FrozenNow string `json:"frozen_now"`
|
||||
Cases []frozenCase `json:"cases"`
|
||||
}
|
||||
if err := json.Unmarshal([]byte(frozenTruthJSON), &env); err != nil {
|
||||
t.Fatalf("decode truth envelope: %v", err)
|
||||
}
|
||||
if len(env.Cases) == 0 {
|
||||
t.Fatal("no truth cases — the parity gate is empty")
|
||||
}
|
||||
for _, c := range cases {
|
||||
if env.FrozenNow != "2026-10-13T10:30:00.123456+00:00" {
|
||||
t.Fatalf("frozen clock moved: %q", env.FrozenNow)
|
||||
}
|
||||
savedClock := clock
|
||||
clock = func() string { return env.FrozenNow }
|
||||
t.Cleanup(func() { clock = savedClock })
|
||||
|
||||
for i, c := range env.Cases {
|
||||
var actual any
|
||||
switch c.Fn {
|
||||
case "classify":
|
||||
body := str(c.Input, "body")
|
||||
in := ClassifyInput{
|
||||
Body: body,
|
||||
HasLocalRepro: boolField(c.Input, "has_local_repro"),
|
||||
OwningAuthorityKnown: boolField(c.Input, "owning_authority_known"),
|
||||
TestableLocally: boolField(c.Input, "testable_locally"),
|
||||
NarrowAndReversible: boolField(c.Input, "narrow_and_reversible"),
|
||||
PipelineSHA: str(c.Input, "pipeline_sha"),
|
||||
}
|
||||
d := ClassifyRoute(in)
|
||||
e := c.Expected
|
||||
jsonEq(t, c.Name, "route", d.Route, e["route"])
|
||||
jsonEq(t, c.Name, "confidence", d.Confidence, e["confidence"])
|
||||
jsonEq(t, c.Name, "reasons", d.Reasons, e["reasons"])
|
||||
jsonEq(t, c.Name, "risk", d.RiskFlags, e["risk"])
|
||||
jsonEq(t, c.Name, "uncertainty", d.UncertaintyFlags, e["uncertainty"])
|
||||
jsonEq(t, c.Name, "floor", d.Floor, e["floor"])
|
||||
jsonEq(t, c.Name, "task_description", d.TaskDescription, e["task_description"])
|
||||
case "derive":
|
||||
in := DeriveInput{Title: str(c.Input, "title"), Body: str(c.Input, "body"), Ambiguous: boolField(c.Input, "ambiguous")}
|
||||
s := DeriveSufficiency(in)
|
||||
e := c.Expected
|
||||
jsonEq(t, c.Name, "local_authority", s.LocalAuthority, e["local_authority"])
|
||||
jsonEq(t, c.Name, "external_dependency", s.ExternalDependency, e["external_dependency"])
|
||||
jsonEq(t, c.Name, "version_freshness_risk", s.VersionFreshnessRisk, e["version_freshness_risk"])
|
||||
jsonEq(t, c.Name, "verifiability", s.Verifiability, e["verifiability"])
|
||||
jsonEq(t, c.Name, "scope_novelty", s.ScopeNovelty, e["scope_novelty"])
|
||||
unk := s.Unknowns()
|
||||
if unk == nil {
|
||||
unk = []string{}
|
||||
}
|
||||
jsonEq(t, c.Name, "unknowns", unk, e["unknowns"])
|
||||
jsonEq(t, c.Name, "task_class", s.Provenance["task_class_language"], e["task_class"])
|
||||
case "floor_trigger":
|
||||
got := BodyHasFloorTrigger(str(c.Input, "body"))
|
||||
jsonEq(t, c.Name, "result", got, c.Expected["result"])
|
||||
case "_now":
|
||||
actual = now()
|
||||
case "body_has_floor_trigger":
|
||||
actual = BodyHasFloorTrigger(c.Args[0].(string))
|
||||
case "floor_applies":
|
||||
var flags []string
|
||||
b, _ := json.Marshal(c.Input["flags"])
|
||||
json.Unmarshal(b, &flags)
|
||||
got := FloorApplies(str(c.Input, "route"), flags)
|
||||
jsonEq(t, c.Name, "result", got, c.Expected["result"])
|
||||
case "escalate":
|
||||
base := RouteDecision{Route: str(c.Input, "decision_route"), Reasons: []string{}}
|
||||
out, err := escalateRoute(base, str(c.Input, "new_route"), str(c.Input, "trigger"), str(c.Input, "evidence"))
|
||||
if err != nil {
|
||||
t.Errorf("%s: escalate errored: %v", c.Name, err)
|
||||
continue
|
||||
}
|
||||
e := c.Expected
|
||||
jsonEq(t, c.Name, "route", out.Route, e["route"])
|
||||
jsonEq(t, c.Name, "previous", out.PreviousRoute, e["previous"])
|
||||
jsonEq(t, c.Name, "decided_by", out.DecidedBy, e["decided_by"])
|
||||
jsonEq(t, c.Name, "trigger", out.Trigger, e["trigger"])
|
||||
case "downgrade":
|
||||
base := RouteDecision{Route: str(c.Input, "base_route"), Floor: boolField(c.Input, "base_floor"), Reasons: []string{}}
|
||||
out, err := downgradeRoute(base, str(c.Input, "new_route"), "trigger", "evidence")
|
||||
e := c.Expected
|
||||
if e["error"] != nil {
|
||||
if err == nil {
|
||||
t.Errorf("%s: expected FloorViolation, got none", c.Name)
|
||||
} else if err.Error()[:14] != "FloorViolation" {
|
||||
t.Errorf("%s: expected FloorViolation, got %v", c.Name, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("%s: unexpected error: %v", c.Name, err)
|
||||
continue
|
||||
}
|
||||
jsonEq(t, c.Name, "route", out.Route, e["route"])
|
||||
case "evidence_short":
|
||||
base := RouteDecision{Route: "LOCAL_FIX", Reasons: []string{}}
|
||||
_, err := EscalateOnVerificationFailure(base, str(c.Input, "evidence"))
|
||||
got := "ACCEPTED"
|
||||
if err != nil {
|
||||
got = "REJECTED"
|
||||
}
|
||||
jsonEq(t, c.Name, "result", got, c.Expected["result"])
|
||||
case "dispatch":
|
||||
floored := ClassifyRoute(ClassifyInput{Body: "adopt a replacement for the memory index"})
|
||||
d := BuildResearchDispatch(str(c.Input, "task_id"), str(c.Input, "description"), floored)
|
||||
e := c.Expected
|
||||
jsonEq(t, c.Name, "task_id", d["task_id"], e["task_id"])
|
||||
jsonEq(t, c.Name, "title", d["title"], e["title"])
|
||||
jsonEq(t, c.Name, "route", d["route"], e["route"])
|
||||
jsonEq(t, c.Name, "floor", d["floor"], e["floor"])
|
||||
jsonEq(t, c.Name, "executor_entry", d["executor_entry"], e["executor_entry"])
|
||||
actual = FloorApplies(c.Args[0].(string), asStrSlice(t, c.Args[1]))
|
||||
case "_floor_applies_from_flags":
|
||||
actual = FloorAppliesFromFlags(asStrSlice(t, c.Args[0]), asStrSlice(t, c.Args[1]), c.Args[2].(string))
|
||||
case "_floor_reason":
|
||||
actual = FloorReason(asStrSlice(t, c.Args[0]), asStrSlice(t, c.Args[1]), c.Args[2].(string))
|
||||
case "_detect_external_refs":
|
||||
actual = DetectExternalRefs(c.Args[0].(string))
|
||||
case "SufficiencyInputs.unknowns":
|
||||
s := suffFromRecv(t, c.Recv)
|
||||
actual = s.Unknowns()
|
||||
default:
|
||||
t.Errorf("unknown truth fn %q", c.Fn)
|
||||
t.Fatalf("case %d: unknown fn %q", i, c.Fn)
|
||||
}
|
||||
var want any
|
||||
if err := json.Unmarshal(c.Expected, &want); err != nil {
|
||||
t.Fatalf("case %d (%s): decode expected: %v", i, c.Fn, err)
|
||||
}
|
||||
// recv-form cases carry {"result": …}; everything else is bare.
|
||||
if c.Recv != nil {
|
||||
want = want.(map[string]any)["result"]
|
||||
}
|
||||
if got := canonical(t, actual); !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("case %d (%s): got %v want %v", i, c.Fn, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── seam unit battery: dataclass surface + recording family ──────────────
|
||||
// The frozen envelope cannot carry dataclass args/returns, so these pin the
|
||||
// classify/derive branches and the transition/recording behavior against
|
||||
// oracle-captured values (see /tmp capture in the wave log; the same
|
||||
// matrix is driven live by scripts/check_adaptive_router_parity.py).
|
||||
|
||||
type classifyCase struct {
|
||||
name string
|
||||
body string
|
||||
hasLocalRepro bool
|
||||
owningKnown bool
|
||||
testable bool
|
||||
narrow bool
|
||||
pipelineSHA string
|
||||
route string
|
||||
confidence string
|
||||
floor bool
|
||||
risk []string
|
||||
uncertainty []string
|
||||
reasons []string
|
||||
task string
|
||||
localAuthority string
|
||||
verifiability string
|
||||
}
|
||||
|
||||
var classifyMatrix = []classifyCase{
|
||||
{"local_fix_happy", "fix the broken regex crash", true, true, true, true, "UNKNOWN",
|
||||
"LOCAL_FIX", "HIGH", false, []string{}, []string{},
|
||||
[]string{"all LOCAL_FIX conditions met; no unknowns; no floor"},
|
||||
"fix the broken regex crash", "GROUNDED", "TESTABLE"},
|
||||
{"local_fix_via_none", "fix the typo", true, true, false, true, "UNKNOWN",
|
||||
"LOCAL_FIX", "HIGH", false, []string{}, []string{},
|
||||
[]string{"all LOCAL_FIX conditions met; no unknowns; no floor"},
|
||||
"fix the typo", "GROUNDED", "NONE"},
|
||||
{"not_grounded", "fix the crash", false, true, true, true, "UNKNOWN",
|
||||
"STANDARD_CHANGE", "MEDIUM", false, []string{}, []string{},
|
||||
[]string{"clear change, existing authority, no consequential selection"},
|
||||
"fix the crash", "NOT_GROUNDED", "TESTABLE"},
|
||||
{"standard_change", "add a progress bar to the export dialog", true, true, true, true, "UNKNOWN",
|
||||
"STANDARD_CHANGE", "MEDIUM", false, []string{}, []string{},
|
||||
[]string{"clear change, existing authority, no consequential selection"},
|
||||
"add a progress bar to the export dialog", "GROUNDED", "TESTABLE"},
|
||||
{"floor_markers", "adopt a replacement for the memory index", false, false, false, false, "UNKNOWN",
|
||||
"RESEARCHED_CHANGE", "HIGH", true, []string{}, []string{},
|
||||
[]string{"deep-research floor: adopt, replacement"},
|
||||
"adopt a replacement for the memory index", "NOT_GROUNDED", "NONE"},
|
||||
{"floor_risk", "this creates a new authority path", false, false, false, false, "UNKNOWN",
|
||||
"RESEARCHED_CHANGE", "HIGH", true, []string{"new_authority"}, []string{},
|
||||
[]string{"deep-research floor: new_authority"},
|
||||
"this creates a new authority path", "NOT_GROUNDED", "NONE"},
|
||||
{"floor_uncertainty", "there are competing designs here", true, true, false, false, "UNKNOWN",
|
||||
"RESEARCHED_CHANGE", "HIGH", true, []string{}, []string{"competing_designs"},
|
||||
[]string{"deep-research floor: competing_designs"},
|
||||
"there are competing designs here", "GROUNDED", "NONE"},
|
||||
{"risk_no_floor", "integrate a new dependency for parsing", false, false, false, false, "UNKNOWN",
|
||||
"RESEARCHED_CHANGE", "HIGH", false, []string{"new_dependency"}, []string{},
|
||||
[]string{"risk/uncertainty flags: new_dependency", "RESEARCHED_CHANGE: external evidence and adversarial validation justified"},
|
||||
"integrate a new dependency for parsing", "NOT_GROUNDED", "NONE"},
|
||||
{"uncertainty_no_floor", "the prior artifact matters here", false, false, false, false, "UNKNOWN",
|
||||
"RESEARCHED_CHANGE", "HIGH", false, []string{}, []string{"prior_artifact_matters"},
|
||||
[]string{"risk/uncertainty flags: prior_artifact_matters", "RESEARCHED_CHANGE: external evidence and adversarial validation justified"},
|
||||
"the prior artifact matters here", "NOT_GROUNDED", "NONE"},
|
||||
{"sha_passthrough", "fix the crash", true, true, true, true, "abc123def456",
|
||||
"LOCAL_FIX", "HIGH", false, []string{}, []string{},
|
||||
[]string{"all LOCAL_FIX conditions met; no unknowns; no floor"},
|
||||
"fix the crash", "GROUNDED", "TESTABLE"},
|
||||
}
|
||||
|
||||
func TestClassifyMatrix(t *testing.T) {
|
||||
savedClock := clock
|
||||
clock = func() string { return "2026-10-13T10:30:00.123456+00:00" }
|
||||
defer func() { clock = savedClock }()
|
||||
for _, c := range classifyMatrix {
|
||||
sha := c.pipelineSHA
|
||||
if sha == "" {
|
||||
sha = "UNKNOWN"
|
||||
}
|
||||
d := ClassifyRoute(ClassifyInput{
|
||||
Body: c.body, HasLocalRepro: c.hasLocalRepro,
|
||||
OwningAuthorityKnown: c.owningKnown, TestableLocally: c.testable,
|
||||
NarrowAndReversible: c.narrow, PipelineSHA: sha,
|
||||
})
|
||||
if d.Route != c.route || d.Confidence != c.confidence || d.Floor != c.floor {
|
||||
t.Errorf("%s: got %s/%s/floor=%v want %s/%s/floor=%v", c.name,
|
||||
d.Route, d.Confidence, d.Floor, c.route, c.confidence, c.floor)
|
||||
}
|
||||
if got, want := canonical(t, d.Reasons), canonical(t, c.reasons); !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("%s reasons: got %v want %v", c.name, got, want)
|
||||
}
|
||||
if got, want := canonical(t, d.RiskFlags), canonical(t, c.risk); !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("%s risk: got %v want %v", c.name, got, want)
|
||||
}
|
||||
if got, want := canonical(t, d.UncertaintyFlags), canonical(t, c.uncertainty); !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("%s uncertainty: got %v want %v", c.name, got, want)
|
||||
}
|
||||
if d.TaskDescription != c.task {
|
||||
t.Errorf("%s task: got %q want %q", c.name, d.TaskDescription, c.task)
|
||||
}
|
||||
if d.BaselineSHA != sha {
|
||||
t.Errorf("%s sha: got %q want %q", c.name, d.BaselineSHA, sha)
|
||||
}
|
||||
if d.DecidedBy != "deterministic" || d.DecidedAt != "2026-10-13T10:30:00.123456+00:00" {
|
||||
t.Errorf("%s decided: %q %q", c.name, d.DecidedBy, d.DecidedAt)
|
||||
}
|
||||
if d.Sufficiency == nil || d.Sufficiency.LocalAuthority != c.localAuthority {
|
||||
t.Errorf("%s legacy shim: %+v", c.name, d.Sufficiency)
|
||||
}
|
||||
if d.Sufficiency.Verifiability != c.verifiability ||
|
||||
d.Sufficiency.ExternalDependency != "NONE" ||
|
||||
d.Sufficiency.VersionFreshnessRisk != "NONE" ||
|
||||
d.Sufficiency.ScopeNovelty != "KNOWN_PATTERN" {
|
||||
t.Errorf("%s legacy shim fields: %+v", c.name, d.Sufficiency)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type deriveCase struct {
|
||||
name string
|
||||
title string
|
||||
body string
|
||||
evidence *[]string
|
||||
ambiguous bool
|
||||
la string
|
||||
external string
|
||||
verif string
|
||||
scope string
|
||||
vfr string
|
||||
taskClass string
|
||||
mechUnc string
|
||||
ambiguousS string
|
||||
}
|
||||
|
||||
func strPtrSlice(s ...string) *[]string { return &s }
|
||||
|
||||
var deriveMatrix = []deriveCase{
|
||||
{"grounded_fix", "fix crash", "the parser crashes on empty input, see tests/test_parse.py", nil, false,
|
||||
"GROUNDED", "NONE", "TESTABLE", "KNOWN_PATTERN", "NONE", "defect", "False", "False"},
|
||||
{"mechanism", "investigate", "performance is bad under load, benchmark needed", nil, false,
|
||||
"NOT_GROUNDED", "NONE", "NONE", "KNOWN_PATTERN", "NONE", "neutral", "True", "False"},
|
||||
{"measurement_override", "improve speed", "latency spikes under load; run the tests to see", nil, false,
|
||||
"NOT_GROUNDED", "NONE", "MEASUREMENT_REQUIRED", "KNOWN_PATTERN", "NONE", "neutral", "True", "False"},
|
||||
{"external_url", "read the docs", "see https://example.com/api for the endpoint", nil, false,
|
||||
"NOT_GROUNDED", "PRESENT", "NONE", "KNOWN_PATTERN", "NONE", "neutral", "False", "False"},
|
||||
{"version_risk", "upgrade", "upgrade the library to the latest version, current behaviour differs", nil, false,
|
||||
"NOT_GROUNDED", "PRESENT", "NONE", "KNOWN_PATTERN", "RISK", "neutral", "False", "False"},
|
||||
{"inspection", "explain", "why does the failing test fail", nil, false,
|
||||
"GROUNDED", "NONE", "NONE", "KNOWN_PATTERN", "NONE", "neutral", "False", "False"},
|
||||
{"new_subsystem", "adopt", "this adopts a replacement architecture for storage", nil, false,
|
||||
"NOT_GROUNDED", "NONE", "NONE", "NEW_SUBSYSTEM", "NONE", "neutral", "False", "False"},
|
||||
{"contract_repo", "research", "survey the options", strPtrSlice("repo"), false,
|
||||
"GROUNDED", "NONE", "NONE", "KNOWN_PATTERN", "NONE", "neutral", "False", "False"},
|
||||
{"contract_web", "research", "survey the options", strPtrSlice("web"), false,
|
||||
"NOT_GROUNDED", "NONE", "NONE", "KNOWN_PATTERN", "NONE", "neutral", "False", "False"},
|
||||
{"ambiguous", "write", "a poem about the sea", nil, true,
|
||||
"GROUNDED", "NONE", "NONE", "KNOWN_PATTERN", "NONE", "neutral", "False", "True"},
|
||||
}
|
||||
|
||||
func TestDeriveMatrix(t *testing.T) {
|
||||
for _, c := range deriveMatrix {
|
||||
s := DeriveSufficiency(DeriveInput{Title: c.title, Body: c.body, EvidenceClasses: c.evidence, Ambiguous: c.ambiguous})
|
||||
if s.LocalAuthority != c.la || s.ExternalDependency != c.external ||
|
||||
s.Verifiability != c.verif || s.ScopeNovelty != c.scope ||
|
||||
s.VersionFreshnessRisk != c.vfr {
|
||||
t.Errorf("%s: got %s/%s/%s/%s/%s", c.name,
|
||||
s.LocalAuthority, s.ExternalDependency, s.Verifiability, s.ScopeNovelty, s.VersionFreshnessRisk)
|
||||
}
|
||||
p := s.Provenance
|
||||
if p["task_class_language"] != c.taskClass || p["mechanism_uncertain"] != c.mechUnc ||
|
||||
p["ambiguous"] != c.ambiguousS {
|
||||
t.Errorf("%s provenance: task=%q mech=%q amb=%q", c.name,
|
||||
p["task_class_language"], p["mechanism_uncertain"], p["ambiguous"])
|
||||
}
|
||||
if p["local_authority"] == "" || p["external_dependency"] == "" ||
|
||||
p["version_freshness_risk"] == "" || p["verifiability"] == "" || p["scope_novelty"] == "" {
|
||||
t.Errorf("%s: provenance key dropped: %v", c.name, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestTransitions — escalate/downgrade/verification-failure incl. the floor
|
||||
// guard, invalid-route errors, and log_path recording (log content is
|
||||
// kind-shaped; byte-parity lives in the live sweep).
|
||||
func TestTransitions(t *testing.T) {
|
||||
savedClock := clock
|
||||
clock = func() string { return "2026-10-13T10:30:00.123456+00:00" }
|
||||
defer func() { clock = savedClock }()
|
||||
base := RouteDecision{Route: "LOCAL_FIX", Reasons: []string{}, RiskFlags: []string{},
|
||||
UncertaintyFlags: []string{}, Confidence: "HIGH", TaskDescription: "fix the crash",
|
||||
BaselineSHA: "UNKNOWN"}
|
||||
|
||||
up, err := escalateRoute(base, "RESEARCHED_CHANGE", "verify_fail", "evidence", "")
|
||||
if err != nil || up.Route != "RESEARCHED_CHANGE" || up.PreviousRoute != "LOCAL_FIX" ||
|
||||
up.DecidedBy != "escalation" || up.Trigger != "verify_fail" {
|
||||
t.Errorf("escalate: %+v %v", up, err)
|
||||
}
|
||||
if _, err := escalateRoute(base, "NOPE", "t", "e", ""); err == nil {
|
||||
t.Error("escalate invalid route: want error")
|
||||
}
|
||||
|
||||
dir := t.TempDir()
|
||||
logPath := filepath.Join(dir, "route.jsonl")
|
||||
floored := ClassifyRoute(ClassifyInput{Body: "adopt a replacement for the memory index"})
|
||||
if !floored.Floor {
|
||||
t.Fatal("fixture: expected floor on markers body")
|
||||
}
|
||||
if _, err := downgradeRoute(floored, "STANDARD_CHANGE", "efficiency", "faster", ""); err == nil {
|
||||
t.Error("downgrade across floor: want FloorViolation")
|
||||
} else if err.Error()[:14] != "FloorViolation" {
|
||||
t.Errorf("downgrade error class: %v", err)
|
||||
}
|
||||
down, err := downgradeRoute(base, "STANDARD_CHANGE", "try", "ev", logPath)
|
||||
if err != nil || down.Route != "STANDARD_CHANGE" || down.DecidedBy != "downgrade" {
|
||||
t.Errorf("downgrade ok: %+v %v", down, err)
|
||||
}
|
||||
if _, err := downgradeRoute(base, "NOPE", "t", "e", ""); err == nil {
|
||||
t.Error("downgrade invalid route: want error")
|
||||
}
|
||||
raw, _ := os.ReadFile(logPath)
|
||||
var ev map[string]any
|
||||
if err := json.Unmarshal(line0(t, raw), &ev); err != nil {
|
||||
t.Fatalf("downgrade log line: %v", err)
|
||||
}
|
||||
if ev["kind"] != "DOWNGRADE" || ev["new_route"] != "STANDARD_CHANGE" || ev["previous_route"] != "LOCAL_FIX" {
|
||||
t.Errorf("downgrade log shape: %v", ev)
|
||||
}
|
||||
|
||||
if _, err := EscalateOnVerificationFailure(base, "test failed", ""); err == nil {
|
||||
t.Error("short evidence: want rejection")
|
||||
}
|
||||
long := "the fix works locally but the production parser accepts different input shapes — input contract uncertain"
|
||||
out, err := EscalateOnVerificationFailure(base, long, logPath)
|
||||
if err != nil || out.Trigger != "verification_failure_external_uncertainty" || out.Route != "RESEARCHED_CHANGE" {
|
||||
t.Errorf("verify-fail: %+v %v", out, err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestRecordFamily — RecordTransition / RecordFinalRoute /
|
||||
// RecordRouteDecision / RecordTransitionWith incl. the dispatch payload and
|
||||
// the INITIAL vs transition event shapes.
|
||||
func TestRecordFamily(t *testing.T) {
|
||||
savedClock := clock
|
||||
clock = func() string { return "2026-10-13T10:30:00.123456+00:00" }
|
||||
defer func() { clock = savedClock }()
|
||||
dir := t.TempDir()
|
||||
d := ClassifyRoute(ClassifyInput{Body: "adopt a replacement for the memory index"})
|
||||
|
||||
p1 := filepath.Join(dir, "r1.jsonl")
|
||||
if err := RecordTransition("ESCALATION", d, p1); err != nil {
|
||||
t.Fatalf("record: %v", err)
|
||||
}
|
||||
p2 := filepath.Join(dir, "sub", "r2.jsonl")
|
||||
if err := RecordFinalRoute(d, p2); err != nil {
|
||||
t.Fatalf("final: %v", err)
|
||||
}
|
||||
p3 := filepath.Join(dir, "r3.jsonl")
|
||||
dispatch := BuildResearchDispatch("T-1", "research the memory index options", d)
|
||||
if dispatch["task_id"] != "T-1" || dispatch["title"] != "research the memory index options" ||
|
||||
dispatch["route"] != "RESEARCHED_CHANGE" || dispatch["floor"] != true ||
|
||||
dispatch["executor_entry"] != "research_stage_executor.py --stage ISSUE (with issue-shaped input)" {
|
||||
t.Errorf("dispatch shape: %v", dispatch)
|
||||
}
|
||||
if err := RecordRouteDecision(d, p3, dispatch); err != nil {
|
||||
t.Fatalf("route decision: %v", err)
|
||||
}
|
||||
p4 := filepath.Join(dir, "r4.jsonl")
|
||||
if err := RecordTransitionWith("INITIAL_ROUTE", d, p4, nil); err != nil {
|
||||
t.Fatalf("with nil dispatch: %v", err)
|
||||
}
|
||||
|
||||
ev := readOne(t, p1)
|
||||
if ev["kind"] != "ESCALATION" || ev["new_route"] != "RESEARCHED_CHANGE" || ev["floor"] != true {
|
||||
t.Errorf("escalation event: %v", ev)
|
||||
}
|
||||
if _, ok := ev["provenance"]; !ok {
|
||||
t.Errorf("event drops provenance: %v", ev)
|
||||
}
|
||||
ev2 := readOne(t, p2)
|
||||
if ev2["kind"] != "FINAL_ROUTE" {
|
||||
t.Errorf("final event: %v", ev2)
|
||||
}
|
||||
ev3 := readOne(t, p3)
|
||||
if ev3["kind"] != "INITIAL_ROUTE" || ev3["dispatch"] == nil {
|
||||
t.Errorf("initial+dispatch event: %v", ev3)
|
||||
}
|
||||
if _, hasPrev := ev3["previous_route"]; hasPrev {
|
||||
t.Errorf("initial event carries transition keys: %v", ev3)
|
||||
}
|
||||
ev4 := readOne(t, p4)
|
||||
if _, hasDisp := ev4["dispatch"]; hasDisp {
|
||||
t.Errorf("nil dispatch recorded: %v", ev4)
|
||||
}
|
||||
}
|
||||
|
||||
func line0(t *testing.T, raw []byte) []byte {
|
||||
t.Helper()
|
||||
lines := splitLines(raw)
|
||||
if len(lines) == 0 {
|
||||
t.Fatal("empty log")
|
||||
}
|
||||
return lines[0]
|
||||
}
|
||||
|
||||
func readOne(t *testing.T, path string) map[string]any {
|
||||
t.Helper()
|
||||
raw, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Fatalf("read %s: %v", path, err)
|
||||
}
|
||||
var ev map[string]any
|
||||
if err := json.Unmarshal(line0(t, raw), &ev); err != nil {
|
||||
t.Fatalf("decode %s: %v", path, err)
|
||||
}
|
||||
return ev
|
||||
}
|
||||
|
||||
func splitLines(raw []byte) [][]byte {
|
||||
var out [][]byte
|
||||
start := 0
|
||||
for i, b := range raw {
|
||||
if b == '\n' {
|
||||
if i > start {
|
||||
out = append(out, raw[start:i])
|
||||
}
|
||||
start = i + 1
|
||||
}
|
||||
}
|
||||
if start < len(raw) {
|
||||
out = append(out, raw[start:])
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// TestRuneSafeTruncation — Python str[:200] is char-based; multibyte bodies
|
||||
// must not split a rune.
|
||||
func TestRuneSafeTruncation(t *testing.T) {
|
||||
body := ""
|
||||
for i := 0; i < 150; i++ {
|
||||
body += "é—"
|
||||
}
|
||||
d := ClassifyRoute(ClassifyInput{Body: body})
|
||||
if len([]rune(d.TaskDescription)) != 200 {
|
||||
t.Errorf("rune length: %d", len([]rune(d.TaskDescription)))
|
||||
}
|
||||
for _, r := range d.TaskDescription {
|
||||
if r == 0xFFFD {
|
||||
t.Fatal("split rune in truncation")
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user