Files
FESADev/docs2/HARNESS.md
T
2026-09-05 15:14:21 +09:00

10 KiB

Harness Operations Guide

1. 역할과 현재 상태

Harness는 agent가 작성한 implementation Step을 독립 Codex session에서 실행하고, C/C++ 변경의 TDD guard, configured MSVC build/test, 상태와 local Git commit을 관리한다.

현재 repository에는 solver C++ source와 build metadata가 없다. 따라서 repository 자체에서는 Python Harness regression suite를 검증하며, C++ build 성공을 주장하지 않는다. Solver feature가 CMake 또는 Visual Studio metadata를 추가하면 Stop hook이 해당 project를 검증한다.

2. 요구 환경

  • Windows와 Python 3.11 이상
  • Git과 Codex CLI
  • Harness test 실행용 uv
  • C++ project를 검증할 때 Visual Studio Desktop development with C++, MSBuild, CMake/CTest

실제 toolchain, generator, configuration과 test command는 .harness/config.json 및 project build metadata가 source of truth다.

3. 설정

설정이 없으면 Harness는 자동 감지를 사용한다. Override가 필요할 때만 tracked example을 machine-local 설정으로 복사한다.

Copy-Item .harness/config.example.json .harness/config.json

.harness/config.json은 ignore되며 code와 같은 실행 가능한 신뢰 경계다. Preset과 test executable은 arbitrary child process를 실행할 수 있으므로 review 없이 외부에서 받은 설정을 사용하지 않는다.

지원하는 projectType은 auto, cmake, msbuild다. Unknown field, absolute/escaping path와 불완전한 preset은 거부된다. TDD pattern은 directory separator가 없는 safe basename이어야 하며 format field로 {stem}만 사용할 수 있다.

CMake

{
  "version": 1,
  "projectType": "cmake",
  "cmake": {
    "sourceDir": ".",
    "binaryDir": ".harness/build"
  }
}

binaryDir은 non-preset plan에서도 사용할 수 있다. Preset을 사용하면 configurePreset, buildPreset, testPreset, binaryDir를 모두 지정한다. 자동 감지에서도 명시된 cmake.sourceDir을 먼저 검사한다.

Preset이 없을 때의 기본 계획은 다음 형태다.

cmake -S <sourceDir> -B <binaryDir> -A x64
cmake --build <binaryDir> --config Debug
ctest --test-dir <binaryDir> -C Debug --show-only=json-v1
ctest --test-dir <binaryDir> -C Debug --output-on-failure

Harness는 compiler metadata가 MSVC인지, CTest가 한 개 이상의 test를 발견했는지도 확인한다.

Visual Studio/MSBuild

{
  "version": 1,
  "projectType": "msbuild",
  "msbuild": {
    "solution": "Solver.sln",
    "configuration": "Debug",
    "platform": "x64",
    "testCommand": ["build/tests/Debug/SolverTests.exe"]
  }
}

testCommand는 필수이며 첫 항목은 repository-relative executable path여야 한다. Bare PATH program, absolute path와 repository escape는 허용되지 않는다. 인수는 배열의 후속 항목으로 전달된다.

TDD path mapping

{
  "version": 1,
  "tdd": {
    "testRoots": ["tests", "integration-tests"],
    "testPatterns": ["{stem}_test.cpp", "test_{stem}.cpp"],
    "exclude": ["generated/**"]
  }
}

Project exclude는 Harness의 build/vendor/generated 기본 exclude에 추가된다. Production C/C++ 파일을 편집하려면 mapping되는 test 파일이 먼저 존재해야 한다.

4. 계획과 실행

Coordinator가 승인된 feature bundle로 planning/execution을 dispatch하면 routine local 작업을 위해 별도 사용자 승인을 반복하지 않는다. Planning Agent는 .agents/skills/harness/SKILL.md에 따라 phases/ index와 self-contained Step을 만든다.

Executor 시작 전 worktree는 선택한 phase의 선언된 control file 밖에서 깨끗해야 한다. 허용되는 control file은 top/phase index, phase index에 선언된 stepN.md와 해당 local raw output뿐이다.

git status --short
python scripts/execute.py <phase-name>

Executor는 다음을 수행한다.

  1. Safe phase name, repository/phase containment, top-level index, phase metadata, contiguous Step와 status order 검증
  2. Implementation profile, exact Git top-level, sandbox 권한과 strict worktree preflight
  3. feat-<phase> branch checkout/create
  4. Implementation Agent 역할, project guardrail, prior summary와 current Step 주입
  5. Current Step의 Codex session 실행과 최대 2회 transport attempt
  6. Exit code와 Step payload 검증
  7. Implementation path와 phase metadata의 path-scoped local commit
  8. 모든 Step 완료 시 phase closure

일반 implementation failure는 Executor가 반복 수정하지 않는다. Step을 error로 기록하고 Coordinator가 normalized retry_key와 correction budget으로 route한다.

5. Sandbox와 외부 변경

기본 sandbox는 workspace-write다. Windows native sandbox가 실제 compiler process를 막는 것이 재현된 경우에만 사용자가 해당 실행을 명시 승인하고 clean linked worktree에서 다음 두 조건을 함께 제공한다.

$env:FEM_HARNESS_CODEX_SANDBOX = "danger-full-access"
python scripts/execute.py <phase-name> --approve-danger-full-access
Remove-Item Env:FEM_HARNESS_CODEX_SANDBOX

Primary worktree, dirty worktree, 환경 변수만 설정한 실행은 거부된다. danger-full-access는 hook을 끄지 않지만 hook이 완전한 sandbox라는 의미도 아니다.

원격 push는 별도 critical action이다. 사용자가 정확한 branch push를 승인한 경우에만 실행한다.

python scripts/execute.py <phase-name> --push

Executor는 force push, history rewrite, tag, publish 또는 deploy를 수행하지 않는다.

6. Hook 동작

.codex/hooks.json은 Codex Hooks contract에 따라 PreToolUse와 Stop hook을 등록한다. Hook은 자동 실행하며 검증 대체를 위해 수동 호출하지 않는다.

PreToolUse

  • 위험한 Git/delete/database command pattern을 exit code 2로 차단한다.
  • Implementation session의 commit, push, branch/switch, tag, merge/rebase와 history 변경 Git command를 차단한다. 이 동작은 Executor 또는 별도 승인된 외부 작업만 소유한다.
  • 공식 apply_patch payload의 tool_input.command에서 patch path를 추출한다.
  • C/C++ production path에 대응 test가 없으면 편집을 차단한다.
  • Malformed/unknown edit payload는 fail closed한다.

Test path 존재 검사는 실제 RED, assertion 품질 또는 coverage를 증명하지 않는다.

Stop

  • Payload의 cwd에서 Git root를 찾고 재진입을 방지한다.
  • Config/discovery/toolchain/adapter plan을 전체 deadline 안에서 실행한다.
  • 실패 시 current Codex hook contract의 {"decision":"block","reason":"..."}을 반환해 같은 session이 수정하게 한다.
  • stop_hook_active 재진입은 즉시 허용한다.
  • Config argument의 control character를 거부하고 예상하지 못한 일반 예외도 block response로 변환한다.
  • C/C++ source와 build metadata가 모두 없으면 검증 target 없음으로 정상 종료한다.
  • C/C++ source만 있고 build metadata가 없으면 orphan project로 차단한다.

7. Git과 runtime artifact

Preflight 이후 생성된 implementation path만 feature commit에 stage한다. 각 commit 직전에 working tree는 보존한 채 index를 초기화한 다음 대상 path만 다시 stage하므로 agent가 미리 stage한 metadata가 feature commit에 섞이지 않는다. Selected phase index, Step과 timestamps는 별도 metadata commit에 stage한다. 관련 없는 pre-existing worktree 변경이나 phase 안의 미선언 파일이 있으면 branch checkout 전에 실행을 거부한다.

각 Codex attempt 전후에는 symbolic branch와 HEAD도 비교한다. Session이 자체 commit, checkout 또는 branch 변경으로 hook을 우회하면 결과를 신뢰하지 않고 current Step을 error로 고정한다.

Raw Codex stdout/stderr는 다음 local diagnostic file에 저장하지만 source control에는 포함하지 않는다.

phases/<phase>/step<N>-output.json

.gitignore는 위 파일, .harness/config.json, build output와 Python cache를 제외한다. Raw output은 credential을 포함할 수 있으므로 공유하거나 artifact로 게시하기 전에 검토한다. Control file은 non-link regular file로 읽고 phase/root containment를 매번 확인한다. Index, instruction 복구와 raw output write는 같은 directory의 임시 파일을 os.replace하는 atomic write를 사용해 symlink·hardlink target을 따라가 덮어쓰지 않는다.

8. 상태와 복구

Step status 필수 payload 다음 처리
pending 없음 Executor가 선택 가능
completed summary timestamp와 commits 후 다음 Step
error error_message Coordinator classification 전 중단
blocked blocked_reason 환경/권한/critical decision 해결 전 중단

Nonzero Codex exit는 Step이 completed를 썼더라도 신뢰하지 않는다. 두 transport attempt가 실패하면 error와 failed_at을 기록한다. 재개는 Coordinator가 owner와 evidence를 확인한 뒤 current Step을 pending으로 돌리는 방식으로 수행한다. Executor는 stale outcome timestamp를 정리한다.

Codex session 전후로 top-level index와 모든 stepN.md를 byte 비교하고 phase index의 current Step outcome 외 변경을 거부한다. Future Step, instruction 또는 control metadata가 바뀌면 snapshot을 복원하고 current Step을 trusted error로 기록하므로 다음 실행에서 조작된 완료 상태를 수락하지 않는다.

9. Harness 자체 검증

uv run --with pytest python -B -m pytest -v -rs
python -B scripts/execute.py --help

실제 Executor 기능 시험은 현재 repository에서 직접 실행하지 않는다. Branch/commit/Codex를 변경하기 때문이다. Regression suite가 temporary Git repository와 hook subprocess를 사용해 state, Git, payload와 failure behavior를 검증한다.

10. 한계

  • shell=False와 repository-relative executable은 shell injection을 줄이지만 configured executable/preset의 side effect를 격리하지 않는다.
  • PreToolUse는 shell redirection 등 모든 파일 쓰기를 완전하게 해석하지 않는다.
  • Stop pass는 configured target의 build/test 결과이며 formulation과 physics correctness를 대신하지 않는다.
  • External solver reference execution과 reference artifact 생성은 Harness 기본 권한 밖이다.