commitgate 0.3.1 → 0.7.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/AGENTS.template.md +17 -0
- package/CHANGELOG.md +115 -0
- package/README.en.md +194 -49
- package/README.md +201 -51
- package/bin/commitgate.mjs +15 -6
- package/bin/dispatch.d.mts +6 -0
- package/bin/dispatch.mjs +38 -0
- package/bin/init.ts +916 -57
- package/bin/migrate.ts +244 -0
- package/bin/uninstall.ts +97 -13
- package/package.json +10 -4
- package/req.config.json.sample +16 -13
- package/scripts/req/lib/adapters.ts +56 -8
- package/scripts/req/lib/config.ts +57 -1
- package/scripts/req/lib/porcelain.ts +104 -0
- package/scripts/req/lib/scratch.ts +104 -0
- package/scripts/req/req-commit.ts +16 -3
- package/scripts/req/req-doctor.ts +135 -31
- package/scripts/req/req-new.ts +60 -10
- package/scripts/req/req-next.ts +654 -0
- package/scripts/req/review-codex.ts +349 -73
- package/scripts/verify-review-overrides.mjs +96 -0
- package/templates/CLAUDE.template.md +16 -0
- package/templates/claude-command.md +39 -0
- package/templates/claude-skill.md +62 -0
- package/templates/cursor-rule.mdc +58 -0
- package/templates/workflow.gitignore +8 -0
- package/workflow/machine.schema.json +5 -1
- package/workflow/req.config.schema.json +6 -0
- package/workflow/review-persona.md +66 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* REQ-2026-013 P1 — 리뷰 모델·추론강도 override **실효성** live 검증(수동/smoke).
|
|
4
|
+
*
|
|
5
|
+
* arg-캡처 단위 테스트(tests/unit/req-adapters.test.ts)는 도구가 `-c` 를 **넘기는지**만 본다.
|
|
6
|
+
* codex가 그 override를 **존중하는지**(무시하고 전역 상속하지 않는지)는 live로만 확인된다 —
|
|
7
|
+
* 자기-리뷰 성공은 "적용"과 "무시하고 ultra 상속"을 구분 못 하기 때문(설계 D7).
|
|
8
|
+
*
|
|
9
|
+
* 그래서 **bogus 값**을 주고 codex가 **거부**하면 override가 codex에 도달·해석됐다는 증거다:
|
|
10
|
+
* - bogus model → `... model is not supported` / `Model metadata for ... not found`
|
|
11
|
+
* - bogus effort → `[reasoning.effort] [invalid_enum_value]`
|
|
12
|
+
* exec·resume 두 경로 각각에 대해 확인한다(어댑터가 `-c` 를 양쪽에 주입하므로).
|
|
13
|
+
*
|
|
14
|
+
* ⚠️ 실제 codex CLI + 인증이 필요하다(CI 게이트 아님 — 로컬/수동 실행). exit 0 = 4/4 통과.
|
|
15
|
+
* 사용: `node scripts/verify-review-overrides.mjs`
|
|
16
|
+
*/
|
|
17
|
+
import spawn from 'cross-spawn'
|
|
18
|
+
|
|
19
|
+
const BOGUS_MODEL = '__bogus_model_xyz__'
|
|
20
|
+
const BOGUS_EFFORT = '__bogus_effort_xyz__'
|
|
21
|
+
const VALID_MODEL = 'gpt-5.6-terra'
|
|
22
|
+
const VALID_EFFORT = 'high'
|
|
23
|
+
|
|
24
|
+
/** 어댑터(adapters.ts:review)와 동일한 `-c` 오버라이드 조립. */
|
|
25
|
+
function overrideArgs(model, effort) {
|
|
26
|
+
const a = []
|
|
27
|
+
if (model) a.push('-c', `model="${model}"`)
|
|
28
|
+
if (effort) a.push('-c', `model_reasoning_effort="${effort}"`)
|
|
29
|
+
return a
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** codex 한 번 실행(exec 또는 resume) → { text: 합쳐진 오류 메시지, code }. cross-spawn(어댑터와 동일 spawn). */
|
|
33
|
+
function runCodex({ resumeThreadId, model, effort }) {
|
|
34
|
+
const base = resumeThreadId
|
|
35
|
+
? ['exec', 'resume', resumeThreadId, '-c', 'sandbox_mode="read-only"', ...overrideArgs(model, effort), '--json', '-']
|
|
36
|
+
: ['exec', ...overrideArgs(model, effort), '--json', '--sandbox', 'read-only', '-']
|
|
37
|
+
const r = spawn.sync('codex', base, { input: 'reply with the single word ok', encoding: 'utf8', maxBuffer: 64 * 1024 * 1024 })
|
|
38
|
+
const out = (r.stdout || '') + '\n' + (r.stderr || '')
|
|
39
|
+
// JSONL에서 turn.failed/error/item.completed(error)의 message를 모은다(실측 계약).
|
|
40
|
+
let msgs = ''
|
|
41
|
+
let threadId = null
|
|
42
|
+
for (const line of out.split('\n')) {
|
|
43
|
+
const t = line.trim()
|
|
44
|
+
if (!t) continue
|
|
45
|
+
try {
|
|
46
|
+
const ev = JSON.parse(t)
|
|
47
|
+
if (ev.type === 'thread.started' && typeof ev.thread_id === 'string') threadId = ev.thread_id
|
|
48
|
+
if (ev.type === 'error' && typeof ev.message === 'string') msgs += ev.message + '\n'
|
|
49
|
+
if (ev.type === 'turn.failed' && ev.error?.message) msgs += ev.error.message + '\n'
|
|
50
|
+
if (ev.type === 'item.completed' && ev.item?.type === 'error' && ev.item?.message) msgs += ev.item.message + '\n'
|
|
51
|
+
} catch {
|
|
52
|
+
msgs += t + '\n' // 비-JSONL(에러 텍스트)도 포함
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return { text: msgs + out, code: r.status, threadId }
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
let pass = 0
|
|
59
|
+
let fail = 0
|
|
60
|
+
function check(label, cond, detail) {
|
|
61
|
+
if (cond) {
|
|
62
|
+
pass++
|
|
63
|
+
console.log(`PASS ${label}`)
|
|
64
|
+
} else {
|
|
65
|
+
fail++
|
|
66
|
+
console.log(`FAIL ${label}\n ${detail}`)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const modelRejected = (t) => /not supported|not found|invalid.*model|unknown model/i.test(t)
|
|
71
|
+
const effortRejected = (t) => /reasoning.?effort|invalid_enum_value/i.test(t)
|
|
72
|
+
|
|
73
|
+
console.log('REQ-2026-013 P1 override 실효성 live 검증 (codex CLI 필요)\n')
|
|
74
|
+
|
|
75
|
+
// 1) 유효 override로 throwaway 스레드 확보(resume 검증용).
|
|
76
|
+
const seed = runCodex({ resumeThreadId: null, model: VALID_MODEL, effort: VALID_EFFORT })
|
|
77
|
+
if (!seed.threadId) {
|
|
78
|
+
console.log(`FAIL seed exec가 thread_id를 반환하지 못함 — 유효 model/effort(${VALID_MODEL}/${VALID_EFFORT})로 실행 실패?\n ${seed.text.slice(0, 400)}`)
|
|
79
|
+
process.exit(1)
|
|
80
|
+
}
|
|
81
|
+
console.log(`(seed thread = ${seed.threadId})\n`)
|
|
82
|
+
|
|
83
|
+
// 2) exec — bogus model / bogus effort
|
|
84
|
+
const em = runCodex({ resumeThreadId: null, model: BOGUS_MODEL, effort: VALID_EFFORT })
|
|
85
|
+
check('exec + bogus model → codex 거부', modelRejected(em.text), em.text.slice(0, 300))
|
|
86
|
+
const ee = runCodex({ resumeThreadId: null, model: VALID_MODEL, effort: BOGUS_EFFORT })
|
|
87
|
+
check('exec + bogus effort → codex 거부', effortRejected(ee.text), ee.text.slice(0, 300))
|
|
88
|
+
|
|
89
|
+
// 3) resume — bogus model / bogus effort (override가 resume에서도 재적용됨을 확인)
|
|
90
|
+
const rm = runCodex({ resumeThreadId: seed.threadId, model: BOGUS_MODEL, effort: VALID_EFFORT })
|
|
91
|
+
check('resume + bogus model → codex 거부', modelRejected(rm.text), rm.text.slice(0, 300))
|
|
92
|
+
const re = runCodex({ resumeThreadId: seed.threadId, model: VALID_MODEL, effort: BOGUS_EFFORT })
|
|
93
|
+
check('resume + bogus effort → codex 거부', effortRejected(re.text), re.text.slice(0, 300))
|
|
94
|
+
|
|
95
|
+
console.log(`\n${pass}/${pass + fail} 통과`)
|
|
96
|
+
process.exit(fail === 0 ? 0 : 1)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# 프로젝트 지침
|
|
2
|
+
|
|
3
|
+
<!-- 이 파일은 `npx commitgate`가 CLAUDE.md가 없을 때만 생성한 템플릿입니다. 자유롭게 수정하세요. -->
|
|
4
|
+
|
|
5
|
+
## CommitGate
|
|
6
|
+
|
|
7
|
+
이 저장소는 **CommitGate**를 쓴다. 코드 변경은 REQ 티켓 단위로 묶이고, Codex가 승인한 staged tree만 커밋된다.
|
|
8
|
+
|
|
9
|
+
코드를 커밋하게 되는 요청은 일반 구현으로 처리하지 말고 이 워크플로를 따른다.
|
|
10
|
+
|
|
11
|
+
- **계약 정본**: 저장소 루트의 [`AGENTS.md`](./AGENTS.md). 절대 규칙·통제점·승인 문장이 거기 있다.
|
|
12
|
+
(`<!-- commitgate:contract -->` 마커가 없으면 CommitGate 계약이 아니다 — init이 함께 설치한 루트의 `AGENTS.commitgate.md`를 계약으로 읽고, 사용자에게 `AGENTS.md`로의 병합을 요청하라.)
|
|
13
|
+
- **다음 행동은 추측하지 않는다**: `req:next <REQ-id>`가 알려 준다.
|
|
14
|
+
`RUN`은 그대로 실행, `AGENT`는 그 작업 수행 후 `git add`, `AWAIT_HUMAN`은 **멈추고 승인 문장을 그대로** 받는다.
|
|
15
|
+
워크플로 명령은 이 저장소의 **패키지매니저 실행 형식**으로 돌린다. `req:next`의 `RUN` 출력이 정확한 형태를 그대로 보여 준다.
|
|
16
|
+
- 자세한 진입 절차는 `/req` 슬래시 커맨드 또는 `.claude/skills/commitgate/SKILL.md`에 있다.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: CommitGate REQ 워크플로로 요구사항을 처리한다 (티켓 발행 → 설계 → Codex 리뷰 → 구현 → 커밋)
|
|
3
|
+
argument-hint: <요구사항>
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
이 요청을 일반 구현으로 처리하지 말고, 이 저장소에 설치된 **CommitGate**로 처리하라.
|
|
7
|
+
|
|
8
|
+
## 요구사항
|
|
9
|
+
|
|
10
|
+
$ARGUMENTS
|
|
11
|
+
|
|
12
|
+
위 내용이 아래 네 칸으로 정리되지 않으면 **먼저 사용자에게 물어라.** 추측해서 채우지 마라.
|
|
13
|
+
|
|
14
|
+
```text
|
|
15
|
+
- 무엇을:
|
|
16
|
+
- 왜:
|
|
17
|
+
- 제약:
|
|
18
|
+
- 완료 기준:
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## 계약
|
|
22
|
+
|
|
23
|
+
저장소 루트의 `AGENTS.md`를 읽어라. 절대 규칙·통제점·승인 문장의 정본이다.
|
|
24
|
+
(`<!-- commitgate:contract -->` 마커가 없으면 init이 함께 설치한 루트의 `AGENTS.commitgate.md`를 계약으로 읽고, 사용자에게 `AGENTS.md`로의 병합을 요청하라.)
|
|
25
|
+
|
|
26
|
+
## 절차
|
|
27
|
+
|
|
28
|
+
> 아래 명령은 **저장소의 패키지매니저 실행 형식**으로 돌린다(npm은 `run`과 `--` 구분자가 필요하고, pnpm·yarn은 스크립트 이름을 바로 받는다).
|
|
29
|
+
> `npx commitgate` 설치 출력과 `req:next`의 `RUN` 출력이 언제나 이 저장소에 맞는 정확한 형태를 보여 준다 — 그걸 그대로 쓰면 된다.
|
|
30
|
+
|
|
31
|
+
1. `req:new <slug> --run` — 티켓과 브랜치를 만든다.
|
|
32
|
+
2. 그다음부터는 **`req:next <REQ-id>`가 시키는 대로** 한다.
|
|
33
|
+
- `RUN` → 출력된 명령을 그대로 실행 → 다시 `req:next`
|
|
34
|
+
- `AGENT` → 그 작업을 하고 `git add` → 다시 `req:next`
|
|
35
|
+
- `AWAIT_HUMAN` → **멈추고** 출력된 승인 문장을 그대로 받는다
|
|
36
|
+
- `DONE` / `BLOCKED` → 사용자에게 보고
|
|
37
|
+
3. 이 루프를 끊지 말고 반복한다. 다음 행동을 스스로 추측하지 마라.
|
|
38
|
+
|
|
39
|
+
첫 응답은 발행한 REQ 번호, 브랜치, phase 분해, 통제점을 요약해서 보여 준다.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: commitgate
|
|
3
|
+
description: 이 저장소의 REQ 워크플로(CommitGate)로 요구사항을 처리한다. 기능 추가·버그 수정·리팩터링·문서 변경 등 코드를 커밋하게 되는 모든 작업에 사용한다. Codex 리뷰 승인 없이는 커밋이 통과하지 않는다.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# CommitGate
|
|
7
|
+
|
|
8
|
+
이 저장소는 **CommitGate**를 쓴다. 코드 변경은 REQ 티켓 단위로 묶이고, Codex가 승인한 staged tree만 커밋된다.
|
|
9
|
+
|
|
10
|
+
**일반 구현으로 처리하지 마라.** 아래 계약을 먼저 읽어라.
|
|
11
|
+
|
|
12
|
+
## 계약 (정본)
|
|
13
|
+
|
|
14
|
+
저장소 루트의 **`AGENTS.md`**가 정본이다. 절대 규칙, 통제점 표, 승인 문장이 거기 있다.
|
|
15
|
+
|
|
16
|
+
> `AGENTS.md`에 `<!-- commitgate:contract -->` 마커가 없으면 그 파일은 CommitGate 계약이 아니다.
|
|
17
|
+
> 그 경우 `npx commitgate`가 저장소 루트에 **`AGENTS.commitgate.md`**(계약 템플릿 사본)를 함께 설치해 둔다.
|
|
18
|
+
> 그 파일을 계약으로 읽고, 사용자에게 `AGENTS.md`로의 병합을 요청하라.
|
|
19
|
+
|
|
20
|
+
## 요구사항 받기
|
|
21
|
+
|
|
22
|
+
사용자 요청이 아래 네 칸으로 정리되지 않으면 먼저 물어라. 추측하지 마라.
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
- 무엇을:
|
|
26
|
+
- 왜:
|
|
27
|
+
- 제약:
|
|
28
|
+
- 완료 기준:
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
정리되면 `req:new`로 티켓을 만든다.
|
|
32
|
+
|
|
33
|
+
## 진행 방법 — `req:next`가 시키는 대로
|
|
34
|
+
|
|
35
|
+
**다음 행동을 스스로 추측하지 마라.** 도구가 상태에서 계산해 준다.
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
req:next <REQ-id>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
> 워크플로 명령은 이 저장소의 **패키지매니저 실행 형식**으로 돌린다(npm은 `run`과 `--` 구분자가 필요하고, pnpm·yarn은 스크립트 이름을 바로 받는다).
|
|
42
|
+
> `req:next`의 `RUN` 출력이 언제나 정확한 형태를 그대로 보여 주므로, 그 줄을 복사해 쓰면 된다.
|
|
43
|
+
|
|
44
|
+
출력의 `kind`가 정본이다(`--json`으로 기계 판독 가능).
|
|
45
|
+
|
|
46
|
+
| kind | 할 일 |
|
|
47
|
+
|---|---|
|
|
48
|
+
| `RUN` | 출력된 명령을 **그대로** 실행하고, 다시 `req:next` |
|
|
49
|
+
| `AGENT` | 도구가 대신 못 하는 작업(구현·문서 작성·`git add`). 하고 나서 다시 `req:next` |
|
|
50
|
+
| `AWAIT_HUMAN` | **멈춘다.** 출력된 승인 문장을 사용자에게서 **그 문장 그대로** 받기 전에는 진행하지 않는다 |
|
|
51
|
+
| `DONE` | 이 티켓에서 도구가 할 일이 없다. 통합은 별도 통제점 |
|
|
52
|
+
| `BLOCKED` | 사람에게 보고한다. 같은 리뷰를 재시도하지 마라 |
|
|
53
|
+
|
|
54
|
+
이 루프를 **끊지 말고** 반복한다. `AWAIT_HUMAN`·`BLOCKED`·오류에서만 멈춘다.
|
|
55
|
+
|
|
56
|
+
## 반드시 지킬 것
|
|
57
|
+
|
|
58
|
+
- 리뷰 대상은 `git add` 한 파일뿐이다.
|
|
59
|
+
- `state.json`과 `responses/`는 직접 `git add` 하지 않는다 — 도구가 관리한다.
|
|
60
|
+
- `req:review-codex`가 exit 3(NEEDS_FIX)이면 findings를 수정하고 재리뷰한다.
|
|
61
|
+
- exit 2(BLOCKED)면 **같은 리뷰를 재시도하지 말고** 사람에게 보고한다.
|
|
62
|
+
- 승인은 받은 문장 그대로만 유효하며, 다음 통제점으로 **이월되지 않는다**.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: CommitGate — 코드 변경은 REQ 티켓 + Codex 리뷰 승인을 거쳐야 커밋된다
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# CommitGate
|
|
7
|
+
|
|
8
|
+
이 저장소는 **CommitGate**를 쓴다. 코드 변경은 REQ 티켓 단위로 묶이고, Codex가 승인한 staged tree만 커밋된다.
|
|
9
|
+
|
|
10
|
+
코드를 커밋하게 되는 요청은 **일반 구현으로 처리하지 마라.**
|
|
11
|
+
|
|
12
|
+
## 계약 (정본)
|
|
13
|
+
|
|
14
|
+
저장소 루트의 **`AGENTS.md`**가 정본이다. 절대 규칙, 통제점 표, 승인 문장이 거기 있다.
|
|
15
|
+
|
|
16
|
+
> `AGENTS.md`에 `<!-- commitgate:contract -->` 마커가 없으면 그 파일은 CommitGate 계약이 아니다.
|
|
17
|
+
> 그 경우 저장소 루트의 **`AGENTS.commitgate.md`**(init이 함께 설치한 계약 템플릿 사본)를 계약으로 읽고,
|
|
18
|
+
> 사용자에게 `AGENTS.md`로의 병합을 요청하라.
|
|
19
|
+
|
|
20
|
+
## 요구사항 받기
|
|
21
|
+
|
|
22
|
+
아래 네 칸이 채워지지 않으면 먼저 물어라. 추측하지 마라.
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
- 무엇을:
|
|
26
|
+
- 왜:
|
|
27
|
+
- 제약:
|
|
28
|
+
- 완료 기준:
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## 진행 방법 — `req:next`가 시키는 대로
|
|
32
|
+
|
|
33
|
+
다음 행동을 스스로 추측하지 마라. 도구가 상태에서 계산한다.
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
req:new <slug> --run # 티켓·브랜치 생성
|
|
37
|
+
req:next <REQ-id> # 그다음은 항상 이것
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
> 이 명령들은 저장소의 **패키지매니저 실행 형식**으로 돌린다(npm은 `run`과 `--` 구분자가 필요하고, pnpm·yarn은 스크립트 이름을 바로 받는다).
|
|
41
|
+
> `req:next`의 `RUN` 출력이 정확한 형태를 그대로 보여 준다.
|
|
42
|
+
|
|
43
|
+
| kind | 할 일 |
|
|
44
|
+
|---|---|
|
|
45
|
+
| `RUN` | 출력된 명령을 그대로 실행하고 다시 `req:next` |
|
|
46
|
+
| `AGENT` | 구현·문서 작성·`git add` 후 다시 `req:next` |
|
|
47
|
+
| `AWAIT_HUMAN` | **멈춘다.** 출력된 승인 문장을 그대로 받기 전엔 진행 금지 |
|
|
48
|
+
| `DONE` | 도구가 할 일 없음. 통합은 별도 통제점 |
|
|
49
|
+
| `BLOCKED` | 사람에게 보고. 같은 리뷰 재시도 금지 |
|
|
50
|
+
|
|
51
|
+
이 루프를 끊지 말고 반복한다.
|
|
52
|
+
|
|
53
|
+
## 반드시 지킬 것
|
|
54
|
+
|
|
55
|
+
- 리뷰 대상은 `git add` 한 파일뿐이다.
|
|
56
|
+
- `state.json`·`responses/`는 직접 `git add` 하지 않는다.
|
|
57
|
+
- `req:review-codex` exit 3(NEEDS_FIX) → 수정 후 재리뷰. exit 2(BLOCKED) → 재시도 금지, 사람에게 보고.
|
|
58
|
+
- 승인은 받은 문장 그대로만 유효하고 다음 통제점으로 이월되지 않는다.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# CommitGate 워크플로 스크래치 산출물(티켓 내부) — commitgate init이 대상 repo의 workflow/.gitignore로 설치.
|
|
2
|
+
#
|
|
3
|
+
# ⚠️ 이 패턴은 **이 .gitignore 파일이 있는 디렉터리(= workflow/) 기준 상대경로**다(중첩 .gitignore, gitignore(5)).
|
|
4
|
+
# 루트 .gitignore가 쓰는 `workflow/**/…` 형태를 여기 그대로 복사하면 `workflow/workflow/…`를 찾아 무효가 된다.
|
|
5
|
+
# `/REQ-*/…`로 **앵커드**해 티켓 직계만 무시한다 — 티켓 밖에 흘러든 동명 파일까지 숨기지 않는다(fail-closed 정합).
|
|
6
|
+
/REQ-*/codex-response.json
|
|
7
|
+
/REQ-*/.review-preview.txt
|
|
8
|
+
/REQ-*/.codex-*.tmp
|
|
@@ -31,7 +31,11 @@
|
|
|
31
31
|
"additionalProperties": false,
|
|
32
32
|
"required": ["severity", "detail", "file"],
|
|
33
33
|
"properties": {
|
|
34
|
-
"severity": {
|
|
34
|
+
"severity": {
|
|
35
|
+
"type": "string",
|
|
36
|
+
"enum": ["P1", "P2", "P3"],
|
|
37
|
+
"description": "Blocking severity. ONLY P1 belongs in findings, and the output schema you are given permits P1 only — P2/P3 remain in this enum solely so previously archived reviews still validate, and you MUST NOT emit them. A defect is P1 only when ALL THREE hold. (1) CATEGORY: it is a requirement violation, data loss or corruption, a security hole, a monetary error, or a fail-closed bypass. (2) NORMAL PATH: it reproduces on the normal supported usage path — this project supports a single active worktree and a cooperative worker, so rare recovery races, multi-worktree divergence and full distributed consistency are outside the supported model. (3) EVIDENCE: you state a concrete reproduction path or failure scenario ('with this input/state, this wrong result occurs'). EXCLUSION RULE: if it is not in the CATEGORY list it is NOT P1 — even if it reproduces on the normal path, and even if it is genuinely worth fixing. Portability, structure, maintainability, readability, naming, future extensibility, scope-adjacent improvements and follow-up debt all belong in observations, never in findings. If you are guessing or it 'might' be a problem, that is observations too. Putting non-P1 remarks in findings is the failure mode this field exists to prevent: it blocks approval indefinitely and the review never converges."
|
|
38
|
+
},
|
|
35
39
|
"detail": { "type": "string" },
|
|
36
40
|
"file": { "type": ["string", "null"] }
|
|
37
41
|
}
|
|
@@ -5,9 +5,15 @@
|
|
|
5
5
|
"ticketRoot": { "type": "string", "minLength": 1 },
|
|
6
6
|
"schemaPath": { "type": "string", "minLength": 1 },
|
|
7
7
|
"handoffPath": { "type": ["string", "null"] },
|
|
8
|
+
"reviewPersonaPath": { "type": ["string", "null"], "minLength": 1 },
|
|
8
9
|
"branchPrefix": { "type": "string", "minLength": 1 },
|
|
9
10
|
"packageManager": { "type": "string", "enum": ["pnpm", "npm", "yarn"] },
|
|
10
11
|
"granularityMaxFiles": { "type": "integer", "minimum": 1 },
|
|
12
|
+
"reviewModel": { "type": ["string", "null"], "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]*$" },
|
|
13
|
+
"reviewReasoningEffort": {
|
|
14
|
+
"type": ["string", "null"],
|
|
15
|
+
"enum": ["none", "minimal", "low", "medium", "high", "xhigh", null]
|
|
16
|
+
},
|
|
11
17
|
"designDocs": {
|
|
12
18
|
"type": "object",
|
|
13
19
|
"additionalProperties": false,
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Reviewer 역할 (PM)
|
|
2
|
+
|
|
3
|
+
너는 이 프로젝트의 **PM**이다. 이 변경의 **적절성과 완성도**를 확보할 책임이 있다.
|
|
4
|
+
|
|
5
|
+
- Builder가 작성한 리뷰 요청서(`codex-request.md`)의 "리뷰 포인트"는 심사 범위의 **하한**이지 상한이 아니다. 요청서가 묻지 않은 결함도 스스로 분석해 지적하라.
|
|
6
|
+
- Builder가 짜 놓은 리뷰 프레임에 갇히지 마라. 무엇을 봐야 하는지는 네가 판단한다.
|
|
7
|
+
- 개발 부채를 식별하되 **`observations`에 기록해 다음 티켓의 입력으로 만들어라**. 지금 넘어가도 되는 부채까지 차단하는 것은 리뷰의 실패다.
|
|
8
|
+
|
|
9
|
+
## 보장 범위 경계 (이 경계 밖은 결함이 아니다)
|
|
10
|
+
|
|
11
|
+
**넓게 보되, 이 프로젝트가 약속한 범위 안에서 판정하라.** 탐색 범위와 차단 범위는 다르다.
|
|
12
|
+
|
|
13
|
+
- 이 프로젝트는 **하나의 활성 worktree와 협조적 작업자**만 지원한다. CommitGate는 정상적인 반복 호출에서 실수를 막는 도구이지, 비협조적·분산 동시 실행을 합의 없이 해결하는 시스템이 아니다.
|
|
14
|
+
- 따라서 **다중 worktree state 발산·드문 recovery 경합·완전한 분산 정합성**은 지원 범위 밖이다. 이것들은 결함이 아니라 **명시된 경계**다.
|
|
15
|
+
- 지원하지 않는 운영 모델을 근거로 차단하지 마라. 그 경계를 푸는 **새 서브시스템을 요구하지 마라**.
|
|
16
|
+
- **정상 사용 경로를 우선하라.** 정상 경로에서 재현되지 않는 이론적 조합은 `observations`다.
|
|
17
|
+
|
|
18
|
+
## P1 정의 (차단의 유일한 기준)
|
|
19
|
+
|
|
20
|
+
`findings`에는 **P1만** 넣는다. P1은 아래 셋을 **모두** 만족할 때만 성립한다.
|
|
21
|
+
|
|
22
|
+
1. **카테고리**: 요구 위반 · 데이터 손상 · 보안 구멍 · 금전 오류 · fail-closed 우회 중 하나다.
|
|
23
|
+
2. **정상 경로**: 정상 사용 경로에서 재현된다.
|
|
24
|
+
3. **증거**: 재현 경로나 실패 시나리오를 명시했다.
|
|
25
|
+
|
|
26
|
+
**배제 규칙**: 카테고리에 없으면 **정상 경로에서 재현되더라도, 고칠 가치가 있더라도 P1이 아니다.** 포터빌리티·구조·유지보수·가독성·이름·장래 확장성·범위 인접 개선·후속 부채는 전부 `observations`다. 추측이면 `observations`다.
|
|
27
|
+
|
|
28
|
+
**절대 표현 금지**: "모든 경우"·"우회 불가"·"어떤 worktree에서도"는 transactional backend가 있을 때만 쓸 수 있다. 설계가 그런 절대 보장을 약속하지 않았다는 이유로 차단하지 마라.
|
|
29
|
+
|
|
30
|
+
## 판정은 구조화 응답 필드로만 낸다
|
|
31
|
+
|
|
32
|
+
| 필드 | 담는 것 |
|
|
33
|
+
|---|---|
|
|
34
|
+
| `findings[]` | **이 변경을 지금 커밋하면 안 되는 이유**만 |
|
|
35
|
+
| `observations[]` | 비차단 의견 — 스타일 취향, 범위 밖 개선, 후속 티켓 후보 |
|
|
36
|
+
| `next_action` | Builder가 다음에 할 일 |
|
|
37
|
+
| `status` / `commit_approved` | 게이트 판정 |
|
|
38
|
+
|
|
39
|
+
## 승인 규칙 (어기면 워크플로가 응답을 거부한다)
|
|
40
|
+
|
|
41
|
+
**승인(`commit_approved=yes`)은 `findings`가 0건일 때만 가능하다.** 지적이 하나라도 있으면 승인할 수 없다 — 워크플로가 그 모순을 검출해 응답 전체를 무효 처리한다.
|
|
42
|
+
|
|
43
|
+
그래서 `findings`에 무엇을 넣을지가 곧 승인 여부다.
|
|
44
|
+
|
|
45
|
+
- **`findings`에 넣을 것**: 정확성 결함, 안전·보안 구멍, fail-closed 우회, 계약 위반, 범위 이탈, 검증 누락 — 지금 커밋되면 안 되는 것.
|
|
46
|
+
- **`observations`에 넣을 것**: 이름·주석·구조 취향, 후속 리팩터 제안, 이 phase 범위를 넘는 개선, "나중에 보면 좋을 것".
|
|
47
|
+
|
|
48
|
+
**"개발 부채가 남지 않도록 하라"를 "부채 후보를 전부 `findings`로 올리라"로 읽지 마라.** 지금 넘어가도 되는 부채는 `observations`에 기록해 다음 티켓의 입력으로 만든다. 차단과 비차단의 경계를 흐리면 승인이 영영 나지 않고, 그것은 리뷰의 실패다.
|
|
49
|
+
|
|
50
|
+
`observations`에는 `severity`를 붙이지 않는다. severity가 붙는 순간 차단 신호가 되어 경계가 무너진다.
|
|
51
|
+
|
|
52
|
+
## 지적의 형태
|
|
53
|
+
|
|
54
|
+
- 결함마다 **재현 경로 또는 실패 시나리오**를 적어라. "이 입력/상태에서 이 결과가 나온다."
|
|
55
|
+
- 파일이 특정되면 `file`에 적어라. 전역 지적이면 비워도 된다.
|
|
56
|
+
- 추측이면 추측이라고 말하고 `observations`로 내려라. 확신하는 것만 `findings`에 올린다.
|
|
57
|
+
- 결함이 없으면 `findings` 없이 승인하라. 하고 싶은 말은 `observations`에 남긴다.
|
|
58
|
+
|
|
59
|
+
## 리뷰 대상
|
|
60
|
+
|
|
61
|
+
리뷰 종류는 프롬프트의 **REVIEW_KIND**를 따른다.
|
|
62
|
+
|
|
63
|
+
- `design` — 권위 아티팩트는 설계 문서 3종이다. 구현 diff가 없는 것이 정상이다. 설계의 결함·누락·모순을 본다.
|
|
64
|
+
- `phase` — 권위 아티팩트는 staged diff다. 그 diff만 심사한다.
|
|
65
|
+
|
|
66
|
+
리뷰 대상이 아닌 것을 근거로 지적하지 마라. 설계 리뷰에서 "구현이 없다"는 지적은 성립하지 않는다.
|