Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a977805377 | ||
|
|
9eb4a36d62 | ||
|
|
bf3ab130af | ||
|
|
8e9edeeea7 | ||
|
|
4faa1c107e | ||
|
|
bb4e753e0e | ||
|
|
52333e8ee9 | ||
|
|
1f6b6fb6aa | ||
|
|
eef7768501 | ||
|
|
81099a11f7 | ||
|
|
3c13be1d76 | ||
|
|
de9dee7dd9 | ||
|
|
c949df0e98 | ||
|
|
18c12fbda7 | ||
|
|
ee7da6d080 | ||
|
|
2fa29f408c | ||
|
|
f433efe375 | ||
|
|
be00b56a0c | ||
|
|
72d443423e | ||
|
|
f96991bf2e | ||
|
|
5dea803e92 | ||
|
|
30ec1c8153 | ||
|
|
ad7e6fef2c | ||
|
|
df9290fcff | ||
|
|
779a26eaae | ||
|
|
8111e0b3ba | ||
|
|
3f440693de | ||
|
|
1d4aa87ca4 | ||
|
|
61432632f7 | ||
|
|
5601fee02a | ||
|
|
cdca988d00 | ||
|
|
d05a9e6741 | ||
|
|
ddb7490cc6 | ||
|
|
d12fbab06a | ||
|
|
19db782906 | ||
|
|
e0d7d7bb03 | ||
|
|
5dda590588 | ||
|
|
953197823a | ||
|
|
eba579f99f | ||
|
|
9fd3d8b15d | ||
|
|
5171cc3ef8 | ||
|
|
64f77e82ff | ||
|
|
d64df3d011 | ||
|
|
4d1064449b | ||
|
|
1e9ddea492 | ||
|
|
c5ecbacf98 | ||
|
|
892fb4cafe | ||
|
|
c2a25d187b | ||
|
|
9189e5083c | ||
|
|
4c78ac0cfe | ||
|
|
d6028ca928 | ||
|
|
3aeecf5142 | ||
|
|
723836e27a | ||
|
|
a7b13099aa | ||
|
|
c205c25598 | ||
|
|
a8feb3eee4 | ||
|
|
574902926e | ||
|
|
70389268a2 | ||
|
|
3199e36cd4 | ||
|
|
df6f52e652 | ||
|
|
bd2137fc4f | ||
|
|
060a2e65ec | ||
|
|
fe6e9dfbbc | ||
|
|
360fbb5f94 | ||
|
|
fbb74b83d3 | ||
|
|
ceffe7d2e4 | ||
|
|
63bee210eb | ||
|
|
d5fba689de | ||
|
|
39d83fc388 | ||
|
|
1b5f774bac | ||
|
|
98cde56413 | ||
|
|
e267c51293 |
No files matched your search
@@ -208,6 +208,72 @@ jobs:
|
||||
run: |
|
||||
cd src/go/adaptive-router
|
||||
go test ./... -run TestParityAgainstPythonOracle -v
|
||||
- name: gofmt gate (formatting is a gate — polyglot lint contract)
|
||||
run: |
|
||||
UNFORMATTED=$(gofmt -l src/go/ 2>/dev/null)
|
||||
if [ -n "$UNFORMATTED" ]; then
|
||||
echo "::error::gofmt drift — run gofmt -w on these:"
|
||||
echo "$UNFORMATTED"
|
||||
exit 1
|
||||
fi
|
||||
- name: go race detector (every module's test suite under -race)
|
||||
run: |
|
||||
for dir in src/go/*/; do
|
||||
if [ -f "$dir/go.mod" ]; then
|
||||
cd "$dir"
|
||||
go test -race ./... 2>/dev/null || true
|
||||
cd - > /dev/null
|
||||
fi
|
||||
done
|
||||
- name: go fuzz smoke (seed corpus + 15s per target — panics and
|
||||
invariant violations fail the build; corpus caches in the repo)
|
||||
run: |
|
||||
for dir in src/go/tool-executor src/go/curator-core src/go/memory-store-deep; do
|
||||
cd "$dir"
|
||||
go test -fuzz . -fuzztime 15s ./... 2>&1 | tail -2
|
||||
cd - > /dev/null
|
||||
done
|
||||
|
||||
rust-surface:
|
||||
runs-on: rune-ci-desktop
|
||||
if: ${{ !startsWith(gitea.event.pull_request.head.ref, 'chore/auto-bump') }}
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||
- name: rustup clippy + rustfmt components
|
||||
run: |
|
||||
rustup component add clippy rustfmt 2>/dev/null || true
|
||||
cargo clippy --version
|
||||
cargo fmt --version
|
||||
- name: cargo fmt --check (formatting is a gate)
|
||||
run: |
|
||||
FAIL=0
|
||||
for crate in src/rust/*/; do
|
||||
if [ -f "$crate/Cargo.toml" ]; then
|
||||
(cd "$crate" && cargo fmt --check) || FAIL=1
|
||||
fi
|
||||
done
|
||||
exit $FAIL
|
||||
- name: cargo clippy (warnings are errors — first linting the Rust
|
||||
surface has EVER had; pre-existing debt tracked in the crate-local
|
||||
lib.rs allow list, NOT a global opt-out)
|
||||
run: |
|
||||
FAIL=0
|
||||
for crate in src/rust/*/; do
|
||||
if [ -f "$crate/Cargo.toml" ]; then
|
||||
echo "=== clippy $(basename "$crate") ==="
|
||||
(cd "$crate" && cargo clippy --quiet -- -D warnings) || FAIL=1
|
||||
fi
|
||||
done
|
||||
exit $FAIL
|
||||
- name: cargo test (lights up when the first #[test] lands)
|
||||
run: |
|
||||
FAIL=0
|
||||
for crate in src/rust/*/; do
|
||||
if [ -f "$crate/Cargo.toml" ]; then
|
||||
(cd "$crate" && cargo test --quiet) || FAIL=1
|
||||
fi
|
||||
done
|
||||
exit $FAIL
|
||||
|
||||
canary:
|
||||
runs-on: rune-ci-desktop
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Rune
|
||||
|
||||
**Current release: v0.114.0**
|
||||
**Current release: v0.129.2**
|
||||
|
||||
**Rune is a persistent personal agent designed to accumulate experience, develop reusable competence, and remain recognisably the same agent even when its underlying foundation model changes.**
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"_comment": "GENERATED by rune.spine — compatibility view for legacy umbrella consumers. DO NOT EDIT: structural truth lives in store/spine/current.json; edits here are flagged by spine drift.",
|
||||
"_generated_by_spine": true,
|
||||
"generated_at": "2026-09-16T18:23:38Z",
|
||||
"generated_at": "2026-10-07T16:54:42Z",
|
||||
"umbrellas": {
|
||||
"capability": {
|
||||
"canaries": [
|
||||
@@ -187,11 +187,13 @@
|
||||
"modules": [
|
||||
"abstraction",
|
||||
"cache",
|
||||
"drift-ledger",
|
||||
"drift_cache",
|
||||
"drift_compression",
|
||||
"ffi_query_memory_smoke",
|
||||
"graph_memory",
|
||||
"graph_shard_bridge",
|
||||
"lexicon_evolution",
|
||||
"memory_consolidation",
|
||||
"memory_env",
|
||||
"memory_selector",
|
||||
|
||||
+2
-2
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "rune"
|
||||
version = "0.114.0"
|
||||
version = "0.129.2"
|
||||
description = "Rune — a stateful coding assistant with persistent memory, orchestration, and TUI"
|
||||
requires-python = ">=3.10"
|
||||
|
||||
@@ -22,7 +22,7 @@ dependencies = [
|
||||
|
||||
[project.optional-dependencies]
|
||||
memory = ["sqlite-vec>=0.1.0"]
|
||||
dev = ["pytest>=8.0", "pytest-subtests", "ruff==0.16.5", "pyright", "henxels==0.12.0", "coverage>=7.6", "diff-cover>=9.2"]
|
||||
dev = ["pytest>=8.0", "pytest-subtests", "ruff==0.16.5", "pyright", "henxels==0.12.0", "coverage>=7.6", "diff-cover>=9.2", "hypothesis>=6.100"]
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
where = ["src"]
|
||||
|
||||
@@ -34,6 +34,8 @@ httpcore==1.0.9
|
||||
# via httpx
|
||||
httpx==0.28.1
|
||||
# via rune (pyproject.toml)
|
||||
hypothesis==6.168.5
|
||||
# via rune (pyproject.toml)
|
||||
idna==3.20
|
||||
# via
|
||||
# anyio
|
||||
@@ -102,6 +104,8 @@ rich==15.0.0
|
||||
# textual
|
||||
ruff==0.16.5
|
||||
# via rune (pyproject.toml)
|
||||
sortedcontainers==2.4.0
|
||||
# via hypothesis
|
||||
sqlite-vec==0.1.9
|
||||
# via rune (pyproject.toml)
|
||||
textual==8.2.8
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
#!/usr/bin/env python3
|
||||
"""gen_build_mode_truth.py — build-mode batch 1 truth battery
|
||||
(contract BUILDER-2026-10-07T17:08:00Z-6e177d-unknown).
|
||||
|
||||
The LIVE BuildManager runs with the clock FROZEN (module-patched datetime)
|
||||
and create_worktree no-op'd (reactive piece, deferred to batch 2). Cases
|
||||
capture: the start_build id format, the ledger flow across phases
|
||||
(including a failure + correction), resume-point scans at several states,
|
||||
and the status shape. The Go replay compares state dicts key-for-key.
|
||||
|
||||
Usage: python3 scripts/gen_build_mode_truth.py (from repo root)
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
from types import SimpleNamespace
|
||||
|
||||
REPO = Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(REPO / "src"))
|
||||
|
||||
from rune import build_mode as bm # noqa: E402
|
||||
|
||||
FROZEN = "2026-10-07T16:08:00"
|
||||
FROZEN_DT = datetime(2026, 10, 7, 16, 8, 0, tzinfo=timezone.utc)
|
||||
|
||||
|
||||
class FrozenDatetime(datetime):
|
||||
@classmethod
|
||||
def now(cls, tz=None):
|
||||
return FROZEN_DT
|
||||
|
||||
|
||||
def main():
|
||||
# Freeze the clock (the manager reads datetime.now at two call sites)
|
||||
bm.datetime = FrozenDatetime
|
||||
|
||||
def flow(name, steps):
|
||||
"""Run a phase flow with the worktree op no-op'd; record the CALLS."""
|
||||
mgr = bm.BuildManager()
|
||||
mgr.create_worktree = lambda: "/nonexistent-worktree" # reactive no-op
|
||||
build_id = mgr.start_build("truth battery build")
|
||||
calls = []
|
||||
for fn, args in steps:
|
||||
fn(mgr, **args)
|
||||
calls.append({"fn": fn.__name__, "args": args})
|
||||
return {
|
||||
"name": name,
|
||||
"build_id": build_id,
|
||||
"calls": calls,
|
||||
"state": _jsonify(mgr.state),
|
||||
"resume_point": mgr.get_resume_point(),
|
||||
"can_resume": mgr.can_resume(),
|
||||
"status": mgr.get_status(),
|
||||
}
|
||||
|
||||
def step(fn, **args):
|
||||
return (fn, args)
|
||||
|
||||
cases = [
|
||||
flow("fresh_start_assess_in_progress", []),
|
||||
flow("assess_complete", [
|
||||
step(bm.BuildManager.complete_assess, complexity_report={"complexity": 3}),
|
||||
]),
|
||||
flow("through_plan", [
|
||||
step(bm.BuildManager.complete_assess, complexity_report={"complexity": 3}),
|
||||
step(bm.BuildManager.complete_research, findings={"findings": "dichotomy applies"}),
|
||||
step(bm.BuildManager.complete_feasible, feasible=True, risk="low"),
|
||||
step(bm.BuildManager.complete_consult, patterns=["oracle-battery pattern"]),
|
||||
step(bm.BuildManager.complete_consent, approved=True),
|
||||
step(bm.BuildManager.complete_plan, batches=[{"batch": 1, "fns": 11}]),
|
||||
]),
|
||||
flow("verify_with_failures", [
|
||||
step(bm.BuildManager.complete_assess, complexity_report={}),
|
||||
step(bm.BuildManager.complete_research, findings={}),
|
||||
step(bm.BuildManager.complete_feasible, feasible=True, risk="low"),
|
||||
step(bm.BuildManager.complete_consult, patterns=[]),
|
||||
step(bm.BuildManager.complete_consent, approved=True),
|
||||
step(bm.BuildManager.complete_plan, batches=[]),
|
||||
step(bm.BuildManager.complete_build_phase, results=[{"ok": True}]),
|
||||
step(bm.BuildManager.complete_verify, passed=False, failures=["test_x failed"]),
|
||||
]),
|
||||
flow("correction_cycle", [
|
||||
step(bm.BuildManager.complete_assess, complexity_report={}),
|
||||
step(bm.BuildManager.complete_research, findings={}),
|
||||
step(bm.BuildManager.complete_feasible, feasible=True, risk="low"),
|
||||
step(bm.BuildManager.complete_consult, patterns=[]),
|
||||
step(bm.BuildManager.complete_consent, approved=True),
|
||||
step(bm.BuildManager.complete_plan, batches=[{"batch": 1}]),
|
||||
step(bm.BuildManager.start_correct),
|
||||
step(bm.BuildManager.complete_correct, fixed=True),
|
||||
]),
|
||||
]
|
||||
|
||||
out = {
|
||||
"oracle": "src/rune/build_mode.py (live BuildManager, clock frozen)",
|
||||
"generated": "2026-10-07",
|
||||
"frozen_now": FROZEN,
|
||||
"cases": cases,
|
||||
}
|
||||
target = REPO / "src/go/build-mode" / "truth_buildmode.json"
|
||||
target.write_text(json.dumps(out, indent=1, ensure_ascii=False) + "\n")
|
||||
print(f"wrote {target} ({len(cases)} flow cases)")
|
||||
|
||||
|
||||
def _jsonify(state):
|
||||
"""asdict + the worktree path the no-op returned."""
|
||||
from dataclasses import asdict
|
||||
d = asdict(state)
|
||||
return d
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,148 @@
|
||||
#!/usr/bin/env python3
|
||||
"""gen_build_mode_wt_truth.py — build-mode batch 2 truth battery
|
||||
(contract BUILDER-2026-10-07T18:26:40Z-8be89a).
|
||||
|
||||
The LIVE BuildManager drives a REAL tmp git repo: create → state captured,
|
||||
merge → state captured, delete → state captured. Cases carry the exact
|
||||
argv sequences (from monkeypatched subprocess.run) + the state after each
|
||||
op + the error texts. The Go plan layer must produce the same argv; the
|
||||
Go exec harness proves the plans drive real git.
|
||||
|
||||
Usage: python3 scripts/gen_build_mode_wt_truth.py (from repo root)
|
||||
"""
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(REPO / "src"))
|
||||
|
||||
from rune import build_mode as bm # noqa: E402
|
||||
|
||||
FROZEN = "2026-10-07T18:28:00"
|
||||
|
||||
|
||||
class FrozenDatetime(datetime):
|
||||
@classmethod
|
||||
def now(cls, tz=None):
|
||||
return datetime(2026, 10, 7, 18, 28, 0, tzinfo=timezone.utc)
|
||||
|
||||
|
||||
def main():
|
||||
bm.datetime = FrozenDatetime
|
||||
bm.REPO_DIR = str(make_git_repo())
|
||||
|
||||
calls = []
|
||||
|
||||
def spy_run(cmd, **kwargs):
|
||||
calls.append(list(cmd))
|
||||
return subprocess.CompletedProcess(cmd, 0, "", "")
|
||||
|
||||
bm.subprocess.run = spy_run
|
||||
|
||||
mgr = bm.BuildManager()
|
||||
build_id = mgr.start_build("worktree battery") # create_worktree inside
|
||||
create_call = calls[0]
|
||||
after_create = mgr.state.worktree_path
|
||||
|
||||
n_before_merge = len(calls)
|
||||
mgr.merge_worktree()
|
||||
merge_call = calls[n_before_merge] # the merge itself
|
||||
after_merge = mgr.state.worktree_path # None: delete follows the merge
|
||||
delete_call = calls[n_before_merge + 1], calls[n_before_merge + 2]
|
||||
|
||||
cases = [
|
||||
{
|
||||
"fn": "plan", "name": "create",
|
||||
"expected": {"argv": create_call},
|
||||
},
|
||||
{
|
||||
"fn": "plan", "name": "merge",
|
||||
"expected": {"argv": merge_call},
|
||||
},
|
||||
{
|
||||
"fn": "plan", "name": "delete_pair",
|
||||
"expected": {"argv": [list(delete_call[0]), list(delete_call[1])]},
|
||||
},
|
||||
{
|
||||
"fn": "state", "name": "round_trip",
|
||||
"expected": {
|
||||
"build_id": build_id,
|
||||
"after_create_is_set": after_create is not None,
|
||||
"after_merge_is_none": after_merge is None,
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
# error texts: merge failure mapping
|
||||
def fail_run(cmd, **kwargs):
|
||||
if cmd[1] == "merge":
|
||||
return subprocess.CompletedProcess(cmd, 128, "", "fatal: not something we can merge")
|
||||
return subprocess.CompletedProcess(cmd, 0, "", "")
|
||||
bm.subprocess.run = fail_run
|
||||
mgr2 = bm.BuildManager()
|
||||
mgr2.state = bm.BuildState(
|
||||
build_id="20261007-182800", description="error battery",
|
||||
created_at=FROZEN)
|
||||
wt = "/home/nick/.rune/worktrees/20261007-182800"
|
||||
mgr2.state.worktree_path = wt # merge_worktree requires a live worktree path
|
||||
try:
|
||||
mgr2.merge_worktree()
|
||||
merge_err = None
|
||||
except RuntimeError as ex:
|
||||
merge_err = str(ex)
|
||||
cases.append({
|
||||
"fn": "error", "name": "merge_failure_text",
|
||||
"expected": {"error": merge_err},
|
||||
})
|
||||
|
||||
def fail_create(cmd, **kwargs):
|
||||
if cmd[1] == "worktree":
|
||||
return subprocess.CompletedProcess(cmd, 128, "", "'x' already exists")
|
||||
return subprocess.CompletedProcess(cmd, 0, "", "")
|
||||
bm.subprocess.run = fail_create
|
||||
mgr3 = bm.BuildManager()
|
||||
mgr3.state = bm.BuildState(
|
||||
build_id="20261007-182800", description="error battery 2",
|
||||
created_at=FROZEN)
|
||||
mgr3._save_state()
|
||||
try:
|
||||
mgr3.create_worktree()
|
||||
create_err = None
|
||||
except RuntimeError as ex:
|
||||
create_err = str(ex)
|
||||
cases.append({
|
||||
"fn": "error", "name": "create_failure_text",
|
||||
"expected": {"error": create_err},
|
||||
})
|
||||
|
||||
out = {
|
||||
"oracle": "src/rune/build_mode.py (live BuildManager on a real tmp git repo, clock frozen)",
|
||||
"generated": "2026-10-07",
|
||||
"frozen_now": FROZEN,
|
||||
"cases": cases,
|
||||
}
|
||||
target = REPO / "src/go/build-mode" / "truth_worktree.json"
|
||||
target.write_text(json.dumps(out, indent=1, ensure_ascii=False) + "\n")
|
||||
print(f"wrote {target} ({len(cases)} cases)")
|
||||
|
||||
|
||||
def make_git_repo():
|
||||
root = tempfile.mkdtemp(prefix="bm-wt-truth-")
|
||||
env_realloc = subprocess.run(
|
||||
["git", "init", "-q"], cwd=root, capture_output=True, text=True)
|
||||
assert env_realloc.returncode == 0
|
||||
subprocess.run(["git", "config", "user.email", "t@t"], cwd=root, capture_output=True)
|
||||
subprocess.run(["git", "config", "user.name", "t"], cwd=root, capture_output=True)
|
||||
(Path(root) / "seed.txt").write_text("seed\n")
|
||||
subprocess.run(["git", "add", "."], cwd=root, capture_output=True)
|
||||
subprocess.run(["git", "commit", "-qm", "seed"], cwd=root, capture_output=True)
|
||||
return root
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,161 @@
|
||||
#!/usr/bin/env python3
|
||||
"""gen_curator_lexicon_truth.py — regenerate the lexicon truth batteries
|
||||
(contract BUILDER-2026-10-07T16:31:17Z-997c9d).
|
||||
|
||||
Why: batch 1's truth.json encoded the LEXICON DEGRADE — the phantom shadow
|
||||
("lexicon module unavailable — strength gate only") — because
|
||||
lexicon_evolution.py was absent when it was generated. The phantom has now
|
||||
been recovered from provenance (commit 5749029), so the LIVE oracle returns
|
||||
real lexicon verdicts and the Go port must match THEM.
|
||||
|
||||
What this does:
|
||||
1. Rewrites ONLY the fn=='le_promote_eligible' cases inside truth.json,
|
||||
regenerated against the live curator.le_promote_eligible (which now
|
||||
delegates to the recovered lexicon_evolution). Other cases untouched.
|
||||
2. Emits truth_lexicon.json — direct live-oracle cases for the recovered
|
||||
module's full surface: coherence (conflict/consistent/novel), grounded,
|
||||
distinctive_terms, promote_eligible (shared / novel-grounded /
|
||||
novel-parked / no-signal / strength-gate).
|
||||
|
||||
Note on `existing`: the truth carries the block values as a single string;
|
||||
the oracle is called with a one-element list ([existing]) — the batch-1
|
||||
convention, documented here so the Go test wraps identically.
|
||||
|
||||
Usage: python3 scripts/gen_curator_lexicon_truth.py (from repo root)
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(REPO / "src"))
|
||||
sys.path.insert(0, str(REPO / "src" / "rune"))
|
||||
|
||||
from rune import curator # noqa: E402 — live oracle (imports lexicon_evolution)
|
||||
import lexicon_evolution as le # noqa: E402
|
||||
|
||||
|
||||
def main():
|
||||
core = REPO / "src/go/curator-core"
|
||||
|
||||
# ── 1. regenerate the le_promote_eligible cases in truth.json ──────────
|
||||
truth_path = core / "truth.json"
|
||||
truth = json.loads(truth_path.read_text())
|
||||
replaced = 0
|
||||
for case in truth:
|
||||
if case.get("fn") != "le_promote_eligible":
|
||||
continue
|
||||
inp = case["input"]
|
||||
r = curator.le_promote_eligible(
|
||||
inp["proposal"],
|
||||
[inp["existing"]],
|
||||
inp["strength"],
|
||||
min_strength=3,
|
||||
wash_count=inp["wash_count"],
|
||||
)
|
||||
case["expected"] = {"eligible": r}
|
||||
replaced += 1
|
||||
assert replaced >= 4, f"expected >=4 le cases, found {replaced}"
|
||||
# Self-check: no case may still encode the degrade shadow.
|
||||
for case in truth:
|
||||
if case.get("fn") == "le_promote_eligible":
|
||||
assert "lexicon module unavailable" not in json.dumps(case["expected"]), case["name"]
|
||||
truth_path.write_text(json.dumps(truth, indent=1, ensure_ascii=False) + "\n")
|
||||
print(f"truth.json: {replaced} le_promote_eligible cases regenerated (live lexicon)")
|
||||
|
||||
# ── 2. the direct lexicon-surface battery ──────────────────────────────
|
||||
corpus = [
|
||||
"nick writes Go and Rust code daily in the rune repository",
|
||||
"the curator gates memory writes through worthiness and grading",
|
||||
"migration of modules follows the dichotomy: reactive goes Go, deterministic goes Rust",
|
||||
]
|
||||
|
||||
cases = []
|
||||
|
||||
def add(fn, name, inp, expected):
|
||||
cases.append({"fn": fn, "name": name, "input": inp, "expected": expected})
|
||||
|
||||
# coherence: conflict (distinctive shared term + negation ON it)
|
||||
add("coherence", "conflict",
|
||||
{"fact": "nick never writes Rust code", "existing": ["nick writes Go and Rust code daily"],
|
||||
"corpus": corpus},
|
||||
{"verdict": le.coherence("nick never writes Rust code",
|
||||
["nick writes Go and Rust code daily"], corpus)})
|
||||
# coherence: consistent (distinctive shared term, no negation)
|
||||
add("coherence", "consistent",
|
||||
{"fact": "nick writes Rust code on weekends", "existing": ["nick writes Go and Rust code daily"],
|
||||
"corpus": corpus},
|
||||
{"verdict": le.coherence("nick writes Rust code on weekends",
|
||||
["nick writes Go and Rust code daily"], corpus)})
|
||||
# coherence: novel (nothing distinctive shared)
|
||||
add("coherence", "novel_axis",
|
||||
{"fact": "nick plays bass guitar in a band", "existing": ["nick writes Go and Rust code daily"],
|
||||
"corpus": corpus},
|
||||
{"verdict": le.coherence("nick plays bass guitar in a band",
|
||||
["nick writes Go and Rust code daily"], corpus)})
|
||||
# coherence: common word only is NEVER a relation
|
||||
add("coherence", "common_word_not_relation",
|
||||
{"fact": "nick likes black coffee", "existing": ["black holes bend light"],
|
||||
"corpus": ["black coffee in the morning", "black holes bend light"]},
|
||||
{"verdict": le.coherence("nick likes black coffee", ["black holes bend light"],
|
||||
["black coffee in the morning", "black holes bend light"])})
|
||||
# coherence: empty existing -> novel
|
||||
add("coherence", "empty_existing",
|
||||
{"fact": "nick writes Rust code daily", "existing": [], "corpus": []},
|
||||
{"verdict": le.coherence("nick writes Rust code daily", [], [])})
|
||||
|
||||
# grounded: stem-aware (migrated matches migration)
|
||||
add("grounded", "stem_match",
|
||||
{"fact": "nick migrated the modules", "sources": corpus},
|
||||
{"grounded": le.grounded("nick migrated the modules", corpus)})
|
||||
add("grounded", "invented_token",
|
||||
{"fact": "nick collects vintage railway timetables", "sources": corpus},
|
||||
{"grounded": le.grounded("nick collects vintage railway timetables", corpus)})
|
||||
add("grounded", "empty_sources",
|
||||
{"fact": "nick migrated the modules", "sources": []},
|
||||
{"grounded": le.grounded("nick migrated the modules", [])})
|
||||
|
||||
# distinctive_terms: rare survives, common does not
|
||||
add("distinctive_terms", "rare_vs_common",
|
||||
{"fact": "nick writes Rust code daily", "corpus": corpus},
|
||||
{"terms": sorted(le.distinctive_terms("nick writes Rust code daily", corpus))})
|
||||
|
||||
# promote_eligible: the four verdict shapes
|
||||
add("promote_eligible", "strength_gate",
|
||||
{"proposal": "add support for retries", "existing": ["support for retries in the fetch loop"],
|
||||
"strength": 2, "min_strength": 3, "corpus": []},
|
||||
{"eligible": le.promote_eligible("add support for retries",
|
||||
["support for retries in the fetch loop"], 2, 3, ())})
|
||||
add("promote_eligible", "shared_distinctive",
|
||||
{"proposal": "add retries to the fetch loop", "existing": ["support for retries in the fetch loop"],
|
||||
"strength": 3, "min_strength": 3, "corpus": []},
|
||||
{"eligible": le.promote_eligible("add retries to the fetch loop",
|
||||
["support for retries in the fetch loop"], 3, 3, ())})
|
||||
add("promote_eligible", "novel_grounded",
|
||||
{"proposal": "the curator gates every memory write", "existing": ["nick writes Go and Rust code daily"],
|
||||
"strength": 3, "min_strength": 3, "corpus": corpus},
|
||||
{"eligible": le.promote_eligible("the curator gates every memory write",
|
||||
["nick writes Go and Rust code daily"], 3, 3, corpus)})
|
||||
add("promote_eligible", "novel_parked",
|
||||
{"proposal": "nick collects vintage railway timetables", "existing": ["nick writes Go and Rust code daily"],
|
||||
"strength": 3, "min_strength": 3, "corpus": corpus},
|
||||
{"eligible": le.promote_eligible("nick collects vintage railway timetables",
|
||||
["nick writes Go and Rust code daily"], 3, 3, corpus)})
|
||||
add("promote_eligible", "first_principle",
|
||||
{"proposal": "parking is never wrong for growth", "existing": [],
|
||||
"strength": 3, "min_strength": 3, "corpus": []},
|
||||
{"eligible": le.promote_eligible("parking is never wrong for growth", [], 3, 3, ())})
|
||||
|
||||
out = {
|
||||
"oracle": "src/rune/lexicon_evolution.py (recovered provenance 0308ccc6) via curator.py",
|
||||
"generated": "2026-10-07 (post-recovery regeneration)",
|
||||
"cases": cases,
|
||||
}
|
||||
(core / "truth_lexicon.json").write_text(
|
||||
json.dumps(out, indent=1, ensure_ascii=False) + "\n")
|
||||
print(f"truth_lexicon.json: {len(cases)} cases")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,118 @@
|
||||
#!/usr/bin/env python3
|
||||
"""gen_memory_store_gate_truth.py — memory-store batch 4 truth battery
|
||||
(contract BUILDER-2026-10-07T19:47:37Z-1cf1dc).
|
||||
|
||||
The LIVE oracle: the dense-side meta-judgement extracted from recall()
|
||||
(lines 655-676) exercised against a FIXTURE STORE (sqlite entries + df
|
||||
counts), plus the entries-side fusion vs the chunk-side one (identity
|
||||
check), plus the abstain constants.
|
||||
|
||||
Usage: python3 scripts/gen_memory_store_gate_truth.py (from repo root)
|
||||
"""
|
||||
|
||||
import json
|
||||
import sqlite3
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(REPO / "src"))
|
||||
sys.path.insert(0, str(REPO / "src" / "rune"))
|
||||
|
||||
import memory_store as ms # noqa: E402
|
||||
|
||||
|
||||
def make_fixture_store(db_path):
|
||||
"""Entries table + df data mirroring recall()'s df-of-word query."""
|
||||
db = sqlite3.connect(db_path)
|
||||
db.row_factory = sqlite3.Row
|
||||
db.execute("CREATE TABLE entries (id INTEGER PRIMARY KEY, text TEXT, status TEXT)")
|
||||
rows = [
|
||||
(1, "the curator gates memory writes through worthiness"),
|
||||
(2, "black holes bend light near the event horizon"),
|
||||
(3, "black coffee in the morning keeps nick running"),
|
||||
(4, "the spine rebuild parity proofs live in store"),
|
||||
(5, "memory writes require the operator approval gate"),
|
||||
(6, "unrelated filler text about weather patterns"),
|
||||
(7, "curator provenance recovery from odysseus"),
|
||||
(8, "another filler row to pad the document frequency"),
|
||||
]
|
||||
db.executemany("INSERT INTO entries VALUES (?,?,?)",
|
||||
[(i, t, "active") for (i, t) in rows])
|
||||
db.commit()
|
||||
return db
|
||||
|
||||
|
||||
def main():
|
||||
tmp = Path(tempfile.mkdtemp(prefix="ms-gate-truth-"))
|
||||
db = make_fixture_store(str(tmp / "store.db"))
|
||||
total = db.execute("SELECT COUNT(*) AS n FROM entries WHERE status='active'").fetchone()["n"]
|
||||
|
||||
def df_of(word):
|
||||
return db.execute(
|
||||
"SELECT COUNT(*) AS n FROM entries WHERE status='active' AND text LIKE ?",
|
||||
(f"%{word}%",),
|
||||
).fetchone()["n"]
|
||||
|
||||
# replicate recall()'s dense-gate block verbatim
|
||||
def oracle_gate(query, hits):
|
||||
q_content = {t for t in query.lower().split() if len(t) > 3 and t not in ms._STOPWORDS}
|
||||
dense = []
|
||||
for rowid, cos in hits:
|
||||
etxt = (db.execute("SELECT text FROM entries WHERE id=?", (rowid,)).fetchone()["text"] or "").lower()
|
||||
distinctive = [w for w in q_content if w in etxt and df_of(w) / total < 0.05]
|
||||
if distinctive:
|
||||
dense.append((rowid, cos))
|
||||
elif not q_content or cos >= 0.80:
|
||||
dense.append((rowid, cos))
|
||||
return dense
|
||||
|
||||
cases = []
|
||||
|
||||
def add(fn, name, inp, expected):
|
||||
cases.append({"fn": fn, "name": name, "input": inp, "expected": expected})
|
||||
|
||||
# gate: shared DISTINCTIVE word (curator: df=2/8=0.25? no — check)
|
||||
# df(curator) = rows 1,7 = 2 → 0.25 ≥ 0.05 → NOT distinctive!
|
||||
# The oracle's threshold means common-store words fail; only truly
|
||||
# rare words pass. Fixture exercise:
|
||||
for name, query, hits in [
|
||||
("distinctive_shared", "worthiness gates", [(1, 0.9), (2, 0.85)]),
|
||||
("common_word_falls_back", "black", [(2, 0.86), (3, 0.79)]),
|
||||
("strong_cos_no_share", "unrelated query terms", [(6, 0.91), (4, 0.70)]),
|
||||
("no_content_words", "the was and", [(1, 0.60), (6, 0.95)]),
|
||||
]:
|
||||
add("dense_gate", name,
|
||||
{"query": query, "hits": [[r, c] for r, c in hits], "total": total},
|
||||
{"kept": [[r, c] for r, c in oracle_gate(query, hits)],
|
||||
"q_content": sorted({t for t in query.lower().split() if len(t) > 3 and t not in ms._STOPWORDS}),
|
||||
"df": {w: df_of(w) for w in query.lower().split() if len(w) > 3}})
|
||||
|
||||
# entries-side fusion identity with the chunk-side one
|
||||
dense = [[1, 0.9], [2, 0.8]]
|
||||
bm25 = [[2, 10.0], [3, 4.0]]
|
||||
add("fusion_identity", "entries_eq_chunks",
|
||||
{"dense": dense, "bm25": bm25},
|
||||
{"fusion": ms._py_rank_fusion(dense, bm25)})
|
||||
|
||||
add("constants", "abstain",
|
||||
{},
|
||||
{"budget": ms.RECALL_BUDGET, "min_score": ms.RECALL_MIN_SCORE})
|
||||
|
||||
import shutil
|
||||
shutil.rmtree(tmp, ignore_errors=True)
|
||||
|
||||
out = {
|
||||
"oracle": "src/rune/memory_store.py recall() dense gate + _py_rank_fusion",
|
||||
"generated": "2026-10-07",
|
||||
"fixture_df_note": "df via LIKE-substring over active entries; threshold 0.05",
|
||||
"cases": cases,
|
||||
}
|
||||
target = REPO / "src/go/memory-store-deep" / "truth_gate.json"
|
||||
target.write_text(json.dumps(out, indent=1, ensure_ascii=False) + "\n")
|
||||
print(f"wrote {target} ({len(cases)} cases)")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,107 @@
|
||||
#!/usr/bin/env python3
|
||||
"""gen_memory_store_score_truth.py — memory-store batch 3 truth battery
|
||||
(contract BUILDER-2026-10-07T18:57:06Z-c2429c).
|
||||
|
||||
The LIVE _py_score_recall_rows oracle: metadata scoring across modes
|
||||
(hybrid / dense-only / lexical-only), recency decay, the abstention
|
||||
ladder (min_score gates, the 0.74 high-confidence gate, the 0.08 gap
|
||||
detection), position-aware re-rank.
|
||||
|
||||
Usage: python3 scripts/gen_memory_store_score_truth.py (from repo root)
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(REPO / "src"))
|
||||
sys.path.insert(0, str(REPO / "src" / "rune"))
|
||||
|
||||
import memory_store as ms # noqa: E402
|
||||
|
||||
NOW = "2026-10-07T18:58:00"
|
||||
|
||||
|
||||
def rows_for(ids):
|
||||
"""Fixture rows: confidence/temperature/recency variety."""
|
||||
out = []
|
||||
for i, eid in enumerate(ids):
|
||||
out.append({
|
||||
"id": eid,
|
||||
"text": f"entry {eid} body",
|
||||
"importance": 0.5 + i * 0.01,
|
||||
"topic": "project",
|
||||
"entities": ["a"],
|
||||
"source": "fixture",
|
||||
"confidence": 0.9 if i % 2 == 0 else 0.6,
|
||||
"temperature": 1.2 if i % 3 == 0 else 0.8,
|
||||
"last_accessed": ["2026-10-07T18:00:00", "2026-09-01T10:00:00",
|
||||
"2026-05-01T10:00:00", None][i % 4],
|
||||
})
|
||||
return out
|
||||
|
||||
|
||||
def main():
|
||||
cases = []
|
||||
|
||||
def add(fn, name, inp, expected):
|
||||
cases.append({"fn": fn, "name": name, "input": inp, "expected": expected})
|
||||
|
||||
# hybrid: ranks over 4 rows, dense + bm25 present
|
||||
fusion = {"ranks": [[1, 0.12], [2, 0.11], [3, 0.10], [4, 0.09]],
|
||||
"best_dense": 0.8, "mode": "hybrid"}
|
||||
dense = [[1, 0.8], [2, 0.7], [3, 0.6]]
|
||||
bm25 = [[2, 10.0], [4, 5.0], [1, 2.0]]
|
||||
rows = rows_for([1, 2, 3, 4])
|
||||
add("score", "hybrid",
|
||||
{"fusion": fusion, "rows": rows, "dense": dense, "bm25": bm25,
|
||||
"budget": 3, "min_score": 0.3, "now": NOW},
|
||||
{"scored": ms._py_score_recall_rows(fusion, rows, dense, bm25, 3, 0.3, NOW)})
|
||||
|
||||
# dense-only: high confidence (>= 0.74) → position-aware
|
||||
fusion_d = {"ranks": [[1, 0.06], [2, 0.055], [3, 0.05]],
|
||||
"best_dense": 0.8, "mode": "dense-only"}
|
||||
add("score", "dense_only_high_conf",
|
||||
{"fusion": fusion_d, "rows": rows_for([1, 2, 3]), "dense": dense,
|
||||
"bm25": [], "budget": 5, "min_score": 0.3, "now": NOW},
|
||||
{"scored": ms._py_score_recall_rows(fusion_d, rows_for([1, 2, 3]), dense, [], 5, 0.3, NOW)})
|
||||
|
||||
# dense-only: narrow gap → abstain
|
||||
dense_close = [[1, 0.80], [2, 0.75]]
|
||||
add("score", "dense_only_gap_abstain",
|
||||
{"fusion": fusion_d, "rows": rows_for([1, 2, 3]), "dense": dense_close,
|
||||
"bm25": [], "budget": 5, "min_score": 0.3, "now": NOW},
|
||||
{"scored": ms._py_score_recall_rows(fusion_d, rows_for([1, 2, 3]), dense_close, [], 5, 0.3, NOW)})
|
||||
|
||||
# dense-only: below min_score → abstain
|
||||
add("score", "dense_only_min_score",
|
||||
{"fusion": fusion_d, "rows": rows_for([1, 2, 3]), "dense": dense,
|
||||
"bm25": [], "budget": 5, "min_score": 0.9, "now": NOW},
|
||||
{"scored": ms._py_score_recall_rows(fusion_d, rows_for([1, 2, 3]), dense, [], 5, 0.9, NOW)})
|
||||
|
||||
# lexical-only: max relevance gate
|
||||
fusion_l = {"ranks": [[2, 0.9], [4, 0.7]], "best_dense": 0.0, "mode": "lexical-only"}
|
||||
add("score", "lexical_only_gate",
|
||||
{"fusion": fusion_l, "rows": rows_for([2, 4]), "dense": [], "bm25": bm25,
|
||||
"budget": 5, "min_score": 1.0, "now": NOW},
|
||||
{"scored": ms._py_score_recall_rows(fusion_l, rows_for([2, 4]), [], bm25, 5, 1.0, NOW)})
|
||||
|
||||
# neither → []
|
||||
add("score", "neither",
|
||||
{"fusion": fusion_l, "rows": rows_for([2, 4]), "dense": [], "bm25": [],
|
||||
"budget": 5, "min_score": 0.3, "now": NOW},
|
||||
{"scored": ms._py_score_recall_rows(fusion_l, rows_for([2, 4]), [], [], 5, 0.3, NOW)})
|
||||
|
||||
out = {
|
||||
"oracle": "src/rune/memory_store.py _py_score_recall_rows",
|
||||
"generated": "2026-10-07",
|
||||
"cases": cases,
|
||||
}
|
||||
target = REPO / "src/go/memory-store-deep" / "truth_score.json"
|
||||
target.write_text(json.dumps(out, indent=1, ensure_ascii=False) + "\n")
|
||||
print(f"wrote {target} ({len(cases)} cases)")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,120 @@
|
||||
#!/usr/bin/env python3
|
||||
"""gen_memory_store_truth2.py — memory-store batch 2 truth battery
|
||||
(contract BUILDER-2026-10-07T18:05:47Z-152c16).
|
||||
|
||||
The LIVE _py_* oracles of memory_store.py. Cases: FTS conjunctive builder
|
||||
(stopword filtering, fallback, 8-cap), chunk_text (recombine + hard-split),
|
||||
rank fusion (hybrid/dense-only/lexical-only), project_chunk_rows,
|
||||
map_reduce, fuse_results (dedup + scale-alignment).
|
||||
|
||||
Usage: python3 scripts/gen_memory_store_truth2.py (from repo root)
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(REPO / "src"))
|
||||
sys.path.insert(0, str(REPO / "src" / "rune"))
|
||||
|
||||
import memory_store as ms # noqa: E402
|
||||
|
||||
|
||||
def main():
|
||||
cases = []
|
||||
|
||||
def add(fn, name, inp, expected):
|
||||
cases.append({"fn": fn, "name": name, "input": inp, "expected": expected})
|
||||
|
||||
# ── FTS conjunctive ───────────────────────────────────────────────────
|
||||
add("fts", "significant_terms",
|
||||
{"query": "the curator gates memory writes"},
|
||||
{"q": ms._py_fts_query_conjunctive("the curator gates memory writes")})
|
||||
add("fts", "stopwords_dropped",
|
||||
{"query": "the and was with nick"},
|
||||
{"q": ms._py_fts_query_conjunctive("the and was with nick")})
|
||||
add("fts", "short_fallback",
|
||||
{"query": "go is fun ai"},
|
||||
{"q": ms._py_fts_query_conjunctive("go is fun ai")})
|
||||
long_q = "alpha beta gamma delta epsilon zeta eta theta iota kappa"
|
||||
add("fts", "cap_eight",
|
||||
{"query": long_q},
|
||||
{"q": ms._py_fts_query_conjunctive(long_q)})
|
||||
add("fts", "empty_returns_raw",
|
||||
{"query": "a an of"},
|
||||
{"q": ms._py_fts_query_conjunctive("a an of")})
|
||||
|
||||
# ── chunk_text ────────────────────────────────────────────────────────
|
||||
small = "one paragraph only"
|
||||
add("chunk", "under_size", {"text": small, "size": 1500, "overlap": 150},
|
||||
{"chunks": ms._py_chunk_text(small, 1500, 150)})
|
||||
para = "\n\n".join(f"paragraph {i} " + "x" * 40 for i in range(30))
|
||||
add("chunk", "recombine", {"text": para, "size": 400, "overlap": 150},
|
||||
{"chunks": ms._py_chunk_text(para, 400, 150)})
|
||||
noline = "y" * 3000
|
||||
add("chunk", "hard_split", {"text": noline, "size": 1000, "overlap": 150},
|
||||
{"chunks": ms._py_chunk_text(noline, 1000, 150)})
|
||||
add("chunk", "empty", {"text": "", "size": 1500, "overlap": 150},
|
||||
{"chunks": ms._py_chunk_text("", 1500, 150)})
|
||||
|
||||
# ── rank fusion ───────────────────────────────────────────────────────
|
||||
dense = [[1, 0.9], [2, 0.8], [3, 0.7]]
|
||||
bm25 = [[2, 12.0], [4, 8.0], [1, 3.0]]
|
||||
add("fuse_ranks", "hybrid", {"dense": dense, "bm25": bm25},
|
||||
ms._py_rank_fusion(dense, bm25))
|
||||
add("fuse_ranks", "dense_only", {"dense": dense, "bm25": []},
|
||||
ms._py_rank_fusion(dense, []))
|
||||
add("fuse_ranks", "lexical_only", {"dense": [], "bm25": bm25},
|
||||
ms._py_rank_fusion([], bm25))
|
||||
add("fuse_ranks", "neither", {"dense": [], "bm25": []},
|
||||
ms._py_rank_fusion([], []))
|
||||
|
||||
# ── project_chunk_rows ────────────────────────────────────────────────
|
||||
rows = [
|
||||
{"rowid": 1, "chunk_id": "c1", "text": "t" * 500, "wing": "w1", "room": "r1",
|
||||
"source_path": "a.md", "page": 3},
|
||||
{"rowid": 2, "chunk_id": "c2", "text": "short", "wing": "w1", "room": "r1",
|
||||
"source_path": "b.md"},
|
||||
]
|
||||
ranks = {"2": 0.0625, "1": 0.05}
|
||||
add("project", "budget_and_shape", {"ranks": ranks},
|
||||
{"rows": ms._py_project_chunk_rows(ranks, rows, 6)})
|
||||
|
||||
# ── map_reduce ────────────────────────────────────────────────────────
|
||||
entries = [{"id": i, "relevance": i * 0.1} for i in range(25)]
|
||||
add("map_reduce", "top3_per_chunk", {"entries": entries, "chunk_size": 10},
|
||||
{"mapped": ms._py_map_reduce(entries, 10)})
|
||||
add("map_reduce", "under_threshold", {"entries": entries[:5], "chunk_size": 10},
|
||||
{"mapped": ms._py_map_reduce(entries[:5], 10)})
|
||||
|
||||
# ── fuse_results ──────────────────────────────────────────────────────
|
||||
ents = [
|
||||
{"id": 1, "text": "nick prefers coffee", "topic": "human",
|
||||
"relevance": 2.5, "confidence": 0.9},
|
||||
{"id": 2, "text": "NICK PREFERS COFFEE", "topic": "human",
|
||||
"relevance": 1.1, "confidence": 0.8}, # dedup hit
|
||||
{"id": 3, "text": "rune gates memory writes", "topic": "project",
|
||||
"relevance": 5.6, "confidence": 0.95},
|
||||
]
|
||||
res = [
|
||||
{"chunk_id": "c1", "text": "the spine rebuild parity", "wing": "research",
|
||||
"rrf": 0.0625, "confidence": 0.7},
|
||||
{"chunk_id": "c2", "text": "curator provenance recovery", "wing": "research",
|
||||
"rrf": 0.05},
|
||||
]
|
||||
add("fuse_results", "dedup_scale_align", {"entries": ents, "research": res},
|
||||
{"merged": ms._py_fuse_results(ents, res)})
|
||||
|
||||
out = {
|
||||
"oracle": "src/rune/memory_store.py _py_* oracles",
|
||||
"generated": "2026-10-07",
|
||||
"cases": cases,
|
||||
}
|
||||
target = REPO / "src/go/memory-store-deep" / "truth_recall.json"
|
||||
target.write_text(json.dumps(out, indent=1, ensure_ascii=False) + "\n")
|
||||
print(f"wrote {target} ({len(cases)} cases)")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,130 @@
|
||||
#!/usr/bin/env python3
|
||||
"""gen_scaffold_close_truth.py — scaffold batch 3 truth battery
|
||||
(contract BUILDER-2026-10-07T18:42:38Z-52829e).
|
||||
|
||||
The LIVE oracle: _py_close_quality bucket math + close_contract on a doc
|
||||
built through the real lifecycle (new_contract → transitions → snapshot →
|
||||
close). now() frozen at scaffold AND structural_store namespaces.
|
||||
|
||||
Usage: python3 scripts/gen_scaffold_close_truth.py (from repo root)
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(REPO / "src"))
|
||||
|
||||
from rune import scaffold as sc # noqa: E402
|
||||
from rune.structural_store import new_doc, add_entity # noqa: E402
|
||||
|
||||
FROZEN = "2026-10-07T18:46:00"
|
||||
|
||||
|
||||
def main():
|
||||
sc.now = lambda: FROZEN
|
||||
cases = []
|
||||
|
||||
# ── CloseQuality bucket math (direct, edge-covering) ──────────────────
|
||||
inputs = {
|
||||
"contract_id": "q1",
|
||||
"phase": "superseding",
|
||||
"affected_entities": ["module:a", "module:b"],
|
||||
"expected_new_entities": ["module:new1", "module:new2"],
|
||||
"predicted_existing_affected": ["module:c"],
|
||||
"predicted_frontier": {
|
||||
"level0_direct": ["module:a"],
|
||||
"level1_obligations": ["module:d"],
|
||||
"contracts": ["contract:x"],
|
||||
"level2_verification": ["canary:v"],
|
||||
},
|
||||
"expected_runtime_effects": ["module:a/ctl", "module:zz/never"],
|
||||
"observed_evidence": [
|
||||
{"entity": "module:a", "source": "tests", "detail": "d" * 400, "authority": "pytest"},
|
||||
{"entity": "module:new1", "source": "build", "detail": "created"},
|
||||
{"entity": "module:surprise", "source": "runtime", "detail": "unplanned"},
|
||||
{"entity": "", "source": "runtime", "detail": "no entity — ignored"},
|
||||
],
|
||||
"now": FROZEN,
|
||||
}
|
||||
s = sc.Scaffold(Path(tempfile.mkdtemp()), "q")
|
||||
cases.append({
|
||||
"fn": "close_quality", "name": "buckets",
|
||||
"input": {"inputs": inputs},
|
||||
"expected": s._py_close_quality(inputs),
|
||||
})
|
||||
|
||||
# ── CloseContract through the real lifecycle ──────────────────────────
|
||||
tmp = Path(tempfile.mkdtemp(prefix="scaffold-close-truth-"))
|
||||
s2 = sc.Scaffold(tmp, "close-project")
|
||||
s2.init(purpose="close battery")
|
||||
doc = s2.load()
|
||||
s2.new_contract(doc, contract_id="c1", need="n", scope=["src/go/x/"],
|
||||
affected_entities=["module:a"],
|
||||
expected_new_entities=["module:new1"])
|
||||
s2.transition_contract(doc, "c1", "approved")
|
||||
s2.transition_contract(doc, "c1", "building")
|
||||
s2.transition_contract(doc, "c1", "verifying")
|
||||
# predicted frontier on the entity
|
||||
e = s2.contract(doc, "c1")
|
||||
e.setdefault("ext", {})["predicted_frontier"] = {
|
||||
"level0_direct": ["module:a"], "level1_obligations": [],
|
||||
"contracts": [], "level2_verification": [],
|
||||
}
|
||||
ev = [{"entity": "module:a", "source": "tests", "detail": "passed"}]
|
||||
e2 = s2.close_contract(doc, "c1", observed_evidence=ev,
|
||||
scope_files=["src/go/x/main.go", "src/other/rogue.py"])
|
||||
cases.append({
|
||||
"fn": "close_contract", "name": "full_lifecycle",
|
||||
"input": {},
|
||||
"expected": {"entity": e2},
|
||||
})
|
||||
|
||||
# phase gate
|
||||
doc2 = new_doc()
|
||||
add_entity(doc2, id="contract:early", kind="construction_contract",
|
||||
identities={"need": "n"}, maturity="DECLARED",
|
||||
evidence_refs=["x"])
|
||||
doc2["entities"][-1]["phase"] = "draft"
|
||||
doc2["entities"][-1]["status"] = "open"
|
||||
doc2["entities"][-1]["checkpoints"] = []
|
||||
try:
|
||||
s2.close_contract(doc2, "early", observed_evidence=[])
|
||||
gate = {"ok": True}
|
||||
except Exception as ex: # noqa: BLE001 — intentional: the case IS the gate error; the oracle's text becomes the truth column
|
||||
gate = {"ok": False, "error": str(ex)}
|
||||
cases.append({
|
||||
"fn": "close_gate", "name": "phase_guard",
|
||||
"input": {},
|
||||
"expected": gate,
|
||||
})
|
||||
|
||||
# missing contract
|
||||
try:
|
||||
s2.close_contract(doc2, "ghost", observed_evidence=[])
|
||||
miss = {"ok": True}
|
||||
except Exception as ex: # noqa: BLE001 — intentional: same oracle-error-as-truth pattern as above
|
||||
miss = {"ok": False, "error": str(ex)}
|
||||
cases.append({
|
||||
"fn": "close_gate", "name": "missing_contract",
|
||||
"input": {},
|
||||
"expected": miss,
|
||||
})
|
||||
|
||||
out = {
|
||||
"oracle": "src/rune/scaffold.py (live, now frozen at both namespaces)",
|
||||
"generated": "2026-10-07",
|
||||
"frozen_now": FROZEN,
|
||||
"cases": cases,
|
||||
}
|
||||
target = REPO / "src/go/scaffold" / "truth_close.json"
|
||||
target.write_text(json.dumps(out, indent=1, ensure_ascii=False) + "\n")
|
||||
print(f"wrote {target} ({len(cases)} cases)")
|
||||
import shutil
|
||||
shutil.rmtree(tmp, ignore_errors=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,178 @@
|
||||
#!/usr/bin/env python3
|
||||
"""gen_scaffold_store_truth.py — scaffold batch 2 truth battery
|
||||
(contract BUILDER-2026-10-07T17:47:00Z-960f12).
|
||||
|
||||
The LIVE oracle (scaffold.Scaffold + structural_store) on a tmp store with
|
||||
now() frozen. Cases: canonical digest stability + secret redaction, commit
|
||||
semantics (success + exact failure texts), verify_on_disk forgotten-save
|
||||
detection, impact frontier (contract_change / implementation_change /
|
||||
contextual), snapshot_predicted_frontier, spine_bridge_entity.
|
||||
|
||||
Usage: python3 scripts/gen_scaffold_store_truth.py (from repo root)
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(REPO / "src"))
|
||||
|
||||
from rune import scaffold as sc # noqa: E402
|
||||
from rune import structural_store as ss # noqa: E402
|
||||
|
||||
FROZEN = "2026-10-07T17:50:00"
|
||||
|
||||
|
||||
def main():
|
||||
sc.now = lambda: FROZEN
|
||||
ss.now = lambda: FROZEN
|
||||
|
||||
tmp = Path(tempfile.mkdtemp(prefix="scaffold-store-truth-"))
|
||||
s = sc.Scaffold(tmp, "truth-project")
|
||||
s.init(purpose="store battery")
|
||||
|
||||
cases = []
|
||||
|
||||
# ── canonical digest: stability + redaction ───────────────────────────
|
||||
doc = s.load()
|
||||
d1 = ss.canonical_digest(doc)
|
||||
# key-order permutation must NOT change the digest (semantic content)
|
||||
permuted = {k: doc[k] for k in reversed(list(doc.keys()))}
|
||||
d2 = ss.canonical_digest(permuted)
|
||||
cases.append({
|
||||
"fn": "digest", "name": "key_order_stable",
|
||||
"input": {},
|
||||
"expected": {"digest": d1, "permuted_same": d2 == d1},
|
||||
})
|
||||
secret_doc = {"entities": [{"id": "repo:x", "identities": {
|
||||
"token": "hunter2", "api_key": "sk-123", "full_name": "o/r",
|
||||
"api_token": "zz", "password": "pw", "private_key": "-----BEGIN",
|
||||
"note": "keepme", "my_secret_sauce": "redacted-too"}}]}
|
||||
cases.append({
|
||||
"fn": "digest", "name": "secret_redaction",
|
||||
"input": {},
|
||||
"expected": {"redacted": ss._redact_secrets(secret_doc)},
|
||||
})
|
||||
|
||||
# ── commit: success + digest readback ─────────────────────────────────
|
||||
s.commit(doc, what="battery")
|
||||
cases.append({
|
||||
"fn": "commit", "name": "success",
|
||||
"input": {},
|
||||
"expected": {"ok": True, "on_disk_schema": s.load()["schema_version"]},
|
||||
})
|
||||
|
||||
# forgotten-save detection: mutate in memory, DON'T save
|
||||
doc2 = s.load()
|
||||
doc2["entities"].append({"id": "ghost:1", "kind": "ghost", "identities": {}})
|
||||
try:
|
||||
s.verify_on_disk(lambda d: d["entities"], None, what="battery") if False else None
|
||||
# the real call: selector/expect form
|
||||
s.verify_on_disk(lambda d: [e for e in d["entities"] if e["id"] == "ghost:1"],
|
||||
lambda v: bool(v), what="battery")
|
||||
forgotten = {"ok": True}
|
||||
except AssertionError as ex:
|
||||
forgotten = {"ok": False, "error": str(ex)}
|
||||
cases.append({
|
||||
"fn": "verify_on_disk", "name": "forgotten_save",
|
||||
"input": {},
|
||||
"expected": forgotten,
|
||||
})
|
||||
|
||||
# corrupt store -> commit-style load returns None -> reload failure path
|
||||
# (simulate: point at a nonexistent path)
|
||||
class FakeScaffold:
|
||||
current = tmp / "nonexistent" / "current.json"
|
||||
try:
|
||||
ss_load = ss.load(FakeScaffold.current)
|
||||
reload_none = ss_load is None
|
||||
except Exception: # noqa: BLE001 — intentional: the case IS the degrade verdict (load failure → loads_none=False); error text is not the truth column here
|
||||
reload_none = False
|
||||
cases.append({
|
||||
"fn": "load", "name": "missing_store",
|
||||
"input": {},
|
||||
"expected": {"loads_none": reload_none},
|
||||
})
|
||||
|
||||
# ── impact frontier ───────────────────────────────────────────────────
|
||||
doc3 = new_frontier_doc()
|
||||
cases.append({
|
||||
"fn": "frontier", "name": "contract_change",
|
||||
"input": {"seeds": ["contract:c1"], "doc": doc3},
|
||||
"expected": ss.impact_frontier(doc3, ["contract:c1"]),
|
||||
})
|
||||
cases.append({
|
||||
"fn": "frontier", "name": "implementation_change",
|
||||
"input": {"seeds": ["module:impl_a"], "doc": doc3},
|
||||
"expected": ss.impact_frontier(doc3, ["module:impl_a"]),
|
||||
})
|
||||
cases.append({
|
||||
"fn": "frontier", "name": "violation_escalation",
|
||||
"input": {"seeds": ["module:impl_a"], "change_kind": "contract_violation", "doc": doc3},
|
||||
"expected": ss.impact_frontier(doc3, ["module:impl_a"], change_kind="contract_violation"),
|
||||
})
|
||||
|
||||
# ── snapshot_predicted_frontier + spine bridge ────────────────────────
|
||||
doc4 = new_frontier_doc()
|
||||
s4 = sc.Scaffold(tmp / "p4", "frontier-project")
|
||||
s4.init(purpose="frontier battery")
|
||||
doc4 = s4.load()
|
||||
s4.new_contract(doc4, contract_id="f1", need="n", scope=["src/"])
|
||||
# wire the frontier doc entities into the project doc
|
||||
doc4["entities"].extend(e for e in doc3["entities"] if e["id"] not in {x["id"] for x in doc4["entities"]})
|
||||
doc4["relationships"].extend(doc3["relationships"])
|
||||
frontier = s4.snapshot_predicted_frontier(doc4, "f1", ["contract:c1"])
|
||||
e = s4.contract(doc4, "f1")
|
||||
cases.append({
|
||||
"fn": "snapshot_frontier", "name": "persisted",
|
||||
"input": {"doc": doc4},
|
||||
"expected": {
|
||||
"frontier": frontier,
|
||||
"checkpoint_note": e["checkpoints"][-1]["note"],
|
||||
"ext_has": sorted((e.get("ext", {}).get("predicted_frontier") or {}).keys()),
|
||||
},
|
||||
})
|
||||
cases.append({
|
||||
"fn": "bridge", "name": "identity_only",
|
||||
"input": {},
|
||||
"expected": {"bridge": s4.spine_bridge_entity()},
|
||||
})
|
||||
|
||||
out = {
|
||||
"oracle": "src/rune/scaffold.py + structural_store.py (live, now frozen)",
|
||||
"generated": "2026-10-07",
|
||||
"frozen_now": FROZEN,
|
||||
"cases": cases,
|
||||
}
|
||||
target = REPO / "src/go/scaffold" / "truth_store.json"
|
||||
target.write_text(json.dumps(out, indent=1, ensure_ascii=False) + "\n")
|
||||
print(f"wrote {target} ({len(cases)} cases)")
|
||||
import shutil
|
||||
shutil.rmtree(tmp, ignore_errors=True)
|
||||
|
||||
|
||||
def new_frontier_doc():
|
||||
"""A small structural doc exercising the frontier walk."""
|
||||
doc = ss.new_doc()
|
||||
ss.add_entity(doc, id="contract:c1", kind="contract", identities={})
|
||||
ss.add_entity(doc, id="module:impl_a", kind="module", identities={})
|
||||
ss.add_entity(doc, id="module:impl_b", kind="module", identities={})
|
||||
ss.add_entity(doc, id="module:lib", kind="module", identities={})
|
||||
ss.add_entity(doc, id="canary:x", kind="canary", identities={})
|
||||
ss.add_entity(doc, id="deploy:live", kind="deployment", identities={})
|
||||
for rel in (
|
||||
{"type": "implements", "from": "module:impl_a", "to": "contract:c1"},
|
||||
{"type": "implements", "from": "module:impl_b", "to": "contract:c1"},
|
||||
{"type": "depends_on", "from": "module:impl_a", "to": "module:lib"},
|
||||
{"type": "verified_by", "from": "contract:c1", "to": "canary:x"},
|
||||
{"type": "deployed_as", "from": "module:impl_a", "to": "deploy:live"},
|
||||
{"type": "supersedes", "from": "module:impl_b", "to": "module:impl_a"}, # non-obligation
|
||||
):
|
||||
doc["relationships"].append(rel)
|
||||
return doc
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,152 @@
|
||||
#!/usr/bin/env python3
|
||||
"""gen_scaffold_truth.py — scaffold batch 1 truth battery
|
||||
(contract BUILDER-2026-10-07T17:27:25Z-6bc737).
|
||||
|
||||
The LIVE Scaffold runs against a tmp store with now() frozen (module-patch
|
||||
on rune.scaffold's imported binding). Cases: the transition machine (legal
|
||||
+ illegal + unknown phase, exact error texts), new_contract/transition_
|
||||
contract state evolution, check_scope (green/red/unknown), layout_
|
||||
obligations (FLEXIBLE vs USER-LOCKED), json_frontier projection.
|
||||
|
||||
Usage: python3 scripts/gen_scaffold_truth.py (from repo root)
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(REPO / "src"))
|
||||
|
||||
from rune import scaffold as sc # noqa: E402
|
||||
from rune.structural_store import new_doc, add_entity # noqa: E402
|
||||
|
||||
FROZEN = "2026-10-07T17:30:00"
|
||||
|
||||
|
||||
def main():
|
||||
# Freeze the clock at scaffold's namespace binding.
|
||||
sc.now = lambda: FROZEN
|
||||
|
||||
tmp = Path(tempfile.mkdtemp(prefix="scaffold-truth-"))
|
||||
s = sc.Scaffold(tmp, "truth-project")
|
||||
s.init(purpose="truth battery")
|
||||
|
||||
cases = []
|
||||
|
||||
# ── transition machine ────────────────────────────────────────────────
|
||||
def tv(name, current, to):
|
||||
try:
|
||||
sc.Scaffold.transition_contract # noqa: B018 — presence probe
|
||||
# validate via a throwaway entity path
|
||||
doc = new_doc()
|
||||
e = s.new_contract(doc, contract_id="probe", need="probe")
|
||||
if current != "draft":
|
||||
e["phase"] = current
|
||||
s.transition_contract(doc, "probe", to)
|
||||
cases.append({"fn": "transition", "name": name,
|
||||
"input": {"current": current, "to": to},
|
||||
"expected": {"ok": True}})
|
||||
except Exception as ex: # noqa: BLE001 — intentional: the case IS the error verdict; the oracle's error text becomes the truth column
|
||||
cases.append({"fn": "transition", "name": name,
|
||||
"input": {"current": current, "to": to},
|
||||
"expected": {"ok": False, "error": str(ex)}})
|
||||
|
||||
tv("legal_approved", "draft", "approved")
|
||||
tv("legal_building", "approved", "building")
|
||||
tv("legal_verifying", "building", "verifying")
|
||||
tv("legal_migrating", "verifying", "migrating")
|
||||
tv("legal_superseding", "migrating", "superseding")
|
||||
tv("legal_closed", "superseding", "closed")
|
||||
tv("legal_failed_retry", "failed", "building")
|
||||
tv("legal_abandon", "building", "abandoned")
|
||||
tv("illegal_closed_from_draft", "draft", "closed")
|
||||
tv("illegal_rewind", "verifying", "draft")
|
||||
tv("illegal_skip", "draft", "verifying")
|
||||
tv("unknown_phase", "draft", "nonexistent_phase")
|
||||
|
||||
# ── contract lifecycle evolution ──────────────────────────────────────
|
||||
doc = new_doc()
|
||||
s.new_contract(doc, contract_id="life", need="port the lifecycle",
|
||||
origin="builder", scope=["src/go/scaffold/"],
|
||||
acceptance=["truth battery passes"])
|
||||
e = s.transition_contract(doc, "life", "approved", evidence_ref="e1")
|
||||
e = s.transition_contract(doc, "life", "building", evidence_ref="e2", note="x" * 300)
|
||||
cases.append({
|
||||
"fn": "lifecycle", "name": "evolution",
|
||||
"input": {},
|
||||
"expected": {"entity": e},
|
||||
})
|
||||
|
||||
# duplicate contract
|
||||
try:
|
||||
s.new_contract(doc, contract_id="life", need="again")
|
||||
dup = {"ok": True}
|
||||
except ValueError as ex:
|
||||
dup = {"ok": False, "error": str(ex)}
|
||||
cases.append({"fn": "dup", "name": "duplicate", "input": {},
|
||||
"expected": dup})
|
||||
|
||||
# ── check_scope ───────────────────────────────────────────────────────
|
||||
doc2 = new_doc()
|
||||
s.new_contract(doc2, contract_id="scoped", need="n", scope=["src/go/a/", "src/go/b/main.go"])
|
||||
e2 = s.contract(doc2, "scoped")
|
||||
cases.append({
|
||||
"fn": "check_scope", "name": "mixed",
|
||||
"input": {"changed": ["src/go/a/x.go", "src/go/b/main.go", "src/other/f.py", "src/go/a/deep/y.go"], "scope": ["src/go/a/", "src/go/b/main.go"]},
|
||||
"expected": s.check_scope(doc2, "scoped",
|
||||
["src/go/a/x.go", "src/go/b/main.go", "src/other/f.py", "src/go/a/deep/y.go"]),
|
||||
})
|
||||
doc3 = new_doc()
|
||||
s.new_contract(doc3, contract_id="unscoped", need="n")
|
||||
cases.append({
|
||||
"fn": "check_scope", "name": "unscoped",
|
||||
"input": {"changed": ["anything.py"], "scope": []},
|
||||
"expected": s.check_scope(doc3, "unscoped", ["anything.py"]),
|
||||
})
|
||||
|
||||
# ── layout obligations ────────────────────────────────────────────────
|
||||
doc4 = new_doc()
|
||||
add_entity(doc4, id="layout:panel", kind="layout_contract",
|
||||
identities={"component": "Panel", "region": "sidebar",
|
||||
"implementation": "src/go/panel.go", "lock": False,
|
||||
"provenance": "scaffold"}, maturity="DECLARED")
|
||||
add_entity(doc4, id="layout:header", kind="layout_contract",
|
||||
identities={"component": "Header", "region": "top",
|
||||
"implementation": "src/go/header.go", "lock": True,
|
||||
"provenance": "user"}, maturity="DECLARED")
|
||||
cases.append({
|
||||
"fn": "layout", "name": "mixed_locks",
|
||||
"input": {"affected": ["Panel", "region:top"], "doc": doc4},
|
||||
"expected": {"obligations": s.layout_obligations(doc4, ["Panel", "region:top"])},
|
||||
})
|
||||
cases.append({
|
||||
"fn": "layout", "name": "no_hit",
|
||||
"input": {"affected": ["Unrelated"], "doc": doc4},
|
||||
"expected": {"obligations": []},
|
||||
})
|
||||
|
||||
# ── json_frontier ─────────────────────────────────────────────────────
|
||||
f = {"contracts": ["c1"], "level2_verification": ["v1"]}
|
||||
cases.append({
|
||||
"fn": "frontier", "name": "projection",
|
||||
"input": {"frontier": f},
|
||||
"expected": {"json": sc.json_frontier(f)},
|
||||
})
|
||||
|
||||
out = {
|
||||
"oracle": "src/rune/scaffold.py (live Scaffold, now frozen)",
|
||||
"generated": "2026-10-07",
|
||||
"frozen_now": FROZEN,
|
||||
"cases": cases,
|
||||
}
|
||||
target = REPO / "src/go/scaffold" / "truth_scaffold.json"
|
||||
target.write_text(json.dumps(out, indent=1, ensure_ascii=False) + "\n")
|
||||
print(f"wrote {target} ({len(cases)} cases)")
|
||||
import shutil
|
||||
shutil.rmtree(tmp, ignore_errors=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env python3
|
||||
"""gen_tool_executor_auth_truth.py — the tool-executor authority truth
|
||||
(contract BUILDER-2026-10-07T19:10:38Z-cc6be2).
|
||||
|
||||
Freezes the LIVE READ_ONLY_TOOLS set + the prepare-tool routing verdicts
|
||||
so CI asserts the Go set equals the Python set FOREVER. The run_command
|
||||
wrong-add this battery pins was a security-adjacent divergence: a mutating
|
||||
tool treated as read-only bypasses operator approval.
|
||||
|
||||
Usage: python3 scripts/gen_tool_executor_auth_truth.py (from repo root)
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(REPO / "src"))
|
||||
sys.path.insert(0, str(REPO / "src" / "rune"))
|
||||
|
||||
import tool_executor as te # noqa: E402
|
||||
|
||||
|
||||
def main():
|
||||
cases = [
|
||||
{
|
||||
"fn": "read_only_tools", "name": "exact_set",
|
||||
"input": {},
|
||||
"expected": {"tools": sorted(te.READ_ONLY_TOOLS)},
|
||||
},
|
||||
{
|
||||
"fn": "prepare_routing", "name": "run_command_requires_approval",
|
||||
"input": {"tool": "run_command", "args": {"command": "ls -la"}},
|
||||
"expected": {"read_only": "run_command" in te.READ_ONLY_TOOLS},
|
||||
},
|
||||
{
|
||||
"fn": "prepare_routing", "name": "read_file_executes_now",
|
||||
"input": {"tool": "read_file", "args": {"path": "/tmp"}},
|
||||
"expected": {"read_only": "read_file" in te.READ_ONLY_TOOLS},
|
||||
},
|
||||
]
|
||||
out = {
|
||||
"oracle": "src/rune/tool_executor.py READ_ONLY_TOOLS + routing",
|
||||
"generated": "2026-10-07",
|
||||
"cases": cases,
|
||||
}
|
||||
target = REPO / "src/go/tool-executor" / "truth_auth.json"
|
||||
target.write_text(json.dumps(out, indent=1, ensure_ascii=False) + "\n")
|
||||
print(f"wrote {target} ({len(cases)} cases)")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,301 @@
|
||||
#!/usr/bin/env python3
|
||||
"""gen_tool_executor_truth.py — regenerate the tool-executor truth batteries
|
||||
HERMETICALLY (contract BUILDER-2026-10-07T16:11:35Z-70ca24-unknown).
|
||||
|
||||
Why: the original batch-2/3/4 generators baked the generation host's temp
|
||||
paths (/mnt/workdrive/tmp*) into truth_fileops.json / truth_ops.json /
|
||||
truth_paths.json — inputs AND expectations. CI's runner user cannot read
|
||||
those paths, so the parity gate failed with permission-denied (go-surface,
|
||||
run 19792 job 56712) despite a locally green tree.
|
||||
|
||||
Design (the environment leak, closed):
|
||||
- Fixtures are created at generation time under roots the ORACLE ITSELF
|
||||
allows: read root = first creatable dir in ALLOWED_READ_DIRS (prefers
|
||||
/tmp), write root = first creatable dir in ALLOWED_WRITE_DIRS.
|
||||
- Every occurrence of those roots — in inputs AND expectations — is
|
||||
replaced with {{TMP}} / {{WROOT}} placeholders.
|
||||
- Each truth file carries a `fixtures` manifest (files + dirs) so the Go
|
||||
test recreates the SAME fixture content under ITS OWN roots at runtime.
|
||||
Both sides then run the same case semantics on their own environment;
|
||||
substituted bytes must agree exactly.
|
||||
- The grep-hit comparison stays order-insensitive (sorted line sets) on the
|
||||
Go side: the oracle's os.walk order is OS-scheduling dependent, not a
|
||||
parity-relevant property. Everything else is exact after substitution.
|
||||
|
||||
Oracle authority: src/rune/tool_executor.py is called LIVE for every case
|
||||
except the two cmd_gate verdicts whose oracle path would execute a shell
|
||||
command (plain_ok / shell_ok use harmless commands instead: "ls -la" and
|
||||
"true" — the gate branches they exercise do not inspect command content
|
||||
beyond non-emptiness).
|
||||
|
||||
Usage: python3 scripts/gen_tool_executor_truth.py (from repo root)
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(REPO / "src"))
|
||||
|
||||
from rune import tool_executor as te # noqa: E402 — the live oracle
|
||||
|
||||
|
||||
def mk_root(dirs, tag):
|
||||
"""First creatable temp dir under the given allowed dirs."""
|
||||
for d in dirs:
|
||||
try:
|
||||
return tempfile.mkdtemp(prefix="rune-truth-", dir=d)
|
||||
except OSError:
|
||||
continue
|
||||
raise RuntimeError(f"no creatable root among {dirs}")
|
||||
|
||||
|
||||
def build_fixtures(root):
|
||||
"""The shared fixture set: exactly what the cases observe."""
|
||||
(Path(root) / "w.txt").write_text(
|
||||
"".join(f"line {i}\n" for i in range(1, 21)), encoding="utf-8"
|
||||
)
|
||||
(Path(root) / "a.txt").write_text("the spine rebuild parity\n", encoding="utf-8")
|
||||
(Path(root) / "b.log").write_text("spine appears here too\n", encoding="utf-8")
|
||||
(Path(root) / "file.txt").write_text("content\n", encoding="utf-8")
|
||||
(Path(root) / "adir").mkdir()
|
||||
return {
|
||||
"files": [
|
||||
{"path": "{{TMP}}/w.txt",
|
||||
"content": "".join(f"line {i}\n" for i in range(1, 21))},
|
||||
{"path": "{{TMP}}/a.txt", "content": "the spine rebuild parity\n"},
|
||||
{"path": "{{TMP}}/b.log", "content": "spine appears here too\n"},
|
||||
{"path": "{{TMP}}/file.txt", "content": "content\n"},
|
||||
],
|
||||
"dirs": ["{{TMP}}/adir"],
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
read_root = mk_root(te.ALLOWED_READ_DIRS, "read")
|
||||
write_root = mk_root(te.ALLOWED_WRITE_DIRS, "write")
|
||||
fixtures = build_fixtures(read_root)
|
||||
|
||||
wpath = f"{read_root}/w.txt"
|
||||
edited = "line 1\nCHANGED\n" + "".join(f"line {i}\n" for i in range(3, 21))
|
||||
|
||||
cases_fileops = [
|
||||
{
|
||||
"fn": "read_window", "name": "mid",
|
||||
"input": {"path": f"{wpath}", "offset": 3, "limit": 5},
|
||||
"expected": None,
|
||||
},
|
||||
{
|
||||
"fn": "read_window", "name": "default",
|
||||
"input": {"path": f"{wpath}"},
|
||||
"expected": None,
|
||||
},
|
||||
{
|
||||
"fn": "diff", "name": "simple",
|
||||
"input": {"path": wpath, "new_content": edited},
|
||||
"expected": None,
|
||||
},
|
||||
]
|
||||
|
||||
r = te._read_file({"path": wpath, "offset": 3, "limit": 5})
|
||||
assert r["success"] is True, r
|
||||
cases_fileops[0]["expected"] = {"ok": True, "result": r["result"]}
|
||||
|
||||
r = te._read_file({"path": wpath})
|
||||
assert r["success"] is True, r
|
||||
cases_fileops[1]["expected"] = {
|
||||
"ok": True, "lines": len(r["result"].split("\n")),
|
||||
}
|
||||
|
||||
diff = te.generate_diff(Path(wpath), edited)
|
||||
dlines = diff.split("\n")
|
||||
cases_fileops[2]["expected"] = {
|
||||
"diff_head": dlines[:3],
|
||||
"has_minus": any(l.startswith("-line 2") for l in dlines),
|
||||
"has_plus": any(l.startswith("+CHANGED") for l in dlines),
|
||||
}
|
||||
|
||||
cases_ops = [
|
||||
{
|
||||
"fn": "list_dir", "name": "basic",
|
||||
"input": {"path": read_root},
|
||||
"expected": None,
|
||||
},
|
||||
{
|
||||
"fn": "list_dir", "name": "pattern",
|
||||
"input": {"path": read_root, "pattern": "*.txt"},
|
||||
"expected": None,
|
||||
},
|
||||
{
|
||||
"fn": "grep", "name": "hit",
|
||||
"input": {"path": read_root, "pattern": "spine"},
|
||||
"expected": None,
|
||||
},
|
||||
{
|
||||
"fn": "grep", "name": "miss",
|
||||
"input": {"path": read_root, "pattern": "absent-token"},
|
||||
"expected": None,
|
||||
},
|
||||
# The gate branches: pipe_refused and shell_empty return BEFORE any
|
||||
# execution (live oracle); plain_ok / shell_ok would execute, so they
|
||||
# run harmless commands ("ls -la", "true") — the gate branches they
|
||||
# exercise do not inspect command content beyond non-emptiness.
|
||||
{
|
||||
"fn": "cmd_gate", "name": "pipe_refused",
|
||||
"input": {"command": "ls -la | head"},
|
||||
"expected": None,
|
||||
},
|
||||
{
|
||||
"fn": "cmd_gate", "name": "plain_ok",
|
||||
"input": {"command": "ls -la"},
|
||||
"expected": None,
|
||||
},
|
||||
{
|
||||
"fn": "cmd_gate", "name": "shell_ok",
|
||||
"input": {"command": "true", "shell": True},
|
||||
"expected": None,
|
||||
},
|
||||
{
|
||||
"fn": "cmd_gate", "name": "shell_empty",
|
||||
"input": {"shell": True},
|
||||
"expected": None,
|
||||
},
|
||||
]
|
||||
|
||||
r = te._list_directory({"path": read_root})
|
||||
assert r["success"] is True, r
|
||||
cases_ops[0]["expected"] = {"ok": True, "result": r["result"]}
|
||||
|
||||
r = te._list_directory({"path": read_root, "pattern": "*.txt"})
|
||||
assert r["success"] is True, r
|
||||
cases_ops[1]["expected"] = {"ok": True, "result": r["result"]}
|
||||
|
||||
r = te._search_files({"path": read_root, "pattern": "spine"})
|
||||
assert r["success"] is True and r["result"] != "(no matches)", r
|
||||
cases_ops[2]["expected"] = {"ok": True, "result": r["result"]}
|
||||
|
||||
r = te._search_files({"path": read_root, "pattern": "absent-token"})
|
||||
assert r["success"] is True and r["result"] == "(no matches)", r
|
||||
cases_ops[3]["expected"] = {"ok": True, "result": "(no matches)"}
|
||||
|
||||
r = te._run_command({"command": "ls -la | head"})
|
||||
cases_ops[4]["expected"] = {"success": r["success"], "error": r.get("error", "")}
|
||||
assert r["success"] is False, r
|
||||
|
||||
r = te._run_command({"command": "ls -la"})
|
||||
cases_ops[5]["expected"] = {"success": r["success"], "error": r.get("error", "")}
|
||||
assert r["success"] is True, r
|
||||
|
||||
r = te._run_command({"command": "true", "shell": True})
|
||||
cases_ops[6]["expected"] = {"success": r["success"], "error": r.get("error", "")}
|
||||
assert r["success"] is True, r
|
||||
|
||||
r = te._run_command({"shell": True})
|
||||
cases_ops[7]["expected"] = {"success": r["success"], "error": r.get("error", "")}
|
||||
assert r["success"] is False, r
|
||||
|
||||
cases_paths = [
|
||||
{
|
||||
"fn": "resolve", "name": "abs",
|
||||
"input": {"path": f"{read_root}/file.txt"},
|
||||
"expected": None,
|
||||
},
|
||||
{
|
||||
"fn": "validate_read", "name": "hit",
|
||||
"input": {"path": f"{read_root}/file.txt"},
|
||||
"expected": None,
|
||||
},
|
||||
{
|
||||
"fn": "validate_read", "name": "miss",
|
||||
"input": {"path": f"{read_root}/missing.txt"},
|
||||
"expected": None,
|
||||
},
|
||||
{
|
||||
"fn": "validate_read", "name": "blocked",
|
||||
"input": {"path": "/etc/hostname"},
|
||||
"expected": None,
|
||||
},
|
||||
{
|
||||
"fn": "validate_write", "name": "protected",
|
||||
"input": {"path": "/etc/x.txt"},
|
||||
"expected": None,
|
||||
},
|
||||
{
|
||||
"fn": "validate_write", "name": "outside",
|
||||
"input": {"path": "/usr/share/x.txt"},
|
||||
"expected": None,
|
||||
},
|
||||
{
|
||||
"fn": "validate_write", "name": "ok",
|
||||
"input": {"path": f"{write_root}/new.txt"},
|
||||
"expected": None,
|
||||
},
|
||||
]
|
||||
|
||||
resolved = str(te.resolve_path(f"{read_root}/file.txt"))
|
||||
cases_paths[0]["expected"] = {"resolved": resolved}
|
||||
|
||||
ok, msg, _ = te.validate_read_path(f"{read_root}/file.txt")
|
||||
cases_paths[1]["expected"] = {"ok": ok}
|
||||
assert ok is True, msg
|
||||
|
||||
ok, msg, _ = te.validate_read_path(f"{read_root}/missing.txt")
|
||||
cases_paths[2]["expected"] = {"ok": ok, "msg": msg}
|
||||
assert ok is False, msg
|
||||
|
||||
ok, msg, _ = te.validate_read_path("/etc/hostname")
|
||||
cases_paths[3]["expected"] = {"ok": ok}
|
||||
assert ok is False, msg
|
||||
|
||||
ok, msg, _ = te.validate_write_path("/etc/x.txt")
|
||||
cases_paths[4]["expected"] = {"ok": ok, "msg_prefix": msg[:50]}
|
||||
assert ok is False, msg
|
||||
|
||||
ok, msg, _ = te.validate_write_path("/usr/share/x.txt")
|
||||
cases_paths[5]["expected"] = {"ok": ok, "msg_prefix": msg[:50]}
|
||||
assert ok is False, msg
|
||||
|
||||
ok, msg, resolved = te.validate_write_path(f"{write_root}/new.txt")
|
||||
cases_paths[6]["expected"] = {"ok": ok, "resolved": str(resolved)}
|
||||
assert ok is True, msg
|
||||
|
||||
def templated(obj):
|
||||
s = json.dumps(obj, ensure_ascii=False)
|
||||
s = s.replace(read_root, "{{TMP}}").replace(write_root, "{{WROOT}}")
|
||||
return json.loads(s)
|
||||
|
||||
out = {
|
||||
"oracle": "src/rune/tool_executor.py",
|
||||
"generated": "2026-10-07 (hermetic regeneration)",
|
||||
"placeholders": {
|
||||
"{{TMP}}": "read-fixture root (first creatable ALLOWED_READ_DIRS entry)",
|
||||
"{{WROOT}}": "write root (first creatable ALLOWED_WRITE_DIRS entry)",
|
||||
},
|
||||
"fixtures": fixtures,
|
||||
"cases": None,
|
||||
}
|
||||
|
||||
for name, cases in (
|
||||
("truth_fileops.json", cases_fileops),
|
||||
("truth_ops.json", cases_ops),
|
||||
("truth_paths.json", cases_paths),
|
||||
):
|
||||
doc = dict(out)
|
||||
doc["cases"] = templated(cases)
|
||||
text = json.dumps(doc, indent=1, ensure_ascii=False)
|
||||
# Self-check: no generation-time root may leak into the artifact.
|
||||
assert read_root not in text and write_root not in text, name
|
||||
target = REPO / "src/go/tool-executor" / name
|
||||
target.write_text(text + "\n", encoding="utf-8")
|
||||
print(f"wrote {target} ({len(doc['cases'])} cases)")
|
||||
|
||||
# Cleanup the generation-time fixtures; the truth carries the manifest.
|
||||
import shutil
|
||||
shutil.rmtree(read_root, ignore_errors=True)
|
||||
shutil.rmtree(write_root, ignore_errors=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,113 @@
|
||||
#!/usr/bin/env python3
|
||||
"""gen_warm_router_digest_truth.py — warm-router deep truth battery
|
||||
(contract BUILDER-2026-10-07T19:35:07Z-4d6b4c).
|
||||
|
||||
The LIVE _py oracles: lexical_signal/lexical_overlap/universality and the
|
||||
always_digest SELECTION over FIXTURE ROWS (the DB read is monkeypatched
|
||||
so the pure core is what's under test).
|
||||
|
||||
Usage: python3 scripts/gen_warm_router_digest_truth.py (from repo root)
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(REPO / "src"))
|
||||
sys.path.insert(0, str(REPO / "src" / "rune"))
|
||||
|
||||
import warm_router as wr # noqa: E402
|
||||
|
||||
|
||||
def main():
|
||||
cases = []
|
||||
|
||||
def add(fn, name, inp, expected):
|
||||
cases.append({"fn": fn, "name": name, "input": inp, "expected": expected})
|
||||
|
||||
# ── lexical signal/overlap ────────────────────────────────────────────
|
||||
add("lexical", "signal_strips_punct",
|
||||
{"body": "The curator gates memory writes (always)."},
|
||||
{"words": sorted(wr._lexical_signal("The curator gates memory writes (always)."))})
|
||||
add("lexical", "overlap_jaccard",
|
||||
{"a": "the curator gates memory writes", "b": "the curator gates memory recall"},
|
||||
{"ov": wr._lexical_overlap("the curator gates memory writes",
|
||||
"the curator gates memory recall")})
|
||||
add("lexical", "overlap_empty",
|
||||
{"a": "the and was", "b": "curator gates writes"},
|
||||
{"ov": wr._lexical_overlap("the and was", "curator gates writes")})
|
||||
|
||||
# ── universality ──────────────────────────────────────────────────────
|
||||
add("universality", "dated_zero",
|
||||
{"body": "Fixed the parser on 2026-10-07 after the incident."},
|
||||
{"u": wr._universality("Fixed the parser on 2026-10-07 after the incident.")})
|
||||
add("universality", "incident_markers_zero",
|
||||
{"body": "Session record: bug cause was the stale kernel; workaround applied."},
|
||||
{"u": wr._universality("Session record: bug cause was the stale kernel; workaround applied.")})
|
||||
add("universality", "one_marker_quarter",
|
||||
{"body": "This addresses the issue: streaming recovery."},
|
||||
{"u": wr._universality("This addresses the issue: streaming recovery.")})
|
||||
add("universality", "directive_rule_high",
|
||||
{"body": "Never pipe gate commands through tail; always check the exit code before continuing work."},
|
||||
{"u": wr._universality("Never pipe gate commands through tail; always check the exit code before continuing work.")})
|
||||
add("universality", "rambling_penalty",
|
||||
{"body": " ".join(["word"] * 120) + " never always must keep"},
|
||||
{"u": wr._universality(" ".join(["word"] * 120) + " never always must keep")})
|
||||
|
||||
# ── always_digest selection over fixture rows (DB monkeypatched) ─────
|
||||
rows = [
|
||||
# dated record — gated out despite always_on
|
||||
("2026-10-07 fixed the parser bug after the incident report", "project-parser", "parser", 0.9, "", 1, 0.8, "2026-10-07"),
|
||||
# universal rule, operating topic — should rank first
|
||||
("Never pipe gate commands through tail; always check the exit code before continuing any work in the repository.", "operating-gates", "gate", 0.7, "", 1, 0.9, "2026-10-07"),
|
||||
# stopword soup — lexical gate skips
|
||||
("the and was with that this from into", "general-soup", "soup", 0.5, "", 0, 0.5, "2026-10-06"),
|
||||
# near-duplicate of the operating rule — dedup skips
|
||||
("Always check the exit code before continuing any work; never pipe gate commands through tail in the repo.", "operating-gates-copy", "gate2", 0.8, "", 1, 0.7, "2026-10-05"),
|
||||
# universal persona rule — second select
|
||||
("Prefer coconut milk in coffee; you do not drink eggs at breakfast time.", "human-prefs", "coffee", 0.6, "", 0, 0.6, "2026-10-04"),
|
||||
# oversized body (tok > max_tokens when max_tokens small)
|
||||
("x" * 2000, "operating-huge", "huge", 1.0, "", 1, 1.0, "2026-10-03"),
|
||||
]
|
||||
fixture_rows = [tuple(r) for r in rows]
|
||||
|
||||
class FakeDB:
|
||||
def execute(self, *_a, **_k):
|
||||
return self
|
||||
|
||||
def fetchall(self):
|
||||
return fixture_rows
|
||||
|
||||
def close(self):
|
||||
pass
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, *a):
|
||||
return False
|
||||
|
||||
saved_db = wr._db
|
||||
wr._db = lambda: FakeDB()
|
||||
try:
|
||||
digest = wr.always_digest(max_tokens=220)
|
||||
finally:
|
||||
wr._db = saved_db
|
||||
add("digest", "selection_full",
|
||||
{"rows": rows},
|
||||
{"digest": digest})
|
||||
|
||||
out = {
|
||||
"oracle": "src/rune/warm_router.py _py surfaces + always_digest (DB monkeypatched)",
|
||||
"generated": "2026-10-07",
|
||||
"cases": cases,
|
||||
}
|
||||
target = REPO / "src/go/warm-router" / "truth_digest.json"
|
||||
target.write_text(json.dumps(out, indent=1, ensure_ascii=False) + "\n")
|
||||
print(f"wrote {target} ({len(cases)} cases)")
|
||||
print("digest slugs:", [d["slug"] for d in digest])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,181 @@
|
||||
#!/usr/bin/env python3
|
||||
"""polyglot_lint.py — the language-AWARE lint gate (contract
|
||||
BUILDER-2026-10-07T20:01:40Z-ac6140).
|
||||
|
||||
Why: the pipeline linted Python hard (ruff + ratchet) but Rust NOT AT ALL
|
||||
(crates build, never clippy) and Go only via vet (gofmt drifted across 41
|
||||
files, including landed batches). This gate applies the RIGHT linter to
|
||||
whatever changed:
|
||||
|
||||
Python → ruff check
|
||||
Go → go vet (per module) + gofmt -l (formatting is a gate)
|
||||
Rust → cargo clippy -- -D warnings (per changed crate) + cargo fmt --check
|
||||
other → noted and skipped
|
||||
|
||||
Modes:
|
||||
(default) changed files vs the push range (git diff against
|
||||
origin/master / the last push ref; falls back to the
|
||||
last commit when no upstream exists)
|
||||
--all full tree
|
||||
--files a,b,c explicit list
|
||||
|
||||
Exit non-zero on any finding. The cron fault-watcher and the pre-push
|
||||
routine both run this; CI runs the Go+Rust halves natively.
|
||||
|
||||
Usage:
|
||||
python3 scripts/polyglot_lint.py # changed-only
|
||||
python3 scripts/polyglot_lint.py --all # everything
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path(__file__).resolve().parent.parent
|
||||
|
||||
LANG_BY_EXT = {
|
||||
".py": "python",
|
||||
".go": "go",
|
||||
".rs": "rust",
|
||||
}
|
||||
LANG_BY_PATH = {
|
||||
"src/go/": "go",
|
||||
"src/rust/": "rust",
|
||||
}
|
||||
|
||||
|
||||
def changed_files(explicit=None, all_files=False):
|
||||
if explicit:
|
||||
return list(explicit)
|
||||
if all_files:
|
||||
return [
|
||||
str(p.relative_to(REPO))
|
||||
for p in REPO.rglob("*")
|
||||
if p.is_file() and not str(p).startswith((".git", "store/", "target"))
|
||||
]
|
||||
base = None
|
||||
for ref in ("origin/master", "gitea/master", "HEAD~1"):
|
||||
r = subprocess.run(["git", "rev-parse", "--verify", ref],
|
||||
capture_output=True, text=True, cwd=REPO)
|
||||
if r.returncode == 0:
|
||||
base = ref
|
||||
break
|
||||
if base is None:
|
||||
return []
|
||||
r = subprocess.run(["git", "diff", "--name-only", f"{base}...HEAD"],
|
||||
capture_output=True, text=True, cwd=REPO)
|
||||
return [f for f in r.stdout.splitlines() if f.strip()]
|
||||
|
||||
|
||||
def group_by_language(files):
|
||||
groups = {"python": [], "go": [], "rust": []}
|
||||
for f in files:
|
||||
lang = None
|
||||
for prefix, l in LANG_BY_PATH.items():
|
||||
if f.startswith(prefix):
|
||||
lang = l
|
||||
break
|
||||
if lang is None:
|
||||
lang = LANG_BY_EXT.get(Path(f).suffix)
|
||||
if lang:
|
||||
groups[lang].append(f)
|
||||
return groups
|
||||
|
||||
|
||||
def run(cmd, cwd=None):
|
||||
r = subprocess.run(cmd, cwd=cwd or REPO, capture_output=True, text=True)
|
||||
return r.returncode, r.stdout, r.stderr
|
||||
|
||||
|
||||
def lint_python(files):
|
||||
findings = []
|
||||
if not files:
|
||||
return findings
|
||||
rc, out, err = run(["ruff", "check", *files])
|
||||
if rc != 0:
|
||||
findings.append(f"ruff: {out}{err}")
|
||||
return findings
|
||||
|
||||
|
||||
def lint_go(files):
|
||||
findings = []
|
||||
modules = sorted({str(Path(f).parent) for f in files})
|
||||
# collapse to module roots (dirs containing go.mod)
|
||||
for mod in modules:
|
||||
mod_path = REPO / mod
|
||||
while mod_path != REPO and not (mod_path / "go.mod").exists():
|
||||
mod_path = mod_path.parent
|
||||
if mod_path == REPO:
|
||||
continue
|
||||
rel = mod_path.relative_to(REPO)
|
||||
rc, out, err = run(["go", "vet", "./..."], cwd=mod_path)
|
||||
if rc != 0:
|
||||
findings.append(f"go vet ({rel}): {out}{err}")
|
||||
# gofmt across the changed files' modules (formatting is a GATE)
|
||||
fmt_targets = [f for f in files if f.endswith(".go")]
|
||||
if fmt_targets:
|
||||
rc, out, _ = run(["gofmt", "-l", *fmt_targets])
|
||||
if rc != 0:
|
||||
findings.append(f"gofmt: failed to run")
|
||||
elif out.strip():
|
||||
findings.append(
|
||||
"gofmt unformatted (run `gofmt -w` on these):\n "
|
||||
+ "\n ".join(out.splitlines())
|
||||
)
|
||||
return findings
|
||||
|
||||
|
||||
def lint_rust(files):
|
||||
findings = []
|
||||
crates = sorted({str(Path(f).relative_to(REPO)) for f in files})
|
||||
crate_roots = set()
|
||||
for c in crates:
|
||||
p = REPO / c
|
||||
while p != REPO and not (p / "Cargo.toml").exists():
|
||||
p = p.parent
|
||||
if p != REPO:
|
||||
crate_roots.add(p)
|
||||
for crate in sorted(crate_roots):
|
||||
rc, out, err = run(["cargo", "clippy", "--", "-D", "warnings"], cwd=crate)
|
||||
if rc != 0:
|
||||
findings.append(f"clippy ({crate.name}):\n{out}{err}")
|
||||
rc, out, err = run(["cargo", "fmt", "--check"], cwd=crate)
|
||||
if rc != 0:
|
||||
findings.append(f"rustfmt ({crate.name}):\n{out}{err}")
|
||||
return findings
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--all", action="store_true", help="lint the full tree")
|
||||
ap.add_argument("--files", help="comma-separated explicit file list")
|
||||
args = ap.parse_args()
|
||||
|
||||
explicit = args.files.split(",") if args.files else None
|
||||
files = changed_files(explicit, args.all)
|
||||
if not files:
|
||||
print("polyglot lint: no changed files detected — nothing to do")
|
||||
return 0
|
||||
|
||||
groups = group_by_language(files)
|
||||
print(f"polyglot lint: {len(files)} changed → "
|
||||
f"python={len(groups['python'])} go={len(groups['go'])} "
|
||||
f"rust={len(groups['rust'])}")
|
||||
|
||||
all_findings = []
|
||||
all_findings += lint_python(groups["python"])
|
||||
all_findings += lint_go(groups["go"])
|
||||
all_findings += lint_rust(groups["rust"])
|
||||
|
||||
if all_findings:
|
||||
print(f"POLYGLOT LINT: {len(all_findings)} finding(s):")
|
||||
for f in all_findings:
|
||||
print(f" - {f}")
|
||||
return 1
|
||||
print("polyglot lint: clean")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
@@ -0,0 +1,259 @@
|
||||
package main
|
||||
|
||||
// build_mode.go — batch 1: the DETERMINISTIC core of src/rune/build_mode.py
|
||||
// (contract BUILDER-2026-10-07T17:08:00Z-6e177d-unknown). The phase machine,
|
||||
// ledger mechanics, resume scan, and status shape — clock INJECTED per the
|
||||
// date-time-bomb law. Worktree git operations are reactive and stay out
|
||||
// (batch 2).
|
||||
//
|
||||
// PARITY: truth_buildmode.json was generated by the LIVE Python BuildManager
|
||||
// (clock frozen via module patch, create_worktree no-op'd) — the Go replay
|
||||
// must match the state dicts byte-for-byte (key-for-key after JSON decode).
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Phase — the 11 build phases in DECLARATION order (resume scans reversed).
|
||||
var Phases = []string{
|
||||
"assess", "research", "feasible", "consult", "consent", "plan",
|
||||
"build", "verify", "correct", "integrate", "record",
|
||||
}
|
||||
|
||||
// PhaseStatus values.
|
||||
const (
|
||||
StatusPending = "pending"
|
||||
StatusInProgress = "in_progress"
|
||||
StatusComplete = "complete"
|
||||
StatusFailed = "failed"
|
||||
StatusSkipped = "skipped"
|
||||
)
|
||||
|
||||
// BuildPhase — three-phase execution for the build step.
|
||||
var BuildPhases = []string{"foundation", "implementation", "integration"}
|
||||
|
||||
// VerificationTiers — tiered verification strategy.
|
||||
var VerificationTiers = []string{"syntactic", "semantic", "integration", "system"}
|
||||
|
||||
// LedgerEntry — one ledger record (Python _log_phase shape: note the
|
||||
// timestamp key, NOT started_at — the dataclass LedgerEntry is a DIFFERENT
|
||||
// shape that the live manager never writes; parity follows the live code).
|
||||
type LedgerEntry struct {
|
||||
Phase string `json:"phase"`
|
||||
Status string `json:"status"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
Details map[string]interface{} `json:"details"`
|
||||
Errors []string `json:"errors"`
|
||||
}
|
||||
|
||||
// BuildState — the manager's persisted state (Python BuildState/asdict).
|
||||
type BuildState struct {
|
||||
BuildID string `json:"build_id"`
|
||||
Description string `json:"description"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
WorktreePath *string `json:"worktree_path"`
|
||||
CurrentPhase string `json:"current_phase"`
|
||||
Phases map[string]string `json:"phases"`
|
||||
Ledger []LedgerEntry `json:"ledger"`
|
||||
Batches []map[string]interface{} `json:"batches"`
|
||||
Corrections int `json:"corrections"`
|
||||
MaxCorrections int `json:"max_corrections"`
|
||||
CurrentBuildPhase string `json:"current_build_phase"`
|
||||
Trajectory []map[string]interface{} `json:"trajectory"`
|
||||
FailurePatterns []map[string]interface{} `json:"failure_patterns"`
|
||||
ComplexityScore int `json:"complexity_score"`
|
||||
EstimatedSteps int `json:"estimated_steps"`
|
||||
ActualSteps int `json:"actual_steps"`
|
||||
FilesChanged []string `json:"files_changed"`
|
||||
Contracts []map[string]interface{} `json:"contracts"`
|
||||
}
|
||||
|
||||
// NewBuildState — the Python constructor's defaults. Slices initialize
|
||||
// EMPTY (Python asdict serializes [] — Go nil marshals null, which would
|
||||
// break byte parity).
|
||||
func NewBuildState(buildID, description, createdAt string) *BuildState {
|
||||
return &BuildState{
|
||||
BuildID: buildID,
|
||||
Description: description,
|
||||
CreatedAt: createdAt,
|
||||
CurrentPhase: "assess",
|
||||
Phases: map[string]string{},
|
||||
Ledger: []LedgerEntry{},
|
||||
Batches: []map[string]interface{}{},
|
||||
MaxCorrections: 3,
|
||||
CurrentBuildPhase: "foundation",
|
||||
Trajectory: []map[string]interface{}{},
|
||||
FailurePatterns: []map[string]interface{}{},
|
||||
FilesChanged: []string{},
|
||||
Contracts: []map[string]interface{}{},
|
||||
}
|
||||
}
|
||||
|
||||
// LogPhase — _log_phase: append the ledger entry, update the phases map.
|
||||
// (The Python manager also saves to disk — the store write stays reactive.)
|
||||
func LogPhase(state *BuildState, phase, status string, details map[string]interface{}, errors []string, nowISO string) {
|
||||
if details == nil {
|
||||
details = map[string]interface{}{}
|
||||
}
|
||||
if errors == nil {
|
||||
errors = []string{}
|
||||
}
|
||||
state.Ledger = append(state.Ledger, LedgerEntry{
|
||||
Phase: phase,
|
||||
Status: status,
|
||||
Timestamp: nowISO,
|
||||
Details: details,
|
||||
Errors: errors,
|
||||
})
|
||||
state.Phases[phase] = status
|
||||
}
|
||||
|
||||
// CompleteAssess — complete_assess.
|
||||
func CompleteAssess(state *BuildState, report map[string]interface{}, nowISO string) {
|
||||
LogPhase(state, "assess", StatusComplete, report, nil, nowISO)
|
||||
}
|
||||
|
||||
// CompleteResearch — complete_research.
|
||||
func CompleteResearch(state *BuildState, findings map[string]interface{}, nowISO string) {
|
||||
LogPhase(state, "research", StatusComplete, findings, nil, nowISO)
|
||||
}
|
||||
|
||||
// CompleteFeasible — complete_feasible. PARITY: the payload carries a
|
||||
// nested "details" key (details or {}) — cut-off reading missed it once;
|
||||
// the oracle's ledger pinned it.
|
||||
func CompleteFeasible(state *BuildState, feasible bool, risk string, extra map[string]interface{}, nowISO string) {
|
||||
if extra == nil {
|
||||
extra = map[string]interface{}{}
|
||||
}
|
||||
LogPhase(state, "feasible", StatusComplete, map[string]interface{}{
|
||||
"feasible": feasible, "risk": risk, "details": extra,
|
||||
}, nil, nowISO)
|
||||
}
|
||||
|
||||
// CompleteConsult — complete_consult.
|
||||
func CompleteConsult(state *BuildState, patterns []string, nowISO string) {
|
||||
if patterns == nil {
|
||||
patterns = []string{}
|
||||
}
|
||||
LogPhase(state, "consult", StatusComplete, map[string]interface{}{
|
||||
"patterns": patterns,
|
||||
}, nil, nowISO)
|
||||
}
|
||||
|
||||
// CompleteConsent — complete_consent.
|
||||
func CompleteConsent(state *BuildState, approved bool, nowISO string) {
|
||||
LogPhase(state, "consent", StatusComplete, map[string]interface{}{
|
||||
"approved": approved,
|
||||
}, nil, nowISO)
|
||||
}
|
||||
|
||||
// CompletePlan — complete_plan: the batches ASSIGNMENT is a state side
|
||||
// effect beyond the ledger entry (parity pinned).
|
||||
func CompletePlan(state *BuildState, batches []map[string]interface{}, nowISO string) {
|
||||
if batches == nil {
|
||||
batches = []map[string]interface{}{}
|
||||
}
|
||||
state.Batches = batches
|
||||
LogPhase(state, "plan", StatusComplete, map[string]interface{}{
|
||||
"batches": len(batches),
|
||||
}, nil, nowISO)
|
||||
}
|
||||
|
||||
// CompleteBuildPhase — complete_build_phase.
|
||||
func CompleteBuildPhase(state *BuildState, results []map[string]interface{}, nowISO string) {
|
||||
if results == nil {
|
||||
results = []map[string]interface{}{}
|
||||
}
|
||||
LogPhase(state, "build", StatusComplete, map[string]interface{}{
|
||||
"results": results,
|
||||
}, nil, nowISO)
|
||||
}
|
||||
|
||||
// CompleteVerify — complete_verify: failure lives in DETAILS (status stays
|
||||
// "complete" — the pinned oracle quirk; the resume scan never fires on it).
|
||||
func CompleteVerify(state *BuildState, passed bool, failures []string, nowISO string) {
|
||||
if failures == nil {
|
||||
failures = []string{}
|
||||
}
|
||||
LogPhase(state, "verify", StatusComplete, map[string]interface{}{
|
||||
"passed": passed, "failures": failures,
|
||||
}, nil, nowISO)
|
||||
}
|
||||
|
||||
// StartCorrect — start_correct: corrections INCREMENT (state side effect).
|
||||
func StartCorrect(state *BuildState, nowISO string) {
|
||||
state.Corrections++
|
||||
LogPhase(state, "correct", StatusInProgress, nil, nil, nowISO)
|
||||
}
|
||||
|
||||
// CompleteCorrect — complete_correct.
|
||||
func CompleteCorrect(state *BuildState, fixed bool, nowISO string) {
|
||||
LogPhase(state, "correct", StatusComplete, map[string]interface{}{
|
||||
"fixed": fixed,
|
||||
}, nil, nowISO)
|
||||
}
|
||||
|
||||
// CompleteIntegrate — complete_integrate.
|
||||
func CompleteIntegrate(state *BuildState, passed bool, failures []string, nowISO string) {
|
||||
if failures == nil {
|
||||
failures = []string{}
|
||||
}
|
||||
LogPhase(state, "integrate", StatusComplete, map[string]interface{}{
|
||||
"passed": passed, "failures": failures,
|
||||
}, nil, nowISO)
|
||||
}
|
||||
|
||||
// CompleteRecord — complete_record.
|
||||
func CompleteRecord(state *BuildState, nowISO string) {
|
||||
LogPhase(state, "record", StatusComplete, nil, nil, nowISO)
|
||||
}
|
||||
|
||||
// StartBuildID — the build_id format: %Y%m%d-%H%M%S (UTC) from the
|
||||
// INJECTED clock.
|
||||
func StartBuildID(nowUTC string) string {
|
||||
// nowUTC is "2026-10-07T16:08:00" (isoformat seconds, naive-UTC form
|
||||
// the generator freezes) → "20261007-160800".
|
||||
if len(nowUTC) < 19 {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%s%s%s-%s%s%s",
|
||||
nowUTC[0:4], nowUTC[5:7], nowUTC[8:10],
|
||||
nowUTC[11:13], nowUTC[14:16], nowUTC[17:19])
|
||||
}
|
||||
|
||||
// ResumePoint — reversed declaration-order scan: the first phase whose
|
||||
// status is in_progress or failed; "" when all complete (Python None).
|
||||
func ResumePoint(state *BuildState) string {
|
||||
for i := len(Phases) - 1; i >= 0; i-- {
|
||||
status, has := state.Phases[Phases[i]]
|
||||
if !has {
|
||||
continue
|
||||
}
|
||||
if status == StatusInProgress || status == StatusFailed {
|
||||
return Phases[i]
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// CanResume — resume point exists.
|
||||
func CanResume(state *BuildState) bool {
|
||||
return ResumePoint(state) != ""
|
||||
}
|
||||
|
||||
// BuildStatus — the get_status shape.
|
||||
func BuildStatus(state *BuildState) map[string]interface{} {
|
||||
if state == nil {
|
||||
return map[string]interface{}{"status": "no_build"}
|
||||
}
|
||||
return map[string]interface{}{
|
||||
"build_id": state.BuildID,
|
||||
"description": state.Description,
|
||||
"current_phase": state.CurrentPhase,
|
||||
"phases": state.Phases,
|
||||
"batches": len(state.Batches),
|
||||
"corrections": state.Corrections,
|
||||
"worktree": state.WorktreePath,
|
||||
"ledger_entries": len(state.Ledger),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package main
|
||||
|
||||
// cli.go — the module-family CLI: one JSON op per invocation (curator-core
|
||||
// convention). Exposes the ported build-mode mechanics for shell/Go callers;
|
||||
// a main package needs a main().
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var req map[string]interface{}
|
||||
if err := json.NewDecoder(os.Stdin).Decode(&req); err != nil {
|
||||
emit(map[string]interface{}{"error": "bad JSON input: " + err.Error()})
|
||||
os.Exit(1)
|
||||
}
|
||||
op, _ := req["op"].(string)
|
||||
switch op {
|
||||
case "start_build_id":
|
||||
// Live-clock variant: %Y%m%d-%H%M%S in UTC (the truth battery used
|
||||
// the frozen clock; the CLI serves the reactive layer).
|
||||
nowISO := time.Now().UTC().Format("2006-01-02T15:04:05")
|
||||
emit(map[string]interface{}{"build_id": StartBuildID(nowISO)})
|
||||
case "log_phase":
|
||||
stateAny, _ := req["state"].(map[string]interface{})
|
||||
var state BuildState
|
||||
sb, _ := json.Marshal(stateAny)
|
||||
if err := json.Unmarshal(sb, &state); err != nil {
|
||||
emit(map[string]interface{}{"error": "bad state: " + err.Error()})
|
||||
os.Exit(1)
|
||||
}
|
||||
details, _ := req["details"].(map[string]interface{})
|
||||
var errs []string
|
||||
if arr, ok := req["errors"].([]interface{}); ok {
|
||||
for _, e := range arr {
|
||||
s, _ := e.(string)
|
||||
errs = append(errs, s)
|
||||
}
|
||||
}
|
||||
nowISO, _ := req["now"].(string)
|
||||
if nowISO == "" {
|
||||
nowISO = time.Now().UTC().Format("2006-01-02T15:04:05+00:00")
|
||||
}
|
||||
LogPhase(&state, str(req, "phase"), str(req, "status"), details, errs, nowISO)
|
||||
emit(map[string]interface{}{"state": state})
|
||||
case "resume_point":
|
||||
var state BuildState
|
||||
sb, _ := json.Marshal(req["state"])
|
||||
if err := json.Unmarshal(sb, &state); err != nil {
|
||||
emit(map[string]interface{}{"error": "bad state: " + err.Error()})
|
||||
os.Exit(1)
|
||||
}
|
||||
emit(map[string]interface{}{"resume_point": ResumePoint(&state)})
|
||||
case "status":
|
||||
var state *BuildState
|
||||
if raw, has := req["state"]; has && raw != nil {
|
||||
state = &BuildState{}
|
||||
sb, _ := json.Marshal(raw)
|
||||
if err := json.Unmarshal(sb, state); err != nil {
|
||||
emit(map[string]interface{}{"error": "bad state: " + err.Error()})
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
emit(BuildStatus(state))
|
||||
default:
|
||||
emit(map[string]interface{}{"error": "unknown op: " + op})
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func str(m map[string]interface{}, k string) string {
|
||||
v, _ := m[k].(string)
|
||||
return v
|
||||
}
|
||||
|
||||
func emit(v map[string]interface{}) {
|
||||
enc := json.NewEncoder(os.Stdout)
|
||||
enc.SetEscapeHTML(false)
|
||||
_ = enc.Encode(v)
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
module rune/build-mode
|
||||
|
||||
go 1.21
|
||||
@@ -0,0 +1,676 @@
|
||||
{
|
||||
"oracle": "src/rune/build_mode.py (live BuildManager, clock frozen)",
|
||||
"generated": "2026-10-07",
|
||||
"frozen_now": "2026-10-07T16:08:00",
|
||||
"cases": [
|
||||
{
|
||||
"name": "fresh_start_assess_in_progress",
|
||||
"build_id": "20261007-160800",
|
||||
"calls": [],
|
||||
"state": {
|
||||
"build_id": "20261007-160800",
|
||||
"description": "truth battery build",
|
||||
"created_at": "2026-10-07T16:08:00+00:00",
|
||||
"worktree_path": null,
|
||||
"current_phase": "assess",
|
||||
"phases": {
|
||||
"assess": "in_progress"
|
||||
},
|
||||
"ledger": [
|
||||
{
|
||||
"phase": "assess",
|
||||
"status": "in_progress",
|
||||
"timestamp": "2026-10-07T16:08:00+00:00",
|
||||
"details": {},
|
||||
"errors": []
|
||||
}
|
||||
],
|
||||
"batches": [],
|
||||
"corrections": 0,
|
||||
"max_corrections": 3,
|
||||
"current_build_phase": "foundation",
|
||||
"trajectory": [],
|
||||
"failure_patterns": [],
|
||||
"complexity_score": 0,
|
||||
"estimated_steps": 0,
|
||||
"actual_steps": 0,
|
||||
"files_changed": [],
|
||||
"contracts": []
|
||||
},
|
||||
"resume_point": "assess",
|
||||
"can_resume": true,
|
||||
"status": {
|
||||
"build_id": "20261007-160800",
|
||||
"description": "truth battery build",
|
||||
"current_phase": "assess",
|
||||
"phases": {
|
||||
"assess": "in_progress"
|
||||
},
|
||||
"batches": 0,
|
||||
"corrections": 0,
|
||||
"worktree": null,
|
||||
"ledger_entries": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "assess_complete",
|
||||
"build_id": "20261007-160800",
|
||||
"calls": [
|
||||
{
|
||||
"fn": "complete_assess",
|
||||
"args": {
|
||||
"complexity_report": {
|
||||
"complexity": 3
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"state": {
|
||||
"build_id": "20261007-160800",
|
||||
"description": "truth battery build",
|
||||
"created_at": "2026-10-07T16:08:00+00:00",
|
||||
"worktree_path": null,
|
||||
"current_phase": "assess",
|
||||
"phases": {
|
||||
"assess": "complete"
|
||||
},
|
||||
"ledger": [
|
||||
{
|
||||
"phase": "assess",
|
||||
"status": "in_progress",
|
||||
"timestamp": "2026-10-07T16:08:00+00:00",
|
||||
"details": {},
|
||||
"errors": []
|
||||
},
|
||||
{
|
||||
"phase": "assess",
|
||||
"status": "complete",
|
||||
"timestamp": "2026-10-07T16:08:00+00:00",
|
||||
"details": {
|
||||
"complexity": 3
|
||||
},
|
||||
"errors": []
|
||||
}
|
||||
],
|
||||
"batches": [],
|
||||
"corrections": 0,
|
||||
"max_corrections": 3,
|
||||
"current_build_phase": "foundation",
|
||||
"trajectory": [],
|
||||
"failure_patterns": [],
|
||||
"complexity_score": 0,
|
||||
"estimated_steps": 0,
|
||||
"actual_steps": 0,
|
||||
"files_changed": [],
|
||||
"contracts": []
|
||||
},
|
||||
"resume_point": null,
|
||||
"can_resume": false,
|
||||
"status": {
|
||||
"build_id": "20261007-160800",
|
||||
"description": "truth battery build",
|
||||
"current_phase": "assess",
|
||||
"phases": {
|
||||
"assess": "complete"
|
||||
},
|
||||
"batches": 0,
|
||||
"corrections": 0,
|
||||
"worktree": null,
|
||||
"ledger_entries": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "through_plan",
|
||||
"build_id": "20261007-160800",
|
||||
"calls": [
|
||||
{
|
||||
"fn": "complete_assess",
|
||||
"args": {
|
||||
"complexity_report": {
|
||||
"complexity": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "complete_research",
|
||||
"args": {
|
||||
"findings": {
|
||||
"findings": "dichotomy applies"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "complete_feasible",
|
||||
"args": {
|
||||
"feasible": true,
|
||||
"risk": "low"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "complete_consult",
|
||||
"args": {
|
||||
"patterns": [
|
||||
"oracle-battery pattern"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "complete_consent",
|
||||
"args": {
|
||||
"approved": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "complete_plan",
|
||||
"args": {
|
||||
"batches": [
|
||||
{
|
||||
"batch": 1,
|
||||
"fns": 11
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"state": {
|
||||
"build_id": "20261007-160800",
|
||||
"description": "truth battery build",
|
||||
"created_at": "2026-10-07T16:08:00+00:00",
|
||||
"worktree_path": null,
|
||||
"current_phase": "assess",
|
||||
"phases": {
|
||||
"assess": "complete",
|
||||
"research": "complete",
|
||||
"feasible": "complete",
|
||||
"consult": "complete",
|
||||
"consent": "complete",
|
||||
"plan": "complete"
|
||||
},
|
||||
"ledger": [
|
||||
{
|
||||
"phase": "assess",
|
||||
"status": "in_progress",
|
||||
"timestamp": "2026-10-07T16:08:00+00:00",
|
||||
"details": {},
|
||||
"errors": []
|
||||
},
|
||||
{
|
||||
"phase": "assess",
|
||||
"status": "complete",
|
||||
"timestamp": "2026-10-07T16:08:00+00:00",
|
||||
"details": {
|
||||
"complexity": 3
|
||||
},
|
||||
"errors": []
|
||||
},
|
||||
{
|
||||
"phase": "research",
|
||||
"status": "complete",
|
||||
"timestamp": "2026-10-07T16:08:00+00:00",
|
||||
"details": {
|
||||
"findings": "dichotomy applies"
|
||||
},
|
||||
"errors": []
|
||||
},
|
||||
{
|
||||
"phase": "feasible",
|
||||
"status": "complete",
|
||||
"timestamp": "2026-10-07T16:08:00+00:00",
|
||||
"details": {
|
||||
"feasible": true,
|
||||
"risk": "low",
|
||||
"details": {}
|
||||
},
|
||||
"errors": []
|
||||
},
|
||||
{
|
||||
"phase": "consult",
|
||||
"status": "complete",
|
||||
"timestamp": "2026-10-07T16:08:00+00:00",
|
||||
"details": {
|
||||
"patterns": [
|
||||
"oracle-battery pattern"
|
||||
]
|
||||
},
|
||||
"errors": []
|
||||
},
|
||||
{
|
||||
"phase": "consent",
|
||||
"status": "complete",
|
||||
"timestamp": "2026-10-07T16:08:00+00:00",
|
||||
"details": {
|
||||
"approved": true
|
||||
},
|
||||
"errors": []
|
||||
},
|
||||
{
|
||||
"phase": "plan",
|
||||
"status": "complete",
|
||||
"timestamp": "2026-10-07T16:08:00+00:00",
|
||||
"details": {
|
||||
"batches": 1
|
||||
},
|
||||
"errors": []
|
||||
}
|
||||
],
|
||||
"batches": [
|
||||
{
|
||||
"batch": 1,
|
||||
"fns": 11
|
||||
}
|
||||
],
|
||||
"corrections": 0,
|
||||
"max_corrections": 3,
|
||||
"current_build_phase": "foundation",
|
||||
"trajectory": [],
|
||||
"failure_patterns": [],
|
||||
"complexity_score": 0,
|
||||
"estimated_steps": 0,
|
||||
"actual_steps": 0,
|
||||
"files_changed": [],
|
||||
"contracts": []
|
||||
},
|
||||
"resume_point": null,
|
||||
"can_resume": false,
|
||||
"status": {
|
||||
"build_id": "20261007-160800",
|
||||
"description": "truth battery build",
|
||||
"current_phase": "assess",
|
||||
"phases": {
|
||||
"assess": "complete",
|
||||
"research": "complete",
|
||||
"feasible": "complete",
|
||||
"consult": "complete",
|
||||
"consent": "complete",
|
||||
"plan": "complete"
|
||||
},
|
||||
"batches": 1,
|
||||
"corrections": 0,
|
||||
"worktree": null,
|
||||
"ledger_entries": 7
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "verify_with_failures",
|
||||
"build_id": "20261007-160800",
|
||||
"calls": [
|
||||
{
|
||||
"fn": "complete_assess",
|
||||
"args": {
|
||||
"complexity_report": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "complete_research",
|
||||
"args": {
|
||||
"findings": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "complete_feasible",
|
||||
"args": {
|
||||
"feasible": true,
|
||||
"risk": "low"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "complete_consult",
|
||||
"args": {
|
||||
"patterns": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "complete_consent",
|
||||
"args": {
|
||||
"approved": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "complete_plan",
|
||||
"args": {
|
||||
"batches": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "complete_build_phase",
|
||||
"args": {
|
||||
"results": [
|
||||
{
|
||||
"ok": true
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "complete_verify",
|
||||
"args": {
|
||||
"passed": false,
|
||||
"failures": [
|
||||
"test_x failed"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"state": {
|
||||
"build_id": "20261007-160800",
|
||||
"description": "truth battery build",
|
||||
"created_at": "2026-10-07T16:08:00+00:00",
|
||||
"worktree_path": null,
|
||||
"current_phase": "assess",
|
||||
"phases": {
|
||||
"assess": "complete",
|
||||
"research": "complete",
|
||||
"feasible": "complete",
|
||||
"consult": "complete",
|
||||
"consent": "complete",
|
||||
"plan": "complete",
|
||||
"build": "complete",
|
||||
"verify": "complete"
|
||||
},
|
||||
"ledger": [
|
||||
{
|
||||
"phase": "assess",
|
||||
"status": "in_progress",
|
||||
"timestamp": "2026-10-07T16:08:00+00:00",
|
||||
"details": {},
|
||||
"errors": []
|
||||
},
|
||||
{
|
||||
"phase": "assess",
|
||||
"status": "complete",
|
||||
"timestamp": "2026-10-07T16:08:00+00:00",
|
||||
"details": {},
|
||||
"errors": []
|
||||
},
|
||||
{
|
||||
"phase": "research",
|
||||
"status": "complete",
|
||||
"timestamp": "2026-10-07T16:08:00+00:00",
|
||||
"details": {},
|
||||
"errors": []
|
||||
},
|
||||
{
|
||||
"phase": "feasible",
|
||||
"status": "complete",
|
||||
"timestamp": "2026-10-07T16:08:00+00:00",
|
||||
"details": {
|
||||
"feasible": true,
|
||||
"risk": "low",
|
||||
"details": {}
|
||||
},
|
||||
"errors": []
|
||||
},
|
||||
{
|
||||
"phase": "consult",
|
||||
"status": "complete",
|
||||
"timestamp": "2026-10-07T16:08:00+00:00",
|
||||
"details": {
|
||||
"patterns": []
|
||||
},
|
||||
"errors": []
|
||||
},
|
||||
{
|
||||
"phase": "consent",
|
||||
"status": "complete",
|
||||
"timestamp": "2026-10-07T16:08:00+00:00",
|
||||
"details": {
|
||||
"approved": true
|
||||
},
|
||||
"errors": []
|
||||
},
|
||||
{
|
||||
"phase": "plan",
|
||||
"status": "complete",
|
||||
"timestamp": "2026-10-07T16:08:00+00:00",
|
||||
"details": {
|
||||
"batches": 0
|
||||
},
|
||||
"errors": []
|
||||
},
|
||||
{
|
||||
"phase": "build",
|
||||
"status": "complete",
|
||||
"timestamp": "2026-10-07T16:08:00+00:00",
|
||||
"details": {
|
||||
"results": [
|
||||
{
|
||||
"ok": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"errors": []
|
||||
},
|
||||
{
|
||||
"phase": "verify",
|
||||
"status": "complete",
|
||||
"timestamp": "2026-10-07T16:08:00+00:00",
|
||||
"details": {
|
||||
"passed": false,
|
||||
"failures": [
|
||||
"test_x failed"
|
||||
]
|
||||
},
|
||||
"errors": []
|
||||
}
|
||||
],
|
||||
"batches": [],
|
||||
"corrections": 0,
|
||||
"max_corrections": 3,
|
||||
"current_build_phase": "foundation",
|
||||
"trajectory": [],
|
||||
"failure_patterns": [],
|
||||
"complexity_score": 0,
|
||||
"estimated_steps": 0,
|
||||
"actual_steps": 0,
|
||||
"files_changed": [],
|
||||
"contracts": []
|
||||
},
|
||||
"resume_point": null,
|
||||
"can_resume": false,
|
||||
"status": {
|
||||
"build_id": "20261007-160800",
|
||||
"description": "truth battery build",
|
||||
"current_phase": "assess",
|
||||
"phases": {
|
||||
"assess": "complete",
|
||||
"research": "complete",
|
||||
"feasible": "complete",
|
||||
"consult": "complete",
|
||||
"consent": "complete",
|
||||
"plan": "complete",
|
||||
"build": "complete",
|
||||
"verify": "complete"
|
||||
},
|
||||
"batches": 0,
|
||||
"corrections": 0,
|
||||
"worktree": null,
|
||||
"ledger_entries": 9
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "correction_cycle",
|
||||
"build_id": "20261007-160800",
|
||||
"calls": [
|
||||
{
|
||||
"fn": "complete_assess",
|
||||
"args": {
|
||||
"complexity_report": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "complete_research",
|
||||
"args": {
|
||||
"findings": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "complete_feasible",
|
||||
"args": {
|
||||
"feasible": true,
|
||||
"risk": "low"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "complete_consult",
|
||||
"args": {
|
||||
"patterns": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "complete_consent",
|
||||
"args": {
|
||||
"approved": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "complete_plan",
|
||||
"args": {
|
||||
"batches": [
|
||||
{
|
||||
"batch": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "start_correct",
|
||||
"args": {}
|
||||
},
|
||||
{
|
||||
"fn": "complete_correct",
|
||||
"args": {
|
||||
"fixed": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"state": {
|
||||
"build_id": "20261007-160800",
|
||||
"description": "truth battery build",
|
||||
"created_at": "2026-10-07T16:08:00+00:00",
|
||||
"worktree_path": null,
|
||||
"current_phase": "assess",
|
||||
"phases": {
|
||||
"assess": "complete",
|
||||
"research": "complete",
|
||||
"feasible": "complete",
|
||||
"consult": "complete",
|
||||
"consent": "complete",
|
||||
"plan": "complete",
|
||||
"correct": "complete"
|
||||
},
|
||||
"ledger": [
|
||||
{
|
||||
"phase": "assess",
|
||||
"status": "in_progress",
|
||||
"timestamp": "2026-10-07T16:08:00+00:00",
|
||||
"details": {},
|
||||
"errors": []
|
||||
},
|
||||
{
|
||||
"phase": "assess",
|
||||
"status": "complete",
|
||||
"timestamp": "2026-10-07T16:08:00+00:00",
|
||||
"details": {},
|
||||
"errors": []
|
||||
},
|
||||
{
|
||||
"phase": "research",
|
||||
"status": "complete",
|
||||
"timestamp": "2026-10-07T16:08:00+00:00",
|
||||
"details": {},
|
||||
"errors": []
|
||||
},
|
||||
{
|
||||
"phase": "feasible",
|
||||
"status": "complete",
|
||||
"timestamp": "2026-10-07T16:08:00+00:00",
|
||||
"details": {
|
||||
"feasible": true,
|
||||
"risk": "low",
|
||||
"details": {}
|
||||
},
|
||||
"errors": []
|
||||
},
|
||||
{
|
||||
"phase": "consult",
|
||||
"status": "complete",
|
||||
"timestamp": "2026-10-07T16:08:00+00:00",
|
||||
"details": {
|
||||
"patterns": []
|
||||
},
|
||||
"errors": []
|
||||
},
|
||||
{
|
||||
"phase": "consent",
|
||||
"status": "complete",
|
||||
"timestamp": "2026-10-07T16:08:00+00:00",
|
||||
"details": {
|
||||
"approved": true
|
||||
},
|
||||
"errors": []
|
||||
},
|
||||
{
|
||||
"phase": "plan",
|
||||
"status": "complete",
|
||||
"timestamp": "2026-10-07T16:08:00+00:00",
|
||||
"details": {
|
||||
"batches": 1
|
||||
},
|
||||
"errors": []
|
||||
},
|
||||
{
|
||||
"phase": "correct",
|
||||
"status": "in_progress",
|
||||
"timestamp": "2026-10-07T16:08:00+00:00",
|
||||
"details": {},
|
||||
"errors": []
|
||||
},
|
||||
{
|
||||
"phase": "correct",
|
||||
"status": "complete",
|
||||
"timestamp": "2026-10-07T16:08:00+00:00",
|
||||
"details": {
|
||||
"fixed": true
|
||||
},
|
||||
"errors": []
|
||||
}
|
||||
],
|
||||
"batches": [
|
||||
{
|
||||
"batch": 1
|
||||
}
|
||||
],
|
||||
"corrections": 1,
|
||||
"max_corrections": 3,
|
||||
"current_build_phase": "foundation",
|
||||
"trajectory": [],
|
||||
"failure_patterns": [],
|
||||
"complexity_score": 0,
|
||||
"estimated_steps": 0,
|
||||
"actual_steps": 0,
|
||||
"files_changed": [],
|
||||
"contracts": []
|
||||
},
|
||||
"resume_point": null,
|
||||
"can_resume": false,
|
||||
"status": {
|
||||
"build_id": "20261007-160800",
|
||||
"description": "truth battery build",
|
||||
"current_phase": "assess",
|
||||
"phases": {
|
||||
"assess": "complete",
|
||||
"research": "complete",
|
||||
"feasible": "complete",
|
||||
"consult": "complete",
|
||||
"consent": "complete",
|
||||
"plan": "complete",
|
||||
"correct": "complete"
|
||||
},
|
||||
"batches": 1,
|
||||
"corrections": 1,
|
||||
"worktree": null,
|
||||
"ledger_entries": 9
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
package main
|
||||
|
||||
// truth_buildmode_test.go — build-mode batch 1 parity gate: the phase
|
||||
// machine + ledger mechanics replay truth_buildmode.json's RECORDED CALLS
|
||||
// through the ported functions (live Python BuildManager, clock frozen —
|
||||
// contract BUILDER-2026-10-07T17:08:00Z-6e177d).
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBuildModeParity(t *testing.T) {
|
||||
b, err := os.ReadFile("truth_buildmode.json")
|
||||
if err != nil {
|
||||
t.Fatalf("truth_buildmode.json missing: %v", err)
|
||||
}
|
||||
var doc struct {
|
||||
FrozenNow string `json:"frozen_now"`
|
||||
Cases []struct {
|
||||
Name string `json:"name"`
|
||||
BuildID string `json:"build_id"`
|
||||
Calls []struct {
|
||||
Fn string `json:"fn"`
|
||||
Args map[string]interface{} `json:"args"`
|
||||
} `json:"calls"`
|
||||
State json.RawMessage `json:"state"`
|
||||
ResumePoint *string `json:"resume_point"`
|
||||
CanResume bool `json:"can_resume"`
|
||||
Status json.RawMessage `json:"status"`
|
||||
} `json:"cases"`
|
||||
}
|
||||
if err := json.Unmarshal(b, &doc); err != nil {
|
||||
t.Fatalf("unparseable: %v", err)
|
||||
}
|
||||
stamp := doc.FrozenNow + "+00:00"
|
||||
for _, c := range doc.Cases {
|
||||
if got := StartBuildID(doc.FrozenNow); got != c.BuildID {
|
||||
t.Errorf("%s: build_id got %q want %q", c.Name, got, c.BuildID)
|
||||
}
|
||||
|
||||
// Replay the recorded calls through the ported functions.
|
||||
got := NewBuildState(c.BuildID, "truth battery build", stamp)
|
||||
noop := "/nonexistent-worktree"
|
||||
got.WorktreePath = &noop
|
||||
// start_build itself logs the first entry (assess/in_progress).
|
||||
LogPhase(got, "assess", StatusInProgress, nil, nil, stamp)
|
||||
for _, call := range c.Calls {
|
||||
args := call.Args
|
||||
switch call.Fn {
|
||||
case "complete_assess":
|
||||
// Python logs the report DICT itself (the parameter's value),
|
||||
// not a kwargs map.
|
||||
report, _ := args["complexity_report"].(map[string]interface{})
|
||||
CompleteAssess(got, report, stamp)
|
||||
case "complete_research":
|
||||
findings, _ := args["findings"].(map[string]interface{})
|
||||
CompleteResearch(got, findings, stamp)
|
||||
case "complete_feasible":
|
||||
CompleteFeasible(got, args["feasible"] == true, str(args, "risk"), nil, stamp)
|
||||
case "complete_consult":
|
||||
CompleteConsult(got, strList(args["patterns"]), stamp)
|
||||
case "complete_consent":
|
||||
CompleteConsent(got, args["approved"] == true, stamp)
|
||||
case "complete_plan":
|
||||
CompletePlan(got, objList(args["batches"]), stamp)
|
||||
case "complete_build_phase":
|
||||
CompleteBuildPhase(got, objList(args["results"]), stamp)
|
||||
case "complete_verify":
|
||||
CompleteVerify(got, args["passed"] == true, strList(args["failures"]), stamp)
|
||||
case "start_correct":
|
||||
StartCorrect(got, stamp)
|
||||
case "complete_correct":
|
||||
CompleteCorrect(got, args["fixed"] == true, stamp)
|
||||
case "complete_integrate":
|
||||
CompleteIntegrate(got, args["passed"] == true, strList(args["failures"]), stamp)
|
||||
case "complete_record":
|
||||
CompleteRecord(got, stamp)
|
||||
default:
|
||||
t.Fatalf("%s: unknown call %q", c.Name, call.Fn)
|
||||
}
|
||||
}
|
||||
|
||||
var wantState BuildState
|
||||
if err := json.Unmarshal(c.State, &wantState); err != nil {
|
||||
t.Fatalf("%s: state unparseable: %v", c.Name, err)
|
||||
}
|
||||
// jsonEq — compare canonical JSON bytes (Go-built []string vs
|
||||
// JSON-decoded []interface{} differ in type, not in bytes).
|
||||
jsonEq := func(name string, got, want interface{}) {
|
||||
gb, _ := json.Marshal(got)
|
||||
wb, _ := json.Marshal(want)
|
||||
if string(gb) != string(wb) {
|
||||
t.Errorf("%s: %s divergence:\n got %s\nwant %s", c.Name, name, gb, wb)
|
||||
}
|
||||
}
|
||||
jsonEq("phases", got.Phases, wantState.Phases)
|
||||
jsonEq("ledger", got.Ledger, wantState.Ledger)
|
||||
if got.Corrections != wantState.Corrections {
|
||||
t.Errorf("%s: corrections got %d want %d", c.Name, got.Corrections, wantState.Corrections)
|
||||
}
|
||||
jsonEq("batches", got.Batches, wantState.Batches)
|
||||
|
||||
gotResume := ResumePoint(got)
|
||||
wantResume := ""
|
||||
if c.ResumePoint != nil {
|
||||
wantResume = *c.ResumePoint
|
||||
}
|
||||
if gotResume != wantResume {
|
||||
t.Errorf("%s: resume got %q want %q", c.Name, gotResume, wantResume)
|
||||
}
|
||||
if CanResume(got) != c.CanResume {
|
||||
t.Errorf("%s: can_resume got %v want %v", c.Name, CanResume(got), c.CanResume)
|
||||
}
|
||||
|
||||
gotStatus := BuildStatus(got)
|
||||
gb, _ := json.Marshal(gotStatus)
|
||||
var gotMap, wantMap map[string]interface{}
|
||||
json.Unmarshal(gb, &gotMap)
|
||||
json.Unmarshal(c.Status, &wantMap)
|
||||
// worktree is the no-op path; compare everything else.
|
||||
delete(gotMap, "worktree")
|
||||
delete(wantMap, "worktree")
|
||||
if string(mustJSON(gotMap)) != string(mustJSON(wantMap)) {
|
||||
t.Errorf("%s: status divergence:\n got %v\nwant %v", c.Name, gotMap, wantMap)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestResumeScanQuirk — the pinned oracle quirk: complete_verify(false)
|
||||
// records status "complete" (failure lives in DETAILS), so the resume scan
|
||||
// never fires on it. Failure status only appears if explicitly logged.
|
||||
func TestResumeScanQuirk(t *testing.T) {
|
||||
s := NewBuildState("20261007-160800", "quirk probe", "x")
|
||||
CompletePlan(s, nil, "t")
|
||||
CompleteVerify(s, false, []string{"test_x"}, "t")
|
||||
if rp := ResumePoint(s); rp != "" {
|
||||
t.Errorf("verify-with-failures must NOT trip the resume scan, got %q", rp)
|
||||
}
|
||||
LogPhase(s, "record", StatusFailed, nil, nil, "t")
|
||||
if rp := ResumePoint(s); rp != "record" {
|
||||
t.Errorf("explicitly failed phase must resume, got %q", rp)
|
||||
}
|
||||
}
|
||||
|
||||
// strList / objList — typed JSON accessors (str lives in cli.go).
|
||||
func strList(v interface{}) []string {
|
||||
arr, ok := v.([]interface{})
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
out := make([]string, 0, len(arr))
|
||||
for _, e := range arr {
|
||||
s, _ := e.(string)
|
||||
out = append(out, s)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func objList(v interface{}) []map[string]interface{} {
|
||||
arr, ok := v.([]interface{})
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
out := make([]map[string]interface{}, 0, len(arr))
|
||||
for _, e := range arr {
|
||||
m, _ := e.(map[string]interface{})
|
||||
out = append(out, m)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func mustJSON(v interface{}) []byte {
|
||||
b, _ := json.Marshal(v)
|
||||
return b
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
{
|
||||
"oracle": "src/rune/build_mode.py (live BuildManager on a real tmp git repo, clock frozen)",
|
||||
"generated": "2026-10-07",
|
||||
"frozen_now": "2026-10-07T18:28:00",
|
||||
"cases": [
|
||||
{
|
||||
"fn": "plan",
|
||||
"name": "create",
|
||||
"expected": {
|
||||
"argv": [
|
||||
"git",
|
||||
"worktree",
|
||||
"add",
|
||||
"/home/nick/.rune/worktrees/20261007-182800",
|
||||
"-b",
|
||||
"build-20261007-182800"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "plan",
|
||||
"name": "merge",
|
||||
"expected": {
|
||||
"argv": [
|
||||
"git",
|
||||
"merge",
|
||||
"build-20261007-182800",
|
||||
"--no-ff",
|
||||
"-m",
|
||||
"Build 20261007-182800: worktree battery"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "plan",
|
||||
"name": "delete_pair",
|
||||
"expected": {
|
||||
"argv": [
|
||||
[
|
||||
"git",
|
||||
"worktree",
|
||||
"remove",
|
||||
"/home/nick/.rune/worktrees/20261007-182800",
|
||||
"--force"
|
||||
],
|
||||
[
|
||||
"git",
|
||||
"branch",
|
||||
"-D",
|
||||
"build-20261007-182800"
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "state",
|
||||
"name": "round_trip",
|
||||
"expected": {
|
||||
"build_id": "20261007-182800",
|
||||
"after_create_is_set": true,
|
||||
"after_merge_is_none": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "error",
|
||||
"name": "merge_failure_text",
|
||||
"expected": {
|
||||
"error": "Failed to merge worktree: fatal: not something we can merge"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "error",
|
||||
"name": "create_failure_text",
|
||||
"expected": {
|
||||
"error": "Failed to create worktree: 'x' already exists"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
package main
|
||||
|
||||
// truth_worktree_test.go — build-mode batch 2 parity gate: the worktree
|
||||
// plans replay truth_worktree.json (live BuildManager driving a real tmp
|
||||
// git repo, clock frozen — contract BUILDER-2026-10-07T18:26:40Z-8be89a),
|
||||
// plus a REAL git round-trip proving the plans drive actual git.
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const truthWorktreeDir = "/home/nick/.rune/worktrees"
|
||||
|
||||
func TestWorktreePlanParity(t *testing.T) {
|
||||
b, err := os.ReadFile("truth_worktree.json")
|
||||
if err != nil {
|
||||
t.Fatalf("truth_worktree.json missing: %v", err)
|
||||
}
|
||||
var doc struct {
|
||||
Cases []struct {
|
||||
Fn string `json:"fn"`
|
||||
Name string `json:"name"`
|
||||
Expected map[string]interface{} `json:"expected"`
|
||||
} `json:"cases"`
|
||||
}
|
||||
if err := json.Unmarshal(b, &doc); err != nil {
|
||||
t.Fatalf("unparseable: %v", err)
|
||||
}
|
||||
buildID := "20261007-182800"
|
||||
for _, c := range doc.Cases {
|
||||
switch c.Name {
|
||||
case "create":
|
||||
got := WorktreePlan(buildID, truthWorktreeDir)
|
||||
wantArr, _ := c.Expected["argv"].([]interface{})
|
||||
argvEq(t, c.Name, got.Args, wantArr)
|
||||
case "merge":
|
||||
got := MergePlan(buildID, "worktree battery")
|
||||
wantArr, _ := c.Expected["argv"].([]interface{})
|
||||
argvEq(t, c.Name, got.Args, wantArr)
|
||||
case "delete_pair":
|
||||
got := DeletePlan(buildID, filepath.Join(truthWorktreeDir, buildID))
|
||||
wantPair, _ := c.Expected["argv"].([]interface{})
|
||||
if len(got) != len(wantPair) {
|
||||
t.Fatalf("%s: %d plans, want %d", c.Name, len(got), len(wantPair))
|
||||
}
|
||||
for i, w := range wantPair {
|
||||
wantArr, _ := w.([]interface{})
|
||||
argvEq(t, c.Name, got[i].Args, wantArr)
|
||||
}
|
||||
case "round_trip":
|
||||
// state transitions verified in the live round-trip below.
|
||||
case "merge_failure_text":
|
||||
// exercised by the real-repo failure test below.
|
||||
case "create_failure_text":
|
||||
// exercised by the real-repo failure test below.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestWorktreeLiveRoundTrip — the plans drive REAL git: create → path
|
||||
// exists; merge of a committed branch lands; delete removes worktree+branch.
|
||||
func TestWorktreeLiveRoundTrip(t *testing.T) {
|
||||
repo := makeGitRepo(t)
|
||||
// redirect the plan cwd to the fixture repo
|
||||
orig := RepoDir
|
||||
RepoDir = repo
|
||||
t.Cleanup(func() { RepoDir = orig })
|
||||
|
||||
buildID := "20261007-182800"
|
||||
wtDir := t.TempDir()
|
||||
|
||||
// CREATE
|
||||
path, err := CreateWorktree(&BuildState{BuildID: buildID}, wtDir)
|
||||
if err != nil {
|
||||
t.Fatalf("create: %v", err)
|
||||
}
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
t.Fatalf("worktree path missing after create: %v", err)
|
||||
}
|
||||
|
||||
// commit something ON the build branch (gives merge something to do)
|
||||
if err := gitIn(path, "config", "user.email", "t@t"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := gitIn(path, "config", "user.name", "t"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(path, "feature.txt"), []byte("work\n"), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := gitIn(path, "add", "."); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := gitIn(path, "commit", "-qm", "feature"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// MERGE (from the repo root — git merge requires a work tree, and the
|
||||
// fixture repo's default branch is checked out at the root)
|
||||
state := &BuildState{BuildID: buildID, WorktreePath: &path}
|
||||
if err := MergeWorktree(state, "worktree battery"); err != nil {
|
||||
t.Fatalf("merge: %v", err)
|
||||
}
|
||||
if state.WorktreePath != nil {
|
||||
t.Error("merge must clear the worktree path (delete follows)")
|
||||
}
|
||||
data, err := os.ReadFile(filepath.Join(repo, "feature.txt"))
|
||||
if err != nil {
|
||||
t.Fatalf("merged file missing in repo root: %v", err)
|
||||
}
|
||||
if string(data) != "work\n" {
|
||||
t.Errorf("merged content %q", data)
|
||||
}
|
||||
|
||||
// error texts
|
||||
if err := gitIn(repo, "checkout", "-q", "-b", "conflict"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(repo, "feature.txt"), []byte("other\n"), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := gitIn(repo, "commit", "-qam", "conflict"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
plan := MergePlan(buildID, "worktree battery")
|
||||
cmd := exec.Command(plan.Args[0], plan.Args[1:]...)
|
||||
cmd.Dir = plan.Cwd
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err == nil {
|
||||
t.Fatal("conflicting merge unexpectedly succeeded")
|
||||
}
|
||||
got := "Failed to merge worktree: " + strings.TrimSpace(string(out))
|
||||
// The wrapper text is the stable contract; git's own message varies by
|
||||
// version ("fatal: not something we can merge" / "merge: <b> - not ...").
|
||||
if !strings.HasPrefix(got, "Failed to merge worktree: ") ||
|
||||
!strings.Contains(got, "not something we can merge") {
|
||||
t.Errorf("merge failure text shape: %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCreateFailureText — duplicate worktree → exact Python RuntimeError.
|
||||
func TestCreateFailureText(t *testing.T) {
|
||||
repo := makeGitRepo(t)
|
||||
orig := RepoDir
|
||||
RepoDir = repo
|
||||
t.Cleanup(func() { RepoDir = orig })
|
||||
|
||||
buildID := "20261007-182800"
|
||||
wtDir := t.TempDir()
|
||||
if _, err := CreateWorktree(&BuildState{BuildID: buildID}, wtDir); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err := CreateWorktree(&BuildState{BuildID: buildID}, wtDir)
|
||||
if err == nil {
|
||||
t.Fatal("duplicate create must fail")
|
||||
}
|
||||
want := "Failed to create worktree: "
|
||||
if !strings.HasPrefix(err.Error(), want) {
|
||||
t.Errorf("create failure text %q, want prefix %q", err.Error(), want)
|
||||
}
|
||||
}
|
||||
|
||||
// makeGitRepo — a real git repo fixture (seed commit; deterministic).
|
||||
func makeGitRepo(t *testing.T) string {
|
||||
t.Helper()
|
||||
root := t.TempDir()
|
||||
git := func(args ...string) {
|
||||
cmd := exec.Command("git", args...)
|
||||
cmd.Dir = root
|
||||
if out, err := cmd.CombinedOutput(); err != nil {
|
||||
t.Fatalf("git %v: %v: %s", args, err, out)
|
||||
}
|
||||
}
|
||||
git("init", "-q", "-b", "main")
|
||||
git("config", "user.email", "t@t")
|
||||
git("config", "user.name", "t")
|
||||
if err := os.WriteFile(filepath.Join(root, "seed.txt"), []byte("seed\n"), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
git("add", ".")
|
||||
git("commit", "-qm", "seed")
|
||||
return root
|
||||
}
|
||||
|
||||
func gitIn(dir string, args ...string) error {
|
||||
cmd := exec.Command("git", args...)
|
||||
cmd.Dir = dir
|
||||
if _, err := cmd.CombinedOutput(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func argvEq(t *testing.T, name string, got []string, wantArr []interface{}) {
|
||||
t.Helper()
|
||||
if len(got) != len(wantArr) {
|
||||
t.Errorf("%s: argv len %d, want %d: got %v", name, len(got), len(wantArr), got)
|
||||
return
|
||||
}
|
||||
for i, w := range wantArr {
|
||||
want, _ := w.(string)
|
||||
if got[i] != want {
|
||||
t.Errorf("%s: argv[%d] got %q want %q", name, i, got[i], want)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
package main
|
||||
|
||||
// worktree.go — batch 2: the worktree git operations of build_mode.py
|
||||
// (contract BUILDER-2026-10-07T18:26:40Z-8be89a). The PLAN layer is pure
|
||||
// (command argv + error mapping + state transitions — oracle-batteried);
|
||||
// the EXEC is reactive (real git, fixture-tested).
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// RepoDir — build_mode.REPO_DIR (var: tests redirect to a fixture repo).
|
||||
var RepoDir = "/mnt/workdrive/rune"
|
||||
|
||||
// GitPlan — one planned git invocation.
|
||||
type GitPlan struct {
|
||||
Args []string `json:"args"`
|
||||
Cwd string `json:"cwd"`
|
||||
}
|
||||
|
||||
// WorktreePlan — create_worktree: `git worktree add {path} -b build-{id}`.
|
||||
func WorktreePlan(buildID, worktreeDir string) GitPlan {
|
||||
return GitPlan{
|
||||
Args: []string{"git", "worktree", "add",
|
||||
filepath.Join(worktreeDir, buildID), "-b", "build-" + buildID},
|
||||
Cwd: RepoDir,
|
||||
}
|
||||
}
|
||||
|
||||
// DeletePlan — delete_worktree: `git worktree remove {path} --force` then
|
||||
// `git branch -D build-{id}`. BOTH run regardless of the first's outcome
|
||||
// (Python runs two bare subprocess.run calls — no failure short-circuit).
|
||||
func DeletePlan(buildID, worktreePath string) []GitPlan {
|
||||
return []GitPlan{
|
||||
{Args: []string{"git", "worktree", "remove", worktreePath, "--force"}, Cwd: RepoDir},
|
||||
{Args: []string{"git", "branch", "-D", "build-" + buildID}, Cwd: RepoDir},
|
||||
}
|
||||
}
|
||||
|
||||
// MergePlan — merge_worktree: `git merge build-{id} --no-ff -m {msg}`.
|
||||
func MergePlan(buildID, description string) GitPlan {
|
||||
return GitPlan{
|
||||
Args: []string{"git", "merge", "build-" + buildID, "--no-ff",
|
||||
"-m", fmt.Sprintf("Build %s: %s", buildID, description)},
|
||||
Cwd: RepoDir,
|
||||
}
|
||||
}
|
||||
|
||||
// RunPlan — execute one plan; map failure to the Python RuntimeError text.
|
||||
func RunPlan(p GitPlan) error {
|
||||
cmd := exec.Command(p.Args[0], p.Args[1:]...)
|
||||
cmd.Dir = p.Cwd
|
||||
if out, err := cmd.CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("Failed to create worktree: %s", strings.TrimSpace(string(out)))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RunPlans — execute a sequence; failures don't short-circuit the
|
||||
// remaining steps (parity with the Python's two bare subprocess calls).
|
||||
// The FIRST error is returned (the merge path raises on its single call;
|
||||
// the delete path swallows both).
|
||||
func RunPlans(plans []GitPlan) error {
|
||||
var firstErr error
|
||||
for _, p := range plans {
|
||||
if err := RunPlan(p); err != nil && firstErr == nil {
|
||||
firstErr = err
|
||||
}
|
||||
}
|
||||
return firstErr
|
||||
}
|
||||
|
||||
// CreateWorktree — the reactive wrapper: plan + exec + state transition.
|
||||
func CreateWorktree(state *BuildState, worktreeDir string) (string, error) {
|
||||
plan := WorktreePlan(state.BuildID, worktreeDir)
|
||||
if err := RunPlan(mapCreateError(plan)); err != nil {
|
||||
return "", err
|
||||
}
|
||||
path := plan.Args[3]
|
||||
state.WorktreePath = &path
|
||||
return path, nil
|
||||
}
|
||||
|
||||
// mapCreateError — create failures carry the "create worktree" text;
|
||||
// RunPlan's default is already that text (single call site).
|
||||
func mapCreateError(p GitPlan) GitPlan { return p }
|
||||
|
||||
// DeleteWorktree — plan + exec + clear state. Errors swallowed (parity:
|
||||
// Python ignores both results).
|
||||
func DeleteWorktree(state *BuildState) {
|
||||
if state.WorktreePath == nil {
|
||||
return
|
||||
}
|
||||
_ = RunPlans(DeletePlan(state.BuildID, *state.WorktreePath))
|
||||
state.WorktreePath = nil
|
||||
}
|
||||
|
||||
// MergeWorktree — plan + exec + cleanup (delete after merge). Error text:
|
||||
// "Failed to merge worktree: {stderr}".
|
||||
func MergeWorktree(state *BuildState, description string) error {
|
||||
if state.WorktreePath == nil {
|
||||
return nil
|
||||
}
|
||||
plan := MergePlan(state.BuildID, description)
|
||||
cmd := exec.Command(plan.Args[0], plan.Args[1:]...)
|
||||
cmd.Dir = plan.Cwd
|
||||
if out, err := cmd.CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("Failed to merge worktree: %s", strings.TrimSpace(string(out)))
|
||||
}
|
||||
DeleteWorktree(state)
|
||||
return nil
|
||||
}
|
||||
|
||||
// EnsureDir — mkdir -p for the builds/worktree dirs (the Python __init__).
|
||||
func EnsureDirs(buildsDir, worktreeDir string) error {
|
||||
for _, d := range []string{buildsDir, worktreeDir} {
|
||||
if err := os.MkdirAll(d, 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Vendored
BIN
Binary file not shown.
Vendored
+31
-12
@@ -2,11 +2,12 @@
|
||||
// Replaces src/rune/cache.py. Uses modernc.org/sqlite (pure Go).
|
||||
//
|
||||
// Usage:
|
||||
// cache get <key>
|
||||
// cache put <key> <value> [--scope <s>]
|
||||
// cache invalidate <key> [reason]
|
||||
// cache invalidate-scope <scope> [reason]
|
||||
// cache stats
|
||||
//
|
||||
// cache get <key>
|
||||
// cache put <key> <value> [--scope <s>]
|
||||
// cache invalidate <key> [reason]
|
||||
// cache invalidate-scope <scope> [reason]
|
||||
// cache stats
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -40,7 +41,9 @@ func connect() (*sql.DB, error) {
|
||||
p := dbPath()
|
||||
os.MkdirAll(filepath.Dir(p), 0755)
|
||||
db, err := sql.Open("sqlite", p)
|
||||
if err != nil { return nil, err }
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
db.Exec("PRAGMA journal_mode=WAL")
|
||||
db.Exec("PRAGMA synchronous=NORMAL")
|
||||
db.Exec(`CREATE TABLE IF NOT EXISTS cache (
|
||||
@@ -78,7 +81,10 @@ func main() {
|
||||
|
||||
switch cmd {
|
||||
case "get":
|
||||
if len(args) < 1 { fmt.Fprintln(os.Stderr, "usage: cache get <key>"); os.Exit(1) }
|
||||
if len(args) < 1 {
|
||||
fmt.Fprintln(os.Stderr, "usage: cache get <key>")
|
||||
os.Exit(1)
|
||||
}
|
||||
var compressed, full, contentHash, scope, createdAt sql.NullString
|
||||
var hitCount int
|
||||
err := db.QueryRow(
|
||||
@@ -107,12 +113,17 @@ func main() {
|
||||
fmt.Println(string(out))
|
||||
|
||||
case "put":
|
||||
if len(args) < 2 { fmt.Fprintln(os.Stderr, "usage: cache put <key> <value> [--scope <s>]"); os.Exit(1) }
|
||||
if len(args) < 2 {
|
||||
fmt.Fprintln(os.Stderr, "usage: cache put <key> <value> [--scope <s>]")
|
||||
os.Exit(1)
|
||||
}
|
||||
key := args[0]
|
||||
value := args[1]
|
||||
scope := "general"
|
||||
for i := 2; i < len(args); i++ {
|
||||
if args[i] == "--scope" && i+1 < len(args) { scope = args[i+1] }
|
||||
if args[i] == "--scope" && i+1 < len(args) {
|
||||
scope = args[i+1]
|
||||
}
|
||||
}
|
||||
contentHash := hashText(value)
|
||||
db.Exec(`INSERT INTO cache (key, scope, content_hash, full_value, compressed_value, source_hash, created_at, invalidated_at, hit_count, miss_count)
|
||||
@@ -124,13 +135,19 @@ func main() {
|
||||
fmt.Println(string(out))
|
||||
|
||||
case "invalidate":
|
||||
if len(args) < 1 { fmt.Fprintln(os.Stderr, "usage: cache invalidate <key>"); os.Exit(1) }
|
||||
if len(args) < 1 {
|
||||
fmt.Fprintln(os.Stderr, "usage: cache invalidate <key>")
|
||||
os.Exit(1)
|
||||
}
|
||||
db.Exec("UPDATE cache SET invalidated_at=? WHERE key=? AND invalidated_at IS NULL", nowISO(), args[0])
|
||||
out, _ := json.Marshal(Result{Success: true, Data: map[string]interface{}{"invalidated": args[0]}})
|
||||
fmt.Println(string(out))
|
||||
|
||||
case "invalidate-scope":
|
||||
if len(args) < 1 { fmt.Fprintln(os.Stderr, "usage: cache invalidate-scope <scope>"); os.Exit(1) }
|
||||
if len(args) < 1 {
|
||||
fmt.Fprintln(os.Stderr, "usage: cache invalidate-scope <scope>")
|
||||
os.Exit(1)
|
||||
}
|
||||
db.Exec("UPDATE cache SET invalidated_at=? WHERE scope=? AND invalidated_at IS NULL", nowISO(), args[0])
|
||||
out, _ := json.Marshal(Result{Success: true, Data: map[string]interface{}{"invalidated_scope": args[0]}})
|
||||
fmt.Println(string(out))
|
||||
@@ -149,6 +166,8 @@ func main() {
|
||||
}
|
||||
|
||||
func min(a, b int) int {
|
||||
if a < b { return a }
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
@@ -21,9 +21,12 @@ func call(symbol string, arg string) (map[string]interface{}, error) {
|
||||
defer C.free(unsafe.Pointer(cArg))
|
||||
var result *C.char
|
||||
switch symbol {
|
||||
case "cs_evaluate_fit": result = C.cs_evaluate_fit(cArg)
|
||||
case "cs_score_card": result = C.cs_score_card(cArg)
|
||||
default: return nil, fmt.Errorf("unknown symbol: %s", symbol)
|
||||
case "cs_evaluate_fit":
|
||||
result = C.cs_evaluate_fit(cArg)
|
||||
case "cs_score_card":
|
||||
result = C.cs_score_card(cArg)
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown symbol: %s", symbol)
|
||||
}
|
||||
defer C.cs_free_string(result)
|
||||
var out map[string]interface{}
|
||||
@@ -34,4 +37,4 @@ func call(symbol string, arg string) (map[string]interface{}, error) {
|
||||
}
|
||||
|
||||
func EvaluateFit(arg string) (map[string]interface{}, error) { return call("cs_evaluate_fit", arg) }
|
||||
func ScoreCard(arg string) (map[string]interface{}, error) { return call("cs_score_card", arg) }
|
||||
func ScoreCard(arg string) (map[string]interface{}, error) { return call("cs_score_card", arg) }
|
||||
@@ -92,7 +92,9 @@ func TierGet(entry map[string]interface{}, now string, hotDays, warmDays float64
|
||||
"entry": entry, "now": now, "hot": hotDays, "warm": warmDays,
|
||||
})
|
||||
result, err := call("cc_tier_get", string(arg))
|
||||
if err != nil { return nil, err }
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var out map[string]interface{}
|
||||
json.Unmarshal([]byte(result), &out)
|
||||
return out, nil
|
||||
@@ -105,7 +107,9 @@ func ConsolidationMemory(text string, score float64, washCount int, entryID *int
|
||||
"entry_id": entryID, "now": now,
|
||||
})
|
||||
result, err := call("cc_consolidation_memory", string(arg))
|
||||
if err != nil { return nil, err }
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var out map[string]interface{}
|
||||
json.Unmarshal([]byte(result), &out)
|
||||
return out, nil
|
||||
@@ -119,7 +123,9 @@ func ConsolidationNeuron(slug, body string, importance float64, triggers []strin
|
||||
"entry_id": entryID, "now": now,
|
||||
})
|
||||
result, err := call("cc_consolidation_neuron", string(arg))
|
||||
if err != nil { return nil, err }
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var out map[string]interface{}
|
||||
json.Unmarshal([]byte(result), &out)
|
||||
return out, nil
|
||||
@@ -131,7 +137,9 @@ func WitnessExtractTopic(text string, stopwords []string, cap int) ([]string, er
|
||||
"text": text, "stopwords": stopwords, "cap": cap,
|
||||
})
|
||||
result, err := call("cc_witness_extract_topic", string(arg))
|
||||
if err != nil { return nil, err }
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var out []string
|
||||
json.Unmarshal([]byte(result), &out)
|
||||
return out, nil
|
||||
@@ -145,7 +153,9 @@ func WitnessSignals(message string, tables map[string]interface{}, toolError, ta
|
||||
"recent_topics": recentTopics,
|
||||
})
|
||||
result, err := call("cc_witness_signals", string(arg))
|
||||
if err != nil { return nil, err }
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var out map[string]interface{}
|
||||
json.Unmarshal([]byte(result), &out)
|
||||
return out, nil
|
||||
@@ -155,7 +165,9 @@ func WitnessSignals(message string, tables map[string]interface{}, toolError, ta
|
||||
func SafetyGetParams(rows []map[string]interface{}) (map[string]interface{}, error) {
|
||||
arg, _ := json.Marshal(rows)
|
||||
result, err := call("cc_safety_get_params", string(arg))
|
||||
if err != nil { return nil, err }
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var out map[string]interface{}
|
||||
json.Unmarshal([]byte(result), &out)
|
||||
return out, nil
|
||||
@@ -167,7 +179,9 @@ func DriftCheck(text string, patterns []map[string]interface{}) ([]string, error
|
||||
"text": text, "patterns": patterns,
|
||||
})
|
||||
result, err := call("cc_drift_check", string(arg))
|
||||
if err != nil { return nil, err }
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var out []string
|
||||
json.Unmarshal([]byte(result), &out)
|
||||
return out, nil
|
||||
@@ -179,7 +193,9 @@ func PinsRecall(pins []map[string]interface{}, term string, limit int) ([]map[st
|
||||
"pins": pins, "term": term, "limit": limit,
|
||||
})
|
||||
result, err := call("cc_pins_recall", string(arg))
|
||||
if err != nil { return nil, err }
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var out []map[string]interface{}
|
||||
json.Unmarshal([]byte(result), &out)
|
||||
return out, nil
|
||||
@@ -189,7 +205,9 @@ func PinsRecall(pins []map[string]interface{}, term string, limit int) ([]map[st
|
||||
func GrowthNearDup(a, b string) (bool, error) {
|
||||
arg, _ := json.Marshal(map[string]interface{}{"a": a, "b": b})
|
||||
result, err := call("cc_growth_near_dup", string(arg))
|
||||
if err != nil { return false, err }
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
var out map[string]interface{}
|
||||
json.Unmarshal([]byte(result), &out)
|
||||
isDup, _ := out["value"].(bool)
|
||||
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
|
||||
func TestTierGet(t *testing.T) {
|
||||
entry := map[string]interface{}{
|
||||
"wash_count": 25,
|
||||
"last_accessed": "2026-10-06T12:00:00Z",
|
||||
"wash_count": 25,
|
||||
"last_accessed": "2026-10-06T12:00:00Z",
|
||||
}
|
||||
result, err := TierGet(entry, "2026-10-06T20:00:00Z", 7.0, 30.0)
|
||||
if err != nil {
|
||||
|
||||
@@ -27,15 +27,24 @@ func call(symbol string, arg string) (map[string]interface{}, error) {
|
||||
defer C.free(unsafe.Pointer(cArg))
|
||||
var result *C.char
|
||||
switch symbol {
|
||||
case "ep_append_entry": result = C.ep_append_entry(cArg)
|
||||
case "ep_bl_accept_limitation": result = C.ep_bl_accept_limitation(cArg)
|
||||
case "ep_bl_define": result = C.ep_bl_define(cArg)
|
||||
case "ep_bl_get": result = C.ep_bl_get(cArg)
|
||||
case "ep_bl_invalidate": result = C.ep_bl_invalidate(cArg)
|
||||
case "ep_bl_outstanding": result = C.ep_bl_outstanding(cArg)
|
||||
case "ep_bl_record_auto": result = C.ep_bl_record_auto(cArg)
|
||||
case "ep_bl_record_real_world": result = C.ep_bl_record_real_world(cArg)
|
||||
default: return nil, fmt.Errorf("unknown symbol: %s", symbol)
|
||||
case "ep_append_entry":
|
||||
result = C.ep_append_entry(cArg)
|
||||
case "ep_bl_accept_limitation":
|
||||
result = C.ep_bl_accept_limitation(cArg)
|
||||
case "ep_bl_define":
|
||||
result = C.ep_bl_define(cArg)
|
||||
case "ep_bl_get":
|
||||
result = C.ep_bl_get(cArg)
|
||||
case "ep_bl_invalidate":
|
||||
result = C.ep_bl_invalidate(cArg)
|
||||
case "ep_bl_outstanding":
|
||||
result = C.ep_bl_outstanding(cArg)
|
||||
case "ep_bl_record_auto":
|
||||
result = C.ep_bl_record_auto(cArg)
|
||||
case "ep_bl_record_real_world":
|
||||
result = C.ep_bl_record_real_world(cArg)
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown symbol: %s", symbol)
|
||||
}
|
||||
defer C.ep_free_string(result)
|
||||
var out map[string]interface{}
|
||||
@@ -46,10 +55,14 @@ func call(symbol string, arg string) (map[string]interface{}, error) {
|
||||
}
|
||||
|
||||
func AppendEntry(arg string) (map[string]interface{}, error) { return call("ep_append_entry", arg) }
|
||||
func BLAcceptLimitation(arg string) (map[string]interface{}, error) { return call("ep_bl_accept_limitation", arg) }
|
||||
func BLDefine(arg string) (map[string]interface{}, error) { return call("ep_bl_define", arg) }
|
||||
func BLGet(arg string) (map[string]interface{}, error) { return call("ep_bl_get", arg) }
|
||||
func BLInvalidate(arg string) (map[string]interface{}, error) { return call("ep_bl_invalidate", arg) }
|
||||
func BLAcceptLimitation(arg string) (map[string]interface{}, error) {
|
||||
return call("ep_bl_accept_limitation", arg)
|
||||
}
|
||||
func BLDefine(arg string) (map[string]interface{}, error) { return call("ep_bl_define", arg) }
|
||||
func BLGet(arg string) (map[string]interface{}, error) { return call("ep_bl_get", arg) }
|
||||
func BLInvalidate(arg string) (map[string]interface{}, error) { return call("ep_bl_invalidate", arg) }
|
||||
func BLOutstanding(arg string) (map[string]interface{}, error) { return call("ep_bl_outstanding", arg) }
|
||||
func BLRecordAuto(arg string) (map[string]interface{}, error) { return call("ep_bl_record_auto", arg) }
|
||||
func BLRecordRealWorld(arg string) (map[string]interface{}, error) { return call("ep_bl_record_real_world", arg) }
|
||||
func BLRecordAuto(arg string) (map[string]interface{}, error) { return call("ep_bl_record_auto", arg) }
|
||||
func BLRecordRealWorld(arg string) (map[string]interface{}, error) {
|
||||
return call("ep_bl_record_real_world", arg)
|
||||
}
|
||||
@@ -26,13 +26,20 @@ func call(symbol string, arg string) (map[string]interface{}, error) {
|
||||
defer C.free(unsafe.Pointer(cArg))
|
||||
var result *C.char
|
||||
switch symbol {
|
||||
case "qm_adaptive_rate": result = C.qm_adaptive_rate(cArg)
|
||||
case "qm_classify": result = C.qm_classify(cArg)
|
||||
case "qm_dead_weight": result = C.qm_dead_weight(cArg)
|
||||
case "qm_health": result = C.qm_health(cArg)
|
||||
case "qm_new": result = C.qm_new(cArg)
|
||||
case "qm_record": result = C.qm_record(cArg)
|
||||
default: return nil, fmt.Errorf("unknown symbol: %s", symbol)
|
||||
case "qm_adaptive_rate":
|
||||
result = C.qm_adaptive_rate(cArg)
|
||||
case "qm_classify":
|
||||
result = C.qm_classify(cArg)
|
||||
case "qm_dead_weight":
|
||||
result = C.qm_dead_weight(cArg)
|
||||
case "qm_health":
|
||||
result = C.qm_health(cArg)
|
||||
case "qm_new":
|
||||
result = C.qm_new(cArg)
|
||||
case "qm_record":
|
||||
result = C.qm_record(cArg)
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown symbol: %s", symbol)
|
||||
}
|
||||
defer C.qm_string_free(result)
|
||||
var out map[string]interface{}
|
||||
@@ -43,8 +50,8 @@ func call(symbol string, arg string) (map[string]interface{}, error) {
|
||||
}
|
||||
|
||||
func AdaptiveRate(arg string) (map[string]interface{}, error) { return call("qm_adaptive_rate", arg) }
|
||||
func Classify(arg string) (map[string]interface{}, error) { return call("qm_classify", arg) }
|
||||
func DeadWeight(arg string) (map[string]interface{}, error) { return call("qm_dead_weight", arg) }
|
||||
func Health(arg string) (map[string]interface{}, error) { return call("qm_health", arg) }
|
||||
func New(arg string) (map[string]interface{}, error) { return call("qm_new", arg) }
|
||||
func Record(arg string) (map[string]interface{}, error) { return call("qm_record", arg) }
|
||||
func Classify(arg string) (map[string]interface{}, error) { return call("qm_classify", arg) }
|
||||
func DeadWeight(arg string) (map[string]interface{}, error) { return call("qm_dead_weight", arg) }
|
||||
func Health(arg string) (map[string]interface{}, error) { return call("qm_health", arg) }
|
||||
func New(arg string) (map[string]interface{}, error) { return call("qm_new", arg) }
|
||||
func Record(arg string) (map[string]interface{}, error) { return call("qm_record", arg) }
|
||||
@@ -26,14 +26,22 @@ func call(symbol string, arg string) (map[string]interface{}, error) {
|
||||
defer C.free(unsafe.Pointer(cArg))
|
||||
var result *C.char
|
||||
switch symbol {
|
||||
case "ms_base_level_from_uses": result = C.ms_base_level_from_uses(cArg)
|
||||
case "ms_check_promotion_eligibility": result = C.ms_check_promotion_eligibility(cArg)
|
||||
case "ms_compress_entry": result = C.ms_compress_entry(cArg)
|
||||
case "ms_compress_text": result = C.ms_compress_text(cArg)
|
||||
case "ms_compute_priority": result = C.ms_compute_priority(cArg)
|
||||
case "ms_compute_value": result = C.ms_compute_value(cArg)
|
||||
case "ms_get_compression_ratio": result = C.ms_get_compression_ratio(cArg)
|
||||
default: return nil, fmt.Errorf("unknown symbol: %s", symbol)
|
||||
case "ms_base_level_from_uses":
|
||||
result = C.ms_base_level_from_uses(cArg)
|
||||
case "ms_check_promotion_eligibility":
|
||||
result = C.ms_check_promotion_eligibility(cArg)
|
||||
case "ms_compress_entry":
|
||||
result = C.ms_compress_entry(cArg)
|
||||
case "ms_compress_text":
|
||||
result = C.ms_compress_text(cArg)
|
||||
case "ms_compute_priority":
|
||||
result = C.ms_compute_priority(cArg)
|
||||
case "ms_compute_value":
|
||||
result = C.ms_compute_value(cArg)
|
||||
case "ms_get_compression_ratio":
|
||||
result = C.ms_get_compression_ratio(cArg)
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown symbol: %s", symbol)
|
||||
}
|
||||
defer C.ms_free_string(result)
|
||||
var out map[string]interface{}
|
||||
@@ -43,10 +51,18 @@ func call(symbol string, arg string) (map[string]interface{}, error) {
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func BaseLevelFromUses(arg string) (map[string]interface{}, error) { return call("ms_base_level_from_uses", arg) }
|
||||
func CheckPromotionEligibility(arg string) (map[string]interface{}, error) { return call("ms_check_promotion_eligibility", arg) }
|
||||
func BaseLevelFromUses(arg string) (map[string]interface{}, error) {
|
||||
return call("ms_base_level_from_uses", arg)
|
||||
}
|
||||
func CheckPromotionEligibility(arg string) (map[string]interface{}, error) {
|
||||
return call("ms_check_promotion_eligibility", arg)
|
||||
}
|
||||
func CompressEntry(arg string) (map[string]interface{}, error) { return call("ms_compress_entry", arg) }
|
||||
func CompressText(arg string) (map[string]interface{}, error) { return call("ms_compress_text", arg) }
|
||||
func ComputePriority(arg string) (map[string]interface{}, error) { return call("ms_compute_priority", arg) }
|
||||
func CompressText(arg string) (map[string]interface{}, error) { return call("ms_compress_text", arg) }
|
||||
func ComputePriority(arg string) (map[string]interface{}, error) {
|
||||
return call("ms_compute_priority", arg)
|
||||
}
|
||||
func ComputeValue(arg string) (map[string]interface{}, error) { return call("ms_compute_value", arg) }
|
||||
func GetCompressionRatio(arg string) (map[string]interface{}, error) { return call("ms_get_compression_ratio", arg) }
|
||||
func GetCompressionRatio(arg string) (map[string]interface{}, error) {
|
||||
return call("ms_get_compression_ratio", arg)
|
||||
}
|
||||
@@ -23,11 +23,16 @@ func call(symbol string, arg string) (map[string]interface{}, error) {
|
||||
defer C.free(unsafe.Pointer(cArg))
|
||||
var result *C.char
|
||||
switch symbol {
|
||||
case "sp_drift": result = C.sp_drift(cArg)
|
||||
case "sp_export": result = C.sp_export(cArg)
|
||||
case "sp_project": result = C.sp_project(cArg)
|
||||
case "sp_query": result = C.sp_query(cArg)
|
||||
default: return nil, fmt.Errorf("unknown symbol: %s", symbol)
|
||||
case "sp_drift":
|
||||
result = C.sp_drift(cArg)
|
||||
case "sp_export":
|
||||
result = C.sp_export(cArg)
|
||||
case "sp_project":
|
||||
result = C.sp_project(cArg)
|
||||
case "sp_query":
|
||||
result = C.sp_query(cArg)
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown symbol: %s", symbol)
|
||||
}
|
||||
defer C.sp_free_string(result)
|
||||
var out map[string]interface{}
|
||||
@@ -37,7 +42,7 @@ func call(symbol string, arg string) (map[string]interface{}, error) {
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func Drift(arg string) (map[string]interface{}, error) { return call("sp_drift", arg) }
|
||||
func Export(arg string) (map[string]interface{}, error) { return call("sp_export", arg) }
|
||||
func Drift(arg string) (map[string]interface{}, error) { return call("sp_drift", arg) }
|
||||
func Export(arg string) (map[string]interface{}, error) { return call("sp_export", arg) }
|
||||
func Project(arg string) (map[string]interface{}, error) { return call("sp_project", arg) }
|
||||
func Query(arg string) (map[string]interface{}, error) { return call("sp_query", arg) }
|
||||
func Query(arg string) (map[string]interface{}, error) { return call("sp_query", arg) }
|
||||
@@ -21,9 +21,12 @@ func call(symbol string, arg string) (map[string]interface{}, error) {
|
||||
defer C.free(unsafe.Pointer(cArg))
|
||||
var result *C.char
|
||||
switch symbol {
|
||||
case "tc_compress_context": result = C.tc_compress_context(cArg)
|
||||
case "tc_estimate_tokens": result = C.tc_estimate_tokens(cArg)
|
||||
default: return nil, fmt.Errorf("unknown symbol: %s", symbol)
|
||||
case "tc_compress_context":
|
||||
result = C.tc_compress_context(cArg)
|
||||
case "tc_estimate_tokens":
|
||||
result = C.tc_estimate_tokens(cArg)
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown symbol: %s", symbol)
|
||||
}
|
||||
defer C.tc_free_string(result)
|
||||
var out map[string]interface{}
|
||||
@@ -33,5 +36,9 @@ func call(symbol string, arg string) (map[string]interface{}, error) {
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func CompressContext(arg string) (map[string]interface{}, error) { return call("tc_compress_context", arg) }
|
||||
func EstimateTokens(arg string) (map[string]interface{}, error) { return call("tc_estimate_tokens", arg) }
|
||||
func CompressContext(arg string) (map[string]interface{}, error) {
|
||||
return call("tc_compress_context", arg)
|
||||
}
|
||||
func EstimateTokens(arg string) (map[string]interface{}, error) {
|
||||
return call("tc_estimate_tokens", arg)
|
||||
}
|
||||
Binary file not shown.
@@ -68,9 +68,9 @@ func (p *ConnectionPool) Status() map[string]interface{} {
|
||||
dbs := make([]string, len(p.order))
|
||||
copy(dbs, p.order)
|
||||
return map[string]interface{}{
|
||||
"connections": len(p.conns),
|
||||
"connections": len(p.conns),
|
||||
"max_connections": p.maxConns,
|
||||
"databases": dbs,
|
||||
"databases": dbs,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -2,7 +2,8 @@
|
||||
// Replaces src/rune/core_server.py. Go net/http, goroutine-safe.
|
||||
//
|
||||
// Usage:
|
||||
// core-server [--port <port>]
|
||||
//
|
||||
// core-server [--port <port>]
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -26,7 +27,7 @@ type HealthResponse struct {
|
||||
}
|
||||
|
||||
type ChatRequest struct {
|
||||
Model string `json:"model"`
|
||||
Model string `json:"model"`
|
||||
Messages []map[string]string `json:"messages"`
|
||||
}
|
||||
|
||||
@@ -53,8 +54,8 @@ func (s *Server) chatHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
// Proxy to the Ollama provider
|
||||
out, _ := json.Marshal(map[string]interface{}{
|
||||
"status": "received",
|
||||
"model": req.Model,
|
||||
"status": "received",
|
||||
"model": req.Model,
|
||||
"messages_count": len(req.Messages),
|
||||
})
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
Binary file not shown.
@@ -48,7 +48,7 @@ func main() {
|
||||
strength := intOf(req["strength"], 1)
|
||||
minS := intOf(req["min_strength"], 3)
|
||||
wash := intOf(req["wash_count"], 0)
|
||||
emit(LePromoteEligible(proposal, existing, strength, minS, wash))
|
||||
emit(LePromoteEligible(proposal, []string{existing}, strength, minS, wash))
|
||||
default:
|
||||
emit(map[string]interface{}{"error": "unknown op: " + op})
|
||||
os.Exit(1)
|
||||
|
||||
Executable
BIN
Binary file not shown.
@@ -0,0 +1,69 @@
|
||||
package main
|
||||
|
||||
// fuzz_test.go — native Go fuzz targets on the curator-core byte surfaces
|
||||
// (polyglot gate contract: fuzzing wired into the pipeline). Invariants:
|
||||
// panic-free + structural properties that must hold for ARBITRARY input.
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// FuzzNorm — Norm never panics, emits no markdown/punctuation, and is
|
||||
// deterministic (same input → same output).
|
||||
func FuzzNorm(f *testing.F) {
|
||||
for _, s := range []string{"", "- **bold** entry", "### Header text", " spaced out ",
|
||||
"punctuation, gone!", "> quoted line", "***___```", "\t\ntabs\nand\nnewlines", "üñí©ödé ✅"} {
|
||||
f.Add(s)
|
||||
}
|
||||
f.Fuzz(func(t *testing.T, s string) {
|
||||
a := Norm(s)
|
||||
b := Norm(s)
|
||||
if a != b {
|
||||
t.Fatalf("Norm not deterministic: %q → %q vs %q", s, a, b)
|
||||
}
|
||||
for _, ch := range a {
|
||||
if strings.ContainsRune("*_`#,.!?;:()", ch) {
|
||||
t.Fatalf("Norm kept punctuation %q for input %q → %q", ch, s, a)
|
||||
}
|
||||
}
|
||||
if strings.ToLower(a) != a {
|
||||
t.Fatalf("Norm not lowercased: %q → %q", s, a)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// FuzzStem — the stemmer never panics, never returns an empty stem for
|
||||
// non-empty input of >=4 chars, and is idempotent (stem(stem(w))==stem(w)).
|
||||
func FuzzStem(f *testing.F) {
|
||||
for _, s := range []string{"migration", "migrated", "honesty", "retroactively",
|
||||
"capacity", "abilities", "nations", "ations", "e", "s", "y", ""} {
|
||||
f.Add(s)
|
||||
}
|
||||
f.Fuzz(func(t *testing.T, s string) {
|
||||
one := Stem(s)
|
||||
if s != "" && len([]rune(s)) >= 4 && one == "" {
|
||||
t.Fatalf("Stem emptied a >=4-char word: %q", s)
|
||||
}
|
||||
if len(one) > len(s) {
|
||||
t.Fatalf("Stem grew the word: %q (%d) → %q (%d)", s, len(s), one, len(one))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// FuzzOverlap — Jaccard stays in [0,1] and is symmetric for arbitrary text.
|
||||
func FuzzOverlap(f *testing.F) {
|
||||
f.Add("alpha beta", "beta gamma")
|
||||
f.Add("", "anything")
|
||||
f.Add("!!??", "''``")
|
||||
f.Add("Mixed CASE text", "mixed case TEXT")
|
||||
f.Fuzz(func(t *testing.T, a, b string) {
|
||||
x, y := Overlap(a, b), Overlap(b, a)
|
||||
if x != y {
|
||||
t.Fatalf("Overlap not symmetric: %q vs %q → %f vs %f", a, b, x, y)
|
||||
}
|
||||
if x < 0.0 || x > 1.0 {
|
||||
t.Fatalf("Overlap out of [0,1]: %f (inputs %q %q)", x, a, b)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,302 @@
|
||||
package main
|
||||
|
||||
// lexicon.go — batch 2: the REAL lexicon meta-judgement (verbatim port of
|
||||
// src/rune/lexicon_evolution.py, recovered from ~/odysseus 0308ccc6). This
|
||||
// REPLACES the batch-1 degrade: LePromoteEligible now carries the actual
|
||||
// distinctiveness semantics instead of the strength-only shadow. The degrade
|
||||
// path is retained as LePromoteEligibleDegrade for byte-parity with the
|
||||
// historical batch-1 truth.
|
||||
//
|
||||
// PARITY NOTE: the Python oracle is the LIVE recovered module; truth2.json
|
||||
// cases were generated against it 2026-10-07.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// lexStop — SAME set as memory_store._STOP (Python comment kept verbatim:
|
||||
// single source of truth would be ideal; keep in sync).
|
||||
var lexStop = map[string]bool{
|
||||
"the": true, "and": true, "for": true, "with": true, "that": true,
|
||||
"this": true, "from": true, "into": true, "when": true, "then": true,
|
||||
"were": true, "have": true, "been": true, "will": true, "was": true,
|
||||
"are": true, "but": true, "not": true, "you": true, "your": true,
|
||||
"also": true, "its": true, "his": true, "her": true, "him": true,
|
||||
"over": true, "under": true, "them": true, "they": true, "there": true,
|
||||
"about": true, "after": true, "what": true, "which": true, "who": true,
|
||||
"how": true, "why": true, "where": true, "while": true, "just": true,
|
||||
"more": true, "each": true, "than": true, "very": true, "such": true,
|
||||
"some": true, "only": true, "prefers": true, "needs": true,
|
||||
"wants": true, "likes": true,
|
||||
}
|
||||
|
||||
// lexFraming — generic framing words never carry a relation.
|
||||
var lexFraming = map[string]bool{
|
||||
"work": true, "always": true, "way": true, "thing": true, "value": true,
|
||||
"identity": true, "persona": true, "someone": true, "something": true,
|
||||
"never": true, "before": true, "after": true, "then": true, "when": true,
|
||||
"while": true, "must": true, "should": true, "will": true, "would": true,
|
||||
"could": true, "can": true, "have": true, "has": true, "had": true,
|
||||
"been": true, "being": true, "used": true, "use": true, "uses": true,
|
||||
"make": true, "makes": true, "made": true, "keep": true, "keeps": true,
|
||||
"take": true, "takes": true, "took": true, "get": true, "gets": true,
|
||||
"got": true, "going": true,
|
||||
}
|
||||
|
||||
// lexNegation — negation markers (substring check against the lowercased
|
||||
// fact, Python `n in low` semantics).
|
||||
var lexNegation = []string{
|
||||
"not", "never", "don't", "doesn't", "cannot", "can't", "no",
|
||||
"nor", "neither", "without", "avoid", "reject", "against",
|
||||
"refuses", "refuse", "opposed",
|
||||
}
|
||||
|
||||
const lexDistinctiveDF = 0.05 // same rare-word threshold as recall/associations
|
||||
|
||||
var wordRe = regexp.MustCompile(`[a-z]{4,}`)
|
||||
|
||||
// ContentWords — significant content words (length > 3, not stop/framing).
|
||||
func ContentWords(text string) []string {
|
||||
var out []string
|
||||
for _, w := range wordRe.FindAllString(strings.ToLower(text), -1) {
|
||||
if !lexStop[w] && !lexFraming[w] {
|
||||
out = append(out, w)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Stem — light morphological fold (ordered longest-first suffix rules with
|
||||
// optional post-fold, then the y->i rule).
|
||||
func Stem(w string) string {
|
||||
rules := []struct{ suf, fold string }{
|
||||
{"ationally", ""}, {"ableness", ""}, {"iveness", ""},
|
||||
{"ation", ""}, {"ating", ""}, {"ative", "e"},
|
||||
{"atively", "e"}, {"ability", ""}, {"ities", "y"},
|
||||
{"ingly", ""}, {"fully", ""}, {"ness", ""}, {"ment", ""},
|
||||
{"itive", "e"}, {"atable", "e"}, {"ated", ""}, {"ates", "e"},
|
||||
{"ions", ""}, {"ings", ""}, {"able", "e"}, {"ible", "e"},
|
||||
{"ing", "e"}, {"ed", ""}, {"es", ""}, {"ly", ""}, {"s", ""}, {"e", ""},
|
||||
}
|
||||
for _, r := range rules {
|
||||
if len(w)-len(r.suf) >= 4 && strings.HasSuffix(w, r.suf) {
|
||||
w = w[:len(w)-len(r.suf)]
|
||||
if r.fold != "" {
|
||||
w = w + r.fold
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
// y -> i fold (honesty -> honest)
|
||||
if len(w) > 4 && strings.HasSuffix(w, "y") && !strings.ContainsRune("aeiouy", rune(w[len(w)-2])) {
|
||||
w = w[:len(w)-1]
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
// DistinctiveTerms — terms in fact RARE across the corpus (< 5% df,
|
||||
// stem-aware document frequency, threshold max(total*0.05, 1.5)).
|
||||
func DistinctiveTerms(fact string, corpusTexts []string) map[string]bool {
|
||||
total := len(corpusTexts)
|
||||
if total < 1 {
|
||||
total = 1
|
||||
}
|
||||
factTerms := map[string]bool{}
|
||||
for _, w := range ContentWords(fact) {
|
||||
factTerms[w] = true
|
||||
}
|
||||
if len(factTerms) == 0 {
|
||||
return map[string]bool{}
|
||||
}
|
||||
docTerms := map[string]int{}
|
||||
for _, t := range corpusTexts {
|
||||
seen := map[string]bool{}
|
||||
for _, w := range wordRe.FindAllString(strings.ToLower(t), -1) {
|
||||
seen[Stem(w)] = true
|
||||
}
|
||||
for s := range seen {
|
||||
docTerms[s]++
|
||||
}
|
||||
}
|
||||
thresh := float64(total) * lexDistinctiveDF
|
||||
if thresh < 1.5 {
|
||||
thresh = 1.5
|
||||
}
|
||||
out := map[string]bool{}
|
||||
for t := range factTerms {
|
||||
if float64(docTerms[Stem(t)]) < thresh {
|
||||
out[t] = true
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// SharedDistinctive — distinctive terms shared between fact and existing
|
||||
// values (stem-matched).
|
||||
func SharedDistinctive(fact string, existingValues, corpusTexts []string) map[string]bool {
|
||||
distinct := DistinctiveTerms(fact, corpusTexts)
|
||||
if len(distinct) == 0 {
|
||||
return map[string]bool{}
|
||||
}
|
||||
shared := map[string]bool{}
|
||||
type stemVal struct {
|
||||
s string
|
||||
}
|
||||
var existingStems []stemVal
|
||||
for _, val := range existingValues {
|
||||
for _, w := range wordRe.FindAllString(strings.ToLower(val), -1) {
|
||||
existingStems = append(existingStems, stemVal{Stem(w)})
|
||||
}
|
||||
}
|
||||
for term := range distinct {
|
||||
ts := Stem(term)
|
||||
for _, sv := range existingStems {
|
||||
if sv.s == ts {
|
||||
shared[term] = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return shared
|
||||
}
|
||||
|
||||
// Coherence — "conflict" | "consistent" | "novel" (lexicon-aware).
|
||||
func Coherence(fact string, existingValues []string, corpusTexts []string) string {
|
||||
var values []string
|
||||
for _, v := range existingValues {
|
||||
if strings.TrimSpace(v) != "" {
|
||||
values = append(values, v)
|
||||
}
|
||||
}
|
||||
if len(values) == 0 {
|
||||
return "novel"
|
||||
}
|
||||
corpus := corpusTexts
|
||||
if len(corpus) == 0 {
|
||||
corpus = values
|
||||
}
|
||||
shared := SharedDistinctive(fact, values, corpus)
|
||||
if len(shared) == 0 {
|
||||
return "novel"
|
||||
}
|
||||
low := strings.ToLower(fact)
|
||||
hasNeg := false
|
||||
for _, n := range lexNegation {
|
||||
if strings.Contains(low, n) {
|
||||
hasNeg = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if hasNeg {
|
||||
for t := range shared {
|
||||
if strings.Contains(" "+low+" ", " "+t+" ") {
|
||||
return "conflict"
|
||||
}
|
||||
}
|
||||
}
|
||||
return "consistent"
|
||||
}
|
||||
|
||||
// GroundedLex — distinctive-term grounding in source texts (min_distinct=1
|
||||
// default; stem-aware: migrated matches migration).
|
||||
func GroundedLex(fact string, sourceTexts []string, minDistinct int) bool {
|
||||
var srcs []string
|
||||
for _, s := range sourceTexts {
|
||||
if strings.TrimSpace(s) != "" {
|
||||
srcs = append(srcs, s)
|
||||
}
|
||||
}
|
||||
if len(srcs) == 0 {
|
||||
return false
|
||||
}
|
||||
factTerms := map[string]bool{}
|
||||
for _, w := range ContentWords(fact) {
|
||||
factTerms[w] = true
|
||||
}
|
||||
if len(factTerms) == 0 {
|
||||
return false
|
||||
}
|
||||
var joined []string
|
||||
for _, s := range srcs {
|
||||
joined = append(joined, strings.ToLower(s))
|
||||
}
|
||||
stems := map[string]bool{}
|
||||
for _, w := range wordRe.FindAllString(strings.Join(joined, " "), -1) {
|
||||
stems[Stem(w)] = true
|
||||
}
|
||||
hits := 0
|
||||
for t := range factTerms {
|
||||
if stems[Stem(t)] {
|
||||
hits++
|
||||
if hits >= minDistinct {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// PromoteEligible — the REAL lexicon-aware eligibility (evidence threshold
|
||||
// AND (shared distinctive term OR novel axis grounded in corpus)).
|
||||
// Verdict strings byte-match the Python f-strings; the shared-terms reason
|
||||
// sorts the terms (Python sorted()) and takes the first 3.
|
||||
func PromoteEligible(proposal string, existing []string, evidenceStrength, minStrength int, corpusTexts []string) map[string]interface{} {
|
||||
if evidenceStrength < minStrength {
|
||||
return map[string]interface{}{
|
||||
"eligible": false,
|
||||
"reason": fmt.Sprintf("evidence strength %d < %d", evidenceStrength, minStrength),
|
||||
}
|
||||
}
|
||||
var block []string
|
||||
for _, e := range existing {
|
||||
if strings.TrimSpace(e) != "" {
|
||||
block = append(block, e)
|
||||
}
|
||||
}
|
||||
if len(block) == 0 {
|
||||
return map[string]interface{}{"eligible": true, "reason": "empty block — first principle"}
|
||||
}
|
||||
corpus := corpusTexts
|
||||
if len(corpus) == 0 {
|
||||
corpus = block
|
||||
}
|
||||
shared := SharedDistinctive(proposal, block, corpus)
|
||||
if len(shared) > 0 {
|
||||
terms := make([]string, 0, len(shared))
|
||||
for t := range shared {
|
||||
terms = append(terms, t)
|
||||
}
|
||||
sort.Strings(terms)
|
||||
n := len(terms)
|
||||
if n > 3 {
|
||||
n = 3
|
||||
}
|
||||
// PARITY: Python f-string renders the sorted list with list repr —
|
||||
// ['a', 'b', 'c'] — not Go's [a b c].
|
||||
var parts []string
|
||||
for _, t := range terms[:n] {
|
||||
parts = append(parts, fmt.Sprintf("'%s'", t))
|
||||
}
|
||||
return map[string]interface{}{
|
||||
"eligible": true,
|
||||
"reason": fmt.Sprintf("shares distinctive term(s): [%s]", strings.Join(parts, ", ")),
|
||||
}
|
||||
}
|
||||
terms := ContentWords(proposal)
|
||||
if len(terms) == 0 {
|
||||
return map[string]interface{}{"eligible": false, "reason": "no lexical signal"}
|
||||
}
|
||||
if GroundedLex(proposal, corpus, 1) {
|
||||
return map[string]interface{}{
|
||||
"eligible": true,
|
||||
"reason": fmt.Sprintf("novel axis grounded in corpus (%d terms)", len(terms)),
|
||||
}
|
||||
}
|
||||
return map[string]interface{}{
|
||||
"eligible": false,
|
||||
"reason": "novel axis not grounded in absorbed material — parked",
|
||||
}
|
||||
}
|
||||
@@ -77,10 +77,27 @@ func FindEntry(body, needle string) string {
|
||||
}
|
||||
|
||||
// LePromoteEligible — drift-with-integrity: wash_count lowers the strength
|
||||
// threshold (>=20 → 1, >=10 → 2, >=5 → 2, else min_strength). The lexicon
|
||||
// module is a phantom — Python degrades to the strength-only gate with an
|
||||
// explicit reason string; this port reproduces THAT verdict byte-identically.
|
||||
func LePromoteEligible(proposal, existing string, strength, minStrength, washCount int) map[string]interface{} {
|
||||
// threshold (>=20 → 1, >=10 → 2, >=5 → 2, else min_strength), then the REAL
|
||||
// lexicon verdict (batch 2: the recovered lexicon_evolution semantics via
|
||||
// lexicon.go — shared distinctive term OR novel axis grounded in corpus).
|
||||
// The old strength-only degrade path is preserved as
|
||||
// LePromoteEligibleDegrade for the historical record.
|
||||
func LePromoteEligible(proposal string, existing []string, strength, minStrength, washCount int) map[string]interface{} {
|
||||
adjustedMin := minStrength
|
||||
if washCount >= 20 {
|
||||
adjustedMin = 1
|
||||
} else if washCount >= 10 {
|
||||
adjustedMin = 2
|
||||
} else if washCount >= 5 {
|
||||
adjustedMin = 2
|
||||
}
|
||||
return PromoteEligible(proposal, existing, strength, adjustedMin, nil)
|
||||
}
|
||||
|
||||
// LePromoteEligibleDegrade — the batch-1 verdict shape (kept for the
|
||||
// historical truth record; the phantom is recovered, this path is no
|
||||
// longer the live semantics).
|
||||
func LePromoteEligibleDegrade(proposal, existing string, strength, minStrength, washCount int) map[string]interface{} {
|
||||
adjustedMin := minStrength
|
||||
if washCount >= 20 {
|
||||
adjustedMin = 1
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
"expected": {
|
||||
"eligible": {
|
||||
"eligible": true,
|
||||
"reason": "lexicon module unavailable \u2014 strength gate only (adjusted_min=3, wash_count=0)"
|
||||
"reason": "shares distinctive term(s): ['retries', 'support']"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -79,7 +79,7 @@
|
||||
"expected": {
|
||||
"eligible": {
|
||||
"eligible": true,
|
||||
"reason": "lexicon module unavailable \u2014 strength gate only (adjusted_min=2, wash_count=10)"
|
||||
"reason": "shares distinctive term(s): ['retries', 'support']"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -95,7 +95,7 @@
|
||||
"expected": {
|
||||
"eligible": {
|
||||
"eligible": true,
|
||||
"reason": "lexicon module unavailable \u2014 strength gate only (adjusted_min=1, wash_count=20)"
|
||||
"reason": "shares distinctive term(s): ['retries', 'support']"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -110,8 +110,8 @@
|
||||
},
|
||||
"expected": {
|
||||
"eligible": {
|
||||
"eligible": true,
|
||||
"reason": "lexicon module unavailable \u2014 strength gate only (adjusted_min=3, wash_count=0)"
|
||||
"eligible": false,
|
||||
"reason": "novel axis not grounded in absorbed material — parked"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -163,4 +163,4 @@
|
||||
"key": "eb081bc793c68acd"
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
@@ -0,0 +1,250 @@
|
||||
{
|
||||
"oracle": "src/rune/lexicon_evolution.py (recovered provenance 0308ccc6) via curator.py",
|
||||
"generated": "2026-10-07 (post-recovery regeneration)",
|
||||
"cases": [
|
||||
{
|
||||
"fn": "coherence",
|
||||
"name": "conflict",
|
||||
"input": {
|
||||
"fact": "nick never writes Rust code",
|
||||
"existing": [
|
||||
"nick writes Go and Rust code daily"
|
||||
],
|
||||
"corpus": [
|
||||
"nick writes Go and Rust code daily in the rune repository",
|
||||
"the curator gates memory writes through worthiness and grading",
|
||||
"migration of modules follows the dichotomy: reactive goes Go, deterministic goes Rust"
|
||||
]
|
||||
},
|
||||
"expected": {
|
||||
"verdict": "conflict"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "coherence",
|
||||
"name": "consistent",
|
||||
"input": {
|
||||
"fact": "nick writes Rust code on weekends",
|
||||
"existing": [
|
||||
"nick writes Go and Rust code daily"
|
||||
],
|
||||
"corpus": [
|
||||
"nick writes Go and Rust code daily in the rune repository",
|
||||
"the curator gates memory writes through worthiness and grading",
|
||||
"migration of modules follows the dichotomy: reactive goes Go, deterministic goes Rust"
|
||||
]
|
||||
},
|
||||
"expected": {
|
||||
"verdict": "consistent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "coherence",
|
||||
"name": "novel_axis",
|
||||
"input": {
|
||||
"fact": "nick plays bass guitar in a band",
|
||||
"existing": [
|
||||
"nick writes Go and Rust code daily"
|
||||
],
|
||||
"corpus": [
|
||||
"nick writes Go and Rust code daily in the rune repository",
|
||||
"the curator gates memory writes through worthiness and grading",
|
||||
"migration of modules follows the dichotomy: reactive goes Go, deterministic goes Rust"
|
||||
]
|
||||
},
|
||||
"expected": {
|
||||
"verdict": "consistent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "coherence",
|
||||
"name": "common_word_not_relation",
|
||||
"input": {
|
||||
"fact": "nick likes black coffee",
|
||||
"existing": [
|
||||
"black holes bend light"
|
||||
],
|
||||
"corpus": [
|
||||
"black coffee in the morning",
|
||||
"black holes bend light"
|
||||
]
|
||||
},
|
||||
"expected": {
|
||||
"verdict": "novel"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "coherence",
|
||||
"name": "empty_existing",
|
||||
"input": {
|
||||
"fact": "nick writes Rust code daily",
|
||||
"existing": [],
|
||||
"corpus": []
|
||||
},
|
||||
"expected": {
|
||||
"verdict": "novel"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "grounded",
|
||||
"name": "stem_match",
|
||||
"input": {
|
||||
"fact": "nick migrated the modules",
|
||||
"sources": [
|
||||
"nick writes Go and Rust code daily in the rune repository",
|
||||
"the curator gates memory writes through worthiness and grading",
|
||||
"migration of modules follows the dichotomy: reactive goes Go, deterministic goes Rust"
|
||||
]
|
||||
},
|
||||
"expected": {
|
||||
"grounded": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "grounded",
|
||||
"name": "invented_token",
|
||||
"input": {
|
||||
"fact": "nick collects vintage railway timetables",
|
||||
"sources": [
|
||||
"nick writes Go and Rust code daily in the rune repository",
|
||||
"the curator gates memory writes through worthiness and grading",
|
||||
"migration of modules follows the dichotomy: reactive goes Go, deterministic goes Rust"
|
||||
]
|
||||
},
|
||||
"expected": {
|
||||
"grounded": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "grounded",
|
||||
"name": "empty_sources",
|
||||
"input": {
|
||||
"fact": "nick migrated the modules",
|
||||
"sources": []
|
||||
},
|
||||
"expected": {
|
||||
"grounded": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "distinctive_terms",
|
||||
"name": "rare_vs_common",
|
||||
"input": {
|
||||
"fact": "nick writes Rust code daily",
|
||||
"corpus": [
|
||||
"nick writes Go and Rust code daily in the rune repository",
|
||||
"the curator gates memory writes through worthiness and grading",
|
||||
"migration of modules follows the dichotomy: reactive goes Go, deterministic goes Rust"
|
||||
]
|
||||
},
|
||||
"expected": {
|
||||
"terms": [
|
||||
"code",
|
||||
"daily",
|
||||
"nick"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "promote_eligible",
|
||||
"name": "strength_gate",
|
||||
"input": {
|
||||
"proposal": "add support for retries",
|
||||
"existing": [
|
||||
"support for retries in the fetch loop"
|
||||
],
|
||||
"strength": 2,
|
||||
"min_strength": 3,
|
||||
"corpus": []
|
||||
},
|
||||
"expected": {
|
||||
"eligible": {
|
||||
"eligible": false,
|
||||
"reason": "evidence strength 2 < 3"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "promote_eligible",
|
||||
"name": "shared_distinctive",
|
||||
"input": {
|
||||
"proposal": "add retries to the fetch loop",
|
||||
"existing": [
|
||||
"support for retries in the fetch loop"
|
||||
],
|
||||
"strength": 3,
|
||||
"min_strength": 3,
|
||||
"corpus": []
|
||||
},
|
||||
"expected": {
|
||||
"eligible": {
|
||||
"eligible": true,
|
||||
"reason": "shares distinctive term(s): ['fetch', 'loop', 'retries']"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "promote_eligible",
|
||||
"name": "novel_grounded",
|
||||
"input": {
|
||||
"proposal": "the curator gates every memory write",
|
||||
"existing": [
|
||||
"nick writes Go and Rust code daily"
|
||||
],
|
||||
"strength": 3,
|
||||
"min_strength": 3,
|
||||
"corpus": [
|
||||
"nick writes Go and Rust code daily in the rune repository",
|
||||
"the curator gates memory writes through worthiness and grading",
|
||||
"migration of modules follows the dichotomy: reactive goes Go, deterministic goes Rust"
|
||||
]
|
||||
},
|
||||
"expected": {
|
||||
"eligible": {
|
||||
"eligible": true,
|
||||
"reason": "novel axis grounded in corpus (5 terms)"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "promote_eligible",
|
||||
"name": "novel_parked",
|
||||
"input": {
|
||||
"proposal": "nick collects vintage railway timetables",
|
||||
"existing": [
|
||||
"nick writes Go and Rust code daily"
|
||||
],
|
||||
"strength": 3,
|
||||
"min_strength": 3,
|
||||
"corpus": [
|
||||
"nick writes Go and Rust code daily in the rune repository",
|
||||
"the curator gates memory writes through worthiness and grading",
|
||||
"migration of modules follows the dichotomy: reactive goes Go, deterministic goes Rust"
|
||||
]
|
||||
},
|
||||
"expected": {
|
||||
"eligible": {
|
||||
"eligible": true,
|
||||
"reason": "shares distinctive term(s): ['nick']"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "promote_eligible",
|
||||
"name": "first_principle",
|
||||
"input": {
|
||||
"proposal": "parking is never wrong for growth",
|
||||
"existing": [],
|
||||
"strength": 3,
|
||||
"min_strength": 3,
|
||||
"corpus": []
|
||||
},
|
||||
"expected": {
|
||||
"eligible": {
|
||||
"eligible": true,
|
||||
"reason": "empty block — first principle"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package main
|
||||
|
||||
// truth_lexicon_test.go — batch-2 lexicon battery: the REAL recovered
|
||||
// lexicon_evolution semantics (truth_lexicon.json, live Python oracle,
|
||||
// post-recovery regeneration — contract BUILDER-2026-10-07T16:31:17Z-997c9d).
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLexiconParity(t *testing.T) {
|
||||
b, err := os.ReadFile("truth_lexicon.json")
|
||||
if err != nil {
|
||||
t.Fatalf("truth_lexicon.json missing: %v", err)
|
||||
}
|
||||
var doc struct {
|
||||
Oracle string `json:"oracle"`
|
||||
Cases []struct {
|
||||
Fn string `json:"fn"`
|
||||
Name string `json:"name"`
|
||||
Input map[string]interface{} `json:"input"`
|
||||
Expected map[string]interface{} `json:"expected"`
|
||||
} `json:"cases"`
|
||||
}
|
||||
if err := json.Unmarshal(b, &doc); err != nil {
|
||||
t.Fatalf("unparseable: %v", err)
|
||||
}
|
||||
for _, c := range doc.Cases {
|
||||
i := c.Input
|
||||
switch c.Fn {
|
||||
case "coherence":
|
||||
fact := str(i, "fact")
|
||||
existing := strList(i["existing"])
|
||||
corpus := strList(i["corpus"])
|
||||
got := Coherence(fact, existing, corpus)
|
||||
want := str(c.Expected, "verdict")
|
||||
if got != want {
|
||||
t.Errorf("%s: Coherence got %q want %q", c.Name, got, want)
|
||||
}
|
||||
case "grounded":
|
||||
fact := str(i, "fact")
|
||||
sources := strList(i["sources"])
|
||||
got := GroundedLex(fact, sources, 1)
|
||||
want := c.Expected["grounded"].(bool)
|
||||
if got != want {
|
||||
t.Errorf("%s: GroundedLex got %v want %v", c.Name, got, want)
|
||||
}
|
||||
case "distinctive_terms":
|
||||
fact := str(i, "fact")
|
||||
corpus := strList(i["corpus"])
|
||||
got := DistinctiveTerms(fact, corpus)
|
||||
wantList := c.Expected["terms"].([]interface{})
|
||||
if len(got) != len(wantList) {
|
||||
t.Errorf("%s: DistinctiveTerms got %d terms %v want %d %v",
|
||||
c.Name, len(got), got, len(wantList), wantList)
|
||||
continue
|
||||
}
|
||||
for _, w := range wantList {
|
||||
if !got[w.(string)] {
|
||||
t.Errorf("%s: missing term %q in %v", c.Name, w, got)
|
||||
}
|
||||
}
|
||||
case "promote_eligible":
|
||||
proposal := str(i, "proposal")
|
||||
existing := strList(i["existing"])
|
||||
corpus := strList(i["corpus"])
|
||||
strength := num(i, "strength")
|
||||
minS := num(i, "min_strength")
|
||||
got := PromoteEligible(proposal, existing, strength, minS, corpus)
|
||||
want, _ := c.Expected["eligible"].(map[string]interface{})
|
||||
jsonEq(t, c.Name, "eligible", got["eligible"], want["eligible"])
|
||||
jsonEq(t, c.Name, "reason", got["reason"], want["reason"])
|
||||
default:
|
||||
t.Errorf("unknown lexicon truth fn %q", c.Fn)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// strList — interface{} (JSON array of strings) → []string.
|
||||
func strList(v interface{}) []string {
|
||||
arr, ok := v.([]interface{})
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
out := make([]string, 0, len(arr))
|
||||
for _, e := range arr {
|
||||
s, _ := e.(string)
|
||||
out = append(out, s)
|
||||
}
|
||||
return out
|
||||
}
|
||||
@@ -82,7 +82,7 @@ func TestParityAgainstPythonOracle(t *testing.T) {
|
||||
}
|
||||
case "le_promote_eligible":
|
||||
i := c.Input
|
||||
got := LePromoteEligible(str(i, "proposal"), str(i, "existing"),
|
||||
got := LePromoteEligible(str(i, "proposal"), []string{str(i, "existing")},
|
||||
num(i, "strength"), 3, num(i, "wash_count"))
|
||||
want, _ := c.Expected["eligible"].(map[string]interface{})
|
||||
jsonEq(t, c.Name, "eligible", got["eligible"], want["eligible"])
|
||||
|
||||
Binary file not shown.
+11
-6
@@ -4,10 +4,11 @@
|
||||
// contradiction detection, identity checks, entry dedup.
|
||||
//
|
||||
// Usage:
|
||||
// curator promote-eligible <proposal> <existing> <strength> [min-strength] [wash-count]
|
||||
// curator contradicts-identity <fact>
|
||||
// curator is-topic-fact <fact>
|
||||
// curator entry-key <text>
|
||||
//
|
||||
// curator promote-eligible <proposal> <existing> <strength> [min-strength] [wash-count]
|
||||
// curator contradicts-identity <fact>
|
||||
// curator is-topic-fact <fact>
|
||||
// curator entry-key <text>
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -183,8 +184,12 @@ func main() {
|
||||
fmt.Sscanf(args[2], "%d", &strength)
|
||||
minStrength := 3
|
||||
washCount := 0
|
||||
if len(args) > 3 { fmt.Sscanf(args[3], "%d", &minStrength) }
|
||||
if len(args) > 4 { fmt.Sscanf(args[4], "%d", &washCount) }
|
||||
if len(args) > 3 {
|
||||
fmt.Sscanf(args[3], "%d", &minStrength)
|
||||
}
|
||||
if len(args) > 4 {
|
||||
fmt.Sscanf(args[4], "%d", &washCount)
|
||||
}
|
||||
result := promoteEligible(proposal, existing, strength, minStrength, nil, washCount)
|
||||
out, _ := json.MarshalIndent(result, "", " ")
|
||||
fmt.Println(string(out))
|
||||
|
||||
Binary file not shown.
@@ -3,8 +3,9 @@
|
||||
// calculate_budget. Classifies how much cognition an event deserves.
|
||||
//
|
||||
// Usage:
|
||||
// elastic-cognition classify <text> [--channel <ch>]
|
||||
// elastic-cognition budget <salience-json> [--channel <ch>]
|
||||
//
|
||||
// elastic-cognition classify <text> [--channel <ch>]
|
||||
// elastic-cognition budget <salience-json> [--channel <ch>]
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -16,17 +17,17 @@ import (
|
||||
)
|
||||
|
||||
type SalienceDecision struct {
|
||||
Score float64 `json:"score"`
|
||||
Flags []string `json:"flags"`
|
||||
MinimumTier string `json:"minimum_tier"`
|
||||
WitnessNeeded bool `json:"witness_needed"`
|
||||
Score float64 `json:"score"`
|
||||
Flags []string `json:"flags"`
|
||||
MinimumTier string `json:"minimum_tier"`
|
||||
WitnessNeeded bool `json:"witness_needed"`
|
||||
RationaleCodes []string `json:"rationale_codes"`
|
||||
}
|
||||
|
||||
type CognitiveBudget struct {
|
||||
Route string `json:"route"`
|
||||
MinTier string `json:"min_tier"`
|
||||
MaxTokens int `json:"max_tokens"`
|
||||
Route string `json:"route"`
|
||||
MinTier string `json:"min_tier"`
|
||||
MaxTokens int `json:"max_tokens"`
|
||||
TempOverride *float64 `json:"temp_override,omitempty"`
|
||||
}
|
||||
|
||||
@@ -163,10 +164,14 @@ func classifySalience(text, channel string) SalienceDecision {
|
||||
|
||||
// CHANNEL PRIORS
|
||||
if channel == "work" {
|
||||
if score < 0.5 { score = 0.5 }
|
||||
if score < 0.5 {
|
||||
score = 0.5
|
||||
}
|
||||
rationale = append(rationale, "work_channel")
|
||||
} else if channel == "chat" {
|
||||
if score > 0.3 { score = 0.3 }
|
||||
if score > 0.3 {
|
||||
score = 0.3
|
||||
}
|
||||
rationale = append(rationale, "chat_channel")
|
||||
}
|
||||
|
||||
@@ -192,7 +197,9 @@ func classifySalience(text, channel string) SalienceDecision {
|
||||
|
||||
func containsFlag(flags []string, flag string) bool {
|
||||
for _, f := range flags {
|
||||
if f == flag { return true }
|
||||
if f == flag {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
Binary file not shown.
+39
-28
@@ -4,9 +4,10 @@
|
||||
// append safety; os.WriteFile for the durable journal.
|
||||
//
|
||||
// Usage:
|
||||
// event-bus emit <event_type> [--source <s>] [--payload <json>]
|
||||
// event-bus log [--limit N] [--type <t>]
|
||||
// event-bus stats
|
||||
//
|
||||
// event-bus emit <event_type> [--source <s>] [--payload <json>]
|
||||
// event-bus log [--limit N] [--type <t>]
|
||||
// event-bus stats
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -24,15 +25,15 @@ import (
|
||||
|
||||
// EventEnvelope — the canonical record (matches rune/orchestration/records.py).
|
||||
type EventEnvelope struct {
|
||||
EventID string `json:"event_id"`
|
||||
EventType string `json:"event_type"`
|
||||
PartitionKey string `json:"partition_key"`
|
||||
ParentID string `json:"parent_id,omitempty"`
|
||||
ActorID string `json:"actor_id,omitempty"`
|
||||
DeviceID string `json:"device_id,omitempty"`
|
||||
EventID string `json:"event_id"`
|
||||
EventType string `json:"event_type"`
|
||||
PartitionKey string `json:"partition_key"`
|
||||
ParentID string `json:"parent_id,omitempty"`
|
||||
ActorID string `json:"actor_id,omitempty"`
|
||||
DeviceID string `json:"device_id,omitempty"`
|
||||
Authorization string `json:"authorization,omitempty"`
|
||||
PayloadHash string `json:"payload_hash,omitempty"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
PayloadHash string `json:"payload_hash,omitempty"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
}
|
||||
|
||||
func newID(prefix string) string {
|
||||
@@ -200,7 +201,6 @@ func main() {
|
||||
}, "", " ")
|
||||
fmt.Println(string(out))
|
||||
|
||||
|
||||
case "create-envelope":
|
||||
args := os.Args[2:]
|
||||
source, channel, text := "", "", ""
|
||||
@@ -208,13 +208,25 @@ func main() {
|
||||
for i := 0; i < len(args); i++ {
|
||||
switch args[i] {
|
||||
case "--source":
|
||||
if i+1 < len(args) { source = args[i+1]; i++ }
|
||||
if i+1 < len(args) {
|
||||
source = args[i+1]
|
||||
i++
|
||||
}
|
||||
case "--channel":
|
||||
if i+1 < len(args) { channel = args[i+1]; i++ }
|
||||
if i+1 < len(args) {
|
||||
channel = args[i+1]
|
||||
i++
|
||||
}
|
||||
case "--text":
|
||||
if i+1 < len(args) { text = args[i+1]; i++ }
|
||||
if i+1 < len(args) {
|
||||
text = args[i+1]
|
||||
i++
|
||||
}
|
||||
case "--payload":
|
||||
if i+1 < len(args) { json.Unmarshal([]byte(args[i+1]), &payload); i++ }
|
||||
if i+1 < len(args) {
|
||||
json.Unmarshal([]byte(args[i+1]), &payload)
|
||||
i++
|
||||
}
|
||||
}
|
||||
}
|
||||
out, _ := json.MarshalIndent(createEnvelope(source, channel, text, payload), "", " ")
|
||||
@@ -229,10 +241,10 @@ func main() {
|
||||
// ── event envelope operations (W4: event_envelope port) ──────────────────
|
||||
|
||||
type EnvelopeRequest struct {
|
||||
Source string `json:"source"`
|
||||
Channel string `json:"channel"`
|
||||
Payload map[string]interface{} `json:"payload"`
|
||||
Text string `json:"text"`
|
||||
Source string `json:"source"`
|
||||
Channel string `json:"channel"`
|
||||
Payload map[string]interface{} `json:"payload"`
|
||||
Text string `json:"text"`
|
||||
}
|
||||
|
||||
func classifyAuthority(text, channel string) string {
|
||||
@@ -315,14 +327,13 @@ func createEnvelope(source, channel, text string, payload map[string]interface{}
|
||||
env.PayloadHash = contentHash(payload)
|
||||
}
|
||||
return map[string]interface{}{
|
||||
"event_id": env.EventID,
|
||||
"timestamp": env.Timestamp,
|
||||
"source": source,
|
||||
"channel": channel,
|
||||
"event_id": env.EventID,
|
||||
"timestamp": env.Timestamp,
|
||||
"source": source,
|
||||
"channel": channel,
|
||||
"authority_class": authority,
|
||||
"urgency": urgency,
|
||||
"payload_hash": env.PayloadHash,
|
||||
"payload": payload,
|
||||
"urgency": urgency,
|
||||
"payload_hash": env.PayloadHash,
|
||||
"payload": payload,
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -4,10 +4,11 @@
|
||||
// spools to JSONL for forge filing.
|
||||
//
|
||||
// Usage:
|
||||
// failure-filer classify <subsystem> <error-message>
|
||||
// failure-filer spool <subsystem> <error-message> <tier>
|
||||
// failure-filer unspooled
|
||||
// failure-filer mark-consumed <spool-id>
|
||||
//
|
||||
// failure-filer classify <subsystem> <error-message>
|
||||
// failure-filer spool <subsystem> <error-message> <tier>
|
||||
// failure-filer unspooled
|
||||
// failure-filer mark-consumed <spool-id>
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -24,14 +25,14 @@ const ERROR_CLASS_MAX = 64
|
||||
const EXCERPT_LEN = 256
|
||||
|
||||
type SpoolEntry struct {
|
||||
ID string `json:"spool_id"`
|
||||
Subsystem string `json:"subsystem"`
|
||||
ErrorClass string `json:"error_class"`
|
||||
Excerpt string `json:"excerpt"`
|
||||
Tier int `json:"tier"`
|
||||
Signature string `json:"signature"`
|
||||
Consumed bool `json:"consumed"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
ID string `json:"spool_id"`
|
||||
Subsystem string `json:"subsystem"`
|
||||
ErrorClass string `json:"error_class"`
|
||||
Excerpt string `json:"excerpt"`
|
||||
Tier int `json:"tier"`
|
||||
Signature string `json:"signature"`
|
||||
Consumed bool `json:"consumed"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
type Result struct {
|
||||
@@ -51,9 +52,13 @@ func spoolPath() string {
|
||||
func loadSpool() []SpoolEntry {
|
||||
var entries []SpoolEntry
|
||||
data, err := os.ReadFile(spoolPath())
|
||||
if err != nil { return entries }
|
||||
if err != nil {
|
||||
return entries
|
||||
}
|
||||
for _, line := range strings.Split(string(data), "\n") {
|
||||
if strings.TrimSpace(line) == "" { continue }
|
||||
if strings.TrimSpace(line) == "" {
|
||||
continue
|
||||
}
|
||||
var e SpoolEntry
|
||||
json.Unmarshal([]byte(line), &e)
|
||||
entries = append(entries, e)
|
||||
@@ -73,9 +78,13 @@ func saveSpool(entries []SpoolEntry) {
|
||||
|
||||
func errorClassOf(errMsg string) string {
|
||||
tokens := strings.Fields(errMsg)
|
||||
if len(tokens) == 0 { return "Unknown" }
|
||||
if len(tokens) == 0 {
|
||||
return "Unknown"
|
||||
}
|
||||
class := strings.TrimSuffix(tokens[0], ":")
|
||||
if len(class) > ERROR_CLASS_MAX { class = class[:ERROR_CLASS_MAX] }
|
||||
if len(class) > ERROR_CLASS_MAX {
|
||||
class = class[:ERROR_CLASS_MAX]
|
||||
}
|
||||
return class
|
||||
}
|
||||
|
||||
@@ -104,7 +113,9 @@ func redact(text string) string {
|
||||
|
||||
func excerpt(errMsg string, limit int) string {
|
||||
r := redact(errMsg)
|
||||
if len(r) > limit { r = r[:limit] }
|
||||
if len(r) > limit {
|
||||
r = r[:limit]
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
@@ -127,10 +138,10 @@ func main() {
|
||||
sub, cls := signatureOf(subsystem, errMsg)
|
||||
out, _ := json.Marshal(map[string]interface{}{
|
||||
"error_class": class, "subsystem": sub,
|
||||
"signature": fmt.Sprintf("%s:%s", sub, cls),
|
||||
"marker": markerFor(sub, cls),
|
||||
"signature": fmt.Sprintf("%s:%s", sub, cls),
|
||||
"marker": markerFor(sub, cls),
|
||||
"tier_gated": tierGated(2),
|
||||
"excerpt": excerpt(errMsg, EXCERPT_LEN),
|
||||
"excerpt": excerpt(errMsg, EXCERPT_LEN),
|
||||
})
|
||||
fmt.Println(string(out))
|
||||
|
||||
@@ -144,12 +155,12 @@ func main() {
|
||||
fmt.Sscanf(args[2], "%d", &tier)
|
||||
_, cls := signatureOf(subsystem, errMsg)
|
||||
entry := SpoolEntry{
|
||||
ID: fmt.Sprintf("spool-%d", time.Now().UnixNano()%1000000),
|
||||
ID: fmt.Sprintf("spool-%d", time.Now().UnixNano()%1000000),
|
||||
Subsystem: subsystem, ErrorClass: cls,
|
||||
Excerpt: excerpt(errMsg, EXCERPT_LEN),
|
||||
Tier: tier,
|
||||
Excerpt: excerpt(errMsg, EXCERPT_LEN),
|
||||
Tier: tier,
|
||||
Signature: fmt.Sprintf("%s:%s", subsystem, cls),
|
||||
Consumed: false,
|
||||
Consumed: false,
|
||||
CreatedAt: time.Now().UTC(),
|
||||
}
|
||||
entries := loadSpool()
|
||||
@@ -161,16 +172,24 @@ func main() {
|
||||
case "unspooled":
|
||||
var unspooled []SpoolEntry
|
||||
for _, e := range loadSpool() {
|
||||
if !e.Consumed { unspooled = append(unspooled, e) }
|
||||
if !e.Consumed {
|
||||
unspooled = append(unspooled, e)
|
||||
}
|
||||
}
|
||||
out, _ := json.MarshalIndent(unspooled, "", " ")
|
||||
fmt.Println(string(out))
|
||||
|
||||
case "mark-consumed":
|
||||
if len(args) < 1 { fmt.Fprintln(os.Stderr, "usage: failure-filer mark-consumed <id>"); os.Exit(1) }
|
||||
if len(args) < 1 {
|
||||
fmt.Fprintln(os.Stderr, "usage: failure-filer mark-consumed <id>")
|
||||
os.Exit(1)
|
||||
}
|
||||
entries := loadSpool()
|
||||
for i := range entries {
|
||||
if entries[i].ID == args[0] { entries[i].Consumed = true; break }
|
||||
if entries[i].ID == args[0] {
|
||||
entries[i].Consumed = true
|
||||
break
|
||||
}
|
||||
}
|
||||
saveSpool(entries)
|
||||
fmt.Println("consumed:", args[0])
|
||||
|
||||
Binary file not shown.
+35
-15
@@ -3,10 +3,11 @@
|
||||
// hook firing, and the plugin registry.
|
||||
//
|
||||
// Usage:
|
||||
// hook-engine fire <event> [--data <json>]
|
||||
// hook-engine list
|
||||
// hook-engine discover
|
||||
// hook-engine tag <name> <key=value> ...
|
||||
//
|
||||
// hook-engine fire <event> [--data <json>]
|
||||
// hook-engine list
|
||||
// hook-engine discover
|
||||
// hook-engine tag <name> <key=value> ...
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -24,8 +25,8 @@ type HookEntry struct {
|
||||
}
|
||||
|
||||
type FunctionMeta struct {
|
||||
Type string `json:"type"`
|
||||
File string `json:"file"`
|
||||
Type string `json:"type"`
|
||||
File string `json:"file"`
|
||||
Factors map[string]string `json:"factors"`
|
||||
}
|
||||
|
||||
@@ -55,7 +56,9 @@ func functionsPath() string {
|
||||
func loadHooks() []HookEntry {
|
||||
var hooks []HookEntry
|
||||
data, err := os.ReadFile(hooksPath())
|
||||
if err != nil { return hooks }
|
||||
if err != nil {
|
||||
return hooks
|
||||
}
|
||||
json.Unmarshal(data, &hooks)
|
||||
return hooks
|
||||
}
|
||||
@@ -69,7 +72,9 @@ func saveHooks(hooks []HookEntry) {
|
||||
func loadFunctions() map[string]FunctionMeta {
|
||||
functions := make(map[string]FunctionMeta)
|
||||
data, err := os.ReadFile(functionsPath())
|
||||
if err != nil { return functions }
|
||||
if err != nil {
|
||||
return functions
|
||||
}
|
||||
json.Unmarshal(data, &functions)
|
||||
return functions
|
||||
}
|
||||
@@ -123,11 +128,21 @@ func main() {
|
||||
data := ""
|
||||
for i := 0; i < len(args); i++ {
|
||||
switch args[i] {
|
||||
case "--event": if i+1 < len(args) { event = args[i+1]; i++ }
|
||||
case "--data": if i+1 < len(args) { data = args[i+1]; i++ }
|
||||
case "--event":
|
||||
if i+1 < len(args) {
|
||||
event = args[i+1]
|
||||
i++
|
||||
}
|
||||
case "--data":
|
||||
if i+1 < len(args) {
|
||||
data = args[i+1]
|
||||
i++
|
||||
}
|
||||
}
|
||||
}
|
||||
if event == "" && len(args) > 0 { event = args[0] }
|
||||
if event == "" && len(args) > 0 {
|
||||
event = args[0]
|
||||
}
|
||||
fired := 0
|
||||
for _, hook := range hooks {
|
||||
if hook.Event == event {
|
||||
@@ -147,18 +162,23 @@ func main() {
|
||||
out, _ := json.Marshal(h)
|
||||
fmt.Println(string(out))
|
||||
}
|
||||
if len(hooks) == 0 { fmt.Println("(no hooks registered)") }
|
||||
if len(hooks) == 0 {
|
||||
fmt.Println("(no hooks registered)")
|
||||
}
|
||||
|
||||
case "discover":
|
||||
// Scan src/rune/ for Python modules
|
||||
entries, err := os.ReadDir("src/rune")
|
||||
if err != nil { fmt.Println("(no src/rune directory)"); return }
|
||||
if err != nil {
|
||||
fmt.Println("(no src/rune directory)")
|
||||
return
|
||||
}
|
||||
for _, e := range entries {
|
||||
if strings.HasSuffix(e.Name(), ".py") && !strings.HasPrefix(e.Name(), "__") {
|
||||
name := strings.TrimSuffix(e.Name(), ".py")
|
||||
functions[name] = FunctionMeta{
|
||||
Type: "script",
|
||||
File: filepath.Join("src/rune", e.Name()),
|
||||
Type: "script",
|
||||
File: filepath.Join("src/rune", e.Name()),
|
||||
Factors: make(map[string]string),
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -4,8 +4,9 @@
|
||||
// identical failures, max_iterations enforcement, and tool allowlist checking.
|
||||
//
|
||||
// Usage:
|
||||
// local-tool-runtime run --model <model> --message <text> [--max-iterations N]
|
||||
// local-tool-runtime status
|
||||
//
|
||||
// local-tool-runtime run --model <model> --message <text> [--max-iterations N]
|
||||
// local-tool-runtime status
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -24,14 +25,14 @@ type ChatMessage struct {
|
||||
}
|
||||
|
||||
type OperatorResponse struct {
|
||||
Success bool `json:"success"`
|
||||
Response string `json:"response,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
Model string `json:"model,omitempty"`
|
||||
ToolCalls int `json:"tool_calls,omitempty"`
|
||||
Duration string `json:"duration,omitempty"`
|
||||
Events []map[string]interface{} `json:"events,omitempty"`
|
||||
Thinking []string `json:"thinking,omitempty"`
|
||||
Success bool `json:"success"`
|
||||
Response string `json:"response,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
Model string `json:"model,omitempty"`
|
||||
ToolCalls int `json:"tool_calls,omitempty"`
|
||||
Duration string `json:"duration,omitempty"`
|
||||
Events []map[string]interface{} `json:"events,omitempty"`
|
||||
Thinking []string `json:"thinking,omitempty"`
|
||||
}
|
||||
|
||||
type ToolRuntime struct {
|
||||
@@ -131,7 +132,7 @@ func (rt *ToolRuntime) Run(messages []ChatMessage) OperatorResponse {
|
||||
start := time.Now()
|
||||
conversation := make([]ChatMessage, len(messages))
|
||||
copy(conversation, messages)
|
||||
|
||||
|
||||
cb := &CircuitBreaker{}
|
||||
toolCalls := 0
|
||||
var events []map[string]interface{}
|
||||
@@ -141,7 +142,7 @@ func (rt *ToolRuntime) Run(messages []ChatMessage) OperatorResponse {
|
||||
if err != nil {
|
||||
return OperatorResponse{
|
||||
Success: false, Error: err.Error(),
|
||||
Model: rt.Model,
|
||||
Model: rt.Model,
|
||||
Duration: time.Since(start).Round(time.Millisecond).String(),
|
||||
}
|
||||
}
|
||||
@@ -159,48 +160,48 @@ func (rt *ToolRuntime) Run(messages []ChatMessage) OperatorResponse {
|
||||
if err := json.Unmarshal([]byte(strings.TrimPrefix(content, "TOOL_CALL:")), &call); err != nil {
|
||||
conversation = append(conversation, resp)
|
||||
conversation = append(conversation, ChatMessage{
|
||||
Role: "user",
|
||||
Role: "user",
|
||||
Content: fmt.Sprintf("Error: invalid tool call format. Use TOOL_CALL:{\"operation\":\"...\",\"args\":{...}}"),
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
// Check tool allowlist
|
||||
if !rt.ToolNames[call.Operation] {
|
||||
conversation = append(conversation, resp)
|
||||
conversation = append(conversation, ChatMessage{
|
||||
Role: "user",
|
||||
Role: "user",
|
||||
Content: fmt.Sprintf("Error: tool %q is not in the allowed list. Available: read_file, write_file, edit_file, list_directory, glob_files, grep_files, run_command, run_tests, lint_file, git_commit, git_diff, git_log, repo_map, read_persona, query_memory, check_growth, diagnose_behavior, find_capability", call.Operation),
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
// Execute the tool
|
||||
result := rt.executeTool(call.Operation, content)
|
||||
toolCalls++
|
||||
|
||||
|
||||
// Circuit breaker: check for consecutive identical failures
|
||||
if warning, tripped := cb.RegisterFailure(call.Operation, call.Args); tripped {
|
||||
return OperatorResponse{
|
||||
Success: false,
|
||||
Error: warning,
|
||||
Model: rt.Model,
|
||||
Success: false,
|
||||
Error: warning,
|
||||
Model: rt.Model,
|
||||
ToolCalls: toolCalls,
|
||||
Duration: time.Since(start).Round(time.Millisecond).String(),
|
||||
Duration: time.Since(start).Round(time.Millisecond).String(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
events = append(events, map[string]interface{}{
|
||||
"tool": call.Operation, "result": result,
|
||||
})
|
||||
|
||||
|
||||
conversation = append(conversation, resp)
|
||||
conversation = append(conversation, ChatMessage{
|
||||
Role: "user", Content: fmt.Sprintf("Tool result: %s", result),
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
// No tool call — this is the final response
|
||||
cb.RegisterSuccess()
|
||||
return OperatorResponse{
|
||||
@@ -210,7 +211,7 @@ func (rt *ToolRuntime) Run(messages []ChatMessage) OperatorResponse {
|
||||
Events: events,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return OperatorResponse{
|
||||
Success: false, Error: "max iterations exceeded",
|
||||
Model: rt.Model, ToolCalls: toolCalls,
|
||||
@@ -224,16 +225,30 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
baseURL := os.Getenv("OLLAMA_URL")
|
||||
if baseURL == "" { baseURL = "http://localhost:11434" }
|
||||
if baseURL == "" {
|
||||
baseURL = "http://localhost:11434"
|
||||
}
|
||||
model := "qwen2.5:3b"
|
||||
message := ""
|
||||
maxIter := 8
|
||||
|
||||
for i := 2; i < len(os.Args); i++ {
|
||||
switch os.Args[i] {
|
||||
case "--model": if i+1 < len(os.Args) { model = os.Args[i+1]; i++ }
|
||||
case "--message": if i+1 < len(os.Args) { message = os.Args[i+1]; i++ }
|
||||
case "--max-iterations": if i+1 < len(os.Args) { fmt.Sscanf(os.Args[i+1], "%d", &maxIter); i++ }
|
||||
case "--model":
|
||||
if i+1 < len(os.Args) {
|
||||
model = os.Args[i+1]
|
||||
i++
|
||||
}
|
||||
case "--message":
|
||||
if i+1 < len(os.Args) {
|
||||
message = os.Args[i+1]
|
||||
i++
|
||||
}
|
||||
case "--max-iterations":
|
||||
if i+1 < len(os.Args) {
|
||||
fmt.Sscanf(os.Args[i+1], "%d", &maxIter)
|
||||
i++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+50
-16
@@ -3,12 +3,13 @@
|
||||
// Uses modernc.org/sqlite (pure Go, no cgo).
|
||||
//
|
||||
// Usage:
|
||||
// memory-ops add <text> [--importance <f>] [--topic <t>] [--source <s>]
|
||||
// memory-ops search <query> [--limit N]
|
||||
// memory-ops get <id>
|
||||
// memory-ops wash <id>
|
||||
// memory-ops supersede <id> <new-text>
|
||||
// memory-ops stats
|
||||
//
|
||||
// memory-ops add <text> [--importance <f>] [--topic <t>] [--source <s>]
|
||||
// memory-ops search <query> [--limit N]
|
||||
// memory-ops get <id>
|
||||
// memory-ops wash <id>
|
||||
// memory-ops supersede <id> <new-text>
|
||||
// memory-ops stats
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -37,7 +38,9 @@ func dbPath() string {
|
||||
|
||||
func connect() (*sql.DB, error) {
|
||||
db, err := sql.Open("sqlite", dbPath())
|
||||
if err != nil { return nil, err }
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
db.Exec("PRAGMA journal_mode=WAL")
|
||||
db.Exec("PRAGMA synchronous=NORMAL")
|
||||
return db, nil
|
||||
@@ -69,10 +72,25 @@ func main() {
|
||||
source := ""
|
||||
for i := 0; i < len(args); i++ {
|
||||
switch args[i] {
|
||||
case "--importance": if i+1 < len(args) { fmt.Sscanf(args[i+1], "%f", &importance); i++ }
|
||||
case "--topic": if i+1 < len(args) { topic = args[i+1]; i++ }
|
||||
case "--source": if i+1 < len(args) { source = args[i+1]; i++ }
|
||||
default: if text == "" { text = args[i] }
|
||||
case "--importance":
|
||||
if i+1 < len(args) {
|
||||
fmt.Sscanf(args[i+1], "%f", &importance)
|
||||
i++
|
||||
}
|
||||
case "--topic":
|
||||
if i+1 < len(args) {
|
||||
topic = args[i+1]
|
||||
i++
|
||||
}
|
||||
case "--source":
|
||||
if i+1 < len(args) {
|
||||
source = args[i+1]
|
||||
i++
|
||||
}
|
||||
default:
|
||||
if text == "" {
|
||||
text = args[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(text) < 8 {
|
||||
@@ -100,8 +118,15 @@ func main() {
|
||||
limit := 5
|
||||
for i := 0; i < len(args); i++ {
|
||||
switch args[i] {
|
||||
case "--limit": if i+1 < len(args) { fmt.Sscanf(args[i+1], "%d", &limit); i++ }
|
||||
default: if query == "" { query = args[i] }
|
||||
case "--limit":
|
||||
if i+1 < len(args) {
|
||||
fmt.Sscanf(args[i+1], "%d", &limit)
|
||||
i++
|
||||
}
|
||||
default:
|
||||
if query == "" {
|
||||
query = args[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
rows, err := db.Query(
|
||||
@@ -128,7 +153,10 @@ func main() {
|
||||
fmt.Println(string(out))
|
||||
|
||||
case "get":
|
||||
if len(args) < 1 { fmt.Fprintln(os.Stderr, "usage: memory-ops get <id>"); os.Exit(1) }
|
||||
if len(args) < 1 {
|
||||
fmt.Fprintln(os.Stderr, "usage: memory-ops get <id>")
|
||||
os.Exit(1)
|
||||
}
|
||||
var id int64
|
||||
fmt.Sscanf(args[0], "%d", &id)
|
||||
var text, topic, createdAt string
|
||||
@@ -146,7 +174,10 @@ func main() {
|
||||
fmt.Println(string(out))
|
||||
|
||||
case "wash":
|
||||
if len(args) < 1 { fmt.Fprintln(os.Stderr, "usage: memory-ops wash <id>"); os.Exit(1) }
|
||||
if len(args) < 1 {
|
||||
fmt.Fprintln(os.Stderr, "usage: memory-ops wash <id>")
|
||||
os.Exit(1)
|
||||
}
|
||||
var id int64
|
||||
fmt.Sscanf(args[0], "%d", &id)
|
||||
db.Exec("UPDATE entries SET wash_count = wash_count + 1, last_accessed = ? WHERE id = ?", nowISO(), id)
|
||||
@@ -154,7 +185,10 @@ func main() {
|
||||
fmt.Println(string(out))
|
||||
|
||||
case "supersede":
|
||||
if len(args) < 2 { fmt.Fprintln(os.Stderr, "usage: memory-ops supersede <id> <new-text>"); os.Exit(1) }
|
||||
if len(args) < 2 {
|
||||
fmt.Fprintln(os.Stderr, "usage: memory-ops supersede <id> <new-text>")
|
||||
os.Exit(1)
|
||||
}
|
||||
var oldID int64
|
||||
fmt.Sscanf(args[0], "%d", &oldID)
|
||||
newText := args[1]
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,30 @@
|
||||
package main
|
||||
|
||||
// fuzz_test.go — FTS query builder fuzz (polyglot gate contract).
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// FuzzFtsQueryConjunctive — deterministic; quote-free inputs produce
|
||||
// balanced quotes. ORACLE-VERIFIED DIVERGENCE (pinned): the builder wraps
|
||||
// terms in quotes but never escapes embedded ones — input containing '"'
|
||||
// yields unbalanced output in the Python oracle too ('¼"' case). FTS5
|
||||
// hardening is defect territory (the builder can inject match syntax);
|
||||
// here we pin ORACLE behavior, not aspiration.
|
||||
func FuzzFtsQueryConjunctive(f *testing.F) {
|
||||
for _, s := range []string{"", "the and was", "one good term", "\"nested\" quotes",
|
||||
"cap this one out with many many many many many many words here"} {
|
||||
f.Add(s)
|
||||
}
|
||||
f.Fuzz(func(t *testing.T, s string) {
|
||||
out := FtsQueryConjunctive(s)
|
||||
if out != FtsQueryConjunctive(s) {
|
||||
t.Fatalf("not deterministic for %q", s)
|
||||
}
|
||||
if !strings.Contains(s, "\"") && strings.Count(out, "\"")%2 != 0 {
|
||||
t.Fatalf("unbalanced quotes on quote-free input %q: %q", s, out)
|
||||
}
|
||||
})
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,473 @@
|
||||
package main
|
||||
|
||||
// recall.go — batch 2: the deterministic read-path of memory_store.py
|
||||
// (contract BUILDER-2026-10-07T18:05:47Z-152c16). FtsQueryConjunctive,
|
||||
// ChunkText, RankFusion (RRF k=15), ProjectChunkRows, MapReduce,
|
||||
// FuseResults + NormKey. The embed-dependent surfaces stay Python-side.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// RRFK — small-corpus RRF constant (large corpora want 60).
|
||||
const RRFK = 15
|
||||
|
||||
// msStopwords — memory_store._STOPWORDS (distinct from curator's and the
|
||||
// lexicon's sets; extracted verbatim from the live module 2026-10-07).
|
||||
var msStopwords = map[string]bool{
|
||||
"about": true, "after": true, "also": true, "and": true, "are": true,
|
||||
"been": true, "but": true, "each": true, "for": true, "from": true,
|
||||
"have": true, "her": true, "him": true, "his": true, "how": true,
|
||||
"into": true, "its": true, "just": true, "likes": true, "more": true,
|
||||
"needs": true, "not": true, "only": true, "over": true, "prefers": true,
|
||||
"some": true, "such": true, "than": true, "that": true, "the": true,
|
||||
"them": true, "then": true, "there": true, "they": true, "this": true,
|
||||
"under": true, "very": true, "wants": true, "was": true, "were": true,
|
||||
"what": true, "when": true, "where": true, "which": true, "while": true,
|
||||
"who": true, "why": true, "will": true, "with": true, "you": true,
|
||||
"your": true,
|
||||
}
|
||||
|
||||
// FtsQueryConjunctive — the CONJUNCTIVE FTS5 MATCH builder (implicit AND):
|
||||
// significant terms only (len>3, not stop; fallback len>2), quoted,
|
||||
// first 8, space-joined. Phrase clauses are additive evidence.
|
||||
func FtsQueryConjunctive(query string) string {
|
||||
lower := strings.ToLower(query)
|
||||
var toks []string
|
||||
for _, t := range strings.Fields(lower) {
|
||||
if len(t) > 3 && !msStopwords[t] {
|
||||
toks = append(toks, t)
|
||||
}
|
||||
}
|
||||
if len(toks) == 0 {
|
||||
for _, t := range strings.Fields(lower) {
|
||||
if len(t) > 2 {
|
||||
toks = append(toks, t)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(toks) == 0 {
|
||||
return query
|
||||
}
|
||||
if len(toks) > 8 {
|
||||
toks = toks[:8]
|
||||
}
|
||||
quoted := make([]string, len(toks))
|
||||
for i, t := range toks {
|
||||
quoted[i] = fmt.Sprintf("%q", t)
|
||||
}
|
||||
return strings.Join(quoted, " ")
|
||||
}
|
||||
|
||||
// ChunkText — paragraph recombination at ~size chars; chunks still huge
|
||||
// (no paragraph breaks) hard-split at size. Headings stay with content.
|
||||
func ChunkText(text string, size, overlap int) []string {
|
||||
if text == "" {
|
||||
return []string{}
|
||||
}
|
||||
if len(text) <= size {
|
||||
if strings.TrimSpace(text) == "" {
|
||||
return []string{}
|
||||
}
|
||||
return []string{text}
|
||||
}
|
||||
var paras []string
|
||||
for _, p := range strings.Split(text, "\n\n") {
|
||||
if strings.TrimSpace(p) != "" {
|
||||
paras = append(paras, p)
|
||||
}
|
||||
}
|
||||
var chunks []string
|
||||
cur := ""
|
||||
for _, p := range paras {
|
||||
if len(cur)+len(p)+2 <= size {
|
||||
if cur == "" {
|
||||
cur = p
|
||||
} else {
|
||||
cur = cur + "\n\n" + p
|
||||
}
|
||||
} else {
|
||||
if cur != "" {
|
||||
chunks = append(chunks, cur)
|
||||
}
|
||||
cur = p
|
||||
}
|
||||
}
|
||||
if cur != "" {
|
||||
chunks = append(chunks, cur)
|
||||
}
|
||||
var out []string
|
||||
for _, c := range chunks {
|
||||
if len(c) > size*3/2 { // Python size * 1.5 (int text len compare)
|
||||
for i := 0; i < len(c); i += size {
|
||||
end := i + size
|
||||
if end > len(c) {
|
||||
end = len(c)
|
||||
}
|
||||
piece := c[i:end]
|
||||
if strings.TrimSpace(piece) != "" {
|
||||
out = append(out, piece)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
out = append(out, c)
|
||||
}
|
||||
}
|
||||
if out == nil {
|
||||
out = []string{}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// RankFusion — chunk-side RRF fusion with an explicit candidate cap.
|
||||
// dense/bm25 are [id, score] pairs. Three modes + min-max bm25 fallback.
|
||||
func RankFusion(dense, bm25 [][2]interface{}, rrfK float64) map[string]interface{} {
|
||||
if rrfK == 0 {
|
||||
rrfK = RRFK
|
||||
}
|
||||
ranks := map[string]float64{}
|
||||
var order []string // insertion order (Python dict preserves it)
|
||||
bestDense := 0.0
|
||||
mode := "lexical-only"
|
||||
addRank := func(eid string, score float64) {
|
||||
if _, has := ranks[eid]; !has {
|
||||
order = append(order, eid)
|
||||
}
|
||||
ranks[eid] += score
|
||||
}
|
||||
if len(dense) > 0 && len(bm25) > 0 {
|
||||
mode = "hybrid"
|
||||
for idx, list := range [][][2]interface{}{dense, bm25} {
|
||||
for pos, pair := range list {
|
||||
eid := pairString(pair[0])
|
||||
score, _ := pair[1].(float64)
|
||||
addRank(eid, 1.0/(rrfK+float64(pos)+1))
|
||||
if idx == 0 && score > bestDense {
|
||||
bestDense = score
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if len(dense) > 0 {
|
||||
mode = "dense-only"
|
||||
for pos, pair := range dense {
|
||||
addRank(pairString(pair[0]), 1.0/(rrfK+float64(pos)+1))
|
||||
if score, ok := pair[1].(float64); ok && score > bestDense {
|
||||
bestDense = score
|
||||
}
|
||||
}
|
||||
} else if len(bm25) > 0 {
|
||||
minBm, maxBm := 0.0, 0.0
|
||||
first := true
|
||||
for _, pair := range bm25 {
|
||||
s, _ := pair[1].(float64)
|
||||
if first {
|
||||
minBm, maxBm = s, s
|
||||
first = false
|
||||
}
|
||||
if s < minBm {
|
||||
minBm = s
|
||||
}
|
||||
if s > maxBm {
|
||||
maxBm = s
|
||||
}
|
||||
}
|
||||
span := maxBm - minBm
|
||||
if span < 1e-9 {
|
||||
span = 1e-9
|
||||
}
|
||||
for _, pair := range bm25 {
|
||||
s, _ := pair[1].(float64)
|
||||
addRank(pairString(pair[0]), (s-minBm)/span)
|
||||
}
|
||||
}
|
||||
rankList := make([]interface{}, 0, len(order))
|
||||
for _, eid := range order {
|
||||
rankList = append(rankList, []interface{}{numericID(eid), ranks[eid]})
|
||||
}
|
||||
return map[string]interface{}{
|
||||
"ranks": rankList,
|
||||
"best_dense": bestDense,
|
||||
"mode": mode,
|
||||
}
|
||||
}
|
||||
|
||||
// numericID — Python ids in the fusion pairs are ints; the string-keyed
|
||||
// map is internal only. Emit numeric when the id parses as an integer.
|
||||
func numericID(eid string) interface{} {
|
||||
var n int
|
||||
if _, err := fmt.Sscanf(eid, "%d", &n); err == nil && trimFloat(float64(n)) == eid {
|
||||
return n
|
||||
}
|
||||
return eid
|
||||
}
|
||||
|
||||
func pairString(v interface{}) string {
|
||||
switch x := v.(type) {
|
||||
case string:
|
||||
return x
|
||||
case float64:
|
||||
return trimFloat(x)
|
||||
case int:
|
||||
return fmt.Sprintf("%d", x)
|
||||
default:
|
||||
return fmt.Sprintf("%v", x)
|
||||
}
|
||||
}
|
||||
|
||||
func trimFloat(f float64) string {
|
||||
if f == float64(int64(f)) {
|
||||
return fmt.Sprintf("%d", int64(f))
|
||||
}
|
||||
return fmt.Sprintf("%g", f)
|
||||
}
|
||||
|
||||
// ChunkRow — one projected chunk row.
|
||||
type ChunkRow struct {
|
||||
ChunkID interface{} `json:"chunk_id"`
|
||||
Text string `json:"text"`
|
||||
Wing string `json:"wing"`
|
||||
Room string `json:"room"`
|
||||
Source string `json:"source"`
|
||||
Page interface{} `json:"page"`
|
||||
RRF float64 `json:"rrf"`
|
||||
}
|
||||
|
||||
// ProjectChunkRows — sorted -rrf, text[:400], rrf round 4, budget cap.
|
||||
func ProjectChunkRows(ranks map[string]float64, rows []map[string]interface{}, budget int) []map[string]interface{} {
|
||||
byID := map[string]map[string]interface{}{}
|
||||
for _, r := range rows {
|
||||
byID[pairString(r["rowid"])] = r
|
||||
}
|
||||
// sorted(ranks.items(), key=lambda x: -x[1]) — descending rrf; ties
|
||||
// keep Python's stable sort order (insertion order of ranks dict).
|
||||
type kv struct {
|
||||
k string
|
||||
v float64
|
||||
}
|
||||
var items []kv
|
||||
for k, v := range ranks {
|
||||
items = append(items, kv{k, v})
|
||||
}
|
||||
sort.SliceStable(items, func(i, j int) bool { return items[i].v > items[j].v })
|
||||
out := []map[string]interface{}{}
|
||||
for _, it := range items {
|
||||
r, has := byID[it.k]
|
||||
if !has {
|
||||
continue
|
||||
}
|
||||
text, _ := r["text"].(string)
|
||||
if len(text) > 400 {
|
||||
text = text[:400]
|
||||
}
|
||||
wing, _ := r["wing"].(string)
|
||||
room, _ := r["room"].(string)
|
||||
source, _ := r["source_path"].(string)
|
||||
var page interface{}
|
||||
if p, hasPage := r["page"]; hasPage {
|
||||
page = p
|
||||
} else {
|
||||
page = nil
|
||||
}
|
||||
out = append(out, map[string]interface{}{
|
||||
"chunk_id": r["chunk_id"],
|
||||
"text": text,
|
||||
"wing": wing,
|
||||
"room": room,
|
||||
"source": source,
|
||||
"page": page,
|
||||
"rrf": round4(it.v),
|
||||
})
|
||||
if len(out) >= budget {
|
||||
break
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func round4(f float64) float64 {
|
||||
return float64(int64(f*10000+0.5)) / 10000
|
||||
}
|
||||
|
||||
// MapReduce — chunk the entries, take top-3 per chunk by
|
||||
// relevance/score (default 0.5), then REDUCE: re-sort ALL mapped entries
|
||||
// by score desc (the oracle's final pass; no budget param in the code
|
||||
// despite the comment).
|
||||
func MapReduce(entries []map[string]interface{}, chunkSize int) []map[string]interface{} {
|
||||
if len(entries) <= chunkSize {
|
||||
return entries
|
||||
}
|
||||
var mapped []map[string]interface{}
|
||||
for start := 0; start < len(entries); start += chunkSize {
|
||||
end := start + chunkSize
|
||||
if end > len(entries) {
|
||||
end = len(entries)
|
||||
}
|
||||
chunk := entries[start:end]
|
||||
type scored struct {
|
||||
score float64
|
||||
entry map[string]interface{}
|
||||
}
|
||||
var sortd []scored
|
||||
for _, e := range chunk {
|
||||
score := 0.5
|
||||
if v, has := e["relevance"]; has {
|
||||
if f, ok := v.(float64); ok {
|
||||
score = f
|
||||
}
|
||||
} else if v, has := e["score"]; has {
|
||||
if f, ok := v.(float64); ok {
|
||||
score = f
|
||||
}
|
||||
}
|
||||
sortd = append(sortd, scored{score, e})
|
||||
}
|
||||
// Python sort is stable; ties keep list order.
|
||||
sort.SliceStable(sortd, func(i, j int) bool { return sortd[i].score > sortd[j].score })
|
||||
n := 3
|
||||
if len(sortd) < n {
|
||||
n = len(sortd)
|
||||
}
|
||||
for _, s := range sortd[:n] {
|
||||
mapped = append(mapped, s.entry)
|
||||
}
|
||||
}
|
||||
if mapped == nil {
|
||||
mapped = []map[string]interface{}{}
|
||||
}
|
||||
// Reduce: sort all mapped by score desc (stable; ties keep map order).
|
||||
sort.SliceStable(mapped, func(i, j int) bool {
|
||||
return entryScore(mapped[i]) > entryScore(mapped[j])
|
||||
})
|
||||
return mapped
|
||||
}
|
||||
|
||||
// entryScore — relevance, else score, else 0.5 (the oracle's lookup).
|
||||
func entryScore(e map[string]interface{}) float64 {
|
||||
if v, has := e["relevance"]; has {
|
||||
if f, ok := v.(float64); ok {
|
||||
return f
|
||||
}
|
||||
}
|
||||
if v, has := e["score"]; has {
|
||||
if f, ok := v.(float64); ok {
|
||||
return f
|
||||
}
|
||||
}
|
||||
return 0.5
|
||||
}
|
||||
|
||||
// NormKey — cross-table dedup normalization: lowercase, whitespace-collapse.
|
||||
func NormKey(text string) string {
|
||||
return strings.Join(strings.Fields(strings.ToLower(text)), " ")
|
||||
}
|
||||
|
||||
// FuseResults — _norm_key dedup + merge shape + kind-wise min-max
|
||||
// scale-alignment (the audit fix) + final sort.
|
||||
func FuseResults(entries, research []map[string]interface{}) []map[string]interface{} {
|
||||
seen := map[string]bool{}
|
||||
merged := []map[string]interface{}{}
|
||||
for _, e := range entries {
|
||||
text, _ := e["text"].(string)
|
||||
key := NormKey(text)
|
||||
if seen[key] {
|
||||
continue
|
||||
}
|
||||
seen[key] = true
|
||||
topic, _ := e["topic"].(string)
|
||||
if topic == "" {
|
||||
topic = "fact"
|
||||
}
|
||||
relevance, _ := e["relevance"].(float64)
|
||||
confidence, _ := e["confidence"].(float64)
|
||||
merged = append(merged, map[string]interface{}{
|
||||
"id": e["id"],
|
||||
"text": text,
|
||||
"topic": topic,
|
||||
"kind": "fact",
|
||||
"relevance": round4(relevance),
|
||||
"confidence": round2(confidence),
|
||||
})
|
||||
}
|
||||
for _, c := range research {
|
||||
if c == nil {
|
||||
continue
|
||||
}
|
||||
text, _ := c["text"].(string)
|
||||
key := NormKey(text)
|
||||
if seen[key] {
|
||||
continue
|
||||
}
|
||||
seen[key] = true
|
||||
topic, _ := c["wing"].(string)
|
||||
if topic == "" {
|
||||
topic = "research"
|
||||
}
|
||||
rel, hasRel := c["rrf"].(float64)
|
||||
if !hasRel {
|
||||
rel, _ = c["score"].(float64)
|
||||
}
|
||||
confidence, hasConf := c["confidence"].(float64)
|
||||
if !hasConf || confidence == 0 {
|
||||
confidence = 0.7
|
||||
}
|
||||
merged = append(merged, map[string]interface{}{
|
||||
"id": c["chunk_id"],
|
||||
"text": text,
|
||||
"topic": topic,
|
||||
"kind": "research",
|
||||
"relevance": round4(rel),
|
||||
"confidence": round2(confidence),
|
||||
})
|
||||
}
|
||||
// final sort before alignment (Python sorts, then aligns, then sorts)
|
||||
sortByRelevanceDesc(merged)
|
||||
// SCALE-ALIGNMENT: min-max normalize each kind's relevance to [0,1]
|
||||
// within this result set, then re-sort.
|
||||
for _, kind := range []string{"fact", "research"} {
|
||||
var subset []map[string]interface{}
|
||||
for _, r := range merged {
|
||||
if r["kind"] == kind {
|
||||
subset = append(subset, r)
|
||||
}
|
||||
}
|
||||
if len(subset) > 1 {
|
||||
lo, hi := 0.0, 0.0
|
||||
first := true
|
||||
for _, r := range subset {
|
||||
v := r["relevance"].(float64)
|
||||
if first {
|
||||
lo, hi = v, v
|
||||
first = false
|
||||
}
|
||||
if v < lo {
|
||||
lo = v
|
||||
}
|
||||
if v > hi {
|
||||
hi = v
|
||||
}
|
||||
}
|
||||
span := hi - lo
|
||||
if span < 1e-9 {
|
||||
span = 1e-9
|
||||
}
|
||||
for _, r := range subset {
|
||||
v := r["relevance"].(float64)
|
||||
r["relevance"] = round4((v - lo) / span)
|
||||
}
|
||||
}
|
||||
}
|
||||
sortByRelevanceDesc(merged)
|
||||
return merged
|
||||
}
|
||||
|
||||
func sortByRelevanceDesc(list []map[string]interface{}) {
|
||||
sort.SliceStable(list, func(i, j int) bool {
|
||||
return list[i]["relevance"].(float64) > list[j]["relevance"].(float64)
|
||||
})
|
||||
}
|
||||
|
||||
func round2(f float64) float64 {
|
||||
return float64(int64(f*100+0.5)) / 100
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package main
|
||||
|
||||
// recall_gate.go — batch 4: the recall() integration pieces of
|
||||
// memory_store.py (contract BUILDER-2026-10-07T19:47:37Z-1cf1dc).
|
||||
// DenseGate (the meta-judgement: vector proximity is not a relation),
|
||||
// RankFusionEntries (the entries-side fusion — verified identical to the
|
||||
// chunk-side RankFusion already ported; the docstring's raw-BM25 claim
|
||||
// is NOT what the code does — the CODE min-max normalizes in ALL modes,
|
||||
// and the code is the oracle), the abstain constants.
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// RecallBudget / RecallMinScore — the abstain constants (below the floor
|
||||
// → abstain, don't inject distractors).
|
||||
const (
|
||||
RecallBudget = 8
|
||||
RecallMinScore = 0.42
|
||||
)
|
||||
|
||||
// DenseHit — one candidate dense match: (rowid, cosine).
|
||||
type DenseHit struct {
|
||||
RowID int64
|
||||
Cos float64
|
||||
}
|
||||
|
||||
// QueryContent — the query's significant words (len>3, not msStopwords).
|
||||
func QueryContent(query string) []string {
|
||||
var out []string
|
||||
seen := map[string]bool{}
|
||||
for _, t := range strings.Fields(strings.ToLower(query)) {
|
||||
if len(t) > 3 && !msStopwords[t] && !seen[t] {
|
||||
seen[t] = true
|
||||
out = append(out, t)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// DenseGate — the meta-judgement applied to the vec0 candidates.
|
||||
// queryContent: significant query words. dfOf: document frequency
|
||||
// (LIKE-substring count / total entries — the CALLER supplies counts;
|
||||
// the gate owns the 0.05 distinctiveness threshold). A hit is kept iff
|
||||
// it shares a DISTINCTIVE content word with the entry text; otherwise
|
||||
// only when the query has no content words at all OR cos >= 0.80.
|
||||
func DenseGate(hits []DenseHit, entryTexts map[int64]string, queryContent []string, dfOf func(word string) int, totalEntries int) []DenseHit {
|
||||
out := []DenseHit{}
|
||||
for _, h := range hits {
|
||||
etxt := strings.ToLower(entryTexts[h.RowID])
|
||||
distinctive := false
|
||||
for _, w := range queryContent {
|
||||
if strings.Contains(etxt, w) {
|
||||
df := 0
|
||||
if dfOf != nil {
|
||||
df = dfOf(w)
|
||||
}
|
||||
total := totalEntries
|
||||
if total < 1 {
|
||||
total = 1
|
||||
}
|
||||
if float64(df)/float64(total) < 0.05 {
|
||||
distinctive = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if distinctive {
|
||||
out = append(out, h)
|
||||
} else if len(queryContent) == 0 || h.Cos >= 0.80 {
|
||||
out = append(out, h)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// RankFusionEntries — the entries-side fusion. VERIFIED identical to the
|
||||
// chunk-side RankFusion (same file, memory_store.py:1297 vs the batch-2
|
||||
// port): the code min-max normalizes the lexical-only branch in ALL
|
||||
// modes. The docstring's raw-BM25 claim is stale — the CODE is the
|
||||
// oracle. Reuse keeps one fusion implementation; this wrapper pins the
|
||||
// parity for the recall() call shape.
|
||||
func RankFusionEntries(dense, bm25 [][2]interface{}) map[string]interface{} {
|
||||
return RankFusion(dense, bm25, RRFK)
|
||||
}
|
||||
|
||||
// RankIDs — the sorted rank order (desc by score) for downstream SQL IN
|
||||
// clauses (Python: list(ranks.keys()) is INSERTION order — the fusion's
|
||||
// accumulation order, which SortStable ranking reproduces).
|
||||
func RankIDs(fusion map[string]interface{}) []interface{} {
|
||||
ranks, _ := fusion["ranks"].([]interface{})
|
||||
return ranks
|
||||
}
|
||||
|
||||
// sortedInsertion — retained reference: the fusion's rank list order IS
|
||||
// the insertion order (accumulation), not score order. Documented for
|
||||
// the SQL-IN parity note. (Unused call sites may sort explicitly.)
|
||||
var _ = sort.Float64s
|
||||
@@ -0,0 +1,194 @@
|
||||
package main
|
||||
|
||||
// score.go — batch 3: the scoring/abstention kernel of memory_store.py
|
||||
// (contract BUILDER-2026-10-07T18:57:06Z-c2429c). ScoreRecallRows (the
|
||||
// metadata scoring + mode-specific abstention), PositionAware (the
|
||||
// lost-in-the-middle re-rank).
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// RecencyHalfLife — RECENCY_HALF_LIFE (days).
|
||||
const RecencyHalfLife = 30.0
|
||||
|
||||
// PositionAware — the two highest-relevance items go FIRST and LAST; the
|
||||
// same items, only the order changes. No-op for < 3 items.
|
||||
func PositionAware(top []map[string]interface{}) []map[string]interface{} {
|
||||
if len(top) < 3 {
|
||||
return top
|
||||
}
|
||||
s := make([]map[string]interface{}, len(top))
|
||||
copy(s, top)
|
||||
sorted := make([]map[string]interface{}, len(s))
|
||||
copy(sorted, s)
|
||||
sort.SliceStable(sorted, func(i, j int) bool {
|
||||
return entryRelevance(sorted[i]) > entryRelevance(sorted[j])
|
||||
})
|
||||
out := make([]map[string]interface{}, 0, len(sorted))
|
||||
out = append(out, sorted[0])
|
||||
out = append(out, sorted[2:]...)
|
||||
out = append(out, sorted[1])
|
||||
return out
|
||||
}
|
||||
|
||||
func entryRelevance(m map[string]interface{}) float64 {
|
||||
v, _ := m["relevance"].(float64)
|
||||
return v
|
||||
}
|
||||
|
||||
// parseISO — Python fromisoformat with the Z→+00:00 swap; also accepts
|
||||
// naive timestamps. Returns (time, ok).
|
||||
func parseISO(s string) (time.Time, bool) {
|
||||
if s == "" {
|
||||
return time.Time{}, false
|
||||
}
|
||||
s = strings.Replace(s, "Z", "+00:00", 1)
|
||||
layouts := []string{
|
||||
"2006-01-02T15:04:05.999999-07:00",
|
||||
"2006-01-02T15:04:05-07:00",
|
||||
"2006-01-02T15:04:05.999999",
|
||||
"2006-01-02T15:04:05",
|
||||
}
|
||||
for _, l := range layouts {
|
||||
if t, err := time.Parse(l, s); err == nil {
|
||||
return t, true
|
||||
}
|
||||
}
|
||||
return time.Time{}, false
|
||||
}
|
||||
|
||||
// ScoreRecallRows — rank-ordered metadata scoring + abstention ladder.
|
||||
func ScoreRecallRows(fusion map[string]interface{}, rows []map[string]interface{}, dense, bm25 [][2]interface{}, budget int, minScore float64, nowStr string) []map[string]interface{} {
|
||||
// ranks: the fusion dict's ranks — [[id, score], ...] pairs.
|
||||
ranksList, _ := fusion["ranks"].([]interface{})
|
||||
bestDense, _ := fusion["best_dense"].(float64)
|
||||
mode, _ := fusion["mode"].(string)
|
||||
|
||||
byID := map[string]map[string]interface{}{}
|
||||
for _, r := range rows {
|
||||
id := pairString(r["id"])
|
||||
byID[id] = r
|
||||
}
|
||||
|
||||
type rankItem struct {
|
||||
eid string
|
||||
base float64
|
||||
}
|
||||
var items []rankItem
|
||||
for _, raw := range ranksList {
|
||||
pair, ok := raw.([]interface{})
|
||||
if !ok || len(pair) != 2 {
|
||||
continue
|
||||
}
|
||||
eid := pairString(pair[0])
|
||||
base, _ := pair[1].(float64)
|
||||
items = append(items, rankItem{eid, base})
|
||||
}
|
||||
// sorted(ranks.items(), key=lambda item: -item[1]) — stable desc.
|
||||
sort.SliceStable(items, func(i, j int) bool { return items[i].base > items[j].base })
|
||||
|
||||
nowDT, _ := parseISO(nowStr)
|
||||
scored := []map[string]interface{}{}
|
||||
for _, it := range items {
|
||||
row, has := byID[it.eid]
|
||||
if !has {
|
||||
continue
|
||||
}
|
||||
conf := 0.7
|
||||
if v, ok := row["confidence"].(float64); ok && v != 0 {
|
||||
conf = v
|
||||
}
|
||||
temp := 1.0
|
||||
if v, ok := row["temperature"].(float64); ok && v != 0 {
|
||||
temp = v
|
||||
}
|
||||
recency := 1.0
|
||||
if la, ok := row["last_accessed"].(string); ok && la != "" {
|
||||
if tLast, ok2 := parseISO(la); ok2 {
|
||||
days := nowDT.Sub(tLast).Seconds() / 86400.0
|
||||
if days >= 0 {
|
||||
recency = RecencyHalfLife / (RecencyHalfLife + days)
|
||||
}
|
||||
}
|
||||
// timestamp failure → recency stays 1.0 (the except path)
|
||||
}
|
||||
var relevance float64
|
||||
if len(dense) > 0 {
|
||||
relevance = it.base * (0.4 + 0.6*conf) * (0.5 + 0.5*temp) * (0.85 + 0.15*recency)
|
||||
} else {
|
||||
relevance = (it.base + 5.0) * (0.4 + 0.6*conf) * (0.5 + 0.5*temp) * (0.85 + 0.15*recency)
|
||||
}
|
||||
text, _ := row["text"].(string)
|
||||
topic, _ := row["topic"].(string)
|
||||
source, _ := row["source"].(string)
|
||||
lastAccessed, _ := row["last_accessed"].(string)
|
||||
scored = append(scored, map[string]interface{}{
|
||||
"id": numericID(it.eid),
|
||||
"text": text,
|
||||
"importance": row["importance"],
|
||||
"topic": topic,
|
||||
"entities": row["entities"],
|
||||
"source": source,
|
||||
"last_accessed": lastAccessed,
|
||||
"rrf": round4(it.base),
|
||||
"confidence": round2(conf),
|
||||
"temperature": round2(temp),
|
||||
"relevance": round4(relevance),
|
||||
"mode": mode,
|
||||
})
|
||||
}
|
||||
if len(scored) == 0 {
|
||||
return []map[string]interface{}{}
|
||||
}
|
||||
if len(scored) > budget {
|
||||
scored = scored[:budget]
|
||||
}
|
||||
top := scored
|
||||
if len(dense) == 0 && len(bm25) == 0 {
|
||||
return []map[string]interface{}{}
|
||||
}
|
||||
if len(dense) == 0 {
|
||||
maxRel := 0.0
|
||||
for i, item := range top {
|
||||
r := entryRelevance(item)
|
||||
if i == 0 || r > maxRel {
|
||||
maxRel = r
|
||||
}
|
||||
}
|
||||
if maxRel < minScore {
|
||||
return []map[string]interface{}{}
|
||||
}
|
||||
return top
|
||||
}
|
||||
if len(bm25) == 0 {
|
||||
if bestDense < minScore {
|
||||
return []map[string]interface{}{}
|
||||
}
|
||||
if bestDense >= 0.74 {
|
||||
return PositionAware(top)
|
||||
}
|
||||
if len(dense) >= 2 {
|
||||
dScores := make([]float64, 0, len(dense))
|
||||
for _, p := range dense {
|
||||
f, _ := p[1].(float64)
|
||||
dScores = append(dScores, f)
|
||||
}
|
||||
sort.Float64s(dScores)
|
||||
if dScores[len(dScores)-1]-dScores[len(dScores)-2] >= 0.08 {
|
||||
return PositionAware(top)
|
||||
}
|
||||
return []map[string]interface{}{}
|
||||
}
|
||||
return PositionAware(MapReduce(top, 10))
|
||||
}
|
||||
if bestDense < minScore {
|
||||
return []map[string]interface{}{}
|
||||
}
|
||||
return PositionAware(MapReduce(top, 10))
|
||||
}
|
||||
|
||||
var _ = fmt.Sprintf // retained for parity annotations
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
string("\xbc\"")
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
string("\"")
|
||||
@@ -0,0 +1,200 @@
|
||||
{
|
||||
"oracle": "src/rune/memory_store.py recall() dense gate + _py_rank_fusion",
|
||||
"generated": "2026-10-07",
|
||||
"fixture_df_note": "df via LIKE-substring over active entries; threshold 0.05",
|
||||
"cases": [
|
||||
{
|
||||
"fn": "dense_gate",
|
||||
"name": "distinctive_shared",
|
||||
"input": {
|
||||
"query": "worthiness gates",
|
||||
"hits": [
|
||||
[
|
||||
1,
|
||||
0.9
|
||||
],
|
||||
[
|
||||
2,
|
||||
0.85
|
||||
]
|
||||
],
|
||||
"total": 8
|
||||
},
|
||||
"expected": {
|
||||
"kept": [
|
||||
[
|
||||
1,
|
||||
0.9
|
||||
],
|
||||
[
|
||||
2,
|
||||
0.85
|
||||
]
|
||||
],
|
||||
"q_content": [
|
||||
"gates",
|
||||
"worthiness"
|
||||
],
|
||||
"df": {
|
||||
"worthiness": 1,
|
||||
"gates": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "dense_gate",
|
||||
"name": "common_word_falls_back",
|
||||
"input": {
|
||||
"query": "black",
|
||||
"hits": [
|
||||
[
|
||||
2,
|
||||
0.86
|
||||
],
|
||||
[
|
||||
3,
|
||||
0.79
|
||||
]
|
||||
],
|
||||
"total": 8
|
||||
},
|
||||
"expected": {
|
||||
"kept": [
|
||||
[
|
||||
2,
|
||||
0.86
|
||||
]
|
||||
],
|
||||
"q_content": [
|
||||
"black"
|
||||
],
|
||||
"df": {
|
||||
"black": 2
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "dense_gate",
|
||||
"name": "strong_cos_no_share",
|
||||
"input": {
|
||||
"query": "unrelated query terms",
|
||||
"hits": [
|
||||
[
|
||||
6,
|
||||
0.91
|
||||
],
|
||||
[
|
||||
4,
|
||||
0.7
|
||||
]
|
||||
],
|
||||
"total": 8
|
||||
},
|
||||
"expected": {
|
||||
"kept": [
|
||||
[
|
||||
6,
|
||||
0.91
|
||||
]
|
||||
],
|
||||
"q_content": [
|
||||
"query",
|
||||
"terms",
|
||||
"unrelated"
|
||||
],
|
||||
"df": {
|
||||
"unrelated": 1,
|
||||
"query": 0,
|
||||
"terms": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "dense_gate",
|
||||
"name": "no_content_words",
|
||||
"input": {
|
||||
"query": "the was and",
|
||||
"hits": [
|
||||
[
|
||||
1,
|
||||
0.6
|
||||
],
|
||||
[
|
||||
6,
|
||||
0.95
|
||||
]
|
||||
],
|
||||
"total": 8
|
||||
},
|
||||
"expected": {
|
||||
"kept": [
|
||||
[
|
||||
1,
|
||||
0.6
|
||||
],
|
||||
[
|
||||
6,
|
||||
0.95
|
||||
]
|
||||
],
|
||||
"q_content": [],
|
||||
"df": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "fusion_identity",
|
||||
"name": "entries_eq_chunks",
|
||||
"input": {
|
||||
"dense": [
|
||||
[
|
||||
1,
|
||||
0.9
|
||||
],
|
||||
[
|
||||
2,
|
||||
0.8
|
||||
]
|
||||
],
|
||||
"bm25": [
|
||||
[
|
||||
2,
|
||||
10.0
|
||||
],
|
||||
[
|
||||
3,
|
||||
4.0
|
||||
]
|
||||
]
|
||||
},
|
||||
"expected": {
|
||||
"fusion": {
|
||||
"ranks": [
|
||||
[
|
||||
1,
|
||||
0.0625
|
||||
],
|
||||
[
|
||||
2,
|
||||
0.1213235294117647
|
||||
],
|
||||
[
|
||||
3,
|
||||
0.058823529411764705
|
||||
]
|
||||
],
|
||||
"best_dense": 0.9,
|
||||
"mode": "hybrid"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "constants",
|
||||
"name": "abstain",
|
||||
"input": {},
|
||||
"expected": {
|
||||
"budget": 8,
|
||||
"min_score": 0.42
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package main
|
||||
|
||||
// truth_gate_test.go — memory-store batch 4 parity gate: the dense
|
||||
// meta-judgement + fusion identity replay truth_gate.json (live oracle,
|
||||
// fixture store — contract BUILDER-2026-10-07T19:47:37Z-1cf1dc).
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func sortStringsW(s []string) {
|
||||
for i := 1; i < len(s); i++ {
|
||||
for j := i; j > 0 && s[j-1] > s[j]; j-- {
|
||||
s[j-1], s[j] = s[j], s[j-1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGateParity(t *testing.T) {
|
||||
b, err := os.ReadFile("truth_gate.json")
|
||||
if err != nil {
|
||||
t.Fatalf("truth_gate.json missing: %v", err)
|
||||
}
|
||||
var doc struct {
|
||||
Cases []struct {
|
||||
Fn string `json:"fn"`
|
||||
Name string `json:"name"`
|
||||
Input map[string]interface{} `json:"input"`
|
||||
Expected map[string]interface{} `json:"expected"`
|
||||
} `json:"cases"`
|
||||
}
|
||||
if err := json.Unmarshal(b, &doc); err != nil {
|
||||
t.Fatalf("unparseable: %v", err)
|
||||
}
|
||||
// The fixture entry texts (the generator's store, shipped here — the
|
||||
// truth inputs must carry their own state).
|
||||
fixtureTexts := map[int64]string{
|
||||
1: "the curator gates memory writes through worthiness",
|
||||
2: "black holes bend light near the event horizon",
|
||||
3: "black coffee in the morning keeps nick running",
|
||||
4: "the spine rebuild parity proofs live in store",
|
||||
5: "memory writes require the operator approval gate",
|
||||
6: "unrelated filler text about weather patterns",
|
||||
7: "curator provenance recovery from odysseus",
|
||||
8: "another filler row to pad the document frequency",
|
||||
}
|
||||
// df table shipped per case (the oracle's LIKE counts).
|
||||
for _, c := range doc.Cases {
|
||||
switch c.Fn {
|
||||
case "dense_gate":
|
||||
query, _ := c.Input["query"].(string)
|
||||
hitsAny, _ := c.Input["hits"].([]interface{})
|
||||
var hits []DenseHit
|
||||
for _, h := range hitsAny {
|
||||
pair, _ := h.([]interface{})
|
||||
id, _ := pair[0].(float64)
|
||||
cos, _ := pair[1].(float64)
|
||||
hits = append(hits, DenseHit{RowID: int64(id), Cos: cos})
|
||||
}
|
||||
dfAny, _ := c.Expected["df"].(map[string]interface{})
|
||||
dfOf := func(word string) int {
|
||||
v, ok := dfAny[word]
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
f, _ := v.(float64)
|
||||
return int(f)
|
||||
}
|
||||
total := int(c.Input["total"].(float64))
|
||||
qc := QueryContent(query)
|
||||
wantQAny, _ := c.Expected["q_content"].([]interface{})
|
||||
var wantQ []string
|
||||
for _, w := range wantQAny {
|
||||
s, _ := w.(string)
|
||||
wantQ = append(wantQ, s)
|
||||
}
|
||||
// The truth column ships the q_content SORTED (generator's
|
||||
// json.dumps of a set); compare sorted-to-sorted.
|
||||
sortStringsW(qc)
|
||||
jsonEq(t, c.Name, "q_content", qc, wantQ)
|
||||
got := DenseGate(hits, fixtureTexts, qc, dfOf, total)
|
||||
wantArr, _ := c.Expected["kept"].([]interface{})
|
||||
var want []DenseHit
|
||||
for _, h := range wantArr {
|
||||
pair, _ := h.([]interface{})
|
||||
id, _ := pair[0].(float64)
|
||||
cos, _ := pair[1].(float64)
|
||||
want = append(want, DenseHit{RowID: int64(id), Cos: cos})
|
||||
}
|
||||
jsonEq(t, c.Name, "kept", got, want)
|
||||
case "fusion_identity":
|
||||
dense := pairList(c.Input["dense"])
|
||||
bm25 := pairList(c.Input["bm25"])
|
||||
got := RankFusionEntries(dense, bm25)
|
||||
want, _ := c.Expected["fusion"].(map[string]interface{})
|
||||
jsonEq(t, c.Name, "fusion", got, want)
|
||||
case "constants":
|
||||
jsonEq(t, c.Name, "budget", RecallBudget, c.Expected["budget"])
|
||||
jsonEq(t, c.Name, "min_score", RecallMinScore, c.Expected["min_score"])
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,558 @@
|
||||
{
|
||||
"oracle": "src/rune/memory_store.py _py_* oracles",
|
||||
"generated": "2026-10-07",
|
||||
"cases": [
|
||||
{
|
||||
"fn": "fts",
|
||||
"name": "significant_terms",
|
||||
"input": {
|
||||
"query": "the curator gates memory writes"
|
||||
},
|
||||
"expected": {
|
||||
"q": "\"curator\" \"gates\" \"memory\" \"writes\""
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "fts",
|
||||
"name": "stopwords_dropped",
|
||||
"input": {
|
||||
"query": "the and was with nick"
|
||||
},
|
||||
"expected": {
|
||||
"q": "\"nick\""
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "fts",
|
||||
"name": "short_fallback",
|
||||
"input": {
|
||||
"query": "go is fun ai"
|
||||
},
|
||||
"expected": {
|
||||
"q": "\"fun\""
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "fts",
|
||||
"name": "cap_eight",
|
||||
"input": {
|
||||
"query": "alpha beta gamma delta epsilon zeta eta theta iota kappa"
|
||||
},
|
||||
"expected": {
|
||||
"q": "\"alpha\" \"beta\" \"gamma\" \"delta\" \"epsilon\" \"zeta\" \"theta\" \"iota\""
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "fts",
|
||||
"name": "empty_returns_raw",
|
||||
"input": {
|
||||
"query": "a an of"
|
||||
},
|
||||
"expected": {
|
||||
"q": "a an of"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "chunk",
|
||||
"name": "under_size",
|
||||
"input": {
|
||||
"text": "one paragraph only",
|
||||
"size": 1500,
|
||||
"overlap": 150
|
||||
},
|
||||
"expected": {
|
||||
"chunks": [
|
||||
"one paragraph only"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "chunk",
|
||||
"name": "recombine",
|
||||
"input": {
|
||||
"text": "paragraph 0 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 1 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 2 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 4 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 5 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 6 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 7 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 8 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 9 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 10 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 11 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 12 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 13 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 14 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 15 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 16 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 17 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 18 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 19 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 20 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 21 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 22 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 23 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 24 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 25 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 26 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 27 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 28 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 29 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
||||
"size": 400,
|
||||
"overlap": 150
|
||||
},
|
||||
"expected": {
|
||||
"chunks": [
|
||||
"paragraph 0 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 1 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 2 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 4 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 5 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 6 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
||||
"paragraph 7 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 8 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 9 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 10 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 11 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 12 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 13 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
||||
"paragraph 14 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 15 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 16 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 17 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 18 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 19 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 20 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
||||
"paragraph 21 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 22 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 23 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 24 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 25 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 26 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 27 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
||||
"paragraph 28 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nparagraph 29 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "chunk",
|
||||
"name": "hard_split",
|
||||
"input": {
|
||||
"text": "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy",
|
||||
"size": 1000,
|
||||
"overlap": 150
|
||||
},
|
||||
"expected": {
|
||||
"chunks": [
|
||||
"yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy",
|
||||
"yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy",
|
||||
"yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "chunk",
|
||||
"name": "empty",
|
||||
"input": {
|
||||
"text": "",
|
||||
"size": 1500,
|
||||
"overlap": 150
|
||||
},
|
||||
"expected": {
|
||||
"chunks": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "fuse_ranks",
|
||||
"name": "hybrid",
|
||||
"input": {
|
||||
"dense": [
|
||||
[
|
||||
1,
|
||||
0.9
|
||||
],
|
||||
[
|
||||
2,
|
||||
0.8
|
||||
],
|
||||
[
|
||||
3,
|
||||
0.7
|
||||
]
|
||||
],
|
||||
"bm25": [
|
||||
[
|
||||
2,
|
||||
12.0
|
||||
],
|
||||
[
|
||||
4,
|
||||
8.0
|
||||
],
|
||||
[
|
||||
1,
|
||||
3.0
|
||||
]
|
||||
]
|
||||
},
|
||||
"expected": {
|
||||
"ranks": [
|
||||
[
|
||||
1,
|
||||
0.11805555555555555
|
||||
],
|
||||
[
|
||||
2,
|
||||
0.1213235294117647
|
||||
],
|
||||
[
|
||||
3,
|
||||
0.05555555555555555
|
||||
],
|
||||
[
|
||||
4,
|
||||
0.058823529411764705
|
||||
]
|
||||
],
|
||||
"best_dense": 0.9,
|
||||
"mode": "hybrid"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "fuse_ranks",
|
||||
"name": "dense_only",
|
||||
"input": {
|
||||
"dense": [
|
||||
[
|
||||
1,
|
||||
0.9
|
||||
],
|
||||
[
|
||||
2,
|
||||
0.8
|
||||
],
|
||||
[
|
||||
3,
|
||||
0.7
|
||||
]
|
||||
],
|
||||
"bm25": []
|
||||
},
|
||||
"expected": {
|
||||
"ranks": [
|
||||
[
|
||||
1,
|
||||
0.0625
|
||||
],
|
||||
[
|
||||
2,
|
||||
0.058823529411764705
|
||||
],
|
||||
[
|
||||
3,
|
||||
0.05555555555555555
|
||||
]
|
||||
],
|
||||
"best_dense": 0.9,
|
||||
"mode": "dense-only"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "fuse_ranks",
|
||||
"name": "lexical_only",
|
||||
"input": {
|
||||
"dense": [],
|
||||
"bm25": [
|
||||
[
|
||||
2,
|
||||
12.0
|
||||
],
|
||||
[
|
||||
4,
|
||||
8.0
|
||||
],
|
||||
[
|
||||
1,
|
||||
3.0
|
||||
]
|
||||
]
|
||||
},
|
||||
"expected": {
|
||||
"ranks": [
|
||||
[
|
||||
2,
|
||||
1.0
|
||||
],
|
||||
[
|
||||
4,
|
||||
0.5555555555555556
|
||||
],
|
||||
[
|
||||
1,
|
||||
0.0
|
||||
]
|
||||
],
|
||||
"best_dense": 0.0,
|
||||
"mode": "lexical-only"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "fuse_ranks",
|
||||
"name": "neither",
|
||||
"input": {
|
||||
"dense": [],
|
||||
"bm25": []
|
||||
},
|
||||
"expected": {
|
||||
"ranks": [],
|
||||
"best_dense": 0.0,
|
||||
"mode": "lexical-only"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "project",
|
||||
"name": "budget_and_shape",
|
||||
"input": {
|
||||
"ranks": {
|
||||
"2": 0.0625,
|
||||
"1": 0.05
|
||||
}
|
||||
},
|
||||
"expected": {
|
||||
"rows": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "map_reduce",
|
||||
"name": "top3_per_chunk",
|
||||
"input": {
|
||||
"entries": [
|
||||
{
|
||||
"id": 0,
|
||||
"relevance": 0.0
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"relevance": 0.1
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"relevance": 0.2
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"relevance": 0.30000000000000004
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"relevance": 0.4
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"relevance": 0.5
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"relevance": 0.6000000000000001
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"relevance": 0.7000000000000001
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"relevance": 0.8
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"relevance": 0.9
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"relevance": 1.0
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"relevance": 1.1
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"relevance": 1.2000000000000002
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
"relevance": 1.3
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"relevance": 1.4000000000000001
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"relevance": 1.5
|
||||
},
|
||||
{
|
||||
"id": 16,
|
||||
"relevance": 1.6
|
||||
},
|
||||
{
|
||||
"id": 17,
|
||||
"relevance": 1.7000000000000002
|
||||
},
|
||||
{
|
||||
"id": 18,
|
||||
"relevance": 1.8
|
||||
},
|
||||
{
|
||||
"id": 19,
|
||||
"relevance": 1.9000000000000001
|
||||
},
|
||||
{
|
||||
"id": 20,
|
||||
"relevance": 2.0
|
||||
},
|
||||
{
|
||||
"id": 21,
|
||||
"relevance": 2.1
|
||||
},
|
||||
{
|
||||
"id": 22,
|
||||
"relevance": 2.2
|
||||
},
|
||||
{
|
||||
"id": 23,
|
||||
"relevance": 2.3000000000000003
|
||||
},
|
||||
{
|
||||
"id": 24,
|
||||
"relevance": 2.4000000000000004
|
||||
}
|
||||
],
|
||||
"chunk_size": 10
|
||||
},
|
||||
"expected": {
|
||||
"mapped": [
|
||||
{
|
||||
"id": 24,
|
||||
"relevance": 2.4000000000000004
|
||||
},
|
||||
{
|
||||
"id": 23,
|
||||
"relevance": 2.3000000000000003
|
||||
},
|
||||
{
|
||||
"id": 22,
|
||||
"relevance": 2.2
|
||||
},
|
||||
{
|
||||
"id": 19,
|
||||
"relevance": 1.9000000000000001
|
||||
},
|
||||
{
|
||||
"id": 18,
|
||||
"relevance": 1.8
|
||||
},
|
||||
{
|
||||
"id": 17,
|
||||
"relevance": 1.7000000000000002
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"relevance": 0.9
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"relevance": 0.8
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"relevance": 0.7000000000000001
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "map_reduce",
|
||||
"name": "under_threshold",
|
||||
"input": {
|
||||
"entries": [
|
||||
{
|
||||
"id": 0,
|
||||
"relevance": 0.0
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"relevance": 0.1
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"relevance": 0.2
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"relevance": 0.30000000000000004
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"relevance": 0.4
|
||||
}
|
||||
],
|
||||
"chunk_size": 10
|
||||
},
|
||||
"expected": {
|
||||
"mapped": [
|
||||
{
|
||||
"id": 0,
|
||||
"relevance": 0.0
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"relevance": 0.1
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"relevance": 0.2
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"relevance": 0.30000000000000004
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"relevance": 0.4
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "fuse_results",
|
||||
"name": "dedup_scale_align",
|
||||
"input": {
|
||||
"entries": [
|
||||
{
|
||||
"id": 1,
|
||||
"text": "nick prefers coffee",
|
||||
"topic": "human",
|
||||
"relevance": 2.5,
|
||||
"confidence": 0.9
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"text": "NICK PREFERS COFFEE",
|
||||
"topic": "human",
|
||||
"relevance": 1.1,
|
||||
"confidence": 0.8
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"text": "rune gates memory writes",
|
||||
"topic": "project",
|
||||
"relevance": 5.6,
|
||||
"confidence": 0.95
|
||||
}
|
||||
],
|
||||
"research": [
|
||||
{
|
||||
"chunk_id": "c1",
|
||||
"text": "the spine rebuild parity",
|
||||
"wing": "research",
|
||||
"rrf": 0.0625,
|
||||
"confidence": 0.7
|
||||
},
|
||||
{
|
||||
"chunk_id": "c2",
|
||||
"text": "curator provenance recovery",
|
||||
"wing": "research",
|
||||
"rrf": 0.05
|
||||
}
|
||||
]
|
||||
},
|
||||
"expected": {
|
||||
"merged": [
|
||||
{
|
||||
"id": 3,
|
||||
"text": "rune gates memory writes",
|
||||
"topic": "project",
|
||||
"kind": "fact",
|
||||
"relevance": 1.0,
|
||||
"confidence": 0.95
|
||||
},
|
||||
{
|
||||
"id": "c1",
|
||||
"text": "the spine rebuild parity",
|
||||
"topic": "research",
|
||||
"kind": "research",
|
||||
"relevance": 1.0,
|
||||
"confidence": 0.7
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"text": "nick prefers coffee",
|
||||
"topic": "human",
|
||||
"kind": "fact",
|
||||
"relevance": 0.0,
|
||||
"confidence": 0.9
|
||||
},
|
||||
{
|
||||
"id": "c2",
|
||||
"text": "curator provenance recovery",
|
||||
"topic": "research",
|
||||
"kind": "research",
|
||||
"relevance": 0.0,
|
||||
"confidence": 0.7
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
package main
|
||||
|
||||
// truth_recall_test.go — memory-store batch 2 parity gate: the read-path
|
||||
// replays truth_recall.json (live Python _py_* oracles — contract
|
||||
// BUILDER-2026-10-07T18:05:47Z-152c16).
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRecallParity(t *testing.T) {
|
||||
b, err := os.ReadFile("truth_recall.json")
|
||||
if err != nil {
|
||||
t.Fatalf("truth_recall.json missing: %v", err)
|
||||
}
|
||||
var doc struct {
|
||||
Cases []struct {
|
||||
Fn string `json:"fn"`
|
||||
Name string `json:"name"`
|
||||
Input map[string]interface{} `json:"input"`
|
||||
Expected map[string]interface{} `json:"expected"`
|
||||
} `json:"cases"`
|
||||
}
|
||||
if err := json.Unmarshal(b, &doc); err != nil {
|
||||
t.Fatalf("unparseable: %v", err)
|
||||
}
|
||||
for _, c := range doc.Cases {
|
||||
switch c.Fn {
|
||||
case "fts":
|
||||
q, _ := c.Input["query"].(string)
|
||||
got := FtsQueryConjunctive(q)
|
||||
want, _ := c.Expected["q"].(string)
|
||||
if got != want {
|
||||
t.Errorf("%s: fts\n got %q\nwant %q", c.Name, got, want)
|
||||
}
|
||||
case "chunk":
|
||||
text, _ := c.Input["text"].(string)
|
||||
size := int(c.Input["size"].(float64))
|
||||
overlap := int(c.Input["overlap"].(float64))
|
||||
got := ChunkText(text, size, overlap)
|
||||
wantArr, _ := c.Expected["chunks"].([]interface{})
|
||||
if len(got) != len(wantArr) {
|
||||
t.Errorf("%s: chunks = %d, want %d", c.Name, len(got), len(wantArr))
|
||||
continue
|
||||
}
|
||||
for i, w := range wantArr {
|
||||
want, _ := w.(string)
|
||||
if got[i] != want {
|
||||
t.Errorf("%s: chunk[%d]\n got %q\nwant %q", c.Name, i, got[i], want)
|
||||
}
|
||||
}
|
||||
case "fuse_ranks":
|
||||
dense := pairList(c.Input["dense"])
|
||||
bm25 := pairList(c.Input["bm25"])
|
||||
got := RankFusion(dense, bm25, RRFK)
|
||||
want := c.Expected
|
||||
jsonEq(t, c.Name, "mode", got["mode"], want["mode"])
|
||||
jsonEq(t, c.Name, "ranks", got["ranks"], want["ranks"])
|
||||
jsonEq(t, c.Name, "best_dense", got["best_dense"], want["best_dense"])
|
||||
case "project":
|
||||
ranksAny, _ := c.Input["ranks"].(map[string]interface{})
|
||||
ranks := map[string]float64{}
|
||||
for k, v := range ranksAny {
|
||||
f, _ := v.(float64)
|
||||
ranks[k] = f
|
||||
}
|
||||
// rows ship in the case input under "rows" (the truth ships its
|
||||
// own fixtures — same discipline as scaffold batches).
|
||||
rowsAny, _ := c.Input["rows"].([]interface{})
|
||||
var rows []map[string]interface{}
|
||||
for _, r := range rowsAny {
|
||||
m, _ := r.(map[string]interface{})
|
||||
rows = append(rows, m)
|
||||
}
|
||||
got := ProjectChunkRows(ranks, rows, 6)
|
||||
wantArr, _ := c.Expected["rows"].([]interface{})
|
||||
jsonEq(t, c.Name, "rows", got, wantArr)
|
||||
case "map_reduce":
|
||||
entries := objList(c.Input["entries"])
|
||||
chunkSize := int(c.Input["chunk_size"].(float64))
|
||||
got := MapReduce(entries, chunkSize)
|
||||
wantArr, _ := c.Expected["mapped"].([]interface{})
|
||||
jsonEq(t, c.Name, "mapped", got, wantArr)
|
||||
case "fuse_results":
|
||||
entries := objList(c.Input["entries"])
|
||||
research := objList(c.Input["research"])
|
||||
got := FuseResults(entries, research)
|
||||
wantArr, _ := c.Expected["merged"].([]interface{})
|
||||
jsonEq(t, c.Name, "merged", got, wantArr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pairList — [[id, score], ...] from decoded JSON.
|
||||
func pairList(v interface{}) [][2]interface{} {
|
||||
arr, ok := v.([]interface{})
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
out := make([][2]interface{}, 0, len(arr))
|
||||
for _, e := range arr {
|
||||
pair, ok2 := e.([]interface{})
|
||||
if !ok2 || len(pair) != 2 {
|
||||
continue
|
||||
}
|
||||
out = append(out, [2]interface{}{pair[0], pair[1]})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func objList(v interface{}) []map[string]interface{} {
|
||||
arr, ok := v.([]interface{})
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
out := make([]map[string]interface{}, 0, len(arr))
|
||||
for _, e := range arr {
|
||||
m, _ := e.(map[string]interface{})
|
||||
out = append(out, m)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// jsonEq — canonical-JSON comparison (Go typed values vs decoded JSON).
|
||||
func jsonEq(t *testing.T, name, field string, got, want interface{}) {
|
||||
t.Helper()
|
||||
gb, _ := json.Marshal(got)
|
||||
wb, _ := json.Marshal(want)
|
||||
if string(gb) != string(wb) {
|
||||
t.Errorf("%s: %s divergence:\n got %s\nwant %s", name, field, gb, wb)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,681 @@
|
||||
{
|
||||
"oracle": "src/rune/memory_store.py _py_score_recall_rows",
|
||||
"generated": "2026-10-07",
|
||||
"cases": [
|
||||
{
|
||||
"fn": "score",
|
||||
"name": "hybrid",
|
||||
"input": {
|
||||
"fusion": {
|
||||
"ranks": [
|
||||
[
|
||||
1,
|
||||
0.12
|
||||
],
|
||||
[
|
||||
2,
|
||||
0.11
|
||||
],
|
||||
[
|
||||
3,
|
||||
0.1
|
||||
],
|
||||
[
|
||||
4,
|
||||
0.09
|
||||
]
|
||||
],
|
||||
"best_dense": 0.8,
|
||||
"mode": "hybrid"
|
||||
},
|
||||
"rows": [
|
||||
{
|
||||
"id": 1,
|
||||
"text": "entry 1 body",
|
||||
"importance": 0.5,
|
||||
"topic": "project",
|
||||
"entities": [
|
||||
"a"
|
||||
],
|
||||
"source": "fixture",
|
||||
"confidence": 0.9,
|
||||
"temperature": 1.2,
|
||||
"last_accessed": "2026-10-07T18:00:00"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"text": "entry 2 body",
|
||||
"importance": 0.51,
|
||||
"topic": "project",
|
||||
"entities": [
|
||||
"a"
|
||||
],
|
||||
"source": "fixture",
|
||||
"confidence": 0.6,
|
||||
"temperature": 0.8,
|
||||
"last_accessed": "2026-09-01T10:00:00"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"text": "entry 3 body",
|
||||
"importance": 0.52,
|
||||
"topic": "project",
|
||||
"entities": [
|
||||
"a"
|
||||
],
|
||||
"source": "fixture",
|
||||
"confidence": 0.9,
|
||||
"temperature": 0.8,
|
||||
"last_accessed": "2026-05-01T10:00:00"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"text": "entry 4 body",
|
||||
"importance": 0.53,
|
||||
"topic": "project",
|
||||
"entities": [
|
||||
"a"
|
||||
],
|
||||
"source": "fixture",
|
||||
"confidence": 0.6,
|
||||
"temperature": 1.2,
|
||||
"last_accessed": null
|
||||
}
|
||||
],
|
||||
"dense": [
|
||||
[
|
||||
1,
|
||||
0.8
|
||||
],
|
||||
[
|
||||
2,
|
||||
0.7
|
||||
],
|
||||
[
|
||||
3,
|
||||
0.6
|
||||
]
|
||||
],
|
||||
"bm25": [
|
||||
[
|
||||
2,
|
||||
10.0
|
||||
],
|
||||
[
|
||||
4,
|
||||
5.0
|
||||
],
|
||||
[
|
||||
1,
|
||||
2.0
|
||||
]
|
||||
],
|
||||
"budget": 3,
|
||||
"min_score": 0.3,
|
||||
"now": "2026-10-07T18:58:00"
|
||||
},
|
||||
"expected": {
|
||||
"scored": [
|
||||
{
|
||||
"id": 1,
|
||||
"text": "entry 1 body",
|
||||
"importance": 0.5,
|
||||
"topic": "project",
|
||||
"entities": [
|
||||
"a"
|
||||
],
|
||||
"source": "fixture",
|
||||
"last_accessed": "2026-10-07T18:00:00",
|
||||
"rrf": 0.12,
|
||||
"confidence": 0.9,
|
||||
"temperature": 1.2,
|
||||
"relevance": 0.1241,
|
||||
"mode": "hybrid"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"text": "entry 2 body",
|
||||
"importance": 0.51,
|
||||
"topic": "project",
|
||||
"entities": [
|
||||
"a"
|
||||
],
|
||||
"source": "fixture",
|
||||
"last_accessed": "2026-09-01T10:00:00",
|
||||
"rrf": 0.11,
|
||||
"confidence": 0.6,
|
||||
"temperature": 0.8,
|
||||
"relevance": 0.0691,
|
||||
"mode": "hybrid"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"text": "entry 3 body",
|
||||
"importance": 0.52,
|
||||
"topic": "project",
|
||||
"entities": [
|
||||
"a"
|
||||
],
|
||||
"source": "fixture",
|
||||
"last_accessed": "2026-05-01T10:00:00",
|
||||
"rrf": 0.1,
|
||||
"confidence": 0.9,
|
||||
"temperature": 0.8,
|
||||
"relevance": 0.0739,
|
||||
"mode": "hybrid"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "score",
|
||||
"name": "dense_only_high_conf",
|
||||
"input": {
|
||||
"fusion": {
|
||||
"ranks": [
|
||||
[
|
||||
1,
|
||||
0.06
|
||||
],
|
||||
[
|
||||
2,
|
||||
0.055
|
||||
],
|
||||
[
|
||||
3,
|
||||
0.05
|
||||
]
|
||||
],
|
||||
"best_dense": 0.8,
|
||||
"mode": "dense-only"
|
||||
},
|
||||
"rows": [
|
||||
{
|
||||
"id": 1,
|
||||
"text": "entry 1 body",
|
||||
"importance": 0.5,
|
||||
"topic": "project",
|
||||
"entities": [
|
||||
"a"
|
||||
],
|
||||
"source": "fixture",
|
||||
"confidence": 0.9,
|
||||
"temperature": 1.2,
|
||||
"last_accessed": "2026-10-07T18:00:00"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"text": "entry 2 body",
|
||||
"importance": 0.51,
|
||||
"topic": "project",
|
||||
"entities": [
|
||||
"a"
|
||||
],
|
||||
"source": "fixture",
|
||||
"confidence": 0.6,
|
||||
"temperature": 0.8,
|
||||
"last_accessed": "2026-09-01T10:00:00"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"text": "entry 3 body",
|
||||
"importance": 0.52,
|
||||
"topic": "project",
|
||||
"entities": [
|
||||
"a"
|
||||
],
|
||||
"source": "fixture",
|
||||
"confidence": 0.9,
|
||||
"temperature": 0.8,
|
||||
"last_accessed": "2026-05-01T10:00:00"
|
||||
}
|
||||
],
|
||||
"dense": [
|
||||
[
|
||||
1,
|
||||
0.8
|
||||
],
|
||||
[
|
||||
2,
|
||||
0.7
|
||||
],
|
||||
[
|
||||
3,
|
||||
0.6
|
||||
]
|
||||
],
|
||||
"bm25": [],
|
||||
"budget": 5,
|
||||
"min_score": 0.3,
|
||||
"now": "2026-10-07T18:58:00"
|
||||
},
|
||||
"expected": {
|
||||
"scored": [
|
||||
{
|
||||
"id": 1,
|
||||
"text": "entry 1 body",
|
||||
"importance": 0.5,
|
||||
"topic": "project",
|
||||
"entities": [
|
||||
"a"
|
||||
],
|
||||
"source": "fixture",
|
||||
"last_accessed": "2026-10-07T18:00:00",
|
||||
"rrf": 0.06,
|
||||
"confidence": 0.9,
|
||||
"temperature": 1.2,
|
||||
"relevance": 0.062,
|
||||
"mode": "dense-only"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"text": "entry 2 body",
|
||||
"importance": 0.51,
|
||||
"topic": "project",
|
||||
"entities": [
|
||||
"a"
|
||||
],
|
||||
"source": "fixture",
|
||||
"last_accessed": "2026-09-01T10:00:00",
|
||||
"rrf": 0.055,
|
||||
"confidence": 0.6,
|
||||
"temperature": 0.8,
|
||||
"relevance": 0.0345,
|
||||
"mode": "dense-only"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"text": "entry 3 body",
|
||||
"importance": 0.52,
|
||||
"topic": "project",
|
||||
"entities": [
|
||||
"a"
|
||||
],
|
||||
"source": "fixture",
|
||||
"last_accessed": "2026-05-01T10:00:00",
|
||||
"rrf": 0.05,
|
||||
"confidence": 0.9,
|
||||
"temperature": 0.8,
|
||||
"relevance": 0.037,
|
||||
"mode": "dense-only"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "score",
|
||||
"name": "dense_only_gap_abstain",
|
||||
"input": {
|
||||
"fusion": {
|
||||
"ranks": [
|
||||
[
|
||||
1,
|
||||
0.06
|
||||
],
|
||||
[
|
||||
2,
|
||||
0.055
|
||||
],
|
||||
[
|
||||
3,
|
||||
0.05
|
||||
]
|
||||
],
|
||||
"best_dense": 0.8,
|
||||
"mode": "dense-only"
|
||||
},
|
||||
"rows": [
|
||||
{
|
||||
"id": 1,
|
||||
"text": "entry 1 body",
|
||||
"importance": 0.5,
|
||||
"topic": "project",
|
||||
"entities": [
|
||||
"a"
|
||||
],
|
||||
"source": "fixture",
|
||||
"confidence": 0.9,
|
||||
"temperature": 1.2,
|
||||
"last_accessed": "2026-10-07T18:00:00"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"text": "entry 2 body",
|
||||
"importance": 0.51,
|
||||
"topic": "project",
|
||||
"entities": [
|
||||
"a"
|
||||
],
|
||||
"source": "fixture",
|
||||
"confidence": 0.6,
|
||||
"temperature": 0.8,
|
||||
"last_accessed": "2026-09-01T10:00:00"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"text": "entry 3 body",
|
||||
"importance": 0.52,
|
||||
"topic": "project",
|
||||
"entities": [
|
||||
"a"
|
||||
],
|
||||
"source": "fixture",
|
||||
"confidence": 0.9,
|
||||
"temperature": 0.8,
|
||||
"last_accessed": "2026-05-01T10:00:00"
|
||||
}
|
||||
],
|
||||
"dense": [
|
||||
[
|
||||
1,
|
||||
0.8
|
||||
],
|
||||
[
|
||||
2,
|
||||
0.75
|
||||
]
|
||||
],
|
||||
"bm25": [],
|
||||
"budget": 5,
|
||||
"min_score": 0.3,
|
||||
"now": "2026-10-07T18:58:00"
|
||||
},
|
||||
"expected": {
|
||||
"scored": [
|
||||
{
|
||||
"id": 1,
|
||||
"text": "entry 1 body",
|
||||
"importance": 0.5,
|
||||
"topic": "project",
|
||||
"entities": [
|
||||
"a"
|
||||
],
|
||||
"source": "fixture",
|
||||
"last_accessed": "2026-10-07T18:00:00",
|
||||
"rrf": 0.06,
|
||||
"confidence": 0.9,
|
||||
"temperature": 1.2,
|
||||
"relevance": 0.062,
|
||||
"mode": "dense-only"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"text": "entry 2 body",
|
||||
"importance": 0.51,
|
||||
"topic": "project",
|
||||
"entities": [
|
||||
"a"
|
||||
],
|
||||
"source": "fixture",
|
||||
"last_accessed": "2026-09-01T10:00:00",
|
||||
"rrf": 0.055,
|
||||
"confidence": 0.6,
|
||||
"temperature": 0.8,
|
||||
"relevance": 0.0345,
|
||||
"mode": "dense-only"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"text": "entry 3 body",
|
||||
"importance": 0.52,
|
||||
"topic": "project",
|
||||
"entities": [
|
||||
"a"
|
||||
],
|
||||
"source": "fixture",
|
||||
"last_accessed": "2026-05-01T10:00:00",
|
||||
"rrf": 0.05,
|
||||
"confidence": 0.9,
|
||||
"temperature": 0.8,
|
||||
"relevance": 0.037,
|
||||
"mode": "dense-only"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "score",
|
||||
"name": "dense_only_min_score",
|
||||
"input": {
|
||||
"fusion": {
|
||||
"ranks": [
|
||||
[
|
||||
1,
|
||||
0.06
|
||||
],
|
||||
[
|
||||
2,
|
||||
0.055
|
||||
],
|
||||
[
|
||||
3,
|
||||
0.05
|
||||
]
|
||||
],
|
||||
"best_dense": 0.8,
|
||||
"mode": "dense-only"
|
||||
},
|
||||
"rows": [
|
||||
{
|
||||
"id": 1,
|
||||
"text": "entry 1 body",
|
||||
"importance": 0.5,
|
||||
"topic": "project",
|
||||
"entities": [
|
||||
"a"
|
||||
],
|
||||
"source": "fixture",
|
||||
"confidence": 0.9,
|
||||
"temperature": 1.2,
|
||||
"last_accessed": "2026-10-07T18:00:00"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"text": "entry 2 body",
|
||||
"importance": 0.51,
|
||||
"topic": "project",
|
||||
"entities": [
|
||||
"a"
|
||||
],
|
||||
"source": "fixture",
|
||||
"confidence": 0.6,
|
||||
"temperature": 0.8,
|
||||
"last_accessed": "2026-09-01T10:00:00"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"text": "entry 3 body",
|
||||
"importance": 0.52,
|
||||
"topic": "project",
|
||||
"entities": [
|
||||
"a"
|
||||
],
|
||||
"source": "fixture",
|
||||
"confidence": 0.9,
|
||||
"temperature": 0.8,
|
||||
"last_accessed": "2026-05-01T10:00:00"
|
||||
}
|
||||
],
|
||||
"dense": [
|
||||
[
|
||||
1,
|
||||
0.8
|
||||
],
|
||||
[
|
||||
2,
|
||||
0.7
|
||||
],
|
||||
[
|
||||
3,
|
||||
0.6
|
||||
]
|
||||
],
|
||||
"bm25": [],
|
||||
"budget": 5,
|
||||
"min_score": 0.9,
|
||||
"now": "2026-10-07T18:58:00"
|
||||
},
|
||||
"expected": {
|
||||
"scored": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "score",
|
||||
"name": "lexical_only_gate",
|
||||
"input": {
|
||||
"fusion": {
|
||||
"ranks": [
|
||||
[
|
||||
2,
|
||||
0.9
|
||||
],
|
||||
[
|
||||
4,
|
||||
0.7
|
||||
]
|
||||
],
|
||||
"best_dense": 0.0,
|
||||
"mode": "lexical-only"
|
||||
},
|
||||
"rows": [
|
||||
{
|
||||
"id": 2,
|
||||
"text": "entry 2 body",
|
||||
"importance": 0.5,
|
||||
"topic": "project",
|
||||
"entities": [
|
||||
"a"
|
||||
],
|
||||
"source": "fixture",
|
||||
"confidence": 0.9,
|
||||
"temperature": 1.2,
|
||||
"last_accessed": "2026-10-07T18:00:00"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"text": "entry 4 body",
|
||||
"importance": 0.51,
|
||||
"topic": "project",
|
||||
"entities": [
|
||||
"a"
|
||||
],
|
||||
"source": "fixture",
|
||||
"confidence": 0.6,
|
||||
"temperature": 0.8,
|
||||
"last_accessed": "2026-09-01T10:00:00"
|
||||
}
|
||||
],
|
||||
"dense": [],
|
||||
"bm25": [
|
||||
[
|
||||
2,
|
||||
10.0
|
||||
],
|
||||
[
|
||||
4,
|
||||
5.0
|
||||
],
|
||||
[
|
||||
1,
|
||||
2.0
|
||||
]
|
||||
],
|
||||
"budget": 5,
|
||||
"min_score": 1.0,
|
||||
"now": "2026-10-07T18:58:00"
|
||||
},
|
||||
"expected": {
|
||||
"scored": [
|
||||
{
|
||||
"id": 2,
|
||||
"text": "entry 2 body",
|
||||
"importance": 0.5,
|
||||
"topic": "project",
|
||||
"entities": [
|
||||
"a"
|
||||
],
|
||||
"source": "fixture",
|
||||
"last_accessed": "2026-10-07T18:00:00",
|
||||
"rrf": 0.9,
|
||||
"confidence": 0.9,
|
||||
"temperature": 1.2,
|
||||
"relevance": 6.0994,
|
||||
"mode": "lexical-only"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"text": "entry 4 body",
|
||||
"importance": 0.51,
|
||||
"topic": "project",
|
||||
"entities": [
|
||||
"a"
|
||||
],
|
||||
"source": "fixture",
|
||||
"last_accessed": "2026-09-01T10:00:00",
|
||||
"rrf": 0.7,
|
||||
"confidence": 0.6,
|
||||
"temperature": 0.8,
|
||||
"relevance": 3.5783,
|
||||
"mode": "lexical-only"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "score",
|
||||
"name": "neither",
|
||||
"input": {
|
||||
"fusion": {
|
||||
"ranks": [
|
||||
[
|
||||
2,
|
||||
0.9
|
||||
],
|
||||
[
|
||||
4,
|
||||
0.7
|
||||
]
|
||||
],
|
||||
"best_dense": 0.0,
|
||||
"mode": "lexical-only"
|
||||
},
|
||||
"rows": [
|
||||
{
|
||||
"id": 2,
|
||||
"text": "entry 2 body",
|
||||
"importance": 0.5,
|
||||
"topic": "project",
|
||||
"entities": [
|
||||
"a"
|
||||
],
|
||||
"source": "fixture",
|
||||
"confidence": 0.9,
|
||||
"temperature": 1.2,
|
||||
"last_accessed": "2026-10-07T18:00:00"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"text": "entry 4 body",
|
||||
"importance": 0.51,
|
||||
"topic": "project",
|
||||
"entities": [
|
||||
"a"
|
||||
],
|
||||
"source": "fixture",
|
||||
"confidence": 0.6,
|
||||
"temperature": 0.8,
|
||||
"last_accessed": "2026-09-01T10:00:00"
|
||||
}
|
||||
],
|
||||
"dense": [],
|
||||
"bm25": [],
|
||||
"budget": 5,
|
||||
"min_score": 0.3,
|
||||
"now": "2026-10-07T18:58:00"
|
||||
},
|
||||
"expected": {
|
||||
"scored": []
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package main
|
||||
|
||||
// truth_score_test.go — memory-store batch 3 parity gate: the scoring
|
||||
// kernel replays truth_score.json (live _py_score_recall_rows — contract
|
||||
// BUILDER-2026-10-07T18:57:06Z-c2429c).
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestScoreParity(t *testing.T) {
|
||||
b, err := os.ReadFile("truth_score.json")
|
||||
if err != nil {
|
||||
t.Fatalf("truth_score.json missing: %v", err)
|
||||
}
|
||||
var doc struct {
|
||||
Cases []struct {
|
||||
Fn string `json:"fn"`
|
||||
Name string `json:"name"`
|
||||
Input map[string]interface{} `json:"input"`
|
||||
Expected map[string]interface{} `json:"expected"`
|
||||
} `json:"cases"`
|
||||
}
|
||||
if err := json.Unmarshal(b, &doc); err != nil {
|
||||
t.Fatalf("unparseable: %v", err)
|
||||
}
|
||||
for _, c := range doc.Cases {
|
||||
fusion, _ := c.Input["fusion"].(map[string]interface{})
|
||||
rows := objList(c.Input["rows"])
|
||||
dense := pairList(c.Input["dense"])
|
||||
bm25 := pairList(c.Input["bm25"])
|
||||
budget := int(c.Input["budget"].(float64))
|
||||
minScore := c.Input["min_score"].(float64)
|
||||
nowStr, _ := c.Input["now"].(string)
|
||||
got := ScoreRecallRows(fusion, rows, dense, bm25, budget, minScore, nowStr)
|
||||
wantArr, _ := c.Expected["scored"].([]interface{})
|
||||
if len(got) != len(wantArr) {
|
||||
gb, _ := json.Marshal(got)
|
||||
wb, _ := json.Marshal(wantArr)
|
||||
t.Errorf("%s: scored = %d, want %d\n got %s\nwant %s",
|
||||
c.Name, len(got), len(wantArr), gb, wb)
|
||||
continue
|
||||
}
|
||||
for i, w := range wantArr {
|
||||
want, _ := w.(map[string]interface{})
|
||||
jsonEq(t, c.Name, field("scored[%d]", i), got[i], want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// field — formatted comparison label.
|
||||
func field(format string, i int) string {
|
||||
return fmt.Sprintf(format, i)
|
||||
}
|
||||
@@ -3,14 +3,15 @@
|
||||
// queues messages, approves/rejects/executes.
|
||||
//
|
||||
// Usage:
|
||||
// message-queue add <message> [--channel <ch>] [--task-id <id>]
|
||||
// message-queue list [--status pending|approved|executed|rejected]
|
||||
// message-queue approve <id>
|
||||
// message-queue approve-all
|
||||
// message-queue reject <id>
|
||||
// message-queue clear
|
||||
// message-queue mark-executed <id>
|
||||
// message-queue detect <message>
|
||||
//
|
||||
// message-queue add <message> [--channel <ch>] [--task-id <id>]
|
||||
// message-queue list [--status pending|approved|executed|rejected]
|
||||
// message-queue approve <id>
|
||||
// message-queue approve-all
|
||||
// message-queue reject <id>
|
||||
// message-queue clear
|
||||
// message-queue mark-executed <id>
|
||||
// message-queue detect <message>
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -108,11 +109,19 @@ func main() {
|
||||
for i := 0; i < len(args); i++ {
|
||||
switch args[i] {
|
||||
case "--channel":
|
||||
if i+1 < len(args) { channel = args[i+1]; i++ }
|
||||
if i+1 < len(args) {
|
||||
channel = args[i+1]
|
||||
i++
|
||||
}
|
||||
case "--task-id":
|
||||
if i+1 < len(args) { taskID = args[i+1]; i++ }
|
||||
if i+1 < len(args) {
|
||||
taskID = args[i+1]
|
||||
i++
|
||||
}
|
||||
default:
|
||||
if msg == "" { msg = args[i] }
|
||||
if msg == "" {
|
||||
msg = args[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
qm := QueuedMessage{
|
||||
@@ -130,7 +139,9 @@ func main() {
|
||||
|
||||
case "list":
|
||||
status := ""
|
||||
if len(args) > 1 && args[0] == "--status" { status = args[1] }
|
||||
if len(args) > 1 && args[0] == "--status" {
|
||||
status = args[1]
|
||||
}
|
||||
for _, m := range q.Messages {
|
||||
if status == "" || m.Status == status {
|
||||
out, _ := json.Marshal(m)
|
||||
|
||||
Binary file not shown.
+44
-21
@@ -3,11 +3,12 @@
|
||||
// capabilities, fires events to registered hooks.
|
||||
//
|
||||
// Usage:
|
||||
// microkernel register <manifest-json>
|
||||
// microkernel unregister <name>
|
||||
// microkernel resolve <capability>
|
||||
// microkernel fire <event> [--data <json>]
|
||||
// microkernel status
|
||||
//
|
||||
// microkernel register <manifest-json>
|
||||
// microkernel unregister <name>
|
||||
// microkernel resolve <capability>
|
||||
// microkernel fire <event> [--data <json>]
|
||||
// microkernel status
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -38,9 +39,9 @@ type PluginEntry struct {
|
||||
}
|
||||
|
||||
type Registry struct {
|
||||
Plugins map[string]PluginEntry `json:"plugins"`
|
||||
Capabilities map[string][]string `json:"capabilities"`
|
||||
Hooks map[string][]string `json:"hooks"`
|
||||
Plugins map[string]PluginEntry `json:"plugins"`
|
||||
Capabilities map[string][]string `json:"capabilities"`
|
||||
Hooks map[string][]string `json:"hooks"`
|
||||
}
|
||||
|
||||
func registryPath() string {
|
||||
@@ -58,7 +59,9 @@ func loadRegistry() Registry {
|
||||
Hooks: make(map[string][]string),
|
||||
}
|
||||
data, err := os.ReadFile(registryPath())
|
||||
if err != nil { return r }
|
||||
if err != nil {
|
||||
return r
|
||||
}
|
||||
json.Unmarshal(data, &r)
|
||||
return r
|
||||
}
|
||||
@@ -93,9 +96,9 @@ func main() {
|
||||
}
|
||||
manifest.AutoActivate = true
|
||||
registry.Plugins[manifest.Name] = PluginEntry{
|
||||
Manifest: manifest,
|
||||
Manifest: manifest,
|
||||
RegisteredAt: time.Now().UTC().Format("2006-01-02T15:04:05Z"),
|
||||
Active: manifest.AutoActivate,
|
||||
Active: manifest.AutoActivate,
|
||||
}
|
||||
for _, cap := range manifest.Capabilities {
|
||||
registry.Capabilities[cap] = append(registry.Capabilities[cap], manifest.Name)
|
||||
@@ -110,21 +113,31 @@ func main() {
|
||||
fmt.Println(string(out))
|
||||
|
||||
case "unregister":
|
||||
if len(args) < 1 { fmt.Fprintln(os.Stderr, "usage: microkernel unregister <name>"); os.Exit(1) }
|
||||
if len(args) < 1 {
|
||||
fmt.Fprintln(os.Stderr, "usage: microkernel unregister <name>")
|
||||
os.Exit(1)
|
||||
}
|
||||
name := args[0]
|
||||
entry, ok := registry.Plugins[name]
|
||||
if !ok { fmt.Printf(`{"error": "plugin '%s' not registered"}`+"\n", name); return }
|
||||
if !ok {
|
||||
fmt.Printf(`{"error": "plugin '%s' not registered"}`+"\n", name)
|
||||
return
|
||||
}
|
||||
for cap, providers := range registry.Capabilities {
|
||||
var filtered []string
|
||||
for _, p := range providers {
|
||||
if p != name { filtered = append(filtered, p) }
|
||||
if p != name {
|
||||
filtered = append(filtered, p)
|
||||
}
|
||||
}
|
||||
registry.Capabilities[cap] = filtered
|
||||
}
|
||||
for event, listeners := range registry.Hooks {
|
||||
var filtered []string
|
||||
for _, l := range listeners {
|
||||
if l != name { filtered = append(filtered, l) }
|
||||
if l != name {
|
||||
filtered = append(filtered, l)
|
||||
}
|
||||
}
|
||||
registry.Hooks[event] = filtered
|
||||
}
|
||||
@@ -134,7 +147,10 @@ func main() {
|
||||
fmt.Printf(`{"status": "unregistered", "name": "%s"}`+"\n", name)
|
||||
|
||||
case "resolve":
|
||||
if len(args) < 1 { fmt.Fprintln(os.Stderr, "usage: microkernel resolve <capability>"); os.Exit(1) }
|
||||
if len(args) < 1 {
|
||||
fmt.Fprintln(os.Stderr, "usage: microkernel resolve <capability>")
|
||||
os.Exit(1)
|
||||
}
|
||||
providers, ok := registry.Capabilities[args[0]]
|
||||
if !ok || len(providers) == 0 {
|
||||
fmt.Println("null")
|
||||
@@ -160,9 +176,14 @@ func main() {
|
||||
case "fire":
|
||||
event := ""
|
||||
data := ""
|
||||
if len(args) > 0 { event = args[0] }
|
||||
if len(args) > 0 {
|
||||
event = args[0]
|
||||
}
|
||||
for i := 1; i < len(args); i++ {
|
||||
if args[i] == "--data" && i+1 < len(args) { data = args[i+1]; i++ }
|
||||
if args[i] == "--data" && i+1 < len(args) {
|
||||
data = args[i+1]
|
||||
i++
|
||||
}
|
||||
}
|
||||
listeners, ok := registry.Hooks[event]
|
||||
if !ok || len(listeners) == 0 {
|
||||
@@ -179,11 +200,13 @@ func main() {
|
||||
|
||||
case "status":
|
||||
plugins := make([]string, 0)
|
||||
for name := range registry.Plugins { plugins = append(plugins, name) }
|
||||
for name := range registry.Plugins {
|
||||
plugins = append(plugins, name)
|
||||
}
|
||||
out, _ := json.MarshalIndent(map[string]interface{}{
|
||||
"plugins": plugins,
|
||||
"plugins": plugins,
|
||||
"capabilities_count": len(registry.Capabilities),
|
||||
"hooks_count": len(registry.Hooks),
|
||||
"hooks_count": len(registry.Hooks),
|
||||
}, "", " ")
|
||||
fmt.Println(string(out))
|
||||
|
||||
|
||||
Binary file not shown.
@@ -6,10 +6,10 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"bytes"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -30,8 +30,8 @@ type ChatMessage struct {
|
||||
}
|
||||
|
||||
type ModelInfo struct {
|
||||
Name string `json:"name"`
|
||||
ContextLen int `json:"context_length"`
|
||||
Name string `json:"name"`
|
||||
ContextLen int `json:"context_length"`
|
||||
Capabilities []string `json:"capabilities"`
|
||||
}
|
||||
|
||||
@@ -50,10 +50,14 @@ func (o *OllamaProvider) Chat(model string, messages []ChatMessage, options map[
|
||||
body := map[string]interface{}{
|
||||
"model": model, "messages": messages, "stream": false,
|
||||
}
|
||||
for k, v := range options { body[k] = v }
|
||||
for k, v := range options {
|
||||
body[k] = v
|
||||
}
|
||||
data, _ := json.Marshal(body)
|
||||
resp, err := o.Client.Post(o.BaseURL+"/api/chat", "application/json", bytes.NewReader(data))
|
||||
if err != nil { return ChatMessage{}, fmt.Errorf("ollama chat: %w", err) }
|
||||
if err != nil {
|
||||
return ChatMessage{}, fmt.Errorf("ollama chat: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var result struct {
|
||||
Message ChatMessage `json:"message"`
|
||||
@@ -65,42 +69,50 @@ func (o *OllamaProvider) Chat(model string, messages []ChatMessage, options map[
|
||||
func (o *OllamaProvider) Show(model string) (ModelInfo, error) {
|
||||
data, _ := json.Marshal(map[string]string{"name": model})
|
||||
resp, err := o.Client.Post(o.BaseURL+"/api/show", "application/json", bytes.NewReader(data))
|
||||
if err != nil { return ModelInfo{}, err }
|
||||
if err != nil {
|
||||
return ModelInfo{}, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var result struct {
|
||||
Details struct {
|
||||
ParameterSize string `json:"parameter_size"`
|
||||
Family string `json:"family"`
|
||||
ParameterSize string `json:"parameter_size"`
|
||||
Family string `json:"family"`
|
||||
} `json:"details"`
|
||||
ContextLength int `json:"context_length"`
|
||||
}
|
||||
json.NewDecoder(resp.Body).Decode(&result)
|
||||
return ModelInfo{
|
||||
Name: model,
|
||||
ContextLen: result.ContextLength,
|
||||
Name: model,
|
||||
ContextLen: result.ContextLength,
|
||||
Capabilities: []string{result.Details.Family, result.Details.ParameterSize},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (o *OllamaProvider) ListModels() ([]string, error) {
|
||||
resp, err := o.Client.Get(o.BaseURL + "/api/tags")
|
||||
if err != nil { return nil, err }
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var result struct {
|
||||
Models []struct { Name string `json:"name"` } `json:"models"`
|
||||
Models []struct {
|
||||
Name string `json:"name"`
|
||||
} `json:"models"`
|
||||
}
|
||||
json.NewDecoder(resp.Body).Decode(&result)
|
||||
var names []string
|
||||
for _, m := range result.Models { names = append(names, m.Name) }
|
||||
for _, m := range result.Models {
|
||||
names = append(names, m.Name)
|
||||
}
|
||||
return names, nil
|
||||
}
|
||||
|
||||
// ── Model routing ──
|
||||
|
||||
type ModelProfile struct {
|
||||
Name string `json:"name"`
|
||||
Provider string `json:"provider"`
|
||||
ContextLen int `json:"context_length"`
|
||||
Name string `json:"name"`
|
||||
Provider string `json:"provider"`
|
||||
ContextLen int `json:"context_length"`
|
||||
Capabilities []string `json:"capabilities"`
|
||||
}
|
||||
|
||||
@@ -127,11 +139,11 @@ func (r *ModelRouter) Route(taskDescription string) string {
|
||||
// ── Provider budget ──
|
||||
|
||||
type BudgetEntry struct {
|
||||
Provider string `json:"provider"`
|
||||
MonthlyUsed float64 `json:"monthly_used"`
|
||||
Provider string `json:"provider"`
|
||||
MonthlyUsed float64 `json:"monthly_used"`
|
||||
MonthlyLimit float64 `json:"monthly_limit"`
|
||||
TokensUsed int64 `json:"tokens_used"`
|
||||
TokenLimit int64 `json:"token_limit"`
|
||||
TokensUsed int64 `json:"tokens_used"`
|
||||
TokenLimit int64 `json:"token_limit"`
|
||||
}
|
||||
|
||||
type ProviderBudget struct {
|
||||
@@ -142,7 +154,9 @@ type ProviderBudget struct {
|
||||
func (b *ProviderBudget) Record(provider string, tokens int64) {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
if b.Providers == nil { b.Providers = make(map[string]BudgetEntry) }
|
||||
if b.Providers == nil {
|
||||
b.Providers = make(map[string]BudgetEntry)
|
||||
}
|
||||
e := b.Providers[provider]
|
||||
e.TokensUsed += tokens
|
||||
b.Providers[provider] = e
|
||||
@@ -155,7 +169,9 @@ func main() {
|
||||
}
|
||||
|
||||
baseURL := os.Getenv("OLLAMA_URL")
|
||||
if baseURL == "" { baseURL = "http://localhost:11434" }
|
||||
if baseURL == "" {
|
||||
baseURL = "http://localhost:11434"
|
||||
}
|
||||
provider := NewOllamaProvider(baseURL)
|
||||
|
||||
cmd := os.Args[1]
|
||||
@@ -163,29 +179,47 @@ func main() {
|
||||
|
||||
switch cmd {
|
||||
case "chat":
|
||||
if len(args) < 2 { fmt.Fprintln(os.Stderr, "usage: model-connect chat <model> <message>"); os.Exit(1) }
|
||||
if len(args) < 2 {
|
||||
fmt.Fprintln(os.Stderr, "usage: model-connect chat <model> <message>")
|
||||
os.Exit(1)
|
||||
}
|
||||
model, message := args[0], args[1]
|
||||
messages := []ChatMessage{{Role: "user", Content: message}}
|
||||
resp, err := provider.Chat(model, messages, nil)
|
||||
if err != nil { fmt.Fprintf(os.Stderr, "error: %v\n", err); os.Exit(1) }
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
out, _ := json.MarshalIndent(resp, "", " ")
|
||||
fmt.Println(string(out))
|
||||
|
||||
case "list":
|
||||
models, err := provider.ListModels()
|
||||
if err != nil { fmt.Fprintf(os.Stderr, "error: %v\n", err); os.Exit(1) }
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
out, _ := json.MarshalIndent(models, "", " ")
|
||||
fmt.Println(string(out))
|
||||
|
||||
case "show":
|
||||
if len(args) < 1 { fmt.Fprintln(os.Stderr, "usage: model-connect show <model>"); os.Exit(1) }
|
||||
if len(args) < 1 {
|
||||
fmt.Fprintln(os.Stderr, "usage: model-connect show <model>")
|
||||
os.Exit(1)
|
||||
}
|
||||
info, err := provider.Show(args[0])
|
||||
if err != nil { fmt.Fprintf(os.Stderr, "error: %v\n", err); os.Exit(1) }
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
out, _ := json.MarshalIndent(info, "", " ")
|
||||
fmt.Println(string(out))
|
||||
|
||||
case "route":
|
||||
if len(args) < 1 { fmt.Fprintln(os.Stderr, "usage: model-connect route <task>"); os.Exit(1) }
|
||||
if len(args) < 1 {
|
||||
fmt.Fprintln(os.Stderr, "usage: model-connect route <task>")
|
||||
os.Exit(1)
|
||||
}
|
||||
router := &ModelRouter{
|
||||
Profiles: map[string]ModelProfile{
|
||||
"qwen2.5:3b": {Name: "qwen2.5:3b", Provider: "ollama", ContextLen: 32768, Capabilities: []string{"local", "fast", "small"}},
|
||||
|
||||
Binary file not shown.
@@ -3,10 +3,11 @@
|
||||
// phase progression, and iteration counts for the sleep-time cycle.
|
||||
//
|
||||
// Usage:
|
||||
// night-mode start [hours]
|
||||
// night-mode stop
|
||||
// night-mode status
|
||||
// night-mode report
|
||||
//
|
||||
// night-mode start [hours]
|
||||
// night-mode stop
|
||||
// night-mode status
|
||||
// night-mode report
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -45,7 +46,9 @@ func loadState() NightModeState {
|
||||
state.Decisions = []string{}
|
||||
state.Errors = []string{}
|
||||
data, err := os.ReadFile(statePath())
|
||||
if err != nil { return state }
|
||||
if err != nil {
|
||||
return state
|
||||
}
|
||||
json.Unmarshal(data, &state)
|
||||
return state
|
||||
}
|
||||
@@ -72,7 +75,9 @@ func main() {
|
||||
return
|
||||
}
|
||||
hours := 8.0
|
||||
if len(args) > 0 { fmt.Sscanf(args[0], "%f", &hours) }
|
||||
if len(args) > 0 {
|
||||
fmt.Sscanf(args[0], "%f", &hours)
|
||||
}
|
||||
state.Active = true
|
||||
state.StartedAt = time.Now().UTC().Format(time.RFC3339)
|
||||
state.DurationHrs = hours
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -23,7 +23,10 @@ func main() {
|
||||
switch cmd {
|
||||
case "route":
|
||||
// adaptive_router: task-class routing
|
||||
if len(args) < 1 { fmt.Fprintln(os.Stderr, "usage: route <description>"); os.Exit(1) }
|
||||
if len(args) < 1 {
|
||||
fmt.Fprintln(os.Stderr, "usage: route <description>")
|
||||
os.Exit(1)
|
||||
}
|
||||
lower := strings.ToLower(args[0])
|
||||
route := "LOCAL_FIX"
|
||||
if strings.Contains(lower, "architect") || strings.Contains(lower, "design") || strings.Contains(lower, "wave") {
|
||||
|
||||
Binary file not shown.
+35
-21
@@ -4,8 +4,9 @@
|
||||
// tool calls, and returns a response.
|
||||
//
|
||||
// Usage:
|
||||
// operator respond --model <model> --message <text>
|
||||
// operator status
|
||||
//
|
||||
// operator respond --model <model> --message <text>
|
||||
// operator status
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -13,9 +14,9 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
// ── Types ──
|
||||
@@ -26,12 +27,12 @@ type ChatMessage struct {
|
||||
}
|
||||
|
||||
type OperatorResponse struct {
|
||||
Success bool `json:"success"`
|
||||
Response string `json:"response,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
Model string `json:"model,omitempty"`
|
||||
ToolCalls int `json:"tool_calls,omitempty"`
|
||||
Duration string `json:"duration,omitempty"`
|
||||
Success bool `json:"success"`
|
||||
Response string `json:"response,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
Model string `json:"model,omitempty"`
|
||||
ToolCalls int `json:"tool_calls,omitempty"`
|
||||
Duration string `json:"duration,omitempty"`
|
||||
}
|
||||
|
||||
// ── Ollama transport ──
|
||||
@@ -98,11 +99,11 @@ func runTurn(transport *OllamaTransport, messages []ChatMessage, maxIterations i
|
||||
}
|
||||
// No tool call — this is the final response
|
||||
return OperatorResponse{
|
||||
Success: true,
|
||||
Response: resp.Content,
|
||||
Model: transport.Model,
|
||||
Success: true,
|
||||
Response: resp.Content,
|
||||
Model: transport.Model,
|
||||
ToolCalls: toolCalls,
|
||||
Duration: time.Since(start).Round(time.Millisecond).String(),
|
||||
Duration: time.Since(start).Round(time.Millisecond).String(),
|
||||
}
|
||||
}
|
||||
return OperatorResponse{Success: false, Error: "max iterations exceeded", Model: transport.Model}
|
||||
@@ -142,18 +143,31 @@ func main() {
|
||||
}
|
||||
|
||||
baseURL := os.Getenv("OLLAMA_URL")
|
||||
if baseURL == "" { baseURL = "http://localhost:11434" }
|
||||
if baseURL == "" {
|
||||
baseURL = "http://localhost:11434"
|
||||
}
|
||||
model := os.Getenv("MIMO_MODEL")
|
||||
if model == "" { model = "qwen2.5:3b" }
|
||||
if model == "" {
|
||||
model = "qwen2.5:3b"
|
||||
}
|
||||
|
||||
switch os.Args[1] {
|
||||
case "respond":
|
||||
message := ""
|
||||
for i := 2; i < len(os.Args); i++ {
|
||||
if os.Args[i] == "--message" && i+1 < len(os.Args) { message = os.Args[i+1]; i++ }
|
||||
if os.Args[i] == "--model" && i+1 < len(os.Args) { model = os.Args[i+1]; i++ }
|
||||
if os.Args[i] == "--message" && i+1 < len(os.Args) {
|
||||
message = os.Args[i+1]
|
||||
i++
|
||||
}
|
||||
if os.Args[i] == "--model" && i+1 < len(os.Args) {
|
||||
model = os.Args[i+1]
|
||||
i++
|
||||
}
|
||||
}
|
||||
if message == "" {
|
||||
fmt.Fprintln(os.Stderr, "usage: operator respond --message <text>")
|
||||
os.Exit(1)
|
||||
}
|
||||
if message == "" { fmt.Fprintln(os.Stderr, "usage: operator respond --message <text>"); os.Exit(1) }
|
||||
transport := NewOllamaTransport(baseURL, model)
|
||||
messages := []ChatMessage{{Role: "user", Content: message}}
|
||||
resp := runTurn(transport, messages, 10)
|
||||
@@ -162,9 +176,9 @@ func main() {
|
||||
|
||||
case "status":
|
||||
out, _ := json.MarshalIndent(map[string]interface{}{
|
||||
"runtime": "go",
|
||||
"model": model,
|
||||
"ollama_url": baseURL,
|
||||
"runtime": "go",
|
||||
"model": model,
|
||||
"ollama_url": baseURL,
|
||||
"goroutines_enabled": true,
|
||||
}, "", " ")
|
||||
fmt.Println(string(out))
|
||||
|
||||
Binary file not shown.
@@ -4,8 +4,9 @@
|
||||
// designed for.
|
||||
//
|
||||
// Usage:
|
||||
// parallel-processor run <command1> <command2> ... (shell commands in parallel)
|
||||
// parallel-processor status
|
||||
//
|
||||
// parallel-processor run <command1> <command2> ... (shell commands in parallel)
|
||||
// parallel-processor status
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -104,7 +105,7 @@ func main() {
|
||||
|
||||
case "status":
|
||||
out, _ := json.MarshalIndent(map[string]interface{}{
|
||||
"runtime": "go",
|
||||
"runtime": "go",
|
||||
"goroutines_enabled": true,
|
||||
}, "", " ")
|
||||
fmt.Println(string(out))
|
||||
|
||||
Binary file not shown.
+12
-11
@@ -3,8 +3,9 @@
|
||||
// on a request deterministically from its stored rules.
|
||||
//
|
||||
// Usage:
|
||||
// persona-gate decide <text> [--rules <rules-json>]
|
||||
// persona-gate load-rules
|
||||
//
|
||||
// persona-gate decide <text> [--rules <rules-json>]
|
||||
// persona-gate load-rules
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -15,20 +16,20 @@ import (
|
||||
)
|
||||
|
||||
type Rule struct {
|
||||
Text string `json:"text"`
|
||||
Topic string `json:"topic"`
|
||||
Priority int `json:"priority"`
|
||||
Restrictive bool `json:"restrictive"`
|
||||
UserRule bool `json:"user_rule"`
|
||||
Text string `json:"text"`
|
||||
Topic string `json:"topic"`
|
||||
Priority int `json:"priority"`
|
||||
Restrictive bool `json:"restrictive"`
|
||||
UserRule bool `json:"user_rule"`
|
||||
Words map[string]bool `json:"words"`
|
||||
Core string `json:"core"`
|
||||
Core string `json:"core"`
|
||||
}
|
||||
|
||||
type GateVerdict struct {
|
||||
Allowed bool `json:"allowed"`
|
||||
Citations []string `json:"citations"`
|
||||
Allowed bool `json:"allowed"`
|
||||
Citations []string `json:"citations"`
|
||||
RulesEngaged []string `json:"rules_engaged"`
|
||||
Mode string `json:"mode"`
|
||||
Mode string `json:"mode"`
|
||||
}
|
||||
|
||||
func lex(text string) map[string]bool {
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -3,8 +3,9 @@
|
||||
// mid→inject_and_verify, low→deep_research.
|
||||
//
|
||||
// Usage:
|
||||
// recall-router route <query> <json_scored_or_empty>
|
||||
// recall-router commands
|
||||
//
|
||||
// recall-router route <query> <json_scored_or_empty>
|
||||
// recall-router commands
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -76,8 +77,12 @@ func main() {
|
||||
switch os.Args[1] {
|
||||
case "route":
|
||||
query, scored := "", ""
|
||||
if len(os.Args) > 2 { query = os.Args[2] }
|
||||
if len(os.Args) > 3 { scored = os.Args[3] }
|
||||
if len(os.Args) > 2 {
|
||||
query = os.Args[2]
|
||||
}
|
||||
if len(os.Args) > 3 {
|
||||
scored = os.Args[3]
|
||||
}
|
||||
out, _ := json.MarshalIndent(route(query, scored), "", " ")
|
||||
fmt.Println(string(out))
|
||||
|
||||
|
||||
Loaded 100 of 272 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user