vibe-coding-master 0.9.10 → 0.9.11

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.
@@ -276,7 +276,8 @@ export function createDefaultServerDeps(options = {}) {
276
276
  const artifactService = createArtifactService(fs, {
277
277
  workflowControlService,
278
278
  getDurableDocAssignment: (taskRepoRoot, role) => autoMemoryService.getDurableDocAssignment(taskRepoRoot, role),
279
- getCompletedDurableDocAssignment: (taskRepoRoot, role, artifactPath) => autoMemoryService.getCompletedDurableDocAssignment(taskRepoRoot, role, artifactPath)
279
+ getCompletedDurableDocAssignment: (taskRepoRoot, role, artifactPath) => autoMemoryService.getCompletedDurableDocAssignment(taskRepoRoot, role, artifactPath),
280
+ checkDurableDocReport: (taskRepoRoot, assignment, reportContent) => autoMemoryService.checkDurableDocAssignmentReport(taskRepoRoot, assignment, reportContent)
280
281
  });
281
282
  const projectService = createProjectService({
282
283
  fs,
@@ -240,6 +240,12 @@ export function createArtifactService(fs, deps = {}) {
240
240
  if (validation.errors.length > 0) {
241
241
  throw artifactRejected(input.kind, validation.errors);
242
242
  }
243
+ if (assignment && input.mode === "final") {
244
+ const problem = await deps.checkDurableDocReport?.(input.repoRoot, assignment, normalized);
245
+ if (problem) {
246
+ throw artifactRejected(input.kind, [problem]);
247
+ }
248
+ }
243
249
  if (input.kind === "workflow-progress") {
244
250
  if (input.mode !== "final") {
245
251
  throw artifactRejected(input.kind, ["Workflow Progress must be submitted in final mode."]);
@@ -1,7 +1,7 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import path from "node:path";
3
3
  import { checkMarkdownArtifact, readArtifactSectionContent, readArtifactSectionValue } from "../../shared/validation/artifact-check.js";
4
- import { DOCS_UPDATE_COMMIT_RULE } from "../../shared/validation/artifact-contract.js";
4
+ import { DOCS_UPDATE_COMMIT_RULE, durableDocCommitSubjectPrefix } from "../../shared/validation/artifact-contract.js";
5
5
  import { resolveRepoPath } from "../adapters/filesystem.js";
6
6
  import { VcmError } from "../errors.js";
7
7
  import { submitTerminalInput } from "../runtime/terminal-submit.js";
@@ -383,6 +383,18 @@ export function createAutoMemoryService(deps) {
383
383
  const assignment = state?.status === "documenting" ? currentDurableDocAssignment(state) : undefined;
384
384
  return assignment?.status === "running" && assignment.owner === role ? assignment : undefined;
385
385
  }
386
+ async function checkDurableDocAssignmentReport(taskRepoRoot, assignment, reportContent) {
387
+ if (readArtifactSectionValue(reportContent, "Decision")?.trim().toLowerCase() !== "synced") {
388
+ return undefined;
389
+ }
390
+ try {
391
+ await validateDurableDocCommit(taskRepoRoot, assignment, reportContent, await deps.git.getHeadCommit(taskRepoRoot));
392
+ return undefined;
393
+ }
394
+ catch (error) {
395
+ return errorMessage(error);
396
+ }
397
+ }
386
398
  async function getCompletedDurableDocAssignment(taskRepoRoot, role, artifactPath) {
387
399
  const runId = artifactPath.startsWith(`${MEMORY_REVIEW_RUNS_ROOT}/`)
388
400
  ? artifactPath.slice(MEMORY_REVIEW_RUNS_ROOT.length + 1).split("/")[0]
@@ -893,8 +905,9 @@ export function createAutoMemoryService(deps) {
893
905
  async function validateDurableDocCommit(taskRepoRoot, assignment, reportContent, currentHead) {
894
906
  const reportedCommit = readArtifactSectionContent(reportContent, "Commit");
895
907
  let commit;
908
+ let subject;
896
909
  try {
897
- commit = (await deps.git.getCommitInfo(taskRepoRoot, reportedCommit)).sha;
910
+ ({ sha: commit, subject } = await deps.git.getCommitInfo(taskRepoRoot, reportedCommit));
898
911
  }
899
912
  catch (error) {
900
913
  const detail = error instanceof VcmError && error.hint ? `${errorMessage(error)} ${error.hint}` : errorMessage(error);
@@ -919,6 +932,10 @@ export function createAutoMemoryService(deps) {
919
932
  if (!changedPaths.includes(assignment.targetPath)) {
920
933
  throw new Error(`Commit ${commit} modified ${assignment.targetPath}, but no net change remains in ${assignment.baseCommit}..${currentHead}. Verify the current document before reporting completion.`);
921
934
  }
935
+ const subjectPrefix = durableDocCommitSubjectPrefix(assignment.id);
936
+ if (!subject.startsWith(subjectPrefix)) {
937
+ throw new Error(`Commit ${commit} subject must start with "${subjectPrefix}"; found "${subject}". Reword the commit (git commit --amend when it is the latest commit) and report it again.`);
938
+ }
922
939
  return commit;
923
940
  }
924
941
  async function hashOptionalFile(taskRepoRoot, relativePath) {
@@ -1511,6 +1528,7 @@ export function createAutoMemoryService(deps) {
1511
1528
  isRoleMemoryTurn,
1512
1529
  getDurableDocAssignment,
1513
1530
  getCompletedDurableDocAssignment,
1531
+ checkDurableDocAssignmentReport,
1514
1532
  handleRoleHook: (input) => withTaskLock(input.taskRepoRoot, () => handleRoleHook(input)),
1515
1533
  handleHarnessEngineerHook: (input) => withTaskLock(input.taskRepoRoot, () => handleHarnessEngineerHook(input)),
1516
1534
  assertHarnessEngineerAvailable
@@ -1619,6 +1637,7 @@ function buildDurableDocAssignmentPrompt(taskRepoRoot, assignment) {
1619
1637
  ...assignment.evidence.map((item) => `- ${item}`),
1620
1638
  "",
1621
1639
  "Verify the content, update the target and any directly related durable documentation, run applicable documentation checks, and commit the documentation changes.",
1640
+ `Use the commit subject "${durableDocCommitSubjectPrefix(assignment.id)} <summary>" so Project Manager can tell this post-acceptance documentation commit apart; VCM rejects a synced report whose commit has another subject.`,
1622
1641
  `Assigned report path: ${assignment.reportPath}`,
1623
1642
  `Submit with .ai/tools/vcm-artifact docs-update-report --file <candidate> --path ${assignment.reportPath} --mode final`,
1624
1643
  "Set Assignment ID to the exact ID above.",
@@ -820,7 +820,9 @@ async function allowedArchitectFix(fs, input, state, segment, source, resumedFro
820
820
  if (source === "architect-debug" && artifact.disposition === ARCHITECT_DEBUG_NORMAL_PLAN_DISPOSITION) {
821
821
  return ["code-change/architect"];
822
822
  }
823
- if (source === "architecture-diagnosis" && artifact.disposition === "analysis completed") {
823
+ // Only a completed analysis-only Diagnosis returns to its parent; a pending
824
+ // or blocked one sends the same Architect back.
825
+ if (source === "architecture-diagnosis" && artifact.complete && artifact.disposition === "analysis completed") {
824
826
  return parentFlow ? [`${parentFlow}/architect`] : [];
825
827
  }
826
828
  if (artifact.disposition === "user clarification required") {
@@ -954,7 +956,7 @@ async function artifactState(fs, input, fileName, kind) {
954
956
  if (kind === "architect-debug")
955
957
  value = inline("Status") ?? readArtifactSectionContent(content, "Final Disposition")?.trim().toLowerCase();
956
958
  if (kind === "architecture-diagnosis")
957
- value = readArtifactSectionContent(content, "Final Disposition")?.trim().toLowerCase();
959
+ value = inline("Status") ?? readArtifactSectionContent(content, "Final Disposition")?.trim().toLowerCase();
958
960
  if (kind === "test-report")
959
961
  value = inline("Test Result");
960
962
  if (kind === "docs-update-report" || kind === "final-acceptance") {
@@ -1190,7 +1192,10 @@ async function validateCompletion(fs, input, state, candidate) {
1190
1192
  const inBranch = flowRun.activeBranch === candidate.flow;
1191
1193
  if (candidate.flow === "architecture-diagnosis" && !inBranch) {
1192
1194
  const diagnosis = await artifactState(fs, input, "architecture-diagnosis.md", "architecture-diagnosis");
1193
- if (diagnosis.disposition === "analysis completed"
1195
+ // The final contract rejects analysis completed with a commit or changed
1196
+ // files, so a completed analysis-only Diagnosis changed no code.
1197
+ if (diagnosis.complete
1198
+ && diagnosis.disposition === "analysis completed"
1194
1199
  && evidenceProducedAfterActiveDispatch(state, candidate.flow, "architect", "architecture-diagnosis.md", diagnosis.hash))
1195
1200
  return;
1196
1201
  }
@@ -1,4 +1,4 @@
1
- import { ARCHITECT_DEBUG_DISPOSITIONS, ARCHITECT_DEBUG_STATUSES, ARCHITECTURE_BRIEF_STATUSES, ARCHITECTURE_DIAGNOSIS_DISPOSITIONS, ARCHITECTURE_EVIDENCE_STATUSES, ARCHITECTURE_PLAN_RESULTS, CODER_COMPLETION_DECISIONS, DOCS_REPORT_DECISIONS, DOCS_UPDATE_COMMIT_RULE, FINAL_ACCEPTANCE_DECISIONS, L3_ACTIONS, L3_REQUIRED_VALUES, PLANNING_PROGRESS_STATUSES, STRICT_NONE_VALUE, TEST_INFRASTRUCTURE_STATUSES, TEST_RESULTS, renderArtifactOptions } from "../../shared/validation/artifact-contract.js";
1
+ import { ARCHITECT_DEBUG_DISPOSITIONS, ARCHITECT_DEBUG_STATUSES, ARCHITECTURE_BRIEF_STATUSES, ARCHITECTURE_DIAGNOSIS_DISPOSITIONS, ARCHITECTURE_DIAGNOSIS_STATUSES, ARCHITECTURE_EVIDENCE_STATUSES, ARCHITECTURE_PLAN_RESULTS, CODER_COMPLETION_DECISIONS, DOCS_REPORT_DECISIONS, DOCS_UPDATE_COMMIT_RULE, FINAL_ACCEPTANCE_DECISIONS, L3_ACTIONS, L3_REQUIRED_VALUES, PLANNING_PROGRESS_STATUSES, STRICT_NONE_VALUE, TEST_INFRASTRUCTURE_STATUSES, TEST_RESULTS, renderArtifactOptions } from "../../shared/validation/artifact-contract.js";
2
2
  const CURRENT_HANDOFF_NOTICE = "<!-- VCM current handoff: replace this file with one complete, self-contained snapshot of the current result. Restate all still-relevant evidence; do not refer to a prior revision, route message, Session, or transcript as evidence. -->";
3
3
  export function renderArchitectureBriefTemplate(taskSlug) {
4
4
  return `# Architecture Brief: ${taskSlug}
@@ -417,6 +417,8 @@ export function renderArchitectureDiagnosisTemplate(taskSlug) {
417
417
 
418
418
  ${CURRENT_HANDOFF_NOTICE}
419
419
 
420
+ Status: ${renderArtifactOptions(ARCHITECTURE_DIAGNOSIS_STATUSES)}
421
+
420
422
  ## Diagnosis Boundary
421
423
 
422
424
  TBD
@@ -286,9 +286,11 @@ Each rewritten \`architecture-diagnosis.md\` must be a complete, self-contained
286
286
  - Before reporting \`diagnosis implementation completed\`, run every existing L2/L3 check applicable to the diagnosed failure path.
287
287
  - Every applicable L2/L3 check must pass. If a level is not applicable, record the concrete reason.
288
288
  - If an applicable L2/L3 check is unavailable or cannot complete, do not report \`diagnosis implementation completed\`; report the exact blocker.
289
+ - Keep \`Status: pending\` while the Diagnosis is in progress or blocked, and record the exact blocker under \`Implementation And Validation\`. Set \`Status: completed\` only with a final disposition.
289
290
  - Under \`Implementation And Validation\`, record each L2/L3 command or test case, the diagnosed failure path it covers, its result, and its evidence.
290
291
  - Architect-run Diagnosis validation is implementation evidence and does not replace Tester final validation.
291
292
  - Final disposition must be one of: \`analysis completed\`, \`diagnosis implementation completed\`, or \`user clarification required\`.
293
+ - \`analysis completed\` means the Diagnosis changed no code: \`Changed Files And Public Surface\` and \`Commit\` are exactly \`None.\`. A Diagnosis that changed or committed code reports \`diagnosis implementation completed\`, or stays \`Status: pending\` while blocked.
292
294
 
293
295
  ### Replan And Drift
294
296
 
@@ -66,6 +66,7 @@ If a reusable harness problem is suspected, it is enough to record a concise fee
66
66
  - Communication-Only Flow uses: \`project-manager response or relay -> completion\`.
67
67
  - Gate Review is PM-triggered at its defined trigger points; the tool decides whether review is enabled or required.
68
68
  - Ignore commits whose subject starts with \`[VCM Harness]\`. Do not review, attribute, or route those commits unless they cause a test failure. A test failure caused by a Harness commit follows the normal test-failure flow.
69
+ - Commits whose subject starts with \`[VCM Docs]\` are durable-documentation updates that VCM assigned from a memory review after Final Acceptance. They belong to the task and its PR; do not review, re-gate, or route them.
69
70
  - Final acceptance closes only a complete code-delivery flow. After a Debug or Diagnosis Branch passes its code-diff Gate, final acceptance closes the parent flow it returned to, never the branch alone.
70
71
  - PR-Preparation Flow starts only after the active delivery flow completes; every complete code-delivery flow requires final acceptance to pass.
71
72
  - If Docs-Only Flow or Validation-Only Flow reveals that the accepted outcome requires production-code, runtime-behavior, public-contract, dependency, or system-architecture changes, project-manager routes through the full Code-Change Flow.
@@ -401,6 +401,7 @@ If required work or evidence is incomplete, return to the responsible flow or ro
401
401
  PR-Preparation Flow does not perform technical review, validation, documentation updates, Gate Review, or Final Acceptance.
402
402
 
403
403
  - Create a draft PR by default unless the user requests a ready PR.
404
+ - Commits after the HEAD the latest code-diff Gate approved: \`[VCM Harness]\` and \`[VCM Docs]\` commits belong to the PR without another Gate. List each \`[VCM Docs]\` commit with its assignment ID (the subject's \`[VCM Docs] <assignment-id>:\`) in the PR description. Any other commit after that HEAD is unreviewed task work: return it to the responsible flow before preparing the PR.
404
405
 
405
406
  ### Background Jobs
406
407
 
@@ -1,4 +1,4 @@
1
- import { ARCHITECT_DEBUG_DISPOSITIONS, ARCHITECT_DEBUG_STATUSES, ARCHITECTURE_BRIEF_STATUSES, ARCHITECTURE_DIAGNOSIS_DISPOSITIONS, ARCHITECTURE_EVIDENCE_STATUSES, ARCHITECTURE_PLAN_RESULTS, CODER_COMPLETION_DECISIONS, DOCS_REPORT_DECISIONS, DOCS_UPDATE_COMMIT_PATTERN, DOCS_UPDATE_COMMIT_RULE, FINAL_ACCEPTANCE_DECISIONS, L3_ACTIONS, L3_REQUIRED_VALUES, PLANNING_PROGRESS_STATUSES, STRICT_NONE_VALUE, TEST_INFRASTRUCTURE_STATUSES, TEST_RESULTS } from "./artifact-contract.js";
1
+ import { ARCHITECT_DEBUG_DISPOSITIONS, ARCHITECT_DEBUG_STATUSES, ARCHITECTURE_BRIEF_STATUSES, ARCHITECTURE_DIAGNOSIS_ANALYSIS_ONLY_DISPOSITION, ARCHITECTURE_DIAGNOSIS_ANALYSIS_ONLY_SECTIONS, ARCHITECTURE_DIAGNOSIS_DISPOSITIONS, ARCHITECTURE_DIAGNOSIS_STATUSES, ARCHITECTURE_EVIDENCE_STATUSES, ARCHITECTURE_PLAN_RESULTS, CODER_COMPLETION_DECISIONS, DOCS_REPORT_DECISIONS, DOCS_UPDATE_COMMIT_PATTERN, DOCS_UPDATE_COMMIT_RULE, FINAL_ACCEPTANCE_DECISIONS, L3_ACTIONS, L3_REQUIRED_VALUES, PLANNING_PROGRESS_STATUSES, STRICT_NONE_VALUE, TEST_INFRASTRUCTURE_STATUSES, TEST_RESULTS } from "./artifact-contract.js";
2
2
  import { getArtifactDefinition } from "./artifact-registry.js";
3
3
  const PLACEHOLDER_PATTERN = /(^|\n)\s*(TBD|Not run yet\.?|status:\s*draft)\s*(\n|$)/i;
4
4
  export function checkMarkdownArtifact(kind, artifactPath, content, options = {}) {
@@ -104,10 +104,24 @@ function validateArtifactFields(kind, content, mode) {
104
104
  return invalidFields;
105
105
  }
106
106
  if (kind === "architecture-diagnosis") {
107
- const disposition = readArtifactSectionContent(content, "Final Disposition")?.trim().toLowerCase();
108
- return isAllowedValue(disposition, ARCHITECTURE_DIAGNOSIS_DISPOSITIONS)
109
- ? []
110
- : [renderExactSectionError("Final Disposition", ARCHITECTURE_DIAGNOSIS_DISPOSITIONS.join("|"), disposition)];
107
+ // Like architect-debug, an in-progress or blocked Diagnosis stays
108
+ // `Status: pending`; only a completed one carries a Final Disposition.
109
+ const invalidFields = validateLifecycleField(content, "Status", ARCHITECTURE_DIAGNOSIS_STATUSES, "completed", mode);
110
+ if (mode === "final") {
111
+ const disposition = readArtifactSectionContent(content, "Final Disposition")?.trim().toLowerCase();
112
+ if (!isAllowedValue(disposition, ARCHITECTURE_DIAGNOSIS_DISPOSITIONS)) {
113
+ invalidFields.push(renderExactSectionError("Final Disposition", ARCHITECTURE_DIAGNOSIS_DISPOSITIONS.join("|"), disposition));
114
+ }
115
+ else if (disposition === ARCHITECTURE_DIAGNOSIS_ANALYSIS_ONLY_DISPOSITION) {
116
+ for (const section of ARCHITECTURE_DIAGNOSIS_ANALYSIS_ONLY_SECTIONS) {
117
+ const value = readArtifactSectionContent(content, section)?.trim();
118
+ if (value !== STRICT_NONE_VALUE) {
119
+ invalidFields.push(`${section} must be exactly "${STRICT_NONE_VALUE}" for analysis completed; a Diagnosis that changed or committed code reports diagnosis implementation completed, or stays Status: pending while blocked.`);
120
+ }
121
+ }
122
+ }
123
+ }
124
+ return invalidFields;
111
125
  }
112
126
  if (kind === "test-report") {
113
127
  const result = readInlineField(content, "Test Result");
@@ -15,6 +15,11 @@ export const ARCHITECT_DEBUG_DISPOSITIONS = [
15
15
  ARCHITECT_DEBUG_NORMAL_PLAN_DISPOSITION,
16
16
  "user clarification required"
17
17
  ];
18
+ export const ARCHITECTURE_DIAGNOSIS_STATUSES = ["pending", "completed"];
19
+ export const ARCHITECTURE_DIAGNOSIS_ANALYSIS_ONLY_DISPOSITION = "analysis completed";
20
+ // An analysis-only Diagnosis changes no code: these sections must be exactly
21
+ // "None." for its final disposition.
22
+ export const ARCHITECTURE_DIAGNOSIS_ANALYSIS_ONLY_SECTIONS = ["Changed Files And Public Surface", "Commit"];
18
23
  export const ARCHITECTURE_DIAGNOSIS_DISPOSITIONS = [
19
24
  "analysis completed",
20
25
  "diagnosis implementation completed",
@@ -31,6 +36,12 @@ export const L3_REQUIRED_VALUES = ["yes", "no"];
31
36
  export const L3_ACTIONS = ["run-existing", "updated", "added"];
32
37
  export const DOCS_REPORT_DECISIONS = ["synced", "unchanged", "blocked"];
33
38
  export const DOCS_UPDATE_COMMIT_PATTERN = /^[0-9a-f]{7,40}$/;
39
+ // Subject prefix of the commits a durable-documentation assignment makes, so
40
+ // Project Manager can tell these post-acceptance documentation commits apart.
41
+ export const DURABLE_DOC_COMMIT_PREFIX = "[VCM Docs]";
42
+ export function durableDocCommitSubjectPrefix(assignmentId) {
43
+ return `${DURABLE_DOC_COMMIT_PREFIX} ${assignmentId}:`;
44
+ }
34
45
  export const DOCS_UPDATE_COMMIT_RULE = "When Decision is synced, Commit must contain only one bare lowercase hexadecimal commit hash (7-40 characters, for example abc1234) on a single line. No backticks, bullets, links, subjects, or extra lines; put explanations in Evidence Reviewed. An unchanged result does not require a new commit.";
35
46
  export const FINAL_ACCEPTANCE_DECISIONS = [
36
47
  "accepted",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibe-coding-master",
3
- "version": "0.9.10",
3
+ "version": "0.9.11",
4
4
  "description": "Local GUI session cockpit for Claude Code role sessions.",
5
5
  "type": "module",
6
6
  "files": [