vibe-coding-master 0.6.17 → 0.6.19
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.
- package/dist/backend/cli/install-vcm-harness.js +18 -2
- package/dist/backend/services/harness-service.js +13 -3
- package/dist/backend/services/job-guard-service.js +82 -10
- package/dist/backend/services/runtime-recovery-service.js +13 -0
- package/dist/backend/services/translation-service.js +30 -28
- package/dist/backend/services/translation-worker-service.js +17 -21
- package/dist/backend/templates/harness/architect-agent.js +44 -18
- package/dist/backend/templates/harness/coder-agent.js +23 -4
- package/dist/backend/templates/harness/coder-worker-agent.js +72 -0
- package/dist/backend/templates/harness/project-manager-agent.js +20 -8
- package/dist/backend/templates/harness/reviewer-agent.js +2 -8
- package/package.json +1 -1
|
@@ -6,6 +6,7 @@ import process from "node:process";
|
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
import { renderArchitectHarnessRules } from "../templates/harness/architect-agent.js";
|
|
8
8
|
import { renderCoderHarnessRules } from "../templates/harness/coder-agent.js";
|
|
9
|
+
import { renderCoderWorkerHarnessRules } from "../templates/harness/coder-worker-agent.js";
|
|
9
10
|
import { renderGateReviewerAgentRules, renderRequestGateReviewTool, renderTranslatorAgentRules, renderVcmGateReviewSkillRules } from "../templates/harness/gate-review.js";
|
|
10
11
|
import { renderHarnessEngineerHarnessRules } from "../templates/harness/harness-engineer-agent.js";
|
|
11
12
|
import { renderRootClaudeHarnessRules } from "../templates/harness/claude-root.js";
|
|
@@ -52,7 +53,8 @@ const AGENT_FRONTMATTER = {
|
|
|
52
53
|
description: "VCM architecture role for plans, module boundaries, public contracts, verifiable behavior, and docs sync."
|
|
53
54
|
},
|
|
54
55
|
coder: {
|
|
55
|
-
description: "VCM implementation role for scoped code changes and focused tests."
|
|
56
|
+
description: "VCM implementation role for scoped code changes and focused tests.",
|
|
57
|
+
tools: "Read, Grep, Glob, Bash, Edit, Write, Agent"
|
|
56
58
|
},
|
|
57
59
|
reviewer: {
|
|
58
60
|
description: "VCM independent review role for acceptance, test adequacy, scope checks, and risk findings."
|
|
@@ -65,6 +67,10 @@ const AGENT_FRONTMATTER = {
|
|
|
65
67
|
},
|
|
66
68
|
"harness-engineer": {
|
|
67
69
|
description: "VCM project-scoped harness maintenance role for harness diagnosis, diff proposals, and VCM issue drafts."
|
|
70
|
+
},
|
|
71
|
+
"vcm-coder-worker": {
|
|
72
|
+
description: "Bounded VCM implementation worker for assigned modules, files, and VCM:CODE markers from Coder.",
|
|
73
|
+
model: "inherit"
|
|
68
74
|
}
|
|
69
75
|
};
|
|
70
76
|
const MANAGED_FILES = [
|
|
@@ -146,6 +152,14 @@ const MANAGED_FILES = [
|
|
|
146
152
|
commentStyle: "html",
|
|
147
153
|
category: "agent-harness-engineer",
|
|
148
154
|
content: renderHarnessEngineerHarnessRules()
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
path: ".claude/agents/vcm-coder-worker.md",
|
|
158
|
+
title: "VCM Coder Worker Agent",
|
|
159
|
+
agentName: "vcm-coder-worker",
|
|
160
|
+
commentStyle: "html",
|
|
161
|
+
category: "agent-coder-worker",
|
|
162
|
+
content: renderCoderWorkerHarnessRules()
|
|
149
163
|
}
|
|
150
164
|
];
|
|
151
165
|
const DURABLE_DOC_TEMPLATES = [
|
|
@@ -528,7 +542,9 @@ function renderManagedBlock(definition) {
|
|
|
528
542
|
function renderNewManagedFile(definition, block) {
|
|
529
543
|
if (definition.agentName) {
|
|
530
544
|
const frontmatter = AGENT_FRONTMATTER[definition.agentName];
|
|
531
|
-
|
|
545
|
+
const tools = frontmatter.tools ?? "Read, Grep, Glob, Bash, Edit, Write";
|
|
546
|
+
const model = frontmatter.model ? `\nmodel: ${frontmatter.model}` : "";
|
|
547
|
+
return `---\nname: ${definition.agentName}\ndescription: ${frontmatter.description}\ntools: ${tools}${model}\n---\n\n# ${definition.title}\n\n${block}\n`;
|
|
532
548
|
}
|
|
533
549
|
return `# ${definition.title}\n\n${block}\n`;
|
|
534
550
|
}
|
|
@@ -3,6 +3,7 @@ import path from "node:path";
|
|
|
3
3
|
import { promisify } from "node:util";
|
|
4
4
|
import { renderArchitectHarnessRules } from "../templates/harness/architect-agent.js";
|
|
5
5
|
import { renderCoderHarnessRules } from "../templates/harness/coder-agent.js";
|
|
6
|
+
import { renderCoderWorkerHarnessRules } from "../templates/harness/coder-worker-agent.js";
|
|
6
7
|
import { renderGateReviewerAgentRules, renderRequestGateReviewTool, renderTranslatorAgentRules, renderVcmGateReviewSkillRules } from "../templates/harness/gate-review.js";
|
|
7
8
|
import { renderHarnessEngineerHarnessRules } from "../templates/harness/harness-engineer-agent.js";
|
|
8
9
|
import { renderRootClaudeHarnessRules } from "../templates/harness/claude-root.js";
|
|
@@ -135,6 +136,13 @@ const HARNESS_FILES = [
|
|
|
135
136
|
frontmatter: renderAgentFrontmatter("harness-engineer", "VCM project-scoped harness maintenance role for harness diagnosis, diff proposals, and VCM issue drafts."),
|
|
136
137
|
renderRules: renderHarnessEngineerHarnessRules
|
|
137
138
|
},
|
|
139
|
+
{
|
|
140
|
+
kind: "agent-coder-worker",
|
|
141
|
+
path: ".claude/agents/vcm-coder-worker.md",
|
|
142
|
+
title: "VCM Coder Worker Agent",
|
|
143
|
+
frontmatter: renderAgentFrontmatter("vcm-coder-worker", "Bounded VCM implementation worker for assigned modules, files, and VCM:CODE markers from Coder.", { model: "inherit" }),
|
|
144
|
+
renderRules: renderCoderWorkerHarnessRules
|
|
145
|
+
},
|
|
138
146
|
{
|
|
139
147
|
kind: "tool-request-gate-review",
|
|
140
148
|
path: ".ai/tools/request-gate-review",
|
|
@@ -161,7 +169,7 @@ const HARNESS_FILES = [
|
|
|
161
169
|
kind: "agent-coder",
|
|
162
170
|
path: ".claude/agents/coder.md",
|
|
163
171
|
title: "Coder Agent",
|
|
164
|
-
frontmatter: renderAgentFrontmatter("coder", "VCM implementation role for scoped code changes and focused tests."),
|
|
172
|
+
frontmatter: renderAgentFrontmatter("coder", "VCM implementation role for scoped code changes and focused tests.", { tools: "Read, Grep, Glob, Bash, Edit, Write, Agent" }),
|
|
165
173
|
renderRules: renderCoderHarnessRules
|
|
166
174
|
},
|
|
167
175
|
{
|
|
@@ -1279,8 +1287,10 @@ function isVcmHookMatcher(value) {
|
|
|
1279
1287
|
function isPlainObject(value) {
|
|
1280
1288
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
1281
1289
|
}
|
|
1282
|
-
function renderAgentFrontmatter(name, description) {
|
|
1283
|
-
|
|
1290
|
+
function renderAgentFrontmatter(name, description, options = {}) {
|
|
1291
|
+
const tools = options.tools ?? "Read, Grep, Glob, Bash, Edit, Write";
|
|
1292
|
+
const model = options.model ? `\nmodel: ${options.model}` : "";
|
|
1293
|
+
return `---\nname: ${name}\ndescription: ${description}\ntools: ${tools}${model}\n---`;
|
|
1284
1294
|
}
|
|
1285
1295
|
function renderSkillFrontmatter(name, description) {
|
|
1286
1296
|
return `---\nname: ${name}\ndescription: ${description}\n---`;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
const ACTIVE_JOB_STATUSES = new Set(["queued", "starting", "running"]);
|
|
4
|
+
const ACTIVE_CODER_WORKER_STATUSES = new Set(["planned", "running", "completed", "failed"]);
|
|
4
5
|
const QUEUED_JOB_FRESH_MS = 120_000;
|
|
5
6
|
export const MAX_CONSECUTIVE_STOP_BLOCKS = 3;
|
|
6
7
|
export function createJobGuardService(deps = {}) {
|
|
@@ -73,19 +74,26 @@ export function createJobGuardService(deps = {}) {
|
|
|
73
74
|
async evaluateStop(input) {
|
|
74
75
|
const key = stateKey(input);
|
|
75
76
|
const jobs = await findActiveJobs(input.taskRepoRoot);
|
|
76
|
-
|
|
77
|
+
const coderWorkerTasks = input.role === "coder"
|
|
78
|
+
? await findActiveCoderWorkerTasks(input.taskRepoRoot)
|
|
79
|
+
: [];
|
|
80
|
+
if (jobs.length === 0 && coderWorkerTasks.length === 0) {
|
|
77
81
|
blockStates.delete(key);
|
|
78
82
|
return { behavior: "allow" };
|
|
79
83
|
}
|
|
80
|
-
const
|
|
84
|
+
const jobProgressMtimeMs = jobs.reduce((latest, job) => job.leaseMtimeMs !== undefined && (latest === undefined || job.leaseMtimeMs > latest)
|
|
81
85
|
? job.leaseMtimeMs
|
|
82
86
|
: latest, undefined);
|
|
87
|
+
const workerProgressMtimeMs = coderWorkerTasks.reduce((latest, task) => task.stateMtimeMs !== undefined && (latest === undefined || task.stateMtimeMs > latest)
|
|
88
|
+
? task.stateMtimeMs
|
|
89
|
+
: latest, undefined);
|
|
90
|
+
const progressMtimeMs = latestMtime(jobProgressMtimeMs, workerProgressMtimeMs);
|
|
83
91
|
let state = blockStates.get(key) ?? { count: 0 };
|
|
84
|
-
const
|
|
85
|
-
&&
|
|
86
|
-
&& state.
|
|
87
|
-
&&
|
|
88
|
-
if (
|
|
92
|
+
const progressChanged = state.count > 0
|
|
93
|
+
&& progressMtimeMs !== undefined
|
|
94
|
+
&& state.lastProgressMtimeMs !== undefined
|
|
95
|
+
&& progressMtimeMs > state.lastProgressMtimeMs;
|
|
96
|
+
if (progressChanged) {
|
|
89
97
|
state = { count: 0 };
|
|
90
98
|
}
|
|
91
99
|
if (state.count >= MAX_CONSECUTIVE_STOP_BLOCKS) {
|
|
@@ -94,24 +102,88 @@ export function createJobGuardService(deps = {}) {
|
|
|
94
102
|
blockStates.delete(key);
|
|
95
103
|
return { behavior: "allow" };
|
|
96
104
|
}
|
|
97
|
-
blockStates.set(key, { count: state.count + 1,
|
|
98
|
-
return { behavior: "block", reason: buildBlockReason(jobs) };
|
|
105
|
+
blockStates.set(key, { count: state.count + 1, lastProgressMtimeMs: progressMtimeMs });
|
|
106
|
+
return { behavior: "block", reason: buildBlockReason(jobs, coderWorkerTasks) };
|
|
99
107
|
},
|
|
100
108
|
notePromptSubmitted(input) {
|
|
101
109
|
blockStates.delete(stateKey(input));
|
|
102
110
|
}
|
|
103
111
|
};
|
|
104
112
|
}
|
|
113
|
+
async function findActiveCoderWorkerTasks(taskRepoRoot) {
|
|
114
|
+
const tasksRoot = path.join(taskRepoRoot, ".ai/vcm/coder-workers/tasks");
|
|
115
|
+
let entries;
|
|
116
|
+
try {
|
|
117
|
+
entries = await fs.readdir(tasksRoot);
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
return [];
|
|
121
|
+
}
|
|
122
|
+
const tasks = [];
|
|
123
|
+
for (const entry of entries.sort()) {
|
|
124
|
+
if (!entry.endsWith(".json")) {
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
const statePath = path.join(tasksRoot, entry);
|
|
128
|
+
let state;
|
|
129
|
+
try {
|
|
130
|
+
state = JSON.parse(await fs.readFile(statePath, "utf8"));
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
const status = typeof state.status === "string" ? state.status : "";
|
|
136
|
+
if (state.handled === true || !ACTIVE_CODER_WORKER_STATUSES.has(status)) {
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
let stateMtimeMs;
|
|
140
|
+
try {
|
|
141
|
+
stateMtimeMs = (await fs.stat(statePath)).mtimeMs;
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
stateMtimeMs = undefined;
|
|
145
|
+
}
|
|
146
|
+
tasks.push({
|
|
147
|
+
workerId: typeof state.workerId === "string" ? state.workerId : path.basename(entry, ".json"),
|
|
148
|
+
status,
|
|
149
|
+
reportPath: typeof state.reportPath === "string" ? state.reportPath : undefined,
|
|
150
|
+
error: typeof state.error === "string" ? state.error : undefined,
|
|
151
|
+
stateMtimeMs
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
return tasks;
|
|
155
|
+
}
|
|
105
156
|
function stateKey(input) {
|
|
106
157
|
return `${input.repoRoot}::${input.taskSlug}::${input.role}`;
|
|
107
158
|
}
|
|
108
|
-
function buildBlockReason(jobs) {
|
|
159
|
+
function buildBlockReason(jobs, coderWorkerTasks) {
|
|
160
|
+
if (jobs.length > 0 && coderWorkerTasks.length === 0) {
|
|
161
|
+
return buildValidationJobBlockReason(jobs);
|
|
162
|
+
}
|
|
163
|
+
if (jobs.length === 0) {
|
|
164
|
+
return buildCoderWorkerBlockReason(coderWorkerTasks);
|
|
165
|
+
}
|
|
166
|
+
return `${buildValidationJobBlockReason(jobs)}\n${buildCoderWorkerBlockReason(coderWorkerTasks)}`;
|
|
167
|
+
}
|
|
168
|
+
function buildValidationJobBlockReason(jobs) {
|
|
109
169
|
const first = jobs[0];
|
|
110
170
|
const listing = jobs.map((job) => `${job.jobId} (${job.status})`).join(", ");
|
|
111
171
|
return `VCM: validation job ${listing} is still running. Do not end the turn while a validation job is running. `
|
|
112
172
|
+ `Run \`.ai/tools/watch-job ${first.jobId}\` again now and keep watching until it reports a terminal result `
|
|
113
173
|
+ `(success, failed, timeout, or orphaned), then record the result.`;
|
|
114
174
|
}
|
|
175
|
+
function buildCoderWorkerBlockReason(tasks) {
|
|
176
|
+
const listing = tasks.map((task) => `${task.workerId} (${task.status})`).join(", ");
|
|
177
|
+
const reports = tasks
|
|
178
|
+
.map((task) => task.reportPath)
|
|
179
|
+
.filter((reportPath) => Boolean(reportPath));
|
|
180
|
+
const reportHint = reports.length > 0 ? ` Review report(s): ${reports.join(", ")}.` : "";
|
|
181
|
+
return `VCM: coder worker task ${listing} is still unhandled. Do not end the Coder turn while worker tasks are unhandled. `
|
|
182
|
+
+ `Wait for worker subagents, review reports and commits, resolve failed or incomplete workers, set \`handled: true\` in each worker state, and continue.${reportHint}`;
|
|
183
|
+
}
|
|
184
|
+
function latestMtime(...values) {
|
|
185
|
+
return values.reduce((latest, value) => value !== undefined && (latest === undefined || value > latest) ? value : latest, undefined);
|
|
186
|
+
}
|
|
115
187
|
function numberOrUndefined(value) {
|
|
116
188
|
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
117
189
|
}
|
|
@@ -5,6 +5,7 @@ const TRANSLATOR_SESSION_PATH = ".ai/vcm/translations/session.json";
|
|
|
5
5
|
const HARNESS_ENGINEER_SESSION_PATH = ".ai/vcm/harness-engineer/session.json";
|
|
6
6
|
const BOOTSTRAP_SESSION_PATH = ".ai/vcm/bootstrap/session.json";
|
|
7
7
|
const HARNESS_FEEDBACK_STATE_PATH = ".ai/vcm/harness-feedback/state.json";
|
|
8
|
+
const CODER_WORKERS_RUNTIME_DIR = ".ai/vcm/coder-workers";
|
|
8
9
|
const RECOVERABLE_FEEDBACK_STATES = new Set(["analyzing", "applying"]);
|
|
9
10
|
export function createRuntimeRecoveryService(deps) {
|
|
10
11
|
const now = deps.now ?? (() => new Date().toISOString());
|
|
@@ -28,6 +29,7 @@ export function createRuntimeRecoveryService(deps) {
|
|
|
28
29
|
const roundRecovered = await recoverRound(taskRepoRoot, config.stateRoot, task.taskSlug, recoveredAt, context);
|
|
29
30
|
await recoverMessages(taskRepoRoot, config.stateRoot, task.taskSlug, recoveredAt, context);
|
|
30
31
|
await recoverGateReview(taskRepoRoot, recoveredAt, context);
|
|
32
|
+
await cleanupCoderWorkers(taskRepoRoot, context);
|
|
31
33
|
if ((roundRecovered || task.status === "running") && !hasLiveTaskSession(task.taskSlug)) {
|
|
32
34
|
await deps.taskService.updateTaskStatus(repoRoot, task.taskSlug, "stopped");
|
|
33
35
|
}
|
|
@@ -230,6 +232,17 @@ export function createRuntimeRecoveryService(deps) {
|
|
|
230
232
|
});
|
|
231
233
|
context.changedPaths.add(relativePath);
|
|
232
234
|
}
|
|
235
|
+
async function cleanupCoderWorkers(taskRepoRoot, context) {
|
|
236
|
+
const absolutePath = path.join(taskRepoRoot, CODER_WORKERS_RUNTIME_DIR);
|
|
237
|
+
if (!(await deps.fs.pathExists(absolutePath))) {
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
if (!deps.fs.removePath) {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
await deps.fs.removePath(absolutePath, { recursive: true, force: true });
|
|
244
|
+
context.changedPaths.add(CODER_WORKERS_RUNTIME_DIR);
|
|
245
|
+
}
|
|
233
246
|
async function recoverHarnessBootstrap(repoRoot, _timestamp, context) {
|
|
234
247
|
const absolutePath = path.join(repoRoot, BOOTSTRAP_SESSION_PATH);
|
|
235
248
|
const state = await readJsonIfExists(absolutePath);
|
|
@@ -8,7 +8,6 @@ import { createTranslationQueueRegistry } from "./translation-queue.js";
|
|
|
8
8
|
const TRANSLATION_SOURCE_LANGUAGE = "auto";
|
|
9
9
|
const TRANSLATION_INPUT_MODE = "review-before-send";
|
|
10
10
|
const TRANSLATION_CONTEXT_ENABLED = false;
|
|
11
|
-
const TRANSLATION_TIMEOUT_MS = 120000;
|
|
12
11
|
const TRANSLATION_PROVIDER = "claude-code";
|
|
13
12
|
const TRANSLATION_MODEL = "translator";
|
|
14
13
|
const OUTPUT_TRANSLATION_BATCH_DELAY_MS = 10000;
|
|
@@ -30,8 +29,7 @@ export function createTranslationService(deps) {
|
|
|
30
29
|
targetLanguage: preferences.translationTargetLanguage,
|
|
31
30
|
inputMode: TRANSLATION_INPUT_MODE,
|
|
32
31
|
outputMode: preferences.translationOutputMode,
|
|
33
|
-
contextEnabled: TRANSLATION_CONTEXT_ENABLED
|
|
34
|
-
requestTimeoutMs: TRANSLATION_TIMEOUT_MS
|
|
32
|
+
contextEnabled: TRANSLATION_CONTEXT_ENABLED
|
|
35
33
|
};
|
|
36
34
|
}
|
|
37
35
|
function getState(sessionId) {
|
|
@@ -400,7 +398,7 @@ export function createTranslationService(deps) {
|
|
|
400
398
|
}
|
|
401
399
|
for (const { item, job } of jobs) {
|
|
402
400
|
try {
|
|
403
|
-
const result = await waitForConversationResult(item.repoRoot, job
|
|
401
|
+
const result = await waitForConversationResult(item.repoRoot, job);
|
|
404
402
|
const completed = {
|
|
405
403
|
...item.entry,
|
|
406
404
|
status: "translated",
|
|
@@ -1076,7 +1074,7 @@ export function createTranslationService(deps) {
|
|
|
1076
1074
|
},
|
|
1077
1075
|
async translateGatewayOutput(input) {
|
|
1078
1076
|
const config = await loadConfig();
|
|
1079
|
-
const reusable = await findReusableGatewayOutputTranslation(input
|
|
1077
|
+
const reusable = await findReusableGatewayOutputTranslation(input);
|
|
1080
1078
|
if (reusable) {
|
|
1081
1079
|
return reusable.trim();
|
|
1082
1080
|
}
|
|
@@ -1197,7 +1195,7 @@ export function createTranslationService(deps) {
|
|
|
1197
1195
|
}
|
|
1198
1196
|
return leftTime.localeCompare(rightTime);
|
|
1199
1197
|
}
|
|
1200
|
-
async function findReusableGatewayOutputTranslation(input
|
|
1198
|
+
async function findReusableGatewayOutputTranslation(input) {
|
|
1201
1199
|
const graceDeadline = Date.now() + (input.sourceEntryIds?.length ? GATEWAY_TRANSLATION_REUSE_GRACE_MS : 0);
|
|
1202
1200
|
while (true) {
|
|
1203
1201
|
const lookup = await lookupGatewayOutputTranslation(input);
|
|
@@ -1205,7 +1203,7 @@ export function createTranslationService(deps) {
|
|
|
1205
1203
|
return lookup.text;
|
|
1206
1204
|
}
|
|
1207
1205
|
if (lookup.kind === "active") {
|
|
1208
|
-
return waitForReusableGatewayOutputTranslation(input
|
|
1206
|
+
return waitForReusableGatewayOutputTranslation(input);
|
|
1209
1207
|
}
|
|
1210
1208
|
if (!input.sourceEntryIds?.length || Date.now() >= graceDeadline) {
|
|
1211
1209
|
return undefined;
|
|
@@ -1213,9 +1211,8 @@ export function createTranslationService(deps) {
|
|
|
1213
1211
|
await delay(GATEWAY_TRANSLATION_REUSE_POLL_MS);
|
|
1214
1212
|
}
|
|
1215
1213
|
}
|
|
1216
|
-
async function waitForReusableGatewayOutputTranslation(input
|
|
1217
|
-
|
|
1218
|
-
while (Date.now() <= deadline) {
|
|
1214
|
+
async function waitForReusableGatewayOutputTranslation(input) {
|
|
1215
|
+
while (true) {
|
|
1219
1216
|
const lookup = await lookupGatewayOutputTranslation(input);
|
|
1220
1217
|
if (lookup.kind === "translated") {
|
|
1221
1218
|
return lookup.text;
|
|
@@ -1225,11 +1222,6 @@ export function createTranslationService(deps) {
|
|
|
1225
1222
|
}
|
|
1226
1223
|
await delay(GATEWAY_TRANSLATION_REUSE_POLL_MS);
|
|
1227
1224
|
}
|
|
1228
|
-
throw new VcmError({
|
|
1229
|
-
code: "TRANSLATION_TIMEOUT",
|
|
1230
|
-
message: "Gateway output translation timed out while waiting for the existing PM reply translation.",
|
|
1231
|
-
statusCode: 504
|
|
1232
|
-
});
|
|
1233
1225
|
}
|
|
1234
1226
|
async function lookupGatewayOutputTranslation(input) {
|
|
1235
1227
|
const states = await getGatewayOutputCandidateStates(input);
|
|
@@ -1352,7 +1344,7 @@ export function createTranslationService(deps) {
|
|
|
1352
1344
|
}
|
|
1353
1345
|
async function translateText(input) {
|
|
1354
1346
|
const job = await createConversationJob(input);
|
|
1355
|
-
const result = await waitForConversationResult(input.repoRoot, job
|
|
1347
|
+
const result = await waitForConversationResult(input.repoRoot, job);
|
|
1356
1348
|
return {
|
|
1357
1349
|
text: result.translatedText
|
|
1358
1350
|
};
|
|
@@ -1382,14 +1374,30 @@ export function createTranslationService(deps) {
|
|
|
1382
1374
|
deferDispatch: input.deferDispatch
|
|
1383
1375
|
});
|
|
1384
1376
|
}
|
|
1385
|
-
async function waitForConversationResult(repoRoot, job
|
|
1386
|
-
|
|
1387
|
-
let lastError;
|
|
1388
|
-
while (Date.now() <= deadline) {
|
|
1377
|
+
async function waitForConversationResult(repoRoot, job) {
|
|
1378
|
+
while (true) {
|
|
1389
1379
|
const state = await deps.translationWorkerService.getState(repoRoot);
|
|
1390
1380
|
const item = job.queueItemId
|
|
1391
1381
|
? state.queue.items.find((candidate) => candidate.id === job.queueItemId)
|
|
1392
1382
|
: undefined;
|
|
1383
|
+
if (!item) {
|
|
1384
|
+
try {
|
|
1385
|
+
return await deps.translationWorkerService.validateConversationResult(repoRoot, {
|
|
1386
|
+
resultPath: job.resultPath,
|
|
1387
|
+
sourceHash: job.sourceHash,
|
|
1388
|
+
targetLanguage: job.targetLanguage
|
|
1389
|
+
});
|
|
1390
|
+
}
|
|
1391
|
+
catch (error) {
|
|
1392
|
+
throw new VcmError({
|
|
1393
|
+
code: "TRANSLATION_FAILED",
|
|
1394
|
+
message: error instanceof Error
|
|
1395
|
+
? `translation queue item is unavailable: ${error.message}`
|
|
1396
|
+
: "translation queue item is unavailable.",
|
|
1397
|
+
statusCode: 502
|
|
1398
|
+
});
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1393
1401
|
if (item && ["failed", "cancelled", "interrupted", "skipped"].includes(item.status)) {
|
|
1394
1402
|
throw new VcmError({
|
|
1395
1403
|
code: "TRANSLATION_FAILED",
|
|
@@ -1398,7 +1406,7 @@ export function createTranslationService(deps) {
|
|
|
1398
1406
|
});
|
|
1399
1407
|
}
|
|
1400
1408
|
if (item && item.status !== "completed") {
|
|
1401
|
-
await delay(
|
|
1409
|
+
await delay(500);
|
|
1402
1410
|
continue;
|
|
1403
1411
|
}
|
|
1404
1412
|
try {
|
|
@@ -1409,18 +1417,12 @@ export function createTranslationService(deps) {
|
|
|
1409
1417
|
});
|
|
1410
1418
|
}
|
|
1411
1419
|
catch (error) {
|
|
1412
|
-
lastError = error;
|
|
1413
1420
|
if (item?.status === "completed") {
|
|
1414
1421
|
throw error;
|
|
1415
1422
|
}
|
|
1416
1423
|
}
|
|
1417
|
-
await delay(
|
|
1424
|
+
await delay(500);
|
|
1418
1425
|
}
|
|
1419
|
-
throw new VcmError({
|
|
1420
|
-
code: "TRANSLATION_TIMEOUT",
|
|
1421
|
-
message: lastError instanceof Error ? `translation timed out: ${lastError.message}` : "translation timed out.",
|
|
1422
|
-
statusCode: 504
|
|
1423
|
-
});
|
|
1424
1426
|
}
|
|
1425
1427
|
}
|
|
1426
1428
|
function delay(ms) {
|
|
@@ -17,14 +17,6 @@ const CONVERSATION_BATCHES_DIR = `${CONVERSATION_RUNTIME_DIR}/batches`;
|
|
|
17
17
|
const MEMORY_UPDATE_RUNTIME_DIR = `${TRANSLATIONS_RUNTIME_DIR}/memory-updates`;
|
|
18
18
|
const DEFAULT_PROFILE = "default";
|
|
19
19
|
const DEFAULT_CHUNK_SOURCE_TOKEN_TARGET = 80000;
|
|
20
|
-
// In-flight conversation queue items normally finalize when the Translator
|
|
21
|
-
// session's Stop/StopFailure hook reaches the backend. If that hook is lost
|
|
22
|
-
// (session crash, backend restart/reconnect) a conversation item with no result
|
|
23
|
-
// on disk would block the queue head forever. Treat such an item as stuck once it
|
|
24
|
-
// has been in-flight past this bound and release it so later items can dispatch.
|
|
25
|
-
// Kept comfortably above a normal short composer translation, so a genuinely
|
|
26
|
-
// running conversation turn is never released mid-flight.
|
|
27
|
-
const STALE_CONVERSATION_ITEM_MS = 90000;
|
|
28
20
|
const BOOTSTRAP_DEFAULT_LIMIT = 12;
|
|
29
21
|
const MEMORY_TOTAL_LIMIT_BYTES = 80 * 1024;
|
|
30
22
|
const MEMORY_INITIALIZED_MIN_FILES = 2;
|
|
@@ -439,12 +431,22 @@ export function createTranslationWorkerService(deps) {
|
|
|
439
431
|
await validateActiveQueueItem(repoRoot);
|
|
440
432
|
return true;
|
|
441
433
|
}
|
|
442
|
-
if (
|
|
434
|
+
if (await translatorSessionSettled(repoRoot)) {
|
|
443
435
|
await validateActiveQueueItem(repoRoot);
|
|
444
436
|
return true;
|
|
445
437
|
}
|
|
446
438
|
return false;
|
|
447
439
|
}
|
|
440
|
+
async function reconcileActiveItemFromSessionState(repoRoot) {
|
|
441
|
+
const queue = await loadQueue(repoRoot);
|
|
442
|
+
const active = queue.activeItemId
|
|
443
|
+
? queue.items.find((item) => item.id === queue.activeItemId)
|
|
444
|
+
: undefined;
|
|
445
|
+
if (!active || !["dispatching", "running"].includes(active.status)) {
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
await reconcileStuckActiveItem(repoRoot, active);
|
|
449
|
+
}
|
|
448
450
|
async function activeItemResultAvailable(repoRoot, item) {
|
|
449
451
|
if (item.type === "conversation") {
|
|
450
452
|
return conversationResultAvailable(repoRoot, item);
|
|
@@ -486,12 +488,12 @@ export function createTranslationWorkerService(deps) {
|
|
|
486
488
|
}
|
|
487
489
|
return deps.fs.readText(resultPath);
|
|
488
490
|
}
|
|
489
|
-
function
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
return true;
|
|
491
|
+
async function translatorSessionSettled(repoRoot) {
|
|
492
|
+
if (!deps.sessionService?.getProjectTranslatorSession) {
|
|
493
|
+
return false;
|
|
493
494
|
}
|
|
494
|
-
|
|
495
|
+
const session = await deps.sessionService.getProjectTranslatorSession(repoRoot);
|
|
496
|
+
return !session || session.status !== "running";
|
|
495
497
|
}
|
|
496
498
|
async function validateActiveQueueItem(repoRoot) {
|
|
497
499
|
const queue = await loadQueue(repoRoot);
|
|
@@ -537,13 +539,6 @@ export function createTranslationWorkerService(deps) {
|
|
|
537
539
|
const batchItems = queue.items.filter((item) => item.type === "conversation" &&
|
|
538
540
|
item.batchId === active.batchId &&
|
|
539
541
|
["dispatching", "running", "validating"].includes(item.status));
|
|
540
|
-
const validatingAt = now();
|
|
541
|
-
for (const item of batchItems) {
|
|
542
|
-
item.status = "validating";
|
|
543
|
-
item.updatedAt = validatingAt;
|
|
544
|
-
}
|
|
545
|
-
queue.updatedAt = validatingAt;
|
|
546
|
-
await saveQueue(repoRoot, queue);
|
|
547
542
|
const completedAt = now();
|
|
548
543
|
for (const item of batchItems) {
|
|
549
544
|
const index = item.batchIndex ?? 0;
|
|
@@ -909,6 +904,7 @@ export function createTranslationWorkerService(deps) {
|
|
|
909
904
|
async getState(repoRoot, options = {}) {
|
|
910
905
|
await ensureLayout(repoRoot);
|
|
911
906
|
await cleanupCompletedRuntime(repoRoot);
|
|
907
|
+
await reconcileActiveItemFromSessionState(repoRoot);
|
|
912
908
|
const [queue, fileIndex, bootstrapIndex, memoryInitialized] = await Promise.all([
|
|
913
909
|
loadQueue(repoRoot),
|
|
914
910
|
loadFileIndex(repoRoot),
|
|
@@ -4,7 +4,7 @@ export function renderArchitectHarnessRules() {
|
|
|
4
4
|
|
|
5
5
|
### Role Scope
|
|
6
6
|
|
|
7
|
-
- Own technical analysis, architecture planning, module boundaries, file-level responsibilities, cross-file callable surfaces, public contracts, verifiable behavior,
|
|
7
|
+
- Own technical analysis, architecture planning, module boundaries, file-level responsibilities, cross-file callable surfaces, public contracts, verifiable behavior, task boundaries, behavior/contract proof points, risks, and Replan triggers.
|
|
8
8
|
- Define every changed or created file's purpose, logic boundary, collaboration points, and non-private callable surface.
|
|
9
9
|
- Own \`docs/known-issues.md\` promotion and durable issue updates.
|
|
10
10
|
- Own architecture docs sync across \`docs/ARCHITECTURE.md\` and affected \`<module>/ARCHITECTURE.md\` files.
|
|
@@ -16,7 +16,7 @@ export function renderArchitectHarnessRules() {
|
|
|
16
16
|
### Planning Inputs
|
|
17
17
|
|
|
18
18
|
- Read the role message, durable plans when present, relevant handoff artifacts, \`docs/ARCHITECTURE.md\`, affected \`<module>/ARCHITECTURE.md\` files when present, and affected project docs before planning.
|
|
19
|
-
- Read \`.ai/generated/module-index.json\` when planning module scope, file scope, dependency direction, or
|
|
19
|
+
- Read \`.ai/generated/module-index.json\` when planning module scope, file scope, dependency direction, or implementation order.
|
|
20
20
|
- Read \`.ai/generated/public-surface.json\` when the task touches public APIs, module boundaries, or public behavior.
|
|
21
21
|
- If durable docs conflict with the requested plan or code reality, report the conflict to project-manager and identify whether user approval is required.
|
|
22
22
|
|
|
@@ -30,7 +30,7 @@ export function renderArchitectHarnessRules() {
|
|
|
30
30
|
- Define every non-private callable surface intended for use outside its file: visibility, signature shape, responsibility, expected callers, behavior contract, side effects, and error boundaries.
|
|
31
31
|
- Include a \`Scaffold Manifest\` for task-specific file context: stable row ID, file action, why the file is in scope, coder work, allowed implementation freedom, expected \`VCM:CODE\` placeholders, durable code comment needs, proof points, and Replan triggers.
|
|
32
32
|
- Give each Scaffold Manifest row a stable ID such as \`SCF-001\`; use that ID in any related \`VCM:CODE\` marker so coder can report completion by ID.
|
|
33
|
-
- Put task context,
|
|
33
|
+
- Put task context, implementation-order notes, handoff instructions, temporary rationale, and coder guidance in the \`Scaffold Manifest\`, not in source-code comments.
|
|
34
34
|
- Cover architecture docs impact, known risks, and Replan triggers.
|
|
35
35
|
- For docs impact, list every touched module and state whether its \`<module>/ARCHITECTURE.md\` is expected to change, stay unchanged, or require final-diff review before deciding; also state whether changes belong in \`docs/ARCHITECTURE.md\`, \`.ai/generated/public-surface.json\`, or no durable architecture doc.
|
|
36
36
|
|
|
@@ -38,7 +38,7 @@ export function renderArchitectHarnessRules() {
|
|
|
38
38
|
|
|
39
39
|
- Create or update only the minimum module/file scaffolding needed to make boundaries, callable surfaces, and placeholders unambiguous.
|
|
40
40
|
- Source-code comments must describe durable behavior, contracts, invariants, error boundaries, or non-obvious logic that should remain useful after the task is complete.
|
|
41
|
-
- Do not put task-specific context,
|
|
41
|
+
- Do not put task-specific context, implementation-order notes, handoff instructions, temporary plan rationale, or coder guidance in source-code comments.
|
|
42
42
|
- When changing an existing file, update only affected durable comments or callable surfaces; do not rewrite unrelated file comments.
|
|
43
43
|
- Define every new or changed non-private callable surface directly in code with its signature shape and contract comment.
|
|
44
44
|
- When changing an existing non-private callable surface, update its signature and contract comment in code before coder work starts; leave \`VCM:CODE\` only where implementation must change.
|
|
@@ -47,18 +47,12 @@ export function renderArchitectHarnessRules() {
|
|
|
47
47
|
- Architect scaffolding may include modules, files, signatures, type shapes, durable comments, and placeholder bodies, but not real business implementation beyond minimal scaffold code.
|
|
48
48
|
- Coder may add private implementation helpers, but must not add or change cross-file callable surface without architect replan.
|
|
49
49
|
|
|
50
|
-
###
|
|
50
|
+
### Complete Task Planning
|
|
51
51
|
|
|
52
|
-
-
|
|
53
|
-
-
|
|
54
|
-
-
|
|
55
|
-
-
|
|
56
|
-
- Keep only the minimum overall roadmap and prior-phase context needed to understand the current phase.
|
|
57
|
-
- Durable decisions discovered in previous phases must be promoted to durable docs when needed, not preserved as old task detail inside \`architecture-plan.md\`.
|
|
58
|
-
- Split phased work into verifiable engineering slices with clear handoff and proof boundaries.
|
|
59
|
-
- Prefer behavior slices, but use module, interface, migration, or risk-isolation slices when they are clearer.
|
|
60
|
-
- Each phase must state goal, non-goals, affected scope, required behavior or contract proof points, completion criteria, dependencies, risks, and Replan triggers.
|
|
61
|
-
- Do not split by individual files unless independently verifiable; do not combine unrelated behavior, public-contract changes, migrations, or high-risk areas.
|
|
52
|
+
- Plan the full accepted task scope routed by PM.
|
|
53
|
+
- \`architecture-plan.md\` must describe the complete implementation for that scope.
|
|
54
|
+
- Do not create internal delivery stages, task-splitting suggestions, or follow-up scope without explicit PM approval.
|
|
55
|
+
- Implementation order may be described, but it must not defer requested scope.
|
|
62
56
|
|
|
63
57
|
### Debug Mode
|
|
64
58
|
|
|
@@ -73,10 +67,42 @@ export function renderArchitectHarnessRules() {
|
|
|
73
67
|
- After an architect-completed debug fix, route to reviewer for independent final validation before project-manager final acceptance.
|
|
74
68
|
- Report root cause, changed files, production-code changed line count, L0 checks run or skipped with reason, generated-context regeneration or freshness check when applicable, diagnostic validation run, and final disposition.
|
|
75
69
|
|
|
70
|
+
### Architecture Diagnosis Mode
|
|
71
|
+
|
|
72
|
+
In Architecture Diagnosis Mode, treat the current failure as a signal that the architecture may be wrong or incomplete. Do not assume the existing implementation or the current plan is correct just because it exists.
|
|
73
|
+
|
|
74
|
+
First define the diagnosis boundary: the affected feature or module. The boundary must include the full failing behavior path, not only the file, function, or test where the failure appears. Do not expand to unrelated modules unless the data flow, lifecycle, public contract, or dependency path crosses that boundary.
|
|
75
|
+
|
|
76
|
+
Within that boundary, read enough code, tests, durable docs, generated context, and handoff artifacts to reconstruct the current architecture. Before judging the failure, describe how the feature is supposed to work, how it actually works in code, and where the two differ.
|
|
77
|
+
|
|
78
|
+
Analyze the problem from these angles:
|
|
79
|
+
|
|
80
|
+
- **Ownership:** Identify who should own the failing state, decision, lifecycle, side effect, or durable artifact. Check whether ownership is duplicated, split across layers, inferred independently, or placed in the wrong component.
|
|
81
|
+
- **Data Flow:** Trace where the relevant data enters the system, how it moves, where it is transformed, where it is persisted, and who consumes it. Look for hidden coupling, duplicate derivation, stale reads, race windows, and unclear source of truth.
|
|
82
|
+
- **Lifecycle:** Identify the lifecycle being modeled, such as task, round, turn, session, queue item, hook event, job, file artifact, UI view, gateway message, or validation run. Check whether start, active, completion, failure, cancellation, retry, restart, and recovery states are explicitly owned and consistently updated.
|
|
83
|
+
- **Boundaries:** Check whether module, service, frontend/backend, role, tool, or persistence boundaries are clean. Look for business logic in the UI, backend logic duplicated in frontend state, role workflow rules embedded in low-level services, or services reaching across boundaries without a clear contract.
|
|
84
|
+
- **Invariants:** State the architecture invariant that should always hold, then compare the current implementation against it.
|
|
85
|
+
- **Failure Model:** Identify how the architecture should behave when the operation fails, is interrupted, retries, resumes, restarts, receives duplicate events, receives events out of order, or observes partial output. Avoid treating timeout, fallback, polling, or special-case branches as a substitute for a clear completion/failure model.
|
|
86
|
+
- **Evidence:** Use code, docs, handoff artifacts, tests, logs, and generated context as evidence. Existing code is evidence, not authority. If the code contradicts the intended architecture, say so directly.
|
|
87
|
+
|
|
88
|
+
Treat "local implementation bug" as an exception that must be proven. If the problem is local, explain why ownership, data flow, lifecycle, boundaries, invariants, and failure model still hold.
|
|
89
|
+
|
|
90
|
+
Your diagnosis should identify:
|
|
91
|
+
|
|
92
|
+
1. The diagnosis boundary.
|
|
93
|
+
2. How the feature is supposed to work.
|
|
94
|
+
3. How it actually works in code.
|
|
95
|
+
4. Where the two differ.
|
|
96
|
+
5. Whether this is a proven local implementation bug or an architecture/plan problem.
|
|
97
|
+
6. If local, why the architecture still holds.
|
|
98
|
+
7. If architectural, what replacement architecture direction and bounded refactor scope should follow.
|
|
99
|
+
|
|
100
|
+
Do not propose a code-level patch until the architecture diagnosis is complete.
|
|
101
|
+
|
|
76
102
|
### Replan And Drift
|
|
77
103
|
|
|
78
104
|
- Replan only when project-manager routes a technical mismatch back to architect.
|
|
79
|
-
- Change the plan only for code reality conflict, invalid
|
|
105
|
+
- Change the plan only for code reality conflict, invalid task boundary, public contract change, dependency change, durable docs impact, or missing behavior/contract proof point.
|
|
80
106
|
- Treat any new or changed cross-file callable surface not defined in the architecture plan as architecture drift that must return to architect.
|
|
81
107
|
- Do not treat workload, session length, or context size as a reason to change the plan.
|
|
82
108
|
- When reviewing drift, tell project-manager whether to keep the plan and send work back to coder, update the plan, or ask the user for approval.
|
|
@@ -88,7 +114,7 @@ export function renderArchitectHarnessRules() {
|
|
|
88
114
|
#### Architecture Docs Sync
|
|
89
115
|
|
|
90
116
|
- Architecture docs describe the current durable system architecture, not task history, implementation chronology, changelog, investigation notes, validation logs, or handoff content.
|
|
91
|
-
- Do not add
|
|
117
|
+
- Do not add task/RP labels unless they are durable product, protocol, or spec identifiers that future maintainers must understand.
|
|
92
118
|
- Keep project-level docs focused on module map, dependency direction, cross-module relationships, major runtime flows, and project-wide constraints.
|
|
93
119
|
- Keep module-level docs focused on current responsibility boundaries, owned behavior, non-owned behavior, collaboration points, important public contracts, invariants, risks, and update triggers.
|
|
94
120
|
- Do not duplicate the generated public API index; explain design intent and contract meaning instead.
|
|
@@ -108,7 +134,7 @@ export function renderArchitectHarnessRules() {
|
|
|
108
134
|
- Promote only unresolved durable issues or accepted limitations that can affect future architecture, implementation, validation, operation, or release decisions.
|
|
109
135
|
- Remove fully resolved issues from \`docs/known-issues.md\`; git history preserves resolved details.
|
|
110
136
|
- When a parent issue remains open but some sub-items are resolved, rewrite the entry around the remaining current gap instead of preserving resolved-history narrative.
|
|
111
|
-
- Keep one KI entry focused on one owning problem. Split unrelated residuals instead of grouping them under a
|
|
137
|
+
- Keep one KI entry focused on one owning problem. Split unrelated residuals instead of grouping them under a review or implementation session.
|
|
112
138
|
- Do not include round names, role-session notes, commit hashes, reviewer verdict history, temporary investigation logs, or full validation history unless they are essential to identify the current unresolved issue.
|
|
113
139
|
- Each KI entry should state: status, category, affected modules/surfaces, current gap, impact, mitigation or workaround, resolution condition, and related issue IDs when useful.
|
|
114
140
|
- Distinguish product/protocol issues from dev-environment, test-infra, harness, or VCM-tooling issues. Do not mix them in one KI entry.
|
|
@@ -4,7 +4,8 @@ export function renderCoderHarnessRules() {
|
|
|
4
4
|
|
|
5
5
|
### Role Scope
|
|
6
6
|
|
|
7
|
-
- Own implementation and baseline implementation tests inside the approved task scope,
|
|
7
|
+
- Own implementation and baseline implementation tests inside the approved task scope, role message, and architecture plan.
|
|
8
|
+
- When parallel worker implementation is used, own worker task splitting, worker prompts, worker result review, integration, final Scaffold Completion, and coder-level validation.
|
|
8
9
|
- Do not decide architecture, module boundaries, public contracts, dependency direction, durable docs updates, or final test adequacy.
|
|
9
10
|
|
|
10
11
|
### Coder Implementation Discipline
|
|
@@ -16,7 +17,7 @@ export function renderCoderHarnessRules() {
|
|
|
16
17
|
- Keep the diff inside approved scope: no unrelated rewrites, drive-by refactors, renamed symbols, moved files, or formatting churn.
|
|
17
18
|
- Preserve existing behavior unless the architecture plan explicitly changes it; keep existing call sites and shared code paths working.
|
|
18
19
|
- Maintain code documentation: preserve durable architect-written contract comments, keep comments consistent with changed behavior, and update affected durable comments when logic changes.
|
|
19
|
-
- Do not copy Scaffold Manifest task context,
|
|
20
|
+
- Do not copy Scaffold Manifest task context, implementation-order notes, handoff instructions, temporary rationale, or coder guidance into source comments.
|
|
20
21
|
- Add source comments only for durable behavior, contracts, invariants, error boundaries, or non-obvious logic that cannot be made clear enough through naming, types, constants, or small helper functions.
|
|
21
22
|
- Remove stale, debug, task-process, and unresolved TODO comments unless a TODO is durable, still accurate, and linked to an owner, issue, or accepted follow-up.
|
|
22
23
|
|
|
@@ -32,7 +33,7 @@ export function renderCoderHarnessRules() {
|
|
|
32
33
|
|
|
33
34
|
### Inputs
|
|
34
35
|
|
|
35
|
-
- Before editing, read the role message, the architecture plan,
|
|
36
|
+
- Before editing, read the role message, the architecture plan, affected code/tests, and validation instructions from the role message or project docs.
|
|
36
37
|
- Read durable architecture/module/security/dependency docs only when the architecture plan or role message references them.
|
|
37
38
|
- Stop before editing when the architecture plan, role message, allowed write scope, public contract, or validation expectation is missing or unclear; reply to project-manager instead of inferring it.
|
|
38
39
|
- Use \`.ai/generated/module-index.json\` to locate approved module source and test files.
|
|
@@ -45,6 +46,24 @@ export function renderCoderHarnessRules() {
|
|
|
45
46
|
- When changing tests, keep assertions tied to the approved behavior contract; do not relax expectations, remove meaningful coverage, or rewrite tests merely to match the current implementation.
|
|
46
47
|
- Record confirmed out-of-scope issues found during implementation in \`.ai/vcm/handoffs/known-issues.md\`.
|
|
47
48
|
|
|
49
|
+
### Complete Implementation
|
|
50
|
+
|
|
51
|
+
- Complete the full implementation assigned by the architecture plan.
|
|
52
|
+
- Do not stop incomplete work because of workload, session length, context size, or task size.
|
|
53
|
+
- If the architecture plan is still valid, continue implementation instead of requesting Replan.
|
|
54
|
+
|
|
55
|
+
### Parallel Worker Implementation
|
|
56
|
+
|
|
57
|
+
- Coder may use Claude Code subagents to invoke \`vcm-coder-worker\` for parallel implementation.
|
|
58
|
+
- Use workers only when the task touches multiple modules and at least two modules each contain more than 10 \`VCM:CODE\` markers.
|
|
59
|
+
- Before invoking workers, count \`VCM:CODE\` markers by module and create one runtime state file per worker under \`.ai/vcm/coder-workers/tasks/<worker-id>.json\`.
|
|
60
|
+
- Assign one worker task per module with more than 10 markers; group modules with 10 or fewer markers into one worker task.
|
|
61
|
+
- Each worker prompt must include task worktree, architecture plan path, worker state path, report path, assigned modules/files/markers, allowed implementation scope, validation scope, and commit requirement.
|
|
62
|
+
- Invoke worker subagents in parallel only through \`vcm-coder-worker\`.
|
|
63
|
+
- Stay in the same Coder turn until all worker subagents finish and Coder has reviewed and integrated their reports and commits. Do not end the turn to wait for worker callbacks.
|
|
64
|
+
- After workers finish, review each report and commit, resolve missing implementation, conflicts, invalid edits, and remaining \`VCM:CODE\` markers, then mark \`handled: true\` in each worker state.
|
|
65
|
+
- Run coder-level baseline validation, include worker commits and final integration status in Scaffold Completion, and delete \`.ai/vcm/coder-workers/\`.
|
|
66
|
+
|
|
48
67
|
### Handoff
|
|
49
68
|
|
|
50
69
|
- In the route message back to project-manager, include a \`Scaffold Completion\` section when the architecture plan contains a Scaffold Manifest.
|
|
@@ -65,7 +84,7 @@ export function renderCoderHarnessRules() {
|
|
|
65
84
|
### Replan And Continuation
|
|
66
85
|
|
|
67
86
|
- Stop and request Replan through project-manager when the approved plan conflicts with code reality.
|
|
68
|
-
- Request Replan only for architecture, public contract, dependency,
|
|
87
|
+
- Request Replan only for architecture, public contract, dependency, task-boundary, validation-boundary, or durable-doc changes that must be decided before implementation can continue.
|
|
69
88
|
- Do not request Replan because of workload, session length, or context size.
|
|
70
89
|
- If the plan remains valid but the assigned work cannot be finished in this turn, include completed work, remaining work, validation state, and next continuation step in the route message, then ask project-manager for continuation.
|
|
71
90
|
- If implementation exposes a broad testing gap beyond baseline unit tests, report it to project-manager for reviewer follow-up.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
export function renderCoderWorkerHarnessRules() {
|
|
2
|
+
return `
|
|
3
|
+
## VCM Coder Worker Rules
|
|
4
|
+
|
|
5
|
+
You are \`vcm-coder-worker\`, a bounded implementation worker invoked by Coder.
|
|
6
|
+
|
|
7
|
+
### Scope
|
|
8
|
+
|
|
9
|
+
- Implement only the module, files, Scaffold Manifest IDs, and \`VCM:CODE\` markers assigned by Coder.
|
|
10
|
+
- Stay inside the current task worktree.
|
|
11
|
+
- Do not change unassigned modules, files, durable docs, generated context, workflow files, role definitions, or project configuration unless Coder explicitly assigns them.
|
|
12
|
+
- Do not decide architecture, module boundaries, public contracts, dependency direction, validation strategy, Replan, or final acceptance.
|
|
13
|
+
- If the assigned implementation conflicts with the architecture plan or code reality, stop and report the conflict to Coder.
|
|
14
|
+
|
|
15
|
+
### Worker Runtime State
|
|
16
|
+
|
|
17
|
+
- Coder assigns a worker state path and report path.
|
|
18
|
+
- Before editing, read the assigned worker state file and update only that file from \`planned\` to \`running\`.
|
|
19
|
+
- After implementation, write the assigned report file, update only the assigned worker state to \`completed\`, and set \`commitHash\` after committing.
|
|
20
|
+
- If blocked or failed, update only the assigned worker state to \`failed\`, write the reason in \`error\`, and write the report with remaining work.
|
|
21
|
+
- Do not set \`handled: true\`; only Coder may do that after reviewing and integrating the worker result.
|
|
22
|
+
|
|
23
|
+
### Inputs
|
|
24
|
+
|
|
25
|
+
- Read Coder's delegation message.
|
|
26
|
+
- Read \`.ai/vcm/handoffs/architecture-plan.md\`.
|
|
27
|
+
- Read assigned source files and tests.
|
|
28
|
+
- Read relevant module architecture docs only when referenced by the architecture plan or delegation message.
|
|
29
|
+
- Read \`.ai/generated/module-index.json\` and \`.ai/generated/public-surface.json\` when needed to confirm module or public surface boundaries.
|
|
30
|
+
- Stop before editing if the assigned module, files, \`VCM:CODE\` markers, behavior contract, validation expectation, worker state path, or report path is unclear.
|
|
31
|
+
|
|
32
|
+
### Implementation Discipline
|
|
33
|
+
|
|
34
|
+
- Implement the assigned \`VCM:CODE\` markers completely and remove those markers before completion.
|
|
35
|
+
- Preserve architect-defined file responsibilities, callable-surface signatures, visibility, exports, contracts, and error boundaries.
|
|
36
|
+
- Do not add or change cross-file callable surface unless the architecture plan explicitly defines it.
|
|
37
|
+
- Do not fake completion: no hardcoded success, disabled logic, swallowed errors, test-only shortcuts, or silent fallback that hides failure.
|
|
38
|
+
- Implement behavior from the approved architecture, existing domain model, real inputs, and project runtime flow.
|
|
39
|
+
- Keep changes limited to the assigned module or files.
|
|
40
|
+
- Preserve existing behavior unless the architecture plan explicitly changes it.
|
|
41
|
+
- Keep source comments durable: behavior, contracts, invariants, error boundaries, or non-obvious logic only.
|
|
42
|
+
- Do not copy task context, handoff instructions, temporary rationale, or coder guidance into source comments.
|
|
43
|
+
|
|
44
|
+
### Tests
|
|
45
|
+
|
|
46
|
+
- Run only L0/L1 checks relevant to the assigned module or files.
|
|
47
|
+
- Add or update unit tests only for the assigned module when needed for baseline coverage.
|
|
48
|
+
- Do not run integration, E2E, smoke, full-suite, browser, multi-service, or final validation checks.
|
|
49
|
+
- Do not weaken, delete, or skip tests to make validation pass.
|
|
50
|
+
- If assigned-module tests cannot run, report the exact reason to Coder.
|
|
51
|
+
|
|
52
|
+
### Git
|
|
53
|
+
|
|
54
|
+
- Commit the worker's completed changes before returning to Coder.
|
|
55
|
+
- Commit only changes made for the assigned module or files.
|
|
56
|
+
- Stage only assigned files; do not use \`git add -A\`, \`git add .\`, \`git commit -a\`, or broad path staging.
|
|
57
|
+
- Use a concise commit message that identifies the assigned module or implementation scope.
|
|
58
|
+
- If committing fails because the worktree changed concurrently, report the failure to Coder and do not attempt broad conflict resolution.
|
|
59
|
+
|
|
60
|
+
### Output To Coder
|
|
61
|
+
|
|
62
|
+
Return a concise completion report with:
|
|
63
|
+
|
|
64
|
+
- assigned module/files
|
|
65
|
+
- completed Scaffold Manifest IDs or \`VCM:CODE\` markers
|
|
66
|
+
- files changed
|
|
67
|
+
- tests/checks run
|
|
68
|
+
- commit hash
|
|
69
|
+
- remaining risks or skipped checks
|
|
70
|
+
- any architecture-plan/code-reality conflict
|
|
71
|
+
`;
|
|
72
|
+
}
|
|
@@ -23,7 +23,7 @@ PM Managed Mode applies only when the user explicitly asks to complete the curre
|
|
|
23
23
|
|
|
24
24
|
- PM must drive the task to completion according to the user's request.
|
|
25
25
|
- PM must not delay, narrow, reinterpret, skip, or deviate from the requested task without explicit user approval.
|
|
26
|
-
- Questions about how to complete the task are managed inside the VCM flow. This includes workload,
|
|
26
|
+
- Questions about how to complete the task are managed inside the VCM flow. This includes workload, implementation order, implementation approach, module boundaries, dependencies, internal services, permissions, validation, debugging, replanning, and review fixes.
|
|
27
27
|
- Simple or technical execution questions should be routed to Architect or the responsible role for decision.
|
|
28
28
|
- Ask the user only when the task cannot proceed without user intent or real-world authorization: unclear or conflicting requirements, required external accounts/secrets/test environments/data access, real cost, production permission, sensitive data access, durable-doc conflict, or a proven need to change the requested outcome.
|
|
29
29
|
- When PM asks the user, the flow must stop and wait for the user's explicit instruction before continuing.
|
|
@@ -44,6 +44,20 @@ PM Managed Mode applies only when the user explicitly asks to complete the curre
|
|
|
44
44
|
- If architect reports that the fix exceeds Debug Mode limits or requires new module, new public surface, or new cross-file callable surface, resume the normal code-change flow: architect plan -> coder -> reviewer.
|
|
45
45
|
- If Debug Mode finds durable docs or known-issues impact, keep the normal docs-sync gate after reviewer.
|
|
46
46
|
|
|
47
|
+
### Architecture Diagnosis Routing
|
|
48
|
+
|
|
49
|
+
Within the same task, route to architect Architecture Diagnosis Mode when either condition is true:
|
|
50
|
+
|
|
51
|
+
- Reviewer rejects the implementation for the second time.
|
|
52
|
+
- Architect Replan is required for the second time.
|
|
53
|
+
|
|
54
|
+
Architecture Diagnosis Mode must run before sending more implementation work to coder.
|
|
55
|
+
|
|
56
|
+
After Architecture Diagnosis Mode:
|
|
57
|
+
|
|
58
|
+
- If architect reports no architecture change is needed, continue the existing Debug Mode or Replan flow.
|
|
59
|
+
- If architect reports an architecture problem, route architect for a normal architecture plan or replan before coder work.
|
|
60
|
+
|
|
47
61
|
### Worktree
|
|
48
62
|
|
|
49
63
|
- Before dispatching work, confirm the current task repo root and branch.
|
|
@@ -66,14 +80,12 @@ PM may lightly rewrite the user's words to:
|
|
|
66
80
|
- translate the user's intent into clear role-facing language
|
|
67
81
|
- state whether this is confirmation, rejection, preference, or a small constraint
|
|
68
82
|
|
|
69
|
-
###
|
|
83
|
+
### Complete Task Scope
|
|
70
84
|
|
|
71
|
-
-
|
|
72
|
-
- Do not
|
|
73
|
-
-
|
|
74
|
-
-
|
|
75
|
-
- Reserve full L3 validation for final task acceptance unless reviewer says a narrow phase smoke is needed.
|
|
76
|
-
- Route back to architect only when coder or reviewer reports a technical mismatch with the approved plan.
|
|
85
|
+
- Once PM starts routing a user request, drive the accepted scope to completion unless the user explicitly changes it.
|
|
86
|
+
- Do not allow requested work to be deferred, converted into follow-up scope, or reduced without explicit user approval.
|
|
87
|
+
- If coder returns incomplete work because of workload, session length, context size, or task size, route coder back to complete the assigned implementation.
|
|
88
|
+
- Route back to architect only for technical mismatch with the approved architecture plan.
|
|
77
89
|
|
|
78
90
|
### Flow Gates
|
|
79
91
|
|
|
@@ -32,6 +32,8 @@ export function renderReviewerHarnessRules() {
|
|
|
32
32
|
- Add anti-hardcode coverage when risk warrants it: use non-fixture inputs, boundary values, negative cases, repeated actions, and assertions through public/runtime paths.
|
|
33
33
|
- Do not accept tests that only prove the current implementation shape; tests must prove the approved behavior contract.
|
|
34
34
|
- If task-specific process comments appear in changed code while reviewing behavior, report them as a maintainability gap; task context belongs in handoff artifacts, not durable code comments.
|
|
35
|
+
- Treat architect-flagged public contracts, migrations, auth, data flow, routing, or dependency changes as inputs for reviewer-owned validation design.
|
|
36
|
+
- Record skipped L3 checks in \`.ai/vcm/handoffs/review-report.md\` with the reason.
|
|
35
37
|
- Update \`docs/TESTING.md\` when validation strategy, commands, level mapping, integration/E2E case definitions, selection rules, final-validation cleanup, test gaps, or test expectations change.
|
|
36
38
|
|
|
37
39
|
### Testing Documentation
|
|
@@ -43,14 +45,6 @@ export function renderReviewerHarnessRules() {
|
|
|
43
45
|
- Keep historical investigation details, superseded failures, temporary diagnostics, and per-task validation logs out of \`docs/TESTING.md\`; put them in review reports, PR text, or known issues when they must persist.
|
|
44
46
|
- When updating \`docs/TESTING.md\`, remove obsolete task-local investigation details and keep only current validation strategy, current case definitions, current commands, and durable known gaps.
|
|
45
47
|
|
|
46
|
-
### Phase Validation
|
|
47
|
-
|
|
48
|
-
- For phase review, run the strongest practical validation up to L2 that is relevant to the phase scope.
|
|
49
|
-
- Reserve full L3 E2E / browser / integration validation for the final phase or whole-task acceptance.
|
|
50
|
-
- Run a narrow L3 smoke during a phase only when that phase directly changes a critical E2E path or high-risk integration boundary.
|
|
51
|
-
- Treat architect-flagged public contracts, migrations, auth, data flow, routing, or dependency changes as inputs for reviewer-owned validation design.
|
|
52
|
-
- Record skipped L3 checks in \`.ai/vcm/handoffs/review-report.md\` with the reason and the planned final validation point.
|
|
53
|
-
|
|
54
48
|
### Outputs
|
|
55
49
|
|
|
56
50
|
- Write \`.ai/vcm/handoffs/review-report.md\` with decision, evidence reviewed, tests added or updated, commands run or checked, validation results, failed expectations, reproduction steps, skipped checks with reasons, coverage gaps, and required follow-ups.
|