mdkg 0.1.9 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,6 +10,14 @@ exports.failProjectQueueMessage = failProjectQueueMessage;
10
10
  exports.deadLetterProjectQueueMessage = deadLetterProjectQueueMessage;
11
11
  exports.releaseExpiredProjectQueueLeases = releaseExpiredProjectQueueLeases;
12
12
  exports.readProjectQueueStats = readProjectQueueStats;
13
+ exports.createProjectQueue = createProjectQueue;
14
+ exports.pauseProjectQueue = pauseProjectQueue;
15
+ exports.resumeProjectQueue = resumeProjectQueue;
16
+ exports.readProjectQueue = readProjectQueue;
17
+ exports.listProjectQueues = listProjectQueues;
18
+ exports.readProjectQueueMessage = readProjectQueueMessage;
19
+ exports.listProjectQueueMessages = listProjectQueueMessages;
20
+ exports.readProjectQueueSnapshotSummary = readProjectQueueSnapshotSummary;
13
21
  const crypto_1 = __importDefault(require("crypto"));
14
22
  function loadDatabaseCtor() {
15
23
  try {
@@ -91,12 +99,44 @@ function toMessage(row) {
91
99
  last_error: row.last_error === null || row.last_error === undefined ? null : String(row.last_error),
92
100
  };
93
101
  }
102
+ function toQueue(row) {
103
+ return {
104
+ queue_name: String(row.queue_name),
105
+ status: String(row.status),
106
+ paused_reason: row.paused_reason === null || row.paused_reason === undefined ? null : String(row.paused_reason),
107
+ created_at_ms: Number(row.created_at_ms),
108
+ updated_at_ms: Number(row.updated_at_ms),
109
+ };
110
+ }
94
111
  function getMessage(db, queueName, messageId) {
95
112
  const row = db
96
113
  .prepare("SELECT * FROM project_queue_message WHERE queue_name = ? AND message_id = ?")
97
114
  .get(queueName, messageId);
98
115
  return row ? toMessage(row) : null;
99
116
  }
117
+ function getQueue(db, queueName) {
118
+ const row = db.prepare("SELECT * FROM project_queue WHERE queue_name = ?").get(queueName);
119
+ return row ? toQueue(row) : null;
120
+ }
121
+ function ensureQueue(db, queueName, currentNow) {
122
+ const existing = getQueue(db, queueName);
123
+ if (existing) {
124
+ return existing;
125
+ }
126
+ db
127
+ .prepare("INSERT INTO project_queue (queue_name, status, paused_reason, created_at_ms, updated_at_ms) VALUES (?, 'active', NULL, ?, ?)")
128
+ .run(queueName, currentNow, currentNow);
129
+ const queue = getQueue(db, queueName);
130
+ if (!queue) {
131
+ throw new Error(`queue could not be loaded after create: ${queueName}`);
132
+ }
133
+ return queue;
134
+ }
135
+ function requireQueueActive(queue, action) {
136
+ if (queue.status === "paused") {
137
+ throw new Error(`queue ${queue.queue_name} is paused; cannot ${action}`);
138
+ }
139
+ }
100
140
  function withDb(databasePath, fn) {
101
141
  const DatabaseSync = loadDatabaseCtor();
102
142
  const db = new DatabaseSync(databasePath);
@@ -139,6 +179,7 @@ function enqueueProjectQueueMessage(databasePath, input) {
139
179
  const payloadJson = stableJson(input.payload);
140
180
  const hash = payloadHash(payloadJson);
141
181
  return withDb(databasePath, (db) => withImmediateTransaction(db, () => {
182
+ requireQueueActive(ensureQueue(db, input.queue_name, currentNow), "enqueue");
142
183
  if (input.dedupe_key) {
143
184
  const duplicate = db
144
185
  .prepare("SELECT * FROM project_queue_message WHERE queue_name = ? AND dedupe_key = ?")
@@ -167,6 +208,7 @@ function claimProjectQueueMessage(databasePath, input) {
167
208
  assertPositiveInteger(input.lease_ms, "lease_ms");
168
209
  const currentNow = nowMs(input.now_ms);
169
210
  return withDb(databasePath, (db) => withImmediateTransaction(db, () => {
211
+ requireQueueActive(ensureQueue(db, input.queue_name, currentNow), "claim");
170
212
  const row = db
171
213
  .prepare([
172
214
  "SELECT * FROM project_queue_message",
@@ -315,3 +357,147 @@ function readProjectQueueStats(databasePath, input = {}) {
315
357
  };
316
358
  });
317
359
  }
360
+ function createProjectQueue(databasePath, input) {
361
+ assertNonEmpty(input.queue_name, "queue_name");
362
+ const currentNow = nowMs(input.now_ms);
363
+ if (input.reason !== undefined && input.reason !== null) {
364
+ assertNonEmpty(input.reason, "reason");
365
+ }
366
+ return withDb(databasePath, (db) => withImmediateTransaction(db, () => {
367
+ const existing = getQueue(db, input.queue_name);
368
+ if (existing) {
369
+ return { created: false, queue: existing };
370
+ }
371
+ const status = input.paused ? "paused" : "active";
372
+ db
373
+ .prepare("INSERT INTO project_queue (queue_name, status, paused_reason, created_at_ms, updated_at_ms) VALUES (?, ?, ?, ?, ?)")
374
+ .run(input.queue_name, status, input.paused ? input.reason ?? null : null, currentNow, currentNow);
375
+ const queue = getQueue(db, input.queue_name);
376
+ if (!queue) {
377
+ throw new Error(`queue could not be loaded after create: ${input.queue_name}`);
378
+ }
379
+ return { created: true, queue };
380
+ }));
381
+ }
382
+ function pauseProjectQueue(databasePath, input) {
383
+ assertNonEmpty(input.queue_name, "queue_name");
384
+ if (input.reason !== undefined && input.reason !== null) {
385
+ assertNonEmpty(input.reason, "reason");
386
+ }
387
+ const currentNow = nowMs(input.now_ms);
388
+ return withDb(databasePath, (db) => withImmediateTransaction(db, () => {
389
+ ensureQueue(db, input.queue_name, currentNow);
390
+ db
391
+ .prepare("UPDATE project_queue SET status = 'paused', paused_reason = ?, updated_at_ms = ? WHERE queue_name = ?")
392
+ .run(input.reason ?? null, currentNow, input.queue_name);
393
+ const queue = getQueue(db, input.queue_name);
394
+ if (!queue) {
395
+ throw new Error(`queue could not be loaded after pause: ${input.queue_name}`);
396
+ }
397
+ return queue;
398
+ }));
399
+ }
400
+ function resumeProjectQueue(databasePath, input) {
401
+ assertNonEmpty(input.queue_name, "queue_name");
402
+ const currentNow = nowMs(input.now_ms);
403
+ return withDb(databasePath, (db) => withImmediateTransaction(db, () => {
404
+ ensureQueue(db, input.queue_name, currentNow);
405
+ db
406
+ .prepare("UPDATE project_queue SET status = 'active', paused_reason = NULL, updated_at_ms = ? WHERE queue_name = ?")
407
+ .run(currentNow, input.queue_name);
408
+ const queue = getQueue(db, input.queue_name);
409
+ if (!queue) {
410
+ throw new Error(`queue could not be loaded after resume: ${input.queue_name}`);
411
+ }
412
+ return queue;
413
+ }));
414
+ }
415
+ function readProjectQueue(databasePath, queueName) {
416
+ assertNonEmpty(queueName, "queue_name");
417
+ return withDb(databasePath, (db) => getQueue(db, queueName));
418
+ }
419
+ function listProjectQueues(databasePath) {
420
+ return withDb(databasePath, (db) => db.prepare("SELECT * FROM project_queue ORDER BY queue_name ASC").all().map(toQueue));
421
+ }
422
+ function readProjectQueueMessage(databasePath, queueName, messageId) {
423
+ assertNonEmpty(queueName, "queue_name");
424
+ assertNonEmpty(messageId, "message_id");
425
+ return withDb(databasePath, (db) => getMessage(db, queueName, messageId));
426
+ }
427
+ function listProjectQueueMessages(databasePath, input) {
428
+ assertNonEmpty(input.queue_name, "queue_name");
429
+ const limit = input.limit ?? 50;
430
+ assertPositiveInteger(limit, "limit");
431
+ if (input.status && input.status !== "all" && !["ready", "leased", "acked", "dead_letter"].includes(input.status)) {
432
+ throw new Error("status must be ready, leased, acked, dead_letter, or all");
433
+ }
434
+ return withDb(databasePath, (db) => {
435
+ const sql = input.status && input.status !== "all"
436
+ ? [
437
+ "SELECT * FROM project_queue_message",
438
+ "WHERE queue_name = ? AND status = ?",
439
+ "ORDER BY available_at_ms ASC, created_at_ms ASC, message_id ASC",
440
+ "LIMIT ?",
441
+ ].join(" ")
442
+ : [
443
+ "SELECT * FROM project_queue_message",
444
+ "WHERE queue_name = ?",
445
+ "ORDER BY available_at_ms ASC, created_at_ms ASC, message_id ASC",
446
+ "LIMIT ?",
447
+ ].join(" ");
448
+ const params = input.status && input.status !== "all" ? [input.queue_name, input.status, limit] : [input.queue_name, limit];
449
+ return db.prepare(sql).all(...params).map(toMessage);
450
+ });
451
+ }
452
+ function readProjectQueueSnapshotSummary(databasePath) {
453
+ return withDb(databasePath, (db) => {
454
+ const rows = db
455
+ .prepare([
456
+ "SELECT q.queue_name, q.status, q.paused_reason,",
457
+ "COUNT(m.message_id) AS total,",
458
+ "SUM(CASE WHEN m.status = 'ready' THEN 1 ELSE 0 END) AS ready,",
459
+ "SUM(CASE WHEN m.status = 'leased' THEN 1 ELSE 0 END) AS leased,",
460
+ "SUM(CASE WHEN m.status = 'acked' THEN 1 ELSE 0 END) AS acked,",
461
+ "SUM(CASE WHEN m.status = 'dead_letter' THEN 1 ELSE 0 END) AS dead_letter",
462
+ "FROM project_queue q",
463
+ "LEFT JOIN project_queue_message m ON m.queue_name = q.queue_name",
464
+ "GROUP BY q.queue_name, q.status, q.paused_reason",
465
+ "ORDER BY q.queue_name ASC",
466
+ ].join(" "))
467
+ .all();
468
+ const queues = rows.map((row) => ({
469
+ queue_name: String(row.queue_name),
470
+ status: String(row.status),
471
+ paused_reason: row.paused_reason === null || row.paused_reason === undefined ? null : String(row.paused_reason),
472
+ total: Number(row.total ?? 0),
473
+ ready: Number(row.ready ?? 0),
474
+ leased: Number(row.leased ?? 0),
475
+ acked: Number(row.acked ?? 0),
476
+ dead_letter: Number(row.dead_letter ?? 0),
477
+ }));
478
+ const summary = {
479
+ queues,
480
+ total: 0,
481
+ ready: 0,
482
+ leased: 0,
483
+ acked: 0,
484
+ dead_letter: 0,
485
+ paused_ready: 0,
486
+ active_ready: 0,
487
+ };
488
+ for (const queue of queues) {
489
+ summary.total += queue.total;
490
+ summary.ready += queue.ready;
491
+ summary.leased += queue.leased;
492
+ summary.acked += queue.acked;
493
+ summary.dead_letter += queue.dead_letter;
494
+ if (queue.status === "paused") {
495
+ summary.paused_ready += queue.ready;
496
+ }
497
+ else {
498
+ summary.active_ready += queue.ready;
499
+ }
500
+ }
501
+ return summary;
502
+ });
503
+ }
@@ -18,6 +18,7 @@ const version_1 = require("./version");
18
18
  const atomic_1 = require("../util/atomic");
19
19
  const errors_1 = require("../util/errors");
20
20
  const project_db_migrations_1 = require("./project_db_migrations");
21
+ const project_db_queue_1 = require("./project_db_queue");
21
22
  function loadDatabaseCtor() {
22
23
  try {
23
24
  const loaded = require("node:sqlite");
@@ -170,7 +171,7 @@ function readManifest(filePath) {
170
171
  }
171
172
  return manifest;
172
173
  }
173
- function buildManifest(root, config, snapshotFile, runtimeHash) {
174
+ function buildManifest(root, config, snapshotFile, runtimeHash, queuePolicy, queueSummary) {
174
175
  const layout = (0, project_db_1.resolveConfiguredProjectDbLayout)(root, config.db);
175
176
  const metadata = collectSnapshotMetadata(snapshotFile, config.db.migration_table);
176
177
  return {
@@ -188,17 +189,39 @@ function buildManifest(root, config, snapshotFile, runtimeHash) {
188
189
  byte_size: fs_1.default.statSync(snapshotFile).size,
189
190
  table_counts: metadata.table_counts,
190
191
  migrations: metadata.migrations,
192
+ queue_policy: queuePolicy,
193
+ queue_summary: queueSummary,
191
194
  };
192
195
  }
196
+ function assertQueueSnapshotPolicy(policy, summary) {
197
+ if (policy === "drain") {
198
+ if (summary.ready > 0 || summary.leased > 0) {
199
+ throw new errors_1.ValidationError(`db snapshot seal requires drained queues; found ready=${summary.ready}, leased=${summary.leased}`);
200
+ }
201
+ return;
202
+ }
203
+ if (policy === "paused") {
204
+ if (summary.leased > 0) {
205
+ throw new errors_1.ValidationError(`db snapshot seal --queue-policy paused requires no leased messages; found leased=${summary.leased}`);
206
+ }
207
+ if (summary.active_ready > 0) {
208
+ throw new errors_1.ValidationError(`db snapshot seal --queue-policy paused requires ready messages to be in paused queues; found active_ready=${summary.active_ready}`);
209
+ }
210
+ return;
211
+ }
212
+ throw new errors_1.ValidationError(`unsupported queue snapshot policy: ${policy}`);
213
+ }
193
214
  function warningListFromVerify(root, config) {
194
215
  return (0, project_db_migrations_1.verifyProjectDb)(root, config).warnings;
195
216
  }
196
- function sealProjectDbSnapshot(root, config) {
217
+ function sealProjectDbSnapshot(root, config, queuePolicy = "drain") {
197
218
  const verification = (0, project_db_migrations_1.verifyProjectDb)(root, config);
198
219
  if (!verification.ok) {
199
220
  throw new errors_1.ValidationError(`db snapshot seal requires a valid project DB; run mdkg db verify`);
200
221
  }
201
222
  const layout = (0, project_db_1.resolveConfiguredProjectDbLayout)(root, config.db);
223
+ const queueSummary = (0, project_db_queue_1.readProjectQueueSnapshotSummary)(layout.runtimeFile);
224
+ assertQueueSnapshotPolicy(queuePolicy, queueSummary);
202
225
  const oldHash = fs_1.default.existsSync(layout.stateFile) ? sha256File(layout.stateFile) : null;
203
226
  fs_1.default.mkdirSync(layout.stateDir, { recursive: true });
204
227
  const tempSnapshot = path_1.default.join(layout.stateDir, `.project.sqlite.${process.pid}-${Date.now()}.tmp`);
@@ -228,7 +251,7 @@ function sealProjectDbSnapshot(root, config) {
228
251
  }
229
252
  try {
230
253
  const runtimeHash = sha256File(layout.runtimeFile);
231
- const manifest = buildManifest(root, config, tempSnapshot, runtimeHash);
254
+ const manifest = buildManifest(root, config, tempSnapshot, runtimeHash, queuePolicy, queueSummary);
232
255
  (0, atomic_1.atomicWriteFile)(tempManifest, `${JSON.stringify(manifest, null, 2)}\n`);
233
256
  fs_1.default.renameSync(tempSnapshot, layout.stateFile);
234
257
  fs_1.default.renameSync(tempManifest, layout.stateManifest);
@@ -243,6 +266,8 @@ function sealProjectDbSnapshot(root, config) {
243
266
  byte_size: manifest.byte_size,
244
267
  table_counts: manifest.table_counts,
245
268
  migrations: manifest.migrations,
269
+ queue_policy: queuePolicy,
270
+ queue_summary: queueSummary,
246
271
  warnings: warningListFromVerify(root, config),
247
272
  };
248
273
  }
@@ -34,15 +34,17 @@ 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 foundation plus internal local
38
- node:sqlite queue, event/receipt/reducer, and writer lease/CAS foundation
39
- migrations. Queue state is delivery infrastructure, not canonical event
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
37
+ the runtime SQLite database with mdkg-owned foundation plus public local
38
+ node:sqlite queue delivery, internal event/receipt/reducer, writer lease/CAS,
39
+ and queue control migrations. Queue state is delivery infrastructure, not
40
+ canonical event history; use `mdkg db queue ...` to create, pause, enqueue,
41
+ claim, settle, inspect, and drain local queues. Event rows are durable local
42
+ project DB history; receipts, reducers, writer leases, and materializers are
43
+ internal local helper surfaces, with no public `mdkg db event`,
44
+ `mdkg db reducer`, `mdkg db lease`, or `mdkg db materializer` CLI yet. Use `mdkg db verify` and `mdkg db stats` for
44
45
  non-mutating health and summary receipts. Use `mdkg db snapshot seal` for
45
- explicit sealed checkpoints,
46
+ explicit sealed checkpoints; default queue policy is drain, and
47
+ `--queue-policy paused` is only for intentionally paused queues,
46
48
  `mdkg db snapshot verify/status` for checkpoint health, and
47
49
  `mdkg db snapshot dump/diff` for deterministic review aids. Keep
48
50
  `.mdkg/db/runtime/` and WAL/SHM/journal/lock/temp files ignored unless a
@@ -39,7 +39,8 @@ Project database commands:
39
39
  - `mdkg db migrate [--json]`
40
40
  - `mdkg db verify [--json]`
41
41
  - `mdkg db stats [--json]`
42
- - `mdkg db snapshot seal [--json]`
42
+ - `mdkg db queue create|pause|resume|enqueue|claim|ack|fail|dead-letter|release-expired|stats|list|show ... [--json]`
43
+ - `mdkg db snapshot seal [--queue-policy drain|paused] [--json]`
43
44
  - `mdkg db snapshot verify [--json]`
44
45
  - `mdkg db snapshot status [--json]`
45
46
  - `mdkg db snapshot dump [--snapshot <path>] [--output <path>] [--json]`
@@ -53,19 +54,23 @@ Project database commands:
53
54
  queue, event/receipt/reducer, and writer lease/CAS foundation migrations
54
55
  - `mdkg db migrate` records migration order, checksums, and applied timestamps
55
56
  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
57
+ - `mdkg db queue ...` exposes durable local delivery operations backed by
58
+ node:sqlite; queue rows are delivery state, not canonical event history
59
+ - paused queues reject enqueue and claim, but ack/fail/dead-letter and
60
+ release-expired remain available so leased work can settle
58
61
  - 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
62
+ receipts, typed reducers, writer leases, and materializers remain internal
63
+ helper surfaces in this release, with no public `mdkg db event`,
64
+ `mdkg db reducer`, `mdkg db lease`, or `mdkg db materializer` CLI yet
62
65
  - `mdkg db verify` checks config, layout, runtime SQLite integrity, migration
63
66
  metadata, receipt directory policy, and transient runtime files
64
67
  - `mdkg db stats` reports table counts, database size, migration state,
65
68
  transient runtime files, receipt-file count, and state snapshot presence
66
69
  - `mdkg db snapshot seal` writes an opt-in sealed checkpoint and manifest under
67
- `.mdkg/db/state`; `snapshot verify/status/dump/diff` inspect and review that
68
- checkpoint without treating raw binary diffs as human-readable truth
70
+ `.mdkg/db/state`; default `--queue-policy drain` requires no ready or leased
71
+ messages, while `--queue-policy paused` allows ready messages only in paused
72
+ queues. `snapshot verify/status/dump/diff` inspect and review that checkpoint
73
+ without treating raw binary diffs as human-readable truth
69
74
  - active `.mdkg/db/runtime/` files and `.mdkg/db` WAL/SHM/journal/lock/temp files are ignored by default
70
75
 
71
76
  Validation commands:
@@ -75,17 +75,19 @@ 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 foundation plus
79
- internal local node:sqlite queue, event/receipt/reducer, and writer lease/CAS
80
- foundation migrations. Queue state is delivery infrastructure, not canonical
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
78
+ update the active runtime SQLite database with mdkg-owned foundation plus public
79
+ local node:sqlite queue delivery, internal event/receipt/reducer, writer
80
+ lease/CAS, and queue control migrations. Queue state is delivery
81
+ infrastructure, not canonical event history; use `mdkg db queue ...` to create,
82
+ pause, enqueue, claim, settle, inspect, and drain local queues. Event rows are
83
+ durable local project DB history; receipts, reducers, writer leases, and
84
+ materializers are internal local helper surfaces, with no public `mdkg db event`,
85
+ `mdkg db reducer`, `mdkg db lease`, or `mdkg db materializer` CLI yet. Use `mdkg db verify` for non-mutating health checks and
85
86
  `mdkg db stats` for table counts, DB size, migration state, and receipt-file
86
87
  counts. Use `mdkg db snapshot seal` to create an opt-in sealed checkpoint under
87
- `.mdkg/db/state`, then use `mdkg db snapshot verify/status` for integrity and
88
- freshness checks. Use `mdkg db snapshot dump/diff` as deterministic review aids
88
+ `.mdkg/db/state`; the default queue policy is drain, and
89
+ `--queue-policy paused` is only for intentionally paused queues. Then use
90
+ `mdkg db snapshot verify/status` for integrity and freshness checks. Use `mdkg db snapshot dump/diff` as deterministic review aids
89
91
  for SQLite snapshots. Keep active runtime DB files and transient
90
92
  WAL/SHM/journal, lock, and temp files ignored. Commit schema files, manifests,
91
93
  receipts, and sealed state snapshots only by explicit repo policy.
@@ -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.2.0",
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": "14ba3fa0657a43345ae81631024519955868fa34081b7517cb253b0be96cf163"
65
65
  },
66
66
  {
67
67
  "path": ".mdkg/skills/build-pack-and-execute-task/SKILL.md",
@@ -81,7 +81,7 @@
81
81
  {
82
82
  "path": ".mdkg/skills/verify-close-and-checkpoint/SKILL.md",
83
83
  "category": "default_skill",
84
- "sha256": "f85ffa4a139db10c3bc3203cde0e4f41603fbc7f3925e6fdaba821346ffa28b8"
84
+ "sha256": "3e4137b7b6a71f088dee79b5ee2f4743aefa3b43adae07337307db89a24416b6"
85
85
  },
86
86
  {
87
87
  "path": ".mdkg/templates/default/archive.md",
@@ -183,10 +183,65 @@
183
183
  "category": "template",
184
184
  "sha256": "9d8b971ded7a587105fb8b14bb79f68b612fa6e1962cf06859bab82e2aee57c7"
185
185
  },
186
+ {
187
+ "path": ".mdkg/templates/skills/base.SKILL.md",
188
+ "category": "template",
189
+ "sha256": "08a1bd65297173a1dc9df95776775d406337a419d4bc51863593b6f28777ebdb"
190
+ },
191
+ {
192
+ "path": ".mdkg/templates/specs/agent.SPEC.md",
193
+ "category": "template",
194
+ "sha256": "6facd5a424fa91d23f7df3e357c98ca4d31691410fad024d5bc8ff3781b0f571"
195
+ },
196
+ {
197
+ "path": ".mdkg/templates/specs/api.SPEC.md",
198
+ "category": "template",
199
+ "sha256": "42f6119478241a3c7dd12a2a25bd03b30d9c2bc60528287136ea044f20b215c4"
200
+ },
201
+ {
202
+ "path": ".mdkg/templates/specs/base.SPEC.md",
203
+ "category": "template",
204
+ "sha256": "cff1ee53e9369184e2d92194f165f131254d4439bc79bcbbaaae4a13d616c97b"
205
+ },
206
+ {
207
+ "path": ".mdkg/templates/specs/capability.SPEC.md",
208
+ "category": "template",
209
+ "sha256": "755c24dea04f687da4714d0a12489e1ae36c5f17b838c2e24bb993378a7d9510"
210
+ },
211
+ {
212
+ "path": ".mdkg/templates/specs/integration.SPEC.md",
213
+ "category": "template",
214
+ "sha256": "990005a805bc6e6397f03e0de1ca69d536ae9d55719782a3075781289b44b941"
215
+ },
216
+ {
217
+ "path": ".mdkg/templates/specs/model.SPEC.md",
218
+ "category": "template",
219
+ "sha256": "eb73ed195139cf5655ba70c948aaaa28fc2c85d2db1b6258d703f10e73bf3713"
220
+ },
221
+ {
222
+ "path": ".mdkg/templates/specs/omniruntime-agent.SPEC.md",
223
+ "category": "template",
224
+ "sha256": "343c7c148faae0c2b24364c276ce345757b5725a750cfebeee4cdeb3dde55009"
225
+ },
226
+ {
227
+ "path": ".mdkg/templates/specs/project.SPEC.md",
228
+ "category": "template",
229
+ "sha256": "5ade0aa05907b9ec418c41ee051493d8ecc8ad3d45608ab74fc3cbe2974e79a4"
230
+ },
231
+ {
232
+ "path": ".mdkg/templates/specs/runtime-image.SPEC.md",
233
+ "category": "template",
234
+ "sha256": "24c1b2fb0bd0d63bbce41ee36b43f9edf531b26438bca2706ad1ab556eaabbf0"
235
+ },
236
+ {
237
+ "path": ".mdkg/templates/specs/tool.SPEC.md",
238
+ "category": "template",
239
+ "sha256": "18340481891bba180dd077d82b86248ede885131fb75675b775ea0c3749ee5c0"
240
+ },
186
241
  {
187
242
  "path": "AGENT_START.md",
188
243
  "category": "startup_doc",
189
- "sha256": "1e1d9d94e2aa761b1a7978ab4b44e8bb7c3f0d33e94ed2d98cb06d641f4c42c6"
244
+ "sha256": "5948ed5222b2cbce0468191d00287ce5dbc1aaa434640f4daa676afb92d48d4b"
190
245
  },
191
246
  {
192
247
  "path": "AGENTS.md",
@@ -201,7 +256,7 @@
201
256
  {
202
257
  "path": "CLI_COMMAND_MATRIX.md",
203
258
  "category": "startup_doc",
204
- "sha256": "9f90882d698d0ad1b16bad0fbe37065e4ff9f2b79b27373ef7dce74b78e99265"
259
+ "sha256": "baf9792f0bd50ce44de2dc16babc73f631a1191acff853fdc4caa7d452f50561"
205
260
  },
206
261
  {
207
262
  "path": "llms.txt",
@@ -46,13 +46,14 @@ Finish work with evidence, validation, and minimal memory drift.
46
46
  Use this local repo-only checklist before publishing mdkg:
47
47
 
48
48
  1. Confirm package intent and version in `package.json`, `package-lock.json`, `README.md`, `CLI_COMMAND_MATRIX.md`, and `CHANGELOG.md`.
49
- 2. Use a clean npm cache: `export NPM_CONFIG_CACHE=/private/tmp/mdkg-npm-cache`.
50
- 3. Run `npm ci`, `npm run build`, `node scripts/assert-publish-ready.js`, `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:archive-work`, `npm run smoke:bundle`, `npm run smoke:subgraph`, and `npm run smoke:visibility`.
51
- 4. Run `npm pack --dry-run --json` and confirm the tarball includes `dist/cli.js`, compiled folders, `dist/init/`, release docs, and `scripts/postinstall.js`.
52
- 5. Confirm registry state with `npm view mdkg version --registry=https://registry.npmjs.org/`.
53
- 6. Publish only after the registry still shows the previous version and npm auth is known to have write access.
54
- 7. If publishing fails with 2FA or token policy errors, do not commit; fix npm auth or package policy, then rerun publish.
55
- 8. After successful publish, verify `npm view mdkg version` and `npm view mdkg dist-tags`, then commit the release changes.
49
+ 2. Confirm release-line intent before bumping: when a change crosses a capability-track boundary, prefer the next minor release line over patch-style continuation. For the current project DB track, follow `0.1.9 -> 0.2.0` rather than naming the next planned source line `0.1.10`.
50
+ 3. Use a clean npm cache: `export NPM_CONFIG_CACHE=/private/tmp/mdkg-npm-cache`.
51
+ 4. Run `npm ci`, `npm run build`, `node scripts/assert-publish-ready.js`, `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:archive-work`, `npm run smoke:bundle`, `npm run smoke:subgraph`, and `npm run smoke:visibility`.
52
+ 5. Run `npm pack --dry-run --json` and confirm the tarball includes `dist/cli.js`, compiled folders, `dist/init/`, release docs, and `scripts/postinstall.js`.
53
+ 6. Confirm registry state with `npm view mdkg version --registry=https://registry.npmjs.org/`.
54
+ 7. Publish only after the registry still shows the previous version and npm auth is known to have write access.
55
+ 8. If publishing fails with 2FA or token policy errors, do not commit; fix npm auth or package policy, then rerun publish.
56
+ 9. After successful publish, verify `npm view mdkg version` and `npm view mdkg dist-tags`, then commit the release changes.
56
57
 
57
58
  ## Bundle-Aware Commit Gate
58
59
 
@@ -0,0 +1,66 @@
1
+ ---
2
+ name: {{skill_slug}}
3
+ description: {{description}}
4
+ tags: [stage:plan, writer:orchestrator]
5
+ version: 0.1.0
6
+ authors: [{{owner}}]
7
+ links: []
8
+ ---
9
+
10
+ # Purpose
11
+
12
+ Describe the repeatable workflow and the durable outcome this skill produces.
13
+
14
+ ## When To Use
15
+
16
+ - Trigger condition.
17
+
18
+ ## Inputs
19
+
20
+ - Required context or artifacts.
21
+
22
+ ## Outputs
23
+
24
+ - Result, patch, artifact, report, or handoff.
25
+
26
+ ## Required Capabilities
27
+
28
+ - Capability needed by the worker or orchestrator.
29
+
30
+ ## Resources Touched
31
+
32
+ - Files, repos, services, or mdkg nodes the skill may inspect or mutate.
33
+
34
+ ## Steps
35
+
36
+ 1. Ground in source and mdkg truth.
37
+ 2. Execute the smallest deterministic workflow.
38
+ 3. Record evidence.
39
+
40
+ ## Validation Checks
41
+
42
+ - Command or review gate.
43
+
44
+ ## Closeout Evidence
45
+
46
+ - Evidence required before work can be considered done.
47
+
48
+ ## Failure Modes
49
+
50
+ - Known blocker or ambiguity.
51
+
52
+ ## Safety Rules
53
+
54
+ - No secrets or unrelated broad mutation.
55
+
56
+ ## Related SPECs
57
+
58
+ - SPEC refs.
59
+
60
+ ## Projection Targets
61
+
62
+ - Runtime or agent config projections, if any.
63
+
64
+ ## Open Questions
65
+
66
+ - Question to resolve before implementation.
@@ -0,0 +1,39 @@
1
+ ---
2
+ extends: base.SPEC.md
3
+ template_kind: agent
4
+ ---
5
+
6
+ # Agent Role
7
+
8
+ Define the durable agent role and trigger conditions.
9
+
10
+ # Allowed Resources
11
+
12
+ - Resources the agent may read or write.
13
+
14
+ # Forbidden Actions
15
+
16
+ - Actions this agent must never perform.
17
+
18
+ # Input Context
19
+
20
+ - Required room, goal, task, pack, or queue context.
21
+
22
+ # Output Contract
23
+
24
+ - Required report, patch, receipt, or handoff.
25
+
26
+ # Receipt / Evidence Contract
27
+
28
+ - Attempt, validation, and final evidence requirements.
29
+
30
+ # Escalation Behavior
31
+
32
+ - When to stop, ask, or return a blocker.
33
+
34
+ # Projection Targets
35
+
36
+ - `.codex/agents` TOML
37
+ - future OmniRuntime AgentManifest
38
+ - future OmniTx capability object
39
+ - future OmniPL agent definition