vibe-coding-master 0.9.7 → 0.9.8

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.
@@ -22,6 +22,9 @@ const ARTIFACT_PATH_KEYS = [
22
22
  ["workflow-progress", "workflowProgressPath"],
23
23
  ["final-acceptance", "finalAcceptancePath"]
24
24
  ];
25
+ // Gate requests whose report VCM still waits for; any other status means the
26
+ // run already ended and a report would be ignored.
27
+ const OPEN_GATE_REQUEST_STATUSES = new Set(["requested", "running"]);
25
28
  const ROLE_COMMAND_PLACEHOLDER_PATTERN = /(^|\n)\s*(TBD|status:\s*draft)\s*(\n|$)/i;
26
29
  const DEFAULT_MESSAGE_ROUTES = [
27
30
  ["project-manager", "architect"],
@@ -331,6 +334,11 @@ async function validateDynamicArtifact(fs, input, content, workflowControlServic
331
334
  `Report path must match the assigned request path ${request.reportPath ?? "<missing>"}.`
332
335
  ]);
333
336
  }
337
+ if (request.status && !OPEN_GATE_REQUEST_STATUSES.has(request.status)) {
338
+ throw artifactRejected(input.kind, [
339
+ `Gate request ${request.requestId ?? requestId} is already ${request.status}, so VCM will not use this report. Stop this review; VCM sends a new request if the gate runs again.`
340
+ ]);
341
+ }
334
342
  assertManagedArtifactContent(input.kind, content, artifactPath, input.mode, {
335
343
  expectedGate: request.gate,
336
344
  expectedRequestId: request.requestId ?? requestId
@@ -1029,7 +1029,11 @@ function normalizeDiagnosticString(value, maxLength) {
1029
1029
  function stringOrUndefined(value) {
1030
1030
  return typeof value === "string" ? value : undefined;
1031
1031
  }
1032
+ // VCM submits its prompts as a terminal paste, and newer Claude Code versions
1033
+ // report a pasted prompt wrapped in <pasted_content id="..."> tags, so the
1034
+ // wrapper is removed before looking for VCM's marker.
1035
+ const PASTED_CONTENT_OPEN_TAG = /^<pasted_content\b[^>]*>/i;
1032
1036
  function isDirectUserPrompt(prompt) {
1033
- const normalized = prompt.trim();
1037
+ const normalized = prompt.trim().replace(PASTED_CONTENT_OPEN_TAG, "").trim();
1034
1038
  return normalized.length > 0 && !/^\[VCM(?:\s|\])/i.test(normalized);
1035
1039
  }
@@ -15,6 +15,9 @@ const GATE_REVIEW_VERSION = 1;
15
15
  const HARNESS_COMMIT_PREFIX = "[VCM Harness] ";
16
16
  const REVIEWER_ROLE = "reviewer";
17
17
  const DEFAULT_REPORT_POLL_INTERVAL_MS = 1000;
18
+ // A Reviewer that just submitted a report usually ends its turn within
19
+ // seconds; a missed Stop hook leaves the activity stale, so the wait is capped.
20
+ const DEFAULT_REVIEWER_IDLE_WAIT_MS = 5 * 60 * 1000;
18
21
  const activeRuns = new Map();
19
22
  const gateStateLocks = new Map();
20
23
  const SOURCE_ARTIFACTS = {
@@ -54,6 +57,7 @@ const VALID_CODE_DIFF_FINDING_SCOPES = new Set(CODE_DIFF_FINDING_SCOPES);
54
57
  export function createGateReviewService(deps) {
55
58
  const now = deps.now ?? (() => new Date().toISOString());
56
59
  const reportPollIntervalMs = deps.reportPollIntervalMs ?? DEFAULT_REPORT_POLL_INTERVAL_MS;
60
+ const reviewerIdleWaitMs = deps.reviewerIdleWaitMs ?? DEFAULT_REVIEWER_IDLE_WAIT_MS;
57
61
  async function getContext(repoRoot, taskSlug) {
58
62
  const projectConfig = await deps.projectService.loadConfig(repoRoot);
59
63
  const task = await deps.taskService.loadTask(repoRoot, taskSlug);
@@ -529,6 +533,7 @@ export function createGateReviewService(deps) {
529
533
  });
530
534
  }
531
535
  const session = await ensureReviewerSession(context);
536
+ await waitForReviewerIdle(context, controller.signal);
532
537
  throwIfGateRunCancelled(controller.signal);
533
538
  await submitTerminalInput(deps.runtime, session.id, prompt);
534
539
  await deps.sessionService.markRoleActivityRunning(context.repoRoot, context.taskSlug, REVIEWER_ROLE, session.id);
@@ -647,6 +652,20 @@ export function createGateReviewService(deps) {
647
652
  return next.gates[gate];
648
653
  });
649
654
  }
655
+ // Claude Code queues a prompt typed into a running turn but sends its
656
+ // UserPromptSubmit hook at once, so the running turn's Stop would look like
657
+ // the end of this request's turn and fail it. Wait for that turn to end.
658
+ async function waitForReviewerIdle(context, signal) {
659
+ const deadline = Date.now() + reviewerIdleWaitMs;
660
+ while (true) {
661
+ throwIfGateRunCancelled(signal);
662
+ const session = await deps.sessionService.getRoleSession(context.repoRoot, context.taskSlug, REVIEWER_ROLE);
663
+ if (session?.activityStatus !== "running" || Date.now() >= deadline) {
664
+ return;
665
+ }
666
+ await delay(reportPollIntervalMs);
667
+ }
668
+ }
650
669
  async function ensureReviewerSession(context) {
651
670
  const existing = await deps.sessionService.getRoleSession(context.repoRoot, context.taskSlug, REVIEWER_ROLE);
652
671
  if (existing?.status === "running" && deps.runtime.getSession(existing.id)) {
@@ -833,9 +833,12 @@ async function allowedArchitectFix(fs, input, state, segment, source, resumedFro
833
833
  return [`${source}/architect`];
834
834
  }
835
835
  // A new fix needs validation; any other post-validation Architect change
836
- // is reviewed by an incremental code-diff Gate.
837
- if (artifactFresh && artifact.complete)
836
+ // is reviewed by an incremental code-diff Gate. Architect also rewrites its
837
+ // artifact for a docs correction, so once a code-diff Gate has reviewed
838
+ // this dispatch, that Gate's result routes the flow instead.
839
+ if (artifactFresh && artifact.complete && !(await codeDiffReviewedDispatch(fs, input, state, source))) {
838
840
  return [`${source}/tester`];
841
+ }
839
842
  return allowedPostImplementationArchitect(fs, input, state, source, acceptanceFlow);
840
843
  }
841
844
  return allowedAfterTester(fs, input, state, source, source, {
@@ -843,6 +846,18 @@ async function allowedArchitectFix(fs, input, state, segment, source, resumedFro
843
846
  acceptanceFlow
844
847
  });
845
848
  }
849
+ // True when a code-diff Gate finished, or was skipped or overridden, after the
850
+ // active Architect dispatch was confirmed.
851
+ async function codeDiffReviewedDispatch(fs, input, state, flow) {
852
+ if (!matchingEvidenceBaseline(state, flow, "architect"))
853
+ return false;
854
+ const codeDiff = await gateState(fs, input, "code-diff");
855
+ // A disabled Gate reviews nothing, so it never stands in for Tester.
856
+ if (!codeDiff || codeDiff.status === "disabled")
857
+ return false;
858
+ return (codeDiff.status === "completed" && freshGateDecision(state, flow, "architect", "code-diff", codeDiff) !== undefined)
859
+ || gatePassedForDispatch(state, flow, "architect", "code-diff", codeDiff);
860
+ }
846
861
  async function allowedPostImplementationArchitect(fs, input, state, flow, acceptanceFlow) {
847
862
  if (flow === "code-change") {
848
863
  const planningTransition = await allowedFreshArchitecturePlan(fs, input, state, matchingEvidenceBaseline(state, "code-change", "architect")?.confirmedAt);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibe-coding-master",
3
- "version": "0.9.7",
3
+ "version": "0.9.8",
4
4
  "description": "Local GUI session cockpit for Claude Code role sessions.",
5
5
  "type": "module",
6
6
  "files": [