vibe-coding-master 0.7.5 → 0.7.7

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 (55) hide show
  1. package/README.md +40 -16
  2. package/dist/backend/adapters/git-adapter.js +15 -0
  3. package/dist/backend/api/artifact-routes.js +3 -0
  4. package/dist/backend/api/harness-routes.js +57 -27
  5. package/dist/backend/api/runtime-state-routes.js +7 -3
  6. package/dist/backend/api/task-routes.js +36 -4
  7. package/dist/backend/api/translation-routes.js +11 -2
  8. package/dist/backend/api/translation-worker-routes.js +37 -9
  9. package/dist/backend/cli/install-vcm-harness.js +40 -2
  10. package/dist/backend/gateway/gateway-service.js +34 -17
  11. package/dist/backend/server.js +12 -3
  12. package/dist/backend/services/artifact-service.js +4 -1
  13. package/dist/backend/services/auto-memory-service.js +156 -81
  14. package/dist/backend/services/claude-hook-service.js +50 -35
  15. package/dist/backend/services/command-dispatcher.js +1 -1
  16. package/dist/backend/services/gate-review-service.js +44 -0
  17. package/dist/backend/services/harness-feedback-service.js +47 -8
  18. package/dist/backend/services/harness-service.js +112 -34
  19. package/dist/backend/services/message-service.js +39 -2
  20. package/dist/backend/services/round-service.js +10 -121
  21. package/dist/backend/services/runtime-coordinator-service.js +18 -10
  22. package/dist/backend/services/runtime-recovery-service.js +1 -2
  23. package/dist/backend/services/session-service.js +36 -98
  24. package/dist/backend/services/status-service.js +1 -0
  25. package/dist/backend/services/task-close-service.js +12 -27
  26. package/dist/backend/services/task-workflow-service.js +228 -0
  27. package/dist/backend/services/translation-worker-service.js +14 -7
  28. package/dist/backend/templates/handoff.js +40 -1
  29. package/dist/backend/templates/harness/architect-agent.js +67 -19
  30. package/dist/backend/templates/harness/claude-root.js +25 -29
  31. package/dist/backend/templates/harness/gate-review.js +26 -13
  32. package/dist/backend/templates/harness/harness-engineer-agent.js +8 -8
  33. package/dist/backend/templates/harness/memory-block.js +69 -0
  34. package/dist/backend/templates/harness/project-known-issues.js +1 -0
  35. package/dist/backend/templates/harness/project-manager-agent.js +211 -73
  36. package/dist/backend/templates/harness/role-memory.js +9 -12
  37. package/dist/backend/templates/harness/tester-agent.js +4 -1
  38. package/dist/backend/templates/harness/vcm-architecture-interview-skill.js +82 -0
  39. package/dist/backend/templates/harness/vcm-final-acceptance-skill.js +4 -3
  40. package/dist/backend/templates/harness/vcm-harness-bootstrap-skill.js +14 -3
  41. package/dist/backend/templates/harness/vcm-propose-memory-skill.js +2 -2
  42. package/dist/backend/templates/harness/vcm-route-message-skill.js +5 -0
  43. package/dist/backend/templates/harness/vcm-task-state-skill.js +110 -0
  44. package/dist/backend/templates/message-envelope.js +1 -1
  45. package/dist/shared/constants.js +0 -10
  46. package/dist/shared/types/workflow.js +1 -0
  47. package/dist/shared/validation/artifact-check.js +20 -0
  48. package/dist-frontend/assets/index-42EpETgd.js +97 -0
  49. package/dist-frontend/assets/index-D65x2x0F.css +32 -0
  50. package/dist-frontend/index.html +2 -2
  51. package/package.json +1 -1
  52. package/scripts/harness-tools/check-durable-docs +298 -0
  53. package/scripts/verify-package.mjs +1 -0
  54. package/dist-frontend/assets/index-DCb-S6Ls.css +0 -32
  55. package/dist-frontend/assets/index-NTlycxx9.js +0 -97
@@ -16,6 +16,7 @@ const DEFAULT_REPORT_TIMEOUT_MS = 30 * 60 * 1000;
16
16
  const activeRuns = new Set();
17
17
  const ARCHITECTURE_ANALYSIS_FIELDS = [
18
18
  "Evidence Read",
19
+ "Architecture Brief Fit",
19
20
  "End-To-End Flow",
20
21
  "Scope Fit",
21
22
  "Code Reality",
@@ -54,6 +55,7 @@ const CODE_DIFF_ANALYSIS_FIELDS = [
54
55
  ];
55
56
  const SOURCE_ARTIFACTS = {
56
57
  "architecture-plan": [
58
+ ".ai/vcm/handoffs/architecture-brief.md",
57
59
  ".ai/vcm/handoffs/architecture-plan.md"
58
60
  ],
59
61
  "validation-adequacy": [
@@ -201,6 +203,32 @@ export function createGateReviewService(deps) {
201
203
  };
202
204
  }
203
205
  }
206
+ if (gate === "architecture-plan") {
207
+ const architectureBriefError = await readArchitectureBriefError(deps.fs, context.taskRepoRoot);
208
+ if (architectureBriefError) {
209
+ index = applyGateState(index, gate, {
210
+ status: "failed",
211
+ decision: undefined,
212
+ error: architectureBriefError,
213
+ exceptionReason: undefined,
214
+ requestId: undefined,
215
+ requestPath: undefined,
216
+ inputHash: undefined,
217
+ requestedAt: undefined,
218
+ startedAt: undefined,
219
+ completedAt: now(),
220
+ callbackStatus: "not_sent",
221
+ callbackError: undefined
222
+ }, now(), true);
223
+ await saveIndex(deps.fs, context.taskRepoRoot, index);
224
+ return {
225
+ status: "failed_to_start",
226
+ gate,
227
+ record: index.gates[gate],
228
+ message: architectureBriefError
229
+ };
230
+ }
231
+ }
204
232
  const coreInput = await readCoreInputArtifact(deps.fs, context.taskRepoRoot, gate);
205
233
  if (coreInput && coreInput.status !== "ready") {
206
234
  index = applyGateState(index, gate, {
@@ -917,6 +945,22 @@ async function readCoreInputArtifact(fs, taskRepoRoot, gate) {
917
945
  }
918
946
  return { path: relativePath, status: "ready" };
919
947
  }
948
+ async function readArchitectureBriefError(fs, taskRepoRoot) {
949
+ const relativePath = ".ai/vcm/handoffs/architecture-brief.md";
950
+ const absolutePath = resolveRepoPath(taskRepoRoot, relativePath);
951
+ if (!await fs.pathExists(absolutePath)) {
952
+ return `${relativePath} is missing. Complete Architect Interview before architecture planning.`;
953
+ }
954
+ const content = await fs.readText(absolutePath);
955
+ const check = checkMarkdownArtifact("architecture-brief", relativePath, content);
956
+ if (check.status !== "ok") {
957
+ return `${relativePath} is incomplete. Complete and confirm Architect Interview before requesting architecture-plan review.`;
958
+ }
959
+ if (!/^\s*Architecture Brief Status\s*:\s*confirmed\s*$/im.test(content)) {
960
+ return `${relativePath} is not confirmed. Obtain explicit user confirmation before architecture planning.`;
961
+ }
962
+ return undefined;
963
+ }
920
964
  async function commandStdout(runner, cwd, args) {
921
965
  const result = await runner.run("git", args, { cwd });
922
966
  return result.exitCode === 0 ? result.stdout : "";
@@ -21,6 +21,23 @@ export function createHarnessFeedbackService(deps) {
21
21
  warnings: []
22
22
  };
23
23
  }
24
+ async function sendPendingFeedback(repoRoot, input) {
25
+ await cleanupLegacyState(repoRoot);
26
+ const feedbackPath = input.feedbackPath.trim();
27
+ const pending = await listPendingFeedback(repoRoot);
28
+ const feedback = pending.find((item) => item.path === feedbackPath);
29
+ if (!feedback) {
30
+ throw new VcmError({
31
+ code: "HARNESS_FEEDBACK_NOT_PENDING",
32
+ message: "The selected Harness Feedback is no longer pending.",
33
+ statusCode: 404,
34
+ hint: "Refresh Harness Studio and select a feedback item that is still listed in the Inbox."
35
+ });
36
+ }
37
+ const session = await ensureIdleHarnessEngineer(repoRoot, input.taskSlug);
38
+ await submitTerminalInput(deps.runtime, session.id, buildPendingFeedbackPrompt(repoRoot, feedback.path));
39
+ return session;
40
+ }
24
41
  async function startTaskRetrospective(repoRoot, input) {
25
42
  await cleanupLegacyState(repoRoot);
26
43
  const taskSlug = input.taskSlug.trim();
@@ -76,8 +93,8 @@ export function createHarnessFeedbackService(deps) {
76
93
  async function assertHarnessEngineerAvailable(_repoRoot) {
77
94
  return undefined;
78
95
  }
79
- async function ensureIdleHarnessEngineer(repoRoot, taskSlug) {
80
- const existing = await deps.sessionService.getProjectHarnessEngineerSession(repoRoot);
96
+ async function getIdleHarnessEngineer(repoRoot, taskSlug) {
97
+ const existing = await deps.sessionService.getRoleSession(repoRoot, taskSlug, "harness-engineer");
81
98
  if (existing?.status === "running" && existing.activityStatus === "running") {
82
99
  throw new VcmError({
83
100
  code: "HARNESS_ENGINEER_BUSY",
@@ -86,12 +103,23 @@ export function createHarnessFeedbackService(deps) {
86
103
  hint: "Wait for the current Harness Engineer turn to finish, then retry."
87
104
  });
88
105
  }
89
- const session = await deps.sessionService.ensureProjectHarnessEngineerSession(repoRoot, {
90
- taskSlug,
91
- cols: 120,
92
- rows: 32
93
- });
94
- if (session.status !== "running" || session.activityStatus === "running" || !deps.runtime.getSession(session.id)) {
106
+ const input = { cols: 120, rows: 32 };
107
+ const session = existing?.status === "running"
108
+ ? existing
109
+ : existing?.claudeSessionId
110
+ ? await deps.sessionService.resumeRoleSession(repoRoot, taskSlug, "harness-engineer", input)
111
+ : await deps.sessionService.startRoleSession(repoRoot, taskSlug, "harness-engineer", input);
112
+ if (session.status !== "running" || session.activityStatus === "running") {
113
+ return undefined;
114
+ }
115
+ if (!deps.runtime.getSession(session.id)) {
116
+ return undefined;
117
+ }
118
+ return session;
119
+ }
120
+ async function ensureIdleHarnessEngineer(repoRoot, taskSlug) {
121
+ const session = await getIdleHarnessEngineer(repoRoot, taskSlug);
122
+ if (!session) {
95
123
  throw new VcmError({
96
124
  code: "HARNESS_ENGINEER_BUSY",
97
125
  message: "Harness Engineer is busy or unavailable.",
@@ -144,6 +172,16 @@ export function createHarnessFeedbackService(deps) {
144
172
  "End your turn after writing the result."
145
173
  ].join("\n");
146
174
  }
175
+ function buildPendingFeedbackPrompt(repoRoot, feedbackPath) {
176
+ return [
177
+ "[VCM Harness Feedback]",
178
+ "",
179
+ "Review this feedback:",
180
+ resolveRepoPath(repoRoot, feedbackPath),
181
+ "",
182
+ "Verify the issue against the current harness and project evidence. Report your findings and proposed changes to the user."
183
+ ].join("\n");
184
+ }
147
185
  async function loadTaskRetrospectiveMarker(repoRoot, taskSlug) {
148
186
  const markerPath = resolveRepoPath(repoRoot, getTaskRetrospectiveMarkerPath(taskSlug));
149
187
  if (!(await deps.fs.pathExists(markerPath))) {
@@ -174,6 +212,7 @@ export function createHarnessFeedbackService(deps) {
174
212
  }
175
213
  return {
176
214
  getState,
215
+ sendPendingFeedback,
177
216
  startTaskRetrospective,
178
217
  assertHarnessEngineerAvailable
179
218
  };
@@ -8,18 +8,21 @@ import { renderGateReviewerAgentRules, renderRequestGateReviewTool, renderTransl
8
8
  import { renderHarnessEngineerHarnessRules } from "../templates/harness/harness-engineer-agent.js";
9
9
  import { renderRootClaudeHarnessRules } from "../templates/harness/claude-root.js";
10
10
  import { renderGitignoreHarnessRules } from "../templates/harness/gitignore.js";
11
+ import { ensureVcmMemoryBlock } from "../templates/harness/memory-block.js";
11
12
  import { renderLegacyProjectCodingStandardsTemplate, renderProjectCodingStandardsProjectSection, renderProjectCodingStandardsRules } from "../templates/harness/project-coding-standards.js";
12
13
  import { renderProjectGlossaryTemplate } from "../templates/harness/project-glossary.js";
13
14
  import { renderLegacyProjectKnownIssuesTemplate, renderProjectKnownIssuesRules, renderProjectKnownIssuesSection } from "../templates/harness/project-known-issues.js";
14
15
  import { renderProjectManagerHarnessRules } from "../templates/harness/project-manager-agent.js";
15
16
  import { renderPullRequestTemplateHarnessRules } from "../templates/harness/pull-request-template.js";
16
17
  import { renderTesterHarnessRules } from "../templates/harness/tester-agent.js";
18
+ import { renderVcmArchitectureInterviewSkillRules } from "../templates/harness/vcm-architecture-interview-skill.js";
17
19
  import { renderVcmFinalAcceptanceSkillRules } from "../templates/harness/vcm-final-acceptance-skill.js";
18
20
  import { renderVcmHarnessBootstrapSkillRules } from "../templates/harness/vcm-harness-bootstrap-skill.js";
19
21
  import { renderVcmLongRunningValidationSkillRules } from "../templates/harness/vcm-long-running-validation-skill.js";
20
22
  import { renderVcmProposeMemorySkillRules } from "../templates/harness/vcm-propose-memory-skill.js";
21
23
  import { renderVcmReportHarnessIssueSkillRules } from "../templates/harness/vcm-report-harness-issue-skill.js";
22
24
  import { renderVcmRouteMessageSkillRules } from "../templates/harness/vcm-route-message-skill.js";
25
+ import { renderUpdateTaskStateTool, renderVcmTaskStateSkillRules } from "../templates/harness/vcm-task-state-skill.js";
23
26
  import { submitTerminalInput } from "../runtime/terminal-submit.js";
24
27
  import { VcmError } from "../errors.js";
25
28
  import { bumpHarnessRevision, readHarnessRevisionState } from "./harness-revision.js";
@@ -57,6 +60,7 @@ const HARNESS_FILES = [
57
60
  path: "CLAUDE.md",
58
61
  title: "CLAUDE.md",
59
62
  blankLineBeforeEnd: true,
63
+ memoryBlock: true,
60
64
  renderRules: renderRootClaudeHarnessRules
61
65
  },
62
66
  {
@@ -95,6 +99,14 @@ const HARNESS_FILES = [
95
99
  title: "Pull Request Template",
96
100
  renderRules: renderPullRequestTemplateHarnessRules
97
101
  },
102
+ {
103
+ kind: "skill-vcm-architecture-interview",
104
+ path: ".claude/skills/vcm-architecture-interview/SKILL.md",
105
+ title: "VCM Architecture Interview Skill",
106
+ frontmatter: renderSkillFrontmatter("vcm-architecture-interview", "Use when Architect must confirm user-owned behavior and contract decisions before architecture planning."),
107
+ ownership: "whole-file",
108
+ renderRules: renderVcmArchitectureInterviewSkillRules
109
+ },
98
110
  {
99
111
  kind: "skill-vcm-route-message",
100
112
  path: ".claude/skills/vcm-route-message/SKILL.md",
@@ -103,6 +115,14 @@ const HARNESS_FILES = [
103
115
  ownership: "whole-file",
104
116
  renderRules: renderVcmRouteMessageSkillRules
105
117
  },
118
+ {
119
+ kind: "skill-vcm-task-state",
120
+ path: ".claude/skills/vcm-task-state/SKILL.md",
121
+ title: "VCM Task State Skill",
122
+ frontmatter: renderSkillFrontmatter("vcm-task-state", "Use only as project-manager to declare the current task workflow checkpoint to VCM."),
123
+ ownership: "whole-file",
124
+ renderRules: renderVcmTaskStateSkillRules
125
+ },
106
126
  {
107
127
  kind: "skill-vcm-final-acceptance",
108
128
  path: ".claude/skills/vcm-final-acceptance/SKILL.md",
@@ -155,6 +175,7 @@ const HARNESS_FILES = [
155
175
  kind: "agent-gate-reviewer",
156
176
  path: ".claude/agents/gate-reviewer.md",
157
177
  title: "Gate Reviewer Agent",
178
+ memoryBlock: true,
158
179
  frontmatter: renderAgentFrontmatter("gate-reviewer", "VCM independent gate review role for architecture plans, validation adequacy, and code diffs.", { tools: "Read, Grep, Glob, Bash, Write" }),
159
180
  renderRules: renderGateReviewerAgentRules
160
181
  },
@@ -162,14 +183,15 @@ const HARNESS_FILES = [
162
183
  kind: "agent-translator",
163
184
  path: ".claude/agents/translator.md",
164
185
  title: "Translator Agent",
165
- frontmatter: renderAgentFrontmatter("translator", "VCM project translation tool role for conversation translation, file translation, bootstrap, and memory updates."),
186
+ frontmatter: renderAgentFrontmatter("translator", "VCM task-scoped translation tool role for conversation translation, file translation, bootstrap, and memory updates."),
166
187
  renderRules: renderTranslatorAgentRules
167
188
  },
168
189
  {
169
190
  kind: "agent-harness-engineer",
170
191
  path: ".claude/agents/harness-engineer.md",
171
192
  title: "Harness Engineer Agent",
172
- frontmatter: renderAgentFrontmatter("harness-engineer", "VCM project-scoped harness maintenance role for harness diagnosis, diff proposals, and VCM issue drafts."),
193
+ memoryBlock: true,
194
+ frontmatter: renderAgentFrontmatter("harness-engineer", "VCM task-scoped harness maintenance role for harness diagnosis, diff proposals, and VCM issue drafts."),
173
195
  renderRules: renderHarnessEngineerHarnessRules
174
196
  },
175
197
  {
@@ -186,10 +208,18 @@ const HARNESS_FILES = [
186
208
  ownership: "raw-file",
187
209
  renderRules: renderRequestGateReviewTool
188
210
  },
211
+ {
212
+ kind: "tool-update-task-state",
213
+ path: ".ai/tools/update-task-state",
214
+ title: "Update Task State Tool",
215
+ ownership: "raw-file",
216
+ renderRules: renderUpdateTaskStateTool
217
+ },
189
218
  {
190
219
  kind: "agent-project-manager",
191
220
  path: ".claude/agents/project-manager.md",
192
221
  title: "Project Manager Agent",
222
+ memoryBlock: true,
193
223
  frontmatter: renderAgentFrontmatter("project-manager", "User-facing VCM orchestration role for task clarification, role routing, handoffs, acceptance, and PR preparation."),
194
224
  renderRules: renderProjectManagerHarnessRules
195
225
  },
@@ -197,6 +227,7 @@ const HARNESS_FILES = [
197
227
  kind: "agent-architect",
198
228
  path: ".claude/agents/architect.md",
199
229
  title: "Architect Agent",
230
+ memoryBlock: true,
200
231
  blankLineBeforeEnd: true,
201
232
  frontmatter: renderAgentFrontmatter("architect", "VCM architecture role for plans, module boundaries, public contracts, verifiable behavior, and docs sync."),
202
233
  renderRules: renderArchitectHarnessRules
@@ -205,6 +236,7 @@ const HARNESS_FILES = [
205
236
  kind: "agent-coder",
206
237
  path: ".claude/agents/coder.md",
207
238
  title: "Coder Agent",
239
+ memoryBlock: true,
208
240
  frontmatter: renderAgentFrontmatter("coder", "VCM implementation role for scoped code changes and focused tests.", { tools: "Read, Grep, Glob, Bash, Edit, Write, Agent" }),
209
241
  renderRules: renderCoderHarnessRules
210
242
  },
@@ -212,6 +244,7 @@ const HARNESS_FILES = [
212
244
  kind: "agent-tester",
213
245
  path: ".claude/agents/tester.md",
214
246
  title: "Tester Agent",
247
+ memoryBlock: true,
215
248
  frontmatter: renderAgentFrontmatter("tester", "VCM testing role for validation, test adequacy, approved-scope validation, and risk findings."),
216
249
  renderRules: renderTesterHarnessRules
217
250
  }
@@ -316,12 +349,12 @@ export function createHarnessService(deps) {
316
349
  async mergeRepositoryDiffToCurrentBranch(baseRepoRoot, input) {
317
350
  return mergeRepositoryDiffToCurrentBranch(requireRepositoryMergeGit(deps.git), baseRepoRoot, input, now());
318
351
  },
319
- async getBootstrapStatus(repoRoot, targetRepoRoot = repoRoot) {
320
- return getHarnessBootstrapStatus(deps, repoRoot, targetRepoRoot, now, vcmVersion);
352
+ async getBootstrapStatus(repoRoot, targetRepoRoot = repoRoot, taskSlug) {
353
+ return getHarnessBootstrapStatus(deps, repoRoot, targetRepoRoot, now, vcmVersion, taskSlug);
321
354
  },
322
355
  async startHarnessBootstrap(repoRoot, targetRepoRoot = repoRoot, input = {}) {
323
356
  const session = await ensureHarnessEngineerForBootstrap(deps, repoRoot, targetRepoRoot, now, vcmVersion, input);
324
- const nextStatus = await getHarnessBootstrapStatus(deps, repoRoot, targetRepoRoot, now, vcmVersion);
357
+ const nextStatus = await getHarnessBootstrapStatus(deps, repoRoot, targetRepoRoot, now, vcmVersion, input.taskSlug);
325
358
  return {
326
359
  status: {
327
360
  ...nextStatus,
@@ -333,7 +366,7 @@ export function createHarnessService(deps) {
333
366
  },
334
367
  async restartHarnessBootstrap(repoRoot, targetRepoRoot = repoRoot, input = {}) {
335
368
  const session = await restartHarnessEngineerForBootstrap(deps, repoRoot, targetRepoRoot, now, vcmVersion, input);
336
- const nextStatus = await getHarnessBootstrapStatus(deps, repoRoot, targetRepoRoot, now, vcmVersion);
369
+ const nextStatus = await getHarnessBootstrapStatus(deps, repoRoot, targetRepoRoot, now, vcmVersion, input.taskSlug);
337
370
  return {
338
371
  status: {
339
372
  ...nextStatus,
@@ -343,8 +376,8 @@ export function createHarnessService(deps) {
343
376
  prompt: buildHarnessBootstrapPrompt(repoRoot, targetRepoRoot)
344
377
  };
345
378
  },
346
- async stopHarnessBootstrap(repoRoot) {
347
- const existing = await getCurrentHarnessEngineerBootstrapSession(deps, repoRoot);
379
+ async stopHarnessBootstrap(repoRoot, targetRepoRoot = repoRoot, taskSlug) {
380
+ const existing = await getCurrentHarnessEngineerBootstrapSession(deps, repoRoot, taskSlug);
348
381
  if (!existing) {
349
382
  throw new VcmError({
350
383
  code: "HARNESS_BOOTSTRAP_SESSION_MISSING",
@@ -352,11 +385,13 @@ export function createHarnessService(deps) {
352
385
  statusCode: 404
353
386
  });
354
387
  }
355
- await deps.harnessEngineerSessions?.stopProjectHarnessEngineerSession(repoRoot);
388
+ if (taskSlug) {
389
+ await deps.harnessEngineerSessions?.stopRoleSession(repoRoot, taskSlug, "harness-engineer");
390
+ }
356
391
  await clearHarnessBootstrapRunState(deps.fs, repoRoot);
357
- return getHarnessBootstrapStatus(deps, repoRoot, repoRoot, now, vcmVersion);
392
+ return getHarnessBootstrapStatus(deps, repoRoot, targetRepoRoot, now, vcmVersion, taskSlug);
358
393
  },
359
- async runHarnessBootstrap(repoRoot, targetRepoRoot = repoRoot) {
394
+ async runHarnessBootstrap(repoRoot, targetRepoRoot = repoRoot, taskSlug) {
360
395
  if (!deps.runtime || !deps.harnessEngineerSessions) {
361
396
  throw new VcmError({
362
397
  code: "HARNESS_BOOTSTRAP_UNAVAILABLE",
@@ -365,7 +400,7 @@ export function createHarnessService(deps) {
365
400
  });
366
401
  }
367
402
  await assertHarnessWorktreeClean(deps.git, targetRepoRoot);
368
- const status = await getHarnessBootstrapStatus(deps, repoRoot, targetRepoRoot, now, vcmVersion);
403
+ const status = await getHarnessBootstrapStatus(deps, repoRoot, targetRepoRoot, now, vcmVersion, taskSlug);
369
404
  const session = status.session;
370
405
  if (!session || session.status !== "running" || !deps.runtime.getSession(session.id)) {
371
406
  throw new VcmError({
@@ -379,6 +414,7 @@ export function createHarnessService(deps) {
379
414
  await persistHarnessBootstrapRunState(deps.fs, repoRoot, {
380
415
  version: 1,
381
416
  status: "running",
417
+ taskSlug,
382
418
  targetRepoRoot,
383
419
  sessionId: session.id,
384
420
  claudeSessionId: session.claudeSessionId,
@@ -386,7 +422,7 @@ export function createHarnessService(deps) {
386
422
  updatedAt: timestamp
387
423
  });
388
424
  await submitTerminalInput(deps.runtime, session.id, prompt);
389
- const nextStatus = await getHarnessBootstrapStatus(deps, repoRoot, targetRepoRoot, now, vcmVersion);
425
+ const nextStatus = await getHarnessBootstrapStatus(deps, repoRoot, targetRepoRoot, now, vcmVersion, taskSlug);
390
426
  return {
391
427
  status: {
392
428
  ...nextStatus,
@@ -400,7 +436,7 @@ export function createHarnessService(deps) {
400
436
  async recordHarnessBootstrapHook(repoRoot, input) {
401
437
  const state = await loadPersistedHarnessBootstrapRunState(deps.fs, repoRoot);
402
438
  if (state?.status !== "running" || !matchesBootstrapRunState(state, input)) {
403
- return getHarnessBootstrapStatus(deps, repoRoot, state?.targetRepoRoot ?? repoRoot, now, vcmVersion);
439
+ return getHarnessBootstrapStatus(deps, repoRoot, state?.targetRepoRoot ?? repoRoot, now, vcmVersion, state?.taskSlug);
404
440
  }
405
441
  const timestamp = now();
406
442
  if (input.eventName === "Stop" && state.targetRepoRoot) {
@@ -413,7 +449,7 @@ export function createHarnessService(deps) {
413
449
  updatedAt: timestamp,
414
450
  lastHookEvent: input.eventName
415
451
  });
416
- return getHarnessBootstrapStatus(deps, repoRoot, state.targetRepoRoot ?? repoRoot, now, vcmVersion);
452
+ return getHarnessBootstrapStatus(deps, repoRoot, state.targetRepoRoot ?? repoRoot, now, vcmVersion, state.taskSlug);
417
453
  }
418
454
  };
419
455
  }
@@ -425,7 +461,8 @@ async function ensureHarnessEngineerForBootstrap(deps, repoRoot, targetRepoRoot,
425
461
  statusCode: 501
426
462
  });
427
463
  }
428
- const currentStatus = await getHarnessBootstrapStatus(deps, repoRoot, targetRepoRoot, now, vcmVersion);
464
+ const taskSlug = requireHarnessEngineerTaskSlug(input.taskSlug);
465
+ const currentStatus = await getHarnessBootstrapStatus(deps, repoRoot, targetRepoRoot, now, vcmVersion, taskSlug);
429
466
  if (currentStatus.session?.status === "running") {
430
467
  return currentStatus.session;
431
468
  }
@@ -436,7 +473,14 @@ async function ensureHarnessEngineerForBootstrap(deps, repoRoot, targetRepoRoot,
436
473
  statusCode: 409
437
474
  });
438
475
  }
439
- return toHarnessBootstrapSession(await deps.harnessEngineerSessions.ensureProjectHarnessEngineerSession(repoRoot, input));
476
+ const existing = await deps.harnessEngineerSessions.getRoleSession(repoRoot, taskSlug, "harness-engineer");
477
+ if (existing?.status === "running") {
478
+ return toHarnessBootstrapSession(existing);
479
+ }
480
+ if (existing?.claudeSessionId) {
481
+ return toHarnessBootstrapSession(await deps.harnessEngineerSessions.resumeRoleSession(repoRoot, taskSlug, "harness-engineer", input));
482
+ }
483
+ return toHarnessBootstrapSession(await deps.harnessEngineerSessions.startRoleSession(repoRoot, taskSlug, "harness-engineer", input));
440
484
  }
441
485
  async function restartHarnessEngineerForBootstrap(deps, repoRoot, targetRepoRoot, now, vcmVersion, input) {
442
486
  if (!deps.harnessEngineerSessions) {
@@ -446,7 +490,8 @@ async function restartHarnessEngineerForBootstrap(deps, repoRoot, targetRepoRoot
446
490
  statusCode: 501
447
491
  });
448
492
  }
449
- const currentStatus = await getHarnessBootstrapStatus(deps, repoRoot, targetRepoRoot, now, vcmVersion);
493
+ const taskSlug = requireHarnessEngineerTaskSlug(input.taskSlug);
494
+ const currentStatus = await getHarnessBootstrapStatus(deps, repoRoot, targetRepoRoot, now, vcmVersion, taskSlug);
450
495
  if (!currentStatus.canStart) {
451
496
  throw new VcmError({
452
497
  code: "HARNESS_BOOTSTRAP_NOT_READY",
@@ -454,12 +499,27 @@ async function restartHarnessEngineerForBootstrap(deps, repoRoot, targetRepoRoot
454
499
  statusCode: 409
455
500
  });
456
501
  }
457
- return toHarnessBootstrapSession(await deps.harnessEngineerSessions.restartProjectHarnessEngineerSession(repoRoot, input));
502
+ return toHarnessBootstrapSession(await deps.harnessEngineerSessions.restartRoleSession(repoRoot, taskSlug, "harness-engineer", input));
458
503
  }
459
- async function getCurrentHarnessEngineerBootstrapSession(deps, repoRoot) {
460
- const session = await deps.harnessEngineerSessions?.getProjectHarnessEngineerSession(repoRoot);
504
+ async function getCurrentHarnessEngineerBootstrapSession(deps, repoRoot, taskSlug) {
505
+ if (!taskSlug) {
506
+ return undefined;
507
+ }
508
+ const session = await deps.harnessEngineerSessions?.getRoleSession(repoRoot, taskSlug, "harness-engineer");
461
509
  return session ? toHarnessBootstrapSession(session) : undefined;
462
510
  }
511
+ function requireHarnessEngineerTaskSlug(value) {
512
+ const taskSlug = value?.trim();
513
+ if (!taskSlug) {
514
+ throw new VcmError({
515
+ code: "HARNESS_ENGINEER_TASK_REQUIRED",
516
+ message: "Harness Engineer requires an active task.",
517
+ statusCode: 409,
518
+ hint: "Create or select a task before using Harness Engineer."
519
+ });
520
+ }
521
+ return taskSlug;
522
+ }
463
523
  function toHarnessBootstrapSession(session) {
464
524
  return {
465
525
  id: session.id,
@@ -1059,6 +1119,7 @@ async function analyzeHarnessFile(fs, repoRoot, definition) {
1059
1119
  : getManagedBlockPattern(definition);
1060
1120
  const exists = await fs.pathExists(absolutePath);
1061
1121
  if (!exists) {
1122
+ const newContent = expectedContent ?? renderNewHarnessFile(definition, expectedBlock ?? "");
1062
1123
  return {
1063
1124
  definition,
1064
1125
  status: {
@@ -1073,7 +1134,7 @@ async function analyzeHarnessFile(fs, repoRoot, definition) {
1073
1134
  action: "create",
1074
1135
  reason: "File is missing; VCM will create a recommended default."
1075
1136
  },
1076
- nextContent: expectedContent ?? renderNewHarnessFile(definition, expectedBlock ?? "")
1137
+ nextContent: definition.memoryBlock ? ensureVcmMemoryBlock(newContent) : newContent
1077
1138
  };
1078
1139
  }
1079
1140
  const currentContent = await fs.readText(absolutePath);
@@ -1105,6 +1166,7 @@ async function analyzeHarnessFile(fs, repoRoot, definition) {
1105
1166
  if (!match) {
1106
1167
  const migratedContent = migrateLegacyHarnessFile(definition, currentContent, expectedBlock);
1107
1168
  if (migratedContent) {
1169
+ const nextContent = definition.memoryBlock ? ensureVcmMemoryBlock(migratedContent) : migratedContent;
1108
1170
  return {
1109
1171
  definition,
1110
1172
  status: {
@@ -1119,9 +1181,10 @@ async function analyzeHarnessFile(fs, repoRoot, definition) {
1119
1181
  action: "update",
1120
1182
  reason: "Legacy VCM whole-file baseline will be migrated to a managed block."
1121
1183
  },
1122
- nextContent: migratedContent
1184
+ nextContent
1123
1185
  };
1124
1186
  }
1187
+ const insertedContent = `${currentContent.trimEnd()}\n\n${expectedBlock}\n`;
1125
1188
  return {
1126
1189
  definition,
1127
1190
  status: {
@@ -1136,12 +1199,14 @@ async function analyzeHarnessFile(fs, repoRoot, definition) {
1136
1199
  action: "insert",
1137
1200
  reason: "File exists but does not contain VCM managed rules."
1138
1201
  },
1139
- nextContent: `${currentContent.trimEnd()}\n\n${expectedBlock}\n`
1202
+ nextContent: definition.memoryBlock ? ensureVcmMemoryBlock(insertedContent) : insertedContent
1140
1203
  };
1141
1204
  }
1142
1205
  const managedVersion = match[1] ? Number(match[1]) : undefined;
1143
1206
  const currentBlock = match[0];
1144
- const action = currentBlock === expectedBlock ? "ok" : "update";
1207
+ const blockUpdatedContent = currentContent.replace(managedBlockPattern, expectedBlock);
1208
+ const nextContent = definition.memoryBlock ? ensureVcmMemoryBlock(blockUpdatedContent) : blockUpdatedContent;
1209
+ const action = currentContent === nextContent ? "ok" : "update";
1145
1210
  return {
1146
1211
  definition,
1147
1212
  status: {
@@ -1157,13 +1222,15 @@ async function analyzeHarnessFile(fs, repoRoot, definition) {
1157
1222
  : {
1158
1223
  path: definition.path,
1159
1224
  action,
1160
- reason: managedVersion === VCM_HARNESS_VERSION
1161
- ? "VCM managed rules differ from the current recommended template."
1162
- : `VCM managed block version is ${managedVersion ?? "missing"}; current version is ${VCM_HARNESS_VERSION}.`
1225
+ reason: currentBlock === expectedBlock && definition.memoryBlock
1226
+ ? "VCM memory block is missing."
1227
+ : managedVersion === VCM_HARNESS_VERSION
1228
+ ? "VCM managed rules differ from the current recommended template."
1229
+ : `VCM managed block version is ${managedVersion ?? "missing"}; current version is ${VCM_HARNESS_VERSION}.`
1163
1230
  },
1164
1231
  nextContent: action === "ok"
1165
1232
  ? undefined
1166
- : currentContent.replace(managedBlockPattern, expectedBlock)
1233
+ : nextContent
1167
1234
  };
1168
1235
  }
1169
1236
  async function analyzeLegacyCodexHarnessPaths(fs, repoRoot) {
@@ -1417,7 +1484,7 @@ async function analyzeHarnessManifest(fs, repoRoot, vcmVersion) {
1417
1484
  }
1418
1485
  return undefined;
1419
1486
  }
1420
- async function getHarnessBootstrapStatus(deps, repoRoot, targetRepoRoot, now, vcmVersion) {
1487
+ async function getHarnessBootstrapStatus(deps, repoRoot, targetRepoRoot, now, vcmVersion, taskSlug) {
1421
1488
  const moduleIndex = await readOptionalJsonObject(deps.fs, targetRepoRoot, ".ai/generated/module-index.json");
1422
1489
  const checks = [
1423
1490
  await checkFixedHarness(deps.fs, targetRepoRoot, vcmVersion),
@@ -1430,8 +1497,11 @@ async function getHarnessBootstrapStatus(deps, repoRoot, targetRepoRoot, now, vc
1430
1497
  await checkModuleArchitectureDocs(deps.fs, targetRepoRoot, moduleIndex),
1431
1498
  await checkFilledMarkdown(deps.fs, targetRepoRoot, "docs/TESTING.md", "Testing doc", "testing-doc")
1432
1499
  ];
1433
- const runState = await loadPersistedHarnessBootstrapRunState(deps.fs, repoRoot);
1434
- const session = await getCurrentHarnessEngineerBootstrapSession(deps, repoRoot);
1500
+ const persistedRunState = await loadPersistedHarnessBootstrapRunState(deps.fs, repoRoot);
1501
+ const runState = taskSlug && persistedRunState?.taskSlug !== taskSlug
1502
+ ? undefined
1503
+ : persistedRunState;
1504
+ const session = await getCurrentHarnessEngineerBootstrapSession(deps, repoRoot, taskSlug);
1435
1505
  const fixedHarnessReady = checks[0]?.status === "ok";
1436
1506
  const projectChecks = checks.slice(1);
1437
1507
  const projectComplete = projectChecks.every((check) => check.status === "ok");
@@ -1461,6 +1531,7 @@ async function checkFixedHarness(fs, repoRoot, vcmVersion) {
1461
1531
  ".claude/skills/vcm-harness-bootstrap/SKILL.md",
1462
1532
  "docs/GLOSSARY.md",
1463
1533
  "docs/CODING_STANDARDS.md",
1534
+ ".ai/tools/check-durable-docs",
1464
1535
  ".ai/tools/generate-module-index",
1465
1536
  ".ai/tools/generate-public-surface"
1466
1537
  ];
@@ -1680,6 +1751,8 @@ async function loadPersistedHarnessBootstrapRunState(fs, repoRoot) {
1680
1751
  return {
1681
1752
  version: 1,
1682
1753
  status,
1754
+ taskSlug: typeof payload.taskSlug === "string" ? payload.taskSlug : undefined,
1755
+ targetRepoRoot: typeof payload.targetRepoRoot === "string" ? payload.targetRepoRoot : undefined,
1683
1756
  sessionId: typeof payload.sessionId === "string" ? payload.sessionId : undefined,
1684
1757
  claudeSessionId: typeof payload.claudeSessionId === "string" ? payload.claudeSessionId : undefined,
1685
1758
  startedAt: typeof payload.startedAt === "string" ? payload.startedAt : undefined,
@@ -1746,7 +1819,9 @@ function bootstrapWarnings(status, checks, session, runState) {
1746
1819
  return warnings;
1747
1820
  }
1748
1821
  function buildHarnessBootstrapPrompt(baseRepoRoot, targetRepoRoot) {
1749
- return `Use the vcm-harness-bootstrap skill to finish the VCM harness bootstrap for the active task worktree.
1822
+ return `[VCM HARNESS BOOTSTRAP]
1823
+
1824
+ Use the vcm-harness-bootstrap skill to finish the VCM harness bootstrap for the active task worktree.
1750
1825
 
1751
1826
  Base repository root:
1752
1827
  ${baseRepoRoot}
@@ -1764,6 +1839,7 @@ Required work:
1764
1839
  - Fill target docs/ARCHITECTURE.md with project-level module overview, responsibilities, relationships, dependency direction, project-wide constraints, and links to module-level architecture docs.
1765
1840
  - Create or update target module-level ARCHITECTURE.md files for clear non-root module boundaries with architectureDoc paths in module-index.json.
1766
1841
  - Fill target docs/TESTING.md with project-native validation levels, commands, validation selection rules, final-validation cleanup, test layout, integration/E2E case lists, generated-context freshness checks, and known testing gaps.
1842
+ - Run .ai/tools/check-durable-docs and correct every bootstrap-owned finding.
1767
1843
  - Review git status and git diff in the target task worktree.
1768
1844
  - Stage only allowed bootstrap harness changes and create a commit in the target task worktree.
1769
1845
 
@@ -1775,7 +1851,9 @@ Boundaries:
1775
1851
  - VCM will not create the bootstrap commit for you.
1776
1852
 
1777
1853
  Final response:
1778
- Summarize files reviewed, files updated, generated artifacts, commit hash, final git status, verified claims, inferred claims, unknowns, confirmation-needed items, and suggested validation commands.`;
1854
+ Summarize files reviewed, files updated, generated artifacts, durable-doc audit result, commit hash, final git status, verified claims, inferred claims, unknowns, confirmation-needed items, and suggested validation commands.
1855
+
1856
+ [/VCM HARNESS BOOTSTRAP]`;
1779
1857
  }
1780
1858
  export function createScriptFixedHarnessInstaller(scriptPath = path.resolve(process.cwd(), "scripts/install-vcm-harness.mjs")) {
1781
1859
  return async (repoRoot) => {