@dikolab/kbdb 0.1.5 → 0.2.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.
Files changed (193) hide show
  1. package/README.md +30 -9
  2. package/dist/cli.cjs +181 -50
  3. package/dist/cli.cjs.map +3 -3
  4. package/dist/cli.mjs +181 -50
  5. package/dist/cli.mjs.map +3 -3
  6. package/dist/kbdb-worker.cjs +283 -29
  7. package/dist/kbdb-worker.cjs.map +4 -4
  8. package/dist/mod.cjs +13 -2
  9. package/dist/mod.cjs.map +2 -2
  10. package/dist/mod.mjs +13 -2
  11. package/dist/mod.mjs.map +2 -2
  12. package/dist/src/shared/cli/functions/format-output.function.d.ts +7 -7
  13. package/dist/src/shared/cli/functions/run-search.function.d.ts +25 -16
  14. package/dist/src/shared/cli/index.d.ts +1 -1
  15. package/dist/src/shared/cli/typings/cli-options.interface.d.ts +2 -0
  16. package/dist/src/shared/cli/typings/search-display.model.d.ts +25 -7
  17. package/dist/src/shared/mcp/functions/handle-recall.function.d.ts +29 -0
  18. package/dist/src/shared/mcp/typings/mcp-client.interface.d.ts +30 -11
  19. package/dist/src/shared/version/constants/version.constant.d.ts +1 -1
  20. package/dist/src/shared/wasm-codec/functions/decode-recall-result.function.d.ts +28 -0
  21. package/dist/src/shared/wasm-codec/functions/decode-scored-results.function.d.ts +14 -7
  22. package/dist/src/shared/wasm-codec/functions/encode-recall-params.function.d.ts +21 -0
  23. package/dist/src/shared/wasm-codec/functions/encode-search-params.function.d.ts +1 -1
  24. package/dist/src/shared/wasm-codec/index.d.ts +5 -2
  25. package/dist/src/shared/worker-client/classes/worker-client.class.d.ts +13 -3
  26. package/dist/src/shared/worker-client/index.d.ts +2 -1
  27. package/dist/src/shared/worker-client/typings/recall-result.model.d.ts +94 -0
  28. package/dist/src/shared/worker-client/typings/search-params.model.d.ts +2 -0
  29. package/dist/src/shared/worker-client/typings/search-result.model.d.ts +23 -7
  30. package/dist/src/shared/worker-daemon/classes/worker-daemon.class.d.ts +1 -0
  31. package/dist/wasm/fs-database/kbdb_fs_database.d.ts +18 -0
  32. package/dist/wasm/fs-database/kbdb_fs_database.js +30 -0
  33. package/dist/wasm/fs-database/kbdb_fs_database_bg.wasm +0 -0
  34. package/dist/wasm/fs-database/kbdb_fs_database_bg.wasm.d.ts +2 -0
  35. package/dist/wasm/kb-worker/kbdb_worker.d.ts +38 -0
  36. package/dist/wasm/kb-worker/kbdb_worker.js +56 -0
  37. package/dist/wasm/kb-worker/kbdb_worker_bg.wasm +0 -0
  38. package/dist/wasm/kb-worker/kbdb_worker_bg.wasm.d.ts +3 -0
  39. package/dist/worker.mjs +283 -29
  40. package/dist/worker.mjs.map +4 -4
  41. package/docs/details/README.md +26 -0
  42. package/docs/details/cli.md +608 -0
  43. package/docs/details/library-api.md +406 -0
  44. package/docs/details/mcp-server.md +430 -0
  45. package/docs/details/search-and-ranking.md +457 -0
  46. package/docs/details/storage.md +529 -0
  47. package/docs/goals/agents.md +751 -0
  48. package/docs/goals/architecture.svg +198 -0
  49. package/docs/goals/cli.md +2308 -0
  50. package/docs/goals/content-composer.md +609 -0
  51. package/docs/goals/content-parser.md +279 -0
  52. package/docs/goals/database.md +1452 -0
  53. package/docs/goals/document.md +368 -0
  54. package/docs/goals/mcp.md +1467 -0
  55. package/docs/goals/overview.md +124 -0
  56. package/docs/goals/query-parser.md +271 -0
  57. package/docs/goals/query-result.md +693 -0
  58. package/docs/goals/skills.md +738 -0
  59. package/docs/goals/worker-client.md +944 -0
  60. package/docs/goals/worker-daemon.md +1447 -0
  61. package/docs/modules/overview.md +298 -0
  62. package/docs/modules/rust/fs-database/functions/document.md +48 -0
  63. package/docs/modules/rust/fs-database/functions/integrity.md +68 -0
  64. package/docs/modules/rust/fs-database/functions/io.md +87 -0
  65. package/docs/modules/rust/fs-database/functions/module.md +22 -0
  66. package/docs/modules/rust/fs-database/functions/query-exec.md +44 -0
  67. package/docs/modules/rust/fs-database/functions/section.md +59 -0
  68. package/docs/modules/rust/fs-database/indexes/btree.md +35 -0
  69. package/docs/modules/rust/fs-database/indexes/corpus.md +61 -0
  70. package/docs/modules/rust/fs-database/indexes/inverted.md +60 -0
  71. package/docs/modules/rust/fs-database/indexes/module.md +64 -0
  72. package/docs/modules/rust/fs-database/indexes/positional.md +39 -0
  73. package/docs/modules/rust/fs-database/module.md +149 -0
  74. package/docs/modules/rust/fs-database/types/catalog.md +53 -0
  75. package/docs/modules/rust/fs-database/types/module.md +12 -0
  76. package/docs/modules/rust/fs-database/types/section.md +101 -0
  77. package/docs/modules/rust/kb-worker/functions/compose.md +57 -0
  78. package/docs/modules/rust/kb-worker/functions/module.md +20 -0
  79. package/docs/modules/rust/kb-worker/functions/retrieve.md +194 -0
  80. package/docs/modules/rust/kb-worker/functions/search.md +173 -0
  81. package/docs/modules/rust/kb-worker/functions/write.md +172 -0
  82. package/docs/modules/rust/kb-worker/module.md +171 -0
  83. package/docs/modules/rust/kb-worker/types/cache.md +90 -0
  84. package/docs/modules/rust/kb-worker/types/context.md +26 -0
  85. package/docs/modules/rust/kb-worker/types/module.md +16 -0
  86. package/docs/modules/rust/kb-worker/types/status.md +29 -0
  87. package/docs/modules/rust/kb-worker/types/token.md +50 -0
  88. package/docs/modules/rust/overview.md +495 -0
  89. package/docs/modules/rust/query-parser/functions/extract.md +56 -0
  90. package/docs/modules/rust/query-parser/functions/module.md +16 -0
  91. package/docs/modules/rust/query-parser/functions/parse.md +33 -0
  92. package/docs/modules/rust/query-parser/functions/pipeline.md +103 -0
  93. package/docs/modules/rust/query-parser/functions/rank.md +85 -0
  94. package/docs/modules/rust/query-parser/module.md +136 -0
  95. package/docs/modules/rust/query-parser/types/cache.md +39 -0
  96. package/docs/modules/rust/query-parser/types/keyphrase.md +60 -0
  97. package/docs/modules/rust/query-parser/types/module.md +17 -0
  98. package/docs/modules/rust/query-parser/types/token.md +42 -0
  99. package/docs/modules/rust/shared/content-composer/functions.md +114 -0
  100. package/docs/modules/rust/shared/content-composer/module.md +147 -0
  101. package/docs/modules/rust/shared/content-composer/types.md +93 -0
  102. package/docs/modules/rust/shared/content-parser/functions.md +72 -0
  103. package/docs/modules/rust/shared/content-parser/module.md +99 -0
  104. package/docs/modules/rust/shared/content-parser/types.md +71 -0
  105. package/docs/modules/rust/shared/corpus/module.md +24 -0
  106. package/docs/modules/rust/shared/corpus/types.md +141 -0
  107. package/docs/modules/rust/shared/document/module.md +25 -0
  108. package/docs/modules/rust/shared/document/types.md +154 -0
  109. package/docs/modules/rust/shared/encode/module.md +22 -0
  110. package/docs/modules/rust/shared/encode/traits.md +304 -0
  111. package/docs/modules/rust/shared/error/module.md +28 -0
  112. package/docs/modules/rust/shared/error/types.md +302 -0
  113. package/docs/modules/rust/shared/kbid/module.md +24 -0
  114. package/docs/modules/rust/shared/kbid/types.md +147 -0
  115. package/docs/modules/rust/shared/memory/functions.md +209 -0
  116. package/docs/modules/rust/shared/memory/module.md +24 -0
  117. package/docs/modules/rust/shared/module.md +168 -0
  118. package/docs/modules/rust/shared/query/module.md +27 -0
  119. package/docs/modules/rust/shared/query/types.md +236 -0
  120. package/docs/modules/rust/shared/section/module.md +25 -0
  121. package/docs/modules/rust/shared/section/types.md +294 -0
  122. package/docs/modules/rust/shared/term/module.md +25 -0
  123. package/docs/modules/rust/shared/term/types.md +139 -0
  124. package/docs/modules/typescript/cli.md +129 -0
  125. package/docs/modules/typescript/kbdb-worker.md +135 -0
  126. package/docs/modules/typescript/overview.md +378 -0
  127. package/docs/modules/typescript/shared/cli/constants.md +23 -0
  128. package/docs/modules/typescript/shared/cli/functions.md +630 -0
  129. package/docs/modules/typescript/shared/cli/module.md +125 -0
  130. package/docs/modules/typescript/shared/cli/typings.md +172 -0
  131. package/docs/modules/typescript/shared/hash/functions.md +20 -0
  132. package/docs/modules/typescript/shared/hash/module.md +21 -0
  133. package/docs/modules/typescript/shared/mcp/classes.md +179 -0
  134. package/docs/modules/typescript/shared/mcp/functions.md +386 -0
  135. package/docs/modules/typescript/shared/mcp/module.md +160 -0
  136. package/docs/modules/typescript/shared/mcp/typings.md +522 -0
  137. package/docs/modules/typescript/shared/module.md +94 -0
  138. package/docs/modules/typescript/shared/platform/functions.md +42 -0
  139. package/docs/modules/typescript/shared/platform/module.md +25 -0
  140. package/docs/modules/typescript/shared/runtime/functions.md +26 -0
  141. package/docs/modules/typescript/shared/runtime/module.md +27 -0
  142. package/docs/modules/typescript/shared/runtime/typings.md +13 -0
  143. package/docs/modules/typescript/shared/version/module.md +27 -0
  144. package/docs/modules/typescript/shared/wasm-codec/functions.md +391 -0
  145. package/docs/modules/typescript/shared/wasm-codec/module.md +98 -0
  146. package/docs/modules/typescript/shared/worker-client/classes.md +264 -0
  147. package/docs/modules/typescript/shared/worker-client/functions.md +175 -0
  148. package/docs/modules/typescript/shared/worker-client/module.md +84 -0
  149. package/docs/modules/typescript/shared/worker-client/typings.md +508 -0
  150. package/docs/modules/typescript/shared/worker-daemon/classes.md +239 -0
  151. package/docs/modules/typescript/shared/worker-daemon/constants.md +37 -0
  152. package/docs/modules/typescript/shared/worker-daemon/functions.md +221 -0
  153. package/docs/modules/typescript/shared/worker-daemon/module.md +112 -0
  154. package/docs/modules/typescript/shared/worker-daemon/typings.md +126 -0
  155. package/docs/overview.md +188 -0
  156. package/docs/release-notes/0.1.0.md +189 -0
  157. package/docs/release-notes/0.1.1.md +46 -0
  158. package/docs/release-notes/0.1.2.md +38 -0
  159. package/docs/release-notes/0.1.3.md +42 -0
  160. package/docs/release-notes/0.2.0.md +147 -0
  161. package/docs/release-notes/README.md +9 -0
  162. package/package.json +8 -2
  163. package/src/shared/cli/functions/dispatch-command.function.ts +3 -3
  164. package/src/shared/cli/functions/format-output.function.ts +41 -34
  165. package/src/shared/cli/functions/parse-args.function.ts +7 -0
  166. package/src/shared/cli/functions/run-search.function.ts +53 -27
  167. package/src/shared/cli/index.ts +4 -1
  168. package/src/shared/cli/typings/cli-options.interface.ts +2 -0
  169. package/src/shared/cli/typings/search-display.model.ts +26 -7
  170. package/src/shared/mcp/functions/create-tool-definitions.function.ts +64 -4
  171. package/src/shared/mcp/functions/handle-recall.function.ts +66 -0
  172. package/src/shared/mcp/functions/handle-tool-call.function.ts +11 -1
  173. package/src/shared/mcp/typings/mcp-client.interface.ts +33 -15
  174. package/src/shared/version/constants/version.constant.ts +1 -1
  175. package/src/shared/wasm-codec/functions/decode-recall-result.function.ts +267 -0
  176. package/src/shared/wasm-codec/functions/decode-scored-results.function.ts +81 -27
  177. package/src/shared/wasm-codec/functions/encode-recall-params.function.ts +68 -0
  178. package/src/shared/wasm-codec/functions/encode-search-params.function.ts +4 -1
  179. package/src/shared/wasm-codec/index.ts +11 -3
  180. package/src/shared/worker-client/classes/worker-client.class.ts +24 -4
  181. package/src/shared/worker-client/index.ts +11 -1
  182. package/src/shared/worker-client/typings/recall-result.model.ts +98 -0
  183. package/src/shared/worker-client/typings/search-params.model.ts +2 -0
  184. package/src/shared/worker-client/typings/search-result.model.ts +24 -7
  185. package/src/shared/worker-daemon/classes/worker-daemon.class.ts +25 -4
  186. package/src/wasm/fs-database/kbdb_fs_database.d.ts +18 -0
  187. package/src/wasm/fs-database/kbdb_fs_database.js +30 -0
  188. package/src/wasm/fs-database/kbdb_fs_database_bg.wasm +0 -0
  189. package/src/wasm/fs-database/kbdb_fs_database_bg.wasm.d.ts +2 -0
  190. package/src/wasm/kb-worker/kbdb_worker.d.ts +38 -0
  191. package/src/wasm/kb-worker/kbdb_worker.js +56 -0
  192. package/src/wasm/kb-worker/kbdb_worker_bg.wasm +0 -0
  193. package/src/wasm/kb-worker/kbdb_worker_bg.wasm.d.ts +3 -0
@@ -0,0 +1,457 @@
1
+ # How Search Works
2
+
3
+ kbdb uses field-weighted BM25F ranking to score search
4
+ results by relevance. Sections are indexed across
5
+ three fields -- heading, body, and code -- each with a
6
+ different weight so that a match in a title counts
7
+ more than a match buried in body text. Queries
8
+ support simple keyword searches, exact phrases,
9
+ boolean operators, and term exclusions.
10
+
11
+ ## Query Syntax
12
+
13
+ Queries are plain text with optional operators.
14
+ The query parser tokenizes and normalizes query
15
+ terms through the same pipeline used for indexing,
16
+ so search terms match consistently regardless of
17
+ case or inflection.
18
+
19
+ ### Examples
20
+
21
+ | Query | Meaning |
22
+ |---------------------|----------------------------------|
23
+ | `auth flow` | Sections containing both terms |
24
+ | `"error handling"` | Exact phrase match |
25
+ | `auth -oauth` | "auth" but not "oauth" |
26
+ | `auth AND token` | Both terms required |
27
+ | `cache OR redis` | Either term matches |
28
+
29
+ ### Syntax Reference
30
+
31
+ | Syntax | Effect |
32
+ |-----------------|------------------------------------|
33
+ | `word` | Match normalized/stemmed form |
34
+ | `"two words"` | Match exact phrase in order |
35
+ | `-word` | Exclude sections containing term |
36
+ | `AND` | Both operands required |
37
+ | `OR` | Either operand sufficient |
38
+
39
+ Operators can be combined. Unquoted multi-word
40
+ queries are implicitly ANDed -- `auth flow` matches
41
+ sections containing both "auth" and "flow".
42
+
43
+ ## Tokenization Pipeline
44
+
45
+ Both indexing and search queries pass through the
46
+ same eight-stage pipeline. Using identical processing
47
+ for both sides guarantees that stored terms match
48
+ query terms.
49
+
50
+ ```
51
+ Raw text
52
+ |
53
+ v
54
+ 1. Strip content markers
55
+ |
56
+ v
57
+ 2. Unicode NFC normalization
58
+ |
59
+ v
60
+ 3. Word boundary detection
61
+ |
62
+ v
63
+ 4. Identifier splitting
64
+ |
65
+ v
66
+ 5. Case folding
67
+ |
68
+ v
69
+ 6. Stop word removal
70
+ |
71
+ v
72
+ 7. Stemming
73
+ |
74
+ v
75
+ 8. N-gram extraction
76
+ |
77
+ v
78
+ Normalized tokens
79
+ ```
80
+
81
+ 1. **Marker stripping** -- Remove all six content
82
+ marker types (`$(image:)`, `$(code:)`, `$(text:)`,
83
+ `$(document:)`, `$(link:)`, `$(doc-link:)`) before
84
+ processing. Markers are structural references, not
85
+ searchable content.
86
+
87
+ 2. **Unicode NFC normalization** -- Compose decomposed
88
+ Unicode sequences into their canonical form. For
89
+ example, `e` + combining acute accent becomes the
90
+ single codepoint `e` (U+00E9). Ensures equivalent
91
+ Unicode representations produce the same tokens.
92
+
93
+ 3. **Word boundary detection** -- Split text at
94
+ Unicode-defined word boundaries using the Unicode
95
+ Segmentation algorithm. Handles punctuation,
96
+ whitespace, and mixed-script text correctly.
97
+
98
+ 4. **Identifier splitting** -- Decompose programming
99
+ identifiers into constituent words.
100
+ `camelCase` becomes `["camel", "case"]`;
101
+ `snake_case` becomes `["snake", "case"]`;
102
+ `PascalCase` becomes `["pascal", "case"]`.
103
+ This stage makes code searchable by individual
104
+ words within identifiers.
105
+
106
+ 5. **Case folding** -- Convert all tokens to
107
+ lowercase. `HTTP`, `Http`, and `http` all become
108
+ `http`.
109
+
110
+ 6. **Stop word removal** -- Remove common words with
111
+ no discriminative value (`the`, `is`, `and`,
112
+ etc.). The stop word list is configurable.
113
+
114
+ 7. **Stemming** -- Reduce words to root forms using
115
+ the Snowball/Porter2 stemmer. `running` and `runs`
116
+ both stem to `run`. Both the stemmed form and the
117
+ original are stored so exact-match queries still
118
+ work.
119
+
120
+ 8. **N-gram extraction** -- Generate bigrams and
121
+ trigrams for phrase indexing and multi-word term
122
+ matching. Supports phrase queries without requiring
123
+ a separate phrase index.
124
+
125
+ ### Per-Field Processing
126
+
127
+ During indexing, the pipeline runs once per field for
128
+ each section:
129
+
130
+ | Content source | Field |
131
+ |-------------------------|-----------|
132
+ | `title` attribute | heading |
133
+ | Body text + description | body |
134
+ | Code content | code |
135
+
136
+ Per-field term frequencies are stored separately in
137
+ the inverted index. This separation is what enables
138
+ BM25F field weights to be tuned at query time without
139
+ rebuilding the indexes.
140
+
141
+ ## BM25F Ranking
142
+
143
+ BM25F (Field-Weighted Okapi BM25) is the primary
144
+ ranking function. It is a probabilistic scoring model
145
+ from information retrieval that balances three
146
+ factors: how often a term appears in a section, how
147
+ rare the term is across the corpus, and how long the
148
+ section is relative to average.
149
+
150
+ ### The Three Fields
151
+
152
+ Each section is indexed across three fields, each
153
+ with a default weight:
154
+
155
+ | Field | Weight | Content source |
156
+ |-----------|--------|----------------------------|
157
+ | `heading` | 2.0x | Section `title` attribute |
158
+ | `body` | 1.0x | Body text + `description` |
159
+ | `code` | 1.5x | Code section content |
160
+
161
+ A term match in a heading contributes twice as much
162
+ to the score as the same match in body text. A match
163
+ in code contributes 1.5 times as much. This reflects
164
+ the intuition that titles are the strongest signal of
165
+ what a section is about.
166
+
167
+ ### How Scoring Works
168
+
169
+ For each query term `t` matched in section `d`:
170
+
171
+ 1. **IDF (Inverse Document Frequency)** -- Measures
172
+ how rare the term is across all sections. Common
173
+ terms like "function" get a low IDF; rare terms
174
+ like "kademlia" get a high IDF.
175
+
176
+ ```
177
+ idf(t) = ln((N - df(t) + 0.5) / (df(t) + 0.5) + 1)
178
+ ```
179
+
180
+ `N` = total indexed sections, `df(t)` = number of
181
+ sections containing `t`.
182
+
183
+ 2. **Per-field score** -- For each field `f` (heading,
184
+ body, code), compute a BM25 score using that
185
+ field's term frequency and average length:
186
+
187
+ ```
188
+ score_f(t, d) = idf(t)
189
+ * (tf_f(t,d) * (k1 + 1))
190
+ / (tf_f(t,d) + k1 * (1 - b + b * |d_f| / avgdl_f))
191
+ ```
192
+
193
+ Where:
194
+ - `tf_f(t,d)` = term frequency in field `f`
195
+ - `|d_f|` = token count of field `f` in section `d`
196
+ - `avgdl_f` = average token count of field `f`
197
+ across the corpus
198
+ - `k1` = 1.2 (term saturation parameter)
199
+ - `b` = 0.75 (length normalization parameter)
200
+
201
+ 3. **Weighted combination** -- Multiply each field
202
+ score by its weight and sum:
203
+
204
+ ```
205
+ bm25f(t, d) = 2.0 * score_heading(t, d)
206
+ + 1.0 * score_body(t, d)
207
+ + 1.5 * score_code(t, d)
208
+ ```
209
+
210
+ 4. **Total section score** -- Sum `bm25f(t, d)`
211
+ across all matched query terms.
212
+
213
+ ### Key Parameters
214
+
215
+ | Parameter | Default | Effect |
216
+ |------------------|---------|---------------------------|
217
+ | `k1` | 1.2 | Term saturation. Higher = more weight to repeated terms |
218
+ | `b` | 0.75 | Length normalization. 0 = none, 1 = full |
219
+ | `heading` weight | 2.0 | Title match influence |
220
+ | `body` weight | 1.0 | Body text match influence |
221
+ | `code` weight | 1.5 | Code match influence |
222
+
223
+ ### Why Per-Field Storage Matters
224
+
225
+ Per-field term frequencies (`heading_tf`, `body_tf`,
226
+ `code_tf`) are stored separately in the inverted
227
+ index rather than as a single aggregated value. This
228
+ means field weights can be changed at query time --
229
+ for example, boosting code matches to 3.0x for a
230
+ code-focused search -- without rebuilding any index
231
+ files.
232
+
233
+ ### Per-Field Length Normalization
234
+
235
+ Corpus statistics store per-field average lengths
236
+ (`avg_heading_length`, `avg_body_length`,
237
+ `avg_code_length`) alongside overall averages. This
238
+ prevents unfair comparisons: a 3-word title is
239
+ normalized against the average title length, not
240
+ against the average body length of 500 words.
241
+
242
+ ## Supplementary Scoring Signals
243
+
244
+ Three additional signals are added to the BM25F base
245
+ score:
246
+
247
+ ### Proximity Boost
248
+
249
+ Boosts sections where query terms appear close
250
+ together. For multi-term queries, the proximity
251
+ scorer computes the minimum distance between each
252
+ pair of query terms using their stored positions. A
253
+ smaller span produces a higher boost. Single-term
254
+ queries receive no proximity boost.
255
+
256
+ ```
257
+ final proximity = inverse of minimum span
258
+ across all query term pairs
259
+ ```
260
+
261
+ ### Zone Scoring
262
+
263
+ Structural weighting based on position within a
264
+ section. Terms in the first ~20% of a section (the
265
+ "heading zone") receive a higher weight than terms in
266
+ the rest of the body. The zone boundary is computed
267
+ as `0.2 * token_count` using the pre-computed total
268
+ token count. This approximates the idea that
269
+ headings and opening paragraphs carry more topical
270
+ signal.
271
+
272
+ ### Freshness Weighting (Optional)
273
+
274
+ Time-decay boost that favors recently added sections.
275
+ Uses exponential decay with a configurable half-life:
276
+ after one half-life period, the freshness score is
277
+ halved. Can be disabled entirely by omitting the
278
+ freshness weight from score combination.
279
+
280
+ ### Score Combination
281
+
282
+ ```
283
+ final_score = bm25f_score
284
+ + proximity_weight * proximity_score
285
+ + zone_weight * zone_score
286
+ + freshness_weight * freshness_score
287
+ ```
288
+
289
+ BM25F is always the dominant signal. Proximity, zone,
290
+ and freshness are additive boosts with configurable
291
+ weights. Results are sorted by `final_score`
292
+ descending; ties broken by kbid lexicographic order.
293
+
294
+ ## Keyword Extraction
295
+
296
+ When content is added to the knowledge base, the
297
+ system extracts searchable terms and computes
298
+ weights using statistical methods that require no
299
+ training data or external models.
300
+
301
+ ### RAKE (Rapid Automatic Keyword Extraction)
302
+
303
+ Splits text on stop words to identify candidate
304
+ keyphrases. Scores candidates by word degree divided
305
+ by frequency ratio -- multi-word phrases with
306
+ infrequent component words score highest. Runs on
307
+ both text and code content.
308
+
309
+ ### N-Gram Scoring
310
+
311
+ Bigrams and trigrams are scored by co-occurrence
312
+ frequency. High-frequency pairs and triples become
313
+ indexed terms, supporting multi-word searches without
314
+ exact phrase syntax.
315
+
316
+ ### TF-IDF Term Weighting
317
+
318
+ For each section, per-field term frequency (TF) is
319
+ computed separately for heading, body, and code
320
+ fields. These per-field TFs are combined with
321
+ corpus-wide inverse document frequency (IDF) to
322
+ produce weights reflecting each term's
323
+ discriminative value. The per-field TFs are stored in
324
+ the inverted index; IDF is computed from corpus
325
+ statistics at query time.
326
+
327
+ ### What Gets Indexed
328
+
329
+ | Section type | Body indexed? | Title indexed? | Description indexed? |
330
+ |----------------|---------------|----------------|----------------------|
331
+ | `text/*` | Yes (body) | Yes (heading) | Yes (body) |
332
+ | `image/*` | No | Yes (heading) | Yes (body) |
333
+
334
+ Image binary content is skipped entirely. Image
335
+ sections are searchable only through their `title`
336
+ (required) and `description` (optional) attributes.
337
+
338
+ ## Index Structure
339
+
340
+ All index files live in `.kbdb/indexes/`. They are
341
+ binary, derived, and rebuildable from source data.
342
+ The `.gitignore` at the database root excludes them
343
+ from version control.
344
+
345
+ ```
346
+ indexes/
347
+ +-- meta.toml human-readable metadata
348
+ +-- terms.idx inverted index (B+ tree)
349
+ +-- positions.idx positional index (B+ tree)
350
+ +-- corpus.dat corpus statistics (binary)
351
+ ```
352
+
353
+ ### `terms.idx` -- Inverted Index
354
+
355
+ B+ tree mapping normalized terms to posting lists.
356
+ Each posting entry contains:
357
+
358
+ | Field | Type | Description |
359
+ |--------------|-------|----------------------------|
360
+ | `kbid` | bytes | Section identifier |
361
+ | `heading_tf` | f32 | Term frequency in heading |
362
+ | `body_tf` | f32 | Term frequency in body |
363
+ | `code_tf` | f32 | Term frequency in code |
364
+
365
+ Supports term lookup, IDF computation (via document
366
+ frequency counts), and per-field term frequency
367
+ retrieval. O(log n) lookup via B+ tree structure.
368
+
369
+ ### `positions.idx` -- Positional Index
370
+
371
+ B+ tree mapping `(term, kbid)` pairs to arrays of
372
+ zero-based token offsets. Supports three functions:
373
+
374
+ - **Phrase queries** -- Verify that terms appear in
375
+ the correct order at consecutive positions.
376
+ - **Proximity scoring** -- Compute minimum span
377
+ between query terms.
378
+ - **Zone scoring** -- Determine whether a term falls
379
+ in the heading zone (first 20% of the section).
380
+
381
+ ### `corpus.dat` -- Corpus Statistics
382
+
383
+ Binary file storing corpus-wide aggregates:
384
+
385
+ | Field | Type | Description |
386
+ |-----------------------|------|--------------------------|
387
+ | `total_sections` | u32 | Indexed section count |
388
+ | `total_terms` | u32 | Distinct term count |
389
+ | `avg_section_length` | f32 | Average tokens/section |
390
+ | `avg_heading_length` | f32 | Average heading tokens |
391
+ | `avg_body_length` | f32 | Average body tokens |
392
+ | `avg_code_length` | f32 | Average code tokens |
393
+ | per-term doc freqs | -- | Per-term section counts |
394
+
395
+ Used for IDF computation and per-field BM25 length
396
+ normalization.
397
+
398
+ ### `meta.toml` -- Index Metadata
399
+
400
+ Human-readable TOML file with informational metadata
401
+ about the indexes:
402
+
403
+ - Last rebuild timestamp
404
+ - Total term count
405
+ - Corpus statistics summary
406
+
407
+ This file is informational only -- the binary index
408
+ files are the source of truth for search operations.
409
+
410
+ ## Ranking Pipeline (End to End)
411
+
412
+ ```
413
+ "auth token" (query string)
414
+ |
415
+ v
416
+ Tokenize + parse query (query-parser.wasm)
417
+ |
418
+ v
419
+ QueryPlan: terms=["auth","token"], mode=AND
420
+ |
421
+ v
422
+ Execute plan against indexes (fs-database.wasm)
423
+ |
424
+ v
425
+ Vec<RawMatch>: kbids + per-field TFs + positions
426
+ |
427
+ v
428
+ Fetch corpus statistics (fs-database.wasm)
429
+ |
430
+ v
431
+ Score: BM25F (query-parser.wasm)
432
+ Score: proximity (query-parser.wasm)
433
+ Score: zone (query-parser.wasm)
434
+ Score: freshness (optional) (query-parser.wasm)
435
+ |
436
+ v
437
+ Combine scores, sort descending, truncate
438
+ |
439
+ v
440
+ Shape into result mode (sections/documents/stats)
441
+ |
442
+ v
443
+ Ranked results
444
+ ```
445
+
446
+ `kb-worker.wasm` orchestrates the entire flow,
447
+ calling `query-parser.wasm` for text processing and
448
+ scoring, and `fs-database.wasm` for storage lookups.
449
+ Neither standalone module calls the other directly.
450
+
451
+ ## Result Modes
452
+
453
+ | Mode | Returns |
454
+ |-------------|-------------------------------------|
455
+ | `sections` | Individual matching sections with scores, snippets, and matched terms |
456
+ | `documents` | Documents containing matches, with aggregated scores and per-section details |
457
+ | `stats` | Match counts and top terms without content bodies |