vibe-coding-master 0.9.4 → 0.9.5

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.
@@ -320,7 +320,8 @@ export function createDefaultServerDeps(options = {}) {
320
320
  fs,
321
321
  projectService,
322
322
  taskService,
323
- sessionService
323
+ sessionService,
324
+ workflowControlService
324
325
  });
325
326
  const messageService = createMessageService({
326
327
  fs,
@@ -7,6 +7,7 @@ import { resolveRepoPath } from "../adapters/filesystem.js";
7
7
  import { submitTerminalInput } from "../runtime/terminal-submit.js";
8
8
  import { getTaskRuntimeRepoRoot } from "./task-service.js";
9
9
  import { parseGateReviewReportArtifact } from "./managed-artifact-validation.js";
10
+ import { collectFlowRouteMessages, renderRouteMessages } from "./route-message-evidence.js";
10
11
  const REVIEWER_AGENT_PATH = ".claude/agents/reviewer.md";
11
12
  const GATE_REVIEW_DIR = ".ai/vcm/gate-reviews";
12
13
  const REQUESTS_DIR = ".ai/vcm/gate-reviews/requests";
@@ -39,10 +40,7 @@ const CODE_DIFF_SOURCE_ARTIFACTS = {
39
40
  ".ai/vcm/handoffs/architecture-plan.md",
40
41
  ".ai/vcm/handoffs/coder-completion.md"
41
42
  ],
42
- "architect-debug": [
43
- ".ai/vcm/handoffs/role-commands/architect.md",
44
- ".ai/vcm/handoffs/architect-debug.md"
45
- ],
43
+ "architect-debug": [".ai/vcm/handoffs/architect-debug.md"],
46
44
  "architect-diagnosis": [
47
45
  ".ai/vcm/handoffs/architecture-diagnosis.md"
48
46
  ]
@@ -438,6 +436,9 @@ export function createGateReviewService(deps) {
438
436
  const promptPath = path.posix.join(REQUESTS_DIR, `${requestId}.prompt.md`);
439
437
  const requestReportPath = reportPathForRequest(requestId);
440
438
  const inputSnapshots = await captureGateInputSnapshots(deps.fs, context.taskRepoRoot, requestId, getSourceArtifacts(gate, codeDiffSources));
439
+ if (gate === "code-diff" && codeDiffSources) {
440
+ inputSnapshots.push(await captureRouteMessageSnapshot(deps, context, requestId, codeDiffSources));
441
+ }
441
442
  const nextRecord = {
442
443
  ...record,
443
444
  status: "running",
@@ -1487,7 +1488,12 @@ function splitLines(value) {
1487
1488
  function buildGatePrompt(context, gate, requestId, codeDiffInput, codeDiffSources, inputSnapshots = []) {
1488
1489
  const reportPath = reportPathForRequest(requestId);
1489
1490
  const absoluteReportPath = resolveRepoPath(context.taskRepoRoot, reportPath);
1490
- const evidence = getSourceArtifacts(gate, codeDiffSources)
1491
+ const evidence = [
1492
+ ...getSourceArtifacts(gate, codeDiffSources),
1493
+ ...(gate === "code-diff"
1494
+ ? [`${path.posix.join(context.stateRoot, "messages", `${context.taskSlug}.jsonl`)} (PM route messages for the reviewed flow)`]
1495
+ : [])
1496
+ ]
1491
1497
  .map((relativePath) => `- ${relativePath}`)
1492
1498
  .join("\n");
1493
1499
  const capturedEvidence = inputSnapshots.length > 0
@@ -1561,6 +1567,19 @@ async function captureGateInputSnapshots(fs, taskRepoRoot, requestId, sourcePath
1561
1567
  }
1562
1568
  return snapshots;
1563
1569
  }
1570
+ // The reviewer judges authorization and scope against the flow's PM route
1571
+ // messages, captured like any other immutable Gate input.
1572
+ async function captureRouteMessageSnapshot(deps, context, requestId, codeDiffSources) {
1573
+ const sourcePath = path.posix.join(context.stateRoot, "messages", `${context.taskSlug}.jsonl`);
1574
+ const roles = new Set(codeDiffSources.map((source) => source === "coder" ? "coder" : "architect"));
1575
+ const flow = await collectFlowRouteMessages(deps.fs, deps.workflowControlService, context, roles);
1576
+ if (flow.messages.length === 0) {
1577
+ return { sourcePath, status: "missing" };
1578
+ }
1579
+ const snapshotPath = inputSnapshotPathForRequest(requestId, ".ai/vcm/messages/route-messages.md");
1580
+ await deps.fs.writeText(resolveRepoPath(context.taskRepoRoot, snapshotPath), renderRouteMessages(context.taskSlug, [`Code sources: ${codeDiffSources.join(" -> ")}`], flow));
1581
+ return { sourcePath, snapshotPath, status: "captured" };
1582
+ }
1564
1583
  function isTaskEvidencePath(sourcePath) {
1565
1584
  return sourcePath.startsWith(".ai/vcm/handoffs/")
1566
1585
  || sourcePath.startsWith(".ai/vcm/gate-reviews/");
@@ -527,6 +527,11 @@ function arraysEqual(left, right) {
527
527
  }
528
528
  return left.every((value, index) => value === right[index]);
529
529
  }
530
+ // Reads the task's message log outside the service's task lock, for consumers
531
+ // that only need a point-in-time view, such as Gate Review evidence capture.
532
+ export function readTaskMessages(fs, input) {
533
+ return readLatestMessages(fs, getMessagesPath(getStateRepoRoot(input), input.stateRoot, input.taskSlug));
534
+ }
530
535
  async function readLatestMessages(fs, messagesPath) {
531
536
  if (!(await fs.pathExists(messagesPath))) {
532
537
  return [];
@@ -2,6 +2,7 @@ import path from "node:path";
2
2
  import { VCM_ROLE_NAMES } from "../../shared/constants.js";
3
3
  import { resolveRepoPath } from "../adapters/filesystem.js";
4
4
  import { toVcmError, VcmError } from "../errors.js";
5
+ import { collectFlowRouteMessages, renderRouteMessages } from "./route-message-evidence.js";
5
6
  import { getTaskRuntimeRepoRoot } from "./task-service.js";
6
7
  const STATE_DIR = "restart-with-context";
7
8
  export function createRoleContextRestartService(deps) {
@@ -126,7 +127,11 @@ export function createRoleContextRestartService(deps) {
126
127
  ]);
127
128
  const handoffDir = task.handoffDir;
128
129
  const stateRoot = config.stateRoot;
129
- const files = roleContextFiles(role, handoffDir, stateRoot);
130
+ const routeMessagesPath = await writeRouteMessages(repoRoot, task, stateRoot, role);
131
+ const files = [
132
+ ...(routeMessagesPath ? [routeMessagesPath] : []),
133
+ ...roleContextFiles(role, handoffDir, stateRoot)
134
+ ];
130
135
  return {
131
136
  system: [
132
137
  `This fresh ${role} session continues the current VCM task after Restart With Context.`,
@@ -140,6 +145,28 @@ export function createRoleContextRestartService(deps) {
140
145
  user: `[VCM RESTART WITH CONTEXT]\nRestore the current ${role} work from the listed task artifacts and continue the accepted assignment.`
141
146
  };
142
147
  }
148
+ // A delivery role's assignment lives only in the PM route messages, so render
149
+ // the current flow run's messages to that role for the fresh session.
150
+ async function writeRouteMessages(repoRoot, task, stateRoot, role) {
151
+ if (role !== "architect" && role !== "coder" && role !== "tester") {
152
+ return undefined;
153
+ }
154
+ const taskRepoRoot = getTaskRuntimeRepoRoot(task);
155
+ const context = {
156
+ repoRoot,
157
+ taskRepoRoot,
158
+ stateRoot,
159
+ handoffDir: task.handoffDir,
160
+ taskSlug: task.taskSlug
161
+ };
162
+ const flow = await collectFlowRouteMessages(deps.fs, deps.workflowControlService, context, new Set([role]));
163
+ if (flow.messages.length === 0) {
164
+ return undefined;
165
+ }
166
+ const relativePath = path.posix.join(stateRoot, STATE_DIR, `${role}-route-messages.md`);
167
+ await deps.fs.writeText(resolveRepoPath(taskRepoRoot, relativePath), renderRouteMessages(task.taskSlug, [`Role: ${role}`], flow));
168
+ return relativePath;
169
+ }
143
170
  async function persist(repoRoot, state) {
144
171
  await deps.fs.writeJsonAtomic(await statePath(repoRoot, state.taskSlug, state.role), state);
145
172
  pending.set(restartKey(repoRoot, state.taskSlug, state.role), state);
@@ -205,7 +232,6 @@ function roleContextFiles(role, handoffDir, stateRoot) {
205
232
  ];
206
233
  case "architect":
207
234
  return [
208
- `${handoffDir}/role-commands/architect.md`,
209
235
  `${handoffDir}/architecture-brief.md`,
210
236
  `${handoffDir}/architecture-evidence.md`,
211
237
  `${handoffDir}/planning-progress.md`,
@@ -216,14 +242,12 @@ function roleContextFiles(role, handoffDir, stateRoot) {
216
242
  ];
217
243
  case "coder":
218
244
  return [
219
- `${handoffDir}/role-commands/coder.md`,
220
245
  `${handoffDir}/architecture-plan.md`,
221
246
  `${handoffDir}/coder-completion.md`,
222
247
  ".ai/vcm/coder-workers/"
223
248
  ];
224
249
  case "tester":
225
250
  return [
226
- `${handoffDir}/role-commands/tester.md`,
227
251
  `${handoffDir}/architecture-plan.md`,
228
252
  `${handoffDir}/coder-completion.md`,
229
253
  `${handoffDir}/test-report.md`
@@ -0,0 +1,83 @@
1
+ import { readTaskMessages } from "./message-service.js";
2
+ // PM route messages are the operative instructions for a role: the dispatch
3
+ // that started the current flow run and every later relay of a user decision.
4
+ // They exist only in the task message log, so callers render this slice of it.
5
+ export async function collectFlowRouteMessages(fs, workflowControlService, context, roles) {
6
+ const messages = await readTaskMessages(fs, {
7
+ repoRoot: context.repoRoot,
8
+ stateRepoRoot: context.taskRepoRoot,
9
+ stateRoot: context.stateRoot,
10
+ taskSlug: context.taskSlug
11
+ });
12
+ const flowRunStart = await findFlowRunStart(workflowControlService, context);
13
+ const acceptedPmMessages = messages
14
+ .filter((message) => message.fromRole === "project-manager" && message.acceptedAt)
15
+ .sort((left, right) => left.acceptedAt.localeCompare(right.acceptedAt));
16
+ // A PM message is accepted immediately before Workflow Progress confirms its
17
+ // dispatch, so the flow's starting message is the last one accepted at or
18
+ // before the first confirmation of the flow run.
19
+ const windowStart = flowRunStart
20
+ ? acceptedPmMessages.filter((message) => message.acceptedAt <= flowRunStart).at(-1)?.acceptedAt ?? flowRunStart
21
+ : undefined;
22
+ return {
23
+ windowStart,
24
+ messages: messages
25
+ .filter((message) => message.fromRole === "user"
26
+ ? !windowStart || message.createdAt >= windowStart
27
+ : message.fromRole === "project-manager"
28
+ && roles.has(message.toRole)
29
+ && Boolean(message.acceptedAt)
30
+ && (!windowStart || message.acceptedAt >= windowStart))
31
+ .sort((left, right) => messageTime(left).localeCompare(messageTime(right)))
32
+ };
33
+ }
34
+ export function renderRouteMessages(taskSlug, header, flow) {
35
+ return [
36
+ "# PM Route Messages",
37
+ "",
38
+ `Task: ${taskSlug}`,
39
+ ...header,
40
+ flow.windowStart
41
+ ? `Scope: messages since the current flow run started at ${flow.windowStart}`
42
+ : "Scope: the whole task message log; the current flow run start was unavailable",
43
+ "",
44
+ "PM route messages to the listed roles and user messages to PM, oldest first.",
45
+ "A later message overrides an earlier one where they conflict.",
46
+ ...flow.messages.flatMap((message, index) => [
47
+ "",
48
+ `## ${index + 1}. ${message.fromRole} -> ${message.toRole} (${message.type})`,
49
+ "",
50
+ `Message: ${message.id}`,
51
+ `Time: ${messageTime(message)}`,
52
+ ...(message.artifactRefs.length > 0 ? [`Artifacts: ${message.artifactRefs.join(", ")}`] : []),
53
+ "",
54
+ message.body.trimEnd()
55
+ ]),
56
+ ""
57
+ ].join("\n");
58
+ }
59
+ async function findFlowRunStart(workflowControlService, context) {
60
+ if (!workflowControlService) {
61
+ return undefined;
62
+ }
63
+ try {
64
+ const workflowContext = {
65
+ taskRepoRoot: context.taskRepoRoot,
66
+ stateRoot: context.stateRoot,
67
+ handoffDir: context.handoffDir,
68
+ taskSlug: context.taskSlug
69
+ };
70
+ const [state, progress] = await Promise.all([
71
+ workflowControlService.getState(workflowContext),
72
+ workflowControlService.getProgress(workflowContext)
73
+ ]);
74
+ const startedAtSequence = state.flowRun?.startedAtSequence;
75
+ return progress.history.find((entry) => entry.sequence === startedAtSequence)?.confirmedAt;
76
+ }
77
+ catch {
78
+ return undefined;
79
+ }
80
+ }
81
+ function messageTime(message) {
82
+ return message.acceptedAt ?? message.createdAt;
83
+ }
@@ -334,6 +334,13 @@ Treat every production and test hunk introduced by \`architect-debug\` or
334
334
  review to confirming that the reported symptom disappeared or that the recorded
335
335
  root cause was addressed; apply the complete code-diff checks to every repair.
336
336
 
337
+ When the VCM prompt captures PM route messages, they are the operative
338
+ instructions for the reviewed flow: the dispatch that started it and every
339
+ later PM relay of a user decision, oldest first. A later message, such as a user grant, a narrowed scope, or a
340
+ changed acceptance target, overrides the plan or an earlier message where they
341
+ conflict. Judge authorization and scope against them, and record under Code
342
+ Diff Analysis any change they neither request nor permit.
343
+
337
344
  Before deciding:
338
345
 
339
346
  - Inspect every changed file and diff hunk. Read the complete implementation of
@@ -351,7 +358,7 @@ Before deciding:
351
358
  Do not satisfy review by repeating a generic checklist.
352
359
 
353
360
  For \`coder\`, compare the commits with the approved architecture plan,
354
- scaffold, and coder completion evidence. Verify that the complete planned
361
+ scaffold, coder completion evidence, and the captured PM route messages. Verify that the complete planned
355
362
  behavior is implemented without changing architect-owned boundaries or
356
363
  contracts.
357
364
 
@@ -362,8 +369,8 @@ the Tester role, the repair or coverage evidence, and
362
369
  because it is not a Coder scaffold item. Verify that Tester changes remain
363
370
  test-only, preserve real behavior paths, and are committed and validated.
364
371
 
365
- For \`architect-debug\`, compare the commits with the current Architect route
366
- command and \`.ai/vcm/handoffs/architect-debug.md\`. Verify that the confirmed
372
+ For \`architect-debug\`, compare the commits with the captured PM route messages
373
+ to Architect and \`.ai/vcm/handoffs/architect-debug.md\`. Verify that the confirmed
367
374
  root cause is supported by the code, the implementation fixes that cause rather
368
375
  than only its surface symptom, temporary diagnostics are removed, and affected
369
376
  callers, contracts, and tests are updated. Verify that the Debug evidence records
@@ -372,7 +379,8 @@ when an applicable check was not run, did not pass, or does not exercise that
372
379
  failure path.
373
380
 
374
381
  For \`architect-diagnosis\`, compare the commits with
375
- \`.ai/vcm/handoffs/architecture-diagnosis.md\`, and verify that the commits implement the diagnosed
382
+ \`.ai/vcm/handoffs/architecture-diagnosis.md\` and the captured PM route messages
383
+ to Architect, and verify that the commits implement the diagnosed
376
384
  ownership, data flow, lifecycle, boundaries, invariants, and failure model.
377
385
  Request changes when the architecture problem remains, the required direction
378
386
  is contradicted, or the implementation is only a local workaround for the surface failure.
@@ -599,10 +607,7 @@ const CODE_DIFF_SOURCE_ARTIFACTS = {
599
607
  ".ai/vcm/handoffs/architecture-plan.md",
600
608
  ".ai/vcm/handoffs/coder-completion.md"
601
609
  ],
602
- "architect-debug": [
603
- ".ai/vcm/handoffs/role-commands/architect.md",
604
- ".ai/vcm/handoffs/architect-debug.md"
605
- ],
610
+ "architect-debug": [".ai/vcm/handoffs/architect-debug.md"],
606
611
  "architect-diagnosis": [".ai/vcm/handoffs/architecture-diagnosis.md"]
607
612
  };
608
613
  const CORE_INPUT_ARTIFACTS = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibe-coding-master",
3
- "version": "0.9.4",
3
+ "version": "0.9.5",
4
4
  "description": "Local GUI session cockpit for Claude Code role sessions.",
5
5
  "type": "module",
6
6
  "files": [