@quolu/lattice 0.52.3 → 0.53.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 (188) hide show
  1. package/LICENSE +147 -147
  2. package/README.ja.md +355 -355
  3. package/README.md +258 -258
  4. package/bin/lattice-bridge.mjs +25 -0
  5. package/bin/lattice-hub.mjs +67 -0
  6. package/bin/lattice-mcp.mjs +0 -0
  7. package/bin/lattice-scripted-adapter.mjs +0 -0
  8. package/bin/lattice-scripted-worker.mjs +0 -0
  9. package/bin/lattice-work-order-adapter.mjs +0 -0
  10. package/bin/lattice.mjs +0 -0
  11. package/docs/bridge-setup.md +132 -132
  12. package/docs/schemas/lattice.executor_packet.v1.schema.json +57 -57
  13. package/docs/schemas/lattice.executor_receipt.v1.schema.json +66 -66
  14. package/docs/schemas/lattice.phase_todo_revision.v3.schema.json +360 -360
  15. package/docs/schemas/lattice.plan_create_input.v1.schema.json +56 -56
  16. package/docs/schemas/lattice.plan_create_input.v2.schema.json +72 -72
  17. package/docs/schemas/lattice.plan_create_input.v3.schema.json +81 -81
  18. package/docs/schemas/lattice.plan_create_input.v4.schema.json +85 -85
  19. package/docs/schemas/lattice.run_request.v1.schema.json +238 -238
  20. package/docs/schemas/lattice.runtime_adapter_capabilities.v2.schema.json +55 -55
  21. package/docs/schemas/lattice.runtime_adapter_registration_input.v1.schema.json +78 -78
  22. package/docs/schemas/lattice.runtime_adapter_registration_input.v2.schema.json +86 -86
  23. package/docs/schemas/lattice.todo_extraction.v2.schema.json +298 -298
  24. package/docs/schemas/lattice.todo_extraction.v3.schema.json +146 -146
  25. package/docs/schemas/lattice.todo_revision.v2.schema.json +260 -260
  26. package/docs/schemas/lattice.todo_revision_set.v3.schema.json +363 -363
  27. package/package.json +103 -103
  28. package/sensor/LICENSE +21 -21
  29. package/sensor/NOTICE +19 -19
  30. package/sensor/dist/bin/lattice-sensor.js +9 -9
  31. package/sensor/dist/db/index.js +24 -24
  32. package/sensor/dist/db/migrations.js +41 -41
  33. package/sensor/dist/db/queries.js +164 -164
  34. package/sensor/dist/db/schema.sql +205 -205
  35. package/sensor/dist/directory.js +5 -5
  36. package/sensor/dist/extraction/wasm/tree-sitter-c_sharp.wasm +0 -0
  37. package/sensor/dist/extraction/wasm/tree-sitter-cfml.wasm +0 -0
  38. package/sensor/dist/extraction/wasm/tree-sitter-cfquery.wasm +0 -0
  39. package/sensor/dist/extraction/wasm/tree-sitter-cfscript.wasm +0 -0
  40. package/sensor/dist/extraction/wasm/tree-sitter-cobol.wasm +0 -0
  41. package/sensor/dist/extraction/wasm/tree-sitter-erlang.wasm +0 -0
  42. package/sensor/dist/extraction/wasm/tree-sitter-go.wasm +0 -0
  43. package/sensor/dist/extraction/wasm/tree-sitter-java.wasm +0 -0
  44. package/sensor/dist/extraction/wasm/tree-sitter-javascript.wasm +0 -0
  45. package/sensor/dist/extraction/wasm/tree-sitter-nix.wasm +0 -0
  46. package/sensor/dist/extraction/wasm/tree-sitter-pascal.wasm +0 -0
  47. package/sensor/dist/extraction/wasm/tree-sitter-python.wasm +0 -0
  48. package/sensor/dist/extraction/wasm/tree-sitter-tsx.wasm +0 -0
  49. package/sensor/dist/extraction/wasm/tree-sitter-typescript.wasm +0 -0
  50. package/sensor/dist/extraction/wasm/tree-sitter-vbnet.wasm +0 -0
  51. package/sensor/dist/mcp/liveness-watchdog.js +53 -53
  52. package/sensor/dist/mcp/server-instructions.js +95 -95
  53. package/sensor/package.json +56 -56
  54. package/src/artifact-contracts-v2.mjs +325 -325
  55. package/src/artifact-contracts.mjs +895 -895
  56. package/src/boundary-compiler.mjs +712 -712
  57. package/src/boundary-observation-compiler-v2.mjs +344 -344
  58. package/src/bounded-seam.mjs +230 -230
  59. package/src/bridge-address.mjs +107 -107
  60. package/src/bridge-cli.mjs +303 -297
  61. package/src/bridge-config.mjs +382 -346
  62. package/src/bridge-daemon.mjs +378 -378
  63. package/src/bridge-hub-heartbeat.mjs +170 -0
  64. package/src/bridge-hub-protocol.mjs +198 -0
  65. package/src/bridge-hub-server.mjs +544 -0
  66. package/src/bridge-launch-agent.mjs +323 -323
  67. package/src/bridge-registrar.mjs +102 -102
  68. package/src/bridge-server.mjs +376 -376
  69. package/src/cli-help.mjs +308 -308
  70. package/src/cli-stdio.mjs +40 -40
  71. package/src/control-compiler.mjs +532 -532
  72. package/src/dag-chain.mjs +261 -261
  73. package/src/factory-diagnostics.mjs +188 -188
  74. package/src/git-process.mjs +72 -0
  75. package/src/hash-chain.mjs +76 -76
  76. package/src/hooks-cli.mjs +1057 -1057
  77. package/src/isolation-runner.mjs +409 -409
  78. package/src/node-version-guard.mjs +44 -44
  79. package/src/project-cli.mjs +3 -3
  80. package/src/project-identity.mjs +130 -130
  81. package/src/rc1-black-box-oracle.mjs +906 -906
  82. package/src/rc1-comparison.mjs +154 -154
  83. package/src/rc1-evidence-bundle.mjs +456 -456
  84. package/src/rc1-v4-campaign.mjs +708 -708
  85. package/src/rc1-v4-transform.mjs +467 -467
  86. package/src/rc1-v5-artifact-set.mjs +855 -855
  87. package/src/rc1-v5-behavior-evidence.mjs +594 -594
  88. package/src/rc1-v5-campaign.mjs +797 -797
  89. package/src/rc1-v5-transform.mjs +421 -421
  90. package/src/rc1-v6-artifact-set.mjs +807 -807
  91. package/src/rc1-v6-behavior-evidence.mjs +273 -273
  92. package/src/rc1-v6-campaign.mjs +623 -623
  93. package/src/rc1-v6-causal-binding.mjs +473 -473
  94. package/src/rc1-v6-measurement.mjs +313 -313
  95. package/src/rc2-artifact-set.mjs +1584 -1584
  96. package/src/rc2-campaign.mjs +1506 -1506
  97. package/src/rc2-delivery-policy-front-end.mjs +1079 -1079
  98. package/src/rc2-delivery-policy-oracle.mjs +134 -134
  99. package/src/rc2-delivery-policy-transform.mjs +1127 -1127
  100. package/src/rc2-rc1-transfer-front-end.mjs +511 -511
  101. package/src/rc3-actual-dogfood.mjs +652 -652
  102. package/src/rc3-dogfood-scaffold.mjs +315 -315
  103. package/src/rc3-scripted-campaign.mjs +1383 -1383
  104. package/src/rc4-stage1-dogfood.mjs +673 -673
  105. package/src/runtime-adapter-registry.mjs +524 -524
  106. package/src/runtime-cli.mjs +4588 -4588
  107. package/src/runtime-contracts.mjs +824 -824
  108. package/src/runtime-control-store.mjs +604 -604
  109. package/src/runtime-controller-protocol.mjs +587 -587
  110. package/src/runtime-decision-verifier.mjs +701 -701
  111. package/src/runtime-diff-observer.mjs +361 -361
  112. package/src/runtime-direct-os-observer.mjs +301 -301
  113. package/src/runtime-driver-state.mjs +166 -166
  114. package/src/runtime-engine.mjs +779 -779
  115. package/src/runtime-errors.mjs +356 -356
  116. package/src/runtime-event-store.mjs +189 -189
  117. package/src/runtime-front-end.mjs +925 -925
  118. package/src/runtime-gate-store.mjs +481 -481
  119. package/src/runtime-hold-recompile.mjs +916 -916
  120. package/src/runtime-io-sentinel.mjs +391 -391
  121. package/src/runtime-lifecycle-lock.mjs +294 -294
  122. package/src/runtime-managed-supervisor.mjs +1490 -1490
  123. package/src/runtime-multi-epoch-store.mjs +838 -838
  124. package/src/runtime-projection.mjs +269 -269
  125. package/src/runtime-pull-intake.mjs +1192 -1192
  126. package/src/runtime-scripted-adapter-controller.mjs +1160 -1160
  127. package/src/runtime-scripted-executor.mjs +163 -163
  128. package/src/runtime-scripted-worktree.mjs +104 -104
  129. package/src/runtime-seam-resolve.mjs +428 -428
  130. package/src/runtime-seam-treatment.mjs +173 -173
  131. package/src/runtime-socket-owner.mjs +125 -125
  132. package/src/runtime-work-order-contracts.mjs +91 -91
  133. package/src/runtime-work-order-controller.mjs +1171 -1171
  134. package/src/runtime-worktree-executor.mjs +199 -199
  135. package/src/schedulability-compiler-v2.mjs +303 -303
  136. package/src/schedulability-verifier-v2.mjs +317 -317
  137. package/src/seam-apply.mjs +549 -549
  138. package/src/seam-commit-shared.mjs +22 -22
  139. package/src/seam-commit-transform.mjs +81 -81
  140. package/src/seam-commit.mjs +18 -18
  141. package/src/seam-cost.mjs +322 -322
  142. package/src/seam-derivation.mjs +188 -188
  143. package/src/seam-gate.mjs +146 -146
  144. package/src/seam-proposal-contracts.mjs +446 -446
  145. package/src/seam-proposal-queries.mjs +521 -521
  146. package/src/seam-proposal.mjs +2011 -2011
  147. package/src/seam-ref.mjs +33 -33
  148. package/src/seam-rewrite.mjs +286 -286
  149. package/src/seam-transform.mjs +554 -554
  150. package/src/seam-verification.mjs +260 -260
  151. package/src/sensor-adapter.mjs +432 -432
  152. package/src/sensor-cli.mjs +139 -139
  153. package/src/sensor-diff.mjs +661 -661
  154. package/src/sensor-node-runtime.mjs +53 -53
  155. package/src/sensor-runtime.mjs +52 -52
  156. package/src/timestamp-contract.mjs +8 -8
  157. package/src/todo-audit-pending.mjs +91 -91
  158. package/src/todo-chain.mjs +178 -178
  159. package/src/todo-cli.mjs +8 -8
  160. package/src/todo-contracts.mjs +728 -728
  161. package/src/todo-dashboard-registry.mjs +573 -573
  162. package/src/todo-dispatch-shape.mjs +190 -190
  163. package/src/todo-gantt-html-independence.mjs +239 -239
  164. package/src/todo-gantt-html-shared.mjs +226 -226
  165. package/src/todo-gantt-html-style.mjs +131 -131
  166. package/src/todo-gantt-html.mjs +248 -248
  167. package/src/todo-gantt-layout.mjs +974 -974
  168. package/src/todo-gantt-live.mjs +361 -361
  169. package/src/todo-gantt-nested.mjs +263 -263
  170. package/src/todo-gantt-presentation.mjs +217 -217
  171. package/src/todo-gantt-scope.mjs +123 -123
  172. package/src/todo-gantt-svg.mjs +353 -353
  173. package/src/todo-independence-contracts.mjs +595 -595
  174. package/src/todo-independence-guidance.mjs +322 -322
  175. package/src/todo-independence.mjs +640 -640
  176. package/src/todo-markdown-renderer.mjs +260 -260
  177. package/src/todo-migration.mjs +448 -448
  178. package/src/todo-narrative-anchor.mjs +130 -130
  179. package/src/todo-note-store.mjs +629 -629
  180. package/src/todo-parallel-candidates.mjs +114 -114
  181. package/src/todo-revision.mjs +995 -995
  182. package/src/todo-split.mjs +472 -472
  183. package/src/todo-status.mjs +690 -690
  184. package/src/todo-store-git-transaction.mjs +418 -418
  185. package/src/todo-store.mjs +42 -21
  186. package/src/treatment-compiler.mjs +728 -728
  187. package/src/treatment-runner.mjs +656 -656
  188. package/src/witness-scaffold.mjs +180 -180
@@ -1,205 +1,205 @@
1
- -- LatticeSensor SQLite Schema
2
- -- Version 1
3
-
4
- -- Schema version tracking
5
- CREATE TABLE IF NOT EXISTS schema_versions (
6
- version INTEGER PRIMARY KEY,
7
- applied_at INTEGER NOT NULL,
8
- description TEXT
9
- );
10
-
11
- -- Insert initial version
12
- INSERT INTO schema_versions (version, applied_at, description)
13
- VALUES (1, strftime('%s', 'now') * 1000, 'Initial schema');
14
-
15
- -- =============================================================================
16
- -- Core Tables
17
- -- =============================================================================
18
-
19
- -- Nodes: Code symbols (functions, classes, variables, etc.)
20
- CREATE TABLE IF NOT EXISTS nodes (
21
- id TEXT PRIMARY KEY,
22
- kind TEXT NOT NULL,
23
- name TEXT NOT NULL,
24
- qualified_name TEXT NOT NULL,
25
- file_path TEXT NOT NULL,
26
- language TEXT NOT NULL,
27
- start_line INTEGER NOT NULL,
28
- end_line INTEGER NOT NULL,
29
- start_column INTEGER NOT NULL,
30
- end_column INTEGER NOT NULL,
31
- docstring TEXT,
32
- signature TEXT,
33
- visibility TEXT,
34
- is_exported INTEGER DEFAULT 0,
35
- is_async INTEGER DEFAULT 0,
36
- is_static INTEGER DEFAULT 0,
37
- is_abstract INTEGER DEFAULT 0,
38
- decorators TEXT, -- JSON array
39
- type_parameters TEXT, -- JSON array
40
- return_type TEXT,
41
- -- v11: first line including preceding decorators/attributes (NULL = none).
42
- extent_start_line INTEGER, -- normalized return/result type name (e.g. C++ method return, for receiver-type inference)
43
- updated_at INTEGER NOT NULL
44
- );
45
-
46
- -- Edges: Relationships between nodes
47
- CREATE TABLE IF NOT EXISTS edges (
48
- id INTEGER PRIMARY KEY AUTOINCREMENT,
49
- source TEXT NOT NULL,
50
- target TEXT NOT NULL,
51
- kind TEXT NOT NULL,
52
- metadata TEXT, -- JSON object
53
- line INTEGER,
54
- col INTEGER,
55
- provenance TEXT DEFAULT NULL,
56
- confidence REAL, -- resolution confidence (0.0-1.0), mirrors metadata.confidence — v9
57
- resolved_by TEXT, -- resolution strategy name, mirrors metadata.resolvedBy — v9
58
- FOREIGN KEY (source) REFERENCES nodes(id) ON DELETE CASCADE,
59
- FOREIGN KEY (target) REFERENCES nodes(id) ON DELETE CASCADE
60
- );
61
-
62
- -- Files: Tracked source files
63
- CREATE TABLE IF NOT EXISTS files (
64
- path TEXT PRIMARY KEY,
65
- content_hash TEXT NOT NULL,
66
- language TEXT NOT NULL,
67
- size INTEGER NOT NULL,
68
- modified_at INTEGER NOT NULL,
69
- indexed_at INTEGER NOT NULL,
70
- node_count INTEGER DEFAULT 0,
71
- -- EXTRACTION_VERSION of the engine that wrote this row. 0 = written before
72
- -- the stamp existed; sync treats any mismatch with the running engine as a
73
- -- pending change so extractor upgrades heal incrementally (v12).
74
- extraction_version INTEGER NOT NULL DEFAULT 0,
75
- errors TEXT -- JSON array
76
- );
77
-
78
- -- Unresolved References: References that need resolution after full indexing.
79
- -- status lifecycle: rows are inserted 'pending' by extraction; a completed
80
- -- resolution pass either deletes a row (resolved) or marks it 'failed'
81
- -- (attempted, no match — kept so a later sync can retry it when a changed
82
- -- file introduces a symbol that could satisfy it, #1240). name_tail is the
83
- -- last segment of reference_name ('util.greet' → 'greet'), written when a
84
- -- row is marked failed, so the retry lookup matches new node names against
85
- -- dotted refs too. Rows follow their from_node via ON DELETE CASCADE, so
86
- -- re-extracting or deleting a file clears its stale rows in any status.
87
- CREATE TABLE IF NOT EXISTS unresolved_refs (
88
- id INTEGER PRIMARY KEY AUTOINCREMENT,
89
- from_node_id TEXT NOT NULL,
90
- reference_name TEXT NOT NULL,
91
- reference_kind TEXT NOT NULL,
92
- line INTEGER NOT NULL,
93
- col INTEGER NOT NULL,
94
- candidates TEXT, -- JSON array
95
- file_path TEXT NOT NULL DEFAULT '',
96
- language TEXT NOT NULL DEFAULT 'unknown',
97
- status TEXT NOT NULL DEFAULT 'pending',
98
- name_tail TEXT NOT NULL DEFAULT '',
99
- -- Import binding shape (v10): default/named/namespace + source-side name.
100
- binding_form TEXT,
101
- imported_name TEXT,
102
- FOREIGN KEY (from_node_id) REFERENCES nodes(id) ON DELETE CASCADE
103
- );
104
-
105
- -- =============================================================================
106
- -- Indexes for Query Performance
107
- -- =============================================================================
108
-
109
- -- Node indexes
110
- CREATE INDEX IF NOT EXISTS idx_nodes_kind ON nodes(kind);
111
- CREATE INDEX IF NOT EXISTS idx_nodes_name ON nodes(name);
112
- CREATE INDEX IF NOT EXISTS idx_nodes_qualified_name ON nodes(qualified_name);
113
- CREATE INDEX IF NOT EXISTS idx_nodes_file_path ON nodes(file_path);
114
- CREATE INDEX IF NOT EXISTS idx_nodes_language ON nodes(language);
115
- CREATE INDEX IF NOT EXISTS idx_nodes_file_line ON nodes(file_path, start_line);
116
- CREATE INDEX IF NOT EXISTS idx_nodes_lower_name ON nodes(lower(name));
117
-
118
- -- Full-text search index on node names, docstrings, and signatures
119
- CREATE VIRTUAL TABLE IF NOT EXISTS nodes_fts USING fts5(
120
- id,
121
- name,
122
- qualified_name,
123
- docstring,
124
- signature,
125
- content='nodes',
126
- content_rowid='rowid'
127
- );
128
-
129
- -- Triggers to keep FTS index in sync
130
- CREATE TRIGGER IF NOT EXISTS nodes_ai AFTER INSERT ON nodes BEGIN
131
- INSERT INTO nodes_fts(rowid, id, name, qualified_name, docstring, signature)
132
- VALUES (NEW.rowid, NEW.id, NEW.name, NEW.qualified_name, NEW.docstring, NEW.signature);
133
- END;
134
-
135
- CREATE TRIGGER IF NOT EXISTS nodes_ad AFTER DELETE ON nodes BEGIN
136
- INSERT INTO nodes_fts(nodes_fts, rowid, id, name, qualified_name, docstring, signature)
137
- VALUES ('delete', OLD.rowid, OLD.id, OLD.name, OLD.qualified_name, OLD.docstring, OLD.signature);
138
- END;
139
-
140
- CREATE TRIGGER IF NOT EXISTS nodes_au AFTER UPDATE ON nodes BEGIN
141
- INSERT INTO nodes_fts(nodes_fts, rowid, id, name, qualified_name, docstring, signature)
142
- VALUES ('delete', OLD.rowid, OLD.id, OLD.name, OLD.qualified_name, OLD.docstring, OLD.signature);
143
- INSERT INTO nodes_fts(rowid, id, name, qualified_name, docstring, signature)
144
- VALUES (NEW.rowid, NEW.id, NEW.name, NEW.qualified_name, NEW.docstring, NEW.signature);
145
- END;
146
-
147
- -- Prose-word → symbol-name lookup for the prompt hook's graph-derived gate.
148
- -- One row per (segment, name): segment is a lowercased word of a symbol name
149
- -- ("OrderStateMachine" → order, state, machine — see identifier-segments.ts),
150
- -- which lets natural-language prompt words be verified against the graph in
151
- -- any language whose technical nouns are Latin script. File nodes are
152
- -- excluded — a file's basename duplicates the symbols inside it and skews the
153
- -- singleton-vs-cluster rarity statistics. FTS can't serve this lookup (its
154
- -- tokenizer keeps camelCase names as single tokens), so segments are
155
- -- materialized on the node write path.
156
- -- Deletions leave orphan rows ON PURPOSE: rows are PROPOSALS, always
157
- -- re-verified against nodes before being surfaced (LatticeSensor.getSegmentMatches),
158
- -- and a full index clears the table at its start. Populated lazily on old
159
- -- databases (empty until the next index/sync heals it).
160
- CREATE TABLE IF NOT EXISTS name_segment_vocab (
161
- segment TEXT NOT NULL,
162
- name TEXT NOT NULL,
163
- PRIMARY KEY (segment, name)
164
- ) WITHOUT ROWID;
165
-
166
- -- Edge indexes.
167
- -- idx_edges_source / idx_edges_target are intentionally omitted —
168
- -- the (source, kind) and (target, kind) composites below cover the
169
- -- corresponding source-only / target-only lookups via SQLite's
170
- -- left-prefix scan, so the narrow indexes are dead weight on writes.
171
- -- Migration v4 drops them on existing databases.
172
- CREATE INDEX IF NOT EXISTS idx_edges_kind ON edges(kind);
173
- CREATE INDEX IF NOT EXISTS idx_edges_source_kind ON edges(source, kind);
174
- CREATE INDEX IF NOT EXISTS idx_edges_target_kind ON edges(target, kind);
175
-
176
- -- Edge identity uniqueness. An edge IS uniquely (source, target, kind, line,
177
- -- col); insertEdge uses `INSERT OR IGNORE`, but without something UNIQUE to
178
- -- conflict on it behaved like a plain INSERT, so two passes emitting the same
179
- -- edge produced byte-identical duplicate rows that inflated counts and flowed
180
- -- into callers/impact (#1034). IFNULL folds the nullable line/col so
181
- -- coordinate-less edges (synthesized / file-level) dedup too — SQLite treats
182
- -- each NULL as distinct otherwise. Migration v6 dedups existing rows + adds
183
- -- this on older databases.
184
- CREATE UNIQUE INDEX IF NOT EXISTS idx_edges_identity
185
- ON edges(source, target, kind, IFNULL(line, -1), IFNULL(col, -1));
186
-
187
- -- File indexes
188
- CREATE INDEX IF NOT EXISTS idx_files_language ON files(language);
189
- CREATE INDEX IF NOT EXISTS idx_files_modified_at ON files(modified_at);
190
-
191
- -- Unresolved refs indexes
192
- CREATE INDEX IF NOT EXISTS idx_unresolved_from_node ON unresolved_refs(from_node_id);
193
- CREATE INDEX IF NOT EXISTS idx_unresolved_name ON unresolved_refs(reference_name);
194
- CREATE INDEX IF NOT EXISTS idx_unresolved_file_path ON unresolved_refs(file_path);
195
- CREATE INDEX IF NOT EXISTS idx_unresolved_from_name ON unresolved_refs(from_node_id, reference_name);
196
- CREATE INDEX IF NOT EXISTS idx_unresolved_status ON unresolved_refs(status);
197
- CREATE INDEX IF NOT EXISTS idx_unresolved_failed_tail ON unresolved_refs(name_tail) WHERE status = 'failed';
198
- CREATE INDEX IF NOT EXISTS idx_edges_provenance ON edges(provenance);
199
-
200
- -- Project metadata for version/provenance tracking
201
- CREATE TABLE IF NOT EXISTS project_metadata (
202
- key TEXT PRIMARY KEY,
203
- value TEXT NOT NULL,
204
- updated_at INTEGER NOT NULL
205
- );
1
+ -- LatticeSensor SQLite Schema
2
+ -- Version 1
3
+
4
+ -- Schema version tracking
5
+ CREATE TABLE IF NOT EXISTS schema_versions (
6
+ version INTEGER PRIMARY KEY,
7
+ applied_at INTEGER NOT NULL,
8
+ description TEXT
9
+ );
10
+
11
+ -- Insert initial version
12
+ INSERT INTO schema_versions (version, applied_at, description)
13
+ VALUES (1, strftime('%s', 'now') * 1000, 'Initial schema');
14
+
15
+ -- =============================================================================
16
+ -- Core Tables
17
+ -- =============================================================================
18
+
19
+ -- Nodes: Code symbols (functions, classes, variables, etc.)
20
+ CREATE TABLE IF NOT EXISTS nodes (
21
+ id TEXT PRIMARY KEY,
22
+ kind TEXT NOT NULL,
23
+ name TEXT NOT NULL,
24
+ qualified_name TEXT NOT NULL,
25
+ file_path TEXT NOT NULL,
26
+ language TEXT NOT NULL,
27
+ start_line INTEGER NOT NULL,
28
+ end_line INTEGER NOT NULL,
29
+ start_column INTEGER NOT NULL,
30
+ end_column INTEGER NOT NULL,
31
+ docstring TEXT,
32
+ signature TEXT,
33
+ visibility TEXT,
34
+ is_exported INTEGER DEFAULT 0,
35
+ is_async INTEGER DEFAULT 0,
36
+ is_static INTEGER DEFAULT 0,
37
+ is_abstract INTEGER DEFAULT 0,
38
+ decorators TEXT, -- JSON array
39
+ type_parameters TEXT, -- JSON array
40
+ return_type TEXT,
41
+ -- v11: first line including preceding decorators/attributes (NULL = none).
42
+ extent_start_line INTEGER, -- normalized return/result type name (e.g. C++ method return, for receiver-type inference)
43
+ updated_at INTEGER NOT NULL
44
+ );
45
+
46
+ -- Edges: Relationships between nodes
47
+ CREATE TABLE IF NOT EXISTS edges (
48
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
49
+ source TEXT NOT NULL,
50
+ target TEXT NOT NULL,
51
+ kind TEXT NOT NULL,
52
+ metadata TEXT, -- JSON object
53
+ line INTEGER,
54
+ col INTEGER,
55
+ provenance TEXT DEFAULT NULL,
56
+ confidence REAL, -- resolution confidence (0.0-1.0), mirrors metadata.confidence — v9
57
+ resolved_by TEXT, -- resolution strategy name, mirrors metadata.resolvedBy — v9
58
+ FOREIGN KEY (source) REFERENCES nodes(id) ON DELETE CASCADE,
59
+ FOREIGN KEY (target) REFERENCES nodes(id) ON DELETE CASCADE
60
+ );
61
+
62
+ -- Files: Tracked source files
63
+ CREATE TABLE IF NOT EXISTS files (
64
+ path TEXT PRIMARY KEY,
65
+ content_hash TEXT NOT NULL,
66
+ language TEXT NOT NULL,
67
+ size INTEGER NOT NULL,
68
+ modified_at INTEGER NOT NULL,
69
+ indexed_at INTEGER NOT NULL,
70
+ node_count INTEGER DEFAULT 0,
71
+ -- EXTRACTION_VERSION of the engine that wrote this row. 0 = written before
72
+ -- the stamp existed; sync treats any mismatch with the running engine as a
73
+ -- pending change so extractor upgrades heal incrementally (v12).
74
+ extraction_version INTEGER NOT NULL DEFAULT 0,
75
+ errors TEXT -- JSON array
76
+ );
77
+
78
+ -- Unresolved References: References that need resolution after full indexing.
79
+ -- status lifecycle: rows are inserted 'pending' by extraction; a completed
80
+ -- resolution pass either deletes a row (resolved) or marks it 'failed'
81
+ -- (attempted, no match — kept so a later sync can retry it when a changed
82
+ -- file introduces a symbol that could satisfy it, #1240). name_tail is the
83
+ -- last segment of reference_name ('util.greet' → 'greet'), written when a
84
+ -- row is marked failed, so the retry lookup matches new node names against
85
+ -- dotted refs too. Rows follow their from_node via ON DELETE CASCADE, so
86
+ -- re-extracting or deleting a file clears its stale rows in any status.
87
+ CREATE TABLE IF NOT EXISTS unresolved_refs (
88
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
89
+ from_node_id TEXT NOT NULL,
90
+ reference_name TEXT NOT NULL,
91
+ reference_kind TEXT NOT NULL,
92
+ line INTEGER NOT NULL,
93
+ col INTEGER NOT NULL,
94
+ candidates TEXT, -- JSON array
95
+ file_path TEXT NOT NULL DEFAULT '',
96
+ language TEXT NOT NULL DEFAULT 'unknown',
97
+ status TEXT NOT NULL DEFAULT 'pending',
98
+ name_tail TEXT NOT NULL DEFAULT '',
99
+ -- Import binding shape (v10): default/named/namespace + source-side name.
100
+ binding_form TEXT,
101
+ imported_name TEXT,
102
+ FOREIGN KEY (from_node_id) REFERENCES nodes(id) ON DELETE CASCADE
103
+ );
104
+
105
+ -- =============================================================================
106
+ -- Indexes for Query Performance
107
+ -- =============================================================================
108
+
109
+ -- Node indexes
110
+ CREATE INDEX IF NOT EXISTS idx_nodes_kind ON nodes(kind);
111
+ CREATE INDEX IF NOT EXISTS idx_nodes_name ON nodes(name);
112
+ CREATE INDEX IF NOT EXISTS idx_nodes_qualified_name ON nodes(qualified_name);
113
+ CREATE INDEX IF NOT EXISTS idx_nodes_file_path ON nodes(file_path);
114
+ CREATE INDEX IF NOT EXISTS idx_nodes_language ON nodes(language);
115
+ CREATE INDEX IF NOT EXISTS idx_nodes_file_line ON nodes(file_path, start_line);
116
+ CREATE INDEX IF NOT EXISTS idx_nodes_lower_name ON nodes(lower(name));
117
+
118
+ -- Full-text search index on node names, docstrings, and signatures
119
+ CREATE VIRTUAL TABLE IF NOT EXISTS nodes_fts USING fts5(
120
+ id,
121
+ name,
122
+ qualified_name,
123
+ docstring,
124
+ signature,
125
+ content='nodes',
126
+ content_rowid='rowid'
127
+ );
128
+
129
+ -- Triggers to keep FTS index in sync
130
+ CREATE TRIGGER IF NOT EXISTS nodes_ai AFTER INSERT ON nodes BEGIN
131
+ INSERT INTO nodes_fts(rowid, id, name, qualified_name, docstring, signature)
132
+ VALUES (NEW.rowid, NEW.id, NEW.name, NEW.qualified_name, NEW.docstring, NEW.signature);
133
+ END;
134
+
135
+ CREATE TRIGGER IF NOT EXISTS nodes_ad AFTER DELETE ON nodes BEGIN
136
+ INSERT INTO nodes_fts(nodes_fts, rowid, id, name, qualified_name, docstring, signature)
137
+ VALUES ('delete', OLD.rowid, OLD.id, OLD.name, OLD.qualified_name, OLD.docstring, OLD.signature);
138
+ END;
139
+
140
+ CREATE TRIGGER IF NOT EXISTS nodes_au AFTER UPDATE ON nodes BEGIN
141
+ INSERT INTO nodes_fts(nodes_fts, rowid, id, name, qualified_name, docstring, signature)
142
+ VALUES ('delete', OLD.rowid, OLD.id, OLD.name, OLD.qualified_name, OLD.docstring, OLD.signature);
143
+ INSERT INTO nodes_fts(rowid, id, name, qualified_name, docstring, signature)
144
+ VALUES (NEW.rowid, NEW.id, NEW.name, NEW.qualified_name, NEW.docstring, NEW.signature);
145
+ END;
146
+
147
+ -- Prose-word → symbol-name lookup for the prompt hook's graph-derived gate.
148
+ -- One row per (segment, name): segment is a lowercased word of a symbol name
149
+ -- ("OrderStateMachine" → order, state, machine — see identifier-segments.ts),
150
+ -- which lets natural-language prompt words be verified against the graph in
151
+ -- any language whose technical nouns are Latin script. File nodes are
152
+ -- excluded — a file's basename duplicates the symbols inside it and skews the
153
+ -- singleton-vs-cluster rarity statistics. FTS can't serve this lookup (its
154
+ -- tokenizer keeps camelCase names as single tokens), so segments are
155
+ -- materialized on the node write path.
156
+ -- Deletions leave orphan rows ON PURPOSE: rows are PROPOSALS, always
157
+ -- re-verified against nodes before being surfaced (LatticeSensor.getSegmentMatches),
158
+ -- and a full index clears the table at its start. Populated lazily on old
159
+ -- databases (empty until the next index/sync heals it).
160
+ CREATE TABLE IF NOT EXISTS name_segment_vocab (
161
+ segment TEXT NOT NULL,
162
+ name TEXT NOT NULL,
163
+ PRIMARY KEY (segment, name)
164
+ ) WITHOUT ROWID;
165
+
166
+ -- Edge indexes.
167
+ -- idx_edges_source / idx_edges_target are intentionally omitted —
168
+ -- the (source, kind) and (target, kind) composites below cover the
169
+ -- corresponding source-only / target-only lookups via SQLite's
170
+ -- left-prefix scan, so the narrow indexes are dead weight on writes.
171
+ -- Migration v4 drops them on existing databases.
172
+ CREATE INDEX IF NOT EXISTS idx_edges_kind ON edges(kind);
173
+ CREATE INDEX IF NOT EXISTS idx_edges_source_kind ON edges(source, kind);
174
+ CREATE INDEX IF NOT EXISTS idx_edges_target_kind ON edges(target, kind);
175
+
176
+ -- Edge identity uniqueness. An edge IS uniquely (source, target, kind, line,
177
+ -- col); insertEdge uses `INSERT OR IGNORE`, but without something UNIQUE to
178
+ -- conflict on it behaved like a plain INSERT, so two passes emitting the same
179
+ -- edge produced byte-identical duplicate rows that inflated counts and flowed
180
+ -- into callers/impact (#1034). IFNULL folds the nullable line/col so
181
+ -- coordinate-less edges (synthesized / file-level) dedup too — SQLite treats
182
+ -- each NULL as distinct otherwise. Migration v6 dedups existing rows + adds
183
+ -- this on older databases.
184
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_edges_identity
185
+ ON edges(source, target, kind, IFNULL(line, -1), IFNULL(col, -1));
186
+
187
+ -- File indexes
188
+ CREATE INDEX IF NOT EXISTS idx_files_language ON files(language);
189
+ CREATE INDEX IF NOT EXISTS idx_files_modified_at ON files(modified_at);
190
+
191
+ -- Unresolved refs indexes
192
+ CREATE INDEX IF NOT EXISTS idx_unresolved_from_node ON unresolved_refs(from_node_id);
193
+ CREATE INDEX IF NOT EXISTS idx_unresolved_name ON unresolved_refs(reference_name);
194
+ CREATE INDEX IF NOT EXISTS idx_unresolved_file_path ON unresolved_refs(file_path);
195
+ CREATE INDEX IF NOT EXISTS idx_unresolved_from_name ON unresolved_refs(from_node_id, reference_name);
196
+ CREATE INDEX IF NOT EXISTS idx_unresolved_status ON unresolved_refs(status);
197
+ CREATE INDEX IF NOT EXISTS idx_unresolved_failed_tail ON unresolved_refs(name_tail) WHERE status = 'failed';
198
+ CREATE INDEX IF NOT EXISTS idx_edges_provenance ON edges(provenance);
199
+
200
+ -- Project metadata for version/provenance tracking
201
+ CREATE TABLE IF NOT EXISTS project_metadata (
202
+ key TEXT PRIMARY KEY,
203
+ value TEXT NOT NULL,
204
+ updated_at INTEGER NOT NULL
205
+ );
@@ -551,11 +551,11 @@ function planFrontload(cwd, prompt) {
551
551
  * explicit allowlist that never listed `daemon.pid` or the socket, so those
552
552
  * runtime files were silently committed.
553
553
  */
554
- const GITIGNORE_CONTENT = `# LatticeSensor data files — local to each machine, not for committing.
555
- # Ignore everything in .lattice/sensor/ except this file itself, so transient
556
- # files (the database, daemon.pid, sockets, logs) never show up in git.
557
- *
558
- !.gitignore
554
+ const GITIGNORE_CONTENT = `# LatticeSensor data files — local to each machine, not for committing.
555
+ # Ignore everything in .lattice/sensor/ except this file itself, so transient
556
+ # files (the database, daemon.pid, sockets, logs) never show up in git.
557
+ *
558
+ !.gitignore
559
559
  `;
560
560
  /** Header line that prefixes every .gitignore LatticeSensor has auto-generated. */
561
561
  const GITIGNORE_MARKER = '# LatticeSensor data files';
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -134,59 +134,59 @@ function debug(msg) {
134
134
  * target pid + timeout from argv; an MSG built once at startup (the child is
135
135
  * never wedged, so allocation here is fine).
136
136
  */
137
- const CHILD_SOURCE = `
138
- const fs = require('fs');
139
- const parentPid = Number(process.argv[1]);
140
- const timeoutMs = Number(process.argv[2]);
141
- const capMs = Number(process.argv[3]);
142
- const progressPaths = process.argv.slice(4);
143
- const secs = Math.round(timeoutMs / 1000);
144
- function kill(extra) {
145
- // Timestamped so daemon.log kills can be correlated with anything (#1431) —
146
- // computed here at kill time; this child process is never the wedged one.
147
- try { fs.writeSync(2, Buffer.from('[' + new Date().toISOString() + '] [LatticeSensor] Main thread unresponsive for ~' + secs + 's' + (extra || '') + ' — killing the wedged process so a fresh one can start (#850). Disable with LATTICE_SENSOR_NO_WATCHDOG=1.\\n')); } catch (e) {}
148
- try { process.kill(parentPid, 'SIGKILL'); } catch (e) {}
149
- process.exit(0);
150
- }
151
- // Fingerprint of the watched files (size + mtime). A change between checks is
152
- // forward disk progress — a slow synchronous SQLite statement, not a wedge.
153
- function snap() {
154
- let s = '';
155
- for (const p of progressPaths) {
156
- try { const st = fs.statSync(p); s += st.size + ':' + st.mtimeMs + ';'; } catch (e) { s += 'x;'; }
157
- }
158
- return s;
159
- }
160
- let lastSnap = progressPaths.length ? snap() : '';
161
- let lastSnapAt = Date.now();
162
- let silentSince = null; // start of the current continuous-silence episode
163
- function onTimeout() {
164
- if (!progressPaths.length) return kill('');
165
- const now = Date.now();
166
- if (silentSince === null) silentSince = now - timeoutMs; // silence began ~one timeout ago
167
- const cur = snap();
168
- if (cur !== lastSnap && now - silentSince < capMs) {
169
- // The event loop is blocked but the DB files are advancing: a legitimate
170
- // long store on slow storage. Defer, re-baseline, keep watching.
171
- lastSnap = cur;
172
- timer = setTimeout(onTimeout, timeoutMs);
173
- return;
174
- }
175
- kill(cur !== lastSnap ? ' despite ongoing disk activity (hard cap ' + Math.round(capMs / 1000) + 's reached)' : '');
176
- }
177
- let timer = setTimeout(onTimeout, timeoutMs);
178
- process.stdin.on('data', () => {
179
- silentSince = null;
180
- // Keep the baseline fresh while healthy (throttled — a stat per second).
181
- if (progressPaths.length) {
182
- const t = Date.now();
183
- if (t - lastSnapAt >= 1000) { lastSnap = snap(); lastSnapAt = t; }
184
- }
185
- clearTimeout(timer); timer = setTimeout(onTimeout, timeoutMs);
186
- });
187
- process.stdin.on('end', () => process.exit(0)); // parent closed the pipe (exited) -> no orphan
188
- process.stdin.on('error', () => process.exit(0)); // pipe broke -> parent gone
189
- process.stdin.resume();
137
+ const CHILD_SOURCE = `
138
+ const fs = require('fs');
139
+ const parentPid = Number(process.argv[1]);
140
+ const timeoutMs = Number(process.argv[2]);
141
+ const capMs = Number(process.argv[3]);
142
+ const progressPaths = process.argv.slice(4);
143
+ const secs = Math.round(timeoutMs / 1000);
144
+ function kill(extra) {
145
+ // Timestamped so daemon.log kills can be correlated with anything (#1431) —
146
+ // computed here at kill time; this child process is never the wedged one.
147
+ try { fs.writeSync(2, Buffer.from('[' + new Date().toISOString() + '] [LatticeSensor] Main thread unresponsive for ~' + secs + 's' + (extra || '') + ' — killing the wedged process so a fresh one can start (#850). Disable with LATTICE_SENSOR_NO_WATCHDOG=1.\\n')); } catch (e) {}
148
+ try { process.kill(parentPid, 'SIGKILL'); } catch (e) {}
149
+ process.exit(0);
150
+ }
151
+ // Fingerprint of the watched files (size + mtime). A change between checks is
152
+ // forward disk progress — a slow synchronous SQLite statement, not a wedge.
153
+ function snap() {
154
+ let s = '';
155
+ for (const p of progressPaths) {
156
+ try { const st = fs.statSync(p); s += st.size + ':' + st.mtimeMs + ';'; } catch (e) { s += 'x;'; }
157
+ }
158
+ return s;
159
+ }
160
+ let lastSnap = progressPaths.length ? snap() : '';
161
+ let lastSnapAt = Date.now();
162
+ let silentSince = null; // start of the current continuous-silence episode
163
+ function onTimeout() {
164
+ if (!progressPaths.length) return kill('');
165
+ const now = Date.now();
166
+ if (silentSince === null) silentSince = now - timeoutMs; // silence began ~one timeout ago
167
+ const cur = snap();
168
+ if (cur !== lastSnap && now - silentSince < capMs) {
169
+ // The event loop is blocked but the DB files are advancing: a legitimate
170
+ // long store on slow storage. Defer, re-baseline, keep watching.
171
+ lastSnap = cur;
172
+ timer = setTimeout(onTimeout, timeoutMs);
173
+ return;
174
+ }
175
+ kill(cur !== lastSnap ? ' despite ongoing disk activity (hard cap ' + Math.round(capMs / 1000) + 's reached)' : '');
176
+ }
177
+ let timer = setTimeout(onTimeout, timeoutMs);
178
+ process.stdin.on('data', () => {
179
+ silentSince = null;
180
+ // Keep the baseline fresh while healthy (throttled — a stat per second).
181
+ if (progressPaths.length) {
182
+ const t = Date.now();
183
+ if (t - lastSnapAt >= 1000) { lastSnap = snap(); lastSnapAt = t; }
184
+ }
185
+ clearTimeout(timer); timer = setTimeout(onTimeout, timeoutMs);
186
+ });
187
+ process.stdin.on('end', () => process.exit(0)); // parent closed the pipe (exited) -> no orphan
188
+ process.stdin.on('error', () => process.exit(0)); // pipe broke -> parent gone
189
+ process.stdin.resume();
190
190
  `;
191
191
  /**
192
192
  * Install the main-thread liveness watchdog for a long-lived process. Returns a