vibe-coding-master 0.7.38 → 0.7.40

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 (49) hide show
  1. package/README.md +12 -6
  2. package/dist/backend/adapters/claude-adapter.js +1 -0
  3. package/dist/backend/api/artifact-routes.js +72 -1
  4. package/dist/backend/api/runtime-state-routes.js +17 -5
  5. package/dist/backend/api/task-routes.js +6 -0
  6. package/dist/backend/api/workflow-control-routes.js +56 -0
  7. package/dist/backend/cli/install-vcm-harness.js +46 -22
  8. package/dist/backend/server.js +21 -5
  9. package/dist/backend/services/artifact-service.js +309 -5
  10. package/dist/backend/services/auto-memory-service.js +2 -2
  11. package/dist/backend/services/code-intelligence-service.js +5 -5
  12. package/dist/backend/services/gate-review-service.js +81 -33
  13. package/dist/backend/services/harness-feedback-service.js +11 -4
  14. package/dist/backend/services/harness-service.js +42 -26
  15. package/dist/backend/services/message-service.js +105 -37
  16. package/dist/backend/services/status-service.js +6 -0
  17. package/dist/backend/services/workflow-control-service.js +854 -0
  18. package/dist/backend/templates/handoff.js +158 -3
  19. package/dist/backend/templates/harness/architect-agent.js +3 -3
  20. package/dist/backend/templates/harness/check-scaffold-ledger.js +105 -39
  21. package/dist/backend/templates/harness/claude-root.js +9 -1
  22. package/dist/backend/templates/harness/coder-agent.js +2 -3
  23. package/dist/backend/templates/harness/coder-worker-agent.js +1 -1
  24. package/dist/backend/templates/harness/gate-review.js +4 -5
  25. package/dist/backend/templates/harness/harness-engineer-agent.js +18 -1
  26. package/dist/backend/templates/harness/project-manager-agent.js +3 -2
  27. package/dist/backend/templates/harness/tester-agent.js +1 -0
  28. package/dist/backend/templates/harness/vcm-architecture-interview-skill.js +5 -0
  29. package/dist/backend/templates/harness/vcm-code-navigation-skill.js +7 -6
  30. package/dist/backend/templates/harness/vcm-final-acceptance-skill.js +2 -2
  31. package/dist/backend/templates/harness/vcm-long-running-validation-skill.js +4 -2
  32. package/dist/backend/templates/harness/vcm-propose-memory-skill.js +4 -2
  33. package/dist/backend/templates/harness/vcm-report-harness-issue-skill.js +2 -1
  34. package/dist/backend/templates/harness/vcm-route-message-skill.js +4 -7
  35. package/dist/backend/templates/harness/vcm-task-state-skill.js +2 -13
  36. package/dist/backend/templates/harness/vcm-workflow-review-skill.js +62 -0
  37. package/dist/backend/templates/message-envelope.js +4 -4
  38. package/dist/shared/types/workflow.js +7 -1
  39. package/dist/shared/validation/artifact-check.js +99 -95
  40. package/dist/shared/validation/artifact-contract.js +9 -0
  41. package/dist/shared/validation/artifact-registry.js +211 -0
  42. package/dist-frontend/assets/{index-VillnFRo.js → index-Bocc2DWF.js} +34 -34
  43. package/dist-frontend/assets/{index-CXSOe-NN.css → index-C_XHGNBD.css} +1 -1
  44. package/dist-frontend/index.html +2 -2
  45. package/package.json +1 -1
  46. package/scripts/harness-tools/run-long-check +38 -8
  47. package/scripts/harness-tools/vcm-artifact +148 -0
  48. package/scripts/harness-tools/vcm-bash-guard +42 -47
  49. package/scripts/harness-tools/watch-job +58 -4
@@ -25,6 +25,7 @@ import { renderVcmProposeMemorySkillRules } from "../templates/harness/vcm-propo
25
25
  import { renderVcmReportHarnessIssueSkillRules } from "../templates/harness/vcm-report-harness-issue-skill.js";
26
26
  import { renderVcmRouteMessageSkillRules } from "../templates/harness/vcm-route-message-skill.js";
27
27
  import { renderUpdateTaskStateTool, renderVcmTaskStateSkillRules } from "../templates/harness/vcm-task-state-skill.js";
28
+ import { renderVcmWorkflowReviewSkillRules } from "../templates/harness/vcm-workflow-review-skill.js";
28
29
  import { renderCheckScaffoldLedgerTool } from "../templates/harness/check-scaffold-ledger.js";
29
30
  import { renderRequestArchitectRestartTool, renderRestartArchitectSkillRules } from "../templates/harness/restart-architect-skill.js";
30
31
  import { submitTerminalInput } from "../runtime/terminal-submit.js";
@@ -53,7 +54,7 @@ const VCM_BASH_DEFAULT_TIMEOUT_MS = "600000";
53
54
  const VCM_AUTO_MEMORY_ENABLED = false;
54
55
  const VCM_HOOK_DEFINITIONS = [
55
56
  { eventName: "PreToolUse", matcher: "Bash", command: VCM_BASH_GUARD_HOOK_COMMAND, timeout: 10 },
56
- { eventName: "PreToolUse", matcher: "Grep", command: VCM_BASH_GUARD_HOOK_COMMAND, timeout: 10 },
57
+ { eventName: "PreToolUse", matcher: "Write|Edit", command: VCM_BASH_GUARD_HOOK_COMMAND, timeout: 10 },
57
58
  { eventName: "UserPromptSubmit", command: VCM_HOOK_COMMAND, timeout: 5 },
58
59
  { eventName: "Stop", command: VCM_STOP_HOOK_COMMAND, timeout: 10 },
59
60
  { eventName: "StopFailure", command: VCM_HOOK_COMMAND, timeout: 5 },
@@ -137,6 +138,14 @@ const HARNESS_FILES = [
137
138
  ownership: "whole-file",
138
139
  renderRules: renderVcmTaskStateSkillRules
139
140
  },
141
+ {
142
+ kind: "skill-vcm-workflow-review",
143
+ path: ".claude/skills/vcm-workflow-review/SKILL.md",
144
+ title: "VCM Workflow Review Skill",
145
+ frontmatter: renderSkillFrontmatter("vcm-workflow-review", "Use before every project-manager dispatch to Architect, Coder, or Tester so VCM can approve the workflow transition."),
146
+ ownership: "whole-file",
147
+ renderRules: renderVcmWorkflowReviewSkillRules
148
+ },
140
149
  {
141
150
  kind: "skill-vcm-final-acceptance",
142
151
  path: ".claude/skills/vcm-final-acceptance/SKILL.md",
@@ -198,9 +207,9 @@ const HARNESS_FILES = [
198
207
  path: ".claude/agents/reviewer.md",
199
208
  title: "Reviewer Agent",
200
209
  memoryBlock: true,
201
- requiredTools: ["LSP"],
202
- forbiddenTools: ["Grep"],
203
- frontmatter: renderAgentFrontmatter("reviewer", "VCM independent gate review role for architecture plans, validation adequacy, and code diffs.", { tools: "Read, Glob, Bash, Write, LSP" }),
210
+ requiredTools: ["Grep", "LSP"],
211
+ requiredSkills: ["vcm-code-navigation"],
212
+ frontmatter: renderAgentFrontmatter("reviewer", "VCM independent gate review role for architecture plans, validation adequacy, and code diffs.", { tools: "Read, Grep, Glob, Bash, Write, LSP", skills: ["vcm-code-navigation"] }),
204
213
  renderRules: renderReviewerAgentRules
205
214
  },
206
215
  {
@@ -273,10 +282,10 @@ const HARNESS_FILES = [
273
282
  path: ".claude/agents/architect.md",
274
283
  title: "Architect Agent",
275
284
  memoryBlock: true,
276
- requiredTools: ["Agent", "LSP"],
277
- forbiddenTools: ["Grep"],
285
+ requiredTools: ["Grep", "Agent", "LSP"],
286
+ requiredSkills: ["vcm-code-navigation"],
278
287
  blankLineBeforeEnd: true,
279
- frontmatter: renderAgentFrontmatter("architect", "VCM architecture role for plans, module boundaries, public contracts, verifiable behavior, and docs sync.", { tools: "Read, Glob, Bash, Edit, Write, Agent, LSP" }),
288
+ frontmatter: renderAgentFrontmatter("architect", "VCM architecture role for plans, module boundaries, public contracts, verifiable behavior, and docs sync.", { tools: "Read, Grep, Glob, Bash, Edit, Write, Agent, LSP", skills: ["vcm-code-navigation"] }),
280
289
  renderRules: renderArchitectHarnessRules
281
290
  },
282
291
  {
@@ -284,9 +293,9 @@ const HARNESS_FILES = [
284
293
  path: ".claude/agents/coder.md",
285
294
  title: "Coder Agent",
286
295
  memoryBlock: true,
287
- requiredTools: ["Agent", "LSP"],
288
- forbiddenTools: ["Grep"],
289
- frontmatter: renderAgentFrontmatter("coder", "VCM implementation role for scoped code changes and focused tests.", { tools: "Read, Glob, Bash, Edit, Write, Agent, LSP" }),
296
+ requiredTools: ["Grep", "Agent", "LSP"],
297
+ requiredSkills: ["vcm-code-navigation"],
298
+ frontmatter: renderAgentFrontmatter("coder", "VCM implementation role for scoped code changes and focused tests.", { tools: "Read, Grep, Glob, Bash, Edit, Write, Agent, LSP", skills: ["vcm-code-navigation"] }),
290
299
  renderRules: renderCoderHarnessRules
291
300
  },
292
301
  {
@@ -1224,7 +1233,7 @@ async function analyzeHarnessFile(fs, repoRoot, definition) {
1224
1233
  const migratedContent = migrateLegacyHarnessFile(definition, currentContent, expectedBlock);
1225
1234
  if (migratedContent) {
1226
1235
  const memoryUpdatedContent = definition.memoryBlock ? ensureVcmMemoryBlock(migratedContent) : migratedContent;
1227
- const nextContent = normalizeAgentTools(memoryUpdatedContent, definition.requiredTools, definition.forbiddenTools);
1236
+ const nextContent = normalizeAgentFrontmatter(memoryUpdatedContent, definition);
1228
1237
  return {
1229
1238
  definition,
1230
1239
  status: {
@@ -1243,7 +1252,7 @@ async function analyzeHarnessFile(fs, repoRoot, definition) {
1243
1252
  };
1244
1253
  }
1245
1254
  const insertedContent = `${currentContent.trimEnd()}\n\n${expectedBlock}\n`;
1246
- const nextContent = normalizeAgentTools(insertedContent, definition.requiredTools, definition.forbiddenTools);
1255
+ const nextContent = normalizeAgentFrontmatter(insertedContent, definition);
1247
1256
  return {
1248
1257
  definition,
1249
1258
  status: {
@@ -1265,7 +1274,7 @@ async function analyzeHarnessFile(fs, repoRoot, definition) {
1265
1274
  const currentBlock = match[0];
1266
1275
  const blockUpdatedContent = currentContent.replace(managedBlockPattern, expectedBlock);
1267
1276
  const memoryUpdatedContent = definition.memoryBlock ? ensureVcmMemoryBlock(blockUpdatedContent) : blockUpdatedContent;
1268
- const nextContent = normalizeAgentTools(memoryUpdatedContent, definition.requiredTools, definition.forbiddenTools);
1277
+ const nextContent = normalizeAgentFrontmatter(memoryUpdatedContent, definition);
1269
1278
  const action = currentContent === nextContent ? "ok" : "update";
1270
1279
  return {
1271
1280
  definition,
@@ -1396,25 +1405,29 @@ function ensureAgentTool(content, requiredTool) {
1396
1405
  function ensureAgentTools(content, requiredTools) {
1397
1406
  return (requiredTools ?? []).reduce(ensureAgentTool, content);
1398
1407
  }
1399
- function removeAgentTool(content, forbiddenTool) {
1408
+ function normalizeAgentFrontmatter(content, definition) {
1409
+ const toolsUpdated = ensureAgentTools(content, definition.requiredTools);
1410
+ return (definition.requiredSkills ?? []).reduce(ensureAgentSkill, toolsUpdated);
1411
+ }
1412
+ function ensureAgentSkill(content, requiredSkill) {
1400
1413
  const frontmatterMatch = content.match(/^---\r?\n[\s\S]*?\r?\n---/);
1401
1414
  if (!frontmatterMatch) {
1402
1415
  return content;
1403
1416
  }
1404
- const toolsMatch = frontmatterMatch[0].match(/^tools:\s*(.*)$/m);
1405
- if (!toolsMatch) {
1406
- return content;
1417
+ const frontmatter = frontmatterMatch[0];
1418
+ const skillsMatch = frontmatter.match(/^skills:[ \t]*(?:\r?\n((?:\s+-\s+[^\r\n]+\r?\n?)*))?/m);
1419
+ if (!skillsMatch) {
1420
+ return content.replace(frontmatter, frontmatter.replace(/\r?\n---$/, `\nskills:\n - ${requiredSkill}\n---`));
1407
1421
  }
1408
- const tools = toolsMatch[1].split(",").map((tool) => tool.trim()).filter(Boolean);
1409
- const nextTools = tools.filter((tool) => tool !== forbiddenTool);
1410
- if (nextTools.length === tools.length) {
1422
+ const listedSkills = (skillsMatch[1] ?? "")
1423
+ .split(/\r?\n/)
1424
+ .map((line) => line.match(/^\s+-\s+(.+)$/)?.[1]?.trim())
1425
+ .filter((skill) => Boolean(skill));
1426
+ if (listedSkills.includes(requiredSkill)) {
1411
1427
  return content;
1412
1428
  }
1413
- return content.replace(frontmatterMatch[0], frontmatterMatch[0].replace(toolsMatch[0], `tools: ${nextTools.join(", ")}`));
1414
- }
1415
- function normalizeAgentTools(content, requiredTools, forbiddenTools) {
1416
- const required = ensureAgentTools(content, requiredTools);
1417
- return (forbiddenTools ?? []).reduce(removeAgentTool, required);
1429
+ const nextSkills = `${skillsMatch[0].trimEnd()}\n - ${requiredSkill}`;
1430
+ return content.replace(frontmatter, frontmatter.replace(skillsMatch[0], nextSkills));
1418
1431
  }
1419
1432
  function migrateLegacyHarnessFile(definition, currentContent, block) {
1420
1433
  const legacyContent = definition.legacyWholeFile?.trimEnd();
@@ -1548,7 +1561,10 @@ function renderAgentFrontmatter(name, description, options = {}) {
1548
1561
  const tools = options.tools ?? "Read, Grep, Glob, Bash, Edit, Write";
1549
1562
  const model = options.model ? `\nmodel: ${options.model}` : "";
1550
1563
  const effort = options.effort ? `\neffort: ${options.effort}` : "";
1551
- return `---\nname: ${name}\ndescription: ${description}\ntools: ${tools}${model}${effort}\n---`;
1564
+ const skills = options.skills?.length
1565
+ ? `\nskills:\n${options.skills.map((skill) => ` - ${skill}`).join("\n")}`
1566
+ : "";
1567
+ return `---\nname: ${name}\ndescription: ${description}\ntools: ${tools}${model}${effort}${skills}\n---`;
1552
1568
  }
1553
1569
  function renderSkillFrontmatter(name, description) {
1554
1570
  return `---\nname: ${name}\ndescription: ${description}\n---`;
@@ -1,5 +1,5 @@
1
1
  import path from "node:path";
2
- import { randomUUID } from "node:crypto";
2
+ import { createHash, randomUUID } from "node:crypto";
3
3
  import { CORE_VCM_ROLE_NAMES } from "../../shared/constants.js";
4
4
  import { resolveRepoPath } from "../adapters/filesystem.js";
5
5
  import { submitTerminalInput } from "../runtime/terminal-submit.js";
@@ -94,8 +94,28 @@ export function createMessageService(deps) {
94
94
  failureReason: `${routeFile.toRole} is still running.`
95
95
  };
96
96
  }
97
+ const messageId = id();
98
+ if (routeFile.fromRole === PM_ROLE && deps.workflowControlService) {
99
+ try {
100
+ await deps.workflowControlService.claimDispatch({
101
+ ...toWorkflowContext(input),
102
+ routePath: routeFile.path,
103
+ targetRole: routeFile.toRole,
104
+ routeContentHash: routeFile.contentHash ?? "",
105
+ messageId
106
+ });
107
+ }
108
+ catch (error) {
109
+ return {
110
+ delivered: false,
111
+ requiresUserApproval: false,
112
+ clearedRouteFile: false,
113
+ failureReason: errorMessage(error)
114
+ };
115
+ }
116
+ }
97
117
  const message = {
98
- id: id(),
118
+ id: messageId,
99
119
  taskSlug: input.taskSlug,
100
120
  createdAt: timestamp,
101
121
  fromRole: routeFile.fromRole,
@@ -103,7 +123,6 @@ export function createMessageService(deps) {
103
123
  type: routeFile.type,
104
124
  body: routeFile.body,
105
125
  artifactRefs: routeFile.artifactRefs,
106
- workflow: routeFile.fromRole === PM_ROLE ? routeFile.workflow : undefined,
107
126
  bodyPath: routeFile.path,
108
127
  routePath: routeFile.path,
109
128
  dispatchingAt: timestamp,
@@ -115,16 +134,24 @@ export function createMessageService(deps) {
115
134
  ...message,
116
135
  deliveredAt: timestamp
117
136
  };
118
- await submitTerminalInput(deps.runtime, session.id, renderMessageEnvelope(delivered), {
119
- enterDelayMs: autoDispatchEnterDelayMs
120
- });
137
+ try {
138
+ await submitTerminalInput(deps.runtime, session.id, renderMessageEnvelope(delivered), {
139
+ enterDelayMs: autoDispatchEnterDelayMs
140
+ });
141
+ }
142
+ catch (error) {
143
+ if (routeFile.fromRole === PM_ROLE) {
144
+ await deps.workflowControlService?.releaseDispatch(toWorkflowContext(input), messageId);
145
+ }
146
+ throw error;
147
+ }
121
148
  await deps.sessionService.markRoleActivityRunning(input.repoRoot, input.taskSlug, routeFile.toRole, session.id);
122
149
  if (routeFile.fromRole === PM_ROLE) {
123
150
  await deps.taskWorkflowService?.recordPmDispatch({
124
151
  taskRepoRoot: input.taskRepoRoot ?? input.repoRoot,
125
152
  stateRoot: input.stateRoot,
126
153
  taskSlug: input.taskSlug
127
- }, routeFile.workflow, {
154
+ }, undefined, {
128
155
  messageId: delivered.id,
129
156
  toRole: delivered.toRole
130
157
  }).catch(() => undefined);
@@ -158,7 +185,7 @@ export function createMessageService(deps) {
158
185
  const messages = await readLatestMessages(deps.fs, getMessagesPath(getStateRepoRoot(input), input.stateRoot, input.taskSlug));
159
186
  const messageId = extractVcmMessageId(input.prompt);
160
187
  if (!messageId) {
161
- return undefined;
188
+ return confirmManualRoute(input, timestamp);
162
189
  }
163
190
  const message = findDeliveredMessageForPrompt(messages, input.role, messageId);
164
191
  if (!message) {
@@ -169,6 +196,9 @@ export function createMessageService(deps) {
169
196
  acceptedAt: timestamp,
170
197
  failureReason: undefined
171
198
  };
199
+ if (accepted.fromRole === PM_ROLE) {
200
+ await deps.workflowControlService?.confirmDispatch(toWorkflowContext(input), accepted.id);
201
+ }
172
202
  await appendMessageSnapshot(deps.fs, input, accepted);
173
203
  await clearRouteFileIfStillMatchesMessage(deps.fs, input, accepted);
174
204
  return accepted;
@@ -251,6 +281,58 @@ export function createMessageService(deps) {
251
281
  return messages.find((message) => message.id === messageId);
252
282
  });
253
283
  }
284
+ async function confirmManualRoute(input, timestamp) {
285
+ if (!input.prompt?.trim() || input.role === PM_ROLE)
286
+ return undefined;
287
+ const routes = await listRouteFiles(deps.fs, toRouteContext(input));
288
+ const routeFile = routes.find((candidate) => candidate.pending
289
+ && candidate.fromRole === PM_ROLE
290
+ && candidate.toRole === input.role
291
+ && candidate.body.trim() === input.prompt.trim());
292
+ if (!routeFile)
293
+ return undefined;
294
+ const messageId = id();
295
+ await deps.workflowControlService?.claimDispatch({
296
+ ...toWorkflowContext(input),
297
+ routePath: routeFile.path,
298
+ targetRole: routeFile.toRole,
299
+ routeContentHash: routeFile.contentHash ?? "",
300
+ messageId
301
+ });
302
+ const accepted = {
303
+ id: messageId,
304
+ taskSlug: input.taskSlug,
305
+ createdAt: timestamp,
306
+ fromRole: routeFile.fromRole,
307
+ toRole: routeFile.toRole,
308
+ type: routeFile.type,
309
+ body: routeFile.body,
310
+ artifactRefs: routeFile.artifactRefs,
311
+ bodyPath: routeFile.path,
312
+ routePath: routeFile.path,
313
+ dispatchingAt: timestamp,
314
+ deliveredAt: timestamp,
315
+ acceptedAt: timestamp
316
+ };
317
+ try {
318
+ await deps.workflowControlService?.confirmDispatch(toWorkflowContext(input), messageId);
319
+ }
320
+ catch (error) {
321
+ await deps.workflowControlService?.releaseDispatch(toWorkflowContext(input), messageId);
322
+ throw error;
323
+ }
324
+ await appendMessageSnapshot(deps.fs, input, accepted);
325
+ await clearRouteFileIfStillMatchesMessage(deps.fs, input, accepted);
326
+ await deps.taskWorkflowService?.recordPmDispatch({
327
+ taskRepoRoot: input.taskRepoRoot ?? input.repoRoot,
328
+ stateRoot: input.stateRoot,
329
+ taskSlug: input.taskSlug
330
+ }, undefined, {
331
+ messageId: accepted.id,
332
+ toRole: accepted.toRole
333
+ }).catch(() => undefined);
334
+ return accepted;
335
+ }
254
336
  async function markDispatchConfirmationFailure(input, messageId, failureReason) {
255
337
  await withTaskLock(taskLocks, getMessagesPath(getStateRepoRoot(input), input.stateRoot, input.taskSlug), async () => {
256
338
  const messages = await readLatestMessages(deps.fs, getMessagesPath(getStateRepoRoot(input), input.stateRoot, input.taskSlug));
@@ -258,6 +340,9 @@ export function createMessageService(deps) {
258
340
  if (!current || current.acceptedAt) {
259
341
  return;
260
342
  }
343
+ if (current.fromRole === PM_ROLE) {
344
+ await deps.workflowControlService?.releaseDispatch(toWorkflowContext(input), messageId);
345
+ }
261
346
  await appendMessageSnapshot(deps.fs, input, {
262
347
  ...current,
263
348
  failureReason
@@ -272,6 +357,14 @@ function toRouteContext(input) {
272
357
  handoffDir: input.handoffDir
273
358
  };
274
359
  }
360
+ function toWorkflowContext(input) {
361
+ return {
362
+ taskRepoRoot: input.taskRepoRoot ?? input.repoRoot,
363
+ stateRoot: input.stateRoot,
364
+ handoffDir: input.handoffDir,
365
+ taskSlug: input.taskSlug
366
+ };
367
+ }
275
368
  async function listRouteFiles(fs, input) {
276
369
  const repoRoot = input.taskRepoRoot ?? input.repoRoot;
277
370
  const routeDir = path.posix.join(input.handoffDir, "messages");
@@ -296,9 +389,9 @@ async function listRouteFiles(fs, input) {
296
389
  type: parsed.type,
297
390
  body: parsed.body,
298
391
  artifactRefs: parsed.artifactRefs,
299
- workflow: parsed.workflow,
300
392
  exists: true,
301
- pending: parsed.body.trim().length > 0
393
+ pending: parsed.body.trim().length > 0,
394
+ contentHash: createHash("sha256").update(content).digest("hex")
302
395
  });
303
396
  }
304
397
  return routeFiles;
@@ -320,12 +413,10 @@ function parseRouteFileContent(content, fromRole, toRole) {
320
413
  const { frontmatter, body } = splitFrontmatter(content);
321
414
  const type = parseMessageType(frontmatter.type) ?? getDefaultMessageType(fromRole, toRole);
322
415
  const artifactRefs = parseArtifactRefs(frontmatter);
323
- const workflow = fromRole === PM_ROLE ? parseWorkflowDeclaration(frontmatter) : undefined;
324
416
  return {
325
417
  type,
326
418
  body: body.trim(),
327
- artifactRefs,
328
- workflow
419
+ artifactRefs
329
420
  };
330
421
  }
331
422
  function splitFrontmatter(content) {
@@ -376,25 +467,6 @@ function parseArtifactRefs(frontmatter) {
376
467
  .map((entry) => entry.trim())
377
468
  .filter(Boolean);
378
469
  }
379
- function parseWorkflowDeclaration(frontmatter) {
380
- const declaration = {
381
- flow: frontmatter.workflow_flow,
382
- step: frontmatter.workflow_step,
383
- branch: frontmatter.workflow_branch,
384
- resumePoint: frontmatter.workflow_resume_point,
385
- status: frontmatter.workflow_status,
386
- evidenceRefs: parseCommaSeparated(frontmatter.workflow_evidence_refs)
387
- };
388
- return Object.values(declaration).some((value) => Array.isArray(value) ? value.length > 0 : value !== undefined)
389
- ? declaration
390
- : undefined;
391
- }
392
- function parseCommaSeparated(value) {
393
- if (value === undefined) {
394
- return undefined;
395
- }
396
- return value.split(",").map((entry) => entry.trim()).filter(Boolean);
397
- }
398
470
  function getDefaultMessageType(fromRole, toRole) {
399
471
  if (fromRole === PM_ROLE && toRole !== PM_ROLE) {
400
472
  return "task";
@@ -454,14 +526,10 @@ async function clearRouteFileIfStillMatchesMessage(fs, input, message) {
454
526
  const parsed = parseRouteFileContent(routeContent, fromRole, message.toRole);
455
527
  if (parsed.body.trim() === message.body.trim() &&
456
528
  parsed.type === message.type &&
457
- arraysEqual(parsed.artifactRefs, message.artifactRefs) &&
458
- workflowDeclarationsEqual(parsed.workflow, message.workflow)) {
529
+ arraysEqual(parsed.artifactRefs, message.artifactRefs)) {
459
530
  await fs.writeText(absolutePath, "");
460
531
  }
461
532
  }
462
- function workflowDeclarationsEqual(left, right) {
463
- return JSON.stringify(left ?? null) === JSON.stringify(right ?? null);
464
- }
465
533
  function isCoreRouteRole(role) {
466
534
  return CORE_VCM_ROLE_NAMES.some((candidate) => candidate === role);
467
535
  }
@@ -51,10 +51,16 @@ function degradedArtifactSummary(handoffDir) {
51
51
  roleCommandPaths: Object.fromEntries(DISPATCHABLE_ROLES.map((role) => [role, `${roleCommandsDir}/${role}.md`])),
52
52
  messageRoutePaths: {},
53
53
  architectureBriefPath: `${handoffDir}/architecture-brief.md`,
54
+ architectureEvidencePath: `${handoffDir}/architecture-evidence.md`,
55
+ planningProgressPath: `${handoffDir}/planning-progress.md`,
54
56
  architecturePlanPath: `${handoffDir}/architecture-plan.md`,
55
57
  knownIssuesPath: `${handoffDir}/known-issues.md`,
58
+ coderCompletionPath: `${handoffDir}/coder-completion.md`,
59
+ architectDebugPath: `${handoffDir}/architect-debug.md`,
60
+ architectureDiagnosisPath: `${handoffDir}/architecture-diagnosis.md`,
56
61
  testReportPath: `${handoffDir}/test-report.md`,
57
62
  docsSyncReportPath: `${handoffDir}/docs-sync-report.md`,
63
+ workflowProgressPath: `${handoffDir}/workflow-progress.md`,
58
64
  finalAcceptancePath: `${handoffDir}/final-acceptance.md`
59
65
  },
60
66
  checks: []