vibe-coding-master 0.8.10 → 0.8.12

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.
@@ -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
  });
@@ -215,18 +219,37 @@ export function createWorkflowControlService(deps) {
215
219
  status: "pending",
216
220
  routeContentHash: undefined,
217
221
  messageId: undefined,
222
+ confirmationError: undefined,
223
+ evidenceBaseline: undefined,
218
224
  updatedAt: now()
219
225
  },
220
226
  updatedAt: now()
221
227
  });
222
228
  });
223
229
  }
230
+ async function markDispatchUnconfirmed(input, messageId, reason) {
231
+ await withLock(statePath(input), async () => {
232
+ const state = await getState(input);
233
+ const pending = state.pendingDispatch;
234
+ if (!pending || pending.status !== "dispatching" || pending.messageId !== messageId)
235
+ return;
236
+ await saveState(input, {
237
+ ...state,
238
+ pendingDispatch: { ...pending, confirmationError: reason, updatedAt: now() },
239
+ updatedAt: now()
240
+ });
241
+ });
242
+ }
224
243
  async function cancelPendingDispatch(input) {
225
244
  await withLock(statePath(input), async () => {
226
245
  const state = await getState(input);
227
246
  const pending = state.pendingDispatch;
228
247
  if (!pending)
229
248
  return;
249
+ if (pending.status === "dispatching" && !pending.confirmationError) {
250
+ throw workflowError("WORKFLOW_DISPATCH_PENDING", `Message ${pending.messageId} is being delivered and cannot be cleared before confirmation.`);
251
+ }
252
+ const timestamp = now();
230
253
  const progress = await readProgress(deps.fs, input);
231
254
  await commitStateAndProgress(input, renderWorkflowProgress({
232
255
  ...progress,
@@ -234,21 +257,26 @@ export function createWorkflowControlService(deps) {
234
257
  }), {
235
258
  ...state,
236
259
  pendingDispatch: null,
237
- userAuthorizations: state.userAuthorizations.filter((entry) => entry.id !== pending.overrideAuthorizationId),
238
- userApprovedFollowUps: state.userApprovedFollowUps.filter((entry) => entry.id !== pending.followUpApprovalId),
239
- updatedAt: now()
260
+ ...abandonPendingApprovals(state, pending, "Pending route was cleared before confirmation.", timestamp),
261
+ updatedAt: timestamp
240
262
  });
241
263
  });
242
264
  }
243
- async function confirmDispatch(input, messageId) {
265
+ async function confirmDispatch(input, messageId, source = "hook") {
244
266
  await withLock(statePath(input), async () => {
245
267
  const state = await getState(input);
246
- failOnStateWarnings(state);
268
+ failOnStateWarnings({
269
+ ...state,
270
+ warnings: state.warnings.filter((warning) => warning !== unconfirmedDispatchWarning(state.pendingDispatch))
271
+ });
247
272
  failWhileAwaitingUser(state);
248
273
  const pending = state.pendingDispatch;
249
274
  if (!pending || pending.status !== "dispatching" || pending.messageId !== messageId) {
250
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.");
251
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
+ }
252
280
  const current = await readProgress(deps.fs, input);
253
281
  if (current.revision !== pending.revision || historyHash(current.history) !== pending.baseHistoryHash) {
254
282
  throw workflowError("WORKFLOW_APPROVAL_STALE", "Workflow Progress changed before the approved dispatch was confirmed.", "Repair workflow-progress.md before continuing.");
@@ -258,12 +286,14 @@ export function createWorkflowControlService(deps) {
258
286
  sequence: current.history.length + 1,
259
287
  flow: pending.effectiveFlow,
260
288
  targetRole: pending.targetRole,
261
- evidence: pending.evidence,
289
+ evidence: source === "manual"
290
+ ? `${pending.evidence} [User manually confirmed execution of message ${messageId}.]`
291
+ : pending.evidence,
262
292
  overrideAuthorizationId: pending.overrideAuthorizationId,
263
293
  followUpApprovalId: pending.followUpApprovalId,
264
294
  confirmedAt: timestamp
265
295
  };
266
- 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);
267
297
  const flowRun = await advanceFlowRun(deps.fs, input, state, current, pending.effectiveFlow, entry.sequence);
268
298
  const completed = {
269
299
  ...current,
@@ -293,8 +323,24 @@ export function createWorkflowControlService(deps) {
293
323
  return withLock(statePath(input), async () => {
294
324
  await recoverCommitJournal(input);
295
325
  const state = await getState(input);
296
- if (state.warnings.length > 0 || state.pendingDispatch?.status !== "dispatching")
326
+ if (state.pendingDispatch?.status !== "dispatching"
327
+ || state.warnings.some((warning) => warning !== unconfirmedDispatchWarning(state.pendingDispatch)))
297
328
  return false;
329
+ if (await wasDispatchDelivered(deps.fs, input, state.pendingDispatch.messageId)) {
330
+ if (state.pendingDispatch.confirmationError)
331
+ return false;
332
+ const timestamp = now();
333
+ await saveState(input, {
334
+ ...state,
335
+ pendingDispatch: {
336
+ ...state.pendingDispatch,
337
+ confirmationError: "VCM restarted before the delivered message received UserPromptSubmit confirmation.",
338
+ updatedAt: timestamp
339
+ },
340
+ updatedAt: timestamp
341
+ });
342
+ return true;
343
+ }
298
344
  await saveState(input, {
299
345
  ...state,
300
346
  pendingDispatch: {
@@ -302,6 +348,8 @@ export function createWorkflowControlService(deps) {
302
348
  status: "pending",
303
349
  routeContentHash: undefined,
304
350
  messageId: undefined,
351
+ confirmationError: undefined,
352
+ evidenceBaseline: undefined,
305
353
  updatedAt: now()
306
354
  },
307
355
  updatedAt: now()
@@ -323,6 +371,7 @@ export function createWorkflowControlService(deps) {
323
371
  assertRouteAuthorized,
324
372
  claimDispatch,
325
373
  releaseDispatch,
374
+ markDispatchUnconfirmed,
326
375
  cancelPendingDispatch,
327
376
  confirmDispatch,
328
377
  recoverTask
@@ -361,10 +410,12 @@ export function createWorkflowControlService(deps) {
361
410
  async function requestUserInput(input, question) {
362
411
  return withLock(statePath(input), async () => {
363
412
  const state = await getState(input);
364
- failOnStateWarnings(state);
365
413
  if (state.pendingDispatch?.status === "dispatching") {
366
- throw workflowError("WORKFLOW_DISPATCH_PENDING", "A role dispatch is already being submitted and cannot be canceled by a user question.", "Wait for the target UserPromptSubmit confirmation or recover the dispatch before asking the user.");
414
+ throw workflowError("WORKFLOW_DISPATCH_PENDING", state.pendingDispatch.confirmationError
415
+ ? `A delivered role dispatch is unconfirmed: ${state.pendingDispatch.confirmationError}`
416
+ : "A role dispatch is already being submitted and cannot be canceled by a user question.", "Inspect the target session and confirm or clear the pending dispatch before asking the user.");
367
417
  }
418
+ failOnStateWarnings(state);
368
419
  const normalizedQuestion = question.trim();
369
420
  if (!normalizedQuestion) {
370
421
  throw workflowError("WORKFLOW_USER_QUESTION_REQUIRED", "A non-empty user question is required.");
@@ -378,8 +429,7 @@ export function createWorkflowControlService(deps) {
378
429
  requestedAt: timestamp
379
430
  },
380
431
  pendingDispatch: null,
381
- userAuthorizations: state.userAuthorizations.filter((entry) => entry.id !== pending?.overrideAuthorizationId),
382
- userApprovedFollowUps: state.userApprovedFollowUps.filter((entry) => entry.id !== pending?.followUpApprovalId),
432
+ ...(pending ? abandonPendingApprovals(state, pending, "PM asked the user before dispatch confirmation.", timestamp) : {}),
383
433
  updatedAt: timestamp
384
434
  };
385
435
  if (pending) {
@@ -1226,6 +1276,9 @@ function validateCandidateAgainstCurrent(current, candidate) {
1226
1276
  function stateProgressConsistencyWarnings(state, progress) {
1227
1277
  const warnings = [];
1228
1278
  const pending = state.pendingDispatch;
1279
+ const unconfirmedWarning = unconfirmedDispatchWarning(pending);
1280
+ if (unconfirmedWarning)
1281
+ warnings.push(unconfirmedWarning);
1229
1282
  if (!pending && progress.proposal) {
1230
1283
  warnings.push("Workflow Progress contains a dispatch proposal without a runtime approval.");
1231
1284
  }
@@ -1248,6 +1301,40 @@ function stateProgressConsistencyWarnings(state, progress) {
1248
1301
  }
1249
1302
  return warnings;
1250
1303
  }
1304
+ function unconfirmedDispatchWarning(pending) {
1305
+ return pending?.confirmationError
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.`
1307
+ : undefined;
1308
+ }
1309
+ function abandonPendingApprovals(state, pending, reason, timestamp) {
1310
+ return {
1311
+ userAuthorizations: state.userAuthorizations.map((entry) => entry.id === pending.overrideAuthorizationId
1312
+ ? { ...entry, status: "abandoned", abandonedAt: timestamp, abandonReason: reason }
1313
+ : entry),
1314
+ userApprovedFollowUps: state.userApprovedFollowUps.map((entry) => entry.id === pending.followUpApprovalId
1315
+ ? { ...entry, status: "abandoned", abandonedAt: timestamp, abandonReason: reason }
1316
+ : entry)
1317
+ };
1318
+ }
1319
+ async function wasDispatchDelivered(fs, input, messageId) {
1320
+ if (!messageId)
1321
+ return false;
1322
+ const messagesPath = path.join(input.taskRepoRoot, input.stateRoot, "messages", `${input.taskSlug}.jsonl`);
1323
+ if (!(await fs.pathExists(messagesPath)))
1324
+ return false;
1325
+ try {
1326
+ let latest;
1327
+ for (const line of (await fs.readText(messagesPath)).split(/\r?\n/).filter(Boolean)) {
1328
+ const message = JSON.parse(line);
1329
+ if (message.id === messageId)
1330
+ latest = message;
1331
+ }
1332
+ return Boolean(latest?.deliveredAt);
1333
+ }
1334
+ catch {
1335
+ return true;
1336
+ }
1337
+ }
1251
1338
  function createUserAuthorization(authorizationId, current, candidate, effectiveFlow, baseHistoryHash, violation, authorizationText, timestamp) {
1252
1339
  return {
1253
1340
  id: authorizationId,
@@ -1492,6 +1579,8 @@ function isPendingDispatch(value) {
1492
1579
  && (value.status === "pending" || value.status === "dispatching")
1493
1580
  && (value.routeContentHash === undefined || typeof value.routeContentHash === "string")
1494
1581
  && (value.messageId === undefined || typeof value.messageId === "string")
1582
+ && (value.confirmationError === undefined || typeof value.confirmationError === "string")
1583
+ && (value.evidenceBaseline === undefined || Boolean(normalizeDispatchEvidenceBaseline(value.evidenceBaseline)))
1495
1584
  && typeof value.createdAt === "string"
1496
1585
  && typeof value.updatedAt === "string";
1497
1586
  }
@@ -1499,7 +1588,7 @@ function isUserAuthorization(value) {
1499
1588
  if (!isRecord(value))
1500
1589
  return false;
1501
1590
  return typeof value.id === "string"
1502
- && (value.status === "accepted" || value.status === "consumed")
1591
+ && (value.status === "accepted" || value.status === "consumed" || value.status === "abandoned")
1503
1592
  && value.role === "project-manager"
1504
1593
  && value.operation === "workflow-dispatch"
1505
1594
  && Number.isInteger(value.baseRevision)
@@ -1511,13 +1600,15 @@ function isUserAuthorization(value) {
1511
1600
  && typeof value.violatedRule === "string"
1512
1601
  && typeof value.authorizationText === "string"
1513
1602
  && typeof value.createdAt === "string"
1514
- && (value.consumedAt === undefined || typeof value.consumedAt === "string");
1603
+ && (value.consumedAt === undefined || typeof value.consumedAt === "string")
1604
+ && (value.abandonedAt === undefined || typeof value.abandonedAt === "string")
1605
+ && (value.abandonReason === undefined || typeof value.abandonReason === "string");
1515
1606
  }
1516
1607
  function isUserApprovedFollowUp(value) {
1517
1608
  if (!isRecord(value))
1518
1609
  return false;
1519
1610
  return typeof value.id === "string"
1520
- && (value.status === "accepted" || value.status === "consumed")
1611
+ && (value.status === "accepted" || value.status === "consumed" || value.status === "abandoned")
1521
1612
  && value.role === "project-manager"
1522
1613
  && value.operation === "post-validation-follow-up"
1523
1614
  && Number.isInteger(value.baseRevision)
@@ -1529,7 +1620,9 @@ function isUserApprovedFollowUp(value) {
1529
1620
  && typeof value.evidence === "string"
1530
1621
  && typeof value.approvalText === "string"
1531
1622
  && typeof value.createdAt === "string"
1532
- && (value.consumedAt === undefined || typeof value.consumedAt === "string");
1623
+ && (value.consumedAt === undefined || typeof value.consumedAt === "string")
1624
+ && (value.abandonedAt === undefined || typeof value.abandonedAt === "string")
1625
+ && (value.abandonReason === undefined || typeof value.abandonReason === "string");
1533
1626
  }
1534
1627
  function normalizeDispatchEvidenceBaseline(value) {
1535
1628
  if (!isRecord(value) || !isRecord(value.artifactHashes) || !isRecord(value.gateFingerprints))
@@ -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.
@@ -141,7 +141,7 @@ ${renderRoleMemoryRules("architect")}
141
141
  #### Code Scaffolding
142
142
 
143
143
  - Use the Agent tool to invoke \`vcm-architect-scaffold-worker\` in the foreground after the plan and Scaffold Manifest are complete. Give it the exact plan path and require it to return before this Architect turn continues.
144
- - Use one scaffold worker. Do not run it in the background or end the Architect turn while it is active.
144
+ - Run at most one scaffold worker at a time. Do not run it in the background or end the Architect turn while it is active. If its report leaves scaffold items unfinished, verify the completed work and invoke another foreground scaffold worker with the previous report and exact remaining items. Continue until every item is complete or report the concrete failure to project-manager.
145
145
  - Review the worker commit, actual diff, callable surfaces, marker placement, ledger reconciliation, and L0 results yourself. Architect owns every final scaffold claim and must correct any worker error before marking planning complete.
146
146
  - Create or update only the minimum module/file scaffolding needed to make boundaries, callable surfaces, and placeholders unambiguous. Minimum limits depth (no business implementation), never breadth: every \`create\`, \`change\`, and \`delete\` item must be scaffolded.
147
147
  - When a required configuration, package manifest, or build-definition change cannot safely contain a \`VCM:CODE\` marker, complete and commit it directly as Architect-owned scaffold work. Record it in the Module/File Plan and Scaffold Build Evidence. Do not add it to the Scaffold Manifest.
@@ -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.
@@ -12,6 +12,11 @@ planning-session memory candidate and provides an exact path.
12
12
  - If VCM did not provide a path, do not create a proposal.
13
13
  - Propose only verified, durable, reusable project knowledge supported by task
14
14
  evidence.
15
+ - Before proposing an add or update, check current durable documents, Harness
16
+ rules and skills, and the tool's help, usage, or error output. Cite the checked
17
+ source in Evidence. Do not duplicate existing knowledge in memory; propose
18
+ \`durable-doc\` only when the existing document is wrong or incomplete, or use
19
+ \`memory-reference\` when roles need a short pointer across tasks.
15
20
  - Target shared project knowledge to \`shared\`. Target knowledge used only by
16
21
  the current role to \`current-role\`.
17
22
  - For every add or update, explain why the memory is necessary, what future