* chore(repo): land the retrieval contract and drop root cruft Lands work that had been sitting uncommitted in the working tree: - docs/architecture/retrieval_contracts.md plus its executable form, tests/benchmarks/retrieval_contract.py and test_retrieval_contract.py, now registered in meson as retrieval_contract_unit (suite: policy). .gitignore keeps them tracked beneath the tests/benchmarks/*.py ignore. - AGENTS.md: the Development Memory section, and the product default recorded as hybrid_assist + shadow with topology-off as the control, which is what search_topology_stage already does. docs/benchmarks/index.md gains the matching search_shadow_cost plan. - .gitleaks.toml, and a Secret scan workflow that actually runs it on push and pull requests (nothing in .github invoked gitleaks before). Deletes three root files with zero references anywhere in the tree: bench_profile.json (ad-hoc profiling leftover), CppProperties.json (Visual Studio config; the repo standardises on Meson), and spdlog.pc (a hand-copied spdlog 1.12 pkg-config with prefix=/usr/local that would mislead any PKG_CONFIG_PATH lookup; spdlog comes from Conan). docs/plans/ (two p2p designs already shipped in5a53cdfaandd012d95b) was ingested into YAMS with task=desloppification source=plan, hydrated via yams cat and byte-compared before removal, per the new AGENTS.md rule; tests/scripts/analyze_simeon_adaptivity.py no longer points at a plans directory. Also reflows two stale line anchors in raw_environment_allowlist.txt for tools/yams-cli/main.cpp (shifted by11c39157) so the policy suite passes again; no new environment writes. Not included: the external/yams-repo submodule pointer in the working tree was a stale local clone two commits behind, not an intended bump. Signed-off-by: trvon <me@trevon.dev> * test(policy): make the configuration-authority ratchet bidirectional check_configuration_authority.py only reported keys and TOML readers present in code but missing from the allowlists, so the reviewed YAMS_* surface could grow but never shrink: production_environment_keys.txt even said removals "may remain grandfathered". An allowlist that only accretes stops describing the code. The policy now also fails on allowlist entries with no remaining occurrence in src/, include/, or tools/, naming the lines to delete. Key removals in later commits therefore have to update the allowlist in the same change, and the count in the success banner becomes a real measure of the environment surface (432 today). stale_entries() exposes the two stale sets for tooling; three unit tests cover stale key, stale reader, and the returned names. Signed-off-by: trvon <me@trevon.dev> * tools(policy): report where every configuration key is referenced Adds tests/scripts/report_config_key_usage.py: for each YAMS_* key in production_environment_keys.txt and each dotted TOML key literally resolved in ConfigResolver.cpp, count whole-token references per tree bucket (src, test, bench, docs, plugin; build output and the allowlists themselves excluded) and assign a verdict: KEEP when anything outside product code exercises or documents the key, CANDIDATE when only product code mentions it, NOT_IN_SRC when the allowlist is stale. The report is evidence, not a decision. From here on a key deletion must paste its CANDIDATE rows into the change description; the first audit pass hand-listed keys that turned out to have bench and test consumers, which is exactly the mistake a mechanical inventory avoids. Today: 432 env keys (215 CANDIDATE, 217 KEEP), 117 TOML keys (103 CANDIDATE, 14 KEEP). Registered as config_key_usage_report_unit. The report counts only files git tracks (git ls-files) when the root is a work tree, so an ignored local bench harness cannot turn a CANDIDATE into a KEEP on one machine and not another; outside a work tree it falls back to scanning the directory, which is what the synthetic-tree test exercises. Signed-off-by: trvon <me@trevon.dev> --------- Signed-off-by: trvon <me@trevon.dev>
403 lines
14 KiB
Python
Executable file
403 lines
14 KiB
Python
Executable file
#!/usr/bin/env python3
|
||
"""Phase 0 report for the simeon adaptivity matrix.
|
||
|
||
Reads JSONL cells produced by `run_simeon_adaptivity_matrix.sh` and emits:
|
||
* Per-corpus pivot tables: backend x alpha vs MRR / Recall@10 / nDCG@10.
|
||
* Fusion-contribution summary (dense_contrib vs bm25_contrib).
|
||
* Promotion-bar verdicts for the candidate levers defined in the plan
|
||
(plans live in YAMS with task/source=plan metadata, per AGENTS.md).
|
||
|
||
Usage (with uv):
|
||
uv run tests/scripts/analyze_simeon_adaptivity.py \
|
||
--matrix-dir bench_results/simeon_adaptivity/<run-id> \
|
||
--output build/benchmarks/simeon_adaptivity_report.md
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import argparse
|
||
import json
|
||
import math
|
||
import statistics
|
||
from dataclasses import dataclass, field
|
||
from pathlib import Path
|
||
from typing import Any, Iterable
|
||
|
||
|
||
@dataclass
|
||
class Cell:
|
||
backend: str
|
||
alpha: float
|
||
corpus: str
|
||
path: Path
|
||
rows: list[dict[str, Any]] = field(default_factory=list)
|
||
|
||
@property
|
||
def successful(self) -> list[dict[str, Any]]:
|
||
return [r for r in self.rows if _is_success(r)]
|
||
|
||
|
||
def _is_success(row: dict[str, Any]) -> bool:
|
||
if isinstance(row.get("success"), bool):
|
||
return bool(row.get("success"))
|
||
return str(row.get("status", "")).strip().lower() == "ok"
|
||
|
||
|
||
def _get(row: dict[str, Any], path: Iterable[str], default: float = 0.0) -> float:
|
||
cur: Any = row
|
||
for key in path:
|
||
if not isinstance(cur, dict) or key not in cur:
|
||
return default
|
||
cur = cur[key]
|
||
try:
|
||
val = float(cur)
|
||
if math.isnan(val) or math.isinf(val):
|
||
return default
|
||
return val
|
||
except (TypeError, ValueError):
|
||
return default
|
||
|
||
|
||
def _mean(values: list[float]) -> float:
|
||
return statistics.fmean(values) if values else 0.0
|
||
|
||
|
||
def _parse_cell_path(matrix_dir: Path, jsonl_path: Path) -> tuple[str, float, str] | None:
|
||
try:
|
||
rel = jsonl_path.relative_to(matrix_dir)
|
||
except ValueError:
|
||
return None
|
||
parts = rel.parts
|
||
if len(parts) < 4 or parts[-1] != "bench.jsonl":
|
||
return None
|
||
backend = parts[0]
|
||
alpha_seg = parts[1]
|
||
corpus = parts[2]
|
||
if not alpha_seg.startswith("alpha_"):
|
||
return None
|
||
try:
|
||
alpha = float(alpha_seg.removeprefix("alpha_"))
|
||
except ValueError:
|
||
return None
|
||
return backend, alpha, corpus
|
||
|
||
|
||
def load_cells(matrix_dir: Path) -> list[Cell]:
|
||
cells: list[Cell] = []
|
||
for jsonl in sorted(matrix_dir.rglob("bench.jsonl")):
|
||
parsed = _parse_cell_path(matrix_dir, jsonl)
|
||
if not parsed:
|
||
continue
|
||
backend, alpha, corpus = parsed
|
||
rows: list[dict[str, Any]] = []
|
||
with jsonl.open("r", encoding="utf-8") as handle:
|
||
for line_no, line in enumerate(handle, start=1):
|
||
line = line.strip()
|
||
if not line:
|
||
continue
|
||
try:
|
||
rows.append(json.loads(line))
|
||
except json.JSONDecodeError as exc:
|
||
print(
|
||
f"warn: {jsonl}:{line_no}: invalid JSON ({exc}); skipping",
|
||
flush=True,
|
||
)
|
||
cells.append(Cell(backend=backend, alpha=alpha, corpus=corpus, path=jsonl, rows=rows))
|
||
return cells
|
||
|
||
|
||
def summarize_cell(cell: Cell) -> dict[str, Any]:
|
||
rows = cell.successful
|
||
return {
|
||
"backend": cell.backend,
|
||
"alpha": cell.alpha,
|
||
"corpus": cell.corpus,
|
||
"path": str(cell.path),
|
||
"n_rows": len(cell.rows),
|
||
"n_success": len(rows),
|
||
"hybrid_mrr": _mean([_get(r, ["hybrid", "mrr"]) for r in rows]),
|
||
"hybrid_recall_at_k": _mean(
|
||
[_get(r, ["hybrid", "recall_at_k"]) for r in rows]
|
||
),
|
||
"hybrid_ndcg_at_k": _mean([_get(r, ["hybrid", "ndcg_at_k"]) for r in rows]),
|
||
"keyword_mrr": _mean([_get(r, ["keyword", "mrr"]) for r in rows]),
|
||
"keyword_recall_at_k": _mean(
|
||
[_get(r, ["keyword", "recall_at_k"]) for r in rows]
|
||
),
|
||
"fusion_alpha": _mean([_get(r, ["fusion_alpha"]) for r in rows]),
|
||
"dense_contrib": _mean([_get(r, ["dense_contrib"]) for r in rows]),
|
||
"bm25_contrib": _mean([_get(r, ["bm25_contrib"]) for r in rows]),
|
||
"other_contrib": _mean([_get(r, ["other_contrib"]) for r in rows]),
|
||
}
|
||
|
||
|
||
def pivot_by_corpus(
|
||
summaries: list[dict[str, Any]],
|
||
) -> dict[str, dict[tuple[str, float], dict[str, Any]]]:
|
||
pivot: dict[str, dict[tuple[str, float], dict[str, Any]]] = {}
|
||
for s in summaries:
|
||
pivot.setdefault(s["corpus"], {})[(s["backend"], s["alpha"])] = s
|
||
return pivot
|
||
|
||
|
||
def _fmt(val: float, digits: int = 4) -> str:
|
||
return "n/a" if val is None else f"{val:.{digits}f}"
|
||
|
||
|
||
def render_pivot_tables(
|
||
pivot: dict[str, dict[tuple[str, float], dict[str, Any]]],
|
||
) -> list[str]:
|
||
lines: list[str] = []
|
||
for corpus in sorted(pivot.keys()):
|
||
lines.append(f"### corpus = `{corpus}`")
|
||
lines.append("")
|
||
lines.append(
|
||
"| backend | α (vector_weight) | rows | MRR (hybrid) | Recall@10 (hybrid) | nDCG@10 (hybrid) | dense_contrib | bm25_contrib |"
|
||
)
|
||
lines.append(
|
||
"| --- | --- | --- | --- | --- | --- | --- | --- |"
|
||
)
|
||
cells = pivot[corpus]
|
||
for key in sorted(cells.keys(), key=lambda k: (k[0], k[1])):
|
||
s = cells[key]
|
||
lines.append(
|
||
"| {backend} | {alpha} | {n} | {mrr} | {recall} | {ndcg} | {dense} | {bm25} |".format(
|
||
backend=s["backend"],
|
||
alpha=_fmt(s["alpha"], 2),
|
||
n=s["n_success"],
|
||
mrr=_fmt(s["hybrid_mrr"]),
|
||
recall=_fmt(s["hybrid_recall_at_k"]),
|
||
ndcg=_fmt(s["hybrid_ndcg_at_k"]),
|
||
dense=_fmt(s["dense_contrib"], 3),
|
||
bm25=_fmt(s["bm25_contrib"], 3),
|
||
)
|
||
)
|
||
lines.append("")
|
||
return lines
|
||
|
||
|
||
def evaluate_p0_hybrid_alpha(
|
||
pivot: dict[str, dict[tuple[str, float], dict[str, Any]]],
|
||
) -> tuple[bool, list[str]]:
|
||
"""P0: some α ≠ default improves MRR by ≥ +0.03 on at least one corpus
|
||
without regressing the others > 0.01."""
|
||
notes: list[str] = []
|
||
improvements: list[tuple[str, float, float, float]] = []
|
||
regressions: list[tuple[str, float, float, float]] = []
|
||
|
||
for corpus, cells in pivot.items():
|
||
simeon_cells = {
|
||
alpha: s for (backend, alpha), s in cells.items() if backend == "simeon"
|
||
}
|
||
if not simeon_cells:
|
||
continue
|
||
baseline_alpha = min(simeon_cells.keys(), key=lambda a: abs(a - 0.5))
|
||
baseline_mrr = simeon_cells[baseline_alpha]["hybrid_mrr"]
|
||
for alpha, s in simeon_cells.items():
|
||
if alpha == baseline_alpha:
|
||
continue
|
||
delta = s["hybrid_mrr"] - baseline_mrr
|
||
if delta >= 0.03:
|
||
improvements.append((corpus, alpha, delta, s["hybrid_mrr"]))
|
||
elif delta <= -0.01:
|
||
regressions.append((corpus, alpha, delta, s["hybrid_mrr"]))
|
||
|
||
if not improvements:
|
||
notes.append("No α sweep improved hybrid MRR by ≥ +0.03 on any corpus.")
|
||
return False, notes
|
||
|
||
winning_alphas = {alpha for _, alpha, _, _ in improvements}
|
||
disqualifying = [r for r in regressions if r[1] in winning_alphas]
|
||
|
||
for corpus, alpha, delta, mrr in improvements:
|
||
notes.append(
|
||
f"`{corpus}` α={alpha:.2f}: +{delta:.3f} MRR vs. α≈0.5 baseline (MRR={mrr:.4f})"
|
||
)
|
||
for corpus, alpha, delta, mrr in disqualifying:
|
||
notes.append(
|
||
f"regression: `{corpus}` α={alpha:.2f}: Δ={delta:.3f} MRR (MRR={mrr:.4f})"
|
||
)
|
||
|
||
passed = bool(improvements) and not disqualifying
|
||
return passed, notes
|
||
|
||
|
||
def evaluate_p1_idf(
|
||
summaries: list[dict[str, Any]],
|
||
) -> tuple[bool, list[str]]:
|
||
"""P1 IDF bar: simeon's dense_contrib is consistently swamped, i.e.,
|
||
bm25_contrib >> dense_contrib on prose corpora (proxy for head-term
|
||
dominance). We cannot compute per-token energy from the current JSONL;
|
||
flag this as a proxy check and note explicit follow-up."""
|
||
notes: list[str] = []
|
||
simeon = [s for s in summaries if s["backend"] == "simeon"]
|
||
if not simeon:
|
||
notes.append("No simeon cells — cannot evaluate P1.")
|
||
return False, notes
|
||
|
||
suspicious = [
|
||
s
|
||
for s in simeon
|
||
if s["dense_contrib"] < 0.1 and s["bm25_contrib"] > 0.8
|
||
]
|
||
for s in suspicious:
|
||
notes.append(
|
||
f"`{s['corpus']}` α={s['alpha']:.2f}: dense_contrib={s['dense_contrib']:.3f} "
|
||
f"bm25_contrib={s['bm25_contrib']:.3f} — simeon not contributing to fused rank"
|
||
)
|
||
if not suspicious:
|
||
notes.append(
|
||
"No cells show dense_contrib<0.1 with bm25_contrib>0.8; head-term dominance "
|
||
"proxy not triggered."
|
||
)
|
||
return False, notes
|
||
|
||
notes.append(
|
||
"Proxy triggered. Full P1 bar (top-5 tokens >30% of sketch energy) requires "
|
||
"per-token energy emission — follow-up item."
|
||
)
|
||
return True, notes
|
||
|
||
|
||
def evaluate_p2_intent(summaries: list[dict[str, Any]]) -> tuple[bool, list[str]]:
|
||
"""P2: needs `query_intent_bucket` column; skip until present."""
|
||
return False, [
|
||
"Skipped: `query_intent_bucket` not yet emitted by bench JSONL; see Phase 0.1 follow-up.",
|
||
]
|
||
|
||
|
||
def evaluate_p3_rerank(cells: list[Cell]) -> tuple[bool, list[str]]:
|
||
"""P3: deferred by plan."""
|
||
return False, [
|
||
"Deferred: P3 learned reranker waits on P0/P1 results and RelevanceLabelStore volume."
|
||
]
|
||
|
||
|
||
def render_promotion_bars(
|
||
pivot: dict[str, dict[tuple[str, float], dict[str, Any]]],
|
||
summaries: list[dict[str, Any]],
|
||
cells: list[Cell],
|
||
) -> list[str]:
|
||
lines: list[str] = []
|
||
lines.append("## Promotion bars")
|
||
lines.append("")
|
||
lines.append("| Lever | Verdict | Notes |")
|
||
lines.append("| --- | --- | --- |")
|
||
|
||
passed_p0, notes_p0 = evaluate_p0_hybrid_alpha(pivot)
|
||
passed_p1, notes_p1 = evaluate_p1_idf(summaries)
|
||
passed_p2, notes_p2 = evaluate_p2_intent(summaries)
|
||
passed_p3, notes_p3 = evaluate_p3_rerank(cells)
|
||
|
||
def _verdict(ok: bool) -> str:
|
||
return "PASS" if ok else "FAIL"
|
||
|
||
def _fold_notes(xs: list[str]) -> str:
|
||
return "<br>".join(xs) if xs else "—"
|
||
|
||
lines.append(
|
||
f"| P0 hybrid α | {_verdict(passed_p0)} | {_fold_notes(notes_p0)} |"
|
||
)
|
||
lines.append(f"| P1 IDF | {_verdict(passed_p1)} | {_fold_notes(notes_p1)} |")
|
||
lines.append(f"| P2 query-intent n-gram | SKIP | {_fold_notes(notes_p2)} |")
|
||
lines.append(f"| P3 learned rerank | SKIP | {_fold_notes(notes_p3)} |")
|
||
lines.append("")
|
||
return lines
|
||
|
||
|
||
def build_report(matrix_dir: Path, cells: list[Cell]) -> str:
|
||
summaries = [summarize_cell(c) for c in cells]
|
||
pivot = pivot_by_corpus(summaries)
|
||
|
||
skipped_log = matrix_dir / "skipped_cells.log"
|
||
skipped_lines: list[str] = []
|
||
if skipped_log.exists():
|
||
skipped_lines = [
|
||
ln.strip() for ln in skipped_log.read_text().splitlines() if ln.strip()
|
||
]
|
||
|
||
manifest_path = matrix_dir / "matrix_manifest.env"
|
||
manifest_kv: dict[str, str] = {}
|
||
if manifest_path.exists():
|
||
for line in manifest_path.read_text().splitlines():
|
||
if "=" in line:
|
||
k, v = line.split("=", 1)
|
||
manifest_kv[k.strip()] = v.strip()
|
||
|
||
lines: list[str] = []
|
||
lines.append("# Simeon adaptivity — Phase 0 report")
|
||
lines.append("")
|
||
lines.append(f"Matrix dir: `{matrix_dir}`")
|
||
lines.append("")
|
||
if manifest_kv:
|
||
lines.append("## Manifest")
|
||
lines.append("")
|
||
for k in sorted(manifest_kv.keys()):
|
||
lines.append(f"- `{k}` = `{manifest_kv[k]}`")
|
||
lines.append("")
|
||
lines.append("## Pivot tables")
|
||
lines.append("")
|
||
lines.extend(render_pivot_tables(pivot))
|
||
lines.extend(render_promotion_bars(pivot, summaries, cells))
|
||
if skipped_lines:
|
||
lines.append("## Skipped cells")
|
||
lines.append("")
|
||
for ln in skipped_lines:
|
||
lines.append(f"- {ln}")
|
||
lines.append("")
|
||
lines.append("## Raw cell summaries")
|
||
lines.append("")
|
||
lines.append("```json")
|
||
lines.append(json.dumps(summaries, indent=2, sort_keys=True))
|
||
lines.append("```")
|
||
lines.append("")
|
||
return "\n".join(lines)
|
||
|
||
|
||
def main() -> int:
|
||
parser = argparse.ArgumentParser(description=__doc__)
|
||
parser.add_argument(
|
||
"--matrix-dir",
|
||
required=True,
|
||
help="Directory produced by run_simeon_adaptivity_matrix.sh",
|
||
)
|
||
parser.add_argument(
|
||
"--output",
|
||
required=True,
|
||
help="Destination Markdown report path",
|
||
)
|
||
parser.add_argument(
|
||
"--json-out",
|
||
default=None,
|
||
help="Optional JSON summary dump (cell-level summaries)",
|
||
)
|
||
args = parser.parse_args()
|
||
|
||
matrix_dir = Path(args.matrix_dir).resolve()
|
||
output = Path(args.output).resolve()
|
||
if not matrix_dir.is_dir():
|
||
print(f"error: matrix dir not found: {matrix_dir}", flush=True)
|
||
return 1
|
||
|
||
cells = load_cells(matrix_dir)
|
||
if not cells:
|
||
print(f"error: no bench.jsonl cells found under {matrix_dir}", flush=True)
|
||
return 2
|
||
|
||
report = build_report(matrix_dir, cells)
|
||
output.parent.mkdir(parents=True, exist_ok=True)
|
||
output.write_text(report, encoding="utf-8")
|
||
print(f"wrote report: {output}")
|
||
|
||
if args.json_out:
|
||
json_path = Path(args.json_out).resolve()
|
||
json_path.parent.mkdir(parents=True, exist_ok=True)
|
||
summaries = [summarize_cell(c) for c in cells]
|
||
json_path.write_text(json.dumps(summaries, indent=2, sort_keys=True))
|
||
print(f"wrote json summary: {json_path}")
|
||
|
||
return 0
|
||
|
||
|
||
if __name__ == "__main__":
|
||
raise SystemExit(main())
|