• v6.7.1 8d647cff2c

    v6.7.1
    Some checks failed
    Security Scans / Bandit SAST (pull_request) Successful in 22s
    Security Scans / Dependency Audit (mcp_server) (pull_request) Successful in 1m5s
    Security Scans / Dependency Audit (rss_worker) (pull_request) Successful in 28s
    Security Scans / Test Coverage (pull_request) Failing after 1m32s
    Security Scans / Secret Scan (pull_request) Successful in 5s
    Security Scans / Dependency Audit (web_ui) (pull_request) Successful in 26s
    Docker Build & Push / Build mcp_server (push) Successful in 15m20s
    Docker Build & Push / Build web_ui (push) Successful in 5m58s
    Docker Build & Push / Build rss_worker (push) Successful in 6m48s
    Stable

    ric released this 2026-09-15 07:59:27 +00:00 | 2 commits to v6.7.x since this release

    The 6.6 and 6.7 lines in one release: projects that say what kind of work they hold, memories that say where they came from and whether they can be shared, an embedding backend that's three times faster with no PyTorch in sight, and a recall that stops padding its answers with noise. This release rolls up 6.6.0 through 6.7.1.

    A third of the latency, a third of the image (6.7.0)

    Embeddings now run on ONNX Runtime instead of sentence-transformers on PyTorch. Same model (all-MiniLM-L6-v2), same vectors: cosine 1.0000 on every sampled text and an identical top-10 on every benchmark query, so nothing you've stored needs re-embedding and no threshold moves.

    Measured on arm64 with a 400-memory corpus and 44 queries:

    6.6.2 (PyTorch) 6.7.0 (ONNX)
    Model load 7.8 s 0.8 s
    Peak memory 924 MB 301 MB
    embed() p50 22.3 ms 7.9 ms
    remember() p50 25 ms 11 ms
    recall() p50 35 ms 23 ms
    MCP server image 2.04 GB 634 MB

    PyTorch isn't installed in any image any more. If you need the old path, EMBEDDING_BACKEND=torch plus requirements-torch.txt brings it back. The default model is pinned to a known commit so an upstream re-export can't shift vectors under a live store, EMBEDDING_MODEL accepts a local directory for air-gapped hosts, and the benchmark behind these numbers ships as scripts/embedding_bench.py so you can check any other model or quantised graph the same way.

    Recall stops padding (6.7.1)

    recall() used to fill top_k with whatever was left, so two good matches came back with three unrelated memories attached, all headed straight into an agent's context. There's now a relevance floor (RECALL_MIN_SCORE, 0.15), and top_k is a ceiling: fewer results, or none, is a normal answer.

    The floor is deliberately low. On the real embedder, correct and incorrect matches overlap between roughly 0.19 and 0.24, and the 0.4 cut first proposed dropped two of eight correct answers. So rather than hide that band, results between the floor and RECALL_WEAK_SCORE (0.35) come back flagged weak_match: true, and the agent instructions say to read those but not build on them. The floor gates raw similarity, not the adjusted score, so extracted facts and abandoned experiences that are scored down on purpose still come back when they genuinely match.

    find_skills() gets its own floor (SKILL_MIN_SCORE, 0.25) and a high/low confidence on each result. Before, find_skills("python") on a store with no Python skill cheerfully returned a preferences skill, and a skill loads whole.

    Also fixed in 6.7.1:

    • Phantom index entries (#28): an index row whose record had gone could reach recall as an empty result taking a top_k slot. It's skipped now, and index drift is logged at startup and surfaced in briefing() instead of only in health(), which is how 762 orphans built up unnoticed
    • reindex() covers all five namespaces (#29): preference and skill were missing from the docs, and memory_audit() silently skipped preferences on an unscoped run
    • Full held_back rule text (#32): the list you choose what to bless() from was cut at 80 characters, mid-word
    • The stale list stopped nagging about your best memories (#34): memories compiled into a skill stop getting touched because the skill carries them, so they were being flagged as stale. They're exempt now
    • compile_skill() explains an empty compile (#33): when every candidate in a domain comes from one project, it says so and points at bless() or a narrower domain rather than suggesting you lower the gate

    Where a memory came from, and whether you can share it (6.6.1, 6.6.2)

    Every memory now carries two new fields.

    licence records redistribution rights: own, open, restricted or unknown. It's decided at write time. The RSS worker stamps each article from its feed's new licence: declaration in feeds.yml (ogl-3.0, cc-by-4.0 and friends resolve to a class and keep the identifier as a note), and RSS_REQUIRE_LICENCE=true refuses undeclared feeds outright. When recall returns something still unknown, a licence_notice asks you to classify it while it's in front of you, and set_licence(feed_name="...", licence="...") does a whole feed in one call.

    provenance records who's speaking: asserted (you said it), concluded (the system's own reasoning or write-up) or retrieved (an external source). Without it, an inference from last month gets recalled and read as independent evidence for the reasoning that produced it. set_provenance(..., "asserted") is how you vouch for a memory.

    Both are indexed, returned on every read surface, editable in the web UI, and inherited by extracted facts. Neither changes ranking: landing a field and changing what recall does with it are separate releases, so any future benchmark move can be attributed. Licence is about redistribution rights, not who can see a memory; that's a different axis and a later release.

    Projects declare their kind of work (6.6.0)

    A project context can now list work-type domains (python, docker, wcag-accessibility), and recall(query, domain_filter="python") searches every Python project at once. That reaches the lessons that never cleared a skill's reinforcement gate, which used to need an unfiltered search of everything. Domains use the same vocabulary as compiled skills, so the two can't drift apart. compile_project_domains() suggests them from your stack and recurring tags, with the evidence for each, and the projects page filters on them.

    Fixed: index migration never worked (6.6.0)

    _migrate_indexes() has failed silently on every upgrade since it was written. valkey-py's dropindex() sends a trailing empty argument that valkey-search rejects, and the error was swallowed by the handler for "index doesn't exist". Any field added to an index in a past release, the knowledge namespace's project tag among them, was only searchable on fresh installs. It now issues the raw command, logs real failures, and the test fake raises the same error the server does so this can't pass the suite again.

    Upgrading

    • The first start rebuilds indexes. Between the migration fix and the two new fields, most instances will drop and recreate their indexes on first boot. It's data-safe: only the index goes, and valkey-search re-indexes the existing records itself. Recall can be incomplete for a short while as that happens, and the startup drift check expects this and stays quiet
    • Backfills run once. Episodic, project and preference memories become licence=own; RSS articles and untraceable knowledge become unknown. Provenance lands as retrieved for articles, asserted for preferences and project context, and concluded for everything else, every episodic memory included. That last one is deliberately conservative, not a judgement on the work
    • First start downloads an 86 MB ONNX file into a new shared hf_cache volume. Air-gapped hosts need to fetch it online first or point EMBEDDING_MODEL at a local directory: a cache filled by the old torch backend doesn't contain it
    • Building from source? The RSS worker image now builds from the repo root (-f rss_worker/Dockerfile), like the web UI. The bundled compose files already do this; a hand-rolled one needs the same change and the hf_cache volume
    • Expect shorter recall results. That's the floor doing its job. RECALL_MIN_SCORE=0 and SKILL_MIN_SCORE=0 restore the old behaviour if you need it

    The suite is up to 1913 tests, with memory/ and tools/ at 100% line coverage, and the index, backfill and tag-filter paths verified against a real valkey-search rather than only the fakes.

    Self-hosted, open source, runs on Docker Compose. Python 3.12, FastMCP, Valkey.

    Full detail in CHANGELOG.md.

    Downloads