vibe-coding-master 0.7.42 → 0.7.44

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 (50) hide show
  1. package/README.md +49 -17
  2. package/dist/backend/api/artifact-routes.js +3 -0
  3. package/dist/backend/api/harness-routes.js +16 -0
  4. package/dist/backend/api/task-routes.js +32 -2
  5. package/dist/backend/api/workflow-control-routes.js +7 -26
  6. package/dist/backend/cli/install-vcm-harness.js +61 -6
  7. package/dist/backend/role-tool-policy.js +1 -1
  8. package/dist/backend/server.js +14 -5
  9. package/dist/backend/services/artifact-service.js +10 -32
  10. package/dist/backend/services/auto-memory-service.js +642 -14
  11. package/dist/backend/services/claude-hook-service.js +183 -6
  12. package/dist/backend/services/gate-review-service.js +173 -74
  13. package/dist/backend/services/harness-feedback-service.js +180 -80
  14. package/dist/backend/services/harness-service.js +65 -9
  15. package/dist/backend/services/memory-review-paths.js +13 -0
  16. package/dist/backend/services/role-stall-detector-service.js +322 -0
  17. package/dist/backend/services/round-service.js +25 -0
  18. package/dist/backend/services/runtime-coordinator-service.js +12 -1
  19. package/dist/backend/services/session-service.js +70 -3
  20. package/dist/backend/services/status-service.js +1 -0
  21. package/dist/backend/services/translation-worker-service.js +19 -4
  22. package/dist/backend/services/workflow-control-service.js +524 -204
  23. package/dist/backend/templates/handoff.js +46 -5
  24. package/dist/backend/templates/harness/architect-agent.js +12 -6
  25. package/dist/backend/templates/harness/architect-scaffold-worker-agent.js +1 -1
  26. package/dist/backend/templates/harness/check-scaffold-ledger.js +234 -10
  27. package/dist/backend/templates/harness/claude-root.js +3 -2
  28. package/dist/backend/templates/harness/coder-agent.js +12 -5
  29. package/dist/backend/templates/harness/gate-review.js +150 -57
  30. package/dist/backend/templates/harness/harness-engineer-agent.js +38 -13
  31. package/dist/backend/templates/harness/project-manager-agent.js +18 -12
  32. package/dist/backend/templates/harness/resolve-durable-doc-assignment.js +60 -0
  33. package/dist/backend/templates/harness/tester-agent.js +13 -0
  34. package/dist/backend/templates/harness/vcm-ask-user-skill.js +82 -0
  35. package/dist/backend/templates/harness/vcm-code-navigation-skill.js +7 -6
  36. package/dist/backend/templates/harness/vcm-task-state-skill.js +2 -2
  37. package/dist/backend/templates/harness/vcm-workflow-review-skill.js +7 -9
  38. package/dist/shared/types/role-stall.js +1 -0
  39. package/dist/shared/types/workflow.js +15 -0
  40. package/dist/shared/validation/artifact-check.js +3 -3
  41. package/dist/shared/validation/artifact-contract.js +1 -1
  42. package/dist/shared/validation/artifact-registry.js +17 -1
  43. package/dist-frontend/assets/{index-C_XHGNBD.css → index-B0d4Z6ny.css} +1 -1
  44. package/dist-frontend/assets/index-BvCmrFlN.js +97 -0
  45. package/dist-frontend/index.html +2 -2
  46. package/package.json +1 -1
  47. package/scripts/claude-plugins/vcm-lsp-bridge/.claude-plugin/plugin.json +21 -6
  48. package/scripts/harness-tools/vcm-artifact +1 -2
  49. package/scripts/harness-tools/vcm-bash-guard +204 -14
  50. package/dist-frontend/assets/index-Bocc2DWF.js +0 -97
@@ -10,6 +10,7 @@ const REVIEWER_AGENT_PATH = ".claude/agents/reviewer.md";
10
10
  const GATE_REVIEW_DIR = ".ai/vcm/gate-reviews";
11
11
  const REQUESTS_DIR = ".ai/vcm/gate-reviews/requests";
12
12
  const GATE_REVIEW_VERSION = 1;
13
+ const HARNESS_COMMIT_PREFIX = "[VCM Harness] ";
13
14
  const REVIEWER_ROLE = "reviewer";
14
15
  const DEFAULT_REPORT_POLL_INTERVAL_MS = 1000;
15
16
  const activeRuns = new Map();
@@ -107,6 +108,7 @@ export function createGateReviewService(deps) {
107
108
  taskSlug,
108
109
  taskRepoRoot,
109
110
  stateRoot: projectConfig.stateRoot,
111
+ handoffDir: task.handoffDir,
110
112
  config: loadRuntimeConfig(reviewSettings)
111
113
  };
112
114
  }
@@ -345,42 +347,21 @@ export function createGateReviewService(deps) {
345
347
  }
346
348
  }
347
349
  if (gate === "code-diff") {
348
- const prerequisiteError = await readCodeDiffPrerequisiteError(deps, context, index, codeDiffSource);
349
- if (prerequisiteError) {
350
- index = applyGateState(index, gate, {
351
- status: "failed",
352
- decision: undefined,
353
- error: prerequisiteError,
354
- exceptionReason: undefined,
355
- requestId: undefined,
356
- requestPath: undefined,
357
- inputHash: undefined,
358
- baseCommit: undefined,
359
- headCommit: undefined,
360
- commits: undefined,
361
- changedFiles: undefined,
362
- diffStat: undefined,
363
- codeDiffSource,
364
- codeDiffSources: codeDiffSource ? [codeDiffSource] : undefined,
365
- requestedAt: undefined,
366
- startedAt: undefined,
367
- completedAt: now(),
368
- callbackStatus: "not_sent",
369
- callbackError: undefined
370
- }, now(), true);
371
- await saveIndex(deps.fs, context.taskRepoRoot, index);
350
+ const evidenceError = await readCodeDiffEvidenceError(deps.fs, context.taskRepoRoot, codeDiffSource);
351
+ if (evidenceError) {
352
+ index = await recordCodeDiffStartFailure(index, context, gate, codeDiffSource, evidenceError);
372
353
  return {
373
354
  status: "failed_to_start",
374
355
  gate,
375
356
  record: index.gates[gate],
376
- message: prerequisiteError
357
+ message: evidenceError
377
358
  };
378
359
  }
379
360
  }
380
361
  const codeDiffInput = gate === "code-diff"
381
362
  ? await resolveCodeDiffInput(deps, context, record)
382
363
  : undefined;
383
- if (gate === "code-diff" && !codeDiffInput) {
364
+ if (gate === "code-diff" && (!codeDiffInput || codeDiffInput.commits.length === 0)) {
384
365
  index = applyGateState(index, gate, {
385
366
  status: "not_required",
386
367
  decision: undefined,
@@ -389,16 +370,16 @@ export function createGateReviewService(deps) {
389
370
  requestId: undefined,
390
371
  requestPath: undefined,
391
372
  inputHash: undefined,
392
- baseCommit: undefined,
393
- headCommit: undefined,
394
- commits: undefined,
395
- changedFiles: undefined,
396
- diffStat: undefined,
373
+ baseCommit: codeDiffInput?.baseCommit,
374
+ headCommit: codeDiffInput?.headCommit,
375
+ commits: codeDiffInput?.commits,
376
+ changedFiles: codeDiffInput?.changedFiles,
377
+ diffStat: codeDiffInput?.diffStat,
397
378
  codeDiffSource,
398
379
  codeDiffSources: codeDiffSource ? [codeDiffSource] : undefined,
399
380
  requestedAt: undefined,
400
381
  startedAt: undefined,
401
- completedAt: undefined,
382
+ completedAt: codeDiffInput ? now() : undefined,
402
383
  callbackStatus: "not_sent",
403
384
  callbackError: undefined
404
385
  }, now(), true);
@@ -407,11 +388,51 @@ export function createGateReviewService(deps) {
407
388
  status: "not_required",
408
389
  gate,
409
390
  record: index.gates[gate],
410
- message: "No new commits to review."
391
+ message: codeDiffInput ? "No non-Harness commits to review." : "No new commits to review."
411
392
  };
412
393
  }
413
- const codeDiffSources = gate === "code-diff" && codeDiffInput && codeDiffSource
414
- ? resolveCodeDiffSources(record, codeDiffInput, codeDiffSource)
394
+ if (gate === "code-diff") {
395
+ const prerequisiteError = await readCodeDiffValidationGateError(deps, context, index);
396
+ if (prerequisiteError) {
397
+ index = await recordCodeDiffStartFailure(index, context, gate, codeDiffSource, prerequisiteError, codeDiffInput);
398
+ return {
399
+ status: "failed_to_start",
400
+ gate,
401
+ record: index.gates[gate],
402
+ message: prerequisiteError
403
+ };
404
+ }
405
+ }
406
+ async function recordCodeDiffStartFailure(currentIndex, currentContext, currentGate, currentSource, message, currentCodeDiffInput) {
407
+ const failedIndex = applyGateState(currentIndex, currentGate, {
408
+ status: "failed",
409
+ decision: undefined,
410
+ error: message,
411
+ exceptionReason: undefined,
412
+ requestId: undefined,
413
+ requestPath: undefined,
414
+ inputHash: undefined,
415
+ baseCommit: currentCodeDiffInput?.baseCommit,
416
+ headCommit: currentCodeDiffInput?.headCommit,
417
+ commits: currentCodeDiffInput?.commits,
418
+ changedFiles: currentCodeDiffInput?.changedFiles,
419
+ diffStat: currentCodeDiffInput?.diffStat,
420
+ codeDiffSource: currentSource,
421
+ codeDiffSources: currentSource ? [currentSource] : undefined,
422
+ requestedAt: undefined,
423
+ startedAt: undefined,
424
+ completedAt: now(),
425
+ callbackStatus: "not_sent",
426
+ callbackError: undefined
427
+ }, now(), true);
428
+ await saveIndex(deps.fs, currentContext.taskRepoRoot, failedIndex);
429
+ return failedIndex;
430
+ }
431
+ const currentCodeDiffSources = gate === "code-diff" && codeDiffSource
432
+ ? await resolveWorkflowCodeDiffSources(deps, context, codeDiffSource)
433
+ : undefined;
434
+ const codeDiffSources = gate === "code-diff" && codeDiffInput && currentCodeDiffSources
435
+ ? resolveCodeDiffSources(record, codeDiffInput, currentCodeDiffSources)
415
436
  : undefined;
416
437
  const inputHash = await computeInputHash(deps, context.taskRepoRoot, gate, codeDiffInput, codeDiffSources);
417
438
  if (!options.force
@@ -1059,44 +1080,78 @@ async function resolveCodeDiffInput(deps, context, record) {
1059
1080
  return undefined;
1060
1081
  }
1061
1082
  const range = `${baseCommit}..${headCommit}`;
1062
- const commits = splitLines(await commandStdout(deps.runner, context.taskRepoRoot, [
1083
+ const allCommits = splitLines(await commandStdout(deps.runner, context.taskRepoRoot, [
1063
1084
  "log",
1064
1085
  "--oneline",
1065
1086
  "--reverse",
1066
1087
  range
1067
1088
  ]));
1068
- if (commits.length === 0) {
1089
+ if (allCommits.length === 0) {
1069
1090
  return undefined;
1070
1091
  }
1071
- const changedFiles = splitLines(await commandStdout(deps.runner, context.taskRepoRoot, [
1072
- "diff",
1073
- "--name-only",
1074
- "--find-renames",
1075
- range
1076
- ]));
1077
- const diffStat = await commandStdout(deps.runner, context.taskRepoRoot, [
1078
- "diff",
1079
- "--stat",
1080
- "--find-renames",
1081
- range
1082
- ]);
1083
- const diff = await commandStdout(deps.runner, context.taskRepoRoot, [
1084
- "diff",
1085
- "--binary",
1086
- "--find-renames",
1087
- range
1088
- ]);
1092
+ const commits = allCommits.filter((line) => !isHarnessCommitLine(line));
1093
+ const commitShas = commits.map(commitShaFromOneline);
1094
+ const changedFiles = new Set();
1095
+ const diffStats = [];
1096
+ const diffs = [];
1097
+ for (const commitSha of commitShas) {
1098
+ for (const changedFile of splitLines(await commandStdout(deps.runner, context.taskRepoRoot, [
1099
+ "show",
1100
+ "--format=",
1101
+ "--name-only",
1102
+ "--find-renames",
1103
+ commitSha
1104
+ ]))) {
1105
+ changedFiles.add(changedFile);
1106
+ }
1107
+ const stat = await commandStdout(deps.runner, context.taskRepoRoot, [
1108
+ "show",
1109
+ "--format=",
1110
+ "--stat",
1111
+ "--find-renames",
1112
+ commitSha
1113
+ ]);
1114
+ if (stat.trim()) {
1115
+ diffStats.push(stat.trim());
1116
+ }
1117
+ const patch = await commandStdout(deps.runner, context.taskRepoRoot, [
1118
+ "show",
1119
+ "--format=",
1120
+ "--binary",
1121
+ "--find-renames",
1122
+ commitSha
1123
+ ]);
1124
+ if (patch) {
1125
+ diffs.push(patch);
1126
+ }
1127
+ }
1128
+ const diffStat = diffStats.join("\n");
1129
+ const diff = diffs.join("\n");
1089
1130
  const diffHash = createHash("sha256").update(diff).digest("hex");
1090
1131
  return {
1091
1132
  baseCommit,
1092
1133
  headCommit,
1093
1134
  commits,
1094
- changedFiles,
1135
+ commitShas,
1136
+ changedFiles: [...changedFiles],
1095
1137
  diffStat,
1096
1138
  diffHash
1097
1139
  };
1098
1140
  }
1141
+ function commitShaFromOneline(line) {
1142
+ return line.split(/\s+/, 1)[0] ?? line;
1143
+ }
1144
+ function isHarnessCommitLine(line) {
1145
+ const separator = line.indexOf(" ");
1146
+ return separator >= 0 && line.slice(separator + 1).startsWith(HARNESS_COMMIT_PREFIX);
1147
+ }
1099
1148
  async function resolveCodeDiffBaseCommit(deps, context, record, headCommit) {
1149
+ if (record.gate === "code-diff"
1150
+ && record.status === "failed"
1151
+ && record.baseCommit
1152
+ && await isAncestor(deps.runner, context.taskRepoRoot, record.baseCommit, headCommit)) {
1153
+ return record.baseCommit;
1154
+ }
1100
1155
  if (record.gate === "code-diff"
1101
1156
  && record.status === "completed"
1102
1157
  && record.decision === "request_changes"
@@ -1111,6 +1166,12 @@ async function resolveCodeDiffBaseCommit(deps, context, record, headCommit) {
1111
1166
  && await isAncestor(deps.runner, context.taskRepoRoot, record.headCommit, headCommit)) {
1112
1167
  return record.headCommit;
1113
1168
  }
1169
+ if (record.gate === "code-diff"
1170
+ && record.status === "not_required"
1171
+ && record.headCommit
1172
+ && await isAncestor(deps.runner, context.taskRepoRoot, record.headCommit, headCommit)) {
1173
+ return record.headCommit;
1174
+ }
1114
1175
  const rootHead = (await commandStdout(deps.runner, context.repoRoot, ["rev-parse", "HEAD"])).trim();
1115
1176
  if (rootHead
1116
1177
  && rootHead !== headCommit
@@ -1165,7 +1226,7 @@ async function computeInputHash(deps, taskRepoRoot, gate, codeDiffInput, codeDif
1165
1226
  digest.update(coreArtifact);
1166
1227
  digest.update(await deps.fs.readText(resolveRepoPath(taskRepoRoot, coreArtifact)));
1167
1228
  }
1168
- const common = [
1229
+ const common = gate === "code-diff" ? [] : [
1169
1230
  "CLAUDE.md",
1170
1231
  ".claude/agents/architect.md",
1171
1232
  ".claude/agents/coder.md",
@@ -1189,10 +1250,6 @@ async function computeInputHash(deps, taskRepoRoot, gate, codeDiffInput, codeDif
1189
1250
  if (gate === "code-diff" && codeDiffInput) {
1190
1251
  digest.update("codeDiffSources");
1191
1252
  digest.update(codeDiffSources?.join("\n") ?? "<missing>");
1192
- digest.update("baseCommit");
1193
- digest.update(codeDiffInput.baseCommit);
1194
- digest.update("headCommit");
1195
- digest.update(codeDiffInput.headCommit);
1196
1253
  digest.update("commits");
1197
1254
  digest.update(codeDiffInput.commits.join("\n"));
1198
1255
  digest.update("changedFiles");
@@ -1295,11 +1352,14 @@ async function readValidationReportError(fs, taskRepoRoot) {
1295
1352
  }
1296
1353
  return undefined;
1297
1354
  }
1298
- async function readCodeDiffPrerequisiteError(deps, context, index, source) {
1299
- const reportError = await readValidationReportError(deps.fs, context.taskRepoRoot);
1355
+ async function readCodeDiffEvidenceError(fs, taskRepoRoot, source) {
1356
+ const reportError = await readValidationReportError(fs, taskRepoRoot);
1300
1357
  if (reportError) {
1301
1358
  return "code-diff requires completed Tester validation. " + reportError;
1302
1359
  }
1360
+ return readCodeDiffSourceArtifactError(fs, taskRepoRoot, source);
1361
+ }
1362
+ async function readCodeDiffValidationGateError(deps, context, index) {
1303
1363
  const validationGate = index.gates["validation-adequacy"];
1304
1364
  if (validationGate.required && validationGate.status !== "skipped" && validationGate.status !== "overridden") {
1305
1365
  if (validationGate.status !== "completed" || validationGate.decision !== "approve") {
@@ -1310,7 +1370,7 @@ async function readCodeDiffPrerequisiteError(deps, context, index, source) {
1310
1370
  return "code-diff requires a current validation-adequacy approval; code or test evidence changed after the recorded approval.";
1311
1371
  }
1312
1372
  }
1313
- return readCodeDiffSourceArtifactError(deps.fs, context.taskRepoRoot, source);
1373
+ return undefined;
1314
1374
  }
1315
1375
  async function readArchitectureEvidenceError(fs, taskRepoRoot) {
1316
1376
  const relativePath = ".ai/vcm/handoffs/architecture-evidence.md";
@@ -1399,17 +1459,16 @@ Use each captured snapshot as the immutable handoff or prior-Gate input for this
1399
1459
  Code Diff Input:
1400
1460
  This code-diff gate reviews the new commits from one PM route flow, not the whole task and not one terminal turn.
1401
1461
  Code sources: ${codeDiffSources?.join(" -> ") ?? "<missing>"}
1402
- Base commit: ${codeDiffInput.baseCommit}
1403
- Head commit: ${codeDiffInput.headCommit}
1404
- Commits:
1462
+ Reviewable commits:
1405
1463
  ${codeDiffInput.commits.map((line) => `- ${line}`).join("\n")}
1406
1464
  Changed files:
1407
1465
  ${codeDiffInput.changedFiles.length > 0 ? codeDiffInput.changedFiles.map((line) => `- ${line}`).join("\n") : "- <none>"}
1408
1466
  Diff commands:
1409
- - git diff --stat --find-renames ${codeDiffInput.baseCommit}..${codeDiffInput.headCommit}
1410
- - git diff --find-renames ${codeDiffInput.baseCommit}..${codeDiffInput.headCommit}
1411
- - git show --stat --oneline <commit>
1412
- Review only this commit range.`
1467
+ ${codeDiffInput.commitShas.flatMap((commitSha) => [
1468
+ `- git show --stat --oneline ${commitSha}`,
1469
+ `- git show --find-renames ${commitSha}`
1470
+ ]).join("\n")}
1471
+ Review only these commits.`
1413
1472
  : "";
1414
1473
  return `[VCM GATE REVIEW]
1415
1474
  Task: ${context.taskSlug}
@@ -1763,18 +1822,58 @@ function getSourceArtifacts(gate, codeDiffSources) {
1763
1822
  ...(codeDiffSources ?? []).flatMap((source) => CODE_DIFF_SOURCE_ARTIFACTS[source])
1764
1823
  ])];
1765
1824
  }
1766
- function resolveCodeDiffSources(record, codeDiffInput, currentSource) {
1825
+ function resolveCodeDiffSources(record, codeDiffInput, currentSources) {
1767
1826
  const continuingRecordedRange = record.baseCommit === codeDiffInput.baseCommit
1768
1827
  && ((record.status === "completed" && record.decision === "request_changes")
1769
1828
  || record.status === "failed");
1770
1829
  if (!continuingRecordedRange) {
1771
- return [currentSource];
1830
+ return currentSources;
1772
1831
  }
1773
1832
  return [...new Set([
1774
1833
  ...(normalizeCodeDiffSources(record.codeDiffSources, record.codeDiffSource) ?? []),
1775
- currentSource
1834
+ ...currentSources
1776
1835
  ])];
1777
1836
  }
1837
+ async function resolveWorkflowCodeDiffSources(deps, context, currentSource) {
1838
+ if (!deps.workflowControlService) {
1839
+ return [currentSource];
1840
+ }
1841
+ try {
1842
+ const workflowContext = {
1843
+ taskRepoRoot: context.taskRepoRoot,
1844
+ stateRoot: context.stateRoot,
1845
+ handoffDir: context.handoffDir,
1846
+ taskSlug: context.taskSlug
1847
+ };
1848
+ const [state, progress] = await Promise.all([
1849
+ deps.workflowControlService.getState(workflowContext),
1850
+ deps.workflowControlService.getProgress(workflowContext)
1851
+ ]);
1852
+ const startedAtSequence = state.flowRun?.startedAtSequence;
1853
+ if (state.warnings.length > 0 || startedAtSequence === undefined) {
1854
+ return [currentSource];
1855
+ }
1856
+ const sources = [];
1857
+ for (const entry of progress.history) {
1858
+ if (entry.sequence < startedAtSequence)
1859
+ continue;
1860
+ if (entry.flow === "code-change" && entry.targetRole === "coder") {
1861
+ sources.push("coder");
1862
+ }
1863
+ else if (entry.flow === "architect-debug" && entry.targetRole === "architect") {
1864
+ sources.push("architect-debug");
1865
+ }
1866
+ else if (entry.flow === "architecture-diagnosis" && entry.targetRole === "architect") {
1867
+ sources.push("architect-diagnosis");
1868
+ }
1869
+ }
1870
+ sources.push(currentSource);
1871
+ return [...new Set(sources)];
1872
+ }
1873
+ catch {
1874
+ return [currentSource];
1875
+ }
1876
+ }
1778
1877
  function normalizeCodeDiffSources(sources, source) {
1779
1878
  const normalized = Array.isArray(sources) ? sources.filter(isCodeDiffSource) : [];
1780
1879
  if (normalized.length === 0 && isCodeDiffSource(source)) {