The hardening release. Three days after 0.10.0, fifty merges: a systematic audit of the association read/write layer, the cluster subsystem taken from "replicates" to "converges", a cross-tenant isolation fix, and the read path made provably write-free. Nearly every fix landed with a test proven to fail without it.
⚠️ Upgrade notes
- Migrations v5 and v6 run automatically on first start. A pre-migration binary refuses to start against a migrated data directory — this release's
muninn upgradenow keeps the outgoing binary asmuninn.<version>.bakbeside the live one, so a rollback is a file rename away. - Entity
mention_countvalues will go DOWN. Migration v6 recomputes them from each vault's own links, replacing an upsert counter that re-enrichment and replay had been inflating. - Cluster operators: migration v5 relocates the replication keyspace and drops historical log entries — Lobes re-sync via snapshot. And a write sent to a Lobe is now refused (REST
421naming the Cortex, gRPCFAILED_PRECONDITION, MCP-32002, MBP error4015) instead of accepted and silently lost. Repoint any load balancer that lacks leader affinity. Single-node deployments only need the rollback note.
Security
- Entity records were global across vaults (#683): a cross-tenant read oracle over another vault's entity vocabulary AND a cross-tenant write (
SetEntityStatetook no vault at all). Records are now vault-keyed; migration v6 relocates existing ones. - CLI cluster commands now authenticate (#632); enrichment and embed errors no longer carry memory content into logs (#641, #790);
muninn upgraderenders release notes inertly on the terminal (#857);muninn_stateis correctly classified as mutating, so observe-mode credentials can no longer transition lifecycle state (#731).
Highlights
- UPSERT write mode (#556): a caller-supplied
op_idpins to a head engram — key miss creates, identical content no-ops, changed content evolves with declared supersession, atomically. muninn_update_tags(#720): in-place retagging that preserves the ULID, lineage, and access history.- The association layer, audited end to end: a symmetric association now boosts recall from both endpoints (#800 — measured, the same link scored 0.7150 from one side and 0.3862 from the other); failed reads are never written back as fabricated defaults (#809); cache-returned engrams are read-only and writers clone (#858); laundered reads, unevicted caches, and an aliased slice closed (#804/#808/#818/#820).
- A
read_onlyrecall is now provably write-free (#846): no archive restores, no cache recency stamps, no reinforcement — a scoring pass is not a user access. - Cluster convergence: the evolve batch path replicates (#686), decay is leader-gated (#760), the replication log is pruned and bounded (#724/#725 — measured ~10 GB/day of unbounded growth before), role flaps no longer crash the process (#629), node identity survives hostname changes (#630), and a stale Lobe can no longer rejoin silently blind (#631).
- Consolidation declares supersession (#779): a query phrased in a merged source's wording now finds the merged memory instead of nothing.
- Recall-health and MCP auth-source metrics (#606/#648), per-vault embed status (#802), SIGHUP log rotation (#850), and a self-
upgradethat is safe to run and to undo (#600/#749).
The full record — including what was deliberately not shipped and the measured negatives — is in CHANGELOG.md.
What's Changed
- feat(auth): add working plasticity preset (RFC #597) by @madeinoz67 in #599
- ci: rename stale 'minilm-v2' embed-asset cache keys to bge-small-en-v1.5 (#601) by @johanneshauer in #603
- fix(storage): serialize DeleteEngram with CompareAndSet's stripe lock by @ad-astra-bot in #594
- fix(activation): preserve explicit search threshold under RRF + expose Search Scoring setting by @gitssie in #590
- feat(mcp): capability tokens + muninn_create_workflow_vault (RFC #597) by @madeinoz67 in #612
- feat(mcp): toolset profiles for tools/list — env default + per-connection header override (#588) by @isaac-ranger in #604
- docs: AI-agent working guide, internals reference, and code-review agent by @scrypster in #623
- fix(hnsw): late inserts born with zero in-edges stay permanently unreachable — protect the fresh back-edge in prune, repair orphans on load by @isaac-ranger in #621
- fix(mcp): SSE mk_ re-validation + vault-exists test + session.go removal (RFC #597 inc 3) by @madeinoz67 in #617
- fix(storage/auth): relocate auth prefixes + prefix registry + v3 migration (#611) by @madeinoz67 in #618
- fix(recall): seed tag-index candidates for tags_all/tags_any recall (#607) by @ad-astra-bot in #619
- feat(storage,engine): persist per-recall surfaced sets as recall events by @isaac-ranger in #574
- docs: add the review rubric that makes AI code review dependable by @scrypster in #624
- fix(mcp): expose stored memory type on read/recall/where_left_off/find_by_entity by @isaac-ranger in #616
- feat(cli):
muninn remember— REST write verb with--content-file(#610) by @isaac-ranger in #613 - docs: sync internals after #617, #618, #619 merged by @scrypster in #633
- docs(changelog): reconcile 0.8.0 + add 0.9.0 (closes #602) by @scrypster in #635
- chore: reconcile main into develop (close the changelog merge-back gap) by @scrypster in #637
- chore(deps): bump golang.org/x/text to v0.39.0 (GO-2026-5970) by @scrypster in #649
- fix(enrich): preserve transient provider failures by @dpearson2699 in #643
- feat(grpc,engine): AdjustConfidence RPC with contradiction signaling (#559) by @madeinoz67 in #625
- Close three documented gaps: upgrade checksums (#600), Windows CI model drift, and drift guards that never ran by @scrypster in #666
- build(deps): bump golang.org/x/crypto from 0.50.0 to 0.52.0 by @dependabot[bot] in #667
- chore(deps): bump esbuild, vite and vitest in /web by @dependabot[bot] in #664
- chore(deps-dev): bump vitest from 3.2.4 to 3.2.6 in /sdk/node by @dependabot[bot] in #665
- build(deps-dev): bump picomatch from 4.0.3 to 4.0.5 in /sdk/node by @dependabot[bot] in #670
- build(deps): bump google.golang.org/grpc from 1.79.3 to 1.82.1 by @dependabot[bot] in #668
- build(deps): bump golang.org/x/net from 0.54.0 to 0.55.0 by @dependabot[bot] in #669
- Migrate Tailwind v3 → v4, unblocking #662 by @scrypster in #671
- Clear the last 9 Dependabot alerts, and gate the Node SDK in CI by @scrypster in #672
- Ignore the third maintainer-private skill directory by @scrypster in #673
- Move GitHub Actions to their Node 24 runtimes by @scrypster in #674
- fix(fts): Stop() must drain the queue even before workers are scheduled by @scrypster in #675
- Add Worker.Flush and make awaitFTS actually await by @scrypster in #676
- Correct two stale code comments; close out fixed drift entries by @scrypster in #677
- Pin plasticity preset names across Go and the web console by @scrypster in #678
- fix(engine): inherit MemoryType and TypeLabel across evolve by @isaac-ranger in #655
- docs: fix Title Case heading capitalization in CONTRIBUTING.md by @amir-rezaei in #652
- Run the web console's vitest suite in CI by @scrypster in #679
- fix(engine): carry entity links and relationships across evolve, funding the mention ledgers by @isaac-ranger in #646
- feat(reflex): session-start orientation primitives — reinforcement, reminders, trust tiering (S0-S8) by @scrypster in #685
- Land reflex stack: append-mode, tag_filter, dedup-guard, supersedes-aware recall, valid-time by @scrypster in #688
- fix(engine): rarity-weight entity boost, cap accumulation, gate injection on threshold by @isaac-ranger in #570
- feat(cognition): importance dimension + pruning protection by @scrypster in #689
- feat(mcp,engine): optional inline entities on evolve, replacing the carried set by @isaac-ranger in #680
- fix(engine,storage): startup repair for evolve-stripped successors — atomic, funded, watermarked by @isaac-ranger in #681
- docs(skill): /increment — the repeatable build loop by @scrypster in #690
- test(engine): entity-boost lease-fail-closed coverage (#570 follow-up) by @scrypster in #691
- feat(prospective): THE PUSH increment 1 — armed intentions + notices over MCP by @scrypster in #694
- fix(trigger): carry the full 8-byte vault prefix on trigger events (#692) by @scrypster in #697
- fix(recall): deterministic top-N ordering — ULID tie-break at equal scores (#698) by @scrypster in #699
- fix(prospective): exclude an intention's own engram from its focality (#693) by @scrypster in #703
- fix(recall): RRF vaults no longer return silently-empty default recall (ranking-honesty R1) by @scrypster in #705
- fix(contradict): stop fabricating contradictions from same-relation-type/different-target shape (ranking-honesty R2) by @scrypster in #707
- fix(recall): calibrated FTS relevance → recall can abstain (reliable-colleague #711) by @scrypster in #715
- fix(recall): recall-mode presets no longer bypass the rrf mode-aware threshold default (#704) by @isaac-ranger in #710
- fix(engine): shared visibility gate for post-pipeline injections; supersession substitutes atomically under the caller's view by @isaac-ranger in #701
- docs: calibration is per-vault and self-derived, never hardcoded (principle #11) by @scrypster in #717
- feat(recall): semantic-abstention floor — anisotropy-calibrated cosine (COG-26) by @scrypster in #718
- docs(guide): use muninn_evolve, not repeated remember, for superseding facts by @scrypster in #723
- fix(recall): restore post-load cosine — GetEngrams omits ERF v2 embeddings (#714) by @scrypster in #722
- docs(internals): test hermeticity guide — drain async before asserting by @scrypster in #727
- chore(test,docs): use neutral fixtures, remove vault-specific identifiers by @scrypster in #734
- feat(recall): per-vault exclude-tags knob (#713) by @scrypster in #735
- feat(recall): advisory version-cluster annotation — currency (#712, COG-25) by @scrypster in #738
- feat(provenance): record the real write verb instead of hardcoded "create" by @scrypster in #739
- fix(recall): degrade loudly on embedding failure (semantic_degraded) by @scrypster in #740
- feat(guide): curator reflex — treat MuninnDB as living memory, reconcile at write by @scrypster in #741
- test(engine): stop TestCurrencyAnnotation_Latency flaking on CI runner noise by @scrypster in #744
- fix(mcp): never silently swallow an unrecognized memory type by @scrypster in #742
- fix(mcp): drop the entity-type enum from the write paths (it cost 64pp of entity coverage) by @scrypster in #743
- fix(mcp): never silently discard an unrecognized link relation by @scrypster in #745
- fix: agent-experience hardening — six silent-substitution defects found by hands-on AI evaluation by @scrypster in #746
- fix(engine): declaring a contradiction no longer destroys both memories by @scrypster in #747
- fix(cli): handle stale PID file in 'muninn stop' by @ad-astra-bot in #650
- docs(decision-record): associative surprise — killed, refuted at its premise by @scrypster in #751
- test(engine): stop TestStartImport_OrphanedVaultCleanup misreporting a timeout as a cleanup bug by @scrypster in #753
- fix: solid ground — consolidate data loss, inverted abstention, contradictions surface, and the write-effort cliff by @scrypster in #754
- fix: full-confidence weight destruction, evolve provenance, and the measured recall ceiling by @scrypster in #757
- fix(engine): currency advisory precision — universal marker gate + declared-chain suppression by @scrypster in #758
- fix(storage,engine): startup repair for pre-#757 full-weight association keys (#756) by @scrypster in #759
- fix(storage,auth,engine): time-normalize association decay — 13.5-minute half-life becomes a real forgetting curve (#762) by @scrypster in #766
- feat(engine,activation,mbp): resolve declared version chains to their head before ranking (#763) by @scrypster in #767
- fix(cognitive,engine,mcp,mbp,rest): declaring a contradiction changes what recall returns (#764) by @scrypster in #772
- chore(privacy): deep-review triage, vault-naming rule, and the increment-skill scrub by @scrypster in #775
- feat(engine,mcp,mbp,rest): a first-class relevance band — recall stops dressing weak matches as certainties (#773) by @scrypster in #778
- test: drain the activation log before the saturating-arm recall by @scrypster in #782
- docs(changelog): 0.10.0 — the trust release by @scrypster in #781
- test: drain write-time workers before the currency zero-writes snapshots (#777) by @scrypster in #784
- chore(agents): purpose-built designer, builder, and vault-measurer agents by @scrypster in #791
- chore(gitignore): keep the contributor PR vetter maintainer-private by @scrypster in #793
- fix(enrich): parse errors carry a category, never the model's output (#641) by @johanneshauer in #750
- fix(cli): refuse to self-update when a service manager owns the daemon (#600) by @johanneshauer in #749
- fix(web): report authoritative cognitive worker state by @dpearson2699 in #645
- chore(agents,skills): mechanism-critic, adversary, and the panel protocol by @scrypster in #795
- feat(memory): proposal ledger and drain — findings stop dying in transcripts by @scrypster in #796
- fix(storage,cognitive): a failed association read is no longer written back as fabricated defaults by @scrypster in #809
- fix(storage,engine): a symmetric association is now readable from both endpoints during ranking (#800) by @scrypster in #824
- fix(storage,engine): association edges to deleted engrams — three leaks, and the guard that closes them (#803) by @scrypster in #828
- fix(memory): the drain pipeline — three defects that made wiring it harmful, then the wiring (#825) by @scrypster in #829
- docs,ci: claim discipline — two green runs that measured nothing (#812, #814) by @scrypster in #830
- docs(decision-record): three measured negatives (#706, #716, #786) by @scrypster in #837
- fix: six small defects — shadow gate, chain-depth warn, annotation visibility, export reinforcement, stale constant, hard delete (#776, #794, #700, #733, #821, #807) by @scrypster in #836
- fix(keys): two opposite prefix-bound defects, and the third copy neither issue named (#816, #819) by @scrypster in #834
- fix(storage): four association-layer defects — read laundering, unchecked iterators, stale caches, aliasing (#804, #808, #818, #820) by @scrypster in #833
- docs: associative traversal has never fired, and no threshold repairs it (#801) by @scrypster in #831
- fix(mcp): classify muninn_state as mutating so observe-mode credentials cannot write (#731) by @timharsch in #732
- docs: trim SEC-6 to the declared format, move its narrative to the decision record by @scrypster in #839
- fix(cluster): a panic that took the process down, hostname-derived node identity, and CLI cluster auth (#629, #630, #632) by @scrypster in #840
- fix(storage): a cloned vault's LastAccess decoded to 1754, and the census that was supposed to catch it (#810) by @scrypster in #835
- feat(cognition): the Phase-1 trial harness, and the three production changes that make it measurable (#797) by @scrypster in #838
- fix(replication): relocate the log off the idempotency prefix, and stop shipping it to every lobe (#726, #826) by @scrypster in #841
- fix(cluster): a fixed deadline on a variable-size transfer, and a runtime-enabled node that replicated nothing (#627, #628) by @scrypster in #842
- fix(cluster): a follower accepted client writes and lost them silently, and a resnapshotted node skipped everything after it (#596, #631) by @scrypster in #843
- fix(replication): prune the Pebble replication log, bounded by a backlog ceiling (#724, #725) by @likesjx in #737
- test(engine): drain the reinforcement goroutine instead of racing a 15s budget (#813) by @scrypster in #844
- ci: a leak guard contributors actually have, and fix the private one skipping in worktrees by @scrypster in #845
- fix(storage,cluster): batch commits never replicated, role-blind decay, RelType collision, repair-watermark reset (#686, #760, #771, #761) by @scrypster in #848
- fix(engine): read_only must suppress every write side effect, not just the activation log (#598) by @scrypster in #846
- docs,test(activation): CGDN has never executed — label it INERT and build the census for the class (#768, #805) by @scrypster in #849
- fix: provider error leaks, false upgrade success, silent dangling relationships, NUL-invisible source, blind where_left_off (#790, #792, #817, #827, #811) by @scrypster in #847
- fix(cli,rest): log reopen on SIGHUP, an independent access-log toggle, and honest muninn logs (#850, #851, #852) by @scrypster in #853
- fix(plugin,engine): a transient embed failure recorded as permanent, a breaker tripped by callers, and an unreachable BM25 fallback (#605, #642, #658) by @scrypster in #854
- fix(storage,engine,mcp): entity records were global — a cross-tenant read oracle AND a cross-tenant write (#683) by @scrypster in #855
- feat(mcp): add muninn_update_tags for in-place retagging; evolve rejects tags (#720) by @timharsch in #730
- fix(consolidation,engine): consolidation now declares supersession — the one content-replacing verb that did not (#779, #769, #780) by @scrypster in #856
- fix(cli): keep the outgoing binary and surface the release's upgrade note (#600) by @johanneshauer in #748
- feat(embed): canonical corpus + reproducible anisotropy baseline (#719) by @johanneshauer in #752
- fix(cli): render the upgrade note inertly on the terminal by @scrypster in #857
- feat: UPSERT write mode for Write/BatchWrite (#556) by @madeinoz67 in #659
- fix(activation,rest): a time constant named half-life, and a vault param accepted then ignored (#799, #802) by @scrypster in #859
- fix(storage,consolidation,trigger): symmetric archive restore, a hard dedup cap, and a collision estimate off by 1000x (#806, #728, #696) by @scrypster in #860
- fix(cli): a vault switch that could not persist said it had (#634) by @scrypster in #861
- fix(engine,mcp): recall-health and auth-source metrics, and a Push fixture that proved nothing (#606, #648, #702) by @scrypster in #862
- fix(storage): seven writers mutate the shared cached engram, not one — and #815 does not reproduce (#858, #815) by @scrypster in #863
- docs(invariants): COG-22 amendment label — #763 is COG-28, not COG-27 by @isaac-ranger in #832
- docs(decision-record): the claim-discipline pattern, found eleven more times in a day by @scrypster in #864
- test(cognition): the null moves to SHIP, K4 dies as a clause, and zeros stop reading as a measured KILL (#798, refs #797) by @scrypster in #865
- fix(activation,storage): read-only recalls restored archived edges and freshened cache recency (refs #846, #712) by @scrypster in #866
- docs(changelog): the 0.11.0 entry — fifty merges since 0.10.0 by @scrypster in #867
New Contributors
- @madeinoz67 made their first contribution in #599
- @gitssie made their first contribution in #590
- @dependabot[bot] made their first contribution in #667
- @amir-rezaei made their first contribution in #652
- @likesjx made their first contribution in #737
Full Changelog: v0.8.0...v0.11.0