vibe-coding-master 0.7.39 → 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.
- package/README.md +11 -6
- package/dist/backend/adapters/claude-adapter.js +1 -6
- package/dist/backend/api/artifact-routes.js +72 -1
- package/dist/backend/api/runtime-state-routes.js +17 -5
- package/dist/backend/api/task-routes.js +6 -0
- package/dist/backend/api/workflow-control-routes.js +56 -0
- package/dist/backend/cli/install-vcm-harness.js +21 -31
- package/dist/backend/server.js +21 -5
- package/dist/backend/services/artifact-service.js +309 -5
- package/dist/backend/services/auto-memory-service.js +2 -2
- package/dist/backend/services/gate-review-service.js +81 -33
- package/dist/backend/services/harness-feedback-service.js +11 -4
- package/dist/backend/services/harness-service.js +17 -31
- package/dist/backend/services/message-service.js +105 -37
- package/dist/backend/services/status-service.js +6 -0
- package/dist/backend/services/workflow-control-service.js +854 -0
- package/dist/backend/templates/handoff.js +158 -3
- package/dist/backend/templates/harness/architect-agent.js +2 -2
- package/dist/backend/templates/harness/check-scaffold-ledger.js +105 -39
- package/dist/backend/templates/harness/claude-root.js +9 -1
- package/dist/backend/templates/harness/coder-agent.js +1 -2
- package/dist/backend/templates/harness/coder-worker-agent.js +1 -1
- package/dist/backend/templates/harness/gate-review.js +3 -4
- package/dist/backend/templates/harness/harness-engineer-agent.js +18 -1
- package/dist/backend/templates/harness/project-manager-agent.js +3 -2
- package/dist/backend/templates/harness/tester-agent.js +1 -0
- package/dist/backend/templates/harness/vcm-architecture-interview-skill.js +5 -0
- package/dist/backend/templates/harness/vcm-code-navigation-skill.js +1 -2
- package/dist/backend/templates/harness/vcm-final-acceptance-skill.js +2 -2
- package/dist/backend/templates/harness/vcm-long-running-validation-skill.js +4 -2
- package/dist/backend/templates/harness/vcm-propose-memory-skill.js +4 -2
- package/dist/backend/templates/harness/vcm-report-harness-issue-skill.js +2 -1
- package/dist/backend/templates/harness/vcm-route-message-skill.js +4 -7
- package/dist/backend/templates/harness/vcm-task-state-skill.js +2 -13
- package/dist/backend/templates/harness/vcm-workflow-review-skill.js +62 -0
- package/dist/backend/templates/message-envelope.js +4 -4
- package/dist/shared/types/workflow.js +7 -1
- package/dist/shared/validation/artifact-check.js +99 -95
- package/dist/shared/validation/artifact-contract.js +9 -0
- package/dist/shared/validation/artifact-registry.js +211 -0
- package/dist-frontend/assets/{index-DkM0mnQD.js → index-Bocc2DWF.js} +34 -34
- package/dist-frontend/assets/{index-CXSOe-NN.css → index-C_XHGNBD.css} +1 -1
- package/dist-frontend/index.html +2 -2
- package/package.json +1 -1
- package/scripts/harness-tools/run-long-check +38 -8
- package/scripts/harness-tools/vcm-artifact +148 -0
- package/scripts/harness-tools/vcm-bash-guard +42 -47
- 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: "
|
|
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,10 +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"],
|
|
210
|
+
requiredTools: ["Grep", "LSP"],
|
|
203
211
|
requiredSkills: ["vcm-code-navigation"],
|
|
204
|
-
frontmatter: renderAgentFrontmatter("reviewer", "VCM independent gate review role for architecture plans, validation adequacy, and code diffs.", { tools: "Read, Glob, Bash, Write, LSP", skills: ["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"] }),
|
|
205
213
|
renderRules: renderReviewerAgentRules
|
|
206
214
|
},
|
|
207
215
|
{
|
|
@@ -274,11 +282,10 @@ const HARNESS_FILES = [
|
|
|
274
282
|
path: ".claude/agents/architect.md",
|
|
275
283
|
title: "Architect Agent",
|
|
276
284
|
memoryBlock: true,
|
|
277
|
-
requiredTools: ["Agent", "LSP"],
|
|
278
|
-
forbiddenTools: ["Grep"],
|
|
285
|
+
requiredTools: ["Grep", "Agent", "LSP"],
|
|
279
286
|
requiredSkills: ["vcm-code-navigation"],
|
|
280
287
|
blankLineBeforeEnd: true,
|
|
281
|
-
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", skills: ["vcm-code-navigation"] }),
|
|
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"] }),
|
|
282
289
|
renderRules: renderArchitectHarnessRules
|
|
283
290
|
},
|
|
284
291
|
{
|
|
@@ -286,10 +293,9 @@ const HARNESS_FILES = [
|
|
|
286
293
|
path: ".claude/agents/coder.md",
|
|
287
294
|
title: "Coder Agent",
|
|
288
295
|
memoryBlock: true,
|
|
289
|
-
requiredTools: ["Agent", "LSP"],
|
|
290
|
-
forbiddenTools: ["Grep"],
|
|
296
|
+
requiredTools: ["Grep", "Agent", "LSP"],
|
|
291
297
|
requiredSkills: ["vcm-code-navigation"],
|
|
292
|
-
frontmatter: renderAgentFrontmatter("coder", "VCM implementation role for scoped code changes and focused tests.", { tools: "Read, Glob, Bash, Edit, Write, Agent, LSP", skills: ["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"] }),
|
|
293
299
|
renderRules: renderCoderHarnessRules
|
|
294
300
|
},
|
|
295
301
|
{
|
|
@@ -1399,28 +1405,8 @@ function ensureAgentTool(content, requiredTool) {
|
|
|
1399
1405
|
function ensureAgentTools(content, requiredTools) {
|
|
1400
1406
|
return (requiredTools ?? []).reduce(ensureAgentTool, content);
|
|
1401
1407
|
}
|
|
1402
|
-
function removeAgentTool(content, forbiddenTool) {
|
|
1403
|
-
const frontmatterMatch = content.match(/^---\r?\n[\s\S]*?\r?\n---/);
|
|
1404
|
-
if (!frontmatterMatch) {
|
|
1405
|
-
return content;
|
|
1406
|
-
}
|
|
1407
|
-
const toolsMatch = frontmatterMatch[0].match(/^tools:\s*(.*)$/m);
|
|
1408
|
-
if (!toolsMatch) {
|
|
1409
|
-
return content;
|
|
1410
|
-
}
|
|
1411
|
-
const tools = toolsMatch[1].split(",").map((tool) => tool.trim()).filter(Boolean);
|
|
1412
|
-
const nextTools = tools.filter((tool) => tool !== forbiddenTool);
|
|
1413
|
-
if (nextTools.length === tools.length) {
|
|
1414
|
-
return content;
|
|
1415
|
-
}
|
|
1416
|
-
return content.replace(frontmatterMatch[0], frontmatterMatch[0].replace(toolsMatch[0], `tools: ${nextTools.join(", ")}`));
|
|
1417
|
-
}
|
|
1418
|
-
function normalizeAgentTools(content, requiredTools, forbiddenTools) {
|
|
1419
|
-
const required = ensureAgentTools(content, requiredTools);
|
|
1420
|
-
return (forbiddenTools ?? []).reduce(removeAgentTool, required);
|
|
1421
|
-
}
|
|
1422
1408
|
function normalizeAgentFrontmatter(content, definition) {
|
|
1423
|
-
const toolsUpdated =
|
|
1409
|
+
const toolsUpdated = ensureAgentTools(content, definition.requiredTools);
|
|
1424
1410
|
return (definition.requiredSkills ?? []).reduce(ensureAgentSkill, toolsUpdated);
|
|
1425
1411
|
}
|
|
1426
1412
|
function ensureAgentSkill(content, requiredSkill) {
|
|
@@ -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:
|
|
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
|
-
|
|
119
|
-
|
|
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
|
-
},
|
|
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
|
|
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: []
|