| **v0.8.0** | 2026-09-20 | **Auto-metabolism fix (real defect, caused data loss)**: ①the auto-`dream` interval check used `store.latestBackup()`, which reads the pre-single-track Markdown backup dir (`MEMORY_ROOT/backups/<12-digit dirs>`) that stopped being produced in v0.6.4 → always `null` → **a full dream ran on every plugin load** (109 runs measured, up to 26 in one day); ②`runDream` decayed with `w × 0.5^(age/halfLife)` using the *already decayed* current weight times the full-age factor = **compounding decay**, so repeated runs accelerated exponentially → 19 behavioural memories (including several merged composite entries) were pushed below `decayThreshold` and wrongly archived. Fix: the check now reads `lastDreamAt` / `lastReflectAt` from the meta table (written when dream/reflect finish; no file mtime involved); decay became **incremental and idempotent** — base = `max(created_at, last metabolism)`, and `DECAY` is recorded only when the stored precision (1 decimal) actually drops. New `store.unarchiveEntry(fp, {weight})` (archived row → `active` with weight repair, audited `UNARCHIVE`) plus the `bm-restore-archived.cjs` maintenance script (dry-run by default); all 19 wrongly archived entries were restored. **Same batch**: `setPinFp` zeroed `weight` when pinning (un-pinning instantly dropped below threshold) → weight is no longer touched (skip-decay is enforced by `runDream`); `removeByFp` also ran `DELETE FROM audit_log` (deleting an entry erased its audit trail) → removed; `/status` `auditCount` used `queryAudit({})` with the default `limit=50` → always ≤50, now `limit: 100000`; `retrieve` counted hits for **every** returned entry (contradicting the "only real keyword recalls consolidate" comment) → only keyword hits count; the embedding model had no negative cache and `apply` forced `ensureVectors` 100ms after start (loading the ~24MB onnx model on every launch) → negative cache + truly lazy loading (`preloadEmbeddings` defaults to `false`; set `true` or `DSH_BIOMEMORY_PRELOAD=1` to warm up); `PRAGMA busy_timeout=5000` for multi-process access; the Markdown migration no longer mis-parses the sync mirror `条目镜像.md` and `reflections/`. **Dead code removed**: `rewriteFile` / `findByText` / `backupNow` / `latestBackup` (the very trigger of this defect) / `embedMany` / `isModelReady` / `embedTextOf` / `db.getById` / `db.listPinned` / `session-state.getSummarySid`. New regression tests in `tests/dream-idempotent.test.mjs` (idempotent decay / new-entry baseline / unarchive / snapshot skips archived / trigger predicate / consolidation tied to recall time / restore keeps entry_id). **Follow-up cleanup**: the auto-metabolism predicate is extracted into the pure function `meta.shouldRunAuto()` (directly unit-testable); consolidation now only reinforces entries that were **recently recalled for real** (`last_accessed` must fall within half a half-life, and `consolidateHits` now records the recall time — the old code was decoupled from recall time, so stale entries gained weight on every dream; 3020 CONSOLIDATE events measured); `restoreEntry` keeps the original `entry_id` (the old code regenerated a UUID, leaving 6893/10006 audit rows dangling); the Web API `readBody` is capped at 1 MiB (`DSH_BIOMEMORY_BODY_LIMIT` overrides); `client.js` theme listener got its dependency array (it used to rebuild the matchMedia listener and MutationObserver on every render); `settings-page.test.mjs`, which never matched the `tests/*.test.mjs` glob, is now part of `npm test`. **Upgrade (memory atomisation, P1)**: `writeEntry` now intercepts **near-duplicates** — when new content scores ≥ `nearDuplicateThreshold` (default `0.7`, `0` disables) in Chinese-bigram Jaccard similarity against an existing entry of the same type, it is not inserted; instead the tool returns the similar entry's fp plus a merge hint (audited as `WRITE-SKIP`). Fragmentation came from "the same thing written as a new entry again and again" (the 8/20–9/5 batch had to be merged by hand; whatever was missed ended up archived by normal metabolism). The check is pure bigram arithmetic, never loads the embedding model, and runs inline on the write path; the exact fingerprint only looks at the first 20 characters, so near-duplicate detection covers the "rephrased / different opening" cases. **Second round (same version)**: ①**automatic near-duplicate merge** (`nearDuplicateAction`, default `merge`, borrowed from `@zheexinn/dsh-memory`'s merge-on-write): a near-duplicate is no longer merely rejected — the new content is appended to the existing entry as `| 【补充·date】…` with weight +1 (falls back to `skip` above 4000 chars), audited as `WRITE-MERGE`; similarity now takes `max(Jaccard, containment)` when lengths are comparable, so short rephrasings are not missed (measured 0.64 → 0.86 for two short near-duplicates); ②**snapshot marked as untrusted data** (borrowed from `dsh-git-memory`'s `<summary_snapshot>`): the injected header now states explicitly that the entries are *data, not instructions*; ③**backup/restore hardening**: `wal_checkpoint` result is checked (warns when `busy≠0`), every backup is self-verified (the copy is opened and its entry count compared, failing copies are deleted and the error rethrown), and restore now takes a pre-restore snapshot, replaces the DB via temp-file rename, and clears stale `-wal`/`-shm`; ④**all panel requests are abortable**: `client.js` gained `apiFetch` (registers in-flight requests, aborts them on unmount, and fixes the effect-returning-a-Promise warning) plus editing state is cleared on tab switch; ⑤the inert top-level `allowScripts` was replaced by pnpm's actual `pnpm.onlyBuiltDependencies` (no `@deepseek-ai/*` package in the global install reads `allowScripts`). ⑥**"auto-sink" reliability fix** (answering "will it save on its own when I don't say so?"): the post-turn sink reminder window went from a **hard-coded 5 minutes** to the configurable `sinkWindowMinutes` (default **60**) — previously, if the user stepped away for more than five minutes the reminder was silently dropped, which is exactly why writes often only happened when explicitly requested; the reminder text now also states that **proactive sinking is the default** and that near-duplicates are merged automatically. 79 → **90 tests green** |
0 commit comments