vibe-coding-master 0.2.13 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/backend/api/codex-review-routes.js +54 -0
- package/dist/backend/cli/install-vcm-harness.js +55 -0
- package/dist/backend/server.js +15 -0
- package/dist/backend/services/codex-review-service.js +765 -0
- package/dist/backend/services/harness-service.js +63 -3
- package/dist/backend/services/task-service.js +2 -0
- package/dist/backend/templates/harness/claude-root.js +2 -0
- package/dist/backend/templates/harness/codex-review.js +574 -0
- package/dist/backend/templates/harness/project-manager-agent.js +13 -3
- package/dist/backend/templates/harness/vcm-final-acceptance-skill.js +6 -1
- package/dist/shared/types/codex-review.js +5 -0
- package/dist-frontend/assets/index-C9l94uxA.js +92 -0
- package/dist-frontend/assets/index-D6jWo6Jd.css +32 -0
- package/dist-frontend/index.html +2 -2
- package/docs/codex-review-gates.md +515 -0
- package/docs/product-design.md +31 -2
- package/package.json +1 -1
- package/scripts/verify-package.mjs +4 -0
- package/dist-frontend/assets/index-CT20u9Fk.js +0 -92
- package/dist-frontend/assets/index-DC9-SB7F.css +0 -32
|
@@ -0,0 +1,574 @@
|
|
|
1
|
+
export function renderCodexAgentsHarnessRules() {
|
|
2
|
+
return `## Role
|
|
3
|
+
|
|
4
|
+
You are VCM \`codex-reviewer\`: an independent gate reviewer.
|
|
5
|
+
|
|
6
|
+
Review only the requested gate evidence. Decide whether the gate can pass:
|
|
7
|
+
|
|
8
|
+
- \`approve\`: no finding prevents the gate from passing.
|
|
9
|
+
- \`request_changes\`: one or more findings mean the gate should not pass yet.
|
|
10
|
+
|
|
11
|
+
Missing, stale, contradictory, or incomplete evidence is a finding. Do not decide who should fix a finding, how VCM should route it, or whether the user must intervene.
|
|
12
|
+
|
|
13
|
+
## Evidence
|
|
14
|
+
|
|
15
|
+
Use relevant evidence from:
|
|
16
|
+
|
|
17
|
+
- \`CLAUDE.md\`
|
|
18
|
+
- \`.claude/agents/architect.md\`
|
|
19
|
+
- \`.claude/agents/coder.md\`
|
|
20
|
+
- \`.claude/agents/reviewer.md\`
|
|
21
|
+
- \`.ai/generated/module-index.json\`
|
|
22
|
+
- \`.ai/generated/public-surface.json\`
|
|
23
|
+
- \`.ai/vcm/handoffs/**\`
|
|
24
|
+
|
|
25
|
+
## Gate Checks
|
|
26
|
+
|
|
27
|
+
### Architecture Plan
|
|
28
|
+
|
|
29
|
+
Check that the plan:
|
|
30
|
+
|
|
31
|
+
- matches the user request and approved scope
|
|
32
|
+
- names affected modules/files, file responsibilities, and user-visible changes
|
|
33
|
+
- defines new or changed non-private callable surfaces: visibility, signature shape, callers, contract, side effects, and error boundaries
|
|
34
|
+
- preserves dependency direction and avoids unapproved dependencies
|
|
35
|
+
- states docs/generated-context impact or explains why none is needed
|
|
36
|
+
- names risks, proof points, phase boundaries when needed, and Replan triggers
|
|
37
|
+
- uses \`VCM:CODE\` for incomplete implementation and leaves no coder ambiguity
|
|
38
|
+
- does not take over reviewer-owned validation strategy or test adequacy
|
|
39
|
+
|
|
40
|
+
### Validation Adequacy
|
|
41
|
+
|
|
42
|
+
Check that the review report:
|
|
43
|
+
|
|
44
|
+
- validates approved scope, architecture plan, and public contracts
|
|
45
|
+
- uses appropriate L1/L2/L3/L4 validation depth
|
|
46
|
+
- records evidence, commands, results, failures, skipped checks, gaps, and follow-ups
|
|
47
|
+
- performs clean final validation after cache cleanup when final validation is required
|
|
48
|
+
- justifies skipped checks and explains residual validation risk
|
|
49
|
+
- updates \`docs/TESTING.md\` when durable validation strategy or gaps changed
|
|
50
|
+
- keeps production-code reading limited to behavior, test seams, fixtures, and coverage gaps
|
|
51
|
+
|
|
52
|
+
### Final Diff
|
|
53
|
+
|
|
54
|
+
Check that the final diff:
|
|
55
|
+
|
|
56
|
+
- stays inside the approved plan, phase, and user constraints
|
|
57
|
+
- introduces no unapproved modules, dependencies, public contracts, cross-file callable surfaces, or durable-doc changes
|
|
58
|
+
- removes all \`VCM:CODE\` markers
|
|
59
|
+
- contains no fake completion: hardcoded success, disabled logic, swallowed errors, test-only shortcuts, or silent fallback hiding failure
|
|
60
|
+
- preserves existing behavior unless the plan changes it
|
|
61
|
+
- keeps changed functions focused and meaningfully named
|
|
62
|
+
- validates boundary inputs and handles fallible operations explicitly
|
|
63
|
+
- does not weaken, delete, or skip tests to pass validation
|
|
64
|
+
- verifies or regenerates generated context when module structure or public APIs change
|
|
65
|
+
- includes docs-sync and known-issues disposition when applicable
|
|
66
|
+
|
|
67
|
+
## Findings
|
|
68
|
+
|
|
69
|
+
For each finding, report severity, title, evidence, expected, gap, and risk.
|
|
70
|
+
|
|
71
|
+
Use \`request_changes\` for unresolved \`critical\` or \`high\` findings, and for \`medium\` findings that affect correctness, validation confidence, or maintainability. \`low\` findings do not prevent approval unless they reveal a gate-impacting pattern.
|
|
72
|
+
|
|
73
|
+
## Report Format
|
|
74
|
+
|
|
75
|
+
Begin the report with:
|
|
76
|
+
|
|
77
|
+
\`\`\`text
|
|
78
|
+
Gate: <gate>
|
|
79
|
+
Request: <request-id>
|
|
80
|
+
Decision: approve|request_changes
|
|
81
|
+
Summary: <one or two sentences>
|
|
82
|
+
\`\`\`
|
|
83
|
+
|
|
84
|
+
## Constraints
|
|
85
|
+
|
|
86
|
+
- Write only under \`.ai/vcm/codex-reviews/\` when asked to write output.
|
|
87
|
+
- Do not edit production code, tests, durable docs, Claude role files, route files, or handoff artifacts.
|
|
88
|
+
- Do not write \`.ai/vcm/handoffs/messages/\`.
|
|
89
|
+
- Do not run long validation jobs unless the gate prompt explicitly asks for command execution.
|
|
90
|
+
- Do not request broader filesystem or network permissions.`;
|
|
91
|
+
}
|
|
92
|
+
export function renderCodexConfigHarnessRules() {
|
|
93
|
+
return `model = "gpt-5.5"
|
|
94
|
+
model_reasoning_effort = "xhigh"
|
|
95
|
+
approval_policy = "never"
|
|
96
|
+
default_permissions = "vcm_codex_reviewer"
|
|
97
|
+
|
|
98
|
+
[vcm.codex_review]
|
|
99
|
+
enabled = true
|
|
100
|
+
required_gates = [
|
|
101
|
+
"architecture-plan",
|
|
102
|
+
"validation-adequacy",
|
|
103
|
+
"final-diff",
|
|
104
|
+
]
|
|
105
|
+
|
|
106
|
+
[permissions.vcm_codex_reviewer.workspace_roots]
|
|
107
|
+
"../.." = true
|
|
108
|
+
|
|
109
|
+
[permissions.vcm_codex_reviewer.filesystem]
|
|
110
|
+
":minimal" = "read"
|
|
111
|
+
|
|
112
|
+
[permissions.vcm_codex_reviewer.filesystem.":workspace_roots"]
|
|
113
|
+
"." = "read"
|
|
114
|
+
".ai/codex" = "read"
|
|
115
|
+
".ai/vcm/codex-reviews" = "write"
|
|
116
|
+
"**/*.env" = "deny"
|
|
117
|
+
|
|
118
|
+
[permissions.vcm_codex_reviewer.network]
|
|
119
|
+
enabled = false`;
|
|
120
|
+
}
|
|
121
|
+
export function renderCodexArchitecturePlanPrompt() {
|
|
122
|
+
return `# Codex Gate: architecture-plan
|
|
123
|
+
|
|
124
|
+
Review whether the architecture plan is ready for coder implementation.
|
|
125
|
+
|
|
126
|
+
## Required Evidence
|
|
127
|
+
|
|
128
|
+
- \`../../CLAUDE.md\`
|
|
129
|
+
- \`../../.claude/agents/architect.md\`
|
|
130
|
+
- \`../../.claude/agents/coder.md\`
|
|
131
|
+
- \`../../.claude/agents/reviewer.md\`
|
|
132
|
+
- \`../../.ai/vcm/handoffs/architecture-plan.md\`
|
|
133
|
+
- \`../../.ai/generated/module-index.json\`
|
|
134
|
+
- \`../../.ai/generated/public-surface.json\`
|
|
135
|
+
|
|
136
|
+
## Task
|
|
137
|
+
|
|
138
|
+
Check the plan against the VCM Codex Reviewer rules in \`AGENTS.md\`.
|
|
139
|
+
|
|
140
|
+
Write exactly one Markdown report:
|
|
141
|
+
|
|
142
|
+
\`\`\`text
|
|
143
|
+
../vcm/codex-reviews/architecture-plan-review.md
|
|
144
|
+
\`\`\`
|
|
145
|
+
|
|
146
|
+
The report decision must be exactly \`approve\` or \`request_changes\`.
|
|
147
|
+
Do not modify any other file.`;
|
|
148
|
+
}
|
|
149
|
+
export function renderCodexValidationAdequacyPrompt() {
|
|
150
|
+
return `# Codex Gate: validation-adequacy
|
|
151
|
+
|
|
152
|
+
Review whether the reviewer report provides enough validation evidence for the task to continue toward docs sync or final acceptance.
|
|
153
|
+
|
|
154
|
+
## Required Evidence
|
|
155
|
+
|
|
156
|
+
- \`../../CLAUDE.md\`
|
|
157
|
+
- \`../../.claude/agents/reviewer.md\`
|
|
158
|
+
- \`../../.ai/vcm/handoffs/architecture-plan.md\`
|
|
159
|
+
- \`../../.ai/vcm/handoffs/review-report.md\`
|
|
160
|
+
- \`../../docs/TESTING.md\`
|
|
161
|
+
- \`../../.ai/generated/module-index.json\`
|
|
162
|
+
- \`../../.ai/generated/public-surface.json\`
|
|
163
|
+
|
|
164
|
+
## Task
|
|
165
|
+
|
|
166
|
+
Check validation adequacy against the VCM Codex Reviewer rules in \`AGENTS.md\`.
|
|
167
|
+
|
|
168
|
+
Write exactly one Markdown report:
|
|
169
|
+
|
|
170
|
+
\`\`\`text
|
|
171
|
+
../vcm/codex-reviews/validation-adequacy-review.md
|
|
172
|
+
\`\`\`
|
|
173
|
+
|
|
174
|
+
The report decision must be exactly \`approve\` or \`request_changes\`.
|
|
175
|
+
Do not modify any other file.`;
|
|
176
|
+
}
|
|
177
|
+
export function renderCodexFinalDiffPrompt() {
|
|
178
|
+
return `# Codex Gate: final-diff
|
|
179
|
+
|
|
180
|
+
Review whether the final task diff is ready for PR preparation.
|
|
181
|
+
|
|
182
|
+
## Required Evidence
|
|
183
|
+
|
|
184
|
+
- \`../../CLAUDE.md\`
|
|
185
|
+
- \`../../.claude/agents/architect.md\`
|
|
186
|
+
- \`../../.claude/agents/coder.md\`
|
|
187
|
+
- \`../../.claude/agents/reviewer.md\`
|
|
188
|
+
- \`../../.ai/vcm/handoffs/architecture-plan.md\`
|
|
189
|
+
- \`../../.ai/vcm/handoffs/review-report.md\`
|
|
190
|
+
- \`../../.ai/vcm/handoffs/docs-sync-report.md\`
|
|
191
|
+
- \`../../.ai/vcm/handoffs/final-acceptance.md\`
|
|
192
|
+
- current git status and diff from \`../..\`
|
|
193
|
+
- \`../../.ai/generated/module-index.json\`
|
|
194
|
+
- \`../../.ai/generated/public-surface.json\`
|
|
195
|
+
|
|
196
|
+
## Task
|
|
197
|
+
|
|
198
|
+
Check the final diff against the VCM Codex Reviewer rules in \`AGENTS.md\`.
|
|
199
|
+
|
|
200
|
+
Write exactly one Markdown report:
|
|
201
|
+
|
|
202
|
+
\`\`\`text
|
|
203
|
+
../vcm/codex-reviews/final-diff-review.md
|
|
204
|
+
\`\`\`
|
|
205
|
+
|
|
206
|
+
The report decision must be exactly \`approve\` or \`request_changes\`.
|
|
207
|
+
Do not modify any other file.`;
|
|
208
|
+
}
|
|
209
|
+
export function renderCodexReviewResultSchema() {
|
|
210
|
+
return `{
|
|
211
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
212
|
+
"title": "VCM Codex Review Result",
|
|
213
|
+
"type": "object",
|
|
214
|
+
"additionalProperties": false,
|
|
215
|
+
"required": ["gate", "decision", "summary", "findings", "inputsReviewed"],
|
|
216
|
+
"properties": {
|
|
217
|
+
"gate": {
|
|
218
|
+
"type": "string",
|
|
219
|
+
"enum": ["architecture-plan", "validation-adequacy", "final-diff"]
|
|
220
|
+
},
|
|
221
|
+
"decision": {
|
|
222
|
+
"type": "string",
|
|
223
|
+
"enum": ["approve", "request_changes"]
|
|
224
|
+
},
|
|
225
|
+
"summary": {
|
|
226
|
+
"type": "string"
|
|
227
|
+
},
|
|
228
|
+
"findings": {
|
|
229
|
+
"type": "array",
|
|
230
|
+
"items": {
|
|
231
|
+
"type": "object",
|
|
232
|
+
"additionalProperties": false,
|
|
233
|
+
"required": ["severity", "title", "evidence", "expected", "gap", "risk"],
|
|
234
|
+
"properties": {
|
|
235
|
+
"severity": {
|
|
236
|
+
"type": "string",
|
|
237
|
+
"enum": ["critical", "high", "medium", "low"]
|
|
238
|
+
},
|
|
239
|
+
"title": {
|
|
240
|
+
"type": "string"
|
|
241
|
+
},
|
|
242
|
+
"file": {
|
|
243
|
+
"type": "string"
|
|
244
|
+
},
|
|
245
|
+
"line": {
|
|
246
|
+
"type": "integer",
|
|
247
|
+
"minimum": 1
|
|
248
|
+
},
|
|
249
|
+
"evidence": {
|
|
250
|
+
"type": "string"
|
|
251
|
+
},
|
|
252
|
+
"expected": {
|
|
253
|
+
"type": "string"
|
|
254
|
+
},
|
|
255
|
+
"gap": {
|
|
256
|
+
"type": "string"
|
|
257
|
+
},
|
|
258
|
+
"risk": {
|
|
259
|
+
"type": "string"
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
},
|
|
264
|
+
"residualRisks": {
|
|
265
|
+
"type": "array",
|
|
266
|
+
"items": {
|
|
267
|
+
"type": "string"
|
|
268
|
+
},
|
|
269
|
+
"default": []
|
|
270
|
+
},
|
|
271
|
+
"inputsReviewed": {
|
|
272
|
+
"type": "array",
|
|
273
|
+
"items": {
|
|
274
|
+
"type": "string"
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}`;
|
|
279
|
+
}
|
|
280
|
+
export function renderVcmCodexReviewGateSkillRules() {
|
|
281
|
+
return `## Purpose
|
|
282
|
+
|
|
283
|
+
Use this skill when project-manager reaches a VCM Codex Review Gate or receives a VCM Codex Review callback.
|
|
284
|
+
|
|
285
|
+
## Trigger Points
|
|
286
|
+
|
|
287
|
+
- \`architecture-plan\`: after architect writes \`.ai/vcm/handoffs/architecture-plan.md\`, before coder dispatch.
|
|
288
|
+
- \`validation-adequacy\`: after reviewer writes \`.ai/vcm/handoffs/review-report.md\`, before docs sync or final acceptance.
|
|
289
|
+
- \`final-diff\`: after final acceptance evidence is ready, before PR preparation.
|
|
290
|
+
|
|
291
|
+
## Request
|
|
292
|
+
|
|
293
|
+
Run:
|
|
294
|
+
|
|
295
|
+
\`\`\`sh
|
|
296
|
+
.ai/tools/request-codex-review --gate <architecture-plan|validation-adequacy|final-diff>
|
|
297
|
+
\`\`\`
|
|
298
|
+
|
|
299
|
+
Interpret the first output line:
|
|
300
|
+
|
|
301
|
+
- \`disabled\`, \`not_required\`, \`already_approved\`: continue the normal VCM flow.
|
|
302
|
+
- \`started\` or \`running\`: stop this turn and wait for the VCM callback.
|
|
303
|
+
- \`failed_to_start\`: report the failure to the user.
|
|
304
|
+
|
|
305
|
+
Do not run \`codex exec\` yourself. VCM owns the Codex adapter and gate state.
|
|
306
|
+
|
|
307
|
+
## Callback
|
|
308
|
+
|
|
309
|
+
When VCM sends \`[VCM CODEX REVIEW CALLBACK]\`, read the named report path.
|
|
310
|
+
|
|
311
|
+
- \`approve\`: continue to the next normal VCM gate.
|
|
312
|
+
- \`request_changes\`: summarize the findings and route follow-up through the responsible VCM role.
|
|
313
|
+
- \`failed\`: stop and ask the user to retry, skip, or override in VCM.
|
|
314
|
+
- \`skipped\` or \`overridden\`: record the exception reason in PM context and continue only as appropriate.
|
|
315
|
+
|
|
316
|
+
Do not ask Codex Reviewer to choose owners, fixes, Replan, or user-intervention needs. PM routes those decisions through normal VCM responsibilities.`;
|
|
317
|
+
}
|
|
318
|
+
export function renderRequestCodexReviewTool() {
|
|
319
|
+
return `#!/usr/bin/env python3
|
|
320
|
+
"""Request a VCM-managed Codex Review Gate."""
|
|
321
|
+
import argparse
|
|
322
|
+
import hashlib
|
|
323
|
+
import json
|
|
324
|
+
import os
|
|
325
|
+
import subprocess
|
|
326
|
+
import sys
|
|
327
|
+
import urllib.error
|
|
328
|
+
import urllib.parse
|
|
329
|
+
import urllib.request
|
|
330
|
+
import uuid
|
|
331
|
+
from datetime import datetime, timezone
|
|
332
|
+
from pathlib import Path
|
|
333
|
+
|
|
334
|
+
try:
|
|
335
|
+
import tomllib
|
|
336
|
+
except ModuleNotFoundError:
|
|
337
|
+
tomllib = None
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
GATES = ("architecture-plan", "validation-adequacy", "final-diff")
|
|
341
|
+
REPORTS = {
|
|
342
|
+
"architecture-plan": ".ai/vcm/codex-reviews/architecture-plan-review.md",
|
|
343
|
+
"validation-adequacy": ".ai/vcm/codex-reviews/validation-adequacy-review.md",
|
|
344
|
+
"final-diff": ".ai/vcm/codex-reviews/final-diff-review.md",
|
|
345
|
+
}
|
|
346
|
+
SOURCE_ARTIFACTS = {
|
|
347
|
+
"architecture-plan": [".ai/vcm/handoffs/architecture-plan.md"],
|
|
348
|
+
"validation-adequacy": [
|
|
349
|
+
".ai/vcm/handoffs/architecture-plan.md",
|
|
350
|
+
".ai/vcm/handoffs/review-report.md",
|
|
351
|
+
],
|
|
352
|
+
"final-diff": [
|
|
353
|
+
".ai/vcm/handoffs/architecture-plan.md",
|
|
354
|
+
".ai/vcm/handoffs/review-report.md",
|
|
355
|
+
".ai/vcm/handoffs/docs-sync-report.md",
|
|
356
|
+
".ai/vcm/handoffs/final-acceptance.md",
|
|
357
|
+
],
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
def root_dir() -> Path:
|
|
362
|
+
return Path(__file__).resolve().parents[2]
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
def now_iso() -> str:
|
|
366
|
+
return datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
def print_result(status: str, **fields: str) -> None:
|
|
370
|
+
print(status)
|
|
371
|
+
for key, value in fields.items():
|
|
372
|
+
if value is not None:
|
|
373
|
+
print(f"{key}={value}")
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
def call_vcm_api(gate: str) -> int | None:
|
|
377
|
+
base_url = os.environ.get("VCM_API_URL")
|
|
378
|
+
task_slug = os.environ.get("VCM_TASK_SLUG")
|
|
379
|
+
if not base_url or not task_slug:
|
|
380
|
+
return None
|
|
381
|
+
|
|
382
|
+
url = (
|
|
383
|
+
base_url.rstrip("/")
|
|
384
|
+
+ "/api/tasks/"
|
|
385
|
+
+ urllib.parse.quote(task_slug, safe="")
|
|
386
|
+
+ "/codex-review/"
|
|
387
|
+
+ urllib.parse.quote(gate, safe="")
|
|
388
|
+
+ "/request"
|
|
389
|
+
)
|
|
390
|
+
request = urllib.request.Request(
|
|
391
|
+
url,
|
|
392
|
+
data=b"{}",
|
|
393
|
+
method="POST",
|
|
394
|
+
headers={"content-type": "application/json"},
|
|
395
|
+
)
|
|
396
|
+
try:
|
|
397
|
+
with urllib.request.urlopen(request, timeout=10) as response:
|
|
398
|
+
payload = json.loads(response.read().decode("utf-8"))
|
|
399
|
+
except urllib.error.HTTPError as error:
|
|
400
|
+
try:
|
|
401
|
+
payload = json.loads(error.read().decode("utf-8"))
|
|
402
|
+
reason = payload.get("error", {}).get("message", str(error))
|
|
403
|
+
except Exception:
|
|
404
|
+
reason = str(error)
|
|
405
|
+
print_result("failed_to_start", gate=gate, reason=reason)
|
|
406
|
+
return 2
|
|
407
|
+
except Exception as error:
|
|
408
|
+
print_result("failed_to_start", gate=gate, reason=str(error))
|
|
409
|
+
return 2
|
|
410
|
+
|
|
411
|
+
record = payload.get("record", {}) if isinstance(payload, dict) else {}
|
|
412
|
+
print_result(
|
|
413
|
+
payload.get("status", "failed_to_start"),
|
|
414
|
+
gate=gate,
|
|
415
|
+
request=record.get("requestPath"),
|
|
416
|
+
report=record.get("reportPath"),
|
|
417
|
+
message=payload.get("message"),
|
|
418
|
+
)
|
|
419
|
+
return 0
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
def read_json(path: Path) -> dict:
|
|
423
|
+
try:
|
|
424
|
+
return json.loads(path.read_text())
|
|
425
|
+
except (OSError, ValueError):
|
|
426
|
+
return {"version": 1, "enabled": False, "activeGate": None, "gates": {}}
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
def write_json(path: Path, data: dict) -> None:
|
|
430
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
431
|
+
tmp = path.with_suffix(path.suffix + ".tmp")
|
|
432
|
+
tmp.write_text(json.dumps(data, indent=2, sort_keys=True) + "\\n")
|
|
433
|
+
tmp.replace(path)
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
def load_config(root: Path) -> dict:
|
|
437
|
+
path = root / ".ai/codex/config.toml"
|
|
438
|
+
if not path.is_file() or tomllib is None:
|
|
439
|
+
return {}
|
|
440
|
+
return tomllib.loads(path.read_text())
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
def codex_review_config(root: Path) -> dict:
|
|
444
|
+
config = load_config(root)
|
|
445
|
+
vcm = config.get("vcm", {})
|
|
446
|
+
review = vcm.get("codex_review", {}) if isinstance(vcm, dict) else {}
|
|
447
|
+
return review if isinstance(review, dict) else {}
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
def command_output(root: Path, command: list[str]) -> bytes:
|
|
451
|
+
result = subprocess.run(
|
|
452
|
+
command,
|
|
453
|
+
cwd=root,
|
|
454
|
+
check=False,
|
|
455
|
+
stdout=subprocess.PIPE,
|
|
456
|
+
stderr=subprocess.DEVNULL,
|
|
457
|
+
)
|
|
458
|
+
return result.stdout if result.returncode == 0 else b""
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
def input_hash(root: Path, gate: str) -> str:
|
|
462
|
+
digest = hashlib.sha256()
|
|
463
|
+
common = [
|
|
464
|
+
"CLAUDE.md",
|
|
465
|
+
".ai/codex/AGENTS.md",
|
|
466
|
+
".ai/codex/config.toml",
|
|
467
|
+
f".ai/codex/prompts/{gate}-gate.md",
|
|
468
|
+
]
|
|
469
|
+
for relative in common + SOURCE_ARTIFACTS[gate]:
|
|
470
|
+
path = root / relative
|
|
471
|
+
digest.update(relative.encode())
|
|
472
|
+
if path.is_file():
|
|
473
|
+
digest.update(path.read_bytes())
|
|
474
|
+
else:
|
|
475
|
+
digest.update(b"<missing>")
|
|
476
|
+
if gate == "final-diff":
|
|
477
|
+
digest.update(command_output(root, ["git", "status", "--porcelain=v1"]))
|
|
478
|
+
digest.update(command_output(root, ["git", "diff", "--binary"]))
|
|
479
|
+
digest.update(command_output(root, ["git", "diff", "--cached", "--binary"]))
|
|
480
|
+
return digest.hexdigest()
|
|
481
|
+
|
|
482
|
+
|
|
483
|
+
def request_id(gate: str) -> str:
|
|
484
|
+
stamp = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
|
|
485
|
+
return f"{stamp}-{gate}-{uuid.uuid4().hex[:8]}"
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
def local_request(gate: str) -> int:
|
|
489
|
+
root = root_dir()
|
|
490
|
+
review_config = codex_review_config(root)
|
|
491
|
+
enabled = bool(review_config.get("enabled", False))
|
|
492
|
+
required = set(review_config.get("required_gates", []))
|
|
493
|
+
index_path = root / ".ai/vcm/codex-reviews/index.json"
|
|
494
|
+
index = read_json(index_path)
|
|
495
|
+
index.update({"version": 1, "enabled": enabled})
|
|
496
|
+
index.setdefault("gates", {})
|
|
497
|
+
|
|
498
|
+
if not enabled:
|
|
499
|
+
index["activeGate"] = None
|
|
500
|
+
write_json(index_path, index)
|
|
501
|
+
print_result("disabled", gate=gate)
|
|
502
|
+
return 0
|
|
503
|
+
|
|
504
|
+
if gate not in required:
|
|
505
|
+
gate_record = index["gates"].setdefault(gate, {})
|
|
506
|
+
gate_record.update({"required": False, "status": "not_required", "updatedAt": now_iso()})
|
|
507
|
+
write_json(index_path, index)
|
|
508
|
+
print_result("not_required", gate=gate)
|
|
509
|
+
return 0
|
|
510
|
+
|
|
511
|
+
current_hash = input_hash(root, gate)
|
|
512
|
+
gate_record = index["gates"].get(gate, {})
|
|
513
|
+
if (
|
|
514
|
+
gate_record.get("status") == "completed"
|
|
515
|
+
and gate_record.get("decision") == "approve"
|
|
516
|
+
and gate_record.get("inputHash") == current_hash
|
|
517
|
+
):
|
|
518
|
+
print_result("already_approved", gate=gate, report=gate_record.get("reportPath", REPORTS[gate]))
|
|
519
|
+
return 0
|
|
520
|
+
|
|
521
|
+
rid = request_id(gate)
|
|
522
|
+
request_path = root / ".ai/vcm/codex-reviews/requests" / f"{rid}.json"
|
|
523
|
+
report_path = REPORTS[gate]
|
|
524
|
+
requested_at = now_iso()
|
|
525
|
+
write_json(request_path, {
|
|
526
|
+
"version": 1,
|
|
527
|
+
"requestId": rid,
|
|
528
|
+
"gate": gate,
|
|
529
|
+
"status": "requested",
|
|
530
|
+
"requestedAt": requested_at,
|
|
531
|
+
"inputHash": current_hash,
|
|
532
|
+
"reportPath": report_path,
|
|
533
|
+
"promptPath": f".ai/codex/prompts/{gate}-gate.md",
|
|
534
|
+
})
|
|
535
|
+
|
|
536
|
+
index["activeGate"] = gate
|
|
537
|
+
index["gates"][gate] = {
|
|
538
|
+
"gate": gate,
|
|
539
|
+
"required": True,
|
|
540
|
+
"status": "running",
|
|
541
|
+
"decision": None,
|
|
542
|
+
"reportPath": report_path,
|
|
543
|
+
"promptPath": f".ai/codex/prompts/{gate}-gate.md",
|
|
544
|
+
"inputHash": current_hash,
|
|
545
|
+
"requestId": rid,
|
|
546
|
+
"requestPath": request_path.relative_to(root).as_posix(),
|
|
547
|
+
"requestedAt": requested_at,
|
|
548
|
+
"updatedAt": requested_at,
|
|
549
|
+
}
|
|
550
|
+
write_json(index_path, index)
|
|
551
|
+
print_result("started", gate=gate, request=index["gates"][gate]["requestPath"], report=report_path)
|
|
552
|
+
return 0
|
|
553
|
+
|
|
554
|
+
|
|
555
|
+
def main() -> int:
|
|
556
|
+
parser = argparse.ArgumentParser()
|
|
557
|
+
parser.add_argument("--gate", required=True, choices=GATES)
|
|
558
|
+
args = parser.parse_args()
|
|
559
|
+
|
|
560
|
+
expected_root = os.environ.get("VCM_TASK_REPO_ROOT")
|
|
561
|
+
if expected_root and Path(expected_root).resolve() != Path.cwd().resolve():
|
|
562
|
+
print_result("failed_to_start", gate=args.gate, reason="cwd does not match VCM_TASK_REPO_ROOT")
|
|
563
|
+
return 2
|
|
564
|
+
|
|
565
|
+
api_result = call_vcm_api(args.gate)
|
|
566
|
+
if api_result is not None:
|
|
567
|
+
return api_result
|
|
568
|
+
return local_request(args.gate)
|
|
569
|
+
|
|
570
|
+
|
|
571
|
+
if __name__ == "__main__":
|
|
572
|
+
sys.exit(main())
|
|
573
|
+
`;
|
|
574
|
+
}
|
|
@@ -54,6 +54,16 @@ export function renderProjectManagerHarnessRules() {
|
|
|
54
54
|
- If a required artifact is missing, stale, blocked, or asks for a decision, route the issue to the responsible role or user.
|
|
55
55
|
- Request architect post-review docs sync after reviewer completes.
|
|
56
56
|
|
|
57
|
+
### Codex Review Gates
|
|
58
|
+
|
|
59
|
+
- Use the \`vcm-codex-review-gate\` skill to request a gate review or handle a VCM Codex review callback.
|
|
60
|
+
- If Codex gates are enabled, accept only \`approve\` or \`request_changes\`.
|
|
61
|
+
- Before coder dispatch, request \`architecture-plan\`; on \`request_changes\`, route the report to architect.
|
|
62
|
+
- Before docs sync or final acceptance, request \`validation-adequacy\`; on \`request_changes\`, route the report to reviewer.
|
|
63
|
+
- Before PR preparation, request \`final-diff\`; on \`request_changes\`, route the report to architect for Debug Mode or Replan assessment.
|
|
64
|
+
- Do not infer finding owners from Codex output; route only by gate type.
|
|
65
|
+
- Record gate decision, report path, and any skip or override reason.
|
|
66
|
+
|
|
57
67
|
### Partial Role Results
|
|
58
68
|
|
|
59
69
|
- Treat partial, blocked, or continuation-needed role results as incomplete gates.
|
|
@@ -65,8 +75,8 @@ export function renderProjectManagerHarnessRules() {
|
|
|
65
75
|
### Final Acceptance
|
|
66
76
|
|
|
67
77
|
- Use the \`vcm-final-acceptance\` skill before declaring the task complete.
|
|
68
|
-
- Start final acceptance only after reviewer and docs-sync gates pass or an explicit exception is approved.
|
|
69
|
-
- Confirm required evidence exists: validation result, review decision, docs-sync decision, unresolved risks, known-issues disposition, and cleanup status.
|
|
78
|
+
- Start final acceptance only after reviewer, required Codex gates, and docs-sync gates pass or an explicit exception is approved.
|
|
79
|
+
- Confirm required evidence exists: validation result, review decision, required Codex gate decisions, docs-sync decision, unresolved risks, known-issues disposition, and cleanup status.
|
|
70
80
|
- If final acceptance finds missing evidence, unresolved risk, or required user approval, route it to the responsible role or user before closing the task.
|
|
71
81
|
|
|
72
82
|
### PR Preparation
|
|
@@ -74,7 +84,7 @@ export function renderProjectManagerHarnessRules() {
|
|
|
74
84
|
- Prepare or update a GitHub PR only after final acceptance passes.
|
|
75
85
|
- Confirm \`git status\` has no uncommitted changes before creating or updating the PR.
|
|
76
86
|
- Use \`.github/pull_request_template.md\` when present.
|
|
77
|
-
- Fill the PR body from final acceptance, review report, docs-sync report, known-issues disposition, and commits.
|
|
87
|
+
- Fill the PR body from final acceptance, review report, Codex gate reports when present, docs-sync report, known-issues disposition, and commits.
|
|
78
88
|
- Do not perform technical review or validation during PR preparation; route missing evidence to the responsible role.
|
|
79
89
|
- Create a draft PR by default unless the user requests a ready PR.
|
|
80
90
|
|
|
@@ -16,6 +16,7 @@ Read the relevant task evidence before deciding:
|
|
|
16
16
|
- \`.ai/vcm/handoffs/review-report.md\` when reviewer validation was required
|
|
17
17
|
- \`.ai/vcm/handoffs/docs-sync-report.md\` when durable docs could be affected
|
|
18
18
|
- \`.ai/vcm/handoffs/known-issues.md\` when unresolved findings were recorded
|
|
19
|
+
- \`.ai/vcm/codex-reviews/index.json\` and referenced Codex reports when Codex gates were required, skipped, or overridden
|
|
19
20
|
- current \`git status\` and changed file list
|
|
20
21
|
- relevant long-term docs only when needed to confirm that a docs-sync artifact exists and names the correct durable docs
|
|
21
22
|
|
|
@@ -27,6 +28,7 @@ Acceptable evidence must show:
|
|
|
27
28
|
|
|
28
29
|
- architect plan or docs-sync decision when architecture, public contracts, durable docs, or known issues changed
|
|
29
30
|
- reviewer decision and validation evidence when code, behavior, tests, or generated context changed
|
|
31
|
+
- required Codex gate decisions, skip reasons, or override reasons when Codex gates were enabled
|
|
30
32
|
- known-issues disposition when unresolved findings were recorded
|
|
31
33
|
- explicit user approval for accepted high-risk decisions or intentionally skipped required gates
|
|
32
34
|
|
|
@@ -54,6 +56,7 @@ Check:
|
|
|
54
56
|
- required handoff artifacts exist and are current
|
|
55
57
|
- architecture plan completion, Replan, or architect follow-up decision is recorded
|
|
56
58
|
- reviewer report records validation commands, results, skipped checks with reasons, and an acceptable decision
|
|
59
|
+
- required Codex gates are approved, skipped with a recorded reason, or overridden with a recorded reason
|
|
57
60
|
- docs-sync report records docs updated, docs intentionally left unchanged, or required follow-up
|
|
58
61
|
- known issues are either resolved, promoted to durable docs by architect, or explicitly accepted
|
|
59
62
|
- temporary task state is ready to clean after durable facts are promoted
|
|
@@ -69,7 +72,7 @@ Choose exactly one:
|
|
|
69
72
|
- needs-docs-sync
|
|
70
73
|
- blocked-by-user-decision
|
|
71
74
|
|
|
72
|
-
Do not accept when required role evidence is missing, reviewer findings are unresolved, docs sync is missing for durable changes, known-issues disposition is missing, or unexplained high-risk files remain.
|
|
75
|
+
Do not accept when required role evidence is missing, required Codex gate evidence is missing, reviewer findings are unresolved, docs sync is missing for durable changes, known-issues disposition is missing, or unexplained high-risk files remain.
|
|
73
76
|
|
|
74
77
|
## Output
|
|
75
78
|
|
|
@@ -106,6 +109,8 @@ accepted | accepted-with-known-risks | needs-coder-follow-up | needs-architect-r
|
|
|
106
109
|
|
|
107
110
|
## Review And Docs Sync
|
|
108
111
|
|
|
112
|
+
## Codex Review Gates
|
|
113
|
+
|
|
109
114
|
## Cleanup Readiness
|
|
110
115
|
|
|
111
116
|
## Final User Summary
|