modify harness framework

This commit is contained in:
김경종
2026-09-08 10:30:07 +09:00
parent 1239d5b9f6
commit 1a0f3c9aaf
68 changed files with 6729 additions and 957 deletions
@@ -0,0 +1,171 @@
# Harness 실행 흐름
Harness는 승인된 phase를 독립 Codex 세션으로 실행하고 C#·.NET 8 제품 규칙을
검증한다. 설정 예시는 [HARNESS.md](HARNESS.md), phase 기록 형식은
[phase README](phase/README.md)를 참고한다.
## 1. 계획
`AGENTS.md`와 변경에 관련된 제품 문서·코드·테스트를 확인하고 작업을 한 가지 책임의
step으로 나눈다. 각 step은 목표와 범위, 관련 파일, 핵심 제약, 실제 Acceptance
Criteria를 가진다. 공개 계약이 필요한 경우에만 시그니처를 적는다. 이전 대화에
의존하지 않으며 문서 전체를 step 본문에 복사하지 않는다.
제품 기능의 red-green 작업은 한 step 안에서 끝낸다.
```text
실패 테스트 작성·확인 → 최소 구현 → 전체 관련 테스트 성공 → step 종료
```
## 2. Phase 파일
```text
phases/
index.json
<phase>/
index.json
step0.md
docs/phase/<phase>/
handoff.md
step0.md
```
`phases/{phase}/index.json`이 실행 상태의 기준이다. `context_files`는 기존 참고 문서,
`validation`은 `full` 또는 `docs-only`를 선택한다.
```json
{
"project": "<제품명 또는 미정>",
"phase": "add-parser",
"steps": [
{
"step": 0,
"name": "implement-parser",
"status": "pending",
"context_files": ["docs/ARCHITECTURE.md"],
"validation": "full"
}
]
}
```
## 3. 읽기 전용 확인
```powershell
python scripts/execute.py <phase> --dry-run
```
첫 pending step의 현재 주입 경로, 경로별 문자 수와 전체 프롬프트 문자 수를
출력한다. Codex, Git, timestamp와 파일을 변경하지 않는다. `--push`와 동시 사용은
오류다.
## 4. 프롬프트 구성
각 호출 직전에 index, `context_files`와 handoff를 다시 읽고 다음 순서로 한 번만
결합한다.
```text
AGENTS.md
→ 공통 작업 규칙
→ context_files
→ 현재 phase handoff 또는 호환 summary
→ 직전 실패 요약
→ 현재 step
```
`context_files` 생략은 추가 문서 없음이다. 잘못된 배열, 없는 파일, UTF-8로 읽을 수
없는 파일, 절대 경로, 저장소 밖 경로와 외부를 가리키는 symlink/junction은 Codex나 Git 변경 전에
거부한다. 경로 표기와 대소문자만 다른 중복도 한 번만 넣는다.
handoff가 없을 때만 현재 step보다 앞선 완료 step 중 가장 최근 하나의 summary를
최대 1,000자로 전달한다. 이후 step이나 모든 과거 summary를 누적하지 않는다.
## 5. 실행과 상태
Executor는 `feat-{phase}` branch에서 각 pending step을 독립 `codex exec` 프로세스로
실행한다. Codex는 종료 전에 현재 `docs/phase/{phase}/stepN.md`, handoff와 phase index를
갱신한다.
| 상태 | 의미 |
|---|---|
| `completed` | exit code 0과 executor 독립 검증 통과 후 timestamp와 commit 처리 |
| `error` | 자동 시도 예산 소진, 이유와 timestamp 기록 후 중단 |
| `blocked` | 사용자 또는 외부 환경 조치 필요, 추가 호출 없이 중단 |
프로세스가 nonzero로 끝나면 index에 `completed`가 있어도 성공으로 처리하지 않는다.
Stop 실패가 exit code 0으로 끝나는 경우도 있으므로 완료 보고 직후 executor가 제품을
다시 검증한다. 실패하면 성공 상태를 취소하고 같은 최대 3회 호출 예산에서 재시도한다.
독립 검증에도 실제 docs-only 판정과 별도의 총 1,800초 검증 제한을 적용한다.
## 6. 재시도와 로그
Executor의 호출 예산은 최초 호출을 포함해 총 3회다. 한 Codex 세션 안의 일반적인
red-green 수정 횟수를 별도로 3회로 제한하지 않는다.
재시도 입력의 오류 본문은 총 4,000자로 제한한다. 앞 1,500자와 끝부분 최대 2,500자를
남기고 중간 생략 marker를 제한 안에 포함한다. 전체 stdout/stderr 결과는
`phases/{phase}/stepN-output.json`에 저장하지만 다음 시도에서 같은 파일을 덮어쓴다.
장기 보관할 시행착오는 step 기록에 요약한다. timeout도 같은 호출 예산의 실패이며
인증·CLI 옵션 같은 환경 오류는 즉시 중단한다.
## 7. 편집 전 정책
PreToolUse 훅은 위험한 삭제, 강제 push, hard reset과 SQL 파괴 명령을 차단한다.
Windows 셸의 종료 코드 변환에 의존하지 않도록 JSON `permissionDecision: "deny"`로
거부한다. 실제 Codex 편집 payload의 `tool_input.command`에서 patch 경로를 읽는다.
C# 제품 `.cs` 편집에는 대응 테스트 파일 존재 여부를 검사한다. 기본 루트는
저장소 `tests/`이며 편집 파일 옆 `tests/`, `test/`도 대응 테스트를 찾는다.
`Foo.cs`의 대응 이름은 `FooTests.cs` 또는 `FooTest.cs`다.
테스트 루트의 보조 코드, 중첩 `bin/`·`obj/` 등 산출물, 외부 의존성·명시 generated
경로는 제외한다. `Program.cs`나 `.Designer.cs`라는 이름만으로 면제하지 않는다.
필요한 면제는 정확한 `tdd.exclude` 경로와 이유를 기록한다. 이 설정은 제품 존재
탐색을 우회하지 않는다. 테스트 파일 존재와 실제 red-green 증거를 구분한다.
`tdd.testPatterns`는 `{stem}`을 포함하는 C# 파일명 패턴이다. 경로 구분자는 허용하지
않으며 테스트 위치는 `testRoots`로 지정한다. 한 검사 호출 안에서 후보 목록을 재사용하고
다음 호출에는 파일 추가·삭제를 반영한다. 제품 탐색에서 제외한 산출물·fixture 경로의
파일을 대응 테스트 증거로 사용하지 않는다.
## 8. 종료 전 검증
`validation` 생략 또는 `full`이면 Stop 훅이 발견한 C# 제품 전체를 검증한다.
- 저장소 루트 `global.json`에 따른 .NET 8 SDK 선택 확인
- target의 실제 각 프로젝트와 모든 test project의 `TargetFramework`/`TargetFrameworks`를
SDK로 평가해 .NET 8인지 확인; 상속된 props·속성 및 ProjectReference 그래프도 평가하며 빈 값·비지원 TFM·저장소 밖 참조는 거부
- version 2 설정의 target과 명시된 모든 `testProjects`를 restore/build;
build 경고는 오류로 처리하고 solution에 빠진 test project도 직접 build
- 각 test project를 VSTest로 실행하고 현재 호출의 고유 디렉터리에 생성된 모든 TRX 검사
- 실행 0건·skip-only·누락·손상·실패 TRX는 실패; 과거 결과를 성공 근거로 사용하지 않음
- 지원 metadata 없음: 제외되지 않은 C# 소스가 있으면 orphan 오류
- 제품과 metadata 모두 없음: 제품 검증 대상 없음; 제품 build 성공으로 기록하지 않음
전체 검증은 1,800초 deadline을 공유하며 SDK·TFM 평가·restore·build·test 중 실패한 단계에서
중단한다. `tests/fixtures/`는 제품 자동 탐색에서 제외하는 도구 검증용이다.
Stop 프로세스 종료 코드만으로 통과를 판정하지 않는다. `continue: false`와
`stopReason` 응답은 검증 실패이며 실제 자동 훅 호출은 별도로 확인한다.
`docs-only`이면 executor가 현재 phase를 자식 프로세스 환경으로 전달하고 Stop 훅이
실제 Git 변경을 다시 검사한다. 루트 `AGENTS.md`, `docs/**/*.md`, 현재 phase의
`index.json`과 `stepN-output.json`만 허용하며 실제 문서 변경이 있어야 생략한다.
rename은 원래 경로와 새 경로를 모두 검사한다. Git 실패, 빈 변경, bookkeeping만 있는
변경, untracked C# 또는 다른 파일이 하나라도 있으면 전체 검증으로 돌아간다.
symlink나 junction으로 해석되는 변경 경로도 보수적으로 전체 검증한다.
## 9. Commit, 완료와 재개
step 성공 시 executor는 제품 변경과 metadata를 분리해 commit한다. 모든 step이
성공하면 phase와 top-level 상태를 `completed`로 바꾸고 `--push`가 있으면
`origin/feat-{phase}`에 push한다. 실행기는 광범위한 staging을 할 수 있으므로 깨끗한
전용 worktree에서 실행한다.
재개 절차:
1. `error` 또는 `blocked` 원인을 해결한다.
2. 해당 step status를 `pending`으로 바꾼다.
3. `error_message`/`failed_at` 또는 `blocked_reason`/`blocked_at`을 제거한다.
4. handoff, 직전 원본 출력과 index를 확인한 뒤 다시 실행한다.
강제 종료로 handoff가 낡았을 수 있으므로 index 상태를 우선한다. 이 저장소의 fixture
검증은 제품 요구사항이나 실제 제품 build 완료를 의미하지 않는다. 실행한 명령과
결과, SDK·테스트 건수와 남은 미검증 항목은 해당 phase 기록에서 확인한다.