mdkg 0.1.5 → 0.1.7

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.
@@ -34,7 +34,15 @@ function validateEdgeTargets(index, allowMissing, knownSkillSlugs, externalWorks
34
34
  }
35
35
  for (const [edgeKey, values] of edgeLists) {
36
36
  for (const value of values) {
37
- if (!nodes[value]) {
37
+ const targetNode = nodes[value];
38
+ if (targetNode &&
39
+ ["epic", "parent", "prev", "next"].includes(edgeKey) &&
40
+ !node.source?.imported &&
41
+ targetNode.source?.imported) {
42
+ pushError(errors, `${qid}: ${edgeKey} cannot target read-only subgraph node ${value}`);
43
+ continue;
44
+ }
45
+ if (!targetNode) {
38
46
  const [workspace] = value.split(":");
39
47
  if (workspace && externalWorkspaces?.has(workspace)) {
40
48
  continue;
@@ -106,6 +114,18 @@ function pathEndsWithContractRef(nodePath, contractRef) {
106
114
  return (normalizedNodePath === normalizedContractRef ||
107
115
  normalizedNodePath.endsWith(`/${normalizedContractRef}`));
108
116
  }
117
+ function resolveNodeRef(index, ws, value) {
118
+ const qid = value.includes(":") ? value : `${ws}:${value}`;
119
+ return index.nodes[qid];
120
+ }
121
+ function resolveTypedNodeRef(index, ws, value, type) {
122
+ const node = resolveNodeRef(index, ws, value);
123
+ return node?.type === type ? node : undefined;
124
+ }
125
+ function isExternalWorkspaceRef(value, externalWorkspaces) {
126
+ const [workspace] = value.split(":");
127
+ return Boolean(workspace && value.includes(":") && externalWorkspaces?.has(workspace));
128
+ }
109
129
  function validateAgentWorkflowSpecWorkContracts(index, allowMissing, errors) {
110
130
  const workNodesByWorkspace = {};
111
131
  for (const node of Object.values(index.nodes)) {
@@ -156,20 +176,7 @@ function validateAgentWorkflowSpecWorkContracts(index, allowMissing, errors) {
156
176
  }
157
177
  }
158
178
  }
159
- function validateAgentWorkflowWorkOrderWorkRefs(index, allowMissing, errors) {
160
- const workNodesByWorkspaceAndId = {};
161
- for (const node of Object.values(index.nodes)) {
162
- if (node.type !== "work") {
163
- continue;
164
- }
165
- if (!workNodesByWorkspaceAndId[node.ws]) {
166
- workNodesByWorkspaceAndId[node.ws] = {};
167
- }
168
- workNodesByWorkspaceAndId[node.ws][node.id] = {
169
- qid: node.qid,
170
- version: typeof node.attributes.version === "string" ? node.attributes.version : undefined,
171
- };
172
- }
179
+ function validateAgentWorkflowWorkOrderWorkRefs(index, allowMissing, externalWorkspaces, errors) {
173
180
  for (const [qid, node] of Object.entries(index.nodes)) {
174
181
  if (node.type !== "work_order") {
175
182
  continue;
@@ -178,8 +185,11 @@ function validateAgentWorkflowWorkOrderWorkRefs(index, allowMissing, errors) {
178
185
  if (typeof workId !== "string") {
179
186
  continue;
180
187
  }
181
- const workNode = workNodesByWorkspaceAndId[node.ws]?.[workId];
188
+ const workNode = resolveTypedNodeRef(index, node.ws, workId, "work");
182
189
  if (!workNode) {
190
+ if (isExternalWorkspaceRef(workId, externalWorkspaces)) {
191
+ continue;
192
+ }
183
193
  if (allowMissing) {
184
194
  continue;
185
195
  }
@@ -188,23 +198,13 @@ function validateAgentWorkflowWorkOrderWorkRefs(index, allowMissing, errors) {
188
198
  }
189
199
  const workVersion = node.attributes.work_version;
190
200
  if (typeof workVersion === "string" &&
191
- workNode.version !== undefined &&
192
- workVersion !== workNode.version) {
193
- pushError(errors, `${qid}: work_version ${workVersion} does not match ${workNode.qid} version ${workNode.version}`);
201
+ typeof workNode.attributes.version === "string" &&
202
+ workVersion !== workNode.attributes.version) {
203
+ pushError(errors, `${qid}: work_version ${workVersion} does not match ${workNode.qid} version ${workNode.attributes.version}`);
194
204
  }
195
205
  }
196
206
  }
197
- function validateAgentWorkflowReceiptWorkOrderRefs(index, allowMissing, errors) {
198
- const workOrderIdsByWorkspace = {};
199
- for (const node of Object.values(index.nodes)) {
200
- if (node.type !== "work_order") {
201
- continue;
202
- }
203
- if (!workOrderIdsByWorkspace[node.ws]) {
204
- workOrderIdsByWorkspace[node.ws] = new Set();
205
- }
206
- workOrderIdsByWorkspace[node.ws].add(node.id);
207
- }
207
+ function validateAgentWorkflowReceiptWorkOrderRefs(index, allowMissing, externalWorkspaces, errors) {
208
208
  for (const [qid, node] of Object.entries(index.nodes)) {
209
209
  if (node.type !== "receipt") {
210
210
  continue;
@@ -213,7 +213,10 @@ function validateAgentWorkflowReceiptWorkOrderRefs(index, allowMissing, errors)
213
213
  if (typeof workOrderId !== "string") {
214
214
  continue;
215
215
  }
216
- if (workOrderIdsByWorkspace[node.ws]?.has(workOrderId)) {
216
+ if (resolveTypedNodeRef(index, node.ws, workOrderId, "work_order")) {
217
+ continue;
218
+ }
219
+ if (isExternalWorkspaceRef(workOrderId, externalWorkspaces)) {
217
220
  continue;
218
221
  }
219
222
  if (allowMissing) {
@@ -238,7 +241,17 @@ function buildSpecRolesByWorkspace(index) {
238
241
  }
239
242
  return specRolesByWorkspace;
240
243
  }
241
- function validateAgentWorkflowSubagentRefs(index, allowMissing, errors) {
244
+ function resolveSpecRole(index, ws, value) {
245
+ const node = resolveTypedNodeRef(index, ws, value, "spec");
246
+ if (!node) {
247
+ return undefined;
248
+ }
249
+ return {
250
+ qid: node.qid,
251
+ role: typeof node.attributes.role === "string" ? node.attributes.role : undefined,
252
+ };
253
+ }
254
+ function validateAgentWorkflowSubagentRefs(index, allowMissing, externalWorkspaces, errors) {
242
255
  const specRolesByWorkspace = buildSpecRolesByWorkspace(index);
243
256
  for (const [qid, node] of Object.entries(index.nodes)) {
244
257
  if (node.type !== "spec" && node.type !== "work") {
@@ -252,8 +265,13 @@ function validateAgentWorkflowSubagentRefs(index, allowMissing, errors) {
252
265
  if (typeof value !== "string") {
253
266
  continue;
254
267
  }
255
- const specNode = specRolesByWorkspace[node.ws]?.[value];
268
+ const specNode = value.includes(":")
269
+ ? resolveSpecRole(index, node.ws, value)
270
+ : specRolesByWorkspace[node.ws]?.[value];
256
271
  if (!specNode) {
272
+ if (isExternalWorkspaceRef(value, externalWorkspaces)) {
273
+ continue;
274
+ }
257
275
  if (allowMissing) {
258
276
  continue;
259
277
  }
@@ -266,28 +284,7 @@ function validateAgentWorkflowSubagentRefs(index, allowMissing, errors) {
266
284
  }
267
285
  }
268
286
  }
269
- function validateAgentWorkflowDisputeRefs(index, allowMissing, errors) {
270
- const workOrderIdsByWorkspace = {};
271
- const receiptNodesByWorkspaceAndId = {};
272
- for (const node of Object.values(index.nodes)) {
273
- if (node.type === "work_order") {
274
- if (!workOrderIdsByWorkspace[node.ws]) {
275
- workOrderIdsByWorkspace[node.ws] = new Set();
276
- }
277
- workOrderIdsByWorkspace[node.ws].add(node.id);
278
- }
279
- if (node.type === "receipt") {
280
- if (!receiptNodesByWorkspaceAndId[node.ws]) {
281
- receiptNodesByWorkspaceAndId[node.ws] = {};
282
- }
283
- receiptNodesByWorkspaceAndId[node.ws][node.id] = {
284
- qid: node.qid,
285
- workOrderId: typeof node.attributes.work_order_id === "string"
286
- ? node.attributes.work_order_id
287
- : undefined,
288
- };
289
- }
290
- }
287
+ function validateAgentWorkflowDisputeRefs(index, allowMissing, externalWorkspaces, errors) {
291
288
  for (const [qid, node] of Object.entries(index.nodes)) {
292
289
  if (node.type !== "dispute") {
293
290
  continue;
@@ -296,23 +293,35 @@ function validateAgentWorkflowDisputeRefs(index, allowMissing, errors) {
296
293
  if (typeof workOrderId !== "string") {
297
294
  continue;
298
295
  }
299
- if (!workOrderIdsByWorkspace[node.ws]?.has(workOrderId) && !allowMissing) {
296
+ if (!resolveTypedNodeRef(index, node.ws, workOrderId, "work_order") &&
297
+ !isExternalWorkspaceRef(workOrderId, externalWorkspaces) &&
298
+ !allowMissing) {
300
299
  pushError(errors, `${qid}: work_order_id references missing WORK_ORDER.md ${workOrderId}`);
301
300
  }
302
301
  const receiptId = node.attributes.receipt_id;
303
302
  if (typeof receiptId !== "string") {
304
303
  continue;
305
304
  }
306
- const receiptNode = receiptNodesByWorkspaceAndId[node.ws]?.[receiptId];
305
+ const receiptNode = resolveTypedNodeRef(index, node.ws, receiptId, "receipt");
307
306
  if (!receiptNode) {
307
+ if (isExternalWorkspaceRef(receiptId, externalWorkspaces)) {
308
+ continue;
309
+ }
308
310
  if (allowMissing) {
309
311
  continue;
310
312
  }
311
313
  pushError(errors, `${qid}: receipt_id references missing RECEIPT.md ${receiptId}`);
312
314
  continue;
313
315
  }
314
- if (receiptNode.workOrderId !== undefined && receiptNode.workOrderId !== workOrderId) {
315
- pushError(errors, `${qid}: receipt_id ${receiptId} belongs to work_order_id ${receiptNode.workOrderId}, not ${workOrderId}`);
316
+ const receiptWorkOrderId = typeof receiptNode.attributes.work_order_id === "string"
317
+ ? receiptNode.attributes.work_order_id
318
+ : undefined;
319
+ const receiptWorkOrderQid = receiptWorkOrderId
320
+ ? receiptWorkOrderId.includes(":") ? receiptWorkOrderId : `${receiptNode.ws}:${receiptWorkOrderId}`
321
+ : undefined;
322
+ const disputeWorkOrderQid = workOrderId.includes(":") ? workOrderId : `${node.ws}:${workOrderId}`;
323
+ if (receiptWorkOrderQid !== undefined && receiptWorkOrderQid !== disputeWorkOrderQid) {
324
+ pushError(errors, `${qid}: receipt_id ${receiptId} belongs to work_order_id ${receiptWorkOrderId}, not ${workOrderId}`);
316
325
  }
317
326
  }
318
327
  }
@@ -331,6 +340,9 @@ function validateAgentWorkflowNodeIdRef(qid, ws, field, value, nodeIdsByWorkspac
331
340
  if (workspace && value.includes(":") && externalWorkspaces?.has(workspace)) {
332
341
  return;
333
342
  }
343
+ if (value.includes(":") && nodeIdsByWorkspace[workspace]?.has(value.split(":").slice(1).join(":"))) {
344
+ return;
345
+ }
334
346
  if (nodeIdsByWorkspace[ws]?.has(value)) {
335
347
  return;
336
348
  }
@@ -514,10 +526,10 @@ function collectGraphErrors(index, options = {}) {
514
526
  validateEdgeTargets(index, allowMissing, knownSkillSlugs, externalWorkspaces, errors);
515
527
  validatePrevNextSymmetry(index, allowMissing, errors);
516
528
  validateAgentWorkflowSpecWorkContracts(index, allowMissing, errors);
517
- validateAgentWorkflowWorkOrderWorkRefs(index, allowMissing, errors);
518
- validateAgentWorkflowReceiptWorkOrderRefs(index, allowMissing, errors);
519
- validateAgentWorkflowSubagentRefs(index, allowMissing, errors);
520
- validateAgentWorkflowDisputeRefs(index, allowMissing, errors);
529
+ validateAgentWorkflowWorkOrderWorkRefs(index, allowMissing, externalWorkspaces, errors);
530
+ validateAgentWorkflowReceiptWorkOrderRefs(index, allowMissing, externalWorkspaces, errors);
531
+ validateAgentWorkflowSubagentRefs(index, allowMissing, externalWorkspaces, errors);
532
+ validateAgentWorkflowDisputeRefs(index, allowMissing, externalWorkspaces, errors);
521
533
  validateAgentWorkflowFeedbackProposalRefs(index, allowMissing, knownSkillSlugs, externalWorkspaces, errors);
522
534
  validateArchiveUriRefs(index, allowMissing, errors);
523
535
  validateGoalRefs(index, allowMissing, errors);
@@ -31,18 +31,30 @@ Agent operating prompt:
31
31
  - Use `mdkg skill list`, `mdkg skill search`, and `mdkg skill show <slug>` for skill discovery.
32
32
  - Use `mdkg capability list/search/show` for deterministic skills, `SPEC.md`, `WORK.md`, core-doc, and design-doc capability discovery.
33
33
  - Use `mdkg index` to refresh JSON compatibility caches and `.mdkg/index/mdkg.sqlite` when SQLite mode is enabled.
34
+ - Treat `.mdkg/db` as project application state; use `mdkg db init` to create
35
+ the generic scaffold and enable `db.enabled` without creating an active
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,
40
+ `mdkg db snapshot verify/status` for checkpoint health, and
41
+ `mdkg db snapshot dump/diff` for deterministic review aids. Keep
42
+ `.mdkg/db/runtime/` and WAL/SHM/journal/lock/temp files ignored unless a
43
+ sealed artifact policy explicitly says otherwise.
34
44
  - Use `mdkg archive add/list/show/verify/compress` for committed source and artifact sidecars under `.mdkg/archive`.
35
45
  - Use `mdkg work ...` helpers for semantic mirror contracts, work orders, receipts, and artifact registration.
36
46
  - Treat work contracts, orders, and receipts as committed semantic mirrors only; never store raw secrets, credentials, live payment state, ledger mutations, or canonical marketplace state in mdkg.
37
47
  - Use `artifact://...` for external/runtime-managed artifacts and `archive://...` for committed mdkg archive sidecars.
38
48
  - Use `mdkg bundle create/list/show/verify` for explicit full `.mdkg` graph snapshot bundles.
39
- - Use `mdkg subgraph add/list/verify` to register child bundle snapshots as read-only planning context.
49
+ - Use `mdkg subgraph add/list/verify/sync/materialize` to register child bundle snapshots as read-only planning context, refresh root-owned child bundle snapshots, and optionally generate ignored inspection trees.
40
50
  - Use `mdkg capability resolve` to rank local and subgraph capabilities for orchestration planning.
51
+ - Use `mdkg subgraph sync --dry-run --json` before refreshing root-owned child bundle snapshots, then `mdkg subgraph sync --json` when the receipt is acceptable.
52
+ - Use `mdkg subgraph materialize ... --target .mdkg/subgraphs --gitignore` only for generated read-only inspection trees; never mutate materialized files.
41
53
  - Use `mdkg pack <id> --visibility public|internal` only when you need a public-safe or internal-safe pack; no flag remains private-capable local behavior.
42
54
  - Mark archive sidecars public only with explicit `mdkg archive add --visibility public` intent.
43
55
  - Treat sidecar `.md` plus deterministic `.zip` caches as the commit-eligible archive record; validation and `mdkg archive verify` both check ZIP payload integrity.
44
56
  - Before committing repos that track archive caches or `.mdkg/bundles/`, run `mdkg archive compress --all`, `mdkg archive verify --json`, `mdkg bundle create --profile private`, and `mdkg bundle verify .mdkg/bundles/private/all.mdkg.zip`.
45
- - Use `mdkg task start/update/done` for structured task, bug, and test lifecycle fields.
57
+ - Use `mdkg task start/update/done` for structured feature, task, bug, and test lifecycle fields.
46
58
  - Use `mdkg upgrade` to preview scaffold updates; only run `mdkg upgrade --apply` after reviewing the receipt.
47
59
  - Keep nuanced summaries, body text, and manual parent closeout edits in markdown.
48
60
  - Use `mdkg event enable` only if `events.jsonl` is missing and provenance should be restored.
@@ -27,6 +27,39 @@ Index backend:
27
27
  - fresh mdkg workspaces default to `index.backend: sqlite`
28
28
  - `.mdkg/index/mdkg.sqlite` is rebuildable and commit-eligible when intentionally tracked
29
29
  - JSON compatibility caches remain generated and ignored by default
30
+ - `mdkg db index rebuild` writes the same derived caches as `mdkg index`
31
+ - `mdkg db index status` reports graph cache health without mutating files
32
+ - `mdkg db index verify` fails on missing, stale, corrupt, schema-mismatched, or SQLite source-fingerprint-mismatched cache state
33
+
34
+ Project database commands:
35
+ - `mdkg db index rebuild [--tolerant] [--json]`
36
+ - `mdkg db index status [--json]`
37
+ - `mdkg db index verify [--json]`
38
+ - `mdkg db init [--json]`
39
+ - `mdkg db migrate [--json]`
40
+ - `mdkg db verify [--json]`
41
+ - `mdkg db stats [--json]`
42
+ - `mdkg db snapshot seal [--json]`
43
+ - `mdkg db snapshot verify [--json]`
44
+ - `mdkg db snapshot status [--json]`
45
+ - `mdkg db snapshot dump [--snapshot <path>] [--output <path>] [--json]`
46
+ - `mdkg db snapshot diff <left-snapshot> <right-snapshot> [--json]`
47
+ - `.mdkg/db` is project application state, separate from `.mdkg/index`
48
+ - `mdkg db init` creates the generic `.mdkg/db` scaffold, writes
49
+ `.mdkg/db/project-db.json`, enables `db.enabled`, and does not create an
50
+ active runtime SQLite database
51
+ - `mdkg db migrate` creates or updates the configured active runtime SQLite
52
+ database and applies mdkg-owned generic foundation migrations only
53
+ - `mdkg db migrate` records migration order, checksums, and applied timestamps
54
+ in the configured migration table
55
+ - `mdkg db verify` checks config, layout, runtime SQLite integrity, migration
56
+ metadata, receipt directory policy, and transient runtime files
57
+ - `mdkg db stats` reports table counts, database size, migration state,
58
+ transient runtime files, receipt-file count, and state snapshot presence
59
+ - `mdkg db snapshot seal` writes an opt-in sealed checkpoint and manifest under
60
+ `.mdkg/db/state`; `snapshot verify/status/dump/diff` inspect and review that
61
+ checkpoint without treating raw binary diffs as human-readable truth
62
+ - active `.mdkg/db/runtime/` files and `.mdkg/db` WAL/SHM/journal/lock/temp files are ignored by default
30
63
 
31
64
  Validation commands:
32
65
  - `mdkg validate [--out <path>] [--quiet] [--json]`
@@ -133,8 +166,12 @@ Subgraph orchestration:
133
166
  - `mdkg subgraph disable <alias> [--json]`
134
167
  - `mdkg subgraph verify [alias|--all] [--json]`
135
168
  - `mdkg subgraph refresh [alias|--all] [--json]`
169
+ - `mdkg subgraph sync [alias|--all] [--dry-run] [--allow-dirty] [--json]`
170
+ - `mdkg subgraph materialize [alias|--all] --target <path> [--clean] [--gitignore] [--json]`
136
171
  - subgraphs are read-only planning views and use subgraph-alias qids such as `child_repo:task-1`
137
172
  - subgraph refresh reloads configured bundle sources only and never mutates child repos
173
+ - subgraph sync builds root-owned bundle snapshots from configured clean child Git repo `source_path` entries
174
+ - subgraph materialize extracts generated inspection trees under a target directory; `.mdkg/subgraphs/` is local generated state
138
175
  - public/internal subgraphs require public bundle profiles
139
176
 
140
177
  Work semantic mirrors:
@@ -13,6 +13,7 @@ mdkg is pre-v1 public alpha software. Graph, cache, bundle, and DAL contracts ma
13
13
  - `archive/`: sidecar metadata and deterministic compressed source/artifact caches
14
14
  - `bundles/`: optional committed full graph snapshot bundles
15
15
  - `index/`: generated JSON caches plus optional commit-eligible `mdkg.sqlite`
16
+ - `db/`: future project application database layout and receipts
16
17
  - `pack/`: generated context packs (do not commit)
17
18
 
18
19
  ## Next Commands
@@ -59,11 +60,31 @@ Ensure ignore files include:
59
60
  - `.mdkg/index/*.sqlite-wal`
60
61
  - `.mdkg/index/*.sqlite-shm`
61
62
  - `.mdkg/index/*.sqlite-journal`
63
+ - `.mdkg/db/runtime/`
64
+ - `.mdkg/db/**/*.sqlite-wal`
65
+ - `.mdkg/db/**/*.sqlite-shm`
66
+ - `.mdkg/db/**/*.sqlite-journal`
67
+ - `.mdkg/db/**/*.lock`
68
+ - `.mdkg/db/**/*.tmp`
62
69
  - `.mdkg/pack/`
63
70
  - `.mdkg/archive/**/source/`
64
71
 
65
72
  Fresh mdkg workspaces default to `index.backend: sqlite`; `.mdkg/index/mdkg.sqlite` is a rebuildable cache and may be committed when the repo intentionally tracks it and it stays reasonably small.
66
73
 
74
+ `.mdkg/db` is reserved for project application database state, separate from
75
+ `.mdkg/index`. Run `mdkg db init` to create the generic scaffold, write
76
+ `.mdkg/db/project-db.json`, and enable `db.enabled`; it does not create an
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
80
+ `mdkg db stats` for table counts, DB size, migration state, and receipt-file
81
+ counts. Use `mdkg db snapshot seal` to create an opt-in sealed checkpoint under
82
+ `.mdkg/db/state`, then use `mdkg db snapshot verify/status` for integrity and
83
+ freshness checks. Use `mdkg db snapshot dump/diff` as deterministic review aids
84
+ for SQLite snapshots. Keep active runtime DB files and transient
85
+ WAL/SHM/journal, lock, and temp files ignored. Commit schema files, manifests,
86
+ receipts, and sealed state snapshots only by explicit repo policy.
87
+
67
88
  Recommended:
68
89
 
69
90
  ```bash
@@ -97,6 +118,10 @@ mdkg capability resolve "child capability" --json
97
118
  mdkg subgraph verify child_repo --json
98
119
  ```
99
120
 
121
+ Use `mdkg subgraph sync child_repo --dry-run --json` before writing a refreshed root-owned child bundle snapshot, then `mdkg subgraph sync child_repo --json` when the receipt is acceptable. `sync` inspects the configured child Git repo `source_path`, refuses dirty tracked changes by default, verifies the new bundle, and records `source_repo` as `<branch>@<sha>` without committing, pulling, pushing, checking out, resetting, or mutating child mdkg Markdown.
122
+
123
+ Use `mdkg subgraph materialize child_repo --target .mdkg/subgraphs --gitignore --json` only when you need a generated read-only inspection tree. Materialized views are local generated state and are not root graph nodes.
124
+
100
125
  Subgraph nodes use the subgraph alias as their qid prefix and can be inspected or packed, but mutations must happen in the owning child repo.
101
126
 
102
127
  ## Archive and Work Mirrors
@@ -21,6 +21,17 @@
21
21
  "output_dir": ".mdkg/bundles",
22
22
  "default_profile": "private"
23
23
  },
24
+ "db": {
25
+ "enabled": false,
26
+ "schema_version": 1,
27
+ "root_path": ".mdkg/db",
28
+ "schema_path": ".mdkg/db/schema",
29
+ "migrations_path": ".mdkg/db/schema/migrations",
30
+ "runtime_path": ".mdkg/db/runtime/project.sqlite",
31
+ "state_path": ".mdkg/db/state/project.sqlite",
32
+ "receipts_path": ".mdkg/db/receipts",
33
+ "migration_table": "mdkg_schema_migration"
34
+ },
24
35
  "subgraphs": {},
25
36
  "pack": {
26
37
  "default_depth": 2,
@@ -254,8 +254,15 @@ Common flags:
254
254
  - `mdkg subgraph disable <alias> [--json]`
255
255
  - `mdkg subgraph verify [alias|--all] [--json]`
256
256
  - `mdkg subgraph refresh [alias|--all] [--json]`
257
+ - `mdkg subgraph sync [alias|--all] [--dry-run] [--allow-dirty] [--json]`
258
+ - `mdkg subgraph materialize [alias|--all] --target <path> [--clean] [--gitignore] [--json]`
257
259
  - subgraphs are read-only projected graph views; child repos remain owners of real mutations and commits
258
260
  - `subgraph refresh` reloads configured bundle sources only and never builds or mutates child repos
261
+ - `subgraph sync` is the explicit child-bundle build command; it uses configured root-relative `source_path`, requires a contained child Git repo with `.mdkg`, refuses dirty tracked child changes unless `--allow-dirty` is supplied, writes configured root-owned bundle paths, verifies bundles, and updates `source_repo` to `<branch>@<sha>`
262
+ - `subgraph sync --dry-run` writes no bundles, config, or indexes and reports intended paths, bundle hashes, dirty state, and source repo
263
+ - `subgraph materialize` extracts configured bundles into generated read-only inspection trees under `<target>/<alias>`, writes `.mdkg-materialized.json`, and only cleans existing alias directories with both `--clean` and a materialization marker
264
+ - `.mdkg/subgraphs/` materialized views are ignored by local graph scanning, search, validation, packing, bundle creation, and SQLite hydration
265
+ - root-authored relationship/reference fields may point at configured subgraph qids; local ownership fields (`epic`, `parent`, `prev`, `next`) remain local-only
259
266
  - `subgraph verify` exits nonzero for stale, missing, corrupt, profile-mismatched, or duplicate-id subgraphs
260
267
  - public/internal subgraphs require `expected_profile: public`; private bundle profiles cannot be promoted through subgraph visibility
261
268
  - legacy `mdkg bundle import ...` exits with guidance to run `mdkg upgrade --apply` and use `mdkg subgraph ...`
@@ -10,7 +10,7 @@ relates: []
10
10
  refs: []
11
11
  aliases: []
12
12
  created: 2026-01-06
13
- updated: 2026-01-14
13
+ updated: 2026-06-03
14
14
  ---
15
15
 
16
16
  # Repo safety and ignores
@@ -23,6 +23,8 @@ mdkg content may contain sensitive notes and internal project planning. This rul
23
23
  - `.mdkg/` must not be published to npm.
24
24
  - Generated JSON index, temp, lock, WAL, SHM, and journal files under `.mdkg/index/` must not be committed.
25
25
  - `.mdkg/index/mdkg.sqlite` is a rebuildable access cache and may be committed when the repo intentionally tracks it and it stays reasonably small.
26
+ - `.mdkg/db/runtime/` active project database files and `.mdkg/db` transient WAL, SHM, journal, lock, and temp files must not be committed.
27
+ - `.mdkg/db/schema/`, `.mdkg/db/receipts/`, manifests, and sealed state snapshots are commit-eligible only by explicit repo policy.
26
28
  - `.mdkg/state/` stores local workflow convenience state and must not be committed.
27
29
  - `.mdkg/bundles/` may be committed only when the repo intentionally tracks private or public snapshot bundles.
28
30
 
@@ -38,6 +40,12 @@ The repo MUST ignore at minimum:
38
40
  - `.mdkg/index/*.sqlite-wal`
39
41
  - `.mdkg/index/*.sqlite-shm`
40
42
  - `.mdkg/index/*.sqlite-journal`
43
+ - `.mdkg/db/runtime/`
44
+ - `.mdkg/db/**/*.sqlite-wal`
45
+ - `.mdkg/db/**/*.sqlite-shm`
46
+ - `.mdkg/db/**/*.sqlite-journal`
47
+ - `.mdkg/db/**/*.lock`
48
+ - `.mdkg/db/**/*.tmp`
41
49
  - `.mdkg/state/`
42
50
  - `.mdkg/pack/`
43
51
  - `.mdkg/archive/**/source/`
@@ -49,6 +57,12 @@ Recommended `.gitignore` entries:
49
57
  - `.mdkg/index/*.sqlite-wal`
50
58
  - `.mdkg/index/*.sqlite-shm`
51
59
  - `.mdkg/index/*.sqlite-journal`
60
+ - `.mdkg/db/runtime/`
61
+ - `.mdkg/db/**/*.sqlite-wal`
62
+ - `.mdkg/db/**/*.sqlite-shm`
63
+ - `.mdkg/db/**/*.sqlite-journal`
64
+ - `.mdkg/db/**/*.lock`
65
+ - `.mdkg/db/**/*.tmp`
52
66
  - `.mdkg/state/`
53
67
  - `.mdkg/pack/`
54
68
  - `.mdkg/archive/**/source/`
@@ -79,6 +93,12 @@ If the repo is containerized:
79
93
  - `.mdkg/index/*.sqlite-wal`
80
94
  - `.mdkg/index/*.sqlite-shm`
81
95
  - `.mdkg/index/*.sqlite-journal`
96
+ - `.mdkg/db/runtime/`
97
+ - `.mdkg/db/**/*.sqlite-wal`
98
+ - `.mdkg/db/**/*.sqlite-shm`
99
+ - `.mdkg/db/**/*.sqlite-journal`
100
+ - `.mdkg/db/**/*.lock`
101
+ - `.mdkg/db/**/*.tmp`
82
102
  - `node_modules/`
83
103
  - `dist/` (if built in container)
84
104
  - any other local artifacts
@@ -90,7 +110,7 @@ For application builds:
90
110
 
91
111
  `mdkg init` updates ignore files by default for safety:
92
112
 
93
- - `.gitignore` appends generated index cache/temp/lock patterns, `.mdkg/state/`, `.mdkg/pack/`, and raw archive source ignores.
113
+ - `.gitignore` appends generated index cache/temp/lock patterns, project DB runtime/transient patterns, `.mdkg/state/`, `.mdkg/pack/`, and raw archive source ignores.
94
114
  - `.npmignore` appends `.mdkg/`, generated index cache/temp/lock patterns, and `.mdkg/pack/`.
95
115
  - `--no-update-ignores` disables these default writes
96
116
 
@@ -108,6 +128,19 @@ Explicit flags remain available and take precedence:
108
128
  - `.mdkg/state/` contains local workflow convenience state such as selected goals and MUST stay ignored.
109
129
  - Index rebuild should be deterministic and safe to regenerate at any time.
110
130
 
131
+ ## Project DB safety
132
+
133
+ - `.mdkg/db/` is reserved for project application database state, separate from
134
+ the rebuildable `.mdkg/index/` graph cache.
135
+ - The generic layout is `.mdkg/db/schema/`, `.mdkg/db/runtime/`,
136
+ `.mdkg/db/state/`, and `.mdkg/db/receipts/`.
137
+ - Active runtime DB files under `.mdkg/db/runtime/` and transient WAL, SHM,
138
+ journal, lock, and temp files under `.mdkg/db/` stay ignored by default.
139
+ - Schema files, manifests, receipt artifacts, and opt-in sealed snapshots are
140
+ commit-eligible only by explicit repo policy.
141
+ - `mdkg doctor` should warn when active project DB runtime/transient files are
142
+ present so they are not mistaken for sealed state.
143
+
111
144
  ## Bundle safety
112
145
 
113
146
  - `.mdkg/bundles/` stores explicit snapshot artifacts and is not ignored by default.
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "schema_version": 1,
3
3
  "tool": "mdkg",
4
- "mdkg_version": "0.1.5",
4
+ "mdkg_version": "0.1.7",
5
5
  "files": [
6
6
  {
7
7
  "path": ".mdkg/config.json",
8
8
  "category": "config",
9
- "sha256": "5d2cf5e773353a59178fe86f8528413a00a73e2879fb1e020d01b49c0715a9ce"
9
+ "sha256": "8055c72aa3c55f8a49d532a735bc29e3baa2e2bf478e9d092b83bec9e46fa78c"
10
10
  },
11
11
  {
12
12
  "path": ".mdkg/core/core.md",
@@ -36,12 +36,12 @@
36
36
  {
37
37
  "path": ".mdkg/core/rule-3-cli-contract.md",
38
38
  "category": "core",
39
- "sha256": "0b72a2448cc91779278fed6e011c3936c31c5b11a7365a4145941dcc90f805a3"
39
+ "sha256": "f61b84ff6e5abae91365c4f623ccbd78f25dfcfef09822fefb3abf0119d65739"
40
40
  },
41
41
  {
42
42
  "path": ".mdkg/core/rule-4-repo-safety-and-ignores.md",
43
43
  "category": "core",
44
- "sha256": "d8aff39af7a00e63db51831d4324856bbf49d3e3434141416903bad42c7d5380"
44
+ "sha256": "3c7a4e34febf05b3202af4bb21cbbbeb430c2b872e3a7030920bbc6b045bd0b8"
45
45
  },
46
46
  {
47
47
  "path": ".mdkg/core/rule-5-release-and-versioning.md",
@@ -61,7 +61,7 @@
61
61
  {
62
62
  "path": ".mdkg/README.md",
63
63
  "category": "mdkg_doc",
64
- "sha256": "f733792daa2b0f3318fd7b39fa2f812c61b8a3f5f630dba2f271cdcb10af9927"
64
+ "sha256": "57a55e0f3743415f5ba1d166d3abf61b2ad868cc11d0e7083b7fd8075dae95a1"
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": "bb8654d11adb2ed6cb45f3514bbf7fad8fe6cda8a2e0e1676462542563df0df9"
189
+ "sha256": "d9ba16476749bd96c986f356b40a7abe4b1321b10260f61f85198220c11ac994"
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": "a2f59afae403fff7335b0cf0109ee550c7a864b9ef6744b39a645096432e5206"
204
+ "sha256": "51c8f3e6fff4c2a0c385e39c2c41a0b26136ca9d2efcd4593f0099c3cdc0a4a8"
205
205
  },
206
206
  {
207
207
  "path": "llms.txt",
@@ -84,6 +84,8 @@ const VALUE_FLAGS = new Set([
84
84
  "--source-repo",
85
85
  "--max-stale-seconds",
86
86
  "--requires",
87
+ "--target",
88
+ "--snapshot",
87
89
  ]);
88
90
  const BOOLEAN_FLAGS = new Set([
89
91
  "--tolerant",
@@ -118,6 +120,9 @@ const BOOLEAN_FLAGS = new Set([
118
120
  "--clear-blocked-by",
119
121
  "--all",
120
122
  "--fresh-only",
123
+ "--allow-dirty",
124
+ "--clean",
125
+ "--gitignore",
121
126
  ]);
122
127
  const FLAG_ALIASES = {
123
128
  "--o": "--out",
package/dist/util/refs.js CHANGED
@@ -27,7 +27,7 @@ function isSha256Ref(value) {
27
27
  return SHA256_RE.test(value);
28
28
  }
29
29
  function isPortableOrUriRef(value) {
30
- return (0, id_1.isPortableId)(value.toLowerCase()) || isUriRef(value);
30
+ return (0, id_1.isPortableIdRef)(value.toLowerCase()) || isUriRef(value);
31
31
  }
32
32
  function validatePortableOrUriRef(value) {
33
33
  if (isUriRef(value)) {
@@ -36,5 +36,5 @@ function validatePortableOrUriRef(value) {
36
36
  }
37
37
  return true;
38
38
  }
39
- return value === value.toLowerCase() && (0, id_1.isPortableId)(value);
39
+ return value === value.toLowerCase() && (0, id_1.isPortableIdRef)(value);
40
40
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mdkg",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "Markdown Knowledge Graph",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -16,6 +16,8 @@
16
16
  "smoke:upgrade": "npm run build && node scripts/smoke-upgrade.js",
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
+ "smoke:db": "npm run build && node scripts/smoke-db.js",
20
+ "smoke:db-snapshot": "npm run build && node scripts/smoke-db-snapshot.js",
19
21
  "smoke:archive-work": "npm run build && node scripts/smoke-archive-work.js",
20
22
  "smoke:bundle": "npm run build && node scripts/smoke-bundle.js",
21
23
  "smoke:bundle-import": "npm run smoke:subgraph",
@@ -26,7 +28,7 @@
26
28
  "cli:snapshot": "npm run build && node scripts/cli_help_snapshot.js",
27
29
  "cli:check": "npm run build && node scripts/cli_help_snapshot.js --check",
28
30
  "prepack": "npm run build && node scripts/assert-publish-ready.js",
29
- "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: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",
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",
30
32
  "postinstall": "node scripts/postinstall.js",
31
33
  "smoke:subgraph": "npm run build && node scripts/smoke-subgraph.js"
32
34
  },