vibe-coding-master 0.3.26 → 0.3.27

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.
@@ -1,16 +1,15 @@
1
1
  import { createHash, randomUUID } from "node:crypto";
2
2
  import path from "node:path";
3
- import { CODEX_REVIEW_GATES } from "../../shared/types/codex-review.js";
3
+ import { GATE_REVIEW_GATES } from "../../shared/types/gate-review.js";
4
4
  import { VcmError } from "../errors.js";
5
5
  import { resolveRepoPath } from "../adapters/filesystem.js";
6
6
  import { submitTerminalInput } from "../runtime/terminal-submit.js";
7
7
  import { getTaskRuntimeRepoRoot } from "./task-service.js";
8
- const CODEX_DIR = ".ai/codex";
9
- const CODEX_CONFIG_PATH = ".ai/codex/config.toml";
10
- const CODEX_REVIEW_DIR = ".ai/vcm/codex-reviews";
11
- const REQUESTS_DIR = ".ai/vcm/codex-reviews/requests";
12
- const CODEX_REVIEW_VERSION = 1;
13
- const CODEX_REVIEWER_ROLE = "codex-reviewer";
8
+ const GATE_REVIEW_AGENT_PATH = ".claude/agents/gate-reviewer.md";
9
+ const GATE_REVIEW_DIR = ".ai/vcm/gate-reviews";
10
+ const REQUESTS_DIR = ".ai/vcm/gate-reviews/requests";
11
+ const GATE_REVIEW_VERSION = 1;
12
+ const GATE_REVIEWER_ROLE = "gate-reviewer";
14
13
  const DEFAULT_REPORT_POLL_INTERVAL_MS = 1000;
15
14
  const DEFAULT_REPORT_TIMEOUT_MS = 30 * 60 * 1000;
16
15
  const activeRuns = new Set();
@@ -30,7 +29,7 @@ const SOURCE_ARTIFACTS = {
30
29
  ]
31
30
  };
32
31
  const VALID_SEVERITIES = new Set(["critical", "high", "medium", "low"]);
33
- export function createCodexReviewService(deps) {
32
+ export function createGateReviewService(deps) {
34
33
  const now = deps.now ?? (() => new Date().toISOString());
35
34
  const reportPollIntervalMs = deps.reportPollIntervalMs ?? DEFAULT_REPORT_POLL_INTERVAL_MS;
36
35
  const reportTimeoutMs = deps.reportTimeoutMs ?? DEFAULT_REPORT_TIMEOUT_MS;
@@ -38,7 +37,7 @@ export function createCodexReviewService(deps) {
38
37
  const projectConfig = await deps.projectService.loadConfig(repoRoot);
39
38
  const task = await deps.taskService.loadTask(repoRoot, taskSlug);
40
39
  const taskRepoRoot = getTaskRuntimeRepoRoot(task);
41
- const reviewSettings = await deps.appSettings.getCodexReviewSettings(repoRoot, taskSlug);
40
+ const reviewSettings = await deps.appSettings.getGateReviewSettings(repoRoot, taskSlug);
42
41
  return {
43
42
  repoRoot,
44
43
  taskSlug,
@@ -59,7 +58,7 @@ export function createCodexReviewService(deps) {
59
58
  error: undefined
60
59
  }, now());
61
60
  await saveIndex(deps.fs, context.taskRepoRoot, index);
62
- return { status: "disabled", gate, record: index.gates[gate], message: "Codex review is disabled." };
61
+ return { status: "disabled", gate, record: index.gates[gate], message: "Gate review is disabled." };
63
62
  }
64
63
  if (!record.required) {
65
64
  index = applyGateState(index, gate, {
@@ -75,11 +74,11 @@ export function createCodexReviewService(deps) {
75
74
  status: "running",
76
75
  gate,
77
76
  record,
78
- message: `Codex review is already running for ${index.activeGate}.`
77
+ message: `Gate review is already running for ${index.activeGate}.`
79
78
  };
80
79
  }
81
80
  if (record.status === "running" && !options.force) {
82
- return { status: "running", gate, record, message: "Codex review is already running." };
81
+ return { status: "running", gate, record, message: "Gate review is already running." };
83
82
  }
84
83
  const inputHash = await computeInputHash(deps, context.taskRepoRoot, gate);
85
84
  if (!options.force
@@ -90,12 +89,13 @@ export function createCodexReviewService(deps) {
90
89
  status: "already_approved",
91
90
  gate,
92
91
  record,
93
- message: "Codex review already approved the current inputs."
92
+ message: "Gate review already approved the current inputs."
94
93
  };
95
94
  }
96
95
  const timestamp = now();
97
96
  const requestId = createRequestId(gate);
98
97
  const requestPath = path.posix.join(REQUESTS_DIR, `${requestId}.json`);
98
+ const promptPath = path.posix.join(REQUESTS_DIR, `${requestId}.prompt.md`);
99
99
  const nextRecord = {
100
100
  ...record,
101
101
  status: "running",
@@ -104,6 +104,7 @@ export function createCodexReviewService(deps) {
104
104
  exceptionReason: undefined,
105
105
  requestId,
106
106
  requestPath,
107
+ promptPath,
107
108
  inputHash,
108
109
  requestedAt: timestamp,
109
110
  startedAt: undefined,
@@ -122,7 +123,7 @@ export function createCodexReviewService(deps) {
122
123
  updatedAt: timestamp
123
124
  };
124
125
  await deps.fs.writeJsonAtomic(resolveRepoPath(context.taskRepoRoot, requestPath), {
125
- version: CODEX_REVIEW_VERSION,
126
+ version: GATE_REVIEW_VERSION,
126
127
  requestId,
127
128
  gate,
128
129
  status: "requested",
@@ -132,23 +133,23 @@ export function createCodexReviewService(deps) {
132
133
  promptPath: nextRecord.promptPath
133
134
  });
134
135
  await saveIndex(deps.fs, context.taskRepoRoot, index);
135
- void runCodexReview(context, gate, requestId).catch(() => {
136
- // runCodexReview records failures in the persisted gate state.
136
+ void runGateReview(context, gate, requestId).catch(() => {
137
+ // runGateReview records failures in the persisted gate state.
137
138
  });
138
139
  return {
139
140
  status: "started",
140
141
  gate,
141
142
  record: nextRecord,
142
- message: "Codex review started."
143
+ message: "Gate review started."
143
144
  };
144
145
  }
145
- async function runCodexReview(context, gate, requestId) {
146
+ async function runGateReview(context, gate, requestId) {
146
147
  const runKey = `${context.taskRepoRoot}:${context.taskSlug}:${gate}`;
147
148
  if (activeRuns.has(runKey)) {
148
149
  return;
149
150
  }
150
151
  activeRuns.add(runKey);
151
- let codexTurnStarted = false;
152
+ let gateTurnStarted = false;
152
153
  try {
153
154
  const timestamp = now();
154
155
  await updateGateRecord(context, gate, {
@@ -157,37 +158,39 @@ export function createCodexReviewService(deps) {
157
158
  updatedAt: timestamp
158
159
  });
159
160
  await updateRequestStatus(deps.fs, context, requestId, "running", { startedAt: timestamp });
160
- const codexDir = resolveRepoPath(context.taskRepoRoot, CODEX_DIR);
161
- const reviewDir = resolveRepoPath(context.taskRepoRoot, CODEX_REVIEW_DIR);
162
- const prompt = await buildCodexPrompt(deps.fs, context.taskRepoRoot, gate, requestId);
161
+ const reviewDir = resolveRepoPath(context.taskRepoRoot, GATE_REVIEW_DIR);
162
+ const agentPath = resolveRepoPath(context.repoRoot, GATE_REVIEW_AGENT_PATH);
163
+ const prompt = buildGatePrompt(context, gate, requestId);
163
164
  await deps.fs.ensureDir(reviewDir);
164
- if (!(await deps.fs.pathExists(codexDir))) {
165
+ await deps.fs.ensureDir(resolveRepoPath(context.taskRepoRoot, REQUESTS_DIR));
166
+ await deps.fs.writeText(resolveRepoPath(context.taskRepoRoot, promptPathForRequest(requestId)), prompt);
167
+ if (!(await deps.fs.pathExists(agentPath))) {
165
168
  throw new VcmError({
166
- code: "CODEX_REVIEW_CONFIG_MISSING",
167
- message: `${CODEX_DIR} does not exist.`,
169
+ code: "GATE_REVIEW_AGENT_MISSING",
170
+ message: `${GATE_REVIEW_AGENT_PATH} does not exist.`,
168
171
  statusCode: 409,
169
- hint: "Apply the VCM harness before requesting Codex review gates."
172
+ hint: "Apply the VCM harness before requesting Gate Review Gates."
170
173
  });
171
174
  }
172
- const session = await ensureCodexReviewerSession(context);
175
+ const session = await ensureGateReviewerSession(context);
173
176
  await submitTerminalInput(deps.runtime, session.id, prompt);
174
- await deps.sessionService.markRoleActivityRunning(context.repoRoot, context.taskSlug, CODEX_REVIEWER_ROLE);
177
+ await deps.sessionService.markRoleActivityRunning(context.repoRoot, context.taskSlug, GATE_REVIEWER_ROLE);
175
178
  await deps.roundService.recordRoleTurnEvent({
176
179
  repoRoot: context.repoRoot,
177
180
  stateRepoRoot: context.taskRepoRoot,
178
181
  stateRoot: context.stateRoot,
179
182
  taskSlug: context.taskSlug,
180
- role: CODEX_REVIEWER_ROLE,
183
+ role: GATE_REVIEWER_ROLE,
181
184
  eventName: "UserPromptSubmit"
182
185
  });
183
- codexTurnStarted = true;
186
+ gateTurnStarted = true;
184
187
  const parsed = await waitForGateReport(deps.fs, context.taskRepoRoot, gate, requestId, now(), {
185
188
  intervalMs: reportPollIntervalMs,
186
189
  timeoutMs: reportTimeoutMs
187
190
  });
188
191
  const completedAt = now();
189
- await recordCodexReviewerTurnStop(context, codexTurnStarted);
190
- codexTurnStarted = false;
192
+ await recordGateReviewerTurnStop(context, gateTurnStarted);
193
+ gateTurnStarted = false;
191
194
  await updateGateRecord(context, gate, {
192
195
  status: "completed",
193
196
  decision: parsed.decision,
@@ -209,8 +212,8 @@ export function createCodexReviewService(deps) {
209
212
  catch (error) {
210
213
  const timestamp = now();
211
214
  const message = errorMessage(error);
212
- await recordCodexReviewerTurnStop(context, codexTurnStarted);
213
- codexTurnStarted = false;
215
+ await recordGateReviewerTurnStop(context, gateTurnStarted);
216
+ gateTurnStarted = false;
214
217
  await updateGateRecord(context, gate, {
215
218
  status: "failed",
216
219
  error: message,
@@ -229,38 +232,38 @@ export function createCodexReviewService(deps) {
229
232
  activeRuns.delete(runKey);
230
233
  }
231
234
  }
232
- async function recordCodexReviewerTurnStop(context, shouldRecord) {
235
+ async function recordGateReviewerTurnStop(context, shouldRecord) {
233
236
  if (!shouldRecord) {
234
237
  return;
235
238
  }
236
- await deps.sessionService.markRoleActivityIdle(context.repoRoot, context.taskSlug, CODEX_REVIEWER_ROLE);
239
+ await deps.sessionService.markRoleActivityIdle(context.repoRoot, context.taskSlug, GATE_REVIEWER_ROLE);
237
240
  await deps.roundService.recordRoleTurnEvent({
238
241
  repoRoot: context.repoRoot,
239
242
  stateRepoRoot: context.taskRepoRoot,
240
243
  stateRoot: context.stateRoot,
241
244
  taskSlug: context.taskSlug,
242
- role: CODEX_REVIEWER_ROLE,
245
+ role: GATE_REVIEWER_ROLE,
243
246
  eventName: "Stop"
244
247
  });
245
248
  }
246
- async function ensureCodexReviewerSession(context) {
247
- const existing = await deps.sessionService.getRoleSession(context.repoRoot, context.taskSlug, CODEX_REVIEWER_ROLE);
249
+ async function ensureGateReviewerSession(context) {
250
+ const existing = await deps.sessionService.getRoleSession(context.repoRoot, context.taskSlug, GATE_REVIEWER_ROLE);
248
251
  if (existing?.status === "running" && deps.runtime.getSession(existing.id)) {
249
252
  return existing;
250
253
  }
251
254
  if (existing?.claudeSessionId) {
252
255
  try {
253
- return await deps.sessionService.resumeRoleSession(context.repoRoot, context.taskSlug, CODEX_REVIEWER_ROLE, {
256
+ return await deps.sessionService.resumeRoleSession(context.repoRoot, context.taskSlug, GATE_REVIEWER_ROLE, {
254
257
  cols: 100,
255
258
  rows: 28,
256
259
  model: "default"
257
260
  });
258
261
  }
259
262
  catch {
260
- // Fall through to a fresh Codex Reviewer terminal if the saved session cannot be resumed.
263
+ // Fall through to a fresh Gate Reviewer terminal if the saved session cannot be resumed.
261
264
  }
262
265
  }
263
- return deps.sessionService.startRoleSession(context.repoRoot, context.taskSlug, CODEX_REVIEWER_ROLE, {
266
+ return deps.sessionService.startRoleSession(context.repoRoot, context.taskSlug, GATE_REVIEWER_ROLE, {
264
267
  cols: 100,
265
268
  rows: 28,
266
269
  model: "default"
@@ -321,10 +324,10 @@ export function createCodexReviewService(deps) {
321
324
  return loadIndex(deps.fs, context, now());
322
325
  },
323
326
  async updateSettings(repoRoot, taskSlug, input) {
324
- const currentSettings = await deps.appSettings.getCodexReviewSettings(repoRoot, taskSlug);
327
+ const currentSettings = await deps.appSettings.getGateReviewSettings(repoRoot, taskSlug);
325
328
  const requiredGates = new Set(currentSettings.requiredGates);
326
329
  for (const [gate, enabled] of Object.entries(input?.gates ?? {})) {
327
- if (!isCodexReviewGate(gate)) {
330
+ if (!isGateReviewGate(gate)) {
328
331
  continue;
329
332
  }
330
333
  if (enabled) {
@@ -334,7 +337,7 @@ export function createCodexReviewService(deps) {
334
337
  requiredGates.delete(gate);
335
338
  }
336
339
  }
337
- await deps.appSettings.updateCodexReviewSettings(repoRoot, taskSlug, [...requiredGates]);
340
+ await deps.appSettings.updateGateReviewSettings(repoRoot, taskSlug, [...requiredGates]);
338
341
  const nextContext = await getContext(repoRoot, taskSlug);
339
342
  const index = await loadIndex(deps.fs, nextContext, now());
340
343
  await saveIndex(deps.fs, nextContext.taskRepoRoot, {
@@ -355,10 +358,10 @@ export function createCodexReviewService(deps) {
355
358
  const current = await loadIndex(deps.fs, context, now());
356
359
  if (current.gates[gate].status === "running") {
357
360
  throw new VcmError({
358
- code: "CODEX_REVIEW_RUNNING",
359
- message: "Cannot skip a running Codex review gate.",
361
+ code: "GATE_REVIEW_RUNNING",
362
+ message: "Cannot skip a running Gate review gate.",
360
363
  statusCode: 409,
361
- hint: "Wait for the Codex run to finish, then choose retry, skip, or override."
364
+ hint: "Wait for the gate review to finish, then choose retry, skip, or override."
362
365
  });
363
366
  }
364
367
  const index = await updateGateRecord(context, gate, {
@@ -380,10 +383,10 @@ export function createCodexReviewService(deps) {
380
383
  const current = await loadIndex(deps.fs, context, now());
381
384
  if (current.gates[gate].status === "running") {
382
385
  throw new VcmError({
383
- code: "CODEX_REVIEW_RUNNING",
384
- message: "Cannot override a running Codex review gate.",
386
+ code: "GATE_REVIEW_RUNNING",
387
+ message: "Cannot override a running Gate review gate.",
385
388
  statusCode: 409,
386
- hint: "Wait for the Codex run to finish, then choose retry, skip, or override."
389
+ hint: "Wait for the gate review to finish, then choose retry, skip, or override."
387
390
  });
388
391
  }
389
392
  const index = await updateGateRecord(context, gate, {
@@ -405,8 +408,8 @@ export function createCodexReviewService(deps) {
405
408
  }
406
409
  };
407
410
  }
408
- export function isCodexReviewGate(value) {
409
- return CODEX_REVIEW_GATES.includes(value);
411
+ export function isGateReviewGate(value) {
412
+ return GATE_REVIEW_GATES.includes(value);
410
413
  }
411
414
  function loadRuntimeConfig(reviewSettings) {
412
415
  return {
@@ -425,7 +428,7 @@ function normalizeIndex(raw, config, timestamp) {
425
428
  : {};
426
429
  const gates = {};
427
430
  const requiredSet = new Set(config.enabled ? config.requiredGates : []);
428
- for (const gate of CODEX_REVIEW_GATES) {
431
+ for (const gate of GATE_REVIEW_GATES) {
429
432
  const existing = existingGates[gate];
430
433
  const required = requiredSet.has(gate);
431
434
  const fallbackStatus = config.enabled
@@ -457,11 +460,11 @@ function normalizeIndex(raw, config, timestamp) {
457
460
  callbackError: typeof existing?.callbackError === "string" ? existing.callbackError : undefined
458
461
  };
459
462
  }
460
- const activeGate = isCodexReviewGate(String(raw?.activeGate)) && gates[raw?.activeGate].status === "running"
463
+ const activeGate = isGateReviewGate(String(raw?.activeGate)) && gates[raw?.activeGate].status === "running"
461
464
  ? raw?.activeGate
462
465
  : null;
463
466
  return {
464
- version: CODEX_REVIEW_VERSION,
467
+ version: GATE_REVIEW_VERSION,
465
468
  enabled: config.enabled,
466
469
  activeGate,
467
470
  gates,
@@ -494,11 +497,9 @@ async function computeInputHash(deps, taskRepoRoot, gate) {
494
497
  const digest = createHash("sha256");
495
498
  const common = [
496
499
  "CLAUDE.md",
497
- ".ai/codex/AGENTS.md",
498
- ".ai/codex/config.toml",
499
- ".ai/codex/.codex/config.toml",
500
- ".ai/codex/.codex/hooks.json",
501
- promptPathForGate(gate)
500
+ ".claude/agents/gate-reviewer.md",
501
+ ".claude/skills/vcm-gate-review/SKILL.md",
502
+ ".ai/tools/request-gate-review"
502
503
  ];
503
504
  for (const relativePath of [...common, ...SOURCE_ARTIFACTS[gate]]) {
504
505
  digest.update(relativePath);
@@ -521,35 +522,31 @@ async function commandStdout(runner, cwd, args) {
521
522
  const result = await runner.run("git", args, { cwd });
522
523
  return result.exitCode === 0 ? result.stdout : "";
523
524
  }
524
- async function buildCodexPrompt(fs, taskRepoRoot, gate, requestId) {
525
- const promptPath = resolveRepoPath(taskRepoRoot, promptPathForGate(gate));
526
- if (!(await fs.pathExists(promptPath))) {
527
- throw new VcmError({
528
- code: "CODEX_REVIEW_PROMPT_MISSING",
529
- message: `Codex review prompt is missing: ${promptPathForGate(gate)}`,
530
- statusCode: 409,
531
- hint: "Apply the VCM harness before requesting Codex review gates."
532
- });
533
- }
534
- const basePrompt = await fs.readText(promptPath);
535
- const reportPath = path.posix.relative(CODEX_DIR, reportPathForGate(gate));
536
- return `${basePrompt.trimEnd()}
537
-
538
- ## VCM Runtime Contract
539
-
540
- - Gate: ${gate}
541
- - Request: ${requestId}
542
- - Report path from this working directory: ${reportPath}
525
+ function buildGatePrompt(context, gate, requestId) {
526
+ const reportPath = reportPathForGate(gate);
527
+ const absoluteReportPath = resolveRepoPath(context.taskRepoRoot, reportPath);
528
+ const evidence = SOURCE_ARTIFACTS[gate]
529
+ .map((relativePath) => `- ${relativePath}`)
530
+ .join("\n");
531
+ const gitLine = gate === "architecture-plan" || gate === "final-diff"
532
+ ? "\nDiff: inspect git status/diff in Worktree."
533
+ : "";
534
+ return `[VCM GATE REVIEW]
535
+ Task: ${context.taskSlug}
536
+ Worktree: ${context.taskRepoRoot}
537
+ Gate: ${gate}
538
+ Request: ${requestId}
539
+ Report: ${absoluteReportPath}
543
540
 
544
- Your report must begin with these exact fields:
541
+ Evidence:
542
+ ${evidence}${gitLine}
545
543
 
546
- \`\`\`text
544
+ Write only Report. Start exactly:
547
545
  Gate: ${gate}
548
546
  Request: ${requestId}
549
547
  Decision: approve|request_changes
550
- \`\`\`
551
-
552
- Write only that report file. Do not edit any other file.`;
548
+ Summary: <one or two sentences>
549
+ [/VCM GATE REVIEW]`;
553
550
  }
554
551
  async function waitForGateReport(fs, taskRepoRoot, gate, requestId, timestamp, options) {
555
552
  const startedAt = Date.now();
@@ -568,8 +565,8 @@ async function waitForGateReport(fs, taskRepoRoot, gate, requestId, timestamp, o
568
565
  }
569
566
  const detail = errorMessage(lastError);
570
567
  throw new VcmError({
571
- code: "CODEX_REVIEW_REPORT_TIMEOUT",
572
- message: `Codex Reviewer did not produce a valid ${gate} report within ${Math.round(options.timeoutMs / 1000)}s.`,
568
+ code: "GATE_REVIEW_REPORT_TIMEOUT",
569
+ message: `Gate Reviewer did not produce a valid ${gate} report within ${Math.round(options.timeoutMs / 1000)}s.`,
573
570
  statusCode: 504,
574
571
  hint: detail
575
572
  });
@@ -579,8 +576,8 @@ async function parseGateReport(fs, taskRepoRoot, gate, requestId, timestamp) {
579
576
  const absolutePath = resolveRepoPath(taskRepoRoot, reportPath);
580
577
  if (!(await fs.pathExists(absolutePath))) {
581
578
  throw new VcmError({
582
- code: "CODEX_REVIEW_REPORT_MISSING",
583
- message: `Codex review report was not written: ${reportPath}`,
579
+ code: "GATE_REVIEW_REPORT_MISSING",
580
+ message: `Gate review report was not written: ${reportPath}`,
584
581
  statusCode: 500
585
582
  });
586
583
  }
@@ -588,24 +585,24 @@ async function parseGateReport(fs, taskRepoRoot, gate, requestId, timestamp) {
588
585
  const parsedGate = matchField(content, "Gate");
589
586
  if (parsedGate && parsedGate !== gate) {
590
587
  throw new VcmError({
591
- code: "CODEX_REVIEW_REPORT_GATE_MISMATCH",
592
- message: `Codex review report gate is ${parsedGate}, expected ${gate}.`,
588
+ code: "GATE_REVIEW_REPORT_GATE_MISMATCH",
589
+ message: `Gate review report gate is ${parsedGate}, expected ${gate}.`,
593
590
  statusCode: 500
594
591
  });
595
592
  }
596
593
  const parsedRequest = matchField(content, "Request");
597
594
  if (requestId && parsedRequest !== requestId) {
598
595
  throw new VcmError({
599
- code: "CODEX_REVIEW_REPORT_STALE",
600
- message: `Codex review report request is ${parsedRequest ?? "missing"}, expected ${requestId}.`,
596
+ code: "GATE_REVIEW_REPORT_STALE",
597
+ message: `Gate review report request is ${parsedRequest ?? "missing"}, expected ${requestId}.`,
601
598
  statusCode: 500
602
599
  });
603
600
  }
604
601
  const decision = normalizeDecision(matchField(content, "Decision"));
605
602
  if (!decision) {
606
603
  throw new VcmError({
607
- code: "CODEX_REVIEW_DECISION_MISSING",
608
- message: `Codex review report must contain Decision: approve or Decision: request_changes.`,
604
+ code: "GATE_REVIEW_DECISION_MISSING",
605
+ message: `Gate review report must contain Decision: approve or Decision: request_changes.`,
609
606
  statusCode: 500
610
607
  });
611
608
  }
@@ -623,7 +620,7 @@ async function parseGateReport(fs, taskRepoRoot, gate, requestId, timestamp) {
623
620
  async function updateRequestStatus(fs, context, requestId, status, patch) {
624
621
  const requestPath = resolveRepoPath(context.taskRepoRoot, path.posix.join(REQUESTS_DIR, `${requestId}.json`));
625
622
  const current = await readJsonOrNull(fs, requestPath) ?? {
626
- version: CODEX_REVIEW_VERSION,
623
+ version: GATE_REVIEW_VERSION,
627
624
  requestId
628
625
  };
629
626
  await fs.writeJsonAtomic(requestPath, {
@@ -634,13 +631,16 @@ async function updateRequestStatus(fs, context, requestId, status, patch) {
634
631
  });
635
632
  }
636
633
  function reportPathForGate(gate) {
637
- return path.posix.join(CODEX_REVIEW_DIR, `${gate}-review.md`);
634
+ return path.posix.join(GATE_REVIEW_DIR, `${gate}-review.md`);
635
+ }
636
+ function promptPathForRequest(requestId) {
637
+ return path.posix.join(REQUESTS_DIR, `${requestId}.prompt.md`);
638
638
  }
639
639
  function promptPathForGate(gate) {
640
- return path.posix.join(CODEX_DIR, "prompts", `${gate}-gate.md`);
640
+ return path.posix.join(GATE_REVIEW_DIR, "prompts", `${gate}-gate.md`);
641
641
  }
642
642
  function getIndexPath(taskRepoRoot) {
643
- return resolveRepoPath(taskRepoRoot, path.posix.join(CODEX_REVIEW_DIR, "index.json"));
643
+ return resolveRepoPath(taskRepoRoot, path.posix.join(GATE_REVIEW_DIR, "index.json"));
644
644
  }
645
645
  function createRequestId(gate) {
646
646
  const stamp = new Date().toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
@@ -736,7 +736,7 @@ function parsePositiveInteger(value) {
736
736
  function assertExceptionReason(reason) {
737
737
  if (!reason?.trim()) {
738
738
  throw new VcmError({
739
- code: "CODEX_REVIEW_REASON_REQUIRED",
739
+ code: "GATE_REVIEW_REASON_REQUIRED",
740
740
  message: "A reason is required.",
741
741
  statusCode: 400
742
742
  });
@@ -744,7 +744,7 @@ function assertExceptionReason(reason) {
744
744
  }
745
745
  function renderProjectManagerCallback(input) {
746
746
  const lines = [
747
- "[VCM CODEX REVIEW CALLBACK]",
747
+ "[VCM GATE REVIEW CALLBACK]",
748
748
  `task: ${input.taskSlug}`,
749
749
  `gate: ${input.gate}`,
750
750
  `status: ${input.status}`,
@@ -752,11 +752,11 @@ function renderProjectManagerCallback(input) {
752
752
  `report: ${input.reportPath}`,
753
753
  ...(input.error ? [`error: ${input.error}`] : []),
754
754
  "",
755
- "Use the vcm-codex-review-gate skill to handle this callback.",
755
+ "Use the vcm-gate-review skill to handle this callback.",
756
756
  "If status is completed and decision is approve, continue the VCM flow.",
757
757
  "If decision is request_changes, analyze the report and route follow-up through the normal VCM roles.",
758
758
  "If status is failed, stop and ask the user to retry, skip, or override in VCM.",
759
- "[/VCM CODEX REVIEW CALLBACK]"
759
+ "[/VCM GATE REVIEW CALLBACK]"
760
760
  ];
761
761
  return lines.join("\n");
762
762
  }
@@ -767,14 +767,14 @@ function errorMessage(error) {
767
767
  if (error instanceof Error) {
768
768
  return error.message;
769
769
  }
770
- return "Unknown Codex review error.";
770
+ return "Unknown Gate review error.";
771
771
  }
772
772
  function isPendingReportError(error) {
773
773
  return error instanceof VcmError && [
774
- "CODEX_REVIEW_DECISION_MISSING",
775
- "CODEX_REVIEW_REPORT_GATE_MISMATCH",
776
- "CODEX_REVIEW_REPORT_MISSING",
777
- "CODEX_REVIEW_REPORT_STALE"
774
+ "GATE_REVIEW_DECISION_MISSING",
775
+ "GATE_REVIEW_REPORT_GATE_MISMATCH",
776
+ "GATE_REVIEW_REPORT_MISSING",
777
+ "GATE_REVIEW_REPORT_STALE"
778
778
  ].includes(error.code);
779
779
  }
780
780
  function delay(ms) {
@@ -4,7 +4,7 @@ import path from "node:path";
4
4
  import { promisify } from "node:util";
5
5
  import { renderArchitectHarnessRules } from "../templates/harness/architect-agent.js";
6
6
  import { renderCoderHarnessRules } from "../templates/harness/coder-agent.js";
7
- import { renderCodexAgentsHarnessRules, renderCodexArchitecturePlanPrompt, renderCodexCliConfigHarnessRules, renderCodexConfigHarnessRules, renderCodexFinalDiffPrompt, renderCodexHooksHarnessRules, renderCodexReviewResultSchema, renderCodexTranslatorAgentsHarnessRules, renderCodexTranslatorConfigHarnessRules, renderCodexValidationAdequacyPrompt, renderRequestCodexReviewTool, renderVcmCodexReviewGateSkillRules } from "../templates/harness/codex-review.js";
7
+ import { renderCodexCliConfigHarnessRules, renderCodexHooksHarnessRules, renderCodexTranslatorAgentsHarnessRules, renderCodexTranslatorConfigHarnessRules, renderGateReviewerAgentRules, renderRequestGateReviewTool, renderVcmGateReviewSkillRules } from "../templates/harness/gate-review.js";
8
8
  import { renderRootClaudeHarnessRules } from "../templates/harness/claude-root.js";
9
9
  import { renderGitignoreHarnessRules } from "../templates/harness/gitignore.js";
10
10
  import { renderProjectManagerHarnessRules } from "../templates/harness/project-manager-agent.js";
@@ -92,39 +92,19 @@ const HARNESS_FILES = [
92
92
  renderRules: renderVcmLongRunningValidationSkillRules
93
93
  },
94
94
  {
95
- kind: "skill-vcm-codex-review-gate",
96
- path: ".claude/skills/vcm-codex-review-gate/SKILL.md",
97
- title: "VCM Codex Review Gate Skill",
98
- frontmatter: renderSkillFrontmatter("vcm-codex-review-gate", "Use when project-manager reaches a Codex Review Gate or receives a VCM Codex Review callback."),
95
+ kind: "skill-vcm-gate-review",
96
+ path: ".claude/skills/vcm-gate-review/SKILL.md",
97
+ title: "VCM Gate Review Skill",
98
+ frontmatter: renderSkillFrontmatter("vcm-gate-review", "Use when project-manager reaches a Gate Review trigger or receives a VCM Gate Review callback."),
99
99
  ownership: "whole-file",
100
- renderRules: renderVcmCodexReviewGateSkillRules
100
+ renderRules: renderVcmGateReviewSkillRules
101
101
  },
102
102
  {
103
- kind: "codex-agents",
104
- path: ".ai/codex/AGENTS.md",
105
- title: "VCM Codex Reviewer",
106
- renderRules: renderCodexAgentsHarnessRules
107
- },
108
- {
109
- kind: "codex-config",
110
- path: ".ai/codex/config.toml",
111
- title: "VCM Codex Config",
112
- ownership: "raw-file",
113
- renderRules: renderCodexConfigHarnessRules
114
- },
115
- {
116
- kind: "codex-cli-config",
117
- path: ".ai/codex/.codex/config.toml",
118
- title: "VCM Codex CLI Config",
119
- ownership: "raw-file",
120
- renderRules: renderCodexCliConfigHarnessRules
121
- },
122
- {
123
- kind: "codex-hooks",
124
- path: ".ai/codex/.codex/hooks.json",
125
- title: "VCM Codex Hooks",
126
- ownership: "raw-file",
127
- renderRules: renderCodexHooksHarnessRules
103
+ kind: "agent-gate-reviewer",
104
+ path: ".claude/agents/gate-reviewer.md",
105
+ title: "Gate Reviewer Agent",
106
+ frontmatter: renderAgentFrontmatter("gate-reviewer", "VCM independent gate review role for architecture plans, validation adequacy, and final diffs."),
107
+ renderRules: renderGateReviewerAgentRules
128
108
  },
129
109
  {
130
110
  kind: "codex-translator-agents",
@@ -154,39 +134,11 @@ const HARNESS_FILES = [
154
134
  renderRules: () => renderCodexHooksHarnessRules("codex-translator")
155
135
  },
156
136
  {
157
- kind: "codex-prompt-architecture-plan",
158
- path: ".ai/codex/prompts/architecture-plan-gate.md",
159
- title: "Codex Architecture Plan Gate Prompt",
160
- ownership: "raw-file",
161
- renderRules: renderCodexArchitecturePlanPrompt
162
- },
163
- {
164
- kind: "codex-prompt-validation-adequacy",
165
- path: ".ai/codex/prompts/validation-adequacy-gate.md",
166
- title: "Codex Validation Adequacy Gate Prompt",
167
- ownership: "raw-file",
168
- renderRules: renderCodexValidationAdequacyPrompt
169
- },
170
- {
171
- kind: "codex-prompt-final-diff",
172
- path: ".ai/codex/prompts/final-diff-gate.md",
173
- title: "Codex Final Diff Gate Prompt",
174
- ownership: "raw-file",
175
- renderRules: renderCodexFinalDiffPrompt
176
- },
177
- {
178
- kind: "codex-review-schema",
179
- path: ".ai/codex/schemas/codex-review-result.schema.json",
180
- title: "Codex Review Result Schema",
181
- ownership: "raw-file",
182
- renderRules: renderCodexReviewResultSchema
183
- },
184
- {
185
- kind: "tool-request-codex-review",
186
- path: ".ai/tools/request-codex-review",
187
- title: "Request Codex Review Tool",
137
+ kind: "tool-request-gate-review",
138
+ path: ".ai/tools/request-gate-review",
139
+ title: "Request Gate Review Tool",
188
140
  ownership: "raw-file",
189
- renderRules: renderRequestCodexReviewTool
141
+ renderRules: renderRequestGateReviewTool
190
142
  },
191
143
  {
192
144
  kind: "agent-project-manager",