vibe-coding-master 0.3.0 → 0.3.2
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 +10 -7
- package/dist/backend/adapters/claude-adapter.js +4 -1
- package/dist/backend/api/codex-hook-routes.js +9 -0
- package/dist/backend/api/codex-review-routes.js +4 -0
- package/dist/backend/api/translation-routes.js +2 -2
- package/dist/backend/cli/install-vcm-harness.js +14 -1
- package/dist/backend/gateway/gateway-service.js +4 -3
- package/dist/backend/server.js +10 -0
- package/dist/backend/services/app-settings-service.js +12 -4
- package/dist/backend/services/artifact-service.js +2 -1
- package/dist/backend/services/claude-hook-service.js +3 -3
- package/dist/backend/services/codex-hook-service.js +87 -0
- package/dist/backend/services/codex-review-service.js +145 -60
- package/dist/backend/services/harness-service.js +28 -1
- package/dist/backend/services/message-service.js +8 -7
- package/dist/backend/services/project-service.js +2 -2
- package/dist/backend/services/round-service.js +29 -25
- package/dist/backend/services/session-service.js +141 -12
- package/dist/backend/templates/handoff.js +10 -0
- package/dist/backend/templates/harness/architect-agent.js +8 -4
- package/dist/backend/templates/harness/claude-root.js +1 -1
- package/dist/backend/templates/harness/coder-agent.js +1 -1
- package/dist/backend/templates/harness/codex-review.js +41 -6
- package/dist/backend/templates/harness/reviewer-agent.js +1 -0
- package/dist/shared/constants.js +15 -1
- package/dist/shared/types/app-settings.js +4 -3
- package/dist/shared/types/codex-hook.js +1 -0
- package/dist/shared/types/session.js +44 -0
- package/dist/shared/validation/artifact-check.js +1 -0
- package/dist-frontend/assets/index-BavJjWQY.js +92 -0
- package/dist-frontend/assets/index-CR1EOe-w.css +32 -0
- package/dist-frontend/index.html +2 -2
- package/docs/ARCHITECTURE.md +1 -0
- package/docs/TESTING.md +82 -0
- package/docs/cc-best-practices.md +1 -0
- package/docs/codex-review-gates.md +88 -45
- package/docs/full-harness-baseline.md +1 -1
- package/docs/gateway-design.md +5 -4
- package/docs/known-issues.md +1 -0
- package/docs/product-design.md +28 -11
- package/docs/vcm-cc-best-practices.md +15 -5
- package/package.json +1 -1
- package/dist-frontend/assets/index-C9l94uxA.js +0 -92
- package/dist-frontend/assets/index-D6jWo6Jd.css +0 -32
|
@@ -5,6 +5,10 @@ import { VcmError } from "../errors.js";
|
|
|
5
5
|
import { resolveRepoPath } from "../adapters/filesystem.js";
|
|
6
6
|
import { claudeTranscriptPath } from "./claude-transcript-service.js";
|
|
7
7
|
import { getTaskRuntimeRepoRoot } from "./task-service.js";
|
|
8
|
+
const CODEX_REVIEWER_ROLE = "codex-reviewer";
|
|
9
|
+
const CODEX_DIR = ".ai/codex";
|
|
10
|
+
const CODEX_REVIEW_DIR = ".ai/vcm/codex-reviews";
|
|
11
|
+
const CODEX_CONFIG_PATH = ".ai/codex/config.toml";
|
|
8
12
|
export function createSessionService(deps) {
|
|
9
13
|
const now = deps.now ?? (() => new Date().toISOString());
|
|
10
14
|
async function launchRoleSession(repoRoot, taskSlug, role, input, launchMode) {
|
|
@@ -17,29 +21,38 @@ export function createSessionService(deps) {
|
|
|
17
21
|
const taskRepoRoot = getTaskRuntimeRepoRoot(task);
|
|
18
22
|
const paths = deps.artifactService.getHandoffPaths(taskRepoRoot, task.handoffDir);
|
|
19
23
|
const persisted = await loadPersistedRoleRecord(deps.fs, taskRepoRoot, config.stateRoot, taskSlug, role);
|
|
24
|
+
const isCodexReviewer = role === CODEX_REVIEWER_ROLE;
|
|
20
25
|
const permissionMode = normalizeClaudePermissionMode(input.permissionMode ?? persisted?.permissionMode);
|
|
21
|
-
const model =
|
|
26
|
+
const model = isCodexReviewer
|
|
27
|
+
? normalizeCodexModel(input.model ?? persisted?.model)
|
|
28
|
+
: normalizeClaudeModel(input.model ?? persisted?.model);
|
|
29
|
+
const effort = normalizeSessionEffort(input.effort ?? persisted?.effort);
|
|
22
30
|
const claudeSessionId = launchMode === "resume"
|
|
23
31
|
? persisted?.claudeSessionId
|
|
24
32
|
: randomUUID();
|
|
25
33
|
if (!claudeSessionId) {
|
|
26
34
|
throw new VcmError({
|
|
27
35
|
code: "CLAUDE_SESSION_MISSING",
|
|
28
|
-
message: `${role} does not have a
|
|
36
|
+
message: `${role} does not have a session id to resume.`,
|
|
29
37
|
statusCode: 409,
|
|
30
38
|
hint: "Start the role once before using Resume."
|
|
31
39
|
});
|
|
32
40
|
}
|
|
33
41
|
const transcriptPath = launchMode === "resume" && persisted?.transcriptPath
|
|
34
42
|
? persisted.transcriptPath
|
|
35
|
-
: claudeTranscriptPath(taskRepoRoot, claudeSessionId);
|
|
36
|
-
const startCommand =
|
|
43
|
+
: isCodexReviewer ? undefined : claudeTranscriptPath(taskRepoRoot, claudeSessionId);
|
|
44
|
+
const startCommand = isCodexReviewer
|
|
45
|
+
? await buildCodexReviewerStartCommand(deps.fs, taskRepoRoot, launchMode, model, effort)
|
|
46
|
+
: {
|
|
47
|
+
...deps.claude.buildRoleStartCommand(role, config.claudeCommand, permissionMode, claudeSessionId, launchMode === "resume", model, effort),
|
|
48
|
+
cwd: taskRepoRoot
|
|
49
|
+
};
|
|
37
50
|
const runtimeSession = await deps.runtime.createSession({
|
|
38
51
|
taskSlug,
|
|
39
52
|
role,
|
|
40
53
|
command: startCommand.command,
|
|
41
54
|
args: startCommand.args,
|
|
42
|
-
cwd:
|
|
55
|
+
cwd: startCommand.cwd,
|
|
43
56
|
env: {
|
|
44
57
|
VCM_API_URL: deps.apiUrl,
|
|
45
58
|
VCM_TASK_REPO_ROOT: taskRepoRoot,
|
|
@@ -63,7 +76,8 @@ export function createSessionService(deps) {
|
|
|
63
76
|
command: startCommand.display,
|
|
64
77
|
permissionMode,
|
|
65
78
|
model,
|
|
66
|
-
|
|
79
|
+
effort,
|
|
80
|
+
cwd: startCommand.cwd,
|
|
67
81
|
terminalBackend: "node-pty",
|
|
68
82
|
pid: runtimeSession.pid,
|
|
69
83
|
logPath: paths.roleLogPaths[role],
|
|
@@ -159,15 +173,18 @@ export function createSessionService(deps) {
|
|
|
159
173
|
}
|
|
160
174
|
return sessions;
|
|
161
175
|
},
|
|
162
|
-
async
|
|
176
|
+
async recordRoleHookEvent(repoRoot, input) {
|
|
163
177
|
const current = await this.getRoleSession(repoRoot, input.taskSlug, input.role);
|
|
164
|
-
if (!current || !
|
|
178
|
+
if (!current || (!input.allowSessionMismatch && !matchesRoleHookSession(current, input))) {
|
|
165
179
|
return undefined;
|
|
166
180
|
}
|
|
167
181
|
const timestamp = now();
|
|
168
182
|
const isStop = input.eventName === "Stop";
|
|
169
183
|
const updated = {
|
|
170
184
|
...current,
|
|
185
|
+
claudeSessionId: input.sessionId ?? current.claudeSessionId,
|
|
186
|
+
transcriptPath: input.transcriptPath ?? current.transcriptPath,
|
|
187
|
+
cwd: input.cwd ?? current.cwd,
|
|
171
188
|
activityStatus: isStop ? "idle" : "running",
|
|
172
189
|
lastHookEventAt: timestamp,
|
|
173
190
|
lastTurnEndedAt: isStop ? timestamp : current.lastTurnEndedAt,
|
|
@@ -180,6 +197,16 @@ export function createSessionService(deps) {
|
|
|
180
197
|
await persistTaskSession(deps.fs, getTaskRuntimeRepoRoot(task), config.stateRoot, updated);
|
|
181
198
|
return updated;
|
|
182
199
|
},
|
|
200
|
+
recordClaudeHookEvent(repoRoot, input) {
|
|
201
|
+
return this.recordRoleHookEvent(repoRoot, {
|
|
202
|
+
taskSlug: input.taskSlug,
|
|
203
|
+
role: input.role,
|
|
204
|
+
eventName: input.eventName,
|
|
205
|
+
sessionId: input.claudeSessionId,
|
|
206
|
+
transcriptPath: input.transcriptPath,
|
|
207
|
+
cwd: input.cwd
|
|
208
|
+
});
|
|
209
|
+
},
|
|
183
210
|
async markRoleActivityRunning(repoRoot, taskSlug, role) {
|
|
184
211
|
const current = await this.getRoleSession(repoRoot, taskSlug, role);
|
|
185
212
|
if (!current) {
|
|
@@ -201,14 +228,67 @@ export function createSessionService(deps) {
|
|
|
201
228
|
}
|
|
202
229
|
};
|
|
203
230
|
}
|
|
204
|
-
function
|
|
205
|
-
|
|
231
|
+
async function buildCodexReviewerStartCommand(fs, taskRepoRoot, launchMode, selectedModel, selectedEffort) {
|
|
232
|
+
const codexDir = resolveRepoPath(taskRepoRoot, CODEX_DIR);
|
|
233
|
+
const reviewDir = resolveRepoPath(taskRepoRoot, CODEX_REVIEW_DIR);
|
|
234
|
+
if (!(await fs.pathExists(codexDir))) {
|
|
235
|
+
throw new VcmError({
|
|
236
|
+
code: "CODEX_REVIEW_CONFIG_MISSING",
|
|
237
|
+
message: `${CODEX_DIR} does not exist.`,
|
|
238
|
+
statusCode: 409,
|
|
239
|
+
hint: "Apply the VCM harness before starting Codex Reviewer."
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
await fs.ensureDir(reviewDir);
|
|
243
|
+
const config = await loadCodexSessionConfig(fs, taskRepoRoot);
|
|
244
|
+
const model = selectedModel === "default"
|
|
245
|
+
? config.model
|
|
246
|
+
: selectedModel;
|
|
247
|
+
const modelReasoningEffort = selectedEffort === "default"
|
|
248
|
+
? config.modelReasoningEffort
|
|
249
|
+
: selectedEffort;
|
|
250
|
+
const args = launchMode === "resume"
|
|
251
|
+
? ["resume", "--last"]
|
|
252
|
+
: [];
|
|
253
|
+
args.push("--cd", codexDir, "--add-dir", reviewDir, "--sandbox", "workspace-write", "--ask-for-approval", "never", "--dangerously-bypass-hook-trust");
|
|
254
|
+
if (model && model !== "default") {
|
|
255
|
+
args.push("--model", model);
|
|
256
|
+
}
|
|
257
|
+
if (modelReasoningEffort && modelReasoningEffort !== "default") {
|
|
258
|
+
args.push("--config", `model_reasoning_effort="${modelReasoningEffort}"`);
|
|
259
|
+
}
|
|
260
|
+
return {
|
|
261
|
+
command: config.command,
|
|
262
|
+
args,
|
|
263
|
+
cwd: taskRepoRoot,
|
|
264
|
+
display: [config.command, ...args].map(formatDisplayArg).join(" ")
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
async function loadCodexSessionConfig(fs, taskRepoRoot) {
|
|
268
|
+
const configPath = resolveRepoPath(taskRepoRoot, CODEX_CONFIG_PATH);
|
|
269
|
+
if (!(await fs.pathExists(configPath))) {
|
|
270
|
+
return {
|
|
271
|
+
command: "codex",
|
|
272
|
+
model: "gpt-5.5",
|
|
273
|
+
modelReasoningEffort: "xhigh"
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
const content = await fs.readText(configPath);
|
|
277
|
+
const reviewSection = extractTomlSection(content, "vcm.codex_review");
|
|
278
|
+
return {
|
|
279
|
+
command: parseTomlString(reviewSection, "command") ?? "codex",
|
|
280
|
+
model: parseTomlString(content, "model") ?? "gpt-5.5",
|
|
281
|
+
modelReasoningEffort: parseTomlString(content, "model_reasoning_effort") ?? "xhigh"
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
function matchesRoleHookSession(record, input) {
|
|
285
|
+
if (input.sessionId && record.claudeSessionId === input.sessionId) {
|
|
206
286
|
return true;
|
|
207
287
|
}
|
|
208
288
|
if (input.transcriptPath && record.transcriptPath === input.transcriptPath) {
|
|
209
289
|
return true;
|
|
210
290
|
}
|
|
211
|
-
if (!input.
|
|
291
|
+
if (!input.sessionId && !input.transcriptPath) {
|
|
212
292
|
return true;
|
|
213
293
|
}
|
|
214
294
|
return false;
|
|
@@ -249,7 +329,10 @@ async function loadPersistedRoleRecord(fs, repoRoot, stateRoot, taskSlug, role)
|
|
|
249
329
|
? legacy.lastStopAt
|
|
250
330
|
: undefined),
|
|
251
331
|
permissionMode: normalizeClaudePermissionMode(record.permissionMode),
|
|
252
|
-
model:
|
|
332
|
+
model: record.role === CODEX_REVIEWER_ROLE
|
|
333
|
+
? normalizeCodexModel(record.model)
|
|
334
|
+
: normalizeClaudeModel(record.model),
|
|
335
|
+
effort: normalizeSessionEffort(record.effort)
|
|
253
336
|
}
|
|
254
337
|
: undefined;
|
|
255
338
|
}
|
|
@@ -311,3 +394,49 @@ function normalizeClaudeModel(value) {
|
|
|
311
394
|
}
|
|
312
395
|
return "default";
|
|
313
396
|
}
|
|
397
|
+
function normalizeCodexModel(value) {
|
|
398
|
+
if (value === "default" || value === "gpt-5.5") {
|
|
399
|
+
return value;
|
|
400
|
+
}
|
|
401
|
+
return "gpt-5.5";
|
|
402
|
+
}
|
|
403
|
+
function normalizeSessionEffort(value) {
|
|
404
|
+
if (value === "low"
|
|
405
|
+
|| value === "medium"
|
|
406
|
+
|| value === "high"
|
|
407
|
+
|| value === "xhigh"
|
|
408
|
+
|| value === "max") {
|
|
409
|
+
return value;
|
|
410
|
+
}
|
|
411
|
+
return "default";
|
|
412
|
+
}
|
|
413
|
+
function extractTomlSection(content, sectionName) {
|
|
414
|
+
const lines = content.split(/\r?\n/);
|
|
415
|
+
const header = `[${sectionName}]`;
|
|
416
|
+
const start = lines.findIndex((line) => line.trim() === header);
|
|
417
|
+
if (start < 0) {
|
|
418
|
+
return "";
|
|
419
|
+
}
|
|
420
|
+
const section = [];
|
|
421
|
+
for (let index = start + 1; index < lines.length; index += 1) {
|
|
422
|
+
const line = lines[index];
|
|
423
|
+
if (/^\s*\[[^\]]+\]\s*$/.test(line)) {
|
|
424
|
+
break;
|
|
425
|
+
}
|
|
426
|
+
section.push(line);
|
|
427
|
+
}
|
|
428
|
+
return section.join("\n");
|
|
429
|
+
}
|
|
430
|
+
function parseTomlString(content, key) {
|
|
431
|
+
const pattern = new RegExp(`^\\s*${escapeRegExp(key)}\\s*=\\s*"([^"]*)"\\s*$`, "m");
|
|
432
|
+
return pattern.exec(content)?.[1];
|
|
433
|
+
}
|
|
434
|
+
function escapeRegExp(value) {
|
|
435
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
436
|
+
}
|
|
437
|
+
function formatDisplayArg(value) {
|
|
438
|
+
if (/^[A-Za-z0-9_./:=@+-]+$/.test(value)) {
|
|
439
|
+
return value;
|
|
440
|
+
}
|
|
441
|
+
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
442
|
+
}
|
|
@@ -9,6 +9,16 @@ TBD
|
|
|
9
9
|
|
|
10
10
|
TBD
|
|
11
11
|
|
|
12
|
+
## Scaffold Manifest
|
|
13
|
+
|
|
14
|
+
Task-specific context and coder guidance go here, not in source-code comments.
|
|
15
|
+
Source-code comments should only describe durable behavior, contracts, invariants,
|
|
16
|
+
error boundaries, or non-obvious logic that should remain useful after this task.
|
|
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 |
|
|
21
|
+
|
|
12
22
|
## Implementation Plan
|
|
13
23
|
|
|
14
24
|
TBD
|
|
@@ -21,24 +21,28 @@ export function renderArchitectHarnessRules() {
|
|
|
21
21
|
|
|
22
22
|
### Architecture Plan
|
|
23
23
|
|
|
24
|
-
- Before coder work starts, write \`.ai/vcm/handoffs/architecture-plan.md
|
|
24
|
+
- Before coder work starts, write \`.ai/vcm/handoffs/architecture-plan.md\`, choose the minimum necessary code scaffolding, and include a Scaffold Manifest for task-specific context and coder guidance.
|
|
25
25
|
|
|
26
26
|
#### Plan Document
|
|
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.
|
|
31
|
+
- Put task context, phase notes, handoff instructions, temporary rationale, and coder guidance in the \`Scaffold Manifest\`, not in source-code comments.
|
|
30
32
|
- Cover architecture docs impact, known risks, and Replan triggers.
|
|
31
33
|
- 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.
|
|
32
34
|
|
|
33
35
|
#### Code Scaffolding
|
|
34
36
|
|
|
35
|
-
- Create or update module/file scaffolding
|
|
36
|
-
-
|
|
37
|
+
- Create or update only the minimum module/file scaffolding needed to make boundaries, callable surfaces, and placeholders unambiguous.
|
|
38
|
+
- Source-code comments must describe durable behavior, contracts, invariants, error boundaries, or non-obvious logic that should remain useful after the task is complete.
|
|
39
|
+
- Do not put task-specific context, phase notes, handoff instructions, temporary plan rationale, or coder guidance in source-code comments.
|
|
40
|
+
- When changing an existing file, update only affected durable comments or callable surfaces; do not rewrite unrelated file comments.
|
|
37
41
|
- Define every new or changed non-private callable surface directly in code with its signature shape and contract comment.
|
|
38
42
|
- 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.
|
|
39
43
|
- 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.
|
|
40
44
|
- Mark incomplete implementation bodies with \`VCM:CODE\`; coder must implement them and remove the markers before handoff.
|
|
41
|
-
- Architect scaffolding may include modules, files, signatures, type shapes, comments, and placeholder bodies, but not real business implementation beyond minimal scaffold code.
|
|
45
|
+
- Architect scaffolding may include modules, files, signatures, type shapes, durable comments, and placeholder bodies, but not real business implementation beyond minimal scaffold code.
|
|
42
46
|
- Coder may add private implementation helpers, but must not add or change cross-file callable surface without architect replan.
|
|
43
47
|
|
|
44
48
|
### Phase Planning
|
|
@@ -27,7 +27,7 @@ export function renderRootClaudeHarnessRules() {
|
|
|
27
27
|
## VCM Task Flow
|
|
28
28
|
|
|
29
29
|
- Code changes use the full route: \`project-manager -> architect -> coder -> reviewer -> architect docs sync -> project-manager final acceptance\`.
|
|
30
|
-
- Before code changes, architect must write an architecture plan and code scaffolding that cover file responsibilities, cross-file callable surfaces, user-visible behavior, docs impact, risks, and Replan triggers.
|
|
30
|
+
- Before code changes, architect must write an architecture plan with a Scaffold Manifest and minimum necessary code scaffolding that cover file responsibilities, cross-file callable surfaces, user-visible behavior, docs impact, risks, and Replan triggers.
|
|
31
31
|
- Docs-only changes may use: \`project-manager -> architect -> project-manager final acceptance\`.
|
|
32
32
|
- Test-only or validation-only work may use: \`project-manager -> reviewer -> project-manager final acceptance\`.
|
|
33
33
|
- If a docs/test/validation-only task reveals required code, architecture, public contract, dependency, durable-doc, or test-strategy changes, route back through the full code-change flow.
|
|
@@ -14,7 +14,7 @@ export function renderCoderHarnessRules() {
|
|
|
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.
|
|
17
|
-
- Maintain code documentation: preserve architect-written comments, add comments for non-obvious
|
|
17
|
+
- Maintain code documentation: preserve durable architect-written contract comments, do not copy Scaffold Manifest task context into source comments, add comments only for non-obvious durable logic, remove stale/debug/TODO/task-process comments, and keep code and comments consistent.
|
|
18
18
|
|
|
19
19
|
### General Coding Standards
|
|
20
20
|
|
|
@@ -31,10 +31,12 @@ Check that the plan:
|
|
|
31
31
|
- matches the user request and approved scope
|
|
32
32
|
- names affected modules/files, file responsibilities, and user-visible changes
|
|
33
33
|
- defines new or changed non-private callable surfaces: visibility, signature shape, callers, contract, side effects, and error boundaries
|
|
34
|
+
- includes a Scaffold Manifest that carries task-specific context, coder guidance, allowed freedom, expected \`VCM:CODE\`, durable code comment needs, proof points, and Replan triggers
|
|
34
35
|
- preserves dependency direction and avoids unapproved dependencies
|
|
35
36
|
- states docs/generated-context impact or explains why none is needed
|
|
36
37
|
- names risks, proof points, phase boundaries when needed, and Replan triggers
|
|
37
38
|
- uses \`VCM:CODE\` for incomplete implementation and leaves no coder ambiguity
|
|
39
|
+
- keeps task-specific context, phase notes, handoff instructions, and coder guidance out of source-code comments
|
|
38
40
|
- does not take over reviewer-owned validation strategy or test adequacy
|
|
39
41
|
|
|
40
42
|
### Validation Adequacy
|
|
@@ -56,6 +58,7 @@ Check that the final diff:
|
|
|
56
58
|
- stays inside the approved plan, phase, and user constraints
|
|
57
59
|
- introduces no unapproved modules, dependencies, public contracts, cross-file callable surfaces, or durable-doc changes
|
|
58
60
|
- removes all \`VCM:CODE\` markers
|
|
61
|
+
- leaves no task-specific process comments in source or test code, such as role handoff notes, phase notes, current-task rationale, or coder instructions
|
|
59
62
|
- contains no fake completion: hardcoded success, disabled logic, swallowed errors, test-only shortcuts, or silent fallback hiding failure
|
|
60
63
|
- preserves existing behavior unless the plan changes it
|
|
61
64
|
- keeps changed functions focused and meaningfully named
|
|
@@ -96,12 +99,8 @@ approval_policy = "never"
|
|
|
96
99
|
default_permissions = "vcm_codex_reviewer"
|
|
97
100
|
|
|
98
101
|
[vcm.codex_review]
|
|
99
|
-
enabled =
|
|
100
|
-
required_gates = [
|
|
101
|
-
"architecture-plan",
|
|
102
|
-
"validation-adequacy",
|
|
103
|
-
"final-diff",
|
|
104
|
-
]
|
|
102
|
+
enabled = false
|
|
103
|
+
required_gates = []
|
|
105
104
|
|
|
106
105
|
[permissions.vcm_codex_reviewer.workspace_roots]
|
|
107
106
|
"../.." = true
|
|
@@ -118,6 +117,41 @@ required_gates = [
|
|
|
118
117
|
[permissions.vcm_codex_reviewer.network]
|
|
119
118
|
enabled = false`;
|
|
120
119
|
}
|
|
120
|
+
export function renderCodexCliConfigHarnessRules() {
|
|
121
|
+
return `[features]
|
|
122
|
+
hooks = true`;
|
|
123
|
+
}
|
|
124
|
+
export function renderCodexHooksHarnessRules() {
|
|
125
|
+
const eventScript = "let s=\"\";process.stdin.setEncoding(\"utf8\");process.stdin.on(\"data\",d=>s+=d);process.stdin.on(\"end\",()=>{let event={};try{event=s.trim()?JSON.parse(s):{};}catch{event={raw:s};}process.stdout.write(JSON.stringify({taskSlug:process.env.VCM_TASK_SLUG,role:process.env.VCM_ROLE,event}));});";
|
|
126
|
+
const userPromptCommand = `sh -c 'if [ -z "\${VCM_TASK_SLUG:-}" ] || [ -z "\${VCM_ROLE:-}" ] || [ -z "\${VCM_API_URL:-}" ]; then exit 0; fi; node -e '"'"'${eventScript}'"'"' | curl -fsS --max-time 2 -X POST "\${VCM_API_URL}/api/hooks/codex-reviewer" -H "content-type: application/json" --data-binary @- >/dev/null || true'`;
|
|
127
|
+
const stopCommand = `sh -c 'if [ -z "\${VCM_TASK_SLUG:-}" ] || [ -z "\${VCM_ROLE:-}" ] || [ -z "\${VCM_API_URL:-}" ]; then printf "{}"; exit 0; fi; node -e '"'"'${eventScript}'"'"' | curl -fsS --max-time 5 -X POST "\${VCM_API_URL}/api/hooks/codex-reviewer/stop" -H "content-type: application/json" --data-binary @- || printf "{}"'`;
|
|
128
|
+
return JSON.stringify({
|
|
129
|
+
hooks: {
|
|
130
|
+
UserPromptSubmit: [
|
|
131
|
+
{
|
|
132
|
+
hooks: [
|
|
133
|
+
{
|
|
134
|
+
type: "command",
|
|
135
|
+
command: userPromptCommand,
|
|
136
|
+
timeout: 5
|
|
137
|
+
}
|
|
138
|
+
]
|
|
139
|
+
}
|
|
140
|
+
],
|
|
141
|
+
Stop: [
|
|
142
|
+
{
|
|
143
|
+
hooks: [
|
|
144
|
+
{
|
|
145
|
+
type: "command",
|
|
146
|
+
command: stopCommand,
|
|
147
|
+
timeout: 10
|
|
148
|
+
}
|
|
149
|
+
]
|
|
150
|
+
}
|
|
151
|
+
]
|
|
152
|
+
}
|
|
153
|
+
}, null, 2);
|
|
154
|
+
}
|
|
121
155
|
export function renderCodexArchitecturePlanPrompt() {
|
|
122
156
|
return `# Codex Gate: architecture-plan
|
|
123
157
|
|
|
@@ -130,6 +164,7 @@ Review whether the architecture plan is ready for coder implementation.
|
|
|
130
164
|
- \`../../.claude/agents/coder.md\`
|
|
131
165
|
- \`../../.claude/agents/reviewer.md\`
|
|
132
166
|
- \`../../.ai/vcm/handoffs/architecture-plan.md\`
|
|
167
|
+
- current git status and scaffold diff from \`../..\`
|
|
133
168
|
- \`../../.ai/generated/module-index.json\`
|
|
134
169
|
- \`../../.ai/generated/public-surface.json\`
|
|
135
170
|
|
|
@@ -23,6 +23,7 @@ export function renderReviewerHarnessRules() {
|
|
|
23
23
|
- Record failed commands, observed behavior, expected behavior, reproduction steps, skipped checks, and coverage gaps.
|
|
24
24
|
- If validation fails or expected behavior is unclear, report the evidence to project-manager; architect owns diagnosis and next-step routing.
|
|
25
25
|
- Add or modify tests, fixtures, or test helpers needed for validation confidence.
|
|
26
|
+
- 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.
|
|
26
27
|
- 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.
|
|
27
28
|
|
|
28
29
|
### Testing Documentation
|
package/dist/shared/constants.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export const DEFAULT_BACKEND_PORT = 4173;
|
|
2
2
|
export const DEFAULT_FRONTEND_PORT = 5173;
|
|
3
|
-
export const
|
|
3
|
+
export const VCM_ROLE_DEFINITIONS = [
|
|
4
4
|
{
|
|
5
5
|
name: "project-manager",
|
|
6
6
|
label: "Project Manager",
|
|
@@ -26,6 +26,17 @@ export const ROLE_DEFINITIONS = [
|
|
|
26
26
|
dispatchable: true
|
|
27
27
|
}
|
|
28
28
|
];
|
|
29
|
+
export const CODEX_REVIEWER_ROLE_DEFINITION = {
|
|
30
|
+
name: "codex-reviewer",
|
|
31
|
+
label: "Codex Reviewer",
|
|
32
|
+
commandAgent: "codex-reviewer",
|
|
33
|
+
dispatchable: false
|
|
34
|
+
};
|
|
35
|
+
export const ROLE_DEFINITIONS = [
|
|
36
|
+
...VCM_ROLE_DEFINITIONS,
|
|
37
|
+
CODEX_REVIEWER_ROLE_DEFINITION
|
|
38
|
+
];
|
|
39
|
+
export const VCM_ROLE_NAMES = VCM_ROLE_DEFINITIONS.map((role) => role.name);
|
|
29
40
|
export const ROLE_NAMES = ROLE_DEFINITIONS.map((role) => role.name);
|
|
30
41
|
export const DISPATCHABLE_ROLES = ROLE_DEFINITIONS
|
|
31
42
|
.filter((role) => role.dispatchable)
|
|
@@ -33,6 +44,9 @@ export const DISPATCHABLE_ROLES = ROLE_DEFINITIONS
|
|
|
33
44
|
export function isRoleName(value) {
|
|
34
45
|
return ROLE_NAMES.includes(value);
|
|
35
46
|
}
|
|
47
|
+
export function isVcmRoleName(value) {
|
|
48
|
+
return VCM_ROLE_NAMES.includes(value);
|
|
49
|
+
}
|
|
36
50
|
export function isDispatchableRole(value) {
|
|
37
51
|
return DISPATCHABLE_ROLES.includes(value);
|
|
38
52
|
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { VCM_ROLE_NAMES } from "../constants.js";
|
|
2
2
|
export const THEME_MODES = ["system", "light", "dark"];
|
|
3
3
|
export const PERMISSION_REQUEST_MODES = ["off", "allowAll"];
|
|
4
4
|
export function createDefaultLaunchTemplate() {
|
|
5
5
|
const roles = {};
|
|
6
|
-
for (const role of
|
|
6
|
+
for (const role of VCM_ROLE_NAMES) {
|
|
7
7
|
roles[role] = {
|
|
8
8
|
permissionMode: "default",
|
|
9
|
-
model: "default"
|
|
9
|
+
model: "default",
|
|
10
|
+
effort: "default"
|
|
10
11
|
};
|
|
11
12
|
}
|
|
12
13
|
return {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -35,3 +35,47 @@ export const CLAUDE_MODEL_OPTIONS = [
|
|
|
35
35
|
description: "Opus 4.8 + 1M context"
|
|
36
36
|
}
|
|
37
37
|
];
|
|
38
|
+
export const CODEX_MODEL_OPTIONS = [
|
|
39
|
+
{
|
|
40
|
+
value: "gpt-5.5",
|
|
41
|
+
label: "GPT-5.5",
|
|
42
|
+
description: "Strong Codex reviewer default"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
value: "default",
|
|
46
|
+
label: "Default",
|
|
47
|
+
description: "Codex account default"
|
|
48
|
+
}
|
|
49
|
+
];
|
|
50
|
+
export const SESSION_EFFORT_OPTIONS = [
|
|
51
|
+
{
|
|
52
|
+
value: "default",
|
|
53
|
+
label: "Default",
|
|
54
|
+
description: "CLI or project default"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
value: "low",
|
|
58
|
+
label: "Low",
|
|
59
|
+
description: "Fastest reasoning"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
value: "medium",
|
|
63
|
+
label: "Medium",
|
|
64
|
+
description: "Balanced reasoning"
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
value: "high",
|
|
68
|
+
label: "High",
|
|
69
|
+
description: "Deeper reasoning"
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
value: "xhigh",
|
|
73
|
+
label: "XHigh",
|
|
74
|
+
description: "Extra high reasoning"
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
value: "max",
|
|
78
|
+
label: "Max",
|
|
79
|
+
description: "Maximum reasoning"
|
|
80
|
+
}
|
|
81
|
+
];
|