nexusmem 0.8.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -11,6 +11,67 @@ built from, matched by publish timestamp: `v0.1.0` → `67a4776`, `v0.1.1` → `
11
11
 
12
12
  No unreleased changes yet.
13
13
 
14
+ ## [0.9.1] — 2026-08-29
15
+
16
+ Three ranking/retrieval correctness fixes, found and validated against a new 28-case retrieval-quality
17
+ eval harness (`npm run eval`, dev-only, not part of the published package).
18
+
19
+ ### Fixed
20
+
21
+ - `nodes_vec` (vector search) over-fetched `k` globally, then filtered by `project_id` after the join —
22
+ a heuristic, not a guarantee. A sparse project sharing `memory.db` with a much larger one could have
23
+ every true nearest neighbour fall outside the over-fetch window and get silently dropped (reproduced:
24
+ 495 rows in one project + 5 in another, global `k=50` surfaced 0 of the 5). Schema V11 gives `nodes_vec`
25
+ a `PARTITION KEY` on `project_id` (`sqlite-vec` 0.1.9+), pushing the equality filter into the k-NN
26
+ search itself so cross-project exactness is now guaranteed, not probable. Migrates existing databases
27
+ automatically on next `sync`/`query`. `--as-of` date filtering still over-fetches — only the
28
+ `project_id` dimension was ever a correctness guarantee.
29
+ - `MAX_PRIOR_OVERTURN` (the cap on how far a recency/signal prior may overturn relevance) raised
30
+ `2 → 2.4`, the highest value that both improves eval MRR (0.924→0.943) and still passes every legacy
31
+ regression test guarding against the original same-day-fix-commits bug this constant exists to bound.
32
+ - A commit's `code_diff` siblings (one node per changed file, all sharing the same `ts`) could crowd a
33
+ packed result and bury that commit's own `git_commit` node — its answer — several ranks down. Now
34
+ capped per commit, matching the existing `conversation_turn`/`doc_section` family cap.
35
+ - `package.json`/`tsconfig.json` diffs were ranking above more relevant results when a changed file's
36
+ path happened to echo its commit's conventional-commit scope (title is weighted 10x body, so the scope
37
+ word counted twice). Down-weighted as mechanical wiring, same reasoning `TEST_PATHS` already applies to
38
+ tests. Only affects nodes ingested from here on — existing manifest/config diffs need `sync --rebuild`
39
+ to get the corrected signal retroactively.
40
+
41
+ ## [0.9.0] — 2026-08-27
42
+
43
+ Closes the three remaining mechanisms from a recurring external review (Simon Strandgaard, Agent
44
+ Memory Atlas): trust_state, bi-temporal reads, and a dismiss verb for standing suggestions. Also
45
+ fixes two real Windows-specific correctness bugs found dogfooding.
46
+
47
+ ### Added
48
+
49
+ - `nodes.trust_state` (`candidate` | `verified` | `rejected`, schema V10) and `nexusmem review
50
+ <nodeId> --verify|--reject`: a human verdict on a node, independent of the SLM contradiction
51
+ checker. `--reject` down-weights ranking (harsher than the existing supersede penalty, since a
52
+ human said no directly); `--verify` is a label only, no ranking boost. Both render as a tag in
53
+ packed context. Never deletes — same demote-not-delete rule as `mark-stale`.
54
+ - `--as-of <date>` on `nexusmem query` and the MCP `search_memory` tool: restricts both the BM25
55
+ and vector arms to nodes recorded at or before that instant via `created_at` (record time),
56
+ independent of how old the events themselves (`ts`, event time) are. Read-only — there is no
57
+ equivalent write, and nodes stay write-once.
58
+ - `nexusmem stale --dismiss`: silences a contradiction suggestion the user disagrees with, without
59
+ fabricating a `supersedes` relationship. Previously a wrong `--check-contradictions` verdict had
60
+ no way to be rejected — it re-decorated every future `stale`/`sync` run forever.
61
+ - `--prune-source`/`--prune-stale-shell` now record a `mutation_audit` row on their `--yes` path,
62
+ matching what `forget` already did. The dry-run preview stays a pure read.
63
+
64
+ ### Fixed
65
+
66
+ - `git rev-parse` failures reported as "error launching git: Access is denied." (Git for Windows'
67
+ launcher shim failing to exec `git.exe` under handle/AV contention) were treated as git's own
68
+ verdict instead of a transient spawn failure, turning a one-off environment hiccup into a hard
69
+ failure. Now retried the same as the other known transient-spawn classes.
70
+ - The PowerShell prompt hook read only `$LASTEXITCODE`, which cmdlets (`Remove-Item`, `Copy-Item`,
71
+ …) never set — every failing cmdlet was silently logged as a success, and a stale
72
+ `$LASTEXITCODE` from an earlier native command could misattribute to later successful cmdlet
73
+ runs. Now reads `$?` alongside `$LASTEXITCODE`, before anything else can overwrite `$?`.
74
+
14
75
  ## [0.8.0] — 2026-08-22
15
76
 
16
77
  ### Added
@@ -504,7 +565,8 @@ First public release.
504
565
  there is no local-model summarization pass, and the conversation collector has never been audited
505
566
  for the stale-node bug that was found and fixed in the docs collector.
506
567
 
507
- [Unreleased]: https://github.com/yaminbkk/NexusMem/compare/v0.8.0...HEAD
568
+ [Unreleased]: https://github.com/yaminbkk/NexusMem/compare/v0.9.0...HEAD
569
+ [0.9.0]: https://github.com/yaminbkk/NexusMem/compare/v0.8.0...v0.9.0
508
570
  [0.8.0]: https://github.com/yaminbkk/NexusMem/compare/v0.7.0...v0.8.0
509
571
  [0.7.0]: https://github.com/yaminbkk/NexusMem/compare/v0.6.0...v0.7.0
510
572
  [0.6.0]: https://github.com/yaminbkk/NexusMem/compare/v0.5.4...v0.6.0
package/README.md CHANGED
@@ -248,7 +248,9 @@ One thing that is not a percentage: shell commands and conversation turns have n
248
248
  equivalent. Without something recording them, they are gone, not merely more expensive to find.
249
249
 
250
250
  For how these numbers compare to a similar tool's own claims, see
251
- [`docs/competitor-comparison.md`](docs/competitor-comparison.md).
251
+ [`docs/competitor-comparison.md`](docs/competitor-comparison.md) (vs. projectmem) and
252
+ [`docs/competitor-comparison-yesmem.md`](docs/competitor-comparison-yesmem.md) (vs. YesMem, including
253
+ native Windows support vs. its documented WSL2 requirement).
252
254
 
253
255
  Latency on a ~530-node corpus, warm, p50 over 10 runs:
254
256
 
@@ -316,6 +318,21 @@ candidate against nodes *found by embedding similarity*; a contradiction from an
316
318
  node would never surface. Comprehensive contradiction detection (not just for the pair the vector
317
319
  search happens to surface) is still an open problem, and nothing here supersedes a node on its own.
318
320
 
321
+ `provenance` is a separate question from `trust_state`: provenance says where a claim came from,
322
+ never whether anyone checked it.
323
+
324
+ ```bash
325
+ nexusmem review <nodeId> --verify
326
+ nexusmem review <nodeId> --reject
327
+ ```
328
+
329
+ Records your own verdict on one node, independent of the SLM contradiction checker above (which only
330
+ ever writes a suggestion, never a verdict). `--reject` down-weights the node in ranking — same
331
+ demote-not-delete rule as `mark-stale`, it stays queryable, just usually loses to better matches —
332
+ and both verdicts are shown as a `[verified]`/`[rejected]` tag on every query result that returns the
333
+ node afterward. `--verify` is a label only; it does not boost ranking. Every node starts `candidate`
334
+ (untagged) until reviewed, and a re-sync never overwrites a verdict once one is set.
335
+
319
336
  ## Where it breaks
320
337
 
321
338
  - **Shell history without the hook is unscoped.** Scraped history has no directory context, so it is
@@ -381,16 +398,20 @@ search happens to surface) is still an open problem, and nothing here supersedes
381
398
 
382
399
  ## Commands
383
400
 
384
- `init`, `sync`, `query <text>`, `status` (add `--share` for a plain-text summary worth pasting
385
- somewhere), `projects`, `mcp`, `forget <value>`, `stale` (add `--check-contradictions` for a local-SLM
386
- content check, see above), `mark-stale <nodeId> --supersedes <newNodeId>`, and
387
- `hook install|remove|status`.
388
-
389
- There are also five dry-run previews (`scan-git`, `scan-diff`, `scan-shell`, `scan-docs`,
390
- `scan-conversation`)
391
- that write nothing and print the nodes ingestion *would* create along with their signal scores. That
392
- is the intended way to tune scoring against a real repository before committing to a change. Add
393
- `--json` to pipe them somewhere.
401
+ `init`, `sync`, `query <text>` (add `--as-of <date>` for a bi-temporal read, see below), `status` (add
402
+ `--share` for a plain-text summary worth pasting somewhere), `projects`, `mcp`, `forget <value>`,
403
+ `stale` (add `--check-contradictions` for a local-SLM content check, see above), `mark-stale
404
+ <nodeId> --supersedes <newNodeId>`, `review <nodeId> --verify|--reject` (record a human verdict on
405
+ one node, see above), `precheck` (advisory — warns about staged files with an unresolved past
406
+ failure or high recent churn; exits 0 unless `--strict`), `hook install|remove|status` (the
407
+ PowerShell exit-code hook), and `hook git install|remove|status` (a git pre-commit hook that runs
408
+ `precheck` before each commit).
409
+
410
+ There are also seven dry-run previews (`scan-git`, `scan-diff`, `scan-shell`, `scan-docs`,
411
+ `scan-conversation`, `scan-session`, `scan-structure`) that write nothing and print what ingestion
412
+ *would* produce — nodes and their signal scores for the first six, import-graph edges for
413
+ `scan-structure`. That is the intended way to tune scoring against a real repository before
414
+ committing to a change. Add `--json` to pipe them somewhere.
394
415
 
395
416
  Every command takes `-C <path>` to target another repository. On `sync`, `--conversation` opts the
396
417
  transcript source in for one run without persisting it, `--no-embed` skips the vector pass,
@@ -423,6 +444,24 @@ scope 2 project(s): NexusMem, uploader
423
444
  - 2026-08-09 [observed] [NexusMem] fix(git): retry a transient failure to spawn git
424
445
  ```
425
446
 
447
+ ## Bi-temporal reads
448
+
449
+ Every node carries two clocks: `ts`, the event's own time ("what happened then"), and `created_at`,
450
+ the moment the store actually recorded it ("what did the store hold then") — normally the same
451
+ question, but not when a sync runs late, a backfill lands weeks after the events it describes, or a
452
+ teammate's clone catches up all at once. `query`/`search_memory` answer the first by default;
453
+ `--as-of <date>` switches to the second:
454
+
455
+ ```bash
456
+ nexusmem query "why does the ranker cap joint priors" --as-of 2026-08-10
457
+ ```
458
+
459
+ Only nodes recorded at or before that instant are considered, even if the events they describe are
460
+ older still. There is no equivalent write — this is a read-time filter over `created_at`, not a
461
+ snapshot or a way to query a value that has since changed, since nodes are write-once (see
462
+ [Staleness & provenance](#staleness--provenance) for what changes a node's *weight*, not its
463
+ record).
464
+
426
465
  Databases stay per-repository — there is no shared global store, and deleting one repo's
427
466
  `.nexusmem/` still removes exactly that repo's memory. What makes the others findable is a plain
428
467
  index at `~/.nexusmem/projects.json`, written by `init` and refreshed by every `sync`. `nexusmem
@@ -458,7 +497,7 @@ Deleting `.nexusmem/` loses nothing that `sync` cannot rebuild.
458
497
 
459
498
  ## Status
460
499
 
461
- Ingestion, hybrid retrieval, budgeted packing and the MCP server all work and are covered by 493
500
+ Ingestion, hybrid retrieval, budgeted packing and the MCP server all work and are covered by 701
462
501
  tests running on Linux and Windows across Node 22 and 24.
463
502
 
464
503
  ## Development