Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
6ed0e91
feat(evals): bind held-out qualification to reviewed evidence
ScriptedAlchemy Nov 8, 2026
9dfcb8f
test(evals): reject altered resumed qualification
ScriptedAlchemy Nov 8, 2026
bce313b
fix(evals): keep held-out results out of exploratory scoring
ScriptedAlchemy Nov 8, 2026
470cbb0
fix(evals): require pinned server identity for qualification
ScriptedAlchemy Nov 8, 2026
09915be
fix(evals): bind qualification to original rendered requests
ScriptedAlchemy Nov 8, 2026
c7b6e0e
fix(evals): gate qualification on faithful measured inputs
ScriptedAlchemy Nov 8, 2026
711a18f
fix(evals): bind qualification split groups to frozen cases
ScriptedAlchemy Nov 8, 2026
d656712
fix(evals): fail qualification commands on rejected evidence
ScriptedAlchemy Nov 8, 2026
1259eef
fix(evals): freeze client calibration before held-out inference
ScriptedAlchemy Nov 8, 2026
1e27a2c
fix(evals): refuse non-prefix resumed inference rows
ScriptedAlchemy Nov 8, 2026
f449431
fix(evals): freeze held-out HTTP evaluation arm
ScriptedAlchemy Nov 8, 2026
3f1b332
fix(evals): freeze and verify held-out HTTP request byte limits
ScriptedAlchemy Nov 8, 2026
990a88b
test(evals): exercise measured request sizes on HTTP resume
ScriptedAlchemy Nov 8, 2026
ed80f99
fix(evals): require consistent server-reported model evidence
ScriptedAlchemy Nov 8, 2026
2f7a39d
fix(evals): require measured token budgets for qualification
ScriptedAlchemy Nov 8, 2026
24f004a
fix(evals): preserve native Kev truncation evidence
ScriptedAlchemy Nov 8, 2026
c5efdd5
fix(evals): refuse cross-model frozen qualification resumes
ScriptedAlchemy Nov 8, 2026
23cd79c
fix(evals): reject ambiguous frozen JSON evidence
ScriptedAlchemy Nov 8, 2026
a45f85a
fix(evals): require source counters for zero-truncation qualification
ScriptedAlchemy Nov 8, 2026
0b7ead4
fix(evals): refuse unknown model identity before held-out inference
ScriptedAlchemy Nov 8, 2026
d858813
feat(evals): score frozen nonhistorical Noul vocabularies
ScriptedAlchemy Nov 8, 2026
6ca5cfa
fix(evals): freeze Noul question order before held-out scoring
ScriptedAlchemy Nov 8, 2026
d5527ca
fix(evals): precommit frozen held-out request bodies
ScriptedAlchemy Nov 8, 2026
eb09b12
fix(evals): keep Scout HTTP evidence private on disk
ScriptedAlchemy Nov 8, 2026
8af44e0
feat(evals): replay frozen Scout HTTP request bytes
ScriptedAlchemy Nov 8, 2026
331fc44
fix(evals): validate resumed and frozen qualification evidence
ScriptedAlchemy Nov 8, 2026
51cad35
fix(evals): preserve frozen question order in Scout replay
ScriptedAlchemy Nov 8, 2026
6ea4efe
fix(evals): report captured question order drift precisely
ScriptedAlchemy Nov 8, 2026
a35cfe6
fix(evals): bound remote decision response memory
ScriptedAlchemy Nov 8, 2026
ff0838b
test(evals): assert the actual partial header newline
ScriptedAlchemy Nov 8, 2026
7923a3a
test(evals): assert bounded read on entered HTTP stream
ScriptedAlchemy Nov 8, 2026
12445c6
fix(evals): match captured Scout question wire order
ScriptedAlchemy Nov 8, 2026
846b01a
fix(evals): reject contradictory Kev token usage evidence
ScriptedAlchemy Nov 8, 2026
77556fa
fix(evals): reject non-Noul fields in qualified evidence
ScriptedAlchemy Nov 9, 2026
86b95c2
fix(evals): freeze and enforce qualified HTTP timeouts
ScriptedAlchemy Nov 9, 2026
398ca9d
fix(evals): reject noncanonical captured Scout HTTP JSON
ScriptedAlchemy Nov 9, 2026
0eb8139
fix(evals): require Scout HTTP 200 for qualification evidence
ScriptedAlchemy Nov 9, 2026
ff68b4a
fix(evals): bind captured Scout state to runtime UTF-8 byte budget
ScriptedAlchemy Nov 9, 2026
42cd1af
test(evals): align captured Scout state-cap fixture arguments
ScriptedAlchemy Nov 9, 2026
29d9a29
fix(evals): enforce Scout HTTP endpoint in captured qualification
ScriptedAlchemy Nov 9, 2026
f6cde28
test(evals): verify the frozen request deadline during truncation
ScriptedAlchemy Nov 9, 2026
79fd6aa
feat(scout): retain decisions without delivered hints
ScriptedAlchemy Nov 9, 2026
e9498a2
refactor(scout): group decision receipt evidence
ScriptedAlchemy Nov 9, 2026
c83989e
docs(evals): identify live Scout negative decision receipts
ScriptedAlchemy Nov 9, 2026
49993c9
fix(scout): keep full prediction and latency in decision receipts
ScriptedAlchemy Nov 9, 2026
c4f38a1
style(scout): format decision receipt and latency checks
ScriptedAlchemy Nov 9, 2026
0020f71
fix(scout): bound production decision response reads
ScriptedAlchemy Nov 9, 2026
b0af164
style(scout): format bounded response fixture
ScriptedAlchemy Nov 9, 2026
eabbdc6
fix(scout): remove redundant response test import
ScriptedAlchemy Nov 9, 2026
da6e317
feat(scout): record prepared HTTP request commitments
ScriptedAlchemy Nov 9, 2026
6d1822a
fix(evals): bind qualified raw samples to their experiment
ScriptedAlchemy Nov 9, 2026
05cde28
fix(evals): bind qualified raw response to resumed evidence
ScriptedAlchemy Nov 9, 2026
c77ca78
Merge master into feat/4535-frozen-heldout-qualification-20261107
ScriptedAlchemy Nov 9, 2026
a685c63
style(scout): format prepared request commitment checks
ScriptedAlchemy Nov 9, 2026
ef48b85
fix(evals): reject impossible normalized state-token counts
ScriptedAlchemy Nov 9, 2026
d3dff1e
fix(evals): recheck first Scout sample before qualified publication
ScriptedAlchemy Nov 9, 2026
7043cd7
fix(evals): refuse swapped qualified first-response samples
ScriptedAlchemy Nov 9, 2026
2deaf01
docs(evals): clarify no-follow sample admission
ScriptedAlchemy Nov 9, 2026
2179bfe
fix(evals): cross-check qualified raw samples in independent scoring
ScriptedAlchemy Nov 9, 2026
bde1edd
test(evals): exercise qualified raw-sample CLI refusal and recovery
ScriptedAlchemy Nov 9, 2026
7190726
fix(evals): bound raw Scout qualification sample admission
ScriptedAlchemy Nov 9, 2026
2ccfd40
fix(evals): bound resumed HTTP evidence reads
ScriptedAlchemy Nov 9, 2026
66b9f10
fix(scout): retain original window and state bounds in receipts
ScriptedAlchemy Nov 9, 2026
9be5347
style(scout): format receipt bounds assertion
ScriptedAlchemy Nov 9, 2026
b85961c
fix(scout): preserve precise window selection time in receipts
ScriptedAlchemy Nov 9, 2026
b92b46a
fix(evals): measure single-pick heldout recall and repair rustfmt
ScriptedAlchemy Nov 9, 2026
07a7ff4
fix(evals): align frozen selection gates with Scout rounding
ScriptedAlchemy Nov 9, 2026
e5dc30b
fix(evals): select tied hints in frozen question order
ScriptedAlchemy Nov 9, 2026
c1adb1f
fix(evals): freeze Scout margins in captured qualification
ScriptedAlchemy Nov 9, 2026
79c3aae
fix(evals): forbid client calibration in captured Scout parity
ScriptedAlchemy Nov 9, 2026
6ac8e82
fix(evals): verify raw Scout samples against scored evidence
ScriptedAlchemy Nov 9, 2026
0a7381c
fix(scout): require typed Choice picks at the probability maximum
ScriptedAlchemy Nov 9, 2026
642015a
fix(scout): reject duplicate keys in decision HTTP answers
ScriptedAlchemy Nov 10, 2026
1a3058c
style(scout): align strict decoder with Rust formatting
ScriptedAlchemy Nov 10, 2026
ab5acd6
fix(scout): refuse unrequested decision response answers
ScriptedAlchemy Nov 10, 2026
74bb6b8
fix(scout): align qualified evidence with actual HTTP bytes
ScriptedAlchemy Nov 10, 2026
65d9828
fix(evals): reject in-flight substitution of raw qualification sample
ScriptedAlchemy Nov 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

406 changes: 394 additions & 12 deletions crates/tracedecay-agent-hosts/src/agents/context_scout/intent_pass.rs

Large diffs are not rendered by default.

32 changes: 32 additions & 0 deletions evals/intent_engine/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ py_test(
size = "small",
srcs = [
"common.py",
"qualification.py",
"run_http.py",
"score.py",
"test_run_http.py",
Expand All @@ -13,11 +14,42 @@ py_test(
main = "test_run_http.py",
)

py_test(
name = "qualification_test",
size = "small",
srcs = [
"common.py",
"qualification.py",
"run_http.py",
"score.py",
"test_qualification.py",
],
imports = ["."],
main = "test_qualification.py",
)

py_test(
name = "qualification_cli_test",
size = "small",
srcs = [
"common.py",
"qualification.py",
"run_http.py",
"score.py",
"test_qualification.py",
"test_qualification_cli.py",
],
imports = ["."],
main = "test_qualification_cli.py",
)

py_test(
name = "scoring_test",
size = "small",
srcs = [
"common.py",
"qualification.py",
"run_http.py",
"compare.py",
"score.py",
"test_score.py",
Expand Down
364 changes: 362 additions & 2 deletions evals/intent_engine/HTTP_RUNNER.md

Large diffs are not rendered by default.

681 changes: 681 additions & 0 deletions evals/intent_engine/qualification.py

Large diffs are not rendered by default.

561 changes: 527 additions & 34 deletions evals/intent_engine/run_http.py

Large diffs are not rendered by default.

79 changes: 60 additions & 19 deletions evals/intent_engine/score.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ def publish_summary(path, summary):
os.unlink(staged)


def validate_rows(rows, config):
def validate_rows(rows, config, cats=CATS):
"""Refuse invalid measured evidence before any threshold or AUC arithmetic."""
if not isinstance(rows, list) or not rows:
raise ValueError("configuration %r has no rows to score" % config)
categories = set(CATS)
categories = set(cats)
seen = set()
for row in rows:
if not isinstance(row, dict):
Expand Down Expand Up @@ -172,7 +172,7 @@ def case_metrics(rows, ths):
none_rows = [r for r in rows if not r["labels"]]
none_fh = 0
for r in rows:
fired = {c for c in CATS if r["probs"][c] >= ths[c]}
fired = {c for c in ths if r["probs"][c] >= ths[c]}
exact += fired == set(r["labels"])
passing = [(r["probs"][c] - ths[c], c) for c in fired]
pick = max(passing)[1] if passing else None
Expand Down Expand Up @@ -210,6 +210,8 @@ def lat(rows):
observed_truncated = sum(value > 0 for value in dropped)
result = {
"median_ms": round(statistics.median(ms)),
"p50_ms": statistics.median(ms),
"p95_ms": ms[math.ceil(0.95 * len(ms)) - 1],
"p90_ms": round(ms[int(0.9 * (len(ms) - 1))]),
"total_cases": len(rows),
"cases_truncated": observed_truncated if len(dropped) == len(rows) else None,
Expand All @@ -223,21 +225,21 @@ def lat(rows):
result[f"{field}_measured_cases"] = len(values)
return result

def score_config(res, fixed_t=None):
def score_config(res, fixed_t=None, cats=CATS):
if "experiment" in res:
experiment = res["experiment"]
if not isinstance(experiment, dict) or experiment.get("version") != 2:
raise ValueError("HTTP result telemetry version is unsupported; rerun with a new tag")
rows = res["rows"]
validate_rows(rows, res.get("config"))
validate_rows(rows, res.get("config"), cats)
validate_calibration(res, rows)
validate_http_provenance(res, rows)
choices = ["choice" in row for row in rows]
if any(choices) and not all(choices):
raise ValueError("configuration %r has incomplete choice coverage" % res.get("config"))
ctx = [r for r in rows if r["context_needed"]]; nctx = [r for r in rows if not r["context_needed"]]
per = {}
for c in CATS:
for c in cats:
pos = [r["probs"][c] for r in rows if c in r["labels"]]
neg = [r["probs"][c] for r in rows if c not in r["labels"]]
sweep = {str(t): prf(rows, c, t) for t in TH}
Expand All @@ -247,7 +249,7 @@ def score_config(res, fixed_t=None):
"sweep": sweep, "best_t": best, "best_f1": sweep[str(best)]["f1"]}
# A singleton has no training rows after holding out its only case.
loo_cases = len(rows) if len(rows) > 1 else 0
for c in CATS:
for c in cats:
if not loo_cases:
per[c].update(loo_f1=None, loo_p=None, loo_r=None)
continue
Expand Down Expand Up @@ -276,19 +278,19 @@ def training_f1(t):
per[c]["loo_p"] = tp / (tp + fp) if tp + fp else None
per[c]["loo_r"] = tp / (tp + fn) if tp + fn else None
out = {"per_cat": per, "loo_evaluated_cases": loo_cases,
"macro_loo_f1": statistics.mean(per[c]["loo_f1"] or 0 for c in CATS) if loo_cases else None}
for name, t in [("t0.5", {c: 0.5 for c in CATS}), ("t0.6", {c: 0.6 for c in CATS}),
("t0.7", {c: 0.7 for c in CATS}),
("best_t_in_sample", {c: per[c]["best_t"] for c in CATS})] + ([("fixed", fixed_t)] if fixed_t else []):
mf = statistics.mean(prf(rows, c, t[c])["f1"] or 0 for c in CATS)
"macro_loo_f1": statistics.mean(per[c]["loo_f1"] or 0 for c in cats) if loo_cases else None}
for name, t in [("t0.5", {c: 0.5 for c in cats}), ("t0.6", {c: 0.6 for c in cats}),
("t0.7", {c: 0.7 for c in cats}),
("best_t_in_sample", {c: per[c]["best_t"] for c in cats})] + ([("fixed", fixed_t)] if fixed_t else []):
mf = statistics.mean(prf(rows, c, t[c])["f1"] or 0 for c in cats)
out[name] = {"macro_f1": mf,
"macro_f1_ctx": statistics.mean(prf(ctx, c, t[c])["f1"] or 0 for c in CATS) if ctx else None,
"macro_f1_ctx": statistics.mean(prf(ctx, c, t[c])["f1"] or 0 for c in cats) if ctx else None,
"all": case_metrics(rows, t), "ctx": case_metrics(ctx, t), "no_ctx": case_metrics(nctx, t)}
out["macro_auc"] = mean_defined(per[c]["auc"] for c in CATS)
out["macro_auc_ctx"] = mean_defined(per[c]["auc_ctx"] for c in CATS)
out["macro_auc_n_categories"] = sum(per[c]["auc"] is not None for c in CATS)
out["macro_auc_ctx_n_categories"] = sum(per[c]["auc_ctx"] is not None for c in CATS)
out["reliability"] = reliability(rows)
out["macro_auc"] = mean_defined(per[c]["auc"] for c in cats)
out["macro_auc_ctx"] = mean_defined(per[c]["auc_ctx"] for c in cats)
out["macro_auc_n_categories"] = sum(per[c]["auc"] is not None for c in cats)
out["macro_auc_ctx_n_categories"] = sum(per[c]["auc_ctx"] is not None for c in cats)
out["reliability"] = reliability(rows, cats)
out["latency"] = lat(rows)
if all(choices):
def ch(rs):
Expand All @@ -299,15 +301,54 @@ def ch(rs):
out["choice_argmax_per_cat"] = {c: {
"tp": sum(r["choice"] == c and c in r["labels"] for r in rows),
"fp": sum(r["choice"] == c and c not in r["labels"] for r in rows),
"fn": sum(r["choice"] != c and c in r["labels"] for r in rows)} for c in CATS}
"fn": sum(r["choice"] != c and c in r["labels"] for r in rows)} for c in cats}
return out

if __name__ == "__main__":
here = os.path.dirname(os.path.abspath(__file__))
if len(sys.argv) > 1:
usage = ("usage: score.py [--qualified MANIFEST DATASET RESULT "
"[--questions-file FILE] [--captured-requests FILE] [--sample-file FILE]]")
if sys.argv[1] != "--qualified" or len(sys.argv) < 5 or len(sys.argv[5:]) % 2:
raise ValueError(usage)
options = {}
for index in range(5, len(sys.argv), 2):
flag = sys.argv[index]
if flag not in ("--questions-file", "--captured-requests", "--sample-file") or flag in options:
raise ValueError(usage)
options[flag] = sys.argv[index + 1]
from qualification import load_manifest, load_questions, loads_unambiguous_json, qualified_report
from common import noul_questions
manifest, manifest_sha = load_manifest(sys.argv[2])
with open(sys.argv[3], "rb") as source:
dataset_bytes = source.read()
with open(sys.argv[4], encoding="utf-8") as source:
result = loads_unambiguous_json(source.read())
questions = load_questions(options["--questions-file"]) if "--questions-file" in options else noul_questions()
captured_requests_bytes = None
if "--captured-requests" in options:
with open(options["--captured-requests"], "rb") as source:
captured_requests_bytes = source.read()
report = qualified_report(
result, manifest, manifest_sha, questions, dataset_bytes,
captured_requests_bytes, options.get("--sample-file"),
)
publish_summary(os.path.join(here, "qualification_summary.json"), report)
print(json.dumps(report, indent=2, allow_nan=False))
# A completed evaluation is not necessarily a qualified model. Keep
# the measurements available while failing the calling automation.
if not report["qualified_against_frozen_tolerances"]:
failed = ", ".join(name for name, passed in report["checks"].items() if not passed)
print(f"qualification failed frozen checks: {failed}", file=sys.stderr)
sys.exit(1)
sys.exit(0)
fixed = json.load(open(os.path.join(here, "fixed_thresholds.json"))) if os.path.exists(os.path.join(here, "fixed_thresholds.json")) else None
summary = {}; allres = {}
for f in sorted(glob.glob(os.path.join(here, "results", "*.json"))):
res = json.load(open(f))
if (isinstance(res.get("experiment"), dict)
and "qualification_manifest_sha256" in res["experiment"]):
raise ValueError("held-out results require --qualified MANIFEST DATASET RESULT; refuse in-sample scoring")
if res["config"] in allres:
raise ValueError("duplicate configuration %r in result files" % res["config"])
allres[res["config"]] = res
Expand Down
Loading