@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,6 +1,5 @@
1
1
  /**
2
- * `triples` — the SPO graph behind `ask` / `chain`, the structural contradiction signals and
3
- * the supersede cleanup.
2
+ * `triples` — the SPO graph behind the structural contradiction signals and the supersede cleanup.
4
3
  *
5
4
  * The two structural lookups are exported as SQL builders so that the statement whose QUERY PLAN
6
5
  * matters (they must seek a triples index and never scan) is the very one production runs, not a
@@ -25,12 +24,6 @@ interface TripleRow {
25
24
  export declare function polarityLookupSql(predCount: number): string;
26
25
  /** Structural lookup ②: same (subj, pred), different object. Same rewrite as above. */
27
26
  export declare function sameSubjPredLookupSql(): string;
28
- /** The optional `subj`/`pred`/`obj` slots an `ask` pattern may fill. */
29
- interface TriplePatternParts {
30
- subj?: string;
31
- pred?: string;
32
- obj?: string;
33
- }
34
27
  export declare class TriplesDao {
35
28
  private readonly db;
36
29
  constructor(db: Db);
@@ -58,23 +51,5 @@ export declare class TriplesDao {
58
51
  findSameSubjPredOtherObj(subj: string, pred: string, obj: string, excludeFactId: number): {
59
52
  fact_id: number;
60
53
  }[];
61
- /**
62
- * First hop of `chain`: the objects reachable from `subj` (optionally via one predicate).
63
- *
64
- * JOIN, not `fact_id IN (SELECT … WHERE status='active')` — the same shape {@link polarityLookupSql}
65
- * documents as a measured 202 ms on a 300k-triple corpus with zero matches, because the subquery
66
- * materializes EVERY active fact id per call. The chain path paid that on every hop.
67
- */
68
- objectsForSubject(subj: string, pred?: string): string[];
69
- /**
70
- * Second hop of `chain`: active facts whose subject is one of the mid names.
71
- *
72
- * JOIN for the same reason as {@link objectsForSubject}.
73
- */
74
- activeFactsBySubject(subjs: readonly string[], secondPred?: string): number[];
75
- /** Active facts whose triple matches every given slot exactly (`ask`, exact layer). */
76
- activeFactsMatchingExact(parts: TriplePatternParts): number[];
77
- /** Same, with `=` relaxed to `LIKE %term%` (`ask`, recall-recovery layer). */
78
- activeFactsMatchingLike(parts: TriplePatternParts): number[];
79
54
  }
80
55
  export {};
@@ -0,0 +1,249 @@
1
+ import type { Db } from './port.js';
2
+ import type { EdgeReextractOptions, EdgeReextractReport, EdgeRebuildReport, EdgeSourceTriple, EdgeWriteReport, GraphCoverageReport } from '../interfaces/repositories.js';
3
+ export type { EdgeDropReason, EdgeDropSample, EdgeRebuildReport, EdgeReextractMode, EdgeReextractOptions, EdgeReextractReport, EdgeSourceTriple, EdgeVersionCount, EdgeWriteReport, GraphCoverageReport, } from '../interfaces/repositories.js';
4
+ /**
5
+ * `edges.extraction_version` for rows converted from the legacy string `triples`.
6
+ *
7
+ * 0 = "no versioned edge extractor produced this" — the same meaning the value has for
8
+ * `facts.entities_version` (a row from before the rules were versioned). Phase 2's extractor stamps
9
+ * 1 and up, so a re-extraction pass selects these rows with `extraction_version < :current` instead
10
+ * of needing to know which migration wrote them.
11
+ */
12
+ export declare const LEGACY_EDGE_EXTRACTION_VERSION = 0;
13
+ /** One edge as the graph reads it: IDs for walkers, names for a result payload. */
14
+ export interface GraphEdgeRow {
15
+ edge_id: number;
16
+ fact_id: number;
17
+ subject_id: number;
18
+ predicate_id: number;
19
+ object_id: number;
20
+ subject: string;
21
+ predicate: string;
22
+ object: string;
23
+ confidence: number;
24
+ source: string;
25
+ extraction_version: number;
26
+ }
27
+ /** What one schema+backfill pass did (0 for every `*Created` counter on a repeated run). */
28
+ export interface RelationalGraphReport {
29
+ /** Legacy `triples` rows seen. */
30
+ triples: number;
31
+ /** Of those, rows skipped because a slot is blank (no node is invented for `''`). */
32
+ skipped: number;
33
+ /** `entities` rows this run created. */
34
+ entitiesCreated: number;
35
+ /** `predicates` rows this run created. */
36
+ predicatesCreated: number;
37
+ /** `edges` rows this run created. */
38
+ edgesCreated: number;
39
+ /** `edges` rows in the table after the run. */
40
+ edges: number;
41
+ }
42
+ /** `CREATE … IF NOT EXISTS` for the four tables and their indexes — then the backfill. */
43
+ export declare function applyRelationalGraphSchema(db: Db): RelationalGraphReport;
44
+ /**
45
+ * Convert `triples` → `edges` (idempotent; the tables must already exist).
46
+ *
47
+ * Kept separate from {@link applyRelationalGraphSchema} so a re-run can never be confused with a
48
+ * schema change: this function only inserts rows, and it reports how many it actually inserted.
49
+ *
50
+ * Only NOTE triples are converted (STRUCTURED_RELATIONS §四). The report's `triples`/`skipped`
51
+ * counters still describe every legacy row the conversion looked at, so "the conversion saw N rows
52
+ * but created fewer edges" stays explainable; `edgesCreated` is what a re-run can be compared against.
53
+ */
54
+ export declare function backfillEdges(db: Db): RelationalGraphReport;
55
+ /**
56
+ * One fact's edges, resolved back to names — the evidence contract of R1/R4 ("every edge can be
57
+ * traced back to its `fact_id`").
58
+ *
59
+ * The IDs are returned next to the names because a path walker joins on them; the names are what a
60
+ * `RelationPath` payload shows. `ORDER BY edge_id` is insertion order, i.e. stable across reads and
61
+ * deterministic in tests.
62
+ */
63
+ export declare function edgesForFact(db: Db, factId: number): GraphEdgeRow[];
64
+ /**
65
+ * One fact's EXPLICIT relations, resolved back to their three stored names
66
+ * (STRUCTURED_RELATIONS §2) — the read model behind `FactDetail.relations`, the v5 export and the
67
+ * restore write.
68
+ *
69
+ * Only `source = 'explicit'` rows are returned: an extracted edge is derivable from the fact text
70
+ * and is not something a caller stated, so it must not appear as a declared relation. `ORDER BY
71
+ * edge_id` is insertion order (the order the caller listed them, minus the four-tuple duplicates),
72
+ * which is what makes an export→restore round trip compare equal.
73
+ *
74
+ * A journal has no edges at all (§四), so this returns `[]` for one without a second query.
75
+ */
76
+ export declare function explicitRelationsForFact(db: Db, factId: number): {
77
+ subject: string;
78
+ predicate: string;
79
+ object: string;
80
+ }[];
81
+ /**
82
+ * `edges.extraction_version` for rows THIS module's extractor wrote (Phase 2, R2/R3).
83
+ *
84
+ * 1 is the first versioned rule set: alias/canonical normalization of both entity slots and of the
85
+ * predicate, the confidence policy below, and the direction rule. `LEGACY_EDGE_EXTRACTION_VERSION`
86
+ * (0) stays what the migration wrote, so `reextractEdges` selects "older than me" without knowing
87
+ * which code path produced a row. Bump this whenever a change here would produce different edges for
88
+ * the same triples (the same contract `ENTITY_EXTRACTOR_VERSION` has).
89
+ *
90
+ * 2 (Phase 5b, docs/RELATIONAL_RECALL.md §十七) adds the two QUALITY gates every edge now passes:
91
+ * - `relations/name-quality.ts#graphNameIssue`: both slots must be bounded, punctuation-free names.
92
+ * This is what removes the fragments (`)。**`, `,仓库…`) that Phase 5a measured in 69 of 87 live
93
+ * node names;
94
+ * - `relations/statement.ts#isStatedRelation`: a triple the text extractor produced (`source: 'svo'`)
95
+ * must appear in the fact text as one clause. On the 2026-10-25 live snapshot NONE of the 48
96
+ * curated triples the v1 policy had accepted is a stated clause, so every edge v1 wrote was a
97
+ * fragment.
98
+ *
99
+ * 3 (2026-10-25 relation review, docs/REVIEW-RELATION-EXTRACTION.md) — what changes the EDGE SET for
100
+ * the same stored triples:
101
+ * - `合作` is a `symmetric` predicate (`relations/normalize.ts`), so a walk from either end of that
102
+ * family answers (a WRITE is unchanged: one edge, sentence order);
103
+ * - the curated vocabulary gained reachable surfaces, so the triples `reextractEdges` re-derives are
104
+ * scored against a fuller predicate table (a surface resolves to a curated family instead of
105
+ * creating an undescribed one, which is the difference between 0.3 and 0.75 for the same triple).
106
+ * Triples themselves are re-derived under `ENTITY_EXTRACTOR_VERSION` 3 — `reextractEdges` reads
107
+ * what `triples` holds, so both bumps are required (see `EDGE_REEXTRACT_BATCH`'s counterpart in
108
+ * `store/memory.ts#reindexEntities`).
109
+ */
110
+ export declare const EDGE_EXTRACTOR_VERSION = 3;
111
+ /**
112
+ * Default facts one {@link reextractEdges} pass may rewrite.
113
+ *
114
+ * The same size as `ENTITY_SWEEP_BATCH` on purpose: both are "one bounded pass" of the derived state
115
+ * run from `maintenance()`, and a corpus should take the same number of passes through either half.
116
+ * This pass is the cheaper of the two — it reads stored triples instead of re-tagging content, and
117
+ * measured ~0.04 ms per fact (§14.5).
118
+ */
119
+ export declare const EDGE_REEXTRACT_BATCH = 2000;
120
+ /**
121
+ * Write one fact's edges from its extracted triples — the call the WRITE PATH makes (add / update /
122
+ * revive / merge) right after it inserted the same triples, inside the same transaction.
123
+ *
124
+ * The legacy `triples` row is not replaced or read back: it stays the record of "what the string
125
+ * extractor saw" (the contradiction detector reads it), while `edges` is its normalized, versioned
126
+ * view. That double write is what lets Phase 2 land without a schema change or a re-extraction of the
127
+ * corpus, and it is also why this function can be a no-op without breaking anything.
128
+ *
129
+ * Never throws on a bad triple (it reports it); it CAN throw on a broken database, which is why the
130
+ * store calls it best-effort — a graph write must not fail a fact write (§4.3).
131
+ *
132
+ * Phase 5b reads the fact's `content` once when at least one triple came from the TEXT extractor
133
+ * (`source: 'svo'`), because the statement test (`relations/statement.ts`) is a test against what the
134
+ * fact actually says. One indexed row read per fact, and none for explicitly asserted triples.
135
+ *
136
+ * The gates themselves are `relations/confidence.ts#evaluateRelation`; this function reads the
137
+ * content the test needs and materializes what the policy accepted.
138
+ *
139
+ * @param options.dryRun resolve WITHOUT creating entities/predicates and report what would happen
140
+ * (the coverage report's "why is this fact missing from the graph" view).
141
+ */
142
+ export declare function writeEdgesForFact(db: Db, factId: number, triples: readonly EdgeSourceTriple[], options?: {
143
+ dryRun?: boolean;
144
+ samples?: number;
145
+ }): EdgeWriteReport;
146
+ /**
147
+ * Write the relations a CALLER stated (`mem_remember.relations`, STRUCTURED_RELATIONS §2).
148
+ *
149
+ * A thin, named wrapper over {@link writeEdgesForFact}: the caller's `{subject, predicate, object}`
150
+ * are mapped onto the same triple shape the extractor produces, with `source: 'explicit'` and
151
+ * confidence 1 — that source is what makes the writer skip the confidence floor and the statement
152
+ * test, while still normalizing both entity slots and the predicate and still applying the name
153
+ * gate. Callers pass already-validated relations (`store/explicit_relations.ts`); this function
154
+ * reports what was written, dropped or already present, and the STORE decides what a drop means.
155
+ */
156
+ export declare function writeExplicitEdgesForFact(db: Db, factId: number, relations: readonly {
157
+ subject: string;
158
+ predicate: string;
159
+ object: string;
160
+ }[]): EdgeWriteReport;
161
+ /**
162
+ * Drop one fact's edges (a superseded revision leaves the live graph, like its triples).
163
+ *
164
+ * Two narrowing flags, both about STRUCTURED_RELATIONS §2's `source: 'explicit'` rows — an explicit
165
+ * edge is the only one NOT derivable from the fact text, so the passes that re-derive edges must be
166
+ * able to leave them alone while a write that REPLACES the declared relations must be able to drop
167
+ * exactly them:
168
+ *
169
+ * - `keepExplicit` — re-derivation (`reextractEdges`, the entity sweep) deletes what it can
170
+ * recreate and keeps what it cannot;
171
+ * - `onlyExplicit` — an `update` that carries new relations rebuilds that revision's explicit set.
172
+ *
173
+ * With neither (the default) everything goes: the revision itself is gone (`supersede`).
174
+ */
175
+ export declare function deleteEdgesForFact(db: Db, factId: number, options?: {
176
+ keepExplicit?: boolean;
177
+ onlyExplicit?: boolean;
178
+ }): number;
179
+ /**
180
+ * Delete every edge whose fact is NOT a note (STRUCTURED_RELATIONS §四/§七).
181
+ *
182
+ * The one-time cleanup of the pre-5c graph: before this phase a `journal` write produced edges, and
183
+ * an upgraded database still carries them. It is a DELETE on the derived table only — the facts and
184
+ * their `triples` are untouched (the contradiction detector still reads those) — and it is
185
+ * idempotent, so `maintenance` can call it on every pass and report 0.
186
+ *
187
+ * `kind <> 'note'` rather than `kind = 'journal'`: `journal` is the only other value the closed set
188
+ * has, and legacy values a pre-13 database might still hold all map onto `note`
189
+ * (`store/kinds.ts#mapLegacyKind`), so the negative form is the one that cannot under-clean.
190
+ */
191
+ export declare function deleteNonNoteEdges(db: Db): number;
192
+ /**
193
+ * Re-derive one fact's edge set under the CURRENT policy (STRUCTURED_RELATIONS §五/§七).
194
+ *
195
+ * WHY THIS EXISTS. `reextractEdges` only selects ACTIVE facts, so an archived row keeps whatever
196
+ * edges it had — including the version-0 rows migration 15's backfill created from legacy triples.
197
+ * Those are exactly the rows Phase 5b's rules were written to refuse (fragments, unstated
198
+ * clauses), and the reader hides them only while the row is archived: a restore would put them
199
+ * back on the graph without ever re-deriving them. This is the one-fact rebuild both re-entry paths
200
+ * call (`store/memory.ts#restore` and the revive branch of `persistFact`), so an archived row comes
201
+ * back with the edges TODAY's rules would write and never with an older extractor's leftovers.
202
+ *
203
+ * Two properties that make it safe to call blindly:
204
+ * - it is IDEMPOTENT in edge SET (same triples + same policy → same rows; only `edge_id`s move);
205
+ * - it never touches `source: 'explicit'` rows. A declared relation cannot be re-derived from the
206
+ * fact text (`deleteEdgesForFact`'s `keepExplicit`), so losing one here would lose a caller's
207
+ * statement — the one thing a restore must preserve even when the extracted half is rebuilt.
208
+ *
209
+ * A JOURNAL gets its non-explicit edges dropped and nothing written (§四). The caller decides the
210
+ * failure policy: the write path and `restore` call it best-effort inside a savepoint, so a broken
211
+ * row keeps its previous edges and the fact write is never blocked (§4.3).
212
+ */
213
+ export declare function rebuildEdgesForFact(db: Db, factId: number, kind: string): EdgeRebuildReport;
214
+ /**
215
+ * Re-derive edges from the STORED triples of facts whose edges predate the current extractor
216
+ * (§4.3 "支持按 extraction_version 重抽/回填").
217
+ *
218
+ * Why it re-derives from `triples` and not from the fact text: the rules that changed in Phase 2 are
219
+ * functions of the triples (alias/canonical resolution, confidence, direction). A change to the TEXT
220
+ * extraction is `ENTITY_EXTRACTOR_VERSION`'s business — `reindexEntities` re-tags the content, and
221
+ * it now rewrites the edges in the same transaction (`store/memory.ts`). Re-tagging here would mean
222
+ * a jieba round-trip per fact for a pass that is otherwise pure SQL.
223
+ *
224
+ * One transaction per fact, so a row that cannot be re-derived keeps its previous edges and stays
225
+ * selected for the next pass instead of leaving a half-rewritten graph.
226
+ *
227
+ * ONLY NOTES ARE SELECTED (STRUCTURED_RELATIONS §四): the graph expresses stable facts, so a
228
+ * `journal` row's triples are no longer turned into edges at all — its leftover edges from before
229
+ * this phase are removed by {@link deleteNonNoteEdges}, not rewritten here. Explicit relations
230
+ * (`source: 'explicit'`) survive the rewrite: they cannot be re-derived from `triples`.
231
+ *
232
+ * Idempotent in version mode: a second pass finds nothing older than the current version and
233
+ * reports `facts: 0`. In `all` mode it re-derives everything again by definition — the edge SET is
234
+ * identical, only `edge_id`s are new.
235
+ */
236
+ export declare function reextractEdges(db: Db, options?: EdgeReextractOptions): EdgeReextractReport;
237
+ /**
238
+ * Read the coverage numbers — what Phase 2 actually produces on this corpus, and why the rest did
239
+ * not make it (§R6: "抽取覆盖率、错误边样本").
240
+ *
241
+ * `samples` is a bounded PREVIEW (it re-runs the resolver with `dryRun` over the first
242
+ * `sampleFacts` facts that have triples), not a persisted log: a drop is a decision, not a row, and
243
+ * the triples are what the decision was made from. Keeping it computed means it can never disagree
244
+ * with the write path — both call the same policy functions.
245
+ */
246
+ export declare function graphCoverage(db: Db, options?: {
247
+ samples?: number;
248
+ sampleFacts?: number;
249
+ }): GraphCoverageReport;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * The one schema+data step that turns the old `category` vocabulary into the two memory layers
3
+ * (JOURNAL_NOTES_REDESIGN N1–N5).
4
+ *
5
+ * It is a FUNCTION, not an inline migration body, because two callers need the exact same work:
6
+ *
7
+ * - `MEMORY_SCHEMA` migration 13 (`journal-and-notes`) — the published upgrade path, which starts
8
+ * from the last released schema (`user_version = 10`, 16 free-form categories);
9
+ * - `scripts/migrate-local-kind.mjs` — the ONE-OFF manual conversion of a local database that was
10
+ * already put into the never-published eight-category shape by an earlier build of migration 13.
11
+ * That database sits at `user_version = 13` with a `categories` table, the `facts_category_*`
12
+ * triggers and an eight-value `category` column, so it needs the DATA half without a version bump.
13
+ *
14
+ * Both states are handled by the same predicates: the old `category` column is mapped when it exists,
15
+ * the lookup table and triggers are dropped when they exist, and every step is idempotent so a
16
+ * second run is a no-op. `worklog` (the one episodic value in the published set) becomes `journal`;
17
+ * every other value — the other 15 published ones and all eight experimental ones — becomes `note`,
18
+ * because the eight-category experiment already collapsed the episodic/stable distinction and only
19
+ * `worklog` was ever an episode.
20
+ *
21
+ * WHAT IT DOES NOT DO: rebuild FTS (`kind` is not indexed by it), touch `created_at`/`content`, or
22
+ * bump `user_version` (the caller does that — `migrate()` for the published path, nobody for the
23
+ * manual one).
24
+ */
25
+ import type { Db } from './port.js';
26
+ /** What one run did, for the operator-facing script and the tests. */
27
+ export interface JournalNotesMigrationReport {
28
+ /** Rows the old `category` column labelled `worklog` (→ `journal`). */
29
+ journal: number;
30
+ /** Every other row (→ `note`). */
31
+ note: number;
32
+ /** Whether the old `category` column existed and was dropped by this run. */
33
+ droppedCategoryColumn: boolean;
34
+ }
35
+ export declare function applyJournalAndNotesSchema(db: Db): JournalNotesMigrationReport;
@@ -44,6 +44,15 @@ export interface MigrationResult {
44
44
  * no-op, while an existing one is upgraded here.
45
45
  */
46
46
  export declare function addColumnIfMissing(db: Db, table: string, column: string, columnDdl: string): void;
47
+ /**
48
+ * Whether `table` already has `column`.
49
+ *
50
+ * Used by {@link addColumnIfMissing} and by a step whose data fix depends on a column an earlier
51
+ * step may or may not have created — a database from the never-published eight-category era has no
52
+ * `facts.kind` at all until the local conversion script adds it, and such a file must still be
53
+ * upgradable by the plugin (docs/JOURNAL_NOTES_REDESIGN.md §6.2).
54
+ */
55
+ export declare function hasColumn(db: Db, table: string, column: string): boolean;
47
56
  /** The schema version stored in the database header (0 = pre-versioning). */
48
57
  export declare function readUserVersion(db: Db): number;
49
58
  /**
@@ -13,9 +13,11 @@
13
13
  * `pragma`, 4 `close` call sites, and none of the statement extras such as
14
14
  * `pluck`/`raw`/`iterate`/`columns`/`function`/`loadExtension`).
15
15
  *
16
- * SYNCHRONOUS by design: `persistFact` runs contradiction detection inside its transaction
17
- * and the lifecycle tick is one synchronous pass, so an async driver does not fit this port.
18
- * That is a decision, not an oversight.
16
+ * SYNCHRONOUS CALLS, ONE ASYNC ENTRY POINT: a statement call is synchronous, because the SQLite
17
+ * driver is and the lifecycle tick/hot read paths are synchronous passes over it. What a REMOTE
18
+ * backend needs is not an async `prepare` but an async UNIT OF WORK, which is
19
+ * {@link Db.transactionAsync}: the store's write paths await repository calls inside a transaction,
20
+ * and that is the only place this port has to span an `await` (DB-2).
19
21
  */
20
22
  export interface DbRunResult {
21
23
  changes: number;
@@ -48,10 +50,27 @@ export interface DbTransaction<T> {
48
50
  deferred(): T;
49
51
  exclusive(): T;
50
52
  }
53
+ /**
54
+ * The transaction mode of {@link Db.transactionAsync}. `IMMEDIATE` takes the write lock up front,
55
+ * which is what a cross-process read-modify-write needs (see {@link DbTransaction.immediate}).
56
+ */
57
+ export type DbTransactionMode = 'DEFERRED' | 'IMMEDIATE';
51
58
  export interface Db {
52
59
  prepare<Row = unknown>(sql: string): DbStatement<Row>;
53
60
  exec(sql: string): void;
54
61
  transaction<T>(fn: () => T): DbTransaction<T>;
62
+ /**
63
+ * An ASYNC body inside one transaction (DB-2). Added beside the synchronous runner because the
64
+ * store's write paths now await repository calls: a remote backend has no synchronous transaction
65
+ * to offer, and the connection-scoped "one unit of work" must survive those awaits.
66
+ *
67
+ * The two runners share the connection's nesting depth, so an async transaction opened inside a
68
+ * synchronous one (or vice versa) becomes a SAVEPOINT rather than a second `BEGIN`. Bodies that
69
+ * do not await are unaffected by this method; bodies that do must accept that a single SQLite
70
+ * connection cannot interleave two units of work — the adapter serializes async transactions so a
71
+ * second one waits for the first to commit or roll back.
72
+ */
73
+ transactionAsync<T>(fn: () => T | Promise<T>, mode?: DbTransactionMode): Promise<T>;
55
74
  pragma(source: string): unknown;
56
75
  close(): void;
57
76
  }
@@ -6,6 +6,101 @@
6
6
  * so adopting them must be a no-op plus a version stamp (see `migrations.ts`). Later schema
7
7
  * changes belong in a numbered migration — `ONE_OPEN_CONTRADICTION_ROW_SQL` below is v2.
8
8
  */
9
+ /**
10
+ * The two columns that replace the eight-value `category` vocabulary
11
+ * (JOURNAL_NOTES_REDESIGN N1–N5).
12
+ *
13
+ * `kind` is the memory LAYER — `journal` (episodic, must carry an event date) or `note` (semantic,
14
+ * no event time required) — and it is the only classification any code or tool reads. The CHECK is
15
+ * the DB-level backstop for a non-cooperating writer (an older build sharing the file, a hand-run
16
+ * `sqlite3`); the application guard refuses an unknown value before it gets here.
17
+ *
18
+ * `legacy_category` is the AUDIT column: migration 13 copies the old free-form `category` into it
19
+ * (all 16 published values, `worklog` included) so the provenance of a migrated row is not lost. No
20
+ * tool schema, filter or query reads it.
21
+ *
22
+ * Both are exported as the exact column DDL because the base schema and migration 13 must produce
23
+ * BYTE-IDENTICAL column definitions (`test/db_upgrade_parity.spec.ts` compares sqlite_master).
24
+ */
25
+ export declare const FACT_KIND_COLUMN_DDL = "TEXT NOT NULL DEFAULT 'note' CHECK (kind IN ('journal','note'))";
26
+ export declare const FACT_LEGACY_CATEGORY_COLUMN_DDL = "TEXT";
27
+ /**
28
+ * The journal title's column DDL (JOURNAL_TITLE_SEARCH §2.1/§11.1).
29
+ *
30
+ * NULLABLE on purpose: every journal written before this column existed has no title, and the
31
+ * model-facing "a journal needs a title" rule is enforced at the WRITE boundary, never by a CHECK
32
+ * (a `CHECK (kind='journal' → title IS NOT NULL)` could not be added to a corpus that is all NULL).
33
+ * Shared with migration 14 so the base schema and the upgrade produce byte-identical definitions.
34
+ */
35
+ export declare const FACT_TITLE_COLUMN_DDL = "TEXT";
36
+ /**
37
+ * A title this fact USED to carry (v17; JOURNAL_TITLE_SEARCH §2.2 + R3's controlled retitle).
38
+ *
39
+ * A title is immutable inside a revision chain *by default*, but a wrong handle had no way out: the
40
+ * only accepted change was the one-time backfill of a NULL title, so a typo stayed searchable-by-
41
+ * nothing forever and the only repair was deleting the transaction. `retitle: true` on an update is
42
+ * that way out, and this table is what keeps it honest: the OLD title is recorded as an alias of the
43
+ * revision that replaced it, so every search for the old handle still finds the transaction (with
44
+ * `matched_title` naming what actually matched).
45
+ *
46
+ * Why `(fact_id, title)` rather than a chain id: aliases are derived search state of the revision
47
+ * that carries the title, exactly like `fact_sources`. The write path copies the predecessor's alias
48
+ * rows forward onto each new revision (see `copyTitleAliases`), so a correction never strands an old
49
+ * handle on an archived row. `ON DELETE CASCADE` keeps a purge from leaving orphans.
50
+ *
51
+ * No global UNIQUE on `title`: two unrelated transactions may have carried the same handle at
52
+ * different times, and refusing the second one would make the table a naming registry instead of a
53
+ * search aid.
54
+ */
55
+ export declare const TITLE_ALIASES_TABLE_DDL = "CREATE TABLE IF NOT EXISTS title_aliases (\n fact_id INTEGER NOT NULL REFERENCES facts(fact_id) ON DELETE CASCADE,\n title TEXT NOT NULL,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (fact_id, title)\n)";
56
+ /**
57
+ * v17's alias index: both the EXACT title comparison and the fuzzy title statement look an alias up
58
+ * by its TEXT (`ta.title = ?` / `ta.title LIKE ?`), and `(fact_id, title)` cannot serve that
59
+ * direction. The table only holds corrected handles, so it stays small; the index is what keeps the
60
+ * per-search cost independent of how many corrections the corpus has seen.
61
+ */
62
+ export declare const TITLE_ALIASES_INDEXES: string[];
63
+ /**
64
+ * v15's relation graph (RELATIONAL_RECALL §3): `entity_aliases`, `predicates`, `predicate_aliases`
65
+ * and `edges`.
66
+ *
67
+ * Why a second set of tables next to `entities` / `fact_entities` / `triples` instead of rewriting
68
+ * them: `triples` stores `subj` / `pred` / `obj` as TEXT, so every graph question is a string match
69
+ * that cannot tell two spellings of one entity apart and cannot keep a predicate's aliases in one
70
+ * place. `edges` keeps the same fact-level provenance (`fact_id`, `confidence`, `source`) but points
71
+ * at IDs, which is what a path query and a confidence policy need. The legacy `triples` table stays
72
+ * exactly as it is (the write path fills it, the contradiction detector reads it); `edges` is
73
+ * DERIVED from it by migration 15 until Phase 2 moves extraction onto IDs.
74
+ *
75
+ * Two shapes here are deliberate and easy to get wrong:
76
+ * - `extraction_version` is NOT NULL DEFAULT 0, the same convention as `facts.entities_version`:
77
+ * 0 means "before edge extraction was versioned", which is exactly what a row converted from
78
+ * `triples` is. A later re-extraction pass can therefore adopt it with
79
+ * `WHERE extraction_version < :current` instead of the row hiding behind a NULL forever.
80
+ * - `edges.created_at` carries the LEGACY triple's timestamp (`COALESCE(created_at, now)`), not the
81
+ * moment the backfill ran: this graph describes when a relation was recorded, and stamping a
82
+ * migration's wall clock onto a years-old relation would destroy that.
83
+ *
84
+ * `direction` has NO CHECK on purpose: the §4.2 vocabulary is Phase 2's to finish, and a CHECK on an
85
+ * enum that is still being designed costs a table rebuild to extend. The only value this phase
86
+ * writes is `'forward'`, documented as "subject → object is the stored direction; a reverse query
87
+ * reads the same edge backwards and is not a second edge".
88
+ */
89
+ export declare const ENTITY_ALIASES_TABLE_DDL = "CREATE TABLE IF NOT EXISTS entity_aliases (\n alias TEXT NOT NULL UNIQUE,\n entity_id INTEGER NOT NULL REFERENCES entities(entity_id) ON DELETE CASCADE,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n)";
90
+ export declare const PREDICATES_TABLE_DDL = "CREATE TABLE IF NOT EXISTS predicates (\n predicate_id INTEGER PRIMARY KEY AUTOINCREMENT,\n canonical_name TEXT NOT NULL UNIQUE,\n direction TEXT NOT NULL DEFAULT 'forward',\n description TEXT,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n)";
91
+ export declare const PREDICATE_ALIASES_TABLE_DDL = "CREATE TABLE IF NOT EXISTS predicate_aliases (\n alias TEXT NOT NULL UNIQUE,\n predicate_id INTEGER NOT NULL REFERENCES predicates(predicate_id) ON DELETE CASCADE,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n)";
92
+ export declare const EDGES_TABLE_DDL = "CREATE TABLE IF NOT EXISTS edges (\n edge_id INTEGER PRIMARY KEY AUTOINCREMENT,\n fact_id INTEGER NOT NULL REFERENCES facts(fact_id) ON DELETE CASCADE,\n subject_id INTEGER NOT NULL REFERENCES entities(entity_id) ON DELETE CASCADE,\n predicate_id INTEGER NOT NULL REFERENCES predicates(predicate_id) ON DELETE CASCADE,\n object_id INTEGER NOT NULL REFERENCES entities(entity_id) ON DELETE CASCADE,\n confidence REAL NOT NULL DEFAULT 0.5,\n extraction_version INTEGER NOT NULL DEFAULT 0,\n source TEXT NOT NULL DEFAULT 'heuristic',\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n UNIQUE (fact_id, subject_id, predicate_id, object_id)\n)";
93
+ /**
94
+ * The graph's indexes, spelled once for the base DDL and migration 15.
95
+ *
96
+ * `UNIQUE (fact_id, subject_id, predicate_id, object_id)` already serves "this fact's edges" and the
97
+ * `ON DELETE CASCADE` check for `fact_id`. The other three columns each need their own leading index
98
+ * because they are ALSO cascade parents and because they are the two directions a graph query walks:
99
+ * subject → (predicate) → object is one-hop/two-hop, object → predicate → subject is the reverse
100
+ * query ("who manages 李娜"). Without them every entity/predicate delete scans `edges`, and the
101
+ * reverse walk has no index at all.
102
+ */
103
+ export declare const RELATIONAL_GRAPH_INDEXES: string[];
9
104
  export declare const DDL: string[];
10
105
  /**
11
106
  * v2 — at most one OPEN row per contradiction pair.
@@ -24,3 +119,27 @@ export declare const ONE_OPEN_CONTRADICTION_ROW_SQL: string[];
24
119
  */
25
120
  export declare function factsFtsTableDdl(tokenizer: string): string;
26
121
  export declare const FTS_TRIGGERS: string[];
122
+ /** The title-side FTS table's name — one spelling for the DDL, the triggers and the query builder. */
123
+ export declare const TITLE_FTS_TABLE = "facts_title_fts";
124
+ /**
125
+ * The title FTS table (JOURNAL_TITLE_SEARCH §3/§11.3): a SECOND, independent external-content index.
126
+ *
127
+ * `facts_fts` keeps indexing `content` only and is not touched by this at all — mixing titles into the
128
+ * content score is exactly what the separate action exists to avoid. The table is external-content
129
+ * (`content='facts'`) so the rows are the fact table itself and `rebuild` can re-derive the index;
130
+ * `COALESCE(title,'')` in the triggers is what makes a NULL title indexable (external-content FTS5
131
+ * cannot store a NULL).
132
+ *
133
+ * A function, like {@link factsFtsTableDdl}, because the tokenizer is the one the database was built
134
+ * with (read back from `sqlite_master`), not necessarily this process's preference.
135
+ */
136
+ export declare function titleFtsTableDdl(tokenizer: string): string;
137
+ /**
138
+ * Keep {@link TITLE_FTS_TABLE} in step with `facts.title` — insert, delete, and `UPDATE OF title`.
139
+ *
140
+ * `AFTER UPDATE OF title` is deliberate and narrow: the content triggers fire for every content
141
+ * rewrite, while a title change is the one event this index cares about (a backfill, a merge copying
142
+ * a title onto an existing row). The `'delete'` command needs the OLD value, which is why the row is
143
+ * re-inserted rather than updated in place.
144
+ */
145
+ export declare const TITLE_FTS_TRIGGERS: string[];
@@ -82,3 +82,12 @@ export declare function likeSubstring(column: string, terms: readonly string[]):
82
82
  count: string;
83
83
  params: string[];
84
84
  };
85
+ /**
86
+ * The bind values {@link likeSubstring} uses, in its own order.
87
+ *
88
+ * Split out because a statement can compute the same substring predicate over TWO different columns
89
+ * (the title search ranks on the title that matched, which is either `facts.title` or an alias) while
90
+ * binding one pattern list. Empty terms are dropped here, so the caller never gets a `%%` that
91
+ * matches everything, and `%`/`_` stay literal through the same escaping.
92
+ */
93
+ export declare function likePatterns(terms: readonly string[]): string[];
@@ -0,0 +1,75 @@
1
+ /**
2
+ * The MOCK-REMOTE storage driver (DB-4, docs/DB_DRIVER_LAYOUT.md §五).
3
+ *
4
+ * What `storage.driver: fake` selects. It is a REAL driver — registered in the same registry as
5
+ * SQLite, declaring `contractVersion`, capabilities and an async `open()` — whose backend is the
6
+ * SQLite backend placed behind a simulated hop ({@link MockRemoteTransport}):
7
+ *
8
+ * store → StorageBackend (fake) → async surface (one hop per call) → SQLite core (in-memory)
9
+ *
10
+ * The purpose is narrow and stated: it proves the CONFIG SWITCH and the ASYNC BOUNDARY
11
+ * (`docs/DB_DRIVER_LAYOUT.md` §五, `docs/DATABASE_PORTABILITY.md` §十四). It is NOT a second storage
12
+ * engine and NOT the SQL independence proof: its core is this build's SQLite, so a query that only
13
+ * SQLite can answer would still "pass". A truly independent fake (or DB-5's `postgres`) plugs into
14
+ * the same registry and must pass the same `test/helpers/storage_contract.ts` suite unchanged — which
15
+ * is exactly the acceptance criterion, not this driver's implementation.
16
+ *
17
+ * Consequences of that choice, stated rather than hidden:
18
+ *
19
+ * - capabilities are the core's, because the mock has no capability of its own (no probing);
20
+ * - `relationalHandle()` returns the core's handle: the runtime's transitional SQL surfaces keep
21
+ * working on a fake-backed store (a genuinely remote driver would omit it and the runtime fails
22
+ * loudly, see `buildRuntime`);
23
+ * - the default core is `:memory:`, so opening a fake creates no file; `storage.fake.path` opts into
24
+ * a file-backed core for a test that wants one.
25
+ *
26
+ * @module dbdrivers/fake/driver
27
+ */
28
+ import type { StorageConfig } from '../../../mem-contract/index.js';
29
+ import type { StorageCapabilities } from '../../interfaces/capabilities.js';
30
+ import type { MigrationRunner } from '../../interfaces/migrations.js';
31
+ import type { ContradictionRepository, KeyValueRepository, MemoryRepository, RelationRepository, SourceRepository } from '../../interfaces/repositories.js';
32
+ import type { SearchIndex } from '../../interfaces/search_index.js';
33
+ import type { LifecycleService, StorageBackend } from '../../interfaces/storage_backend.js';
34
+ import type { StorageDriver } from '../../interfaces/storage_driver.js';
35
+ import { SqliteStorageBackend } from '../sqlite/driver.js';
36
+ import { MockRemoteTransport } from './transport.js';
37
+ /** The in-memory core a mock remote serves when `storage.fake.path` is unset. */
38
+ export declare const FAKE_IN_MEMORY_PATH = ":memory:";
39
+ /** What this driver can do: the core's capabilities, declared once and never probed above. */
40
+ export declare const FAKE_CAPABILITIES: StorageCapabilities;
41
+ /**
42
+ * The mock-remote backend: the same repositories as the core, every call one transport hop.
43
+ *
44
+ * The surface tree is derived from the core ({@link asyncSurface}), so a method added to a port is
45
+ * routed through the transport automatically — there is no hand-written delegation list that can
46
+ * silently miss one.
47
+ */
48
+ export declare class FakeStorageBackend implements StorageBackend {
49
+ readonly kind = "fake";
50
+ readonly capabilities: StorageCapabilities;
51
+ readonly facts: MemoryRepository;
52
+ readonly relations: RelationRepository;
53
+ readonly sources: SourceRepository;
54
+ readonly contradictions: ContradictionRepository;
55
+ readonly search: SearchIndex;
56
+ readonly keyValue: KeyValueRepository;
57
+ readonly lifecycle: LifecycleService;
58
+ readonly migrations: MigrationRunner;
59
+ /** The simulated hop every call above crosses (exposed for tests and diagnostics). */
60
+ readonly transport: MockRemoteTransport;
61
+ private readonly core;
62
+ constructor(core: SqliteStorageBackend, transport: MockRemoteTransport);
63
+ /**
64
+ * The transitional relational handle, delegated to the core (see this module's doc: a genuinely
65
+ * remote driver omits it, and `buildRuntime` refuses to start rather than inventing one).
66
+ */
67
+ relationalHandle(): unknown;
68
+ /**
69
+ * Closing the backend is not a REQUEST: the core is released directly, so a simulated failure can
70
+ * never leave a test (or a process) holding a half-closed store.
71
+ */
72
+ close(): Promise<void>;
73
+ }
74
+ /** The mock-remote driver: what `storage.driver: fake` (or the `storage.backend` alias) selects. */
75
+ export declare const fakeDriver: StorageDriver<StorageConfig>;
@@ -0,0 +1,7 @@
1
+ import type { StorageBackend } from '../../interfaces/storage_backend.js';
2
+ import { FakeStorageBackend } from './driver.js';
3
+ export { FAKE_CAPABILITIES, FAKE_IN_MEMORY_PATH, FakeStorageBackend, fakeDriver, } from './driver.js';
4
+ export { MOCK_REMOTE_FAILURE_CODE, MockRemoteTransport, type MockRemoteTurnaround, type StorageTransport, type TransportRequestRecord, } from './transport.js';
5
+ export { asyncSurface, type AsyncSurfaceOptions } from './surface.js';
6
+ /** Whether a backend was opened by THIS driver (a mock remote over this build's SQLite core). */
7
+ export declare function isFakeStorageBackend(backend: StorageBackend): backend is FakeStorageBackend;