# Harness 운영 가이드 Harness는 Windows에서 C#·.NET 8 제품 개발의 step 실행, 편집 정책과 build/test를 지원하는 Python 도구다. 제품 목표와 기능은 [PRD](PRD.md)에 별도로 둔다. 상세 실행 흐름은 [HARNESS_WORKFLOW.md](HARNESS_WORKFLOW.md)를 참고한다. ## 요구 환경과 설정 설치, PATH·인증·훅 점검과 첫 검증은 [환경 구축 가이드](ENVIRONMENT_SETUP.md)를 따른다. - Windows, Git, 실제 `python` 명령으로 실행 가능한 Python 3.10 이상, 호환 Codex CLI - 루트 `global.json`을 만족하는 .NET 8 **SDK**와 NuGet restore 접근 - 신규 테스트 기본값 xUnit v2 + VSTest; IDE는 선택 사항 - Harness 자체 테스트에는 pytest/uv 사용 `global.json`의 기준은 SDK `8.0.424`, `latestPatch`, `allowPrerelease=false`다. SDK 선택과 `.csproj`의 제품 대상 TFM은 별개이며 신규 프로젝트 기본 TFM은 `net8.0`이다. 다음 경로는 실제 제품이 아닌 설정 예시다. `.harness/config.example.json`을 참고해 실제 제품에 맞는 `.harness/config.json`을 작성한다. 제품이 없는 템플릿에는 가상의 target을 넣은 설정 파일을 만들지 않는다. ```json { "version": 2, "projectType": "dotnet", "dotnet": { "target": "Example.sln", "configuration": "Debug", "testProjects": ["tests/Example.Tests/Example.Tests.csproj"] }, "tdd": { "testRoots": ["tests"], "testPatterns": ["{stem}Tests.cs", "{stem}Test.cs"], "exclude": [] } } ``` `projectType`는 `auto`(기본값) 또는 `dotnet`이다. `dotnet.target`은 선택 사항이며, 명시한 저장소 내부 `.sln` 또는 SDK 스타일 `.csproj`를 우선한다. 생략하면 루트의 단일 solution, 다음으로 제외 경로 밖의 단일 C# project를 찾는다. 후보가 여러 개면 target을 명시한다. C# 소스가 있지만 지원 metadata가 없으면 오류다. 제품이 있으면 `dotnet.testProjects`는 전체 제품 테스트를 담은 비어 있지 않은 명시 목록이어야 한다. `configuration`의 기본값은 `Debug`다. 모든 설정 경로는 저장소 상대 경로이며 외부 symlink/junction 대상, 없는 target, 알 수 없는 필드와 version 1은 거부한다. `tdd.testRoots`와 `testPatterns`를 명시하면 빈 배열을 허용하지 않는다. `tdd.exclude: []`는 유효하다. 테스트 패턴은 `{stem}`을 포함하는 `.cs` 파일명이며 경로 구분자를 넣지 않는다. SDK·TFM·패키지 버전을 Harness 설정에 중복하지 않는다. ## Phase 실행 ```powershell python -X utf8 scripts/execute.py --dry-run python -X utf8 scripts/execute.py python -X utf8 scripts/execute.py --push ``` 먼저 `--dry-run`으로 첫 pending step이 읽을 파일과 프롬프트 문자 수를 확인한다. 이 모드는 Codex 호출, branch 전환, timestamp, 파일 쓰기, commit과 push를 하지 않는다. `--push`와 함께 사용할 수 없다. 실제 실행은 자동 staging·commit을 수행하므로 검토한 기준 commit이 있는 전용 checkout/worktree에서 시작한다. ## 선택 문맥과 호환성 step index의 `context_files`에 필요한 기존 문서를 저장소 상대 경로로 적는다. ```json { "step": 1, "name": "implement-parser", "status": "pending", "context_files": ["docs/ARCHITECTURE.md"] } ``` 필드를 생략하거나 빈 배열로 두면 `AGENTS.md`, 자동 인계와 step 이외의 문서는 추가되지 않는다. glob, URL, 절대 경로와 저장소 밖 경로는 지원하지 않는다. 이전에 모든 문서를 자동 주입하던 동작에 의존하는 phase는 필요한 문서를 명시한다. 현재 phase의 `docs/phase/{phase}/handoff.md`가 있으면 매 호출 다시 읽는다. 없을 때만 현재 step보다 앞선 완료 step 중 가장 최근 하나의 summary를 최대 1,000자로 전달한다. 상세 기록 형식은 [phase README](phase/README.md)를 따른다. ## 검증 모드 ```json {"step": 2, "name": "update-docs", "status": "pending", "validation": "docs-only"} ``` `validation` 생략 또는 `full`은 전체 제품 검증을 수행한다. `docs-only`도 실제 Git 변경이 루트 `AGENTS.md`, `docs/**/*.md`, 현재 phase의 `index.json`과 `stepN-output.json`에만 있고 실제 문서 변경이 있을 때만 제품 검증을 생략한다. Git 조회 실패, 빈 변경, bookkeeping만 있는 변경, 다른 코드·설정·스킬 파일이 섞인 변경은 전체 검증으로 돌아간다. rename은 이전·새 경로를 모두 확인하며 symlink나 junction 경로는 문서 전용으로 인정하지 않는다. 제품 build가 Markdown을 입력으로 사용하면 `full`을 선택한다. ## .NET 검증 Stop 훅은 `scripts/dotnet_harness/`를 통해 저장소 루트에서 다음 순서로 검증한다. 1. SDK 선택 확인. 2. target·test project와 평가된 `ProjectReference`로 연결되는 모든 프로젝트의 저장소 내부 경로·C# SDK 형식·실제 .NET 8 TFM 확인. 3. target과 모든 test project의 중복 경로를 제거한 뒤 같은 Configuration으로 각각 restore. 4. 각 대상을 `--no-restore -warnaserror`로 build. 5. 각 test project를 `--no-build --no-restore --logger trx`로 실행. 6. 현재 호출의 project별 고유 결과 디렉터리에 생성된 모든 TRX 검사. TFM과 참조 확인은 각 `.csproj`에 `dotnet msbuild -getProperty:TargetFramework,TargetFrameworks -getItem:ProjectReference -p:Configuration=`을 실행한다. 다중 TFM은 각 `TargetFramework`의 조건부 참조도 평가한다. 프로젝트 XML의 문자만 확인하지 않고 상속된 props와 속성 참조를 평가한 결과를 검사한다. 값이 없거나 .NET 8 이외의 TFM이 포함되면 restore 전에 실패한다. `net8.0`과 .NET 8의 플랫폼별 TFM을 허용한다. 참조별 `AdditionalProperties`, `GlobalPropertiesToRemove`, `SetConfiguration`, `SetPlatform`, `SetTargetFramework`로 평가 문맥을 바꾸는 구성은 현재 지원하지 않으며 명확한 오류로 거부한다. 프로젝트는 공유 Configuration과 각 프로젝트의 TFM을 사용한다. solution에 빠진 test project도 직접 restore/build한다. 한 단계라도 실패하면 이후 단계를 멈춘다. 전체 deadline은 1,800초이며 명령은 shell 확장 없이 argv 배열로 실행한다. NuGet 오류는 restore 실패로 보고한다. 실패·손상·누락된 TRX, 실행된 테스트 0건, skip-only 결과는 검증 실패다. 여러 결과 중 하나라도 실패하면 성공 결과로 덮지 않는다. 오래된 TRX는 현재 실행의 근거가 아니다. 기본 xUnit 이외의 기존 테스트도 VSTest/TRX 계약을 만족해야 한다. C# 제품과 지원 metadata가 모두 없는 템플릿은 `제품 검증 대상 없음`으로 구분한다. `tests/fixtures/`는 도구 검증용이므로 제품 자동 탐색에서 제외한다. 대상 없음과 docs-only 생략을 제품 빌드 성공으로 기록하지 않는다. ## TDD와 복구 `Foo.cs` 편집에는 기본 test root인 저장소 `tests/`와 편집 파일 옆 `tests/`, `test/`에서 `FooTests.cs` 또는 `FooTest.cs`가 먼저 있어야 한다. 설정으로 테스트 루트·패턴을 조정할 수 있다. 테스트 루트의 보조 코드, 중첩된 `bin/`, `obj/`, `.git/`, `.vs/`, `.harness/`, `TestResults/`, 외부 의존성·명시 generated 경로는 정책에서 제외한다. `Program.cs`, DTO, interface, `.Designer.cs`는 파일명만으로 면제하지 않는다. 필요하면 예를 들어 `src/Example/Program.cs`처럼 정확한 `tdd.exclude` 경로와 composition root 등 실제 이유를 기록한다. 이 설정은 편집 전 테스트 요구만 면제하며 제품 존재 탐색을 숨기지 않는다. 수동 작성 partial 파일은 각 stem에 대응 테스트가 필요하다. 파일 존재 검사는 테스트 품질이나 실제 red-green 실행을 대신하지 않는다. Executor는 Codex를 최초 호출 포함 최대 3회 호출한다. 재시도 입력은 marker를 포함해 4,000자로 제한하며 앞 1,500자와 끝부분 최대 2,500자를 보존한다. 원본 `stepN-output.json`은 온전히 저장되지만 다음 시도에서 덮어쓴다. exit code 0과 `completed` 상태와 executor의 독립 제품 검증 성공이 모두 있어야 성공한다. Stop 실패도 CLI exit 0으로 끝날 수 있어 executor가 완료 보고 후 검증을 다시 실행한다. 독립 검증은 별도의 1,800초 제한을 공유하며 실패하면 같은 호출 예산 안에서 재시도한다. `docs-only` 판정도 실제 변경을 다시 확인한다. `blocked`는 즉시 멈춘다. `error` 또는 `blocked`를 재개할 때는 원인을 해결하고 step을 `pending`으로 바꾼 뒤 오류 이유와 해당 timestamp를 제거한다. 인증이나 CLI 환경 오류는 즉시 중단한다. Harness 자체 Python 검증: ```powershell uv run --with pytest python -m pytest tests/harness -v -rs uv run --with pytest python -m compileall -q scripts tests/harness ``` 실제 SDK 통합 테스트의 skip과 subprocess 대역 테스트 성공을 구분한다. 검증한 OS·SDK·패키지 버전, 실행 건수, 경고와 남은 미검증 항목은 phase 기록에 남긴다.