vibe-coding-master 0.6.22 → 0.7.0
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/README.md +37 -3
- package/dist/backend/adapters/filesystem.js +8 -0
- package/dist/backend/api/harness-routes.js +54 -0
- package/dist/backend/api/runtime-state-routes.js +6 -2
- package/dist/backend/api/task-routes.js +2 -32
- package/dist/backend/cli/install-vcm-harness.js +1 -1
- package/dist/backend/gateway/gateway-service.js +4 -37
- package/dist/backend/server.js +50 -15
- package/dist/backend/services/app-settings-service.js +1 -0
- package/dist/backend/services/auto-memory-service.js +760 -0
- package/dist/backend/services/claude-hook-service.js +108 -2
- package/dist/backend/services/claude-transcript-reply.js +81 -1
- package/dist/backend/services/harness-service.js +1 -1
- package/dist/backend/services/runtime-coordinator-service.js +69 -1
- package/dist/backend/services/runtime-recovery-service.js +6 -0
- package/dist/backend/services/session-service.js +3 -0
- package/dist/backend/services/task-close-service.js +88 -0
- package/dist/backend/services/task-service.js +152 -35
- package/dist/backend/services/turn-reconciler-service.js +122 -0
- package/dist/backend/templates/harness/architect-agent.js +3 -0
- package/dist/backend/templates/harness/claude-root.js +3 -1
- package/dist/backend/templates/harness/coder-agent.js +3 -0
- package/dist/backend/templates/harness/gate-review.js +3 -0
- package/dist/backend/templates/harness/harness-engineer-agent.js +32 -8
- package/dist/backend/templates/harness/project-manager-agent.js +3 -0
- package/dist/backend/templates/harness/role-memory.js +9 -0
- package/dist/backend/templates/harness/tester-agent.js +3 -0
- package/dist/shared/types/memory.js +8 -0
- package/dist-frontend/assets/{index-DmSHDyiQ.css → index-C2QzumXk.css} +1 -1
- package/dist-frontend/assets/index-e8Tqa8Qh.js +97 -0
- package/dist-frontend/index.html +2 -2
- package/package.json +1 -1
- package/dist-frontend/assets/index-DYBg_qYS.js +0 -96
|
@@ -118,61 +118,130 @@ export function createTaskService(deps) {
|
|
|
118
118
|
await this.saveTask(repoRoot, updated);
|
|
119
119
|
return updated;
|
|
120
120
|
},
|
|
121
|
-
async
|
|
122
|
-
assertValidTaskSlug(taskSlug);
|
|
123
|
-
if (!deps.fs.removePath) {
|
|
124
|
-
throw new VcmError({
|
|
125
|
-
code: "FILESYSTEM_REMOVE_UNAVAILABLE",
|
|
126
|
-
message: "This VCM runtime cannot remove task files.",
|
|
127
|
-
statusCode: 500
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
const config = await deps.projectService.loadConfig(repoRoot);
|
|
121
|
+
async markTaskCleaned(repoRoot, taskSlug) {
|
|
131
122
|
const task = await this.loadTask(repoRoot, taskSlug);
|
|
123
|
+
if (task.cleanupStatus === "cleaned" && task.cleanedAt) {
|
|
124
|
+
return task;
|
|
125
|
+
}
|
|
126
|
+
const timestamp = now();
|
|
127
|
+
const updated = {
|
|
128
|
+
...task,
|
|
129
|
+
status: "stopped",
|
|
130
|
+
cleanupStatus: "cleaned",
|
|
131
|
+
cleanedAt: timestamp,
|
|
132
|
+
updatedAt: timestamp
|
|
133
|
+
};
|
|
134
|
+
await this.saveTask(repoRoot, updated);
|
|
135
|
+
return updated;
|
|
136
|
+
},
|
|
137
|
+
async cleanupTask(repoRoot, taskSlug) {
|
|
138
|
+
assertValidTaskSlug(taskSlug);
|
|
139
|
+
const task = await this.markTaskCleaned(repoRoot, taskSlug);
|
|
132
140
|
const taskRepoRoot = getTaskRuntimeRepoRoot(task);
|
|
133
141
|
const taskStoreRoot = deps.projectService.getProjectDataRoot(repoRoot);
|
|
134
142
|
const taskPath = getTaskPath(taskStoreRoot, taskSlug);
|
|
135
|
-
const statePaths = getTaskStatePaths(taskStoreRoot, taskRepoRoot, config.stateRoot, config.handoffRoot, taskSlug);
|
|
136
|
-
const removedStatePaths = [];
|
|
137
143
|
const warnings = [];
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
144
|
+
if (!deps.fs.removePath) {
|
|
145
|
+
warnings.push("This VCM runtime cannot remove task files; the task remains logically closed.");
|
|
146
|
+
}
|
|
147
|
+
let stateRoot = ".ai/vcm";
|
|
148
|
+
let handoffRoot = task.handoffDir;
|
|
149
|
+
try {
|
|
150
|
+
const config = await deps.projectService.loadConfig(repoRoot);
|
|
151
|
+
stateRoot = config.stateRoot;
|
|
152
|
+
handoffRoot = config.handoffRoot;
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
warnings.push(`Unable to load project cleanup paths; using task defaults: ${describeError(error)}`);
|
|
156
|
+
}
|
|
157
|
+
const statePaths = getTaskStatePaths(taskStoreRoot, taskRepoRoot, stateRoot, handoffRoot, taskSlug);
|
|
158
|
+
const removedStatePaths = [];
|
|
159
|
+
let worktreeRemoved = false;
|
|
160
|
+
try {
|
|
161
|
+
assertTaskWorktreePath(repoRoot, task.worktreePath);
|
|
162
|
+
worktreeRemoved = await removeTaskWorktreeBestEffort(deps.fs, deps.git, repoRoot, task.worktreePath, warnings);
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
warnings.push(`Skipped unsafe task worktree path ${task.worktreePath}: ${describeError(error)}`);
|
|
166
|
+
}
|
|
167
|
+
const branchCleanup = await deleteTaskBranchBestEffort(deps.git, repoRoot, task.branch, warnings);
|
|
142
168
|
for (const statePath of statePaths.filter((candidate) => candidate !== taskPath)) {
|
|
143
169
|
await removeWorktreeStatePathBestEffort(deps.fs, statePath, removedStatePaths, warnings);
|
|
144
170
|
}
|
|
145
|
-
|
|
146
|
-
|
|
171
|
+
let stateRemoved = false;
|
|
172
|
+
if (worktreeRemoved && branchCleanup.resolved) {
|
|
173
|
+
try {
|
|
174
|
+
await deps.fs.removePath?.(taskPath, { recursive: true, force: true });
|
|
175
|
+
stateRemoved = !(await deps.fs.pathExists(taskPath));
|
|
176
|
+
if (stateRemoved) {
|
|
177
|
+
removedStatePaths.push(taskPath);
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
warnings.push(`Task state remained after cleanup: ${taskPath}`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
warnings.push(`Unable to remove cleaned task state ${taskPath}: ${describeError(error)}`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
warnings.push("Retained cleaned task state so unresolved resource cleanup can be retried later.");
|
|
189
|
+
}
|
|
147
190
|
return {
|
|
148
191
|
taskSlug,
|
|
149
|
-
|
|
192
|
+
taskClosed: true,
|
|
193
|
+
worktreeRemoved,
|
|
194
|
+
branchDeleted: branchCleanup.resolved,
|
|
195
|
+
stateRemoved,
|
|
196
|
+
removedWorktreePath: worktreeRemoved ? task.worktreePath : null,
|
|
150
197
|
removedStatePaths,
|
|
151
|
-
deletedBranch: task.branch,
|
|
152
|
-
cleanedAt,
|
|
198
|
+
deletedBranch: branchCleanup.deleted ? task.branch : null,
|
|
199
|
+
cleanedAt: task.cleanedAt ?? now(),
|
|
153
200
|
warnings: warnings.length > 0 ? warnings : undefined
|
|
154
201
|
};
|
|
155
202
|
}
|
|
156
203
|
};
|
|
157
204
|
}
|
|
158
|
-
async function
|
|
159
|
-
|
|
160
|
-
|
|
205
|
+
async function removeTaskWorktreeBestEffort(fs, git, repoRoot, worktreePath, warnings) {
|
|
206
|
+
let wasRegistered;
|
|
207
|
+
try {
|
|
208
|
+
wasRegistered = await git.isWorktreeRegistered(repoRoot, worktreePath);
|
|
209
|
+
}
|
|
210
|
+
catch (error) {
|
|
211
|
+
warnings.push(`Unable to inspect task worktree registration for ${worktreePath}: ${describeError(error)}`);
|
|
212
|
+
}
|
|
213
|
+
if (wasRegistered !== false) {
|
|
161
214
|
try {
|
|
162
|
-
await git.removeWorktree(repoRoot, worktreePath, { force });
|
|
215
|
+
await git.removeWorktree(repoRoot, worktreePath, { force: true });
|
|
163
216
|
}
|
|
164
217
|
catch (error) {
|
|
165
218
|
await pruneWorktreesBestEffort(git, repoRoot, warnings);
|
|
166
|
-
|
|
167
|
-
|
|
219
|
+
let stillRegistered = true;
|
|
220
|
+
try {
|
|
221
|
+
stillRegistered = await git.isWorktreeRegistered(repoRoot, worktreePath);
|
|
222
|
+
}
|
|
223
|
+
catch (inspectionError) {
|
|
224
|
+
warnings.push(`Unable to verify task worktree registration after forced removal: ${describeError(inspectionError)}`);
|
|
225
|
+
}
|
|
226
|
+
if (stillRegistered) {
|
|
227
|
+
warnings.push(`Unable to force-remove Git worktree ${worktreePath}: ${describeError(error)}`);
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
warnings.push(`Git worktree metadata was already cleared for ${worktreePath}; continuing cleanup.`);
|
|
168
231
|
}
|
|
169
|
-
warnings.push(`Git worktree metadata was already cleared for ${worktreePath}; continuing cleanup.`);
|
|
170
232
|
}
|
|
171
233
|
}
|
|
172
234
|
else {
|
|
173
235
|
await pruneWorktreesBestEffort(git, repoRoot, warnings);
|
|
174
236
|
}
|
|
175
|
-
|
|
237
|
+
let staleDirectoryExists = true;
|
|
238
|
+
try {
|
|
239
|
+
staleDirectoryExists = await fs.pathExists(worktreePath);
|
|
240
|
+
}
|
|
241
|
+
catch (error) {
|
|
242
|
+
warnings.push(`Unable to inspect stale task worktree directory ${worktreePath}: ${describeError(error)}`);
|
|
243
|
+
}
|
|
244
|
+
if (staleDirectoryExists) {
|
|
176
245
|
try {
|
|
177
246
|
await fs.removePath?.(worktreePath, { recursive: true, force: true });
|
|
178
247
|
}
|
|
@@ -180,19 +249,67 @@ async function removeTaskWorktreeIdempotent(fs, git, repoRoot, worktreePath, for
|
|
|
180
249
|
warnings.push(`Unable to remove stale task worktree directory ${worktreePath}: ${describeError(error)}`);
|
|
181
250
|
}
|
|
182
251
|
}
|
|
252
|
+
let pathExists = true;
|
|
253
|
+
let registered = true;
|
|
254
|
+
try {
|
|
255
|
+
pathExists = await fs.pathExists(worktreePath);
|
|
256
|
+
}
|
|
257
|
+
catch (error) {
|
|
258
|
+
warnings.push(`Unable to verify task worktree directory cleanup: ${describeError(error)}`);
|
|
259
|
+
}
|
|
260
|
+
try {
|
|
261
|
+
registered = await git.isWorktreeRegistered(repoRoot, worktreePath);
|
|
262
|
+
}
|
|
263
|
+
catch (error) {
|
|
264
|
+
warnings.push(`Unable to verify task worktree metadata cleanup: ${describeError(error)}`);
|
|
265
|
+
}
|
|
266
|
+
return !pathExists && !registered;
|
|
183
267
|
}
|
|
184
|
-
async function
|
|
185
|
-
|
|
186
|
-
|
|
268
|
+
async function deleteTaskBranchBestEffort(git, repoRoot, branch, warnings) {
|
|
269
|
+
let branchExists;
|
|
270
|
+
try {
|
|
271
|
+
branchExists = await git.branchExists(repoRoot, branch);
|
|
272
|
+
}
|
|
273
|
+
catch (error) {
|
|
274
|
+
warnings.push(`Unable to inspect task branch ${branch}: ${describeError(error)}`);
|
|
187
275
|
}
|
|
276
|
+
if (branchExists === false) {
|
|
277
|
+
return { resolved: true, deleted: false };
|
|
278
|
+
}
|
|
279
|
+
await warnAboutDiscardedCommits(git, repoRoot, branch, warnings);
|
|
188
280
|
try {
|
|
189
|
-
await git.deleteBranch(repoRoot, branch, { force });
|
|
281
|
+
await git.deleteBranch(repoRoot, branch, { force: true });
|
|
282
|
+
return { resolved: true, deleted: true };
|
|
190
283
|
}
|
|
191
284
|
catch (error) {
|
|
192
|
-
|
|
285
|
+
try {
|
|
286
|
+
if (!(await git.branchExists(repoRoot, branch))) {
|
|
287
|
+
return { resolved: true, deleted: true };
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
catch (inspectionError) {
|
|
291
|
+
warnings.push(`Unable to verify task branch cleanup for ${branch}: ${describeError(inspectionError)}`);
|
|
292
|
+
}
|
|
293
|
+
warnings.push(`Unable to force-delete task branch ${branch}: ${describeError(error)}`);
|
|
294
|
+
return { resolved: false, deleted: false };
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
async function warnAboutDiscardedCommits(git, repoRoot, branch, warnings) {
|
|
298
|
+
try {
|
|
299
|
+
const baseBranch = await git.getCurrentBranch(repoRoot);
|
|
300
|
+
const commits = await git.getCommitList(repoRoot, `${baseBranch}..${branch}`);
|
|
301
|
+
if (commits.length === 0) {
|
|
193
302
|
return;
|
|
194
303
|
}
|
|
195
|
-
|
|
304
|
+
const summary = commits
|
|
305
|
+
.slice(0, 5)
|
|
306
|
+
.map((commit) => `${commit.sha.slice(0, 8)} ${commit.subject}`)
|
|
307
|
+
.join("; ");
|
|
308
|
+
const remainder = commits.length > 5 ? `; and ${commits.length - 5} more` : "";
|
|
309
|
+
warnings.push(`${branch} has ${commits.length} commit(s) not contained in ${baseBranch}; close force-deletes the branch: ${summary}${remainder}`);
|
|
310
|
+
}
|
|
311
|
+
catch (error) {
|
|
312
|
+
warnings.push(`Unable to inspect commits before force-deleting ${branch}: ${describeError(error)}`);
|
|
196
313
|
}
|
|
197
314
|
}
|
|
198
315
|
async function pruneWorktreesBestEffort(git, repoRoot, warnings) {
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { readTranscriptTurnEvidence } from "./claude-transcript-reply.js";
|
|
2
|
+
import { getTaskRuntimeRepoRoot } from "./task-service.js";
|
|
3
|
+
export const TURN_STALL_THRESHOLD_MS = 30 * 60_000;
|
|
4
|
+
export const TURN_INTERRUPT_GRACE_MS = 10_000;
|
|
5
|
+
export function createTurnReconcilerService(deps) {
|
|
6
|
+
const now = deps.now ?? (() => new Date().toISOString());
|
|
7
|
+
const stallThresholdMs = deps.stallThresholdMs ?? TURN_STALL_THRESHOLD_MS;
|
|
8
|
+
const interruptGraceMs = deps.interruptGraceMs ?? TURN_INTERRUPT_GRACE_MS;
|
|
9
|
+
const readEvidence = deps.readTranscriptEvidence ?? readTranscriptTurnEvidence;
|
|
10
|
+
const pendingInterrupts = new Map();
|
|
11
|
+
return {
|
|
12
|
+
async reconcileTask(repoRoot, task, stateRoot) {
|
|
13
|
+
const taskRepoRoot = getTaskRuntimeRepoRoot(task);
|
|
14
|
+
const round = await deps.roundService.getSessionRoundState({
|
|
15
|
+
repoRoot,
|
|
16
|
+
stateRepoRoot: taskRepoRoot,
|
|
17
|
+
stateRoot,
|
|
18
|
+
taskSlug: task.taskSlug
|
|
19
|
+
});
|
|
20
|
+
if (round.status !== "running"
|
|
21
|
+
|| !round.activeRole
|
|
22
|
+
|| !round.activeTurnStartedAt
|
|
23
|
+
|| round.roleRecovery) {
|
|
24
|
+
clearTaskInterrupts(repoRoot, task.taskSlug);
|
|
25
|
+
return { status: "inactive" };
|
|
26
|
+
}
|
|
27
|
+
const role = round.activeRole;
|
|
28
|
+
const interruptKey = `${repoRoot}:${task.taskSlug}:${role}`;
|
|
29
|
+
const session = await deps.sessionService.getRoleSession(repoRoot, task.taskSlug, role);
|
|
30
|
+
const evidence = session
|
|
31
|
+
? await readEvidence({ ...session, lastTurnStartedAt: round.activeTurnStartedAt })
|
|
32
|
+
: {};
|
|
33
|
+
if (evidence.completion) {
|
|
34
|
+
pendingInterrupts.delete(interruptKey);
|
|
35
|
+
await deps.claudeHookService.handleReconciledTurnEnd(buildReconciledHook(task.taskSlug, role, session, "Stop", {
|
|
36
|
+
vcm_reconcile_reason: "transcript-end-turn",
|
|
37
|
+
vcm_completion_id: evidence.completion.id,
|
|
38
|
+
vcm_completion_at: evidence.completion.timestamp
|
|
39
|
+
}));
|
|
40
|
+
return { status: "completed", role, reason: "transcript-end-turn" };
|
|
41
|
+
}
|
|
42
|
+
if (!session || session.status !== "running") {
|
|
43
|
+
pendingInterrupts.delete(interruptKey);
|
|
44
|
+
const reason = session ? "terminal-session-exited" : "terminal-session-missing";
|
|
45
|
+
await deps.claudeHookService.handleReconciledTurnEnd(buildReconciledHook(task.taskSlug, role, session, "StopFailure", {
|
|
46
|
+
error: reason.replaceAll("-", "_"),
|
|
47
|
+
error_details: `VCM reconciled an active turn because its ${reason.replaceAll("-", " ")}.`
|
|
48
|
+
}));
|
|
49
|
+
return { status: "failed", role, reason };
|
|
50
|
+
}
|
|
51
|
+
const lastActivityAt = latestTimestamp([
|
|
52
|
+
round.activeTurnStartedAt,
|
|
53
|
+
session.lastHookEventAt,
|
|
54
|
+
session.lastOutputAt,
|
|
55
|
+
evidence.lastActivityAt
|
|
56
|
+
]);
|
|
57
|
+
const currentTime = now();
|
|
58
|
+
if (!isStale(lastActivityAt, currentTime, stallThresholdMs)) {
|
|
59
|
+
pendingInterrupts.delete(interruptKey);
|
|
60
|
+
return { status: "active" };
|
|
61
|
+
}
|
|
62
|
+
const pendingInterrupt = pendingInterrupts.get(interruptKey);
|
|
63
|
+
if (!pendingInterrupt || pendingInterrupt.turnStartedAt !== round.activeTurnStartedAt) {
|
|
64
|
+
deps.runtime.write(session.id, "\u0003");
|
|
65
|
+
pendingInterrupts.set(interruptKey, {
|
|
66
|
+
turnStartedAt: round.activeTurnStartedAt,
|
|
67
|
+
requestedAt: currentTime
|
|
68
|
+
});
|
|
69
|
+
return { status: "active" };
|
|
70
|
+
}
|
|
71
|
+
if (!isStale(pendingInterrupt.requestedAt, currentTime, interruptGraceMs)) {
|
|
72
|
+
return { status: "active" };
|
|
73
|
+
}
|
|
74
|
+
pendingInterrupts.delete(interruptKey);
|
|
75
|
+
await deps.claudeHookService.handleReconciledTurnEnd(buildReconciledHook(task.taskSlug, role, session, "StopFailure", {
|
|
76
|
+
error: "turn_stalled",
|
|
77
|
+
error_details: `No hook, terminal output, or transcript activity was observed for ${stallThresholdMs}ms.`
|
|
78
|
+
}));
|
|
79
|
+
return { status: "failed", role, reason: "turn-stalled" };
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
function clearTaskInterrupts(repoRoot, taskSlug) {
|
|
83
|
+
for (const key of pendingInterrupts.keys()) {
|
|
84
|
+
if (key.startsWith(`${repoRoot}:${taskSlug}:`)) {
|
|
85
|
+
pendingInterrupts.delete(key);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function buildReconciledHook(taskSlug, role, session, eventName, evidence) {
|
|
91
|
+
return {
|
|
92
|
+
taskSlug,
|
|
93
|
+
role,
|
|
94
|
+
event: {
|
|
95
|
+
hook_event_name: eventName,
|
|
96
|
+
...(session?.claudeSessionId ? { session_id: session.claudeSessionId } : {}),
|
|
97
|
+
...(session?.transcriptPath ? { transcript_path: session.transcriptPath } : {}),
|
|
98
|
+
...(session?.cwd ? { cwd: session.cwd } : {}),
|
|
99
|
+
vcm_reconciled: true,
|
|
100
|
+
...evidence
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function latestTimestamp(values) {
|
|
105
|
+
return values.reduce((latest, value) => {
|
|
106
|
+
if (!value) {
|
|
107
|
+
return latest;
|
|
108
|
+
}
|
|
109
|
+
const valueMs = Date.parse(value);
|
|
110
|
+
const latestMs = latest ? Date.parse(latest) : Number.NaN;
|
|
111
|
+
return Number.isFinite(valueMs) && (!Number.isFinite(latestMs) || valueMs > latestMs)
|
|
112
|
+
? value
|
|
113
|
+
: latest;
|
|
114
|
+
}, undefined);
|
|
115
|
+
}
|
|
116
|
+
function isStale(lastActivityAt, currentTime, thresholdMs) {
|
|
117
|
+
const activityMs = lastActivityAt ? Date.parse(lastActivityAt) : Number.NaN;
|
|
118
|
+
const currentMs = Date.parse(currentTime);
|
|
119
|
+
return Number.isFinite(activityMs)
|
|
120
|
+
&& Number.isFinite(currentMs)
|
|
121
|
+
&& currentMs - activityMs >= thresholdMs;
|
|
122
|
+
}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import { renderRoleMemoryRules } from "./role-memory.js";
|
|
1
2
|
export function renderArchitectHarnessRules() {
|
|
2
3
|
return `
|
|
3
4
|
## VCM Architect Rules
|
|
4
5
|
|
|
6
|
+
${renderRoleMemoryRules("architect")}
|
|
7
|
+
|
|
5
8
|
### Role Scope
|
|
6
9
|
|
|
7
10
|
- Own technical analysis, architecture planning, module boundaries, file-level responsibilities, cross-file callable surfaces, public contracts, verifiable behavior, implementation boundaries within the accepted scope, behavior/contract proof points, risks, and architect-owned replan decisions.
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export function renderRootClaudeHarnessRules() {
|
|
2
|
-
return
|
|
2
|
+
return `@.ai/vcm/memory/shared.md
|
|
3
|
+
|
|
4
|
+
## VCM Start Here
|
|
3
5
|
|
|
4
6
|
- Use the durable project docs below as role-relevant project truth.
|
|
5
7
|
- Read module-local \`CLAUDE.md\` before editing a subdirectory if one exists.
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import { renderRoleMemoryRules } from "./role-memory.js";
|
|
1
2
|
export function renderCoderHarnessRules() {
|
|
2
3
|
return `
|
|
3
4
|
## VCM Coder Rules
|
|
4
5
|
|
|
6
|
+
${renderRoleMemoryRules("coder")}
|
|
7
|
+
|
|
5
8
|
### Role Scope
|
|
6
9
|
|
|
7
10
|
- Own function-level implementation and baseline implementation tests inside the approved task scope, role message, and architecture plan.
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import { renderRoleMemoryRules } from "./role-memory.js";
|
|
1
2
|
export function renderGateReviewerAgentRules() {
|
|
2
3
|
return `## Role
|
|
3
4
|
|
|
4
5
|
You are VCM \`gate-reviewer\`.
|
|
5
6
|
|
|
7
|
+
${renderRoleMemoryRules("gate-reviewer")}
|
|
8
|
+
|
|
6
9
|
Review only the gate in the VCM prompt. Use the task and worktree paths named there. Project memory may orient you, but only current worktree evidence can decide the gate.
|
|
7
10
|
|
|
8
11
|
Use only these decisions:
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { renderRoleMemoryRules } from "./role-memory.js";
|
|
1
2
|
export function renderHarnessEngineerHarnessRules() {
|
|
2
3
|
return `## Role
|
|
3
4
|
|
|
@@ -7,6 +8,8 @@ Maintain and improve this repository's VCM harness. Understand both VCM fixed
|
|
|
7
8
|
harness rules and project-specific harness customization before proposing any
|
|
8
9
|
change.
|
|
9
10
|
|
|
11
|
+
${renderRoleMemoryRules("harness-engineer")}
|
|
12
|
+
|
|
10
13
|
## Scope
|
|
11
14
|
|
|
12
15
|
You may inspect:
|
|
@@ -22,7 +25,8 @@ You may inspect:
|
|
|
22
25
|
\`docs/known-issues.md\`
|
|
23
26
|
- task evidence such as handoffs, route messages, commits, commit diffs,
|
|
24
27
|
generated context, validation reports, Gate Review reports, final acceptance
|
|
25
|
-
artifacts, and
|
|
28
|
+
artifacts, memory drafts and diffs under .ai/vcm/memory-review, current memory
|
|
29
|
+
under .ai/vcm/memory, and user corrections
|
|
26
30
|
|
|
27
31
|
You are not part of the task workflow round state.
|
|
28
32
|
|
|
@@ -33,18 +37,21 @@ You are not part of the task workflow round state.
|
|
|
33
37
|
- Bootstrap Apply Mode: when VCM explicitly asks for bootstrap apply work, make
|
|
34
38
|
permitted bootstrap edits directly in the active task worktree and commit them
|
|
35
39
|
yourself.
|
|
36
|
-
- Retrospective Mode: analyze a completed task for reusable harness problems.
|
|
37
|
-
not edit files.
|
|
40
|
+
- Retrospective Mode: analyze a completed task for reusable harness problems.
|
|
41
|
+
Do not edit harness files; proven repeated findings may update VCM memory.
|
|
42
|
+
- Memory Review Mode: review role memory drafts or proven retrospective memory
|
|
43
|
+
findings and write only the memory files assigned by VCM.
|
|
38
44
|
- VCM Feedback Mode: draft VCM product, installer, UI, or fixed-template issue
|
|
39
45
|
feedback. Do not submit without explicit in-session user authorization.
|
|
40
46
|
|
|
41
47
|
## Change Policy
|
|
42
48
|
|
|
43
|
-
- Apply edits only in Bootstrap Apply Mode or when VCM
|
|
44
|
-
apply an approved harness change.
|
|
49
|
+
- Apply edits only in Bootstrap Apply Mode, Memory Review Mode, or when VCM
|
|
50
|
+
explicitly asks you to apply an approved harness change.
|
|
45
51
|
- When applying edits, work only in the active task worktree named by VCM. Do not
|
|
46
52
|
edit the base repository root unless VCM explicitly says so.
|
|
47
|
-
- In Proposal Mode
|
|
53
|
+
- In Proposal Mode, do not edit files. In Retrospective Mode, do not edit
|
|
54
|
+
harness files; only the memory exception above may write files.
|
|
48
55
|
- Commit every applied harness change yourself before ending your turn.
|
|
49
56
|
- Do not overwrite VCM fixed managed blocks.
|
|
50
57
|
- Keep project-specific customization outside VCM managed blocks.
|
|
@@ -54,6 +61,22 @@ You are not part of the task workflow round state.
|
|
|
54
61
|
validation recommendations with every proposal.
|
|
55
62
|
- Do not edit production source code as part of harness maintenance.
|
|
56
63
|
|
|
64
|
+
## Memory Management
|
|
65
|
+
|
|
66
|
+
- Own VCM-managed project memory under \`.ai/vcm/memory/**\`.
|
|
67
|
+
- During an Auto Memory review, verify role drafts against task evidence, merge
|
|
68
|
+
duplicates, remove stale entries, and keep role-specific knowledge in the
|
|
69
|
+
matching role memory file.
|
|
70
|
+
- Keep task narrative, temporary state, unverified conclusions, and harness
|
|
71
|
+
rules out of memory.
|
|
72
|
+
- A repeated problem confirmed by Task Harness Retrospective may become memory
|
|
73
|
+
without collecting new role drafts.
|
|
74
|
+
- For a direct user-requested memory correction, edit the current task
|
|
75
|
+
worktree's assigned memory file; VCM records and applies the change when the
|
|
76
|
+
turn stops.
|
|
77
|
+
- When VCM assigns review output paths, edit only those paths. VCM applies the
|
|
78
|
+
reviewed memory and records the diff.
|
|
79
|
+
|
|
57
80
|
## Task Harness Retrospective
|
|
58
81
|
|
|
59
82
|
After a complete code-change flow passes Final Acceptance, you may be asked to
|
|
@@ -67,7 +90,8 @@ complete correctly.
|
|
|
67
90
|
Inspect the active task worktree as needed. Useful evidence may include
|
|
68
91
|
handoffs, route messages, commits, commit diffs, durable docs, generated
|
|
69
92
|
context, validation reports, Gate Review reports, final acceptance artifacts,
|
|
70
|
-
and user corrections during
|
|
93
|
+
memory drafts, applied memory diffs, current memory, and user corrections during
|
|
94
|
+
the task.
|
|
71
95
|
|
|
72
96
|
For each finding, decide whether it is:
|
|
73
97
|
|
|
@@ -79,7 +103,7 @@ Do not create new rules from weak evidence, one-off execution mistakes, or role
|
|
|
79
103
|
behavior that existing harness rules already cover. If no reusable harness
|
|
80
104
|
problem is proven, say so clearly.
|
|
81
105
|
|
|
82
|
-
Do not edit files during retrospective analysis. Write a concise analysis with:
|
|
106
|
+
Do not edit harness files during retrospective analysis. Write a concise analysis with:
|
|
83
107
|
|
|
84
108
|
- finding
|
|
85
109
|
- evidence
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import { renderRoleMemoryRules } from "./role-memory.js";
|
|
1
2
|
export function renderProjectManagerHarnessRules() {
|
|
2
3
|
return `
|
|
3
4
|
## VCM Project Manager Rules
|
|
4
5
|
|
|
6
|
+
${renderRoleMemoryRules("project-manager")}
|
|
7
|
+
|
|
5
8
|
### Role Scope
|
|
6
9
|
|
|
7
10
|
- You are the user-facing orchestration hub for this VCM-managed repository.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export function renderRoleMemoryRules(role) {
|
|
2
|
+
return `### Role Memory
|
|
3
|
+
|
|
4
|
+
Before handling work in a session, read \`.ai/vcm/memory/roles/${role}.md\`.
|
|
5
|
+
Read it again after context compaction before continuing.
|
|
6
|
+
|
|
7
|
+
Treat memory as accumulated project context, not authority. Verify it against
|
|
8
|
+
current code, documentation, and task evidence.`;
|
|
9
|
+
}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import { renderRoleMemoryRules } from "./role-memory.js";
|
|
1
2
|
export function renderTesterHarnessRules() {
|
|
2
3
|
return `
|
|
3
4
|
## VCM Tester Rules
|
|
4
5
|
|
|
6
|
+
${renderRoleMemoryRules("tester")}
|
|
7
|
+
|
|
5
8
|
### Role Scope
|
|
6
9
|
|
|
7
10
|
- Own independent validation, tester-owned test design, test implementation, test adequacy, \`docs/TESTING.md\`, and final validation confidence.
|