Commit Graph
6 Commits
Author SHA1 Message Date
0845aeb58a fix(db): keep archived notes out of search, lists, counts and the vector rebuild (#74)
* fix(db): keep archived notes out of search, lists, counts and the vector rebuild

An archived note is meant to be kept but unused, yet most read paths did not
filter on is_archived: full-text and LIKE search, vector-hit hydration,
context injection, the paginated lists, counts and the vector rebuild all still
returned it. A vector hit for an archived note also still pointed at a live
embedding, because archiving never dropped it.

- add notArchivedFilter/visibleScope and apply them to every read path
- the paginated lists take includeArchived; the API reads
  ?include_archived=true, and exports ask for them so a backup is complete
- archiving drops the note's vectors, unarchiving syncs them back
- add //go:build fts5 to observation_update_test.go so `go vet` without tags
  builds (it referenced a helper from a tagged file)

Adds a store test that archives one of three notes and checks every path, and
that fails if the filter is removed.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>

* fix(worker): drop the vectors of notes archived through bulk-status too

POST /api/observations/bulk-status with action "archive" archived notes but left
their vectors in the index; only the dedicated archive handler dropped them.
Both paths now call dropArchivedFromVectors.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>

---------

Co-authored-by: Claude Sonnet 5.5 <[email protected]>
2026-10-13 09:34:58 +01:00
8c2c69582a fix(lint): clear the two findings left by #69 and #70 (#72)
- observation_update_test.go (#69) lacked the fts5 build tag its helper
  (testObservationStore, in observation_store_test.go) has, so `go vet ./...`
  without tags failed with "undefined: testObservationStore".
- Client.knn (#70) concatenates its WHERE clause into the query. The clause is
  built only from constant text with "?" placeholders (see nearest) and every
  value goes through args, so gosec's G202 is a false positive; annotate it the
  way the file already annotates G201.

Co-authored-by: Claude Sonnet 5.5 <[email protected]>
2026-10-06 20:32:14 +01:00
f2886bbedb fix(mcp): do not end an idle stdio server by closing stdin (#71)
After 30 minutes without a message the idle monitor closed stdin and expected the read loop to end. On a real, blocking stdin a read already in progress is not woken by closing the file, so the server lived on, the client's next message completed that read and the one after failed with 'read /dev/stdin: file already closed', which ended the connection mid-call.

The idle shutdown is now off by default (a stdio server lives as long as its client; orphans are still caught by the parent check). CLAUDE_MNEMONIC_MCP_IDLE_TIMEOUT (a Go duration) turns it on, and then it cancels the loop's context so Run returns nil instead of closing stdin.

Co-authored-by: Claude Sonnet 5.5 <[email protected]>
2026-10-04 23:16:27 +01:00
9b6e5f7648 fix(vector): answer project-filtered queries with two searches (#70)
A project filter was '(project = ? OR scope = global)', which vec0 rejects in a nearest-neighbour query ('A LIMIT or k = ? constraint is required'). The only caller that passes a project is the de-duplication of new observations, which treats an error as 'insert', so semantic de-duplication never took effect.

Run one search for the project and one for global documents and merge them by distance without repeats, cut to the limit. QueryMultiField asks for enough field documents and keeps the best per observation; its two skipped tests are enabled. Because de-duplication now runs, canMergeInto skips a superseded or deleted note as a merge target.

Co-authored-by: Claude Sonnet 5.5 <[email protected]>
2026-10-04 23:16:16 +01:00
055103cb3b fix(db): stop writing a column that does not exist when editing an observation (#69)
UpdateObservation set updated_at_epoch, but the observations table has no such column, so every PUT /api/observations/{id} failed with HTTP 500 (no such column: updated_at_epoch). Remove the assignment; nothing reads an edited-at time. An update that changes nothing now returns the observation, as intended. UpdateObservation had no tests: add store and handler tests.

Co-authored-by: Claude Sonnet 5.5 <[email protected]>
2026-10-04 23:16:01 +01:00
67de0f0c84 fix(vector): re-sync a note's vectors by replacing them; roll back a failed insert (#68)
The vectors table is a vec0 virtual table, where INSERT OR REPLACE does not replace, so syncing a note that already had vectors failed with a UNIQUE constraint error and the edit never reached the index. AddDocuments also shadowed err inside its loop, so a failed insert left the write transaction open and later writes failed with 'database is locked'.

Add Client.ReplaceDocuments (delete and insert in one transaction) and use it from SyncObservation and SyncSummary. The shared write function has a named result, so every failed return rolls back. Fixes #66.

Co-authored-by: Claude Sonnet 5.5 <[email protected]>
2026-10-04 23:15:46 +01:00