mirror of
https://github.com/thedotmack/claude-mem.git
synced 2026-09-20 04:23:02 +08:00
1c6c187688
* fix(hooks): dedupe file-context injection per session (#3480) The PreToolUse file-context handler re-injected the same "prior observations" timeline on every Read/Grep of an unchanged file within a session — the only gate was file-mtime staleness, with no per-(session, file) memoization. Repeatedly reading the same file (iterative debugging, long sessions) multiplied context cost with zero new information. Add a small persistent per-session store (one JSON file per session under DATA_DIR/file-context-seen) keyed by resolved file path → newest injected observation epoch. A repeated unchanged Read is now skipped; re-injection happens only when a newer observation has been recorded since. In-memory memoization is impossible here because each hook runs in a freshly spawned process. Stale session files are pruned (7-day TTL) on a session's first write so the store stays bounded. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(hooks): make file-context dedupe keys collision-free (#3480) Address two P1 review findings on the per-session file-context dedupe store: - Session-id store filenames: replace the non-injective char-replace scheme (`[^A-Za-z0-9_-] -> _`) with a sha256 hex digest of the complete session id. `a.b` and `a:b` no longer collapse to the same `a_b.json` and cross-suppress each other's initial injection. Still path-safe and traversal-proof. - Dedupe path key: normalize with `path.resolve(cwd, filePath)` for absolute inputs too, so dot-segment aliases (`/p/src/../src/f.ts` vs `/p/src/f.ts`) collapse to one canonical key instead of double-injecting the same file. RED -> GREEN: two new regression tests in tests/hooks/file-context.test.ts fail on the pre-fix code (collision suppresses the distinct session; alias bypasses dedupe) and pass after. Full file-context suite 17 pass; typecheck clean; full `bun test tests` baseline unchanged (27 pre-existing failures before and after, +2 new passing tests). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(hooks): order file-context store setup before prune check (#3486) * fix(hooks): persist the file-context gate in SQLite, not a JSON side-store (#3480) Review feedback on #3486 asked for plan-20 (#3608) step 4: the file-context injection gate must be a row in the main database keyed per (session, file, observation epoch), not a JSON file under DATA_DIR. What changed: - The gate moves from one JSON file per session under `file-context-seen/` to a `file_context_injections` table in `claude-mem.db`. The table is device- local: sync enumerates the tables it pushes explicitly, and this is not one. - `wasFileContextInjected` + `recordFileContextInjection` collapse into a single `claimFileContextInjection`. Claiming IS recording, in one `INSERT ... ON CONFLICT ... RETURNING` statement, so two concurrent Reads of the same file in one session can no longer both inject — the read-before-write race the previous store shipped with (and documented as acceptable) is gone. - The upsert guard `excluded.observation_epoch > stored` keeps the stored epoch monotonic, so a hook that finishes late carrying an older epoch neither wins the claim nor rolls the row back to a stale value. - The claim moved to the very end of `buildFileContextTimeline`, after every other reason to bail out, so a suppressed injection never burns the claim. - `resolveDbPath()` resolves the database path at call time. `DB_PATH` freezes `DATA_DIR` at import, which is right for the long-lived worker but wrong for a per-invocation hook process that must honour a `CLAUDE_MEM_DATA_DIR` set after the module loaded (and is what lets the tests point at a temp dir). Failing open is preserved: an unopenable database or a failed claim injects rather than throwing, which is never worse than the un-deduped behaviour. --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>