mdkg 0.0.8 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/CONTRIBUTING.md +124 -0
  3. package/README.md +49 -14
  4. package/dist/cli.js +113 -32
  5. package/dist/commands/checkpoint.js +19 -2
  6. package/dist/commands/event.js +12 -0
  7. package/dist/commands/init.js +4 -0
  8. package/dist/commands/init_manifest.js +131 -0
  9. package/dist/commands/new.js +57 -21
  10. package/dist/commands/pack.js +14 -0
  11. package/dist/commands/query_output.js +2 -0
  12. package/dist/commands/search.js +8 -0
  13. package/dist/commands/show.js +7 -0
  14. package/dist/commands/skill.js +80 -12
  15. package/dist/commands/task.js +42 -12
  16. package/dist/commands/upgrade.js +286 -0
  17. package/dist/commands/validate.js +31 -3
  18. package/dist/commands/workspace.js +105 -13
  19. package/dist/core/config.js +217 -22
  20. package/dist/core/migrate.js +39 -5
  21. package/dist/core/version.js +31 -0
  22. package/dist/core/workspace_path.js +41 -0
  23. package/dist/graph/agent_file_types.js +392 -0
  24. package/dist/graph/edges.js +13 -10
  25. package/dist/graph/frontmatter.js +33 -0
  26. package/dist/graph/indexer.js +1 -0
  27. package/dist/graph/node.js +43 -16
  28. package/dist/graph/skills_indexer.js +14 -1
  29. package/dist/graph/template_schema.js +13 -126
  30. package/dist/graph/validate_graph.js +302 -2
  31. package/dist/init/AGENT_START.md +14 -2
  32. package/dist/init/CLI_COMMAND_MATRIX.md +49 -1
  33. package/dist/init/README.md +14 -0
  34. package/dist/init/core/rule-6-templates-and-schemas.md +1 -3
  35. package/dist/init/init-manifest.json +197 -0
  36. package/dist/init/legacy/v0.0.9-init-manifest.json +197 -0
  37. package/dist/init/skills/default/verify-close-and-checkpoint/SKILL.md +12 -11
  38. package/dist/init/templates/default/dispute.md +31 -0
  39. package/dist/init/templates/default/feedback.md +27 -0
  40. package/dist/init/templates/default/proposal.md +35 -0
  41. package/dist/init/templates/default/receipt.md +31 -0
  42. package/dist/init/templates/default/spec.md +43 -0
  43. package/dist/init/templates/default/work.md +44 -0
  44. package/dist/init/templates/default/work_order.md +32 -0
  45. package/dist/pack/export_json.js +3 -0
  46. package/dist/pack/export_md.js +9 -0
  47. package/dist/pack/export_xml.js +9 -0
  48. package/dist/pack/order.js +7 -0
  49. package/dist/pack/pack.js +1 -0
  50. package/dist/templates/loader.js +2 -2
  51. package/dist/util/argparse.js +2 -0
  52. package/dist/util/id.js +19 -0
  53. package/package.json +10 -2
  54. package/scripts/postinstall.js +89 -0
@@ -3,77 +3,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.ALLOWED_TYPES = exports.DEC_TYPES = exports.WORK_TYPES = void 0;
7
- exports.buildNodeSchemas = buildNodeSchemas;
8
- exports.getNodeSchema = getNodeSchema;
9
- exports.schemaAllowsKey = schemaAllowsKey;
10
6
  exports.loadTemplateSchemas = loadTemplateSchemas;
11
7
  const fs_1 = __importDefault(require("fs"));
12
8
  const path_1 = __importDefault(require("path"));
13
9
  const frontmatter_1 = require("./frontmatter");
14
- exports.WORK_TYPES = new Set(["epic", "feat", "task", "bug", "checkpoint", "test"]);
15
- exports.DEC_TYPES = new Set(["dec"]);
16
- exports.ALLOWED_TYPES = new Set([
17
- "rule",
18
- "prd",
19
- "edd",
20
- "dec",
21
- "prop",
22
- "epic",
23
- "feat",
24
- "task",
25
- "bug",
26
- "checkpoint",
27
- "test",
28
- ]);
29
- const COMMON_SCALAR_KEYS = ["id", "type", "title", "created", "updated"];
30
- const COMMON_LIST_KEYS = ["tags", "owners", "links", "artifacts", "relates", "refs", "aliases"];
31
- const NODE_SCHEMA_DEFS = {
32
- rule: {
33
- scalars: [...COMMON_SCALAR_KEYS],
34
- lists: [...COMMON_LIST_KEYS],
35
- },
36
- prd: {
37
- scalars: [...COMMON_SCALAR_KEYS],
38
- lists: [...COMMON_LIST_KEYS],
39
- },
40
- edd: {
41
- scalars: [...COMMON_SCALAR_KEYS],
42
- lists: [...COMMON_LIST_KEYS],
43
- },
44
- prop: {
45
- scalars: [...COMMON_SCALAR_KEYS],
46
- lists: [...COMMON_LIST_KEYS],
47
- },
48
- dec: {
49
- scalars: [...COMMON_SCALAR_KEYS, "status", "supersedes"],
50
- lists: [...COMMON_LIST_KEYS],
51
- },
52
- epic: {
53
- scalars: [...COMMON_SCALAR_KEYS, "status", "priority"],
54
- lists: [...COMMON_LIST_KEYS, "blocked_by", "blocks", "skills"],
55
- },
56
- feat: {
57
- scalars: [...COMMON_SCALAR_KEYS, "status", "priority", "epic", "parent", "prev", "next"],
58
- lists: [...COMMON_LIST_KEYS, "blocked_by", "blocks", "skills"],
59
- },
60
- task: {
61
- scalars: [...COMMON_SCALAR_KEYS, "status", "priority", "epic", "parent", "prev", "next"],
62
- lists: [...COMMON_LIST_KEYS, "blocked_by", "blocks", "skills"],
63
- },
64
- bug: {
65
- scalars: [...COMMON_SCALAR_KEYS, "status", "priority", "epic", "parent", "prev", "next"],
66
- lists: [...COMMON_LIST_KEYS, "blocked_by", "blocks", "skills"],
67
- },
68
- checkpoint: {
69
- scalars: [...COMMON_SCALAR_KEYS, "status", "priority", "epic", "parent", "prev", "next"],
70
- lists: [...COMMON_LIST_KEYS, "blocked_by", "blocks", "skills", "scope"],
71
- },
72
- test: {
73
- scalars: [...COMMON_SCALAR_KEYS, "status", "priority", "epic", "parent", "prev", "next"],
74
- lists: [...COMMON_LIST_KEYS, "blocked_by", "blocks", "skills", "cases"],
75
- },
76
- };
77
10
  function listMarkdownFiles(dir) {
78
11
  if (!fs_1.default.existsSync(dir)) {
79
12
  return [];
@@ -111,65 +44,13 @@ function addKeyToSchema(schema, key, kind, filePath) {
111
44
  schema.listKeys.add(key);
112
45
  }
113
46
  }
114
- function buildSchema(type, definition) {
115
- const schema = {
116
- type,
117
- allowedKeys: new Set(),
118
- keyKinds: {},
119
- listKeys: new Set(),
120
- };
121
- for (const key of definition.scalars) {
122
- addKeyToSchema(schema, key, "scalar", type);
123
- }
124
- for (const key of definition.lists ?? []) {
125
- addKeyToSchema(schema, key, "list", type);
126
- }
127
- for (const key of definition.booleans ?? []) {
128
- addKeyToSchema(schema, key, "boolean", type);
129
- }
130
- return schema;
131
- }
132
- function buildNodeSchemas(requiredTypes) {
133
- const schemas = {};
134
- for (const [type, definition] of Object.entries(NODE_SCHEMA_DEFS)) {
135
- schemas[type] = buildSchema(type, definition);
136
- }
137
- if (requiredTypes) {
138
- const required = Array.from(requiredTypes, (value) => value.toLowerCase());
139
- const missing = required.filter((value) => !schemas[value]);
140
- if (missing.length > 0) {
141
- throw new Error(`template schema missing for type(s): ${missing.join(", ")}`);
142
- }
143
- }
144
- return schemas;
145
- }
146
- function getNodeSchema(type) {
147
- return buildNodeSchemas()[type.toLowerCase()];
148
- }
149
- function schemaAllowsKey(type, key) {
150
- const schema = getNodeSchema(type);
151
- return Boolean(schema?.allowedKeys.has(key));
152
- }
153
- function validateTemplateAgainstSchema(frontmatter, schema, filePath) {
154
- for (const [key, value] of Object.entries(frontmatter)) {
155
- const expected = schema.keyKinds[key];
156
- if (!expected) {
157
- throw new Error(`template key not allowed for ${schema.type}: ${key} (${filePath})`);
158
- }
159
- const kind = getValueKind(value);
160
- if (expected !== kind) {
161
- throw new Error(`template schema mismatch for ${schema.type}.${key}: expected ${expected} but found ${kind} (${filePath})`);
162
- }
163
- }
164
- }
165
47
  function loadTemplateSchemas(root, config, requiredTypes) {
166
- const schemas = buildNodeSchemas(requiredTypes);
167
48
  const templateRoot = path_1.default.resolve(root, config.templates.root_path, config.templates.default_set);
168
49
  const files = listMarkdownFiles(templateRoot);
169
50
  if (files.length === 0) {
170
51
  throw new Error(`no templates found at ${templateRoot}`);
171
52
  }
172
- const discoveredTypes = new Set();
53
+ const schemas = {};
173
54
  for (const filePath of files) {
174
55
  const content = fs_1.default.readFileSync(filePath, "utf8");
175
56
  const { frontmatter } = (0, frontmatter_1.parseFrontmatter)(content, filePath);
@@ -181,16 +62,22 @@ function loadTemplateSchemas(root, config, requiredTypes) {
181
62
  if (normalizedType !== typeValue) {
182
63
  throw new Error(`template type must be lowercase in ${filePath}`);
183
64
  }
184
- const schema = schemas[normalizedType];
185
- if (!schema) {
186
- throw new Error(`template schema missing for type ${normalizedType}`);
65
+ const schema = schemas[normalizedType] ??
66
+ {
67
+ type: normalizedType,
68
+ allowedKeys: new Set(),
69
+ keyKinds: {},
70
+ listKeys: new Set(),
71
+ };
72
+ schemas[normalizedType] = schema;
73
+ for (const [key, value] of Object.entries(frontmatter)) {
74
+ const kind = getValueKind(value);
75
+ addKeyToSchema(schema, key, kind, filePath);
187
76
  }
188
- validateTemplateAgainstSchema(frontmatter, schema, filePath);
189
- discoveredTypes.add(normalizedType);
190
77
  }
191
78
  if (requiredTypes) {
192
79
  const required = Array.from(requiredTypes, (value) => value.toLowerCase());
193
- const missing = required.filter((value) => !discoveredTypes.has(value));
80
+ const missing = required.filter((value) => !schemas[value]);
194
81
  if (missing.length > 0) {
195
82
  throw new Error(`template schema missing for type(s): ${missing.join(", ")}`);
196
83
  }
@@ -9,7 +9,7 @@ function pushError(errors, message) {
9
9
  }
10
10
  throw new Error(message);
11
11
  }
12
- function validateEdgeTargets(index, allowMissing, errors) {
12
+ function validateEdgeTargets(index, allowMissing, knownSkillSlugs, errors) {
13
13
  const nodes = index.nodes;
14
14
  for (const [qid, node] of Object.entries(nodes)) {
15
15
  const edges = node.edges;
@@ -33,6 +33,12 @@ function validateEdgeTargets(index, allowMissing, errors) {
33
33
  for (const [edgeKey, values] of edgeLists) {
34
34
  for (const value of values) {
35
35
  if (!nodes[value]) {
36
+ if (edgeKey === "relates" &&
37
+ node.type === "proposal" &&
38
+ node.attributes.proposal_kind === "skill_update" &&
39
+ validateSkillRef(qid, edgeKey, value, knownSkillSlugs, allowMissing, errors)) {
40
+ continue;
41
+ }
36
42
  if (allowMissing) {
37
43
  continue;
38
44
  }
@@ -42,6 +48,25 @@ function validateEdgeTargets(index, allowMissing, errors) {
42
48
  }
43
49
  }
44
50
  }
51
+ const SKILL_DOT_REF_RE = /^skill\.([a-z0-9]+(?:-[a-z0-9]+)*)$/;
52
+ function skillSlugFromDotRef(value) {
53
+ const localValue = value.includes(":") ? value.split(":").pop() ?? value : value;
54
+ return SKILL_DOT_REF_RE.exec(localValue)?.[1];
55
+ }
56
+ function validateSkillRef(qid, field, value, knownSkillSlugs, allowMissing, errors) {
57
+ const slug = skillSlugFromDotRef(value);
58
+ if (!slug) {
59
+ return false;
60
+ }
61
+ if (knownSkillSlugs === undefined || knownSkillSlugs.has(slug)) {
62
+ return true;
63
+ }
64
+ if (allowMissing) {
65
+ return true;
66
+ }
67
+ pushError(errors, `${qid}: ${field} references missing skill ${value}`);
68
+ return true;
69
+ }
45
70
  function validatePrevNextSymmetry(index, _allowMissing, errors) {
46
71
  const nodes = index.nodes;
47
72
  for (const [qid, node] of Object.entries(nodes)) {
@@ -66,6 +91,274 @@ function validatePrevNextSymmetry(index, _allowMissing, errors) {
66
91
  }
67
92
  }
68
93
  }
94
+ function normalizeDocPath(value) {
95
+ return value.replace(/\\/g, "/").replace(/^\.\//, "");
96
+ }
97
+ function pathEndsWithContractRef(nodePath, contractRef) {
98
+ const normalizedNodePath = normalizeDocPath(nodePath);
99
+ const normalizedContractRef = normalizeDocPath(contractRef);
100
+ return (normalizedNodePath === normalizedContractRef ||
101
+ normalizedNodePath.endsWith(`/${normalizedContractRef}`));
102
+ }
103
+ function validateAgentWorkflowSpecWorkContracts(index, allowMissing, errors) {
104
+ const workNodesByWorkspace = {};
105
+ for (const node of Object.values(index.nodes)) {
106
+ if (node.type !== "work") {
107
+ continue;
108
+ }
109
+ if (!workNodesByWorkspace[node.ws]) {
110
+ workNodesByWorkspace[node.ws] = [];
111
+ }
112
+ workNodesByWorkspace[node.ws].push({
113
+ qid: node.qid,
114
+ path: node.path,
115
+ agentId: typeof node.attributes.agent_id === "string" ? node.attributes.agent_id : undefined,
116
+ });
117
+ }
118
+ for (const [qid, node] of Object.entries(index.nodes)) {
119
+ if (node.type !== "spec") {
120
+ continue;
121
+ }
122
+ const workContracts = node.attributes.work_contracts;
123
+ if (!Array.isArray(workContracts)) {
124
+ continue;
125
+ }
126
+ const workspaceWorkNodes = workNodesByWorkspace[node.ws] ?? [];
127
+ for (const [indexValue, value] of workContracts.entries()) {
128
+ if (typeof value !== "string") {
129
+ continue;
130
+ }
131
+ const matches = workspaceWorkNodes.filter((workNode) => pathEndsWithContractRef(workNode.path, value));
132
+ if (matches.length === 0) {
133
+ if (allowMissing) {
134
+ continue;
135
+ }
136
+ pushError(errors, `${qid}: work_contracts[${indexValue}] references missing WORK.md ${value}`);
137
+ continue;
138
+ }
139
+ if (matches.length > 1) {
140
+ pushError(errors, `${qid}: work_contracts[${indexValue}] ambiguously matches ${matches
141
+ .map((match) => match.qid)
142
+ .sort()
143
+ .join(", ")}`);
144
+ continue;
145
+ }
146
+ const matchedWorkNode = matches[0];
147
+ if (matchedWorkNode.agentId !== undefined && matchedWorkNode.agentId !== node.id) {
148
+ pushError(errors, `${qid}: work_contracts[${indexValue}] references ${matchedWorkNode.qid} owned by agent_id ${matchedWorkNode.agentId}, not ${node.id}`);
149
+ }
150
+ }
151
+ }
152
+ }
153
+ function validateAgentWorkflowWorkOrderWorkRefs(index, allowMissing, errors) {
154
+ const workNodesByWorkspaceAndId = {};
155
+ for (const node of Object.values(index.nodes)) {
156
+ if (node.type !== "work") {
157
+ continue;
158
+ }
159
+ if (!workNodesByWorkspaceAndId[node.ws]) {
160
+ workNodesByWorkspaceAndId[node.ws] = {};
161
+ }
162
+ workNodesByWorkspaceAndId[node.ws][node.id] = {
163
+ qid: node.qid,
164
+ version: typeof node.attributes.version === "string" ? node.attributes.version : undefined,
165
+ };
166
+ }
167
+ for (const [qid, node] of Object.entries(index.nodes)) {
168
+ if (node.type !== "work_order") {
169
+ continue;
170
+ }
171
+ const workId = node.attributes.work_id;
172
+ if (typeof workId !== "string") {
173
+ continue;
174
+ }
175
+ const workNode = workNodesByWorkspaceAndId[node.ws]?.[workId];
176
+ if (!workNode) {
177
+ if (allowMissing) {
178
+ continue;
179
+ }
180
+ pushError(errors, `${qid}: work_id references missing WORK.md ${workId}`);
181
+ continue;
182
+ }
183
+ const workVersion = node.attributes.work_version;
184
+ if (typeof workVersion === "string" &&
185
+ workNode.version !== undefined &&
186
+ workVersion !== workNode.version) {
187
+ pushError(errors, `${qid}: work_version ${workVersion} does not match ${workNode.qid} version ${workNode.version}`);
188
+ }
189
+ }
190
+ }
191
+ function validateAgentWorkflowReceiptWorkOrderRefs(index, allowMissing, errors) {
192
+ const workOrderIdsByWorkspace = {};
193
+ for (const node of Object.values(index.nodes)) {
194
+ if (node.type !== "work_order") {
195
+ continue;
196
+ }
197
+ if (!workOrderIdsByWorkspace[node.ws]) {
198
+ workOrderIdsByWorkspace[node.ws] = new Set();
199
+ }
200
+ workOrderIdsByWorkspace[node.ws].add(node.id);
201
+ }
202
+ for (const [qid, node] of Object.entries(index.nodes)) {
203
+ if (node.type !== "receipt") {
204
+ continue;
205
+ }
206
+ const workOrderId = node.attributes.work_order_id;
207
+ if (typeof workOrderId !== "string") {
208
+ continue;
209
+ }
210
+ if (workOrderIdsByWorkspace[node.ws]?.has(workOrderId)) {
211
+ continue;
212
+ }
213
+ if (allowMissing) {
214
+ continue;
215
+ }
216
+ pushError(errors, `${qid}: work_order_id references missing WORK_ORDER.md ${workOrderId}`);
217
+ }
218
+ }
219
+ function buildSpecRolesByWorkspace(index) {
220
+ const specRolesByWorkspace = {};
221
+ for (const node of Object.values(index.nodes)) {
222
+ if (node.type !== "spec") {
223
+ continue;
224
+ }
225
+ if (!specRolesByWorkspace[node.ws]) {
226
+ specRolesByWorkspace[node.ws] = {};
227
+ }
228
+ specRolesByWorkspace[node.ws][node.id] = {
229
+ qid: node.qid,
230
+ role: typeof node.attributes.role === "string" ? node.attributes.role : undefined,
231
+ };
232
+ }
233
+ return specRolesByWorkspace;
234
+ }
235
+ function validateAgentWorkflowSubagentRefs(index, allowMissing, errors) {
236
+ const specRolesByWorkspace = buildSpecRolesByWorkspace(index);
237
+ for (const [qid, node] of Object.entries(index.nodes)) {
238
+ if (node.type !== "spec" && node.type !== "work") {
239
+ continue;
240
+ }
241
+ const subagentRefs = node.attributes.subagent_refs;
242
+ if (!Array.isArray(subagentRefs)) {
243
+ continue;
244
+ }
245
+ for (const [indexValue, value] of subagentRefs.entries()) {
246
+ if (typeof value !== "string") {
247
+ continue;
248
+ }
249
+ const specNode = specRolesByWorkspace[node.ws]?.[value];
250
+ if (!specNode) {
251
+ if (allowMissing) {
252
+ continue;
253
+ }
254
+ pushError(errors, `${qid}: subagent_refs[${indexValue}] references missing SPEC.md ${value}`);
255
+ continue;
256
+ }
257
+ if (specNode.role !== undefined && specNode.role !== "subagent") {
258
+ pushError(errors, `${qid}: subagent_refs[${indexValue}] references ${specNode.qid} with role ${specNode.role}, not subagent`);
259
+ }
260
+ }
261
+ }
262
+ }
263
+ function validateAgentWorkflowDisputeRefs(index, allowMissing, errors) {
264
+ const workOrderIdsByWorkspace = {};
265
+ const receiptNodesByWorkspaceAndId = {};
266
+ for (const node of Object.values(index.nodes)) {
267
+ if (node.type === "work_order") {
268
+ if (!workOrderIdsByWorkspace[node.ws]) {
269
+ workOrderIdsByWorkspace[node.ws] = new Set();
270
+ }
271
+ workOrderIdsByWorkspace[node.ws].add(node.id);
272
+ }
273
+ if (node.type === "receipt") {
274
+ if (!receiptNodesByWorkspaceAndId[node.ws]) {
275
+ receiptNodesByWorkspaceAndId[node.ws] = {};
276
+ }
277
+ receiptNodesByWorkspaceAndId[node.ws][node.id] = {
278
+ qid: node.qid,
279
+ workOrderId: typeof node.attributes.work_order_id === "string"
280
+ ? node.attributes.work_order_id
281
+ : undefined,
282
+ };
283
+ }
284
+ }
285
+ for (const [qid, node] of Object.entries(index.nodes)) {
286
+ if (node.type !== "dispute") {
287
+ continue;
288
+ }
289
+ const workOrderId = node.attributes.work_order_id;
290
+ if (typeof workOrderId !== "string") {
291
+ continue;
292
+ }
293
+ if (!workOrderIdsByWorkspace[node.ws]?.has(workOrderId) && !allowMissing) {
294
+ pushError(errors, `${qid}: work_order_id references missing WORK_ORDER.md ${workOrderId}`);
295
+ }
296
+ const receiptId = node.attributes.receipt_id;
297
+ if (typeof receiptId !== "string") {
298
+ continue;
299
+ }
300
+ const receiptNode = receiptNodesByWorkspaceAndId[node.ws]?.[receiptId];
301
+ if (!receiptNode) {
302
+ if (allowMissing) {
303
+ continue;
304
+ }
305
+ pushError(errors, `${qid}: receipt_id references missing RECEIPT.md ${receiptId}`);
306
+ continue;
307
+ }
308
+ if (receiptNode.workOrderId !== undefined && receiptNode.workOrderId !== workOrderId) {
309
+ pushError(errors, `${qid}: receipt_id ${receiptId} belongs to work_order_id ${receiptNode.workOrderId}, not ${workOrderId}`);
310
+ }
311
+ }
312
+ }
313
+ function buildNodeIdsByWorkspace(index) {
314
+ const nodeIdsByWorkspace = {};
315
+ for (const node of Object.values(index.nodes)) {
316
+ if (!nodeIdsByWorkspace[node.ws]) {
317
+ nodeIdsByWorkspace[node.ws] = new Set();
318
+ }
319
+ nodeIdsByWorkspace[node.ws].add(node.id);
320
+ }
321
+ return nodeIdsByWorkspace;
322
+ }
323
+ function validateAgentWorkflowNodeIdRef(qid, ws, field, value, nodeIdsByWorkspace, allowSkillRef, knownSkillSlugs, allowMissing, errors) {
324
+ if (nodeIdsByWorkspace[ws]?.has(value)) {
325
+ return;
326
+ }
327
+ if (allowSkillRef &&
328
+ validateSkillRef(qid, field, value, knownSkillSlugs, allowMissing, errors)) {
329
+ return;
330
+ }
331
+ if (allowMissing) {
332
+ return;
333
+ }
334
+ pushError(errors, `${qid}: ${field} references missing node ${value}`);
335
+ }
336
+ function validateAgentWorkflowFeedbackProposalRefs(index, allowMissing, knownSkillSlugs, errors) {
337
+ const nodeIdsByWorkspace = buildNodeIdsByWorkspace(index);
338
+ for (const [qid, node] of Object.entries(index.nodes)) {
339
+ if (node.type !== "feedback" && node.type !== "proposal") {
340
+ continue;
341
+ }
342
+ const targetId = node.attributes.target_id;
343
+ if (typeof targetId === "string") {
344
+ const allowSkillTarget = node.type === "proposal" && node.attributes.proposal_kind === "skill_update";
345
+ validateAgentWorkflowNodeIdRef(qid, node.ws, "target_id", targetId, nodeIdsByWorkspace, allowSkillTarget, knownSkillSlugs, allowMissing, errors);
346
+ }
347
+ if (node.type !== "proposal") {
348
+ continue;
349
+ }
350
+ const evidenceRefs = node.attributes.evidence_refs;
351
+ if (!Array.isArray(evidenceRefs)) {
352
+ continue;
353
+ }
354
+ for (const [indexValue, value] of evidenceRefs.entries()) {
355
+ if (typeof value !== "string") {
356
+ continue;
357
+ }
358
+ validateAgentWorkflowNodeIdRef(qid, node.ws, `evidence_refs[${indexValue}]`, value, nodeIdsByWorkspace, true, knownSkillSlugs, allowMissing, errors);
359
+ }
360
+ }
361
+ }
69
362
  function detectPrevNextCycles(index, errors) {
70
363
  const nodes = index.nodes;
71
364
  const seen = new Set();
@@ -102,8 +395,15 @@ function detectPrevNextCycles(index, errors) {
102
395
  function collectGraphErrors(index, options = {}) {
103
396
  const errors = [];
104
397
  const allowMissing = options.allowMissing ?? false;
105
- validateEdgeTargets(index, allowMissing, errors);
398
+ const knownSkillSlugs = options.knownSkillSlugs;
399
+ validateEdgeTargets(index, allowMissing, knownSkillSlugs, errors);
106
400
  validatePrevNextSymmetry(index, allowMissing, errors);
401
+ validateAgentWorkflowSpecWorkContracts(index, allowMissing, errors);
402
+ validateAgentWorkflowWorkOrderWorkRefs(index, allowMissing, errors);
403
+ validateAgentWorkflowReceiptWorkOrderRefs(index, allowMissing, errors);
404
+ validateAgentWorkflowSubagentRefs(index, allowMissing, errors);
405
+ validateAgentWorkflowDisputeRefs(index, allowMissing, errors);
406
+ validateAgentWorkflowFeedbackProposalRefs(index, allowMissing, knownSkillSlugs, errors);
107
407
  detectPrevNextCycles(index, errors);
108
408
  return errors;
109
409
  }
@@ -15,6 +15,20 @@ Trust order:
15
15
  - relevant skills
16
16
  - chat history
17
17
 
18
+ Agent operating prompt:
19
+ - You are working in a repository that uses mdkg for deterministic project memory.
20
+ - Prefer `mdkg pack <id>` over ad-hoc file lists when a work item is known.
21
+ - Treat mdkg rules, EDDs, DECs, PRDs, and work nodes as more authoritative than chat memory.
22
+ - Use `mdkg show <id>` for direct inspection and `mdkg show <id> --meta` for card-only inspection.
23
+ - Use `mdkg search "..."` and `mdkg next` to discover current work.
24
+ - Use `mdkg skill list`, `mdkg skill search`, and `mdkg skill show <slug>` for skill discovery.
25
+ - Use `mdkg task start/update/done` for structured task, bug, and test lifecycle fields.
26
+ - Use `mdkg upgrade` to preview scaffold updates; only run `mdkg upgrade --apply` after reviewing the receipt.
27
+ - Keep nuanced summaries, body text, and manual parent closeout edits in markdown.
28
+ - Use `mdkg event enable` only if `events.jsonl` is missing and provenance should be restored.
29
+ - Use `CLI_COMMAND_MATRIX.md` for the canonical command and flag surface.
30
+ - Run `mdkg validate` before marking work done.
31
+
18
32
  If the active task is known:
19
33
  - `mdkg pack <id>`
20
34
  - `mdkg task start <id>` when durable work begins
@@ -41,8 +55,6 @@ Conventions:
41
55
  - mdkg does not execute skill scripts; runtimes decide when and whether to do that.
42
56
  - Prefer packs over ad-hoc file lists.
43
57
  - Prefer task/event commands for structured work-state changes and use markdown edits for narrative/body updates.
44
- - `skills` is valid metadata on all work items (`epic`, `feat`, `task`, `bug`, `checkpoint`, `test`), but `mdkg task ...` still mutates only `task`, `bug`, and `test`.
45
- - Treat mdkg-generated work items as schema-consistent by default: `mdkg new`, `mdkg validate`, and `mdkg task ...` should not require manual frontmatter repair.
46
58
  - Use `mdkg task done <id> --checkpoint "<title>"` for milestone compression, not every routine task completion.
47
59
  - Prefer checkpoints for feat/epic closeout summaries; parent status edits remain manual.
48
60
  - If `events.jsonl` is missing, `mdkg task start` and `mdkg task done` will remind you how to recreate it.
@@ -8,6 +8,7 @@ Verify live help with:
8
8
 
9
9
  Primary commands:
10
10
  - `mdkg init`
11
+ - `mdkg upgrade [--dry-run] [--apply] [--json]`
11
12
  - `mdkg new`
12
13
  - `mdkg show`
13
14
  - `mdkg list`
@@ -17,17 +18,64 @@ Primary commands:
17
18
  - `mdkg task`
18
19
  - `mdkg validate`
19
20
 
21
+ Validation commands:
22
+ - `mdkg validate [--out <path>] [--quiet] [--json]`
23
+
24
+ Node creation commands:
25
+ - `mdkg new <type> "<title>" [options] [--json]`
26
+
27
+ Agent workflow file type creation:
28
+ - `mdkg new spec "<title>" [options] [--json]`
29
+ - `mdkg new work "<title>" [options] [--json]`
30
+ - `mdkg new work_order "<title>" [options] [--json]`
31
+ - `mdkg new receipt "<title>" [options] [--json]`
32
+ - `mdkg new feedback "<title>" [options] [--json]`
33
+ - `mdkg new dispute "<title>" [options] [--json]`
34
+ - `mdkg new proposal "<title>" [options] [--json]`
35
+ - `mdkg new spec "image worker" --id agent.image-worker`
36
+ - `mdkg new work "generate image" --id work.generate-image`
37
+
38
+ Agent workflow notes:
39
+ - `--id <portable-id>` is only for agent workflow file types.
40
+ - `spec` and `work` scaffold as validation-clean standalone docs.
41
+ - `work_order`, `receipt`, `feedback`, `dispute`, and `proposal` need real refs before strict `mdkg validate` passes.
42
+
43
+ Workspace registry commands:
44
+ - `mdkg workspace ls [--json]`
45
+ - `mdkg workspace add <alias> <path> [--mdkg-dir <dir>] [--json]`
46
+ - `mdkg workspace rm <alias> [--json]`
47
+ - `mdkg workspace enable <alias> [--json]`
48
+ - `mdkg workspace disable <alias> [--json]`
49
+
50
+ Event log commands:
51
+ - `mdkg event enable [--ws <alias>] [--json]`
52
+ - `mdkg event append --kind <kind> --status <ok|error|retry|skipped> --refs <id,...> [options] [--json]`
53
+
54
+ Task mutation commands:
55
+ - `mdkg task start <id-or-qid> [--ws <alias>] [--run-id <id>] [--note "<text>"] [--json]`
56
+ - `mdkg task update <id-or-qid> [options] [--json]`
57
+ - `mdkg task done <id-or-qid> [--checkpoint "<title>"] [options] [--json]`
58
+
59
+ Checkpoint commands:
60
+ - `mdkg checkpoint new <title> [--ws <alias>] [--json]`
61
+
20
62
  Agent bootstrap:
21
63
  - `mdkg init --llm`
22
64
  - `mdkg init --agent`
23
65
  - `mdkg init --llm --agent`
24
66
  - published bootstrap config is root-only by default
25
67
 
68
+ Upgrade:
69
+ - `mdkg upgrade` previews safe scaffold updates and writes nothing by default
70
+ - `mdkg upgrade --apply` updates only managed or unchanged init assets
71
+ - customized docs, templates, skills, and core files are preserved and reported
72
+
26
73
  Skill discovery:
27
74
  - `mdkg skill list --tags stage:plan --json`
28
75
  - `mdkg skill search "<query>" --json`
29
76
  - `mdkg skill show <slug> --json`
30
- - `mdkg skill sync`
77
+ - `mdkg skill validate [<slug>] [--json]`
78
+ - `mdkg skill sync [--force] [--json]`
31
79
 
32
80
  Discovery/show export flags:
33
81
  - `--json`
@@ -15,12 +15,20 @@ This repository is initialized for mdkg.
15
15
 
16
16
  ```bash
17
17
  mdkg init --llm --agent
18
+ mdkg upgrade
18
19
  mdkg search "..."
19
20
  mdkg show <id>
20
21
  mdkg pack <id>
21
22
  mdkg validate
22
23
  ```
23
24
 
25
+ Agent workflow docs can use semantic ids:
26
+
27
+ ```bash
28
+ mdkg new spec "image worker" --id agent.image-worker
29
+ mdkg new work "generate image" --id work.generate-image
30
+ ```
31
+
24
32
  Read `AGENT_START.md` first when this repo includes it.
25
33
 
26
34
  ## Pack Profiles
@@ -43,3 +51,9 @@ Recommended:
43
51
  ```bash
44
52
  mdkg init --update-gitignore --update-npmignore
45
53
  ```
54
+
55
+ ## Upgrade
56
+
57
+ `mdkg upgrade` previews safe scaffold updates for existing workspaces and writes nothing by default.
58
+
59
+ Use `mdkg upgrade --apply` only after reviewing the receipt. Local customizations are preserved and reported instead of overwritten.
@@ -95,9 +95,7 @@ Work items (`epic/feat/task/bug/checkpoint/test`):
95
95
  - `status` (enum)
96
96
  - optional `priority` (0..9)
97
97
  - optional `skills: [slug, ...]` (kebab-case skill slugs)
98
- - optional graph fields are type-specific by the shared schema/templates
99
- - `relates`, `blocked_by`, and `blocks` are common work-item list fields
100
- - `epic`, `parent`, `prev`, and `next` are only valid on the work-item types whose templates/schema include them
98
+ - optional graph edges: `epic`, `parent`, `relates`, `blocked_by`, `blocks`, `prev`, `next`
101
99
 
102
100
  Decision records (`dec-*`):
103
101
  - `status` (enum: `proposed`, `accepted`, `rejected`, `superseded`)