All notable changes to supamem will be documented in this file.
[0.4.0a2] — 2026-08-20 — Post-release field-report fixes: truthful doctor / config / install surfaces (Phase 19.1)
Phase 19.1 is a correctness-and-truthfulness pass driven by a field
report filed against 0.4.0a1 (findings SM-1…SM-9 plus release note
RN-1). Every fix removes a place where supamem told the user something
that was not true: a ✓ stamped on stale data, a --dry-run that
wrote state and mispredicted its own work, a repair that crashed on
the exact state it exists to heal, a ⚠ on a declared-optional extra,
a config key silently discarded, a repair round-trip that downgraded
a working config or clobbered a file another generator owns, and a
documented install command that resolved backwards to a release older
than the features the docs describe. No retrieval-stack code changed in
Phase 19.1 — every lever is in the config / installer / doctor / docs
layers, so recall / MRR / nDCG / latency bench metrics are
definitionally unchanged (coderag floors N/A).
- SM-1 — unknown keys in
[supamem.eval](and every other config table) are no longer silently discarded. A warn-only unknown-key diff now runs at the shared_apply_section/_apply_nestedchoke point, so every load path (pyproject.toml,.supamem/config.toml,SUPAMEM_CONFIG) surfaces typos on stderr naming the table, the unknown keys, and the accepted keys. Never raises — a typo must not break MCP stdio startup; stdout stays byte-empty. Cited Plan 19.1-03. - SM-2 —
doctorcan no longer stamp✓on a stale update-check cache.doctor_reportgainedstale+cache_age_secondsas the single source of truth; explicitdoctorruns attempt one bounded, suppression-honored, offline-saferefresh_stale_cachefirst.✓is now structurally unreachable from stale data — a stale cache renders a neutral info marker naming the cache age and last-seen version. Cited Plan 19.1-02. - SM-3 — a declared-optional extra is no longer reported at fault
severity. The coderag doctor panel forks on
importlib.util.find_spec: optional-absent renders→with an install hint, and⚠is reserved for present-but-broken. Cited Plan 19.1-02. - SM-4 — upgrades no longer accumulate duplicate managed blocks.
New
config_io.sweep_managed_blocksprimitive merges duplicatedBEGIN/END SUPAMEMfences into one canonical block at the target version, preserving user text verbatim and idempotent on re-run (byte-identical no-op on healthy input). Wired ahead ofextract_managed_blockat every managed-block call site in theclaude-codeandopencodeinstallers; uninstall paths write a.bak.<time_ns>sibling before healing a user file. SM-4d:doctornow counts BEGIN markers per target and reports duplicate managed-block drift with repair advice. Cited Plans 19.1-01, 19.1-02. - SM-6 — duplicate managed blocks no longer crash
repairanduninstall.extract_managed_blockkeeps its strict multi-BEGIN raise (locked), but the healing layer above it sweeps first, so the recovery tool is no longer disabled by the state it exists to recover from.--dry-runinspection works on duplicated state too. Cited Plan 19.1-01. - SM-7 —
--dry-runis now honored at every write site on the repair path, and its accounting is truthful.dry_runis plumbed through uninstall (all three clients), the agent patcher, share-dir sync, and model pre-fetch;would_writecounts derive from the sameWriteResult.diffcondition the real run uses, so prediction cannot diverge from reality (pinned by accounting-invariant tests on both the install and repair paths). The patcher still runs its full detection pass under dry-run and withholds only writes, so the would-patch count stays truthful. Console vocabulary fixed: no✓claims under dry-run. Cited Plan 19.1-04. - SM-8 —
repairno longer replaces a working, explicit MCP config with a less robust one. Client MCP entries (claude-codeandcursor) are now emitted with ashutil.which("supamem")-resolved command (falling back to the bare name whenwhichreturns nothing) and pinSUPAMEM_CONFIGalongsideSUPAMEM_PROJECT_ROOTwhenever a project config exists. A repair round-trip can no longer downgrade config robustness. Cited Plan 19.1-04. - SM-9 —
repairno longer writes into paths another generator owns. Both cursor write sites (the.mdcwhole-file copy and thehooks.jsondeep-merge) detect generator-managed destinations via a head-marker heuristic (generated/do-not-edit/auto-generatedin the first ~10 lines) or a sibling*.manifest.json/.manifest, skip the write, and warn naming the file plus both remedies. Unmarked differing targets still update (the legitimate upgrade path is intact) but now warn visibly — detection never keys on content difference alone. Cited Plan 19.1-05. - SM-5 — the documented install command no longer resolves backwards
to 0.2.0. All five READMEs and
llms.txtnow teach explicitly pinned commands (uv tool install 'supamem==0.4.0a2',pipx install supamem==0.4.0a2,pip install supamem==0.4.0a2). Because the newest stable release (0.2.0) predates the entire0.3.x/0.4.xpre-release line, an unpinned install resolved to a version older than every feature the docs describe — anduv tool upgrade supamemwas a downgrade. An exact pre-release pin is a first-party requirement carrying a pre-release identifier, so it resolves with no--prereleaseflag while dependencies stay stable;--prerelease allowis deliberately not documented because it applies to the whole resolution and was reproduced pullingpydantic 2.14.0b1into the tool environment. A new docs-drift guard locks the pin so future edits cannot silently unpin it. Cited Plan 19.1-06.
A deep code review of the Phase 19.1 changes ran before this tag was cut. Two of its findings are defects live in the published 0.4.0a1; the rest were regressions introduced by the SM fixes above and caught before release.
- A file that merely documents supamem no longer bricks
install,uninstall, andrepair(live in 0.4.0a1).extract_managed_blockaborted on the count of unpairedBEGIN SUPAMEMmarkers whilesweep_managed_blockshealed only completeBEGIN/ENDpairs, and neither pattern was line-anchored. One prose sentence quoting the marker plus one real installed block therefore raisedValueError: multiple BEGIN SUPAMEM markers found in texton all three verbs, and the documented remedy (supamem repair) reported zero duplicates to heal. Marker detection is now one shared grammar in which aBEGIN/ENDpair is the smallest well-formed region. repair --client cursorno longer destroys a generator-managed.cursor/rules/*.mdc(live in 0.4.0a1).installcorrectly refused to overwrite a file another generator owns, but theuninstallhalf ofrepairunlinked it unconditionally — no guard, no backup, no warning — after which the reinstall wrote the packaged copy over it. The generated-marker guard now covers the removal side as well, and the write path takes a.baktwin like every sibling target.doctorno longer reports phantom duplicate managed blocks. Its block counter matched bare marker mentions rather than fences (despite a comment claiming parity withconfig_io), so a prose mention drovedrift, exit 1, and a "run supamem repair" hint that could not clear it. It now uses the sharedcount_managed_blocks.- A config typo can no longer break CLI or MCP startup. Unknown
key names were interpolated into Rich markup unescaped, so a key
such as
"[/bold]"raisedMarkupErrorout ofload_config()— the one thing SM-1 promised could never happen — and"[bold]typo"rendered with the offending name swallowed. User-supplied keys are now escaped, and the warning fires once per load rather than twice. __version__now matches the versionpyproject.tomlships. It was left at0.4.0a1, with nothing tying the two together. Because the update check compares__version__against the newest release tag, every user already running 0.4.0a2 would have been told an update was available, permanently. A parity guard now locks them.- Also fixed:
repair/uninstallno longer rewrite and back up third-party JSON config purely to normalize whitespace;--dry-runsays "would sweep" instead of claiming "swept"; the duplicate-block sweep dedups whole blocks rather than lines;doctor's optional-extra probe and cache-age reporting no longer crash or fabricate an age; the SM-4d duplicate check inspects theAGENTS.mdthe opencode installer actually writes; and the SM-5 install-pin guard also catches upgrade-form commands.
sweep_managed_blocksremoves orphanedBEGIN/ENDmarker lines left behind by an interrupted upgrade. Surrounding user text is preserved, and the managed-block file paths take a.baktwin before healing — but supamem now deletes its own stray fence markers from a file you own, which it previously left in place.
--force-cursor-rulesonsupamem installandsupamem repair— escape hatch overriding only the SM-9 generated-marker skip. The overwrite-warning floor always prints, so force never degrades back to a silent clobber. Cited Plan 19.1-05.- Flat
regress_baseline_*aliases accepted as[supamem.eval]keys with chain source attribution — the flat namesdoctorprints are now valid config keys. Canonical spelling wins when both forms are present. Cited Plan 19.1-03. - Dual-scope help text for the subagent patcher across
install/repair/initand theunpatch-agentsdocstring: both~/.claude/agents/and<project>/.claude/agents/are named as patch targets (they were already both scanned; the help text lied by omission). Cited Plan 19.1-04.
-
Release-day installs may hit uv's stale index cache (RN-1). If
uv tool install 'supamem==0.4.0a2'reports that no such version exists while PyPI already serves it, add--refresh:uv tool install 'supamem==0.4.0a2' --refreshThis is uv index-cache behavior, not a supamem bug.
-
TextContentis still the compact summary card (0.4.0a1 behavior change, restated for anyone upgrading straight from 0.2.x):structuredContentremains the complete canonical payload.
Phase 19 migrates the MCP server onto the official MCP SDK v2 server
class and halves measured tool-response tokens via single-arm
CallToolResult returns, with two new [supamem.mcp] config keys
(response_format, cache_ttl_ms) and an offline MCP-response token
instrument backing every number below. No retrieval-stack code changed
in Phase 19 — all levers are serialization/config-layer, so recall /
MRR / nDCG / latency bench metrics are definitionally unchanged
(coderag floors N/A: git diff aedd10f..HEAD -- src/supamem/retrieval src/supamem/eval/runner.py is empty).
- Migration to the official MCP SDK v2 server class (
MCPServer). The dependency pin moves from the unboundedmcp>=1.13(which broke fresh installs by resolving mcp 2.x against v1-only import paths) tomcp>=2,<3(resolved: mcp 2.0.0; transitives httpx2 2.12.0, opentelemetry-api 1.44.0, mcp-types 2.0.0). Constructors are identity-only (MCPServer("supamem")); transport kwargs (host,port) move torun(transport="streamable-http", ...). The pydantic floor is raised>=2.5→>=2.12(resolved 2.13.3). 2025-era clients keep working — v2 serves every earlier protocol revision. Cited Plans 19-01. - Behavior change — tool
TextContentnow carries the compact Markdown summary card (🧠 search summary / write confirmation) instead of the full pretty-printed JSON the v1 SDK appended to every tool result.structuredContentremains the complete canonical payload (unchanged shape) — hosts or programmatic consumers relying on the old full-JSONTextContentarm must readstructuredContent(RESEARCH Open Question 5 disclosure). Applies to all four registered tools (dual_memory_search,dual_memory_write,qdrant_find,qdrant_store). Cited Plan 19-03. - Measured response-token delta (Phase 19 instrument,
supamem.eval.mcp_response_tokens— fixed 10-query workload over top_k=5 responses, 3 runs, deterministic corpus, p50 est. tokens): total 3418 → 1712 (0.501×) by default; 1456 (0.426×) withresponse_format = "concise"; text arm 1728 → 22 (0.013×); structured arm byte-identical at 1690 (default mode). Baseline from 19-BASELINE.json (pre-lever double-arm shape, sanity-banded per RESEARCH §2.2). Cited Plans 19-02, 19-03.
[supamem.mcp] response_format = "concise" | "detailed"(default"detailed"— byte-identical responses;"concise"empties the display-onlypreviewfields while everyChunk.textremains fully intact). Fail-closedload_configenum gate; ConfigChain mirror; shipped defaults block inshare/default.toml. Cited Plan 19-03.[supamem.mcp] cache_ttl_ms(default0= off; negative values exit 2 at config load). When > 0,tools/listresponses carry SEP-2549 cache hints (honored by 2026-era clients) via the SDK's per-method constructor map —tools/callresults are never cached (the installed mcp 2.0.0 exposes no per-result stamping surface), so write-then-read visibility is always preserved. Cited Plan 19-03.- MCP-response token instrument (
supamem.eval.mcp_response_tokensmodule +python -mCLI): measures the serializedCallToolResultwire shape (both content arms) through the same SDK tool-manager layer hosts use, with a locked percentile contract (p50 = median, p95 = nearest-rank) and an injectable offline backend. Cited Plan 19-02. - Unconditional
file_pathdedup on search results —file_pathis set tonullwhen it duplicatessource(the path survives insource); applies in both response_format modes (~2.6% smaller payloads). Cited Plan 19-03.
Phase 17 ships two opt-in retrieval-stack plugins (AST chunker for Python, HyDE-style query expansion via local Ollama) plus a chunk-level recall metric, an Ollama warm-pool doctor panel, and ADR-0002 §9 — the paired-bootstrap uplift comparison vs the Phase 16 baseline-3. Defaults are unchanged in the 0.3.x line. Default-flip is gated on v0.4 per D-LAT-01 (the new HyDE retrieval violates the 5000 ms p95 hard ceiling on 4/5 cells against the live corpus; AST chunker stays inside the ceiling but the recall lift is modest).
tree_sitter_codeAST chunker plugin (opt-in, Python only) (Req-02). Registered under the existingsupamem.chunkerentry-point group alongsidemarkdown_headerandtranscript:tree_sitter_code = "supamem.indexer.chunker_tree_sitter:chunk_tree_sitter_python". Function-style entry mirrorschunk_markdown. Installed only when the user opts in via the new optional extrapip install supamem[ast-chunker](tree-sitter>=0.23,<0.26,tree-sitter-python>=0.23,<0.26). Token budget enforced viafastembed.TextEmbedding.token_count(matches the MiniLM tokenizer used downstream). Parse errors fall back tochunk_markdownwith anerr_consolewarning per D-AST-03 — never raises into the indexer hot loop. When the extra is missing and a user has setchunker = "tree_sitter_code", the lazy import raises aRuntimeErrornaming the fix command (pip install supamem[ast-chunker]) per D-PKG-02 — silent fallback would hide the misconfiguration. Cited Plan 17-B.tuned_hybrid_hyderetrieval plugin (opt-in, Ollama-backed) (Req-03). Registered undersupamem.retrievalnext totuned_hybrid/filtered_dense/dense/bm25. Composition-over-inheritance —self._inner = TunedHybridBackend(config=config)keeps thetuned_hybrid.pybackend byte-identical. Per query: POST to<localhost-ollama>/api/generatewith the locked HyDE prompt (Write a 3-5 sentence decision rationale that would answer this question, as if extracted from an ADR or code comment. Be specific and technical.per D-HYDE-01),keep_alive=-1(warm-pool retention per D-HYDE-04, RESEARCH Pitfall 2), 600 ms timeout with one retry, fall back to original query text on any failure (D-HYDE-03) and emit anerr_consolewarning. Localhost-only guard reused fromsupamem.eval.judge._resolve_ollama_host(SystemExit(2)on non-localhost — inherits D-07). Latency telemetry viastats.counter.bump("hyde", "hyde_latency_ms", 0, elapsed_ms). Cited Plan 17-C.- Chunk-level recall metric +
payload.chunk_idenvelope key (Req-01). Coderag eval envelopes now carryrecall_at_1_chunk/recall_at_5_chunk/recall_at_10_chunk/recall_at_20_chunksiblings beside the existing doc-level keys.payload.chunk_idis set ONLY by the coderag bench ingest path (supamem.eval.coderag.ingest) — deterministic<rel_path>#<sha1(text)[:12]>. New_build_run_chunksibling does NOT dedup on duplicate doc_ids (Pitfall 4 — Phase 16's_build_runcollapsed chunks of the same file to one row, hiding the very signal chunk-level recall is supposed to expose). Doc-level path stays byte-identical — Phase 16 floors test re-runs green (Req-06). Cited Plan 17-A. - Ollama warm-pool diagnostic panel in
supamem doctor(Req-04 mitigation). Fires only whenretrieval.backend = "tuned_hybrid_hyde"is configured. Probes/api/pswith a 1s timeout; surfaces loaded model + load duration. Read-only — NEVER raises, NEVER flips the doctor exit code per D-DOCTOR-04. Same localhost guard as the HyDE retrieval backend. Cited Plan 17-D. - ADR-0002 §9 — "Phase 17 uplift comparison" (Req-07). Three sibling
sub-tables (
### default vs ast_on,### default vs hyde_on,### default vs ast_plus_hyde) carrying paired-bootstrap deltas of the three Wave-3 LIVE envelopes against the Phase 16 baseline-3 LIVE envelope, parser-locked bytests/test_adr_phase17_uplift.py(mirrors the Phase 16 §8 ADR-as-test pattern). Reusespaired_bootstrap_deltaunchanged. Cited Plan 17-H. --reingest-coderagCLI flag onsupamem eval --suite coderag(Req-09 / G5 wiring). Default OFF — Phase 16 baseline byte-identical replay path preserved when the flag is absent. When ON: drops thesupamem_eval_coderagcollection and rebuilds it via thesupamem.chunkerentry-point keyed oncfg.chunker(e.g.tree_sitter_code) BEFORE scoring. The same flag also dispatches the retrieval backend bycfg.retrieval(e.g.tuned_hybrid_hyde) so a single CLI invocation drives both interventions end-to-end. Cited Plan 17-B2.- Three Wave-3 LIVE coderag envelopes (Req-05/06).
17-E-LIVE.json(AST-only),17-F-LIVE.json(HyDE-only),17-G-LIVE.json(AST + HyDE combined) — produced against the live 21,235-chunk corpus with--reingest-coderagand re-rank ON. Anchor the ADR §9 deltas.
- HyDE retrieval verdict — opt-in only; defaults UNCHANGED (D-LAT-01).
HyDE meets the Track B recall goal exactly at threshold
(
decision_rationale.supamem_only.recall_at_10.000 → 0.500) but violates the D-LAT-01 hard ceiling on 4/5 cells (max measured p95 6069 ms ondecision_rationale.supamem_only, vs the 5000 ms ceiling set in v0.3.0a6) AND the Req-04 per-cell budget on 5/5 cells (max delta +2270 ms). HyDE rewrites also produce a −0.25 MRR regression on thecode_factaxis that users would feel — the one-size-fits-all prompt over-steers code-fact queries. HyDE stays opt-in only in v0.3.0a7; no default-flip path. Phase 18 follow-up — selectivity gating by axis. Cited Plans 17-F, 17-G, 17-H. - AST chunker verdict — opt-in only; defaults UNCHANGED. AST chunker
stays under the D-LAT-01 ceiling on all cells; modest recall lift
(
code_fact.combined.recall_at_10+0.005,ndcg_at_10+0.227;decision_rationale.supamem_only.recall_at_10+0.500 on small N). Default-flip is gated on v0.4 — 0.3.x defaults preserved so the Phase 16 byte-identical replay path stays the released-and-locked baseline. Cited Plan 17-E. - Combined (AST + HyDE) verdict. Same latency violation as HyDE-only
(the HyDE leg dominates the p95). Combined rescues
code_fact.combinedMRR vs HyDE-alone (back to 0.000 delta, no regression) but does NOT fix the hard-ceiling violation. Opt-in only. Cited Plan 17-G.
- ADR §9 paired-bootstrap CIs collapse to
[delta, delta]. The v1 LIVE envelope schema records per-cell means only — noper_query.<axis>.<col>.<metric>arrays. The §9 §8-shaped tables therefore callpaired_bootstrap_delta(samples_a, samples_b)with constant-mean arrays of lengthn = len(qrels), which produces an exactdeltabut a degenerate[delta, delta]CI. Qualitative tag (win/tie/loss) reads from the CI sign. Delta values are exact; CI bounds do NOT reflect query-level uncertainty. A future envelope-schema bump (preserve per-query arrays) unlocks real CIs through the same call site with no §9 structural change. Surfaced explicitly in §9's intro paragraph and17-H-SUMMARY.md. recall_at_*_chunknull across 17-E/F/G. Common gold-chunk derivation gap — Plan 17-A wired the envelope keys but the derivation either didn't fire or produced empty sets. §9 omits chunk-level rows; doc-level recall remains the gated signal per Req-06. Follow-up: investigatecoderag/runner.pychunk-gold path before any future §9-style write-up.
- Phase 14 + 15 + 16 byte-identical regression locks preserved.
_run_goldens_legacy(D-VEND-04) andsrc/supamem/retrieval/filters.py(D-QGEN-06;repo,axis,session_idall remain pass-through with ZERO new branches) still byte-identical after every Phase 17 commit. The Phase 17 §9 append todocs/adr/0002-coderag-eval-philosophy.mdleaves §§1-8 verbatim — the Phase 16 floors test (§7 + §8) still parses green. - New optional extra
[ast-chunker]is opt-in by construction — users who never setchunker = "tree_sitter_code"and never setretrieval.backend = "tuned_hybrid_hyde"see zero behavior change and pay zero import cost (lazy plugin discovery). - 5-README lockstep + bumped
synced-withSHA per AGENTS.md.tests/test_readme_translations_phase17.pyis the new sibling regression mirroring_phase16.py.
- Auto-queries-from-manifest wiring in
--full(Req-01)._run_coderagin full mode now constructs records fromauto_queries.extract_pr_queries()+extract_adr_queries()against the populated corpus manifest, NOT fromcoderag_smoke.json. The smoke fixture continues to drive the default offline path unchanged. Each record carries aquery_originfield (pr_title/adr_problem/adr_why) and atraining_leakage_suspectedboolean — the latter flipstruefor any query whose source repo's pinned commit-SHA postdates the retrieval model's known training cutoff. Cited Plan 16-B. corpus.ensure_populated_manifestlazy build-on-call (Req-02). New idempotent orchestrator that reads the bundled placeholder manifest, fetches+walks repos at pinned SHAs, and writes the realized manifest (with content-SHAs) toplatformdirs.user_cache_dir("supamem") / "coderag" / "manifest.json". The bundled package manifest stays placeholder; the user-cache copy holds the realized version. Re-runs on an unchanged corpus are byte-identical no-ops. Cited Plan 16-A.metrics.paired_bootstrap_delta(samples_a, samples_b, n_resamples=10000, seed=42)(Req-04). Pure-stdlib paired-bootstrap with percentile CI — no scipy dependency. Sign convention<peer>_vs_supamem: positive delta = peer wins (mem0 better than supamem on this cell). 95% CI by default. Identical sample arrays produce delta=0 with CI bracketing zero; divergent samples produce a delta whose sign matches the mean delta and whose CI does not bracket zero at >95% confidence. Cited Plan 16-C.- Mem0 head-to-head row with paired-bootstrap CI delta (Req-04).
report.pyadds peer-row scoring at the envelope-builder boundary — callsmem0_adapter.query()per record, scores against the same gold IDs viapytrec_eval, and writes results underenvelope.peers.mem0.scoresANDenvelope.comparisons.mem0_vs_supamemwith the paired-bootstrap delta + 95% CI per axis × column × metric. Cited Plans 16-C, 16-D, 16-F.
- ADR-0002 §7 rewritten with live three-run variance-gated floors
(Req-03). Phase 15's offline floors (
recall_at_5 = 1.000on the trivially-recovered 6-question smoke; latency< 0.005 msfrom deterministic dict lookup) are removed. New floors derived frommean(LIVE_1, LIVE_2, LIVE_3) − ε_rankingandmean(...) + ε_latencyper axis × column cell against the populated 21,235-chunk corpus. ε per §4 (ε_ranking = max(stddev, 0.005),ε_latency = max(0.05·mean, 5ms)). The hard latency p95 ceiling moves 500 ms → 5000 ms per D-LAT-01 as a one-shot forward-looking adjustment (max measured live p95 = 4593.35 ms on decision_rationale.supamem_only sat at ~92% of 500 ms × 10) — NOT a sliding scale; subsequent phases tighten or hold, never relax. Cited Plan 16-F. - ADR-0002 §8 (NEW) — "Mem0 peer comparison" (Req-04). Live
head-to-head against mem0 default-config (
mem0ai==2.0.1, HuggingFaceall-MiniLM-L6-v2,infer=False) — 4 markdown tables (code_fact × {supamem_only, fastapi_only, combined} + decision_rationale × supamem_only) withmetric / supamem / mem0 / delta / ci_lower / ci_upper / qualitativecolumns. Sign convention: positive delta = mem0 wins. Aggregate Phase 16-E tally: 9 wins / 21 ties / 0 losses across 30 cells; mem0 wins concentrate on the recall@k tail (k ∈ {10, 20}) under the chunker-granularity caveat (mem0 ingested 2147 finer-grained records;_build_rundedups by doc_id, so more chunks ⇒ more shots per query — surfaced explicitly in §8 prose). Reproducibility footer pinsn_resamples=10000,seed=42, supamem SHA, mem0 SDK version, and the ROCm GPU rerank stack used to capture the numbers. Cited Plans 16-C, 16-D, 16-F. - Schema-compat:
peersandcomparisonsare always-present dicts (D-PEER-03). Non---peerenvelopes emitpeers: {}ANDcomparisons: {}(empty dicts, NOT absent keys) so downstream consumers can safelyenvelope["peers"].get("mem0")without a KeyError. Backward-compatible with v0.3.0a5 envelopes that simply omitted these keys.
- Phase 14 + Phase 15 byte-identical regression locks preserved
unchanged (Req-05).
_run_goldens_legacy(D-VEND-04) andsrc/supamem/retrieval/filters.py(D-QGEN-06 —repoandaxisremain pass-through keys with ZERO new branches in the filter dispatcher) — both still byte-identical after every Phase 16 commit. - The Phase 16 ADR rewrite ships the public artefact under the
filename
docs/adr/0002-coderag-eval-philosophy.md(post-Phase-15 rename); §1..§7 unchanged in position, §8 is purely additive (no tail renumbering required). ADR-0001 cross-link audit confirmed no §N references into 0002.
- coderag eval suite. New
supamem.evalplugin entry-point group with the first registered suite,coderag. Deterministic two-repo haystack (supamemself +fastapiexternal; both pinned to commit-SHAs viasrc/supamem/eval/datasets/coderag_corpus_manifest.json— never tag, never track-main). Two query axes:code_fact(PR-derived queries with file-modification gold) anddecision_rationale(ADR Problem/Why-derived queries; supamem-only per A-D-HAY-04 — fastapi has nodocs/adr/at the v1 corpus pin). Three-column metric reporting (supamem_only/fastapi_only/combined) per axis makes self-reference circularity audit-visible. See ADR-0002. supamem eval --suite coderag [--full] [--out PATH] [--peer mem0]CLI surface.--fullruns against the full pinned corpus;--peer mem0adds a parallel mem0 row in the metric envelope.- mem0 peer adapter (
peers-mem0extras:mem0ai>=2.0,<3.0). Single canonical default config; ingests source documents into its OWN Qdrant collection (supamem_eval_coderag_mem0— separate fromsupamem_eval_coderagper A-D-DEF-02 / Pitfall 7: mem0 owns its schema). Reported as a parallel row, never a gate. pytrec_eval>=0.5added to theevalextras for canonical IR metric scoring (Recall@k, MRR, nDCG@10).supamem doctorcoderag panel (read-only) surfaces cache/manifest presence and the resolved bench-collection name.- Bundled
coderag_smoke.json(≤200 KB; 6 questions across both axes) for offline PR-CI — no live Qdrant or network required. - Validation invariants INV-01..10 + INV-A1..A3 enforced in
tests/test_coderag_invariants.py. INV-A1 collapsesdecision_rationale.combinedtosupamem_onlywhenfastapi_only is null(single locus at the envelope-builder boundary). INV-A3 (this release) verifies the REQUIREMENTS.md edits. - Three-run baseline + ε derivation rule (Plan 15-C):
ε_ranking = max(stddev, 0.005);ε_latency = max(0.05 × mean, 5ms); hard latency p95 ceiling 500 ms. Locked numerical floors live in ADR-0002 §7.
- LongMemEval demoted to on-demand-only for full runs. The
5-question
longmemeval_scoped_smokefixture (Phase 14) stays on PR-CI; full LongMemEval_S no longer gates releases. The Phase 13 ship gate moves tosupamem eval --suite coderag --fullno-regression vs measured baseline. See ADR-0002. The diagnosis: LongMemEval measures conversational long-term memory while supamem indexes code chunks for AI coding agents — the gate was workload- misaligned, not the tool. - REQUIREMENTS.md edits per A-D-DOCS-01. PUB-05 rewritten to gate
on
supamem eval --suite coderag --fullno-regression vs measured baseline (Recall@k, MRR, nDCG@10 ≥ baseline − ε; latency p95 ≤ baseline + ε AND ≤ 500ms). EVAL-05 marked DEMOTED with reference to ADR-0002. Original wording preserved for traceability.
- Byte-identical regression locks preserved. Phase 14
_run_goldens_legacy(D-VEND-04) andsrc/supamem/retrieval/filters.py(D-QGEN-06 —repoandaxisare pass-through keys; ZERO new branches in the filter dispatcher) — both still byte-identical after Phase 15 edits. - New plugin entry-point group
supamem.evalmirrors the four existing groups (retrieval / embedder / chunker / reranker); third parties can register additional suites without forking.
- Scoped/unscoped bench passes.
supamem eval --suite longmemeval_snow emits BOTH an unscoped and a scoped retrieval pass per question at the singlerunner.py:428call site (_run_longmemevalper-record loop). The scoped pass derives a per-questionwherefilter from LongMemEval haystack session ids ({"session_id": [list]}), exercising Phase 7 / 9 / 11 / 14 indexer-side filter payloads end-to-end. Smoke vs full continues to be gated by the existingsmoke_idsfilter inside the same loop — no second physical call site. - Bench-only LongMemEval ingestion. New module
supamem.eval.longmemeval_ingestbuilds an isolatedsupamem_eval_longmemeval_scollection, attachespayload.session_idto each haystack chunk, and creates asession_idkeyword payload index at first ingestion (idempotent). Production indexer paths (markdown, transcript) are unchanged. Thesession_idpayload field is bench-only —supamem indexdoes NOT set it. - Bundled smoke fixture. New static fixture at
src/supamem/eval/datasets/longmemeval_scoped_smoke.json(≤5 questions, ≤200 KB, self-contained — does not trigger the ~3 GB lazy fetch). New suite namelongmemeval_scoped_smokefor the CI fast-path;suite_loaderdispatches to the bundled fixture for that suite. - ADR-0001 —
docs/adr/0001-scoped-only-bench-gate.mdrecords the methodology, the v0.1.5 corpus mismatch disclosure (D-GATE-05), and the strict isolation from FUTURE-24 (rerank composition rework) per D-FUT24-01..03. Newdocs/adr/directory established with a convention note (docs/adr/README.md).
- Result JSON shape.
scoresandby_axisnow carryunscoped+scopedsibling sub-dicts._compute_main_scorefor thelongmemeval_ssuite readsscores.scoped.tokens_per_correct_answerfor the Phase 13 gate decision. Unscoped is reported in the same envelope for transparency only — it never gates. Legacy callers (goldens etc.) continue to see the flat shape (sibling-key envelope contract pinned bytests/test_build_report.py). - Gate decision is scoped-only. The Phase 13 publication gate
(
baseline_delta.tokens_per_correct_answer ≤ -0.30) now readsscores.scoped.tokens_per_correct_answeragainst v0.1.5. Unscoped numbers ship in the same envelope but never gate. See ADR-0001.
- v0.1.5 baseline re-captured.
eval/baselines/v0.1.5.jsoncarries bothunscopedandscopedsibling keys plus a legacy mirror at top-level for migration safety. The original devdocs-collection number (1374.59) is preserved aslegacy_devdocs_unscoped_tpcabut does NOT gate; v0.1.5 was re-captured against the new haystack collection. Absolute pre-Phase-14 numbers are not directly comparable to post-Phase-14 numbers — the corpus changed. See ADR-0001 for the disclosure.
- FUTURE-24 (rerank composition rework) — Phase 14's scoped pass runs with rerank-OFF so the measured scoped-vs-unscoped delta attributes cleanly to scoping. FUTURE-24 is a SIBLING unblocker tracked separately. Public claims about scoping gains do NOT extrapolate to assume FUTURE-24 will further close the gap (D-FUT24-03).
runner.py:157(_run_goldens_legacy, v0.1.x regression infra) is byte-identical (D-VEND-04 lock). Plan B touched onlyrunner.py:428.retrieval/filters.pyis byte-identical.session_idflows through Phase 11's existing pass-through path (key-name = payload-key-name); not a magic key. Zero new branches.
- New
filtered_denseretrieval backend (FILT-01) — scoped+capped wrapper aroundtuned_hybridthat accepts awherefilter (room,path_prefix,valid_to) and caps each hit's preview at a configurable char limit. Registered via the existingsupamem.retrievalentry-point group; existing backends (tuned_hybrid,dense,bm25) are unchanged. - New
path_prefixmagic key in the MCPwhereparameter — string or list of strings; left-anchored exact path-segment match against the newpayload.path_prefixes: list[str]payload field. Indexer builds the prefix list per chunk (src/supamem/retrieval/filters.py→["src", "src/supamem", ...]) and creates aKeywordIndex(on_disk=True) at collection init, mirroring Phase 7room. valid_to: "now"accepted as a no-op alias for the always-on temporal clause from Phase 9; any other value raisesValueErrorreferencing the always-on lock (time-travel queries are out of scope).- New config:
[retrieval.filtered_dense] preview_chars = 240(default 240;0disables truncation entirely sopreviewbecomes the full document text). Independent of the MCP transport capmcp.caps.max_preview_chars, which continues to apply on top. - New
supamem doctorpanel "Filtered-dense backend" — surfaces resolvedpreview_charswith[source: ...]provenance. Read-only by construction; never flips the doctor exit code.
mcp_serverretrieval-toolqueryPydanticFieldtightened toField(..., min_length=1, max_length=max_q)at both sites (canonicaldual_memory_search_toolandqdrant_find_alias, D-NOID-01.c). The JSON Schema now requires a non-emptyquerystring at the schema layer — defense-in-depth alongside the preserved runtime.strip()check.
- Legacy chunks lack
path_prefixes. First post-upgradesupamem indexruns a one-shot eager scroll-and-set_payloadsweep that back-fillspath_prefixesper chunk — pure metadata update, zero re-embedding cost, and idempotent on subsequent runs. No--forcereindex required. Mirrors the Phase 7 D-08 classifier-hash sweep precedent.
- supamem does NOT auto-inject identity / wake-up / prelude context
into agent calls — retrieval is always solicited via an explicit
query. Locked by
tests/test_no_identity_tier.py: a CI-enforced regression test that fails if a future MCP tool name matches(?i)(wake[_-]?up|identity|prelude|inject)OR if any retrieval tool's JSON Schema dropsqueryfromrequired/ losesminLength >= 1.
supamem eval --suite longmemeval_s— lazy-fetches LongMemEval_S from a pinned HF revision and runs the supamem retrieval pipeline through a heuristic/Ollama judge, emitting an MTEB-style JSON envelope to~/.supamem/eval/<utc-iso>.json.supamem eval --suite goldens— extends the existing v0.1.x bundled regression baseline to the new envelope shape. Backward-compat:supamem eval --regresscontinues to behave exactly as v0.1.5.- New optional extra:
pip install supamem[eval]brings inragas==0.4.*,datasets, and pinshuggingface_hub>=0.24for the RAGAS triad metrics. Core install stays lean — RAGAS is fail-soft on missing extra (heuristic-only metrics +err_consoleinstall hint). - Two-tier judge: heuristic (default, offline, fastembed-backed) or
EVAL_JUDGE_MODEL=ollama:<model>/--judge ollama:<model>for localhost Ollama. SaaS endpoints (openai/anthropic/cohere/mistral) are explicitly refused per the D-07 invariant (assert_no_saas_llm_env()). - CI fast-path: 10-question axis-stratified seeded subset frozen at
tests/eval/smoke_ids.json. Full ~500 QA run gated behind--full. supamem doctorgains an "Eval bench" panel showing dataset SHA drift vs the pinned revision, cache size, last-run timestamp, RAGAS extra availability, and active baseline file. Read-only — never flips the doctor exit code.supamem eval --list-suitesfor discoverability.
- Phase 13 (Publish & Compare) is gated on
--fullvalidation reportingtokens_per_correct_answer≥30% reduction vs the v0.1.5 baseline (src/supamem/eval/baselines/v0.1.5.json, ships with_baseline_pending: true). No measured numbers are claimed in this release — this release ships the harness only. The PyPI tagv0.3.0a2is held until either the gate clears or the user explicitly chooses to ship the harness without measured claims.
First alpha of the v0.3 line. Ships per-source temporal validity —
every indexed chunk now carries payload.valid_from (= source mtime)
and payload.valid_to = null; re-indexing a CHANGED file scrolls and
set_payload(valid_to=now())s prior chunks atomically BEFORE upserting
new content-hash-keyed chunks (old + new coexist in Qdrant per TEMP-01).
A single always-on retrieval-time filter (IsEmptyCondition on
valid_to, NOT IsNullCondition — see Qdrant#5342) removes superseded
chunks from every backend uniformly.
- Per-source temporal validity (TEMP-01, TEMP-02): every indexed
chunk carries
payload.valid_from(source mtime) andpayload.valid_to = nullby default. Re-indexing a CHANGED file atomically scrolls +set_payload(valid_to=now())s the prior chunks BEFORE upserting new content-hash-keyed chunks (_chunk_idextended withcontent_hashper D-CID-01). Old + new chunks coexist in Qdrant — TEMP-01 literal compliance. Auto-GC at end ofsupamem indexdeletes superseded chunks past[retrieval.temporal] retention_days = 90(default). Setretention_days = 0for kept-forever (compliance / audit) collections. - Always-on retrieval temporal filter (TEMP-02):
retrieval/filters.py:build_qdrant_filteralways emits avalid_to IS missing/null OR valid_to > now()clause. Single construction site (Phase 7 D-03) — all backends (tuned_hybridboth Prefetch arms,dense,bm25,qdrant_find,dual_memory_search) inherit it. UsesIsEmptyCondition(NOTIsNullCondition— Qdrant#5342:IsNulldoes not match missing fields). - Transcript-only opt-in recency decay (TEMP-03):
[retrieval.recency.per_source.transcript]table withenabled = falsedefault,half_life_days = 14.0,alpha = 0.7. When enabled, transcripts get a post-rerank multiplicative-floor decayscore *= alpha + (1 - alpha) * 0.5 ** (age_days / half_life_days). Code / ADR / doc / null-room rankings remain byte-identical when the knob is flipped — orthogonal pass after rerank-or-RRF, before T-5 dedup. - Doctor Temporal-validity panel:
supamem doctorbetween Reranker and Subagent reachability panels — live / superseded / awaiting_gc / future_dated counts, per-source breakdown, oldest + newestvalid_from,retention_daysprovenance, validity-migration status. Read-only — never flips exit code. - Eager validity migration (D-NULL-03): first post-upgrade
supamem indexback-fillsvalid_to=nullon legacy points (gated by manifest__validity_migration__reserved key, idempotent on subsequent runs). Defense-in-depth alongside the IsEmpty runtime filter. - Payload indexes: idempotent
create_payload_indexonvalid_to(DATETIME) andchunker(KEYWORD) atrun_indexboot — sub-ms range queries on large collections (D-INDEX-01, D-INDEX-02).
_chunk_idsignature now takescontent_hash— unchanged content is idempotent under re-index; changed content gets a fresh uuid so old + new coexist in Qdrant.- Default retention is destructive for users upgrading from v0.2.x
with audit-mode collections older than 90 days. Set
[retrieval.temporal] retention_days = 0to disable auto-GC entirely (kept-forever escape hatch).
Per the v0.2.1 milestone gate, the "−30% tokens-per-correct-answer" claim is BLOCKED until Phase 10 (LongMemEval_S + RAGAS bench) validates the number. Phase 9 ships the temporal-validity infrastructure feeding into that measurement; no public benchmark claim accompanies this release.
- Decay-shape rationale: Customers.ai recency-weighted scoring + Snowflake Cortex Search docs (multiplicative-floor decay for uncalibrated cross-encoder scores).
- Qdrant API behavior: filtering docs (IsEmpty vs IsNull semantics, DatetimeRange RFC 3339), payload index (DATETIME schema), point delete (PointIdsList scroll-then-batch).
First alpha of the v0.2.5 line. Ships the subagent reachability
auto-patcher (Phase 8.1) — closes the silent dogfooding gap where
subagents shipping with restrictive tools: whitelists (GSD's
gsd-executor, superpowers/*, hookify, etc.) could not reach the
supamem MCP server, so dual-memory lookups inside subagent sessions
silently returned empty.
- Subagent reachability auto-patcher:
supamem installandsupamem repairnow scan~/.claude/agents/AND<project>/.claude/agents/and idempotently appendmcp__supamem__*to any restrictivetools:whitelist that doesn't already cover supamem. Files with a missing or emptytools:line inherit all parent tools (Claude Code semantics) and are left untouched. Symlinked agent files are skipped with a warning to avoid polluting upstream repos. (REACH-01..03) supamem unpatch-agentssubcommand restores patched agent files cleanly. Run BEFOREpip uninstall supamemfor a clean uninstall. Skips files the user has edited since the patch (frontmatter SHA match) and emits a per-file warning naming them. (REACH-05)--skip-patch-agentsflag oninstall/init/repairfor users who manage agent whitelists by hand. (REACH-07)supamem doctorSubagent reachabilitypanel: per-agent listing (patched / OK already-covered / OK full-inheritance / skipped / needs-patching), grouped by[global]and[project]scope. Renders the manifest path + anunpatch-agentsreminder when a manifest exists, or asupamem repairhint when patchable agents are detected without a manifest. Read-only by construction; never flips the doctor exit code. (REACH-08)- Backup manifest at
platformdirs.user_cache_dir("supamem")/agent_patches.json— single rolling JSON, FileLock-protected, atomic temp+rename writes. Per-entry: file path (relative to scope root), original frontmatter SHA-256 (newline-normalized), patched frontmatter SHA, originaltools:value (verbatim), timestamp, supamem version. (REACH-06)
src/supamem/share/rules/dual-memory.mdrewritten to reference the real MCP tool names (mcp__supamem__qdrant_find,mcp__supamem__dual_memory_search) instead of the non-existentqdrant-findshell command the rule used to advertise (D-LOCK-07). Adds aSubagent reachabilitysection explaining the auto-patcher, the--skip-patch-agentsopt-out, thesupamem unpatch-agentsreverse path, and the two-step uninstall contract.
- Added:
ruamel.yaml>=0.18,<0.20(~112 kB pure-Python wheel on Py3.12+) for round-trip-preserving YAML mutations on agent frontmatter — preserves user comments, indentation, and CSV vs list-style formatting. (REACH-04)
There is no portable pip uninstall hook in pip / uv / pipx
(verified 2026-05-02), so reversibility is a documented two-step
contract:
supamem unpatch-agents # restore agent whitelists first
pip uninstall supamem # then remove the packagesupamem doctor displays the manifest path and the reminder so users
discover this flow naturally without consulting docs.
First alpha of the v0.2.4 line. Ships the code-aware reranker
(Phase 8 of the v0.2.0 milestone train) — every tuned_hybrid query
now rescores RRF-fused candidates through a cross-encoder by default.
- Code-aware cross-encoder reranker:
mxbai-rerank-base-v2(Apache-2.0) plugged intotuned_hybridretrieval as the new default (retrieval.reranker = "mxbai_v2"). Settingretrieval.reranker = "off"restores pre-Phase-8 byte-identical behavior. (Phase 8, RERANK-01..04) - New
supamem.rerankerplugin entry-point group — third parties register custom rerankers without forking. Registered default:mxbai_v2 = supamem.rerankers.mxbai_v2:MxbaiV2Reranker. (RERANK-03) supamem installandsupamem initproactively download all ML prerequisites (MiniLM ~90 MB + BM25 ~10 MB + mxbai-rerank-base-v2 ~1 GB) withrich.progress. Cold post-install CLI invocations (supamem --help,supamem doctor,supamem --version) trigger zero network egress. (RERANK-02)supamem install --skip-modelsopt-out flag for air-gapped first-run; backfill viasupamem repair. (D-FETCH-07)supamem repairextended to doctor-driven self-heal: re-fetches missing/partial reranker model, re-syncsshare/, repairs managed CLAUDE.md/AGENTS.md blocks, restores client config. Idempotent. (D-FETCH-03)supamem doctorReranker panel: name, model_id, cache path, on-disk size + partial-download detection, last-load latency, last-100-query rerank p50/p95, detected device (cuda/mps/cpu). (RERANK-04, D-DOCTOR-01)- New env vars:
SUPAMEM_CACHE_DIR(override platformdirs cache root),HF_HUB_OFFLINE=1/TRANSFORMERS_OFFLINE=1(respected byprepare()),SUPAMEM_INTEGRATION_RERANKER=1(opt-in integration test gate).
RetrievedChunkgains optionalrerank_score: float | Nonefield for telemetry; primaryscorecarries the rerank score when reranker is on. (D-CONTRACT-05)- When reranker is on: PREFETCH_LIMIT widens to 50 per arm; T-4 recency multiplier is skipped; T-5 dedup + T-8 token budget run AFTER rerank. (D-COMPOSE-01..03, D-POOL-01..04)
- Added:
mxbai-rerank>=0.1.6,<0.2,huggingface_hub>=0.24,filelock>=3.13. Pullstransformers>=4.49,torch>=2.0,accelerate>=1.5transitively.
First alpha of the v0.2.3 line. Ships the coding-path classifier
(Phase 7 of the v0.2.0 milestone train) — every indexed chunk now
carries a payload.room facet that dual_memory_search and
qdrant_find can filter on via the new where parameter.
- Coding-path classifier: every indexed chunk gains
payload.roomvia exact path-component equality (set(Path.parts) ∩ set(keywords)), never substring matching.data/chest_xray/img.pngis NEVER classified astests. Defaults cover backend, frontend, tests, docs, scripts, config, migrations, types in priority order (CLASS-01, CLASS-02). [supamem.classifier.rooms]TOML config table — override the default keyword map per-project; priority is encoded by config order (first-match-wins, D-01a). User TOML REPLACES the defaults dict (leaf-replace, mirrorstranscript_*precedent).whereparameter ondual_memory_searchandqdrant_findMCP tools (D-17 alias parity):where={"room": "backend"}filters retrieval to that scope;where={"room": ["backend", "tests"]}is OR-within-key (QdrantMatchAny); multiple keys are AND. Single QdrantFilterbuilt once at the retrieval boundary and threaded to BOTH dense and sparse Prefetch arms PLUS the top-levelquery_filter(defense-in-depth, D-03). v1 documentsroomas the only key; unknown keys pass through to Qdrant for forward-compat with Phase 9/11 (CLASS-03).payload.roomis ALWAYS present (string or JSONnull) on every point — uniform schema (D-06). Transcript chunks classify toroom = nullby construction (filter via existingpayload.chunker).supamem doctorsurfaces the active classifier rooms map with[source: ...]provenance, the storedclassifier_hash, and a per-room histogram (including anullbucket for unmatched chunks).- Hash-drift sweep:
manifest.classifier_hash = sha256(json.dumps(rooms, sort_keys=False))captures both content AND priority order. On everysupamem indexrun, if the stored hash differs from the current config hash, supamem scrolls the collection in batches andclient.set_payload({"room": new_room}, points=[ids], wait=True)per-room — pure metadata update, zero re-embedding cost (D-08).
- Manifest gains
__classifier_hash__reserved top-level key (additive; emitted only when not None, byte-stable round-trip when unset — mirrors__transcripts__precedent from Phase 6). tuned_hybridretrieval threads thewhere-derivedqmodels.Filterto BOTH dense and sparse Prefetch arms via a single construction site (src/supamem/retrieval/filters.py) — anti-drift, no duplicated filter logic across arms.
- Pre-0.2.3 collections auto-migrate on first post-upgrade
supamem indexinvocation: missing__classifier_hash__is treated as drift from null, triggering a one-time sweep that stamps every existing chunk with aroomvalue. - No new dependencies.
qdrant-client,mcp,pydanticversions unchanged.
First alpha of the v0.2.2 line. Ships the transcript chunker plugin
(Phase 6 of the v0.2.0 milestone train) — supamem can now index Claude
Code session JSONL as Q+A drawer chunks alongside the existing Markdown
corpus. Default-OFF: opt in with --transcripts.
supamem index --transcripts(bare flag) ingests Claude Code session JSONL from~/.claude/projects/(or[supamem.transcript] default_root) as Q+A drawer chunks via the newsupamem.chunker = transcriptentry-point. Pass an explicit path withsupamem index --transcripts /path/to/sessions/. Mixed corpora dispatch per-suffix:*.md→markdown_header,*.jsonl→transcript(INGEST-01..INGEST-05).--transcripts-onlyskips the default project corpus and indexes only transcripts in the same run.--since 30d(orNh) filters transcript JSONL by mtime;--since 0disables. Defaults to[supamem.transcript] since_days = 180.[supamem.transcript]config table with six keys:default_root,since_days(180),tool_payload_max_chars(2000),chunk_soft_max_tokens(600),include_paths_glob,exclude_paths_glob. All surfaced bysupamem doctorwith[source: default|user|project]provenance.- Per-message-uuid dedupe in
manifest.pyunder the__transcripts__key — re-running on an unchanged corpus reports0 new, 0 changed. Editing one message purges-then-reinserts only that message's chunk (append-only on the rest). - Tool-use payloads above 2000 chars are elided to a synthesis stub;
tool_usesmetadata always lists{id, tool_name, status}regardless. Status correlates withtool_result.is_errorfrom the next pair when observable. rich.progressindexing bar shows session/chunk/elapsed counts (auto-disabled underNO_COLOR=1or non-tty).supamem doctorgains a Transcript config section between MCP caps and Installed clients, surfacing the six[supamem.transcript]keys with config-source attribution.
- ⚠ Transcripts may contain secrets (API keys, tokens from
copy-pasted env files, credentials in tool payloads). v0.2.2a1 ships
no redaction — review your
~/.cache/supamemQdrant collection before sharing it. Hand-exclude sensitive sessions via[supamem.transcript] exclude_paths_glob. Redaction is tracked for v0.3 via a futuresupamem.redactorplugin group.
- Cursor SQLite + ChatGPT export ingestion → follow-on plugins (third-party or supamem-shipped after v1 stabilizes).
Patch fixing two v0.2.0 banner gaps that turned out to matter:
- User-visible SessionStart banner — the v0.2.0 banner reached the
model via
additionalContextbut was invisible to the user. Adds asystemMessagefield to the SessionStart hook payload, which Claude Code renders as theSessionStart:startup says: <line>row in the terminal (officially documented dual-channel pattern). Cursoruser_messageis included for forward-compat (per Cursor docs the field is "accepted but not enforced" today; will surface once Cursor ships UI for it). Suppress only the user-visible row withSUPAMEM_BANNER_QUIET=1(keeps context injection alive for the model).SUPAMEM_BANNER_DISABLE=1still kills both channels. supamem doctorinstall-drift surfaced in the banner — the health flag now flips to⚠when any installed client's managed- block version differs from the running CLI (i.e. you upgraded supamem but a client's CLAUDE.md/.cursor rules still reference the old version). Prompts runningsupamem repairto resync. The drift probe is cheap (small text reads, never raises); banner failures fall back to✓rather than blocking session-start.
First milestone of the v0.2.0 token-economy line. Ships server-side hard
caps on every MCP retrieval response (Phase 5), the multi-project install
fix and supamem repair migration verb, agent-discipline hooks
(claude-code edit-gate + Cursor advisory), and the SessionStart banner
enrichment. See the Behavior change notes below — review before upgrading.
- New
[supamem.mcp.caps]TOML config table with three keys:max_top_k(default: 25) — silently clamps requestedtop_kon every retrieval call; the response carriesSearchResult.clamped_toso callers can detect it.max_query_chars(default: 250) — enforced via PydanticField(max_length=...)baked into the MCP tool schema at registration time; over-cap queries fail at the schema boundary as a structured MCP validation error (no silent truncation, no stdout pollution).max_preview_chars(default: 200) — display preview cap applied toChunk.previewon each hit. The full canonical payload inChunk.textis never truncated.
- New
Chunk.preview: strfield on MCP search responses — display-only excerpt ofChunk.text, capped atmax_preview_chars. ExistingChunk.textconsumers see byte-identical full payloads (backward-compat). - New top-level
SearchResult.clamped_to: Optional[int]field — set to the effective cap when the server clamped requestedtop_k;Noneotherwise. summary_mdrendering now includes a⚠️warning line on clamp events (D-14):⚠️ Clamped \top_k`: {requested} → {N} (raise mcp.caps.max_top_k)`.supamem doctorsurfaces all three cap values in a dedicated MCP caps section with config-source attribution ([source: default|user|project]).qdrant_findalias inherits identical caps and response shape via shared closure-captured locals — alias drift is impossible by construction (D-17).
- Query-length enforcement is now config-driven at the MCP schema
boundary. The previous internal
MAX_QUERY_LEN = 4096constant insrc/supamem/mcp_server.pyhas been removed; the cap lives atcfg.mcp_caps_max_query_charsand is baked into the tool's JSON Schema at registration time so MCP clients (Cursor, Claude Code) see the limit at tool-discovery time.
The default max_query_chars is 250, dramatically lower than the previous
internal MAX_QUERY_LEN = 4096. Agents (or callers) submitting queries longer
than 250 characters now receive a structured MCP validation error instead of
the request silently working. If your workflow legitimately needs longer
queries — long natural-language prompts, embedded code excerpts, paragraph
seeds — raise the cap explicitly in your project config:
# .supamem/config.toml
[supamem.mcp.caps]
max_query_chars = 4096 # restore v0.1.x behaviorThe new default is calibrated for token economy on small focused queries,
which is the intended retrieval-key shape. Long contexts belong in the
ingestion path (write to dual_memory_write), not in retrieval queries.
- This is one phase of the v0.2.0 milestone;
pyproject.tomlis not bumped here. The version bump lands at the milestone Definition-of-Done point. - README.md and the four translations (
README.{zh-CN,es,ja,ru}.md) are intentionally untouched in this entry per PUB-05: README updates are gated on Phase 13 bench validation so the user-facing narrative ships with measured numbers, not pre-bench claims.
Closes the silent wrong-collection bug on multi-project machines and lands
the --enforce-search opt-in gate that turns the project's "search BEFORE
choosing an approach" rule from advisory into mechanical.
- Per-workspace install is now the default.
supamem installwrites to<repo>/.mcp.json(Claude Code project scope, per Anthropic docs) and<repo>/.cursor/mcp.json(Cursor per-workspace path). Each per-workspace file carries an explicitSUPAMEM_PROJECT_ROOTenv pointing to the install-time cwd. Pass--scope userto keep the legacy global write to~/.claude.json/~/.cursor/mcp.json. - Defense-in-depth project-root resolution in
cmd_mcp_server: honorSUPAMEM_PROJECT_ROOTfirst, then walk parents fromPath.cwd()for.supamem/config.tomlorpyproject.toml [tool.supamem](stops at$HOME/ filesystem root). Both miss + collection still default → one-line stderr warning naming cwd, env-var presence (never values), and the fix command. Stdout stays JSON-RPC clean. supamem repairverb — migrates a user from legacy global install to per-workspace files in one command. Strips stale supamem entries from globals, re-installs at project scope. Auto-detects clients; idempotent on a healthy install. Forwards--enforce-search.- Claude Code edit-gate hook (
--enforce-searchon install). Registers a PreToolUseEdit|Write|MultiEditmatcher that DENIES the tool call when nomcp__supamem__dual_memory_search(orqdrant_findalias) is found in the session transcript since the last user turn (strategy A — strict per-turn). Emits Anthropic'spermissionDecision: denyJSON contract on stdout; reverse-scans the transcript with a 256 KB byte cap. Override per-session withSUPAMEM_GATE_DISABLE=1. - Cursor
beforeSubmitPromptadvisory hook — Cursor 1.7's hooks API has no fail-closed pre-edit event, so this is advisory-only: when the user's prompt looks edit-bound (regex over fix/refactor/rename/...), inject anagentMessagereminding the agent to calldual_memory_searchfirst. Override withSUPAMEM_ADVISORY_DISABLE=1. - SessionStart banner enrichment — banner now leads with a 1-char
health flag (
✓healthy /⚠qdrant unreachable or default collection still in effect) and appendsupdate v0.X.Y availablewhen the existingupdate_checkdaemon has cached a newer release. No auto-heal — surfacing only.
supamem install --client claude-codeno longer writes to~/.claude.jsonmcpServers.supamemby default. The new default is<repo>/.mcp.json(project scope). Behavioral migration path:supamem repair(recommended) or pass--scope userto keep legacy.supamem install --client cursorno longer writes the MCP entry to~/.cursor/mcp.jsonby default. New default:<repo>/.cursor/mcp.json.supamem uninstall --client {claude-code,cursor}is now defensive: strips supamem from BOTH project and user scopes regardless of which scope the user originally installed with.
If you previously ran supamem install from inside a workspace, you
likely have a mcpServers.supamem entry in ~/.claude.json and
possibly ~/.cursor/mcp.json. After upgrading, the per-workspace
files take precedence in their respective workspaces, but the stale
global entry will be used by hosts opened in a directory that has no
per-workspace file — and that entry has no SUPAMEM_PROJECT_ROOT, so
it'll silently fall through to the default collection
(dev_memory_tuned_hybrid).
Recommended: run supamem repair from each of your supamem-enabled
workspaces. It strips the stale globals and re-installs per-workspace.
supamem install --client claude-code now wires the SessionStart banner
hook automatically — closes the v0.1.4 gap where the new supamem hook session-start command shipped but no installer registered it.
supamem.install.claude_codeadds aSessionStarthook entry to~/.claude/settings.jsonthat runssupamem hook session-starton every Claude Code session open. Idempotent — reinstalling on top of a v0.1.4 user-home that already has the entry is a no-op.
- Cursor SessionStart already wires
supamem index --snapshot cursorvia.cursor/hooks.json— the v0.1.4 banner is not appended there because Cursor'ssessionStarthook fires shell commands (not MCP-context injection); thesupamem livedashboard remains the visibility surface for Cursor users. - OpenCode SessionStart hook contract is still upstream-pending, so the OpenCode installer leaves SessionStart wiring as a no-op until the feature lands. Tracked at github.com/anomalyco/opencode#5409.
Visibility round: gives users observable evidence that supamem is alive and working. Three additions; no behavior change to retrieval.
- SessionStart banner (
supamem hook session-start): one-line plain-text status injected at session start in Claude Code / Cursor / OpenCode viaadditionalContext. Format:🧠 supamem v0.1.4 · <collection> · <N> chunks · audit <path>Cross-client portability via dual JSON keys (hookSpecificOutput.additionalContext- snake-case
additional_context). Auto-detects calling client from env vars (CLAUDECODE,OPENCODE,CURSOR_AGENT) when--clientis omitted. Fail-soft per hook discipline — never raises, never blocks session start.
- snake-case
supamem liveCLI: Rich-Live terminal dashboard tailing the audit JSONL in real time. Run in a side terminal alongside Claude Code / Cursor / OpenCode for instant visibility into the silent PreToolUse-hook injections (which save tokens by NOT showing UI). Useswatchfiles.awatchfor OS-native file change notifications; falls back to polling ifwatchfilesisn't available. Pipe-safe: prints plain JSONL when stdout isn't a TTY. Handles file rotation, terminal resize, and Ctrl-C cleanly.
watchfiles>=0.24— Rust-backed async file watcher forsupamem live. Has manylinux + macOS arm64 wheels, no source build required at install.
- A per-injection footer in PreToolUse
additionalContextwas considered but dropped: chat hosts don't render Markdown<details>, so the "collapsible" pattern is fiction. A footer would just add ~80 tokens to every Edit (+20%) for no UI benefit. The banner + dashboard provide visibility without the per-Edit token tax.
Adds the missing write path so supamem can serve as the only memory
layer per project (no need to keep upstream mcp-server-qdrant alongside
for the qdrant-store workflow).
- New
dual_memory_writeMCP tool: agents persist insights/research findings mid-session. Writes a deterministic Markdown file with YAML frontmatter to<project>/.claude/insights/_agent/<slug>.mdand immediately upserts it into the project's tuned-hybrid Qdrant collection withwait=Trueso the very nextdual_memory_searchsees it. - Idempotent on
topic: same topic → sameslugify()slug → same on-disk path → sameUUIDv5(NAMESPACE_AGENT_WRITE, slug)Qdrant point id. Re-saving overwrites in place. - Backward-compat aliases for upstream
mcp-server-qdrantusers:qdrant_find(alias ofdual_memory_search) andqdrant_store(alias ofdual_memory_write). Default-on; disable withSUPAMEM_QDRANT_ALIASES=0. Lets existing prose / agent instructions ("save with qdrant-store", "query qdrant-find") keep working without rewrites. - New
supamem.memory_writerpublic module:write_memory()for non-MCP callers (CLI plugins, scripts).
- Partial-failure semantics: if Qdrant indexing fails after the on-disk
write succeeded, return
indexed: falsewith the error message instead of leaving the file unwritten — the file is still valuable and the nextsupamem index --target tunedrun will pick it up.
- Path-traversal hardened: target path resolution refuses anything that
doesn't
is_relative_to(project_root). - Size limits enforced:
topic <= 120,content <= 64K,description <= 300,tags <= 10items × 32 chars each.
PyYAML>=6.0promoted from transitive to direct dependency (memory_writerwrites YAML frontmatter; explicit dep avoids surprise removal if a transitive provider drops it).
Project-tunable regress baselines and config-resolved goldens path. Unblocks brownfield projects (e.g. SoftChat, Plan 80.6-14) where the bundled Phase 80.1 thresholds — calibrated against the supamem-internal corpus — don't fit the project's corpus size.
[supamem.eval]config block acceptsbaseline_recall_at_5,baseline_total_tokens,baseline_p95_latency_msto override the bundled D-19 defaults per project.- Env-var overrides (highest precedence):
SUPAMEM_BASELINE_RECALL_AT_5,SUPAMEM_BASELINE_TOTAL_TOKENS,SUPAMEM_BASELINE_P95_LATENCY_MS. cfg.goldens_pathnow used as fallback when--goldensflag is omitted — previously the config field existed but was ignored by the eval runner.
supamem eval --regressno longer fails projects with healthy retrieval but corpus sizes outside Phase 80.1's calibration window. Default behavior is unchanged for callers that don't set overrides.
First PyPI release. Hardens v0.1.0 with CI fixes, agent guides, an update-check notifier, and a published wheel/sdist on PyPI so downstream consumers can pin a version range instead of a git tag.
supamem.update_check— pip-style fire-and-forget GitHub Releases probe. Daemon thread writesplatformdirs.user_cache_dir("supamem")/update_check.json; the next invocation prints a stderr footer if a newer release is cached. 24h TTL, 6h backoff on 403/429, ETag-aware. Suppress withSUPAMEM_NO_UPDATE_CHECK=1,NO_UPDATE_NOTIFIER=1, orCI=1. Skipped when stderr is non-TTY. Surfaced insupamem doctor(new "Update check" section).AGENTS.md+CLAUDE.md— agent-facing project guides per the Apr 2026 cross-tool convention.
- License metadata migrated to PEP 639 SPDX expression (
license = "MIT"), removing legacy{ text = "MIT" }form. - Distribution channel: PyPI (was git-tag-only). Install via
pip install supamemoruv tool install supamem.
tests/test_cli_smoke.pysubprocess env now pinsNO_COLOR=1,TERM=dumb,COLUMNS=200, and popsFORCE_COLOR— eliminates Rich color escapes that broke CI assertions on GitHub Actions runners.
The initial public release. Extracted from the SoftChat dual-memory stack (Phases 80.1–80.5) and shipped as a project-agnostic Python package under MIT.
supamem init— greenfield bootstrap (probes Qdrant, creates per-project hybrid collection, writes.supamem/config.toml).supamem install --client {claude-code|cursor|opencode}— patches the client config atomically; idempotent;--dry-runsupported.supamem uninstall --client X— reverses install cleanly.supamem index [--target tuned] [--force] [--snapshot cursor]— embeds Markdown sources into Qdrant or refreshes the Cursor.mdcsnapshot.supamem mcp-server [--transport stdio|http] [--port N] [--host H]— runs the MCP server. Streamable HTTP per Nov 2025 MCP spec (D-45).supamem hook {claude-code|opencode} [--file-path X]— per-client PreToolUse hook delegate. Fail-soft: never blocks the calling tool.supamem doctor [--show-secrets]— health probe + resolved config chain- version-drift advisory across detected clients.
supamem stats [--show today|week|all] [--format table|json]— Welford schema-v2 usage counters from~/.cache/supamem/audit.jsonl.supamem migrate --source X --target Y --path {coexist|migrate|adopt-as-is}— brownfield migration with snapshot-before-destructive guard.supamem eval [--regress] [--goldens path]— recall@5 + p95 latency + total tokens against the bundled 33-query corpus. Phase 80.1 baselines:mean_recall_at_5 >= 0.60,total_tokens <= 4000,p95_latency_ms <= 500.
~/.supamem/share/{rules,skills,commands,hooks,cursor-rules}/— canonical artifacts referenced by every client config (SC-3 reference-not-copy contract; Cursor.mdcis the documented copy exception).- 33-query golden corpus at
supamem.eval.goldens.phase_80_1_tuned_hybridfor--regress(per D-46). - Plugin entry-point groups for retrieval, embedder, and chunker so third-party packages can extend supamem without forking (per D-48).
- Hybrid retrieval: BM25 (
Qdrant/bm25) + dense (sentence-transformers/ all-MiniLM-L6-v2) with Qdrant nativeFusionQuery(RRF), prefetch=20. - T-1 markdown header chunker, soft-max 250 tokens, fallback 200/20.
- T-4 recency boost, T-5 cosine dedup (threshold 0.97), T-8 token-budget truncation (1500).
uv tool install git+https://github.com/dzmitrys-dev/supamem@v0.1.0PyPI publish is deferred to v0.2 per D-44. v0.1.0 is git-tag-only so SoftChat's rip-out commit (Plan 80.6-14) can pin a verifiable artifact before the broader package distribution surface lands.
- Forward-compat: tool
titlefield set on every MCP tool registration; Cursor / Claude.ai web honor it today; Claude Code TUI ignores but will pick up automatically when the upstream lands. - Stylish
rich-powered CLI output (banner, panels, spinners, status tables) on every long-running command. - Made with care by SoftChat and SoftSkillz.