ralph-hero-knowledge-index 0.1.45 → 0.1.46

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 (34) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.mcp.json +1 -1
  3. package/README.md +79 -0
  4. package/coverage/coverage-summary.json +3 -3
  5. package/coverage/lcov-report/chunker.ts.html +1 -1
  6. package/coverage/lcov-report/config.ts.html +1 -1
  7. package/coverage/lcov-report/db.ts.html +237 -51
  8. package/coverage/lcov-report/embedder.ts.html +1 -1
  9. package/coverage/lcov-report/file-scanner.ts.html +1 -1
  10. package/coverage/lcov-report/format.ts.html +1 -1
  11. package/coverage/lcov-report/generate-indexes.ts.html +1 -1
  12. package/coverage/lcov-report/graph-builder.ts.html +1 -1
  13. package/coverage/lcov-report/graph-tools.ts.html +8 -8
  14. package/coverage/lcov-report/hybrid-search.ts.html +6 -6
  15. package/coverage/lcov-report/ignore.ts.html +1 -1
  16. package/coverage/lcov-report/index.html +28 -28
  17. package/coverage/lcov-report/index.ts.html +294 -39
  18. package/coverage/lcov-report/llm-client.ts.html +1 -1
  19. package/coverage/lcov-report/parser.ts.html +1 -1
  20. package/coverage/lcov-report/reindex.ts.html +1 -1
  21. package/coverage/lcov-report/reranker.ts.html +7 -7
  22. package/coverage/lcov-report/search.ts.html +2 -2
  23. package/coverage/lcov-report/traverse.ts.html +2 -2
  24. package/coverage/lcov-report/vector-search.ts.html +3 -3
  25. package/coverage/lcov.info +423 -360
  26. package/dist/db.d.ts +20 -0
  27. package/dist/db.js +50 -0
  28. package/dist/db.js.map +1 -1
  29. package/dist/index.js +74 -2
  30. package/dist/index.js.map +1 -1
  31. package/package.json +1 -1
  32. package/src/__tests__/index.test.ts +341 -0
  33. package/src/db.ts +62 -0
  34. package/src/index.ts +87 -2
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ralph-knowledge",
3
- "version": "0.1.45",
3
+ "version": "0.1.46",
4
4
  "description": "Knowledge graph for ralph-hero: semantic search, relationship traversal, and document indexing across thoughts/ documents. Optional companion to ralph-hero.",
5
5
  "author": {
6
6
  "name": "Chad Dubiel",
package/.mcp.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "mcpServers": {
3
3
  "ralph-knowledge": {
4
4
  "command": "npx",
5
- "args": ["-y", "ralph-hero-knowledge-index@0.1.45"]
5
+ "args": ["-y", "ralph-hero-knowledge-index@0.1.46"]
6
6
  }
7
7
  }
8
8
  }
package/README.md CHANGED
@@ -142,6 +142,85 @@ gather, and ALSO calls `knowledge_search(type="research", ...)` for an explicit
142
142
  artifact lookup where it needs a precise type filter. Keeping both tools in a
143
143
  skill's allowlist is the recommended pattern.
144
144
 
145
+ ## `knowledge_expert` — domain-keyed memory bundles
146
+
147
+ `knowledge_expert(domain, issue_number, ...)` returns a curated context bundle
148
+ for a named domain — wiki entries, recent reflections, and prior outcomes — so
149
+ sub-agents become per-domain experts via memory rather than per-domain prompts.
150
+ It is the **domain-keyed** companion to `knowledge_recall`'s **role-keyed**
151
+ retrieval: role decides which tiers to surface; domain decides which slice of
152
+ the corpus.
153
+
154
+ ### Signature
155
+
156
+ ```
157
+ knowledge_expert(
158
+ domain: string, // Tag to match (e.g. "auth", "memory-tiers", "ralph-knowledge")
159
+ issue_number: number, // GitHub issue on whose behalf this call is made — required for telemetry
160
+ limit?: number, // Max entries per bucket. Default 5.
161
+ recency_window_days?: number, // Reflection age cutoff in days. Default 30.
162
+ path_prefix?: string, // Optional secondary filter: only docs whose path starts with this prefix.
163
+ session_id?: string, // Team/hero session ID — passed through to the outcome event.
164
+ )
165
+ ```
166
+
167
+ Domain matching uses the `tags` table (frontmatter `tags:` arrays are the
168
+ primary signal). `path_prefix` is a secondary narrowing filter — not a
169
+ replacement for tags. Pass `"thoughts/shared/"` to restrict to the shared
170
+ corpus, for example.
171
+
172
+ ### Return shape
173
+
174
+ ```json
175
+ {
176
+ "query_id": "uuid-v4",
177
+ "domain": "auth",
178
+ "wiki": [ ...DocumentRow ],
179
+ "reflections": [ ...DocumentRow ],
180
+ "prior_outcomes": [ ...OutcomeEventRow ],
181
+ "warning": null
182
+ }
183
+ ```
184
+
185
+ | Field | Description |
186
+ |-------|-------------|
187
+ | `query_id` | UUID generated per call. Save this and pass it to `knowledge_record_outcome` as `query_id` to correlate downstream outcomes back to this expert call. |
188
+ | `domain` | Echo of the requested domain. |
189
+ | `wiki` | Up to `limit` documents with `memory_tier = 'wiki'` tagged with `domain`, ordered by date descending. |
190
+ | `reflections` | Up to `limit` documents with `memory_tier = 'reflection'` tagged with `domain` and dated within `recency_window_days`. |
191
+ | `prior_outcomes` | Up to `limit` `outcome_events` rows whose `payload` JSON contains `domain` — pipeline history for this domain. |
192
+ | `warning` | Non-null string when both `wiki` and `reflections` are empty, suggesting the caller tag existing docs or broaden the domain term. `null` on a successful hit. |
193
+
194
+ ### Telemetry
195
+
196
+ Every `knowledge_expert` call writes an `outcome_events` row with
197
+ `event_type = 'expert_call'`. The `payload` JSON carries `query_id`, `domain`,
198
+ `returned_doc_ids`, `limit`, `recency_window_days`, `path_prefix`, and
199
+ `warning`. This makes per-domain hit rate queryable from day one:
200
+
201
+ ```
202
+ knowledge_query_outcomes({ event_type: "expert_call", aggregate: true })
203
+ ```
204
+
205
+ Pass `query_id` to `knowledge_record_outcome` to tie subsequent phase/research
206
+ outcomes back to the originating expert call:
207
+
208
+ ```
209
+ knowledge_record_outcome({
210
+ event_type: "research_completed",
211
+ issue_number: 1306,
212
+ query_id: "<query_id from knowledge_expert>",
213
+ verdict: "complete"
214
+ })
215
+ ```
216
+
217
+ ### Degradation
218
+
219
+ `knowledge_expert` degrades the same way as the other knowledge tools — return
220
+ an empty bundle with a `warning` when no matching documents exist; never throw
221
+ on a valid call. If the domain cannot be determined at call time, callers should
222
+ skip the call rather than pass an empty string.
223
+
145
224
  ## Environment variables
146
225
 
147
226
  | Variable | Purpose |
@@ -1,7 +1,7 @@
1
- {"total": {"lines":{"total":1489,"covered":1346,"skipped":0,"pct":90.39},"statements":{"total":1623,"covered":1453,"skipped":0,"pct":89.52},"functions":{"total":218,"covered":200,"skipped":0,"pct":91.74},"branches":{"total":936,"covered":762,"skipped":0,"pct":81.41},"branchesTrue":{"total":0,"covered":0,"skipped":0,"pct":"Unknown"}}
1
+ {"total": {"lines":{"total":1521,"covered":1375,"skipped":0,"pct":90.4},"statements":{"total":1655,"covered":1482,"skipped":0,"pct":89.54},"functions":{"total":223,"covered":205,"skipped":0,"pct":91.92},"branches":{"total":957,"covered":782,"skipped":0,"pct":81.71},"branchesTrue":{"total":0,"covered":0,"skipped":0,"pct":"Unknown"}}
2
2
  ,"/home/runner/work/ralph-hero/ralph-hero/plugin/ralph-knowledge/src/chunker.ts": {"lines":{"total":91,"covered":85,"skipped":0,"pct":93.4},"functions":{"total":6,"covered":6,"skipped":0,"pct":100},"statements":{"total":94,"covered":88,"skipped":0,"pct":93.61},"branches":{"total":45,"covered":39,"skipped":0,"pct":86.66}}
3
3
  ,"/home/runner/work/ralph-hero/ralph-hero/plugin/ralph-knowledge/src/config.ts": {"lines":{"total":34,"covered":32,"skipped":0,"pct":94.11},"functions":{"total":5,"covered":5,"skipped":0,"pct":100},"statements":{"total":36,"covered":34,"skipped":0,"pct":94.44},"branches":{"total":31,"covered":31,"skipped":0,"pct":100}}
4
- ,"/home/runner/work/ralph-hero/ralph-hero/plugin/ralph-knowledge/src/db.ts": {"lines":{"total":119,"covered":117,"skipped":0,"pct":98.31},"functions":{"total":31,"covered":31,"skipped":0,"pct":100},"statements":{"total":125,"covered":122,"skipped":0,"pct":97.6},"branches":{"total":78,"covered":69,"skipped":0,"pct":88.46}}
4
+ ,"/home/runner/work/ralph-hero/ralph-hero/plugin/ralph-knowledge/src/db.ts": {"lines":{"total":134,"covered":130,"skipped":0,"pct":97.01},"functions":{"total":33,"covered":33,"skipped":0,"pct":100},"statements":{"total":140,"covered":135,"skipped":0,"pct":96.42},"branches":{"total":85,"covered":75,"skipped":0,"pct":88.23}}
5
5
  ,"/home/runner/work/ralph-hero/ralph-hero/plugin/ralph-knowledge/src/embedder.ts": {"lines":{"total":55,"covered":52,"skipped":0,"pct":94.54},"functions":{"total":8,"covered":8,"skipped":0,"pct":100},"statements":{"total":60,"covered":57,"skipped":0,"pct":95},"branches":{"total":38,"covered":30,"skipped":0,"pct":78.94}}
6
6
  ,"/home/runner/work/ralph-hero/ralph-hero/plugin/ralph-knowledge/src/file-scanner.ts": {"lines":{"total":16,"covered":16,"skipped":0,"pct":100},"functions":{"total":2,"covered":2,"skipped":0,"pct":100},"statements":{"total":19,"covered":19,"skipped":0,"pct":100},"branches":{"total":21,"covered":21,"skipped":0,"pct":100}}
7
7
  ,"/home/runner/work/ralph-hero/ralph-hero/plugin/ralph-knowledge/src/format.ts": {"lines":{"total":6,"covered":6,"skipped":0,"pct":100},"functions":{"total":4,"covered":4,"skipped":0,"pct":100},"statements":{"total":8,"covered":8,"skipped":0,"pct":100},"branches":{"total":6,"covered":6,"skipped":0,"pct":100}}
@@ -10,7 +10,7 @@
10
10
  ,"/home/runner/work/ralph-hero/ralph-hero/plugin/ralph-knowledge/src/graph-tools.ts": {"lines":{"total":242,"covered":230,"skipped":0,"pct":95.04},"functions":{"total":34,"covered":32,"skipped":0,"pct":94.11},"statements":{"total":257,"covered":243,"skipped":0,"pct":94.55},"branches":{"total":151,"covered":120,"skipped":0,"pct":79.47}}
11
11
  ,"/home/runner/work/ralph-hero/ralph-hero/plugin/ralph-knowledge/src/hybrid-search.ts": {"lines":{"total":174,"covered":173,"skipped":0,"pct":99.42},"functions":{"total":19,"covered":19,"skipped":0,"pct":100},"statements":{"total":203,"covered":195,"skipped":0,"pct":96.05},"branches":{"total":129,"covered":110,"skipped":0,"pct":85.27}}
12
12
  ,"/home/runner/work/ralph-hero/ralph-hero/plugin/ralph-knowledge/src/ignore.ts": {"lines":{"total":17,"covered":16,"skipped":0,"pct":94.11},"functions":{"total":2,"covered":2,"skipped":0,"pct":100},"statements":{"total":19,"covered":18,"skipped":0,"pct":94.73},"branches":{"total":12,"covered":11,"skipped":0,"pct":91.66}}
13
- ,"/home/runner/work/ralph-hero/ralph-hero/plugin/ralph-knowledge/src/index.ts": {"lines":{"total":183,"covered":162,"skipped":0,"pct":88.52},"functions":{"total":26,"covered":22,"skipped":0,"pct":84.61},"statements":{"total":202,"covered":177,"skipped":0,"pct":87.62},"branches":{"total":122,"covered":84,"skipped":0,"pct":68.85}}
13
+ ,"/home/runner/work/ralph-hero/ralph-hero/plugin/ralph-knowledge/src/index.ts": {"lines":{"total":200,"covered":178,"skipped":0,"pct":89},"functions":{"total":29,"covered":25,"skipped":0,"pct":86.2},"statements":{"total":219,"covered":193,"skipped":0,"pct":88.12},"branches":{"total":136,"covered":98,"skipped":0,"pct":72.05}}
14
14
  ,"/home/runner/work/ralph-hero/ralph-hero/plugin/ralph-knowledge/src/llm-client.ts": {"lines":{"total":31,"covered":31,"skipped":0,"pct":100},"functions":{"total":6,"covered":5,"skipped":0,"pct":83.33},"statements":{"total":33,"covered":32,"skipped":0,"pct":96.96},"branches":{"total":13,"covered":13,"skipped":0,"pct":100}}
15
15
  ,"/home/runner/work/ralph-hero/ralph-hero/plugin/ralph-knowledge/src/parser.ts": {"lines":{"total":49,"covered":49,"skipped":0,"pct":100},"functions":{"total":5,"covered":5,"skipped":0,"pct":100},"statements":{"total":53,"covered":53,"skipped":0,"pct":100},"branches":{"total":46,"covered":41,"skipped":0,"pct":89.13}}
16
16
  ,"/home/runner/work/ralph-hero/ralph-hero/plugin/ralph-knowledge/src/reindex.ts": {"lines":{"total":241,"covered":157,"skipped":0,"pct":65.14},"functions":{"total":18,"covered":9,"skipped":0,"pct":50},"statements":{"total":258,"covered":164,"skipped":0,"pct":63.56},"branches":{"total":114,"covered":74,"skipped":0,"pct":64.91}}
@@ -940,7 +940,7 @@ export function chunkText(text: string, opts: ChunkerOptions = {}): Chunk[] {
940
940
  <div class='footer quiet pad2 space-top1 center small'>
941
941
  Code coverage generated by
942
942
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
943
- at 2026-05-19T02:12:26.778Z
943
+ at 2026-05-22T03:06:22.333Z
944
944
  </div>
945
945
  <script src="prettify.js"></script>
946
946
  <script>
@@ -385,7 +385,7 @@ export function loadConfig(): KnowledgeConfig {
385
385
  <div class='footer quiet pad2 space-top1 center small'>
386
386
  Code coverage generated by
387
387
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
388
- at 2026-05-19T02:12:26.778Z
388
+ at 2026-05-22T03:06:22.333Z
389
389
  </div>
390
390
  <script src="prettify.js"></script>
391
391
  <script>
@@ -23,30 +23,30 @@
23
23
  <div class='clearfix'>
24
24
 
25
25
  <div class='fl pad1y space-right2'>
26
- <span class="strong">97.6% </span>
26
+ <span class="strong">96.42% </span>
27
27
  <span class="quiet">Statements</span>
28
- <span class='fraction'>122/125</span>
28
+ <span class='fraction'>135/140</span>
29
29
  </div>
30
30
 
31
31
 
32
32
  <div class='fl pad1y space-right2'>
33
- <span class="strong">88.46% </span>
33
+ <span class="strong">88.23% </span>
34
34
  <span class="quiet">Branches</span>
35
- <span class='fraction'>69/78</span>
35
+ <span class='fraction'>75/85</span>
36
36
  </div>
37
37
 
38
38
 
39
39
  <div class='fl pad1y space-right2'>
40
40
  <span class="strong">100% </span>
41
41
  <span class="quiet">Functions</span>
42
- <span class='fraction'>31/31</span>
42
+ <span class='fraction'>33/33</span>
43
43
  </div>
44
44
 
45
45
 
46
46
  <div class='fl pad1y space-right2'>
47
- <span class="strong">98.31% </span>
47
+ <span class="strong">97.01% </span>
48
48
  <span class="quiet">Lines</span>
49
- <span class='fraction'>117/119</span>
49
+ <span class='fraction'>130/134</span>
50
50
  </div>
51
51
 
52
52
 
@@ -610,7 +610,69 @@
610
610
  <a name='L545'></a><a href='#L545'>545</a>
611
611
  <a name='L546'></a><a href='#L546'>546</a>
612
612
  <a name='L547'></a><a href='#L547'>547</a>
613
- <a name='L548'></a><a href='#L548'>548</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
613
+ <a name='L548'></a><a href='#L548'>548</a>
614
+ <a name='L549'></a><a href='#L549'>549</a>
615
+ <a name='L550'></a><a href='#L550'>550</a>
616
+ <a name='L551'></a><a href='#L551'>551</a>
617
+ <a name='L552'></a><a href='#L552'>552</a>
618
+ <a name='L553'></a><a href='#L553'>553</a>
619
+ <a name='L554'></a><a href='#L554'>554</a>
620
+ <a name='L555'></a><a href='#L555'>555</a>
621
+ <a name='L556'></a><a href='#L556'>556</a>
622
+ <a name='L557'></a><a href='#L557'>557</a>
623
+ <a name='L558'></a><a href='#L558'>558</a>
624
+ <a name='L559'></a><a href='#L559'>559</a>
625
+ <a name='L560'></a><a href='#L560'>560</a>
626
+ <a name='L561'></a><a href='#L561'>561</a>
627
+ <a name='L562'></a><a href='#L562'>562</a>
628
+ <a name='L563'></a><a href='#L563'>563</a>
629
+ <a name='L564'></a><a href='#L564'>564</a>
630
+ <a name='L565'></a><a href='#L565'>565</a>
631
+ <a name='L566'></a><a href='#L566'>566</a>
632
+ <a name='L567'></a><a href='#L567'>567</a>
633
+ <a name='L568'></a><a href='#L568'>568</a>
634
+ <a name='L569'></a><a href='#L569'>569</a>
635
+ <a name='L570'></a><a href='#L570'>570</a>
636
+ <a name='L571'></a><a href='#L571'>571</a>
637
+ <a name='L572'></a><a href='#L572'>572</a>
638
+ <a name='L573'></a><a href='#L573'>573</a>
639
+ <a name='L574'></a><a href='#L574'>574</a>
640
+ <a name='L575'></a><a href='#L575'>575</a>
641
+ <a name='L576'></a><a href='#L576'>576</a>
642
+ <a name='L577'></a><a href='#L577'>577</a>
643
+ <a name='L578'></a><a href='#L578'>578</a>
644
+ <a name='L579'></a><a href='#L579'>579</a>
645
+ <a name='L580'></a><a href='#L580'>580</a>
646
+ <a name='L581'></a><a href='#L581'>581</a>
647
+ <a name='L582'></a><a href='#L582'>582</a>
648
+ <a name='L583'></a><a href='#L583'>583</a>
649
+ <a name='L584'></a><a href='#L584'>584</a>
650
+ <a name='L585'></a><a href='#L585'>585</a>
651
+ <a name='L586'></a><a href='#L586'>586</a>
652
+ <a name='L587'></a><a href='#L587'>587</a>
653
+ <a name='L588'></a><a href='#L588'>588</a>
654
+ <a name='L589'></a><a href='#L589'>589</a>
655
+ <a name='L590'></a><a href='#L590'>590</a>
656
+ <a name='L591'></a><a href='#L591'>591</a>
657
+ <a name='L592'></a><a href='#L592'>592</a>
658
+ <a name='L593'></a><a href='#L593'>593</a>
659
+ <a name='L594'></a><a href='#L594'>594</a>
660
+ <a name='L595'></a><a href='#L595'>595</a>
661
+ <a name='L596'></a><a href='#L596'>596</a>
662
+ <a name='L597'></a><a href='#L597'>597</a>
663
+ <a name='L598'></a><a href='#L598'>598</a>
664
+ <a name='L599'></a><a href='#L599'>599</a>
665
+ <a name='L600'></a><a href='#L600'>600</a>
666
+ <a name='L601'></a><a href='#L601'>601</a>
667
+ <a name='L602'></a><a href='#L602'>602</a>
668
+ <a name='L603'></a><a href='#L603'>603</a>
669
+ <a name='L604'></a><a href='#L604'>604</a>
670
+ <a name='L605'></a><a href='#L605'>605</a>
671
+ <a name='L606'></a><a href='#L606'>606</a>
672
+ <a name='L607'></a><a href='#L607'>607</a>
673
+ <a name='L608'></a><a href='#L608'>608</a>
674
+ <a name='L609'></a><a href='#L609'>609</a>
675
+ <a name='L610'></a><a href='#L610'>610</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
614
676
  <span class="cline-any cline-neutral">&nbsp;</span>
615
677
  <span class="cline-any cline-neutral">&nbsp;</span>
616
678
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -705,15 +767,15 @@
705
767
  <span class="cline-any cline-neutral">&nbsp;</span>
706
768
  <span class="cline-any cline-neutral">&nbsp;</span>
707
769
  <span class="cline-any cline-neutral">&nbsp;</span>
708
- <span class="cline-any cline-yes">387x</span>
709
- <span class="cline-any cline-yes">387x</span>
710
- <span class="cline-any cline-yes">387x</span>
711
- <span class="cline-any cline-yes">387x</span>
712
770
  <span class="cline-any cline-neutral">&nbsp;</span>
771
+ <span class="cline-any cline-yes">399x</span>
772
+ <span class="cline-any cline-yes">399x</span>
773
+ <span class="cline-any cline-yes">399x</span>
774
+ <span class="cline-any cline-yes">399x</span>
713
775
  <span class="cline-any cline-neutral">&nbsp;</span>
714
776
  <span class="cline-any cline-neutral">&nbsp;</span>
715
- <span class="cline-any cline-yes">387x</span>
716
777
  <span class="cline-any cline-neutral">&nbsp;</span>
778
+ <span class="cline-any cline-yes">399x</span>
717
779
  <span class="cline-any cline-neutral">&nbsp;</span>
718
780
  <span class="cline-any cline-neutral">&nbsp;</span>
719
781
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -789,9 +851,9 @@
789
851
  <span class="cline-any cline-neutral">&nbsp;</span>
790
852
  <span class="cline-any cline-neutral">&nbsp;</span>
791
853
  <span class="cline-any cline-neutral">&nbsp;</span>
792
- <span class="cline-any cline-yes">387x</span>
793
- <span class="cline-any cline-yes">387x</span>
794
854
  <span class="cline-any cline-neutral">&nbsp;</span>
855
+ <span class="cline-any cline-yes">399x</span>
856
+ <span class="cline-any cline-yes">399x</span>
795
857
  <span class="cline-any cline-neutral">&nbsp;</span>
796
858
  <span class="cline-any cline-neutral">&nbsp;</span>
797
859
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -799,15 +861,15 @@
799
861
  <span class="cline-any cline-neutral">&nbsp;</span>
800
862
  <span class="cline-any cline-neutral">&nbsp;</span>
801
863
  <span class="cline-any cline-neutral">&nbsp;</span>
802
- <span class="cline-any cline-yes">387x</span>
803
- <span class="cline-any cline-yes">387x</span>
804
864
  <span class="cline-any cline-neutral">&nbsp;</span>
865
+ <span class="cline-any cline-yes">399x</span>
866
+ <span class="cline-any cline-yes">399x</span>
805
867
  <span class="cline-any cline-neutral">&nbsp;</span>
806
868
  <span class="cline-any cline-neutral">&nbsp;</span>
807
869
  <span class="cline-any cline-neutral">&nbsp;</span>
808
870
  <span class="cline-any cline-neutral">&nbsp;</span>
809
- <span class="cline-any cline-yes">387x</span>
810
871
  <span class="cline-any cline-neutral">&nbsp;</span>
872
+ <span class="cline-any cline-yes">399x</span>
811
873
  <span class="cline-any cline-neutral">&nbsp;</span>
812
874
  <span class="cline-any cline-neutral">&nbsp;</span>
813
875
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -815,11 +877,12 @@
815
877
  <span class="cline-any cline-neutral">&nbsp;</span>
816
878
  <span class="cline-any cline-neutral">&nbsp;</span>
817
879
  <span class="cline-any cline-neutral">&nbsp;</span>
818
- <span class="cline-any cline-yes">387x</span>
819
- <span class="cline-any cline-yes">387x</span>
820
880
  <span class="cline-any cline-neutral">&nbsp;</span>
881
+ <span class="cline-any cline-yes">399x</span>
882
+ <span class="cline-any cline-yes">399x</span>
821
883
  <span class="cline-any cline-neutral">&nbsp;</span>
822
- <span class="cline-any cline-yes">387x</span>
884
+ <span class="cline-any cline-neutral">&nbsp;</span>
885
+ <span class="cline-any cline-yes">399x</span>
823
886
  <span class="cline-any cline-yes">1x</span>
824
887
  <span class="cline-any cline-neutral">&nbsp;</span>
825
888
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -847,8 +910,8 @@
847
910
  <span class="cline-any cline-neutral">&nbsp;</span>
848
911
  <span class="cline-any cline-neutral">&nbsp;</span>
849
912
  <span class="cline-any cline-neutral">&nbsp;</span>
850
- <span class="cline-any cline-yes">387x</span>
851
- <span class="cline-any cline-yes">387x</span>
913
+ <span class="cline-any cline-yes">399x</span>
914
+ <span class="cline-any cline-yes">399x</span>
852
915
  <span class="cline-any cline-neutral">&nbsp;</span>
853
916
  <span class="cline-any cline-yes">2x</span>
854
917
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -875,11 +938,11 @@
875
938
  <span class="cline-any cline-neutral">&nbsp;</span>
876
939
  <span class="cline-any cline-neutral">&nbsp;</span>
877
940
  <span class="cline-any cline-neutral">&nbsp;</span>
878
- <span class="cline-any cline-yes">968x</span>
941
+ <span class="cline-any cline-yes">975x</span>
879
942
  <span class="cline-any cline-neutral">&nbsp;</span>
880
943
  <span class="cline-any cline-neutral">&nbsp;</span>
881
944
  <span class="cline-any cline-neutral">&nbsp;</span>
882
- <span class="cline-any cline-yes">968x</span>
945
+ <span class="cline-any cline-yes">975x</span>
883
946
  <span class="cline-any cline-neutral">&nbsp;</span>
884
947
  <span class="cline-any cline-neutral">&nbsp;</span>
885
948
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -907,9 +970,9 @@
907
970
  <span class="cline-any cline-neutral">&nbsp;</span>
908
971
  <span class="cline-any cline-neutral">&nbsp;</span>
909
972
  <span class="cline-any cline-neutral">&nbsp;</span>
910
- <span class="cline-any cline-yes">171x</span>
911
- <span class="cline-any cline-yes">171x</span>
912
- <span class="cline-any cline-yes">300x</span>
973
+ <span class="cline-any cline-yes">178x</span>
974
+ <span class="cline-any cline-yes">178x</span>
975
+ <span class="cline-any cline-yes">308x</span>
913
976
  <span class="cline-any cline-neutral">&nbsp;</span>
914
977
  <span class="cline-any cline-neutral">&nbsp;</span>
915
978
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -917,6 +980,43 @@
917
980
  <span class="cline-any cline-neutral">&nbsp;</span>
918
981
  <span class="cline-any cline-neutral">&nbsp;</span>
919
982
  <span class="cline-any cline-neutral">&nbsp;</span>
983
+ <span class="cline-any cline-neutral">&nbsp;</span>
984
+ <span class="cline-any cline-neutral">&nbsp;</span>
985
+ <span class="cline-any cline-neutral">&nbsp;</span>
986
+ <span class="cline-any cline-neutral">&nbsp;</span>
987
+ <span class="cline-any cline-neutral">&nbsp;</span>
988
+ <span class="cline-any cline-neutral">&nbsp;</span>
989
+ <span class="cline-any cline-neutral">&nbsp;</span>
990
+ <span class="cline-any cline-neutral">&nbsp;</span>
991
+ <span class="cline-any cline-neutral">&nbsp;</span>
992
+ <span class="cline-any cline-neutral">&nbsp;</span>
993
+ <span class="cline-any cline-neutral">&nbsp;</span>
994
+ <span class="cline-any cline-yes">22x</span>
995
+ <span class="cline-any cline-yes">22x</span>
996
+ <span class="cline-any cline-neutral">&nbsp;</span>
997
+ <span class="cline-any cline-yes">22x</span>
998
+ <span class="cline-any cline-no">&nbsp;</span>
999
+ <span class="cline-any cline-no">&nbsp;</span>
1000
+ <span class="cline-any cline-neutral">&nbsp;</span>
1001
+ <span class="cline-any cline-yes">22x</span>
1002
+ <span class="cline-any cline-yes">11x</span>
1003
+ <span class="cline-any cline-yes">11x</span>
1004
+ <span class="cline-any cline-neutral">&nbsp;</span>
1005
+ <span class="cline-any cline-neutral">&nbsp;</span>
1006
+ <span class="cline-any cline-yes">22x</span>
1007
+ <span class="cline-any cline-neutral">&nbsp;</span>
1008
+ <span class="cline-any cline-neutral">&nbsp;</span>
1009
+ <span class="cline-any cline-neutral">&nbsp;</span>
1010
+ <span class="cline-any cline-neutral">&nbsp;</span>
1011
+ <span class="cline-any cline-neutral">&nbsp;</span>
1012
+ <span class="cline-any cline-neutral">&nbsp;</span>
1013
+ <span class="cline-any cline-neutral">&nbsp;</span>
1014
+ <span class="cline-any cline-neutral">&nbsp;</span>
1015
+ <span class="cline-any cline-neutral">&nbsp;</span>
1016
+ <span class="cline-any cline-yes">22x</span>
1017
+ <span class="cline-any cline-neutral">&nbsp;</span>
1018
+ <span class="cline-any cline-neutral">&nbsp;</span>
1019
+ <span class="cline-any cline-neutral">&nbsp;</span>
920
1020
  <span class="cline-any cline-yes">341x</span>
921
1021
  <span class="cline-any cline-neutral">&nbsp;</span>
922
1022
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -929,11 +1029,11 @@
929
1029
  <span class="cline-any cline-neutral">&nbsp;</span>
930
1030
  <span class="cline-any cline-neutral">&nbsp;</span>
931
1031
  <span class="cline-any cline-neutral">&nbsp;</span>
932
- <span class="cline-any cline-yes">28x</span>
933
- <span class="cline-any cline-yes">28x</span>
934
- <span class="cline-any cline-yes">28x</span>
1032
+ <span class="cline-any cline-yes">50x</span>
1033
+ <span class="cline-any cline-yes">50x</span>
1034
+ <span class="cline-any cline-yes">50x</span>
935
1035
  <span class="cline-any cline-neutral">&nbsp;</span>
936
- <span class="cline-any cline-yes">28x</span>
1036
+ <span class="cline-any cline-yes">50x</span>
937
1037
  <span class="cline-any cline-neutral">&nbsp;</span>
938
1038
  <span class="cline-any cline-neutral">&nbsp;</span>
939
1039
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -953,56 +1053,80 @@
953
1053
  <span class="cline-any cline-neutral">&nbsp;</span>
954
1054
  <span class="cline-any cline-neutral">&nbsp;</span>
955
1055
  <span class="cline-any cline-neutral">&nbsp;</span>
956
- <span class="cline-any cline-yes">28x</span>
1056
+ <span class="cline-any cline-yes">50x</span>
957
1057
  <span class="cline-any cline-neutral">&nbsp;</span>
958
1058
  <span class="cline-any cline-neutral">&nbsp;</span>
959
1059
  <span class="cline-any cline-neutral">&nbsp;</span>
960
- <span class="cline-any cline-yes">18x</span>
961
- <span class="cline-any cline-yes">18x</span>
1060
+ <span class="cline-any cline-yes">32x</span>
1061
+ <span class="cline-any cline-yes">32x</span>
962
1062
  <span class="cline-any cline-neutral">&nbsp;</span>
963
- <span class="cline-any cline-yes">18x</span>
964
- <span class="cline-any cline-yes">9x</span>
965
- <span class="cline-any cline-yes">9x</span>
1063
+ <span class="cline-any cline-yes">32x</span>
1064
+ <span class="cline-any cline-yes">12x</span>
1065
+ <span class="cline-any cline-yes">12x</span>
966
1066
  <span class="cline-any cline-neutral">&nbsp;</span>
967
- <span class="cline-any cline-yes">18x</span>
968
- <span class="cline-any cline-yes">2x</span>
969
- <span class="cline-any cline-yes">2x</span>
1067
+ <span class="cline-any cline-yes">32x</span>
1068
+ <span class="cline-any cline-yes">4x</span>
1069
+ <span class="cline-any cline-yes">4x</span>
970
1070
  <span class="cline-any cline-neutral">&nbsp;</span>
971
- <span class="cline-any cline-yes">18x</span>
1071
+ <span class="cline-any cline-yes">32x</span>
972
1072
  <span class="cline-any cline-yes">2x</span>
973
1073
  <span class="cline-any cline-yes">2x</span>
974
1074
  <span class="cline-any cline-neutral">&nbsp;</span>
975
- <span class="cline-any cline-yes">18x</span>
1075
+ <span class="cline-any cline-yes">32x</span>
976
1076
  <span class="cline-any cline-yes">1x</span>
977
1077
  <span class="cline-any cline-yes">1x</span>
978
1078
  <span class="cline-any cline-neutral">&nbsp;</span>
979
- <span class="cline-any cline-yes">18x</span>
1079
+ <span class="cline-any cline-yes">32x</span>
980
1080
  <span class="cline-any cline-yes">1x</span>
981
1081
  <span class="cline-any cline-yes">1x</span>
982
1082
  <span class="cline-any cline-neutral">&nbsp;</span>
983
- <span class="cline-any cline-yes">18x</span>
1083
+ <span class="cline-any cline-yes">32x</span>
984
1084
  <span class="cline-any cline-no">&nbsp;</span>
985
1085
  <span class="cline-any cline-no">&nbsp;</span>
986
1086
  <span class="cline-any cline-neutral">&nbsp;</span>
987
- <span class="cline-any cline-yes">18x</span>
1087
+ <span class="cline-any cline-yes">32x</span>
988
1088
  <span class="cline-any cline-yes">2x</span>
989
1089
  <span class="cline-any cline-yes">2x</span>
990
1090
  <span class="cline-any cline-neutral">&nbsp;</span>
1091
+ <span class="cline-any cline-yes">32x</span>
1092
+ <span class="cline-any cline-yes">11x</span>
1093
+ <span class="cline-any cline-yes">11x</span>
1094
+ <span class="cline-any cline-neutral">&nbsp;</span>
1095
+ <span class="cline-any cline-neutral">&nbsp;</span>
1096
+ <span class="cline-any cline-yes">32x</span>
1097
+ <span class="cline-any cline-yes">32x</span>
1098
+ <span class="cline-any cline-neutral">&nbsp;</span>
1099
+ <span class="cline-any cline-yes">32x</span>
1100
+ <span class="cline-any cline-neutral">&nbsp;</span>
1101
+ <span class="cline-any cline-neutral">&nbsp;</span>
991
1102
  <span class="cline-any cline-neutral">&nbsp;</span>
992
- <span class="cline-any cline-yes">18x</span>
993
- <span class="cline-any cline-yes">18x</span>
994
1103
  <span class="cline-any cline-neutral">&nbsp;</span>
995
- <span class="cline-any cline-yes">18x</span>
996
1104
  <span class="cline-any cline-neutral">&nbsp;</span>
997
1105
  <span class="cline-any cline-neutral">&nbsp;</span>
998
1106
  <span class="cline-any cline-neutral">&nbsp;</span>
999
1107
  <span class="cline-any cline-neutral">&nbsp;</span>
1000
1108
  <span class="cline-any cline-neutral">&nbsp;</span>
1109
+ <span class="cline-any cline-yes">32x</span>
1001
1110
  <span class="cline-any cline-neutral">&nbsp;</span>
1002
1111
  <span class="cline-any cline-neutral">&nbsp;</span>
1003
1112
  <span class="cline-any cline-neutral">&nbsp;</span>
1004
1113
  <span class="cline-any cline-neutral">&nbsp;</span>
1005
- <span class="cline-any cline-yes">18x</span>
1114
+ <span class="cline-any cline-neutral">&nbsp;</span>
1115
+ <span class="cline-any cline-neutral">&nbsp;</span>
1116
+ <span class="cline-any cline-neutral">&nbsp;</span>
1117
+ <span class="cline-any cline-neutral">&nbsp;</span>
1118
+ <span class="cline-any cline-neutral">&nbsp;</span>
1119
+ <span class="cline-any cline-yes">3x</span>
1120
+ <span class="cline-any cline-neutral">&nbsp;</span>
1121
+ <span class="cline-any cline-neutral">&nbsp;</span>
1122
+ <span class="cline-any cline-neutral">&nbsp;</span>
1123
+ <span class="cline-any cline-neutral">&nbsp;</span>
1124
+ <span class="cline-any cline-neutral">&nbsp;</span>
1125
+ <span class="cline-any cline-neutral">&nbsp;</span>
1126
+ <span class="cline-any cline-neutral">&nbsp;</span>
1127
+ <span class="cline-any cline-neutral">&nbsp;</span>
1128
+ <span class="cline-any cline-neutral">&nbsp;</span>
1129
+ <span class="cline-any cline-yes">3x</span>
1006
1130
  <span class="cline-any cline-neutral">&nbsp;</span>
1007
1131
  <span class="cline-any cline-neutral">&nbsp;</span>
1008
1132
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -1228,6 +1352,7 @@ export interface OutcomeQueryParams {
1228
1352
  verdict?: string;
1229
1353
  sessionId?: string;
1230
1354
  since?: string;
1355
+ domain?: string;
1231
1356
  limit?: number;
1232
1357
  }
1233
1358
  &nbsp;
@@ -1462,6 +1587,43 @@ export class KnowledgeDB {
1462
1587
  getTags(docId: string): string[] {
1463
1588
  return (this.db.prepare("SELECT tag FROM tags WHERE doc_id = ? ORDER BY tag").all(docId) as Array&lt;{ tag: string }&gt;).map(r =&gt; r.tag);
1464
1589
  }
1590
+ &nbsp;
1591
+ /**
1592
+ * Return documents matching a domain tag and memory tier.
1593
+ * Joins documents ↔ tags so the domain (frontmatter tag) is the primary
1594
+ * signal. Optional pathPrefix and sinceDate narrow further.
1595
+ */
1596
+ queryByDomain(params: {
1597
+ domain: string;
1598
+ memoryTier: "wiki" | "reflection" | "doc" | "raw";
1599
+ limit: number;
1600
+ pathPrefix?: string;
1601
+ sinceDate?: string;
1602
+ }): DocumentRow[] {
1603
+ const conditions: string[] = ["t.tag = ?", "d.memory_tier = ?"];
1604
+ const values: unknown[] = [params.domain, params.memoryTier];
1605
+ &nbsp;
1606
+ <span class="missing-if-branch" title="if path not taken" >I</span>if (params.pathPrefix !== undefined) {
1607
+ <span class="cstat-no" title="statement not covered" > conditions.push("d.path LIKE ?");</span>
1608
+ <span class="cstat-no" title="statement not covered" > values.push(`${params.pathPrefix}%`);</span>
1609
+ }
1610
+ if (params.sinceDate !== undefined) {
1611
+ conditions.push("d.date &gt;= ?");
1612
+ values.push(params.sinceDate);
1613
+ }
1614
+ &nbsp;
1615
+ const sql = `
1616
+ SELECT DISTINCT d.id, d.path, d.title, d.date, d.type, d.status,
1617
+ d.github_issue AS githubIssue, d.content, d.is_stub AS isStub
1618
+ FROM documents d
1619
+ JOIN tags t ON t.doc_id = d.id
1620
+ WHERE ${conditions.join(" AND ")}
1621
+ ORDER BY d.date DESC NULLS LAST
1622
+ LIMIT ?
1623
+ `;
1624
+ &nbsp;
1625
+ return this.db.prepare(sql).all(...values, params.limit) as DocumentRow[];
1626
+ }
1465
1627
  &nbsp;
1466
1628
  addRelationship(sourceId: string, targetId: string, type: string, context?: string): void {
1467
1629
  this.db.prepare("INSERT OR IGNORE INTO relationships (source_id, target_id, type, context) VALUES (?, ?, ?, ?)").run(sourceId, targetId, type, context ?? null);
@@ -1535,6 +1697,10 @@ export class KnowledgeDB {
1535
1697
  conditions.push("timestamp &gt;= ?");
1536
1698
  values.push(params.since);
1537
1699
  }
1700
+ if (params.domain !== undefined) {
1701
+ conditions.push("json_extract(payload, '$.domain') = ?");
1702
+ values.push(params.domain);
1703
+ }
1538
1704
  &nbsp;
1539
1705
  const where = conditions.length &gt; 0 ? `WHERE ${conditions.join(" AND ")}` : "";
1540
1706
  const limit = params.limit ?? 50;
@@ -1551,6 +1717,26 @@ export class KnowledgeDB {
1551
1717
  &nbsp;
1552
1718
  return this.db.prepare(sql).all(...values, limit) as OutcomeEventRow[];
1553
1719
  }
1720
+ &nbsp;
1721
+ /**
1722
+ * Return outcome events whose payload JSON contains a matching `query_id`.
1723
+ * Uses SQLite's JSON1 `json_extract()` function (available by default in
1724
+ * better-sqlite3). Rows with malformed payload JSON silently return NULL
1725
+ * from json_extract and are excluded — the desired behavior.
1726
+ */
1727
+ queryOutcomeEventsByQueryId(queryId: string, limit = 50): OutcomeEventRow[] {
1728
+ const sql = `
1729
+ SELECT id, event_type AS eventType, issue_number AS issueNumber, session_id AS sessionId,
1730
+ timestamp, duration_ms AS durationMs, verdict, component_area AS componentArea,
1731
+ estimate, drift_count AS driftCount, model, agent_type AS agentType,
1732
+ iteration_count AS iterationCount, payload
1733
+ FROM outcome_events
1734
+ WHERE json_extract(payload, '$.query_id') = ?
1735
+ ORDER BY timestamp DESC
1736
+ LIMIT ?
1737
+ `;
1738
+ return this.db.prepare(sql).all(queryId, limit) as OutcomeEventRow[];
1739
+ }
1554
1740
  &nbsp;
1555
1741
  aggregateOutcomeEvents(params: OutcomeQueryParams = {}): OutcomeAggregate {
1556
1742
  // Override limit to aggregate over all matching events, not just the caller's limit
@@ -1711,7 +1897,7 @@ export class KnowledgeDB {
1711
1897
  <div class='footer quiet pad2 space-top1 center small'>
1712
1898
  Code coverage generated by
1713
1899
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
1714
- at 2026-05-19T02:12:26.778Z
1900
+ at 2026-05-22T03:06:22.333Z
1715
1901
  </div>
1716
1902
  <script src="prettify.js"></script>
1717
1903
  <script>