@hardkas/query 0.2.2-alpha → 0.3.0-alpha

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 (3) hide show
  1. package/dist/index.d.ts +172 -35
  2. package/dist/index.js +576 -407
  3. package/package.json +4 -3
package/dist/index.js CHANGED
@@ -1,6 +1,164 @@
1
- // src/adapters/artifact-adapter.ts
1
+ // src/backend-fs.ts
2
2
  import fs from "fs/promises";
3
3
  import path from "path";
4
+ var FilesystemQueryBackend = class {
5
+ rootDir;
6
+ constructor(rootDir) {
7
+ this.rootDir = rootDir;
8
+ }
9
+ isReady() {
10
+ return true;
11
+ }
12
+ kind() {
13
+ return "filesystem";
14
+ }
15
+ async findArtifacts(filters) {
16
+ const files = await this.scanFiles(this.rootDir);
17
+ const docs = [];
18
+ for (const f of files) {
19
+ const raw = await this.readJson(f);
20
+ if (!raw || !raw.schema) continue;
21
+ if (filters?.schema && raw.schema !== filters.schema) continue;
22
+ if (filters?.mode && raw.mode !== filters.mode) continue;
23
+ if (filters?.networkId && raw.networkId !== filters.networkId) continue;
24
+ docs.push({
25
+ contentHash: raw.contentHash || "",
26
+ schema: raw.schema,
27
+ version: raw.version || "unknown",
28
+ kind: raw.kind || raw.schema,
29
+ mode: raw.mode,
30
+ networkId: raw.networkId,
31
+ createdAt: raw.createdAt || null,
32
+ txId: raw.txId || null,
33
+ artifactId: raw.artifactId || raw.contentHash || "",
34
+ path: f,
35
+ payload: raw
36
+ });
37
+ }
38
+ return docs;
39
+ }
40
+ async getArtifact(idOrHash) {
41
+ const artifacts = await this.findArtifacts();
42
+ return artifacts.find(
43
+ (a) => a.contentHash === idOrHash || a.payload.lineage?.artifactId === idOrHash || a.payload.artifactId === idOrHash || a.payload.txId === idOrHash
44
+ ) || null;
45
+ }
46
+ async getEvents(filters) {
47
+ const eventsPath = path.join(this.rootDir, ".hardkas", "events.jsonl");
48
+ const docs = [];
49
+ try {
50
+ const content = await fs.readFile(eventsPath, "utf-8");
51
+ const lines = content.split("\n").filter((l) => l.trim() !== "");
52
+ for (const line of lines) {
53
+ try {
54
+ const parsed = JSON.parse(line);
55
+ if (filters?.kind && parsed.kind !== filters.kind) continue;
56
+ if (filters?.txId && parsed.txId !== filters.txId) continue;
57
+ docs.push({
58
+ eventId: parsed.eventId,
59
+ kind: parsed.kind,
60
+ domain: parsed.domain,
61
+ timestamp: parsed.timestamp || null,
62
+ workflowId: parsed.workflowId,
63
+ correlationId: parsed.correlationId,
64
+ causationId: parsed.causationId || null,
65
+ txId: parsed.txId || null,
66
+ artifactId: parsed.artifactId || null,
67
+ networkId: parsed.networkId,
68
+ payload: parsed.payload
69
+ });
70
+ } catch {
71
+ }
72
+ }
73
+ } catch {
74
+ }
75
+ return docs;
76
+ }
77
+ async getLineageEdges(filters) {
78
+ const artifacts = await this.findArtifacts();
79
+ const edges = [];
80
+ for (const a of artifacts) {
81
+ if (a.payload.lineage?.parentArtifactId) {
82
+ edges.push({
83
+ parentArtifactId: a.payload.lineage.parentArtifactId,
84
+ childArtifactId: a.artifactId,
85
+ lineageId: a.payload.lineage.lineageId || "unknown",
86
+ rule: "parent",
87
+ createdAt: a.createdAt
88
+ });
89
+ }
90
+ }
91
+ return edges.filter((e) => {
92
+ if (filters?.parentHash && e.parentArtifactId !== filters.parentHash) return false;
93
+ if (filters?.childHash && e.childArtifactId !== filters.childHash) return false;
94
+ return true;
95
+ });
96
+ }
97
+ async getStoreStatus() {
98
+ return "fresh";
99
+ }
100
+ async doctor() {
101
+ return { ok: true, backend: "filesystem" };
102
+ }
103
+ async rebuild(options) {
104
+ return {
105
+ schema: "hardkas.queryRebuild.v1",
106
+ ok: true,
107
+ artifacts: { scanned: 0, indexed: 0, duplicates: 0, corrupted: 0 },
108
+ events: { scanned: 0, indexed: 0, duplicates: 0, corrupted: 0 },
109
+ warnings: ["Filesystem backend does not support indexing"],
110
+ errors: []
111
+ };
112
+ }
113
+ async findReceipts(filters) {
114
+ return this.findArtifacts({ schema: "hardkas.txReceipt", ...filters });
115
+ }
116
+ async findTraces(filters) {
117
+ const artifacts = await this.findArtifacts({ schema: "hardkas.txTrace" });
118
+ if (filters?.txId) {
119
+ return artifacts.filter((a) => a.payload.txId === filters.txId);
120
+ }
121
+ return artifacts;
122
+ }
123
+ async sync(options) {
124
+ return this.rebuild(options);
125
+ }
126
+ async migrate() {
127
+ return { applied: 0 };
128
+ }
129
+ async executeRawSql(_sql) {
130
+ throw new Error("Raw SQL execution not supported by Filesystem backend. Use SQLite backend.");
131
+ }
132
+ async scanFiles(dir) {
133
+ const results = [];
134
+ try {
135
+ const entries = await fs.readdir(dir, { withFileTypes: true });
136
+ for (const entry of entries) {
137
+ const fullPath = path.join(dir, entry.name);
138
+ if (entry.isDirectory()) {
139
+ if (entry.name === "node_modules" || entry.name === ".git") continue;
140
+ results.push(...await this.scanFiles(fullPath));
141
+ } else if (entry.name.endsWith(".json") && !entry.name.endsWith(".enc.json") && entry.name !== "events.jsonl") {
142
+ results.push(fullPath);
143
+ }
144
+ }
145
+ } catch {
146
+ }
147
+ return results;
148
+ }
149
+ async readJson(file) {
150
+ try {
151
+ const content = await fs.readFile(file, "utf-8");
152
+ return JSON.parse(content);
153
+ } catch {
154
+ return null;
155
+ }
156
+ }
157
+ };
158
+
159
+ // src/adapters/artifact-adapter.ts
160
+ import fs2 from "fs/promises";
161
+ import path2 from "path";
4
162
  import {
5
163
  calculateContentHash,
6
164
  verifyArtifactIntegrity,
@@ -123,144 +281,145 @@ var VALID_TRANSITIONS = {
123
281
  "hardkas.txPlan": ["hardkas.signedTx"],
124
282
  "hardkas.signedTx": ["hardkas.txReceipt"]
125
283
  };
284
+ function createExplainBlock(options) {
285
+ return {
286
+ backend: options.backend,
287
+ executionPlan: options.executionPlan,
288
+ indexesUsed: options.indexesUsed || [],
289
+ filtersApplied: options.filtersApplied || [],
290
+ rowsRead: options.rowsRead,
291
+ scannedFiles: options.scannedFiles,
292
+ freshness: options.freshness,
293
+ warnings: options.warnings || []
294
+ };
295
+ }
126
296
  function explainIntegrity(artifact, integrity) {
127
- const steps = [];
297
+ const causalChain = [];
298
+ const evidence = [];
128
299
  let order = 1;
129
- steps.push({
300
+ if (artifact.contentHash) {
301
+ evidence.push({ type: "contentHash", value: artifact.contentHash });
302
+ }
303
+ evidence.push({ type: "filePath", value: artifact.filePath });
304
+ causalChain.push({
130
305
  order: order++,
131
306
  assertion: integrity.schemaValid ? `Schema "${artifact.schema}" is a recognized HardKAS artifact schema` : `Schema "${artifact.schema}" is not recognized`,
132
307
  evidence: `artifact.schema = "${artifact.schema}"`,
133
308
  rule: "ARTIFACT_SCHEMAS constant (artifacts/constants.ts)"
134
309
  });
135
310
  if (artifact.contentHash) {
136
- steps.push({
311
+ causalChain.push({
137
312
  order: order++,
138
- assertion: integrity.hashMatch ? "Content hash matches recomputed hash" : "Content hash does NOT match recomputed hash \u2014 possible corruption",
139
- evidence: `artifact.contentHash = "${artifact.contentHash}"`,
313
+ assertion: integrity.hashMatch ? "Content hash matches recomputed hash" : "Content hash does NOT match recomputed hash",
314
+ evidence: `hash(payload) === "${artifact.contentHash}"`,
140
315
  rule: "canonicalStringify + SHA-256 (artifacts/canonical.ts)"
141
316
  });
142
- } else {
143
- steps.push({
144
- order: order++,
145
- assertion: "Artifact has no contentHash field (pre-verification artifact)",
146
- evidence: "artifact.contentHash is undefined",
147
- rule: "contentHash is optional but recommended"
148
- });
149
317
  }
150
318
  if (integrity.errors.length > 0) {
151
319
  for (const err of integrity.errors) {
152
- steps.push({
320
+ causalChain.push({
153
321
  order: order++,
154
- assertion: `Integrity issue: ${err}`,
155
- evidence: err,
156
- rule: "verifyArtifactIntegrity (artifacts/verify.ts)"
322
+ assertion: `Semantic violation detected: ${err}`,
323
+ evidence: "Verification engine failure",
324
+ rule: "verifyArtifactSemantics (artifacts/verify.ts)"
157
325
  });
158
326
  }
159
327
  }
160
328
  return {
161
- question: `Why is artifact "${artifact.schema}" at ${artifact.filePath} ${integrity.ok ? "valid" : "invalid"}?`,
162
- conclusion: integrity.ok ? "All integrity checks passed. Schema is valid and content hash matches." : `Integrity check failed: ${integrity.errors.join("; ")}`,
163
- steps,
164
- model: "artifact-verification",
165
- confidence: "definitive",
166
- references: artifact.contentHash ? [artifact.contentHash] : []
329
+ question: `Why is artifact "${artifact.schema}" ${integrity.ok ? "valid" : "invalid"}?`,
330
+ answer: integrity.ok ? "All deterministic checks (schema, hash, semantics) passed successfully." : `Verification failed: ${integrity.errors.join("; ")}`,
331
+ evidence,
332
+ causalChain,
333
+ model: "integrity-verifier",
334
+ confidence: "definitive"
167
335
  };
168
336
  }
169
337
  function explainTransition(transition) {
170
- const steps = [];
338
+ const causalChain = [];
339
+ const evidence = [
340
+ { type: "contentHash", value: transition.from.contentHash },
341
+ { type: "contentHash", value: transition.to.contentHash }
342
+ ];
171
343
  let order = 1;
172
344
  const allowed = VALID_TRANSITIONS[transition.from.schema] ?? [];
173
- steps.push({
174
- order: order++,
175
- assertion: allowed.length > 0 ? `Schema "${transition.from.schema}" allows transitions to: ${allowed.join(", ")}` : `Schema "${transition.from.schema}" has no defined transitions`,
176
- evidence: `VALID_TRANSITIONS["${transition.from.schema}"] = [${allowed.map((s) => `"${s}"`).join(", ")}]`,
177
- rule: "Lineage transition table (artifacts/lineage.ts)"
178
- });
179
- steps.push({
345
+ causalChain.push({
180
346
  order: order++,
181
- assertion: transition.valid ? `Transition "${transition.from.schema}" \u2192 "${transition.to.schema}" is VALID` : `Transition "${transition.from.schema}" \u2192 "${transition.to.schema}" is NOT in the allowed set`,
182
- evidence: `target schema = "${transition.to.schema}", allowed = [${allowed.map((s) => `"${s}"`).join(", ")}]`,
183
- rule: "Lineage transition table (artifacts/lineage.ts)"
347
+ assertion: transition.valid ? `Transition "${transition.from.schema}" \u2192 "${transition.to.schema}" is allowed` : `Transition "${transition.from.schema}" \u2192 "${transition.to.schema}" is NOT allowed`,
348
+ evidence: `allowed_from_${transition.from.schema} = [${allowed.join(", ")}]`,
349
+ rule: "Lineage transition table"
184
350
  });
185
- const networkMatch = transition.from.networkId === transition.to.networkId;
186
- steps.push({
351
+ const contextMatch = transition.from.networkId === transition.to.networkId && transition.from.mode === transition.to.mode;
352
+ causalChain.push({
187
353
  order: order++,
188
- assertion: networkMatch ? `Network is consistent: both "${transition.from.networkId}"` : `NETWORK CONTAMINATION: "${transition.from.networkId}" \u2192 "${transition.to.networkId}"`,
189
- evidence: `parent.networkId = "${transition.from.networkId}", child.networkId = "${transition.to.networkId}"`,
190
- rule: "NETWORK_CONTAMINATION check (artifacts/lineage.ts)"
354
+ assertion: contextMatch ? "Execution context (network, mode) is consistent" : "EXECUTION CONTEXT MISMATCH detected",
355
+ evidence: `from: ${transition.from.networkId}/${transition.from.mode}, to: ${transition.to.networkId}/${transition.to.mode}`,
356
+ rule: "Context isolation policy"
191
357
  });
192
- const modeMatch = transition.from.mode === transition.to.mode;
193
- steps.push({
194
- order: order++,
195
- assertion: modeMatch ? `Mode is consistent: both "${transition.from.mode}"` : `MODE CONTAMINATION: "${transition.from.mode}" \u2192 "${transition.to.mode}"`,
196
- evidence: `parent.mode = "${transition.from.mode}", child.mode = "${transition.to.mode}"`,
197
- rule: "MODE_CONTAMINATION check (artifacts/lineage.ts)"
198
- });
199
- const lineageMatch = transition.from.lineageId === transition.to.lineageId;
200
- steps.push({
201
- order: order++,
202
- assertion: lineageMatch ? "Lineage ID is continuous" : "LINEAGE ID MISMATCH \u2014 artifacts belong to different lineage chains",
203
- evidence: `parent.lineageId = "${transition.from.lineageId}", child.lineageId = "${transition.to.lineageId}"`,
204
- rule: "LINEAGE_ID_MISMATCH check (artifacts/lineage.ts)"
205
- });
206
- const allValid = transition.valid && networkMatch && modeMatch && lineageMatch;
207
358
  return {
208
- question: `Why is the transition "${transition.from.schema}" \u2192 "${transition.to.schema}" ${allValid ? "valid" : "invalid"}?`,
209
- conclusion: allValid ? `Valid transition. Rule: ${transition.from.schema} \u2192 ${transition.to.schema}. Network, mode, and lineage ID are all consistent.` : `Invalid transition. ${!transition.valid ? "Schema transition not allowed. " : ""}${!networkMatch ? "Network mismatch. " : ""}${!modeMatch ? "Mode mismatch. " : ""}${!lineageMatch ? "Lineage ID mismatch." : ""}`,
210
- steps,
211
- model: "lineage-rules",
212
- confidence: "definitive",
213
- references: [transition.from.contentHash, transition.to.contentHash]
359
+ question: `Why transition ${transition.from.schema} \u2192 ${transition.to.schema}?`,
360
+ answer: transition.valid && contextMatch ? "Causal chain is consistent with HardKAS state transition rules." : "Workflow violation: invalid schema transition or context contamination.",
361
+ evidence,
362
+ causalChain,
363
+ model: "causal-lineage",
364
+ confidence: "definitive"
214
365
  };
215
366
  }
216
367
  function explainOrphan(node, missingParentId) {
217
368
  return {
218
- question: `Why is artifact "${node.schema}" (${node.contentHash.slice(0, 12)}...) an orphan?`,
219
- conclusion: `Parent artifact with ID "${missingParentId.slice(0, 12)}..." is not found in the artifact store. This artifact is disconnected from its workflow context.`,
220
- steps: [
369
+ question: `Why is artifact "${node.artifactId.slice(0, 8)}" an orphan?`,
370
+ answer: "The parent artifact referenced in the lineage metadata is missing from the indexed store.",
371
+ evidence: [
372
+ { type: "artifactId", value: node.artifactId },
373
+ { type: "artifactId", value: missingParentId }
374
+ ],
375
+ causalChain: [
221
376
  {
222
377
  order: 1,
223
- assertion: `Artifact declares parentArtifactId = "${missingParentId}"`,
224
- evidence: `artifact.lineage.parentArtifactId = "${missingParentId}"`,
225
- rule: "Lineage parent resolution"
378
+ assertion: "Artifact defines a parent dependency",
379
+ evidence: `parentArtifactId = "${missingParentId}"`,
380
+ rule: "Lineage metadata requirement"
226
381
  },
227
382
  {
228
383
  order: 2,
229
- assertion: "No artifact in the store has a matching lineage.artifactId",
230
- evidence: "Full store scan found 0 artifacts with this artifactId",
231
- rule: "Lineage graph construction (query/lineage-adapter.ts)"
232
- },
233
- {
234
- order: 3,
235
- assertion: "Artifact is classified as an orphan",
236
- evidence: "Missing parent means the lineage chain is broken",
237
- rule: "Orphan detection: parentArtifactId exists but not resolvable"
384
+ assertion: "Parent artifact lookup failed",
385
+ evidence: "Index scan for artifactId returned 0 results",
386
+ rule: "Store integrity policy"
238
387
  }
239
388
  ],
240
- model: "lineage-rules",
241
- confidence: "definitive",
242
- references: [node.contentHash, missingParentId]
389
+ model: "orphan-analysis",
390
+ confidence: "definitive"
243
391
  };
244
392
  }
245
- function formatExplainBrief(chain) {
246
- return `${chain.conclusion} [model: ${chain.model}, confidence: ${chain.confidence}]`;
393
+ function formatExplainBlock(block) {
394
+ const lines = [];
395
+ lines.push(` [Explain: Technical Diagnostics]`);
396
+ lines.push(` Backend: ${block.backend}`);
397
+ lines.push(` Freshness: ${block.freshness}`);
398
+ lines.push(` Rows Read: ${block.rowsRead}`);
399
+ lines.push(` Files Scan: ${block.scannedFiles}`);
400
+ if (block.executionPlan.length > 0) {
401
+ lines.push(` Plan: ${block.executionPlan.join(" \u2192 ")}`);
402
+ }
403
+ if (block.warnings.length > 0) {
404
+ lines.push(` Warnings:`);
405
+ for (const w of block.warnings) lines.push(` \u26A0 ${w}`);
406
+ }
407
+ return lines.join("\n");
247
408
  }
248
- function formatExplainFull(chain) {
409
+ function formatWhyBlock(block) {
249
410
  const lines = [];
250
- lines.push(`Q: ${chain.question}`);
411
+ lines.push(` [Why: Causal Analysis]`);
412
+ lines.push(` Q: ${block.question}`);
413
+ lines.push(` A: ${block.answer}`);
251
414
  lines.push("");
252
- for (const step of chain.steps) {
253
- lines.push(` ${step.order}. ${step.assertion}`);
254
- lines.push(` Evidence: ${step.evidence}`);
255
- if (step.rule) {
256
- lines.push(` Rule: ${step.rule}`);
257
- }
415
+ for (const step of block.causalChain) {
416
+ lines.push(` ${step.order}. ${step.assertion}`);
417
+ lines.push(` Evidence: ${step.evidence}`);
418
+ if (step.rule) lines.push(` Rule: ${step.rule}`);
258
419
  }
259
- lines.push("");
260
- lines.push(`Conclusion: ${chain.conclusion}`);
261
- lines.push(`Model: ${chain.model} | Confidence: ${chain.confidence}`);
262
- if (chain.references.length > 0) {
263
- lines.push(`References: ${chain.references.map((r) => r.slice(0, 16) + "...").join(", ")}`);
420
+ if (block.evidence.length > 0) {
421
+ lines.push("");
422
+ lines.push(` Evidence Refs: ${block.evidence.map((e) => `${e.type}:${e.value.slice(0, 12)}...`).join(", ")}`);
264
423
  }
265
424
  return lines.join("\n");
266
425
  }
@@ -270,8 +429,10 @@ var KNOWN_SCHEMAS = new Set(Object.values(ARTIFACT_SCHEMAS));
270
429
  var ArtifactQueryAdapter = class {
271
430
  domain = "artifacts";
272
431
  rootDir;
273
- constructor(rootDir) {
432
+ backend;
433
+ constructor(rootDir, backend) {
274
434
  this.rootDir = rootDir;
435
+ this.backend = backend;
275
436
  }
276
437
  supportedOps() {
277
438
  return ["list", "inspect", "diff", "verify"];
@@ -298,12 +459,25 @@ var ArtifactQueryAdapter = class {
298
459
  // -------------------------------------------------------------------------
299
460
  async executeList(request) {
300
461
  const start = Date.now();
301
- const files = await this.scanArtifactFiles();
462
+ const docs = await this.backend.findArtifacts();
302
463
  const items = [];
303
- for (const filePath of files) {
304
- const raw = await this.readJsonSafe(filePath);
305
- if (!raw || !raw.schema) continue;
306
- const item = toArtifactQueryItem(raw, filePath);
464
+ for (const doc of docs) {
465
+ const item = {
466
+ filePath: doc.path,
467
+ schema: doc.schema,
468
+ version: doc.version,
469
+ networkId: doc.networkId,
470
+ mode: doc.mode,
471
+ createdAt: doc.createdAt,
472
+ contentHash: doc.contentHash,
473
+ payload: doc.payload,
474
+ // Optional mapping for common fields
475
+ status: doc.payload.status,
476
+ from: doc.payload.from,
477
+ to: doc.payload.to,
478
+ amountSompi: doc.payload.amountSompi,
479
+ lineage: doc.payload.lineage
480
+ };
307
481
  if (evaluateFilters(item, request.filters)) {
308
482
  items.push(item);
309
483
  }
@@ -311,9 +485,9 @@ var ArtifactQueryAdapter = class {
311
485
  const sorted = this.sortItems(items, request.sort);
312
486
  const total = sorted.length;
313
487
  const paged = sorted.slice(request.offset, request.offset + request.limit);
314
- let explain;
488
+ let why;
315
489
  if (request.explain) {
316
- explain = paged.map((item) => explainIntegrity(item, {
490
+ why = paged.map((item) => explainIntegrity(item, {
317
491
  ok: true,
318
492
  hashMatch: true,
319
493
  schemaValid: KNOWN_SCHEMAS.has(item.schema),
@@ -328,11 +502,11 @@ var ArtifactQueryAdapter = class {
328
502
  truncated: total > request.offset + request.limit,
329
503
  deterministic: true,
330
504
  queryHash: computeQueryHash(paged),
331
- explain,
505
+ why,
332
506
  annotations: {
333
507
  executedAt: (/* @__PURE__ */ new Date()).toISOString(),
334
508
  executionMs: Date.now() - start,
335
- filesScanned: files.length
509
+ filesScanned: docs.length
336
510
  }
337
511
  };
338
512
  }
@@ -383,9 +557,9 @@ var ArtifactQueryAdapter = class {
383
557
  staleness,
384
558
  lineageStatus
385
559
  };
386
- let explain;
560
+ let why;
387
561
  if (request.explain) {
388
- explain = [explainIntegrity(item, inspectResult.integrity)];
562
+ why = [explainIntegrity(item, inspectResult.integrity)];
389
563
  }
390
564
  return {
391
565
  domain: "artifacts",
@@ -395,7 +569,7 @@ var ArtifactQueryAdapter = class {
395
569
  truncated: false,
396
570
  deterministic: true,
397
571
  queryHash: computeQueryHash([inspectResult]),
398
- explain,
572
+ why,
399
573
  annotations: {
400
574
  executedAt: (/* @__PURE__ */ new Date()).toISOString(),
401
575
  executionMs: Date.now() - start,
@@ -472,12 +646,12 @@ var ArtifactQueryAdapter = class {
472
646
  async walkDir(dir, out) {
473
647
  let entries;
474
648
  try {
475
- entries = await fs.readdir(dir, { withFileTypes: true });
649
+ entries = await fs2.readdir(dir, { withFileTypes: true });
476
650
  } catch {
477
651
  return;
478
652
  }
479
653
  for (const entry of entries) {
480
- const full = path.join(dir, entry.name);
654
+ const full = path2.join(dir, entry.name);
481
655
  if (entry.isDirectory()) {
482
656
  if (entry.name === "node_modules" || entry.name === ".git" || entry.name === "keystores") continue;
483
657
  await this.walkDir(full, out);
@@ -488,7 +662,7 @@ var ArtifactQueryAdapter = class {
488
662
  }
489
663
  async readJsonSafe(filePath) {
490
664
  try {
491
- const content = await fs.readFile(filePath, "utf-8");
665
+ const content = await fs2.readFile(filePath, "utf-8");
492
666
  return JSON.parse(content);
493
667
  } catch {
494
668
  return null;
@@ -496,7 +670,7 @@ var ArtifactQueryAdapter = class {
496
670
  }
497
671
  async resolveTarget(target) {
498
672
  if (target.includes("/") || target.includes("\\") || target.endsWith(".json")) {
499
- return path.resolve(target);
673
+ return path2.resolve(target);
500
674
  }
501
675
  const files = await this.scanArtifactFiles();
502
676
  for (const f of files) {
@@ -517,7 +691,7 @@ var ArtifactQueryAdapter = class {
517
691
  });
518
692
  } else {
519
693
  sorted.sort((a, b) => {
520
- const cmp = b.createdAt.localeCompare(a.createdAt);
694
+ const cmp = (b.createdAt ?? "").localeCompare(a.createdAt ?? "");
521
695
  return cmp !== 0 ? cmp : a.schema.localeCompare(b.schema);
522
696
  });
523
697
  }
@@ -533,6 +707,7 @@ function toArtifactQueryItem(raw, filePath) {
533
707
  mode: raw.mode || "unknown",
534
708
  createdAt: raw.createdAt || "",
535
709
  contentHash: raw.contentHash,
710
+ payload: raw,
536
711
  from: raw.from,
537
712
  to: raw.to,
538
713
  amountSompi: raw.amountSompi,
@@ -554,8 +729,8 @@ function classifyStaleness(hours) {
554
729
  }
555
730
 
556
731
  // src/adapters/lineage-adapter.ts
557
- import fs2 from "fs/promises";
558
- import path2 from "path";
732
+ import fs3 from "fs/promises";
733
+ import path3 from "path";
559
734
  var VALID_TRANSITIONS2 = {
560
735
  "hardkas.snapshot": ["hardkas.txPlan"],
561
736
  "hardkas.txPlan": ["hardkas.signedTx"],
@@ -564,8 +739,10 @@ var VALID_TRANSITIONS2 = {
564
739
  var LineageQueryAdapter = class {
565
740
  domain = "lineage";
566
741
  rootDir;
567
- constructor(rootDir) {
742
+ backend;
743
+ constructor(rootDir, backend) {
568
744
  this.rootDir = rootDir;
745
+ this.backend = backend;
569
746
  }
570
747
  supportedOps() {
571
748
  return ["chain", "transitions", "orphans"];
@@ -611,9 +788,9 @@ var LineageQueryAdapter = class {
611
788
  transitions,
612
789
  complete
613
790
  };
614
- let explain;
791
+ let why;
615
792
  if (request.explain) {
616
- explain = transitions.map((t) => explainTransition(t));
793
+ why = transitions.map((t) => explainTransition(t));
617
794
  }
618
795
  return {
619
796
  domain: "lineage",
@@ -623,7 +800,7 @@ var LineageQueryAdapter = class {
623
800
  truncated: false,
624
801
  deterministic: true,
625
802
  queryHash: computeQueryHash([result]),
626
- explain,
803
+ why,
627
804
  annotations: {
628
805
  executedAt: (/* @__PURE__ */ new Date()).toISOString(),
629
806
  executionMs: Date.now() - start,
@@ -660,9 +837,9 @@ var LineageQueryAdapter = class {
660
837
  return a.from.contentHash.localeCompare(b.from.contentHash);
661
838
  });
662
839
  const paged = transitions.slice(request.offset, request.offset + request.limit);
663
- let explain;
840
+ let why;
664
841
  if (request.explain) {
665
- explain = paged.map((t) => explainTransition(t));
842
+ why = paged.map((t) => explainTransition(t));
666
843
  }
667
844
  return {
668
845
  domain: "lineage",
@@ -672,7 +849,7 @@ var LineageQueryAdapter = class {
672
849
  truncated: transitions.length > request.offset + request.limit,
673
850
  deterministic: true,
674
851
  queryHash: computeQueryHash(paged),
675
- explain,
852
+ why,
676
853
  annotations: {
677
854
  executedAt: (/* @__PURE__ */ new Date()).toISOString(),
678
855
  executionMs: Date.now() - start,
@@ -700,9 +877,9 @@ var LineageQueryAdapter = class {
700
877
  }
701
878
  orphans.sort((a, b) => a.node.contentHash.localeCompare(b.node.contentHash));
702
879
  const paged = orphans.slice(request.offset, request.offset + request.limit);
703
- let explain;
880
+ let why;
704
881
  if (request.explain) {
705
- explain = paged.map((o) => explainOrphan(o.node, o.missingParentId));
882
+ why = paged.map((o) => explainOrphan(o.node, o.missingParentId));
706
883
  }
707
884
  return {
708
885
  domain: "lineage",
@@ -712,7 +889,7 @@ var LineageQueryAdapter = class {
712
889
  truncated: orphans.length > request.offset + request.limit,
713
890
  deterministic: true,
714
891
  queryHash: computeQueryHash(paged),
715
- explain,
892
+ why,
716
893
  annotations: {
717
894
  executedAt: (/* @__PURE__ */ new Date()).toISOString(),
718
895
  executionMs: Date.now() - start,
@@ -728,22 +905,22 @@ var LineageQueryAdapter = class {
728
905
  const byArtifactId = /* @__PURE__ */ new Map();
729
906
  const byContentHash = /* @__PURE__ */ new Map();
730
907
  const children = /* @__PURE__ */ new Map();
731
- const files = await this.scanJsonFiles();
732
- for (const filePath of files) {
733
- const raw = await this.readJsonSafe(filePath);
908
+ const docs = await this.backend.findArtifacts();
909
+ for (const doc of docs) {
910
+ const raw = doc.payload;
734
911
  if (!raw?.schema || !raw.lineage) continue;
735
912
  const node = {
736
- contentHash: raw.contentHash || "",
737
- schema: raw.schema,
913
+ contentHash: doc.contentHash,
914
+ schema: doc.schema,
738
915
  artifactId: raw.lineage.artifactId || "",
739
916
  parentArtifactId: raw.lineage.parentArtifactId,
740
917
  rootArtifactId: raw.lineage.rootArtifactId || "",
741
918
  lineageId: raw.lineage.lineageId || "",
742
919
  sequence: raw.lineage.sequence,
743
- filePath,
744
- networkId: raw.networkId || "unknown",
745
- mode: raw.mode || "unknown",
746
- createdAt: raw.createdAt || ""
920
+ filePath: doc.path,
921
+ networkId: doc.networkId,
922
+ mode: doc.mode,
923
+ createdAt: doc.createdAt || ""
747
924
  };
748
925
  nodes.push(node);
749
926
  if (node.artifactId) byArtifactId.set(node.artifactId, node);
@@ -754,7 +931,7 @@ var LineageQueryAdapter = class {
754
931
  children.set(node.parentArtifactId, existing);
755
932
  }
756
933
  }
757
- return { nodes, byArtifactId, byContentHash, children, totalFiles: files.length };
934
+ return { nodes, byArtifactId, byContentHash, children, totalFiles: docs.length };
758
935
  }
759
936
  // -------------------------------------------------------------------------
760
937
  // Graph Traversal
@@ -812,12 +989,12 @@ var LineageQueryAdapter = class {
812
989
  async walkDir(dir, out) {
813
990
  let entries;
814
991
  try {
815
- entries = await fs2.readdir(dir, { withFileTypes: true });
992
+ entries = await fs3.readdir(dir, { withFileTypes: true });
816
993
  } catch {
817
994
  return;
818
995
  }
819
996
  for (const entry of entries) {
820
- const full = path2.join(dir, entry.name);
997
+ const full = path3.join(dir, entry.name);
821
998
  if (entry.isDirectory()) {
822
999
  if (entry.name === "node_modules" || entry.name === ".git" || entry.name === "keystores") continue;
823
1000
  await this.walkDir(full, out);
@@ -828,7 +1005,7 @@ var LineageQueryAdapter = class {
828
1005
  }
829
1006
  async readJsonSafe(filePath) {
830
1007
  try {
831
- const content = await fs2.readFile(filePath, "utf-8");
1008
+ const content = await fs3.readFile(filePath, "utf-8");
832
1009
  return JSON.parse(content);
833
1010
  } catch {
834
1011
  return null;
@@ -837,14 +1014,15 @@ var LineageQueryAdapter = class {
837
1014
  };
838
1015
 
839
1016
  // src/adapters/replay-adapter.ts
840
- import fs3 from "fs/promises";
841
- import path3 from "path";
1017
+ import fs4 from "fs/promises";
842
1018
  import { calculateContentHash as calculateContentHash2 } from "@hardkas/artifacts";
843
1019
  var ReplayQueryAdapter = class {
844
1020
  domain = "replay";
845
1021
  rootDir;
846
- constructor(rootDir) {
1022
+ backend;
1023
+ constructor(rootDir, backend) {
847
1024
  this.rootDir = rootDir;
1025
+ this.backend = backend;
848
1026
  }
849
1027
  supportedOps() {
850
1028
  return ["list", "summary", "divergences", "invariants"];
@@ -871,13 +1049,15 @@ var ReplayQueryAdapter = class {
871
1049
  // -------------------------------------------------------------------------
872
1050
  async executeList(request) {
873
1051
  const start = Date.now();
874
- const receipts = await this.loadAllReceipts();
875
- const traces = await this.loadAllTraces();
1052
+ const receipts = await this.backend.findReceipts({
1053
+ status: request.filters.find((f) => f.field === "status")?.value
1054
+ });
1055
+ const traces = await this.backend.findTraces();
876
1056
  const traceMap = new Map(traces.map((t) => [t.txId, t]));
877
1057
  const items = [];
878
1058
  for (const r of receipts) {
879
1059
  const trace = traceMap.get(r.txId);
880
- const item = toSummary(r, trace);
1060
+ const item = toSummary(r.payload, trace?.payload);
881
1061
  if (evaluateFilters(item, request.filters)) {
882
1062
  items.push(item);
883
1063
  }
@@ -910,10 +1090,11 @@ var ReplayQueryAdapter = class {
910
1090
  const start = Date.now();
911
1091
  const txId = request.params["txId"];
912
1092
  if (!txId) throw new Error("summary requires params.txId");
913
- const receipt = await this.loadReceipt(txId);
914
- if (!receipt) throw new Error(`Receipt not found for txId: ${txId}`);
915
- const trace = await this.loadTrace(txId);
916
- const item = toSummary(receipt, trace);
1093
+ const receipt = await this.backend.getArtifact(txId);
1094
+ if (!receipt || receipt.schema !== "hardkas.txReceipt") throw new Error(`Receipt not found for txId: ${txId}`);
1095
+ const traces = await this.backend.findTraces({ txId });
1096
+ const trace = traces[0];
1097
+ const item = toSummary(receipt.payload, trace?.payload);
917
1098
  return {
918
1099
  domain: "replay",
919
1100
  op: "summary",
@@ -934,9 +1115,10 @@ var ReplayQueryAdapter = class {
934
1115
  // -------------------------------------------------------------------------
935
1116
  async executeDivergences(request) {
936
1117
  const start = Date.now();
937
- const receipts = await this.loadAllReceipts();
1118
+ const receipts = await this.backend.findReceipts();
938
1119
  const divergences = [];
939
- for (const receipt of receipts) {
1120
+ for (const doc of receipts) {
1121
+ const receipt = doc.payload;
940
1122
  if (receipt.contentHash) {
941
1123
  const recomputed = computeContentHashSafe(receipt);
942
1124
  if (recomputed !== receipt.contentHash) {
@@ -994,9 +1176,9 @@ var ReplayQueryAdapter = class {
994
1176
  }
995
1177
  divergences.sort((a, b) => a.txId.localeCompare(b.txId));
996
1178
  const paged = divergences.slice(request.offset, request.offset + request.limit);
997
- let explain;
1179
+ let why;
998
1180
  if (request.explain) {
999
- explain = paged.map((d) => explainDivergence(d));
1181
+ why = paged.map((d) => explainDivergence(d));
1000
1182
  }
1001
1183
  return {
1002
1184
  domain: "replay",
@@ -1006,7 +1188,7 @@ var ReplayQueryAdapter = class {
1006
1188
  truncated: divergences.length > request.offset + request.limit,
1007
1189
  deterministic: true,
1008
1190
  queryHash: computeQueryHash(paged),
1009
- explain,
1191
+ why,
1010
1192
  annotations: {
1011
1193
  executedAt: (/* @__PURE__ */ new Date()).toISOString(),
1012
1194
  executionMs: Date.now() - start,
@@ -1021,8 +1203,9 @@ var ReplayQueryAdapter = class {
1021
1203
  const start = Date.now();
1022
1204
  const txId = request.params["txId"];
1023
1205
  if (!txId) throw new Error("invariants requires params.txId");
1024
- const receipt = await this.loadReceipt(txId);
1025
- if (!receipt) throw new Error(`Receipt not found for txId: ${txId}`);
1206
+ const doc = await this.backend.getArtifact(txId);
1207
+ if (!doc || doc.schema !== "hardkas.txReceipt") throw new Error(`Receipt not found for txId: ${txId}`);
1208
+ const receipt = doc.payload;
1026
1209
  const issues = [];
1027
1210
  const planIntegrity = receipt.contentHash ? computeContentHashSafe(receipt) === receipt.contentHash : true;
1028
1211
  if (!planIntegrity) {
@@ -1065,9 +1248,9 @@ var ReplayQueryAdapter = class {
1065
1248
  utxoConservation,
1066
1249
  issues
1067
1250
  };
1068
- let explain;
1251
+ let why;
1069
1252
  if (request.explain) {
1070
- explain = [explainInvariants(result)];
1253
+ why = [explainInvariants(result)];
1071
1254
  }
1072
1255
  return {
1073
1256
  domain: "replay",
@@ -1077,7 +1260,7 @@ var ReplayQueryAdapter = class {
1077
1260
  truncated: false,
1078
1261
  deterministic: true,
1079
1262
  queryHash: computeQueryHash([result]),
1080
- explain,
1263
+ why,
1081
1264
  annotations: {
1082
1265
  executedAt: (/* @__PURE__ */ new Date()).toISOString(),
1083
1266
  executionMs: Date.now() - start,
@@ -1085,43 +1268,9 @@ var ReplayQueryAdapter = class {
1085
1268
  }
1086
1269
  };
1087
1270
  }
1088
- // -------------------------------------------------------------------------
1089
- // Filesystem
1090
- // -------------------------------------------------------------------------
1091
- async loadAllReceipts() {
1092
- const dir = path3.join(this.rootDir, ".hardkas", "receipts");
1093
- return this.loadJsonDir(dir);
1094
- }
1095
- async loadAllTraces() {
1096
- const dir = path3.join(this.rootDir, ".hardkas", "traces");
1097
- return this.loadJsonDir(dir);
1098
- }
1099
- async loadReceipt(txId) {
1100
- const filePath = path3.join(this.rootDir, ".hardkas", "receipts", `${txId}.json`);
1101
- return this.readJsonSafe(filePath);
1102
- }
1103
- async loadTrace(txId) {
1104
- const filePath = path3.join(this.rootDir, ".hardkas", "traces", `${txId}.trace.json`);
1105
- return this.readJsonSafe(filePath);
1106
- }
1107
- async loadJsonDir(dir) {
1108
- const results = [];
1109
- let entries;
1110
- try {
1111
- entries = await fs3.readdir(dir);
1112
- } catch {
1113
- return results;
1114
- }
1115
- for (const file of entries.sort()) {
1116
- if (!file.endsWith(".json")) continue;
1117
- const raw = await this.readJsonSafe(path3.join(dir, file));
1118
- if (raw) results.push(raw);
1119
- }
1120
- return results;
1121
- }
1122
1271
  async readJsonSafe(filePath) {
1123
1272
  try {
1124
- const content = await fs3.readFile(filePath, "utf-8");
1273
+ const content = await fs4.readFile(filePath, "utf-8");
1125
1274
  return JSON.parse(content);
1126
1275
  } catch {
1127
1276
  return null;
@@ -1164,43 +1313,55 @@ var DIVERGENCE_RULES = {
1164
1313
  };
1165
1314
  function explainDivergence(d) {
1166
1315
  return {
1167
- question: `Why is tx ${d.txId.slice(0, 16)}... flagged as divergent?`,
1168
- conclusion: `Divergence type: ${d.kind}. Field "${d.field}" expected ${d.expected.slice(0, 40)}${d.expected.length > 40 ? "..." : ""}, got ${d.actual.slice(0, 40)}${d.actual.length > 40 ? "..." : ""}.`,
1169
- steps: [
1170
- { order: 1, assertion: `Field "${d.field}" does not match expected value`, evidence: `expected: ${d.expected}, actual: ${d.actual}`, rule: DIVERGENCE_RULES[d.kind] },
1171
- { order: 2, assertion: `Classified as ${d.kind}`, evidence: `Divergence category based on field and semantic context`, rule: "Replay divergence classification (query/replay-adapter.ts)" }
1316
+ question: `Why is tx ${d.txId.slice(0, 16)}... divergent?`,
1317
+ answer: `Field "${d.field}" shows unexpected non-deterministic behavior (${d.kind}).`,
1318
+ evidence: [{ type: "txId", value: d.txId }],
1319
+ causalChain: [
1320
+ {
1321
+ order: 1,
1322
+ assertion: `Value mismatch in "${d.field}"`,
1323
+ evidence: `Expected: ${d.expected.slice(0, 40)}, Actual: ${d.actual.slice(0, 40)}`,
1324
+ rule: DIVERGENCE_RULES[d.kind]
1325
+ },
1326
+ {
1327
+ order: 2,
1328
+ assertion: "Divergence detected in replay comparison",
1329
+ evidence: "Verification engine mismatch",
1330
+ rule: "Invariant validation policy"
1331
+ }
1172
1332
  ],
1173
- model: "replay-invariants",
1174
- confidence: "definitive",
1175
- references: [d.txId]
1333
+ model: "replay-analysis",
1334
+ confidence: "definitive"
1176
1335
  };
1177
1336
  }
1178
1337
  function explainInvariants(result) {
1179
- const steps = [];
1338
+ const causalChain = [];
1180
1339
  let order = 1;
1181
- steps.push({ order: order++, assertion: result.planIntegrity ? "Plan contentHash is stable" : "Plan contentHash MISMATCH", evidence: `planIntegrity=${result.planIntegrity}`, rule: "canonicalStringify + SHA-256 determinism" });
1182
- steps.push({ order: order++, assertion: result.receiptReproducible ? "Receipt has pre/post state hashes for comparison" : "Receipt missing state hashes \u2014 replay comparison not possible", evidence: `receiptReproducible=${result.receiptReproducible}`, rule: "preStateHash + postStateHash required for replay verification" });
1183
- steps.push({ order: order++, assertion: result.stateTransitionValid ? "State transition is consistent with status" : "State transition INCONSISTENT with status", evidence: `stateTransitionValid=${result.stateTransitionValid}`, rule: "Confirmed tx must change state. Failed tx must not." });
1184
- steps.push({ order: order++, assertion: result.utxoConservation ? "UTXO conservation holds" : "UTXO conservation VIOLATION", evidence: `utxoConservation=${result.utxoConservation}`, rule: "Confirmed tx must consume and produce UTXOs" });
1340
+ causalChain.push({ order: order++, assertion: result.planIntegrity ? "Plan integrity is OK" : "Plan integrity FAILED", evidence: `planIntegrity=${result.planIntegrity}`, rule: "SHA-256 canonical consistency" });
1341
+ causalChain.push({ order: order++, assertion: result.receiptReproducible ? "Receipt is reproducible" : "Receipt is NOT reproducible", evidence: `receiptReproducible=${result.receiptReproducible}`, rule: "Replay evidence requirements" });
1342
+ causalChain.push({ order: order++, assertion: result.stateTransitionValid ? "State transition is valid" : "State transition INVALID", evidence: `stateTransitionValid=${result.stateTransitionValid}`, rule: "Status/State alignment" });
1343
+ causalChain.push({ order: order++, assertion: result.utxoConservation ? "UTXO conservation holds" : "UTXO conservation VIOLATED", evidence: `utxoConservation=${result.utxoConservation}`, rule: "Value conservation policy" });
1185
1344
  const allOk = result.planIntegrity && result.receiptReproducible && result.stateTransitionValid && result.utxoConservation;
1186
1345
  return {
1187
1346
  question: `Are replay invariants satisfied for tx ${result.txId.slice(0, 16)}...?`,
1188
- conclusion: allOk ? "All replay invariants satisfied." : `Invariant violations: ${result.issues.join("; ")}`,
1189
- steps,
1347
+ answer: allOk ? "All replay invariants satisfied." : `Violations found: ${result.issues.join("; ")}`,
1348
+ evidence: [{ type: "txId", value: result.txId }],
1349
+ causalChain,
1190
1350
  model: "replay-invariants",
1191
- confidence: "definitive",
1192
- references: [result.txId]
1351
+ confidence: "definitive"
1193
1352
  };
1194
1353
  }
1195
1354
 
1196
1355
  // src/adapters/dag-adapter.ts
1197
- import fs4 from "fs/promises";
1356
+ import fs5 from "fs/promises";
1198
1357
  import path4 from "path";
1199
1358
  var DagQueryAdapter = class {
1200
1359
  domain = "dag";
1201
1360
  rootDir;
1202
- constructor(rootDir) {
1361
+ backend;
1362
+ constructor(rootDir, backend) {
1203
1363
  this.rootDir = rootDir;
1364
+ this.backend = backend;
1204
1365
  }
1205
1366
  supportedOps() {
1206
1367
  return ["conflicts", "displaced", "history", "sink-path", "anomalies"];
@@ -1240,9 +1401,9 @@ var DagQueryAdapter = class {
1240
1401
  }));
1241
1402
  items.sort((a, b) => a.outpoint.localeCompare(b.outpoint));
1242
1403
  const paged = items.slice(request.offset, request.offset + request.limit);
1243
- let explain;
1404
+ let why;
1244
1405
  if (request.explain) {
1245
- explain = paged.map((c) => explainConflict(c, dag));
1406
+ why = paged.map((c) => explainConflict(c, dag));
1246
1407
  }
1247
1408
  return {
1248
1409
  domain: "dag",
@@ -1252,7 +1413,7 @@ var DagQueryAdapter = class {
1252
1413
  truncated: items.length > request.offset + request.limit,
1253
1414
  deterministic: true,
1254
1415
  queryHash: computeQueryHash(paged),
1255
- explain,
1416
+ why,
1256
1417
  annotations: { executedAt: (/* @__PURE__ */ new Date()).toISOString(), executionMs: Date.now() - start }
1257
1418
  };
1258
1419
  }
@@ -1276,9 +1437,9 @@ var DagQueryAdapter = class {
1276
1437
  });
1277
1438
  items.sort((a, b) => a.txId.localeCompare(b.txId));
1278
1439
  const paged = items.slice(request.offset, request.offset + request.limit);
1279
- let explain;
1440
+ let why;
1280
1441
  if (request.explain) {
1281
- explain = paged.map((d) => explainDisplacement(d, dag));
1442
+ why = paged.map((d) => explainDisplacement(d, dag));
1282
1443
  }
1283
1444
  return {
1284
1445
  domain: "dag",
@@ -1288,7 +1449,7 @@ var DagQueryAdapter = class {
1288
1449
  truncated: items.length > request.offset + request.limit,
1289
1450
  deterministic: true,
1290
1451
  queryHash: computeQueryHash(paged),
1291
- explain,
1452
+ why,
1292
1453
  annotations: { executedAt: (/* @__PURE__ */ new Date()).toISOString(), executionMs: Date.now() - start }
1293
1454
  };
1294
1455
  }
@@ -1328,9 +1489,9 @@ var DagQueryAdapter = class {
1328
1489
  }
1329
1490
  }
1330
1491
  entries.sort((a, b) => a.daaScore.localeCompare(b.daaScore));
1331
- let explain;
1492
+ let why;
1332
1493
  if (request.explain) {
1333
- explain = entries.map((e) => explainTxHistory(e, dag));
1494
+ why = entries.map((e) => explainTxHistory(e, dag));
1334
1495
  }
1335
1496
  return {
1336
1497
  domain: "dag",
@@ -1340,7 +1501,7 @@ var DagQueryAdapter = class {
1340
1501
  truncated: false,
1341
1502
  deterministic: true,
1342
1503
  queryHash: computeQueryHash(entries),
1343
- explain,
1504
+ why,
1344
1505
  annotations: { executedAt: (/* @__PURE__ */ new Date()).toISOString(), executionMs: Date.now() - start }
1345
1506
  };
1346
1507
  }
@@ -1428,9 +1589,9 @@ var DagQueryAdapter = class {
1428
1589
  }
1429
1590
  anomalies.sort((a, b) => a.kind.localeCompare(b.kind));
1430
1591
  const paged = anomalies.slice(request.offset, request.offset + request.limit);
1431
- let explain;
1592
+ let why;
1432
1593
  if (request.explain) {
1433
- explain = paged.map((a) => explainAnomaly(a, dag));
1594
+ why = paged.map((a) => explainAnomaly(a, dag));
1434
1595
  }
1435
1596
  return {
1436
1597
  domain: "dag",
@@ -1440,7 +1601,7 @@ var DagQueryAdapter = class {
1440
1601
  truncated: anomalies.length > request.offset + request.limit,
1441
1602
  deterministic: true,
1442
1603
  queryHash: computeQueryHash(paged),
1443
- explain,
1604
+ why,
1444
1605
  annotations: { executedAt: (/* @__PURE__ */ new Date()).toISOString(), executionMs: Date.now() - start }
1445
1606
  };
1446
1607
  }
@@ -1450,7 +1611,7 @@ var DagQueryAdapter = class {
1450
1611
  async loadDag() {
1451
1612
  const statePath = path4.join(this.rootDir, ".hardkas", "state.json");
1452
1613
  try {
1453
- const content = await fs4.readFile(statePath, "utf-8");
1614
+ const content = await fs5.readFile(statePath, "utf-8");
1454
1615
  const state = JSON.parse(content);
1455
1616
  return state.dag ?? null;
1456
1617
  } catch {
@@ -1471,7 +1632,7 @@ function emptyDagResult(op, start) {
1471
1632
  };
1472
1633
  }
1473
1634
  function explainConflict(conflict, dag) {
1474
- const steps = [];
1635
+ const causalChain = [];
1475
1636
  let order = 1;
1476
1637
  let winnerBlockId;
1477
1638
  let winnerDaaScore = "0";
@@ -1484,92 +1645,87 @@ function explainConflict(conflict, dag) {
1484
1645
  break;
1485
1646
  }
1486
1647
  }
1487
- steps.push({
1648
+ causalChain.push({
1488
1649
  order: order++,
1489
- assertion: `Outpoint ${conflict.outpoint} was spent by multiple transactions`,
1650
+ assertion: `Outpoint ${conflict.outpoint} spent by multiple txs`,
1490
1651
  evidence: `Winner: ${conflict.winnerTxId}, Losers: ${conflict.loserTxIds.join(", ")}`,
1491
- rule: "UTXO double-spend detection (dag.ts:moveSink)"
1652
+ rule: "UTXO double-spend detection"
1492
1653
  });
1493
1654
  if (winnerBlockId) {
1494
- steps.push({
1495
- order: order++,
1496
- assertion: `Winner tx is in block ${winnerBlockId} (daaScore: ${winnerDaaScore})`,
1497
- evidence: `Block ${winnerBlockId} contains tx ${conflict.winnerTxId}`,
1498
- rule: "Block tx membership"
1499
- });
1500
- steps.push({
1655
+ causalChain.push({
1501
1656
  order: order++,
1502
- assertion: winnerInSinkPath ? `Block ${winnerBlockId} IS in the sink path \u2014 has priority` : `Block ${winnerBlockId} is NOT in the sink path`,
1503
- evidence: `selectedPathToSink.includes("${winnerBlockId}") = ${winnerInSinkPath}`,
1504
- rule: "Sink-ancestry priority (dag.ts:resolveConflictsDeterministically)"
1657
+ assertion: `Winner in block ${winnerBlockId} (daaScore: ${winnerDaaScore})`,
1658
+ evidence: `In sink path: ${winnerInSinkPath}`,
1659
+ rule: "Sink-ancestry priority"
1505
1660
  });
1506
1661
  }
1507
- steps.push({
1508
- order: order++,
1509
- assertion: `Conflict resolved: ${conflict.winnerTxId.slice(0, 16)}... accepted, ${conflict.loserTxIds.length} tx(s) displaced`,
1510
- evidence: `Resolution based on: 1) sink-path priority, 2) daaScore ordering, 3) blockId tie-break`,
1511
- rule: "Deterministic conflict resolution (dag.ts:187-248)"
1512
- });
1513
1662
  return {
1514
- question: `Why did ${conflict.winnerTxId.slice(0, 16)}... win the conflict on outpoint ${conflict.outpoint}?`,
1515
- conclusion: `Winner has ${winnerInSinkPath ? "sink-path priority" : "block ordering priority"}. ${conflict.loserTxIds.length} tx(s) displaced. Model: deterministic-light-model (NOT GHOSTDAG).`,
1516
- steps,
1663
+ question: `Why winner ${conflict.winnerTxId.slice(0, 8)} on outpoint ${conflict.outpoint}?`,
1664
+ answer: `Winner has ${winnerInSinkPath ? "sink-path priority" : "block ordering priority"}.`,
1665
+ evidence: [
1666
+ { type: "txId", value: conflict.winnerTxId },
1667
+ ...conflict.loserTxIds.map((id) => ({ type: "txId", value: id }))
1668
+ ],
1669
+ causalChain,
1517
1670
  model: "deterministic-light-model",
1518
- confidence: "definitive",
1519
- references: [conflict.winnerTxId, ...conflict.loserTxIds]
1671
+ confidence: "definitive"
1520
1672
  };
1521
1673
  }
1522
1674
  function explainDisplacement(d, _dag) {
1523
1675
  return {
1524
- question: `Why was tx ${d.txId.slice(0, 16)}... displaced?`,
1525
- conclusion: `${d.reason}. Currently accepted: ${d.currentlyAccepted}. Model: deterministic-light-model (NOT GHOSTDAG).`,
1526
- steps: [
1527
- { order: 1, assertion: `Transaction ${d.txId.slice(0, 16)}... is in displacedTxIds`, evidence: `dag.displacedTxIds includes "${d.txId}"`, rule: "DAG sink movement (dag.ts:moveSink)" },
1528
- { order: 2, assertion: d.reason, evidence: d.reason, rule: "Conflict resolution or DAG reorganization" }
1676
+ question: `Why displaced tx ${d.txId.slice(0, 8)}?`,
1677
+ answer: d.reason,
1678
+ evidence: [{ type: "txId", value: d.txId }],
1679
+ causalChain: [
1680
+ { order: 1, assertion: "Tx in displaced set", evidence: "dag.displacedTxIds includes txId", rule: "DAG reorganization" },
1681
+ { order: 2, assertion: "Status: displaced", evidence: d.reason }
1529
1682
  ],
1530
1683
  model: "deterministic-light-model",
1531
- confidence: "definitive",
1532
- references: [d.txId]
1684
+ confidence: "definitive"
1533
1685
  };
1534
1686
  }
1535
1687
  function explainTxHistory(entry, _dag) {
1536
1688
  const status = entry.accepted ? "ACCEPTED" : entry.displaced ? "DISPLACED" : "UNKNOWN";
1537
1689
  return {
1538
- question: `What is the DAG lifecycle status of tx ${entry.txId.slice(0, 16)}...?`,
1539
- conclusion: `Status: ${status}. Block: ${entry.blockId} (daaScore: ${entry.daaScore}). In sink path: ${entry.inSinkPath}. Model: deterministic-light-model (NOT GHOSTDAG).`,
1540
- steps: [
1541
- { order: 1, assertion: `Transaction is in block ${entry.blockId}`, evidence: `block.acceptedTxIds includes "${entry.txId}"`, rule: "Block membership scan" },
1542
- { order: 2, assertion: `Block daaScore = ${entry.daaScore}`, evidence: `dag.blocks["${entry.blockId}"].daaScore = "${entry.daaScore}"` },
1543
- { order: 3, assertion: entry.inSinkPath ? "Block IS in selected path to sink" : "Block is NOT in selected path to sink", evidence: `selectedPathToSink.includes("${entry.blockId}") = ${entry.inSinkPath}`, rule: "Sink path computation (dag.ts:calculateSelectedPath)" },
1544
- { order: 4, assertion: `Current status: ${status}`, evidence: `accepted=${entry.accepted}, displaced=${entry.displaced}`, rule: "DAG accepted/displaced set membership" }
1690
+ question: `Causal history of tx ${entry.txId.slice(0, 8)}?`,
1691
+ answer: `Status is ${status} in block ${entry.blockId}.`,
1692
+ evidence: [
1693
+ { type: "txId", value: entry.txId },
1694
+ { type: "blockId", value: entry.blockId }
1695
+ ],
1696
+ causalChain: [
1697
+ { order: 1, assertion: `In block ${entry.blockId}`, evidence: `daaScore=${entry.daaScore}` },
1698
+ { order: 2, assertion: entry.inSinkPath ? "In selected sink path" : "Not in sink path", evidence: `selectedPathToSink.includes("${entry.blockId}")` }
1545
1699
  ],
1546
1700
  model: "deterministic-light-model",
1547
- confidence: "definitive",
1548
- references: [entry.txId]
1701
+ confidence: "definitive"
1549
1702
  };
1550
1703
  }
1551
1704
  function explainAnomaly(a, _dag) {
1705
+ const evidence = [];
1706
+ if (a.txId) evidence.push({ type: "txId", value: a.txId });
1707
+ if (a.blockId) evidence.push({ type: "blockId", value: a.blockId });
1552
1708
  return {
1553
- question: `Why is this a DAG anomaly?`,
1554
- conclusion: `${a.description}. Model: deterministic-light-model (NOT GHOSTDAG).`,
1555
- steps: [
1556
- { order: 1, assertion: a.description, evidence: `Anomaly kind: ${a.kind}`, rule: "DAG invariant check (query/dag-adapter.ts)" }
1709
+ question: `Why anomaly: ${a.kind}?`,
1710
+ answer: a.description,
1711
+ evidence,
1712
+ causalChain: [
1713
+ { order: 1, assertion: a.description, evidence: `Anomaly: ${a.kind}`, rule: "DAG invariant check" }
1557
1714
  ],
1558
1715
  model: "deterministic-light-model",
1559
- confidence: "definitive",
1560
- references: [a.txId ?? a.blockId ?? ""]
1716
+ confidence: "definitive"
1561
1717
  };
1562
1718
  }
1563
1719
 
1564
1720
  // src/adapters/events-adapter.ts
1565
- import fs5 from "fs/promises";
1566
1721
  import path5 from "path";
1567
- import { validateEventEnvelope } from "@hardkas/core";
1568
1722
  var EventsQueryAdapter = class {
1569
1723
  domain = "events";
1570
1724
  rootDir;
1571
- constructor(rootDir) {
1725
+ backend;
1726
+ constructor(rootDir, backend) {
1572
1727
  this.rootDir = rootDir;
1728
+ this.backend = backend;
1573
1729
  }
1574
1730
  supportedOps() {
1575
1731
  return ["list", "summary"];
@@ -1588,7 +1744,7 @@ var EventsQueryAdapter = class {
1588
1744
  async executeList(request) {
1589
1745
  const start = Date.now();
1590
1746
  const events = await this.loadEvents();
1591
- const backendUsed = "filesystem";
1747
+ const backendUsed = this.backend.kind();
1592
1748
  const eventsPath = path5.join(this.rootDir, ".hardkas", "events.jsonl");
1593
1749
  const filtered = [];
1594
1750
  const txFilter = request.params["tx"] || request.params["txId"];
@@ -1619,67 +1775,52 @@ var EventsQueryAdapter = class {
1619
1775
  annotations: {
1620
1776
  executedAt: (/* @__PURE__ */ new Date()).toISOString(),
1621
1777
  executionMs: Date.now() - start,
1622
- filesScanned: 1
1778
+ filesScanned: backendUsed === "sqlite" ? 0 : 1
1623
1779
  },
1624
- ...request.explain ? {
1625
- explain: [{
1626
- question: "How were events loaded?",
1627
- conclusion: `Loaded ${events.length} events from ${backendUsed}. Applied ${effectiveFilters.length} filter(s). Returned ${paged.length}/${total} results sorted by timestamp+eventId.`,
1628
- steps: [
1629
- { order: 1, assertion: `Backend: ${backendUsed} (events.jsonl)`, evidence: eventsPath, rule: "Filesystem fallback" },
1630
- { order: 2, assertion: `Filters applied: ${effectiveFilters.length}`, evidence: effectiveFilters.map((f) => `${f.field} ${f.op} ${f.value}`).join(", ") || "none" },
1631
- { order: 3, assertion: `Ordering: timestamp ASC + eventId tiebreaker`, evidence: "Deterministic" }
1632
- ],
1633
- model: "events-query",
1634
- confidence: "definitive",
1635
- references: []
1636
- }]
1637
- } : {}
1780
+ why: request.explain ? [{
1781
+ question: "How were events loaded and linked?",
1782
+ answer: `Loaded ${total} events matching filters. Causal links (correlation/causation) available in payload.`,
1783
+ evidence: [],
1784
+ causalChain: [
1785
+ { order: 1, assertion: `Source: ${backendUsed}`, evidence: "Event stream processed" },
1786
+ { order: 2, assertion: `Filters: ${effectiveFilters.length} applied`, evidence: effectiveFilters.map((f) => `${f.field} ${f.op} ${f.value}`).join(", ") || "none" },
1787
+ { order: 3, assertion: "Ordering: timestamp ASC", evidence: "Deterministic stream sorting" }
1788
+ ],
1789
+ model: "events-causality",
1790
+ confidence: "definitive"
1791
+ }] : void 0
1638
1792
  };
1639
1793
  }
1640
1794
  async loadEvents() {
1641
- const eventsPath = path5.join(this.rootDir, ".hardkas", "events.jsonl");
1642
- let content;
1643
- try {
1644
- content = await fs5.readFile(eventsPath, "utf-8");
1645
- } catch {
1646
- return [];
1647
- }
1648
- const lines = content.split("\n").filter((l) => l.trim() !== "");
1795
+ const docs = await this.backend.getEvents();
1649
1796
  const events = [];
1650
- for (const line of lines) {
1651
- try {
1652
- const parsed = JSON.parse(line);
1653
- if (!validateEventEnvelope(parsed)) continue;
1654
- events.push({
1655
- eventId: parsed.eventId,
1656
- kind: parsed.kind,
1657
- domain: parsed.domain,
1658
- timestamp: parsed.timestamp || "",
1659
- workflowId: parsed.workflowId,
1660
- correlationId: parsed.correlationId,
1661
- causationId: parsed.causationId,
1662
- txId: parsed.txId,
1663
- artifactId: parsed.artifactId,
1664
- networkId: parsed.networkId,
1665
- payload: parsed.payload
1666
- });
1667
- } catch {
1668
- }
1797
+ for (const doc of docs) {
1798
+ events.push({
1799
+ eventId: doc.eventId,
1800
+ kind: doc.kind,
1801
+ domain: doc.domain,
1802
+ timestamp: doc.timestamp || "",
1803
+ workflowId: doc.workflowId,
1804
+ correlationId: doc.correlationId,
1805
+ causationId: doc.causationId || void 0,
1806
+ txId: doc.txId || void 0,
1807
+ artifactId: doc.artifactId || void 0,
1808
+ networkId: doc.networkId,
1809
+ payload: doc.payload
1810
+ });
1669
1811
  }
1670
1812
  return events;
1671
1813
  }
1672
1814
  };
1673
1815
 
1674
1816
  // src/adapters/tx-adapter.ts
1675
- import fs6 from "fs/promises";
1676
- import path6 from "path";
1677
- import { validateEventEnvelope as validateEventEnvelope2 } from "@hardkas/core";
1678
1817
  var TxQueryAdapter = class {
1679
1818
  domain = "tx";
1680
1819
  rootDir;
1681
- constructor(rootDir) {
1820
+ backend;
1821
+ constructor(rootDir, backend) {
1682
1822
  this.rootDir = rootDir;
1823
+ this.backend = backend;
1683
1824
  }
1684
1825
  supportedOps() {
1685
1826
  return ["aggregate"];
@@ -1716,20 +1857,19 @@ var TxQueryAdapter = class {
1716
1857
  warnings,
1717
1858
  complete
1718
1859
  };
1719
- let explain;
1860
+ let why;
1720
1861
  if (request.explain) {
1721
- explain = [{
1722
- question: `What data exists for transaction ${txId}?`,
1723
- conclusion: complete ? `Found ${artifacts.length} artifact(s) and ${events.length} event(s). Workflow appears complete.` : `Found ${artifacts.length} artifact(s) and ${events.length} event(s). ${warnings.join(". ")}.`,
1724
- steps: [
1725
- { order: 1, assertion: `Backend: filesystem`, evidence: path6.join(this.rootDir, ".hardkas"), rule: "Filesystem scan" },
1726
- { order: 2, assertion: `Artifacts found: ${artifacts.length}`, evidence: artifacts.map((a) => `${a.schema} (${a.role})`).join(", ") || "none" },
1727
- { order: 3, assertion: `Events found: ${events.length}`, evidence: events.map((e) => e.kind).join(", ") || "none" },
1728
- { order: 4, assertion: `Completeness: ${complete ? "yes" : "no"}`, evidence: warnings.join("; ") || "all artifacts present" }
1862
+ why = [{
1863
+ question: `Causal aggregation for transaction ${txId}?`,
1864
+ answer: complete ? `Found ${artifacts.length} artifact(s) and ${events.length} event(s). Workflow is consistent.` : `Aggregation incomplete: ${warnings.join(". ")}.`,
1865
+ evidence: [{ type: "txId", value: txId }],
1866
+ causalChain: [
1867
+ { order: 1, assertion: `Artifacts linked: ${artifacts.length}`, evidence: artifacts.map((a) => a.role).join(", ") },
1868
+ { order: 2, assertion: `Events linked: ${events.length}`, evidence: "Events found in stream" },
1869
+ { order: 3, assertion: `Completeness check: ${complete}`, evidence: warnings.join("; ") || "all required roles found" }
1729
1870
  ],
1730
- model: "tx-aggregation",
1731
- confidence: "definitive",
1732
- references: [txId]
1871
+ model: "tx-causality",
1872
+ confidence: "definitive"
1733
1873
  }];
1734
1874
  }
1735
1875
  return {
@@ -1740,7 +1880,7 @@ var TxQueryAdapter = class {
1740
1880
  truncated: false,
1741
1881
  deterministic: true,
1742
1882
  queryHash: computeQueryHash([result]),
1743
- explain,
1883
+ why,
1744
1884
  annotations: {
1745
1885
  executedAt: (/* @__PURE__ */ new Date()).toISOString(),
1746
1886
  executionMs: Date.now() - start
@@ -1748,92 +1888,96 @@ var TxQueryAdapter = class {
1748
1888
  };
1749
1889
  }
1750
1890
  async findArtifactsByTxId(txId) {
1891
+ const docs = await this.backend.findArtifacts();
1751
1892
  const results = [];
1752
- const files = await this.scanJsonFiles();
1753
- for (const filePath of files) {
1754
- try {
1755
- const content = await fs6.readFile(filePath, "utf-8");
1756
- const parsed = JSON.parse(content);
1757
- if (!parsed.schema) continue;
1758
- const matchesTx = parsed.txId === txId || parsed.transaction?.id === txId || parsed.lineage?.artifactId && parsed.txId === txId;
1759
- if (!matchesTx) continue;
1760
- const schema = String(parsed.schema);
1761
- let role = "unknown";
1762
- if (schema.includes("txPlan")) role = "plan";
1763
- else if (schema.includes("signedTx")) role = "signed";
1764
- else if (schema.includes("txReceipt")) role = "receipt";
1765
- results.push({
1766
- filePath,
1767
- schema,
1768
- contentHash: parsed.contentHash,
1769
- role
1770
- });
1771
- } catch {
1772
- }
1893
+ for (const doc of docs) {
1894
+ const parsed = doc.payload;
1895
+ const matchesTx = parsed.txId === txId || parsed.transaction?.id === txId || parsed.lineage?.artifactId === txId;
1896
+ if (!matchesTx) continue;
1897
+ const schema = String(doc.schema);
1898
+ let role = "unknown";
1899
+ if (schema.includes("txPlan")) role = "plan";
1900
+ else if (schema.includes("signedTx")) role = "signed";
1901
+ else if (schema.includes("txReceipt")) role = "receipt";
1902
+ results.push({
1903
+ filePath: doc.path,
1904
+ schema,
1905
+ contentHash: doc.contentHash,
1906
+ role
1907
+ });
1773
1908
  }
1774
1909
  return results.sort((a, b) => a.filePath.localeCompare(b.filePath));
1775
1910
  }
1776
1911
  async findEventsByTxId(txId) {
1777
- const eventsPath = path6.join(this.rootDir, ".hardkas", "events.jsonl");
1778
- let content;
1779
- try {
1780
- content = await fs6.readFile(eventsPath, "utf-8");
1781
- } catch {
1782
- return [];
1783
- }
1784
- const lines = content.split("\n").filter((l) => l.trim() !== "");
1912
+ const docs = await this.backend.getEvents({ txId });
1785
1913
  const results = [];
1786
- for (const line of lines) {
1787
- try {
1788
- const parsed = JSON.parse(line);
1789
- if (!validateEventEnvelope2(parsed)) continue;
1790
- if (parsed.txId !== txId) continue;
1791
- results.push({
1792
- eventId: parsed.eventId,
1793
- kind: parsed.kind,
1794
- timestamp: parsed.timestamp || ""
1795
- });
1796
- } catch {
1797
- }
1914
+ for (const doc of docs) {
1915
+ results.push({
1916
+ eventId: doc.eventId,
1917
+ kind: doc.kind,
1918
+ timestamp: doc.timestamp || ""
1919
+ });
1798
1920
  }
1799
1921
  return results;
1800
1922
  }
1801
- async scanJsonFiles() {
1802
- const files = [];
1803
- const hardkasDir = path6.join(this.rootDir, ".hardkas");
1804
- await this.walkDir(hardkasDir, files);
1805
- return files.sort();
1806
- }
1807
- async walkDir(dir, out) {
1808
- let entries;
1809
- try {
1810
- entries = await fs6.readdir(dir, { withFileTypes: true });
1811
- } catch {
1812
- return;
1813
- }
1814
- for (const entry of entries) {
1815
- const full = path6.join(dir, entry.name);
1816
- if (entry.isDirectory()) {
1817
- if (entry.name === "node_modules" || entry.name === ".git") continue;
1818
- await this.walkDir(full, out);
1819
- } else if (entry.name.endsWith(".json") && !entry.name.endsWith(".enc.json") && entry.name !== "events.jsonl") {
1820
- out.push(full);
1821
- }
1822
- }
1823
- }
1824
1923
  };
1825
1924
 
1826
1925
  // src/engine.ts
1827
- var QueryEngine = class {
1926
+ import { withLock } from "@hardkas/core";
1927
+ import fs6 from "fs";
1928
+ import path6 from "path";
1929
+ var QueryEngine = class _QueryEngine {
1828
1930
  adapters;
1931
+ backend;
1932
+ /**
1933
+ * Primary entry point for creating a QueryEngine with auto-discovery.
1934
+ */
1935
+ static async create(options) {
1936
+ let backend = options.backend;
1937
+ if (!backend) {
1938
+ const dbPath = path6.join(options.artifactDir, ".hardkas", "store.db");
1939
+ if (fs6.existsSync(dbPath)) {
1940
+ try {
1941
+ const { HardkasStore, SqliteQueryBackend, HardkasIndexer } = await import("@hardkas/query-store");
1942
+ const store = new HardkasStore({ dbPath });
1943
+ if (options.autoSync) {
1944
+ await withLock({
1945
+ rootDir: options.artifactDir,
1946
+ name: "query-store",
1947
+ command: "query-engine-auto-sync",
1948
+ wait: options.waitLock ?? false,
1949
+ timeoutMs: 5e3
1950
+ // Short timeout for auto-sync
1951
+ }, async () => {
1952
+ store.connect({ autoMigrate: true });
1953
+ const indexer = new HardkasIndexer(store.getDatabase());
1954
+ await indexer.sync();
1955
+ });
1956
+ } else {
1957
+ store.connect();
1958
+ }
1959
+ backend = new SqliteQueryBackend(store);
1960
+ } catch (e) {
1961
+ backend = new FilesystemQueryBackend(options.artifactDir);
1962
+ }
1963
+ } else {
1964
+ backend = new FilesystemQueryBackend(options.artifactDir);
1965
+ }
1966
+ }
1967
+ return new _QueryEngine({
1968
+ artifactDir: options.artifactDir,
1969
+ backend
1970
+ });
1971
+ }
1829
1972
  constructor(options) {
1973
+ this.backend = options.backend || new FilesystemQueryBackend(options.artifactDir);
1830
1974
  this.adapters = /* @__PURE__ */ new Map();
1831
- this.adapters.set("artifacts", new ArtifactQueryAdapter(options.artifactDir));
1832
- this.adapters.set("lineage", new LineageQueryAdapter(options.artifactDir));
1833
- this.adapters.set("replay", new ReplayQueryAdapter(options.artifactDir));
1834
- this.adapters.set("dag", new DagQueryAdapter(options.artifactDir));
1835
- this.adapters.set("events", new EventsQueryAdapter(options.artifactDir));
1836
- this.adapters.set("tx", new TxQueryAdapter(options.artifactDir));
1975
+ this.adapters.set("artifacts", new ArtifactQueryAdapter(options.artifactDir, this.backend));
1976
+ this.adapters.set("lineage", new LineageQueryAdapter(options.artifactDir, this.backend));
1977
+ this.adapters.set("replay", new ReplayQueryAdapter(options.artifactDir, this.backend));
1978
+ this.adapters.set("dag", new DagQueryAdapter(options.artifactDir, this.backend));
1979
+ this.adapters.set("events", new EventsQueryAdapter(options.artifactDir, this.backend));
1980
+ this.adapters.set("tx", new TxQueryAdapter(options.artifactDir, this.backend));
1837
1981
  }
1838
1982
  /**
1839
1983
  * Execute a query request against the appropriate adapter.
@@ -1848,7 +1992,31 @@ var QueryEngine = class {
1848
1992
  `Operation "${request.op}" is not supported by the "${request.domain}" adapter. Supported: ${adapter.supportedOps().join(", ")}`
1849
1993
  );
1850
1994
  }
1851
- return adapter.execute(request);
1995
+ const result = await adapter.execute(request);
1996
+ const freshness = await this.backend.getStoreStatus();
1997
+ const backendUsed = this.backend.kind();
1998
+ let explain = void 0;
1999
+ if (request.explain) {
2000
+ explain = {
2001
+ backend: backendUsed,
2002
+ executionPlan: ["Discovery", "Filter", "Sort", "Paginate"],
2003
+ indexesUsed: backendUsed === "sqlite" ? ["PRIMARY", "idx_artifacts_schema"] : [],
2004
+ filtersApplied: request.filters.map((f) => `${f.field} ${f.op} ${f.value}`),
2005
+ rowsRead: result.items.length,
2006
+ scannedFiles: result.annotations.filesScanned || 0,
2007
+ freshness,
2008
+ warnings: freshness === "stale" ? ["Index is STALE. mtime mismatch detected. Run 'hardkas query store rebuild'."] : []
2009
+ };
2010
+ }
2011
+ return {
2012
+ ...result,
2013
+ explain,
2014
+ annotations: {
2015
+ ...result.annotations,
2016
+ backendUsed,
2017
+ freshness
2018
+ }
2019
+ };
1852
2020
  }
1853
2021
  /**
1854
2022
  * List available domains and their operations.
@@ -1886,14 +2054,15 @@ export {
1886
2054
  ReplayQueryAdapter,
1887
2055
  TxQueryAdapter,
1888
2056
  computeQueryHash,
2057
+ createExplainBlock,
1889
2058
  createQueryRequest,
1890
2059
  evaluateFilter,
1891
2060
  evaluateFilters,
1892
2061
  explainIntegrity,
1893
2062
  explainOrphan,
1894
2063
  explainTransition,
1895
- formatExplainBrief,
1896
- formatExplainFull,
2064
+ formatExplainBlock,
2065
+ formatWhyBlock,
1897
2066
  resolveFieldPath,
1898
2067
  serializeQueryResult
1899
2068
  };