mdkg 0.1.7 → 0.1.8

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,6 +6,16 @@ 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.8 - 2026-06-04
10
+
11
+ ### Added
12
+
13
+ - Added internal local node:sqlite queue foundations for project DB delivery state.
14
+
15
+ ### Changed
16
+
17
+ - Source release line now targets the next non-published package version after `0.1.7`.
18
+
9
19
  ## 0.1.7 - 2026-06-04
10
20
 
11
21
  ### Added
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.7`
17
+ Current package version in source: `0.1.8`
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
 
@@ -348,9 +348,12 @@ layout is `.mdkg/db/schema`, `.mdkg/db/runtime`, `.mdkg/db/state`, and
348
348
  Runtime DB files, WAL, SHM, journal, lock, and temp files are ignored by
349
349
  default. `mdkg db init` does not create an active runtime SQLite database.
350
350
  Run `mdkg db migrate` after init to create or update the active runtime
351
- SQLite database at the configured `db.runtime_path`; the first migration writes
352
- only mdkg-owned generic foundation tables and records migration order,
353
- checksums, and applied timestamps.
351
+ SQLite database at the configured `db.runtime_path`; built-in migrations write
352
+ mdkg-owned generic foundation tables, then the internal local node:sqlite queue
353
+ foundation, and record migration order, checksums, and applied timestamps.
354
+ Queue state is durable local delivery infrastructure, not canonical event
355
+ history. It is available only through internal helper modules in this release;
356
+ there is no public `mdkg db queue` CLI yet.
354
357
  Use `mdkg db verify` for non-mutating health checks over config, layout,
355
358
  runtime SQLite integrity, migration metadata, and transient runtime files. Use
356
359
  `mdkg db stats` for deterministic table counts, DB size, migration state,
package/dist/cli.js CHANGED
@@ -222,16 +222,17 @@ function printDbHelp(log, subcommand) {
222
222
  log(" mdkg db snapshot diff <left-snapshot> <right-snapshot> [--json]");
223
223
  log("\nBoundaries:");
224
224
  log(" - `.mdkg/index` is the rebuildable graph cache");
225
- log(" - `.mdkg/db` is future project application state");
225
+ log(" - `.mdkg/db` is project application state");
226
226
  log(" - `mdkg db init` creates the generic layout and enables db config");
227
227
  log(" - `mdkg db init` does not create an active runtime SQLite database");
228
228
  log(" - `mdkg db migrate` creates/updates the active runtime SQLite database");
229
- log(" - `mdkg db migrate` applies mdkg-owned generic foundation migrations only");
229
+ log(" - `mdkg db migrate` applies mdkg-owned foundation and internal queue migrations");
230
+ log(" - queue tables are internal local delivery state; no public `mdkg db queue` CLI is exposed");
230
231
  log(" - `mdkg db verify` checks config, layout, SQLite integrity, migrations, and transient files");
231
232
  log(" - `mdkg db stats` reports table counts, migration state, DB size, and receipt counts");
232
233
  log(" - `mdkg db snapshot ...` manages opt-in sealed checkpoints and review dumps");
233
234
  log(" - active `.mdkg/db/runtime` and transient DB files are ignored by default");
234
- log(" - no raw SQL, hosted queue, profile, or publish behavior is exposed here");
235
+ log(" - no raw SQL, hosted queue, profile, public queue command, or publish behavior is exposed here");
235
236
  printGlobalOptions(log);
236
237
  }
237
238
  }
@@ -19,6 +19,40 @@ CREATE TABLE IF NOT EXISTS project_meta (
19
19
  value TEXT NOT NULL
20
20
  ) STRICT;
21
21
  `;
22
+ const QUEUE_MIGRATION_SQL = `
23
+ CREATE TABLE IF NOT EXISTS project_queue_message (
24
+ queue_name TEXT NOT NULL,
25
+ message_id TEXT NOT NULL,
26
+ dedupe_key TEXT,
27
+ payload_json TEXT NOT NULL,
28
+ payload_hash TEXT NOT NULL,
29
+ status TEXT NOT NULL CHECK(status IN ('ready', 'leased', 'acked', 'dead_letter')),
30
+ available_at_ms INTEGER NOT NULL,
31
+ attempt_count INTEGER NOT NULL DEFAULT 0 CHECK(attempt_count >= 0),
32
+ max_attempts INTEGER NOT NULL CHECK(max_attempts > 0),
33
+ lease_owner TEXT,
34
+ lease_deadline_ms INTEGER,
35
+ created_at_ms INTEGER NOT NULL,
36
+ updated_at_ms INTEGER NOT NULL,
37
+ last_error TEXT,
38
+ CHECK (
39
+ (status = 'leased' AND lease_owner IS NOT NULL AND lease_deadline_ms IS NOT NULL)
40
+ OR
41
+ (status <> 'leased' AND lease_owner IS NULL AND lease_deadline_ms IS NULL)
42
+ ),
43
+ PRIMARY KEY (queue_name, message_id)
44
+ ) STRICT;
45
+
46
+ CREATE UNIQUE INDEX IF NOT EXISTS project_queue_message_dedupe_unique
47
+ ON project_queue_message(queue_name, dedupe_key)
48
+ WHERE dedupe_key IS NOT NULL;
49
+
50
+ CREATE INDEX IF NOT EXISTS project_queue_message_ready_idx
51
+ ON project_queue_message(queue_name, status, available_at_ms, created_at_ms, message_id);
52
+
53
+ CREATE INDEX IF NOT EXISTS project_queue_message_lease_idx
54
+ ON project_queue_message(queue_name, status, lease_deadline_ms, created_at_ms, message_id);
55
+ `;
22
56
  const BUILTIN_MIGRATIONS = [
23
57
  {
24
58
  ordinal: 1,
@@ -26,6 +60,12 @@ const BUILTIN_MIGRATIONS = [
26
60
  filename: "001_mdkg_project_db_foundation.sql",
27
61
  sql: FOUNDATION_MIGRATION_SQL.trim(),
28
62
  },
63
+ {
64
+ ordinal: 2,
65
+ key: "mdkg.project_db.queue.v1",
66
+ filename: "002_mdkg_project_db_queue.sql",
67
+ sql: QUEUE_MIGRATION_SQL.trim(),
68
+ },
29
69
  ];
30
70
  function loadDatabaseCtor() {
31
71
  try {
@@ -0,0 +1,317 @@
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.enqueueProjectQueueMessage = enqueueProjectQueueMessage;
7
+ exports.claimProjectQueueMessage = claimProjectQueueMessage;
8
+ exports.ackProjectQueueMessage = ackProjectQueueMessage;
9
+ exports.failProjectQueueMessage = failProjectQueueMessage;
10
+ exports.deadLetterProjectQueueMessage = deadLetterProjectQueueMessage;
11
+ exports.releaseExpiredProjectQueueLeases = releaseExpiredProjectQueueLeases;
12
+ exports.readProjectQueueStats = readProjectQueueStats;
13
+ const crypto_1 = __importDefault(require("crypto"));
14
+ function loadDatabaseCtor() {
15
+ try {
16
+ const loaded = require("node:sqlite");
17
+ if (!loaded.DatabaseSync) {
18
+ throw new Error("node:sqlite DatabaseSync is unavailable");
19
+ }
20
+ return loaded.DatabaseSync;
21
+ }
22
+ catch (err) {
23
+ const message = err instanceof Error ? err.message : String(err);
24
+ throw new Error(`node:sqlite is required for mdkg project DB queues: ${message}`);
25
+ }
26
+ }
27
+ function nowMs(input) {
28
+ if (input !== undefined) {
29
+ assertInteger(input, "now_ms");
30
+ return input;
31
+ }
32
+ return Date.now();
33
+ }
34
+ function assertNonEmpty(value, field) {
35
+ if (typeof value !== "string" || value.trim() === "") {
36
+ throw new Error(`${field} must be a non-empty string`);
37
+ }
38
+ }
39
+ function assertInteger(value, field) {
40
+ if (!Number.isInteger(value)) {
41
+ throw new Error(`${field} must be an integer`);
42
+ }
43
+ }
44
+ function assertPositiveInteger(value, field) {
45
+ assertInteger(value, field);
46
+ if (value <= 0) {
47
+ throw new Error(`${field} must be greater than 0`);
48
+ }
49
+ }
50
+ function stableJson(value) {
51
+ if (value === undefined || typeof value === "function" || typeof value === "symbol" || typeof value === "bigint") {
52
+ throw new Error("payload must be JSON-serializable");
53
+ }
54
+ if (value === null || typeof value !== "object") {
55
+ if (typeof value === "number" && !Number.isFinite(value)) {
56
+ throw new Error("payload must be JSON-serializable");
57
+ }
58
+ const encoded = JSON.stringify(value);
59
+ if (encoded === undefined) {
60
+ throw new Error("payload must be JSON-serializable");
61
+ }
62
+ return encoded;
63
+ }
64
+ if (Array.isArray(value)) {
65
+ return `[${value.map((item) => stableJson(item)).join(",")}]`;
66
+ }
67
+ const object = value;
68
+ return `{${Object.keys(object)
69
+ .sort()
70
+ .map((key) => `${JSON.stringify(key)}:${stableJson(object[key])}`)
71
+ .join(",")}}`;
72
+ }
73
+ function payloadHash(payloadJson) {
74
+ return `sha256:${crypto_1.default.createHash("sha256").update(payloadJson).digest("hex")}`;
75
+ }
76
+ function toMessage(row) {
77
+ return {
78
+ queue_name: String(row.queue_name),
79
+ message_id: String(row.message_id),
80
+ dedupe_key: row.dedupe_key === null || row.dedupe_key === undefined ? null : String(row.dedupe_key),
81
+ payload_json: String(row.payload_json),
82
+ payload_hash: String(row.payload_hash),
83
+ status: String(row.status),
84
+ available_at_ms: Number(row.available_at_ms),
85
+ attempt_count: Number(row.attempt_count),
86
+ max_attempts: Number(row.max_attempts),
87
+ lease_owner: row.lease_owner === null || row.lease_owner === undefined ? null : String(row.lease_owner),
88
+ lease_deadline_ms: row.lease_deadline_ms === null || row.lease_deadline_ms === undefined ? null : Number(row.lease_deadline_ms),
89
+ created_at_ms: Number(row.created_at_ms),
90
+ updated_at_ms: Number(row.updated_at_ms),
91
+ last_error: row.last_error === null || row.last_error === undefined ? null : String(row.last_error),
92
+ };
93
+ }
94
+ function getMessage(db, queueName, messageId) {
95
+ const row = db
96
+ .prepare("SELECT * FROM project_queue_message WHERE queue_name = ? AND message_id = ?")
97
+ .get(queueName, messageId);
98
+ return row ? toMessage(row) : null;
99
+ }
100
+ function withDb(databasePath, fn) {
101
+ const DatabaseSync = loadDatabaseCtor();
102
+ const db = new DatabaseSync(databasePath);
103
+ try {
104
+ db.exec("PRAGMA foreign_keys = ON;");
105
+ return fn(db);
106
+ }
107
+ finally {
108
+ db.close();
109
+ }
110
+ }
111
+ function withImmediateTransaction(db, fn) {
112
+ db.exec("BEGIN IMMEDIATE");
113
+ try {
114
+ const result = fn();
115
+ db.exec("COMMIT");
116
+ return result;
117
+ }
118
+ catch (err) {
119
+ try {
120
+ db.exec("ROLLBACK");
121
+ }
122
+ catch {
123
+ // ignore rollback failures when no transaction is active
124
+ }
125
+ throw err;
126
+ }
127
+ }
128
+ function enqueueProjectQueueMessage(databasePath, input) {
129
+ assertNonEmpty(input.queue_name, "queue_name");
130
+ assertNonEmpty(input.message_id, "message_id");
131
+ if (input.dedupe_key !== undefined && input.dedupe_key !== null) {
132
+ assertNonEmpty(input.dedupe_key, "dedupe_key");
133
+ }
134
+ const currentNow = nowMs(input.now_ms);
135
+ const availableAt = input.available_at_ms ?? currentNow;
136
+ assertInteger(availableAt, "available_at_ms");
137
+ const maxAttempts = input.max_attempts ?? 3;
138
+ assertPositiveInteger(maxAttempts, "max_attempts");
139
+ const payloadJson = stableJson(input.payload);
140
+ const hash = payloadHash(payloadJson);
141
+ return withDb(databasePath, (db) => withImmediateTransaction(db, () => {
142
+ if (input.dedupe_key) {
143
+ const duplicate = db
144
+ .prepare("SELECT * FROM project_queue_message WHERE queue_name = ? AND dedupe_key = ?")
145
+ .get(input.queue_name, input.dedupe_key);
146
+ if (duplicate) {
147
+ return { created: false, duplicate: true, message: toMessage(duplicate) };
148
+ }
149
+ }
150
+ db
151
+ .prepare([
152
+ "INSERT INTO project_queue_message",
153
+ "(queue_name, message_id, dedupe_key, payload_json, payload_hash, status, available_at_ms, attempt_count, max_attempts, lease_owner, lease_deadline_ms, created_at_ms, updated_at_ms, last_error)",
154
+ "VALUES (?, ?, ?, ?, ?, 'ready', ?, 0, ?, NULL, NULL, ?, ?, NULL)",
155
+ ].join(" "))
156
+ .run(input.queue_name, input.message_id, input.dedupe_key ?? null, payloadJson, hash, availableAt, maxAttempts, currentNow, currentNow);
157
+ const message = getMessage(db, input.queue_name, input.message_id);
158
+ if (!message) {
159
+ throw new Error("queued message could not be reloaded after insert");
160
+ }
161
+ return { created: true, duplicate: false, message };
162
+ }));
163
+ }
164
+ function claimProjectQueueMessage(databasePath, input) {
165
+ assertNonEmpty(input.queue_name, "queue_name");
166
+ assertNonEmpty(input.lease_owner, "lease_owner");
167
+ assertPositiveInteger(input.lease_ms, "lease_ms");
168
+ const currentNow = nowMs(input.now_ms);
169
+ return withDb(databasePath, (db) => withImmediateTransaction(db, () => {
170
+ const row = db
171
+ .prepare([
172
+ "SELECT * FROM project_queue_message",
173
+ "WHERE queue_name = ?",
174
+ "AND (",
175
+ " (status = 'ready' AND available_at_ms <= ?)",
176
+ " OR (status = 'leased' AND lease_deadline_ms <= ?)",
177
+ ")",
178
+ "ORDER BY available_at_ms ASC, created_at_ms ASC, message_id ASC",
179
+ "LIMIT 1",
180
+ ].join(" "))
181
+ .get(input.queue_name, currentNow, currentNow);
182
+ if (!row) {
183
+ return null;
184
+ }
185
+ const message = toMessage(row);
186
+ db
187
+ .prepare("UPDATE project_queue_message SET status = 'leased', lease_owner = ?, lease_deadline_ms = ?, updated_at_ms = ? WHERE queue_name = ? AND message_id = ?")
188
+ .run(input.lease_owner, currentNow + input.lease_ms, currentNow, input.queue_name, message.message_id);
189
+ return getMessage(db, input.queue_name, message.message_id);
190
+ }));
191
+ }
192
+ function requireLeasedMessage(db, queueName, messageId, leaseOwner) {
193
+ const message = getMessage(db, queueName, messageId);
194
+ if (!message) {
195
+ throw new Error(`queue message not found: ${queueName}/${messageId}`);
196
+ }
197
+ if (message.status !== "leased" || message.lease_owner !== leaseOwner) {
198
+ throw new Error(`queue message ${messageId} is not leased by ${leaseOwner}`);
199
+ }
200
+ return message;
201
+ }
202
+ function ackProjectQueueMessage(databasePath, input) {
203
+ assertNonEmpty(input.queue_name, "queue_name");
204
+ assertNonEmpty(input.message_id, "message_id");
205
+ assertNonEmpty(input.lease_owner, "lease_owner");
206
+ const currentNow = nowMs(input.now_ms);
207
+ return withDb(databasePath, (db) => withImmediateTransaction(db, () => {
208
+ requireLeasedMessage(db, input.queue_name, input.message_id, input.lease_owner);
209
+ db
210
+ .prepare("UPDATE project_queue_message SET status = 'acked', lease_owner = NULL, lease_deadline_ms = NULL, updated_at_ms = ? WHERE queue_name = ? AND message_id = ?")
211
+ .run(currentNow, input.queue_name, input.message_id);
212
+ const message = getMessage(db, input.queue_name, input.message_id);
213
+ if (!message) {
214
+ throw new Error("acked message could not be reloaded");
215
+ }
216
+ return message;
217
+ }));
218
+ }
219
+ function failProjectQueueMessage(databasePath, input) {
220
+ assertNonEmpty(input.queue_name, "queue_name");
221
+ assertNonEmpty(input.message_id, "message_id");
222
+ assertNonEmpty(input.lease_owner, "lease_owner");
223
+ assertNonEmpty(input.error, "error");
224
+ const currentNow = nowMs(input.now_ms);
225
+ const retryAfter = input.retry_after_ms ?? 0;
226
+ assertInteger(retryAfter, "retry_after_ms");
227
+ if (retryAfter < 0) {
228
+ throw new Error("retry_after_ms must be greater than or equal to 0");
229
+ }
230
+ return withDb(databasePath, (db) => withImmediateTransaction(db, () => {
231
+ const message = requireLeasedMessage(db, input.queue_name, input.message_id, input.lease_owner);
232
+ const attemptCount = message.attempt_count + 1;
233
+ const status = attemptCount >= message.max_attempts ? "dead_letter" : "ready";
234
+ const availableAt = status === "ready" ? currentNow + retryAfter : currentNow;
235
+ db
236
+ .prepare([
237
+ "UPDATE project_queue_message",
238
+ "SET status = ?, attempt_count = ?, available_at_ms = ?, lease_owner = NULL, lease_deadline_ms = NULL, updated_at_ms = ?, last_error = ?",
239
+ "WHERE queue_name = ? AND message_id = ?",
240
+ ].join(" "))
241
+ .run(status, attemptCount, availableAt, currentNow, input.error, input.queue_name, input.message_id);
242
+ const updated = getMessage(db, input.queue_name, input.message_id);
243
+ if (!updated) {
244
+ throw new Error("failed message could not be reloaded");
245
+ }
246
+ return updated;
247
+ }));
248
+ }
249
+ function deadLetterProjectQueueMessage(databasePath, input) {
250
+ assertNonEmpty(input.queue_name, "queue_name");
251
+ assertNonEmpty(input.message_id, "message_id");
252
+ assertNonEmpty(input.lease_owner, "lease_owner");
253
+ assertNonEmpty(input.error, "error");
254
+ const currentNow = nowMs(input.now_ms);
255
+ return withDb(databasePath, (db) => withImmediateTransaction(db, () => {
256
+ requireLeasedMessage(db, input.queue_name, input.message_id, input.lease_owner);
257
+ db
258
+ .prepare("UPDATE project_queue_message SET status = 'dead_letter', lease_owner = NULL, lease_deadline_ms = NULL, updated_at_ms = ?, last_error = ? WHERE queue_name = ? AND message_id = ?")
259
+ .run(currentNow, input.error, input.queue_name, input.message_id);
260
+ const message = getMessage(db, input.queue_name, input.message_id);
261
+ if (!message) {
262
+ throw new Error("dead-lettered message could not be reloaded");
263
+ }
264
+ return message;
265
+ }));
266
+ }
267
+ function releaseExpiredProjectQueueLeases(databasePath, input = {}) {
268
+ const currentNow = nowMs(input.now_ms);
269
+ if (input.queue_name !== undefined) {
270
+ assertNonEmpty(input.queue_name, "queue_name");
271
+ }
272
+ return withDb(databasePath, (db) => withImmediateTransaction(db, () => {
273
+ const result = input.queue_name
274
+ ? db
275
+ .prepare("UPDATE project_queue_message SET status = 'ready', lease_owner = NULL, lease_deadline_ms = NULL, updated_at_ms = ? WHERE queue_name = ? AND status = 'leased' AND lease_deadline_ms <= ?")
276
+ .run(currentNow, input.queue_name, currentNow)
277
+ : db
278
+ .prepare("UPDATE project_queue_message SET status = 'ready', lease_owner = NULL, lease_deadline_ms = NULL, updated_at_ms = ? WHERE status = 'leased' AND lease_deadline_ms <= ?")
279
+ .run(currentNow, currentNow);
280
+ return { released_count: Number(result.changes ?? 0) };
281
+ }));
282
+ }
283
+ function readProjectQueueStats(databasePath, input = {}) {
284
+ if (input.queue_name !== undefined) {
285
+ assertNonEmpty(input.queue_name, "queue_name");
286
+ }
287
+ const currentNow = nowMs(input.now_ms);
288
+ return withDb(databasePath, (db) => {
289
+ const params = input.queue_name ? [input.queue_name] : [];
290
+ const where = input.queue_name ? "WHERE queue_name = ?" : "";
291
+ const rows = db
292
+ .prepare(`SELECT status, COUNT(*) AS count FROM project_queue_message ${where} GROUP BY status`)
293
+ .all(...params);
294
+ const byStatus = {
295
+ ready: 0,
296
+ leased: 0,
297
+ acked: 0,
298
+ dead_letter: 0,
299
+ };
300
+ for (const row of rows) {
301
+ byStatus[String(row.status)] = Number(row.count);
302
+ }
303
+ const total = Object.values(byStatus).reduce((sum, count) => sum + count, 0);
304
+ const readyAvailable = db
305
+ .prepare(`SELECT COUNT(*) AS count FROM project_queue_message ${where} ${where ? "AND" : "WHERE"} status = 'ready' AND available_at_ms <= ?`)
306
+ .get(...params, currentNow);
307
+ const leasedExpired = db
308
+ .prepare(`SELECT COUNT(*) AS count FROM project_queue_message ${where} ${where ? "AND" : "WHERE"} status = 'leased' AND lease_deadline_ms <= ?`)
309
+ .get(...params, currentNow);
310
+ return {
311
+ total,
312
+ by_status: byStatus,
313
+ ready_available: Number(readyAvailable?.count ?? 0),
314
+ leased_expired: Number(leasedExpired?.count ?? 0),
315
+ };
316
+ });
317
+ }
@@ -34,9 +34,12 @@ Agent operating prompt:
34
34
  - Treat `.mdkg/db` as project application state; use `mdkg db init` to create
35
35
  the generic scaffold and enable `db.enabled` without creating an active
36
36
  runtime SQLite database. Use `mdkg db migrate` after init to create or update
37
- the runtime SQLite database with mdkg-owned generic foundation migrations.
38
- Use `mdkg db verify` and `mdkg db stats` for non-mutating health and summary
39
- receipts. Use `mdkg db snapshot seal` for explicit sealed checkpoints,
37
+ the runtime SQLite database with mdkg-owned foundation and internal local
38
+ node:sqlite queue foundation migrations. Queue state is delivery
39
+ infrastructure, not canonical event history, and there is no public
40
+ `mdkg db queue` CLI yet. Use `mdkg db verify` and `mdkg db stats` for
41
+ non-mutating health and summary receipts. Use `mdkg db snapshot seal` for
42
+ explicit sealed checkpoints,
40
43
  `mdkg db snapshot verify/status` for checkpoint health, and
41
44
  `mdkg db snapshot dump/diff` for deterministic review aids. Keep
42
45
  `.mdkg/db/runtime/` and WAL/SHM/journal/lock/temp files ignored unless a
@@ -49,9 +49,12 @@ Project database commands:
49
49
  `.mdkg/db/project-db.json`, enables `db.enabled`, and does not create an
50
50
  active runtime SQLite database
51
51
  - `mdkg db migrate` creates or updates the configured active runtime SQLite
52
- database and applies mdkg-owned generic foundation migrations only
52
+ database and applies mdkg-owned foundation plus internal local node:sqlite
53
+ queue foundation migrations
53
54
  - `mdkg db migrate` records migration order, checksums, and applied timestamps
54
55
  in the configured migration table
56
+ - queue tables are durable local delivery state, not canonical event history;
57
+ there is no public `mdkg db queue` CLI yet
55
58
  - `mdkg db verify` checks config, layout, runtime SQLite integrity, migration
56
59
  metadata, receipt directory policy, and transient runtime files
57
60
  - `mdkg db stats` reports table counts, database size, migration state,
@@ -75,8 +75,10 @@ Fresh mdkg workspaces default to `index.backend: sqlite`; `.mdkg/index/mdkg.sqli
75
75
  `.mdkg/index`. Run `mdkg db init` to create the generic scaffold, write
76
76
  `.mdkg/db/project-db.json`, and enable `db.enabled`; it does not create an
77
77
  active runtime SQLite database. Run `mdkg db migrate` after init to create or
78
- update the active runtime SQLite database with mdkg-owned generic foundation
79
- migrations. Use `mdkg db verify` for non-mutating health checks and
78
+ update the active runtime SQLite database with mdkg-owned foundation and
79
+ internal local node:sqlite queue foundation migrations. Queue state is delivery
80
+ infrastructure, not canonical event history, and there is no public
81
+ `mdkg db queue` CLI yet. Use `mdkg db verify` for non-mutating health checks and
80
82
  `mdkg db stats` for table counts, DB size, migration state, and receipt-file
81
83
  counts. Use `mdkg db snapshot seal` to create an opt-in sealed checkpoint under
82
84
  `.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.7",
4
+ "mdkg_version": "0.1.8",
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": "57a55e0f3743415f5ba1d166d3abf61b2ad868cc11d0e7083b7fd8075dae95a1"
64
+ "sha256": "6967c433a9a2d63f0fa4680107a318a75af358873f65326678873986a2fe619d"
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": "d9ba16476749bd96c986f356b40a7abe4b1321b10260f61f85198220c11ac994"
189
+ "sha256": "e14276e5b8e6e004997cc93eab7dff468322f01fb203e44007594d67bc2ba149"
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": "51c8f3e6fff4c2a0c385e39c2c41a0b26136ca9d2efcd4593f0099c3cdc0a4a8"
204
+ "sha256": "a942886a2ef48eb895ecaae491d8841d11ee446b5879e4fe48fd2a1ac82c8f53"
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.7",
3
+ "version": "0.1.8",
4
4
  "description": "Markdown Knowledge Graph",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -17,6 +17,7 @@
17
17
  "smoke:init": "npm run build && node scripts/smoke-init.js",
18
18
  "smoke:capabilities": "npm run build && node scripts/smoke-capabilities.js",
19
19
  "smoke:db": "npm run build && node scripts/smoke-db.js",
20
+ "smoke:db-queue": "npm run build && node scripts/smoke-db-queue.js",
20
21
  "smoke:db-snapshot": "npm run build && node scripts/smoke-db-snapshot.js",
21
22
  "smoke:archive-work": "npm run build && node scripts/smoke-archive-work.js",
22
23
  "smoke:bundle": "npm run build && node scripts/smoke-bundle.js",
@@ -28,7 +29,7 @@
28
29
  "cli:snapshot": "npm run build && node scripts/cli_help_snapshot.js",
29
30
  "cli:check": "npm run build && node scripts/cli_help_snapshot.js --check",
30
31
  "prepack": "npm run build && node scripts/assert-publish-ready.js",
31
- "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-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",
32
+ "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-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",
32
33
  "postinstall": "node scripts/postinstall.js",
33
34
  "smoke:subgraph": "npm run build && node scripts/smoke-subgraph.js"
34
35
  },