@oomkapwn/enquire-mcp 3.7.15 → 3.7.18

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.
Files changed (174) hide show
  1. package/CHANGELOG.md +244 -0
  2. package/README.md +5 -5
  3. package/SECURITY.md +26 -0
  4. package/dist/doctor.js +1 -1
  5. package/dist/embeddings.d.ts.map +1 -1
  6. package/dist/embeddings.js +6 -2
  7. package/dist/embeddings.js.map +1 -1
  8. package/dist/fts5.d.ts +19 -4
  9. package/dist/fts5.d.ts.map +1 -1
  10. package/dist/fts5.js +45 -10
  11. package/dist/fts5.js.map +1 -1
  12. package/dist/index.d.ts +1 -1
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +11 -3
  15. package/dist/index.js.map +1 -1
  16. package/dist/ocr.d.ts +42 -3
  17. package/dist/ocr.d.ts.map +1 -1
  18. package/dist/ocr.js +41 -2
  19. package/dist/ocr.js.map +1 -1
  20. package/dist/pdf.d.ts +16 -1
  21. package/dist/pdf.d.ts.map +1 -1
  22. package/dist/pdf.js +16 -1
  23. package/dist/pdf.js.map +1 -1
  24. package/dist/server.d.ts.map +1 -1
  25. package/dist/server.js +6 -1
  26. package/dist/server.js.map +1 -1
  27. package/dist/tools/meta.d.ts +9 -1
  28. package/dist/tools/meta.d.ts.map +1 -1
  29. package/dist/tools/meta.js +25 -1
  30. package/dist/tools/meta.js.map +1 -1
  31. package/dist/tools/search.d.ts.map +1 -1
  32. package/dist/tools/search.js +8 -2
  33. package/dist/tools/search.js.map +1 -1
  34. package/dist/tools/write.d.ts +8 -0
  35. package/dist/tools/write.d.ts.map +1 -1
  36. package/dist/tools/write.js +46 -10
  37. package/dist/tools/write.js.map +1 -1
  38. package/dist/tools.d.ts +980 -0
  39. package/dist/tools.d.ts.map +1 -0
  40. package/dist/tools.js +3132 -0
  41. package/dist/tools.js.map +1 -0
  42. package/dist/vault.d.ts +75 -1
  43. package/dist/vault.d.ts.map +1 -1
  44. package/dist/vault.js +154 -3
  45. package/dist/vault.js.map +1 -1
  46. package/dist/watcher.d.ts +15 -0
  47. package/dist/watcher.d.ts.map +1 -1
  48. package/dist/watcher.js +37 -7
  49. package/dist/watcher.js.map +1 -1
  50. package/docs/COMPARISON.md +1 -1
  51. package/docs/api-reference/.nojekyll +1 -0
  52. package/docs/api-reference/assets/hierarchy.js +1 -0
  53. package/docs/api-reference/assets/highlight.css +71 -0
  54. package/docs/api-reference/assets/icons.js +18 -0
  55. package/docs/api-reference/assets/icons.svg +1 -0
  56. package/docs/api-reference/assets/main.js +60 -0
  57. package/docs/api-reference/assets/navigation.js +1 -0
  58. package/docs/api-reference/assets/search.js +1 -0
  59. package/docs/api-reference/assets/style.css +1633 -0
  60. package/docs/api-reference/functions/index.buildEmbedText.html +15 -0
  61. package/docs/api-reference/functions/index.buildMcpServer.html +4 -0
  62. package/docs/api-reference/functions/index.formatReadyBanner.html +4 -0
  63. package/docs/api-reference/functions/index.main.html +1 -0
  64. package/docs/api-reference/functions/index.parsePositiveInt.html +1 -0
  65. package/docs/api-reference/functions/index.parseQuantizationMode.html +5 -0
  66. package/docs/api-reference/functions/index.prepareServerDeps.html +5 -0
  67. package/docs/api-reference/functions/index.startServer.html +1 -0
  68. package/docs/api-reference/functions/tools.appendToNote.html +17 -0
  69. package/docs/api-reference/functions/tools.archiveNote.html +15 -0
  70. package/docs/api-reference/functions/tools.assertHnswModelMatchesEmbedder.html +13 -0
  71. package/docs/api-reference/functions/tools.chatThreadAppend.html +22 -0
  72. package/docs/api-reference/functions/tools.chatThreadRead.html +16 -0
  73. package/docs/api-reference/functions/tools.contextPack.html +21 -0
  74. package/docs/api-reference/functions/tools.createNote.html +19 -0
  75. package/docs/api-reference/functions/tools.dataviewQuery.html +16 -0
  76. package/docs/api-reference/functions/tools.embeddingsSearch.html +40 -0
  77. package/docs/api-reference/functions/tools.findPath.html +23 -0
  78. package/docs/api-reference/functions/tools.findSimilar.html +21 -0
  79. package/docs/api-reference/functions/tools.frontmatterGet.html +15 -0
  80. package/docs/api-reference/functions/tools.frontmatterSearch.html +16 -0
  81. package/docs/api-reference/functions/tools.frontmatterSet.html +19 -0
  82. package/docs/api-reference/functions/tools.getBacklinks.html +15 -0
  83. package/docs/api-reference/functions/tools.getNoteNeighbors.html +16 -0
  84. package/docs/api-reference/functions/tools.getOpenQuestions.html +19 -0
  85. package/docs/api-reference/functions/tools.getOutboundLinks.html +16 -0
  86. package/docs/api-reference/functions/tools.getRecentEdits.html +14 -0
  87. package/docs/api-reference/functions/tools.getUnresolvedWikilinks.html +14 -0
  88. package/docs/api-reference/functions/tools.getVaultStats.html +13 -0
  89. package/docs/api-reference/functions/tools.lintWiki.html +20 -0
  90. package/docs/api-reference/functions/tools.listCanvases.html +16 -0
  91. package/docs/api-reference/functions/tools.listNotes.html +19 -0
  92. package/docs/api-reference/functions/tools.listPdfs.html +15 -0
  93. package/docs/api-reference/functions/tools.listTags.html +14 -0
  94. package/docs/api-reference/functions/tools.ocrPdf.html +18 -0
  95. package/docs/api-reference/functions/tools.openInUi.html +17 -0
  96. package/docs/api-reference/functions/tools.paperAudit.html +16 -0
  97. package/docs/api-reference/functions/tools.pickEmbedTextForHyde.html +8 -0
  98. package/docs/api-reference/functions/tools.readCanvas.html +19 -0
  99. package/docs/api-reference/functions/tools.readNote.html +20 -0
  100. package/docs/api-reference/functions/tools.readPdf.html +18 -0
  101. package/docs/api-reference/functions/tools.renameNote.html +30 -0
  102. package/docs/api-reference/functions/tools.replaceInNotes.html +20 -0
  103. package/docs/api-reference/functions/tools.resolveTarget.html +24 -0
  104. package/docs/api-reference/functions/tools.resolveWikilink.html +20 -0
  105. package/docs/api-reference/functions/tools.searchHybrid.html +62 -0
  106. package/docs/api-reference/functions/tools.searchText.html +19 -0
  107. package/docs/api-reference/functions/tools.semanticSearch.html +19 -0
  108. package/docs/api-reference/functions/tools.validateNoteProposal.html +19 -0
  109. package/docs/api-reference/hierarchy.html +1 -0
  110. package/docs/api-reference/index.html +1 -0
  111. package/docs/api-reference/interfaces/index.ServeOptions.html +74 -0
  112. package/docs/api-reference/interfaces/index.ServerDeps.html +27 -0
  113. package/docs/api-reference/interfaces/tool-manifest.ToolManifestEntry.html +33 -0
  114. package/docs/api-reference/interfaces/tools.ArchiveNoteArgs.html +12 -0
  115. package/docs/api-reference/interfaces/tools.BacklinkHit.html +15 -0
  116. package/docs/api-reference/interfaces/tools.CanvasEdge.html +19 -0
  117. package/docs/api-reference/interfaces/tools.CanvasSummary.html +16 -0
  118. package/docs/api-reference/interfaces/tools.ChatThreadAppendArgs.html +10 -0
  119. package/docs/api-reference/interfaces/tools.ChatThreadMessage.html +14 -0
  120. package/docs/api-reference/interfaces/tools.ChatThreadReadResult.html +10 -0
  121. package/docs/api-reference/interfaces/tools.ContextPackArgs.html +12 -0
  122. package/docs/api-reference/interfaces/tools.ContextPackResult.html +20 -0
  123. package/docs/api-reference/interfaces/tools.EmbedHit.html +21 -0
  124. package/docs/api-reference/interfaces/tools.EmbedSearchResponse.html +14 -0
  125. package/docs/api-reference/interfaces/tools.FindPathResult.html +17 -0
  126. package/docs/api-reference/interfaces/tools.FrontmatterSearchArgs.html +20 -0
  127. package/docs/api-reference/interfaces/tools.FrontmatterSetArgs.html +13 -0
  128. package/docs/api-reference/interfaces/tools.HnswSearchContext.html +21 -0
  129. package/docs/api-reference/interfaces/tools.LintWikiArgs.html +14 -0
  130. package/docs/api-reference/interfaces/tools.LintWikiFinding.html +14 -0
  131. package/docs/api-reference/interfaces/tools.LintWikiResult.html +9 -0
  132. package/docs/api-reference/interfaces/tools.NoteNeighbors.html +17 -0
  133. package/docs/api-reference/interfaces/tools.NoteReadFull.html +20 -0
  134. package/docs/api-reference/interfaces/tools.NoteReadMap.html +25 -0
  135. package/docs/api-reference/interfaces/tools.NoteSummary.html +14 -0
  136. package/docs/api-reference/interfaces/tools.OcrPdfArgs.html +16 -0
  137. package/docs/api-reference/interfaces/tools.OcrPdfPage.html +15 -0
  138. package/docs/api-reference/interfaces/tools.OcrPdfResult.html +18 -0
  139. package/docs/api-reference/interfaces/tools.OpenInUiResult.html +11 -0
  140. package/docs/api-reference/interfaces/tools.OpenQuestion.html +20 -0
  141. package/docs/api-reference/interfaces/tools.OutboundLink.html +20 -0
  142. package/docs/api-reference/interfaces/tools.PaperAuditFinding.html +17 -0
  143. package/docs/api-reference/interfaces/tools.PathStep.html +9 -0
  144. package/docs/api-reference/interfaces/tools.PdfSummary.html +9 -0
  145. package/docs/api-reference/interfaces/tools.ReadCanvasResult.html +15 -0
  146. package/docs/api-reference/interfaces/tools.ReadPdfArgs.html +8 -0
  147. package/docs/api-reference/interfaces/tools.ReadPdfPage.html +13 -0
  148. package/docs/api-reference/interfaces/tools.ReadPdfResult.html +18 -0
  149. package/docs/api-reference/interfaces/tools.RenameNoteResult.html +14 -0
  150. package/docs/api-reference/interfaces/tools.RenameProposal.html +13 -0
  151. package/docs/api-reference/interfaces/tools.ReplaceInNotesArgs.html +15 -0
  152. package/docs/api-reference/interfaces/tools.ReplaceInNotesFileResult.html +6 -0
  153. package/docs/api-reference/interfaces/tools.ReplaceInNotesResult.html +21 -0
  154. package/docs/api-reference/interfaces/tools.SearchHit.html +16 -0
  155. package/docs/api-reference/interfaces/tools.SearchHybridHit.html +30 -0
  156. package/docs/api-reference/interfaces/tools.SearchHybridResponse.html +23 -0
  157. package/docs/api-reference/interfaces/tools.SearchResponse.html +13 -0
  158. package/docs/api-reference/interfaces/tools.SemanticHit.html +15 -0
  159. package/docs/api-reference/interfaces/tools.SimilarNote.html +15 -0
  160. package/docs/api-reference/interfaces/tools.TagSummary.html +13 -0
  161. package/docs/api-reference/interfaces/tools.UnresolvedWikilink.html +22 -0
  162. package/docs/api-reference/interfaces/tools.ValidateProposalArgs.html +10 -0
  163. package/docs/api-reference/interfaces/tools.ValidateProposalResult.html +14 -0
  164. package/docs/api-reference/interfaces/tools.VaultStats.html +26 -0
  165. package/docs/api-reference/modules/index.html +1 -0
  166. package/docs/api-reference/modules/tool-manifest.html +1 -0
  167. package/docs/api-reference/modules/tools.html +1 -0
  168. package/docs/api-reference/types/tools.CanvasNode.html +7 -0
  169. package/docs/api-reference/types/tools.SearchMode.html +7 -0
  170. package/docs/api-reference/variables/index.VERSION.html +9 -0
  171. package/docs/api-reference/variables/tool-manifest.TOOL_MANIFEST.html +1 -0
  172. package/docs/api.md +1 -1
  173. package/docs/benchmarks.md +9 -5
  174. package/package.json +3 -2
package/CHANGELOG.md CHANGED
@@ -2,6 +2,250 @@
2
2
 
3
3
  All notable changes to this project will be documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
4
4
 
5
+ ## [3.7.18] — 2026-05-19
6
+
7
+ > **TL;DR:** Round-20 external full audit response — 7th independent external audit since v3.6.0, on the v3.7.11 codebase (6 releases behind current main). Most findings already closed by the v3.7.12 → v3.7.17 cascade (11/18 from §2 of the audit report). This patch closes **5 still-open findings** the auditor identified + **closes the OIA blind spots** v3.7.17 shipped — round-20 found 3 NEW classes my v3.7.17 OIA missed (regex too narrow for list-format subcommand claims, OIA only walked `src/` not `docs/` headers, no shell-script staleness check). All 3 gaps now closed in the OIA script.
8
+
9
+ **Patch — round-20 stale-fragment cleanup + OIA recursion fix.**
10
+
11
+ ### Stale-fragment fixes (5)
12
+
13
+ | ID | File | Issue | Fix |
14
+ |----|------|-------|-----|
15
+ | **R-2** | `docs/api.md:5` | "`/ install-model / ... / eval / bench` subcommands" — `bench` is not a CLI subcommand, only `npm run bench:retrieval` | Removed `bench` from the list; added explicit note that benchmarks are an npm script. **OIA gap:** my v3.7.17 OIA matched only `enquire-mcp <cmd>` pattern; missed the list-format `/ A / B / C subcommands.` pattern. Fixed in OIA (new check `CLI-SUBCMD-MISSING-LIST`). |
16
+ | **B-1** | `docs/benchmarks.md:3` | "Last updated: ... (v3.7.0)" — header drifted across 4 latency re-measurements (v3.7.10 / v3.7.13 / v3.7.18) | Rewrote the "Last updated" line to enumerate the actual re-measurement / methodology updates. **OIA gap:** my v3.7.17 OIA only scanned `src/**.ts` first-30-lines for currency claims. Extended to `docs/*.md` first-10-lines (new check `STALE-CURRENCY-CLAIM-DOC`). |
17
+ | **B-3** | `docs/benchmarks.md:275` | "+5.1 NDCG@10 over FS-grep at **4900×** the latency" — but `bench/benchmarks.json` shows 1028 ms / 0.14 ms = **~7,300×** | Recomputed against actual numbers from the JSON. Annotated the recalculation with provenance + the historical 4900× figure for traceability. |
18
+ | **S-C1** | `scripts/post-public-setup.sh` | Bootstrap script from v0.3.x repo-public flip; still required dropped `test (20)` CI gate, didn't require the `docs` gate (added v3.7.10) — running it today would DOWNGRADE branch protection | Replaced contents with a DEPRECATED stub that explains the history + `exit 1` guard. |
19
+ | **S-C2** | `scripts/repo-setup.sh` | One-shot bootstrap from v0.3.1 — hardcoded v0.3.1 description / topics / release; would CLOBBER current marketing surface (gated by `tests/github-metadata-invariant.test.ts`) | Same treatment: DEPRECATED stub + `exit 1` guard. |
20
+
21
+ ### Real correctness fix (1)
22
+
23
+ - **R-8** — `FtsIndex.dropFile()` was NOT wrapped in `db.transaction()` (`src/fts5.ts:351`). Two independent DELETE statements (`chunks` then `source_state`) could partially apply on crash / SIGKILL / DB-lock contention, leaving `source_state` indicating "file is indexed at mtime X" while `chunks` has no rows — causing the next watcher event to skip re-indexing (state matches) but search to miss the file (no chunks). Sibling of v3.7.10 audit-#10 fix that wrapped `reindexFile` / `reindexPdfFile` / `source_state` updates in `db.transaction()` for the same reason. Fix: wrap both DELETEs in `db.transaction()`. Round-20 external auditor caught this as Class B (Non-transactional DB mutations) — that class has now had three full sweeps (v3.7.10 / v3.7.11 / v3.7.18); R-8 was the last open instance.
24
+
25
+ ### OIA recursion meta-fix (3 new checks)
26
+
27
+ The v3.7.17 OIA script closed the change-driven vs state-driven methodology gap — but round-20 found 3 stale fragments that v3.7.17's OIA STILL missed. This is the predicted **OIA recursion** (each external audit reveals MORE state-driven classes I didn't enumerate). v3.7.18 closes the round-20 gaps:
28
+
29
+ 1. **`CLI-SUBCMD-MISSING-LIST`** (new) — detects list-format subcommand claims like `\`A\` / \`B\` / \`C\` subcommands.`. Heuristic: line contains the literal word "subcommand" AND ≥2 backticked tokens — each backticked hyphen-token treated as a subcommand claim to verify.
30
+
31
+ 2. **`STALE-CURRENCY-CLAIM-DOC`** (new) — extends `STALE-CURRENCY-CLAIM` from `src/*.ts` to `docs/*.md` first-10-lines (excluding `docs/audits/`). Catches "Last updated: ... (v3.7.0)" type drift in benchmark/comparison/quickstart headers.
32
+
33
+ 3. **`SHELL-SCRIPT-STALE`** (new) — walks `scripts/*.sh` and flags files that reference `v0.X.Y` or `test (20|18|16|14)` AND don't have a DEPRECATED / `exit 1` guard at the top. Catches one-time-bootstrap scripts that drifted out of currency.
34
+
35
+ After v3.7.18: `npm run check:oia` returns clean across all 7 checks (`STALE-CURRENCY-CLAIM`, `STALE-CURRENCY-CLAIM-DOC`, `WORKFLOW-CLAIM-WITHOUT-EVIDENCE`, `CLI-SUBCMD-MISSING`, `CLI-SUBCMD-MISSING-LIST`, `NPM-SCRIPT-MISSING`, `SHELL-SCRIPT-STALE`, `STALE-DEFAULT-CLAIM`).
36
+
37
+ ### Methodology lesson (the deeper finding)
38
+
39
+ **OIA recursion is structural, not anecdotal.** v3.7.17 added 5 OIA checks based on round-19's findings. Round-20 found 3 more classes that map to checks v3.7.17 didn't add. The pattern is:
40
+
41
+ > Each external auditor's methodology samples a different subset of the state-driven failure space. No single OIA pass can enumerate all possible classes a priori. The honest framing: OIA closes the SPECIFIC blind spots prior audits revealed — not "all" state-driven blind spots forever.
42
+
43
+ This isn't a flaw in OIA; it's the nature of static analysis vs human reading. The mitigation is: every external audit response release should EXTEND OIA with that round's new check classes. The OIA script is a growing catalog of historically-found drift patterns, not a complete one.
44
+
45
+ **Rule (extended in this release):** when responding to round-N audit, add OIA checks for each NEW class the auditor found that's automatable. v3.7.18 adds 3 checks from round-20.
46
+
47
+ ### Auditor findings VERIFIED as already closed (11)
48
+
49
+ Per §2 of the round-20 report, these were closed by v3.7.6 → v3.7.17 before round-20 ran:
50
+
51
+ | # | Finding | Closed in |
52
+ |---|---------|-----------|
53
+ | 1 | README badge v3.6.x → v3.7.x | v3.7.8 positioning calibration |
54
+ | 4 | examples not in npm | v3.7.11 |
55
+ | 5–6 | K-1, K-2 | v3.7.5 (external audit #4) |
56
+ | 12 | release docs gate | v3.7.10 |
57
+ | 13–16 | DQL, FTS txn, deleteNote txn, benchmark sync | v3.7.10 / v3.7.11 |
58
+ | 17 | COMPARISON tools/prompts gate | v3.7.11 |
59
+ | 18 | QUICKSTART version 3.6.1 → 3.7.x | v3.7.12 L2 |
60
+ | 10 | engines >=20 → >=22.13.0 | v3.7.13 H3 |
61
+ | 11 | CodeQL "claim without evidence" | v3.7.17 E (default-setup annotation) |
62
+ | R-14 | CLAUDE.md sprint v3.6.0 / 712+ tests | v3.7.17 B |
63
+
64
+ ### Architectural findings deferred to v3.8.0
65
+
66
+ Round-20 confirmed (no change from prior audits):
67
+
68
+ - **R-3** — `serve-http` missing 8 serve-only flags (`--include-pdfs`, `--enable-reranker`, `--reranker-model`, `--reranker-top-n`, `--use-hnsw`, `--hnsw-ef`, `--late-chunk-context`, `--no-hnsw-persist`). Solution: shared `addServeOptions()` + CLI-parity invariant test. Multi-day refactor.
69
+ - **R-7** — watcher doesn't sync embed-db on .md changes. PDF sync closed v3.7.16 P1-5; embed-db requires ML pipeline in watcher process.
70
+ - **R-9** — Chunk MCP resource lacks `resolveSafePath` normalization.
71
+ - **K-3** — generalized `readOnlyHint: true` → no destructive operations invariant.
72
+ - **4/5 reranker E2E** — `ENQUIRE_LOAD_RERANKER_SMOKE=1` opt-in only; not in default CI (model weights ~110 MB).
73
+ - **R-4 / R-15** — `context_pack` soft budget + missing HNSW in ctx.
74
+ - **R-6** — `bootstrapSchema` DROP+CREATE not in single transaction (cross-database migration; needs careful design).
75
+ - **R-10** — HNSW + privacy under-return.
76
+ - **T-1 to T-5** — test coverage gaps (`contextPack`, `get_communities` handler, `hyde_search` E2E, `serve-http` E2E in cli.test, github-metadata no-op).
77
+ - **V-1** — no CI diff between `bench/benchmarks.json` and `docs/benchmarks.md`.
78
+ - **V-2** — PR template doesn't enumerate CI gates.
79
+ - **B-2** — benchmarks methodology section may still have stale narrative latencies (v3.7.13 M11 dropped the duplicate latency column; partial fix).
80
+ - **CHANGELOG 469 KB** — historical noise; open by design.
81
+
82
+ ### Stats
83
+
84
+ - **818 tests** (unchanged). v3.7.18 is documentation + correctness + tooling; no new code paths to test (R-8 transaction is structural, not test-surfaced).
85
+ - **+3 OIA checks** added: `CLI-SUBCMD-MISSING-LIST`, `STALE-CURRENCY-CLAIM-DOC`, `SHELL-SCRIPT-STALE`.
86
+ - **All 8 required CI gates pass locally.**
87
+
88
+ ### Method note
89
+
90
+ 7 external audit cycles processed in 5 days. The cascade is now self-sustaining: external audits find what internal sweeps miss → OIA gains the new check class → next external audit's blind spots get smaller. The auditor's §0 verdict ("аудит достаточно глубокий для open-source MCP") signals diminishing returns of further static-code-only audits; the outstanding work is in runtime/scale/ML-E2E surfaces (already in v3.8 backlog).
91
+
92
+ ## [3.7.17] — 2026-05-19
93
+
94
+ > **TL;DR:** Round-19 external audit response — 6th independent external audit since v3.6.0, on the v3.7.5 codebase (4 days behind current main). Most findings were already closed by the v3.7.6 → v3.7.16 cascade; this patch addresses 5 **cheap stale-fragment findings** the auditor caught that my internal class-sweeps had missed (A: `src/index.ts` file-header reading as if 3.6.0-rc.2 is current, B: `CLAUDE.md` title + quality bar still saying "v3.6.0 sprint" + "712+ tests", C: `scripts/check-changelog-coverage.mjs` referencing non-existent `npm run check:coverage-drift`, D: `embeddings.ts` inline comment claiming `rerank-multilingual` is the default since v3.6.1 it's actually `rerank-bge`, E: README CI claim about CodeQL ×2 + Analyze actions — they DO run via GitHub default-setup, but the README didn't say so, looking like a workflow-file claim without evidence). **PLUS a meta-methodology fix:** the new `scripts/oia-walk.mjs` automates 5 state-driven outside-in checks that my change-driven sweeps systematically miss. **New CLAUDE.md rule** mandates `npm run check:oia` before claiming "no open audit items" in any release.
95
+
96
+ **Patch — round-19 stale-fragment cleanup + Outside-In Audit (OIA) methodology fix.**
97
+
98
+ ### Cheap stale-fragment fixes (5)
99
+
100
+ | # | Finding | File | Resolution |
101
+ |---|---------|------|-----------|
102
+ | A | `// Slim entry point for enquire-mcp. Version 3.6.0-rc.2 split the previous 3665-line monolith` — reads as currency claim | `src/index.ts:2` | Rewrote header to explicitly mark as History: rc.2 split, with current state separately. Same module description preserved as historical context. |
103
+ | B | `# Project goal — v3.6.0 sprint` + `1. 712+ tests pass` — CLAUDE.md title locked at v3.6.0 sprint despite cascade running to v3.7.17 | `CLAUDE.md:1, 23` | Title now `v3.7.x maintenance + v3.8.0 architectural`; v3.6.0 sprint preserved as historical context section; test count claim re-phrased to "All tests pass (current count: 818+ at v3.7.17)" so future test additions don't require a CLAUDE.md edit. |
104
+ | C | Docstring references `npm run check:coverage-drift` — never existed | `scripts/check-changelog-coverage.mjs:26` | Pointed to the real `npm run check:changelog-coverage`. The reference was a "would-be name" the original author never created. |
105
+ | D | Inline comment near `rerank-multilingual-large` says `(rerank-multilingual default)` — but `DEFAULT_RERANKER_ALIAS = "rerank-bge"` since v3.6.1 CRIT-2 | `src/embeddings.ts:306` | Expanded comment to clarify `rerank-multilingual` is the MULTILINGUAL variant, NOT the project-wide default, and cross-referenced `DEFAULT_RERANKER_ALIAS`. |
106
+ | E | README CI gates row claims `CodeQL ×2 · Analyze actions` but no matching `.github/workflows/*.yml` — auditor flagged as "claim without evidence" | `README.md:185` | Annotated the claim to clarify CodeQL/Analyze run via [GitHub default-setup](https://docs.github.com/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-default-setup-for-code-scanning) (the workflow-file route is one path; default-setup is the other). Both routes produce check-runs visible in `gh pr checks`. |
107
+
108
+ ### Outside-In Audit (OIA) methodology fix
109
+
110
+ **The meta-finding from round-19:** every external auditor since v3.6.0 has found cheap stale fragments that my internal class-sweeps systematically miss. Root cause: my methodology is **change-driven** (look at what changed, fix the class, verify nearby) while external audits are **state-driven** (read every file as it exists today, verify each claim against reality). These find non-overlapping failure modes.
111
+
112
+ **My methodology's specific blind spots** (catalogued from the 6 external audit cycles):
113
+
114
+ 1. **Recency bias** — files I haven't edited in N releases fall out of attention. `src/index.ts:2` was never touched in v3.7.x.
115
+ 2. **Trust my own ledger** — CHANGELOG says "v3.7.13 H3 bumped engines.node"; I treat as canonical without grep-verifying every file.
116
+ 3. **Class-pattern sweeps assume the class is in my mental model** — I sweep TOCTOU, TSDoc drift, hardcoded counts. I didn't enumerate "claim about CI workflows that don't exist" or "CLI subcommand referenced in docs but not in cli.ts" as classes until the auditor named them.
117
+ 4. **Pre-merge RCA sweep (v3.7.15 rule) is narrow** — it sweeps the patch's diff for class siblings. Doesn't sweep stale fragments NOT touched by this PR.
118
+ 5. **Marketing copy = read-only** — I update gated counts but don't routinely diff hero / tagline / badge against reality.
119
+ 6. **File-level vs function-level docstrings** — round-18 catch was function-level; file-level (first 30 lines) almost never re-read.
120
+ 7. **Project meta-docs not walked** — I update bodies (e.g. CLAUDE.md "Current phase status") but never re-read titles.
121
+ 8. **Utility scripts = black box** — I *run* `scripts/check-*.mjs`; I don't *read* their docstrings.
122
+ 9. **Historical vs current inline comments** — tombstones (`// v3.6.1 CRIT-2 — was X`) are legitimate history. Current-claim comments (`(rerank-multilingual default)`) drift silently. I don't distinguish.
123
+ 10. **Internal mental model becomes the lens** — I have a model of "current project state" (3.7.17, 818 tests, BGE default). When I look at code, I VALIDATE its conformance to my model. If code drifted but the model didn't, I can't see the drift.
124
+
125
+ **Solution: `scripts/oia-walk.mjs`** (`npm run check:oia`). Five state-driven walks that automate what external auditors do manually:
126
+
127
+ 1. **STALE-CURRENCY-CLAIM** — scan first 30 lines of every `src/**/*.ts` for currency-claim patterns ("Version X.Y.Z", "rc.N", "alpha.N", "beta.N") not marked as historical (History:, Pre-, was, since, previously). Refined heuristic after a noisy first pass that flagged 21 legitimate tombstones — now only flags claims of currency, not feature-history notes.
128
+ 2. **WORKFLOW-CLAIM-WITHOUT-EVIDENCE** — README claims about CI workflows (CodeQL, Analyze) must have either a matching `.github/workflows/*.yml` OR a "default-setup" annotation.
129
+ 3. **CLI-SUBCMD-MISSING** — every `enquire-mcp <cmd>` reference in `docs/*.md` (excluding `docs/audits/`) must match a `program.command("<cmd>")` in `src/cli.ts`.
130
+ 4. **NPM-SCRIPT-MISSING** — every `npm run <script>` reference in `docs/*.md` and `scripts/*.mjs` docstrings must match `package.json#scripts`.
131
+ 5. **STALE-DEFAULT-CLAIM** — heuristic match: inline comments claiming `"X is the default"` are cross-checked against exported `DEFAULT_*` constants in the same file.
132
+
133
+ **New CLAUDE.md rule (added in this patch):** *"Internal change-driven sweeps miss state-driven failure modes — run OIA before claiming 'no open findings'."* Sits alongside the v3.7.15 "pre-merge RCA sweep" rule. Together they cover both inside-out (RCA on the patch's classes) and outside-in (OIA on the whole repo) audit dimensions.
134
+
135
+ ### Architectural findings still OPEN at v3.7.17 (round-19 audit)
136
+
137
+ These are documented v3.8+ deferrals — the round-19 auditor found them, and they remain open by design until the next minor:
138
+
139
+ - **K-3 — Generalized `readOnlyHint: true` → no destructive operations invariant.** Per v3.7.5 CHANGELOG. Closes the K-1 sibling class structurally.
140
+ - **Watcher doesn't sync embed-db on .md changes.** P1-5 (v3.7.16) closed PDF lifecycle in watcher; embed-db sync requires the embedder pipeline in the watcher process (architectural — adds ML deps to the watcher path).
141
+ - **No per-file mutex in watcher.** Parallel chokidar events for the same file race; last-write-wins. Acceptable for now but not for high-frequency vault changes.
142
+ - **CHANGELOG 469 KB** — historical noise. Open by design. Periodic compaction TBD.
143
+ - **`docs/audits/*`** — internal finding notes; some have `fixed` status in the file body that doesn't match overall file framing. Excluded from npm tarball since v3.7.13 L7.
144
+ - **Audit level inconsistency** — PR CI: `npm audit --audit-level=moderate` for prod deps; release.yml: `--audit-level=high`. Architectural choice — should be unified or explicitly documented.
145
+
146
+ ### Stats
147
+
148
+ - **818 tests** (unchanged; v3.7.17 is a documentation + stale-fragment patch, no new code-paths to test).
149
+ - **+0 docs-consistency invariants** added (the OIA script's STALE-DEFAULT-CLAIM check covers the heuristic; structural invariants are added incrementally when patterns recur).
150
+ - **+1 new gate script**: `npm run check:oia` (advisory in this release; required CI gate scheduled for v3.7.18 once it's been validated in production for one release cycle).
151
+ - All 8 required CI gates pass locally.
152
+
153
+ ### Method
154
+
155
+ Round-19 closes the most important methodological gap of the v3.6.0 → v3.7.17 cascade: **external audits will always find what internal audits miss UNLESS the internal methodology explicitly includes state-driven walks.** Pre-3.7.17 my methodology was 100% change-driven (class sweeps, TSDoc verification on touched files, pre-merge RCA on the patch diff). Adding `oia-walk.mjs` + the CLAUDE.md rule introduces the missing state-driven dimension. **Net effect: round-20 should find significantly fewer stale fragments — if it still does, the OIA script needs another check pattern added.**
156
+
157
+ ## [3.7.16] — 2026-05-18
158
+
159
+ > **TL;DR:** Round-18 external audit response — 5th independent external audit since v3.6.0, run on the v3.7.5 codebase (commit `b9daf39`). The cascade v3.7.6 → v3.7.15 had already closed many findings; this patch addresses the still-open critical + high-impact items. **10 fixes** landed: 5 P1 High (OCR network disclosure + 200-page cap, persistent cache + privacy filters, PDF watcher lifecycle, macOS case-insensitive write bypass), 3 P2 Medium (title-based write ambiguity = silent data corruption, validateNoteProposal privacy check, FTS5 tag LIKE wildcard escape), 2 P3 Low (PDF install hint version, FTS5 reserved-word quoting, issue template ChatGPT). Plus 1 graph-boost path bug (P2-16, C# Notes.md). **+0 tests** (1 test contract change for P3-28). 4 architectural findings deferred to v3.8.0 (serve-http parity, FTS5/embedding chunking parity, search underfill, OCR timeout/concurrency).
160
+
161
+ **Patch — round-18 external audit batch.**
162
+
163
+ ### Already FIXED by the v3.7.6-15 cascade
164
+
165
+ The audit was on v3.7.5 — these findings were closed before this patch:
166
+
167
+ - **#9 FTS5 reindex non-atomic** → v3.7.10 audit #10 (transaction wrap)
168
+ - **#17 TOOL_MANIFEST not in exports** → v3.7.12 H4
169
+ - **#19 7 required CI gates** → v3.7.13 M5 (bumped to 8)
170
+ - **#22 stale test counts (786/670)** → v3.7.11-15 cascade (now gated at 816)
171
+ - **#24 reranker "5 models" misleading** → v3.7.12 L4 + v3.7.15 R17-3
172
+ - **#19 partial** "CodeQL/Analyze workflows don't exist" — actually false positive: those run via GitHub Default Setup (no committed .yml needed); they DO execute and show on every PR
173
+
174
+ ### P1 High fixes (5)
175
+
176
+ - **P1-1 — OCR Tesseract network policy disclosure** (`src/ocr.ts:147+`, `SECURITY.md`). Tesseract.js fetches `<lang>.traineddata` (~10 MB per language) from a CDN on first use — this is the only outbound network call possible in serve mode, contradicting the broader "zero outbound network calls" framing. Pre-3.7.16 the project's privacy-promise didn't disclose this. Fix: stderr disclosure warning fires once per OCR worker creation (`enquire OCR: Tesseract.js may fetch language pack '<lang>' from a CDN on first use...`), plus new SECURITY.md section "OCR (`obsidian_ocr_pdf`): network posture" documenting the trade-off and the v3.8.0 roadmap (offline `install-ocr-lang` subcommand + strict cache check).
177
+ - **P1-2 — OCR `DEFAULT_OCR_MAX_PAGES` safety cap** (`src/ocr.ts`). Pre-3.7.16 a single bearer-authenticated HTTP request could trigger unbounded OCR on a 10000-page PDF — minutes-to-hours of CPU pegged. Now defaults to 200 pages per call (`maxPages` opt to override), checked BEFORE the Tesseract worker spins up so no resources allocate on adversarial inputs.
178
+ - **P1-4 — Persistent cache + privacy filter drift** (`src/vault.ts` `loadDiskCache`). Pre-3.7.16, if a user filled the cache with all notes (no filter), then added `--exclude-glob "Personal/**"` on the next start, the excluded note bodies were loaded back from disk into the in-memory cache (and rewritten on the next save). At-rest privacy boundary broken across filter changes. Now `loadDiskCache` runs every candidate through `isExcluded()` against the LIVE filter state; misses are dropped and `cacheDirty` is set so the next save persists the pruned snapshot. Stderr disclosure when entries are dropped by privacy.
179
+ - **P1-5 — `--watch --include-pdfs` PDF watcher lifecycle** (`src/watcher.ts`, `src/server.ts`). Pre-3.7.16 the watcher hard-coded `.md`-only event handling, so PDFs added/changed/deleted/moved during a serve session left stale rows in FTS5 until restart. New `WatcherOptions.includePdfs`; when on AND `--include-pdfs` is on, the watcher dispatches `.pdf` events to `FtsIndex.reindexPdfFile()` / `dropFile()` via lazy `import("./pdf.js")`.
180
+ - **P1-6 — macOS / Windows case-insensitive write privacy bypass** (`src/vault.ts` `writeNote` + `renameFile`). Default macOS HFS+/APFS and Windows NTFS are case-insensitive. Pre-3.7.16 the write-path privacy check ran on the LEXICAL relative path (`personal/secret.md` ≠ `Personal/secret.md` to a case-sensitive `--exclude-glob`), but the actual write landed in the same physical directory. Fix: new private `canonicalRelForPrivacyCheck()` helper walks up from the target until it finds an existing parent, resolves its realpath (canonical case from disk), then re-joins the not-yet-existing tail. Linux ext4 unaffected (realpath is a no-op for case-sensitive FS).
181
+
182
+ ### P2 Medium fixes (4)
183
+
184
+ - **P2-13 — Title-based write silent data corruption** (`src/vault.ts` + `src/tools/write.ts`). Pre-3.7.16 `findByTitle(title)` returned the first walk-order match; write tools (`appendToNote`, `frontmatterSet`) silently mutated whichever of `Work/Daily.md` / `Personal/Daily.md` came first. New `Vault.findAllByTitle()` + write-path now throws on `length > 1` with candidate paths listed: `Ambiguous title "Daily" — 2 notes share that basename: Personal/Daily.md, Work/Daily.md. Pass an explicit \`path\` argument instead.` Read tools keep the permissive behavior (no mutation risk).
185
+ - **P2-14 — `validateNoteProposal` privacy check** (`src/tools/meta.ts`). Pre-3.7.16 the pre-write validator returned green-light for proposals into excluded destinations; the actual write later failed at runtime. New `path-excluded` error class returned by the validator when `vault.exclusionReason(normalizedPath)` is non-null.
186
+ - **P2-15 — FTS5 tag LIKE wildcard injection** (`src/fts5.ts:482`). Pre-3.7.16 `where.push("(',' || chunks.tags || ',') LIKE ?")` accepted user-supplied tags containing `%` (matches anything) or `_` (matches one char). `tag: "%"` returned every chunk; `tag: "core_team"` matched `coreXteam`. Fix: escape `%`, `_`, and `\` with `\\`, add `ESCAPE '\\'` clause.
187
+ - **P2-16 — Graph boost path mangling on `#`** (`src/tools/search.ts:1456,1493`). Pre-3.7.16 `f.id.split("#")[0]` mangled `C# Notes.md` → `C` (drops the suffix as if it were a chunk index). Now uses regex `id.replace(/#\d+$/, "")` that only strips numeric chunk suffixes at end of id.
188
+
189
+ ### P3 Low fixes (3)
190
+
191
+ - **P3-23 — PDF install hint version** (`src/pdf.ts`, `src/ocr.ts`, `src/doctor.ts`). The "missing optional dep" error message told users to install `pdfjs-dist@^4.10.38` but the actual `optionalDependencies` pin is `^5.7.284`. Now consistent across all 3 surfaces.
192
+ - **P3-28 — `safeFts5Query` reserved words** (`src/fts5.ts:581+`). Pre-3.7.16 `safeFts5Query("operating systems AND databases")` STRIPPED `AND`, dropping a real query term silently. AND there was no way to search for the literal word "AND". Now reserved words (`AND`/`OR`/`NOT`/`NEAR`) get double-quoted as literals — FTS5 treats them as the literal token, not the boolean operator. The existing test was updated to reflect the new contract.
193
+ - **P3-30 — Issue template ChatGPT option** (`.github/ISSUE_TEMPLATE/bug_report.yml`). Added "Claude Desktop" + "ChatGPT (custom GPT / Actions)" to the client dropdown (both were missing despite heavy marketing).
194
+
195
+ ### Audit findings DEFERRED to v3.8.0
196
+
197
+ - **P1-3 serve-http flag parity** (`--enable-reranker` / `--use-hnsw` / `--include-pdfs` etc. missing from `serve-http`). Multi-day refactor; on the v3.8.0 backlog since round-15 M4.
198
+ - **#2 OCR concurrency / timeout** (per-call timeout, concurrent-request cap, HTTP-transport operation budget). Beyond the 200-page cap from P1-2.
199
+ - **P2-7 search underfill** after privacy filter post-processing (iterative over-fetch design change).
200
+ - **P2-8 FTS5/embedding chunking source mismatch** (standardize on parsed body — design change).
201
+ - **P2-10 stateful maxSessions race** (concurrent initialize past cap — needs slot-reservation design).
202
+ - **P2-11 HTTP server close cleanup** (centralized cleanup on `httpServer.close()`).
203
+ - **P2-12 doctor privacy filters** (add `--exclude-glob` / `--read-paths` to doctor).
204
+ - **P2-18 npm package docs broken links** (audit suggested either include `examples/bench/scripts` or rewrite docs to GitHub absolute URLs — partially handled by v3.7.13 L7 + v3.7.14 file-list explicit listing; needs follow-up sweep).
205
+ - **P2-20 canonical CLI docs incomplete** (reranker / HNSW / PDF / late-chunking / quantization flags missing from `docs/api.md` table).
206
+ - **P3-21 `obsidian_full_text_search` gating wording drift** in cli-help.
207
+ - **P3-25 Heading extraction tilde fences** (`~~~` blocks not detected the same way as backticks).
208
+ - **P3-26 Diagnostic substring search body vs raw** (decide contract — `content` includes frontmatter, docs say "body").
209
+ - **P3-27 HNSW metadata shallow validation** (validate `meta.dim`, `meta.size`, rows shape).
210
+ - **P3-29 setup-snippet mkdir** (`docs/http-transport.md` + `examples/chatgpt-actions.md`).
211
+
212
+ ### Pre-merge RCA sweep (in-release recursion prevention)
213
+
214
+ Per the v3.7.15 rule *"single class-sweep is not enough — same-release recursion happens"*, I ran a post-fix class-sweep BEFORE merging this PR. Found **8 additional issues inside v3.7.16's own diff** + 2 cross-class sibling bugs. All fixed in this same patch:
215
+
216
+ | Class | Finding | Fix |
217
+ |---|---|---|
218
+ | A (TSDoc drift) | `loadDiskCache` header didn't mention P1-4 privacy drop | header updated |
219
+ | A (TSDoc drift) | `writeNote` header didn't mention P1-6 canonical case check | header updated |
220
+ | A (TSDoc drift) | `renameFile` header didn't mention P1-6 (mentioned v3.7.14 F2 but missed P1-6 layer) | header updated |
221
+ | A (TSDoc drift) | `findByTitle` header still claimed "silently pick one" (now P2-13-strict for writes) | cross-ref to `findAllByTitle` + `strictOnAmbiguousTitle` |
222
+ | A (TSDoc drift) | `safeFts5Query` header still claimed "strips reserved keywords" (P3-28 changed to quote-as-literal) | header updated |
223
+ | A (TSDoc drift) | `validateNoteProposal` header didn't list `path-excluded` in errors[].kind enum | header updated |
224
+ | A (TSDoc drift) | `extractPdfWithOcr` header didn't mention P1-1 (network) or P1-2 (200-page) | header updated |
225
+ | C (P1-6 sibling) | `renameNote` wrapper at `tools/write.ts:199` did lexical exclusion-check before delegating to `renameFile` — case-variant bypass wasted O(N) backlink work before the inner canonical check caught it | new `Vault.canonicalRelForPrivacyCheckPublic()`; wrapper now pre-checks canonically |
226
+ | F (P1-2 sibling) | `extractPdfText` (called by `obsidian_read_pdf` without `pages`) had no page-count cap — a 5MB / 2000-page PDF could peg CPU for minutes via HTTP | new `DEFAULT_PDF_MAX_PAGES = 500` cap with same pattern as OCR's `DEFAULT_OCR_MAX_PAGES` |
227
+
228
+ **Classes verified clean (no new findings):**
229
+
230
+ - **Class D** (cache/index privacy lifecycle siblings) — `vault.listMarkdown()` filters via `isExcluded()`, and the FTS5/EmbedDb `diff()` sync marks previously-indexed-now-excluded files as deleted on next sync. Tested by inspection.
231
+ - **Class E** (watcher coverage gaps) — `.canvas` and `.base` files are not FTS5-indexed (no chunks stored), so watcher's `.md`+`.pdf`-only coverage is complete. Only the `--include-pdfs` runtime gap (closed by P1-5) was real.
232
+ - **Class G** (user-input wildcard injection siblings) — other `LIKE` usages (`embed-db.ts:475`) already use `substr` not `LIKE`. RegExp constructions from user input (`new RegExp(args.pattern)` in `getOpenQuestions`, `dql.ts`) are documented as intentional advanced-user features.
233
+ - **Class H** (path/wikilink special-char parsing siblings) — wikilink inner parsing (`split("|")[0]` then `split("#")[0]` then `split("^")[0]` in `validateNoteProposal`) is correct per Obsidian wikilink grammar (`#` IS the section separator INSIDE wikilink inner text, unlike chunk-id parsing for graph boost).
234
+ - **Class I** (title-ambiguity write siblings) — only `appendToNote` and `frontmatterSet` accept `title`; both now use `strictOnAmbiguousTitle: true`. `archiveNote` and `replaceInNotes` require `path` (no title path → no ambiguity surface).
235
+
236
+ ### Stats
237
+
238
+ - **816 tests** (unchanged count). 1 test CONTRACT change for P3-28 (quotes vs strips).
239
+ - **+0 docs-consistency invariants** (P2-13 + P1-6 enforced at runtime).
240
+ - **+10 RCA-driven fixes** discovered + fixed BEFORE merge (8 TSDoc + 1 Class C sibling + 1 Class F sibling).
241
+ - All 8 required CI gates pass locally.
242
+
243
+ ### Method
244
+
245
+ 5th independent external audit since v3.6.0. The "audit on v3.7.5 codebase but processed at v3.7.15 state" pattern is now established as the verification workflow — each external audit finds new failure modes that the previous releases' methodologies didn't cover. Round-18 added the OCR network-policy honesty (new privacy-promise class), persistent-cache filter lifecycle (new at-rest privacy class), and case-insensitive filesystem bypass (new portability class).
246
+
247
+ **Pre-merge RCA sweep is now mandatory** (Rule since v3.7.15 — verified working this release): caught 10 additional findings INSIDE v3.7.16's own diff before they shipped as overclaim #10+. This is the rule firing as designed — same-release class recursion happens, and the only way to prevent it from leaking into production is to scan the patch's own diff for fresh instances of every class it claims to fix.
248
+
5
249
  ## [3.7.15] — 2026-05-18
6
250
 
7
251
  > **TL;DR:** Round-17 post-merge audit on v3.7.14 — **meta-recursion finding**: the v3.7.14 patch that closed overclaim #6 (F1) introduced overclaim **#7 inside its own release**. v3.7.14 F2 fixed `vault.renameFile` internals (stat→link()+unlink()) but left the function's TSDoc header claiming *"Atomic via fs.rename"* — exactly the same class as F1 it had just fixed. Plus 2 unfixed instances of v3.7.12 L4's reranker-honesty class missed in COMPARISON.md. 3 fixes + 1 new invariant. **+1 test** (816 total). Documents the methodological lesson: **a single class-sweep round isn't enough — same-release recursion is a real failure mode and requires post-merge re-sweep.**
package/README.md CHANGED
@@ -11,7 +11,7 @@
11
11
  [![CI](https://github.com/oomkapwn/enquire-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/oomkapwn/enquire-mcp/actions/workflows/ci.yml)
12
12
  [![npm](https://img.shields.io/npm/v/@oomkapwn/enquire-mcp.svg?label=npm&color=cb3837)](https://www.npmjs.com/package/@oomkapwn/enquire-mcp)
13
13
  [![downloads](https://img.shields.io/npm/dm/@oomkapwn/enquire-mcp.svg?color=cb3837)](https://www.npmjs.com/package/@oomkapwn/enquire-mcp)
14
- [![tests](https://img.shields.io/badge/tests-816%20passing-brightgreen.svg)](#trust)
14
+ [![tests](https://img.shields.io/badge/tests-818%20passing-brightgreen.svg)](#trust)
15
15
  [![stable](https://img.shields.io/badge/v3.7.x-stable-brightgreen.svg)](./STABILITY.md)
16
16
  [![SLSA-3](https://img.shields.io/badge/SLSA-3-blue.svg)](https://slsa.dev/spec/v1.0/levels#build-l3)
17
17
  [![MCP](https://img.shields.io/badge/MCP-1.29-8A2BE2.svg)](https://modelcontextprotocol.io/)
@@ -36,7 +36,7 @@ Your Obsidian vault becomes **persistent, queryable long-term memory** for any M
36
36
  > 2. **Best-in-class retrieval.** Hybrid BM25 + multilingual embeddings + BGE cross-encoder reranker fused via RRF, scaled with HNSW + int8 quantization. The same IR stack a search startup would build — open-sourced, in one binary.
37
37
  > 3. **Zero cloud calls during serve.** Models cached locally (one-time download from HuggingFace). Your vault content never leaves your machine. Air-gap-safe by default.
38
38
 
39
- **44 tools · 19 MCP prompts · 816 unit tests · 50+ languages · v3.7.x stable · semver-bound · MIT · SLSA-3 signed.**
39
+ **44 tools · 19 MCP prompts · 818 unit tests · 50+ languages · v3.7.x stable · semver-bound · MIT · SLSA-3 signed.**
40
40
 
41
41
  ---
42
42
 
@@ -112,7 +112,7 @@ Auto-generated **[API reference at oomkapwn.github.io/enquire-mcp](https://oomka
112
112
  | **GraphRAG-light** (wikilink community detection via Louvain modularity) | ✅ **only here** | ❌ | ❌ |
113
113
  | **Standalone `.base` query execution** (works without Obsidian running) | ✅ **only here** | ❌ | ❌ delegates to Obsidian |
114
114
  | **HyDE retrieval** (Gao et al 2023) + sub-question decomposition | ✅ **only here** | ❌ | ❌ |
115
- | **816 unit tests · 8 required + 4 advisory CI gates per PR** | ✅ | n/a | rare |
115
+ | **818 unit tests · 8 required + 4 advisory CI gates per PR** | ✅ | n/a | rare |
116
116
  | **SLSA-3 build provenance** | ✅ | n/a | ❌ |
117
117
  | **Semver-bound public surface** ([STABILITY.md](./STABILITY.md)) | ✅ | n/a | ❌ |
118
118
  | Standalone (no Obsidian plugin needed) | ✅ | ❌ requires Obsidian | varies |
@@ -182,7 +182,7 @@ Plus 3 MCP resources (`obsidian://vault/info`, `obsidian://note/{path}`, `obsidi
182
182
  | **HTTP transport** | Bearer auth (constant-time SHA-256 + `timingSafeEqual`), per-token rate-limit, strict CORS |
183
183
  | **Frontmatter** | `gray-matter` (`js-yaml` safeLoad) — no code execution |
184
184
  | **Cache + index files** | chmod 0600, parent dir 0700 |
185
- | **CI** | **8 required** branch-protection gates (lint · test ×2 [Node 22/24] · smoke · audit · coverage · version-consistency · docs) + **4 advisory** (test-macos · CodeQL ×2 · Analyze actions). Release workflow re-verifies all 8 required passed on tagged SHA before npm publish. _v3.7.10 — `docs` (TypeDoc generation gate) added to required set. v3.7.13 — `engines.node` floor bumped to `>=22.13.0` to match the CI matrix._ |
185
+ | **CI** | **8 required** branch-protection gates (lint · test ×2 [Node 22/24] · smoke · audit · coverage · version-consistency · docs) + **4 advisory** (test-macos via `.github/workflows/ci.yml`; CodeQL ×2 + Analyze actions via [GitHub default-setup](https://docs.github.com/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-default-setup-for-code-scanning) — not workflow files in `.github/workflows/`). Release workflow re-verifies all 8 required passed on tagged SHA before npm publish. _v3.7.10 — `docs` (TypeDoc generation gate) added to required set. v3.7.13 — `engines.node` floor bumped to `>=22.13.0` to match the CI matrix._ |
186
186
  | **Coverage** | Lines ≥86% · statements ≥82% · functions ≥75% · branches ≥74% (gated) |
187
187
  | **Releases** | npm + GitHub release per tag · semver · **SLSA-3** build provenance |
188
188
  | **Stability** | v3.0+ semver-bound — every CLI flag, tool name, MCP resource, prompt, exported symbol is contract |
@@ -222,7 +222,7 @@ Channel: `npm install @oomkapwn/enquire-mcp` → latest stable. Full changelog:
222
222
  ```bash
223
223
  git clone https://github.com/oomkapwn/enquire-mcp.git
224
224
  cd enquire-mcp && npm install
225
- npm test # full suite (816 tests, ~5s)
225
+ npm test # full suite (818 tests, ~5s)
226
226
  npm run lint # zero warnings
227
227
  npm run build # tsc → dist/
228
228
  ```
package/SECURITY.md CHANGED
@@ -156,6 +156,32 @@ The `obsidian_embeddings_search` tool plus the `install-model` and `build-embedd
156
156
  - If `@huggingface/transformers` failed to install (e.g., user ran `npm install --omit=optional`, or the platform lacks ONNX runtime binaries), the embedding tools and subcommands surface a clean error message pointing the user at `npm install @huggingface/transformers` — never a cryptic module-not-found stack trace.
157
157
  - Read-only / TF-IDF / FTS5 surfaces are unaffected. The server starts and serves all v1.x tools normally.
158
158
 
159
+ ## OCR (`obsidian_ocr_pdf`): network posture (v3.7.16 disclosure)
160
+
161
+ The `obsidian_ocr_pdf` tool (v2.10+) uses `tesseract.js` for image-PDF OCR. Tesseract.js's default behavior is to fetch the `<lang>.traineddata` file (~10 MB per language) from `https://tessdata.projectnaptha.com/4.0.0/` on first use of each language.
162
+
163
+ **This is the only outbound network call possible in serve mode** — broader "zero outbound network calls in serve mode" framing in README needs this caveat. v3.7.16 added a stderr disclosure warning that fires once per OCR worker creation, so operators see the network-fetch behavior clearly in their logs / journald.
164
+
165
+ **Mitigations:**
166
+ - The OCR tool is only registered when the optional dependencies (`tesseract.js`, `@napi-rs/canvas`) are installed — `npm install --omit=optional` leaves OCR fully unavailable, restoring the strict offline posture.
167
+ - Pre-download trained-data files by running OCR once per language on a known-online machine, then copy the resulting `tessdata/` directory to the offline deployment.
168
+ - The runtime warning is unconditional — it fires whether the trained-data is cached or not, because the WORKER COULD fall back to the CDN if the cache is somehow incomplete.
169
+
170
+ **Roadmap (v3.8.0):**
171
+ - An `enquire-mcp install-ocr-lang <code>` subcommand to mirror `install-model` for embeddings (explicit, opt-in network call; offline posture for `serve`).
172
+ - Strict cache check before `createWorker()` that fails fast on missing trained-data, with a clear "run `install-ocr-lang <code>`" message.
173
+ - `--enable-ocr-online` flag for users who explicitly want the CDN fallback.
174
+
175
+ Tracked in CHANGELOG under v3.7.16 P1-1.
176
+
177
+ ### OCR resource limits (v3.7.16 P1-2)
178
+
179
+ OCR is the slowest path in the project — ~1-2s per page on M1 CPU at default scale. Pre-3.7.16 a single bearer-authenticated HTTP request could trigger unbounded OCR work (the entire PDF, no timeout, no concurrency cap, no per-call budget). A 10000-page PDF would peg the CPU for hours.
180
+
181
+ v3.7.16 adds a default **200-page cap per call** (`DEFAULT_OCR_MAX_PAGES`), bypassable via an explicit `pages: [from, to]` range or via the `maxPages` option. The cap is checked BEFORE the Tesseract worker spins up, so no resources allocate on adversarial inputs.
182
+
183
+ Roadmap (v3.8.0): per-call timeout, concurrent-request cap, HTTP-transport operation budget.
184
+
159
185
  ## Persistent FTS5 index: privacy posture
160
186
 
161
187
  When `--persistent-index` is enabled, the search-index file at `<vault-hash>.fts5.db` (alongside the parse cache) stores **chunked note content** (paragraph-level, ~4 KB each), the **comma-serialized tag list** of each note, and the **list of wikilink targets** as part of the FTS5 enrichment for recall.
package/dist/doctor.js CHANGED
@@ -184,7 +184,7 @@ export async function runDoctor(opts) {
184
184
  label: "pdfjs-dist (PDF read + indexing)",
185
185
  status: hasPdfjs ? "ok" : "warn",
186
186
  detail: hasPdfjs ? "loaded" : "PDFs in vault won't be indexable",
187
- hint: hasPdfjs ? undefined : "npm install pdfjs-dist@^4.10.38 (skip if you have no PDFs)"
187
+ hint: hasPdfjs ? undefined : "npm install pdfjs-dist@^5.7.284 (skip if you have no PDFs)"
188
188
  });
189
189
  // 5. tesseract.js + @napi-rs/canvas — gates obsidian_ocr_pdf.
190
190
  const [hasTesseract, hasCanvas] = await Promise.all([
@@ -1 +1 @@
1
- {"version":3,"file":"embeddings.d.ts","sourceRoot":"","sources":["../src/embeddings.ts"],"names":[],"mappings":"AAcA;;mCAEmC;AACnC,MAAM,WAAW,cAAc;IAC7B,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAC;IACd,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAC;IACb,4DAA4D;IAC5D,GAAG,EAAE,MAAM,CAAC;IACZ,8EAA8E;IAC9E,YAAY,EAAE,MAAM,CAAC;IACrB,gEAAgE;IAChE,YAAY,EAAE,OAAO,CAAC;IACtB,6DAA6D;IAC7D,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAiBpE,CAAC;AAEH,0EAA0E;AAC1E,eAAO,MAAM,mBAAmB,iBAAiB,CAAC;AAElD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,cAAc,CAQtE;AAED,6EAA6E;AAC7E,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;IAC/B;wDACoD;IACpD,KAAK,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;CAC1D;AA2ED;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAyCpE;AAED,2EAA2E;AAC3E,wBAAgB,SAAS,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,YAAY,GAAG,MAAM,CASlE;AAuBD,oEAAoE;AACpE,MAAM,WAAW,aAAa;IAC5B,gEAAgE;IAChE,KAAK,EAAE,MAAM,CAAC;IACd,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,YAAY,EAAE,MAAM,CAAC;IACrB,4CAA4C;IAC5C,YAAY,EAAE,OAAO,CAAC;IACtB,+DAA+D;IAC/D,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,eAAO,MAAM,eAAe,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAqDlE,CAAC;AASH,eAAO,MAAM,sBAAsB,eAAe,CAAC;AAEnD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,aAAa,CAQ7E;AAED,6EAA6E;AAC7E,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B;;;;;;;OAOG;IACH,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;CACtE;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,YAAY,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAuDpE"}
1
+ {"version":3,"file":"embeddings.d.ts","sourceRoot":"","sources":["../src/embeddings.ts"],"names":[],"mappings":"AAcA;;mCAEmC;AACnC,MAAM,WAAW,cAAc;IAC7B,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAC;IACd,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAC;IACb,4DAA4D;IAC5D,GAAG,EAAE,MAAM,CAAC;IACZ,8EAA8E;IAC9E,YAAY,EAAE,MAAM,CAAC;IACrB,gEAAgE;IAChE,YAAY,EAAE,OAAO,CAAC;IACtB,6DAA6D;IAC7D,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAiBpE,CAAC;AAEH,0EAA0E;AAC1E,eAAO,MAAM,mBAAmB,iBAAiB,CAAC;AAElD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,cAAc,CAQtE;AAED,6EAA6E;AAC7E,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;IAC/B;wDACoD;IACpD,KAAK,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;CAC1D;AA2ED;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAyCpE;AAED,2EAA2E;AAC3E,wBAAgB,SAAS,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,YAAY,GAAG,MAAM,CASlE;AAuBD,oEAAoE;AACpE,MAAM,WAAW,aAAa;IAC5B,gEAAgE;IAChE,KAAK,EAAE,MAAM,CAAC;IACd,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,YAAY,EAAE,MAAM,CAAC;IACrB,4CAA4C;IAC5C,YAAY,EAAE,OAAO,CAAC;IACtB,+DAA+D;IAC/D,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,eAAO,MAAM,eAAe,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAyDlE,CAAC;AASH,eAAO,MAAM,sBAAsB,eAAe,CAAC;AAEnD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,aAAa,CAQ7E;AAED,6EAA6E;AAC7E,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B;;;;;;;OAOG;IACH,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;CACtE;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,YAAY,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAuDpE"}
@@ -217,8 +217,12 @@ export const RERANKER_MODELS = Object.freeze({
217
217
  maxTokens: 512
218
218
  },
219
219
  // mxbai-rerank-large-v2 — multilingual, ~280 MB. Higher quality than
220
- // the xsmall variant (rerank-multilingual default). Multi-language
221
- // benchmark performance is solid; cost is the larger download.
220
+ // the xsmall `rerank-multilingual` (which is the multilingual variant,
221
+ // NOT the project-wide default see `DEFAULT_RERANKER_ALIAS` below; it
222
+ // was bumped to `rerank-bge` in v3.6.1 CRIT-2 because 4 of 5 catalog
223
+ // aliases fail at `AutoTokenizer.from_pretrained` due to a
224
+ // transformers.js compat issue). Multi-language benchmark performance
225
+ // is solid; cost is the larger download.
222
226
  "rerank-multilingual-large": {
223
227
  alias: "rerank-multilingual-large",
224
228
  hfId: "Xenova/mxbai-rerank-large-v2",
@@ -1 +1 @@
1
- {"version":3,"file":"embeddings.js","sourceRoot":"","sources":["../src/embeddings.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"embeddings.js","sourceRoot":"","sources":["../src/embeddings.ts"],"names":[],"mappings":"AAAA,kFAAkF;AAClF,gFAAgF;AAChF,sEAAsE;AACtE,gFAAgF;AAChF,EAAE;AACF,gBAAgB;AAChB,8EAA8E;AAC9E,oEAAoE;AACpE,wEAAwE;AACxE,yEAAyE;AACzE,iFAAiF;AACjF,8EAA8E;AAC9E,uEAAuE;AAoBvE;;;;;GAKG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAA6C,MAAM,CAAC,MAAM,CAAC;IACtF,YAAY,EAAE;QACZ,KAAK,EAAE,cAAc;QACrB,IAAI,EAAE,8CAA8C;QACpD,GAAG,EAAE,GAAG;QACR,YAAY,EAAE,GAAG;QACjB,YAAY,EAAE,IAAI;QAClB,SAAS,EAAE,GAAG;KACf;IACD,GAAG,EAAE;QACH,KAAK,EAAE,KAAK;QACZ,IAAI,EAAE,0BAA0B;QAChC,GAAG,EAAE,GAAG;QACR,YAAY,EAAE,EAAE;QAChB,YAAY,EAAE,KAAK;QACnB,SAAS,EAAE,GAAG;KACf;CACF,CAAC,CAAC;AAEH,0EAA0E;AAC1E,MAAM,CAAC,MAAM,mBAAmB,GAAG,cAAc,CAAC;AAElD;;;;;;;;GAQG;AACH,MAAM,UAAU,YAAY,CAAC,KAAyB;IACpD,MAAM,GAAG,GAAG,KAAK,IAAI,mBAAmB,CAAC;IACzC,MAAM,KAAK,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;IACpC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvD,MAAM,IAAI,KAAK,CAAC,kCAAkC,GAAG,qBAAqB,KAAK,GAAG,CAAC,CAAC;IACtF,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAUD,2EAA2E;AAC3E,2EAA2E;AAC3E,4EAA4E;AAC5E,IAAI,YAAY,GAA+D,IAAI,CAAC;AACpF,IAAI,iBAAiB,GAAiF,IAAI,CAAC;AAC3G,IAAI,sBAAsB,GAAiF,IAAI,CAAC;AAEhH,KAAK,UAAU,YAAY;IACzB,IAAI,YAAY;QAAE,OAAO,YAAY,CAAC;IACtC,IAAI,CAAC;QACH,gEAAgE;QAChE,MAAM,GAAG,GAAG,CAAC,MAAM,MAAM,CAAC,2BAA2B,CAAC,CAErD,CAAC;QACF,IAAI,CAAC,GAAG,CAAC,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;QACzF,YAAY,GAAG,GAAG,CAAC,QAAQ,CAAC;QAC5B,OAAO,YAAY,CAAC;IACtB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CACb,6HAA6H;YAC3H,iGAAiG;YACjG,mBAAmB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CACxE,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,KAAK,UAAU,yBAAyB;IAItC,IAAI,iBAAiB,IAAI,sBAAsB,EAAE,CAAC;QAChD,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,kCAAkC,EAAE,sBAAsB,EAAE,CAAC;IAC1G,CAAC;IACD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,CAAC,MAAM,MAAM,CAAC,2BAA2B,CAAC,CAGrD,CAAC;QACF,IAAI,CAAC,GAAG,CAAC,aAAa,IAAI,CAAC,GAAG,CAAC,kCAAkC,EAAE,CAAC;YAClE,MAAM,IAAI,KAAK,CACb,iGAAiG,CAClG,CAAC;QACJ,CAAC;QACD,iBAAiB,GAAG,GAAG,CAAC,aAAa,CAAC;QACtC,sBAAsB,GAAG,GAAG,CAAC,kCAAkC,CAAC;QAChE,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,kCAAkC,EAAE,sBAAsB,EAAE,CAAC;IAC1G,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CACb,4HAA4H;YAC1H,iGAAiG;YACjG,mBAAmB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CACxE,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,KAAc;IAC/C,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IAClC,MAAM,QAAQ,GAAG,MAAM,YAAY,EAAE,CAAC;IACtC,MAAM,SAAS,GAAG,CAAC,MAAM,QAAQ,CAAC,oBAAoB,EAAE,KAAK,CAAC,IAAI,CAAC,CAGN,CAAC;IAE9D,wEAAwE;IACxE,wEAAwE;IACxE,yEAAyE;IACzE,wEAAwE;IACxE,0EAA0E;IAC1E,sEAAsE;IACtE,MAAM,kBAAkB,GAAG,CAAC,CAAC;IAE7B,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC;IACtB,OAAO;QACL,KAAK;QACL,KAAK,CAAC,KAAK,CAAC,KAAwB;YAClC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,EAAE,CAAC;YAClC,MAAM,GAAG,GAAmB,EAAE,CAAC;YAC/B,oEAAoE;YACpE,gEAAgE;YAChE,KAAK,IAAI,UAAU,GAAG,CAAC,EAAE,UAAU,GAAG,KAAK,CAAC,MAAM,EAAE,UAAU,IAAI,kBAAkB,EAAE,CAAC;gBACrF,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,UAAU,EAAE,UAAU,GAAG,kBAAkB,CAAC,CAAC;gBACvE,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,CAAC,GAAG,KAAK,CAAC,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;gBACjF,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;oBAC3B,MAAM,IAAI,KAAK,CACb,SAAS,KAAK,CAAC,IAAI,iBAAiB,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,cAAc,GAAG,sCAAsC,CAC1G,CAAC;gBACJ,CAAC;gBACD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;oBACtC,MAAM,KAAK,GAAG,CAAC,GAAG,GAAG,CAAC;oBACtB,uEAAuE;oBACvE,GAAG,CAAC,IAAI,CAAC,IAAI,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;gBACpE,CAAC;YACH,CAAC;YACD,OAAO,GAAG,CAAC;QACb,CAAC;KACF,CAAC;AACJ,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,SAAS,CAAC,CAAe,EAAE,CAAe;IACxD,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC,MAAM,OAAO,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IACrE,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAClC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IACjC,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAqCD;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAA4C,MAAM,CAAC,MAAM,CAAC;IACpF,6EAA6E;IAC7E,YAAY,EAAE;QACZ,KAAK,EAAE,YAAY;QACnB,IAAI,EAAE,0BAA0B;QAChC,YAAY,EAAE,GAAG;QACjB,YAAY,EAAE,KAAK;QACnB,SAAS,EAAE,GAAG;KACf;IACD,4EAA4E;IAC5E,wEAAwE;IACxE,uEAAuE;IACvE,wBAAwB;IACxB,qBAAqB,EAAE;QACrB,KAAK,EAAE,qBAAqB;QAC5B,IAAI,EAAE,+BAA+B;QACrC,YAAY,EAAE,EAAE;QAChB,YAAY,EAAE,IAAI;QAClB,SAAS,EAAE,GAAG;KACf;IACD,oEAAoE;IACpE,mCAAmC;IACnC,EAAE;IACF,qEAAqE;IACrE,kEAAkE;IAClE,oCAAoC;IACpC,kBAAkB,EAAE;QAClB,KAAK,EAAE,kBAAkB;QACzB,IAAI,EAAE,2BAA2B;QACjC,YAAY,EAAE,GAAG;QACjB,YAAY,EAAE,KAAK;QACnB,SAAS,EAAE,GAAG;KACf;IACD,qEAAqE;IACrE,iEAAiE;IACjE,gDAAgD;IAChD,kBAAkB,EAAE;QAClB,KAAK,EAAE,kBAAkB;QACzB,IAAI,EAAE,iCAAiC;QACvC,YAAY,EAAE,EAAE;QAChB,YAAY,EAAE,KAAK;QACnB,SAAS,EAAE,GAAG;KACf;IACD,qEAAqE;IACrE,uEAAuE;IACvE,wEAAwE;IACxE,qEAAqE;IACrE,2DAA2D;IAC3D,sEAAsE;IACtE,yCAAyC;IACzC,2BAA2B,EAAE;QAC3B,KAAK,EAAE,2BAA2B;QAClC,IAAI,EAAE,8BAA8B;QACpC,YAAY,EAAE,GAAG;QACjB,YAAY,EAAE,IAAI;QAClB,SAAS,EAAE,GAAG;KACf;CACF,CAAC,CAAC;AAEH,2EAA2E;AAC3E,2EAA2E;AAC3E,0EAA0E;AAC1E,qEAAqE;AACrE,4EAA4E;AAC5E,yEAAyE;AACzE,iCAAiC;AACjC,MAAM,CAAC,MAAM,sBAAsB,GAAG,YAAY,CAAC;AAEnD;;;;;;;;GAQG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAyB;IAC5D,MAAM,GAAG,GAAG,KAAK,IAAI,sBAAsB,CAAC;IAC5C,MAAM,KAAK,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;IACnC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtD,MAAM,IAAI,KAAK,CAAC,iCAAiC,GAAG,qBAAqB,KAAK,GAAG,CAAC,CAAC;IACrF,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAgBD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,KAAc;IAC/C,MAAM,KAAK,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;IAC1C,MAAM,EAAE,aAAa,EAAE,kCAAkC,EAAE,GAAG,MAAM,yBAAyB,EAAE,CAAC;IAChG,uEAAuE;IACvE,gDAAgD;IAChD,MAAM,KAAK,GAAG,IAAa,CAAC;IAC5B,MAAM,SAAS,GAAG,CAAC,MAAM,aAAa,CAAC,eAAe,CAAC,KAAK,CAAC,IAAI,CAAC,CAGtD,CAAC;IACb,MAAM,MAAM,GAAG,CAAC,MAAM,kCAAkC,CAAC,eAAe,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,CAEtB,CAAC;IAE1E,uEAAuE;IACvE,sEAAsE;IACtE,gCAAgC;IAChC,MAAM,kBAAkB,GAAG,CAAC,CAAC;IAE7B,OAAO;QACL,KAAK;QACL,KAAK,CAAC,KAAK,CAAC,KAAa,EAAE,QAA2B;YACpD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,EAAE,CAAC;YACrC,MAAM,GAAG,GAAa,EAAE,CAAC;YACzB,KAAK,IAAI,UAAU,GAAG,CAAC,EAAE,UAAU,GAAG,QAAQ,CAAC,MAAM,EAAE,UAAU,IAAI,kBAAkB,EAAE,CAAC;gBACxF,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,UAAU,EAAE,UAAU,GAAG,kBAAkB,CAAC,CAAC;gBAC1E,yEAAyE;gBACzE,oEAAoE;gBACpE,kEAAkE;gBAClE,qEAAqE;gBACrE,MAAM,OAAO,GAAG,IAAI,KAAK,CAAS,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBAC5D,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,CAAC,GAAG,KAAK,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;gBAC9F,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC;gBACxC,6DAA6D;gBAC7D,kEAAkE;gBAClE,iEAAiE;gBACjE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;oBACtC,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;oBAC3B,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;wBACjD,8DAA8D;wBAC9D,wCAAwC;wBACxC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC;wBACpB,SAAS;oBACX,CAAC;oBACD,4DAA4D;oBAC5D,4DAA4D;oBAC5D,6DAA6D;oBAC7D,0DAA0D;oBAC1D,4BAA4B;oBAC5B,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;gBACrC,CAAC;YACH,CAAC;YACD,OAAO,GAAG,CAAC;QACb,CAAC;KACF,CAAC;AACJ,CAAC"}
package/dist/fts5.d.ts CHANGED
@@ -108,7 +108,17 @@ export declare class FtsIndex {
108
108
  deleted: string[];
109
109
  unchanged: string[];
110
110
  };
111
- /** Drop a file's chunks + state row. Idempotent. */
111
+ /** Drop a file's chunks + state row. Idempotent.
112
+ *
113
+ * v3.7.18 R-8 — wrapped in `db.transaction()` for atomicity. Pre-3.7.18
114
+ * the two DELETE statements ran independently; a crash / SIGKILL / DB
115
+ * lock contention between them could leave `source_state` saying "this
116
+ * file is indexed at mtime X" while `chunks` had no rows — causing the
117
+ * next watcher event to skip re-indexing (state matches) but search to
118
+ * miss the file (no chunks). Sibling of v3.7.10 audit #10 fix that
119
+ * wrapped `reindexFile` / `reindexPdfFile` / source_state in a txn for
120
+ * the same reason. Caught by round-20 external audit.
121
+ */
112
122
  dropFile(relPath: string): void;
113
123
  /** Re-chunk a single markdown file, replacing its existing chunks atomically.
114
124
  *
@@ -181,12 +191,17 @@ export declare class FtsIndex {
181
191
  * Sanitize a user query for FTS5. Quote-wraps any token containing
182
192
  * non-alphanumerics so hyphens / colons / dots are treated literally
183
193
  * (without this, `"claude-telegram"` would parse as `claude NOT telegram`).
184
- * Strips reserved keywords (`AND`, `OR`, `NOT`, `NEAR`) so they can't
185
- * inject unexpected boolean logic.
194
+ *
195
+ * v3.7.16 P3-28 reserved keywords (`AND`, `OR`, `NOT`, `NEAR`) are
196
+ * QUOTED as literals instead of stripped. Pre-3.7.16 the strip-path
197
+ * silently dropped real query terms ("operating systems AND databases"
198
+ * lost the connective AND user couldn't search for the literal word
199
+ * "AND"). Quoting makes both cases work: FTS5 treats `"AND"` as the
200
+ * literal token rather than the boolean operator.
186
201
  *
187
202
  * @param q - User query string.
188
203
  * @returns Sanitized query ready to pass to FTS5's `MATCH` operator.
189
- * Empty string when no usable tokens remain.
204
+ * Empty string when input is empty / whitespace-only.
190
205
  */
191
206
  export declare function safeFts5Query(q: string): string;
192
207
  interface ContentChunk {
@@ -1 +1 @@
1
- {"version":3,"file":"fts5.d.ts","sourceRoot":"","sources":["../src/fts5.ts"],"names":[],"mappings":"AA2BA;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG,SAAS,CAAC;AAEnD,oEAAoE;AACpE,MAAM,MAAM,SAAS,GAAG,IAAI,GAAG,KAAK,CAAC;AAErC;kEACkE;AAClE,MAAM,WAAW,YAAY;IAC3B,oDAAoD;IACpD,QAAQ,EAAE,MAAM,CAAC;IACjB,gDAAgD;IAChD,WAAW,EAAE,MAAM,CAAC;IACpB,2CAA2C;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,qDAAqD;IACrD,QAAQ,EAAE,MAAM,CAAC;IACjB,8EAA8E;IAC9E,OAAO,EAAE,MAAM,CAAC;IAChB;+DAC2D;IAC3D,KAAK,EAAE,MAAM,CAAC;IACd,0EAA0E;IAC1E,IAAI,EAAE,SAAS,CAAC;CACjB;AAED,wEAAwE;AACxE,MAAM,WAAW,aAAa;IAC5B,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAC;IACd,4CAA4C;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,OAAO,EAAE,MAAM,CAAC;IAChB,iEAAiE;IACjE,SAAS,EAAE,MAAM,CAAC;IAClB,gDAAgD;IAChD,YAAY,EAAE,MAAM,CAAC;CACtB;AAgED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,EAAE,CAAmB;IAC7B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAe;IACxC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;gBAEvB,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,YAAY,CAAA;KAAE;IAM9E;;;;;;;OAOG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA+B3B,iEAAiE;IAC3D,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAcrC;6BACyB;IACzB,KAAK,IAAI,IAAI;IAOb,OAAO,CAAC,eAAe;IAuDvB,OAAO,CAAC,QAAQ;IAQhB,OAAO,CAAC,SAAS;IAMjB,OAAO,CAAC,SAAS;IAKjB;;;;;;;;;;;OAWG;IACH,IAAI,CACF,WAAW,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,EACxD,IAAI,CAAC,EAAE,SAAS,GACf;QACD,KAAK,EAAE,MAAM,EAAE,CAAC;QAChB,OAAO,EAAE,MAAM,EAAE,CAAC;QAClB,OAAO,EAAE,MAAM,EAAE,CAAC;QAClB,SAAS,EAAE,MAAM,EAAE,CAAC;KACrB;IA0BD,oDAAoD;IACpD,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAM/B;;;;;;;;OAQG;IACH,WAAW,CACT,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,eAAe,GAAE,MAAM,EAAO,EAC9B,IAAI,GAAE,MAAM,EAAO,GAClB,MAAM;IAiCT;;;;;;;;;;;OAWG;IACH,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,CAAC;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,MAAM;IA2BpH;;;;;;;;;;;;;;;OAeG;IACH,MAAM,CACJ,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAO,GAClF,YAAY,EAAE;IAmEjB;;;;;;;OAOG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAQ/G,kEAAkE;IAClE,WAAW,IAAI,MAAM;IAMrB;yEACqE;IACrE,UAAU,IAAI,MAAM;CAKrB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAe/C;AAED,UAAU,YAAY;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB;;;;+EAI2E;IAC3E,UAAU,EAAE,MAAM,CAAC;CACpB;AAID;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,SAAkB,GAAG,YAAY,EAAE,CAwDxF;AA4ED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAM1D;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC3D,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,YAAY,CAAC;CAC9B,GAAG,IAAI,CAAC,CA0BR"}
1
+ {"version":3,"file":"fts5.d.ts","sourceRoot":"","sources":["../src/fts5.ts"],"names":[],"mappings":"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"}
package/dist/fts5.js CHANGED
@@ -254,11 +254,24 @@ export class FtsIndex {
254
254
  deleted.push(relPath);
255
255
  return { added, updated, deleted, unchanged };
256
256
  }
257
- /** Drop a file's chunks + state row. Idempotent. */
257
+ /** Drop a file's chunks + state row. Idempotent.
258
+ *
259
+ * v3.7.18 R-8 — wrapped in `db.transaction()` for atomicity. Pre-3.7.18
260
+ * the two DELETE statements ran independently; a crash / SIGKILL / DB
261
+ * lock contention between them could leave `source_state` saying "this
262
+ * file is indexed at mtime X" while `chunks` had no rows — causing the
263
+ * next watcher event to skip re-indexing (state matches) but search to
264
+ * miss the file (no chunks). Sibling of v3.7.10 audit #10 fix that
265
+ * wrapped `reindexFile` / `reindexPdfFile` / source_state in a txn for
266
+ * the same reason. Caught by round-20 external audit.
267
+ */
258
268
  dropFile(relPath) {
259
269
  const db = this.requireDb();
260
- db.prepare("DELETE FROM chunks WHERE rel_path = ?").run(relPath);
261
- db.prepare("DELETE FROM source_state WHERE rel_path = ?").run(relPath);
270
+ const txn = db.transaction(() => {
271
+ db.prepare("DELETE FROM chunks WHERE rel_path = ?").run(relPath);
272
+ db.prepare("DELETE FROM source_state WHERE rel_path = ?").run(relPath);
273
+ });
274
+ txn();
262
275
  }
263
276
  /** Re-chunk a single markdown file, replacing its existing chunks atomically.
264
277
  *
@@ -370,8 +383,16 @@ export class FtsIndex {
370
383
  if (opts.tag) {
371
384
  // Exact-tag membership inside the comma-separated `tags` column —
372
385
  // wrap both sides with commas so "core" doesn't match "core-team".
373
- where.push("(',' || chunks.tags || ',') LIKE ?");
374
- params.push(`%,${opts.tag},%`);
386
+ //
387
+ // v3.7.16 P2-15 — escape `%` and `_` (SQL LIKE wildcards) so a
388
+ // user-supplied tag with those characters matches LITERALLY. Pre-
389
+ // 3.7.16 a tag like `core_team` would match `coreXteam` (and any
390
+ // other 1-char-substituted variant) because `_` is the LIKE 1-char
391
+ // wildcard; `%` was even worse — `tag: "%"` matched every chunk.
392
+ // ESCAPE clause uses backslash, matching SQLite's standard form.
393
+ const literalTag = opts.tag.replace(/\\/g, "\\\\").replace(/%/g, "\\%").replace(/_/g, "\\_");
394
+ where.push("(',' || chunks.tags || ',') LIKE ? ESCAPE '\\'");
395
+ params.push(`%,${literalTag},%`);
375
396
  }
376
397
  let join = "";
377
398
  if (opts.sinceMtimeMs !== undefined) {
@@ -438,22 +459,36 @@ export class FtsIndex {
438
459
  * Sanitize a user query for FTS5. Quote-wraps any token containing
439
460
  * non-alphanumerics so hyphens / colons / dots are treated literally
440
461
  * (without this, `"claude-telegram"` would parse as `claude NOT telegram`).
441
- * Strips reserved keywords (`AND`, `OR`, `NOT`, `NEAR`) so they can't
442
- * inject unexpected boolean logic.
462
+ *
463
+ * v3.7.16 P3-28 reserved keywords (`AND`, `OR`, `NOT`, `NEAR`) are
464
+ * QUOTED as literals instead of stripped. Pre-3.7.16 the strip-path
465
+ * silently dropped real query terms ("operating systems AND databases"
466
+ * lost the connective AND user couldn't search for the literal word
467
+ * "AND"). Quoting makes both cases work: FTS5 treats `"AND"` as the
468
+ * literal token rather than the boolean operator.
443
469
  *
444
470
  * @param q - User query string.
445
471
  * @returns Sanitized query ready to pass to FTS5's `MATCH` operator.
446
- * Empty string when no usable tokens remain.
472
+ * Empty string when input is empty / whitespace-only.
447
473
  */
448
474
  export function safeFts5Query(q) {
449
475
  const RESERVED = new Set(["AND", "OR", "NOT", "NEAR"]);
450
476
  const parts = q.trim().split(/\s+/);
451
477
  const out = [];
452
478
  for (const p of parts) {
453
- if (RESERVED.has(p.toUpperCase()))
454
- continue;
455
479
  if (!p)
456
480
  continue;
481
+ // v3.7.16 P3-28 — quote reserved keywords as literals instead of
482
+ // stripping. Pre-3.7.16 a user searching "operating systems AND
483
+ // databases" got their AND dropped silently AND the unrelated tokens
484
+ // OR'd implicitly — but they ALSO couldn't search literally for the
485
+ // word "AND" (the SQL boolean conjunction). Now we wrap reserved
486
+ // words in double-quotes so FTS5 treats them as the literal token,
487
+ // matching how we handle any token with non-alphanumerics below.
488
+ if (RESERVED.has(p.toUpperCase())) {
489
+ out.push(`"${p}"`);
490
+ continue;
491
+ }
457
492
  if (/[^A-Za-z0-9_]/.test(p)) {
458
493
  const escaped = p.replace(/"/g, '""');
459
494
  out.push(`"${escaped}"`);