@hardkas/query 0.2.2-alpha → 0.2.2-alpha.1

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 +156 -35
  2. package/dist/index.js +545 -407
  3. package/package.json +4 -3
package/dist/index.js CHANGED
@@ -1,6 +1,147 @@
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() {
104
+ }
105
+ async findReceipts(filters) {
106
+ return this.findArtifacts({ schema: "hardkas.txReceipt", ...filters });
107
+ }
108
+ async findTraces(filters) {
109
+ const artifacts = await this.findArtifacts({ schema: "hardkas.txTrace" });
110
+ if (filters?.txId) {
111
+ return artifacts.filter((a) => a.payload.txId === filters.txId);
112
+ }
113
+ return artifacts;
114
+ }
115
+ async scanFiles(dir) {
116
+ const results = [];
117
+ try {
118
+ const entries = await fs.readdir(dir, { withFileTypes: true });
119
+ for (const entry of entries) {
120
+ const fullPath = path.join(dir, entry.name);
121
+ if (entry.isDirectory()) {
122
+ if (entry.name === "node_modules" || entry.name === ".git") continue;
123
+ results.push(...await this.scanFiles(fullPath));
124
+ } else if (entry.name.endsWith(".json") && !entry.name.endsWith(".enc.json") && entry.name !== "events.jsonl") {
125
+ results.push(fullPath);
126
+ }
127
+ }
128
+ } catch {
129
+ }
130
+ return results;
131
+ }
132
+ async readJson(file) {
133
+ try {
134
+ const content = await fs.readFile(file, "utf-8");
135
+ return JSON.parse(content);
136
+ } catch {
137
+ return null;
138
+ }
139
+ }
140
+ };
141
+
142
+ // src/adapters/artifact-adapter.ts
143
+ import fs2 from "fs/promises";
144
+ import path2 from "path";
4
145
  import {
5
146
  calculateContentHash,
6
147
  verifyArtifactIntegrity,
@@ -123,144 +264,145 @@ var VALID_TRANSITIONS = {
123
264
  "hardkas.txPlan": ["hardkas.signedTx"],
124
265
  "hardkas.signedTx": ["hardkas.txReceipt"]
125
266
  };
267
+ function createExplainBlock(options) {
268
+ return {
269
+ backend: options.backend,
270
+ executionPlan: options.executionPlan,
271
+ indexesUsed: options.indexesUsed || [],
272
+ filtersApplied: options.filtersApplied || [],
273
+ rowsRead: options.rowsRead,
274
+ scannedFiles: options.scannedFiles,
275
+ freshness: options.freshness,
276
+ warnings: options.warnings || []
277
+ };
278
+ }
126
279
  function explainIntegrity(artifact, integrity) {
127
- const steps = [];
280
+ const causalChain = [];
281
+ const evidence = [];
128
282
  let order = 1;
129
- steps.push({
283
+ if (artifact.contentHash) {
284
+ evidence.push({ type: "contentHash", value: artifact.contentHash });
285
+ }
286
+ evidence.push({ type: "filePath", value: artifact.filePath });
287
+ causalChain.push({
130
288
  order: order++,
131
289
  assertion: integrity.schemaValid ? `Schema "${artifact.schema}" is a recognized HardKAS artifact schema` : `Schema "${artifact.schema}" is not recognized`,
132
290
  evidence: `artifact.schema = "${artifact.schema}"`,
133
291
  rule: "ARTIFACT_SCHEMAS constant (artifacts/constants.ts)"
134
292
  });
135
293
  if (artifact.contentHash) {
136
- steps.push({
294
+ causalChain.push({
137
295
  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}"`,
296
+ assertion: integrity.hashMatch ? "Content hash matches recomputed hash" : "Content hash does NOT match recomputed hash",
297
+ evidence: `hash(payload) === "${artifact.contentHash}"`,
140
298
  rule: "canonicalStringify + SHA-256 (artifacts/canonical.ts)"
141
299
  });
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
300
  }
150
301
  if (integrity.errors.length > 0) {
151
302
  for (const err of integrity.errors) {
152
- steps.push({
303
+ causalChain.push({
153
304
  order: order++,
154
- assertion: `Integrity issue: ${err}`,
155
- evidence: err,
156
- rule: "verifyArtifactIntegrity (artifacts/verify.ts)"
305
+ assertion: `Semantic violation detected: ${err}`,
306
+ evidence: "Verification engine failure",
307
+ rule: "verifyArtifactSemantics (artifacts/verify.ts)"
157
308
  });
158
309
  }
159
310
  }
160
311
  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] : []
312
+ question: `Why is artifact "${artifact.schema}" ${integrity.ok ? "valid" : "invalid"}?`,
313
+ answer: integrity.ok ? "All deterministic checks (schema, hash, semantics) passed successfully." : `Verification failed: ${integrity.errors.join("; ")}`,
314
+ evidence,
315
+ causalChain,
316
+ model: "integrity-verifier",
317
+ confidence: "definitive"
167
318
  };
168
319
  }
169
320
  function explainTransition(transition) {
170
- const steps = [];
321
+ const causalChain = [];
322
+ const evidence = [
323
+ { type: "contentHash", value: transition.from.contentHash },
324
+ { type: "contentHash", value: transition.to.contentHash }
325
+ ];
171
326
  let order = 1;
172
327
  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({
328
+ causalChain.push({
180
329
  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)"
330
+ assertion: transition.valid ? `Transition "${transition.from.schema}" \u2192 "${transition.to.schema}" is allowed` : `Transition "${transition.from.schema}" \u2192 "${transition.to.schema}" is NOT allowed`,
331
+ evidence: `allowed_from_${transition.from.schema} = [${allowed.join(", ")}]`,
332
+ rule: "Lineage transition table"
184
333
  });
185
- const networkMatch = transition.from.networkId === transition.to.networkId;
186
- steps.push({
334
+ const contextMatch = transition.from.networkId === transition.to.networkId && transition.from.mode === transition.to.mode;
335
+ causalChain.push({
187
336
  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)"
337
+ assertion: contextMatch ? "Execution context (network, mode) is consistent" : "EXECUTION CONTEXT MISMATCH detected",
338
+ evidence: `from: ${transition.from.networkId}/${transition.from.mode}, to: ${transition.to.networkId}/${transition.to.mode}`,
339
+ rule: "Context isolation policy"
191
340
  });
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
341
  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]
342
+ question: `Why transition ${transition.from.schema} \u2192 ${transition.to.schema}?`,
343
+ answer: transition.valid && contextMatch ? "Causal chain is consistent with HardKAS state transition rules." : "Workflow violation: invalid schema transition or context contamination.",
344
+ evidence,
345
+ causalChain,
346
+ model: "causal-lineage",
347
+ confidence: "definitive"
214
348
  };
215
349
  }
216
350
  function explainOrphan(node, missingParentId) {
217
351
  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: [
352
+ question: `Why is artifact "${node.artifactId.slice(0, 8)}" an orphan?`,
353
+ answer: "The parent artifact referenced in the lineage metadata is missing from the indexed store.",
354
+ evidence: [
355
+ { type: "artifactId", value: node.artifactId },
356
+ { type: "artifactId", value: missingParentId }
357
+ ],
358
+ causalChain: [
221
359
  {
222
360
  order: 1,
223
- assertion: `Artifact declares parentArtifactId = "${missingParentId}"`,
224
- evidence: `artifact.lineage.parentArtifactId = "${missingParentId}"`,
225
- rule: "Lineage parent resolution"
361
+ assertion: "Artifact defines a parent dependency",
362
+ evidence: `parentArtifactId = "${missingParentId}"`,
363
+ rule: "Lineage metadata requirement"
226
364
  },
227
365
  {
228
366
  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"
367
+ assertion: "Parent artifact lookup failed",
368
+ evidence: "Index scan for artifactId returned 0 results",
369
+ rule: "Store integrity policy"
238
370
  }
239
371
  ],
240
- model: "lineage-rules",
241
- confidence: "definitive",
242
- references: [node.contentHash, missingParentId]
372
+ model: "orphan-analysis",
373
+ confidence: "definitive"
243
374
  };
244
375
  }
245
- function formatExplainBrief(chain) {
246
- return `${chain.conclusion} [model: ${chain.model}, confidence: ${chain.confidence}]`;
376
+ function formatExplainBlock(block) {
377
+ const lines = [];
378
+ lines.push(` [Explain: Technical Diagnostics]`);
379
+ lines.push(` Backend: ${block.backend}`);
380
+ lines.push(` Freshness: ${block.freshness}`);
381
+ lines.push(` Rows Read: ${block.rowsRead}`);
382
+ lines.push(` Files Scan: ${block.scannedFiles}`);
383
+ if (block.executionPlan.length > 0) {
384
+ lines.push(` Plan: ${block.executionPlan.join(" \u2192 ")}`);
385
+ }
386
+ if (block.warnings.length > 0) {
387
+ lines.push(` Warnings:`);
388
+ for (const w of block.warnings) lines.push(` \u26A0 ${w}`);
389
+ }
390
+ return lines.join("\n");
247
391
  }
248
- function formatExplainFull(chain) {
392
+ function formatWhyBlock(block) {
249
393
  const lines = [];
250
- lines.push(`Q: ${chain.question}`);
394
+ lines.push(` [Why: Causal Analysis]`);
395
+ lines.push(` Q: ${block.question}`);
396
+ lines.push(` A: ${block.answer}`);
251
397
  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
- }
398
+ for (const step of block.causalChain) {
399
+ lines.push(` ${step.order}. ${step.assertion}`);
400
+ lines.push(` Evidence: ${step.evidence}`);
401
+ if (step.rule) lines.push(` Rule: ${step.rule}`);
258
402
  }
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(", ")}`);
403
+ if (block.evidence.length > 0) {
404
+ lines.push("");
405
+ lines.push(` Evidence Refs: ${block.evidence.map((e) => `${e.type}:${e.value.slice(0, 12)}...`).join(", ")}`);
264
406
  }
265
407
  return lines.join("\n");
266
408
  }
@@ -270,8 +412,10 @@ var KNOWN_SCHEMAS = new Set(Object.values(ARTIFACT_SCHEMAS));
270
412
  var ArtifactQueryAdapter = class {
271
413
  domain = "artifacts";
272
414
  rootDir;
273
- constructor(rootDir) {
415
+ backend;
416
+ constructor(rootDir, backend) {
274
417
  this.rootDir = rootDir;
418
+ this.backend = backend;
275
419
  }
276
420
  supportedOps() {
277
421
  return ["list", "inspect", "diff", "verify"];
@@ -298,12 +442,25 @@ var ArtifactQueryAdapter = class {
298
442
  // -------------------------------------------------------------------------
299
443
  async executeList(request) {
300
444
  const start = Date.now();
301
- const files = await this.scanArtifactFiles();
445
+ const docs = await this.backend.findArtifacts();
302
446
  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);
447
+ for (const doc of docs) {
448
+ const item = {
449
+ filePath: doc.path,
450
+ schema: doc.schema,
451
+ version: doc.version,
452
+ networkId: doc.networkId,
453
+ mode: doc.mode,
454
+ createdAt: doc.createdAt,
455
+ contentHash: doc.contentHash,
456
+ payload: doc.payload,
457
+ // Optional mapping for common fields
458
+ status: doc.payload.status,
459
+ from: doc.payload.from,
460
+ to: doc.payload.to,
461
+ amountSompi: doc.payload.amountSompi,
462
+ lineage: doc.payload.lineage
463
+ };
307
464
  if (evaluateFilters(item, request.filters)) {
308
465
  items.push(item);
309
466
  }
@@ -311,9 +468,9 @@ var ArtifactQueryAdapter = class {
311
468
  const sorted = this.sortItems(items, request.sort);
312
469
  const total = sorted.length;
313
470
  const paged = sorted.slice(request.offset, request.offset + request.limit);
314
- let explain;
471
+ let why;
315
472
  if (request.explain) {
316
- explain = paged.map((item) => explainIntegrity(item, {
473
+ why = paged.map((item) => explainIntegrity(item, {
317
474
  ok: true,
318
475
  hashMatch: true,
319
476
  schemaValid: KNOWN_SCHEMAS.has(item.schema),
@@ -328,11 +485,11 @@ var ArtifactQueryAdapter = class {
328
485
  truncated: total > request.offset + request.limit,
329
486
  deterministic: true,
330
487
  queryHash: computeQueryHash(paged),
331
- explain,
488
+ why,
332
489
  annotations: {
333
490
  executedAt: (/* @__PURE__ */ new Date()).toISOString(),
334
491
  executionMs: Date.now() - start,
335
- filesScanned: files.length
492
+ filesScanned: docs.length
336
493
  }
337
494
  };
338
495
  }
@@ -383,9 +540,9 @@ var ArtifactQueryAdapter = class {
383
540
  staleness,
384
541
  lineageStatus
385
542
  };
386
- let explain;
543
+ let why;
387
544
  if (request.explain) {
388
- explain = [explainIntegrity(item, inspectResult.integrity)];
545
+ why = [explainIntegrity(item, inspectResult.integrity)];
389
546
  }
390
547
  return {
391
548
  domain: "artifacts",
@@ -395,7 +552,7 @@ var ArtifactQueryAdapter = class {
395
552
  truncated: false,
396
553
  deterministic: true,
397
554
  queryHash: computeQueryHash([inspectResult]),
398
- explain,
555
+ why,
399
556
  annotations: {
400
557
  executedAt: (/* @__PURE__ */ new Date()).toISOString(),
401
558
  executionMs: Date.now() - start,
@@ -472,12 +629,12 @@ var ArtifactQueryAdapter = class {
472
629
  async walkDir(dir, out) {
473
630
  let entries;
474
631
  try {
475
- entries = await fs.readdir(dir, { withFileTypes: true });
632
+ entries = await fs2.readdir(dir, { withFileTypes: true });
476
633
  } catch {
477
634
  return;
478
635
  }
479
636
  for (const entry of entries) {
480
- const full = path.join(dir, entry.name);
637
+ const full = path2.join(dir, entry.name);
481
638
  if (entry.isDirectory()) {
482
639
  if (entry.name === "node_modules" || entry.name === ".git" || entry.name === "keystores") continue;
483
640
  await this.walkDir(full, out);
@@ -488,7 +645,7 @@ var ArtifactQueryAdapter = class {
488
645
  }
489
646
  async readJsonSafe(filePath) {
490
647
  try {
491
- const content = await fs.readFile(filePath, "utf-8");
648
+ const content = await fs2.readFile(filePath, "utf-8");
492
649
  return JSON.parse(content);
493
650
  } catch {
494
651
  return null;
@@ -496,7 +653,7 @@ var ArtifactQueryAdapter = class {
496
653
  }
497
654
  async resolveTarget(target) {
498
655
  if (target.includes("/") || target.includes("\\") || target.endsWith(".json")) {
499
- return path.resolve(target);
656
+ return path2.resolve(target);
500
657
  }
501
658
  const files = await this.scanArtifactFiles();
502
659
  for (const f of files) {
@@ -517,7 +674,7 @@ var ArtifactQueryAdapter = class {
517
674
  });
518
675
  } else {
519
676
  sorted.sort((a, b) => {
520
- const cmp = b.createdAt.localeCompare(a.createdAt);
677
+ const cmp = (b.createdAt ?? "").localeCompare(a.createdAt ?? "");
521
678
  return cmp !== 0 ? cmp : a.schema.localeCompare(b.schema);
522
679
  });
523
680
  }
@@ -533,6 +690,7 @@ function toArtifactQueryItem(raw, filePath) {
533
690
  mode: raw.mode || "unknown",
534
691
  createdAt: raw.createdAt || "",
535
692
  contentHash: raw.contentHash,
693
+ payload: raw,
536
694
  from: raw.from,
537
695
  to: raw.to,
538
696
  amountSompi: raw.amountSompi,
@@ -554,8 +712,8 @@ function classifyStaleness(hours) {
554
712
  }
555
713
 
556
714
  // src/adapters/lineage-adapter.ts
557
- import fs2 from "fs/promises";
558
- import path2 from "path";
715
+ import fs3 from "fs/promises";
716
+ import path3 from "path";
559
717
  var VALID_TRANSITIONS2 = {
560
718
  "hardkas.snapshot": ["hardkas.txPlan"],
561
719
  "hardkas.txPlan": ["hardkas.signedTx"],
@@ -564,8 +722,10 @@ var VALID_TRANSITIONS2 = {
564
722
  var LineageQueryAdapter = class {
565
723
  domain = "lineage";
566
724
  rootDir;
567
- constructor(rootDir) {
725
+ backend;
726
+ constructor(rootDir, backend) {
568
727
  this.rootDir = rootDir;
728
+ this.backend = backend;
569
729
  }
570
730
  supportedOps() {
571
731
  return ["chain", "transitions", "orphans"];
@@ -611,9 +771,9 @@ var LineageQueryAdapter = class {
611
771
  transitions,
612
772
  complete
613
773
  };
614
- let explain;
774
+ let why;
615
775
  if (request.explain) {
616
- explain = transitions.map((t) => explainTransition(t));
776
+ why = transitions.map((t) => explainTransition(t));
617
777
  }
618
778
  return {
619
779
  domain: "lineage",
@@ -623,7 +783,7 @@ var LineageQueryAdapter = class {
623
783
  truncated: false,
624
784
  deterministic: true,
625
785
  queryHash: computeQueryHash([result]),
626
- explain,
786
+ why,
627
787
  annotations: {
628
788
  executedAt: (/* @__PURE__ */ new Date()).toISOString(),
629
789
  executionMs: Date.now() - start,
@@ -660,9 +820,9 @@ var LineageQueryAdapter = class {
660
820
  return a.from.contentHash.localeCompare(b.from.contentHash);
661
821
  });
662
822
  const paged = transitions.slice(request.offset, request.offset + request.limit);
663
- let explain;
823
+ let why;
664
824
  if (request.explain) {
665
- explain = paged.map((t) => explainTransition(t));
825
+ why = paged.map((t) => explainTransition(t));
666
826
  }
667
827
  return {
668
828
  domain: "lineage",
@@ -672,7 +832,7 @@ var LineageQueryAdapter = class {
672
832
  truncated: transitions.length > request.offset + request.limit,
673
833
  deterministic: true,
674
834
  queryHash: computeQueryHash(paged),
675
- explain,
835
+ why,
676
836
  annotations: {
677
837
  executedAt: (/* @__PURE__ */ new Date()).toISOString(),
678
838
  executionMs: Date.now() - start,
@@ -700,9 +860,9 @@ var LineageQueryAdapter = class {
700
860
  }
701
861
  orphans.sort((a, b) => a.node.contentHash.localeCompare(b.node.contentHash));
702
862
  const paged = orphans.slice(request.offset, request.offset + request.limit);
703
- let explain;
863
+ let why;
704
864
  if (request.explain) {
705
- explain = paged.map((o) => explainOrphan(o.node, o.missingParentId));
865
+ why = paged.map((o) => explainOrphan(o.node, o.missingParentId));
706
866
  }
707
867
  return {
708
868
  domain: "lineage",
@@ -712,7 +872,7 @@ var LineageQueryAdapter = class {
712
872
  truncated: orphans.length > request.offset + request.limit,
713
873
  deterministic: true,
714
874
  queryHash: computeQueryHash(paged),
715
- explain,
875
+ why,
716
876
  annotations: {
717
877
  executedAt: (/* @__PURE__ */ new Date()).toISOString(),
718
878
  executionMs: Date.now() - start,
@@ -728,22 +888,22 @@ var LineageQueryAdapter = class {
728
888
  const byArtifactId = /* @__PURE__ */ new Map();
729
889
  const byContentHash = /* @__PURE__ */ new Map();
730
890
  const children = /* @__PURE__ */ new Map();
731
- const files = await this.scanJsonFiles();
732
- for (const filePath of files) {
733
- const raw = await this.readJsonSafe(filePath);
891
+ const docs = await this.backend.findArtifacts();
892
+ for (const doc of docs) {
893
+ const raw = doc.payload;
734
894
  if (!raw?.schema || !raw.lineage) continue;
735
895
  const node = {
736
- contentHash: raw.contentHash || "",
737
- schema: raw.schema,
896
+ contentHash: doc.contentHash,
897
+ schema: doc.schema,
738
898
  artifactId: raw.lineage.artifactId || "",
739
899
  parentArtifactId: raw.lineage.parentArtifactId,
740
900
  rootArtifactId: raw.lineage.rootArtifactId || "",
741
901
  lineageId: raw.lineage.lineageId || "",
742
902
  sequence: raw.lineage.sequence,
743
- filePath,
744
- networkId: raw.networkId || "unknown",
745
- mode: raw.mode || "unknown",
746
- createdAt: raw.createdAt || ""
903
+ filePath: doc.path,
904
+ networkId: doc.networkId,
905
+ mode: doc.mode,
906
+ createdAt: doc.createdAt || ""
747
907
  };
748
908
  nodes.push(node);
749
909
  if (node.artifactId) byArtifactId.set(node.artifactId, node);
@@ -754,7 +914,7 @@ var LineageQueryAdapter = class {
754
914
  children.set(node.parentArtifactId, existing);
755
915
  }
756
916
  }
757
- return { nodes, byArtifactId, byContentHash, children, totalFiles: files.length };
917
+ return { nodes, byArtifactId, byContentHash, children, totalFiles: docs.length };
758
918
  }
759
919
  // -------------------------------------------------------------------------
760
920
  // Graph Traversal
@@ -812,12 +972,12 @@ var LineageQueryAdapter = class {
812
972
  async walkDir(dir, out) {
813
973
  let entries;
814
974
  try {
815
- entries = await fs2.readdir(dir, { withFileTypes: true });
975
+ entries = await fs3.readdir(dir, { withFileTypes: true });
816
976
  } catch {
817
977
  return;
818
978
  }
819
979
  for (const entry of entries) {
820
- const full = path2.join(dir, entry.name);
980
+ const full = path3.join(dir, entry.name);
821
981
  if (entry.isDirectory()) {
822
982
  if (entry.name === "node_modules" || entry.name === ".git" || entry.name === "keystores") continue;
823
983
  await this.walkDir(full, out);
@@ -828,7 +988,7 @@ var LineageQueryAdapter = class {
828
988
  }
829
989
  async readJsonSafe(filePath) {
830
990
  try {
831
- const content = await fs2.readFile(filePath, "utf-8");
991
+ const content = await fs3.readFile(filePath, "utf-8");
832
992
  return JSON.parse(content);
833
993
  } catch {
834
994
  return null;
@@ -837,14 +997,15 @@ var LineageQueryAdapter = class {
837
997
  };
838
998
 
839
999
  // src/adapters/replay-adapter.ts
840
- import fs3 from "fs/promises";
841
- import path3 from "path";
1000
+ import fs4 from "fs/promises";
842
1001
  import { calculateContentHash as calculateContentHash2 } from "@hardkas/artifacts";
843
1002
  var ReplayQueryAdapter = class {
844
1003
  domain = "replay";
845
1004
  rootDir;
846
- constructor(rootDir) {
1005
+ backend;
1006
+ constructor(rootDir, backend) {
847
1007
  this.rootDir = rootDir;
1008
+ this.backend = backend;
848
1009
  }
849
1010
  supportedOps() {
850
1011
  return ["list", "summary", "divergences", "invariants"];
@@ -871,13 +1032,15 @@ var ReplayQueryAdapter = class {
871
1032
  // -------------------------------------------------------------------------
872
1033
  async executeList(request) {
873
1034
  const start = Date.now();
874
- const receipts = await this.loadAllReceipts();
875
- const traces = await this.loadAllTraces();
1035
+ const receipts = await this.backend.findReceipts({
1036
+ status: request.filters.find((f) => f.field === "status")?.value
1037
+ });
1038
+ const traces = await this.backend.findTraces();
876
1039
  const traceMap = new Map(traces.map((t) => [t.txId, t]));
877
1040
  const items = [];
878
1041
  for (const r of receipts) {
879
1042
  const trace = traceMap.get(r.txId);
880
- const item = toSummary(r, trace);
1043
+ const item = toSummary(r.payload, trace?.payload);
881
1044
  if (evaluateFilters(item, request.filters)) {
882
1045
  items.push(item);
883
1046
  }
@@ -910,10 +1073,11 @@ var ReplayQueryAdapter = class {
910
1073
  const start = Date.now();
911
1074
  const txId = request.params["txId"];
912
1075
  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);
1076
+ const receipt = await this.backend.getArtifact(txId);
1077
+ if (!receipt || receipt.schema !== "hardkas.txReceipt") throw new Error(`Receipt not found for txId: ${txId}`);
1078
+ const traces = await this.backend.findTraces({ txId });
1079
+ const trace = traces[0];
1080
+ const item = toSummary(receipt.payload, trace?.payload);
917
1081
  return {
918
1082
  domain: "replay",
919
1083
  op: "summary",
@@ -934,9 +1098,10 @@ var ReplayQueryAdapter = class {
934
1098
  // -------------------------------------------------------------------------
935
1099
  async executeDivergences(request) {
936
1100
  const start = Date.now();
937
- const receipts = await this.loadAllReceipts();
1101
+ const receipts = await this.backend.findReceipts();
938
1102
  const divergences = [];
939
- for (const receipt of receipts) {
1103
+ for (const doc of receipts) {
1104
+ const receipt = doc.payload;
940
1105
  if (receipt.contentHash) {
941
1106
  const recomputed = computeContentHashSafe(receipt);
942
1107
  if (recomputed !== receipt.contentHash) {
@@ -994,9 +1159,9 @@ var ReplayQueryAdapter = class {
994
1159
  }
995
1160
  divergences.sort((a, b) => a.txId.localeCompare(b.txId));
996
1161
  const paged = divergences.slice(request.offset, request.offset + request.limit);
997
- let explain;
1162
+ let why;
998
1163
  if (request.explain) {
999
- explain = paged.map((d) => explainDivergence(d));
1164
+ why = paged.map((d) => explainDivergence(d));
1000
1165
  }
1001
1166
  return {
1002
1167
  domain: "replay",
@@ -1006,7 +1171,7 @@ var ReplayQueryAdapter = class {
1006
1171
  truncated: divergences.length > request.offset + request.limit,
1007
1172
  deterministic: true,
1008
1173
  queryHash: computeQueryHash(paged),
1009
- explain,
1174
+ why,
1010
1175
  annotations: {
1011
1176
  executedAt: (/* @__PURE__ */ new Date()).toISOString(),
1012
1177
  executionMs: Date.now() - start,
@@ -1021,8 +1186,9 @@ var ReplayQueryAdapter = class {
1021
1186
  const start = Date.now();
1022
1187
  const txId = request.params["txId"];
1023
1188
  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}`);
1189
+ const doc = await this.backend.getArtifact(txId);
1190
+ if (!doc || doc.schema !== "hardkas.txReceipt") throw new Error(`Receipt not found for txId: ${txId}`);
1191
+ const receipt = doc.payload;
1026
1192
  const issues = [];
1027
1193
  const planIntegrity = receipt.contentHash ? computeContentHashSafe(receipt) === receipt.contentHash : true;
1028
1194
  if (!planIntegrity) {
@@ -1065,9 +1231,9 @@ var ReplayQueryAdapter = class {
1065
1231
  utxoConservation,
1066
1232
  issues
1067
1233
  };
1068
- let explain;
1234
+ let why;
1069
1235
  if (request.explain) {
1070
- explain = [explainInvariants(result)];
1236
+ why = [explainInvariants(result)];
1071
1237
  }
1072
1238
  return {
1073
1239
  domain: "replay",
@@ -1077,7 +1243,7 @@ var ReplayQueryAdapter = class {
1077
1243
  truncated: false,
1078
1244
  deterministic: true,
1079
1245
  queryHash: computeQueryHash([result]),
1080
- explain,
1246
+ why,
1081
1247
  annotations: {
1082
1248
  executedAt: (/* @__PURE__ */ new Date()).toISOString(),
1083
1249
  executionMs: Date.now() - start,
@@ -1085,43 +1251,9 @@ var ReplayQueryAdapter = class {
1085
1251
  }
1086
1252
  };
1087
1253
  }
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
1254
  async readJsonSafe(filePath) {
1123
1255
  try {
1124
- const content = await fs3.readFile(filePath, "utf-8");
1256
+ const content = await fs4.readFile(filePath, "utf-8");
1125
1257
  return JSON.parse(content);
1126
1258
  } catch {
1127
1259
  return null;
@@ -1164,43 +1296,55 @@ var DIVERGENCE_RULES = {
1164
1296
  };
1165
1297
  function explainDivergence(d) {
1166
1298
  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)" }
1299
+ question: `Why is tx ${d.txId.slice(0, 16)}... divergent?`,
1300
+ answer: `Field "${d.field}" shows unexpected non-deterministic behavior (${d.kind}).`,
1301
+ evidence: [{ type: "txId", value: d.txId }],
1302
+ causalChain: [
1303
+ {
1304
+ order: 1,
1305
+ assertion: `Value mismatch in "${d.field}"`,
1306
+ evidence: `Expected: ${d.expected.slice(0, 40)}, Actual: ${d.actual.slice(0, 40)}`,
1307
+ rule: DIVERGENCE_RULES[d.kind]
1308
+ },
1309
+ {
1310
+ order: 2,
1311
+ assertion: "Divergence detected in replay comparison",
1312
+ evidence: "Verification engine mismatch",
1313
+ rule: "Invariant validation policy"
1314
+ }
1172
1315
  ],
1173
- model: "replay-invariants",
1174
- confidence: "definitive",
1175
- references: [d.txId]
1316
+ model: "replay-analysis",
1317
+ confidence: "definitive"
1176
1318
  };
1177
1319
  }
1178
1320
  function explainInvariants(result) {
1179
- const steps = [];
1321
+ const causalChain = [];
1180
1322
  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" });
1323
+ causalChain.push({ order: order++, assertion: result.planIntegrity ? "Plan integrity is OK" : "Plan integrity FAILED", evidence: `planIntegrity=${result.planIntegrity}`, rule: "SHA-256 canonical consistency" });
1324
+ causalChain.push({ order: order++, assertion: result.receiptReproducible ? "Receipt is reproducible" : "Receipt is NOT reproducible", evidence: `receiptReproducible=${result.receiptReproducible}`, rule: "Replay evidence requirements" });
1325
+ causalChain.push({ order: order++, assertion: result.stateTransitionValid ? "State transition is valid" : "State transition INVALID", evidence: `stateTransitionValid=${result.stateTransitionValid}`, rule: "Status/State alignment" });
1326
+ causalChain.push({ order: order++, assertion: result.utxoConservation ? "UTXO conservation holds" : "UTXO conservation VIOLATED", evidence: `utxoConservation=${result.utxoConservation}`, rule: "Value conservation policy" });
1185
1327
  const allOk = result.planIntegrity && result.receiptReproducible && result.stateTransitionValid && result.utxoConservation;
1186
1328
  return {
1187
1329
  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,
1330
+ answer: allOk ? "All replay invariants satisfied." : `Violations found: ${result.issues.join("; ")}`,
1331
+ evidence: [{ type: "txId", value: result.txId }],
1332
+ causalChain,
1190
1333
  model: "replay-invariants",
1191
- confidence: "definitive",
1192
- references: [result.txId]
1334
+ confidence: "definitive"
1193
1335
  };
1194
1336
  }
1195
1337
 
1196
1338
  // src/adapters/dag-adapter.ts
1197
- import fs4 from "fs/promises";
1339
+ import fs5 from "fs/promises";
1198
1340
  import path4 from "path";
1199
1341
  var DagQueryAdapter = class {
1200
1342
  domain = "dag";
1201
1343
  rootDir;
1202
- constructor(rootDir) {
1344
+ backend;
1345
+ constructor(rootDir, backend) {
1203
1346
  this.rootDir = rootDir;
1347
+ this.backend = backend;
1204
1348
  }
1205
1349
  supportedOps() {
1206
1350
  return ["conflicts", "displaced", "history", "sink-path", "anomalies"];
@@ -1240,9 +1384,9 @@ var DagQueryAdapter = class {
1240
1384
  }));
1241
1385
  items.sort((a, b) => a.outpoint.localeCompare(b.outpoint));
1242
1386
  const paged = items.slice(request.offset, request.offset + request.limit);
1243
- let explain;
1387
+ let why;
1244
1388
  if (request.explain) {
1245
- explain = paged.map((c) => explainConflict(c, dag));
1389
+ why = paged.map((c) => explainConflict(c, dag));
1246
1390
  }
1247
1391
  return {
1248
1392
  domain: "dag",
@@ -1252,7 +1396,7 @@ var DagQueryAdapter = class {
1252
1396
  truncated: items.length > request.offset + request.limit,
1253
1397
  deterministic: true,
1254
1398
  queryHash: computeQueryHash(paged),
1255
- explain,
1399
+ why,
1256
1400
  annotations: { executedAt: (/* @__PURE__ */ new Date()).toISOString(), executionMs: Date.now() - start }
1257
1401
  };
1258
1402
  }
@@ -1276,9 +1420,9 @@ var DagQueryAdapter = class {
1276
1420
  });
1277
1421
  items.sort((a, b) => a.txId.localeCompare(b.txId));
1278
1422
  const paged = items.slice(request.offset, request.offset + request.limit);
1279
- let explain;
1423
+ let why;
1280
1424
  if (request.explain) {
1281
- explain = paged.map((d) => explainDisplacement(d, dag));
1425
+ why = paged.map((d) => explainDisplacement(d, dag));
1282
1426
  }
1283
1427
  return {
1284
1428
  domain: "dag",
@@ -1288,7 +1432,7 @@ var DagQueryAdapter = class {
1288
1432
  truncated: items.length > request.offset + request.limit,
1289
1433
  deterministic: true,
1290
1434
  queryHash: computeQueryHash(paged),
1291
- explain,
1435
+ why,
1292
1436
  annotations: { executedAt: (/* @__PURE__ */ new Date()).toISOString(), executionMs: Date.now() - start }
1293
1437
  };
1294
1438
  }
@@ -1328,9 +1472,9 @@ var DagQueryAdapter = class {
1328
1472
  }
1329
1473
  }
1330
1474
  entries.sort((a, b) => a.daaScore.localeCompare(b.daaScore));
1331
- let explain;
1475
+ let why;
1332
1476
  if (request.explain) {
1333
- explain = entries.map((e) => explainTxHistory(e, dag));
1477
+ why = entries.map((e) => explainTxHistory(e, dag));
1334
1478
  }
1335
1479
  return {
1336
1480
  domain: "dag",
@@ -1340,7 +1484,7 @@ var DagQueryAdapter = class {
1340
1484
  truncated: false,
1341
1485
  deterministic: true,
1342
1486
  queryHash: computeQueryHash(entries),
1343
- explain,
1487
+ why,
1344
1488
  annotations: { executedAt: (/* @__PURE__ */ new Date()).toISOString(), executionMs: Date.now() - start }
1345
1489
  };
1346
1490
  }
@@ -1428,9 +1572,9 @@ var DagQueryAdapter = class {
1428
1572
  }
1429
1573
  anomalies.sort((a, b) => a.kind.localeCompare(b.kind));
1430
1574
  const paged = anomalies.slice(request.offset, request.offset + request.limit);
1431
- let explain;
1575
+ let why;
1432
1576
  if (request.explain) {
1433
- explain = paged.map((a) => explainAnomaly(a, dag));
1577
+ why = paged.map((a) => explainAnomaly(a, dag));
1434
1578
  }
1435
1579
  return {
1436
1580
  domain: "dag",
@@ -1440,7 +1584,7 @@ var DagQueryAdapter = class {
1440
1584
  truncated: anomalies.length > request.offset + request.limit,
1441
1585
  deterministic: true,
1442
1586
  queryHash: computeQueryHash(paged),
1443
- explain,
1587
+ why,
1444
1588
  annotations: { executedAt: (/* @__PURE__ */ new Date()).toISOString(), executionMs: Date.now() - start }
1445
1589
  };
1446
1590
  }
@@ -1450,7 +1594,7 @@ var DagQueryAdapter = class {
1450
1594
  async loadDag() {
1451
1595
  const statePath = path4.join(this.rootDir, ".hardkas", "state.json");
1452
1596
  try {
1453
- const content = await fs4.readFile(statePath, "utf-8");
1597
+ const content = await fs5.readFile(statePath, "utf-8");
1454
1598
  const state = JSON.parse(content);
1455
1599
  return state.dag ?? null;
1456
1600
  } catch {
@@ -1471,7 +1615,7 @@ function emptyDagResult(op, start) {
1471
1615
  };
1472
1616
  }
1473
1617
  function explainConflict(conflict, dag) {
1474
- const steps = [];
1618
+ const causalChain = [];
1475
1619
  let order = 1;
1476
1620
  let winnerBlockId;
1477
1621
  let winnerDaaScore = "0";
@@ -1484,92 +1628,87 @@ function explainConflict(conflict, dag) {
1484
1628
  break;
1485
1629
  }
1486
1630
  }
1487
- steps.push({
1631
+ causalChain.push({
1488
1632
  order: order++,
1489
- assertion: `Outpoint ${conflict.outpoint} was spent by multiple transactions`,
1633
+ assertion: `Outpoint ${conflict.outpoint} spent by multiple txs`,
1490
1634
  evidence: `Winner: ${conflict.winnerTxId}, Losers: ${conflict.loserTxIds.join(", ")}`,
1491
- rule: "UTXO double-spend detection (dag.ts:moveSink)"
1635
+ rule: "UTXO double-spend detection"
1492
1636
  });
1493
1637
  if (winnerBlockId) {
1494
- steps.push({
1638
+ causalChain.push({
1495
1639
  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({
1501
- 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)"
1640
+ assertion: `Winner in block ${winnerBlockId} (daaScore: ${winnerDaaScore})`,
1641
+ evidence: `In sink path: ${winnerInSinkPath}`,
1642
+ rule: "Sink-ancestry priority"
1505
1643
  });
1506
1644
  }
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
1645
  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,
1646
+ question: `Why winner ${conflict.winnerTxId.slice(0, 8)} on outpoint ${conflict.outpoint}?`,
1647
+ answer: `Winner has ${winnerInSinkPath ? "sink-path priority" : "block ordering priority"}.`,
1648
+ evidence: [
1649
+ { type: "txId", value: conflict.winnerTxId },
1650
+ ...conflict.loserTxIds.map((id) => ({ type: "txId", value: id }))
1651
+ ],
1652
+ causalChain,
1517
1653
  model: "deterministic-light-model",
1518
- confidence: "definitive",
1519
- references: [conflict.winnerTxId, ...conflict.loserTxIds]
1654
+ confidence: "definitive"
1520
1655
  };
1521
1656
  }
1522
1657
  function explainDisplacement(d, _dag) {
1523
1658
  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" }
1659
+ question: `Why displaced tx ${d.txId.slice(0, 8)}?`,
1660
+ answer: d.reason,
1661
+ evidence: [{ type: "txId", value: d.txId }],
1662
+ causalChain: [
1663
+ { order: 1, assertion: "Tx in displaced set", evidence: "dag.displacedTxIds includes txId", rule: "DAG reorganization" },
1664
+ { order: 2, assertion: "Status: displaced", evidence: d.reason }
1529
1665
  ],
1530
1666
  model: "deterministic-light-model",
1531
- confidence: "definitive",
1532
- references: [d.txId]
1667
+ confidence: "definitive"
1533
1668
  };
1534
1669
  }
1535
1670
  function explainTxHistory(entry, _dag) {
1536
1671
  const status = entry.accepted ? "ACCEPTED" : entry.displaced ? "DISPLACED" : "UNKNOWN";
1537
1672
  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" }
1673
+ question: `Causal history of tx ${entry.txId.slice(0, 8)}?`,
1674
+ answer: `Status is ${status} in block ${entry.blockId}.`,
1675
+ evidence: [
1676
+ { type: "txId", value: entry.txId },
1677
+ { type: "blockId", value: entry.blockId }
1678
+ ],
1679
+ causalChain: [
1680
+ { order: 1, assertion: `In block ${entry.blockId}`, evidence: `daaScore=${entry.daaScore}` },
1681
+ { order: 2, assertion: entry.inSinkPath ? "In selected sink path" : "Not in sink path", evidence: `selectedPathToSink.includes("${entry.blockId}")` }
1545
1682
  ],
1546
1683
  model: "deterministic-light-model",
1547
- confidence: "definitive",
1548
- references: [entry.txId]
1684
+ confidence: "definitive"
1549
1685
  };
1550
1686
  }
1551
1687
  function explainAnomaly(a, _dag) {
1688
+ const evidence = [];
1689
+ if (a.txId) evidence.push({ type: "txId", value: a.txId });
1690
+ if (a.blockId) evidence.push({ type: "blockId", value: a.blockId });
1552
1691
  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)" }
1692
+ question: `Why anomaly: ${a.kind}?`,
1693
+ answer: a.description,
1694
+ evidence,
1695
+ causalChain: [
1696
+ { order: 1, assertion: a.description, evidence: `Anomaly: ${a.kind}`, rule: "DAG invariant check" }
1557
1697
  ],
1558
1698
  model: "deterministic-light-model",
1559
- confidence: "definitive",
1560
- references: [a.txId ?? a.blockId ?? ""]
1699
+ confidence: "definitive"
1561
1700
  };
1562
1701
  }
1563
1702
 
1564
1703
  // src/adapters/events-adapter.ts
1565
- import fs5 from "fs/promises";
1566
1704
  import path5 from "path";
1567
- import { validateEventEnvelope } from "@hardkas/core";
1568
1705
  var EventsQueryAdapter = class {
1569
1706
  domain = "events";
1570
1707
  rootDir;
1571
- constructor(rootDir) {
1708
+ backend;
1709
+ constructor(rootDir, backend) {
1572
1710
  this.rootDir = rootDir;
1711
+ this.backend = backend;
1573
1712
  }
1574
1713
  supportedOps() {
1575
1714
  return ["list", "summary"];
@@ -1588,7 +1727,7 @@ var EventsQueryAdapter = class {
1588
1727
  async executeList(request) {
1589
1728
  const start = Date.now();
1590
1729
  const events = await this.loadEvents();
1591
- const backendUsed = "filesystem";
1730
+ const backendUsed = this.backend.kind();
1592
1731
  const eventsPath = path5.join(this.rootDir, ".hardkas", "events.jsonl");
1593
1732
  const filtered = [];
1594
1733
  const txFilter = request.params["tx"] || request.params["txId"];
@@ -1619,67 +1758,52 @@ var EventsQueryAdapter = class {
1619
1758
  annotations: {
1620
1759
  executedAt: (/* @__PURE__ */ new Date()).toISOString(),
1621
1760
  executionMs: Date.now() - start,
1622
- filesScanned: 1
1761
+ filesScanned: backendUsed === "sqlite" ? 0 : 1
1623
1762
  },
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
- } : {}
1763
+ why: request.explain ? [{
1764
+ question: "How were events loaded and linked?",
1765
+ answer: `Loaded ${total} events matching filters. Causal links (correlation/causation) available in payload.`,
1766
+ evidence: [],
1767
+ causalChain: [
1768
+ { order: 1, assertion: `Source: ${backendUsed}`, evidence: "Event stream processed" },
1769
+ { order: 2, assertion: `Filters: ${effectiveFilters.length} applied`, evidence: effectiveFilters.map((f) => `${f.field} ${f.op} ${f.value}`).join(", ") || "none" },
1770
+ { order: 3, assertion: "Ordering: timestamp ASC", evidence: "Deterministic stream sorting" }
1771
+ ],
1772
+ model: "events-causality",
1773
+ confidence: "definitive"
1774
+ }] : void 0
1638
1775
  };
1639
1776
  }
1640
1777
  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() !== "");
1778
+ const docs = await this.backend.getEvents();
1649
1779
  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
- }
1780
+ for (const doc of docs) {
1781
+ events.push({
1782
+ eventId: doc.eventId,
1783
+ kind: doc.kind,
1784
+ domain: doc.domain,
1785
+ timestamp: doc.timestamp || "",
1786
+ workflowId: doc.workflowId,
1787
+ correlationId: doc.correlationId,
1788
+ causationId: doc.causationId || void 0,
1789
+ txId: doc.txId || void 0,
1790
+ artifactId: doc.artifactId || void 0,
1791
+ networkId: doc.networkId,
1792
+ payload: doc.payload
1793
+ });
1669
1794
  }
1670
1795
  return events;
1671
1796
  }
1672
1797
  };
1673
1798
 
1674
1799
  // 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
1800
  var TxQueryAdapter = class {
1679
1801
  domain = "tx";
1680
1802
  rootDir;
1681
- constructor(rootDir) {
1803
+ backend;
1804
+ constructor(rootDir, backend) {
1682
1805
  this.rootDir = rootDir;
1806
+ this.backend = backend;
1683
1807
  }
1684
1808
  supportedOps() {
1685
1809
  return ["aggregate"];
@@ -1716,20 +1840,19 @@ var TxQueryAdapter = class {
1716
1840
  warnings,
1717
1841
  complete
1718
1842
  };
1719
- let explain;
1843
+ let why;
1720
1844
  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" }
1845
+ why = [{
1846
+ question: `Causal aggregation for transaction ${txId}?`,
1847
+ answer: complete ? `Found ${artifacts.length} artifact(s) and ${events.length} event(s). Workflow is consistent.` : `Aggregation incomplete: ${warnings.join(". ")}.`,
1848
+ evidence: [{ type: "txId", value: txId }],
1849
+ causalChain: [
1850
+ { order: 1, assertion: `Artifacts linked: ${artifacts.length}`, evidence: artifacts.map((a) => a.role).join(", ") },
1851
+ { order: 2, assertion: `Events linked: ${events.length}`, evidence: "Events found in stream" },
1852
+ { order: 3, assertion: `Completeness check: ${complete}`, evidence: warnings.join("; ") || "all required roles found" }
1729
1853
  ],
1730
- model: "tx-aggregation",
1731
- confidence: "definitive",
1732
- references: [txId]
1854
+ model: "tx-causality",
1855
+ confidence: "definitive"
1733
1856
  }];
1734
1857
  }
1735
1858
  return {
@@ -1740,7 +1863,7 @@ var TxQueryAdapter = class {
1740
1863
  truncated: false,
1741
1864
  deterministic: true,
1742
1865
  queryHash: computeQueryHash([result]),
1743
- explain,
1866
+ why,
1744
1867
  annotations: {
1745
1868
  executedAt: (/* @__PURE__ */ new Date()).toISOString(),
1746
1869
  executionMs: Date.now() - start
@@ -1748,92 +1871,82 @@ var TxQueryAdapter = class {
1748
1871
  };
1749
1872
  }
1750
1873
  async findArtifactsByTxId(txId) {
1874
+ const docs = await this.backend.findArtifacts();
1751
1875
  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
- }
1876
+ for (const doc of docs) {
1877
+ const parsed = doc.payload;
1878
+ const matchesTx = parsed.txId === txId || parsed.transaction?.id === txId || parsed.lineage?.artifactId === txId;
1879
+ if (!matchesTx) continue;
1880
+ const schema = String(doc.schema);
1881
+ let role = "unknown";
1882
+ if (schema.includes("txPlan")) role = "plan";
1883
+ else if (schema.includes("signedTx")) role = "signed";
1884
+ else if (schema.includes("txReceipt")) role = "receipt";
1885
+ results.push({
1886
+ filePath: doc.path,
1887
+ schema,
1888
+ contentHash: doc.contentHash,
1889
+ role
1890
+ });
1773
1891
  }
1774
1892
  return results.sort((a, b) => a.filePath.localeCompare(b.filePath));
1775
1893
  }
1776
1894
  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() !== "");
1895
+ const docs = await this.backend.getEvents({ txId });
1785
1896
  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
- }
1897
+ for (const doc of docs) {
1898
+ results.push({
1899
+ eventId: doc.eventId,
1900
+ kind: doc.kind,
1901
+ timestamp: doc.timestamp || ""
1902
+ });
1798
1903
  }
1799
1904
  return results;
1800
1905
  }
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
1906
  };
1825
1907
 
1826
1908
  // src/engine.ts
1827
- var QueryEngine = class {
1909
+ import fs6 from "fs";
1910
+ import path6 from "path";
1911
+ var QueryEngine = class _QueryEngine {
1828
1912
  adapters;
1913
+ backend;
1914
+ /**
1915
+ * Primary entry point for creating a QueryEngine with auto-discovery.
1916
+ */
1917
+ static async create(options) {
1918
+ let backend = options.backend;
1919
+ if (!backend) {
1920
+ const dbPath = path6.join(options.artifactDir, ".hardkas", "store.db");
1921
+ if (fs6.existsSync(dbPath)) {
1922
+ try {
1923
+ const { HardkasStore, SqliteQueryBackend, HardkasIndexer } = await import("@hardkas/query-store");
1924
+ const store = new HardkasStore({ dbPath });
1925
+ store.connect();
1926
+ const indexer = new HardkasIndexer(store.getDatabase());
1927
+ await indexer.sync();
1928
+ backend = new SqliteQueryBackend(store);
1929
+ } catch (e) {
1930
+ backend = new FilesystemQueryBackend(options.artifactDir);
1931
+ }
1932
+ } else {
1933
+ backend = new FilesystemQueryBackend(options.artifactDir);
1934
+ }
1935
+ }
1936
+ return new _QueryEngine({
1937
+ artifactDir: options.artifactDir,
1938
+ backend
1939
+ });
1940
+ }
1829
1941
  constructor(options) {
1942
+ this.backend = options.backend || new FilesystemQueryBackend(options.artifactDir);
1830
1943
  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));
1944
+ this.adapters.set("artifacts", new ArtifactQueryAdapter(options.artifactDir, this.backend));
1945
+ this.adapters.set("lineage", new LineageQueryAdapter(options.artifactDir, this.backend));
1946
+ this.adapters.set("replay", new ReplayQueryAdapter(options.artifactDir, this.backend));
1947
+ this.adapters.set("dag", new DagQueryAdapter(options.artifactDir, this.backend));
1948
+ this.adapters.set("events", new EventsQueryAdapter(options.artifactDir, this.backend));
1949
+ this.adapters.set("tx", new TxQueryAdapter(options.artifactDir, this.backend));
1837
1950
  }
1838
1951
  /**
1839
1952
  * Execute a query request against the appropriate adapter.
@@ -1848,7 +1961,31 @@ var QueryEngine = class {
1848
1961
  `Operation "${request.op}" is not supported by the "${request.domain}" adapter. Supported: ${adapter.supportedOps().join(", ")}`
1849
1962
  );
1850
1963
  }
1851
- return adapter.execute(request);
1964
+ const result = await adapter.execute(request);
1965
+ const freshness = await this.backend.getStoreStatus();
1966
+ const backendUsed = this.backend.kind();
1967
+ let explain = void 0;
1968
+ if (request.explain) {
1969
+ explain = {
1970
+ backend: backendUsed,
1971
+ executionPlan: ["Discovery", "Filter", "Sort", "Paginate"],
1972
+ indexesUsed: backendUsed === "sqlite" ? ["PRIMARY", "idx_artifacts_schema"] : [],
1973
+ filtersApplied: request.filters.map((f) => `${f.field} ${f.op} ${f.value}`),
1974
+ rowsRead: result.items.length,
1975
+ scannedFiles: result.annotations.filesScanned || 0,
1976
+ freshness,
1977
+ warnings: freshness === "stale" ? ["Index is STALE. mtime mismatch detected. Run 'hardkas query store rebuild'."] : []
1978
+ };
1979
+ }
1980
+ return {
1981
+ ...result,
1982
+ explain,
1983
+ annotations: {
1984
+ ...result.annotations,
1985
+ backendUsed,
1986
+ freshness
1987
+ }
1988
+ };
1852
1989
  }
1853
1990
  /**
1854
1991
  * List available domains and their operations.
@@ -1886,14 +2023,15 @@ export {
1886
2023
  ReplayQueryAdapter,
1887
2024
  TxQueryAdapter,
1888
2025
  computeQueryHash,
2026
+ createExplainBlock,
1889
2027
  createQueryRequest,
1890
2028
  evaluateFilter,
1891
2029
  evaluateFilters,
1892
2030
  explainIntegrity,
1893
2031
  explainOrphan,
1894
2032
  explainTransition,
1895
- formatExplainBrief,
1896
- formatExplainFull,
2033
+ formatExplainBlock,
2034
+ formatWhyBlock,
1897
2035
  resolveFieldPath,
1898
2036
  serializeQueryResult
1899
2037
  };