mdkg 0.1.9 → 0.1.10

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.
package/CHANGELOG.md CHANGED
@@ -6,7 +6,20 @@ This project follows a pragmatic changelog style inspired by Keep a Changelog. V
6
6
 
7
7
  mdkg is pre-v1 public alpha software. Command, graph, cache, bundle, and DAL contracts may change quickly while the project converges on a stable v1 surface.
8
8
 
9
- ## 0.1.9 - Unreleased
9
+ ## 0.1.10 - 2026-06-05
10
+
11
+ ### Added
12
+
13
+ - Added an internal queue-backed project DB materializer helper plus packed
14
+ `smoke:db-materializer` coverage for queue delivery, durable events, reducers,
15
+ writer lease/CAS conflicts, receipts, snapshots, stats, index, and validate.
16
+
17
+ ### Changed
18
+
19
+ - Publish readiness now requires the compiled materializer helper and seeded docs
20
+ that keep materializer support internal-only.
21
+
22
+ ## 0.1.9 - 2026-06-04
10
23
 
11
24
  ### Added
12
25
 
package/README.md CHANGED
@@ -14,7 +14,7 @@ mdkg stays deliberately boring:
14
14
  - first-class rebuildable SQLite cache through built-in `node:sqlite`
15
15
  - no daemon, hosted index, or vector DB
16
16
 
17
- Current package version in source: `0.1.9`
17
+ Current package version in source: `0.1.10`
18
18
 
19
19
  mdkg is still pre-v1 public alpha software. The public package is usable, but graph, cache, bundle, and DAL contracts may continue to change quickly while the project converges on a stable v1 surface.
20
20
 
@@ -354,11 +354,12 @@ foundation, then internal local event/receipt/reducer and writer lease/CAS
354
354
  foundations, and record migration order, checksums, and applied timestamps.
355
355
  Queue state is durable local delivery infrastructure, not canonical event
356
356
  history. Event rows are the durable local history for project DB state
357
- transitions, receipts provide audit/review artifacts, reducers gate writes, and
358
- writer leases coordinate snapshot-hash compare-and-swap commits. These
359
- capabilities are available only through internal helper modules in this release;
360
- there is no public `mdkg db queue`, `mdkg db event`, `mdkg db reducer`, or
361
- `mdkg db lease` CLI yet.
357
+ transitions, receipts provide audit/review artifacts, reducers gate writes,
358
+ writer leases coordinate snapshot-hash compare-and-swap commits, and
359
+ materializers run local queue-backed reducer passes. These capabilities are
360
+ available only through internal helper modules in this release; there is no
361
+ public `mdkg db queue`, `mdkg db event`, `mdkg db reducer`, `mdkg db lease`, or
362
+ `mdkg db materializer` CLI yet.
362
363
  Use `mdkg db verify` for non-mutating health checks over config, layout,
363
364
  runtime SQLite integrity, migration metadata, and transient runtime files. Use
364
365
  `mdkg db stats` for deterministic table counts, DB size, migration state,
@@ -0,0 +1,462 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.PROJECT_DB_MATERIALIZER_SCHEMA_VERSION = exports.PROJECT_DB_MATERIALIZER_KIND = exports.PROJECT_DB_MATERIALIZER_QUEUE = void 0;
7
+ exports.enqueueProjectDbMaterialization = enqueueProjectDbMaterialization;
8
+ exports.runNextProjectDbMaterializer = runNextProjectDbMaterializer;
9
+ exports.readProjectDbMaterializerStats = readProjectDbMaterializerStats;
10
+ const crypto_1 = __importDefault(require("crypto"));
11
+ const fs_1 = __importDefault(require("fs"));
12
+ const path_1 = __importDefault(require("path"));
13
+ const config_1 = require("./config");
14
+ const project_db_queue_1 = require("./project_db_queue");
15
+ const project_db_events_1 = require("./project_db_events");
16
+ const project_db_snapshot_1 = require("./project_db_snapshot");
17
+ exports.PROJECT_DB_MATERIALIZER_QUEUE = "project-db.materialize";
18
+ exports.PROJECT_DB_MATERIALIZER_KIND = "project-db.materialize";
19
+ exports.PROJECT_DB_MATERIALIZER_SCHEMA_VERSION = 1;
20
+ function loadDatabaseCtor() {
21
+ try {
22
+ const loaded = require("node:sqlite");
23
+ if (!loaded.DatabaseSync) {
24
+ throw new Error("node:sqlite DatabaseSync is unavailable");
25
+ }
26
+ return loaded.DatabaseSync;
27
+ }
28
+ catch (err) {
29
+ const message = err instanceof Error ? err.message : String(err);
30
+ throw new Error(`node:sqlite is required for mdkg project DB materializers: ${message}`);
31
+ }
32
+ }
33
+ function nowMs(input) {
34
+ if (input !== undefined) {
35
+ assertInteger(input, "now_ms");
36
+ return input;
37
+ }
38
+ return Date.now();
39
+ }
40
+ function assertNonEmpty(value, field) {
41
+ if (typeof value !== "string" || value.trim() === "") {
42
+ throw new Error(`${field} must be a non-empty string`);
43
+ }
44
+ }
45
+ function assertInteger(value, field) {
46
+ if (!Number.isInteger(value)) {
47
+ throw new Error(`${field} must be an integer`);
48
+ }
49
+ }
50
+ function assertPositiveInteger(value, field) {
51
+ assertInteger(value, field);
52
+ if (value <= 0) {
53
+ throw new Error(`${field} must be greater than 0`);
54
+ }
55
+ }
56
+ function sha256File(filePath) {
57
+ return `sha256:${crypto_1.default.createHash("sha256").update(fs_1.default.readFileSync(filePath)).digest("hex")}`;
58
+ }
59
+ function safeSegment(value) {
60
+ return value.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "materializer";
61
+ }
62
+ function isObject(value) {
63
+ return typeof value === "object" && value !== null && !Array.isArray(value);
64
+ }
65
+ function stringField(value, field) {
66
+ const raw = value[field];
67
+ if (typeof raw !== "string" || raw.trim() === "") {
68
+ throw new Error(`materializer payload ${field} must be a non-empty string`);
69
+ }
70
+ return raw;
71
+ }
72
+ function parsePayload(payloadJson) {
73
+ let parsed;
74
+ try {
75
+ parsed = JSON.parse(payloadJson);
76
+ }
77
+ catch (err) {
78
+ const message = err instanceof Error ? err.message : String(err);
79
+ throw new Error(`materializer payload must be valid JSON: ${message}`);
80
+ }
81
+ if (!isObject(parsed)) {
82
+ throw new Error("materializer payload must be an object");
83
+ }
84
+ if (parsed.kind !== exports.PROJECT_DB_MATERIALIZER_KIND) {
85
+ throw new Error(`materializer payload kind must be ${exports.PROJECT_DB_MATERIALIZER_KIND}`);
86
+ }
87
+ if (parsed.schema_version !== exports.PROJECT_DB_MATERIALIZER_SCHEMA_VERSION) {
88
+ throw new Error(`materializer payload schema_version must be ${exports.PROJECT_DB_MATERIALIZER_SCHEMA_VERSION}`);
89
+ }
90
+ const reducerName = stringField(parsed, "reducer_name");
91
+ if (reducerName !== "project_meta.set") {
92
+ throw new Error(`unsupported materializer reducer_name: ${reducerName}`);
93
+ }
94
+ const reducerVersion = stringField(parsed, "reducer_version");
95
+ if (reducerVersion !== "v1") {
96
+ throw new Error(`unsupported materializer reducer_version: ${reducerVersion}`);
97
+ }
98
+ return {
99
+ kind: exports.PROJECT_DB_MATERIALIZER_KIND,
100
+ schema_version: exports.PROJECT_DB_MATERIALIZER_SCHEMA_VERSION,
101
+ project_id: stringField(parsed, "project_id"),
102
+ branch_id: stringField(parsed, "branch_id"),
103
+ event_id: stringField(parsed, "event_id"),
104
+ reducer_name: reducerName,
105
+ reducer_version: reducerVersion,
106
+ };
107
+ }
108
+ function partialPayloadIds(payloadJson) {
109
+ try {
110
+ const parsed = JSON.parse(payloadJson);
111
+ if (isObject(parsed)) {
112
+ return {
113
+ project_id: typeof parsed.project_id === "string" && parsed.project_id.trim() !== "" ? parsed.project_id : "unknown",
114
+ branch_id: typeof parsed.branch_id === "string" && parsed.branch_id.trim() !== "" ? parsed.branch_id : "unknown",
115
+ event_id: typeof parsed.event_id === "string" && parsed.event_id.trim() !== "" ? parsed.event_id : null,
116
+ };
117
+ }
118
+ }
119
+ catch {
120
+ // fall through to unknown identifiers for rejected payload receipts
121
+ }
122
+ return { project_id: "unknown", branch_id: "unknown", event_id: null };
123
+ }
124
+ function findRepoRoot(databasePath, explicitRoot) {
125
+ if (explicitRoot) {
126
+ return path_1.default.resolve(explicitRoot);
127
+ }
128
+ let current = path_1.default.dirname(path_1.default.resolve(databasePath));
129
+ while (true) {
130
+ if (fs_1.default.existsSync(path_1.default.join(current, ".mdkg", "config.json"))) {
131
+ return current;
132
+ }
133
+ const parent = path_1.default.dirname(current);
134
+ if (parent === current) {
135
+ throw new Error("repo_root is required when databasePath is not inside an mdkg repo");
136
+ }
137
+ current = parent;
138
+ }
139
+ }
140
+ function withDb(databasePath, fn) {
141
+ const DatabaseSync = loadDatabaseCtor();
142
+ const db = new DatabaseSync(databasePath);
143
+ try {
144
+ db.exec("PRAGMA foreign_keys = ON;");
145
+ return fn(db);
146
+ }
147
+ finally {
148
+ db.close();
149
+ }
150
+ }
151
+ function readBranchSnapshotHash(databasePath, projectId, branchId) {
152
+ return withDb(databasePath, (db) => {
153
+ const row = db
154
+ .prepare("SELECT current_snapshot_hash FROM project_branch_state WHERE project_id = ? AND branch_id = ?")
155
+ .get(projectId, branchId);
156
+ return row?.current_snapshot_hash === undefined || row.current_snapshot_hash === null
157
+ ? null
158
+ : String(row.current_snapshot_hash);
159
+ });
160
+ }
161
+ function queueName(input) {
162
+ if (input !== undefined) {
163
+ assertNonEmpty(input, "queue_name");
164
+ return input;
165
+ }
166
+ return exports.PROJECT_DB_MATERIALIZER_QUEUE;
167
+ }
168
+ function result(input) {
169
+ return {
170
+ status: input.status,
171
+ queue_name: input.queue_name,
172
+ queue_message: input.queue_message,
173
+ payload: input.payload ?? null,
174
+ reducer: input.reducer ?? null,
175
+ lease: input.lease ?? null,
176
+ receipt: input.receipt ?? null,
177
+ snapshot: input.snapshot ?? null,
178
+ error: input.error ?? null,
179
+ };
180
+ }
181
+ function failStatus(message, fallback) {
182
+ return message.status === "dead_letter" ? "dead_letter" : fallback;
183
+ }
184
+ function writeMaterializerReceipt(databasePath, input) {
185
+ return (0, project_db_events_1.writeProjectDbReceipt)(databasePath, {
186
+ project_id: input.payload.project_id,
187
+ branch_id: input.payload.branch_id,
188
+ kind: input.kind,
189
+ status: input.status,
190
+ event_id: input.payload.event_id,
191
+ actor: input.actor,
192
+ details: {
193
+ message_id: input.message.message_id,
194
+ queue_name: input.message.queue_name,
195
+ error: input.error,
196
+ },
197
+ now_ms: input.now_ms,
198
+ receipts_path: input.receipts_path,
199
+ });
200
+ }
201
+ function tryReleaseWriterLease(databasePath, payload, leaseId, leaseOwner, now, receiptsPath) {
202
+ try {
203
+ (0, project_db_events_1.releaseProjectWriterLease)(databasePath, {
204
+ project_id: payload.project_id,
205
+ branch_id: payload.branch_id,
206
+ lease_id: leaseId,
207
+ lease_owner: leaseOwner,
208
+ now_ms: now,
209
+ receipts_path: receiptsPath,
210
+ });
211
+ }
212
+ catch {
213
+ // The lease may already be committed, conflicted, released, or absent.
214
+ }
215
+ }
216
+ function enqueueProjectDbMaterialization(databasePath, input) {
217
+ assertNonEmpty(input.message_id, "message_id");
218
+ assertNonEmpty(input.project_id, "project_id");
219
+ assertNonEmpty(input.branch_id, "branch_id");
220
+ assertNonEmpty(input.event_id, "event_id");
221
+ const selectedQueue = queueName(input.queue_name);
222
+ const payload = {
223
+ kind: exports.PROJECT_DB_MATERIALIZER_KIND,
224
+ schema_version: exports.PROJECT_DB_MATERIALIZER_SCHEMA_VERSION,
225
+ project_id: input.project_id,
226
+ branch_id: input.branch_id,
227
+ event_id: input.event_id,
228
+ reducer_name: input.reducer_name,
229
+ reducer_version: input.reducer_version,
230
+ };
231
+ return (0, project_db_queue_1.enqueueProjectQueueMessage)(databasePath, {
232
+ queue_name: selectedQueue,
233
+ message_id: input.message_id,
234
+ dedupe_key: input.dedupe_key === undefined
235
+ ? `${input.project_id}:${input.branch_id}:${input.event_id}:${input.reducer_name}:${input.reducer_version}`
236
+ : input.dedupe_key,
237
+ payload,
238
+ available_at_ms: input.available_at_ms,
239
+ max_attempts: input.max_attempts,
240
+ now_ms: input.now_ms,
241
+ });
242
+ }
243
+ function runNextProjectDbMaterializer(databasePath, input) {
244
+ assertNonEmpty(input.lease_owner, "lease_owner");
245
+ assertPositiveInteger(input.lease_ms, "lease_ms");
246
+ const selectedQueue = queueName(input.queue_name);
247
+ const currentNow = nowMs(input.now_ms);
248
+ const retryAfter = input.retry_after_ms ?? 0;
249
+ assertInteger(retryAfter, "retry_after_ms");
250
+ if (retryAfter < 0) {
251
+ throw new Error("retry_after_ms must be greater than or equal to 0");
252
+ }
253
+ (0, project_db_queue_1.releaseExpiredProjectQueueLeases)(databasePath, {
254
+ queue_name: selectedQueue,
255
+ now_ms: currentNow,
256
+ });
257
+ const claimed = (0, project_db_queue_1.claimProjectQueueMessage)(databasePath, {
258
+ queue_name: selectedQueue,
259
+ lease_owner: input.lease_owner,
260
+ lease_ms: input.lease_ms,
261
+ now_ms: currentNow,
262
+ });
263
+ if (!claimed) {
264
+ return result({ status: "idle", queue_name: selectedQueue, queue_message: null });
265
+ }
266
+ let payload;
267
+ try {
268
+ payload = parsePayload(claimed.payload_json);
269
+ }
270
+ catch (err) {
271
+ const error = err instanceof Error ? err.message : String(err);
272
+ const partial = partialPayloadIds(claimed.payload_json);
273
+ const receipt = writeMaterializerReceipt(databasePath, {
274
+ payload: partial,
275
+ kind: "materializer-invalid-payload",
276
+ status: "rejected",
277
+ actor: input.lease_owner,
278
+ message: claimed,
279
+ error,
280
+ now_ms: currentNow,
281
+ receipts_path: input.receipts_path,
282
+ });
283
+ const dead = (0, project_db_queue_1.deadLetterProjectQueueMessage)(databasePath, {
284
+ queue_name: selectedQueue,
285
+ message_id: claimed.message_id,
286
+ lease_owner: input.lease_owner,
287
+ error,
288
+ now_ms: currentNow,
289
+ });
290
+ return result({
291
+ status: "dead_letter",
292
+ queue_name: selectedQueue,
293
+ queue_message: dead,
294
+ receipt,
295
+ error,
296
+ });
297
+ }
298
+ const leaseId = `materializer-${safeSegment(claimed.message_id)}-${claimed.attempt_count}`;
299
+ const currentBranchHash = readBranchSnapshotHash(databasePath, payload.project_id, payload.branch_id);
300
+ const baseHash = input.base_snapshot_hash ?? currentBranchHash ?? sha256File(databasePath);
301
+ let lease = null;
302
+ try {
303
+ lease = (0, project_db_events_1.acquireProjectWriterLease)(databasePath, {
304
+ project_id: payload.project_id,
305
+ branch_id: payload.branch_id,
306
+ lease_id: leaseId,
307
+ lease_owner: input.lease_owner,
308
+ base_snapshot_hash: baseHash,
309
+ lease_ms: input.lease_ms,
310
+ now_ms: currentNow,
311
+ });
312
+ if (currentBranchHash && currentBranchHash !== baseHash) {
313
+ const conflict = (0, project_db_events_1.commitProjectWriterLease)(databasePath, {
314
+ project_id: payload.project_id,
315
+ branch_id: payload.branch_id,
316
+ lease_id: leaseId,
317
+ lease_owner: input.lease_owner,
318
+ result_snapshot_hash: sha256File(databasePath),
319
+ now_ms: currentNow,
320
+ receipts_path: input.receipts_path,
321
+ });
322
+ const failed = (0, project_db_queue_1.failProjectQueueMessage)(databasePath, {
323
+ queue_name: selectedQueue,
324
+ message_id: claimed.message_id,
325
+ lease_owner: input.lease_owner,
326
+ error: `snapshot hash mismatch: current ${currentBranchHash}, base ${baseHash}`,
327
+ retry_after_ms: retryAfter,
328
+ now_ms: currentNow,
329
+ });
330
+ return result({
331
+ status: failStatus(failed, "conflict"),
332
+ queue_name: selectedQueue,
333
+ queue_message: failed,
334
+ payload,
335
+ lease: conflict.lease,
336
+ receipt: conflict.receipt,
337
+ error: failed.last_error,
338
+ });
339
+ }
340
+ const reducer = (0, project_db_events_1.applyProjectDbReducer)(databasePath, {
341
+ event_id: payload.event_id,
342
+ reducer_name: payload.reducer_name,
343
+ reducer_version: payload.reducer_version,
344
+ actor: input.lease_owner,
345
+ now_ms: currentNow,
346
+ receipts_path: input.receipts_path,
347
+ });
348
+ if (!reducer.applied && reducer.receipt.status === "rejected") {
349
+ tryReleaseWriterLease(databasePath, payload, leaseId, input.lease_owner, currentNow, input.receipts_path);
350
+ const failed = (0, project_db_queue_1.failProjectQueueMessage)(databasePath, {
351
+ queue_name: selectedQueue,
352
+ message_id: claimed.message_id,
353
+ lease_owner: input.lease_owner,
354
+ error: reducer.receipt.details_json,
355
+ retry_after_ms: retryAfter,
356
+ now_ms: currentNow,
357
+ });
358
+ return result({
359
+ status: failStatus(failed, "rejected"),
360
+ queue_name: selectedQueue,
361
+ queue_message: failed,
362
+ payload,
363
+ reducer,
364
+ lease,
365
+ receipt: reducer.receipt,
366
+ error: failed.last_error,
367
+ });
368
+ }
369
+ const commit = (0, project_db_events_1.commitProjectWriterLease)(databasePath, {
370
+ project_id: payload.project_id,
371
+ branch_id: payload.branch_id,
372
+ lease_id: leaseId,
373
+ lease_owner: input.lease_owner,
374
+ result_snapshot_hash: sha256File(databasePath),
375
+ now_ms: currentNow,
376
+ receipts_path: input.receipts_path,
377
+ });
378
+ if (!commit.committed) {
379
+ const failed = (0, project_db_queue_1.failProjectQueueMessage)(databasePath, {
380
+ queue_name: selectedQueue,
381
+ message_id: claimed.message_id,
382
+ lease_owner: input.lease_owner,
383
+ error: commit.receipt.details_json,
384
+ retry_after_ms: retryAfter,
385
+ now_ms: currentNow,
386
+ });
387
+ return result({
388
+ status: failStatus(failed, "conflict"),
389
+ queue_name: selectedQueue,
390
+ queue_message: failed,
391
+ payload,
392
+ reducer,
393
+ lease: commit.lease,
394
+ receipt: commit.receipt,
395
+ error: failed.last_error,
396
+ });
397
+ }
398
+ const acked = (0, project_db_queue_1.ackProjectQueueMessage)(databasePath, {
399
+ queue_name: selectedQueue,
400
+ message_id: claimed.message_id,
401
+ lease_owner: input.lease_owner,
402
+ now_ms: currentNow,
403
+ });
404
+ const repoRoot = findRepoRoot(databasePath, input.repo_root);
405
+ const config = (0, config_1.loadConfig)(repoRoot);
406
+ const snapshot = (0, project_db_snapshot_1.sealProjectDbSnapshot)(repoRoot, config);
407
+ return result({
408
+ status: reducer.applied ? "applied" : "duplicate",
409
+ queue_name: selectedQueue,
410
+ queue_message: acked,
411
+ payload,
412
+ reducer,
413
+ lease: commit.lease,
414
+ receipt: reducer.receipt,
415
+ snapshot,
416
+ });
417
+ }
418
+ catch (err) {
419
+ const error = err instanceof Error ? err.message : String(err);
420
+ if (lease) {
421
+ tryReleaseWriterLease(databasePath, payload, leaseId, input.lease_owner, currentNow, input.receipts_path);
422
+ }
423
+ const receipt = writeMaterializerReceipt(databasePath, {
424
+ payload,
425
+ kind: "materializer-error",
426
+ status: "rejected",
427
+ actor: input.lease_owner,
428
+ message: claimed,
429
+ error,
430
+ now_ms: currentNow,
431
+ receipts_path: input.receipts_path,
432
+ });
433
+ const failed = (0, project_db_queue_1.failProjectQueueMessage)(databasePath, {
434
+ queue_name: selectedQueue,
435
+ message_id: claimed.message_id,
436
+ lease_owner: input.lease_owner,
437
+ error,
438
+ retry_after_ms: retryAfter,
439
+ now_ms: currentNow,
440
+ });
441
+ return result({
442
+ status: failStatus(failed, "retry"),
443
+ queue_name: selectedQueue,
444
+ queue_message: failed,
445
+ payload,
446
+ lease,
447
+ receipt,
448
+ error,
449
+ });
450
+ }
451
+ }
452
+ function readProjectDbMaterializerStats(databasePath, input = {}) {
453
+ const selectedQueue = queueName(input.queue_name);
454
+ return {
455
+ queue_name: selectedQueue,
456
+ queue: (0, project_db_queue_1.readProjectQueueStats)(databasePath, {
457
+ queue_name: selectedQueue,
458
+ now_ms: input.now_ms,
459
+ }),
460
+ writer_leases: (0, project_db_events_1.readProjectWriterLeaseStats)(databasePath, { now_ms: input.now_ms }),
461
+ };
462
+ }
@@ -38,9 +38,9 @@ Agent operating prompt:
38
38
  node:sqlite queue, event/receipt/reducer, and writer lease/CAS foundation
39
39
  migrations. Queue state is delivery infrastructure, not canonical event
40
40
  history. Event rows are durable local project DB history; receipts, reducers,
41
- and writer leases are internal local helper surfaces, and there is no public
42
- `mdkg db queue`, `mdkg db event`, `mdkg db reducer`, or `mdkg db lease` CLI
43
- yet. Use `mdkg db verify` and `mdkg db stats` for
41
+ writer leases, and materializers are internal local helper surfaces, and there
42
+ is no public `mdkg db queue`, `mdkg db event`, `mdkg db reducer`,
43
+ `mdkg db lease`, or `mdkg db materializer` CLI yet. Use `mdkg db verify` and `mdkg db stats` for
44
44
  non-mutating health and summary receipts. Use `mdkg db snapshot seal` for
45
45
  explicit sealed checkpoints,
46
46
  `mdkg db snapshot verify/status` for checkpoint health, and
@@ -56,9 +56,9 @@ Project database commands:
56
56
  - queue tables are durable local delivery state, not canonical event history;
57
57
  there is no public `mdkg db queue` CLI yet
58
58
  - event tables are durable local history for project DB state transitions;
59
- receipts, typed reducers, and writer leases remain internal helper surfaces in
60
- this release, with no public `mdkg db event`, `mdkg db reducer`, or
61
- `mdkg db lease` CLI yet
59
+ receipts, typed reducers, writer leases, and materializers remain internal
60
+ helper surfaces in this release, with no public `mdkg db event`,
61
+ `mdkg db reducer`, `mdkg db lease`, or `mdkg db materializer` CLI yet
62
62
  - `mdkg db verify` checks config, layout, runtime SQLite integrity, migration
63
63
  metadata, receipt directory policy, and transient runtime files
64
64
  - `mdkg db stats` reports table counts, database size, migration state,
@@ -79,9 +79,9 @@ update the active runtime SQLite database with mdkg-owned foundation plus
79
79
  internal local node:sqlite queue, event/receipt/reducer, and writer lease/CAS
80
80
  foundation migrations. Queue state is delivery infrastructure, not canonical
81
81
  event history. Event rows are durable local project DB history; receipts,
82
- reducers, and writer leases are internal local helper surfaces, and there is no
83
- public `mdkg db queue`, `mdkg db event`, `mdkg db reducer`, or `mdkg db lease`
84
- CLI yet. Use `mdkg db verify` for non-mutating health checks and
82
+ reducers, writer leases, and materializers are internal local helper surfaces,
83
+ and there is no public `mdkg db queue`, `mdkg db event`, `mdkg db reducer`,
84
+ `mdkg db lease`, or `mdkg db materializer` CLI yet. Use `mdkg db verify` for non-mutating health checks and
85
85
  `mdkg db stats` for table counts, DB size, migration state, and receipt-file
86
86
  counts. Use `mdkg db snapshot seal` to create an opt-in sealed checkpoint under
87
87
  `.mdkg/db/state`, then use `mdkg db snapshot verify/status` for integrity and
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schema_version": 1,
3
3
  "tool": "mdkg",
4
- "mdkg_version": "0.1.9",
4
+ "mdkg_version": "0.1.10",
5
5
  "files": [
6
6
  {
7
7
  "path": ".mdkg/config.json",
@@ -61,7 +61,7 @@
61
61
  {
62
62
  "path": ".mdkg/README.md",
63
63
  "category": "mdkg_doc",
64
- "sha256": "b692c05fcfa1eaaa90edcd3f7a39042615f90e8915c7d447801c93284ed7463e"
64
+ "sha256": "0b5e7fa852aa71616ac108b2af3fb21b9ab66ee1ea69cb75539ba1df80be1072"
65
65
  },
66
66
  {
67
67
  "path": ".mdkg/skills/build-pack-and-execute-task/SKILL.md",
@@ -186,7 +186,7 @@
186
186
  {
187
187
  "path": "AGENT_START.md",
188
188
  "category": "startup_doc",
189
- "sha256": "1e1d9d94e2aa761b1a7978ab4b44e8bb7c3f0d33e94ed2d98cb06d641f4c42c6"
189
+ "sha256": "1e9def4cf02de6eecd164cff7855829dc3632009a2aa801eccb67b89bad4a570"
190
190
  },
191
191
  {
192
192
  "path": "AGENTS.md",
@@ -201,7 +201,7 @@
201
201
  {
202
202
  "path": "CLI_COMMAND_MATRIX.md",
203
203
  "category": "startup_doc",
204
- "sha256": "9f90882d698d0ad1b16bad0fbe37065e4ff9f2b79b27373ef7dce74b78e99265"
204
+ "sha256": "95bc386d88817ca597ae4f0fe4fa2d904843227a81147d606f54a32b93694c83"
205
205
  },
206
206
  {
207
207
  "path": "llms.txt",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mdkg",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "Markdown Knowledge Graph",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -19,6 +19,7 @@
19
19
  "smoke:db": "npm run build && node scripts/smoke-db.js",
20
20
  "smoke:db-queue": "npm run build && node scripts/smoke-db-queue.js",
21
21
  "smoke:db-events": "npm run build && node scripts/smoke-db-events.js",
22
+ "smoke:db-materializer": "npm run build && node scripts/smoke-db-materializer.js",
22
23
  "smoke:db-snapshot": "npm run build && node scripts/smoke-db-snapshot.js",
23
24
  "smoke:archive-work": "npm run build && node scripts/smoke-archive-work.js",
24
25
  "smoke:bundle": "npm run build && node scripts/smoke-bundle.js",
@@ -30,7 +31,7 @@
30
31
  "cli:snapshot": "npm run build && node scripts/cli_help_snapshot.js",
31
32
  "cli:check": "npm run build && node scripts/cli_help_snapshot.js --check",
32
33
  "prepack": "npm run build && node scripts/assert-publish-ready.js",
33
- "prepublishOnly": "npm run test && npm run cli:check && node dist/cli.js validate && npm run smoke:consumer && npm run smoke:matrix && npm run smoke:upgrade && npm run smoke:init && npm run smoke:capabilities && npm run smoke:db && npm run smoke:db-queue && npm run smoke:db-events && npm run smoke:db-snapshot && npm run smoke:archive-work && npm run smoke:bundle && npm run smoke:subgraph && npm run smoke:visibility && npm run smoke:sqlite && npm run smoke:parallel && npm run smoke:goal && node scripts/assert-publish-ready.js",
34
+ "prepublishOnly": "npm run test && npm run cli:check && node dist/cli.js validate && npm run smoke:consumer && npm run smoke:matrix && npm run smoke:upgrade && npm run smoke:init && npm run smoke:capabilities && npm run smoke:db && npm run smoke:db-queue && npm run smoke:db-events && npm run smoke:db-materializer && npm run smoke:db-snapshot && npm run smoke:archive-work && npm run smoke:bundle && npm run smoke:subgraph && npm run smoke:visibility && npm run smoke:sqlite && npm run smoke:parallel && npm run smoke:goal && node scripts/assert-publish-ready.js",
34
35
  "postinstall": "node scripts/postinstall.js",
35
36
  "smoke:subgraph": "npm run build && node scripts/smoke-subgraph.js"
36
37
  },