nexusmem 0.5.4 → 0.7.0

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
@@ -9,6 +9,59 @@ built from, matched by publish timestamp: `v0.1.0` → `67a4776`, `v0.1.1` → `
9
9
 
10
10
  ## [Unreleased]
11
11
 
12
+ No unreleased changes yet.
13
+
14
+ ## [0.7.0] — 2026-08-21
15
+
16
+ ### Added
17
+
18
+ - `nexusmem stale --check-contradictions`: for each stale candidate, finds the most similar newer
19
+ node (local embedding search) and asks a local SLM (Ollama, `qwen2.5:3b` by default) whether it
20
+ actually contradicts the older one, instead of only surfacing by age. Suggest-only — nothing is
21
+ written, same as plain `stale`. Live-dogfooded against this repo's own real database and Ollama
22
+ instance; found and fixed a real gap along the way (below) before the feature surfaced anything
23
+ useful.
24
+ - Import graph: bare Python imports with no leading dot (`import foo`, `from foo import bar`)
25
+ now resolve to a same-directory sibling file too, alongside the existing relative-dot support.
26
+ Found dogfooding two real local projects: neither used a single PEP 328 relative import, both
27
+ relied entirely on this flat-script style. Guarded by a static list of stdlib module names so a
28
+ bare `import os`/`import queue`/etc. is never mistaken for a same-named local file.
29
+
30
+ ### Fixed
31
+
32
+ - Import graph: a Java wildcard import (`import a.b.*;`) no longer merges files from two
33
+ unrelated packages that happen to share a directory-name suffix (e.g. two Gradle/Maven modules
34
+ each with their own `.../foo`) — it now refuses to guess, same as the single-class-import case.
35
+ - `stale --check-contradictions`'s neighbor search now looks past same-timestamp sibling nodes
36
+ (e.g. the many chunks one long conversation gets split into) to reach genuinely newer content.
37
+ Found live-dogfooding against this repo's own database: the first real candidate's closest 15
38
+ neighbors were all same-conversation siblings sharing its exact timestamp, so the original
39
+ 5-neighbor default silently found zero suggestions for every candidate, regardless of what the
40
+ SLM would have said.
41
+
42
+ ## [0.6.0] — 2026-08-20
43
+
44
+ ### Added
45
+
46
+ - Ranker: `inferred` nodes (summaries, doc snapshots) now decay twice as fast as `observed` ones
47
+ (git commits, diffs, shell commands) for retrieval purposes. A judgment call, not a measured
48
+ optimum — see `INFERRED_HALF_LIFE_RATIO` in `src/retrieval/rank.ts`.
49
+ - `nexusmem stale`: lists aging `inferred` nodes nothing has superseded yet, as candidates for
50
+ `mark-stale`. A heuristic on age and provenance, not real contradiction detection — writes nothing.
51
+ - `nexusmem status` now surfaces an `aging` line with the stale-candidate count when any exist.
52
+ - Import graph: Python relative imports (`from .foo import bar`, `from . import x`) now produce
53
+ file edges too, alongside the existing JS/TS support. Absolute Python imports are still skipped —
54
+ same "missed edge over wrong edge" reasoning as JS/TS's bare-specifier skip.
55
+ - Import graph: Go internal imports (resolved against `go.mod`'s module path, one edge per
56
+ non-test file in the imported package) and Rust `mod foo;` declarations (2018+ edition module
57
+ layout) now produce file edges too. External Go imports and Rust `use` paths are out of scope for
58
+ the same reason.
59
+ - Import graph: Java imports (`import a.b.C;` / `import a.b.*;`, resolved by unambiguous suffix
60
+ match against the tracked source tree) and `__DIR__`-anchored PHP `require`/`include` now produce
61
+ file edges too — all six of `nexusmem scan-structure`'s tracked languages. `import static`, PHP's
62
+ autoloaded `use Namespace\Class;`, and any unanchored PHP include are out of scope for the same
63
+ "missed edge over wrong edge" reason as everywhere else in the import graph.
64
+
12
65
  ## [0.5.4] — 2026-08-20
13
66
 
14
67
  ### Added
@@ -428,7 +481,12 @@ First public release.
428
481
  there is no local-model summarization pass, and the conversation collector has never been audited
429
482
  for the stale-node bug that was found and fixed in the docs collector.
430
483
 
431
- [Unreleased]: https://github.com/yaminbkk/NexusMem/compare/v0.5.1...HEAD
484
+ [Unreleased]: https://github.com/yaminbkk/NexusMem/compare/v0.7.0...HEAD
485
+ [0.7.0]: https://github.com/yaminbkk/NexusMem/compare/v0.6.0...v0.7.0
486
+ [0.6.0]: https://github.com/yaminbkk/NexusMem/compare/v0.5.4...v0.6.0
487
+ [0.5.4]: https://github.com/yaminbkk/NexusMem/compare/v0.5.3...v0.5.4
488
+ [0.5.3]: https://github.com/yaminbkk/NexusMem/compare/v0.5.2...v0.5.3
489
+ [0.5.2]: https://github.com/yaminbkk/NexusMem/compare/v0.5.1...v0.5.2
432
490
  [0.5.1]: https://github.com/yaminbkk/NexusMem/compare/v0.5.0...v0.5.1
433
491
  [0.5.0]: https://github.com/yaminbkk/NexusMem/compare/v0.4.0...v0.5.0
434
492
  [0.4.0]: https://github.com/yaminbkk/NexusMem/compare/v0.3.3...v0.4.0
package/README.md CHANGED
@@ -263,16 +263,25 @@ Latency on a ~530-node corpus, warm, p50 over 10 runs:
263
263
  All the SQLite work totals about 5 ms. The embedding call is the only thing on this path worth
264
264
  optimizing, and it is somebody else's process.
265
265
 
266
- ## Manual staleness & provenance
266
+ ## Staleness & provenance
267
267
 
268
268
  Two things a memory layer needs and this one only partly has: a way to tell an observed fact from a
269
- guess, and a way to retire a conclusion once something contradicts it. Neither is automatic here —
270
- this section is what exists and what doesn't.
269
+ guess, and a way to retire a conclusion once something contradicts it. This section is what exists
270
+ and what doesn't.
271
271
 
272
272
  Every node carries a `provenance`: `observed` (a commit that landed, a shell command's real exit
273
273
  code) or `inferred` (a conversation turn, a session summary, a doc section — all readable as claims
274
- that could be wrong or go stale). Set once per collector at ingest time, and shown as a `[observed]`
275
- / `[inferred]` tag on every query result.
274
+ that could be wrong or go stale). Set once per collector at ingest time, shown as a `[observed]` /
275
+ `[inferred]` tag on every query result, and now used to decay retrieval weight too — `inferred` nodes
276
+ fade from ranking twice as fast as `observed` ones as they age.
277
+
278
+ ```bash
279
+ nexusmem stale
280
+ ```
281
+
282
+ Lists `inferred` nodes old enough (45+ days by default) that nothing has confirmed they still hold —
283
+ a heuristic on age and provenance, not on content. It writes nothing; you decide which candidates are
284
+ actually wrong.
276
285
 
277
286
  ```bash
278
287
  nexusmem mark-stale <oldNodeId> --supersedes <newNodeId>
@@ -281,10 +290,22 @@ nexusmem mark-stale <oldNodeId> --supersedes <newNodeId>
281
290
  Links `newNodeId` as the replacement for `oldNodeId`. The ranker down-weights the old node from then
282
291
  on (it stays queryable, just usually loses to its replacement) — nothing is deleted, unlike `forget`.
283
292
 
284
- **What this doesn't do:** nothing here detects staleness on its own. If a later commit contradicts an
285
- earlier doc section, NexusMem has no way to notice that and flag it — a human or an agent has to spot
286
- the contradiction and run `mark-stale` themselves. Automatic staleness detection is still an open
287
- problem.
293
+ ```bash
294
+ nexusmem stale --check-contradictions
295
+ ```
296
+
297
+ For each candidate, finds the most similar newer node (local embedding search) and asks a local SLM
298
+ (Ollama, `qwen2.5:3b` by default) whether it actually contradicts the older one — real content
299
+ comparison, not just age. A match is printed as `likely superseded by <id> <title> -- <reason>`
300
+ under the candidate; nothing is written, same as plain `stale`. Needs Ollama running; costs one
301
+ embedding call and, when a plausible newer node exists, one chat completion per candidate (capped at
302
+ 10 by default — pass `-n` to raise it).
303
+
304
+ **What this doesn't do:** it is one small model's yes/no judgment on one older/newer pair, not a
305
+ verified fact — treat a match as a lead to check, not a conclusion. It also only ever compares a
306
+ candidate against nodes *found by embedding similarity*; a contradiction from an unrelated-sounding
307
+ node would never surface. Real contradiction detection (comprehensively, not just for the pair the
308
+ vector search happens to surface) is still an open problem.
288
309
 
289
310
  ## Where it breaks
290
311
 
@@ -352,8 +373,9 @@ problem.
352
373
  ## Commands
353
374
 
354
375
  `init`, `sync`, `query <text>`, `status` (add `--share` for a plain-text summary worth pasting
355
- somewhere), `projects`, `mcp`, `forget <value>`, `mark-stale <nodeId> --supersedes <newNodeId>`,
356
- and `hook install|remove|status`.
376
+ somewhere), `projects`, `mcp`, `forget <value>`, `stale` (add `--check-contradictions` for a local-SLM
377
+ content check, see above), `mark-stale <nodeId> --supersedes <newNodeId>`, and
378
+ `hook install|remove|status`.
357
379
 
358
380
  There are also five dry-run previews (`scan-git`, `scan-diff`, `scan-shell`, `scan-docs`,
359
381
  `scan-conversation`)