feat(witness): close PARTIAL gaps — dichotomy wave

This commit is contained in:
Nick committed 2026-10-13 23:22:01 +01:00
1 parent 28f5ed5ee1
commit 50039ed83b
5 files changed
+2424 -160

No files matched your search

+224
View File
@@ -0,0 +1,224 @@
#!/usr/bin/env python3
"""check_witness_parity.py — live oracle-vs-Go parity sweep for the
witness gap closure (#2479, contract factory-gap-wi).
The frozen envelope (truth.json, 58 cases) pins the JSON-native surface
byte-identically. THIS script drives the LIVE Python oracle (kernel
delegation on — the Rust cognition_core is the authority) and the Go
binary (`op` mode) across the same branches plus the seams the harness
cannot freeze: the full log round-trip with BYTE-COMPARED log files
(frozen clock → identical jsonl bytes incl. ", "/": " separators,
insertion order, ensure_ascii), read_log deque semantics against
hand-seeded blank/malformed lines, log_stats shapes incl. list-shaped
signals and skip_rate half-even rounding, the missing-file branches, and
_now under the pinned clock.
Usage: PYTHONPATH=src python3 scripts/check_witness_parity.py
[--bin src/go/witness/witness]
Exit 0 on 100% parity, 1 with the first divergence.
"""
import argparse
import json
import os
import shutil
import subprocess
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 / "scripts"))
FROZEN_NOW = "2026-10-14T09:15:00.123456+00:00"
SCRATCH_P = Path("/tmp/witness_parity_py/memory")
SCRATCH_G = Path("/tmp/witness_parity_go/memory")
os.environ["RUNE_MEMORY_DIR"] = str(SCRATCH_P)
os.environ["RUNE_FROZEN_NOW"] = FROZEN_NOW
from freeze_truth import _pin_clock # noqa: E402
import rune.witness as wt # noqa: E402
_pin_clock(wt) # module-global datetime pinned → _now() frozen both sides
IS_CORRECTION = [
"no, that's wrong, fix it",
"Actually, you misunderstood the requirement",
"that answer is wrong",
"incorrect output",
"not right either",
"please try again",
"nope",
"stop doing that",
"we already did the migration",
"that's not what I asked for", # hits via the lowercase that'?s? not (correct|right|what)
"I meant the left column", # capital-I quirk → False
"I already told you", # capital-I quirk → False
"wrongly spelled", # \b boundary → False
"looks good to me",
"",
"don't do that",
"I told you the column",
]
EXTRACT_TOPIC = [
"the deployment runner needs repair now",
"fix this now",
"",
"café résumé matters",
"the fix_it_loop crashed again",
"alpha beta gamma delta epsilon zeta eta theta",
"WHY DOES THIS matter",
"why does the memory keep drifting",
]
DETECT_SIGNALS = [
("", None, None, None),
("hello", "boom", None, None),
("hello", "é" * 250, None, None),
("hello", None, "done", None),
("hello", None, "completed", None),
("hello", None, "abandoned", None),
("hello", None, "failed", None),
("hello", None, "running", None),
("no, that's wrong, try again", None, None, None),
("i need to check that", None, None, None),
("we already did the migration", None, None, None),
("I'll send the report", None, None, None),
("anyway moving on to budgets", None, None, ["budget"]),
("the deployment runner needs repair", None, None, ["deployment", "runner"]),
("urgent! I prefer rust, but actually wait", None, None, None),
("why does the memory keep drifting", None, None, None),
]
LOG_ENTRIES = [
({"correction": True, "corrected_topic": ["budgets"]}, "user corrected the budget", "sess-1"),
({"urgency_detected": True}, "", "sess-2"),
({"correction": True}, "r2", ""),
({"learning_opportunity": True}, "noted — café", "s4"),
({"completion": "done"}, "", "s5"),
({"error": True}, "logged", "s6"),
({"error": True}, "", "s7"),
({"commitment_detected": True}, "planned", "s8"),
]
def run_go(binpath, env, req):
p = subprocess.run([str(binpath), "op"], input=json.dumps(req),
capture_output=True, text=True, env=env)
if p.returncode != 0:
raise RuntimeError(f"go op {req.get('op')} exited {p.returncode}: {p.stdout} {p.stderr}")
out = json.loads(p.stdout)
return out.get("result")
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--bin", default=str(REPO / "src/go/witness/witness"))
args = ap.parse_args()
binpath = Path(args.bin)
if not binpath.exists():
print(f"Go binary missing: {binpath} (go build -o witness . in src/go/witness)")
return 1
for scratch in (SCRATCH_P, SCRATCH_G):
base = scratch.parent
if base.exists():
shutil.rmtree(base)
scratch.mkdir(parents=True)
go_env = dict(os.environ)
go_env["RUNE_MEMORY_DIR"] = str(SCRATCH_G)
go_env["WITNESS_FROZEN_NOW"] = FROZEN_NOW
fails = total = 0
def check(name, oracle_val, go_val):
nonlocal fails, total
total += 1
# log_stats may carry INT signal_types keys (list-shaped signals —
# Python dicts accept them; json.dumps coerces to "1"/"2" on dump
# but sort_keys chokes first). Normalize the way dumps coerces.
if isinstance(oracle_val, dict) and "signal_types" in oracle_val:
oracle_val = dict(oracle_val)
oracle_val["signal_types"] = {
str(k): v for k, v in oracle_val["signal_types"].items()}
if json.dumps(oracle_val, sort_keys=True) != json.dumps(go_val, sort_keys=True):
fails += 1
print(f"DIVERGE {name}:\n oracle: {json.dumps(oracle_val)[:300]}\n"
f" go: {json.dumps(go_val)[:300]}")
# _now under the pinned clock
check("now", wt._now(), run_go(binpath, go_env, {"op": "now"}))
# is_correction — live public fn (capital-letter quirk included)
for text in IS_CORRECTION:
check(f"is_correction:{text[:30]!r}", wt.is_correction(text),
run_go(binpath, go_env, {"op": "is_correction", "text": text}))
# extract_topic — live kernel delegation
for text in EXTRACT_TOPIC:
check(f"extract_topic:{text[:30]!r}", wt.extract_topic(text),
run_go(binpath, go_env, {"op": "extract_topic", "text": text}))
# detect_signals — live kernel delegation, full dicts
for msg, te, ts, rt in DETECT_SIGNALS:
check(f"detect_signals:{msg[:30]!r}",
wt.detect_signals(msg, tool_error=te, task_status=ts, recent_topics=rt),
run_go(binpath, go_env, {"op": "detect_signals", "message": msg,
"tool_error": te, "task_status": ts,
"recent_topics": rt}))
# gather seams
check("stopwords", wt._stopwords(), run_go(binpath, go_env, {"op": "stopwords"}))
check("tables", wt._tables(), run_go(binpath, go_env, {"op": "tables"}))
# log family — read/stats on missing files
check("read_log:missing", wt.read_log(20),
run_go(binpath, go_env, {"op": "read_log", "limit": 20}))
check("log_stats:missing", wt.log_stats(), run_go(binpath, go_env, {"op": "log_stats"}))
# log_entry sequence — then BYTE-COMPARE the two jsonl files
for signals, reflection, session in LOG_ENTRIES:
wt.log_entry(signals, reflection, session)
run_go(binpath, go_env, {"op": "log_entry", "signals": signals,
"reflection": reflection, "session_id": session})
py_log = Path(wt.LOG_PATH).read_bytes()
go_log = (SCRATCH_G / "index" / "witness_log.jsonl").read_bytes()
total += 1
if py_log != go_log:
fails += 1
print(f"DIVERGE log-file-bytes:\n oracle: {py_log[:200]!r}\n go: {go_log[:200]!r}")
# read_log limits (deque semantics) + log_stats (skip_rate half-even)
for limit in (0, 1, 2, 3, 20, 99):
check(f"read_log:{limit}", wt.read_log(limit),
run_go(binpath, go_env, {"op": "read_log", "limit": limit}))
check("log_stats:full", wt.log_stats(), run_go(binpath, go_env, {"op": "log_stats"}))
# hand-seeded blank + malformed lines land on BOTH files identically
seed = b'{bad json\n\n{"signals": [1, 2, 2], "reflection": "x"}\n'
with open(wt.LOG_PATH, "ab") as f:
f.write(seed)
with open(SCRATCH_G / "index" / "witness_log.jsonl", "ab") as f:
f.write(seed)
for limit in (3, 20):
check(f"read_log:seeded:{limit}", wt.read_log(limit),
run_go(binpath, go_env, {"op": "read_log", "limit": limit}))
check("log_stats:seeded", wt.log_stats(), run_go(binpath, go_env, {"op": "log_stats"}))
# missing-file branches again after wipe
os.remove(wt.LOG_PATH)
os.remove(SCRATCH_G / "index" / "witness_log.jsonl")
check("read_log:removed", wt.read_log(5),
run_go(binpath, go_env, {"op": "read_log", "limit": 5}))
check("log_stats:removed", wt.log_stats(), run_go(binpath, go_env, {"op": "log_stats"}))
print(f"parity: {total - fails}/{total} match")
return 1 if fails else 0
if __name__ == "__main__":
sys.exit(main())
+138
View File
@@ -0,0 +1,138 @@
#!/usr/bin/env python3
"""gen_witness_truth.py — shared-harness truth battery for the witness
gap closure (#2479, contract factory-gap-wi).
Uses scripts/freeze_truth.py (the ONE truth law) to freeze
src/rune/witness.py behavior for the Go port at src/go/witness:
* _now — pinned via the envelope frozen clock (microsecond precision).
* is_correction — public pure-Python fn: patterns match AS-IS against
LOWERCASED text, so capital-letter patterns (r"\\bI (said|meant|asked)\\b",
r"\\bthat'?s? not what I\\b", ...) can never fire — the quirk is
frozen, not fixed ("that's not what I asked for" → False).
* extract_topic / detect_signals — frozen under the LIVE kernel (the
pure-Python oracle retired 2026-10-10): cc_witness_extract_topic's
ASCII \\w runs and cc_witness_signals' deterministic order
(error → correction → completion → drift → commitment →
contradiction → preference → urgency → learning), case-INSENSITIVE
family matching (kernel lowercases pattern and text — richer than
public is_correction), rune-safe [:200] error_text truncation, and
the frozen completion triple ("done"/"abandoned"/"failed" only).
* _tables / _stopwords — the gather seams as sorted-list data.
* log_entry / read_log / log_stats — the log family. LOG_PATH derives
from memory_env at import time, so RUNE_MEMORY_DIR pins it to a
scratch dir this script WIPES first; the cases themselves build the
log state in order (log_entry creates the file), keeping --verify
byte-identical. skip_rate pins Python .0f half-even rounding (37.5 →
"38%", 33.33 → "33%").
Verify (byte-identical law):
rm -rf /tmp/witness_gap_freeze && \\
RUNE_MEMORY_DIR=/tmp/witness_gap_freeze/memory PYTHONPATH=src \\
python3 scripts/freeze_truth.py --verify src/go/witness/truth.json \\
--module rune.witness
"""
import os
import shutil
import sys
from pathlib import Path
REPO = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(REPO / "src"))
sys.path.insert(0, str(Path(__file__).resolve().parent))
FROZEN_NOW = "2026-10-14T09:15:00.123456+00:00"
SCRATCH = Path("/tmp/witness_gap_freeze")
os.environ["RUNE_FROZEN_NOW"] = FROZEN_NOW
os.environ["RUNE_MEMORY_DIR"] = str(SCRATCH / "memory")
from freeze_truth import freeze, write_envelope # noqa: E402
def main():
if SCRATCH.exists():
shutil.rmtree(SCRATCH)
cases = [
# _now — frozen clock
("_now", (), {}),
# is_correction — hits across the pattern families
("is_correction", ("no, that's wrong, fix it",), {}),
("is_correction", ("Actually, you misunderstood the requirement",), {}),
("is_correction", ("that answer is wrong",), {}),
("is_correction", ("incorrect output",), {}),
("is_correction", ("not right either",), {}),
("is_correction", ("please try again",), {}),
("is_correction", ("nope",), {}),
("is_correction", ("stop doing that",), {}),
("is_correction", ("we already did the migration",), {}),
# is_correction — misses incl. the capital-letter quirk and boundaries
("is_correction", ("that's not what I asked for",), {}),
("is_correction", ("I meant the left column",), {}),
("is_correction", ("I already told you",), {}),
("is_correction", ("wrongly spelled",), {}),
("is_correction", ("looks good to me",), {}),
("is_correction", ("",), {}),
# extract_topic — kernel ASCII \w runs, stopwords, cap 5
("extract_topic", ("the deployment runner needs repair now",), {}),
("extract_topic", ("fix this now",), {}),
("extract_topic", ("",), {}),
("extract_topic", ("café résumé matters",), {}),
("extract_topic", ("the fix_it_loop crashed again",), {}),
("extract_topic", ("alpha beta gamma delta epsilon zeta eta theta",), {}),
("extract_topic", ("WHY DOES THIS matter",), {}),
# detect_signals — error family + rune-safe [:200]
("detect_signals", ("", None, None, None), {}),
("detect_signals", ("hello", "boom", None, None), {}),
("detect_signals", ("hello", "é" * 250, None, None), {}),
# completion — the frozen triple only
("detect_signals", ("hello", None, "done", None), {}),
("detect_signals", ("hello", None, "completed", None), {}),
("detect_signals", ("hello", None, "abandoned", None), {}),
("detect_signals", ("hello", None, "failed", None), {}),
("detect_signals", ("hello", None, "running", None), {}),
# correction family (kernel: case-insensitive, richer than public)
("detect_signals", ("no, that's wrong, try again", None, None, None), {}),
("detect_signals", ("i need to check that", None, None, None), {}),
("detect_signals", ("we already did the migration", None, None, None), {}),
("detect_signals", ("I'll send the report", None, None, None), {}),
# topic drift — new-topics order and the overlap ratio both ways
("detect_signals", ("anyway moving on to budgets", None, None, ["budget"]), {}),
("detect_signals", ("the deployment runner needs repair", None, None,
["deployment", "runner"]), {}),
# multi-family message — kernel family order pinned
("detect_signals", ("urgent! I prefer rust, but actually wait", None, None, None), {}),
("detect_signals", ("why does the memory keep drifting", None, None, None), {}),
# gather seams
("_tables", (), {}),
("_stopwords", (), {}),
# log family — cases build the scratch log state IN ORDER
("read_log", (3,), {}), # missing file → []
("log_stats", (), {}), # missing file → 3-key zero shape
("log_entry", ({"correction": True, "corrected_topic": ["budgets"]},
"user corrected the budget", "sess-1"), {}),
("log_entry", ({"urgency_detected": True}, "", "sess-2"), {}),
("read_log", (20,), {}),
("read_log", (1,), {}),
("log_stats", (), {}), # total 2, wr 1 → "50%"
("log_entry", ({"correction": True}, "r2", ""), {}),
("log_stats", (), {}), # total 3, wr 2 → "33%"
("read_log", (0,), {}), # deque maxlen=0 → []
("log_entry", ({"learning_opportunity": True}, "noted", "s4"), {}),
("log_entry", ({"completion": "done"}, "", "s5"), {}),
("log_entry", ({"error": True}, "logged", "s6"), {}),
("log_entry", ({"error": True}, "", "s7"), {}),
("log_entry", ({"commitment_detected": True}, "planned", "s8"), {}),
("log_stats", (), {}), # total 8, wr 5 → 37.5 → "38%" (half-even)
("read_log", (99,), {}),
]
truth = freeze("rune.witness", cases, oracle_module="rune.witness")
dest = REPO / "src/go/witness/truth.json"
write_envelope(FROZEN_NOW, truth, dest)
print(f"wrote {len(truth)} cases -> {dest}")
if __name__ == "__main__":
main()
+833 -160
View File
File diff suppressed because it is too large. Load diff
+913
View File
@@ -0,0 +1,913 @@
{
"frozen_now": "2026-10-14T09:15:00.123456+00:00",
"cases": [
{
"fn": "_now",
"args": [],
"kwargs": {},
"expected": "2026-10-14T09:15:00.123456+00:00"
},
{
"fn": "is_correction",
"args": [
"no, that's wrong, fix it"
],
"kwargs": {},
"expected": true
},
{
"fn": "is_correction",
"args": [
"Actually, you misunderstood the requirement"
],
"kwargs": {},
"expected": true
},
{
"fn": "is_correction",
"args": [
"that answer is wrong"
],
"kwargs": {},
"expected": true
},
{
"fn": "is_correction",
"args": [
"incorrect output"
],
"kwargs": {},
"expected": true
},
{
"fn": "is_correction",
"args": [
"not right either"
],
"kwargs": {},
"expected": true
},
{
"fn": "is_correction",
"args": [
"please try again"
],
"kwargs": {},
"expected": true
},
{
"fn": "is_correction",
"args": [
"nope"
],
"kwargs": {},
"expected": true
},
{
"fn": "is_correction",
"args": [
"stop doing that"
],
"kwargs": {},
"expected": true
},
{
"fn": "is_correction",
"args": [
"we already did the migration"
],
"kwargs": {},
"expected": true
},
{
"fn": "is_correction",
"args": [
"that's not what I asked for"
],
"kwargs": {},
"expected": true
},
{
"fn": "is_correction",
"args": [
"I meant the left column"
],
"kwargs": {},
"expected": false
},
{
"fn": "is_correction",
"args": [
"I already told you"
],
"kwargs": {},
"expected": false
},
{
"fn": "is_correction",
"args": [
"wrongly spelled"
],
"kwargs": {},
"expected": false
},
{
"fn": "is_correction",
"args": [
"looks good to me"
],
"kwargs": {},
"expected": false
},
{
"fn": "is_correction",
"args": [
""
],
"kwargs": {},
"expected": false
},
{
"fn": "extract_topic",
"args": [
"the deployment runner needs repair now"
],
"kwargs": {},
"expected": [
"deployment",
"runner",
"needs",
"repair"
]
},
{
"fn": "extract_topic",
"args": [
"fix this now"
],
"kwargs": {},
"expected": []
},
{
"fn": "extract_topic",
"args": [
""
],
"kwargs": {},
"expected": []
},
{
"fn": "extract_topic",
"args": [
"café résumé matters"
],
"kwargs": {},
"expected": [
"matters"
]
},
{
"fn": "extract_topic",
"args": [
"the fix_it_loop crashed again"
],
"kwargs": {},
"expected": [
"fix_it_loop",
"crashed",
"again"
]
},
{
"fn": "extract_topic",
"args": [
"alpha beta gamma delta epsilon zeta eta theta"
],
"kwargs": {},
"expected": [
"alpha",
"beta",
"gamma",
"delta",
"epsilon"
]
},
{
"fn": "extract_topic",
"args": [
"WHY DOES THIS matter"
],
"kwargs": {},
"expected": [
"matter"
]
},
{
"fn": "detect_signals",
"args": [
"",
null,
null,
null
],
"kwargs": {},
"expected": {}
},
{
"fn": "detect_signals",
"args": [
"hello",
"boom",
null,
null
],
"kwargs": {},
"expected": {
"error": true,
"error_text": "boom"
}
},
{
"fn": "detect_signals",
"args": [
"hello",
"éééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééé",
null,
null
],
"kwargs": {},
"expected": {
"error": true,
"error_text": "éééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééé"
}
},
{
"fn": "detect_signals",
"args": [
"hello",
null,
"done",
null
],
"kwargs": {},
"expected": {
"completion": "done"
}
},
{
"fn": "detect_signals",
"args": [
"hello",
null,
"completed",
null
],
"kwargs": {},
"expected": {}
},
{
"fn": "detect_signals",
"args": [
"hello",
null,
"abandoned",
null
],
"kwargs": {},
"expected": {
"completion": "abandoned"
}
},
{
"fn": "detect_signals",
"args": [
"hello",
null,
"failed",
null
],
"kwargs": {},
"expected": {
"completion": "failed"
}
},
{
"fn": "detect_signals",
"args": [
"hello",
null,
"running",
null
],
"kwargs": {},
"expected": {}
},
{
"fn": "detect_signals",
"args": [
"no, that's wrong, try again",
null,
null,
null
],
"kwargs": {},
"expected": {
"correction": true,
"corrected_topic": [
"wrong",
"again"
]
}
},
{
"fn": "detect_signals",
"args": [
"i need to check that",
null,
null,
null
],
"kwargs": {},
"expected": {
"commitment_detected": true
}
},
{
"fn": "detect_signals",
"args": [
"we already did the migration",
null,
null,
null
],
"kwargs": {},
"expected": {
"correction": true,
"corrected_topic": [
"already",
"migration"
]
}
},
{
"fn": "detect_signals",
"args": [
"I'll send the report",
null,
null,
null
],
"kwargs": {},
"expected": {
"commitment_detected": true
}
},
{
"fn": "detect_signals",
"args": [
"anyway moving on to budgets",
null,
null,
[
"budget"
]
],
"kwargs": {},
"expected": {
"topic_drift": true,
"new_topics": [
"anyway",
"moving",
"budgets"
]
}
},
{
"fn": "detect_signals",
"args": [
"the deployment runner needs repair",
null,
null,
[
"deployment",
"runner"
]
],
"kwargs": {},
"expected": {}
},
{
"fn": "detect_signals",
"args": [
"urgent! I prefer rust, but actually wait",
null,
null,
null
],
"kwargs": {},
"expected": {
"possible_contradiction": true,
"preference_detected": true,
"urgency_detected": true
}
},
{
"fn": "detect_signals",
"args": [
"why does the memory keep drifting",
null,
null,
null
],
"kwargs": {},
"expected": {
"learning_opportunity": true
}
},
{
"fn": "_tables",
"args": [],
"kwargs": {},
"expected": {
"stopwords": [
"a",
"after",
"an",
"and",
"are",
"as",
"at",
"be",
"been",
"before",
"being",
"but",
"by",
"can",
"could",
"did",
"do",
"does",
"during",
"for",
"from",
"had",
"has",
"have",
"how",
"in",
"into",
"is",
"it",
"its",
"may",
"might",
"no",
"not",
"of",
"on",
"or",
"shall",
"should",
"that",
"the",
"these",
"this",
"those",
"through",
"to",
"was",
"were",
"what",
"when",
"where",
"which",
"who",
"why",
"will",
"with",
"would"
],
"correction": [
"\\b(don'?t|do not) (do|say|use) (that|this|it)\\b",
"\\bI (already|already told you)\\b",
"\\bI (don'?t|do not) (want|need|mean)\\b",
"\\bI (said|meant|asked)\\b",
"\\bI (told|asked|mentioned) (you|this|that)\\b",
"\\bactually[,.]?\\s*(it'?s?|that'?s?|you)\\b",
"\\bfix (this|that|it)\\b",
"\\bincorrect\\b",
"\\bno[,.]?\\s*(that'?s?|that is|it'?s?|it is)\\s+(wrong|incorrect|not right)\\b",
"\\bnope\\b",
"\\bnot right\\b",
"\\bnot what I\\b",
"\\bstop (doing|saying|that)\\b",
"\\bthat'?s? (not|n'?t) (what|how|why)\\b",
"\\bthat'?s? not (correct|right|what)\\b",
"\\bthat'?s? not what I\\b",
"\\btry again\\b",
"\\bwe (already|already did)\\b",
"\\bwhy (did|are|is) (you|it|that)\\b",
"\\bwrong\\b"
],
"commitment": [
"\\bI need to\\b",
"\\bI promise\\b",
"\\bI will\\s+(send|do|fix|build|check|update|create|write)\\b",
"\\bI'?ll\\s+(send|do|fix|build|check|update|create|write)\\b",
"\\blet me\\s+(send|do|fix|build|check|update|create|write)\\b",
"\\bremind me\\b"
],
"contradiction": [
"\\b(but|however|actually|wait|that contradicts)\\b"
],
"preference": [
"\\b(always|never) (use|do|say)\\b",
"\\b(don'?t|do not) (like|want|need)\\b",
"\\bI (prefer|like|love|hate|dislike)\\b",
"\\bI'?m (not )?(a fan of|into|interested in)\\b"
],
"urgency": [
"\\b(deadline|due|overdue|late)\\b",
"\\b(important|critical|crucial|vital)\\b",
"\\b(urgent|asap|immediately|now|quick|hurry)\\b"
],
"learning": [
"\\b(I don'?t (understand|know|get))\\b",
"\\b(how (does|do|did)|why (does|do|did)|what (is|are|was|were))\\b",
"\\b(learn|understand|explain|teach|show me)\\b"
]
}
},
{
"fn": "_stopwords",
"args": [],
"kwargs": {},
"expected": [
"a",
"after",
"an",
"and",
"are",
"as",
"at",
"be",
"been",
"before",
"being",
"but",
"by",
"can",
"could",
"did",
"do",
"does",
"during",
"for",
"from",
"had",
"has",
"have",
"how",
"in",
"into",
"is",
"it",
"its",
"may",
"might",
"no",
"not",
"of",
"on",
"or",
"shall",
"should",
"that",
"the",
"these",
"this",
"those",
"through",
"to",
"was",
"were",
"what",
"when",
"where",
"which",
"who",
"why",
"will",
"with",
"would"
]
},
{
"fn": "read_log",
"args": [
3
],
"kwargs": {},
"expected": []
},
{
"fn": "log_stats",
"args": [],
"kwargs": {},
"expected": {
"total": 0,
"with_reflection": 0,
"signal_types": {}
}
},
{
"fn": "log_entry",
"args": [
{
"correction": true,
"corrected_topic": [
"budgets"
]
},
"user corrected the budget",
"sess-1"
],
"kwargs": {},
"expected": null
},
{
"fn": "log_entry",
"args": [
{
"urgency_detected": true
},
"",
"sess-2"
],
"kwargs": {},
"expected": null
},
{
"fn": "read_log",
"args": [
20
],
"kwargs": {},
"expected": [
{
"timestamp": "2026-10-14T09:15:00.123456+00:00",
"session_id": "sess-1",
"signals": {
"correction": true,
"corrected_topic": [
"budgets"
]
},
"reflection": "user corrected the budget",
"signal_count": 2
},
{
"timestamp": "2026-10-14T09:15:00.123456+00:00",
"session_id": "sess-2",
"signals": {
"urgency_detected": true
},
"reflection": "",
"signal_count": 1
}
]
},
{
"fn": "read_log",
"args": [
1
],
"kwargs": {},
"expected": [
{
"timestamp": "2026-10-14T09:15:00.123456+00:00",
"session_id": "sess-2",
"signals": {
"urgency_detected": true
},
"reflection": "",
"signal_count": 1
}
]
},
{
"fn": "log_stats",
"args": [],
"kwargs": {},
"expected": {
"total": 2,
"with_reflection": 1,
"skip_rate": "50%",
"signal_types": {
"correction": 1,
"corrected_topic": 1,
"urgency_detected": 1
}
}
},
{
"fn": "log_entry",
"args": [
{
"correction": true
},
"r2",
""
],
"kwargs": {},
"expected": null
},
{
"fn": "log_stats",
"args": [],
"kwargs": {},
"expected": {
"total": 3,
"with_reflection": 2,
"skip_rate": "33%",
"signal_types": {
"correction": 2,
"corrected_topic": 1,
"urgency_detected": 1
}
}
},
{
"fn": "read_log",
"args": [
0
],
"kwargs": {},
"expected": []
},
{
"fn": "log_entry",
"args": [
{
"learning_opportunity": true
},
"noted",
"s4"
],
"kwargs": {},
"expected": null
},
{
"fn": "log_entry",
"args": [
{
"completion": "done"
},
"",
"s5"
],
"kwargs": {},
"expected": null
},
{
"fn": "log_entry",
"args": [
{
"error": true
},
"logged",
"s6"
],
"kwargs": {},
"expected": null
},
{
"fn": "log_entry",
"args": [
{
"error": true
},
"",
"s7"
],
"kwargs": {},
"expected": null
},
{
"fn": "log_entry",
"args": [
{
"commitment_detected": true
},
"planned",
"s8"
],
"kwargs": {},
"expected": null
},
{
"fn": "log_stats",
"args": [],
"kwargs": {},
"expected": {
"total": 8,
"with_reflection": 5,
"skip_rate": "38%",
"signal_types": {
"correction": 2,
"corrected_topic": 1,
"urgency_detected": 1,
"learning_opportunity": 1,
"completion": 1,
"error": 2,
"commitment_detected": 1
}
}
},
{
"fn": "read_log",
"args": [
99
],
"kwargs": {},
"expected": [
{
"timestamp": "2026-10-14T09:15:00.123456+00:00",
"session_id": "sess-1",
"signals": {
"correction": true,
"corrected_topic": [
"budgets"
]
},
"reflection": "user corrected the budget",
"signal_count": 2
},
{
"timestamp": "2026-10-14T09:15:00.123456+00:00",
"session_id": "sess-2",
"signals": {
"urgency_detected": true
},
"reflection": "",
"signal_count": 1
},
{
"timestamp": "2026-10-14T09:15:00.123456+00:00",
"session_id": "",
"signals": {
"correction": true
},
"reflection": "r2",
"signal_count": 1
},
{
"timestamp": "2026-10-14T09:15:00.123456+00:00",
"session_id": "s4",
"signals": {
"learning_opportunity": true
},
"reflection": "noted",
"signal_count": 1
},
{
"timestamp": "2026-10-14T09:15:00.123456+00:00",
"session_id": "s5",
"signals": {
"completion": "done"
},
"reflection": "",
"signal_count": 1
},
{
"timestamp": "2026-10-14T09:15:00.123456+00:00",
"session_id": "s6",
"signals": {
"error": true
},
"reflection": "logged",
"signal_count": 1
},
{
"timestamp": "2026-10-14T09:15:00.123456+00:00",
"session_id": "s7",
"signals": {
"error": true
},
"reflection": "",
"signal_count": 1
},
{
"timestamp": "2026-10-14T09:15:00.123456+00:00",
"session_id": "s8",
"signals": {
"commitment_detected": true
},
"reflection": "planned",
"signal_count": 1
}
]
}
]
}
+316
View File
@@ -0,0 +1,316 @@
package main
// truth_test.go — the parity gate. Replays every case from truth.json
// (the shared freeze_truth.py envelope: frozen clock + 58 Python-oracle
// cases WITH inputs, generated by scripts/gen_witness_truth.py) VERBATIM
// through the ported Go functions: _now, is_correction (capital-letter
// quirk), extract_topic + detect_signals (kernel parity: ASCII \w runs,
// deterministic signal order, case-insensitive families, rune-safe
// truncation, frozen completion triple), _tables/_stopwords gather seams,
// and the log family (log_entry / read_log / log_stats with skip_rate
// half-even rounding). The log cases build the scratch log state IN
// ORDER against an isolated log-path seam — replays are order-sensitive
// by design, mirroring the oracle's append-only log. A divergence fails
// the port, not the oracle.
import (
_ "embed"
"encoding/json"
"os"
"path/filepath"
"reflect"
"regexp"
"strings"
"testing"
)
//go:embed truth.json
var frozenTruthJSON string
type frozenCase struct {
Fn string `json:"fn"`
Args []any `json:"args"`
Kwargs map[string]any `json:"kwargs"`
Expected json.RawMessage `json:"expected"`
}
// canonical — render through the Python-parity serializer, then decode, so
// both sides compare as decoded data (Python None and Go nil → null).
func canonical(t *testing.T, v any) any {
t.Helper()
var out any
if err := json.Unmarshal([]byte(pyDumps(v)), &out); err != nil {
t.Fatalf("decode actual: %v", err)
}
return out
}
func argStr(v any) string {
s, _ := v.(string)
return s
}
func argInt(t *testing.T, v any) int {
t.Helper()
f, ok := v.(float64)
if !ok {
t.Fatalf("expected number arg, got %v (%T)", v, v)
}
return int(f)
}
func argStrSlice(t *testing.T, v any) []string {
t.Helper()
if v == nil {
return []string{} // the facade converts None → recent_topics or []
}
l, ok := v.([]any)
if !ok {
t.Fatalf("expected list arg, got %v (%T)", v, v)
}
out := []string{}
for _, e := range l {
out = append(out, argStr(e))
}
return out
}
func argOrd(t *testing.T, v any) *ordObj {
t.Helper()
raw, err := json.Marshal(v)
if err != nil {
t.Fatalf("marshal signals arg: %v", err)
}
o, err := decodeOrdObj(string(raw))
if err != nil {
t.Fatalf("decode signals arg: %v", err)
}
return o
}
func TestFrozenTruthReplay(t *testing.T) {
var env struct {
FrozenNow string `json:"frozen_now"`
Cases []frozenCase `json:"cases"`
}
if err := json.Unmarshal([]byte(frozenTruthJSON), &env); err != nil {
t.Fatalf("decode truth envelope: %v", err)
}
if len(env.Cases) == 0 {
t.Fatal("no truth cases — the parity gate is empty")
}
if env.FrozenNow != "2026-10-14T09:15:00.123456+00:00" {
t.Fatalf("frozen clock moved: %q", env.FrozenNow)
}
savedClock := clock
clock = func() string { return env.FrozenNow }
t.Cleanup(func() { clock = savedClock })
dir := t.TempDir()
savedLog := logPathFunc
logPathFunc = func() string { return filepath.Join(dir, "witness_log.jsonl") }
t.Cleanup(func() { logPathFunc = savedLog })
for i, c := range env.Cases {
var actual any
switch c.Fn {
case "_now":
actual = now()
case "is_correction":
actual = isCorrection(argStr(c.Args[0]))
case "extract_topic":
actual = extractTopic(argStr(c.Args[0]))
case "detect_signals":
actual = detectSignals(argStr(c.Args[0]), argStr(c.Args[1]),
argStr(c.Args[2]), argStrSlice(t, c.Args[3]))
case "_tables":
actual = tables()
case "_stopwords":
actual = stopwordsSorted()
case "log_entry":
if err := logEntry(argOrd(t, c.Args[0]), argStr(c.Args[1]), argStr(c.Args[2])); err != nil {
t.Fatalf("case %d (log_entry): %v", i, err)
}
actual = nil
case "read_log":
actual = readLog(argInt(t, c.Args[0]))
case "log_stats":
actual = logStats()
default:
t.Fatalf("case %d: unknown fn %q", i, c.Fn)
}
var want any
if err := json.Unmarshal(c.Expected, &want); err != nil {
t.Fatalf("case %d (%s): decode expected: %v", i, c.Fn, err)
}
if got := canonical(t, actual); !reflect.DeepEqual(got, want) {
t.Errorf("case %d (%s): got %v want %v", i, c.Fn, got, want)
}
}
}
// TestLogEntryLineBytes — the JSONL line is byte-identical to Python
// json.dumps: ", "/": " separators, insertion order (timestamp,
// session_id, signals, reflection, signal_count), ensure_ascii.
func TestLogEntryLineBytes(t *testing.T) {
clockSave := clock
clock = func() string { return "2026-10-14T09:15:00.123456+00:00" }
defer func() { clock = clockSave }()
dir := t.TempDir()
p := filepath.Join(dir, "witness_log.jsonl")
logSave := logPathFunc
logPathFunc = func() string { return p }
defer func() { logPathFunc = logSave }()
sig := newOrd()
sig.Set("correction", true)
sig.Set("corrected_topic", []string{"budgets"})
if err := logEntry(sig, "user corrected the budget", "sess-1"); err != nil {
t.Fatalf("logEntry: %v", err)
}
want := `{"timestamp": "2026-10-14T09:15:00.123456+00:00", "session_id": "sess-1", "signals": {"correction": true, "corrected_topic": ["budgets"]}, "reflection": "user corrected the budget", "signal_count": 2}` + "\n"
raw, err := os.ReadFile(p)
if err != nil {
t.Fatalf("read log: %v", err)
}
if string(raw) != want {
t.Errorf("log line bytes:\n got %q\nwant %q", string(raw), want)
}
}
// TestReadLogSeams — deque semantics (limit counts LINES), blank-line and
// malformed-line skipping, trailing line without newline.
func TestReadLogSeams(t *testing.T) {
dir := t.TempDir()
p := filepath.Join(dir, "witness_log.jsonl")
save := logPathFunc
logPathFunc = func() string { return p }
defer func() { logPathFunc = save }()
content := `{"a": 1}` + "\n" +
`{bad json` + "\n" +
"\n" +
`{"b": 2}` + "\n" +
`{"c": 3}` // no trailing newline
if err := os.WriteFile(p, []byte(content), 0o644); err != nil {
t.Fatal(err)
}
// 5 lines total (a, bad, blank, b, c) — limit counts LINES:
// limit 10 → all 3 valid; limit 2 → last two lines (b, c) → 2 valid;
// limit 3 → blank, b, c → 2 valid.
if got := readLog(10); len(got) != 3 {
t.Errorf("limit 10: got %d entries, want 3", len(got))
}
if got := readLog(2); len(got) != 2 {
t.Errorf("limit 2: got %d entries, want 2", len(got))
}
if got := readLog(3); len(got) != 2 {
t.Errorf("limit 3 (blank line consumed): got %d entries, want 2", len(got))
}
if got := readLog(0); len(got) != 0 {
t.Errorf("limit 0: got %d entries, want 0", len(got))
}
// missing file → []
os.Remove(p)
if got := readLog(5); len(got) != 0 {
t.Errorf("missing file: got %d entries, want 0", len(got))
}
}
// TestLogStatsSeams — list-shaped signals counting, missing-file shape
// (no skip_rate key), skip_rate half-even pins.
func TestLogStatsSeams(t *testing.T) {
dir := t.TempDir()
p := filepath.Join(dir, "witness_log.jsonl")
save := logPathFunc
logPathFunc = func() string { return p }
defer func() { logPathFunc = save }()
// signals as a LIST: elements become signal_types keys (oracle parity)
line1 := `{"signals": [1, 2, 2], "reflection": ""}` + "\n"
line2 := `{"signals": {"x": true}, "reflection": "kept"}` + "\n"
if err := os.WriteFile(p, []byte(line1+line2), 0o644); err != nil {
t.Fatal(err)
}
s := logStats()
if got := s.GetInt("total"); got != 2 {
t.Errorf("total: %d", got)
}
if got := s.GetInt("with_reflection"); got != 1 {
t.Errorf("with_reflection: %d", got)
}
st, _ := s.Get("signal_types")
sto := st.(*ordObj)
for k, want := range map[string]int{"1": 1, "2": 2, "x": 1} {
if got := sto.GetInt(k); got != want {
t.Errorf("signal_types[%q]: got %d want %d", k, got, want)
}
}
// skip_rate half-even — Python f"{x:.0f}%" on the exact binary double
for _, tc := range []struct {
total, wr int
want string
}{
{1, 1, "0%"}, {2, 1, "50%"}, {3, 2, "33%"},
{8, 5, "38%"}, // 37.5 → half-even → 38
{8, 3, "62%"}, // 62.5 → half-even → 62
{8, 7, "12%"}, // 12.5 → half-even → 12
} {
if got := skipRate(tc.total, tc.wr); got != tc.want {
t.Errorf("skipRate(%d,%d): got %s want %s", tc.total, tc.wr, got, tc.want)
}
}
// missing file: 3-key zero shape, NO skip_rate key
os.Remove(p)
s2 := logStats()
if _, has := s2.Get("skip_rate"); has {
t.Error("missing-file shape must not carry skip_rate")
}
if s2.GetInt("total") != 0 || s2.GetInt("with_reflection") != 0 {
t.Errorf("missing-file zeros: %+v", s2)
}
if st, _ := s2.Get("signal_types"); st.(*ordObj).Len() != 0 {
t.Errorf("missing-file signal_types: %+v", st)
}
}
// TestErrorTextRuneTruncation — Python str[:200] / kernel chars().take(200)
// are char-based; multibyte error text must not split a rune.
func TestErrorTextRuneTruncation(t *testing.T) {
sig := detectSignals("hello", strings.Repeat("é", 250), "", nil)
v, _ := sig.Get("error_text")
s, ok := v.(string)
if !ok {
t.Fatalf("error_text: %v", v)
}
if len([]rune(s)) != 200 {
t.Errorf("rune length: %d", len([]rune(s)))
}
}
// TestLiveNowShape — the live clock renders datetime.now(timezone.utc)
// .isoformat() shape: microseconds always 6 digits when present, +00:00.
func TestLiveNowShape(t *testing.T) {
re := regexp.MustCompile(`^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{6})?\+00:00$`)
if !re.MatchString(liveNow()) {
t.Errorf("liveNow shape: %q", liveNow())
}
}
// TestNewTopicsOrder — drift's new_topics keep EXTRACTION order (the
// kernel's cur-set iteration), never map order.
func TestNewTopicsOrder(t *testing.T) {
sig := detectSignals("anyway moving on to budgets", "", "", []string{"budget"})
v, _ := sig.Get("new_topics")
got, _ := v.([]string)
want := []string{"anyway", "moving", "budgets"}
if len(got) != len(want) {
t.Fatalf("new_topics: %v", got)
}
for i := range want {
if got[i] != want[i] {
t.Errorf("new_topics[%d]: got %q want %q", i, got[i], want[i])
}
}
}