vibe-coding-master 0.8.11 → 0.8.13

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.
@@ -24,6 +24,10 @@ export function registerMessageRoutes(app, deps) {
24
24
  const context = await getRouteContext(deps, request.params.taskSlug);
25
25
  return deps.messageService.deleteMessageHistory(context);
26
26
  });
27
+ app.post("/api/tasks/:taskSlug/messages/:messageId/confirm-delivered", async (request) => {
28
+ const context = await getRouteContext(deps, request.params.taskSlug);
29
+ return deps.messageService.confirmDeliveredDispatch(context, request.params.messageId);
30
+ });
27
31
  app.get("/api/tasks/:taskSlug/orchestration", async (request) => {
28
32
  return withOpenFileLimitFallback(async () => {
29
33
  const context = await getRouteContext(deps, request.params.taskSlug);
@@ -1470,7 +1470,8 @@ function buildRoleDraftPrompt(taskRepoRoot, state, draft, planningCandidatePath)
1470
1470
  "Review that candidate against final task evidence. Carry forward only facts that remain verified after implementation and testing."
1471
1471
  ]
1472
1472
  : []),
1473
- `Assigned proposal path: ${resolveRepoPath(taskRepoRoot, draft.path)}`
1473
+ `Assigned proposal path: ${draft.path}`,
1474
+ "Pass the assigned repository-relative path to vcm-artifact --path."
1474
1475
  ];
1475
1476
  return [
1476
1477
  ...prompt,
@@ -64,7 +64,9 @@ export function createHarnessCodeIntelligenceDetector(fs, options = {}) {
64
64
  const now = options.now ?? Date.now;
65
65
  const cacheTtlMs = options.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS;
66
66
  return {
67
- async detect(repoRoot) {
67
+ async detect(repoRoot, detectOptions) {
68
+ if (detectOptions?.refresh)
69
+ probeCache.clear();
68
70
  return detectCodeIntelligence(fs, repoRoot, options, async (definition, executablePath) => {
69
71
  const cacheKey = `${executablePath}\u0000${definition.probeArgs.join("\u0000")}`;
70
72
  const cached = probeCache.get(cacheKey);
@@ -1173,7 +1173,7 @@ async function resolveCodeDiffBaseCommit(deps, context, record, headCommit) {
1173
1173
  "merge-base",
1174
1174
  "HEAD",
1175
1175
  rootBranch
1176
- ], { allowFailure: true })).trim();
1176
+ ], { expectedFailure: "no-common-ancestor" })).trim();
1177
1177
  if (mergeBase
1178
1178
  && mergeBase !== headCommit
1179
1179
  && await isAncestor(deps.runner, context.taskRepoRoot, mergeBase, headCommit)) {
@@ -1185,13 +1185,13 @@ async function resolveCodeDiffBaseCommit(deps, context, record, headCommit) {
1185
1185
  "--abbrev-ref",
1186
1186
  "--symbolic-full-name",
1187
1187
  "@{upstream}"
1188
- ], { allowFailure: true })).trim();
1188
+ ], { expectedFailure: "missing-upstream" })).trim();
1189
1189
  if (upstream) {
1190
1190
  const mergeBase = (await commandStdout(deps.runner, context.taskRepoRoot, [
1191
1191
  "merge-base",
1192
1192
  "HEAD",
1193
1193
  upstream
1194
- ], { allowFailure: true })).trim();
1194
+ ], { expectedFailure: "no-common-ancestor" })).trim();
1195
1195
  if (mergeBase
1196
1196
  && mergeBase !== headCommit
1197
1197
  && await isAncestor(deps.runner, context.taskRepoRoot, mergeBase, headCommit)) {
@@ -1201,7 +1201,9 @@ async function resolveCodeDiffBaseCommit(deps, context, record, headCommit) {
1201
1201
  return headCommit;
1202
1202
  }
1203
1203
  async function isAncestor(runner, cwd, ancestor, descendant) {
1204
- const result = await runner.run("git", ["merge-base", "--is-ancestor", ancestor, descendant], { cwd });
1204
+ const result = await readGateGit(runner, cwd, ["merge-base", "--is-ancestor", ancestor, descendant], {
1205
+ expectedFailure: "not-ancestor"
1206
+ });
1205
1207
  return result.exitCode === 0;
1206
1208
  }
1207
1209
  async function computeInputHash(deps, taskRepoRoot, gate, codeDiffInput, codeDiffSources) {
@@ -1412,18 +1414,19 @@ async function readCodeDiffSourceArtifactError(fs, taskRepoRoot, source) {
1412
1414
  return undefined;
1413
1415
  }
1414
1416
  async function commandStdout(runner, cwd, args, options = {}) {
1415
- const retryHead = args.length === 2 && args[0] === "rev-parse" && args[1] === "HEAD";
1416
- const attempts = retryHead ? 3 : 1;
1417
- for (let attempt = 0; attempt < attempts; attempt += 1) {
1418
- const result = await runner.run("git", args, { cwd });
1419
- if (result.exitCode === 0)
1420
- return result.stdout;
1421
- if (attempt + 1 < attempts) {
1422
- await delay(100 * (attempt + 1));
1417
+ const result = await readGateGit(runner, cwd, args, options);
1418
+ return result.exitCode === 0 ? result.stdout : "";
1419
+ }
1420
+ async function readGateGit(runner, cwd, args, options = {}) {
1421
+ const retryDelays = [500, 1000, 2000];
1422
+ for (let attempt = 0;; attempt += 1) {
1423
+ const result = await runner.run("git", ["-c", `safe.directory=${path.resolve(cwd)}`, ...args], { cwd });
1424
+ if (result.exitCode === 0 || isExpectedGitFailure(result, options.expectedFailure))
1425
+ return result;
1426
+ if (attempt < retryDelays.length) {
1427
+ await delay(retryDelays[attempt]);
1423
1428
  continue;
1424
1429
  }
1425
- if (options.allowFailure)
1426
- return "";
1427
1430
  const detail = result.stderr.trim() || result.stdout.trim() || "Git did not provide an error message.";
1428
1431
  throw new VcmError({
1429
1432
  code: "GATE_REVIEW_GIT_COMMAND_FAILED",
@@ -1431,7 +1434,13 @@ async function commandStdout(runner, cwd, args, options = {}) {
1431
1434
  statusCode: 409
1432
1435
  });
1433
1436
  }
1434
- return "";
1437
+ }
1438
+ function isExpectedGitFailure(result, expected) {
1439
+ if (expected === "not-ancestor" || expected === "no-common-ancestor") {
1440
+ return result.exitCode === 1 && !result.stderr.trim() && !result.stdout.trim();
1441
+ }
1442
+ return expected === "missing-upstream" && result.exitCode === 128
1443
+ && /^fatal: (no upstream configured for branch |HEAD does not point to a branch)/.test(result.stderr.trim());
1435
1444
  }
1436
1445
  function splitLines(value) {
1437
1446
  return value
@@ -37,6 +37,7 @@ import { submitTerminalInput } from "../runtime/terminal-submit.js";
37
37
  import { VcmError } from "../errors.js";
38
38
  import { bumpHarnessRevision, readHarnessRevisionState } from "./harness-revision.js";
39
39
  import { createHarnessCodeIntelligenceDetector } from "./code-intelligence-service.js";
40
+ import { lspUnavailableWarning } from "./lsp-plugin.js";
40
41
  const execFileAsync = promisify(execFile);
41
42
  const BOOTSTRAP_SESSION_PATH = ".ai/vcm/bootstrap/session.json";
42
43
  const HARNESS_ENGINEER_SESSION_PATH = ".ai/vcm/harness-engineer/session.json";
@@ -1410,7 +1411,7 @@ function renderHarnessStatus(harnessRevision, analyses, legacyChanges = [], mani
1410
1411
  ...(plannedChanges.length > 0 ? ["Review and commit VCM Harness changes before starting long-running work."] : []),
1411
1412
  ...((codeIntelligence?.languages ?? [])
1412
1413
  .filter((language) => language.state !== "server_runnable")
1413
- .map((language) => `Architect LSP for ${language.label} is unavailable: ${language.error ?? `${language.serverCommand} is not runnable.`}`))
1414
+ .map(lspUnavailableWarning))
1414
1415
  ]
1415
1416
  };
1416
1417
  }
@@ -17,3 +17,29 @@ export async function prepareTaskLspPlugin(fs, taskRepoRoot, status) {
17
17
  await fs.writeJsonAtomic(pluginManifestPath, { ...manifest, lspServers });
18
18
  return [pluginDir];
19
19
  }
20
+ export function lspUnavailableWarning(language) {
21
+ const installCommands = {
22
+ "rust-analyzer": "rustup component add rust-analyzer",
23
+ "typescript-language-server": "npm install -g typescript typescript-language-server",
24
+ "pyright-langserver": "npm install -g pyright",
25
+ "gopls": "go install golang.org/x/tools/gopls@latest"
26
+ };
27
+ const recovery = language.state === "plugin_missing"
28
+ ? "Refresh the VCM Harness to restore the LSP plugin."
29
+ : `Install or repair ${language.serverCommand} in the environment running the VCM backend${installCommands[language.serverCommand] ? ` (${installCommands[language.serverCommand]})` : ""} and ensure it is executable on PATH.`;
30
+ return `Architect LSP for ${language.label} is unavailable: ${language.error ?? `${language.serverCommand} is not runnable.`} ${recovery} Restart Architect after fixing the environment so VCM can detect and register the server again.`;
31
+ }
32
+ export function renderArchitectLspNotice(status) {
33
+ if (status.languages.length === 0)
34
+ return undefined;
35
+ const available = status.languages.filter((language) => language.state === "server_runnable");
36
+ const unavailable = status.languages.filter((language) => language.state !== "server_runnable");
37
+ return [
38
+ "[VCM Architect LSP Availability]",
39
+ `Registered language servers: ${available.map((language) => `${language.label} (${language.serverCommand})`).join(", ") || "none"}.`,
40
+ ...unavailable.map(lspUnavailableWarning),
41
+ ...(unavailable.length > 0 ? [
42
+ "If your assignment requires a semantic query in an unavailable language, stop that work and report the environment prerequisite through the current communication channel. Do not substitute source text search or claim the relationship is verified. Continue only after the server is available."
43
+ ] : [])
44
+ ].join("\n");
45
+ }
@@ -11,7 +11,7 @@ const DEFAULT_PRE_DISPATCH_SWITCH_DELAY_MS = 500;
11
11
  const DEFAULT_AUTO_DISPATCH_ENTER_DELAY_MS = 500;
12
12
  const DEFAULT_DISPATCH_CONFIRMATION_RETRY_DELAYS_MS = [1500, 3000];
13
13
  const DEFAULT_DISPATCH_CONFIRMATION_FAILURE_DELAY_MS = 3000;
14
- const DISPATCH_NOT_CONFIRMED_REASON = "Auto orchestration pasted the message, but Claude Code did not confirm submission. Press Enter in the target terminal or resend the route message.";
14
+ const DISPATCH_NOT_CONFIRMED_REASON = "Auto orchestration pasted the message, but Claude Code did not confirm submission. Inspect the target terminal: press Enter if the prompt was not submitted, or use Confirm Executed if the role already executed it.";
15
15
  const INVALID_ROUTE_REASON = "Invalid route: non-PM roles must route through project-manager.";
16
16
  export function createMessageService(deps) {
17
17
  const now = deps.now ?? (() => new Date().toISOString());
@@ -208,6 +208,22 @@ export function createMessageService(deps) {
208
208
  return accepted;
209
209
  });
210
210
  },
211
+ async confirmDeliveredDispatch(input, messageId) {
212
+ return withTaskLock(taskLocks, getMessagesPath(getStateRepoRoot(input), input.stateRoot, input.taskSlug), async () => {
213
+ const messages = await readLatestMessages(deps.fs, getMessagesPath(getStateRepoRoot(input), input.stateRoot, input.taskSlug));
214
+ const message = messages.find((entry) => entry.id === messageId);
215
+ if (!message || message.fromRole !== PM_ROLE || !message.deliveredAt || !deps.workflowControlService) {
216
+ throw new VcmError({ code: "MESSAGE_NOT_DELIVERED", message: `Message ${messageId} is not a delivered PM workflow dispatch.`, statusCode: 409 });
217
+ }
218
+ if (message.acceptedAt)
219
+ return message;
220
+ await deps.workflowControlService.confirmDispatch(toWorkflowContext(input), messageId, "manual");
221
+ const accepted = { ...message, acceptedAt: now(), confirmationSource: "manual", failureReason: undefined };
222
+ await appendMessageSnapshot(deps.fs, input, accepted);
223
+ await clearRouteFileIfStillMatchesMessage(deps.fs, input, accepted);
224
+ return accepted;
225
+ });
226
+ },
211
227
  async markAllDone(input) {
212
228
  return withTaskLock(taskLocks, getMessagesPath(getStateRepoRoot(input), input.stateRoot, input.taskSlug), async () => {
213
229
  const messages = await readLatestMessages(deps.fs, getMessagesPath(getStateRepoRoot(input), input.stateRoot, input.taskSlug));
@@ -6,7 +6,8 @@ const markdown = unified().use(remarkParse).use(remarkGfm).freeze();
6
6
  const DIRECT_REQUESTS = [
7
7
  /^(?:can|could|do|did|are|have|should|will|would)\s+you\b/i,
8
8
  /^(?:should|shall|may|can|could)\s+(?:i|we)\b/i,
9
- /^(?:which|what|how|where|when|who)\b[^.!?]*\b(?:you|your|(?:should|shall|can|may)\s+(?:i|we))\b/i,
9
+ /^(?:which|what|how|where|when|who)\s+(?:is|are|was|were|do|did|have|has|can|could|will|would|should|shall|may)\s+(?:you|your|i|we)\b/i,
10
+ /^(?:which|what|how|where|when|who)\b[^.!?:,;]*\b(?:do|did|can|could|will|would|should|shall|may)\s+(?:you|i|we)\b/i,
10
11
  /^(?:which|what)\s+(?:option|approach|behavior|behaviour|choice)\b[^.!?]*\bshould\s+be\b/i,
11
12
  /^(?:please\s+)?(?:answer|reply|respond|choose|confirm|decide|provide|select|tell\s+me|let\s+me\s+know|give\s+(?:me|your))\b/i,
12
13
  /^i\s+need\s+your\s+(?:decision|choice|confirmation|answer|approval)\b/i,
@@ -8,7 +8,7 @@ import { submitTerminalInput } from "../runtime/terminal-submit.js";
8
8
  import { claudeTranscriptPath } from "./claude-transcript-service.js";
9
9
  import { readHarnessRevisionState } from "./harness-revision.js";
10
10
  import { getTaskRuntimeRepoRoot } from "./task-service.js";
11
- import { roleUsesLsp, prepareTaskLspPlugin, VCM_LSP_PLUGIN_DIR } from "./lsp-plugin.js";
11
+ import { roleUsesLsp, prepareTaskLspPlugin, renderArchitectLspNotice, VCM_LSP_PLUGIN_DIR } from "./lsp-plugin.js";
12
12
  const TRANSLATOR_ROLE = "translator";
13
13
  const HARNESS_ENGINEER_ROLE = "harness-engineer";
14
14
  const TRANSLATION_DIR = ".ai/vcm/translations";
@@ -82,13 +82,16 @@ export function createSessionService(deps) {
82
82
  : resumeClaudeSessionId
83
83
  ? claudeTranscriptPath(taskRepoRoot, resumeClaudeSessionId, persisted?.claudeConfigDir)
84
84
  : undefined;
85
+ const lspStatus = roleUsesLsp(role) && deps.codeIntelligenceDetector
86
+ ? await deps.codeIntelligenceDetector.detect(taskRepoRoot, { refresh: true })
87
+ : undefined;
85
88
  const lspPluginDirs = roleUsesLsp(role)
86
- ? deps.codeIntelligenceDetector
87
- ? await prepareTaskLspPlugin(deps.fs, taskRepoRoot, await deps.codeIntelligenceDetector.detect(taskRepoRoot))
88
- : [VCM_LSP_PLUGIN_DIR]
89
+ ? lspStatus ? await prepareTaskLspPlugin(deps.fs, taskRepoRoot, lspStatus) : [VCM_LSP_PLUGIN_DIR]
89
90
  : [];
91
+ const appendSystemPrompt = [input.appendSystemPrompt, lspStatus && renderArchitectLspNotice(lspStatus)]
92
+ .filter(Boolean).join("\n\n") || undefined;
90
93
  const startCommand = {
91
- ...deps.claude.buildRoleStartCommand(role, config.claudeCommand, permissionMode, resumeClaudeSessionId, launchMode === "resume", model, effort, modelSettingsOverride, input.appendSystemPrompt, lspPluginDirs),
94
+ ...deps.claude.buildRoleStartCommand(role, config.claudeCommand, permissionMode, resumeClaudeSessionId, launchMode === "resume", model, effort, modelSettingsOverride, appendSystemPrompt, lspPluginDirs),
92
95
  cwd: taskRepoRoot
93
96
  };
94
97
  const runtimeSessionToken = randomUUID();
@@ -189,6 +189,9 @@ export function createWorkflowControlService(deps) {
189
189
  await assertRouteAuthorized(input);
190
190
  const state = await getState(input);
191
191
  const pending = state.pendingDispatch;
192
+ const timestamp = now();
193
+ const progress = await readProgress(deps.fs, input);
194
+ const evidenceBaseline = await captureEvidenceBaseline(deps.fs, input, progress.history.length + 1, pending.effectiveFlow, pending.targetRole, timestamp);
192
195
  await saveState(input, {
193
196
  ...state,
194
197
  pendingDispatch: {
@@ -196,7 +199,8 @@ export function createWorkflowControlService(deps) {
196
199
  status: "dispatching",
197
200
  routeContentHash: input.routeContentHash,
198
201
  messageId: input.messageId,
199
- updatedAt: now()
202
+ evidenceBaseline,
203
+ updatedAt: timestamp
200
204
  },
201
205
  updatedAt: now()
202
206
  });
@@ -216,6 +220,7 @@ export function createWorkflowControlService(deps) {
216
220
  routeContentHash: undefined,
217
221
  messageId: undefined,
218
222
  confirmationError: undefined,
223
+ evidenceBaseline: undefined,
219
224
  updatedAt: now()
220
225
  },
221
226
  updatedAt: now()
@@ -257,7 +262,7 @@ export function createWorkflowControlService(deps) {
257
262
  });
258
263
  });
259
264
  }
260
- async function confirmDispatch(input, messageId) {
265
+ async function confirmDispatch(input, messageId, source = "hook") {
261
266
  await withLock(statePath(input), async () => {
262
267
  const state = await getState(input);
263
268
  failOnStateWarnings({
@@ -269,6 +274,9 @@ export function createWorkflowControlService(deps) {
269
274
  if (!pending || pending.status !== "dispatching" || pending.messageId !== messageId) {
270
275
  throw workflowError("WORKFLOW_DISPATCH_CONFIRMATION_MISMATCH", `Message ${messageId} does not own the pending workflow dispatch.`, "Do not advance Workflow Progress from an unrelated UserPromptSubmit event.");
271
276
  }
277
+ if (source === "manual" && !pending.confirmationError) {
278
+ throw workflowError("WORKFLOW_DISPATCH_NOT_UNCONFIRMED", "Manual confirmation is only available for a delivered dispatch whose confirmation failed.");
279
+ }
272
280
  const current = await readProgress(deps.fs, input);
273
281
  if (current.revision !== pending.revision || historyHash(current.history) !== pending.baseHistoryHash) {
274
282
  throw workflowError("WORKFLOW_APPROVAL_STALE", "Workflow Progress changed before the approved dispatch was confirmed.", "Repair workflow-progress.md before continuing.");
@@ -278,12 +286,14 @@ export function createWorkflowControlService(deps) {
278
286
  sequence: current.history.length + 1,
279
287
  flow: pending.effectiveFlow,
280
288
  targetRole: pending.targetRole,
281
- evidence: pending.evidence,
289
+ evidence: source === "manual"
290
+ ? `${pending.evidence} [User manually confirmed execution of message ${messageId}.]`
291
+ : pending.evidence,
282
292
  overrideAuthorizationId: pending.overrideAuthorizationId,
283
293
  followUpApprovalId: pending.followUpApprovalId,
284
294
  confirmedAt: timestamp
285
295
  };
286
- const activeDispatch = await captureEvidenceBaseline(deps.fs, input, entry.sequence, entry.flow, entry.targetRole, timestamp);
296
+ const activeDispatch = pending.evidenceBaseline ?? await captureEvidenceBaseline(deps.fs, input, entry.sequence, entry.flow, entry.targetRole, timestamp);
287
297
  const flowRun = await advanceFlowRun(deps.fs, input, state, current, pending.effectiveFlow, entry.sequence);
288
298
  const completed = {
289
299
  ...current,
@@ -339,6 +349,7 @@ export function createWorkflowControlService(deps) {
339
349
  routeContentHash: undefined,
340
350
  messageId: undefined,
341
351
  confirmationError: undefined,
352
+ evidenceBaseline: undefined,
342
353
  updatedAt: now()
343
354
  },
344
355
  updatedAt: now()
@@ -1292,7 +1303,7 @@ function stateProgressConsistencyWarnings(state, progress) {
1292
1303
  }
1293
1304
  function unconfirmedDispatchWarning(pending) {
1294
1305
  return pending?.confirmationError
1295
- ? `PM dispatch ${pending.messageId ?? "unknown"} to ${pending.targetRole} was delivered but not confirmed: ${pending.confirmationError} Inspect the target session before clearing or retrying this route.`
1306
+ ? `PM dispatch ${pending.messageId ?? "unknown"} to ${pending.targetRole} was delivered but not confirmed: ${pending.confirmationError} Inspect the target session and use Confirm Executed if it already ran; do not resend executed work.`
1296
1307
  : undefined;
1297
1308
  }
1298
1309
  function abandonPendingApprovals(state, pending, reason, timestamp) {
@@ -1569,6 +1580,7 @@ function isPendingDispatch(value) {
1569
1580
  && (value.routeContentHash === undefined || typeof value.routeContentHash === "string")
1570
1581
  && (value.messageId === undefined || typeof value.messageId === "string")
1571
1582
  && (value.confirmationError === undefined || typeof value.confirmationError === "string")
1583
+ && (value.evidenceBaseline === undefined || Boolean(normalizeDispatchEvidenceBaseline(value.evidenceBaseline)))
1572
1584
  && typeof value.createdAt === "string"
1573
1585
  && typeof value.updatedAt === "string";
1574
1586
  }
@@ -22,7 +22,7 @@ ${renderRoleMemoryRules("architect")}
22
22
  ### Semantic Code Navigation
23
23
 
24
24
  - Follow the preloaded \`vcm-code-navigation\` skill whenever work requires code definitions, implementations, references, callers, callees, or behavior paths. This applies in every Architect mode and in direct user communication.
25
- - LSP is mandatory and directly available for navigating project source code. Start source-code navigation with LSP. If it is unavailable, report a VCM LSP configuration failure; do not substitute source text search.
25
+ - LSP is mandatory for navigating project source code. Start source-code navigation with a registered LSP server. If a required language server is unavailable, stop the affected work and report its language, command, error, and installation hint through the current communication channel. Resume after the environment is fixed and Architect is restarted; do not substitute source text search.
26
26
  - Use LSP definitions, implementations, references, document or workspace symbols, and incoming or outgoing calls for all source-code symbol and relationship queries.
27
27
  - LSP reference results are symbol-specific. An accessor, its backing field, a trait declaration, its implementation method, a wrapper, and an alias are separate symbols. Query every relevant symbol separately; one symbol's reference result cannot prove a complete semantic class. When starting from an accessor or wrapper, read its implementation, resolve its backing field, delegate, or trait item with LSP, then query those symbols too.
28
28
  - Do not use the built-in \`Grep\` tool or shell text-search commands such as \`grep\`, \`rg\`, \`git grep\`, \`ag\`, or \`ack\` to search project source code, test code, or executable scripts, except for the bounded fallback below. This prohibition includes locating or inferring definitions, implementations, references, callers, callees, symbols, and behavior paths.
@@ -42,7 +42,8 @@ ${renderRoleMemoryRules("architect")}
42
42
 
43
43
  - Architect may invoke only \`vcm-architect-evidence-worker\`, \`vcm-architect-scaffold-worker\`, and \`vcm-architect-validation-worker\`.
44
44
  - Every worker must run in the foreground and return before the current Architect turn continues. Do not run workers in the background or end the turn while a worker is active.
45
- - Give each worker an exact bounded assignment, repo-relative paths, and one report path. Pass paths instead of copying full source, documents, or plans into the worker prompt.
45
+ - Give each worker an exact bounded assignment, repo-relative paths, and one report path under \`.ai/vcm/architect-workers/<worker-type>/worker-<worker-id>.md\`. Pass paths instead of copying full source, documents, or plans into the worker prompt.
46
+ - Read the assigned report file before accepting a worker result or continuing its unfinished work; a text-only return is not a completed worker report.
46
47
  - Worker output is evidence or execution output, never an architecture decision. Architect owns every conclusion, plan, change boundary, validation interpretation, and final claim.
47
48
  - Use \`vcm-architect-evidence-worker\` for bounded bulk reading when relevant evidence spans multiple files or modules. Evidence workers may run in parallel only when their read scopes are disjoint.
48
49
  - Give evidence workers only repo-relative paths, symbols, documents, and questions. They have no shell access.
@@ -17,7 +17,8 @@ You are \`vcm-architect-evidence-worker\`, a foreground evidence-collection suba
17
17
 
18
18
  ### Evidence Output
19
19
 
20
- - Write only the assigned report under \`.ai/vcm/architect-workers/evidence/\`.
20
+ - Write the assigned report under \`.ai/vcm/architect-workers/evidence/worker-<worker-id>.md\` before returning. Only that report and its candidate or staging files may be written.
21
+ - For candidate or staging Markdown, use \`worker-<worker-id>-candidate.md\`; never start its basename with \`report\`, \`summary\`, \`findings\`, or \`analysis\` (case-insensitive). If Write rejects a candidate or staging basename, rename it and retry; still deliver the assigned final report file, not just response text.
21
22
  - Keep the report concise and factual. Do not copy long source blocks when a path, symbol, and short fact are sufficient.
22
23
  - Use this structure:
23
24
 
@@ -17,8 +17,14 @@ You are \`vcm-architect-scaffold-worker\`, a foreground subagent invoked by Arch
17
17
 
18
18
  - Run \`.ai/tools/check-scaffold-ledger --mode scaffold\` and the plan's scaffold L0 compile/typecheck checks.
19
19
  - Commit only the scaffold changes after the ledger reconciles and required checks pass.
20
- - Return the commit hash, changed files, ledger result, and exact check results to Architect.
21
- - If the assigned scaffold cannot be completed, return the concrete failure evidence without changing the plan.
20
+ - If the assigned scaffold cannot be completed, record the concrete failure evidence without changing the plan.
21
+
22
+ ### Scaffold Output
23
+
24
+ - Write the assigned report under \`.ai/vcm/architect-workers/scaffold/worker-<worker-id>.md\` before returning, including when scaffold work remains incomplete.
25
+ - For candidate or staging Markdown, use \`worker-<worker-id>-candidate.md\`; never start its basename with \`report\`, \`summary\`, \`findings\`, or \`analysis\` (case-insensitive). If Write rejects a candidate or staging basename, rename it and retry; still deliver the assigned final report file, not just response text.
26
+ - Record completed and remaining Scaffold Manifest IDs, changed files, commit hash (or no commit with the reason), ledger result, exact L0 check commands and results, and concrete failure evidence.
27
+ - Return the report path and a concise completion summary to Architect.
22
28
 
23
29
  Architect reviews the worker's commit and remains responsible for the final scaffold, plan, and evidence.
24
30
  Do not invoke another subagent.`;
@@ -13,7 +13,8 @@ You are \`vcm-architect-validation-worker\`, a foreground command-execution suba
13
13
 
14
14
  ### Validation Output
15
15
 
16
- - Write only the assigned report under \`.ai/vcm/architect-workers/validation/\`.
16
+ - Write the assigned report under \`.ai/vcm/architect-workers/validation/worker-<worker-id>.md\` before returning. Only that report and its candidate or staging files may be written.
17
+ - For candidate or staging Markdown, use \`worker-<worker-id>-candidate.md\`; never start its basename with \`report\`, \`summary\`, \`findings\`, or \`analysis\` (case-insensitive). If Write rejects a candidate or staging basename, rename it and retry; still deliver the assigned final report file, not just response text.
17
18
  - Use this structure:
18
19
 
19
20
  \`\`\`md
@@ -26,6 +26,7 @@ You are \`vcm-coder-worker\`, a bounded implementation worker invoked by Coder.
26
26
  \`\`\`
27
27
 
28
28
  - After the sweep of assigned items and their assigned checks, commit the assigned files. After the commit succeeds, write the assigned report to a candidate outside \`.ai/vcm\`, submit it with \`.ai/tools/vcm-artifact coder-worker-report --file <candidate> --path <assigned-report-path> --mode final\`, then update only the assigned worker state to \`completed\` with the same \`commitHash\` as the final step.
29
+ - For candidate or staging Markdown, use \`worker-<worker-id>-candidate.md\`; never start its basename with \`report\`, \`summary\`, \`findings\`, or \`analysis\` (case-insensitive). If Write rejects a candidate or staging basename, rename it and retry; still submit the assigned final report through \`vcm-artifact\`, not just response text.
29
30
  - The completed state must retain \`workerId\`, \`reportPath\`, and \`handled: false\`, set \`status\` to \`completed\`, and add \`"commitHash": "<exact-report-commit-hash>"\`.
30
31
  - Use \`completed\` only after every assigned item reached a terminal state. A successful item has green assigned proof and its marker removed. A failed item has a genuine attempt committed with objective failure evidence and its marker retained. Use \`success\` only when every item succeeded; otherwise use \`has_failed_items\`.
31
32
  - If execution is interrupted before the sweep, commit, or report completes, leave the worker state as \`running\`. Coder must resume the worker or take over the remaining work.
@@ -6,7 +6,7 @@ Use this skill when Architect must establish symbol definitions, implementations
6
6
  ## Navigation Order
7
7
 
8
8
  1. Define the affected feature or module boundary and locate entry symbols with \`.ai/generated/module-index.json\` and \`.ai/generated/public-surface.json\` when available.
9
- 2. Use LSP workspace or file symbols, definitions, implementations, references, and incoming or outgoing call hierarchy to resolve project-owned relationships. If \`LSP\` is unavailable, report a VCM LSP configuration failure instead of substituting text search.
9
+ 2. Use LSP workspace or file symbols, definitions, implementations, references, and incoming or outgoing call hierarchy to resolve project-owned relationships. If a required language server is unavailable, stop the affected work and report its language, command, error, and installation hint through the current communication channel. Resume after the environment is fixed and Architect is restarted; do not substitute text search.
10
10
  3. Treat LSP results as symbol-specific. An accessor, backing field, trait declaration, implementation method, wrapper, and alias are separate symbols. Query every relevant symbol separately. When starting from an accessor or wrapper, read its implementation, resolve the backing field, delegate, or trait item with LSP, then query those symbols too. Never use one symbol's references as proof of a complete semantic class.
11
11
  4. If an initial workspace-symbol request is empty or reports indexing, wait and retry; startup or indexing time does not permit replacing a required semantic query with text matching. Retry the same bounded workspace query at most two more times. If it still cannot resolve, record the operation and result as unresolved.
12
12
  5. Read the complete project-owned callable unit at every resolved location.
@@ -10,6 +10,10 @@ planning-session memory candidate and provides an exact path.
10
10
  under \`.ai/vcm/memory-review/runs/<run-id>/drafts/\` or a planning candidate
11
11
  under \`.ai/vcm/memory-review/candidates/\` in the active task worktree.
12
12
  - If VCM did not provide a path, do not create a proposal.
13
+ - Pass the assigned repository-relative destination unchanged to \`--path\`;
14
+ do not prefix it with the task worktree or base repository root. \`--file\`
15
+ names the candidate file, not the destination. VCM writes the accepted proposal
16
+ inside the active task worktree.
13
17
  - Propose only verified, durable, reusable project knowledge supported by task
14
18
  evidence.
15
19
  - Before proposing an add or update, check current durable documents, Harness
@@ -5,16 +5,20 @@ This skill records feedback only. It must not call Harness Engineer directly and
5
5
 
6
6
  ## Write Location
7
7
 
8
- Submit one markdown file under:
8
+ Submit one markdown file with this repository-relative \`--path\`:
9
9
 
10
10
  \`\`\`
11
- \${VCM_BASE_REPO_ROOT}/.ai/vcm/harness-feedback/pending/
11
+ .ai/vcm/harness-feedback/pending/<UTC timestamp>-<reporter-role>-<short-slug>.md
12
12
  \`\`\`
13
13
 
14
14
  If \`VCM_BASE_REPO_ROOT\` is not set, do not guess a fallback path. Report the
15
15
  environment problem to project-manager so VCM can retry with the correct base
16
16
  repository root.
17
17
 
18
+ VCM resolves this destination inside the base repository root, not the task
19
+ worktree. Do not prefix \`--path\` with \`VCM_BASE_REPO_ROOT\` or an absolute
20
+ directory. \`--file\` names the candidate file, not the destination.
21
+
18
22
  Use a filename like:
19
23
 
20
24
  \`\`\`