vibe-coding-master 0.3.21 → 0.3.23
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 +2 -2
- package/dist/backend/api/artifact-routes.js +8 -1
- package/dist/backend/api/codex-translation-routes.js +24 -0
- package/dist/backend/api/project-routes.js +3 -1
- package/dist/backend/api/task-routes.js +3 -1
- package/dist/backend/server.js +10 -1
- package/dist/backend/services/artifact-service.js +10 -3
- package/dist/backend/services/codex-hook-service.js +30 -9
- package/dist/backend/services/codex-translation-service.js +183 -79
- package/dist/backend/services/session-service.js +190 -38
- package/dist/backend/services/status-service.js +3 -1
- package/dist/backend/services/translation-service.js +15 -4
- package/dist/backend/templates/handoff.js +3 -3
- package/dist/backend/templates/harness/architect-agent.js +3 -2
- package/dist/backend/templates/harness/coder-agent.js +6 -1
- package/dist/shared/types/app-settings.js +3 -2
- package/dist-frontend/assets/index-C2xTNLmb.js +95 -0
- package/dist-frontend/assets/index-Cfum1Prr.css +32 -0
- package/dist-frontend/index.html +2 -2
- package/docs/codex-translation-plan.md +108 -86
- package/docs/product-design.md +5 -5
- package/docs/v0.4-custom-workflow-plan.md +785 -0
- package/docs/vcm-cc-best-practices.md +15 -12
- package/package.json +1 -1
- package/dist-frontend/assets/index-DOKW0Y5n.js +0 -95
- package/dist-frontend/assets/index-Dmefx9m7.css +0 -32
|
@@ -12,13 +12,9 @@ const CODEX_REVIEW_DIR = ".ai/vcm/codex-reviews";
|
|
|
12
12
|
const CODEX_CONFIG_PATH = ".ai/codex/config.toml";
|
|
13
13
|
const CODEX_TRANSLATOR_DIR = ".ai/codex-translator";
|
|
14
14
|
const CODEX_TRANSLATION_DIR = ".ai/vcm/translations";
|
|
15
|
-
const CODEX_TRANSLATOR_SESSION_PATH = ".ai/vcm/translations/
|
|
16
|
-
const LEGACY_CODEX_TRANSLATOR_SESSION_PATH = ".ai/vcm/translations/session.json";
|
|
17
|
-
const CODEX_TRANSLATOR_LOG_PATHS = [
|
|
18
|
-
".ai/vcm/translations/runtime/codex-translator.log",
|
|
19
|
-
".ai/vcm/translations/codex-translator.log"
|
|
20
|
-
];
|
|
15
|
+
const CODEX_TRANSLATOR_SESSION_PATH = ".ai/vcm/translations/session.json";
|
|
21
16
|
const CODEX_TRANSLATOR_CONFIG_PATH = ".ai/codex-translator/config.toml";
|
|
17
|
+
const PROJECT_TRANSLATOR_SCOPE = "__project__";
|
|
22
18
|
export function createSessionService(deps) {
|
|
23
19
|
const now = deps.now ?? (() => new Date().toISOString());
|
|
24
20
|
async function launchRoleSession(repoRoot, taskSlug, role, input, launchMode) {
|
|
@@ -42,9 +38,6 @@ export function createSessionService(deps) {
|
|
|
42
38
|
const effort = isCodexRole
|
|
43
39
|
? normalizeCodexEffort(input.effort ?? persisted?.effort)
|
|
44
40
|
: normalizeClaudeEffort(input.effort ?? persisted?.effort);
|
|
45
|
-
if (isTranslator) {
|
|
46
|
-
await cleanupCodexTranslatorLogs(deps.fs, repoRoot);
|
|
47
|
-
}
|
|
48
41
|
const claudeSessionId = launchMode === "resume"
|
|
49
42
|
? persisted?.claudeSessionId
|
|
50
43
|
: randomUUID();
|
|
@@ -112,14 +105,167 @@ export function createSessionService(deps) {
|
|
|
112
105
|
await persistRoleSessionRecord(deps.fs, repoRoot, taskRepoRoot, config.stateRoot, record);
|
|
113
106
|
return record;
|
|
114
107
|
}
|
|
108
|
+
async function launchProjectTranslatorSession(repoRoot, input, launchMode) {
|
|
109
|
+
const live = toRoleSessionRecordView(getRegisteredProjectTranslatorSession(deps.registry, deps.runtime), deps.runtime);
|
|
110
|
+
if (live && live.status === "running") {
|
|
111
|
+
return live;
|
|
112
|
+
}
|
|
113
|
+
const persisted = await loadPersistedCodexTranslatorSession(deps.fs, repoRoot);
|
|
114
|
+
const model = normalizeCodexModel(input.model ?? persisted?.model);
|
|
115
|
+
const effort = normalizeCodexEffort(input.effort ?? persisted?.effort);
|
|
116
|
+
const claudeSessionId = launchMode === "resume"
|
|
117
|
+
? persisted?.claudeSessionId
|
|
118
|
+
: randomUUID();
|
|
119
|
+
if (!claudeSessionId) {
|
|
120
|
+
throw new VcmError({
|
|
121
|
+
code: "CODEX_TRANSLATOR_SESSION_MISSING",
|
|
122
|
+
message: "Codex Translator does not have a session id to resume.",
|
|
123
|
+
statusCode: 409,
|
|
124
|
+
hint: "Start the translator once before using Resume."
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
const startCommand = await buildCodexStartCommand(deps.fs, repoRoot, repoRoot, CODEX_TRANSLATOR_ROLE, launchMode, model, effort, deps.sandboxMode, launchMode === "resume" && hasCapturedCodexSession(persisted) ? claudeSessionId : undefined);
|
|
128
|
+
const runtimeSession = await deps.runtime.createSession({
|
|
129
|
+
taskSlug: PROJECT_TRANSLATOR_SCOPE,
|
|
130
|
+
role: CODEX_TRANSLATOR_ROLE,
|
|
131
|
+
command: startCommand.command,
|
|
132
|
+
args: startCommand.args,
|
|
133
|
+
cwd: startCommand.cwd,
|
|
134
|
+
env: {
|
|
135
|
+
VCM_API_URL: deps.apiUrl,
|
|
136
|
+
VCM_TASK_REPO_ROOT: repoRoot,
|
|
137
|
+
VCM_TASK_SLUG: PROJECT_TRANSLATOR_SCOPE,
|
|
138
|
+
VCM_ROLE: CODEX_TRANSLATOR_ROLE,
|
|
139
|
+
VCM_SESSION_ID: claudeSessionId
|
|
140
|
+
},
|
|
141
|
+
cols: input.cols,
|
|
142
|
+
rows: input.rows
|
|
143
|
+
});
|
|
144
|
+
const timestamp = now();
|
|
145
|
+
const record = {
|
|
146
|
+
id: runtimeSession.id,
|
|
147
|
+
claudeSessionId,
|
|
148
|
+
taskSlug: PROJECT_TRANSLATOR_SCOPE,
|
|
149
|
+
role: CODEX_TRANSLATOR_ROLE,
|
|
150
|
+
status: runtimeSession.status,
|
|
151
|
+
activityStatus: "idle",
|
|
152
|
+
command: startCommand.display,
|
|
153
|
+
permissionMode: "default",
|
|
154
|
+
model,
|
|
155
|
+
effort,
|
|
156
|
+
cwd: startCommand.cwd,
|
|
157
|
+
terminalBackend: "node-pty",
|
|
158
|
+
pid: runtimeSession.pid,
|
|
159
|
+
startedAt: runtimeSession.startedAt,
|
|
160
|
+
updatedAt: timestamp,
|
|
161
|
+
lastOutputAt: runtimeSession.lastOutputAt,
|
|
162
|
+
exitCode: runtimeSession.exitCode
|
|
163
|
+
};
|
|
164
|
+
deps.registry.upsert(record);
|
|
165
|
+
await persistCodexTranslatorSession(deps.fs, repoRoot, record);
|
|
166
|
+
return record;
|
|
167
|
+
}
|
|
115
168
|
return {
|
|
169
|
+
startProjectTranslatorSession(repoRoot, input = {}) {
|
|
170
|
+
return launchProjectTranslatorSession(repoRoot, input, "fresh");
|
|
171
|
+
},
|
|
172
|
+
resumeProjectTranslatorSession(repoRoot, input = {}) {
|
|
173
|
+
return launchProjectTranslatorSession(repoRoot, input, "resume");
|
|
174
|
+
},
|
|
175
|
+
async stopProjectTranslatorSession(repoRoot) {
|
|
176
|
+
const existing = await this.getProjectTranslatorSession(repoRoot);
|
|
177
|
+
if (!existing) {
|
|
178
|
+
throw new VcmError({
|
|
179
|
+
code: "SESSION_MISSING",
|
|
180
|
+
message: "Codex Translator session has not been started.",
|
|
181
|
+
statusCode: 404
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
if (deps.runtime.getSession(existing.id)) {
|
|
185
|
+
await deps.runtime.stop(existing.id);
|
|
186
|
+
}
|
|
187
|
+
const updated = {
|
|
188
|
+
...existing,
|
|
189
|
+
status: "exited",
|
|
190
|
+
activityStatus: "idle",
|
|
191
|
+
updatedAt: now()
|
|
192
|
+
};
|
|
193
|
+
deps.registry.upsert(updated);
|
|
194
|
+
await persistCodexTranslatorSession(deps.fs, repoRoot, updated);
|
|
195
|
+
return updated;
|
|
196
|
+
},
|
|
197
|
+
async restartProjectTranslatorSession(repoRoot, input = {}) {
|
|
198
|
+
const existing = await this.getProjectTranslatorSession(repoRoot);
|
|
199
|
+
if (!existing) {
|
|
200
|
+
return launchProjectTranslatorSession(repoRoot, input, "fresh");
|
|
201
|
+
}
|
|
202
|
+
if (deps.runtime.getSession(existing.id)) {
|
|
203
|
+
await deps.runtime.stop(existing.id);
|
|
204
|
+
}
|
|
205
|
+
deps.registry.remove(existing.id);
|
|
206
|
+
return launchProjectTranslatorSession(repoRoot, input, "fresh");
|
|
207
|
+
},
|
|
208
|
+
async getProjectTranslatorSession(repoRoot) {
|
|
209
|
+
const record = getRegisteredProjectTranslatorSession(deps.registry, deps.runtime)
|
|
210
|
+
?? await loadPersistedCodexTranslatorSession(deps.fs, repoRoot);
|
|
211
|
+
return toRoleSessionRecordView(record, deps.runtime);
|
|
212
|
+
},
|
|
213
|
+
async ensureProjectTranslatorSession(repoRoot, input = {}) {
|
|
214
|
+
const existing = await this.getProjectTranslatorSession(repoRoot);
|
|
215
|
+
if (existing?.status === "running") {
|
|
216
|
+
return existing;
|
|
217
|
+
}
|
|
218
|
+
if (existing?.claudeSessionId) {
|
|
219
|
+
return this.resumeProjectTranslatorSession(repoRoot, {
|
|
220
|
+
model: input.model ?? existing.model,
|
|
221
|
+
effort: input.effort ?? existing.effort,
|
|
222
|
+
cols: input.cols,
|
|
223
|
+
rows: input.rows
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
return this.startProjectTranslatorSession(repoRoot, input);
|
|
227
|
+
},
|
|
228
|
+
async recordProjectTranslatorHookEvent(repoRoot, input) {
|
|
229
|
+
const current = await this.getProjectTranslatorSession(repoRoot);
|
|
230
|
+
if (!current) {
|
|
231
|
+
return undefined;
|
|
232
|
+
}
|
|
233
|
+
const timestamp = now();
|
|
234
|
+
const isStop = input.eventName === "Stop";
|
|
235
|
+
const updated = {
|
|
236
|
+
...current,
|
|
237
|
+
claudeSessionId: input.sessionId ?? current.claudeSessionId,
|
|
238
|
+
transcriptPath: input.transcriptPath ?? current.transcriptPath,
|
|
239
|
+
cwd: input.cwd ?? current.cwd,
|
|
240
|
+
activityStatus: isStop ? "idle" : "running",
|
|
241
|
+
lastHookEventAt: timestamp,
|
|
242
|
+
lastTurnEndedAt: isStop ? timestamp : current.lastTurnEndedAt,
|
|
243
|
+
lastTurnStartedAt: isStop ? current.lastTurnStartedAt : timestamp,
|
|
244
|
+
updatedAt: timestamp
|
|
245
|
+
};
|
|
246
|
+
deps.registry.upsert(updated);
|
|
247
|
+
await persistCodexTranslatorSession(deps.fs, repoRoot, updated);
|
|
248
|
+
return updated;
|
|
249
|
+
},
|
|
116
250
|
startRoleSession(repoRoot, taskSlug, role, input = {}) {
|
|
251
|
+
if (role === CODEX_TRANSLATOR_ROLE) {
|
|
252
|
+
void taskSlug;
|
|
253
|
+
return this.startProjectTranslatorSession(repoRoot, input);
|
|
254
|
+
}
|
|
117
255
|
return launchRoleSession(repoRoot, taskSlug, role, input, "fresh");
|
|
118
256
|
},
|
|
119
257
|
resumeRoleSession(repoRoot, taskSlug, role, input = {}) {
|
|
258
|
+
if (role === CODEX_TRANSLATOR_ROLE) {
|
|
259
|
+
void taskSlug;
|
|
260
|
+
return this.resumeProjectTranslatorSession(repoRoot, input);
|
|
261
|
+
}
|
|
120
262
|
return launchRoleSession(repoRoot, taskSlug, role, input, "resume");
|
|
121
263
|
},
|
|
122
264
|
async stopRoleSession(repoRoot, taskSlug, role) {
|
|
265
|
+
if (role === CODEX_TRANSLATOR_ROLE) {
|
|
266
|
+
void taskSlug;
|
|
267
|
+
return this.stopProjectTranslatorSession(repoRoot);
|
|
268
|
+
}
|
|
123
269
|
const existing = await this.getRoleSession(repoRoot, taskSlug, role);
|
|
124
270
|
if (!existing) {
|
|
125
271
|
throw new VcmError({
|
|
@@ -144,6 +290,10 @@ export function createSessionService(deps) {
|
|
|
144
290
|
return updated;
|
|
145
291
|
},
|
|
146
292
|
async restartRoleSession(repoRoot, taskSlug, role, input = {}) {
|
|
293
|
+
if (role === CODEX_TRANSLATOR_ROLE) {
|
|
294
|
+
void taskSlug;
|
|
295
|
+
return this.restartProjectTranslatorSession(repoRoot, input);
|
|
296
|
+
}
|
|
147
297
|
const existing = await this.getRoleSession(repoRoot, taskSlug, role);
|
|
148
298
|
if (!existing) {
|
|
149
299
|
return launchRoleSession(repoRoot, taskSlug, role, input, "fresh");
|
|
@@ -155,6 +305,10 @@ export function createSessionService(deps) {
|
|
|
155
305
|
return launchRoleSession(repoRoot, taskSlug, role, input, "fresh");
|
|
156
306
|
},
|
|
157
307
|
async getRoleSession(repoRoot, taskSlug, role) {
|
|
308
|
+
if (role === CODEX_TRANSLATOR_ROLE) {
|
|
309
|
+
void taskSlug;
|
|
310
|
+
return this.getProjectTranslatorSession(repoRoot);
|
|
311
|
+
}
|
|
158
312
|
const config = await deps.projectService.loadConfig(repoRoot);
|
|
159
313
|
const task = await deps.taskService.loadTask(repoRoot, taskSlug);
|
|
160
314
|
const taskRepoRoot = getTaskRuntimeRepoRoot(task);
|
|
@@ -171,12 +325,9 @@ export function createSessionService(deps) {
|
|
|
171
325
|
const task = await deps.taskService.loadTask(repoRoot, taskSlug);
|
|
172
326
|
const taskRepoRoot = getTaskRuntimeRepoRoot(task);
|
|
173
327
|
const persistedTaskSession = await loadPersistedTaskSessionRecord(deps.fs, taskRepoRoot, config.stateRoot, taskSlug);
|
|
174
|
-
for (const role of ROLE_NAMES) {
|
|
175
|
-
const record = role
|
|
176
|
-
|
|
177
|
-
?? await loadPersistedCodexTranslatorSession(deps.fs, repoRoot, taskSlug)
|
|
178
|
-
: deps.registry.getByRole(taskSlug, role)
|
|
179
|
-
?? normalizePersistedRoleRecord(persistedTaskSession?.roles[role]?.record);
|
|
328
|
+
for (const role of ROLE_NAMES.filter((candidate) => candidate !== CODEX_TRANSLATOR_ROLE)) {
|
|
329
|
+
const record = deps.registry.getByRole(taskSlug, role)
|
|
330
|
+
?? normalizePersistedRoleRecord(persistedTaskSession?.roles[role]?.record);
|
|
180
331
|
const session = toRoleSessionRecordView(record, deps.runtime);
|
|
181
332
|
if (session) {
|
|
182
333
|
sessions.push(session);
|
|
@@ -185,6 +336,15 @@ export function createSessionService(deps) {
|
|
|
185
336
|
return sessions;
|
|
186
337
|
},
|
|
187
338
|
async recordRoleHookEvent(repoRoot, input) {
|
|
339
|
+
if (input.role === CODEX_TRANSLATOR_ROLE) {
|
|
340
|
+
void input.taskSlug;
|
|
341
|
+
return this.recordProjectTranslatorHookEvent(repoRoot, {
|
|
342
|
+
eventName: input.eventName,
|
|
343
|
+
sessionId: input.sessionId,
|
|
344
|
+
transcriptPath: input.transcriptPath,
|
|
345
|
+
cwd: input.cwd
|
|
346
|
+
});
|
|
347
|
+
}
|
|
188
348
|
const current = await this.getRoleSession(repoRoot, input.taskSlug, input.role);
|
|
189
349
|
if (!current || (!input.allowSessionMismatch && !matchesRoleHookSession(current, input))) {
|
|
190
350
|
return undefined;
|
|
@@ -393,6 +553,11 @@ function getRegisteredRoleSession(registry, runtime, taskSlug, role) {
|
|
|
393
553
|
?? candidates.sort(compareSessionUpdatedAtDesc)[0];
|
|
394
554
|
return scopeProjectRoleSession(scoped, taskSlug);
|
|
395
555
|
}
|
|
556
|
+
function getRegisteredProjectTranslatorSession(registry, runtime) {
|
|
557
|
+
const candidates = registry.list().filter((session) => session.role === CODEX_TRANSLATOR_ROLE);
|
|
558
|
+
const live = candidates.find((session) => runtime.getSession(session.id)?.status === "running");
|
|
559
|
+
return live ?? candidates.sort(compareSessionUpdatedAtDesc)[0];
|
|
560
|
+
}
|
|
396
561
|
function compareSessionUpdatedAtDesc(left, right) {
|
|
397
562
|
return (right.updatedAt ?? "").localeCompare(left.updatedAt ?? "");
|
|
398
563
|
}
|
|
@@ -407,38 +572,25 @@ function scopeProjectRoleSession(record, taskSlug) {
|
|
|
407
572
|
}
|
|
408
573
|
async function loadPersistedRoleRecordForRole(fs, baseRepoRoot, taskRepoRoot, stateRoot, taskSlug, role) {
|
|
409
574
|
if (role === CODEX_TRANSLATOR_ROLE) {
|
|
410
|
-
|
|
575
|
+
void taskSlug;
|
|
576
|
+
return loadPersistedCodexTranslatorSession(fs, baseRepoRoot);
|
|
411
577
|
}
|
|
412
578
|
return loadPersistedRoleRecord(fs, taskRepoRoot, stateRoot, taskSlug, role);
|
|
413
579
|
}
|
|
414
|
-
async function loadPersistedCodexTranslatorSession(fs, repoRoot
|
|
415
|
-
|
|
580
|
+
async function loadPersistedCodexTranslatorSession(fs, repoRoot) {
|
|
581
|
+
const sessionPath = resolveRepoPath(repoRoot, CODEX_TRANSLATOR_SESSION_PATH);
|
|
416
582
|
if (!(await fs.pathExists(sessionPath))) {
|
|
417
|
-
|
|
418
|
-
if (!(await fs.pathExists(legacySessionPath))) {
|
|
419
|
-
return undefined;
|
|
420
|
-
}
|
|
421
|
-
sessionPath = legacySessionPath;
|
|
583
|
+
return undefined;
|
|
422
584
|
}
|
|
423
585
|
const payload = await fs.readJson(sessionPath);
|
|
424
586
|
const record = normalizePersistedRoleRecord(isProjectRoleSessionFile(payload) ? payload.record : payload);
|
|
425
587
|
if (!record) {
|
|
426
588
|
return undefined;
|
|
427
589
|
}
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
});
|
|
433
|
-
await fs.removePath?.(sessionPath, { force: true });
|
|
434
|
-
}
|
|
435
|
-
return scopeProjectRoleSession(record, taskSlug);
|
|
436
|
-
}
|
|
437
|
-
async function cleanupCodexTranslatorLogs(fs, repoRoot) {
|
|
438
|
-
if (!fs.removePath) {
|
|
439
|
-
return;
|
|
440
|
-
}
|
|
441
|
-
await Promise.all(CODEX_TRANSLATOR_LOG_PATHS.map((relativePath) => fs.removePath?.(resolveRepoPath(repoRoot, relativePath), { force: true })));
|
|
590
|
+
return {
|
|
591
|
+
...record,
|
|
592
|
+
taskSlug: PROJECT_TRANSLATOR_SCOPE
|
|
593
|
+
};
|
|
442
594
|
}
|
|
443
595
|
async function loadPersistedRoleRecord(fs, repoRoot, stateRoot, taskSlug, role) {
|
|
444
596
|
const current = await loadPersistedTaskSessionRecord(fs, repoRoot, stateRoot, taskSlug);
|
|
@@ -511,7 +663,7 @@ async function persistCodexTranslatorSession(fs, repoRoot, session) {
|
|
|
511
663
|
updatedAt: session.updatedAt,
|
|
512
664
|
record: {
|
|
513
665
|
...session,
|
|
514
|
-
taskSlug:
|
|
666
|
+
taskSlug: PROJECT_TRANSLATOR_SCOPE
|
|
515
667
|
}
|
|
516
668
|
});
|
|
517
669
|
}
|
|
@@ -54,7 +54,9 @@ function degradedArtifactSummary(handoffDir) {
|
|
|
54
54
|
logsDir,
|
|
55
55
|
messagesDir,
|
|
56
56
|
roleCommandPaths: Object.fromEntries(DISPATCHABLE_ROLES.map((role) => [role, `${roleCommandsDir}/${role}.md`])),
|
|
57
|
-
roleLogPaths: Object.fromEntries(ROLE_NAMES
|
|
57
|
+
roleLogPaths: Object.fromEntries(ROLE_NAMES
|
|
58
|
+
.filter((role) => role !== "codex-translator")
|
|
59
|
+
.map((role) => [role, `${logsDir}/${role}.log`])),
|
|
58
60
|
messageRoutePaths: {},
|
|
59
61
|
architecturePlanPath: `${handoffDir}/architecture-plan.md`,
|
|
60
62
|
knownIssuesPath: `${handoffDir}/known-issues.md`,
|
|
@@ -157,6 +157,8 @@ export function createTranslationService(deps) {
|
|
|
157
157
|
}
|
|
158
158
|
function startTranscriptTail(roleSession) {
|
|
159
159
|
const state = getState(roleSession.id);
|
|
160
|
+
state.taskSlug = roleSession.taskSlug;
|
|
161
|
+
state.role = roleSession.role;
|
|
160
162
|
if (state.unsubscribeTranscript) {
|
|
161
163
|
return;
|
|
162
164
|
}
|
|
@@ -183,7 +185,7 @@ export function createTranslationService(deps) {
|
|
|
183
185
|
const config = await loadConfig();
|
|
184
186
|
let displayed = false;
|
|
185
187
|
if (event.kind === "text") {
|
|
186
|
-
const shouldTranslate =
|
|
188
|
+
const shouldTranslate = shouldTranslateTextTranscriptEvent(state, event, config);
|
|
187
189
|
displayed = shouldTranslate
|
|
188
190
|
? processClaudeOutputText(sessionId, event.text, config, event.id, {
|
|
189
191
|
flushImmediately: event.stopReason === "end_turn"
|
|
@@ -206,6 +208,18 @@ export function createTranslationService(deps) {
|
|
|
206
208
|
state.seenTranscriptIds.add(event.id);
|
|
207
209
|
}
|
|
208
210
|
}
|
|
211
|
+
function shouldTranslateTextTranscriptEvent(state, event, config) {
|
|
212
|
+
if (config.outputMode === "all") {
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
if (event.stopReason !== "end_turn") {
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
if (config.outputMode === "pm-final-only") {
|
|
219
|
+
return state.role === "project-manager";
|
|
220
|
+
}
|
|
221
|
+
return true;
|
|
222
|
+
}
|
|
209
223
|
function processClaudeOutputText(sessionId, rawText, config, entryId, options = {}) {
|
|
210
224
|
return startClaudeOutputTranslation(sessionId, rawText, config, {
|
|
211
225
|
entryId,
|
|
@@ -890,8 +904,6 @@ export function createTranslationService(deps) {
|
|
|
890
904
|
});
|
|
891
905
|
}
|
|
892
906
|
return deps.codexTranslationService.createConversationJob(input.repoRoot, {
|
|
893
|
-
taskSlug: input.taskSlug,
|
|
894
|
-
role: input.role,
|
|
895
907
|
direction: input.direction,
|
|
896
908
|
sourceText: input.text,
|
|
897
909
|
sourceLanguage: input.sourceLanguage,
|
|
@@ -921,7 +933,6 @@ export function createTranslationService(deps) {
|
|
|
921
933
|
}
|
|
922
934
|
try {
|
|
923
935
|
return await deps.codexTranslationService.validateConversationResult(repoRoot, {
|
|
924
|
-
taskSlug: job.taskSlug,
|
|
925
936
|
resultPath: job.resultPath,
|
|
926
937
|
sourceHash: job.sourceHash,
|
|
927
938
|
targetLanguage: job.targetLanguage
|
|
@@ -15,9 +15,9 @@ Task-specific context and coder guidance go here, not in source-code comments.
|
|
|
15
15
|
Source-code comments should only describe durable behavior, contracts, invariants,
|
|
16
16
|
error boundaries, or non-obvious logic that should remain useful after this task.
|
|
17
17
|
|
|
18
|
-
| File | Action | Task Context | Durable Code Comment Needed | Coder Work | Allowed Freedom | VCM:CODE | Proof Point | Replan Trigger |
|
|
19
|
-
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
|
20
|
-
| TBD | TBD | TBD | TBD | TBD | TBD | TBD | TBD | TBD |
|
|
18
|
+
| ID | File | Action | Task Context | Durable Code Comment Needed | Coder Work | Allowed Freedom | VCM:CODE | Proof Point | Replan Trigger |
|
|
19
|
+
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
|
20
|
+
| SCF-001 | TBD | TBD | TBD | TBD | TBD | TBD | TBD | TBD | TBD |
|
|
21
21
|
|
|
22
22
|
## Implementation Plan
|
|
23
23
|
|
|
@@ -27,7 +27,8 @@ export function renderArchitectHarnessRules() {
|
|
|
27
27
|
|
|
28
28
|
- Define the expected implementation scope: affected modules, changed or created files, each file's responsibility, why it is in scope, and user-visible behavior changes.
|
|
29
29
|
- 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.
|
|
30
|
-
- Include a \`Scaffold Manifest\` for task-specific file context: 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.
|
|
30
|
+
- 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.
|
|
31
|
+
- 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.
|
|
31
32
|
- Put task context, phase notes, handoff instructions, temporary rationale, and coder guidance in the \`Scaffold Manifest\`, not in source-code comments.
|
|
32
33
|
- Cover architecture docs impact, known risks, and Replan triggers.
|
|
33
34
|
- For docs impact, state whether changes belong in \`docs/ARCHITECTURE.md\`, affected \`<module>/ARCHITECTURE.md\`, \`.ai/generated/public-surface.json\`, or no durable architecture doc.
|
|
@@ -41,7 +42,7 @@ export function renderArchitectHarnessRules() {
|
|
|
41
42
|
- Define every new or changed non-private callable surface directly in code with its signature shape and contract comment.
|
|
42
43
|
- 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.
|
|
43
44
|
- Non-private callable surface includes any function, method, type, trait, enum, constant, re-export, or similar symbol that another file can call or depend on.
|
|
44
|
-
- Mark incomplete implementation bodies with \`VCM:CODE
|
|
45
|
+
- Mark incomplete implementation bodies with \`VCM:CODE <Scaffold Manifest ID>\`; coder must implement them and remove the markers before handoff.
|
|
45
46
|
- Architect scaffolding may include modules, files, signatures, type shapes, durable comments, and placeholder bodies, but not real business implementation beyond minimal scaffold code.
|
|
46
47
|
- Coder may add private implementation helpers, but must not add or change cross-file callable surface without architect replan.
|
|
47
48
|
|
|
@@ -10,7 +10,7 @@ export function renderCoderHarnessRules() {
|
|
|
10
10
|
### Coder Implementation Discipline
|
|
11
11
|
|
|
12
12
|
- Implement the architect-defined scaffold exactly; do not change file responsibilities, callable-surface signatures, or architect-defined contract intent unless the architecture plan explicitly allows it.
|
|
13
|
-
- Implement every \`VCM:CODE\` placeholder and remove all \`VCM:CODE\` markers before handoff.
|
|
13
|
+
- Implement every \`VCM:CODE\` placeholder, track completion by Scaffold Manifest ID when present, and remove all \`VCM:CODE\` markers before handoff.
|
|
14
14
|
- Do not fake completion: no hardcoded success, disabled logic, swallowed errors, test-only shortcuts, or silent fallback that hides failure.
|
|
15
15
|
- Keep the diff inside approved scope: no unrelated rewrites, drive-by refactors, renamed symbols, moved files, or formatting churn.
|
|
16
16
|
- Preserve existing behavior unless the architecture plan explicitly changes it; keep existing call sites and shared code paths working.
|
|
@@ -40,6 +40,11 @@ export function renderCoderHarnessRules() {
|
|
|
40
40
|
- Do not weaken, delete, or skip tests to make validation pass.
|
|
41
41
|
- Record confirmed out-of-scope issues found during implementation in \`.ai/vcm/handoffs/known-issues.md\`.
|
|
42
42
|
|
|
43
|
+
### Handoff
|
|
44
|
+
|
|
45
|
+
- In the route message back to project-manager, include a \`Scaffold Completion\` section when the architecture plan contains a Scaffold Manifest.
|
|
46
|
+
- The \`Scaffold Completion\` section must report completed Scaffold Manifest IDs or \`VCM:CODE\` IDs, remaining markers if any, private helpers added, manifest deviations, and whether Replan is needed.
|
|
47
|
+
|
|
43
48
|
### Generated Context
|
|
44
49
|
|
|
45
50
|
- Regenerate \`.ai/generated/module-index.json\` with \`.ai/tools/generate-module-index\` after module, manifest, source-file, or test-file changes.
|
|
@@ -12,8 +12,9 @@ export const TRANSLATION_TARGET_LANGUAGE_OPTIONS = [
|
|
|
12
12
|
{ value: "es", label: "Spanish" }
|
|
13
13
|
];
|
|
14
14
|
export const TRANSLATION_OUTPUT_MODE_OPTIONS = [
|
|
15
|
-
{ value: "final-only", label: "
|
|
16
|
-
{ value: "
|
|
15
|
+
{ value: "pm-final-only", label: "PM final reply" },
|
|
16
|
+
{ value: "final-only", label: "Each role final reply" },
|
|
17
|
+
{ value: "all", label: "All replies" }
|
|
17
18
|
];
|
|
18
19
|
export function createDefaultLaunchTemplate() {
|
|
19
20
|
const roles = {};
|