mdkg 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,6 +10,8 @@ exports.runSubgraphRemoveCommand = runSubgraphRemoveCommand;
10
10
  exports.runSubgraphEnableCommand = runSubgraphEnableCommand;
11
11
  exports.runSubgraphDisableCommand = runSubgraphDisableCommand;
12
12
  exports.runSubgraphVerifyCommand = runSubgraphVerifyCommand;
13
+ exports.runSubgraphAuditCommand = runSubgraphAuditCommand;
14
+ exports.runSubgraphUpgradePlanCommand = runSubgraphUpgradePlanCommand;
13
15
  exports.runSubgraphRefreshCommand = runSubgraphRefreshCommand;
14
16
  exports.runSubgraphSyncCommand = runSubgraphSyncCommand;
15
17
  exports.runSubgraphMaterializeCommand = runSubgraphMaterializeCommand;
@@ -321,6 +323,304 @@ function runSubgraphVerifyCommand(options) {
321
323
  throw new errors_1.ValidationError("subgraph verify failed");
322
324
  }
323
325
  }
326
+ function pushAuditCheck(receipt, check) {
327
+ receipt.checks.push(check);
328
+ if (!check.ok && check.severity === "error") {
329
+ receipt.errors.push(`${check.id}: ${check.message}`);
330
+ }
331
+ else if (!check.ok && check.severity === "warning") {
332
+ receipt.warnings.push(`${check.id}: ${check.message}`);
333
+ }
334
+ }
335
+ function inspectSourcePathForAudit(root, alias, subgraph) {
336
+ try {
337
+ return inspectSourcePath(root, alias, subgraph, true);
338
+ }
339
+ catch {
340
+ return undefined;
341
+ }
342
+ }
343
+ function auditOneAlias(options) {
344
+ const receipt = {
345
+ alias: options.alias,
346
+ enabled: options.health.enabled,
347
+ visibility: options.health.visibility,
348
+ source_path: options.health.source_path,
349
+ source_repo: options.health.source_repo,
350
+ capability_summary: {
351
+ node_count: options.nodeTypes.length,
352
+ spec_count: options.nodeTypes.filter((type) => type === "spec").length,
353
+ work_count: options.nodeTypes.filter((type) => type === "work").length,
354
+ skill_count: options.nodeTypes.filter((type) => type === "skill").length,
355
+ },
356
+ checks: [],
357
+ warnings: [],
358
+ errors: [],
359
+ ok: true,
360
+ };
361
+ pushAuditCheck(receipt, {
362
+ id: "subgraph.enabled",
363
+ ok: options.subgraph.enabled,
364
+ severity: options.subgraph.enabled ? "info" : "warning",
365
+ message: options.subgraph.enabled ? "subgraph is enabled" : "subgraph is disabled",
366
+ });
367
+ if (!options.subgraph.source_path) {
368
+ pushAuditCheck(receipt, {
369
+ id: "subgraph.source_path.configured",
370
+ ok: false,
371
+ severity: "warning",
372
+ message: "source_path is not configured; sync and upgrade planning cannot inspect child Git state",
373
+ });
374
+ }
375
+ else {
376
+ let gitState;
377
+ try {
378
+ gitState = inspectSourcePath(options.root, options.alias, options.subgraph, true);
379
+ receipt.source_git_head = gitState.head;
380
+ receipt.source_repo_current = gitState.sourceRepo;
381
+ receipt.dirty_tracked = gitState.dirtyTracked;
382
+ receipt.dirty_tracked_paths = gitState.dirtyTrackedPaths;
383
+ pushAuditCheck(receipt, {
384
+ id: "subgraph.source_path.git_root",
385
+ ok: true,
386
+ severity: "info",
387
+ message: "source_path is a contained child Git repo root with .mdkg",
388
+ path: options.subgraph.source_path,
389
+ });
390
+ pushAuditCheck(receipt, {
391
+ id: "subgraph.source_path.clean",
392
+ ok: !gitState.dirtyTracked,
393
+ severity: "warning",
394
+ message: gitState.dirtyTracked
395
+ ? `source_path has dirty tracked changes: ${gitState.dirtyTrackedPaths.join(", ")}`
396
+ : "source_path has no dirty tracked changes",
397
+ path: options.subgraph.source_path,
398
+ details: { dirty_tracked_paths: gitState.dirtyTrackedPaths },
399
+ });
400
+ if (options.subgraph.source_repo && options.subgraph.source_repo !== gitState.sourceRepo) {
401
+ pushAuditCheck(receipt, {
402
+ id: "subgraph.source_repo.current",
403
+ ok: false,
404
+ severity: "warning",
405
+ message: `configured source_repo differs from child repo head: ${options.subgraph.source_repo} -> ${gitState.sourceRepo}`,
406
+ details: { configured: options.subgraph.source_repo, current: gitState.sourceRepo },
407
+ });
408
+ }
409
+ }
410
+ catch (err) {
411
+ pushAuditCheck(receipt, {
412
+ id: "subgraph.source_path.git_root",
413
+ ok: false,
414
+ severity: "error",
415
+ message: err instanceof Error ? err.message : String(err),
416
+ path: options.subgraph.source_path,
417
+ });
418
+ }
419
+ if (gitState) {
420
+ for (const source of options.subgraph.sources) {
421
+ const bundlePath = path_1.default.resolve(options.root, source.path);
422
+ pushAuditCheck(receipt, {
423
+ id: "subgraph.bundle.root_owned",
424
+ ok: !isPathWithin(gitState.sourceRoot, bundlePath),
425
+ severity: "error",
426
+ message: isPathWithin(gitState.sourceRoot, bundlePath)
427
+ ? `bundle path must be root-owned and outside source_path: ${source.path}`
428
+ : `bundle path is root-owned and outside source_path: ${source.path}`,
429
+ path: source.path,
430
+ });
431
+ }
432
+ }
433
+ }
434
+ for (const source of options.health.sources) {
435
+ pushAuditCheck(receipt, {
436
+ id: "subgraph.bundle.enabled",
437
+ ok: source.enabled,
438
+ severity: source.enabled ? "info" : "warning",
439
+ message: source.enabled ? `source is enabled: ${source.path}` : `source is disabled: ${source.path}`,
440
+ path: source.path,
441
+ });
442
+ pushAuditCheck(receipt, {
443
+ id: "subgraph.bundle.valid",
444
+ ok: source.error_count === 0,
445
+ severity: "error",
446
+ message: source.error_count === 0
447
+ ? `bundle source is valid: ${source.path}`
448
+ : `bundle source has errors: ${source.errors.join("; ")}`,
449
+ path: source.path,
450
+ details: { errors: source.errors },
451
+ });
452
+ pushAuditCheck(receipt, {
453
+ id: "subgraph.bundle.fresh",
454
+ ok: !source.stale,
455
+ severity: "warning",
456
+ message: source.stale
457
+ ? `bundle source is stale: ${source.warnings.join("; ")}`
458
+ : `bundle source is fresh: ${source.path}`,
459
+ path: source.path,
460
+ details: { warnings: source.warnings },
461
+ });
462
+ }
463
+ if (options.targetRoot) {
464
+ const outputDir = path_1.default.join(options.targetRoot, options.alias);
465
+ const markerPath = path_1.default.join(outputDir, ".mdkg-materialized.json");
466
+ const exists = fs_1.default.existsSync(outputDir);
467
+ const hasMarker = fs_1.default.existsSync(markerPath);
468
+ pushAuditCheck(receipt, {
469
+ id: "subgraph.materialize.target_safe",
470
+ ok: !exists || hasMarker,
471
+ severity: "error",
472
+ message: !exists
473
+ ? `materialize target is available: ${relativeToRoot(options.root, outputDir)}`
474
+ : hasMarker
475
+ ? `materialize target has mdkg marker: ${relativeToRoot(options.root, outputDir)}`
476
+ : `materialize target exists without mdkg marker: ${relativeToRoot(options.root, outputDir)}`,
477
+ path: relativeToRoot(options.root, outputDir),
478
+ });
479
+ }
480
+ receipt.ok = receipt.errors.length === 0;
481
+ return receipt;
482
+ }
483
+ function runSubgraphAuditCommand(options) {
484
+ const config = (0, config_1.loadConfig)(options.root);
485
+ const aliases = selectAliases(config, options.alias, options.all);
486
+ const projection = (0, subgraphs_1.buildSubgraphsIndex)(options.root, config).index;
487
+ const targetRoot = options.target ? path_1.default.resolve(options.root, normalizeContained(options.target, "--target")) : undefined;
488
+ const results = aliases.map((alias) => {
489
+ const health = projection.subgraphs.find((item) => item.alias === alias);
490
+ if (!health) {
491
+ throw new errors_1.NotFoundError(`subgraph not found: ${alias}`);
492
+ }
493
+ const nodeTypes = Object.values(projection.nodes)
494
+ .filter((node) => node.ws === alias)
495
+ .map((node) => node.type);
496
+ return auditOneAlias({ root: options.root, alias, subgraph: config.subgraphs[alias], health, nodeTypes, targetRoot });
497
+ });
498
+ const errors = results.flatMap((item) => item.errors.map((error) => `${item.alias}: ${error}`));
499
+ const warnings = results.flatMap((item) => item.warnings.map((warning) => `${item.alias}: ${warning}`));
500
+ const receipt = {
501
+ action: "audited",
502
+ ok: errors.length === 0,
503
+ count: results.length,
504
+ target: targetRoot ? relativeToRoot(options.root, targetRoot) : undefined,
505
+ errors,
506
+ warnings,
507
+ subgraphs: results,
508
+ };
509
+ if (options.json) {
510
+ writeJson(receipt);
511
+ }
512
+ else {
513
+ console.log(`subgraphs audited: ${results.length}`);
514
+ for (const item of results) {
515
+ const status = item.errors.length > 0 ? "error" : item.warnings.length > 0 ? "warning" : "ok";
516
+ console.log(`${item.alias} | ${status} | checks:${item.checks.length}`);
517
+ }
518
+ }
519
+ if (!receipt.ok) {
520
+ throw new errors_1.ValidationError("subgraph audit failed");
521
+ }
522
+ }
523
+ function upgradePlanForAlias(options) {
524
+ const actions = [];
525
+ const blockers = [];
526
+ if (!options.subgraph.enabled) {
527
+ actions.push({
528
+ action: "none",
529
+ status: "skipped",
530
+ reason: "subgraph is disabled",
531
+ });
532
+ return { alias: options.alias, ok: true, capability_summary: options.audit.capability_summary, actions, blockers };
533
+ }
534
+ if (!options.subgraph.source_path) {
535
+ blockers.push("source_path is required for upgrade planning");
536
+ }
537
+ const sourceGitError = options.audit.checks.find((check) => check.id === "subgraph.source_path.git_root" && !check.ok && check.severity === "error");
538
+ if (sourceGitError) {
539
+ blockers.push(sourceGitError.message);
540
+ }
541
+ if (options.audit.dirty_tracked) {
542
+ blockers.push(`source_path has dirty tracked changes: ${options.audit.dirty_tracked_paths?.join(", ")}`);
543
+ }
544
+ const rootOwnedErrors = options.audit.checks.filter((check) => check.id === "subgraph.bundle.root_owned" && !check.ok);
545
+ blockers.push(...rootOwnedErrors.map((check) => check.message));
546
+ const bundleErrors = options.health.sources.flatMap((source) => source.errors.map((error) => `${source.path}: ${error}`));
547
+ blockers.push(...bundleErrors);
548
+ const needsSync = options.health.stale ||
549
+ !options.subgraph.source_repo ||
550
+ (options.audit.source_repo_current !== undefined && options.subgraph.source_repo !== options.audit.source_repo_current);
551
+ if (blockers.length > 0) {
552
+ actions.push({
553
+ action: "subgraph.sync",
554
+ status: "blocked",
555
+ command: `mdkg subgraph sync ${options.alias} --dry-run --json`,
556
+ blockers,
557
+ });
558
+ }
559
+ else if (needsSync) {
560
+ actions.push({
561
+ action: "subgraph.sync",
562
+ status: "planned",
563
+ command: `mdkg subgraph sync ${options.alias} --dry-run --json`,
564
+ apply_command: `mdkg subgraph sync ${options.alias} --json`,
565
+ reason: options.health.stale ? "bundle is stale or source HEAD changed" : "configured source_repo is missing or behind current child head",
566
+ current_source_repo: options.audit.source_repo_current,
567
+ configured_source_repo: options.subgraph.source_repo,
568
+ });
569
+ }
570
+ else {
571
+ actions.push({
572
+ action: "subgraph.verify",
573
+ status: "planned",
574
+ command: `mdkg subgraph verify ${options.alias} --json`,
575
+ reason: "bundle snapshot is current; verify before downstream use",
576
+ });
577
+ }
578
+ actions.push({
579
+ action: "subgraph.materialize",
580
+ status: "optional",
581
+ command: `mdkg subgraph materialize ${options.alias} --target .mdkg/subgraphs --gitignore --json`,
582
+ reason: "generate an ignored read-only inspection tree when human review needs file-level child graph context",
583
+ });
584
+ return { alias: options.alias, ok: blockers.length === 0, capability_summary: options.audit.capability_summary, actions, blockers };
585
+ }
586
+ function runSubgraphUpgradePlanCommand(options) {
587
+ const config = (0, config_1.loadConfig)(options.root);
588
+ const aliases = selectAliases(config, options.alias, options.all);
589
+ const projection = (0, subgraphs_1.buildSubgraphsIndex)(options.root, config).index;
590
+ const plans = aliases.map((alias) => {
591
+ const health = projection.subgraphs.find((item) => item.alias === alias);
592
+ if (!health) {
593
+ throw new errors_1.NotFoundError(`subgraph not found: ${alias}`);
594
+ }
595
+ const nodeTypes = Object.values(projection.nodes)
596
+ .filter((node) => node.ws === alias)
597
+ .map((node) => node.type);
598
+ const audit = auditOneAlias({ root: options.root, alias, subgraph: config.subgraphs[alias], health, nodeTypes });
599
+ return upgradePlanForAlias({ root: options.root, alias, subgraph: config.subgraphs[alias], audit, health });
600
+ });
601
+ const blockers = plans.flatMap((plan) => plan.blockers.map((blocker) => `${plan.alias}: ${blocker}`));
602
+ const receipt = {
603
+ action: "upgrade_plan",
604
+ ok: blockers.length === 0,
605
+ count: plans.length,
606
+ apply_supported: false,
607
+ mutation_policy: "read_only_plan",
608
+ blockers,
609
+ subgraphs: plans,
610
+ };
611
+ if (options.json) {
612
+ writeJson(receipt);
613
+ }
614
+ else {
615
+ console.log(`subgraph upgrade plan: ${plans.length}`);
616
+ for (const plan of plans) {
617
+ console.log(`${plan.alias} | ${plan.ok ? "ok" : "blocked"} | actions:${plan.actions.length}`);
618
+ }
619
+ }
620
+ if (!receipt.ok) {
621
+ throw new errors_1.ValidationError("subgraph upgrade plan blocked");
622
+ }
623
+ }
324
624
  function runSubgraphRefreshCommand(options) {
325
625
  withSubgraphLock(options.root, () => {
326
626
  const config = (0, config_1.loadConfig)(options.root);
@@ -274,7 +274,7 @@ function runValidateCommand(options) {
274
274
  });
275
275
  if (idsByWorkspace[alias].has(node.id)) {
276
276
  const firstPath = idsByWorkspace[alias].get(node.id);
277
- errors.push(`${filePath}: duplicate id ${node.id} in workspace ${alias} (also in ${firstPath})`);
277
+ errors.push(`${path_1.default.relative(options.root, filePath).split(path_1.default.sep).join("/")}: duplicate id ${node.id} in workspace ${alias} (also in ${firstPath ? path_1.default.relative(options.root, firstPath).split(path_1.default.sep).join("/") : "unknown"})`);
278
278
  continue;
279
279
  }
280
280
  idsByWorkspace[alias].set(node.id, filePath);
@@ -4,16 +4,18 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.runWorkspaceListCommand = runWorkspaceListCommand;
7
- exports.runWorkspaceAddCommand = runWorkspaceAddCommand;
8
- exports.runWorkspaceRemoveCommand = runWorkspaceRemoveCommand;
9
7
  exports.runWorkspaceEnableCommand = runWorkspaceEnableCommand;
10
8
  exports.runWorkspaceDisableCommand = runWorkspaceDisableCommand;
9
+ exports.runWorkspaceAddCommand = runWorkspaceAddCommand;
10
+ exports.runWorkspaceRemoveCommand = runWorkspaceRemoveCommand;
11
11
  const fs_1 = __importDefault(require("fs"));
12
12
  const path_1 = __importDefault(require("path"));
13
13
  const config_1 = require("../core/config");
14
14
  const migrate_1 = require("../core/migrate");
15
15
  const workspace_path_1 = require("../core/workspace_path");
16
+ const atomic_1 = require("../util/atomic");
16
17
  const errors_1 = require("../util/errors");
18
+ const lock_1 = require("../util/lock");
17
19
  const ALIAS_RE = /^[a-z][a-z0-9_]*$/;
18
20
  function workspaceReceipt(alias, workspace) {
19
21
  return {
@@ -59,7 +61,7 @@ function readRawConfig(root) {
59
61
  return { path: configPath, raw: migrated };
60
62
  }
61
63
  function writeRawConfig(configPath, raw) {
62
- fs_1.default.writeFileSync(configPath, JSON.stringify(raw, null, 2), "utf8");
64
+ (0, atomic_1.atomicWriteFile)(configPath, `${JSON.stringify(raw, null, 2)}\n`);
63
65
  }
64
66
  function normalizeAlias(alias) {
65
67
  if (alias.toLowerCase() === "all") {
@@ -114,7 +116,7 @@ function normalizeVisibility(value) {
114
116
  }
115
117
  throw new errors_1.UsageError("--visibility must be private, internal, or public");
116
118
  }
117
- function runWorkspaceAddCommand(options) {
119
+ function runWorkspaceAddCommandLocked(options) {
118
120
  const alias = normalizeAlias(options.alias);
119
121
  const workspacePath = normalizeCommandWorkspacePath(options.workspacePath, "workspace path");
120
122
  const mdkgDir = normalizeCommandWorkspacePath(options.mdkgDir ?? ".mdkg", "workspace mdkg dir");
@@ -153,7 +155,7 @@ function runWorkspaceAddCommand(options) {
153
155
  fs_1.default.mkdirSync(path_1.default.join(wsRoot, "work"), { recursive: true });
154
156
  printWorkspaceMutationReceipt("added", workspaceReceipt(alias, workspace), options.json);
155
157
  }
156
- function runWorkspaceRemoveCommand(options) {
158
+ function runWorkspaceRemoveCommandLocked(options) {
157
159
  const alias = normalizeAlias(options.alias);
158
160
  if (alias === "root") {
159
161
  throw new errors_1.UsageError("cannot remove root workspace");
@@ -196,8 +198,18 @@ function setWorkspaceEnabled(options, enabled) {
196
198
  printWorkspaceMutationReceipt(enabled ? "enabled" : "disabled", workspaceReceipt(alias, updated), options.json);
197
199
  }
198
200
  function runWorkspaceEnableCommand(options) {
199
- setWorkspaceEnabled(options, true);
201
+ const config = (0, config_1.loadConfig)(options.root);
202
+ return (0, lock_1.withMutationLock)(options.root, config.index.lock_timeout_ms, () => setWorkspaceEnabled(options, true));
200
203
  }
201
204
  function runWorkspaceDisableCommand(options) {
202
- setWorkspaceEnabled(options, false);
205
+ const config = (0, config_1.loadConfig)(options.root);
206
+ return (0, lock_1.withMutationLock)(options.root, config.index.lock_timeout_ms, () => setWorkspaceEnabled(options, false));
207
+ }
208
+ function runWorkspaceAddCommand(options) {
209
+ const config = (0, config_1.loadConfig)(options.root);
210
+ return (0, lock_1.withMutationLock)(options.root, config.index.lock_timeout_ms, () => runWorkspaceAddCommandLocked(options));
211
+ }
212
+ function runWorkspaceRemoveCommand(options) {
213
+ const config = (0, config_1.loadConfig)(options.root);
214
+ return (0, lock_1.withMutationLock)(options.root, config.index.lock_timeout_ms, () => runWorkspaceRemoveCommandLocked(options));
203
215
  }
@@ -26,6 +26,15 @@ Primary commands:
26
26
  - `mdkg goal`
27
27
  - `mdkg task`
28
28
  - `mdkg validate`
29
+ - `mdkg status [--json]`
30
+ - `mdkg fix plan [--family index|refs|ids|all] [--target <id-or-qid>] [--json]`
31
+
32
+ Operator health:
33
+ - `mdkg status [--json]` is a read-only summary for scripts and agents
34
+ - reports mdkg version/config, git state, graph/index freshness, selected-goal state, project DB verification summary, and generated cache status
35
+ - does not rebuild indexes, run migrations, repair files, mutate graph nodes, or change selected-goal state
36
+ - `mdkg fix plan ...` is dry-run repair planning only; it writes nothing and `fix apply` is not exposed
37
+ - `fix plan --json` returns a receipt-shaped plan with selected families, risk counts, paths, reason codes, and `apply_supported: false`
29
38
 
30
39
  Index backend:
31
40
  - fresh mdkg workspaces default to `index.backend: sqlite`
@@ -193,10 +202,14 @@ Subgraph orchestration:
193
202
  - `mdkg subgraph disable <alias> [--json]`
194
203
  - `mdkg subgraph verify [alias|--all] [--json]`
195
204
  - `mdkg subgraph refresh [alias|--all] [--json]`
205
+ - `mdkg subgraph audit [alias|--all] [--target <path>] [--json]`
206
+ - `mdkg subgraph upgrade-plan [alias|--all] [--json]`
196
207
  - `mdkg subgraph sync [alias|--all] [--dry-run] [--allow-dirty] [--json]`
197
208
  - `mdkg subgraph materialize [alias|--all] --target <path> [--clean] [--gitignore] [--json]`
198
209
  - subgraphs are read-only planning views and use subgraph-alias qids such as `child_repo:task-1`
199
210
  - subgraph refresh reloads configured bundle sources only and never mutates child repos
211
+ - subgraph audit reports read-only source/bundle/materialized-target safety checks
212
+ - subgraph upgrade-plan returns a read-only downstream plan with `apply_supported: false`
200
213
  - subgraph sync builds root-owned bundle snapshots from configured clean child Git repo `source_path` entries
201
214
  - subgraph materialize extracts generated inspection trees under a target directory; `.mdkg/subgraphs/` is local generated state
202
215
  - public/internal subgraphs require public bundle profiles
@@ -29,11 +29,23 @@ mdkg spec list --json
29
29
  mdkg archive list
30
30
  mdkg bundle create --profile private
31
31
  mdkg subgraph list --json
32
+ mdkg status --json
33
+ mdkg fix plan --json
32
34
  mdkg validate
33
35
  ```
34
36
 
35
37
  This repo is already initialized. Use `mdkg upgrade` to preview safe scaffold updates, `mdkg new` to create work, `mdkg new goal "..."` plus `mdkg goal select/current/next/claim/evaluate` for recursive long-running objectives, `mdkg search`/`mdkg show` to inspect graph state, `mdkg capability ...` to inspect cached skill/spec/work/core/design capabilities, `mdkg spec ...` for focused optional SPEC records, `mdkg capability resolve ...` to rank local and subgraph capabilities, `mdkg archive ...` to register source/artifact sidecars, `mdkg work ...` to create work contract/order/receipt semantic mirrors and deterministic trigger/verification records, `mdkg bundle ...` to create full graph snapshot bundles, `mdkg subgraph ...` to register read-only child graph planning views, `mdkg pack <id>` to build deterministic context, and `mdkg validate` before closeout.
36
38
 
39
+ Use `mdkg status --json` for a read-only operator summary of Git, graph,
40
+ selected-goal, project DB, and generated-cache health before mutating work. Use
41
+ `mdkg doctor --strict --json` for typed diagnostic checks in CI or agent
42
+ orchestration. These commands inspect state; they do not apply repairs.
43
+
44
+ Use `mdkg fix plan --json` for dry-run repair guidance. It reports generated
45
+ index/cache repair hints, missing graph references, and duplicate local ids as
46
+ receipt-shaped planned changes with risk levels and `apply_supported: false`.
47
+ `fix apply` is not exposed; repair application is intentionally deferred.
48
+
37
49
  Agent workflow docs can use semantic ids:
38
50
 
39
51
  ```bash
@@ -130,8 +142,12 @@ Register child bundle snapshots as read-only subgraphs with:
130
142
  mdkg subgraph add child_repo child-repo/.mdkg/bundles/private/all.mdkg.zip --source-path child-repo
131
143
  mdkg capability resolve "child capability" --json
132
144
  mdkg subgraph verify child_repo --json
145
+ mdkg subgraph audit child_repo --target .mdkg/subgraphs --json
146
+ mdkg subgraph upgrade-plan child_repo --json
133
147
  ```
134
148
 
149
+ Use `mdkg subgraph audit child_repo --target .mdkg/subgraphs --json` to inspect source-path Git state, dirty tracked child files, bundle validity/freshness, root-owned bundle-path safety, optional materialized-target marker safety, and count-only capability summaries. Use `mdkg subgraph upgrade-plan child_repo --json` for a read-only downstream plan; it returns `apply_supported: false`.
150
+
135
151
  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.
136
152
 
137
153
  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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schema_version": 1,
3
3
  "tool": "mdkg",
4
- "mdkg_version": "0.3.0",
4
+ "mdkg_version": "0.3.1",
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": "353aa7318974d4b3dbdf772ae5a3deb2d41b5d71ea5308d260aab8081121548b"
64
+ "sha256": "0661196763bf05681523a576fcd0a29138ccc36df4f48dad7ba16a1f4b7b0418"
65
65
  },
66
66
  {
67
67
  "path": ".mdkg/skills/build-pack-and-execute-task/SKILL.md",
@@ -256,7 +256,7 @@
256
256
  {
257
257
  "path": "CLI_COMMAND_MATRIX.md",
258
258
  "category": "startup_doc",
259
- "sha256": "48c1b7fbef3a01faf5ddf8bb232b19362b49ef2a371a7c38ae11302c8b3bccac"
259
+ "sha256": "a9a7133e5a7c9a07a6814c679d04637370ea144eac19a6500980a37a2c4199f5"
260
260
  },
261
261
  {
262
262
  "path": "llms.txt",
@@ -94,6 +94,7 @@ const VALUE_FLAGS = new Set([
94
94
  "--requires",
95
95
  "--target",
96
96
  "--snapshot",
97
+ "--family",
97
98
  ]);
98
99
  const BOOLEAN_FLAGS = new Set([
99
100
  "--tolerant",
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "mdkg",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Markdown Knowledge Graph",
5
5
  "license": "MIT",
6
6
  "bin": {
7
7
  "mdkg": "dist/cli.js"
8
8
  },
9
9
  "scripts": {
10
- "build": "node scripts/clean-build-output.js && tsc -p tsconfig.build.json && node scripts/add-shebang.js && node scripts/copy-init-assets.js",
10
+ "build": "node scripts/clean-build-output.js && tsc -p tsconfig.build.json && node scripts/add-shebang.js && node scripts/copy-init-assets.js && node scripts/generate-command-contract.js --write",
11
11
  "build:test": "node scripts/clean-build-output.js tests && tsc -p tsconfig.test.json",
12
12
  "test": "npm run build && npm run build:test && node --test dist/tests/**/*.test.js",
13
13
  "test:coverage": "npm run build && npm run build:test && node --test --experimental-test-coverage dist/tests/**/*.test.js",
@@ -25,6 +25,10 @@
25
25
  "smoke:archive-work": "npm run build && node scripts/smoke-archive-work.js",
26
26
  "smoke:work-invocation": "npm run build && node scripts/smoke-work-invocation.js",
27
27
  "smoke:cli-ux-polish": "npm run build && node scripts/smoke-cli-ux-polish.js",
28
+ "smoke:operator-health": "npm run build && node scripts/smoke-operator-health.js",
29
+ "smoke:fix-plan": "npm run build && node scripts/smoke-fix-plan.js",
30
+ "smoke:branch-conflicts": "npm run build && node scripts/smoke-branch-conflicts.js",
31
+ "smoke:command-docs": "npm run build && node scripts/smoke-command-docs.js",
28
32
  "smoke:bundle": "npm run build && node scripts/smoke-bundle.js",
29
33
  "smoke:bundle-import": "npm run smoke:subgraph",
30
34
  "smoke:visibility": "npm run build && node scripts/smoke-visibility.js",
@@ -33,8 +37,9 @@
33
37
  "smoke:goal": "npm run build && node scripts/smoke-goal.js",
34
38
  "cli:snapshot": "npm run build && node scripts/cli_help_snapshot.js",
35
39
  "cli:check": "npm run build && node scripts/cli_help_snapshot.js --check",
40
+ "cli:contract": "npm run build && node scripts/generate-command-contract.js --check",
36
41
  "prepack": "npm run build && node scripts/assert-publish-ready.js",
37
- "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-queue-cli && npm run smoke:db-events && npm run smoke:db-materializer && npm run smoke:db-snapshot && npm run smoke:archive-work && npm run smoke:work-invocation && npm run smoke:cli-ux-polish && 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",
42
+ "prepublishOnly": "npm run test && npm run cli:check && npm run cli:contract && 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-queue-cli && npm run smoke:db-events && npm run smoke:db-materializer && npm run smoke:db-snapshot && npm run smoke:archive-work && npm run smoke:work-invocation && npm run smoke:cli-ux-polish && npm run smoke:operator-health && npm run smoke:fix-plan && npm run smoke:branch-conflicts && npm run smoke:command-docs && 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",
38
43
  "postinstall": "node scripts/postinstall.js",
39
44
  "smoke:subgraph": "npm run build && node scripts/smoke-subgraph.js"
40
45
  },
@@ -44,6 +49,7 @@
44
49
  },
45
50
  "files": [
46
51
  "dist/cli.js",
52
+ "dist/command-contract.json",
47
53
  "dist/commands/",
48
54
  "dist/core/",
49
55
  "dist/graph/",
@@ -53,6 +59,7 @@
53
59
  "dist/util/",
54
60
  "scripts/postinstall.js",
55
61
  "README.md",
62
+ "CLI_COMMAND_MATRIX.md",
56
63
  "CHANGELOG.md",
57
64
  "CONTRIBUTING.md",
58
65
  "LICENSE"