vibe-coding-master 0.4.4 → 0.4.6
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/adapters/git-adapter.js +36 -0
- package/dist/backend/api/harness-routes.js +5 -3
- package/dist/backend/services/harness-service.js +72 -17
- package/dist/backend/services/session-service.js +34 -24
- package/dist/backend/services/translation-service.js +1 -0
- package/dist/backend/services/translation-worker-service.js +30 -3
- package/dist/backend/templates/harness/harness-engineer-agent.js +2 -0
- package/dist/backend/templates/harness/vcm-harness-bootstrap-skill.js +11 -1
- package/dist-frontend/assets/{index-12kW1cl6.css → index-CYmBA_er.css} +1 -1
- package/dist-frontend/assets/{index-Bck3Mfz8.js → index-CeiRFzg5.js} +42 -41
- package/dist-frontend/index.html +2 -2
- package/docs/product-design.md +4 -1
- package/docs/vcm-cc-best-practices.md +3 -2
- package/package.json +1 -1
|
@@ -135,6 +135,42 @@ export function createGitAdapter(runner) {
|
|
|
135
135
|
}
|
|
136
136
|
return result.stdout;
|
|
137
137
|
},
|
|
138
|
+
async getCommitList(repoRoot, range) {
|
|
139
|
+
const result = await runGit(runner, repoRoot, ["log", "--format=%H%x00%s%x00%cI%x1e", range]);
|
|
140
|
+
if (result.exitCode !== 0) {
|
|
141
|
+
throw new VcmError({
|
|
142
|
+
code: "GIT_ERROR",
|
|
143
|
+
message: "Unable to read Git commit list.",
|
|
144
|
+
statusCode: 400,
|
|
145
|
+
hint: result.stderr
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
return result.stdout
|
|
149
|
+
.split("\x1e")
|
|
150
|
+
.map((record) => record.trim())
|
|
151
|
+
.filter(Boolean)
|
|
152
|
+
.map((record) => {
|
|
153
|
+
const [sha = "", subject = "", committedAt = ""] = record.split("\0");
|
|
154
|
+
return {
|
|
155
|
+
sha: sha.trim(),
|
|
156
|
+
subject: subject.trim(),
|
|
157
|
+
committedAt: committedAt.trim() || undefined
|
|
158
|
+
};
|
|
159
|
+
})
|
|
160
|
+
.filter((commit) => commit.sha.length > 0);
|
|
161
|
+
},
|
|
162
|
+
async getMergeBase(repoRoot, leftRef, rightRef) {
|
|
163
|
+
const result = await runGit(runner, repoRoot, ["merge-base", leftRef, rightRef]);
|
|
164
|
+
if (result.exitCode !== 0) {
|
|
165
|
+
throw new VcmError({
|
|
166
|
+
code: "GIT_ERROR",
|
|
167
|
+
message: "Unable to find Git merge base.",
|
|
168
|
+
statusCode: 400,
|
|
169
|
+
hint: result.stderr
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
return result.stdout.trim();
|
|
173
|
+
},
|
|
138
174
|
async isIgnored(repoRoot, repoRelativePath) {
|
|
139
175
|
const result = await runGit(runner, repoRoot, ["check-ignore", "-q", "--", repoRelativePath]);
|
|
140
176
|
if (result.exitCode === 0) {
|
|
@@ -32,9 +32,11 @@ export function registerHarnessRoutes(app, deps) {
|
|
|
32
32
|
return deps.harnessService.updateHarnessFileContent(task.worktreePath, request.query.path ?? "", request.body.content);
|
|
33
33
|
});
|
|
34
34
|
app.get("/api/projects/harness/repository-diff", async (request) => {
|
|
35
|
-
const { task } = await requireHarnessTaskContext(deps, request.query.taskSlug);
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
const { project, task } = await requireHarnessTaskContext(deps, request.query.taskSlug);
|
|
36
|
+
return deps.harnessService.getRepositoryDiff(task.worktreePath, {
|
|
37
|
+
baseRepoRoot: project.repoRoot,
|
|
38
|
+
commitSha: request.query.commit
|
|
39
|
+
});
|
|
38
40
|
});
|
|
39
41
|
app.get("/api/projects/harness/bootstrap", async (request) => {
|
|
40
42
|
const { project, task } = await requireHarnessTaskContext(deps, request.query.taskSlug);
|
|
@@ -246,15 +246,8 @@ export function createHarnessService(deps) {
|
|
|
246
246
|
: formatHarnessApplyMessage(committed.harnessCommit, true)
|
|
247
247
|
};
|
|
248
248
|
},
|
|
249
|
-
async getRepositoryDiff(repoRoot,
|
|
250
|
-
|
|
251
|
-
throw new VcmError({
|
|
252
|
-
code: "HARNESS_GIT_UNAVAILABLE",
|
|
253
|
-
message: "Git-backed repository diff is not available in this VCM runtime.",
|
|
254
|
-
statusCode: 501
|
|
255
|
-
});
|
|
256
|
-
}
|
|
257
|
-
return getRepositoryDiffReport(deps.git, repoRoot, scope, now());
|
|
249
|
+
async getRepositoryDiff(repoRoot, input = {}) {
|
|
250
|
+
return getRepositoryDiffReport(requireRepositoryDiffGit(deps.git), repoRoot, input, now());
|
|
258
251
|
},
|
|
259
252
|
async getBootstrapStatus(repoRoot, targetRepoRoot = repoRoot) {
|
|
260
253
|
return getHarnessBootstrapStatus(deps, repoRoot, targetRepoRoot, now);
|
|
@@ -345,7 +338,6 @@ export function createHarnessService(deps) {
|
|
|
345
338
|
const timestamp = now();
|
|
346
339
|
if (input.eventName === "Stop" && state.targetRepoRoot) {
|
|
347
340
|
await bumpHarnessRevision(deps.fs, state.targetRepoRoot, timestamp);
|
|
348
|
-
await commitHarnessVisibleChanges(deps.git, state.targetRepoRoot, "chore(vcm-harness): bootstrap project context");
|
|
349
341
|
}
|
|
350
342
|
await persistHarnessBootstrapRunState(deps.fs, repoRoot, {
|
|
351
343
|
...state,
|
|
@@ -449,12 +441,42 @@ function shortCommit(commit) {
|
|
|
449
441
|
}
|
|
450
442
|
const REPOSITORY_DIFF_MAX_FILES = 250;
|
|
451
443
|
const REPOSITORY_DIFF_MAX_DIFF_CHARS = 180_000;
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
.
|
|
444
|
+
function requireRepositoryDiffGit(git) {
|
|
445
|
+
if (!git?.getCommitDiff ||
|
|
446
|
+
!git.getCommitInfo ||
|
|
447
|
+
!git.getCommitList ||
|
|
448
|
+
!git.getHeadCommit ||
|
|
449
|
+
!git.getMergeBase) {
|
|
450
|
+
throw new VcmError({
|
|
451
|
+
code: "HARNESS_GIT_UNAVAILABLE",
|
|
452
|
+
message: "Git-backed repository diff is not available in this VCM runtime.",
|
|
453
|
+
statusCode: 501
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
return git;
|
|
457
|
+
}
|
|
458
|
+
async function getRepositoryDiffReport(git, repoRoot, input, generatedAt) {
|
|
459
|
+
const baseRef = input.baseRepoRoot
|
|
460
|
+
? await git.getHeadCommit(input.baseRepoRoot)
|
|
461
|
+
: "HEAD^";
|
|
462
|
+
const mergeBase = await git.getMergeBase(repoRoot, baseRef, "HEAD");
|
|
463
|
+
const commits = (await git.getCommitList(repoRoot, `${mergeBase}..HEAD`)).map(toRepositoryDiffCommit);
|
|
457
464
|
const warnings = [];
|
|
465
|
+
const selectedCommit = selectRepositoryDiffCommit(commits, input.commitSha);
|
|
466
|
+
if (!selectedCommit) {
|
|
467
|
+
return {
|
|
468
|
+
version: 1,
|
|
469
|
+
repoRoot,
|
|
470
|
+
generatedAt,
|
|
471
|
+
commits,
|
|
472
|
+
summary: createEmptyRepositoryDiffSummary(),
|
|
473
|
+
files: [],
|
|
474
|
+
warnings
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
const commitInfo = await git.getCommitInfo(repoRoot, selectedCommit.sha);
|
|
478
|
+
const rawDiff = await git.getCommitDiff(repoRoot, selectedCommit.sha);
|
|
479
|
+
const allFiles = parseCommitDiffFiles(rawDiff);
|
|
458
480
|
const files = allFiles.slice(0, REPOSITORY_DIFF_MAX_FILES);
|
|
459
481
|
if (allFiles.length > files.length) {
|
|
460
482
|
warnings.push(`Diff file list is truncated to ${REPOSITORY_DIFF_MAX_FILES} files.`);
|
|
@@ -466,8 +488,8 @@ async function getRepositoryDiffReport(git, repoRoot, scope, generatedAt) {
|
|
|
466
488
|
return {
|
|
467
489
|
version: 1,
|
|
468
490
|
repoRoot,
|
|
469
|
-
scope,
|
|
470
491
|
generatedAt,
|
|
492
|
+
commits,
|
|
471
493
|
commit: {
|
|
472
494
|
sha: commitInfo.sha,
|
|
473
495
|
shortSha: commitInfo.sha.slice(0, 12),
|
|
@@ -491,6 +513,36 @@ async function getRepositoryDiffReport(git, repoRoot, scope, generatedAt) {
|
|
|
491
513
|
warnings
|
|
492
514
|
};
|
|
493
515
|
}
|
|
516
|
+
function toRepositoryDiffCommit(commit) {
|
|
517
|
+
return {
|
|
518
|
+
sha: commit.sha,
|
|
519
|
+
shortSha: commit.sha.slice(0, 12),
|
|
520
|
+
subject: commit.subject,
|
|
521
|
+
committedAt: commit.committedAt
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
function selectRepositoryDiffCommit(commits, requestedSha) {
|
|
525
|
+
const normalizedSha = requestedSha?.trim();
|
|
526
|
+
if (normalizedSha) {
|
|
527
|
+
return commits.find((commit) => commit.sha === normalizedSha || commit.shortSha === normalizedSha);
|
|
528
|
+
}
|
|
529
|
+
return commits[0];
|
|
530
|
+
}
|
|
531
|
+
function createEmptyRepositoryDiffSummary() {
|
|
532
|
+
return {
|
|
533
|
+
totalFiles: 0,
|
|
534
|
+
committedFiles: 0,
|
|
535
|
+
stagedFiles: 0,
|
|
536
|
+
unstagedFiles: 0,
|
|
537
|
+
untrackedFiles: 0,
|
|
538
|
+
additions: 0,
|
|
539
|
+
deletions: 0,
|
|
540
|
+
harnessFiles: 0,
|
|
541
|
+
productCodeFiles: 0,
|
|
542
|
+
truncatedFiles: 0,
|
|
543
|
+
binaryFiles: 0
|
|
544
|
+
};
|
|
545
|
+
}
|
|
494
546
|
function parseCommitDiffFiles(rawDiff) {
|
|
495
547
|
const chunks = rawDiff.split(/(?=^diff --git )/m).filter((chunk) => chunk.startsWith("diff --git "));
|
|
496
548
|
return chunks.map(parseCommitDiffFile);
|
|
@@ -1435,15 +1487,18 @@ Required work:
|
|
|
1435
1487
|
- Fill target docs/ARCHITECTURE.md with project-level module overview, responsibilities, relationships, dependency direction, project-wide constraints, and links to module-level architecture docs.
|
|
1436
1488
|
- Create or update target module-level ARCHITECTURE.md files for clear module boundaries listed by module-index.json.
|
|
1437
1489
|
- Fill target docs/TESTING.md with project-native validation levels, commands, validation selection rules, final-validation cleanup, test layout, integration/E2E case lists, generated-context freshness checks, and known testing gaps.
|
|
1490
|
+
- Review git status and git diff in the target task worktree.
|
|
1491
|
+
- Stage only allowed bootstrap harness changes and create a commit in the target task worktree.
|
|
1438
1492
|
|
|
1439
1493
|
Boundaries:
|
|
1440
1494
|
- Do not write to the base repository root.
|
|
1441
1495
|
- Do not edit product source, product tests, package manifests, lockfiles, deployment config, secrets, or VCM managed blocks.
|
|
1442
1496
|
- Preserve user-authored content.
|
|
1443
1497
|
- Do not create new validation wrapper tools.
|
|
1498
|
+
- VCM will not create the bootstrap commit for you.
|
|
1444
1499
|
|
|
1445
1500
|
Final response:
|
|
1446
|
-
Summarize files reviewed, files updated, generated artifacts, verified claims, inferred claims, unknowns, confirmation-needed items, and suggested validation commands.`;
|
|
1501
|
+
Summarize files reviewed, files updated, generated artifacts, commit hash, final git status, verified claims, inferred claims, unknowns, confirmation-needed items, and suggested validation commands.`;
|
|
1447
1502
|
}
|
|
1448
1503
|
export function createScriptFixedHarnessInstaller(scriptPath = path.resolve(process.cwd(), "scripts/install-vcm-harness.mjs")) {
|
|
1449
1504
|
return async (repoRoot) => {
|
|
@@ -206,6 +206,7 @@ export function createSessionService(deps) {
|
|
|
206
206
|
return updated;
|
|
207
207
|
}
|
|
208
208
|
async function launchProjectTranslatorSession(repoRoot, input, launchMode) {
|
|
209
|
+
const taskContext = await resolveProjectToolTaskContext(repoRoot, input, "Translator");
|
|
209
210
|
const live = toRoleSessionRecordView(getRegisteredProjectTranslatorSession(deps.registry, deps.runtime), deps.runtime);
|
|
210
211
|
if (live && live.status === "running") {
|
|
211
212
|
return withHarnessRevisionView(repoRoot, live);
|
|
@@ -227,12 +228,10 @@ export function createSessionService(deps) {
|
|
|
227
228
|
});
|
|
228
229
|
}
|
|
229
230
|
await deps.fs.ensureDir(resolveRepoPath(repoRoot, TRANSLATION_DIR));
|
|
230
|
-
const transcriptPath =
|
|
231
|
-
? persisted.transcriptPath
|
|
232
|
-
: claudeTranscriptPath(repoRoot, claudeSessionId);
|
|
231
|
+
const transcriptPath = claudeTranscriptPath(taskContext.taskRepoRoot, claudeSessionId);
|
|
233
232
|
const startCommand = {
|
|
234
233
|
...deps.claude.buildRoleStartCommand(TRANSLATOR_ROLE, config.claudeCommand, permissionMode, claudeSessionId, launchMode === "resume", model, effort),
|
|
235
|
-
cwd:
|
|
234
|
+
cwd: taskContext.taskRepoRoot
|
|
236
235
|
};
|
|
237
236
|
const runtimeSession = await deps.runtime.createSession({
|
|
238
237
|
taskSlug: PROJECT_TRANSLATOR_SCOPE,
|
|
@@ -242,8 +241,8 @@ export function createSessionService(deps) {
|
|
|
242
241
|
cwd: startCommand.cwd,
|
|
243
242
|
env: {
|
|
244
243
|
VCM_API_URL: deps.apiUrl,
|
|
245
|
-
VCM_TASK_REPO_ROOT:
|
|
246
|
-
VCM_TASK_SLUG:
|
|
244
|
+
VCM_TASK_REPO_ROOT: taskContext.taskRepoRoot,
|
|
245
|
+
VCM_TASK_SLUG: taskContext.taskSlug,
|
|
247
246
|
VCM_ROLE: TRANSLATOR_ROLE,
|
|
248
247
|
VCM_SESSION_ID: claudeSessionId
|
|
249
248
|
},
|
|
@@ -278,6 +277,7 @@ export function createSessionService(deps) {
|
|
|
278
277
|
return withHarnessRevisionView(repoRoot, record);
|
|
279
278
|
}
|
|
280
279
|
async function launchProjectHarnessEngineerSession(repoRoot, input, launchMode) {
|
|
280
|
+
const taskContext = await resolveProjectToolTaskContext(repoRoot, input, "Harness Engineer");
|
|
281
281
|
const live = toRoleSessionRecordView(getRegisteredProjectHarnessEngineerSession(deps.registry, deps.runtime), deps.runtime);
|
|
282
282
|
if (live && live.status === "running") {
|
|
283
283
|
return withHarnessRevisionView(repoRoot, live);
|
|
@@ -299,12 +299,10 @@ export function createSessionService(deps) {
|
|
|
299
299
|
});
|
|
300
300
|
}
|
|
301
301
|
await deps.fs.ensureDir(resolveRepoPath(repoRoot, HARNESS_ENGINEER_DIR));
|
|
302
|
-
const transcriptPath =
|
|
303
|
-
? persisted.transcriptPath
|
|
304
|
-
: claudeTranscriptPath(repoRoot, claudeSessionId);
|
|
302
|
+
const transcriptPath = claudeTranscriptPath(taskContext.taskRepoRoot, claudeSessionId);
|
|
305
303
|
const startCommand = {
|
|
306
304
|
...deps.claude.buildRoleStartCommand(HARNESS_ENGINEER_ROLE, config.claudeCommand, permissionMode, claudeSessionId, launchMode === "resume", model, effort),
|
|
307
|
-
cwd:
|
|
305
|
+
cwd: taskContext.taskRepoRoot
|
|
308
306
|
};
|
|
309
307
|
const runtimeSession = await deps.runtime.createSession({
|
|
310
308
|
taskSlug: PROJECT_HARNESS_ENGINEER_SCOPE,
|
|
@@ -314,8 +312,8 @@ export function createSessionService(deps) {
|
|
|
314
312
|
cwd: startCommand.cwd,
|
|
315
313
|
env: {
|
|
316
314
|
VCM_API_URL: deps.apiUrl,
|
|
317
|
-
VCM_TASK_REPO_ROOT:
|
|
318
|
-
VCM_TASK_SLUG:
|
|
315
|
+
VCM_TASK_REPO_ROOT: taskContext.taskRepoRoot,
|
|
316
|
+
VCM_TASK_SLUG: taskContext.taskSlug,
|
|
319
317
|
VCM_ROLE: HARNESS_ENGINEER_ROLE,
|
|
320
318
|
VCM_SESSION_ID: claudeSessionId
|
|
321
319
|
},
|
|
@@ -349,6 +347,22 @@ export function createSessionService(deps) {
|
|
|
349
347
|
await persistHarnessEngineerSession(deps.fs, repoRoot, record);
|
|
350
348
|
return withHarnessRevisionView(repoRoot, record);
|
|
351
349
|
}
|
|
350
|
+
async function resolveProjectToolTaskContext(repoRoot, input, roleLabel) {
|
|
351
|
+
const taskSlug = input.taskSlug?.trim();
|
|
352
|
+
if (!taskSlug) {
|
|
353
|
+
throw new VcmError({
|
|
354
|
+
code: "PROJECT_TOOL_TASK_REQUIRED",
|
|
355
|
+
message: `${roleLabel} requires an active task worktree.`,
|
|
356
|
+
statusCode: 409,
|
|
357
|
+
hint: "Create or select a task, then start or resume this project tool session."
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
const task = await deps.taskService.loadTask(repoRoot, taskSlug);
|
|
361
|
+
return {
|
|
362
|
+
taskSlug: task.taskSlug,
|
|
363
|
+
taskRepoRoot: getTaskRuntimeRepoRoot(task)
|
|
364
|
+
};
|
|
365
|
+
}
|
|
352
366
|
async function notifyHarnessUpdatedForSession(repoRoot, session) {
|
|
353
367
|
const runtimeSession = deps.runtime.getSession(session.id);
|
|
354
368
|
if (!runtimeSession || runtimeSession.status !== "running") {
|
|
@@ -515,6 +529,7 @@ export function createSessionService(deps) {
|
|
|
515
529
|
}
|
|
516
530
|
if (existing?.claudeSessionId) {
|
|
517
531
|
return this.resumeProjectTranslatorSession(repoRoot, {
|
|
532
|
+
taskSlug: input.taskSlug,
|
|
518
533
|
permissionMode: input.permissionMode ?? existing.permissionMode,
|
|
519
534
|
model: input.model ?? existing.model,
|
|
520
535
|
effort: input.effort ?? existing.effort,
|
|
@@ -611,6 +626,7 @@ export function createSessionService(deps) {
|
|
|
611
626
|
}
|
|
612
627
|
if (existing?.claudeSessionId) {
|
|
613
628
|
return this.resumeProjectHarnessEngineerSession(repoRoot, {
|
|
629
|
+
taskSlug: input.taskSlug,
|
|
614
630
|
permissionMode: input.permissionMode ?? existing.permissionMode,
|
|
615
631
|
model: input.model ?? existing.model,
|
|
616
632
|
effort: input.effort ?? existing.effort,
|
|
@@ -661,12 +677,10 @@ export function createSessionService(deps) {
|
|
|
661
677
|
.then((session) => scopeProjectRoleSession(session, taskSlug));
|
|
662
678
|
}
|
|
663
679
|
if (role === TRANSLATOR_ROLE) {
|
|
664
|
-
|
|
665
|
-
return this.startProjectTranslatorSession(repoRoot, input);
|
|
680
|
+
return this.startProjectTranslatorSession(repoRoot, { ...input, taskSlug });
|
|
666
681
|
}
|
|
667
682
|
if (role === HARNESS_ENGINEER_ROLE) {
|
|
668
|
-
|
|
669
|
-
return this.startProjectHarnessEngineerSession(repoRoot, input);
|
|
683
|
+
return this.startProjectHarnessEngineerSession(repoRoot, { ...input, taskSlug });
|
|
670
684
|
}
|
|
671
685
|
return launchRoleSession(repoRoot, taskSlug, role, input, "fresh");
|
|
672
686
|
},
|
|
@@ -676,12 +690,10 @@ export function createSessionService(deps) {
|
|
|
676
690
|
.then((session) => scopeProjectRoleSession(session, taskSlug));
|
|
677
691
|
}
|
|
678
692
|
if (role === TRANSLATOR_ROLE) {
|
|
679
|
-
|
|
680
|
-
return this.resumeProjectTranslatorSession(repoRoot, input);
|
|
693
|
+
return this.resumeProjectTranslatorSession(repoRoot, { ...input, taskSlug });
|
|
681
694
|
}
|
|
682
695
|
if (role === HARNESS_ENGINEER_ROLE) {
|
|
683
|
-
|
|
684
|
-
return this.resumeProjectHarnessEngineerSession(repoRoot, input);
|
|
696
|
+
return this.resumeProjectHarnessEngineerSession(repoRoot, { ...input, taskSlug });
|
|
685
697
|
}
|
|
686
698
|
return launchRoleSession(repoRoot, taskSlug, role, input, "resume");
|
|
687
699
|
},
|
|
@@ -732,12 +744,10 @@ export function createSessionService(deps) {
|
|
|
732
744
|
return scopeProjectRoleSession(await launchProjectGateReviewerSession(repoRoot, input, "fresh", taskSlug), taskSlug);
|
|
733
745
|
}
|
|
734
746
|
if (role === TRANSLATOR_ROLE) {
|
|
735
|
-
|
|
736
|
-
return this.restartProjectTranslatorSession(repoRoot, input);
|
|
747
|
+
return this.restartProjectTranslatorSession(repoRoot, { ...input, taskSlug });
|
|
737
748
|
}
|
|
738
749
|
if (role === HARNESS_ENGINEER_ROLE) {
|
|
739
|
-
|
|
740
|
-
return this.restartProjectHarnessEngineerSession(repoRoot, input);
|
|
750
|
+
return this.restartProjectHarnessEngineerSession(repoRoot, { ...input, taskSlug });
|
|
741
751
|
}
|
|
742
752
|
const existing = await this.getRoleSession(repoRoot, taskSlug, role);
|
|
743
753
|
if (!existing) {
|
|
@@ -904,6 +904,7 @@ export function createTranslationService(deps) {
|
|
|
904
904
|
});
|
|
905
905
|
}
|
|
906
906
|
return deps.translationWorkerService.createConversationJob(input.repoRoot, {
|
|
907
|
+
taskSlug: input.taskSlug,
|
|
907
908
|
direction: input.direction,
|
|
908
909
|
sourceText: input.text,
|
|
909
910
|
sourceLanguage: input.sourceLanguage,
|
|
@@ -207,7 +207,7 @@ export function createTranslationWorkerService(deps) {
|
|
|
207
207
|
queue.updatedAt = next.updatedAt;
|
|
208
208
|
await saveQueue(repoRoot, queue);
|
|
209
209
|
}
|
|
210
|
-
const session = await ensureTranslatorSession(repoRoot, next.targetLanguage);
|
|
210
|
+
const session = await ensureTranslatorSession(repoRoot, next.targetLanguage, next.taskSlug);
|
|
211
211
|
await submitTerminalInput(deps.runtime, session.id, batch?.prompt ?? await buildQueuePrompt(repoRoot, next));
|
|
212
212
|
const dispatchedAt = now();
|
|
213
213
|
for (const item of batch?.items ?? [next]) {
|
|
@@ -255,7 +255,7 @@ export function createTranslationWorkerService(deps) {
|
|
|
255
255
|
await saveQueue(repoRoot, queue);
|
|
256
256
|
return { items: candidates, prompt };
|
|
257
257
|
}
|
|
258
|
-
async function ensureTranslatorSession(repoRoot, targetLanguage) {
|
|
258
|
+
async function ensureTranslatorSession(repoRoot, targetLanguage, taskSlug) {
|
|
259
259
|
void targetLanguage;
|
|
260
260
|
if (!deps.sessionService) {
|
|
261
261
|
throw new VcmError({
|
|
@@ -265,6 +265,7 @@ export function createTranslationWorkerService(deps) {
|
|
|
265
265
|
});
|
|
266
266
|
}
|
|
267
267
|
return deps.sessionService.ensureProjectTranslatorSession(repoRoot, {
|
|
268
|
+
taskSlug,
|
|
268
269
|
model: "default",
|
|
269
270
|
effort: "medium"
|
|
270
271
|
});
|
|
@@ -873,6 +874,7 @@ export function createTranslationWorkerService(deps) {
|
|
|
873
874
|
},
|
|
874
875
|
async createFileJob(repoRoot, input) {
|
|
875
876
|
await ensureLayout(repoRoot);
|
|
877
|
+
const taskSlug = requireTranslationTaskSlug(input.taskSlug);
|
|
876
878
|
const sourcePath = normalizeRepoRelative(input.sourcePath);
|
|
877
879
|
const absoluteSourcePath = resolveRepoPath(repoRoot, sourcePath);
|
|
878
880
|
assertInsideRepo(repoRoot, absoluteSourcePath);
|
|
@@ -925,6 +927,7 @@ export function createTranslationWorkerService(deps) {
|
|
|
925
927
|
type: input.force ? "force-retranslate" : "file",
|
|
926
928
|
status: "queued",
|
|
927
929
|
targetLanguage,
|
|
930
|
+
taskSlug,
|
|
928
931
|
jobId,
|
|
929
932
|
requestPath: job.requestPath,
|
|
930
933
|
expectedResultPath: job.resultPath,
|
|
@@ -997,6 +1000,7 @@ export function createTranslationWorkerService(deps) {
|
|
|
997
1000
|
},
|
|
998
1001
|
async createBootstrapRun(repoRoot, input) {
|
|
999
1002
|
await ensureLayout(repoRoot);
|
|
1003
|
+
const taskSlug = requireTranslationTaskSlug(input.taskSlug);
|
|
1000
1004
|
const targetLanguage = input.targetLanguage.trim() || "zh-CN";
|
|
1001
1005
|
const candidatePaths = (input.candidatePaths?.length
|
|
1002
1006
|
? input.candidatePaths
|
|
@@ -1020,6 +1024,7 @@ export function createTranslationWorkerService(deps) {
|
|
|
1020
1024
|
type: "bootstrap",
|
|
1021
1025
|
status: "queued",
|
|
1022
1026
|
targetLanguage,
|
|
1027
|
+
taskSlug,
|
|
1023
1028
|
jobId: runId,
|
|
1024
1029
|
requestPath: run.requestPath,
|
|
1025
1030
|
expectedResultPath: run.reportPath,
|
|
@@ -1054,6 +1059,7 @@ export function createTranslationWorkerService(deps) {
|
|
|
1054
1059
|
},
|
|
1055
1060
|
async createMemoryUpdate(repoRoot, input) {
|
|
1056
1061
|
await ensureLayout(repoRoot);
|
|
1062
|
+
const taskSlug = requireTranslationTaskSlug(input.taskSlug);
|
|
1057
1063
|
const targetLanguage = input.targetLanguage.trim() || "zh-CN";
|
|
1058
1064
|
const timestamp = now();
|
|
1059
1065
|
const runId = `memory-update-${Date.now()}-${createId().slice(0, 8)}`;
|
|
@@ -1065,6 +1071,7 @@ export function createTranslationWorkerService(deps) {
|
|
|
1065
1071
|
type: "memory-update",
|
|
1066
1072
|
status: "queued",
|
|
1067
1073
|
targetLanguage,
|
|
1074
|
+
taskSlug,
|
|
1068
1075
|
jobId: runId,
|
|
1069
1076
|
requestPath
|
|
1070
1077
|
});
|
|
@@ -1130,6 +1137,7 @@ export function createTranslationWorkerService(deps) {
|
|
|
1130
1137
|
},
|
|
1131
1138
|
async createConversationJob(repoRoot, input) {
|
|
1132
1139
|
await ensureLayout(repoRoot);
|
|
1140
|
+
const taskSlug = requireTranslationTaskSlug(input.taskSlug);
|
|
1133
1141
|
const sourceText = input.sourceText.trimEnd();
|
|
1134
1142
|
if (!sourceText.trim()) {
|
|
1135
1143
|
throw new VcmError({
|
|
@@ -1159,6 +1167,7 @@ export function createTranslationWorkerService(deps) {
|
|
|
1159
1167
|
type: "conversation",
|
|
1160
1168
|
status: "queued",
|
|
1161
1169
|
targetLanguage,
|
|
1170
|
+
taskSlug,
|
|
1162
1171
|
jobId,
|
|
1163
1172
|
requestPath: job.requestPath,
|
|
1164
1173
|
expectedResultPath: job.resultPath
|
|
@@ -1287,10 +1296,27 @@ export function createTranslationWorkerService(deps) {
|
|
|
1287
1296
|
}
|
|
1288
1297
|
},
|
|
1289
1298
|
ensureTranslatorSession(repoRoot) {
|
|
1290
|
-
|
|
1299
|
+
void repoRoot;
|
|
1300
|
+
throw new VcmError({
|
|
1301
|
+
code: "TRANSLATION_TASK_REQUIRED",
|
|
1302
|
+
message: "Translator requires an active task worktree.",
|
|
1303
|
+
statusCode: 409
|
|
1304
|
+
});
|
|
1291
1305
|
}
|
|
1292
1306
|
};
|
|
1293
1307
|
}
|
|
1308
|
+
function requireTranslationTaskSlug(value) {
|
|
1309
|
+
const taskSlug = value?.trim();
|
|
1310
|
+
if (!taskSlug) {
|
|
1311
|
+
throw new VcmError({
|
|
1312
|
+
code: "TRANSLATION_TASK_REQUIRED",
|
|
1313
|
+
message: "Translation requires an active task worktree.",
|
|
1314
|
+
statusCode: 409,
|
|
1315
|
+
hint: "Create or select a task before starting translation."
|
|
1316
|
+
});
|
|
1317
|
+
}
|
|
1318
|
+
return taskSlug;
|
|
1319
|
+
}
|
|
1294
1320
|
async function listBrowserEntries(repoRoot, fs, currentPath, limit) {
|
|
1295
1321
|
const directoryPath = resolveRepoPath(repoRoot, currentPath);
|
|
1296
1322
|
const names = (await fs.readDir(directoryPath)).filter((name) => !isIgnoredBrowserName(name));
|
|
@@ -1496,6 +1522,7 @@ function isQueueItem(value) {
|
|
|
1496
1522
|
typeof candidate.type === "string" &&
|
|
1497
1523
|
typeof candidate.status === "string" &&
|
|
1498
1524
|
typeof candidate.targetLanguage === "string" &&
|
|
1525
|
+
typeof candidate.taskSlug === "string" &&
|
|
1499
1526
|
typeof candidate.requestPath === "string";
|
|
1500
1527
|
}
|
|
1501
1528
|
function isFileJob(value) {
|
|
@@ -27,6 +27,7 @@ You are not part of the task workflow round state.
|
|
|
27
27
|
|
|
28
28
|
- Propose harness changes as reviewable diffs.
|
|
29
29
|
- Do not silently apply edits.
|
|
30
|
+
- During a VCM-managed bootstrap run, apply permitted bootstrap edits directly in the active task worktree and commit them yourself.
|
|
30
31
|
- Do not overwrite VCM fixed managed blocks.
|
|
31
32
|
- Keep project-specific customization outside VCM managed blocks.
|
|
32
33
|
- If a fixed managed block appears wrong, draft a VCM issue instead of editing
|
|
@@ -34,6 +35,7 @@ You are not part of the task workflow round state.
|
|
|
34
35
|
- Include affected files, impacted roles, session restart/reminder impact, and
|
|
35
36
|
validation recommendations with every proposal.
|
|
36
37
|
- Do not edit production source code as part of harness maintenance.
|
|
38
|
+
- VCM does not create Harness Engineer commits after your turn.
|
|
37
39
|
|
|
38
40
|
## VCM Feedback
|
|
39
41
|
|
|
@@ -19,7 +19,9 @@ This skill is an operating procedure. It does not replace the deterministic VCM
|
|
|
19
19
|
3. Fill project context: add or update non-managed project facts in \`CLAUDE.md\` above the VCM managed block.
|
|
20
20
|
4. Fill durable docs: update \`docs/ARCHITECTURE.md\`, module-level \`ARCHITECTURE.md\` files, and \`docs/TESTING.md\` with detailed project-specific content.
|
|
21
21
|
5. Preserve user-authored content and VCM managed blocks.
|
|
22
|
-
6.
|
|
22
|
+
6. Review \`git status\` and \`git diff\`.
|
|
23
|
+
7. Stage only allowed bootstrap harness changes and create a commit in the active task worktree.
|
|
24
|
+
8. Report evidence, commit hash, final git status, unknowns, confirmation-needed areas, generation failures, and recommended deterministic VCM actions.
|
|
23
25
|
|
|
24
26
|
## Typical Outputs
|
|
25
27
|
|
|
@@ -64,12 +66,20 @@ This skill is an operating procedure. It does not replace the deterministic VCM
|
|
|
64
66
|
- Keep historical investigation details, superseded failures, temporary diagnostics, and per-task validation logs out of \`docs/TESTING.md\`.
|
|
65
67
|
- Keep reviewer ownership of validation strategy and testing documentation clear.
|
|
66
68
|
|
|
69
|
+
### Commit
|
|
70
|
+
|
|
71
|
+
- Create the bootstrap commit yourself after the allowed files are updated.
|
|
72
|
+
- Do not include product source, product tests, package manifests, lockfiles, deployment config, secrets, or VCM managed-block changes in the commit.
|
|
73
|
+
- VCM will not commit bootstrap changes after your turn.
|
|
74
|
+
|
|
67
75
|
## Final Summary
|
|
68
76
|
|
|
69
77
|
Include:
|
|
70
78
|
|
|
71
79
|
- files reviewed
|
|
72
80
|
- files drafted or updated
|
|
81
|
+
- commit hash
|
|
82
|
+
- final git status
|
|
73
83
|
- verified claims
|
|
74
84
|
- inferred claims
|
|
75
85
|
- unknowns
|