@avantf/dsh-mem 0.6.4 → 0.8.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 (150) hide show
  1. package/README.md +19 -10
  2. package/lib/client.js +1665 -3466
  3. package/lib/dsh-build.json +6 -5
  4. package/lib/engine/graph-recall/in-memory.d.ts +93 -0
  5. package/lib/engine/graph-recall/index.d.ts +22 -0
  6. package/lib/engine/graph-recall/traverse.d.ts +119 -0
  7. package/lib/engine/graph-recall/types.d.ts +188 -0
  8. package/lib/engine/mem/config/config_files.d.ts +1 -1
  9. package/lib/engine/mem/config/loader.d.ts +2 -3
  10. package/lib/engine/mem/config/paths.d.ts +2 -13
  11. package/lib/engine/mem/db/dao/contradictions.d.ts +4 -22
  12. package/lib/engine/mem/db/dao/entities.d.ts +80 -7
  13. package/lib/engine/mem/db/dao/fact_sources.d.ts +3 -13
  14. package/lib/engine/mem/db/dao/facts.d.ts +311 -56
  15. package/lib/engine/mem/db/dao/triples.d.ts +1 -26
  16. package/lib/engine/mem/db/graph.d.ts +249 -0
  17. package/lib/engine/mem/db/journal_notes.d.ts +35 -0
  18. package/lib/engine/mem/db/migrations.d.ts +9 -0
  19. package/lib/engine/mem/db/port.d.ts +22 -3
  20. package/lib/engine/mem/db/schema.d.ts +119 -0
  21. package/lib/engine/mem/db/tokenizer.d.ts +9 -0
  22. package/lib/engine/mem/dbdrivers/fake/driver.d.ts +75 -0
  23. package/lib/engine/mem/dbdrivers/fake/index.d.ts +7 -0
  24. package/lib/engine/mem/dbdrivers/fake/surface.d.ts +34 -0
  25. package/lib/engine/mem/dbdrivers/fake/transport.d.ts +61 -0
  26. package/lib/engine/mem/dbdrivers/sqlite/connection.d.ts +14 -0
  27. package/lib/engine/mem/dbdrivers/sqlite/contradictions.d.ts +35 -0
  28. package/lib/engine/mem/dbdrivers/sqlite/dao/index.d.ts +16 -0
  29. package/lib/engine/mem/dbdrivers/sqlite/driver.d.ts +54 -0
  30. package/lib/engine/mem/dbdrivers/sqlite/facts.d.ts +117 -0
  31. package/lib/engine/mem/dbdrivers/sqlite/index.d.ts +6 -0
  32. package/lib/engine/mem/dbdrivers/sqlite/migrations.d.ts +15 -0
  33. package/lib/engine/mem/dbdrivers/sqlite/relations.d.ts +62 -0
  34. package/lib/engine/mem/dbdrivers/sqlite/search.d.ts +56 -0
  35. package/lib/engine/mem/dbdrivers/sqlite/sources.d.ts +37 -0
  36. package/lib/engine/mem/defaults.d.ts +3 -4
  37. package/lib/engine/mem/dispatch.d.ts +7 -32
  38. package/lib/engine/mem/entities/extract.d.ts +44 -47
  39. package/lib/engine/mem/eval/derived_runner.d.ts +6 -1
  40. package/lib/engine/mem/eval/relation_gold.d.ts +251 -0
  41. package/lib/engine/mem/eval/write_metrics.d.ts +1 -1
  42. package/lib/engine/mem/facts_io.d.ts +328 -0
  43. package/lib/engine/mem/index.d.ts +10 -2
  44. package/lib/engine/mem/interfaces/capabilities.d.ts +30 -0
  45. package/lib/engine/mem/interfaces/common.d.ts +11 -0
  46. package/lib/engine/mem/interfaces/errors.d.ts +65 -0
  47. package/lib/engine/mem/interfaces/index.d.ts +18 -0
  48. package/lib/engine/mem/interfaces/migrations.d.ts +21 -0
  49. package/lib/engine/mem/interfaces/repositories.d.ts +471 -0
  50. package/lib/engine/mem/interfaces/search_index.d.ts +113 -0
  51. package/lib/engine/mem/interfaces/storage_backend.d.ts +84 -0
  52. package/lib/engine/mem/interfaces/storage_driver.d.ts +43 -0
  53. package/lib/engine/mem/interfaces/version.d.ts +34 -0
  54. package/lib/engine/mem/lifecycle/contradiction.d.ts +2 -2
  55. package/lib/engine/mem/maintenance_paths.d.ts +41 -0
  56. package/lib/engine/mem/modelBootstrap.d.ts +1 -1
  57. package/lib/engine/mem/relations/confidence.d.ts +39 -0
  58. package/lib/engine/mem/relations/extract.d.ts +128 -0
  59. package/lib/engine/mem/relations/index.d.ts +32 -0
  60. package/lib/engine/mem/relations/name-quality.d.ts +78 -0
  61. package/lib/engine/mem/relations/normalize.d.ts +275 -0
  62. package/lib/engine/mem/relations/patterns.d.ts +67 -0
  63. package/lib/engine/mem/relations/statement.d.ts +88 -0
  64. package/lib/engine/mem/relations/types.d.ts +79 -0
  65. package/lib/engine/mem/runtime.d.ts +117 -90
  66. package/lib/engine/mem/storage/create.d.ts +42 -0
  67. package/lib/engine/mem/storage/errors.d.ts +65 -0
  68. package/lib/engine/mem/storage/index.d.ts +15 -0
  69. package/lib/engine/mem/storage/ports.d.ts +472 -0
  70. package/lib/engine/mem/storage/registry.d.ts +37 -0
  71. package/lib/engine/mem/storage/sqlite.d.ts +28 -0
  72. package/lib/engine/mem/store/common.d.ts +1 -1
  73. package/lib/engine/mem/store/entity_leg.d.ts +59 -2
  74. package/lib/engine/mem/store/entity_sweep.d.ts +4 -3
  75. package/lib/engine/mem/store/explicit_relations.d.ts +52 -0
  76. package/lib/engine/mem/store/facts_restore.d.ts +124 -0
  77. package/lib/engine/mem/store/floors.d.ts +4 -1
  78. package/lib/engine/mem/store/graph_reader.d.ts +146 -0
  79. package/lib/engine/mem/store/hybrid.d.ts +50 -13
  80. package/lib/engine/mem/store/kinds.d.ts +47 -0
  81. package/lib/engine/mem/store/legs.d.ts +24 -29
  82. package/lib/engine/mem/store/lexical.d.ts +36 -45
  83. package/lib/engine/mem/store/memory.d.ts +486 -82
  84. package/lib/engine/mem/store/recall_policy.d.ts +49 -0
  85. package/lib/engine/mem/store/relation_backfill.d.ts +210 -0
  86. package/lib/engine/mem/store/relation_planner.d.ts +191 -0
  87. package/lib/engine/mem/store/secret_guard.d.ts +0 -1
  88. package/lib/engine/mem/store/self_query.d.ts +4 -2
  89. package/lib/engine/mem/store/time_filter.d.ts +121 -0
  90. package/lib/engine/mem/store/titles.d.ts +52 -0
  91. package/lib/engine/mem/store/vector_repair.d.ts +44 -54
  92. package/lib/engine/mem-contract/config.d.ts +44 -38
  93. package/lib/engine/mem-contract/text.d.ts +1 -1
  94. package/lib/engine/mem-contract/tools.d.ts +184 -117
  95. package/lib/engine/mem-contract/types.d.ts +364 -288
  96. package/lib/index.js +13898 -11500
  97. package/lib/interface-version.json +1 -1
  98. package/lib/types/client/index.d.ts +2 -2
  99. package/lib/types/client/index.d.ts.map +1 -1
  100. package/lib/types/client/paging.d.ts +3 -12
  101. package/lib/types/client/paging.d.ts.map +1 -1
  102. package/lib/types/client/summarize.d.ts.map +1 -1
  103. package/lib/types/command.d.ts +61 -0
  104. package/lib/types/command.d.ts.map +1 -0
  105. package/lib/types/envinit-bootstrap.d.ts +1 -1
  106. package/lib/types/envinit.d.ts +2 -6
  107. package/lib/types/envinit.d.ts.map +1 -1
  108. package/lib/types/hints.d.ts +28 -24
  109. package/lib/types/hints.d.ts.map +1 -1
  110. package/lib/types/index.d.ts +21 -40
  111. package/lib/types/index.d.ts.map +1 -1
  112. package/lib/types/inject.d.ts +3 -2
  113. package/lib/types/inject.d.ts.map +1 -1
  114. package/lib/types/interface_gate.d.ts +9 -9
  115. package/lib/types/prompt.d.ts +17 -41
  116. package/lib/types/prompt.d.ts.map +1 -1
  117. package/lib/types/provision.d.ts +3 -0
  118. package/lib/types/provision.d.ts.map +1 -1
  119. package/lib/types/remote.d.ts +23 -9
  120. package/lib/types/remote.d.ts.map +1 -1
  121. package/lib/types/render.d.ts +3 -3
  122. package/lib/types/tool_schema.d.ts +4 -0
  123. package/lib/types/tool_schema.d.ts.map +1 -1
  124. package/lib/types/vectorMigration.d.ts +5 -7
  125. package/lib/types/vectorMigration.d.ts.map +1 -1
  126. package/package.json +16 -15
  127. package/lib/engine/mem/config/domains.d.ts +0 -20
  128. package/lib/engine/mem/db/dao/chunks.d.ts +0 -231
  129. package/lib/engine/mem/db/dao/documents.d.ts +0 -51
  130. package/lib/engine/mem/db/hash.d.ts +0 -2
  131. package/lib/engine/mem/db/knowledge.d.ts +0 -17
  132. package/lib/engine/mem/eval/index.d.ts +0 -4
  133. package/lib/engine/mem/git.d.ts +0 -73
  134. package/lib/engine/mem/router.d.ts +0 -51
  135. package/lib/engine/mem/store/doc_files.d.ts +0 -125
  136. package/lib/engine/mem/store/document_text.d.ts +0 -47
  137. package/lib/engine/mem/store/ingest_guard.d.ts +0 -25
  138. package/lib/engine/mem/store/knowledge.d.ts +0 -598
  139. package/lib/engine/mem/store/source_picker.d.ts +0 -43
  140. package/lib/engine/mem-retrieval/adapters/local_reranker.d.ts +0 -52
  141. package/lib/engine/mem-retrieval/adapters/none_reranker.d.ts +0 -10
  142. package/lib/engine/mem-retrieval/rerank.d.ts +0 -13
  143. package/lib/types/client/domains.d.ts +0 -53
  144. package/lib/types/client/domains.d.ts.map +0 -1
  145. package/lib/types/client/ingest.d.ts +0 -24
  146. package/lib/types/client/ingest.d.ts.map +0 -1
  147. package/lib/types/open.d.ts +0 -77
  148. package/lib/types/open.d.ts.map +0 -1
  149. package/lib/types/reconcile.d.ts +0 -64
  150. package/lib/types/reconcile.d.ts.map +0 -1
@@ -1,27 +1,27 @@
1
1
  /**
2
- * Entity + SPO triples extraction. Uses nodejieba (optional dependency) for POS;
3
- * degrades gracefully to a character/regex fallback when unavailable, exactly like
4
- * the `jieba_available()` guard.
2
+ * Entity extraction: POS tagging and the noun-ish names that become `entities` rows.
3
+ *
4
+ * Uses nodejieba (optional dependency) for POS; degrades gracefully to a character/regex fallback
5
+ * when unavailable, exactly like the `jieba_available()` guard.
5
6
  *
6
7
  * Note: nodejieba's `tag()` returns `{ word, tag }` objects, and its POS flag set
7
8
  * differs from Python jieba (e.g. proper nouns → `nr`, 名动词 → `vn`).
9
+ *
10
+ * WHAT THIS MODULE IS NOT. It owns what is an entity NAME — nothing about relations. The relation
11
+ * half (SVO candidate triples, explicit patterns, the name/statement/confidence gates and the
12
+ * predicate vocabulary) lives in `relations/`; the two halves share exactly one thing, the tagging
13
+ * pass ({@link tagText}), so a write pays for the dictionary once and drives both.
8
14
  */
9
15
  export interface ExtractedEntity {
10
16
  name: string;
11
17
  type: string;
12
18
  method: string;
13
19
  }
14
- export interface ExtractedTriple {
15
- subj: string;
16
- pred: string;
17
- obj: string;
18
- confidence: number;
19
- source: string;
20
- }
21
- type PosToken = {
20
+ /** One tagged token — the tokenizer's shape, shared with `relations/extract.ts`. */
21
+ export interface PosToken {
22
22
  word: string;
23
23
  tag: string;
24
- };
24
+ }
25
25
  /**
26
26
  * Attempts made so far — the mechanism, not the wall time.
27
27
  *
@@ -33,52 +33,49 @@ type PosToken = {
33
33
  export declare function jiebaLoadAttempts(): number;
34
34
  export declare function jiebaAvailable(): Promise<boolean>;
35
35
  /**
36
- * Version of the extraction RULES below (accepted tags, stop words, triple patterns).
36
+ * Version of the ENTITY extraction rules below (accepted tags, the regex fallback).
37
+ *
38
+ * Persisted per chunk so a reindex can skip rows whose text is unchanged while still rebuilding the
39
+ * ones produced by an older rule set. Bump this whenever a change here would yield different
40
+ * ENTITIES for the same text — it is the only thing that separates "already extracted" from
41
+ * "extracted by the previous version" (DESIGN §20).
42
+ *
43
+ * The relation rules have their OWN version (`db/graph.ts#EDGE_EXTRACTOR_VERSION` in `edges`):
44
+ * an entity-rule change is not an edge-policy change, and vice versa.
45
+ *
46
+ * 2 (Phase 5b, docs/RELATIONAL_RECALL.md §十七): punctuation is an ARGUMENT BOUNDARY instead of an
47
+ * argument in the relation half; `nrt`/`zg` count as arguments like they already counted as entities
48
+ * (`阿里巴巴`); an opening bracket at an argument's edge is skipped (`(架构评审)`); more negations
49
+ * are kept on the predicate (`不再/不会/不能/未能`); and triples are stamped `source: 'svo'` so the
50
+ * graph can require a STATED relation from this producer (`relations/statement.ts`).
51
+ *
52
+ * 3 (2026-10-25 relation review, docs/REVIEW-RELATION-EXTRACTION.md): the relation half's TRIPLES
53
+ * change, not the entity names — curated-predicate surface matching (`主管/隶属于/汇报/部署在/工作于/
54
+ * 属于/合作` are nouns or non-verbs to the tagger, so they were unreachable), `p`/`c` as a merge
55
+ * BOUNDARY instead of tokens to step over, strict + weak predicate candidates merged instead of
56
+ * fallbacks, and a triple-contiguity filter (`isContiguousTriple`). The bump is what makes
57
+ * `reindexEntities` re-run the extractor over existing facts — `reextractEdges` alone only
58
+ * re-derives edges from STORED triples and would keep the old, partial extraction.
37
59
  *
38
- * Persisted per chunk so a reindex can skip rows whose text is unchanged while still
39
- * rebuilding the ones produced by an older rule set. Bump this whenever a change here would
40
- * yield different entities/triples for the same text — it is the only thing that separates
41
- * "already extracted" from "extracted by the previous version" (DESIGN §20).
60
+ * The relation half's naming rules themselves are unchanged by 3; `triples` move because the
61
+ * identifier of "what the text extractor saw" is this stamp, and `MemoryStore.reindexEntities`
62
+ * rewrites entities, triples and edges together under it.
42
63
  */
43
- export declare const ENTITY_EXTRACTOR_VERSION = 1;
44
- export interface TriplePattern {
45
- subj?: string;
46
- pred?: string;
47
- obj?: string;
48
- }
64
+ export declare const ENTITY_EXTRACTOR_VERSION = 3;
49
65
  /**
50
66
  * POS-tag `text` ONCE, or `null` when nodejieba is unavailable (callers fall back to regex).
51
67
  *
52
- * Exists so one write can drive BOTH extractors from a single `jieba.tag()` call: `add`/`update`
53
- * used to tag the same content twice (once for entities, once for triples), which is pure repeated
54
- * mission — measured ~1.1 ms per 1000 characters per pass.
68
+ * Exists so one write can drive BOTH halves from a single `jieba.tag()` call: entity names and
69
+ * relation candidates used to tag the same content twice, which is pure repeated work — measured
70
+ * ~1.1 ms per 1000 characters per pass.
55
71
  */
56
72
  export declare function tagText(text: string): Promise<PosToken[] | null>;
57
73
  /**
58
74
  * Entities from ALREADY-TAGGED tokens (`null` = tagger unavailable → regex fallback on `text`).
59
75
  *
60
- * The token array is passed in rather than re-derived, so a caller that needs entities AND triples
61
- * pays for one tagging pass (see `tagText`).
76
+ * The token array is passed in rather than re-derived, so a caller that needs entities AND
77
+ * relations pays for one tagging pass (see `tagText`).
62
78
  */
63
79
  export declare function entitiesFromTokens(tokens: PosToken[] | null, text: string): ExtractedEntity[];
64
80
  /** Extract entity names using nodejieba POS when available, else a regex fallback. */
65
81
  export declare function extractEntities(text: string): Promise<ExtractedEntity[]>;
66
- /**
67
- * SPO triples from ALREADY-TAGGED tokens (`null` = tagger unavailable → no triples, as before).
68
- *
69
- * `pos` is exported alongside `entitiesFromTokens` so the write path can tag once and drive both.
70
- */
71
- export declare function triplesFromTokens(rawTokens: PosToken[] | null): ExtractedTriple[];
72
- /** Extract a few SPO triples via a simplified SVO heuristic. Returns [] when jieba unavailable. */
73
- export declare function extractTriples(text: string): Promise<ExtractedTriple[]>;
74
- /**
75
- * Parse a natural-language question into a direction-aware TriplePattern where
76
- * interrogative slots are wildcards (`undefined`).
77
- * `parse_query_pattern`: predicates anchor to the interrogative, and no literal
78
- * interrogative word fills a slot.
79
- *
80
- * 「李娜管理谁」 → { subj: '李娜', pred: '管理' }
81
- * 「谁管理李娜」 → { pred: '管理', obj: '李娜' }
82
- */
83
- export declare function parseQueryPattern(query: string): Promise<TriplePattern>;
84
- export {};
@@ -69,12 +69,17 @@ export interface DerivedRuntimePort {
69
69
  }): Promise<{
70
70
  fact_id: number;
71
71
  }>;
72
+ /**
73
+ * `search` as this harness needs it. `hits` is OPTIONAL because the answer has three modes since
74
+ * Phase 4 (a `mode:"relation"` answer carries paths + evidence instead); a relation answer ranks
75
+ * nothing here — this harness grades content retrieval.
76
+ */
72
77
  recall(req: {
73
78
  action: 'search';
74
79
  query: string;
75
80
  limit?: number;
76
81
  }): Promise<{
77
- hits: {
82
+ hits?: {
78
83
  ref_id: number;
79
84
  }[];
80
85
  }>;
@@ -0,0 +1,251 @@
1
+ /**
2
+ * The relation-recall gold set and its evaluator (docs/RELATIONAL_RECALL.md R6 / Phase 5a).
3
+ *
4
+ * WHAT THIS FILE IS. One deterministic corpus, one list of labelled queries, and a runner that
5
+ * seeds a THROWAWAY database through the production write path and measures the rule the relation
6
+ * stage promises (RELATIONAL_RECALL §6–7):
7
+ *
8
+ * - a COMPLETE relation question (one-hop / reverse / two-hop) is answered with structure;
9
+ * - a PURE multi-entity connection ("张伟和李娜") is answered at the FACT level (`mode:"facts"`,
10
+ * docs/GRAPH_RECALL_AND_SEMANTICS.md §7.9), including journal entries and notes without edges;
11
+ * - an incomplete or unanswerable one (no plan, no path/fact, archived evidence, filter mismatch)
12
+ * falls back to fuzzy and never returns an empty `mode:"relation"` / `mode:"facts"`;
13
+ * - exact content and `titles` still win / stay untouched;
14
+ * - aliases (entity + predicate), `kind` / `source` / `time` filters narrow the graph.
15
+ *
16
+ * WHY A GOLD SET AND NOT MORE SPECS. A spec asserts one behaviour per test; an eval fixture assigns
17
+ * every query an expected ROUTE and an expected ANSWER, then reports route accuracy, path
18
+ * precision/recall and the fallback rate over the whole set. The vitest spec
19
+ * (`test/relation_eval.spec.ts`) freezes the numbers, while `scripts/relation_gold_eval.mjs`
20
+ * prints the same report for the docs — both import THIS module, so the table cannot drift from
21
+ * the assertion.
22
+ *
23
+ * HOW THE FIXTURE IS SEEDED. Facts go through `rt.remember` (real FTS rows, entity links, triples,
24
+ * edges). The facts whose object surface the STRING EXTRACTOR's tokenizer would decide differently
25
+ * (`李娜负责支付网关` yields `网关`) get `writeEdgesForFact` afterwards: extraction quality is R3's
26
+ * subject, while the planner/walk is what this fixture evaluates. `writeEdgesForFact` is still the
27
+ * production edge writer (normalization, confidence policy, version stamp), so the seeded graph is
28
+ * the shape production builds. `archive` is the real admin action.
29
+ *
30
+ * WHAT IS DELIBERATELY NOT HERE. No timing assertions (those are `scripts/bench-relation.mjs`,
31
+ * because a CI runner's p95 is not a specification), no live database, no model, no new action.
32
+ *
33
+ * @module eval/relation_gold
34
+ */
35
+ import type { MemoryKind, TimeFilter } from '../../mem-contract/index.js';
36
+ import type { AvantfRuntime } from '../runtime.js';
37
+ /** One triple the fixture wants in the graph, as the production edge writer takes it. */
38
+ export interface RelationGoldEdge {
39
+ subj: string;
40
+ pred: string;
41
+ obj: string;
42
+ }
43
+ /** One fact of the synthetic corpus. `key` is the only handle expectations use. */
44
+ export interface RelationGoldFact {
45
+ key: string;
46
+ content: string;
47
+ kind?: MemoryKind;
48
+ title?: string;
49
+ eventDate?: string;
50
+ sourceRef?: string;
51
+ /**
52
+ * `facts.created_at` to force after the write — the time axis is a caller-visible filter, and
53
+ * "yesterday's fact" must be constructible without waiting a day.
54
+ */
55
+ createdAt?: string;
56
+ /** Edges to (re)write through `writeEdgesForFact`; omitted = whatever the text extractor produced. */
57
+ edges?: readonly RelationGoldEdge[];
58
+ /** Archive this fact after the corpus is seeded (the edge is written first, then hidden). */
59
+ archived?: boolean;
60
+ }
61
+ /** Which behaviour a query is labelled with. One primary label per query keeps the table readable. */
62
+ export type RelationGoldCategory = 'one-hop' | 'reverse' | 'two-hop' | 'facts' | 'alias' | 'archived' | 'kind' | 'source' | 'time' | 'negative' | 'exact';
63
+ /** One expected path, by entity names and the ordered fact keys behind its edges. */
64
+ export interface RelationGoldPath {
65
+ start: string;
66
+ end: string;
67
+ hops: number;
68
+ facts: readonly string[];
69
+ }
70
+ /** The four routes a gold query may be graded on ('time' is not one: every gold query has a `query`). */
71
+ export type RelationGoldMode = 'relation' | 'facts' | 'fuzzy' | 'exact';
72
+ /** What a query must produce. */
73
+ export interface RelationGoldExpectation {
74
+ mode: RelationGoldMode;
75
+ /** Relation answers: the COMPLETE path set (no extra, no missing). */
76
+ paths?: readonly RelationGoldPath[];
77
+ /**
78
+ * The facts the answer must cite. For `relation` this is the evidence set; for `facts` it is the
79
+ * WHOLE fact page (`facts` requires equality, in any order); for `exact`/`fuzzy` it is the hit set
80
+ * (`exact` requires equality, `fuzzy` requires inclusion in its top-5 page).
81
+ */
82
+ facts?: readonly string[];
83
+ }
84
+ /** One labelled query. */
85
+ export interface RelationGoldQuery {
86
+ id: string;
87
+ query: string;
88
+ category: RelationGoldCategory;
89
+ kind?: MemoryKind;
90
+ source?: string;
91
+ time?: TimeFilter;
92
+ expect: RelationGoldExpectation;
93
+ }
94
+ /** A `mem_recall.titles` regression check (the title search must not see the planner). */
95
+ export interface RelationGoldTitleCheck {
96
+ id: string;
97
+ query: string;
98
+ /** Title-holder fact keys, in the order the exact title page returns them. */
99
+ facts: readonly string[];
100
+ }
101
+ /**
102
+ * The corpus.
103
+ *
104
+ * The entity sets are DELIBERATELY disjoint enough that each query has one unambiguous answer:
105
+ *
106
+ * ```text
107
+ * 张伟 ──管理──▶ 李娜 ──管理(负责)──▶ 支付网关 note
108
+ * 张伟 ──管理──▶ 王强 ──管理(负责)──▶ 风控引擎 note (distilled from the journal below) / note
109
+ * 张伟 ──管理──▶ 赵敏 note, ARCHIVED
110
+ * 张伟, 李娜 ──参与(参加)──▶ 架构评审 note
111
+ * 赵敏 ──管理(负责)──▶ 数据平台 note, created_at 2020
112
+ * 周杰, 陈静 note, NO edge (fact-level AND only)
113
+ * 钱伟, 孙丽 note, NO edge, created_at 2020
114
+ * ```
115
+ *
116
+ * `zhang-zhao` is archived AFTER its edge is written, which is the state the reader must hide.
117
+ *
118
+ * `zhang-wang` is the corpus's JOURNAL, and it states the same relation the note `wang-note`
119
+ * distils — that pair is STRUCTURED_RELATIONS §一's promotion rule in fixture form, and it is what
120
+ * makes the `kind` dimension mean something: the journal keeps its `triples` and produces NO edge,
121
+ * so `kind: "journal"` cannot answer a relation question, while the note that carries the stable
122
+ * fact can. (Before Phase 5c both rows were one journal fact with an edge; the graph is note-only
123
+ * now, so the relation moved to the note it belongs to.)
124
+ *
125
+ * The fact-level AND stage (§7.9) reads `fact_entities`, not `edges`, and that is exactly why
126
+ * `zhang-wang` (the journal, no edge), `note-consensus` and `old-pair` (notes whose text produced
127
+ * no relation) are in the corpus: they are the rows a graph-only answer would silently miss.
128
+ */
129
+ export declare const RELATION_GOLD_FACTS: readonly RelationGoldFact[];
130
+ /** An entity alias registered through the production entry point (explicit, never inferred). */
131
+ export declare const RELATION_GOLD_ALIASES: readonly {
132
+ alias: string;
133
+ canonical: string;
134
+ }[];
135
+ /**
136
+ * The labelled queries.
137
+ *
138
+ * 17 relation questions, 9 fact-level AND questions, 16 negative controls, 1 exact-precedence probe
139
+ * (STRUCTURED_RELATIONS §四 moved the `kind: "journal"` relation question into the negative set: a
140
+ * journal produces no edge, so the layer filter can only answer with fuzzy). The breakdown is what
141
+ * makes the report diagnosable: a route-accuracy regression can be read per category, and the
142
+ * negative controls are the half that catches a planner that guesses.
143
+ */
144
+ export declare const RELATION_GOLD_QUERIES: readonly RelationGoldQuery[];
145
+ /** `titles` must keep answering the title index, never the planner. */
146
+ export declare const RELATION_GOLD_TITLE_CHECKS: readonly RelationGoldTitleCheck[];
147
+ /** The runtime surface the fixture needs (the real `AvantfRuntime` satisfies it). */
148
+ export type RelationGoldRuntime = Pick<AvantfRuntime, 'db' | 'remember' | 'recall' | 'admin' | 'memory'>;
149
+ /** Result of seeding: fact key → `fact_id`. */
150
+ export type RelationGoldIds = Readonly<Record<string, number>>;
151
+ /**
152
+ * Seed the corpus into an EMPTY store through the production write path.
153
+ *
154
+ * Order is load-bearing: every fact is written first (so text extraction has run and the FTS rows
155
+ * exist), then explicit edges replace what the extractor produced, then the clock/alias/archive
156
+ * adjustments are applied. Archiving last is what makes `zhang-zhao` a fact whose edge EXISTS but
157
+ * must not be walkable — the exact state the reader's active filter is for.
158
+ *
159
+ * @param rt - a runtime on a throwaway database.
160
+ * @returns fact key → id, for expectations that name facts.
161
+ */
162
+ export declare function seedRelationGold(rt: RelationGoldRuntime): Promise<RelationGoldIds>;
163
+ /** One query's measured route + answer. */
164
+ export interface RelationGoldRow {
165
+ id: string;
166
+ query: string;
167
+ category: RelationGoldCategory;
168
+ expected_mode: RelationGoldMode;
169
+ mode: RelationGoldMode;
170
+ /** What the planner decided BEFORE any read (`null` = neither planner formed a question). */
171
+ plan: 'one-hop' | 'reverse' | 'two-hop' | 'facts' | null;
172
+ /**
173
+ * For a fuzzy answer: why no structured stage answered (diagnosis, not a failure). `no-fact`
174
+ * means the fact-level plan formed but no active fact mentions every entity; `no-path` means the
175
+ * relation plan formed but the graph had no walk; `planner-refused` means neither did.
176
+ */
177
+ fallback: 'planner-refused' | 'no-fact' | 'no-path' | null;
178
+ expected_facts: string[];
179
+ returned_facts: string[];
180
+ missing_facts: string[];
181
+ extra_facts: string[];
182
+ fact_precision: number | null;
183
+ fact_recall: number | null;
184
+ paths_expected: number | null;
185
+ paths_returned: number | null;
186
+ path_matches: number | null;
187
+ path_precision: number | null;
188
+ path_recall: number | null;
189
+ pass: boolean;
190
+ failure: string | null;
191
+ }
192
+ /** Per-category roll-up. */
193
+ export interface RelationGoldCategoryRow {
194
+ total: number;
195
+ passed: number;
196
+ route_accuracy: number;
197
+ }
198
+ /** The whole report. `failures` is what a reviewer reads when `pass` is not 1. */
199
+ export interface RelationGoldReport {
200
+ rows: RelationGoldRow[];
201
+ title_checks: {
202
+ id: string;
203
+ query: string;
204
+ mode: 'exact' | 'fuzzy';
205
+ passed: boolean;
206
+ expected_facts: string[];
207
+ returned_facts: string[];
208
+ }[];
209
+ summary: {
210
+ total: number;
211
+ passed: number;
212
+ route_accuracy: number;
213
+ relation_queries: number;
214
+ relation_answered: number;
215
+ relation_route_accuracy: number;
216
+ facts_queries: number;
217
+ facts_answered: number;
218
+ facts_route_accuracy: number;
219
+ negative_queries: number;
220
+ negative_fallback_rate: number;
221
+ exact_queries: number;
222
+ exact_answered: number;
223
+ plan_formed: number;
224
+ plan_rate: number;
225
+ /** Relations only: `path_matches / paths_returned`, and `path_matches / paths_expected`. */
226
+ path_precision: number | null;
227
+ path_recall: number | null;
228
+ /** Relations only: fact-set precision/recall over evidence. */
229
+ fact_precision: number | null;
230
+ fact_recall: number | null;
231
+ /** Fraction of ALL queries the store answered with fuzzy (diagnosis of the middle layer). */
232
+ fuzzy_fallback_rate: number;
233
+ titles_total: number;
234
+ titles_passed: number;
235
+ by_category: Record<string, RelationGoldCategoryRow>;
236
+ failures: string[];
237
+ };
238
+ }
239
+ /**
240
+ * Evaluate the whole gold set against one seeded runtime.
241
+ *
242
+ * Both planners are invoked SEPARATELY from the store's answer — the relation planner through the
243
+ * same `memory.graphReader(filter)` the store uses in `relationPage`, the fact-level planner through
244
+ * `memory.factEntitiesIn` — so the report can distinguish "the planner refused" from "the planner
245
+ * planned and the read found nothing". That distinction is the point of a routing metric: both fall
246
+ * back to fuzzy, but only one of them is a coverage gap.
247
+ *
248
+ * @param rt - the runtime seeded by {@link seedRelationGold} (or an equivalent fixture).
249
+ * @param ids - fact key → id returned by the seeder.
250
+ */
251
+ export declare function evaluateRelationGold(rt: RelationGoldRuntime, ids: RelationGoldIds): Promise<RelationGoldReport>;
@@ -22,7 +22,7 @@
22
22
  * it in the text looks natural and is a trap: the token ends up inside the subject/predicate/
23
23
  * object the detector parses, and the case silently stops exercising the signal it exists for
24
24
  * (measured: an in-text token suppressed the polarity conflict in 4 of 5 placements). A label
25
- * the store never interprets — `category` — is stable across extractor changes.
25
+ * the store never interprets — the fact text's own label — is stable across extractor changes.
26
26
  */
27
27
  /** What a case is about. `expect_action` is structural only; `live` is content only. */
28
28
  export interface WriteCase {