mdkg 0.2.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.
Files changed (41) hide show
  1. package/CHANGELOG.md +87 -1
  2. package/CLI_COMMAND_MATRIX.md +1176 -0
  3. package/README.md +58 -5
  4. package/dist/cli.js +267 -12
  5. package/dist/command-contract.json +7473 -0
  6. package/dist/commands/capability.js +13 -8
  7. package/dist/commands/doctor.js +370 -86
  8. package/dist/commands/fix.js +924 -0
  9. package/dist/commands/format.js +9 -3
  10. package/dist/commands/skill.js +13 -3
  11. package/dist/commands/skill_support.js +3 -3
  12. package/dist/commands/spec.js +101 -0
  13. package/dist/commands/status.js +270 -0
  14. package/dist/commands/subgraph.js +300 -0
  15. package/dist/commands/validate.js +1 -1
  16. package/dist/commands/work.js +569 -20
  17. package/dist/commands/workspace.js +19 -7
  18. package/dist/graph/agent_file_types.js +95 -7
  19. package/dist/graph/capabilities_indexer.js +89 -2
  20. package/dist/graph/frontmatter.js +6 -0
  21. package/dist/graph/node.js +8 -2
  22. package/dist/init/AGENT_START.md +5 -1
  23. package/dist/init/CLI_COMMAND_MATRIX.md +36 -0
  24. package/dist/init/README.md +41 -2
  25. package/dist/init/init-manifest.json +20 -20
  26. package/dist/init/templates/default/receipt.md +12 -1
  27. package/dist/init/templates/default/spec.md +8 -6
  28. package/dist/init/templates/default/work.md +5 -1
  29. package/dist/init/templates/default/work_order.md +11 -0
  30. package/dist/init/templates/specs/agent.SPEC.md +45 -4
  31. package/dist/init/templates/specs/api.SPEC.md +1 -0
  32. package/dist/init/templates/specs/base.SPEC.md +45 -12
  33. package/dist/init/templates/specs/capability.SPEC.md +16 -3
  34. package/dist/init/templates/specs/integration.SPEC.md +1 -0
  35. package/dist/init/templates/specs/model.SPEC.md +1 -0
  36. package/dist/init/templates/specs/project.SPEC.md +14 -1
  37. package/dist/init/templates/specs/{omniruntime-agent.SPEC.md → runtime-agent.SPEC.md} +13 -3
  38. package/dist/init/templates/specs/runtime-image.SPEC.md +1 -0
  39. package/dist/init/templates/specs/tool.SPEC.md +1 -0
  40. package/dist/util/argparse.js +9 -0
  41. package/package.json +12 -3
@@ -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
  }
@@ -32,6 +32,7 @@ exports.AGENT_FILE_BASENAMES = {
32
32
  exports.AGENT_ATTRIBUTE_KEY_ORDER = {
33
33
  spec: [
34
34
  "version",
35
+ "spec_kind",
35
36
  "role",
36
37
  "runtime_mode",
37
38
  "work_contracts",
@@ -68,7 +69,10 @@ exports.AGENT_ATTRIBUTE_KEY_ORDER = {
68
69
  "requester",
69
70
  "order_status",
70
71
  "request_ref",
72
+ "trigger_ref",
73
+ "payload_hash",
71
74
  "input_refs",
75
+ "queue_refs",
72
76
  "requested_outputs",
73
77
  "constraint_refs",
74
78
  "artifact_policy",
@@ -79,8 +83,10 @@ exports.AGENT_ATTRIBUTE_KEY_ORDER = {
79
83
  "receipt_status",
80
84
  "outcome",
81
85
  "cost_ref",
86
+ "redaction_policy",
82
87
  "proof_refs",
83
88
  "attestation_refs",
89
+ "evidence_hashes",
84
90
  "input_hashes",
85
91
  "output_hashes",
86
92
  ],
@@ -128,6 +134,27 @@ const UPDATE_POLICY_VALUES = new Set([
128
134
  "automatic",
129
135
  "disabled",
130
136
  ]);
137
+ const SPEC_KIND_VALUES = new Set([
138
+ "cli_tool",
139
+ "api",
140
+ "agent",
141
+ "runtime_agent",
142
+ "capability",
143
+ "tool",
144
+ "model",
145
+ "runtime_image",
146
+ "integration",
147
+ "project_service",
148
+ ]);
149
+ const DOCUMENTATION_ONLY_SPEC_KIND_ROUTES = {
150
+ gap_register: "use an EDD, task, checkpoint, or goal for gap registers",
151
+ checkpoint: "use a checkpoint node for checkpoint evidence",
152
+ roadmap: "use an epic, goal, EDD, or PRD for roadmaps",
153
+ audit: "use a task, checkpoint, or EDD for audits",
154
+ go_no_go: "use a DEC, task, or checkpoint for go/no-go notes",
155
+ planning_note: "use an EDD, task, or checkpoint for planning notes",
156
+ launch_checklist: "use a task, test, checkpoint, or DEC for launch checklists",
157
+ };
131
158
  const PRICING_MODEL_VALUES = new Set([
132
159
  "free",
133
160
  "included",
@@ -146,6 +173,11 @@ const ORDER_STATUS_VALUES = new Set([
146
173
  ]);
147
174
  const RECEIPT_STATUS_VALUES = new Set(["recorded", "verified", "rejected", "superseded"]);
148
175
  const OUTCOME_VALUES = new Set(["success", "partial", "failure"]);
176
+ const REDACTION_POLICY_VALUES = new Set([
177
+ "refs_and_hashes_only",
178
+ "redacted_summary",
179
+ "external_private",
180
+ ]);
149
181
  const ARTIFACT_POLICY_VALUES = new Set([
150
182
  "commit_sidecar_and_zip",
151
183
  "external_only",
@@ -265,6 +297,16 @@ function requireEnum(value, key, allowed, filePath) {
265
297
  throw formatError(filePath, `${key} must be one of ${Array.from(allowed).join(", ")}`);
266
298
  }
267
299
  }
300
+ function validateSpecKind(value, filePath) {
301
+ if (SPEC_KIND_VALUES.has(value)) {
302
+ return;
303
+ }
304
+ const route = DOCUMENTATION_ONLY_SPEC_KIND_ROUTES[value];
305
+ if (route) {
306
+ throw formatError(filePath, `spec_kind ${value} is documentation-only; ${route}. SPEC.md must define a reusable invocable capability surface.`);
307
+ }
308
+ throw formatError(filePath, `spec_kind must be one of ${Array.from(SPEC_KIND_VALUES).join(", ")}; documentation-only records belong in normal mdkg nodes such as task, test, epic, goal, checkpoint, EDD, PRD, DEC, bug, feedback, dispute, or proposal.`);
309
+ }
268
310
  function requireLowerToken(value, key, filePath) {
269
311
  if (!LOWER_TOKEN_RE.test(value)) {
270
312
  throw formatError(filePath, `${key} must be lowercase snake/kebab style`);
@@ -290,6 +332,14 @@ function validatePortableRefs(values, key, filePath) {
290
332
  }
291
333
  }
292
334
  }
335
+ function validateWorkInvocationAnchor(requiredCapabilities, refsByKey, filePath) {
336
+ const hasRequiredCapability = requiredCapabilities.length > 0;
337
+ const hasDependencyRef = Object.values(refsByKey).some((values) => values.length > 0);
338
+ if (hasRequiredCapability || hasDependencyRef) {
339
+ return;
340
+ }
341
+ throw formatError(filePath, "WORK.md must include at least one required_capabilities entry or dependency ref in skill_refs, tool_refs, model_refs, wasm_component_refs, runtime_image_refs, or subagent_refs");
342
+ }
293
343
  function validatePortableOrUriRefs(values, key, filePath) {
294
344
  for (const [index, value] of values.entries()) {
295
345
  if (!(0, refs_1.validatePortableOrUriRef)(value)) {
@@ -315,6 +365,11 @@ function validateHashRefs(values, key, filePath) {
315
365
  }
316
366
  }
317
367
  }
368
+ function validateHashRef(value, key, filePath) {
369
+ if (!(0, refs_1.isSha256Ref)(value)) {
370
+ throw formatError(filePath, `${key} must be sha256:<64 lowercase hex chars>`);
371
+ }
372
+ }
318
373
  function validateRelativeMarkdownPaths(values, key, basename, filePath) {
319
374
  for (const [index, value] of values.entries()) {
320
375
  if (path_1.default.isAbsolute(value) || value.split(/[\\/]/).includes("..")) {
@@ -344,6 +399,10 @@ function validateAgentFrontmatter(type, frontmatter, filePath) {
344
399
  requireSemver(version, "version", filePath);
345
400
  switch (type) {
346
401
  case "spec": {
402
+ const specKind = optionalString(frontmatter, "spec_kind", filePath);
403
+ if (specKind) {
404
+ validateSpecKind(specKind, filePath);
405
+ }
347
406
  const role = expectString(frontmatter, "role", filePath);
348
407
  requireEnum(role, "role", ROLE_VALUES, filePath);
349
408
  const runtimeMode = expectString(frontmatter, "runtime_mode", filePath);
@@ -371,13 +430,28 @@ function validateAgentFrontmatter(type, frontmatter, filePath) {
371
430
  requireLowerToken(kind, "kind", filePath);
372
431
  const pricingModel = expectString(frontmatter, "pricing_model", filePath);
373
432
  requireEnum(pricingModel, "pricing_model", PRICING_MODEL_VALUES, filePath);
374
- validateCapabilities(expectList(frontmatter, "required_capabilities", filePath), "required_capabilities", filePath);
375
- validatePortableRefs(optionalList(frontmatter, "skill_refs", filePath), "skill_refs", filePath);
376
- validatePortableRefs(optionalList(frontmatter, "tool_refs", filePath), "tool_refs", filePath);
377
- validatePortableRefs(optionalList(frontmatter, "model_refs", filePath), "model_refs", filePath);
378
- validatePortableRefs(optionalList(frontmatter, "wasm_component_refs", filePath), "wasm_component_refs", filePath);
379
- validatePortableRefs(optionalList(frontmatter, "runtime_image_refs", filePath), "runtime_image_refs", filePath);
380
- validatePortableRefs(optionalList(frontmatter, "subagent_refs", filePath), "subagent_refs", filePath);
433
+ const requiredCapabilities = expectList(frontmatter, "required_capabilities", filePath);
434
+ validateCapabilities(requiredCapabilities, "required_capabilities", filePath);
435
+ const skillRefs = optionalList(frontmatter, "skill_refs", filePath);
436
+ validatePortableRefs(skillRefs, "skill_refs", filePath);
437
+ const toolRefs = optionalList(frontmatter, "tool_refs", filePath);
438
+ validatePortableRefs(toolRefs, "tool_refs", filePath);
439
+ const modelRefs = optionalList(frontmatter, "model_refs", filePath);
440
+ validatePortableRefs(modelRefs, "model_refs", filePath);
441
+ const wasmComponentRefs = optionalList(frontmatter, "wasm_component_refs", filePath);
442
+ validatePortableRefs(wasmComponentRefs, "wasm_component_refs", filePath);
443
+ const runtimeImageRefs = optionalList(frontmatter, "runtime_image_refs", filePath);
444
+ validatePortableRefs(runtimeImageRefs, "runtime_image_refs", filePath);
445
+ const subagentRefs = optionalList(frontmatter, "subagent_refs", filePath);
446
+ validatePortableRefs(subagentRefs, "subagent_refs", filePath);
447
+ validateWorkInvocationAnchor(requiredCapabilities, {
448
+ skill_refs: skillRefs,
449
+ tool_refs: toolRefs,
450
+ model_refs: modelRefs,
451
+ wasm_component_refs: wasmComponentRefs,
452
+ runtime_image_refs: runtimeImageRefs,
453
+ subagent_refs: subagentRefs,
454
+ }, filePath);
381
455
  validateFieldDescriptors(expectList(frontmatter, "inputs", filePath), "inputs", filePath);
382
456
  validateFieldDescriptors(expectList(frontmatter, "outputs", filePath), "outputs", filePath);
383
457
  expectBoolean(frontmatter, "receipt_required", filePath);
@@ -396,7 +470,16 @@ function validateAgentFrontmatter(type, frontmatter, filePath) {
396
470
  if (requestRef) {
397
471
  validatePortableOrUriScalar(requestRef, "request_ref", filePath);
398
472
  }
473
+ const triggerRef = optionalRefString(frontmatter, "trigger_ref", filePath);
474
+ if (triggerRef) {
475
+ validatePortableOrUriScalar(triggerRef, "trigger_ref", filePath);
476
+ }
477
+ const payloadHash = optionalString(frontmatter, "payload_hash", filePath);
478
+ if (payloadHash) {
479
+ validateHashRef(payloadHash, "payload_hash", filePath);
480
+ }
399
481
  validatePortableOrUriRefs(optionalList(frontmatter, "input_refs", filePath), "input_refs", filePath);
482
+ validatePortableOrUriRefs(optionalList(frontmatter, "queue_refs", filePath), "queue_refs", filePath);
400
483
  validateOptionalFieldDescriptors(optionalList(frontmatter, "requested_outputs", filePath), "requested_outputs", filePath);
401
484
  validatePortableOrUriRefs(optionalList(frontmatter, "constraint_refs", filePath), "constraint_refs", filePath);
402
485
  const artifactPolicy = optionalString(frontmatter, "artifact_policy", filePath);
@@ -416,8 +499,13 @@ function validateAgentFrontmatter(type, frontmatter, filePath) {
416
499
  if (costRef) {
417
500
  validatePortableOrUriScalar(costRef, "cost_ref", filePath);
418
501
  }
502
+ const redactionPolicy = optionalString(frontmatter, "redaction_policy", filePath);
503
+ if (redactionPolicy) {
504
+ requireEnum(redactionPolicy, "redaction_policy", REDACTION_POLICY_VALUES, filePath);
505
+ }
419
506
  validatePortableOrUriRefs(optionalList(frontmatter, "proof_refs", filePath), "proof_refs", filePath);
420
507
  validatePortableOrUriRefs(optionalList(frontmatter, "attestation_refs", filePath), "attestation_refs", filePath);
508
+ validateHashRefs(optionalList(frontmatter, "evidence_hashes", filePath), "evidence_hashes", filePath);
421
509
  validateHashRefs(optionalList(frontmatter, "input_hashes", filePath), "input_hashes", filePath);
422
510
  validateHashRefs(optionalList(frontmatter, "output_hashes", filePath), "output_hashes", filePath);
423
511
  break;
@@ -74,7 +74,92 @@ function pickAttributes(attributes, keys) {
74
74
  }
75
75
  return picked;
76
76
  }
77
- function nodeCapabilityRecord(root, config, node, kind, indexedAt) {
77
+ function toStringList(value) {
78
+ if (!Array.isArray(value)) {
79
+ return [];
80
+ }
81
+ return value.filter((item) => typeof item === "string");
82
+ }
83
+ function nodeRefSet(node) {
84
+ return new Set([node.id, node.qid, `${node.ws}:${node.id}`]);
85
+ }
86
+ function sortedNodes(nodes) {
87
+ return [...nodes].sort((a, b) => a.qid.localeCompare(b.qid));
88
+ }
89
+ function resolveSpecWorkContracts(index, specNode) {
90
+ const candidates = new Map();
91
+ const specDir = path_1.default.posix.dirname(specNode.path);
92
+ for (const contractPath of toStringList(specNode.attributes.work_contracts)) {
93
+ const normalizedPath = path_1.default.posix.normalize(path_1.default.posix.join(specDir, contractPath));
94
+ for (const node of Object.values(index.nodes)) {
95
+ if (node.type === "work" && node.ws === specNode.ws && node.path === normalizedPath) {
96
+ candidates.set(node.qid, node);
97
+ }
98
+ }
99
+ }
100
+ for (const qid of specNode.edges.relates) {
101
+ const node = index.nodes[qid];
102
+ if (node?.type === "work") {
103
+ candidates.set(node.qid, node);
104
+ }
105
+ }
106
+ for (const qid of index.reverse_edges.relates?.[specNode.qid] ?? []) {
107
+ const node = index.nodes[qid];
108
+ if (node?.type === "work") {
109
+ candidates.set(node.qid, node);
110
+ }
111
+ }
112
+ return sortedNodes(candidates.values());
113
+ }
114
+ function resolveWorkSpecs(index, workNode) {
115
+ const candidates = new Map();
116
+ const workRefs = nodeRefSet(workNode);
117
+ for (const node of Object.values(index.nodes)) {
118
+ if (node.type !== "spec" || node.ws !== workNode.ws) {
119
+ continue;
120
+ }
121
+ const agentId = typeof workNode.attributes.agent_id === "string" ? workNode.attributes.agent_id : undefined;
122
+ if (agentId && nodeRefSet(node).has(agentId)) {
123
+ candidates.set(node.qid, node);
124
+ }
125
+ if (resolveSpecWorkContracts(index, node).some((contract) => contract.qid === workNode.qid)) {
126
+ candidates.set(node.qid, node);
127
+ }
128
+ if (node.edges.relates.some((qid) => workRefs.has(qid))) {
129
+ candidates.set(node.qid, node);
130
+ }
131
+ }
132
+ return sortedNodes(candidates.values());
133
+ }
134
+ function resolveWorkOrders(index, workNode) {
135
+ const workRefs = nodeRefSet(workNode);
136
+ return sortedNodes(Object.values(index.nodes).filter((node) => node.type === "work_order" && workRefs.has(String(node.attributes.work_id ?? ""))));
137
+ }
138
+ function resolveReceiptsForOrders(index, orderNodes) {
139
+ const orderRefs = new Set();
140
+ for (const order of orderNodes) {
141
+ for (const ref of nodeRefSet(order)) {
142
+ orderRefs.add(ref);
143
+ }
144
+ }
145
+ return sortedNodes(Object.values(index.nodes).filter((node) => node.type === "receipt" && orderRefs.has(String(node.attributes.work_order_id ?? ""))));
146
+ }
147
+ function buildCapabilityLinkage(index, node, kind) {
148
+ if (kind !== "spec" && kind !== "work") {
149
+ return undefined;
150
+ }
151
+ const workContracts = kind === "spec" ? resolveSpecWorkContracts(index, node) : [node];
152
+ const specNodes = kind === "work" ? resolveWorkSpecs(index, node) : [];
153
+ const workOrders = workContracts.flatMap((workNode) => resolveWorkOrders(index, workNode));
154
+ const receipts = resolveReceiptsForOrders(index, workOrders);
155
+ return {
156
+ spec_qids: specNodes.map((specNode) => specNode.qid),
157
+ work_contract_qids: workContracts.map((workNode) => workNode.qid),
158
+ work_order_qids: workOrders.map((orderNode) => orderNode.qid),
159
+ receipt_qids: receipts.map((receiptNode) => receiptNode.qid),
160
+ };
161
+ }
162
+ function nodeCapabilityRecord(root, config, index, node, kind, indexedAt) {
78
163
  const absolutePath = path_1.default.resolve(root, node.path);
79
164
  const content = fs_1.default.readFileSync(absolutePath, "utf8");
80
165
  const record = {
@@ -99,6 +184,7 @@ function nodeCapabilityRecord(root, config, node, kind, indexedAt) {
99
184
  if (kind === "spec") {
100
185
  record.spec = pickAttributes(node.attributes, [
101
186
  "version",
187
+ "spec_kind",
102
188
  "role",
103
189
  "runtime_mode",
104
190
  "work_contracts",
@@ -131,6 +217,7 @@ function nodeCapabilityRecord(root, config, node, kind, indexedAt) {
131
217
  "receipt_required",
132
218
  ]);
133
219
  }
220
+ record.linkage = buildCapabilityLinkage(index, node, kind);
134
221
  return record;
135
222
  }
136
223
  function skillCapabilityRecord(root, config, skill, indexedAt) {
@@ -210,7 +297,7 @@ function buildCapabilitiesIndex(root, config, nodeIndex) {
210
297
  if (!kind) {
211
298
  continue;
212
299
  }
213
- records.push(nodeCapabilityRecord(root, config, node, kind, generatedAt));
300
+ records.push(nodeCapabilityRecord(root, config, index, node, kind, generatedAt));
214
301
  }
215
302
  records.push(...buildWorkspaceSkillCapabilities(root, config, generatedAt));
216
303
  const sortedRecords = sortRecords(records);
@@ -24,6 +24,7 @@ exports.DEFAULT_FRONTMATTER_KEY_ORDER = [
24
24
  "next",
25
25
  "supersedes",
26
26
  "version",
27
+ "spec_kind",
27
28
  "role",
28
29
  "runtime_mode",
29
30
  "work_contracts",
@@ -42,10 +43,14 @@ exports.DEFAULT_FRONTMATTER_KEY_ORDER = [
42
43
  "requester",
43
44
  "order_status",
44
45
  "request_ref",
46
+ "trigger_ref",
47
+ "payload_hash",
48
+ "queue_refs",
45
49
  "work_order_id",
46
50
  "receipt_status",
47
51
  "outcome",
48
52
  "cost_ref",
53
+ "redaction_policy",
49
54
  "target_id",
50
55
  "sentiment",
51
56
  "feedback_status",
@@ -73,6 +78,7 @@ exports.DEFAULT_FRONTMATTER_KEY_ORDER = [
73
78
  "artifact_policy",
74
79
  "proof_refs",
75
80
  "attestation_refs",
81
+ "evidence_hashes",
76
82
  "input_hashes",
77
83
  "output_hashes",
78
84
  "tags",
@@ -226,14 +226,20 @@ function requireTemplateSchema(type, templateSchemas, filePath) {
226
226
  }
227
227
  return schema;
228
228
  }
229
+ const OPTIONAL_COMPAT_TEMPLATE_KEYS = {
230
+ spec: {
231
+ spec_kind: "scalar",
232
+ },
233
+ };
229
234
  function validateTemplateKeys(frontmatter, schema, filePath) {
230
235
  for (const key of Object.keys(frontmatter)) {
231
- if (!schema.allowedKeys.has(key)) {
236
+ if (!schema.allowedKeys.has(key) &&
237
+ OPTIONAL_COMPAT_TEMPLATE_KEYS[schema.type]?.[key] === undefined) {
232
238
  throw formatError(filePath, `unknown key: ${key}`);
233
239
  }
234
240
  }
235
241
  for (const [key, value] of Object.entries(frontmatter)) {
236
- const expected = schema.keyKinds[key];
242
+ const expected = schema.keyKinds[key] ?? OPTIONAL_COMPAT_TEMPLATE_KEYS[schema.type]?.[key];
237
243
  if (!expected) {
238
244
  continue;
239
245
  }
@@ -30,6 +30,7 @@ Agent operating prompt:
30
30
  - Record skill improvement candidates during normal goal execution; edit `SKILL.md` only when the active node is explicit skill-maintenance work.
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
+ - Use `mdkg spec list/show/validate` for focused optional `SPEC.md` capability records.
33
34
  - Use `mdkg index` to refresh JSON compatibility caches and `.mdkg/index/mdkg.sqlite` when SQLite mode is enabled.
34
35
  - Treat `.mdkg/db` as project application state; use `mdkg db init` to create
35
36
  the generic scaffold and enable `db.enabled` without creating an active
@@ -50,7 +51,7 @@ Agent operating prompt:
50
51
  `.mdkg/db/runtime/` and WAL/SHM/journal/lock/temp files ignored unless a
51
52
  sealed artifact policy explicitly says otherwise.
52
53
  - Use `mdkg archive add/list/show/verify/compress` for committed source and artifact sidecars under `.mdkg/archive`.
53
- - Use `mdkg work ...` helpers for semantic mirror contracts, work orders, receipts, and artifact registration.
54
+ - Use `mdkg work ...` helpers for semantic mirror contracts, deterministic triggers, work order status, receipt verification, and artifact registration.
54
55
  - 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.
55
56
  - Use `artifact://...` for external/runtime-managed artifacts and `archive://...` for committed mdkg archive sidecars.
56
57
  - Use `mdkg bundle create/list/show/verify` for explicit full `.mdkg` graph snapshot bundles.
@@ -107,6 +108,9 @@ Capability discovery:
107
108
  - `mdkg capability list --kind skill --json`
108
109
  - `mdkg capability search "<query>" --kind spec --json`
109
110
  - `mdkg capability search "<query>" --kind work --json`
111
+ - `mdkg spec list --json`
112
+ - `mdkg spec show <id-or-qid-or-alias> --json`
113
+ - `mdkg spec validate <id-or-qid-or-alias> --json`
110
114
 
111
115
  Conventions:
112
116
  - `AGENTS.md` is the Codex/OpenAI-oriented wrapper doc.
@@ -6,6 +6,9 @@ Verify live help with:
6
6
  - `mdkg --help`
7
7
  - `mdkg help <command>`
8
8
 
9
+ Optional reusable SPEC capability records are accessed through `mdkg spec ...`.
10
+ Repos without SPEC files remain valid.
11
+
9
12
  Primary commands:
10
13
  - `mdkg init`
11
14
  - `mdkg upgrade [--dry-run] [--apply] [--json]`
@@ -16,12 +19,22 @@ Primary commands:
16
19
  - `mdkg pack`
17
20
  - `mdkg skill`
18
21
  - `mdkg capability`
22
+ - `mdkg spec`
19
23
  - `mdkg archive`
20
24
  - `mdkg bundle`
21
25
  - `mdkg work`
22
26
  - `mdkg goal`
23
27
  - `mdkg task`
24
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`
25
38
 
26
39
  Index backend:
27
40
  - fresh mdkg workspaces default to `index.backend: sqlite`
@@ -143,8 +156,19 @@ Capability discovery:
143
156
  - `mdkg capability resolve [query] [--requires <capability>] [--fresh-only] [--json]`
144
157
  - capability records are deterministic cache projections from Markdown
145
158
  - records include source hash, headings, refs, and `indexed_at`
159
+ - SPEC and WORK capability records include read-only `linkage` arrays for related SPECs, work contracts, work orders, and receipts when those graph mirrors exist
146
160
  - normal task, epic, feat, bug, test, and checkpoint nodes are intentionally excluded
147
161
 
162
+ Spec capability records:
163
+ - `mdkg spec list [--json]`
164
+ - `mdkg spec show <id-or-qid-or-alias> [--json]`
165
+ - `mdkg spec validate [<id-or-qid-or-alias>] [--json]`
166
+ - `SPEC.md` is optional; repos with no SPEC files still validate
167
+ - SPEC records describe reusable capability surfaces, not general planning notes
168
+ - `mdkg spec validate` with no ref validates the graph and all optional SPEC records
169
+ - `mdkg spec validate <ref>` also checks that the target SPEC reference exists
170
+ - `mdkg spec ...` is the focused SPEC command family; `mdkg capability ...` remains broader skill/spec/work/core/design discovery
171
+
148
172
  Archive sidecars:
149
173
  - `mdkg archive add <file> [--id <archive.id>] [--kind source|artifact] [--visibility private|internal|public] [--title <title>] [--refs <...>] [--relates <...>] [--json]`
150
174
  - `mdkg archive list [--kind source|artifact] [--visibility private|internal|public] [--ws <alias>] [--json]`
@@ -178,21 +202,33 @@ Subgraph orchestration:
178
202
  - `mdkg subgraph disable <alias> [--json]`
179
203
  - `mdkg subgraph verify [alias|--all] [--json]`
180
204
  - `mdkg subgraph refresh [alias|--all] [--json]`
205
+ - `mdkg subgraph audit [alias|--all] [--target <path>] [--json]`
206
+ - `mdkg subgraph upgrade-plan [alias|--all] [--json]`
181
207
  - `mdkg subgraph sync [alias|--all] [--dry-run] [--allow-dirty] [--json]`
182
208
  - `mdkg subgraph materialize [alias|--all] --target <path> [--clean] [--gitignore] [--json]`
183
209
  - subgraphs are read-only planning views and use subgraph-alias qids such as `child_repo:task-1`
184
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`
185
213
  - subgraph sync builds root-owned bundle snapshots from configured clean child Git repo `source_path` entries
186
214
  - subgraph materialize extracts generated inspection trees under a target directory; `.mdkg/subgraphs/` is local generated state
187
215
  - public/internal subgraphs require public bundle profiles
188
216
 
189
217
  Work semantic mirrors:
190
218
  - `mdkg work contract new "<title>" --id <work.id> --agent-id <agent.id> --kind <kind> --inputs <...> --outputs <...> [--required-capabilities <...>] [--pricing-model <...>] [--json]`
219
+ - `mdkg work trigger <work-or-capability-ref> [--id <order.id>] [--title "<title>"] [--requester <ref>] [--enqueue <queue>] [--json]`
191
220
  - `mdkg work order new "<title>" --id <order.id> --work-id <work.id> --requester <ref> [--request-ref <ref>] [--input-refs <...>] [--requested-outputs <...>] [--json]`
221
+ - `mdkg work order status <id-or-qid> [--json]`
192
222
  - `mdkg work order update <id-or-qid> [--status <status>] [--add-input-refs <...>] [--add-artifacts <...>] [--json]`
193
223
  - `mdkg work receipt new "<title>" --id <receipt.id> --work-order-id <order.id> --outcome success|partial|failure [--receipt-status recorded|verified|rejected|superseded] [--json]`
224
+ - `mdkg work receipt verify <id-or-qid> [--json]`
194
225
  - `mdkg work receipt update <id-or-qid> [--receipt-status <status>] [--add-artifacts <...>] [--add-proof-refs <...>] [--add-attestation-refs <...>] [--json]`
195
226
  - `mdkg work artifact add <order-or-receipt-id-or-qid> <file> [--id <archive.id>] [--kind source|artifact] [--json]`
227
+ - `work trigger` accepts a `WORK.md` ref directly or a `SPEC.md` capability ref with exactly one resolvable work contract; it creates a submitted order mirror and never executes work
228
+ - example: `mdkg work trigger work.example --id order.example-1 --requester user://example --json`
229
+ - `work trigger --enqueue <queue>` requires a valid project DB plus an explicitly created active queue, creates a submitted order mirror, and enqueues a local delivery message without executing work
230
+ - `work order status` is read-only and reports deterministic order state plus linked receipts
231
+ - `work receipt verify` is read-only and reports linkage, evidence, archive ref, hash, outcome, and redaction-policy checks
196
232
  - work commands mutate mdkg semantic mirror files only; production order, receipt, feedback, dispute, payment, ledger, marketplace inventory, fulfillment, and execution state remains canonical outside mdkg
197
233
  - do not store raw secrets, credentials, live payment state, ledger mutations, or canonical marketplace state in work mirrors
198
234
  - `artifact://...` refs identify external/runtime-managed artifacts; `archive://...` refs identify committed mdkg archive sidecars
@@ -25,13 +25,26 @@ mdkg search "..."
25
25
  mdkg show <id>
26
26
  mdkg pack <id>
27
27
  mdkg capability search "..."
28
+ mdkg spec list --json
28
29
  mdkg archive list
29
30
  mdkg bundle create --profile private
30
31
  mdkg subgraph list --json
32
+ mdkg status --json
33
+ mdkg fix plan --json
31
34
  mdkg validate
32
35
  ```
33
36
 
34
- 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 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, `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.
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.
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.
35
48
 
36
49
  Agent workflow docs can use semantic ids:
37
50
 
@@ -40,6 +53,12 @@ mdkg new spec "image worker" --id agent.image-worker
40
53
  mdkg new work "generate image" --id work.generate-image
41
54
  ```
42
55
 
56
+ `SPEC.md` is optional. Repos without SPEC files still validate. When present,
57
+ SPEC records describe reusable capability surfaces rather than general planning
58
+ notes. `mdkg spec list/show/validate` is the focused SPEC command family, while
59
+ `mdkg capability ...` remains the broader read-only discovery surface for
60
+ skills, SPECs, WORK contracts, core docs, and design docs.
61
+
43
62
  Read `AGENT_START.md` first when this repo includes it.
44
63
 
45
64
  ## Pack Profiles
@@ -123,8 +142,12 @@ Register child bundle snapshots as read-only subgraphs with:
123
142
  mdkg subgraph add child_repo child-repo/.mdkg/bundles/private/all.mdkg.zip --source-path child-repo
124
143
  mdkg capability resolve "child capability" --json
125
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
126
147
  ```
127
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
+
128
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.
129
152
 
130
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.
@@ -146,13 +169,29 @@ Use work lifecycle helpers for semantic mirrors only:
146
169
 
147
170
  ```bash
148
171
  mdkg work contract new "example capability" --id work.example --agent-id agent.example --kind example --inputs prompt:text:required --outputs result:text:required
149
- mdkg work order new "example request" --id order.example-1 --work-id work.example --requester user://example
172
+ mdkg work trigger work.example --id order.example-1 --requester user://example
173
+ mdkg work order status order.example-1 --json
150
174
  mdkg work receipt new "example receipt" --id receipt.example-1 --work-order-id order.example-1 --outcome success
175
+ mdkg work receipt verify receipt.example-1 --json
176
+ ```
177
+
178
+ Create a manual order instead of a trigger-created order when you need to supply
179
+ input refs at order creation time:
180
+
181
+ ```bash
182
+ mdkg work order new "example request" --id order.example-manual --work-id work.example --requester user://example --input-refs archive://archive.example
151
183
  ```
152
184
 
153
185
  Receipt statuses are `recorded`, `verified`, `rejected`, and `superseded`.
154
186
  Update and artifact commands accept local ids or local qids; subgraph qids are read-only and must be changed in their source workspace.
155
187
 
188
+ `mdkg work trigger` creates a deterministic submitted `WORK_ORDER.md` from a
189
+ WORK contract or a SPEC with exactly one resolvable work contract. `mdkg work
190
+ order status` and `mdkg work receipt verify` are read-only review helpers.
191
+ `mdkg work trigger --enqueue <queue>` optionally writes a local project DB queue
192
+ delivery message after the queue has been explicitly created and is active; it
193
+ still does not execute work.
194
+
156
195
  Production orders, receipts, feedback, disputes, payments, ledgers, marketplace inventory, fulfillment records, and execution state remain canonical outside mdkg. mdkg stores committed semantic mirrors and reviewable evidence. Do not store raw secrets, credentials, live payment state, ledger mutations, canonical marketplace state, or bulky raw payloads in these mirrors.
157
196
 
158
197
  Use `artifact://...` for external or runtime-managed artifact identities. Use `archive://...` only for committed mdkg archive sidecars.