# SEAM Project Status

Current update: 2026-06-27 (HISTORY#341 - README placeholder cleanup: removed unpublished `example.com/seam` public installer placeholder commands, made the Operator Manual section the explicit help path beyond the quickstart, and added audit coverage so placeholder public installer URLs and missing operator-doc links do not return. Focused README/package audit tests and the related audit slice pass.)

Current update: 2026-06-27 (HISTORY#340 - Agent setup prompt + operator manual/error index discoverability: README now includes a handoff prompt for coding agents to install SEAM, verify `seam doctor`, ingest safe repo docs as persistent memory, test memory retrieval/context, and configure MCP; it explicitly says API keys plus local `.env`/`.conf` files are operator-owned and may be set through `seam webui` Settings or manually in ignored local config. README now maps the Operator Manual docs and points to an Error Index; `docs/errors.md` has a top-level error index including `HTTP 429` provider quota/rate-limit handling; focused docs/package audit tests pass.)

Current update: 2026-06-27 (HISTORY#339 - GitHub-first package install path: `pyproject.toml` now points package metadata at `BlackhatShiftey/Seam_Runtime`; README documents direct `pip install "seam-runtime[...] @ git+https://github.com/BlackhatShiftey/Seam_Runtime.git@main"` installs; `MANIFEST.in` covers root policy/docs plus packaged webui assets; CI has a `package-smoke` wheel/sdist build+install job; focused packaging tests, wheel build, clean wheel install, installed webui asset check, and `git diff --check` passed. Existing unrelated mem0/status/history work remains separate in this dirty branch.)

Current update: 2026-06-27 (HISTORY#338 - mem0 retrieval depth knob: `--mem0-search-limit` and `SEAM_BENCH_MEM0_SEARCH_LIMIT` now let fair diagnostics test mem0 above the old default `top_k=8`; default remains 8. Focused mem0 harness tests pass.)

Current update: 2026-06-27 (HISTORY#337 - macOS installer support: added `install_seam_macos.sh`, macOS Application Support runtime layout, macOS shim bootstrap hint, README/install docs, and installer tests. This is a separate follow-up commit after the mem0 rate-limit resilience push.)

Current update: 2026-06-27 (HISTORY#336 - MEM0 RATE-LIMIT RESILIENCE: mem0 ingest/search, shared answerer, and judge calls are retry/backoff wrapped; mem0 ingest has pacing and gpt-4o-mini LLM pinning; grouped LoCoMo runner records per-case errors/checkpoints instead of losing a paid run on late crashes. Existing unrelated macOS installer work remains local and unstaged.)

Last updated: 2026-06-26 (HISTORY#335 - MEM0 ADAPTER FIX (mem0 2.x API drift) caught by the rung-C pre-flight smoke: the LoCoMo mem0 comparator was broken vs installed mem0ai 2.0.2 (search() dropped top-level user_id -> filters={'user_id':..}, limit->top_k); every mem0 retrieval threw -> a paid head-to-head would've produced all-empty mem0 answers. Probed full API (only search drifted), fixed, re-smoked end-to-end (mem0->'Teal'->judge correct). Also fixed 2 exposed test issues: _StubMem0.search signature + test_cli_quickstart_mem0_stub which SECRETLY SPENT MONEY (ran a real quickstart once mem0 worked) -> hardened to run keyless. 14/14 mem0 tests + full canonical suite + PGVECTOR green, 2 xfails, 0 failures. Branch bench/mem0-adapter-2x-fix (PR). NEXT: rung C running (SEAM-broad vs mem0, half LoCoMo-10, ~$4); report head-to-head. Prior: HISTORY#334 - RUNG B: broad WINS (judged) + context_budget confound fix. Fixed JudgedLocomoScorer dropping context_budget (broad got compact's 8000-char ctx = fake broad; free probe 1.00x->5.07x). Paid judged A/B (gpt-4o-mini, 100 holdout cases across all 10 convos, ~$0.29): compact 0.465 vs broad 0.535, delta +0.070 (3.5x the 0.02 margin), win in cat1 (+0.098). Confirms answerer-aware (weak local->compact, capable cloud->broad). +2 tests; full canonical suite + PGVECTOR green, 2 xfails, 0 failures; branch bench/judged-context-budget-fix (PR). NEXT: rung C = SEAM(broad)-vs-mem0 full LoCoMo-10 (~1540 Q), bigger paid run, estimate+go first. Prior: HISTORY#333 - STRAND C STEP 1 + FAIR-COMPARISON HARNESS FIX: pinned mem0's real LoCoMo target ~66.9% LLM-judge/gpt-4o-mini (mem0 paper arXiv 2504.19413), NOT the 91.6 the roadmap wrongly carried (corrected docs/roadmap/COMPETITIVE_ROADMAP.md); fixed the blocker that made a judged head-to-head meaningless - answerer baked in the SEAM adapter so mem0/zep (generated_answer=None) judged on an empty string ~0. NEW benchmarks/external/common/answerer.py (build_answer_prompt single-source + generate_short_answer + SharedAnswererAdapter); run.py _maybe_wrap_answerer wraps mem0/zep when an answerer is set; seam.py _generate_answer sources the shared prompt so every adapter answers identically (only retrieved context varies). Benchmark-harness only, no core change; +8 CI-safe tests (tests/audit/test_shared_answerer.py); full canonical suite + PGVECTOR green, 2 xfails, 0 failures; on branch bench/fair-shared-answerer (PR). NEXT: Strand C - (1) optional free capable-answerer loop confirmation; (2) operator-gated PAID SEAM(broad)-vs-mem0 head-to-head, now meaningful (same answerer both sides). Prior: HISTORY#332 - STRAND B (the #328 A->B->C plan, B DONE): the retrieval profile is now LOOP-TUNABLE by a FREE answer-quality scorer. NEW dilution-sensitive PooledLocomoAnswerQualityScorer (local-Ollama generated-answer token_f1, profile_safe=True); candidate_levers(profile_levers=) opt-in proposes the compact/broad presets; run_improvement_cycle enables them ONLY when every scorer is profile_safe (never self-probe/context_recall, which a bigger budget games); CLI seam improve cycle --locomo-answerer ollama. FREE-validated (~$0, qwen2.5:3b): compact token_f1 0.342 vs broad 0.028 (+0.314, broad collapses the weak answerer); e2e loop profile_levers=true with broad correctly NOT selected. +7 CI-safe tests; full canonical suite + PGVECTOR green, 2 xfails, 0 failures. NEXT: C = operator-gated paid SEAM(broad)-vs-mem0 head-to-head. Prior: HISTORY#331 - SEAM ENGINEERING MANUAL landed + finalized (PR #96): the docs/engineering/ engineering manual (architecture/security/change/test/incident SOPs + epistemic calibration + verification matrix + templates) and the compact skills/seam-engineer/ routing skill are now on main; finalization split the 5 templates inlined in templates/README.md into their own files (CHANGE_PLAN/ARCHITECTURE_DECISION/THREAT_MODEL_DELTA/ENGINEERING_HANDOFF/INCIDENT_REPORT), resolving the dangling SOP links, rewrote README into an index, and added a docs/README.md discoverability pointer; this also closes the dangling docs/engineering/09_EPISTEMIC_CALIBRATION.md reference main has carried (in calibration_scorer.py) since #325; docs-only, gates green. Prior: HISTORY#330 - CODEQL FIX (alert #13, py/insecure-temporary-file, HIGH): swapped `tests/audit/test_retrieval_flags.py`'s deprecated `tempfile.mktemp()` for the pytest `tmp_path` fixture (closes a high-severity TOCTOU CodeQL alert in a test); 16/16 pass, zero other `mktemp` in active code, no runtime change; the GitHub alert clears once this is on main. Prior: HISTORY#329 - PROGRESS TABLES: added `docs/progress_tables/` as the tracked scan-friendly data-table layer for SEAM progress. Tables: `test_runs.csv` (6 seed rows), `benchmark_results.csv` (8 seed rows), and `milestones.csv` (9 seed rows), with README contract that HISTORY.md remains authoritative and rows cite history/evidence/next steps. Verification: all CSVs parsed with Python csv.DictReader; no runtime code changed. NEXT: append rows after material test/benchmark/milestone sessions; add a tiny validator/exporter if hand-maintained tables begin to drift. Prior: HISTORY#328 - CORE ANSWERER-AWARE RETRIEVAL PROFILES (operator: "build the core first ... actually improve agent memory ... topping the charts ... same scale as mem0"). Strand A of A→B→C. RetrievalFlags gains context_budget alongside search_top_k; RETRIEVAL_PROFILES={compact:(100,8000),broad:(300,60000)} + resolve_retrieval_profile(); env SEAM_RETRIEVAL_PROFILE=compact|broad sets the pair in both env paths (load_retrieval_flags→all core surfaces CLI/REST/MCP/dashboard; retrieval_flags_from_env→benchmark adapter); explicit SEAM_RETRIEVAL_TOP_K/CONTEXT_BUDGET override. search_top_k already honored in search_ir (#320) so the profile reaches every surface; context_budget honored in runtime.pack_ir (None→flags else 512). CONFIG knobs, NOT self-improvement candidate_levers (would game #290 self-probe); tuned by free-LoCoMo answer-quality + paid judge. NO REGRESSION (no profile→both None→baseline). Holdout cat1 (61 never-tuned, clean SQLite, real OpenAI judge) 0.566→0.705 (+0.139) at broad; same broad context COLLAPSED a weak 3B (0.46→0.10)=dilution is a weak-model artifact. +6 tests; REPO_LEDGER decision; full suite + PGVECTOR green. NEXT: B=loop-tune profile via free-LoCoMo answer-quality scorer; C=SEAM(broad)-vs-mem0 head-to-head (mem0 runs $$, operator-gated). Prior: HISTORY#327 - JUDGE BUG FIX (operator: "fix the judge bug first ... we want solid architecture"). `benchmarks/external/common/judge.py` OpenAIJudge hardcoded reasoning_effort="minimal" (+ max_completion_tokens=512) in both score() and _build_batch_request(); gpt-5.4+ reject "minimal" (only none/low/medium/high/xhigh) -> BadRequestError 400 = judge path BROKEN for current OpenAI models. The MISSED half of the #321 answerer fix. Fix: module-level `_openai_judge_reasoning_params()` env-driven (SEAM_BENCH_JUDGE_REASONING_EFFORT, fallback SEAM_BENCH_REASONING_EFFORT, default "low"; SEAM_BENCH_JUDGE_MAX_COMPLETION_TOKENS default 512), both sites; ClaudeJudge unaffected. Tests: test_openai_judge_gpt5.py pin minimal->low + 3 new; full canonical suite + PGVECTOR green. Surfaced during the cat1 free->paid investigation: cat1 wall = weak local answerer + a knee tuned for it, NOT SEAM retrieval; real OpenAI-judge cat1 dev 0.525@old-knee -> 0.670@capable-answerer-knee (top_k=300/budget=60000); "raising top_k dilutes" was a weak-model artifact (7b collapsed, capable model rises). All cat1 paid runs gpt-5.4-mini, ~cents; banked to memory. NEXT: holdout-validate the capable-answerer retrieval profile, then productize as an answerer-aware RetrievalFlags lever (tighten ctx for small-model recall; broaden for big models). Prior: HISTORY#326 - WEBUI CONSOLIDATION (operator: "fix the mess... if it's not touching anything running it's okay"). Four webui-ish locations -> ONE canonical, running server untouched. THE webui = `seam_runtime/webui/dashboard.html` (self-contained hand-authored CDN-React + seam-api.js/assets), served by `seam webui`/`seam serve` at http://127.0.0.1:8765/ (server.py:webui_dir, override SEAM_WEBUI_DIR). DELETED untracked strays Webui-final-dash/, webui/dist/ (stale build differing from served), webui/node_modules/. ARCHIVED diverged top-level webui/ Vite project -> archive/webui-vite-source/ (reversible git mv, 24 files; build diverged from served, React rewrite was a reverted regression) + ARCHIVED.md. Dropped stale /webui npm Dependabot entry; rewrote docs/CODE_LAYOUT.md WebUI section. Verified uvicorn stayed healthy (200, title) after every move - only top-level webui/ + untracked dirs touched. Launched both surfaces (browser :8765 + `seam dashboard` TUI). Full suite + PGVECTOR green; no Python changed; no paid run. NEXT: cat1 is SYNTHESIS/PACKING-bound (Phase 0: 82% gold evidence retrieved yet all-retrieved token_f1 ~0.20) -> entity-aggregation dossier PACKER, not ingest-coreference rebuild; record Phase 0; PR #96 merge; meshyface ops-hardening track banked. Prior: HISTORY#325 - CALIBRATION SCORER + adversarial-loader fix (operationalizes PR #96's epistemic-calibration policy). Operator: build the CalibrationScorer + "use #96 to progress SEAM." `benchmarks/external/locomo/calibration_scorer.py` turns `docs/engineering/09_EPISTEMIC_CALIBRATION.md`'s reward matrix into measured selective-prediction metrics (coverage, selective accuracy, abstention precision, hallucination rate, calibration utility) over the LoCoMo split, FREE (local Ollama, deterministic). BENCHMARK-INTEGRITY BUG the build exposed + fixed: the shared loader's `if "answer" not in qa: continue` silently dropped all 444 cat5 adversarial cases (only `adversarial_answer`, no `answer`) so the unanswerable arm DID NOT EXIST on real LoCoMo (any abstention metric scored an empty set = falsely clean). Fix = opt-in `load_locomo_cases(include_unanswerable=False)`, default-OFF byte-identical (1542/0-empty), opt-in = full 444-case arm. REAL measurement (qwen2.5:3b, 15 conv-26 adversarial): 11 correct abstentions, 4 fell for the trap -> hallucination_rate 0.267, abstention_precision 1.0 (previously invisible). 11 CI-safe hermetic tests incl. the policy thesis (abstain-on-everything 0.2 vs calibrated 2.0). Also landed cat1/GraphRAG coreference BLUEPRINT doc (docs/audits/2026-06-17-cat1-coreference-graphrag-blueprint.md; findings, not a build spec). Benchmarks+docs only, core/runtime unchanged; full canonical suite + PGVECTOR green; NO paid run. cat5 is sparse per-conversation -> pool scopes for production calibration. NEXT: cat1 ingest coreference (80% lever); v2 calibration (capture adversarial_answer + multi-scope pooled run); PR #96 should merge (its 09 doc is the spec); evaluate meshyface operational patterns. Prior: HISTORY#324 - SEAM-doctor STASH ADVISORY (operator-requested hygiene, from the #323 stash post-mortem). `seam doctor` now surfaces git stashes as a NON-blocking advisory; stashes are invisible to git status/log/branch/PR listings, so an abandoned one (an agent stashing to clear a tree mid-context-switch and never restoring it - the #323 sweep found a 3-week-old one, dropped) lingers unseen. BUILD: `check_stashes()` (seam_runtime/doctor.py) runs `git stash list` -> clean | advisory | not-a-git-repo with per-stash age+summary, wired into build_doctor_report as the "stashes" key; cli.py renders "Stashes: N present (oldest Xd)". ADVISORY ONLY - never flips PASS/FAIL (like commit_gate/streams); DELIBERATELY NOT added to the blocking commit gate (a stash during active work is legitimate = false positive; hygiene advisories belong in the health check, correctness gates in the blocking gate). AGENTS.md Session End gains a stash-hygiene line. +5 CI-safe hermetic tests (tests/audit/test_doctor_stashes.py, monkeypatched subprocess). Benchmark-unaffected; full canonical suite + PGVECTOR_TEST_DSN green (1089 passed/2 xfailed/3 subtests). Also reviewed PR #96 (operator's SEAM engineering manual + `seam-engineer` skill, meshyface-modeled) favorably: accurate to the real architecture/protocol, anti-drift by routing to canonical sources (not hardcoding commands/counts per #310); still draft pending its own chain + discoverability pointer + restated rules citing AGENTS.md. NEXT: campaign main thread unchanged = cat1 cross-turn entity coreference at INGEST toward 80%; complete PR #96's chain when ready. Prior: HISTORY#323 - ENTITY-AGGREGATION LEVER = MARGINAL (cat1), landed default-OFF. Prototyped + HARDENED the retrieval-time entity-aggregation lever (#321 NEXT) and measured it FREE end-to-end -> a WASH on cat1 ANSWER quality. Free recall A/B (deterministic context_recall, 10 convs/1198 dev q): cat1 +0.025, cat3 +0.034 (concentrated on the weak cats = targeted rescue of ~7-12% of cases; BUT the block is additive + set-based recall = it OVERSTATES). Free local-answerer A/B (qwen2.5:3b token_f1) = DECISIVE: first +0.012 was NOISE - verification caught qwen non-determinism at temp=0 (11/15 identical), a re-run flipped to -0.003; FIXED with seed+top_k=1 (3/3). Deterministic cat1 (112q): raw hack -0.0054 (20/21), hardened (query-rank+stem, cap20) +0.0055 (25/25 EVEN) = at the noise floor. VERDICT: retrieval-time string-match entity-agg is MARGINAL (recall recovers the tokens; the answerer does not convert them - dilution cancels recovery); the ceiling is the INGEST rebuild per #321. Wins ARE real coreference re-attribution fixes ("What did Caroline research?" 0.67->1.00), validating the CONCEPT. NO PAID RUN (free settled the direction; operator rule REFINED: paid is the LAST lever, only after hypothesis+baseline+pro/regression nailed over SETS of data). LANDED: lever default-OFF (SEAM_BENCH_ENTITY_AGG) + the FREE answer-quality infra (local Ollama answerer answerer=ollama + seed determinism + token_f1 A/B = the durable win, makes future levers testable free); audit docs/audits/2026-06-15-entity-aggregation-retrieval.md; +7 CI-safe tests (tests/audit/test_locomo_entity_aggregation.py). Benchmark-adapter only, core/runtime unchanged; full canonical suite + PGVECTOR_TEST_DSN green (exit 0, 2 known xfailed). Ran alongside Codex (#322 folder-org), committed separately. NEXT: the REAL cat1 lever = cross-turn entity coreference at INGEST (stable ids across per-turn mentions + aggregation over real ir_edges); scope+free-validate before building. 80% = milestone to unlock roadmap items, not a one-fix target. Prior: HISTORY#322 - TEST ARTIFACT ROUTING: root cleanup moved 80 ignored `test_pgvector_*` SQLite sidecars into `test_seam/pgvector/` and added `tests/docs/` as the tracked testing documentation tree. Protocol now says tracked test docs live under `tests/docs/` and disposable generated test outputs live under ignored `test_seam/<area>/`; pre-existing dirty benchmark adapter code was not touched. Prior: HISTORY#321 - PUSH TOWARD 80% (operator campaign goal): climbed the ANSWERER ladder past the #320 budget knee (0.52), it TAPS OUT at ~0.60, and proved cat1 is a retrieval-ARCHITECTURE problem. Paid judged (100 cases, knee retrieval top_k=100/budget=8000, judge gpt-4o-mini): gpt-4o-mini=0.52; gpt-4o=0.595 (win ENTIRELY cat2 temporal 0.50->0.70, cat1 FLAT 0.44); o4-mini reasoning(medium)=0.595 (cat1 still 0.48); cross-encoder rerank = 0.0 recall delta. VERDICT: cat1 (32% of cases, ~0.46) moves for NO answerer => not capability/budget/rerank. Real cat1 failures (from the dataset QA) = ATTRIBUTE/AGGREGATION ("what activities does Melanie do?" gold = full list across the convo) + NEEDLE single-facts; recall 0.69 OVERCOUNTS (short gold tokens appear somewhere in 100 turns). ROOT BLOCKER to 80%: SEAM lacks CROSS-TURN ENTITY COREFERENCE + entity-aggregation retrieval (per-turn ingest -> each turn's entity is a different id, ZERO ir_edges) = an INGEST+RETRIEVAL rebuild, not a knob. Cheap-lever ceiling ~0.60-0.68; 0.80 needs the rebuild (also unblocks empty-graph multi-hop); 0.80 on LoCoMo cat1 is near-SOTA. ADAPTER BUG-FIXES (found testing reasoning): _openai_short_answer hardcoded reasoning_effort="minimal" (o4-mini rejects) + max_completion_tokens floor 256 (reasoning exhausts it) -> both env-configurable (SEAM_BENCH_REASONING_EFFORT default low, SEAM_BENCH_MAX_COMPLETION_TOKENS default 2048); the reasoning-answerer path was BROKEN, now works. gpt-4o answerer (0.595) measured best but NOT made default (cost decision). Benchmark-adapter only; suite unchanged (1077). NEXT (campaign): the cat1 retrieval rebuild = cross-turn entity coreference + entity-aggregation retrieval; scope+TEST cheaply (free cat1 recall) BEFORE building. Prior: HISTORY#320 - ROOT-CAUSE FIX (validated): cat1/cat3 weakness = RETRIEVAL BUDGET STARVATION, not multi-hop/embeddings. Chain: paid baseline 0.40 -> bucket (retrieval-miss, answerer honest) -> FREE Phase-0 KILLED decomposition (#319, hurt -0.169) -> FREE top_k/budget sweep (recall climbs hard, cat1 0.385->0.842) -> PAID confirm it converts to ANSWER quality -> test the knee -> productize -> reproduce. CURVE (paid judged, 100 cases, gpt-4o-mini): 20/2000 = ~0.39 (0.40+0.385); 100/8000 KNEE = ~0.53 (0.52,0.525,0.52,0.54 across 4 runs); 200/20000 = 0.525 FLATTENS (recall keeps climbing but judge +0.005 = answerer saturation, ~2x cost). 4 knee runs vs 2 baseline = clean separation ~+0.14 REPRODUCED (not luck); cat1 0.22->0.44, cat3 0.23->0.42 both DOUBLE. BUILD: `RetrievalFlags.search_top_k` (env SEAM_RETRIEVAL_TOP_K) honored by search_ir as the candidate budget; DELIBERATELY NOT a self-improvement candidate_lever (it would GAME the #290 self-probe candidate-set gold) - config knob + measured default, tuned by free-LoCoMo/paid judge. Benchmark defaults bumped to the knee (adapter+run.py search_top_k 20->100, context 2000->8000); CORE search_ir default (budget=5) LEFT UNCHANGED (CLI/REST not benchmarked = measured follow-up, test-before-change-production). New test_retrieval_flags tests (default None + env + override widens candidates); 2 tests pinning old 20/2000 made default-independent. Full CI + PGVECTOR_TEST_DSN + strict no-skip = green (1077 passed/2 xfailed/3 subtests). cat1/cat3 retrieval-quality arc CLOSED. NEXT: optional CORE default bump behind its own CLI/REST measurement; Track O (BIRD/query engine) banked; Stage 5 + server graceful-shutdown gap open. Prior: HISTORY#319 - (A) ROADMAP Track O = SEAM Query Engine (execution-verified NL->typed-plan->SQL; operator: "add BIRD ... add it to the roadmap to start or consider it"). Planned/consider, priority 3, AFTER the recall work; canonical design = docs/roadmap/SEAM_QUERY_ENGINE_SQL2_LEARNINGS.md. BIRD placement: useless until SEAM can EMIT SQL, so it is the FIRST/measurement-first deliverable OF the track (separate query-benchmark slot, NOT the conversational memory_benchmarks.json registry; execution-accuracy scorer + fixture testable before any generator; generator NOT_CONFIGURED until the engine exists). (B) cat1/cat3 Phase 0 multi-hop validation = DECISIVE NEGATIVE: qwen2.5:3b decomposer, 3 dev scopes/259 q, SAME questions - decomposition HURTS recall 0.6119->0.4429 (delta -0.169); cat1 -0.226 (the TARGET), cat4 -0.223. MECHANISM: sub-queries retrieve off-topic records that DISPLACE the correct evidence at the fixed context budget ([[feedback_format_change_displaces_raw]] dynamic). Also confirmed: compile_nl creates ZERO ir_edges (flat ingest) so L2 graph-edge closure has no graph; LoCoMo ingests per-turn so entities don't co-refer. Both naive multi-hop levers (L1 decomposition + L2 edge-closure) are OUT - cat1/cat3 is a ranking/budget/displacement problem (or evidence not retrievable at top-k), NOT a too-few-queries problem. Phase 0 (validate-before-build) saved a regression from being productized. Docs/roadmap only, suite unchanged (1075). NEXT: cat1/cat3 re-aim - test (free) larger context budget / original-first merge (avoid displacement), or diagnose whether gold evidence is retrievable at higher top-k at all (-> if not, the lever is RANKING/expansion not more queries). Prior: HISTORY#318 - SCOPING (docs only): two artifacts. (1) `docs/audits/2026-06-15-cat1-cat3-multihop-scope.md` - DIAGNOSED from the #317 paid run that cat1 multi-hop (12 retrieval-miss/6 answerer-miss) + cat3 open-domain (8/1) are RETRIEVAL-bound; the answerer is HONEST (abstains "unknown", never hallucinates), so the lever is getting evidence INTO context. KEY: the multi-hop machinery EXISTS but is dormant + benchmark-only - adapters/seam.py `_decompose` (LLM multi-query) was OFF in the 0.40 baseline; `_collect_closure_ids` follows only evidence/prov ids NOT ir_edges (the graph is SCORED via _graph_score but never TRAVERSED for retrieval); core search_ir is single-shot. Levers L1 query decomposition (model-independent, Ollama default), L2 graph-edge closure expansion (FREE), L3 cat3 query expansion. Plan: Phase 0 validate cheaply in the adapter (free recall vs 0.372/0.261) BEFORE building -> Phase 1 productize the winner into core search_ir + RetrievalFlags (so the self-improvement loop can tune it - it can't today, the 11 levers are single-shot knobs tapped out at 0.627). (2) `docs/roadmap/SEAM_QUERY_ENGINE_SQL2_LEARNINGS.md` - SQL2 learnings for SEAM OVERALL (operator clarified: not the current problem), a FUTURE direction. Kernel = NL->typed query plan->deterministic compile->verified execution->provenance result (model emits plans not raw SQL; "verify before trust" is already SEAM DNA). TAKE: typed query plan, read-only AST sandbox, schema registry, provenance traces. LEAVE: the SQL2 candidate-consensus ensemble (a deterministic compiler removes the variance it tames), schema-linking, fine-tuned specialist, Gemini dependency. Own roadmap track (overlaps Track K), AFTER the recall work. Docs-only, suite unchanged (1075). NEXT: cat1/cat3 Phase 0 validation. Prior: HISTORY#317 - INGEST QUALITY: regex enrichment OFF by default (operator: "fix the noisy regex" before a paid benchmark). The #311 "transitional" hand-rolled location/date/mentioned/action(felt/went_to) extractor is pure liability, proven by a live smoke + a benchmark A/B. SMOKE (16 varied real sentences, default floor): 16/16 faithful content claim but 6/16 emitted EXTRA enrichment, ~25% CLEARLY WRONG (location=gluten, felt=shipped, location=cardiology). A/B (free LoCoMo recall, 5 dev scopes/599 q, enrichment ON vs OFF, SAME questions): enriched 0.627396 vs content-only 0.626730 = -0.000667 (noise); cat1/2/3/5 byte-identical, cat4 -0.0012 = ZERO recall benefit (the content claim already carries every verbatim token, so enrichment claims are token-SUBSETS = wrong structure, no new signal). FIX (seam_runtime/nl.py): compile_nl reads SEAM_NL_REGEX_ENRICH (default OFF); `_extract_conversational` is now `elif regex_enrich:`. Floor = verbatim content claim + proper-noun entities + grounded subject; legacy path RECOVERABLE via flag, grounded Ollama extractor (#313) is the real structured-triple path. Post-fix smoke 0/16 noisy; LoCoMo non-content claims at scale = 0. THE BENCHMARK ALSO EXPOSED THE REAL HEADROOM (not addressed here): baseline 0.627 with the 11 retrieval-flag levers TAPPED OUT (improve cycle proposed nothing - they're all retrieval-side, can't touch ingest); per-category cat1 multi-hop 0.398 + cat3 open-domain 0.223 are the weakness (cat2 0.679/cat4 0.737 strong; cat5 0.0 unanswerable) = needs a NEW lever (multi-hop retrieval/query decomposition), the next research item. Tests: test_conversation_turn_compile.py autouse-sets the flag (pins opt-in legacy path); new test_seam.py::test_compile_nl_no_regex_enrichment_by_default. Full CI command + PGVECTOR_TEST_DSN + strict no-skip = green, 0 failures (1075 passed/2 xfailed/3 subtests). NEXT: operator-authorized PAID 100-q judged LoCoMo run (after this free verification, cost surfaced first); then the cat1/cat3 recall work; Stage 5 (degenerate ./seam.db records) + the server graceful-shutdown wiring gap remain open. Prior: HISTORY#316 - DENSITY axis, SLICE 3 (collision-safe id factoring on prov/evidence) + CORRECTION to #315. Operator: "do a at least" (a = factor prov/evidence ids; b = §23 predicate/symbol compaction). CORRECTION: #315's claim "prov/evidence index-dedup is moot (bloat left with the dropped entries)" was WRONG - the surviving CLM entries still carried full `prov:compile:<hash>`/`span:<hash>:n` ids = 324 tokens / 38% of the 862-token content pack (measured: refs 163, prov 162, evidence 162, object 95, kind 52, subject 42, predicate 39). Per protocol #315 is not edited in place; #316 records the correction. TOKENIZER FINDING (why b dropped): `content`=1 token and `ct`=1 token, so §23 predicate compaction saves ZERO tokens - the symbol loop only wins on MULTI-token strings; the expensive id part is the doc `<hash>` (7 tokens). So a is the only real lever; b is token-neutral and skipped (would regress). THE CHANGE (seam_runtime/pack.py): `_mine_id_aliases` mines recurring long `:`-segments (freq>=2, token_count>=3 = the doc hashes) from content-record prov/evidence, assigns each a `$N` symbol in a reserved sentinel namespace (real segments never start with `$` -> collision-safe; deterministic -freq-then-segment per §13), rewrites prov/evidence by exact segment substitution, stores the `$N`->segment table ONCE as payload["idsym"]. NET-WIN GATED: if encoded ids + table >= raw, the table is dropped and ids stay full = strict no-regression ratchet (§24 spirit). REVERSIBLE: `_decode_id` is the exact inverse; score_pack/_list_field_retention decode before comparing to full ids, so prov/evidence retention stays 1.0 and traceability holds (>=0.66 evals gate). Context packs are already reversible=false; score_pack is the ONLY reader of entry prov/evidence (cli/server/mcp/context_views read payload["records"]). MEASURED: multi-doc 8-memory corpus 862->808 (-6.3%, 8 hashes each ~3x so the table barely amortizes); single-DOCUMENT corpus (13 claims sharing ONE hash = realistic ingest) 961->769 (-20.0%), traceability 0.802, retention 1.0 - % grows with claims-per-document. New test_context_pack_factors_prov_evidence_ids_reversibly. Full CI command + PGVECTOR_TEST_DSN + strict no-skip = green, 0 failures (1074 passed/2 xfailed/3 subtests). NEXT: the same hash inside `refs` (163 tokens) is the bigger remaining lever but widens blast radius (ref_coverage + entry keying + the readable pack-view display all read refs as full ids) = candidate slice 4; the §23 symbol loop on NL is confirmed LOW-value under this tokenizer (marginal + #309 substring risk) = effectively closed; Stage 5 (migrate degenerate compile_nl records) remains open. Prior: HISTORY#315 - DENSITY axis, SLICE 2 (content-only context packs) - the bulk of the NL->PACK density win #314 deferred. A CONTEXT pack is the meaning an LLM reasons over, so it should carry only meaning-bearing records. THE CHANGE (seam_runtime/pack.py): new module-level `_CONTEXT_CONTENT_KINDS = {CLM,STA,EVT,REL}`; context mode filters `content_records` to those kinds, so RAW/PROV/SPAN/ENT no longer become pack ENTRIES. Safe because the dropped kinds are already represented in what remains - RAW's verbatim lives in a content claim's object (#311 floor), ENT labels are inlined into claim subjects (#314), PROV/SPAN are provenance the model never reasons over; full `ordered` still drives ent_label + the symbol map, and all records stay in the store for traceability/exact-mode reversibility (only the ENTRIES list is content-only). MEASURED (fixed 8-memory corpus, 64 NL tokens, store = 8 RAW/8 PROV/11 ENT/8 SPAN/13 CLM): pack ALL-kinds 2375 tokens/48 entries (cr 0.027) -> content-only 862 tokens/13 CLM entries (cr 0.074) = 2.76x fewer pack tokens, and ~3.8x cumulative with #314 over the slice-0 baseline, context now both dense AND readable. New test_context_pack_is_content_only (entry kinds subset of the content kinds, >=1 CLM); score_pack traceability unaffected (prov/evidence live ON the kept content entries). Full CI command `pytest test_seam_all/ tools/history/test_history_tools.py tools/streams/ tests/` + PGVECTOR_TEST_DSN + strict no-skip = green, 0 failures (1073 passed/2 xfailed/3 subtests, +2 over #313's 1071). NEXT: density slice 3 - the §23 symbol loop on NL objects (collision-safe, the "3" in the operator's "1+3"; deferred to Track J after the #309 whitespace-collision); the #314 prov/evidence index-dedup idea is now moot for the content path (the structural-id bloat left with the dropped entries); Stage 5 (migrate degenerate compile_nl records) remains open. Prior: HISTORY#314 - DENSITY axis, SLICE 1 (operator picked "1+3" = dense pack + §23 symbol loop). DIAGNOSTIC (measured): an 8-memory corpus (114 NL tokens) packed to 5896 tokens (cr 0.019 = 52x LARGER than source); 77% was HASH-ID bloat (clm:<hash>:<hash>:n / ent:<hash>:slug:<hash> repeated 4x/entry as id/subject/prov/evidence) + the subject was an opaque ent:... id, so the LLM-consumed context was unreadable hashes. THIS SLICE (seam_runtime/pack.py + context_views.py): a context entry no longer repeats its own id (carried ONCE in refs[position]; per-entry "id" dropped), and a claim's subject resolves to the entity LABEL. Result: 5896->4117 tokens (cr 0.019->0.028, ~1.4x) AND the context now reads "Priya owns billing service" not "ent:hash content ..." - a correctness/usability fix as much as density. Consumers updated (_context_entries_by_id keys by refs[position]; pack/prompt/evidence/summary views resolve ids/labels); prov/evidence kept as full ids. Tests: test_context_pack_refs rewritten to the dense invariant + new test_context_pack_resolves_subject_to_label. Full CI command + PGVECTOR_TEST_DSN + strict no-skip = green, 0 failures. HONEST: this is the FIRST low-risk slice (~1.4x + readability); the bulk of the 4.3x available is in the NEXT slices (sliced deliberately to keep the agent-context path safe): (a) dedup prov/evidence ids by index (-> ~4.3x); (b) content-only context packs (drop the structural PROV/SPAN/ENT entries, 37/60 records, the LLM never reasons over); (c) the §23 symbol loop on NL (collision-safe = the "3"). NEXT: density slice 2 (prov/evidence index-dedup + content-only packs), then the symbol loop; Stage 5 (migrate degenerate compile_nl records) remains open. Prior: HISTORY#313 - MIRL compiler Stage 4 (operator's "added goal"): the OPT-IN local-Ollama rich extractor. NEW `seam_runtime/nl_extract.py`: `OllamaExtractor` calls a local Ollama HTTP endpoint (urllib, no new dep) with a few-shot strict-verbatim prompt for real (subject, relation, object) triples + entities; `ground_extraction` is the FABRICATION FIREWALL (§3.2/§8) - an entity/claim survives only if every span is a verbatim token-subset of the input, else dropped (empty -> floor fallback). `compile_nl(text, ..., extractor=None)` keeps the floor's content claim (coverage + temporal retention) and ADDS grounded triples, replacing the regex enrichment; the floor stays the DEFAULT (CI never sets SEAM_NL_EXTRACTOR), so determinism is the floor's guarantee, not the LLM path's. VALIDATION (qwen2.5:3b on the T7 via a user ollama on :11435) against the 5 goldens: sr 0.667->1.000 on all 4 real-memory goldens (real relations: (Priya, owns, billing service) matches the canonical triple) + entity_extraction=True on ALL (it extracts "billing service"/"west datacenter", the floor's 2 xfails) + rr/pr/tr=1.0; self_description_overfit sr stays 0.333 (correctly - the model gives the real grammatical triple, not the stub's overfit "goal"). HONEST: the floor's test_compile_fidelity still shows the 2 entity_extraction xfails because it runs the FLOOR (the extractor closes them on the opt-in path, validated by me + the CI-safe stub-extractor test); CI can't run a model + strict-no-skip forbids a skipping test, so the sr->1.0 numbers are MY measurement. NEW tests/fidelity/test_nl_extract.py (5, CI-safe + model-free: grounding firewall + compile_nl(extractor=stub) integration + env). Full CI command + PGVECTOR_TEST_DSN + strict no-skip = green, 0 failures (1071 passed/2 xfailed/3 subtests). The qwen model location/access is in memory [[reference-ollama-qwen-on-t7]]. NEXT: Stage 5 - migrate existing degenerate compile_nl records (keep RAW, replace derived ENT/CLM); optional: extraction cache for determinism + promote the §24 gate on the extractor path. Prior: HISTORY#312 - LOOP VALIDATION (operator's primary goal): the closed self-improvement loop is FUNCTIONAL on faithful ingest now that the compiler is fixed (#308-#311) = the Track-K "benchmarks 100% functional" gate (closed automatable loop with auto-proposer + apply) is MET. Fixed two self-probe signal bugs the now-faithful corpus exposed: (1) generate_probes default kinds -> retrieval candidate kinds (CLM/STA/EVT/REL), excluding RAW (not a default search candidate) + PROV/SPAN/ENT (id/label-only) that always-miss and dilute the signal; (2) _record_text excludes `ent:...` id-reference fields so a short-object enrichment claim doesn't cloze over its subject id. After both, the own-corpus self-probe is genuine NL cloze (was slug-cloze under the old compiler). VALIDATION (FREE, no paid): self-probe on a clean faithful corpus = 1.0 (the #290 structural property: cloze-of-own-record is trivial -> a perfect REGRESSION WATCHDOG, NOT a score-mover; the free-LoCoMo scorer is the mover). FULL CLOSED LOOP (run_improvement_cycle, self_probe + free-LoCoMo, auto_approve, 2 scopes): baseline self_probe=1.0/locomo=0.5795; PROPOSED bm25_all_kinds=True (+0.0266 locomo, +0.0 self_probe = no regression); APPLIED via the #289 reversible reconcile; KEPT (reverted=false). The observe->propose->apply->re-measure loop fired end-to-end. HONEST SCOPE: +0.0266 is a free-metric DEV gain (proves the loop WORKS), NOT a paid-validated production claim (big claims still need operator-gated `seam improve validate --confirm-paid`). tests/audit/test_self_probe_scorer.py 7->9. Full CI command + PGVECTOR_TEST_DSN + strict no-skip = green, 0 failures (1066 passed/2 xfailed/3 subtests). NEXT: Stage 4 (the operator's "added goal") - the opt-in LOCAL OLLAMA rich extractor (real S-P-O triples -> sr->~1.0, close the 2 entity_extraction xfails, replace the regex enrichment). Prior: HISTORY#311 - MIRL compiler Stage 3: UNIFIED compile_nl + compile_conversation_turn into ONE fidelity-clean pipeline; deleted compile_conversation_turn. Operator chose "preserve behavior" + accepted shipping on aggregate-recall improvement. Unified `compile_nl` = honest floor BASE (verbatim RAW, per-proposition SPAN + grounded content claim, proper-noun entities) + conversational rules folded in from the deleted function (speaker->person ENT, dates, locations, named entities, action verbs), run PER-PROPOSITION with the claim localized to its SPAN and subject GROUNDED (turn speaker if present, else the proposition's leading noun phrase - NEVER the old synthetic ent:turn:<hash>). `ingest_conversation_turn` delegates to compile_nl. LOCOMO NO-REGRESSION GATE (free recall, answerer=None, 3 scopes/298 dev q/top_k=20): aggregate 0.614757 -> 0.623128 (+0.0084, IMPROVED); cat1/cat4 UP, cat2/cat3 small dips (within noise on 3 scopes); operator accepted on aggregate. Fidelity goldens UNCHANGED (conversational rules barely fire on them; the IGNORECASE location regex that does fire adds only grounded, span-localized PARTIAL claims - the mandatory content claim still carries the full fact). Tests reconciled (13 conversation tests grounded-subject semantics + prov activity compile_nl; test_compile_generates_core_records counts content claims). REPO_LEDGER tense-corrected. Full CI command `pytest test_seam_all/ tools/history/test_history_tools.py tools/streams/ tests/` + PGVECTOR_TEST_DSN + strict no-skip = 1064 passed/2 xfailed/3 subtests/0 skipped/0 failed. KNOWN (transitional): the regex location/action extractors over-fire on plain English (grounded but noisy) - slated for replacement by Stage 4's Ollama extractor. Prior: HISTORY#310 - docs-only: completed the #309 status chain. CI's `test-and-benchmark` ran the FULL `python -m tools.history.verify_continuity` (recorded-fact audit ON) and failed because #309 bumped the latest entry but left PROJECT_STATUS.md's "Latest continuity handoff is HISTORY#NNN" bullet at #308; the LOCAL commit preflight runs `verify_continuity --no-recorded-fact-audit`, which skips that check, so the mismatch wasn't caught before push. Fix: bumped the bullet + recorded the gap (run `verify_continuity` WITHOUT the flag before pushing, or always bump the "Latest continuity handoff" bullet). No code change. Prior: HISTORY#309 - follow-up to #308 making main green on EVERY CI root + clearing a CodeQL ReDoS. HONEST MISS: #308 (the floor) merged (PR #81) after only `pytest tests/` was run locally; CI's test-and-benchmark also runs `test_seam_all/` where the floor broke 15 legacy tests, and the PR auto-merged on the REQUIRED ruleset checks while advisory CodeQL + test-and-benchmark were RED. Fixes: (1) py/polynomial-redos (CodeQL high) in nl.py - the floor's `[.!?]+(?=\s|$)` sentence regex backtracks O(n^2) on adversarial input; replaced with a linear backtracking-free scan (60k-'!' now ~0.004s; behavior byte-identical); (2) reconciled the 15 test_seam_all tests that pinned the OLD stub output - added `_claim_ids`/`_claim_refs` helpers and replaced hardcoded clm:2/clm:5/prov:compile:1/span:1 with content-derived ids; rewrote test_compile_generates_core_records + test_cli_surface_compile for the floor's records (no ent:project/ent:user); fixed the rollback test (content-unique ids mean different texts no longer overwrite, so `replacement` recompiles the SAME text to exercise the restore path); retargeted test_symbol_promotion to structured DSL input (a whitespace-aware symbol miner flooded the promoter with colliding symbols, so the §23 symbol loop over the floor's natural-language objects is deferred to Track J = KNOWN FOLLOW-UP). Verified the FULL CI command `pytest test_seam_all/ tools/history/test_history_tools.py tools/streams/ tests/` with PGVECTOR_TEST_DSN + strict no-skip = 1064 passed/2 xfailed/3 subtests/0 skipped/0 failed. Memory updated: the full local check is ALL roots, not just tests/. Prior: HISTORY#308 - MIRL compile fidelity Stage 2: replaced the overfit `compile_nl` stub with a deterministic HONEST-MINIMAL FLOOR (operator-chosen backend). The genuine fix for "the same sentence over and over": compile_nl no longer fabricates a (project:SEAM, goal, <slug>) skeleton. The floor preserves one verbatim RAW; segments into propositions with REAL char offsets (.!? not splitting 4.2/9:30/B12); emits one localized SPAN + one GROUNDED claim per proposition (subject = the proposition's leading noun phrase, always drawn from the text -> never "about" an absent entity; predicate="content"; object = the verbatim proposition, so the fact stays recoverable + queryable); extracts high-confidence proper-noun entities only (lowercase common-noun phrases left to the opt-in extractor). Document-unique ids (raw:<hash>/clm:<hash>:<n>) replace the former fixed raw:1/clm:1 that collided on direct multi-persist (production namespaces per document, so it was masked; the un-namespaced path is now correct too). SPEC MOVEMENT (floor vs stub): sr 0.333->0.667 on real memories (subject+object recovered; the structured RELATION is the remaining 1/3, the opt-in extractor's job); rr up; cr UP on every case (leaner records, no boilerplate); pr=tr=qr=1.0. self_description_overfit sr drops 1.0->0.333 BY DESIGN (the stub's 1.0 there was pure overfit). The §24 gate STILL REJECTS the floor (sr 0.667<0.98) - promotion is earned by the rich extractor, not the floor. FIDELITY RATCHET: 9 xfails flipped to XPASS -> baseline_failures shrank (single_fact/two_facts -> {entity_extraction}; personal_event/schedule -> {}); only 2 entity_extraction xfails remain (lowercase common-noun cases = the opt-in-extractor target). Reconciled the spec_metrics "stub" tests + the test_compile_nl_unchanged pin to the floor; the document-unique-id change fixed test_self_probe + graph-isolation at the source (no test edits). Verified: full `pytest tests/` with PGVECTOR_TEST_DSN + strict no-skip = 647 passed/2 xfailed/0 skipped; eyeballed a real two-fact memory (verbatim RAW, two localized spans, two separable grounded claims, proper-noun entities, zero fabrication). NEXT: Stage 3 (unify compile_nl + compile_conversation_turn; delete the conv stub) -> Stage 4 (opt-in LOCAL OLLAMA rich extractor behind this same contract: real S-P-O triples -> sr->~1.0 + close the 2 entity_extraction xfails; CI can't run Ollama so the floor stays the default CI behavior) -> Stage 5 (migrate existing degenerate compile_nl records, keep RAW, replace derived ENT/CLM; re-validate the self-probe own-corpus loop now that ingest is no longer poisoned). Prior: HISTORY#307 - MIRL compile fidelity Slice 2: implemented the SEAM-spec §22 `qr` (retrieval_success@k) metric, completing the §24 promotion gate (qr was the last deferred axis). `benchmarks/fidelity/spec_metrics.py` `retrieval_quality()` now persists the compiled batch into a HERMETIC temp runtime (deterministic hash embedder + SQLite adapter, network-free, ambient-DSN-independent), runs `search_ir(query, budget=k)`, and reports whether a GOLD record (a retrievable-kind {CLM,STA,EVT,REL} record whose tokens cover the primary fact, claims scored subject-aware via claim_content_tokens) is in the top-k = 1.0 else 0.0; 0.0 without persisting when no retrievable record carries the fact; None when no query/fact. `measure_spec_metrics` gained opt-in `measure_qr=False` so the cr/rr/sr/pr/tr path stays pure + embedder-free by default (preserves #305 CI-safe tests; qr=None then, which the §24 gate already blocks). GOLDENS gained a `query` per case. SPEC-METRIC BASELINE with qr (measure_qr=True): every case qr=1.0 - the stub's all-in-one slug claim still carries the fact tokens and survives the round-trip, so the fact IS queryable; qr is "no worse than baseline" per §22, NOT a fabrication discriminator (pr/tr also 1.0); sr (0.333 real vs 1.0 overfit) + cr (0.018-0.040) remain the discriminators; cr/rr/sr/pr/tr byte-identical to #305. The §24 gate still REJECTS the stub on every case (sr<0.98 + cr not strictly>baseline) - now with EVERY axis measured. qr's discriminating power lands later: a future over-compressed rewrite that drops a fact's tokens from all retrievable records -> gold_ids empty -> qr=0.0 (the §24 "denser only when recovery is proven" concern). REPO_LEDGER unchanged (already lists qr + §22/§24 alignment). Verified: tests/fidelity/test_spec_metrics.py 11->17 (6 new qr tests, CI-safe + network-free via the hash embedder, ~0.6s); Stage-1 harness unchanged (no compile_nl change, no ratchet flips); full `pytest tests/` with PGVECTOR_TEST_DSN + strict no-skip = 638 passed/11 xfailed/0 skipped (632+6). NEXT: Stage 2 - the deterministic FLOOR rewrite of compile_nl (segment->propositions; verbatim RAW + per-proposition SPAN with real offsets; entities from high-confidence rules; NEVER fabricate project:SEAM/goal), measured by this now-complete §22/§24 contract (target sr->~1.0, rr->1.0, keep pr=1.0, improve cr; xfails flip XPASS->hard-fail as properties pass). OPEN operator decision before Stage 2: the free-floor backend (recommended honest-minimal zero-new-dep floor + opt-in local Ollama; spaCy-as-default on the table since deps acceptable; heavier hand-rolled extractor ruled out). Prior: HISTORY#306 - session consolidation + handoff (docs/branch hygiene only; no runtime change). State: HEAD==origin/main, clean tree, ZERO open PRs, 0 open CodeQL+Dependabot alerts, full suite 632 passed/11 xfailed/0 skipped. Wrote `docs/handoffs/2026-06-13-mirl-compiler-fidelity-handoff.md` with an HONEST progress assessment (the compile_nl bug is NOT yet fixed - sr still 0.333, cr still ~0.03 on real memories; this session built the measurement apparatus + governing rule, not the fix; the number moves in Stage 2) + the ordered resume point (Slice 2 qr metric -> Stage 2 compile_nl floor rewrite) + the open free-floor backend decision. Deleted this session's merged branches; left non-session branches for the operator (backup/local-pgvector-bootstrap = KEEP). New operator preference recorded: merge PRs promptly, never let them accumulate (queue held at zero this session). Prior: HISTORY#305 - reconciled the MIRL compile fidelity harness to the SEAM spec's OWN metrics (§22/§24) per the governing-contract rule. NEW `benchmarks/fidelity/spec_metrics.py`: §22 cr/rr/sr/pr/tr + the §24 `passes_promotion` gate (sr>=0.98, pr=1.0, tr>=0.99, qr>=baseline, cr strictly>baseline); qr DEFERRED (needs ingest+search harness). The 7 Stage-1 checks are now diagnostic COMPONENTS of these metrics. compile_nl spec baseline: sr=0.333 real vs 1.0 overfit (clean fabrication discriminator); cr=0.018-0.040 (packed IR 25-55x source = the token inflation Stage-1 missed, the north-star axis); pr=tr=1.0 (do NOT expose the stub - it binds provenance + slug contains temporal tokens). §24 gate rejects the stub. Fixed a latent Stage-1 bug: coverage/rr now read the resolved subject-entity label (`contract.claim_content_tokens`) so a faithful claim is recognized as carrying its fact (hand-built faithful batch -> sr=rr=pr=1.0; stub baseline byte-unchanged). 632 passed/11 xfailed/0 skipped (+11 CI-safe tests). NEXT: Slice 2 qr metric (ingest+search_ir) to complete the §24 gate, THEN the Stage-2 compile_nl floor rewrite measured by this spec contract. Prior: HISTORY#304 - PROTOCOL: the SEAM spec is now a mandatory session-start read and the GOVERNING CONTRACT. Root gap (operator: "this is supposed to be a rule" / "read the whole architecture and not assuming"): AGENTS.md Session-Start read order mandated only the PROCESS docs and OMITTED the product spec (`SEAM_SPEC_V0.1.md`, `docs/MIRL_V1.md`) that defines what SEAM IS - so agents/implementations drift (the overfit `compile_nl` stub; and this session I wrongly called RC/1 "broken" by measuring it against a token-reduction contract it was never meant to satisfy). Change: AGENTS.md Session Start item 6 + Context-Loop Phase 1 now require reading the spec when the task touches SEAM product behavior; REPO_LEDGER lead Stable Decision "The SEAM spec is the governing contract" (four-layer RAW/IR/PACK/LENS, north star = max durable intelligence per token, loss model RAW=phrasing/IR=meaning/PACK=utility, NL<->IR<->PACK<->NL translation, symbol/improvement loop, metrics cr/rr/sr/pr/tr/qr + §24 promotion rule; a component is "broken" only if it fails ITS contract - RC/1 = lossless+queryable+exact (token reduction is PACK + symbol loop + Track J), compile_nl genuinely violates §3.2+§8; new fidelity/metric harnesses align to §22/§24). Docs/protocol only, no runtime code change; integrity/routing/continuity/streams green. NEXT: re-ground compiler/fidelity Stage 2 in the spec - reconcile `benchmarks/fidelity` to §22 metrics + §8 recoverability + §24 before rewriting compile_nl. Prior: HISTORY#303 - MIRL compiler rewrite Stage 1: executable fidelity CONTRACT + the current stub's failing baseline. Operator-found bug: `seam_runtime/nl.py:compile_nl` (generic "remember" path: CLI/shell/dashboard/MCP-ingest/REST) is overfit to SEAM's self-description - for any real memory it slugifies the WHOLE input and asserts it as `project:SEAM`'s `goal` w/ hardcoded user:local_user+project:SEAM, so every ingest is the same skeleton. LoCoMo dodges it (uses richer compile_conversation_turn) but the self-improvement loop's own-corpus mode is poisoned + boilerplate ENT/CLM/STA crowd RAW. Landed (no compiler change yet): `benchmarks/fidelity/contract.py` 7 backend-agnostic checks (raw_verbatim, determinism, entity_extraction, subject_grounding, segmentation, separable_coverage, fact_grounding) + `golden.py` 5 cases w/ baseline_failures + `tests/fidelity/test_compile_fidelity.py` xfail(strict=True) the violations = ratchet (a fix flips xfail->XPASS->hard fail). Audit `docs/audits/2026-06-12-mirl-compile-fidelity.md`. Baseline: real memories FAIL entity_extraction+subject_grounding (+seg/cov/grnd multi-fact); the overfit self-description input passes the FULL contract (harness is fair). 621 passed/11 xfailed/0 skipped. NEXT: Stage 2 deterministic FLOOR rewrite (segment + verbatim RAW/SPAN + never fabricate) to green the xfails; backend = honest-minimal+opt-in Ollama (spaCy held optional, heavier hand-rolled ruled out). Prior: HISTORY#302 - PAID judged holdout Scorer built (operator correction: BUILDING it is part of the "benchmarks 100% functional" gate; only RUNNING stays operator-gated). `benchmarks/external/locomo/judged_scorer.py` JudgedLocomoScorer = same Scorer protocol, judge_score_mean (paid answerer + openai|claude judge, the #279 metric) on the HOLDOUT split; `tools/h2/paid_validation.py run_paid_validation` = stock-baseline vs applied/explicit candidate flags, verdict improved/regressed/within-noise (margin 0.02), candidate pass SKIPPED when flags equal baseline; `seam improve validate` CLI = zero-cost dry-run estimate by default, spends ONLY with --confirm-paid. Cost discipline: empty answers score 0.0 without a judge call; judge failures retry then raise. REPO_LEDGER records the never-auto-run gate. 596 passed/0 skipped (+10 CI-safe tests, zero API calls); real-dataset dry-run = 5 scopes/165 holdout cases/660 max paid calls. NEXT: the first actual paid run is operator-gated - suggested sequence: `seam improve cycle --locomo-dataset ... --auto-approve` to apply the #297 w_lexical+0.1 dev win, then `seam improve validate ... --confirm-paid` to judge it on holdout. Prior: HISTORY#301 - maintenance/closeout: PR #70 (the #300 SSRF hardening) is merged to main; CodeQL is fully CLEAR - all 10 alerts resolved (3 fixed by #298/#299, SSRF x2 dismissed won't-fix/mitigated, path-injection x4 dismissed false-positive, sessionStorage dismissed by-design) and 0 open Dependabot security alerts; merged Dependabot PRs #71/#72 (webui npm); FIXED the always-failing weekly Dependabot docker job (.github/dependabot.yml `docker` -> `docker-compose` ecosystem - `docker` never scans docker-compose.yaml; PR #73). Full suite on post-merge main 586 passed/0 skipped + secondary roots 103 passed. Zero open PRs, zero open alerts. Prior: HISTORY#300 - SECURITY: hardened the /chat SSRF/DNS-rebinding residual (CodeQL py/full-ssrf #3/#4). Host allowlist (built-in providers + loopback + operator-set SEAM_CHAT_ALLOWED_HOSTS) closes rebinding by construction; resolved-IP range check kept as defense-in-depth; outbound `_chat_opener` refuses 3xx redirects. Every built-in provider + loopback Ollama still validate and reach the call; only arbitrary attacker hosts are rejected. 586 passed/0 skipped (+5 SSRF tests). NEXT: PR + MEASURE whether CodeQL clears #3/#4 (if not, they're mitigated -> dismiss); the OTHER 5 alerts (path-injection x4 = sound is_relative_to containment FP + sessionStorage x1 by-design) are operator-approved for dismissal via gh api. Prior: HISTORY#299 - CORRECTION to #298: the dashboard.py info->debug log change did NOT clear py/clear-text-logging (CodeQL's logging sink is level-agnostic; PR #68 CodeQL check failed with the same alert re-flagged). Real fix: log only the argv token COUNT (`len(argv)`, an int that breaks the string taint), not the command content; full command stays in the operator UI audit trail (not a logging sink). The #298 workflow-perms + dsl ReDoS fixes stand. 581 passed/0 skipped. Prior: HISTORY#298 - cleared the three honest CodeQL findings (workflow least-privilege `permissions: contents: read` on external-memory-benchmarks.yml; ReDoS-flagged scalar regexes in dsl.py `_parse_scalar` replaced with linear backtracking-free `_is_int_literal`/`_is_float_literal`, parity-verified; dashboard.py shell-command log downgraded info->debug). 581 passed/0 skipped. Remaining 7 CodeQL alerts are operator-decision (SSRF x2 DNS-rebinding residual, path-injection x4 FP, sessionStorage token by-design) - NOT dismissed. Prior: HISTORY#297 - multi-conversation dev gate: the self-improvement loop now scores a pooled DEV split across N conversations (build_locomo_dev_scorer, --locomo-scopes default 5, --locomo-split dev) so proposals generalize and never tune on holdout; validated that it correctly REJECTS the single-conv semantic_zero win (#292) and instead finds w_lexical+0.1 as a generalizable gain. 581 passed/0 skipped. Prior: HISTORY#296 - SECURITY: resolved CRITICAL chromadb Dependabot alert (GHSA-f4j7-r4q5-qw2c) by removing chromadb from requirements.txt + all-extras (it stayed pinned at 1.5.7 in requirements.txt after #293); chromadb is now opt-in `seam[chroma]` only; SEAM uses only the embedded PersistentClient so the server-side vuln isn't reachable. Prior: HISTORY#295 - CI fix: httpx (lost when chromadb went optional in #293) restored for the server tests + pgvector job runs real-pgvector files explicitly. Prior: HISTORY#294 - strict no-skip test enforcement (tests/conftest.py, default on) + fixed 4 pgvector tests that silently skipped in CI forever (gated on PGVECTOR_TEST_DSN which CI never set); service tests now @external, main CI job runs -m "not external", pgvector job runs -m external with the DSN. Prior: HISTORY#293 - `seam improve cycle` CLI wires the self-improvement loop into the main CLI (self-probe default; --locomo-dataset opt-in; --auto-approve); chromadb moved from CORE deps to an optional `chroma` extra (SQLite vector adapter is the default; all chroma imports already lazy; chroma-real-smoke CI installs the extra). 572 passed/4 skipped. Prior: HISTORY#292 - the FREE-LoCoMo scorer that makes the loop ACTUALLY IMPROVE. `benchmarks/external/locomo/recall_scorer.py LocomoRecallScorer` implements the Scorer protocol on the real adapter, reusing the adapter's answer() path + `common.scoring.context_recall` (free, answerer=None, no judge), ablating candidate flags via a `rt._retrieval_flags` override. VALIDATED no-paid on conv-26/25q: baseline locomo_recall 0.4624; the proposer auto-found `semantic_zero_no_vector=True` +0.040 (no regression) and `w_semantic+0.1` +0.040 as improvements, correctly REJECTING `fusion=rrf` (+0.056 but category-regresses) and `bm25_all` (-0.019) - select_best_improvement = {semantic_zero_no_vector: True}. The free always-on loop now finds + keeps real gains. CAVEAT: single-conv/SQLite; #273 shows the gain is category-mixed at full scale, so production needs a multi-conversation dev gate + operator-gated paid validation for big claims. 568 passed/4 skipped)

Prior (HISTORY#291 - H2 front half: the proposer + no-regression ratchet. `run_improvement_cycle` (tools/h2/improvement_loop.py) generates candidate levers, evaluates them against free scorers with a per-category no-regression gate, proposes the best genuine improvement, and (auto-approve opt-in) applies via #289 then auto-reverts if the reconciled state regresses - so applied scores only move up or flat. Scorer-agnostic: free scorers (self-probe now, free-LoCoMo next) drive the always-on loop; paid judged scorers plug into the same list for operator-triggered validation ("both"). Per #290 the free levers have ~no headroom, so wired to self-probe the loop is a correct free regression WATCHDOG; actual score movement needs the free-LoCoMo scorer and/or the paid tier. 565 passed/4 skipped)

## Current State

SEAM is operating as a local machine-first memory runtime with:

- MIRL compile/verify/persist/search/context flows in production use
- External memory benchmark registry + `seam bench external` CLI alias (Track I SOP 0 landed)
- SEAM LoCoMo adapter with string-match scoring + 60s quickstart fixture (Track I SOP 1 landed; `seam bench external --quickstart locomo`)
- Track M P0 standard benchmark readiness: top-level `seam bench external locomo|longmemeval|beam` dataset dry-run routing, mem0 harness adapter contract, pgvector composite-PK upgrade migration, LoCoMo source-evidence retrieval fix, and publication readiness gate requiring non-stub judge plus BIL-2 verification.
- Benchmark Integrity Level Phase 1 support: deterministic BIL-0 inspection plus BIL-1/BIL-2 benchmark sealing, verification, and inspection via `seam bench seal|verify|inspect`
- Full Textual interactive TUI dashboard with chat panel, command palette (/, !, ?), MIRL animation, independently scrollable panes, IDE-style explorer tree, status bar, colored RichLog panels, focus zoom toggle, runtime-smoked Settings tab, and live Overview health bars for database, pgvector, API/config, and settings paths
- Dashboard chat with expanded OpenRouter model defaults (Qwen, DeepSeek, MiMo, Kimi, GLM, Claude, Gemini, Grok, Gemma, Pareto Code Router)
- First interactive agent-style CLI shell: `seam shell` / `seam chat` for persistent memory remember/search/context/stats/doctor workflows
- lossless SEAM-LX/1 compression with integrity verification
- SEAM-HS/1 Holographic Surface PNG snapshots with automatic source-to-MIRL surface compile, direct MIRL/RC query, verify, decode, context, and import commands
- HS/1 surface library adapters landed on `main`: stored surface metadata, stable `hs:<hash>` IDs, list/show/repair, redundant file-backed copies, and direct query/verify/decode/context/import by stored ID
- Roadmap now includes Track G5: a planned zero-ops multi-surface `.seam.png` library index plus drift verifier, replayed from the completed local `roadmap/g5-multi-surface-index` branch onto current `main`.
- Cat4 single-hop attribution audit is recorded at `docs/audits/2026-05-31-cat4-single-hop-attribution.md`: capture-adapter/RAW-vs-abstract crowding is not the measured lever; pack budget is the dominant retrieval-context-recall throttle and needs judged validation before product claims.
- Semantic recovery policy/diagnostic experiment is recorded at `docs/audits/2026-06-01-semantic-recovery-policy-experiment.md`: explicit default-off LoCoMo adapter flags now label pack-budget/deep-candidate runs, saved reports/events include retrieval-policy diagnostics, and the no-paid grid measured baseline `0.623668`, pack-budget `0.682864`, deep-candidates `0.624195`, and pack-budget-deep `0.758217` context_recall_mean.
- Paid LoCoMo slice validation is recorded at `docs/audits/2026-06-01-paid-locomo-slice-validation.md`: a balanced 100-case OpenAI answerer+judge A/B improved judge_score_mean from `0.44` baseline to `0.57` for pack-budget-deep, with zero judge errors. This supports a full paid A/B only if the operator accepts the spend.
- benchmark diff tooling, pass/fail gate tooling, publish-only holdout fixture routing, and tracked CI coverage
- optional FastAPI/Uvicorn REST API surface for local compile, search, context, stats, health, persist, and lossless-compression workflows, with bounded request bodies and authenticated remote-bind guardrails
- agent-safe MCP stdio server for Gemini/Claude/Cursor-style MCP clients, plus legacy JSON-lines bridge, with compact memory search/get, ingest, stats, document status, context packing, doctor, stored HS/1 surface operations, and benchmark run summaries
- PgVector support running locally via Docker Compose on port 55432; installer coverage across Windows/Linux paths
- Linux installer supports two explicit modes: default global command install under `~/.local/share/seam`, and `--dev` repo-local Python bootstrap with the external-drive `.venv/lib64` fallback plus SEAM protocol verification; WebUI setup remains separate.
- Competitive RAG/install polish in progress on `codex/competitive-rag-install-polish`: one-line private install docs, product-first README, document status tracking, progressive memory search/get, `retrieve --mode vector|graph|hybrid|mix`, stdio agent bridge, and vector stale-index reporting
- Active/inactive code and docs separation enforced via `docs/CODE_LAYOUT.md`, `.rgignore`, and archive paths
- The browser dashboard is the functional SEAM GUI, served by the REST server itself: `seam serve` (or `seam webui`, which also opens the browser) serves `seam_runtime/webui/dashboard.html` at `/` on the same origin as the API — no Node, Vite, or CORS at runtime. `dashboard.html` is a self-contained in-browser React app (React+Babel from CDN, no build step). The Vite/React dev project lives at `webui/` (its `publicDir` points at `seam_runtime/webui/`); its `src/` panes remain rework material, but the shipped working UI is the static dashboard. `experimental/` has been removed entirely.
- Adaptive SEAM Skill Factory foundation is merged on `main` through PR #21; current tracked code includes Skill Factory primitives and roadmap docs under `seam_runtime/skills/` and `docs/roadmap/SKILL_FACTORY.md`.

## Current Resume Point

- `main` is the source-of-truth branch. After pulling, verify local `HEAD` equals `origin/main` before starting new work.
- Latest continuity handoff is `HISTORY#341` — **README placeholder cleanup.** The README no longer advertises unpublished `example.com/seam` public installer placeholder commands; the Operator Manual section is now the explicit help path beyond the quickstart; audit tests pin both. Resume: push the packaging/docs slice through a PR once the branch's unrelated mem0/history work is intentionally included or separated.
- Prior continuity handoff is `HISTORY#340` — **Agent setup prompt + operator manual/error index discoverability.** README now gives downstream coding agents a setup prompt for install, `seam doctor`, persistent-memory ingest, memory retrieval/context checks, and MCP setup; it explicitly says API keys plus local `.env`/`.conf` files are operator-owned and can be set through `seam webui` Settings or manually in ignored local config. `docs/errors.md` now has a top-level Error Index including `HTTP 429` provider quota/rate-limit handling.
- Prior continuity handoff is `HISTORY#339` — **GitHub-first package install path.** The `seam-runtime` package metadata now points at `BlackhatShiftey/Seam_Runtime`, README documents direct GitHub `pip install` commands, `MANIFEST.in` covers the source-distribution assets, and CI has a `package-smoke` wheel/sdist build+install job.
- Prior continuity handoff is `HISTORY#338` — **mem0 retrieval depth knob.** The mem0 LoCoMo adapter keeps default retrieval depth at `8`, but now honors `SEAM_BENCH_MEM0_SEARCH_LIMIT` and the runner's `--mem0-search-limit` for controlled fairness diagnostics. Resume: run a small explicitly gated mem0 diagnostic slice with higher top_k before spending on another full judged run.
- Prior continuity handoff is `HISTORY#337` — **macOS installer support.** The installer now has a macOS shell entrypoint, macOS runtime layout under `~/Library/Application Support/SEAM`, a macOS bootstrap hint in generated shims, README/install docs, and targeted installer regression tests. Resume: tree should be clean after the macOS installer commit; mem0 rate-limit resilience is the prior commit on this branch.
- Prior continuity handoff is `HISTORY#336` — **MEM0 RATE-LIMIT RESILIENCE.** The LoCoMo mem0 comparator and shared benchmark harness now retry transient provider failures, pace real mem0 ingest, pin mem0's LLM default to `gpt-4o-mini`, checkpoint after batch judging, and record scope/answer failures as per-case error rows instead of aborting and losing paid-run work. Resume: continue mem0 diagnosis/productization separately from unrelated local macOS installer edits.
- Prior continuity handoff is `HISTORY#335` — **MEM0 ADAPTER FIX (mem0 2.x API drift) — caught by the rung-C pre-flight.** The LoCoMo mem0 comparator was broken vs installed mem0ai 2.0.2 (`search()` dropped top-level `user_id` → `filters={'user_id':…}`, and `limit`→`top_k`) so every mem0 retrieval threw — a paid SEAM-vs-mem0 run would've produced all-empty mem0 answers. Probed the full API (only `search` drifted), fixed it, re-smoked end-to-end (mem0 → 'Teal' → judge correct). Also fixed 2 exposed test issues: the `_StubMem0.search` signature, and `test_cli_quickstart_mem0_stub` which *secretly spent money* (ran a real quickstart once mem0 worked) — hardened to run keyless ($0). 14/14 mem0 tests + full suite green. On branch `bench/mem0-adapter-2x-fix` (PR). **Resume: rung C running — SEAM(broad)-vs-mem0 on half of LoCoMo-10 (5 convos/764 Q, ~$4); report the head-to-head; if SEAM≥mem0, productize the broad profile → core RetrievalFlags + consider the full ~1540-Q run.** Prior handoff is `HISTORY#334` — **RUNG B: broad WINS (judged) + the context_budget confound fix.** Fixed `JudgedLocomoScorer` dropping `context_budget` (broad got compact's 8000-char context = a fake broad; free probe 1.00x→5.07x after fix). Then the paid judged A/B (gpt-4o-mini, 100 holdout cases across all 10 convos, ~$0.29): **compact 0.465 vs broad 0.535, Δ+0.070** (3.5× the 0.02 margin), win concentrated in **cat1 (+0.098)**. Confirms answerer-aware: weak local→compact, capable cloud→broad. +2 tests; full suite green. On branch `bench/judged-context-budget-fix` (PR). **Resume: rung C — SEAM(broad)-vs-mem0 full LoCoMo-10 (~1540 Q), the bigger paid run (mem0 per-turn extraction $$); estimate + explicit go before launching. Productize: the capable-answerer broad profile → core RetrievalFlags.** Prior handoff is `HISTORY#333` — **STRAND C STEP 1 + fair-comparison harness fix:** pinned mem0's real LoCoMo target ≈ 66.9% LLM-judge / gpt-4o-mini (mem0 paper arXiv 2504.19413), NOT the 91.6 the roadmap wrongly carried (corrected `docs/roadmap/COMPETITIVE_ROADMAP.md`); and fixed the blocker that made a judged head-to-head meaningless — the answerer was baked into the SEAM adapter so mem0/zep (`generated_answer=None`) were judged on an empty string (~0). New `benchmarks/external/common/answerer.py` (single-source `build_answer_prompt` + `generate_short_answer` + `SharedAnswererAdapter`); `run.py` `_maybe_wrap_answerer` wraps mem0/zep when an answerer is set; `seam.py` `_generate_answer` sources the shared prompt so every adapter answers identically (only retrieved context — the memory layer — varies). Benchmark-harness only, no core change; +8 CI-safe tests; full canonical suite + PGVECTOR green, 2 xfails, 0 failures; on branch `bench/fair-shared-answerer` (PR). **Resume: Strand C — (1) optional free capable-answerer loop confirmation (pulls toward broad); (2) operator-gated PAID SEAM(broad)-vs-mem0 head-to-head, now meaningful because both sides are judged on the same answerer.** Prior handoff is `HISTORY#332` — **STRAND B: the retrieval profile is loop-tunable by a free answer-quality scorer (#328 A→B→C plan, B done):** new dilution-sensitive `PooledLocomoAnswerQualityScorer` (local-Ollama generated-answer `token_f1`, `profile_safe=True`); `candidate_levers(profile_levers=)` proposes the `compact`/`broad` presets; `run_improvement_cycle` enables them ONLY when every scorer is `profile_safe` (never the budget-gameable self-probe/`context_recall`); CLI `seam improve cycle --locomo-answerer ollama` (profile tuning also needs `--probe-sample 0`). Free-validated (~$0, qwen2.5:3b): `compact` token_f1 0.342 vs `broad` 0.028 (+0.314 — broad collapses the weak answerer); e2e loop `profile_levers=true`, broad correctly NOT selected; +7 CI-safe tests; full canonical suite + PGVECTOR green (2 xfails, 0 failures). **Resume: C = operator-gated PAID SEAM(broad)-vs-mem0 head-to-head on LoCoMo (the free answer-quality scorer is the pre-gate; the paid judge is the last lever); optionally run the loop with a CAPABLE answerer to confirm it pulls toward broad.** Prior handoff is `HISTORY#331` — **SEAM ENGINEERING MANUAL landed + finalized (PR #96):** the `docs/engineering/` manual + `skills/seam-engineer/` routing skill are now on `main`; finalization split the 5 inlined templates into their own files (resolving the dangling SOP links), rewrote `templates/README.md` into an index, and added a `docs/README.md` discoverability pointer; this also closes the dangling `09_EPISTEMIC_CALIBRATION.md` reference `main` has carried (in `calibration_scorer.py`) since #325; docs-only, gates green. Prior handoff is `HISTORY#330` — **CODEQL FIX (alert #13, insecure tempfile):** swapped `tests/audit/test_retrieval_flags.py`'s deprecated `tempfile.mktemp()` for the pytest `tmp_path` fixture (high-severity TOCTOU CodeQL alert in a test); 16/16 pass, no other `mktemp` in active code, no runtime change; the alert closes once this is on `main`. Prior handoff is `HISTORY#329` — **PROGRESS TABLES:** added `docs/progress_tables/` as a tracked scan-friendly data-table layer for SEAM progress. `test_runs.csv` starts with 6 verification rows; `benchmark_results.csv` starts with 8 LoCoMo/retrieval/calibration rows; `milestones.csv` starts with 9 durable progress claims. The README records the rule that `HISTORY.md` remains authoritative and table rows cite history/evidence/next steps. CSV parse verification passed with Python `csv.DictReader`; no runtime code changed. **Resume:** append rows after material test/benchmark/milestone sessions; if the tables drift or grow too large, add a tiny validator/exporter. Prior handoff is `HISTORY#328` — **CORE answerer-aware retrieval profiles (compact/broad) (operator: "build the core first ... for it to actually improve agent memory ... topping the charts ... same scale as mem0"):** productized the holdout-validated capable-answerer knee into core `RetrievalFlags` (Strand A of the agreed A→B→C plan: A core profile / B loop-tune over time / C beat-mem0). `RetrievalFlags` gains `context_budget: int | None` alongside `search_top_k`; `RETRIEVAL_PROFILES = {compact:(100,8000), broad:(300,60000)}` + `resolve_retrieval_profile()`; env `SEAM_RETRIEVAL_PROFILE=compact|broad` sets the pair in BOTH env paths (`_retrieval_env_overrides`→`load_retrieval_flags`→every core surface CLI/REST/MCP/dashboard; and `retrieval_flags_from_env`→benchmark adapter); explicit `SEAM_RETRIEVAL_TOP_K`/`SEAM_RETRIEVAL_CONTEXT_BUDGET` override. `search_top_k` already honored in `search_ir` (#320) so the profile reaches every surface automatically; `context_budget` honored in `runtime.pack_ir` (budget=None→flags.context_budget else prior 512). Both are CONFIG knobs, NOT self-improvement candidate_levers (would game the #290 self-probe); tuned by free-LoCoMo answer-quality + paid judge. **No regression:** no profile → both None → byte-identical (`load_retrieval_flags(None,{})==RetrievalFlags()`). Holdout-validated cat1 (61 never-tuned cases, clean per-scope SQLite, real OpenAI judge): 0.566→0.705 (+0.139) at broad, where the SAME broad context COLLAPSED a weak 3B answerer (0.46→0.10) = dilution is a weak-model artifact → the right knee is answerer-dependent. CORE not benchmark: a live MCP agent with `SEAM_RETRIEVAL_PROFILE=broad` now retrieves deeper on its real recalls. +6 tests in `tests/audit/test_retrieval_flags.py`; REPO_LEDGER stable decision added; full canonical suite + PGVECTOR green (2 known xfails, 0 failures, 0 skips). **Resume: B = wire profile knobs into the self-improvement loop via the free-LoCoMo answer-quality scorer (NOT self-probe); C = SEAM(broad)-vs-mem0 head-to-head on LoCoMo (mem0 2.0.2 + adapter present; mem0 per-turn LLM extraction = $$, operator-gated).** Prior handoff is `HISTORY#327` — **judge bug fix (operator: "fix the judge bug first ... we want solid architecture"):** `benchmarks/external/common/judge.py` `OpenAIJudge` hardcoded `reasoning_effort="minimal"` (+ `max_completion_tokens=512`) in both `score()` and `_build_batch_request()`; gpt-5.4+ models reject `minimal` (only none/low/medium/high/xhigh) → BadRequestError 400, so the judge path was broken for current OpenAI models (this key exposes only gpt-5.x/o-series, no gpt-4o fallback). The **missed half of the #321 answerer fix** (same hardcode in `_openai_short_answer`). Fix: new module-level `_openai_judge_reasoning_params()` reads `SEAM_BENCH_JUDGE_REASONING_EFFORT` (fallback `SEAM_BENCH_REASONING_EFFORT`, default `low`) + `SEAM_BENCH_JUDGE_MAX_COMPLETION_TOKENS` (default 512), applied at both sites; `ClaudeJudge` unaffected. Tests: `tests/audit/test_openai_judge_gpt5.py` pin updated (gpt-5 default minimal→low) + 3 new (env override, shared-env fallback, batch mirror); full canonical suite + PGVECTOR green (2 known xfails, 0 failures, 0 skips). **Found during the cat1 free→paid investigation: cat1's wall = a weak local answerer + a retrieval knee tuned for it, NOT SEAM retrieval** — real OpenAI-judge cat1 dev **0.525 at the old knee → 0.670 at a capable-answerer knee (top_k=300/budget=60000)**; the "raising top_k dilutes" finding was a weak-model artifact (the 7b collapsed, a capable model rises). All cat1 paid runs were gpt-5.4-mini, ~cents total; banked to memory. **Resume: holdout-validate the capable-answerer retrieval profile, then productize it as an answerer-aware RetrievalFlags lever (tighten context for small models' recall; broaden for big models).** Prior handoff is `HISTORY#326` — **webui consolidation (operator: "fix the mess... if it's not touching anything running it's okay"):** the repo had **four** webui-ish locations causing a "which one is real?" mess. Consolidated to **one canonical** with the running server untouched. **THE webui (unchanged, serving): `seam_runtime/webui/dashboard.html`** — a single self-contained hand-authored file (CDN React) + `seam-api.js`/assets, served by `seam webui`/`seam serve` at `http://127.0.0.1:8765/` (`server.py:webui_dir()`, override `SEAM_WEBUI_DIR`; shipped via pyproject package-data). **Deleted** untracked strays `Webui-final-dash/`, `webui/dist/` (stale build that *differed* from the served file), `webui/node_modules/`. **Archived** the diverged top-level `webui/` Vite/React project → `archive/webui-vite-source/` (reversible `git mv`, 24 files; its build had diverged from the served file and its React rewrite was a reverted regression per RESTORE_NOTES) with an `ARCHIVED.md` pointer. Dropped the stale `/webui` npm Dependabot entry; rewrote `docs/CODE_LAYOUT.md`'s WebUI section. **Constraint honored:** verified the uvicorn server stayed healthy (health 200, title intact) after every move — only top-level `webui/` + untracked dirs were touched, never `seam_runtime/webui/`. Launched both surfaces this session (browser webui at :8765 + the `seam dashboard` TUI snapshot). Full canonical suite + PGVECTOR green; no Python changed; no paid run. **Resume: cat1 is SYNTHESIS/PACKING-bound (Phase 0 this session — 82% of cat1 gold evidence retrieved yet all-gold-retrieved token_f1 ~0.20) → next lever is an entity-aggregation dossier PACKER, not the ingest-coreference rebuild; record the Phase 0 diagnostic; PR #96 should merge; meshyface patterns banked as a future ops-hardening track.** Prior handoff is `HISTORY#325` — **calibration scorer (PR #96's epistemic policy made executable) + a benchmark-integrity fix:** operator asked to build the `CalibrationScorer` and "use #96 to progress SEAM." Done — `benchmarks/external/locomo/calibration_scorer.py` turns `docs/engineering/09_EPISTEMIC_CALIBRATION.md`'s reward matrix into measured selective-prediction metrics (coverage, selective accuracy, abstention precision, hallucination rate, calibration utility) over the LoCoMo split, FREE (local Ollama, deterministic). **The build exposed a real bug:** the shared loader's `if "answer" not in qa: continue` silently dropped all **444 cat5 adversarial cases** (they carry only `adversarial_answer`) — so on real LoCoMo the unanswerable arm *did not exist* and any abstention metric would have scored an empty set and looked falsely clean (the exact failure the policy warns about). Fixed with opt-in `load_locomo_cases(include_unanswerable=False)` — **default-off, every existing caller byte-identical** (verified 1542/0-empty), opt-in yields the full 444-case arm. **Real measurement** (qwen2.5:3b, 15 conv-26 adversarial cases): abstained correctly on 11, **fell for the trap on 4 → hallucination_rate 0.267**, abstention_precision 1.0 — a concrete defect number previously invisible. 11 CI-safe hermetic tests incl. the policy thesis (abstain-on-everything 0.2 vs calibrated 2.0). Also landed the cat1/GraphRAG coreference **blueprint** doc (`docs/audits/2026-06-17-cat1-coreference-graphrag-blueprint.md`; findings, not a build spec). Benchmarks+docs only, core/runtime unchanged; full canonical suite + PGVECTOR green; no paid run. Methodological note: cat5 is sparse per-conversation → pool many scopes for production calibration. **Resume: cat1 cross-turn entity coreference at INGEST (the 80% lever); v2 calibration = capture `adversarial_answer` for "fell-for-specific-trap" + multi-scope pooled run; PR #96 should merge (its 09 doc is this scorer's spec); evaluate the operator's meshyface operational-hardening patterns.** Prior handoff is `HISTORY#324` — **`seam doctor` stash advisory (operator-requested hygiene):** the #323 "is everything pushed/merged?" sweep surfaced a 3-week-old abandoned stash (a 2026-05-27 server-hardening WIP, ~85% since independently re-landed; operator dropped it) that had lingered unseen because **stashes are invisible to `git status`, `git log`, and branch/PR listings** — more likely in a multi-agent repo where an agent stashes to clear a tree mid-context-switch. `check_stashes()` (`seam_runtime/doctor.py`) runs `git stash list` and reports clean/advisory/not-a-git-repo with per-stash age + summary; `cli.py` renders a "Stashes: N present (oldest Xd)" line. **Advisory only — never flips PASS/FAIL** (like `commit_gate`/`streams`); **deliberately NOT added to the blocking commit gate** (a stash during active work is legitimate, so it'd be a false positive — hygiene advisories belong in the health check, correctness gates in the blocking gate). AGENTS.md Session End gains a stash-hygiene line. +5 CI-safe hermetic tests (`tests/audit/test_doctor_stashes.py`, monkeypatched subprocess). Benchmark-unaffected; full canonical suite + PGVECTOR_TEST_DSN green (1089 passed/2 xfailed/3 subtests). Also this session: read + reviewed PR #96 (operator's SEAM engineering manual + `seam-engineer` skill, "meshyface"-modeled) favorably — accurate to the real architecture/protocol, well-designed anti-drift (routes to canonical sources, doesn't hardcode commands/counts per the #310 lesson); still draft pending its own SEAM-chain entry + a discoverability pointer + making restated rules cite AGENTS.md as authority. **Resume: the campaign main thread is unchanged — the cat1 ingest-coreference rebuild (cross-turn entity coreference at INGEST) toward the 80% milestone; complete PR #96's chain when ready.** Prior handoff is `HISTORY#323` — **entity-aggregation lever = marginal (cat1), landed default-OFF:** prototyped + hardened the retrieval-time entity-aggregation lever (the #321 NEXT) and measured it FREE end-to-end — it's a **wash on cat1 answer quality**. Free recall A/B (deterministic, 10 convs/1198 q): cat1 +0.025, cat3 +0.034 (targeted rescue of ~7-12% of cases, but additive set-based recall **overstates**). Free local-answerer A/B (qwen2.5:3b token_f1) was decisive: the first +0.012 was **noise** (qwen non-deterministic at temp=0, 11/15; re-run flipped to −0.003), fixed with seed+top_k=1; deterministic cat1 raw −0.0054, hardened (query-rank+stem, cap20) +0.0055 (25/25 even) = at the noise floor. **The retrieval-time string-match hack is marginal; the ceiling is the ingest rebuild.** Wins are real coreference re-attribution fixes (validating the concept). **No paid run** (free settled it; rule refined: paid is the last lever). Landed default-OFF (`SEAM_BENCH_ENTITY_AGG`) + the free answer-quality infra (local Ollama answerer + seed determinism + token_f1 A/B — the durable win) + 7 CI-safe tests + audit `docs/audits/2026-06-15-entity-aggregation-retrieval.md`. Benchmark-adapter only. **Resume: the REAL cat1 lever = cross-turn entity coreference at INGEST (stable ids across per-turn mentions + aggregation over real `ir_edges`); scope + free-validate before building.** Prior handoff is `HISTORY#322` — **test artifact routing cleanup:** root `test_pgvector_*` SQLite sidecars moved to ignored `test_seam/pgvector/`; tracked testing documentation now lives under `tests/docs/`; future disposable generated test outputs route to ignored `test_seam/<area>/`; pre-existing dirty benchmark adapter code was not touched. Prior campaign handoff is `HISTORY#321` — **push toward 80% (campaign): the answerer ladder taps out at ~0.60; cat1 is a retrieval-ARCHITECTURE wall.** Past the #320 budget knee (0.52): gpt-4o answerer **0.595** (win is *all* cat2 temporal 0.50→0.70; cat1 flat 0.44), o4-mini reasoning **0.595** (cat1 still 0.48), cross-encoder rerank **0.0** recall delta. cat1 (32% of cases, ~0.46) moves for **no** answerer — the real failures are **attribute/aggregation** ("what activities does X do?" = a list scattered across the convo) + **needle single-facts**; recall 0.69 overcounts. **Root blocker to 0.80: SEAM has no cross-turn entity coreference + entity-aggregation retrieval** (per-turn ingest → each turn's "Melanie" is a different id; zero `ir_edges`). Cheap-lever ceiling ~0.60–0.68; **0.80 needs the ingest+retrieval rebuild, not a knob** (and it's near-SOTA on LoCoMo cat1). Found + fixed two real adapter bugs (the reasoning-answerer path was entirely broken: hardcoded `reasoning_effort='minimal'` + 256-token floor; both now env-configurable). gpt-4o answerer (0.595) is the measured best but **not** made default (cost). Benchmark-adapter only; suite unchanged (1077). **Resume (campaign toward 80%): the cat1 retrieval rebuild — cross-turn entity coreference (resolve same-entity ids across per-turn ingests) + entity-aggregation retrieval (gather all claims about a subject). Scope + TEST cheaply (free cat1 recall) BEFORE building. Open: core `search_ir` default bump (own measurement), Stage 5, server graceful-shutdown gap, Track O (BIRD).** Previously: HISTORY#320 — cat1/cat3 root-cause fix, validated: it was retrieval BUDGET STARVATION. The whole "multi-hop" framing was a mis-diagnosis. Chain: paid baseline **0.40** → bucket failures (retrieval-miss, answerer abstains honestly) → free Phase-0 *killed* the decomposition hypothesis (#319: hurt −0.169) → free top_k/budget sweep (recall climbs hard, cat1 0.385→0.842) → paid confirm it converts to answer quality → tested the knee → productized → **reproduced**. Paid judged curve (100 cases, gpt-4o-mini): **20/2000 ≈ 0.39 · 100/8000 (knee) ≈ 0.53 (four runs 0.52/0.525/0.52/0.54) · 200/20000 = 0.525 (flattens — answerer saturation past the knee).** Clean cluster separation, **+0.14 reproduced, not a lucky draw**; cat1 0.22→0.44 and cat3 0.23→0.42 both double. Build: `RetrievalFlags.search_top_k` (env `SEAM_RETRIEVAL_TOP_K`) honored by `search_ir` — **deliberately NOT a self-improvement lever** (it would game the #290 self-probe candidate-set gold); benchmark defaults bumped to the knee; **core `search_ir` default (budget=5) left unchanged** (CLI/REST unbenchmarked = measured follow-up). Full suite green (1077 passed, +2 tests). **The cat1/cat3 retrieval-quality arc is CLOSED (0.40→0.52 validated).** **Resume: optional — bump the CORE `search_ir` production default behind its own CLI/REST measurement (budget=5 is starved). Track O (BIRD/query engine) banked for after. Open: Stage 5 (degenerate `./seam.db`), the server graceful-shutdown wiring gap, density slice 4 (refs-hash).** Previously: HISTORY#319 — ROADMAP Track O (SEAM Query Engine) + cat1/cat3 Phase 0 negative result. (A) Added **Track O — SEAM Query Engine** (execution-verified NL→typed-plan→SQL) to `ROADMAP.md`, planned/consider, priority 3, after the recall work; **BIRD is its measurement-first first deliverable** (a query-benchmark slot separate from the conversational memory registry; execution-accuracy scorer + fixture build now, the SQL generator `NOT_CONFIGURED` until the engine exists — so the harness is built *with* the track, not before it). (B) **Phase 0 multi-hop validation = decisive negative:** qwen2.5:3b decomposition over 259 questions *hurts* recall (0.6119→**0.4429**, −0.169; cat1 −0.226, cat4 −0.223) because the sub-queries' off-topic records **displace** the correct evidence at the fixed context budget. Also confirmed `compile_nl` emits **zero `ir_edges`** (flat ingest) so L2 graph-closure has no graph to walk, and LoCoMo ingests per-turn (entities don't co-refer). **Both naive multi-hop levers (decomposition + edge-closure) are out** — cat1/cat3 is a ranking/budget/displacement problem, not too-few-queries. Phase 0 (validate-before-build) prevented productizing a regression. **Resume: re-aim cat1/cat3 — test (free) a larger context budget / original-query-first merge to avoid displacement, or diagnose whether the gold evidence is retrievable at higher top-k at all (if not, the lever is ranking/expansion, not more queries). Track O (BIRD/query engine) banked for after the recall work. Open: Stage 5 (degenerate `./seam.db`), server graceful-shutdown gap, density slice 4 (refs-hash).** Previously: HISTORY#318 — SCOPING (docs only): cat1/cat3 multi-hop retrieval + SQL2-for-SEAM learnings. The paid run (judge **0.40**) is the live baseline; the ingest pipeline is verified working + clean post-#317. **(1) Multi-hop scope** (`docs/audits/2026-06-15-cat1-cat3-multihop-scope.md`): diagnosed cat1 multi-hop (12 retrieval-miss / 6 answerer-miss) + cat3 open-domain (8 / 1) as **retrieval-bound** — the answerer abstains honestly, so the lever is getting evidence into context. The multi-hop machinery **already exists but is dormant + benchmark-only** (`adapters/seam.py:_decompose` was OFF in the baseline; closure follows evidence/prov ids not `ir_edges`; core `search_ir` is single-shot). Levers: L1 query decomposition (model-independent, Ollama default), L2 free graph-edge closure expansion, L3 cat3 query expansion. Plan: Phase 0 validate cheaply in the adapter (free recall vs 0.372 / 0.261) **before** building → Phase 1 productize the winner into core `search_ir` + `RetrievalFlags` (so the self-improvement loop can tune it — it can't today). **(2) SEAM Query Engine** (`docs/roadmap/SEAM_QUERY_ENGINE_SQL2_LEARNINGS.md`): SQL2 learnings for SEAM overall, a **future track** — kernel = NL→typed query plan→deterministic compile→verified execution→provenance result; take the typed plan + read-only sandbox + provenance traces, leave the candidate-consensus ensemble / schema-linking / fine-tuned specialist; after the recall work. **Resume: cat1/cat3 Phase 0 validation (free Ollama decomposer + edge-traversal prototype, measure recall lift), then Phase 1 productize. Open: Stage 5 (degenerate `./seam.db` records), the server graceful-shutdown wiring gap, density slice 4 (refs-hash), the SEAM Query Engine track (future).** `backup/local-pgvector-bootstrap` = KEEP.
- Prior handoff `HISTORY#307` - **MIRL compile fidelity Slice 2: the `qr` (retrieval_success@k) metric landed, completing the §24 promotion gate.** `spec_metrics.retrieval_quality()` persists the compiled batch into a hermetic temp runtime → `search_ir` → is a gold record in top-k?, opt-in via `measure_qr=True`. Baseline with qr: every case `qr=1.0` (qr is "no worse than baseline", not a fabrication discriminator). Full suite 638 passed / 11 xfailed / 0 skipped.
- Prior handoff `HISTORY#305` - **reconciled the MIRL compile fidelity harness to the SEAM spec's own metrics (§22/§24).** Per the #304 governing-contract rule, the compiler is now measured by the spec's contract, not my ad-hoc Stage-1 properties. NEW `benchmarks/fidelity/spec_metrics.py` implements §22 `cr` (NL→PACK compression ratio, the north-star density), `rr` (reconstruction), `sr` (semantic retention via structured subject/relation/object triple match — catches fabrication a word-overlap score misses), `pr` (provenance chain), `tr` (temporal), plus the §24 `passes_promotion` gate (`sr≥0.98, pr=1.00, tr≥0.99, qr≥baseline, cr strictly>baseline` — "denser only when recovery is proven"). `qr` (retrieval_success@k) is DEFERRED to Slice 2 (needs the ingest+`search_ir` harness; it's the "directly queryable" axis). The 7 Stage-1 checks are documented as diagnostic COMPONENTS of the metrics. **compile_nl spec baseline:** `sr=0.333` on every real memory (subject `project:SEAM` + predicate `goal` fabricated, only the slug object matches = 1/3) vs `1.0` on the overfit input; `cr=0.018–0.040` (packed IR is 25–55× the source — the token inflation the Stage-1 harness missed); `pr=tr=1.0` and do NOT expose the stub (it binds provenance; its slug contains the temporal tokens) — honest: the stub's sin is faithfulness+density. The §24 gate rejects the stub on every case. Fixed a latent Stage-1 bug so the contract is satisfiable by a correct compiler: coverage/`rr` now read the resolved subject-entity label (`contract.claim_content_tokens`), so a faithful `(ent:priya, owns, billing_service)` claim is recognized as carrying its fact — a hand-built faithful batch scores `sr=rr=pr=1.0` and passes every check; the stub baseline is byte-unchanged. Verified: 11 new CI-safe tests; Stage-1 harness still 25 passed/11 xfailed; full suite 632 passed/11 xfailed/0 skipped. **NEXT: Slice 2 — add the `qr` retrieval-quality metric (ingest + `search_ir`) to complete the §24 gate, THEN the Stage-2 deterministic floor rewrite of `compile_nl` measured by this spec contract (target: raise `sr`→~1.0 and `rr`→1.0 on real memories, keep `pr=1.0`, improve `cr`).**
- Prior handoff `HISTORY#304` - **PROTOCOL: the SEAM spec is now a mandatory session-start read and the governing contract.** Operator-flagged ("this is supposed to be a rule" / "read the whole architecture and not assuming"): the AGENTS.md Session-Start read order required only the process/continuity docs and omitted `SEAM_SPEC_V0.1.md` + `docs/MIRL_V1.md` — the documents that define what SEAM IS — so agents and implementations drift from the design (the overfit `compile_nl` stub; and, this session, my own wrong call that RC/1 is "broken" — it meets its lossless+directly-queryable+exact-rebuild contract; token reduction was never RC/1's job, it lives in PACK + the symbol/improvement loop + Track J). Change: AGENTS.md Session Start item 6 + Context-Loop Phase 1 now require reading the spec when the task touches SEAM product behavior (compilation, MIRL/IR, compression, PACK, retrieval, surfaces, codecs, the symbol loop, benchmarks, or any design/measurement claim); REPO_LEDGER gains the lead Stable Decision **"The SEAM spec is the governing contract."** Recalibration: the `compile_nl` bug is a REAL violation of §3.2 (compiler responsibilities) + §8 (recoverable meaning), so the Stage-1 fidelity harness (#303) stands but must be reconciled to the spec's own metrics (§22 `cr/rr/sr/pr/tr/qr`, §24 promotion rule) rather than my ad-hoc properties. Docs/protocol only; no runtime code change; integrity/routing/continuity/streams green. **NEXT: re-ground compiler/fidelity Stage 2 in the spec — reconcile `benchmarks/fidelity` to §22 metrics + §8 recoverability + §24 before rewriting `compile_nl`, so the genuine fix is measured by the spec's own contract.**
- Prior handoff `HISTORY#303` - **MIRL compiler rewrite Stage 1: the executable fidelity contract + the current stub's failing baseline (no compiler behavior changed).** Operator-found bug: `seam_runtime/nl.py:compile_nl` (the generic "remember arbitrary text" path behind the CLI/`seam remember`, the shell, the dashboard remember box, the MCP `ingest` tool, and REST `/ingest`) is a template overfit to SEAM's self-description - for any real memory it slugifies the WHOLE input and asserts it as `project:SEAM`'s `goal`, attributed to hardcoded `user:local_user`+`project:SEAM`, so every ingest emits the same skeleton ("the same sentence over and over"). The LoCoMo benchmark path dodges it (conversational ingest uses the richer `compile_conversation_turn`), but the self-improvement loop's own-corpus mode is poisoned and the boilerplate ENT/CLM/STA records crowd the verbatim RAW that token-overlap retrieval reads. Landed: `benchmarks/fidelity/{contract.py,golden.py}` (7 backend-agnostic contract checks + 5 golden cases each annotated with `baseline_failures`) and `tests/fidelity/test_compile_fidelity.py` (every case×property; violations `xfail(strict=True)` = a ratchet that flips to a hard failure the moment a fix makes it pass). Audit doc `docs/audits/2026-06-12-mirl-compile-fidelity.md`. Baseline matrix: every realistic memory FAILS `entity_extraction`+`subject_grounding` (multi-fact also FAILS `segmentation`/`separable_coverage`/`fact_grounding`); the self-description input the stub was overfit to passes the FULL contract (proving the harness is fair). Verified 621 passed / 11 xfailed / 0 skipped (the 11 = the documented baseline, zero unexpected XPASS). **NEXT: Stage 2 — deterministic FLOOR rewrite of `compile_nl` (segment into propositions, verbatim RAW/SPAN per proposition, entities from high-confidence rules, NEVER fabricate a claim) to turn the xfails green; backend recommendation = honest-minimal zero-new-dep floor + opt-in LOCAL OLLAMA for S-P-O triples, spaCy held as a pluggable offline option, heavier hand-rolled extractor ruled out. Then Stage 3 unify with `compile_conversation_turn` (delete the stub), Stage 4 opt-in LLM extractor, Stage 5 migrate existing degenerate records + re-validate the self-probe loop.**
- Prior handoff `HISTORY#302` - **the PAID judged holdout Scorer is built (the last open piece of the self-improvement stack from #292/#297).** Operator correction this session: building this tier is part of the "benchmarks 100% functional" gate, NOT optional; only running it stays operator-gated (fresh explicit confirmation per paid execution, never auto-run). Pieces: `benchmarks/external/locomo/judged_scorer.py` (`JudgedLocomoScorer` - Scorer protocol, judge_score_mean via paid answerer + `openai|claude` judge, holdout-by-default, empty answers skip the judge call, judge failures retry-then-raise, `estimate_locomo_paid_validation` zero-cost dry-run; `build_locomo_holdout_scorer` validates args before any side effect); `tools/h2/paid_validation.py` (`run_paid_validation` - stock baseline vs applied/explicit flags, verdict improved/regressed/within-noise at margin 0.02, equal-flags skips the candidate pass, never proposes/applies/reverts); `seam improve validate` CLI (dry-run estimate without `--confirm-paid`, `--flags` JSON validated via the #289 coercion contract). REPO_LEDGER records the durable never-auto-run gate. Verified: 10 new CI-safe tests (zero API calls), full suite 596 passed/0 skipped + secondary roots 103 passed, real-dataset free dry-run = 5 scopes/165 holdout cases/660 max paid calls. **NEXT (operator-gated): the first real paid holdout validation - suggested: `seam improve cycle --locomo-dataset benchmarks/external/locomo/data/locomo10.json --auto-approve` (free, applies the #297 `w_lexical+0.1` dev win) then `seam improve validate --locomo-dataset ... --confirm-paid` (needs OPENAI_API_KEY; ~660 calls max).**
- Prior handoff `HISTORY#301` - **maintenance/closeout: security + dependency state fully clear.** Verified #300's NEXT was already executed (unrecorded): PR #70 merged to main as `6e41070`; CodeQL ALL 10 alerts resolved (#1/#5/#6 fixed; #3/#4 py/full-ssrf dismissed won't-fix = genuinely mitigated by the #300 layers; #7-#10 path-injection dismissed false-positive; #2 sessionStorage dismissed by-design); 0 open code-scanning + 0 open Dependabot security alerts. Merged Dependabot PRs #71 (@types/node 25.9.3) + #72 (@vitejs/plugin-react 6.0.2). FIXED the weekly Dependabot docker job that failed on EVERY run since #61: `.github/dependabot.yml` used the `docker` ecosystem, which only scans Dockerfiles/K8s YAML and aborts with "No Dockerfiles nor Kubernetes YAML found in /"; switched to `docker-compose` (tracks the `pgvector/pgvector:0.8.2-pg18-trixie` pin) via PR #73 (`75a6815`). Verified on post-merge main: full `pytest tests/` with DSN + strict no-skip = 586 passed/0 skipped (exit 0); secondary roots (test_seam_all + history tools + streams) 103 passed. Remaining external verification: the next weekly docker-compose Dependabot run must succeed. **Open levers: (1) cat4 single-hop ranking-vs-packing `--save-context` diagnostic (~+0.065 recall headroom, #274); (2) operator-gated PAID judged holdout Scorer (last piece from #292/#297, never auto-run); (3) Track K start = operator decision — per the operator's gate definition the free self-improvement loop is complete as of #297, so the gate is plausibly satisfied pending operator confirmation.**
- Prior handoff `HISTORY#300` - **SECURITY: hardened the /chat SSRF / DNS-rebinding residual (CodeQL `py/full-ssrf` #3/#4).** The residual: `_validate_provider_base_url` resolved+validated the host, but `_call_chat_provider` used `urllib.urlopen` which RE-RESOLVES (rebinding) and FOLLOWS REDIRECTS (validated host could 302->169.254.169.254). Fix (seam_runtime/server.py), layered: (1) **host allowlist** `_BUILTIN_CHAT_HOSTS` (built-in dashboard providers) + loopback (Ollama) + operator `SEAM_CHAT_ALLOWED_HOSTS` (comma-sep, operator-set never caller-set) - a non-allowlisted non-loopback host is rejected 400, closing rebinding BY CONSTRUCTION (no attacker-named host to rebind); (2) **resolved-IP range check** kept as defense-in-depth (private/link-local/reserved/multicast/unspecified rejected, loopback exempt); (3) **`_chat_opener()`** refuses 3xx redirects (raises HTTPError->502), both provider branches use it. Built-in providers + loopback still work; only arbitrary hosts rejected. Tests: TestChatBaseUrlSsrf +5 (unlisted-public rejected, built-in passes, env-allowlist permits custom, allowlisted-resolving-to-metadata still rejected via monkeypatched getaddrinfo, live 302-block). Full suite 586 passed/0 skipped. REPO_LEDGER REST API Policy records the allowlist + `SEAM_CHAT_ALLOWED_HOSTS`. **NEXT: open PR + MEASURE whether CodeQL clears #3/#4** (runtime validation isn't always a recognized sanitizer; if it stays, the alerts are genuinely mitigated -> dismiss with that reason). **Operator-approved (this session): dismiss the OTHER 5** - path-injection x4 (sound `is_relative_to` containment under a resolved `_tree_root` = false positive) + sessionStorage x1 (token must live client-side for a loopback single-user dashboard, already session-only = won't fix) - to execute via `gh api`. Branch `fix/chat-ssrf-allowlist`.
- Prior handoff `HISTORY#299` - **CORRECTION to #298: the dashboard clear-text-logging fix was insufficient; corrected.** #298 lowered `dashboard.py:1444` `LOGGER.info`->`LOGGER.debug`, but CodeQL's `py/clear-text-logging-sensitive-data` sink is **level-agnostic** - it fires because the operator command string (which can embed a secret) reaches a `logging` sink at all, so PR #68's CodeQL required check FAILED with the same alert re-flagged on the changed line. Real fix: log only the non-sensitive **argv token count** (`LOGGER.debug("...: %d-token argv", len(argv))` - `len()` returns an int, breaking the string taint); the full command stays in the operator-facing audit trail (`_record_command`/`controller._log`/UI), which is not a clear-text-logging sink. The #298 **workflow-permissions (#1)** and **dsl ReDoS (#5)** fixes already passed all Analyze jobs and stand. Full suite 581 passed/0 skipped. **NEXT: confirm PR #68 CodeQL goes green on the pushed correction, then merge.** Remaining operator-decision alerts unchanged (SSRF x2 DNS-rebinding residual, path-injection x4 FP, sessionStorage token by-design - none dismissed); 5 Dependabot webui PRs (#62-66) pending. Branch `fix/codeql-workflow-perms-redos-logging`.
- Prior handoff `HISTORY#298` - **SECURITY: cleared the three honest CodeQL findings** (the unambiguous code fixes, no judgment call; these alerts are pre-existing debt surfaced when CodeQL scanning was enabled 2026-06-09T18:58, NOT regressions). (1) `.github/workflows/external-memory-benchmarks.yml` gains top-level `permissions: contents: read` (least-privilege GITHUB_TOKEN; the only workflow lacking a permissions block). (2) `seam_runtime/dsl.py` `_parse_scalar`: the two scalar-literal regexes `re.fullmatch(r"-?\d+")`/`r"-?\d+\.\d+"` replaced with linear, backtracking-free `_is_int_literal`/`_is_float_literal` (sign-strip + `str.isdecimal` + single `.` partition); behavioral parity verified vs the old patterns across 18 edge cases incl. Unicode Nd digits; `re` still used by ENTITY_RE/BLOCK_RE. (3) `seam_runtime/dashboard.py:1444` shell-command log downgraded `LOGGER.info`->`LOGGER.debug` (operator command string no longer at info; shell-free subprocess boundary unchanged). Full suite 581 passed/0 skipped (DSN+strict). **Remaining 7 CodeQL alerts are operator-decision, NOT dismissed:** #3/#4 SSRF in `server.py _chat_completion` (DNS-rebinding/TOCTOU residual, not a clean FP - validate resolves host, urlopen re-resolves + follows redirects; harden by pinning resolved IP / blocking redirects, or won't-fix), #7-#10 path-injection (`_resolve_tree_path` is_relative_to containment = genuine FP), #2 sessionStorage token (by-design). Also pending: 5 Dependabot version-update PRs (#62-66, webui npm). Branch `fix/codeql-workflow-perms-redos-logging`.
- Prior handoff `HISTORY#297` - **multi-conversation dev gate for the self-improvement loop (generalization-safe; never tunes on holdout).** `benchmarks/external/locomo/recall_scorer.py`: `PooledLocomoRecallScorer` pools dev questions across N conversations (one aggregate + per-category, each answered via its own scope runtime, flags applied/restored on all); `build_locomo_dev_scorer(max_scopes=5, split="dev", ...)` filters cases via deterministic `holdout_split.assign_one` (dev ONLY; holdout reserved). `seam improve cycle --locomo-dataset` now uses it; `--locomo-scopes` default 5, new `--locomo-split {dev,holdout,all}` (default dev). VALIDATED no-paid (4 convs, 48 dev cases): the gate CHANGES the verdict - `semantic_zero` (single-conv +0.040 in #292) is pooled -0.0002 + regresses cat1 -> REJECTED; `fusion=rrf` regresses cat3 -> rejected; BEST = `w_lexical+0.1` (+0.0104, no regression) = a generalizable gain. The free loop (self-probe + multi-conv LoCoMo dev gate + reversible #289 ratchet) is now complete + generalization-safe. Tests +2; full suite 581 passed/0 skipped. **ONLY remaining from #292:** an operator-gated PAID judged Scorer (never auto-run) for the holdout validation tier. Branch `feat/locomo-multiconv-dev-gate`.
- Prior handoff `HISTORY#296` - **SECURITY: resolved the CRITICAL chromadb Dependabot alert (GHSA-f4j7-r4q5-qw2c, pre-auth code injection, vuln >=1.0.0,<=1.5.9, NO patch - latest PyPI 1.5.9 is the top of the range).** Root: #293 made chromadb optional in pyproject but MISSED `requirements.txt` (installer/bootstrap path), which still pinned `chromadb==1.5.7`. Fix: removed chromadb from `requirements.txt` (now rich+tiktoken only) AND from `all-extras`; kept it ONLY in the explicit opt-in `chroma` extra with a documented advisory. SEAM uses only the embedded chromadb PersistentClient (not the Chroma server), so the server-side pre-auth vuln isn't reachable - but the forced dep is removed regardless. Guards: test_chroma_optional.py asserts chromadb absent from core deps / requirements.txt / all-extras, present only in `chroma`. REPO_LEDGER records the policy. Default install pulls no chromadb (SQLite backend); `seam[chroma]` + CI chroma-real-smoke still get it. After merge Dependabot re-scans and auto-resolves. Verified: 5 chroma guards + full suite 579 passed/0 skipped (DSN+strict). NEXT (when chromadb ships a patched >1.5.9, widen the `chroma` pin). Branch `fix/chromadb-critical-advisory`.
- Prior handoff `HISTORY#295` - **CI fix: httpx restored after chromadb went optional.** PR #58 CI caught it: making chromadb an optional extra (#293) removed httpx (a transitive chromadb dep that `starlette.testclient`/the server tests need), so server-test modules failed COLLECTION in both test-and-benchmark legs + the pgvector job. Fix: main CI test-deps install `pytest httpx` explicitly; the pgvector-integration job runs the real-pgvector test FILES (real_adapter + pk_composite + substream) instead of `pytest tests/ -m external` (which over-collected server/sbert modules whose deps that lean job lacks); guard test updated. Validated locally (guard 4 passed; pgvector 3-file + DSN 11 passed); main-job httpx absence is verified on CI re-run (dev venv still has chromadb so it can't reproduce locally). LESSON: a hidden transitive dep (chromadb -> httpx) made the suite pass locally but break on a fresh CI install. Branch `feat/free-locomo-scorer` PR #58 (#292-#295); #289-#291 on main via PR #57.
- Prior handoff `HISTORY#294` - **strict no-skip enforcement + fixed a silent-skip hole.** "Never skip tests" was behavioral-only, and 4 pgvector tests (test_pgvector_pk_composite x3 + test_substream_isolation x1) gated on `PGVECTOR_TEST_DSN` which CI set NOWHERE, so they silently skipped in every run. Fix: (1) `tests/conftest.py` strict no-skip hook (default ON; `SEAM_STRICT_NO_SKIP=0` to opt out) - fails the session on any skip whose reason is not in a curated allowlist (wrong OS / absent optional extra / no merge-base; xfail excluded); (2) marked the pgvector tests `@pytest.mark.external` so non-service jobs deselect them with `-m "not external"` (not skip); (3) ci.yml main job runs `-m "not external"`, pgvector job runs `tests/ -m external` with `PGVECTOR_TEST_DSN` set so all external tests RUN; (4) guards in test_github_pr_gates.py; (5) AGENTS.md Session-End rule 6 "No silent skips" + canonical zero-skip local command (docker-up + export PGVECTOR_TEST_DSN). Validated: strict fails (exit 1) on an unexplained skip; main-job path 986 passed/0 skipped; pgvector path 7 passed/0 skipped; full `tests/`+DSN 578 passed/0 skipped. Branch `feat/free-locomo-scorer` (#292/#293/#294; #289-#291 on main via PR #57).
- Prior handoff `HISTORY#293` - **`seam improve cycle` CLI + chromadb made optional.** (1) `seam improve cycle` (seam_runtime/cli.py) runs one self-improvement cycle from the main CLI: evaluate levers against free scorers, propose the best non-regressing gain, --auto-approve to apply with revert-on-regression. Default scorer = free SELF-PROBE over args.db (no dataset); `--locomo-dataset` adds the #292 free-LoCoMo headroom scorer. Orchestration lazy-imported from tools/ via `_import_run_improvement_cycle()` (#287-style repo-root fallback); `--db` works before or after the subcommand. (2) chromadb moved from pyproject CORE `dependencies` to a new `chroma` optional extra (+all-extras) - SEAM defaults to the SQLite vector adapter; all chroma imports were already lazy (`ChromaSemanticAdapter._client()` raises a clear RuntimeError if absent), so no test guard was needed (verified by simulating chromadb-absent); ci.yml `chroma-real-smoke` now installs `.[server,chroma]`; regression guard `tests/audit/test_chroma_optional.py`. Tests: test_improve_cli.py (2) + test_chroma_optional.py (2). Full suite 572 passed/4 skipped. Branch `feat/free-locomo-scorer` (#292/#293; #289-#291 already on main via PR #57). **NEXT (from #292):** multi-conversation dev gate (`--locomo-scopes>1`); operator-gated PAID judged Scorer (never auto-run).
- Prior handoff `HISTORY#292` - **the free-LoCoMo scorer that makes the loop actually improve.** `benchmarks/external/locomo/recall_scorer.py LocomoRecallScorer` (Scorer protocol on the real adapter; reuses `answer()` + `common.scoring.context_recall`; FREE answerer=None/no judge; ablates candidate flags via `rt._retrieval_flags` override, ingests each conversation once via `build_locomo_recall_scorers`). VALIDATED no-paid (conv-26, 25q, SQLite): baseline locomo_recall 0.4624; running the real proposer (`evaluate_candidates`+`select_best_improvement`) over it auto-found `semantic_zero_no_vector=True` (+0.040, no regression) and `w_semantic+0.1` (+0.040) as IMPROVEMENTS, correctly rejecting `fusion=rrf` (+0.056 but a category regresses) and `bm25_all` (-0.019); best = `{semantic_zero_no_vector: True}` - the proposer auto-rediscovered #273's R1 lever, free. `ScoreReport.per_case` relaxed to `dict[str,float]`. Tests `tests/audit/test_locomo_recall_scorer.py` (3, CI-safe synthetic). Full suite 568 passed/4 skipped. **CAVEAT:** single-conv/SQLite; per #273 the gain is category-MIXED at full-set/pgvector scale, so a one-conv proposal may not generalize - production needs a multi-conversation DEV gate (`max_scopes>1`, never holdout) + operator-gated PAID judged validation for big claims. **NEXT:** a thin `improvement cycle` CLI (build_locomo_recall_scorers + run_improvement_cycle; propose-only default, --auto-approve opt-in); the multi-conversation dev gate; a paid judged Scorer (same protocol, never auto-run). Branch `feat/free-locomo-scorer` off main (which now has #289-#291 merged via PR #57). Handoff: `docs/handoffs/2026-06-08-h2-self-improvement-loop.md`.
- Prior handoff `HISTORY#291` - **H2 front half: proposer + no-regression ratchet.** `seam_runtime/self_improve.py` adds the proposer core (`candidate_levers`, `evaluate_candidates` with a per-category no-regression gate, `select_best_improvement`); `tools/h2/improvement_loop.py run_improvement_cycle` proposes the best genuine improvement and (auto-approve opt-in) applies via #289 then RE-MEASURES and AUTO-REVERTS on post-apply regression - applied scores can only move up or flat. Scorer-agnostic: free scorers (self-probe; free-LoCoMo next) drive the always-on loop, paid judged scorers plug into the same list for operator-triggered validation ("we want both"). Tests `tests/audit/test_improvement_loop.py` (8). Full suite 565 passed / 4 skipped. **HONEST STATUS:** machinery complete + correct, but per #290 the FREE levers have ~no headroom, so wired to self-probe the loop is a correct free regression WATCHDOG (always trying, never regressing). Actual score MOVEMENT needs the free-LoCoMo scorer (the signal #273 showed has headroom) and/or the operator-gated PAID validation tier (char-budget/top_k) - both plug into `run_improvement_cycle` via the scorer list. **NEXT:** wire a free-LoCoMo `Scorer`; a thin `improvement cycle` CLI; a paid judged scorer (never auto-run). Full state in `docs/handoffs/2026-06-08-h2-self-improvement-loop.md`. Branch `feat/h2-self-improvement-loop` (#289/#290/#291 committed, NOT pushed/PR'd).
- Prior handoff `HISTORY#290` - **H2 front-half foundation + the negative result that reframes the loop.** Built (all green, 557 passed / 4 skipped): the free self-probe scorer (`seam_runtime/self_improve.py` - cloze probes from the runtime's own corpus, binary record-in-set recall, not budget-gameable, deterministic), the `search_ir(..., flags=)` ablation hook (`runtime.py`), and made the fusion weights a continuous apply target (`RetrievalFlags.w_lexical/semantic/graph/temporal` + `weight_pairs()`; `_fuse_weighted` reads them; baseline tuple = byte-identical #273 baseline) wired through the existing #289 apply machinery via a shared `coerce_flag_value` validator (int->float tolerance). **FINDING (do not re-derive): on a realistic no-paid corpus (LoCoMo conv-26 via `ingest_turn`, ~2000 records), NO apply-able lever has free headroom on the self-probe signal** - `search_ir` surfaces CLM not RAW (gold must target the retrieved kind); CLM-gold baseline recall 0.8917 @ budget 20; booleans flat or regressing (rrf worse, matching #273); NO weight vector moves it UP at any budget (best +0.008 @ top-1, within noise). Cloze-of-own-record is a lexical twin by construction = too easy/lever-insensitive; free hardening can't fix it (only paid LLM paraphrase can). So the bottleneck is the signal, not the levers; a proposer over it would idle. **GATED on an operator fork:** (A) driver = FREE LoCoMo string-match (`--answerer none --judge none`; #273 measured `semantic_zero` +0.026 cat1) + self-probe as a free on-corpus REGRESSION WATCHDOG with #289's reversible apply = a no-regression ratchet; (B) accept global levers near-optimal, loop value = regression-prevention. Real score-movers (char-budget/top_k, +0.059..+0.135) are gameable on free metrics and need PAID judged validation, conflicting with "never need paid to improve". Branch `feat/h2-self-improvement-loop` (#289 committed; #290 this checkpoint). Not pushed/PR'd.
- Prior handoff `HISTORY#289` - **closed the back half of the H2 self-improvement loop: the `improvement apply` step.** Before this, an `approved` improvement proposal was a dead end - retrieval flags were env-only (`search_ir` read `retrieval_flags_from_env()`; no persisted layer). Added: (1) `seam_runtime/storage.py` `retrieval_flag_state` table + `iter_/upsert_/replace_retrieval_flag_state` (`replace_*` rewrites the whole table in one transaction, so it is a pure projection of the approved set); (2) `seam_runtime/retrieval.py` `load_retrieval_flags(store, env)` resolving defaults < persisted applied-state < env override (operator kill switch), plus `retrieval_flag_field_types()` and `_retrieval_env_overrides()` (only EXPLICITLY-set env vars overlay, so an unset var never clobbers an applied flag) and a new `SEAM_RETRIEVAL_RRF_K` override; baseline invariant - empty state + empty env reproduces `RetrievalFlags()` byte-identical (preserves HISTORY#273 locked baseline); malformed persisted rows are skipped, never raised; (3) `seam_runtime/runtime.py` `search_ir` builds flags via `load_retrieval_flags(self.store)` cached once per runtime (`_retrieval_flags_cached`) for per-run scoring stability; (4) `tools/h2/improvement_review.py` `apply` subcommand (+`--dry-run`) with `compute_apply_plan` - REVERSIBLE reconcile (withdraw approval + re-apply removes the flag, not a ratchet), gated on the `proposed_change["flags"]` PAYLOAD shape not `kind`, newest approved proposal wins a per-flag conflict, unknown/ill-typed/invalid-`fusion` flags skipped. Tests: new `tests/audit/test_h2_apply.py` (17). Verified: full `python -m pytest tests/` = 548 passed, 4 skipped (= prior 531 + 17; the 4 skips are pre-existing `PGVECTOR_TEST_DSN`-gated). **Front half (auto-proposer) still OPEN** - next agent: read `retrieval_event` data → emit `{"flags": {...}}` proposals (the exact schema apply consumes), evidence-gated on the dev split (never holdout) because per HISTORY#273 most levers are LoCoMo no-ops.
- Prior handoff `HISTORY#288` - **audit-driven correctness + security hardening bundle landed on `fix/audit-correctness-security`** (was uncommitted WIP; reviewed, full-suite-verified, then committed). Six source fixes + audit tests: (1) `seam_runtime/dashboard.py` `_validate_shell_command` rejects a slash/backslash in argv[0] (was basename-allowlisted, so `/custom/path/git` could impersonate `git`) — command must be a bare PATH-resolved name; (2) `seam_runtime/holographic.py` `_read_png` bounds an untrusted IHDR with `MAX_SURFACE_DIMENSION=8192` and new `_bounded_inflate` (refuses to materialize past `(stride+1)*height` — image/decompression-bomb guard); (3) `seam_runtime/lossless.py` `decompress_text_readable` raises a clear `ValueError` on an order entry referencing an unknown chunk id (was opaque `KeyError`); (4) `seam_runtime/retrieval_orchestrator/adapters.py` `SQLiteGraphAdapter` now passes `ns` to `load_ir` and restricts the edge load to edges touching an in-scope/in-namespace record via subquery instead of a full `ir_edges` scan (neighbor counts byte-identical; no-filter path = full scan as before); (5) `seam_runtime/retrieval_orchestrator/planner.py` `mode='vector'` is now semantic-only and never injects the sql leg even when a pure-filter query classifies STRUCTURED; (6) `seam_runtime/server.py` new `_validate_provider_base_url` SSRF guard on `/chat` `base_url` (requires http(s) + resolvable host; rejects private/link-local incl. `169.254.169.254` metadata/reserved/multicast/unspecified; loopback allowed for Ollama). Tests: new `tests/audit/test_audit_2026_06_05.py` (16 tests) covers all six; `tests/audit/test_shell_security.py` updated — `test_command_with_path_validates_basename` renamed to `test_command_with_path_in_argv0_rejected` (now asserts `/bin/ls`, `./ls`, `../bin/ls`, `sub/dir/ls` all raise) plus added `test_path_in_later_arg_allowed`. Verified: full `python -m pytest tests/` = 531 passed, 4 skipped, 0 failed (pgvector up). Not pushed (operator-gated); branch is `fix/audit-correctness-security`, not `main`.
- Prior handoff `HISTORY#287` - **fixed `seam doctor` reporting `Streams: unavailable` from the console-script entry point.** `seam_runtime/doctor.py` `check_streams()` imported `tools.streams.verify_streams` directly, but the `seam` console script does not put the source-checkout root on `sys.path` and `tools/` is a sibling of the package that is intentionally not shipped in the wheel, so `seam doctor` (and the dashboard doctor surface) reported `Streams: unavailable (No module named 'tools')` unless run via `python -m` from the repo root or with `PYTHONPATH` set. Added `_import_streams_verify_all()` which, on `ModuleNotFoundError`, derives the repo root package-relative (`Path(__file__).resolve().parent.parent`) and inserts it into `sys.path` **only when** `tools/streams/verify_streams.py` actually exists there - a genuine wheel install still reports `unavailable` (no false positive), and resolution is deliberately not cwd/`git`-dependent. Regression test `test_doctor_streams_resolves_repo_tools_from_console_script` runs `check_streams()` in a subprocess from a non-repo cwd with `PYTHONPATH` stripped (an in-process assertion is useless since pytest runs from the repo root where `tools` already imports). Verified: bare `seam doctor` from `/tmp` now prints `Streams: PASS`; full gate `pytest test_seam_all tools/history/test_history_tools.py tools/streams/test_streams.py` = 417 passed / 0 failed with pgvector up. Surfaced while relocating the working checkout off the T7 external drive to `/home/terrabyte/Documents/Projects/Seam` (internal ext4); doctor behaved identically on both copies, so it is pre-existing, not relocation-induced.
- Prior handoff `HISTORY#286` - **wired the SEAM-augmented dashboard chat end-to-end and fixed two bugs found in audit.** The dashboard agent panel now chats THROUGH SEAM (retrieve memory → inject as system prompt → call the selected provider) via a new `POST /chat` in `seam_runtime/server.py` (OpenAI-compat + Anthropic schemas, stdlib `urllib`; key order: dashboard key → server env → `local` for Ollama), with `SeamAPI.chat()` and the `dashboard.html` send handler rewired off `context()`. **BUG 1 (fixed):** `/chat` read `rec.get("text")` but MIRL records carry no `text` field, so memory was counted (`memory_used>0`) yet never injected; now renders each candidate via the public `iter_textual_fields(record)` (the same text the embedder indexes) and counts only records that produced text. **BUG 2 (fixed):** retrieval ran outside the try/except, so a vector-backend outage returned a raw 500 on the default `use_memory=true` path; now degrades to a no-memory answer (`memory_used=0` + `memory_error`) and the dashboard shows a "memory unavailable" note. Tests `tests/audit/test_chat_endpoint.py` 5 → 7 (regression guards for both bugs); verified 7 passed, server/api/webui subset 21 passed, live SQLite-adapter harness shows `memory_used=2` with content injected and pgvector-down returns 200 not 500. **Known follow-ups:** server does not auto-load `OPENAI_API_KEY` from `.env.local` (env-key fallback needs a manual export); injected memory text is the indexed slug form, not original NL; a real successful provider reply is still unverified (no local model pulled, no cloud spend while operator away).
- Prior handoff `HISTORY#285` - **wired the browser dashboard in as the functional SEAM GUI and deleted `experimental/` entirely.** Operator decision: SEAM is used either via this dashboard or the CLI; nothing is experimental. Moved `experimental/webui/` → repo-root `webui/` (React/Vite dev project, kept intact) and the served static assets → `seam_runtime/webui/` (packaged); deleted `experimental/`. Wired FastAPI (`create_app`) to serve `dashboard.html` at `/` plus its same-origin assets via a static mount added AFTER the API routes (so `/health` etc. still win); added `webui_dir()` + `_mount_webui()` and a `seam webui`/`seam dashboard-web` command that serves + opens the browser. `dashboard.html` calls the API with relative paths, so one `seam serve` delivers UI + API (no Node/Vite/CORS). Pointed `webui/vite.config.ts` `publicDir` at `../seam_runtime/webui` (one canonical asset copy); added `webui/*` to pyproject package-data. **Browser-verified with Playwright:** found and fixed a real blocker — an HTML comment (`<!-- -->`) inside the inline JSX crashed the Babel transpile and left the page blank; converted it to a `/* */` JS comment, after which the full IDE dashboard renders (explorer, editor, agent chat, terminal, status bar) and `/health`+`/seam-api.js` serve 200. Cleaned stale `experimental` references in `mcp.py` error text, a test docstring, `installers/README.md`, `docs/setup.md`, `docs/CODE_LAYOUT.md`. **Known follow-up:** the `webui/src/` React rewrite is still incomplete rework material (the shipped UI is the static `dashboard.html`); finishing/replacing it is future work.
- Prior handoff `HISTORY#284` - fixed the `experimental/` misrepresentation and deferred packaging. `experimental/retrieval_orchestrator/` was NOT experimental — it is load-bearing runtime code imported by `seam_runtime/{cli,mcp,dashboard,benchmarks}.py` (6 sites). Promoted it via `git mv` to `seam_runtime/retrieval_orchestrator/` (internal imports are relative so they survived; updated 15 import lines across 9 files incl. tests + `tools/ci/chroma_real_smoke.py` + the `BlockRetrievalOrchestrator` MetaPathFinder string); removed vestigial `experimental/__init__.py`; set pyproject `packages.find` to `seam_runtime*` only; updated `docs/CODE_LAYOUT.md`. `experimental/` now holds ONLY the non-Python `experimental/webui/` TypeScript dashboard prototype (separate question, untouched). Packaging/release is deferred to new **ROADMAP Track N — Packaging, Release, and Distribution** (private distribution, keep `Do Not Upload`, name `seam-runtime`, license change to Apache-2.0/MIT pending operator). Full CI suite green on Linux (exit 0); Windows re-verified by CI on the PR. **Full CI matrix (ubuntu + windows) is green as of #282/#283.**
- Prior handoff `HISTORY#283` - after #282 dropped Windows `test-and-benchmark` from `10 failed, 6 errors` to `1 failed, 881 passed`, the lone remaining failure was `tests/audit/test_pool_concurrency.py::test_concurrent_writes_all_commit` raising `OperationalError('database is locked')` — pre-existing Windows write-concurrency flakiness, independent of #282 (the test is self-contained with its own tmp db + pool). Root cause: the test built its pool with a bare `sqlite3.connect(db, timeout=5)` factory that did NOT enable WAL, so it exercised a less-robust config than production `SQLiteStore` (which sets `journal_mode=WAL` + `busy_timeout=5000`). Fixed by establishing WAL in `_bootstrap` and giving the pool a `_wal_connect` factory (WAL + busy_timeout), mirroring production. On the same PR #51 as #282. CI re-verifies Windows.
- Prior handoff `HISTORY#282` - Windows SQLite-handle cleanup so `test-and-benchmark (windows-latest)` can go green (it was red with `[WinError 32] file in use` on ~10 benchmark/surface/mem0 tests; Linux unlinks open files so it never showed there). Root cause: transient `SeamRuntime`/`SQLiteStore`/per-scope LoCoMo adapter runtimes were not closed before their temp DBs/dirs were deleted; on Windows an open SQLite handle locks the file. Added `SeamRuntime.close()` + context-manager and `SeamLocomoAdapter.close()` + context-manager; closed `surface_store`, persist `temp_runtime`/`reopened_runtime`, and the two agent-family `temp_runtime` finally blocks in `seam_runtime/benchmarks.py`; closed the mem0 harness runtime after `add`/`search`; and fixed `_is_ephemeral_path` to recognize POSIX `/tmp`,`/var/tmp` on Windows (where `Path("/tmp").resolve()` becomes `C:\tmp`). Two direct-construction tests (`test_benchmark_reproducibility`, `test_cross_encoder_rerank`) now close their adapters. Verified on Linux (no regression; affected slices 38 passed); Windows-only behavior is verified via CI on the PR. **Known follow-up:** the parallel LoCoMo runner (`run_benchmark_grouped_parallel`) creates factory adapters it does not close — not covered by any CI test, but real Windows parallel runs could still leak; owed a runner-side close.
- Prior handoff `HISTORY#281` - benchmark integrity-hash regression fix. The advisory `test-and-benchmark` CI matrix job had been RED on `main` since 2026-05-31 (1 failing test, independent of #280): `benchmarks/external/common/runner.py` attached `answerer_diagnostics` to the case under `--save-context` (added by #278) but did not exclude it from `integrity_exclude_keys`, so a save-context run produced a different `integrity_hash` than the equivalent default run. Added `"answerer_diagnostics"` to the exclude set; only save-context runs change, normal/sealed bundles are byte-identical. Lands on the same branch as #280 (PR #50) so `test-and-benchmark` can go green. The three required ruleset checks were already passing.
- Prior handoff `HISTORY#280` - full-audit closeout. All four protocol gates (integrity/continuity/routing/streams) pass, `main` matched `origin/main`, and the full suite is green (exit 0) with pgvector up. Fixed a local test-harness leak: an operator shell that exports `SEAM_PGVECTOR_DSN` routed every persist-path test to the optional Docker pgvector backend, so ~21 non-pgvector tests failed with raw `connection refused` when the container was down. New `tests/conftest.py` autouse fixture hides the ambient DSN for non-`external` tests (they run on SQLite, matching CI); `tests/audit/test_pgvector_real_adapter.py` is now marked `external` so it keeps the ambient DSN and self-gates via skipif. Resolved three stale remote branches: deleted `claude/remote-control-AD6Di` (stale PROJECT_STATUS snapshot from #220) and `roadmap-trust-security-manual` (Track K doc already harvested into main's evolved version); kept `backup/local-pgvector-bootstrap` (deliberate backup holding files absent from main). The two `in-progress` history entries #225/#247 are append-only and already superseded by #226/#248 — protocol-correct as-is, not edited. **Operator clarified the Track K gate: "benchmarks 100% functional" means the benchmark results can systematically/automatably improve the system — i.e. the closed observe->propose->apply->re-measure self-improvement loop, which is still OPEN per #274 (no auto-proposer, no apply step; retrieval reads hardcoded weights + env flags, not a stored policy). Tests-green is necessary but not that bar.**
- Prior handoff `HISTORY#279` - paid 100-case OpenAI LoCoMo slice completed for baseline vs pack-budget-deep. Results: judge_score_mean `0.44 -> 0.57`, correct_count `32 -> 40`, incorrect_count `44 -> 26`, zero judge errors, all major categories improved. Raw paid reports are outside git under `../Seam-artifacts/20260601-paid-locomo-slice/`. This is a positive slice, not full LoCoMo proof; the next gated step is the full 1542-case paid A/B if the operator accepts the spend.
- Prior handoff `HISTORY#278` - semantic recovery policy flags and diagnostics landed for the LoCoMo SEAM adapter, with full no-paid pack-budget/deep-candidate reports archived outside git under `../Seam-artifacts/20260601-semantic-recovery-policy/`. The result confirms pack budget is the dominant retrieval-context-recall throttle: deep candidates alone barely move recall, while pack-budget-deep reaches `0.758217` (`+0.134549` vs baseline). This is not yet answer-quality evidence; the next gated step is a paid judged baseline-vs-pack-budget-deep comparison.
- Prior handoff `HISTORY#277` - Cat4 single-hop attribution audit preserved as an active audit doc, generated `diag_out/` raw artifacts moved outside the repo to `../Seam-artifacts/20260601-192239-cat4-diag_out/`, and `.gitignore` now ignores future `diag_out/` dumps. The audit resolves the #274 open question: the measured cat4 gap is not abstract-record crowding/capture-adapter displacement; pack budget and judged validation are the next lever.
- Prior handoff `HISTORY#276` - local branch/PR closeout: `main` was clean and matched `origin/main`; GitHub had zero open PRs; `recovery/locomo-dataset-271` had no unique patch left after PR #43; `roadmap/g5-multi-surface-index` had one completed roadmap commit but was based before later audit work, so its intended G5 card was replayed onto current `main` without merging stale derived files. No runtime code changed.
- Prior handoff `HISTORY#275` - CI regression fix for #274: the mocked `PgVectorAdapter` cursor in `test_seam_all/test_seam.py` unpacked the INSERT into 7 values; #274's new `namespace` column made it 8, breaking 5 ubuntu-CI tests. Fixed the fake cursor to unpack 8 (incl. namespace). **Lesson: the suite has two roots — `tests/` AND `test_seam_all/test_seam.py` — and CI runs both; always run `pytest tests/ test_seam_all/test_seam.py` before pushing schema/SQL changes.** No production code changed. Substance is #274 below.
- Prior handoff `HISTORY#274` - **substream isolation (leak-seal).** `runtime.search_ir` gained an `ns` param that confines BOTH the candidate load (`load_ir(ns=...)`) and the vector top-K (new `namespace` column + filtered `search` on both SQLite and pgvector backends) to a single namespace. The sealed leak: `search_ir` previously filtered candidates by `scope` only + global vector search, so a multi-namespace single store leaked another namespace's records (masked in LoCoMo by per-conversation dbs). `ns=None` is byte-identical to prior behavior; gated for A/B by `SEAM_RETRIEVAL_SCOPED_VECTORS` (`RetrievalFlags.scoped_vectors`), end-state ON for multi-tenant stores. Demonstrate-then-seal regression test in `tests/audit/test_substream_isolation.py`. Validated: scoped-pgvector full-10-conv `0.623668` ≡ the per-scope-isolated SQLite oracle `0.623668` exactly (partition key correct), ~0 recall vs unscoped `0.626719`. **Two gap-analysis findings recorded as the next levers:** (1) **single-hop cat4** (n=841, recall ~0.73) is the biggest retrieval-fixable lever — 89% of gold answers are fully present in-conversation, headroom ≈ +0.065 global; open: ranking-miss vs packing-displacement (needs one `--save-context` diagnostic). (2) **self-improvement loop is OPEN** — observe (`retrieval_event` records gold-hit/ranks/scores/context_recall) + validate + gate + re-measure exist, but there is **no auto-proposer and no apply step** (retrieval reads hardcoded weights + env flags, not a stored policy); `RetrievalFlags` are the seed of the apply step. Also corrected the stale `docs/audits/2026-05-28-locomo-retrieval-memory.md` (banner: RRF/reranker measured to regress, scope-search ~0 recall). Supersedes #273 below.
- Superseded handoff `HISTORY#273` - audit #3 retrieval-scoring levers, implemented behind three default-OFF env flags (`SEAM_RETRIEVAL_SEMANTIC_ZERO` / `_BM25_ALL` / `_RRF`) in `seam_runtime/retrieval.py` (`RetrievalFlags`) and wired through `runtime.search_ir`; flag-OFF is byte-identical to the locked baseline (`context_recall_mean = 0.528308`). Measured on the real pgvector Docker backend (exact NN search + `--workers 1` = deterministic given table contents; the shared `seam_vector_index` table must be TRUNCATEd between runs; noise floor ±0.002 = one borderline question). **R1 (semantic→0 when a live vector backend omits a record from the top-K, instead of bag-of-words fallback) is the only principled win:** full 10-conv (n=1542) baseline `0.626719` → R1 `0.631354` (+0.0046 global), with cat1 multi-hop +0.026 (identical across conv-26/5-conv/10-conv) and cat3 open-domain +0.018, at a small cat4 single-hop −0.004 cost. R2 (BM25-all-kinds), R3 (RRF), and the cross-encoder reranker regress or add noise-level gains with displacement. Per operator decision the infra landed default-OFF; **flipping R1 globally vs category-gating it for multi-hop/open-domain queries is an open operator lever.** Architectural follow-up surfaced: pgvector vector search is scope-UNFILTERED (single shared `seam_vector_index`, global top-K), so cross-scope vectors crowd in-scope records out of the top-K in multi-conversation runs — owed a scope-namespaced-search slice. Supersedes #271 below.
- Superseded handoff `HISTORY#271` - LoCoMo dataset recovery + permanent anti-loss safeguards. The LoCoMo dataset (`locomo10.json`) had been lost (it lived only on the 90%-full root volume, not T7; removed during an operator mount operation). Re-acquired canonical from snap-research/locomo (sha256 `79fa87e9…`, 2805274 bytes, 10 convs, 1986 QA), verified authentic against surviving fragments (this release relabels sample_ids: its `conv-30` == the prior release's `conv-26`; 103/104 fragment questions matched). Permanent safeguards landed: dataset committed in-repo at `benchmarks/external/locomo/data/locomo10.json` (on T7 + offsite via GitHub), `locomo10.manifest.json` pins URL+sha256+counts, and `tools/benchmarks/restore_locomo.py` restores+SHA-verifies from in-repo → T7 `.dataset_store/locomo/` → network (`--verify/--ensure/--to`). The canonical dataset is relocated off the root volume onto T7 per operator decision. No-paid 100-case slice (`--answerer none --judge none`, local bge-small embeddings, SQLite vector backend) reproduced `context_recall_mean = 0.528308`, an exact match to the HISTORY#242 baseline `0.5283084138` — proving byte-identity of the restored data, an intact harness, and SQLite==pgvector equivalence (no-paid path no longer needs the Docker pgvector service). Cleared 23 stale mislabeled per-scope DBs from `test_seam/locomo/`. **audit #3 (retrieval scoring) is now UNBLOCKED** (dataset restored) but not started. The full ~1542-case judged run remains paid + operator-gated and should follow the audit #3 fixes. Supersedes #270 below.
- Prior handoff `HISTORY#270` - SQLite concurrency-layer hardening on `audit/concurrency-hardening-270` (uncommitted in working tree at handoff time). Continues #269: `seam_runtime/pool.py` checkout() split into `_acquire`/`_release` so connection validation runs OFF the global lock (F3) and the blocking-exhaustion path validates+discards dead connections (F4); `seam_runtime/storage.py` decorates the six write methods with `retry_db_operation` so transient `database is locked`/`cannot commit` retries while non-transient errors (e.g. IntegrityError) propagate unchanged (F2). 9 new tests in `tests/audit/test_pool_concurrency.py`. Remaining audit follow-ups: S1 (shell argv execution — highest-value security), F5 (orphan-edge span:/pack:), B5 (fixture_hash divergence), B6 (benchmark determinism), beam/longmemeval durable-archive port, and audit #3 retrieval scoring (gated on restoring `locomo10.json`). Supersedes #269 below.
- Prior handoff `HISTORY#269` - audit-driven correctness slice on `audit/locomo-retrieval-bundle-270` (uncommitted in working tree at handoff time). Landed three audit fixes with 20 new tests: (1) `seam_runtime/pool.py` reset-on-return rollback closing the connection-contamination P1; (2) `seam bench publish` wiring the previously-dead `validate_publication_readiness` gate plus optional `SEAM_BENCHMARK_SIGNING_KEY` HMAC tamper-evidence in `seam_runtime/benchmark_integrity.py`; (3) durable LoCoMo result archiving to `benchmarks/runs/locomo/` (override `SEAM_BENCH_RESULTS_DIR`) with atomic+fsync+verified writes and per-scope `.partial.json` checkpoints on both sequential and parallel runner paths, so a multi-hour run is no longer lost to /tmp. Full audit at `docs/audits/2026-05-28-deep-health-audit.md`. Deferred: audit #3 (retrieval scoring RRF/semantic/BM25/reranker/temporal) pending restoration of `locomo10.json` and a measured dev-slice run; same durable-archive treatment still owed to the beam/longmemeval runners. Supersedes the prior handoff below.
- Prior handoff `HISTORY#268` - completed an unrecorded Qwen hardening pass on `feat/qwen-hardening-completion`. Replaced the unfinished `ShutdownManager` in `seam_runtime/server.py` with `ShutdownState` + `ShutdownMiddleware` + `_cleanup_runtime` + `_shutdown_timeout_from_env` matching the test API; defined module-scope `ALLOWED_SHELL_PATHS` / `ALLOWED_SHELL_COMMANDS` / `BLOCKED_SHELL_COMMANDS` frozensets in `seam_runtime/dashboard.py` plus `_validate_shell_executable` / `_validate_shell_command` / `_validate_shell_cwd` / `_get_shell_timeout` helpers; reverted `seam_runtime/storage.py` to HEAD then surgically re-applied only pool integration with `check_same_thread=False` so the merge-corrupted retrieval-event and improvement-proposal methods are restored. Kept Qwen's `seam_runtime/pool.py` + `seam_runtime/retry.py` + 3 audit test files (125 new tests). Added a Cut-off Recovery section to `AGENTS.md` applying to all agents and a new `QWEN.md` stub. Suite: 653 passed, 0 skipped, 0 failed (pgvector real-adapter tests run with ephemeral docker compose pgvector then torn down). Supersedes #267.
- Prior handoff `HISTORY#267` - polish + bugfix pass merged via PR #39: storage orphan-edge cleanup expansion across all record prefixes, batch_ir LIMIT/OFFSET consistency, temporal.parse_iso slice fix, retrieval LIKE escape, deterministic textual-field ordering, cosine docstrings, new `tools/history/retention.py` snapshot pruning CLI, pyproject upper bounds across optional extras, external-memory-benchmarks workflow bumped to Python 3.12, webui dead pane/component prune + HealthResponse cleanup, AGENTS.md topic vocab expansion.
- Earlier handoff `HISTORY#266` - H2 substrate slice 5 improvement_review gate; H2 phase 2 substrate complete. Slice 5 adds two append-only SQLite tables to `SQLiteStore` (`improvement_proposal` for the proposal body and `proposal_decision` for status transitions including the initial pending), `seam_runtime/improvement.py` with `validate_proposal()` (checks evidence `case_ids` against the slice-4 holdout pool and flags violations) plus `proposal_blocks_promotion()` (returns True unless latest status is approved AND no violation), and `tools/h2/improvement_review.py` CLI with subcommands `propose / list / show / approve / reject / summary` all supporting `--json`. Append-only: prior decisions are preserved so reverse decisions leave both rows in the audit trail. Hard rule preserved: slice 5 never writes to AGENTS.md / REPO_LEDGER.md / PROJECT_STATUS.md (enforced by a runtime test that drives the full CLI workflow in tmp and asserts those filenames never appear). 22 new tests in `tests/audit/test_h2_improvement_review.py`. H2 phase 2 substrate work (slices 1-5) is complete on `main`: substrate is writable both live (slice 2) and from history (slice 3), partitioned (slice 4), and gated for promotion (slice 5). PR #37 (HISTORY#265 slice 4) merged into `main` as commit `ed288d7`; PR #36 (HISTORY#263-#264) merged as `eeadf1c`; PR #34 (HISTORY#251-#261) merged as `c2dd45d`. `HISTORY#250` remains the GitHub PR stack closeout; `HISTORY#246` records the active GitHub ruleset `Protect main (PR + hygiene gates)`, and `HISTORY#249` records the `AGENTS.md` PR workflow for future agents. Materialising `benchmarks/external/locomo/holdout_assignment.json` against the real LoCoMo dataset, producing the first actual ranking-policy proposals from substrate evidence, and Step 4 final paid measurement all remain operator-gated.
- A fresh Linux clone should run `sh ./installers/install_seam_linux.sh --dev`, then verify local `HEAD` equals `origin/main` before starting new work.
- GitHub PR state as of 2026-05-18: PRs #22, #18, #23, #25 (SOP 0), #26, #27 (SOP 1), #28 (SOP 2), #29 (SOPs 3+4), and #30 (production readiness remediation) merged. Track I (SOPs 0-4) is complete on `main`. PR #19 is still draft, conflicting, and must be treated as a partial extraction source because its branch contains private-session-link material in commit metadata. PR #24 (Track I 5-SOP handoff series) was draft and is superseded.
- **Track I COMPLETE milestone.** Next track is the operator's choice per ROADMAP.md: Track J (Prompt Codec), Track K (Trust/Security/Auditability + BIL bundles), Track L (Agent/Skills Compiler), or Track H Phase 2-4 (improvement streams, retrieval integration, generalized library streams). Do not resume from already-merged branches or stale squash-merged PR refs. Do not propose or start Tracks J/K/L without operator direction.

## What Is Stable

- Core runtime paths (compile, verify, persist, search, context, benchmark)
- Textual dashboard (interactive TUI, chat, slash palette, reload command, MIRL animation, independent pane scrolling including Settings and Overview, ExplorerTree navigation, status bar, colored Rich markup panels, focus zoom toggle, Settings tab, and live Overview health bars)
- Dashboard installers: `seam-dash` shim on Windows (`.cmd`) and POSIX; `seam-dash` entrypoint in `pyproject.toml`
- Linux installer modes: `sh ./installers/install_seam_linux.sh` for global user commands, `sh ./installers/install_seam_linux.sh --dev` for repo-local Python development bootstrap and SEAM continuity checks.
- Dashboard launcher: `scripts/windows/launch_dashboard.bat` + `launch_dashboard.ps1`; propagates pgvector config from `SEAM_LOCAL_ENV` or a private Documents `SEAM\local\.env`
- pgvector real adapter: Docker Compose service `seam-pgvector` (image `pgvector/pgvector:0.8.2-pg18-trixie`, port 55432)
- Dashboard snapshot/smoke-test behavior
- Benchmark bundle verification, diff, gate, holdout workflow, and Windows GitHub Actions workflow (see HISTORY#095)
- Benchmark Integrity Level Phase 1 workflow: `seam bench inspect` reports BIL-0 for raw benchmark results, `seam bench seal --level BIL-1|BIL-2` writes deterministic bundles, and `seam bench verify` checks stable result hashes, BIL-2 manifest hashes, and BIL-2 manifest/result consistency. Stub-judge results require explicit `--allow-stub-seal` above BIL-0.
- `seam bench external --plan` CLI alias and `benchmarks/registry/memory_benchmarks.json` registry
- External memory benchmark suite (Track I complete): `seam bench external --quickstart locomo` with `--adapter {seam|mem0|zep}` and `--judge {stub|claude|openai}`; Mem0 comparator behind `seam[bench-mem0]` optional extra; Zep comparator behind `seam[bench-zep]` optional extra; three-way SEAM/Mem0/Zep scoring reproducible on quickstart fixture
- REST API skeleton: `seam serve`, `seam-server`, optional `server` extra, bearer-token protected endpoints, bounded request bodies, env-configurable process-local rate limiting, and remote authenticated bind guardrails
- RAG efficiency surface: `seam ingest <path> --persist`, `seam memory search`, `seam memory get`, `seam retrieve --mode mix`, document status rows, vector source-hash cache/stale checks, and `seam mcp stdio` MCP server
- MCP agent bridge: `seam mcp stdio` and `seam-mcp` expose 16 bounded, documented tools over standard MCP JSON-RPC for Gemini/Claude/Cursor-style clients; Gemini's project config starts it with `--ensure-pgvector` so Docker Compose pgvector is healthy before MCP tool discovery; `seam mcp serve` remains as the legacy JSON-lines bridge. Tools cover memory retrieval, controlled ingest, context, install diagnostics, stored HS/1 surfaces, index status, retrieval modes, and benchmark summaries; canonical `hs:<hex>` surface refs are required for surface tools.
- Interactive CLI shell: `seam shell` and `seam chat` provide the first REPL-style memory interface, with slash commands and prompt-ready context output
- Holographic Surface surface commands: `seam surface compile|encode|decode|verify|query|search|context|import`; `bw1`, `rgb`/`rgb24`, explicit `rgba32`, and explicit `rgba64`; `surface` benchmark exactness gate, stored lookup gate, stored query gate, repair gate, and repair-query gate
- Holographic Surface library commands: `seam surface store|list|show|repair`, plus `compile --store` and `encode --store` for SQLite metadata registration, redundant file-backed copies, and repair of missing/corrupt stored copies without committing generated user artifacts
- Durable history protocol (`AGENTS.md`, `HISTORY.md`, `HISTORY_INDEX.md`)
- Active/inactive separation: `docs/CODE_LAYOUT.md` maps live vs archived paths; `.rgignore` gates code search
- Token-bounded context loading via history snapshots and `tools.history.build_context_pack`
- Route-aware data classification through `tools/history/routing_manifest.json` and `docs/ledgers/`

## Active Focus

- Deferred: ~113 remaining assertTrue→specific-assertion replacements in test_seam.py (backlog card roadmap:track:F:asserttrue-scrub)
- Deferred: 10 low-priority backlog items catalogued under Track F (Phase 7)
- Reduce startup context overhead by relying on compact index + surgical history reads
- Preserve near-complete temporal history without loading all history into model context
- Keep maintenance, security, context, and runtime facts logically routed for AI search without duplicating chronology
- Make compression produce directly readable AI-native machine language, with opaque byte payloads used only as optional reconstruction/integrity backing layers
- Treat SEAM-HS/1 Holographic Surface as a queryable visual snapshot layer for MIRL/RC payloads, not as free compression or a replacement for SQLite truth
- Make the full functional visual-memory loop shippable: documents compile into directly readable MIRL/RC, MIRL/RC packs into SEAM-HS/1 PNG surfaces, stored surfaces remain addressable by metadata/hash, query/context can read the embedded payload directly from the image surface without restoring the original document, and the surface benchmark gates stored lookup plus repair at 100%
- Keep this private GitHub repo as the source-of-truth home for runtime files while leaving generated/operator user surfaces out of git unless deliberately promoted as fixtures or docs assets; future user-file sets belong in a separate repo
- Keep roadmap execution tied to history entries and supersedes chains
- Turn the competitive plan into shippable surfaces: finish README/install polish, graph/vector/mix retrieval hardening, agent bridge docs, and benchmark coverage without breaking existing CLI aliases
- Build the Agent Compiler workstream from `docs/roadmap/AGENT_COMPILER.md`: compile canonical SEAM protocol into model-specific adapters, benchmark those adapters, and audit installed local skills before applying changes.
- Continue hardening the `webui/` browser dashboard (served by `seam serve`/`seam webui` from `seam_runtime/webui/`): the static `dashboard.html` IDE shell is the shipped working UI; finish or replace the incomplete `webui/src/` React rewrite, and deepen live REST coverage without regressing graphs, settings, terminal, or chat.
- Turn the SEAM CLI into a first-class agent CLI like Gemini/Claude/Codex CLI: keep `seam shell` as the entrypoint, then add model routing, tool execution, repo/context awareness, command history, and guardrails on top of SEAM memory.
- Continue feature delivery without reintroducing duplicated continuity text
- Run real-adapter validation through guarded scripts to enforce resource ceilings and automatic service cleanup
- Legacy roadmap entries `HISTORY#028`-`HISTORY#047` remain append-only planning cards. `HISTORY#036` (holdout suites), `HISTORY#037` (benchmark diff tooling), and `HISTORY#046` (REST API surface) are implemented and superseded by `HISTORY#152`, `HISTORY#153`, and `HISTORY#154`; use `ROADMAP.md` Recommended Course for current priority.
- Track H Context Streams Protocol is planned (Phase 1 = H1 substrate; Phase 2 = H2 retrieval-feedback subset now). Full design captured in `docs/roadmap/CONTEXT_STREAMS.md`; HISTORY#243 starts the Track M subset immediately: retrieval-event capture, stale bundle backfill, current no-paid labels, dev/holdout split, and structured experience lessons. Broader stream retrieval integration (H3) and generalized library streams (H4) remain later.

## Operational Baseline

- Use `scripts/windows/launch_dashboard.bat` (wraps `launch_dashboard.ps1`) to start the dashboard on Windows with pgvector configured. Use `/reload` or `reload` inside the dashboard to rebuild dashboard panels, metrics, and chart state without restarting.
- Use `scripts/run_real_adapters_guarded.ps1` for end-to-end real adapter checks.
- Use `scripts/run_guarded.ps1` for heavy local commands where CPU/RAM/disk guardrails are needed.
- Use `scripts/store_benchmark.ps1` to archive benchmark runs under Documents with sequence+time folders, run index, and publication metadata/hashes.
- Use `seam benchmark diff <run-a> <run-b>` before claiming a benchmark improvement, `seam benchmark gate <bundle> [--baseline <run-a>]` before merge/release, and `seam benchmark run --holdout --confirm-holdout` only for publish-time audits.
- Use `python -m tools.history.build_context_pack --topics <tags> --latest <n> --token-budget <budget>` for bounded task context.
- Use `python -m tools.history.verify_continuity` before ending a changed session.
- Use `python -m tools.history.verify_routing` after changing data classifications or ledgers.
- Default memory guardrails are `82%` warning and `90%` hard limit.
- pgvector Docker Compose: `docker compose --env-file <private-env> up -d pgvector`; container `seam-pgvector`; port 55432; credentials stay outside the repo. Agent MCP startup can automate this with `seam-mcp --ensure-pgvector`.

## Working Rule

When resuming:

1. Read `PROJECT_STATUS.md`.
2. Read `REPO_LEDGER.md`.
3. Read `HISTORY_INDEX.md`.
4. Read `docs/CODE_LAYOUT.md`.
5. Read `docs/DATA_ROUTING.md` when the task touches history, ledgers, maintenance records, routing, context budget, or auditability.
6. Pull only required `HISTORY.md` entries by index offsets or with `python -m tools.history.build_context_pack`.
