Files
FESADev/docs/mitc4-shell-feast-eigen/implementation-environment.md
T
2026-09-06 18:00:09 +09:00

930 lines
63 KiB
Markdown

# 구현 환경·실행시간·evidence 운영 계획
- Feature ID: `mitc4-shell-feast-eigen`
- Owner: `implementation-planning-agent`
- Input contract revision: `r2`
- 기본 보고서: [implementation-plan](implementation-plan.md)
E1/E2의 현재 값과 실행 전 표현은 최초 계획 시점 기록이다. Prep attempt1 종료 뒤 네 runtime
상수만 부분 변경되었고 Hook1800은 남았다. [복구 계획](implementation-recovery.md)의 WIP
보존·명시 승인·status-only checkpoint가 실제 완료되기 전 재실행하거나 main을 활성화하지 않는다.
실제 source echo의 classifier 오판도 prep의 최소 TDD 범위다. 이미 import된 Executor에는
Codex의 classifier source 수정이 자동 반영되지 않는다. 새 process activation과 재차 terminal
차단의 처리 경계는 [복구 계획](implementation-recovery.md)을 따른다.
## E1. 실제 backend와 preflight
[현재 환경 evidence](coordination.md#후속-실행-준비-관찰)를 적용한다. 이 계획 단계에서는
Harness, CMake/CTest, C++ 또는 FEAST를 실행하지 않는다. Root의 과거 관찰은 신규 gate 성공이 아니다.
| 선택 | 실제 값 |
| --- | --- |
| Repository | linked worktree, `C:/Users/baram/orca/workspaces/FESADev/Eigen`; Git top-level과 Harness ROOT 동일 |
| Baseline | I/O checkpoint `1d4f5ac`; 사용자 branch `Eigen`에서 계획 작성 |
| Harness config | `.harness/config.json` 없음; autodetect CMake |
| Build | Visual Studio 18 2026 / MSVC / x64 / Debug / `.harness/build` |
| GoogleTest | `C:/git/googletest` |
| MKL_DIR | `C:/Program Files (x86)/Intel/oneAPI/mkl/2026.1/lib/cmake/mkl` |
| TBB_DIR | `C:/Program Files (x86)/Intel/oneAPI/tbb/2023.1/lib/cmake/tbb` |
| HDF5_DIR | `C:/Program Files/HDF_Group/HDF5/2.1.1/cmake` |
| Observed MKL configuration | 2026.1 / dynamic / intel_ilp64 / intel_thread; 실제 FEAST 실행에서 version/ABI를 다시 기록 |
| Python / CLI | Python3.11.15; python/uv/cmake/ctest/codex resolved. Codex transport/login은 아직 실행 검증 전 |
| Executor role | `.codex/agents/implementation-agent.toml`, gpt-5.6-terra / xhigh / workspace-write |
개인 설치 경로를 C++/CMake source에 고정하지 않는다. 아래는 이 workstation의 local cache
구성 명령이며 이미 Root가 exit0으로 확인했다. 실제 실행 전 cache의 경로/compilers/platform을
다시 읽고 stale/missing이면 동일 환경 준비 명령을 실행한다.
```powershell
cmake -S . -B .harness/build -A x64 "-DFESA_GTEST_SOURCE_DIR=C:/git/googletest" "-DMKL_DIR=C:/Program Files (x86)/Intel/oneAPI/mkl/2026.1/lib/cmake/mkl" "-DTBB_DIR=C:/Program Files (x86)/Intel/oneAPI/tbb/2023.1/lib/cmake/tbb" "-DHDF5_DIR=C:/Program Files/HDF_Group/HDF5/2.1.1/cmake"
```
다음부터는 Step의 unadorned configure/build/CTest 명령을 사용한다. Preset은 dependency
환경변수4개를 참조하므로 현재 미설정된 부모 환경에서 preset을 호출하는 계획은 선택하지 않는다.
VS generator는 CMake가 MSVC를 선택하고 `--config Debug`/`-C Debug`로 검증한다.
필수는 CMake/MSVC/x64/Debug이며 Ninja 예시 자체가 필수는 아니다. Hook도 같은 absent-config
autodetect를 사용한다. Subprocess 환경을 부모 Stop에 전파했다고 가정하지 않는다.
## E2. 최소 deadline 변경과 근거
Actual code의 다음 모두가 유한 제한을 적용한다. 지원되는 CLI/config timeout option은 없다.
| 경계 | 현재 | prep 후 |
| --- | ---: | ---: |
| StepExecutor Codex wrapper | 1800 | 14400 |
| 등록 Stop outer | 1800 | 7500 |
| Stop discovery/configure/build/discovery/CTest shared | 1800 | 7200 |
| CommandSpec default per-command | 1800 | 7200 |
| execute_plan의 명시 deadline 없는 default shared | 1800 | 7200 |
전체 regression duration의 기존 합산 추정은1672.67초(약27.9분)다. 이는 여러 run의 값이며
단일 full suite wall time/pass가 아니다. 추가 eigensolver tests와 fresh reference 비용은
미측정이다. 7200초는 기존 약28분 검증에 초기 실행/신규 numerical tests 여유를 두되 유한하게
중단하는 운영 상한이다. Registered7500은 shared7200 종료/진단을 위한300초 여유,
wrapper14400은 coding·RED/GREEN·focused·수동 full VERIFY와 등록 Stop을 함께 감싼다.
상한을 더한 값을 실제 소요 시간이라고 보고하지 않는다.
Main은8개 Step이다. 각 Step은 하나의 완결된 API/data boundary를 담당하고 동일 session에서
작은 behavior별 RED/GREEN을 반복한다. 통상 coding/focused 약2시간 안, 수동 full과 Stop 각
약30~45분을 고려한 분해다. 이는 실행 예산 설계이며 소요 보장이나 시험 결과가 아니다.
예상보다 늦거나 failure이면 terminal evidence를 남기고 Coordinator가 원인을 분류한다.
Timeout을 무한대로 만들거나 test/Hook를 생략하지 않는다.
14개 초기 소단위를8개로 합쳐 불필요한 전체 검증 반복6회를 줄였다. Model+parser는 같은
frequency/density 정의, consistent+lumped+assembly는 같은 moment snapshot, call+search는
하나의 FEAST result/history, validation+procedure는 한 candidate, writer+CLI는 한 transaction,
comparator+fresh execution은 한 source matching 결과를 완성한다. Stabilization과 reduction은
각각 기존 static 영향과 analytic positive/null 경계라 독립 Step으로 유지한다.
## E3. One-time bootstrap와 새 process activation
일반1800초 invocation은 prep 중 source self-edit로 이미 시작된 wrapper의 timeout을 늘리지
못한다. Pressure 일부는 same-binary HDF5 cache를 사용하지만
`RunFesaApplicationOnCopiedInput()`은 nonlinear reference solver를 매번 직접 호출한다.
따라서 full VERIFY+registered Stop을 모두 cached/빠르다고 가정하지 않는다.
Root와 Coordinator가 사용자 승인된 가역적 운영 준비로 해석한 **one-time module invocation**을
prep에만 사용한다. 기존 CLI option이 아니다. 실제 module을 import하고 한 유한 class timeout만
설정한 뒤 unchanged `main()`을 호출하므로 기존 argparse/preflight/branch/ownership/sandbox/
terminal result/transport retry/commit/finalization 경로를 그대로 실행한다.
```powershell
python -B -c "from scripts import execute; execute.StepExecutor.CODEX_TIMEOUT_SECONDS = 14400; execute.main()" mitc4-shell-feast-eigen-execution-prep
```
Bootstrap은 Hook state/reentry flag/execution method를 override하지 않는다. Prep에서
`.codex/hooks.json`을 수정해도 이미 launch된 Codex의 registered Stop outer1800이 유지될
수 있다. 그 세션에 새7500이 반영됐다고 단정하지 않는다. Prep의 actual manual full/registered
Stop/Codex exit와 source/control commits를 확인해야 한다. Stop이 실제 cap을 넘거나 실패하면
원시 log와 terminal 상태를 보존한 채 환경 분류로 반환한다. 성공으로 덮어쓰기/즉시 재실행하지 않는다.
Prep Executor가 **정상 종료하고 Step0 completed·Codex exit0·등록 Stop 성공·정확 경로 commits·
clean 및 Coordinator의 사후 감사 완료**를 확인한 뒤 새 process에서 정상 main invocation을 실행한다.
```powershell
python scripts/execute.py mitc4-shell-feast-eigen
```
새 Executor는 source14400을 새로 읽고 새 Codex session은 변경된 등록7500/Stop7200/command7200
구성으로 시작한다. Source상 cap과 실제 검증 evidence를 함께 기록한다. Prep/main은
`feat-mitc4-shell-feast-eigen-execution-prep`와 `feat-mitc4-shell-feast-eigen` branch를
Executor가 차례로 소유한다. Main branch는 prep 완료 HEAD를 포함해야 한다.
## E4. Git와 활성 실행의 경계
1. Coordinator가 계획 gate를 감사한 뒤 Root가 완료 Feature 문서/계획 및 두 phase 제어 파일의
정확 목록을 checkpoint한다. 두 phase를 만들었으므로 선택되지 않은 phase files까지 미리
commit해 selected-phase allowlist 밖 dirty 파일이 없게 한다. 기존 unrelated pending beam
phase/history는 보존하며 invocation 대상으로 삼지 않는다.
2. 실행 직전 `git rev-parse --show-toplevel`, `git status --porcelain=v1 --untracked-files=all`,
actual role/config를 읽는다. Linked worktree와 Harness ROOT 동일성을 확인한다.
3. Active Executor 동안 Root/Coordinator/다른 specialist는 non-owned edits/commits를 멈춘다.
Implementation은 선택 Step paths와 해당 Step result fields만 수정한다. Commit/branch/
timestamp/attempt_history/phase 진전은 Executor 소유다.
4. Terminal failure는 raw payload·history·logs를 보존한다. Coordinator의 assigned recovery가
없으면 pending reset/재시작하지 않는다. Correction budget과 transport retry는 별개다.
5. Default workspace-write를 사용한다. Danger-full-access는 같은 native command의 실제 실패,
해당 실행 직전 사용자 명시 승인, clean linked worktree, 승인 flag를 모두 요구한다.
이번 timeout 준비는 sandbox 확대 권한이 아니다. Remote push/tag/publish/deploy/package는 없다.
## E5. 실제 실행 evidence 보존
각 Step raw evidence root는 ignored `.harness/build/mitc4-eigen-evidence/stepN/`, prep은
`.../prep/`다. `New-Item -ItemType Directory -Force`와 `Copy-Item -LiteralPath`는 해당
resolved workspace 하위 generated 경로만 대상으로 한다. Recursive delete/move는 계획하지 않는다.
긴 command는 managed process/session으로 실제 exit까지 관찰하며 약60초마다 Coordinator에
의미 있는 진행을 보낸다. Detached/unobserved execution으로 deadline을 피하지 않는다.
RED/focused/full command stdout/stderr·actual exit·wall time·CTest discovery JSON·LastTest.log를
각각 고유 이름으로 보존한다. Tee/pipeline 사용 시 `$LASTEXITCODE`를 즉시 보존한다.
CTest observer shell exit0을 CTest exit0으로 기록하지 않는다. Hook state를 조작하거나
수동 hook 실행으로 evidence를 만들지 않는다. 등록 Stop 이후의 실제 실패는 manual full pass로
덮어쓰지 않는다. Implementation 보고 시점의 실제 manual evidence와 마지막 Stop/exit 확인 대기를 구분하며 미래 성공을 보고서에 쓰지 않는다. Executor 종료 뒤 Coordinator가 raw output·등록 Hook evidence·actual exit를 management 문서에서 감사한다. Coordinator는 specialist 보고서 본문을 대신 수정하지 않는다. Prep→main activation은 이 사후 감사까지 완료된 뒤에만 가능하다.
C++ source 변경 없는 prep의 same-binary 회귀와 새 binary의 fresh eigen/static 비교는 다른
evidence다. Final Step7는 직접 solver invocation·unique generated directory·원본 bytes/hash·
binary/lib hash·result/log mtime/hash·call count를 남겨 freshness를 증명한다. 기존 helper가
파일 존재/mtime로 cache를 사용한 사실만으로 fresh라고 주장하지 않는다.
## E6. Correction 후의 Python/pytest와 process TEMP
[Correction 실제 기록](corrections.md)의 named venv `pytest-venv/Scripts/python.exe`는
Python3.11.15/pytest9.1.1로 native GREEN37개와 전체153개 collection/실행에 성공했다.
아래 모든 경로의 기준은 workspace의
`.harness/build/mitc4-eigen-evidence/prep/classifier-correction/`다.
| 용도 | 확인된 상대 경로 |
| --- | --- |
| 직접 pytest 및 후속 Executor Python | `pytest-venv/Scripts/python.exe` |
| venv의 base runtime | `python-runtime/cpython-3.11.15-windows-x86_64-none/python.exe` |
| uv의 workspace cache | `workspace-python-cache` |
| pytest cache | `pytest-cache` |
Native에서는 direct venv entry를 사용했다. Root의 uv offline 진입도 별도로 관찰됐지만
그 사실을 native uv 실행 성공으로 합치지 않는다. 후속 process의 PATH 앞에 named venv와
base directory를 두고 UV_PYTHON/UV_CACHE_DIR/UV_OFFLINE을 정상 설정하여 child의 기본
python/uv에도 전파한다. Global config·source·Hook command/guard는 바꾸지 않는다.
[설치된 FileTracker IL 관찰](../../.harness/build/mitc4-eigen-evidence/prep/classifier-correction/filetracker-initialization-observation.json)의
SHA-256은 `8d47f96ba913a9adc2d89772cef4b27ea2187f613416912d894608be9730c5cc`다.
Initializer의 Path.GetTempPath→s_tempPath→GetLongFilePath 유일 호출과 실제 접근 거부
stack이 대응하므로 새 workspace TEMP/TMP가 정상 운영 개선 후보라는 근거가 있다.
**이 변경으로 native build가 성공한 evidence는 아직 없다.** FileTracker를 끄거나 DFA가
build 실패까지 해결한다고 추정하지 않는다.
아래 전체 block은 [R6 source checkpoint](implementation-recovery.md#r6-실제-correction-source2의-보존과-남은-실행-조건),
R3 status-only checkpoint와 **해당 exact prep DFA run 직전 명시 사용자 승인** 후에만 실행한다.
이 turn에는 어느 것도 실행하지 않는다. 새 ignored TEMP 경로는 workspace 안에서 생성하고
기존 temp/evidence를 지우지 않는다. 기존 finite wrapper14400 및 모든 Executor guard를 유지한다.
Process 환경은 native 종료/오류 때 finally로 원래 값(미설정 포함)으로 복원한다.
```powershell
$ErrorActionPreference = 'Stop'
$mfeRoot = (Get-Location).Path
$mfeEvidence = Join-Path $mfeRoot '.harness/build/mitc4-eigen-evidence/prep/classifier-correction'
$mfePython = Join-Path $mfeEvidence 'pytest-venv/Scripts/python.exe'
$mfeBasePython = Join-Path $mfeEvidence 'python-runtime/cpython-3.11.15-windows-x86_64-none/python.exe'
$mfeUvCache = Join-Path $mfeEvidence 'workspace-python-cache'
foreach ($mfeFile in @($mfePython, $mfeBasePython)) {
if (-not (Test-Path -LiteralPath $mfeFile -PathType Leaf)) { throw "Missing Python: $mfeFile" }
}
Get-Command uv -ErrorAction Stop | Out-Null
$mfeGitRoot = git rev-parse --show-toplevel
$mfeIdentityExit = $LASTEXITCODE
if ($mfeIdentityExit -ne 0 -or [IO.Path]::GetFullPath($mfeGitRoot) -ne [IO.Path]::GetFullPath($mfeRoot)) {
throw 'Workspace root mismatch'
}
$mfeRunTemp = Join-Path $mfeEvidence ('native-temp-' + [guid]::NewGuid().ToString('N'))
if (-not [IO.Path]::GetFullPath($mfeRunTemp).StartsWith([IO.Path]::GetFullPath($mfeEvidence) + [IO.Path]::DirectorySeparatorChar)) {
throw 'Temp path outside approved workspace evidence'
}
$null = New-Item -ItemType Directory -Path $mfeRunTemp
$mfeEnvNames = @('PATH', 'UV_PYTHON', 'UV_CACHE_DIR', 'UV_OFFLINE', 'TEMP', 'TMP', 'FEM_HARNESS_CODEX_SANDBOX')
$mfeBeforeEnv = @{}
foreach ($mfeName in $mfeEnvNames) {
$mfeBeforeEnv[$mfeName] = [Environment]::GetEnvironmentVariable($mfeName, 'Process')
}
if ($null -ne $mfeBeforeEnv['FEM_HARNESS_CODEX_SANDBOX'] -and $mfeBeforeEnv['FEM_HARNESS_CODEX_SANDBOX'] -ne 'workspace-write') {
throw 'Unexpected pre-existing sandbox environment'
}
$mfeNativeExit = $null
try {
$env:PATH = (Split-Path $mfePython) + ';' + (Split-Path $mfeBasePython) + ';' + $mfeBeforeEnv['PATH']
$env:UV_PYTHON = $mfePython
$env:UV_CACHE_DIR = $mfeUvCache
$env:UV_OFFLINE = 'true'
$env:TEMP = $mfeRunTemp
$env:TMP = $mfeRunTemp
$env:FEM_HARNESS_CODEX_SANDBOX = 'danger-full-access'
& $mfePython -B -X utf8 -c "from scripts import execute; execute.StepExecutor.CODEX_TIMEOUT_SECONDS = 14400; execute.main()" mitc4-shell-feast-eigen-execution-prep --approve-danger-full-access
$mfeNativeExit = $LASTEXITCODE
if ($null -eq $mfeNativeExit) { throw 'Native Executor exit unavailable' }
}
finally {
foreach ($mfeName in $mfeEnvNames) {
[Environment]::SetEnvironmentVariable($mfeName, $mfeBeforeEnv[$mfeName], 'Process')
}
}
exit $mfeNativeExit
```
재개 Implementation은 inherited 환경을 확인한 뒤 pytest entry를 아래 direct venv 방식으로
사용한다. 기존 Step0의 대상/모든 assertions를 유지하며 collection에서3개 classifier test의
6/16/15 parameter inventory를 확인한다. 각 **호출마다 새** basetemp를 만들어 pytest가 기존
evidence directory를 정리하지 않게 한다. 이전 GREEN은 새 RED가 아니다.
```powershell
$ErrorActionPreference = 'Stop'
$mfeEvidence = Join-Path (Get-Location).Path '.harness/build/mitc4-eigen-evidence/prep/classifier-correction'
$mfePython = Join-Path $mfeEvidence 'pytest-venv/Scripts/python.exe'
$mfePytestRuns = @(
@{ label = 'classifier'; targets = @('tests/test_execute.py', '-k', 'environment_classifier or successful_source_echo') },
@{ label = 'focused'; targets = @('tests/test_harness_timeouts.py', 'tests/test_execute.py', 'tests/test_execute_ownership.py', 'tests/test_hook_validation_scope.py', 'tests/test_harness_preset_contract.py') },
@{ label = 'full'; targets = @() }
)
foreach ($mfeRun in $mfePytestRuns) {
$mfeFreshBase = Join-Path $mfeEvidence ('pytest-' + $mfeRun.label + '-' + [guid]::NewGuid().ToString('N'))
$mfeCache = Join-Path $mfeEvidence 'pytest-cache'
$mfeTargets = $mfeRun.targets
& $mfePython -B -X utf8 -m pytest -v -rs -o "cache_dir=$mfeCache" "--basetemp=$mfeFreshBase" @mfeTargets
$mfePytestExit = $LASTEXITCODE
Write-Output ("pytest {0}: exit {1}; basetemp {2}" -f $mfeRun.label, $mfePytestExit, $mfeFreshBase)
if ($null -eq $mfePytestExit -or $mfePytestExit -ne 0) { throw 'pytest verification failed' }
}
```
CMake/MSVC/x64/Debug configure/build와 전체 CTest discovery/run은 기존 Step0의 정확한
명령을 같은 inherited TEMP/TMP에서 별도로 실행한다. 실패 시 actual command/exit와 남은
검증을 기록한다. Registered Stop을 수동 full 검증으로 대체하지 않으며 원 보고서 갱신은
Implementation owner, 종료 뒤 사후 감사는 Coordinator 소유다.
## E7. 짧은 TEMP를 사용하는 정상 재개
E6의 긴 TEMP 이름은 attempt2의
`ShellSurfacePressureApplication.InputWithoutPressureRetainsExistingNonlinearBehavior`
실패를 재현했다. [같은 binary의 짧은 경로 비교](../../.harness/build/mitc4-eigen-evidence/prep/attempt2/path-length-probe-e15c1747/observation.json)
SHA-256은 `511b30e183759b378e6f33f30aa3be4c940b58d1eabde12068151173a0a91979`다.
TEMP152에서 만들어진 result251/lock261과 실패1, TEMP70에서 실제 생성된 lock179와 성공0을
구분한다. 새 `.harness/build/t-<8hex>`는 이 workspace에서70자이며 **TEMP80자 이하**를
검사한다. 관찰된 해당 fixture suffix109자를 더하면 lock189자 이하라는 운영 여유다.
이는 모든 미래 fixture의 길이 보장이 아니므로 기존 전체 suite를 실제 수행한다.
Attempt2의 focused도 처음에는 긴 pytest basetemp에서130pass/6fail(exit1)이었고,
같은136대상을 짧은 `.harness/build/mfe-focus-<8guid>`로 바꾼 뒤136pass(exit0)였다.
확인된 named venv/base/UVoffline/cache와 direct pytest entry/대상은 유지하되 TEMP/TMP와
pytest basetemp를 모두 짧게 만든다. Native child와
registered Stop도 같은 process 환경을 상속한다. 기존 temp/evidence 삭제·test skip·FileTracker
비활성화·source/reference 변경은 없다. Full pytest는 attempt2에서 미실행이므로 필수로 남는다.
[R7 WIP 및 별도 status checkpoint](implementation-recovery.md#r7-attempt2-실패-보존과-정상-workspace-write-재개)
완료 뒤 Coordinator가 아래 정상 workspace-write invocation을 할당한다. 추가 사용자
승인이나 DFA flag는 없다. 오류·정상 종료 모두 환경을 finally에서 원래 값으로 복원하고
native exit를 즉시 전파한다. 유한14400과 기존 registered Stop7500/shared7200은 유지한다.
```powershell
$ErrorActionPreference = 'Stop'
$mfeRoot = (Get-Location).Path
$mfePhase = 'mitc4-shell-feast-eigen-execution-prep'
$mfeEvidence = Join-Path $mfeRoot '.harness/build/mitc4-eigen-evidence/prep/classifier-correction'
$mfePython = Join-Path $mfeEvidence 'pytest-venv/Scripts/python.exe'
$mfeBasePython = Join-Path $mfeEvidence 'python-runtime/cpython-3.11.15-windows-x86_64-none/python.exe'
$mfeUvCache = Join-Path $mfeEvidence 'workspace-python-cache'
foreach ($mfeFile in @($mfePython, $mfeBasePython)) {
if (-not (Test-Path -LiteralPath $mfeFile -PathType Leaf)) { throw "Missing Python: $mfeFile" }
}
Get-Command uv -ErrorAction Stop | Out-Null
$mfeGitRoot = git rev-parse --show-toplevel
$mfeIdentityExit = $LASTEXITCODE
if ($mfeIdentityExit -ne 0 -or [IO.Path]::GetFullPath($mfeGitRoot) -ne [IO.Path]::GetFullPath($mfeRoot)) {
throw 'Workspace root mismatch'
}
$mfeTempParent = Join-Path $mfeRoot '.harness/build'
$mfeRunTemp = Join-Path $mfeTempParent ('t-' + [guid]::NewGuid().ToString('N').Substring(0, 8))
if ($mfeRunTemp.Length -gt 80) { throw 'TEMP exceeds the approved 80-character budget' }
if (-not [IO.Path]::GetFullPath($mfeRunTemp).StartsWith([IO.Path]::GetFullPath($mfeTempParent) + [IO.Path]::DirectorySeparatorChar)) {
throw 'Temp path outside approved workspace evidence'
}
$null = New-Item -ItemType Directory -Path $mfeRunTemp
$mfeEnvNames = @('PATH', 'UV_PYTHON', 'UV_CACHE_DIR', 'UV_OFFLINE', 'TEMP', 'TMP', 'FEM_HARNESS_CODEX_SANDBOX')
$mfeBeforeEnv = @{}
foreach ($mfeName in $mfeEnvNames) {
$mfeBeforeEnv[$mfeName] = [Environment]::GetEnvironmentVariable($mfeName, 'Process')
}
if ($null -ne $mfeBeforeEnv['FEM_HARNESS_CODEX_SANDBOX'] -and $mfeBeforeEnv['FEM_HARNESS_CODEX_SANDBOX'] -ne 'workspace-write') {
throw 'Unexpected pre-existing sandbox environment'
}
$mfeNativeExit = $null
try {
$env:PATH = (Split-Path $mfePython) + ';' + (Split-Path $mfeBasePython) + ';' + $mfeBeforeEnv['PATH']
$env:UV_PYTHON = $mfePython
$env:UV_CACHE_DIR = $mfeUvCache
$env:UV_OFFLINE = 'true'
$env:TEMP = $mfeRunTemp
$env:TMP = $mfeRunTemp
$env:FEM_HARNESS_CODEX_SANDBOX = 'workspace-write'
& $mfePython -B -X utf8 -c "from scripts import execute; execute.StepExecutor.CODEX_TIMEOUT_SECONDS = 14400; execute.main()" $mfePhase
$mfeNativeExit = $LASTEXITCODE
if ($null -eq $mfeNativeExit) { throw 'Native Executor exit unavailable' }
}
finally {
foreach ($mfeName in $mfeEnvNames) {
[Environment]::SetEnvironmentVariable($mfeName, $mfeBeforeEnv[$mfeName], 'Process')
}
}
exit $mfeNativeExit
```
현재 block은 미실행이다. Prep 성공·full manual VERIFY·actual registered Stop/CTest exit와
Coordinator 사후 감사 뒤 main을 별도 할당할 때는 같은 block의 `$mfePhase` 값만
`mitc4-shell-feast-eigen`으로 지정한다. Main도 workspace-write이며 해당8개 Step의
실제 C++ TDD/focused/full/reference gate를 그대로 수행한다. Hook7500이 이미 적용됐다는
사실이나 단독 경로 재현 성공을 전체 prep/main 성공으로 보고하지 않는다.
현재 pytest의 정확한 recipe는 아래다. E6의 긴 basetemp 예시는 과거 이력으로 남기고,
각 호출마다 새 `.harness/build/p-<8guid>`(현재70자, 상한80자)를 사용한다. 기존 directory를
재사용하거나 지우지 않는다. 같은 tests/assertions와 실제 exit 확인을 유지한다.
```powershell
$ErrorActionPreference = 'Stop'
$mfeBuild = Join-Path (Get-Location).Path '.harness/build'
$mfeEvidence = Join-Path $mfeBuild 'mitc4-eigen-evidence/prep/classifier-correction'
$mfePython = Join-Path $mfeEvidence 'pytest-venv/Scripts/python.exe'
$mfePytestRuns = @(
@{ label = 'classifier'; targets = @('tests/test_execute.py', '-k', 'environment_classifier or successful_source_echo') },
@{ label = 'focused'; targets = @('tests/test_harness_timeouts.py', 'tests/test_execute.py', 'tests/test_execute_ownership.py', 'tests/test_hook_validation_scope.py', 'tests/test_harness_preset_contract.py') },
@{ label = 'full'; targets = @() }
)
foreach ($mfeRun in $mfePytestRuns) {
$mfeFreshBase = Join-Path $mfeBuild ('p-' + [guid]::NewGuid().ToString('N').Substring(0, 8))
if ($mfeFreshBase.Length -gt 80 -or (Test-Path -LiteralPath $mfeFreshBase)) { throw 'Pytest basetemp must be short and new' }
$mfeCache = Join-Path $mfeEvidence 'pytest-cache'
$mfeTargets = $mfeRun.targets
& $mfePython -B -X utf8 -m pytest -v -rs -o "cache_dir=$mfeCache" "--basetemp=$mfeFreshBase" @mfeTargets
$mfePytestExit = $LASTEXITCODE
Write-Output ("pytest {0}: exit {1}; basetemp {2}" -f $mfeRun.label, $mfePytestExit, $mfeFreshBase)
if ($null -eq $mfePytestExit -or $mfePytestExit -ne 0) { throw 'pytest verification failed' }
}
```
## E8. Attempt3 후의 정상 backend 대안
이 절은 R8 보존 뒤의 **현재 backend 명령 계약**이다. E1~E7의 당시 실행·실패 기록은 유지한다.
[R8 실제 checkpoint 결과](../../.harness/build/mitc4-eigen-evidence/prep/attempt3/r8-wip-checkpoint-result.json)에서
report1 commit `9601b1f53e8a11d3e08d49cf9528fa6f028108cf`, controls2 commit
`96efbffa425ff5ce9cd1b7cebe673555273122b9`와 native exit0을 확인했다.
이후 status 변경·Ninja 설정·native 검증은 아직 미실행이다.
[기존 preset](../../CMakePresets.json)과 [local config 예시](../../.harness/config.example.json)를
사용한다. 새 binaryDir는 `.harness/build-ninja-msvc-debug`이며 source, 기존 VS
`.harness/build` cache와 모든 attempt evidence를 보존한다.
[CMake adapter](../../scripts/msvc_harness/adapters/cmake.py)가 지원하는 이 세 preset을 함께
선택하면 manual CMake/CTest와 registered Stop이 같은 graph를 사용한다. 기존 config에
없는 Multi-Config 선택이나 public preset/API를 추가하지 않는다.
Single-config에서도 기존 reference helper가 확인하는 실제 library 경로가
`<new binaryDir>/src/fesa/Debug/fesa_solver.lib`가 되도록 표준
`CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG`를 지정한다. Local CMake help가 설명한
per-configuration archive target 속성 초기화를 사용하며 fake cache 파일·복사·mtime 조작은
허용하지 않는다. 이 정상 MSVC/x64/Debug 대안의 **actual native 성공은 미검증**이다.
### E8.1. Root의 ignored 환경 준비
순서는 Root frozen 관리2/Planning3문서 checkpoint와 clean 확인 → 아래 Root setup의
actual configure/config 검증 → Coordinator의 setup 감사 → 별도 Planning status-only
할당과 [R9 control checkpoint](implementation-recovery.md#r9-e8-활성화-전의-status-only-checkpoint)
→ clean 확인 → E8.2 새 native invocation이다. 현재 문서 할당은 아래 setup 실행을 포함하지 않는다.
Root는 별도 할당을 받은 뒤 repository root의 PowerShell에서 아래 block만 수행한다.
Local config는 예시와 정확히 같은 기존 지원 설정만 생성하며 예상 밖 기존 config를 덮어쓰지
않는다. New CMake cache에 archive 경로를 고정하고 native/Stop의 후속 preset configure가
같은 값을 유지하게 한다. Root configure는 준비 evidence이며 native manual VERIFY의 대체가 아니다.
```powershell
$ErrorActionPreference = 'Stop'
$mfeRoot = (Get-Location).Path
$mfeVs = 'C:/Program Files/Microsoft Visual Studio/18/Community'
$mfeNinja = Join-Path $mfeVs 'Common7/IDE/CommonExtensions/Microsoft/CMake/Ninja'
$mfeEvidence = Join-Path $mfeRoot '.harness/build/mitc4-eigen-evidence/prep/classifier-correction'
$mfePython = Join-Path $mfeEvidence 'pytest-venv/Scripts/python.exe'
$mfeBasePython = Join-Path $mfeEvidence 'python-runtime/cpython-3.11.15-windows-x86_64-none/python.exe'
foreach ($mfeFile in @($mfePython, $mfeBasePython)) {
if (-not (Test-Path -LiteralPath $mfeFile -PathType Leaf)) { throw 'Named Python missing' }
}
Get-Command uv -ErrorAction Stop | Out-Null
$mfeNewBuild = Join-Path $mfeRoot '.harness/build-ninja-msvc-debug'
$mfeArchive = (Join-Path $mfeNewBuild 'src/fesa/Debug').Replace('\','/')
$mfeTempParent = Join-Path $mfeRoot '.harness/build'
$mfeTemp = Join-Path $mfeTempParent ('t-' + [guid]::NewGuid().ToString('N').Substring(0,8))
if ($mfeTemp.Length -gt 80 -or (Test-Path -LiteralPath $mfeTemp)) { throw 'TEMP must be short and new' }
$null = New-Item -ItemType Directory -Path $mfeTemp
$mfeEnvBefore = [Environment]::GetEnvironmentVariables('Process')
$mfeExit = $null
try {
& $mfePython -B -X utf8 -c "from scripts import execute; e=execute.StepExecutor('mitc4-shell-feast-eigen-execution-prep'); assert not e._worktree_paths(); assert e._is_isolated_worktree(); e._preflight_repository(); print('Clean linked worktree verified')"
$mfePreflightExit = $LASTEXITCODE
if ($mfePreflightExit -ne 0) { exit $mfePreflightExit }
$mfeConfig = Join-Path $mfeRoot '.harness/config.json'
$mfeExample = Join-Path $mfeRoot '.harness/config.example.json'
if (Test-Path -LiteralPath $mfeConfig) {
if ((Get-Content -LiteralPath $mfeConfig -Raw | ConvertFrom-Json | ConvertTo-Json -Depth 10 -Compress) -ne (Get-Content -LiteralPath $mfeExample -Raw | ConvertFrom-Json | ConvertTo-Json -Depth 10 -Compress)) { throw 'Preserve unexpected existing local config' }
} else { Copy-Item -LiteralPath $mfeExample -Destination $mfeConfig }
if ($null -ne $env:FEM_HARNESS_CODEX_SANDBOX -and $env:FEM_HARNESS_CODEX_SANDBOX -ne 'workspace-write') {
throw 'Unexpected sandbox environment'
}
& (Join-Path $mfeVs 'Common7/Tools/Launch-VsDevShell.ps1') -VsInstallationPath $mfeVs -Arch amd64 -HostArch amd64 -SkipAutomaticLocation -NoLogo
$env:PATH = (Split-Path $mfePython) + ';' + (Split-Path $mfeBasePython) + ';' + $mfeNinja + ';' + $env:PATH
$env:FESA_GTEST_SOURCE_DIR = 'C:/git/googletest'
$env:MKL_DIR = 'C:/Program Files (x86)/Intel/oneAPI/mkl/2026.1/lib/cmake/mkl'
$env:TBB_DIR = 'C:/Program Files (x86)/Intel/oneAPI/tbb/2023.1/lib/cmake/tbb'
$env:HDF5_DIR = 'C:/Program Files/HDF_Group/HDF5/2.1.1/cmake'
$env:UV_PYTHON = $mfePython
$env:UV_CACHE_DIR = Join-Path $mfeEvidence 'workspace-python-cache'
$env:UV_OFFLINE = 'true'
$env:TEMP = $mfeTemp
$env:TMP = $mfeTemp
$env:FEM_HARNESS_CODEX_SANDBOX = 'workspace-write'
Get-Command cl.exe, ninja.exe, cmake.exe, ctest.exe -ErrorAction Stop | Select-Object Name, Source
if ($env:VSCMD_ARG_TGT_ARCH -notin @('x64','amd64')) { throw 'MSVC target must be x64' }
git check-ignore --quiet .harness/config.json
if ($LASTEXITCODE -ne 0) { throw 'Local config must remain ignored' }
git check-ignore --quiet .harness/build-ninja-msvc-debug/
if ($LASTEXITCODE -ne 0) { throw 'New build must remain ignored' }
cmake --preset windows-msvc-ninja-debug "-DCMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG=$mfeArchive"
$mfePrepareExit = $LASTEXITCODE
if ($mfePrepareExit -ne 0) { exit $mfePrepareExit }
& $mfePython -B -X utf8 -c "from pathlib import Path; from scripts.msvc_harness.config import load_config; c=load_config(Path.cwd()); assert c.cmake.configure_preset=='windows-msvc-ninja-debug'; assert c.cmake.build_preset==c.cmake.test_preset==c.cmake.configure_preset; assert c.cmake.binary_dir==Path.cwd()/'.harness/build-ninja-msvc-debug'; print('Ninja local config validated')"
$mfeConfigExit = $LASTEXITCODE
if ($mfeConfigExit -ne 0) { exit $mfeConfigExit }
$mfeExit = $mfeConfigExit
if ($null -eq $mfeExit) { throw 'Root setup exit unavailable' }
}
finally {
foreach ($mfeName in @([Environment]::GetEnvironmentVariables('Process').Keys)) {
if (-not $mfeEnvBefore.Contains($mfeName)) { [Environment]::SetEnvironmentVariable($mfeName, $null, 'Process') }
}
foreach ($mfeName in $mfeEnvBefore.Keys) {
[Environment]::SetEnvironmentVariable($mfeName, $mfeEnvBefore[$mfeName], 'Process')
}
}
exit $mfeExit
```
Installed VS x64 DevShell이 설정하는 INCLUDE/LIB/PATH 등을 포함해 process 환경 전체를
snapshot/finally 복원한다. Config와 새 cache는 ignored 상태로 feature 동안 유지한다.
Root setup process의 종료가 다른 parent/tool shell의 환경을 갱신하지는 않는다.
Native의 **각 tool shell**도 E8.2의 동일 VS/dependency/Python/TEMP 설정을 실제 적용한
상태에서 명령을 실행해야 한다. 이전 attempt3의 명시적 짧은 TEMP 적용 실패를 환경 누락으로
재분류하지 않는다. Setup 실패 시 실제 exit와 남은 ignored 파일을 보존하고 재실행을 멈춘다.
### E8.2. Coordinator의 새 정상 invocation과 native shell 환경
Root setup과 R9의 실제 완료를 감사한 Coordinator만 다음 invocation을 할당한다.
`StepExecutor/main`과 모든 guard는 그대로이며 승인된 유한14400 module invocation을
사용한다. Registered Stop7500/shared·command7200, terminal 보존, transport budget,
terra/xhigh 및 workspace-write를 유지한다. 기존 one-time DFA 승인은 소진됐으며 flag가 없다.
```powershell
$ErrorActionPreference = 'Stop'
$mfeRoot = (Get-Location).Path
$mfePhase = 'mitc4-shell-feast-eigen-execution-prep'
$mfeVs = 'C:/Program Files/Microsoft Visual Studio/18/Community'
$mfeNinja = Join-Path $mfeVs 'Common7/IDE/CommonExtensions/Microsoft/CMake/Ninja'
$mfeEvidence = Join-Path $mfeRoot '.harness/build/mitc4-eigen-evidence/prep/classifier-correction'
$mfePython = Join-Path $mfeEvidence 'pytest-venv/Scripts/python.exe'
$mfeBasePython = Join-Path $mfeEvidence 'python-runtime/cpython-3.11.15-windows-x86_64-none/python.exe'
foreach ($mfeFile in @($mfePython, $mfeBasePython)) {
if (-not (Test-Path -LiteralPath $mfeFile -PathType Leaf)) { throw 'Named Python missing' }
}
Get-Command uv -ErrorAction Stop | Out-Null
$mfeNewBuild = Join-Path $mfeRoot '.harness/build-ninja-msvc-debug'
$mfeArchive = (Join-Path $mfeNewBuild 'src/fesa/Debug').Replace('\','/')
$mfeTempParent = Join-Path $mfeRoot '.harness/build'
$mfeTemp = Join-Path $mfeTempParent ('t-' + [guid]::NewGuid().ToString('N').Substring(0,8))
if ($mfeTemp.Length -gt 80 -or (Test-Path -LiteralPath $mfeTemp)) { throw 'TEMP must be short and new' }
$null = New-Item -ItemType Directory -Path $mfeTemp
$mfeEnvBefore = [Environment]::GetEnvironmentVariables('Process')
$mfeExit = $null
try {
if ($null -ne $env:FEM_HARNESS_CODEX_SANDBOX -and $env:FEM_HARNESS_CODEX_SANDBOX -ne 'workspace-write') {
throw 'Unexpected sandbox environment'
}
& (Join-Path $mfeVs 'Common7/Tools/Launch-VsDevShell.ps1') -VsInstallationPath $mfeVs -Arch amd64 -HostArch amd64 -SkipAutomaticLocation -NoLogo
$env:PATH = (Split-Path $mfePython) + ';' + (Split-Path $mfeBasePython) + ';' + $mfeNinja + ';' + $env:PATH
$env:FESA_GTEST_SOURCE_DIR = 'C:/git/googletest'
$env:MKL_DIR = 'C:/Program Files (x86)/Intel/oneAPI/mkl/2026.1/lib/cmake/mkl'
$env:TBB_DIR = 'C:/Program Files (x86)/Intel/oneAPI/tbb/2023.1/lib/cmake/tbb'
$env:HDF5_DIR = 'C:/Program Files/HDF_Group/HDF5/2.1.1/cmake'
$env:UV_PYTHON = $mfePython
$env:UV_CACHE_DIR = Join-Path $mfeEvidence 'workspace-python-cache'
$env:UV_OFFLINE = 'true'
$env:TEMP = $mfeTemp
$env:TMP = $mfeTemp
$env:FEM_HARNESS_CODEX_SANDBOX = 'workspace-write'
Get-Command cl.exe, ninja.exe, cmake.exe, ctest.exe -ErrorAction Stop | Select-Object Name, Source
if ($env:VSCMD_ARG_TGT_ARCH -notin @('x64','amd64')) { throw 'MSVC target must be x64' }
if (-not (Test-Path -LiteralPath (Join-Path $mfeNewBuild 'CMakeCache.txt'))) { throw 'Root cache preparation missing' }
& $mfePython -B -X utf8 -c "from pathlib import Path; from scripts.msvc_harness.config import load_config; c=load_config(Path.cwd()); assert c.cmake.configure_preset=='windows-msvc-ninja-debug'; assert c.cmake.build_preset==c.cmake.test_preset==c.cmake.configure_preset; assert c.cmake.binary_dir==Path.cwd()/'.harness/build-ninja-msvc-debug'; print('Ninja local config validated')"
$mfeConfigExit = $LASTEXITCODE
if ($mfeConfigExit -ne 0) { exit $mfeConfigExit }
& $mfePython -B -X utf8 -c "from scripts import execute; execute.StepExecutor.CODEX_TIMEOUT_SECONDS = 14400; execute.main()" $mfePhase
$mfeExit = $LASTEXITCODE
if ($null -eq $mfeExit) { throw 'Native Executor exit unavailable' }
}
finally {
foreach ($mfeName in @([Environment]::GetEnvironmentVariables('Process').Keys)) {
if (-not $mfeEnvBefore.Contains($mfeName)) { [Environment]::SetEnvironmentVariable($mfeName, $null, 'Process') }
}
foreach ($mfeName in $mfeEnvBefore.Keys) {
[Environment]::SetEnvironmentVariable($mfeName, $mfeEnvBefore[$mfeName], 'Process')
}
}
exit $mfeExit
```
이 block의 환경 설정은 Executor child와 그 registered Stop에 상속된다. Implementation
tool shell이 이를 상속하지 않았거나 별도 shell을 시작했다면 동일 block의 환경 설정과
finally를 그 shell에서 적용한다. **Manual 검증용 정확한 composition**은 위 block에서
`& $mfePython -B -X utf8 -c "from scripts import execute; execute.StepExecutor.CODEX_TIMEOUT_SECONDS = 14400; execute.main()" $mfePhase`
한 줄을 E8.3의 해당 검증 명령들로 교체하는 것이다. 나머지 config 검사/DevShell/환경
복원은 그대로 두며 manual shell에서 Executor를 중첩 호출하지 않는다. 각 명령 직후
`$LASTEXITCODE`를 캡처하고 0이 아니면 해당 native exit로 종료하여 finally를 실행한다.
Active Executor 동안 Root/Coordinator는 non-owned 편집과 commit을 멈춘다. 구현 owner는
Step0 보고서와 result payload만 할당 범위에서 작성하며 phase/timestamps/history/Git을
변경하지 않는다. 실제 manual evidence와 아직 발생하지 않은 final registered Stop을
구분하고 Coordinator가 native 종료 뒤 raw/Hook/CTest actual exit를 사후 감사한다.
Prep 전체 gate를 통과한 뒤 별도 main invocation은 위 block의 `$mfePhase`만
`mitc4-shell-feast-eigen`으로 바꾼다. Main도 workspace-write와 같은 환경/graph를 사용한다.
### E8.3. Prep와 main8의 정확한 명령 대응 및 evidence
Prep [Step0](../../phases/mitc4-shell-feast-eigen-execution-prep/step0.md)와
[main8](../../phases/mitc4-shell-feast-eigen/index.json)의 모든 Step은 현재 environment를
필수 입력으로 읽는다. 해당 문서의 과거 config absent/VS 고정 명령은 이번 Coordinator
E8 dispatch에서 아래 대응으로 **명시 대체**한다. 이는 선택 backend 변경이며 승인된
owned_paths, TDD 순서, target/test regex/mandatory names, full VERIFY 범위는 그대로다.
Phase 파일을 임의 수정하거나 예전 VS 명령과 새 graph를 섞지 않는다.
| 기존 Step 명령 | E8에서 실행할 명령 |
| --- | --- |
| `cmake -S . -B .harness/build -A x64` | `cmake --preset windows-msvc-ninja-debug "-DCMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG=$mfeArchive"` |
| `cmake --build .harness/build --config Debug --target fesa_tests` | `cmake --build --preset windows-msvc-ninja-debug --target fesa_tests` |
| `cmake --build .harness/build --config Debug` | `cmake --build --preset windows-msvc-ninja-debug` |
| `ctest --test-dir .harness/build -C Debug -R '<Step 원문 regex>' --show-only=json-v1` | `ctest --preset windows-msvc-ninja-debug -R '<같은 원문 regex>' --show-only=json-v1` |
| `ctest --test-dir .harness/build -C Debug -R '<Step 원문 regex>' --output-on-failure` | `ctest --preset windows-msvc-ninja-debug -R '<같은 원문 regex>' --output-on-failure` |
| `ctest --test-dir .harness/build -C Debug --show-only=json-v1` | `ctest --preset windows-msvc-ninja-debug --show-only=json-v1` |
| `ctest --test-dir .harness/build -C Debug --output-on-failure` | `ctest --preset windows-msvc-ninja-debug --output-on-failure` |
Table의 regex 표시는 새 선택값이 아니다. 현재 Step 명령의 `-R` 인수 문자열 전체를
byte 의미 그대로 유지한다. Combined selection이 0보다 크다는 것만 확인하지 않고
[V1~V6](implementation-verification.md)의 새 mandatory test 이름도 discovery에서 확인한다.
`fesa_tests`는 기존 unit/integration/reference 세 executable을 포함하는 aggregate target이다.
Manual full C++ 검증에 넣을 정확한 명령 bundle은 다음과 같다. E8.2의 `$mfeArchive`는
새 binaryDir의 absolute forward-slash 경로다. Ninja preset은 Debug를 명시하므로 VS 전용
`-A x64`나 이전 `--test-dir`를 추가하지 않는다.
```powershell
cmake --preset windows-msvc-ninja-debug "-DCMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG=$mfeArchive"
$mfeVerifyExit = $LASTEXITCODE
if ($null -eq $mfeVerifyExit -or $mfeVerifyExit -ne 0) { exit $mfeVerifyExit }
cmake --build --preset windows-msvc-ninja-debug
$mfeVerifyExit = $LASTEXITCODE
if ($null -eq $mfeVerifyExit -or $mfeVerifyExit -ne 0) { exit $mfeVerifyExit }
ctest --preset windows-msvc-ninja-debug --show-only=json-v1
$mfeVerifyExit = $LASTEXITCODE
if ($null -eq $mfeVerifyExit -or $mfeVerifyExit -ne 0) { exit $mfeVerifyExit }
ctest --preset windows-msvc-ninja-debug --output-on-failure
$mfeVerifyExit = $LASTEXITCODE
if ($null -eq $mfeVerifyExit -or $mfeVerifyExit -ne 0) { exit $mfeVerifyExit }
```
Python은 E7 말미의 정확한 classifier/focused/full 세 호출을 같은 환경 wrapper 안에서
수행한다. Named venv, UV offline/cache, 기존 대상/assertions, 새
`.harness/build/p-<8hex>` basetemp80자 이하와 TEMP80자 이하를 유지한다.
Already GREEN인 timeout/classifier source를 RED로 되돌리지 않는다. 실제 보존된 RED/GREEN을
읽고 classifier37/focused136 및 남은 full pytest를 현재 code로 재검증한다. r2 harness
Correction1회는 사용 완료됐으며 두 번째 source correction을 시작하지 않는다.
Native configure/build evidence에는 actual MSVC compiler/x64/Debug와 Ninja cache,
CMake/CTest 명령·binaryDir, 새 warning0, 실제
`src/fesa/Debug/fesa_solver.lib`의 존재·SHA256·mtime를 기록한다.
Library가 계획 위치에 없으면 환경 gate 실패로 반환한다. 기존 helper의 실제 mtime 판단을
유지하고 새 binary에 대한 fresh solve 및 정확 입력복사/hash를 확인한다. 기존 VS 결과를
새 graph의 fresh evidence로 옮기지 않는다. 새 graph의 최초 전체 suite 시간도 실제 측정하며,
이전 약27.9분 추정이나 finite cap을 성공/소요 보장으로 해석하지 않는다.
각 Step의 focused/full 및 registered Stop에 같은 graph가 사용됐는지 실제 raw를 대조한다.
기존 static reference 비교와 새 eigen Value10 비교의 승인 기준은 그대로다. 다음
Implementation은 보고서의 광범위한 reference 미사용 문장을 정정해 static full CTest가
원본 reference를 읽고 비교한다는 사실, 원본 직접 수정 없음, 새 eigen 비교 미실행을
구분한다. 과거 manual build 실패/미실행과 automatic pass도 보존한다.
Source·수식·schema·tolerance·reference를 이 환경 준비에서 변경하지 않는다.
정상 native 대안이 실제 실패하면 결과를 보존해 Coordinator에 반환하며, 새 exact-run DFA
결정이 필요한지는 그 evidence로 판단한다. 지금 main까지 권한을 확대하지 않는다.
## E9. Attempt4 후의 직렬 Ninja 검증
[R10](implementation-recovery.md#r10-attempt4의-errorhistory4-보존)이 우선이다.
[설치 모듈·생성 경로 관찰](../../.harness/build/mitc4-eigen-evidence/prep/attempt4/root-gtest-discovery-path-observation.json)
SHA256 `9d383f04a347df95b742403ceca4732b331ddf4ec96437993f0ceffaa060bf59`에서
세 POST_BUILD discovery가 누락된 TEST_TARGET 때문에 같은 JSON을 REMOVE/run/READ한다.
이는 병렬 공유 파일 경쟁을 뒷받침하며 최초 interleaving을 재현한 것은 아니다.
정상 환경 변수 `CMAKE_BUILD_PARALLEL_LEVEL=1`로 같은 Ninja build의 discovery를 직렬화한다.
CMake 설치 모듈, generated discovery, source/test/Hook/config와 timeout은 수정하지 않는다.
**Activation 미확정:** Root의 config-only [현재 resolver 관찰](../../.harness/build/mitc4-eigen-evidence/prep/attempt4/root-hook-config-resolution.json)은
canonical Stop1800 해석을 확인했다(server exit0, SHA256
`5deb9a701388605a14868198276993af3884f0cc76c48928831968b272f46de4`).
실제 native raw의 source event는 없으며 현재 resolver 결과와 외부 pwsh 약1802초 관찰을 구분한다.
남은 쟁점은 정상 override 지원·적용 방법이다. 이를 Coordinator가 검토하기 전 status reset/다음
실행을 할당하지 않는다. Canonical Hook 쓰기나 DFA·추가 timeout을 승인으로 추정하지 않는다.
아래는 이 조건 해소 후의 실행 계약이며 현재 실행 준비 완료 또는 실제 성공이 아니다.
부모 Coordinator invocation은 E8.2 block 전체를 사용하되
`$env:FEM_HARNESS_CODEX_SANDBOX = 'workspace-write'` 바로 다음에 다음 한 줄을 추가한다.
```powershell
$env:CMAKE_BUILD_PARALLEL_LEVEL = '1'
```
나머지 named Python/UVoffline/cache, dependency4, 새 짧은 TEMP/TMP와 E7 pytest basetemp,
Root에서 실제 성공한 installed x64 DevShell, 유한14400 module invocation과 finally의
전체 process 환경 복원은 그대로다. 따라서 registered Stop에도 같은 병렬도1과 graph가
상속된다. Actual configured outer Stop7500/shared·command7200 계약은 유지하지만 실제
설정 출처·종료 evidence를 확인해야 하며 새 cap을 만들지 않는다.
원래 invocation line은 그대로다.
```powershell
& $mfePython -B -X utf8 -c "from scripts import execute; execute.StepExecutor.CODEX_TIMEOUT_SECONDS = 14400; execute.main()" $mfePhase
$mfeExit = $LASTEXITCODE
if ($null -eq $mfeExit) { throw 'Native Executor exit unavailable' }
```
Native child tool shell에서는 E8.2 manual composition의 DevShell script 호출 한 줄을
아래 **상속 환경 검사**로 대체한다. 나머지 dependency/Python/짧은 TEMP·병렬도1 설정과
finally는 그대로 적용한다. 실행 정책 변경이나 우회 인수를 사용하지 않는다.
부모가 제공한 x64 환경이 없으면 검증 명령을 시작하지 않고 blocked-environment로 반환한다.
```powershell
if ($env:VSCMD_ARG_TGT_ARCH -notin @('x64','amd64') -or
[string]::IsNullOrWhiteSpace($env:VSINSTALLDIR) -or
[string]::IsNullOrWhiteSpace($env:INCLUDE) -or
[string]::IsNullOrWhiteSpace($env:LIB)) { throw 'Inherited MSVC x64 environment missing' }
$mfeCl = (Get-Command cl.exe -ErrorAction Stop).Source
$mfeNinjaExe = (Get-Command ninja.exe -ErrorAction Stop).Source
if ($mfeCl -notmatch '[\\/]Hostx64[\\/]x64[\\/]cl.exe$') { throw 'Unexpected compiler architecture' }
Write-Output ("Inherited compiler={0}; Ninja={1}" -f $mfeCl, $mfeNinjaExe)
$env:CMAKE_BUILD_PARALLEL_LEVEL = '1'
```
새 prep의 첫 manual build는 아래 **한 번의 clean-first full build**다. 새 Ninja graph의
기존 성공 후 no-op build를 경쟁 해소 evidence로 보고하지 않도록, 실제 세 executable
link와 POST_BUILD discovery를 같은 직렬 build에서 다시 실행한다. CMake의 기존
clean-first만 사용하며 기존 VS cache나 reference/evidence를 삭제하지 않는다.
Registered Stop과 후속 통상 build는 E8의 일반 build 명령에 병렬도1을 상속한다.
```powershell
cmake --preset windows-msvc-ninja-debug "-DCMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG=$mfeArchive"
$mfeVerifyExit = $LASTEXITCODE
if ($null -eq $mfeVerifyExit) { throw 'Configure exit unavailable' }
if ($mfeVerifyExit -ne 0) { exit $mfeVerifyExit }
cmake --build --preset windows-msvc-ninja-debug --clean-first --verbose
$mfeVerifyExit = $LASTEXITCODE
if ($null -eq $mfeVerifyExit) { throw 'Build exit unavailable' }
if ($mfeVerifyExit -ne 0) { exit $mfeVerifyExit }
ctest --preset windows-msvc-ninja-debug --show-only=json-v1
$mfeVerifyExit = $LASTEXITCODE
if ($null -eq $mfeVerifyExit) { throw 'Discovery exit unavailable' }
if ($mfeVerifyExit -ne 0) { exit $mfeVerifyExit }
ctest --preset windows-msvc-ninja-debug --output-on-failure
$mfeVerifyExit = $LASTEXITCODE
if ($null -eq $mfeVerifyExit) { throw 'CTest exit unavailable' }
if ($mfeVerifyExit -ne 0) { exit $mfeVerifyExit }
```
Verbose log에는 `fesa_unit_tests_576e7e9d_discovery.cmake`,
`fesa_integration_tests_d537bc73_discovery.cmake`,
`fesa_reference_tests_ac7cf2f0_discovery.cmake`의 실제 세 호출과 성공을 보존한다.
세 `*_tests.cmake`의 새 mtime/hash·등록 identity와 full discovery JSON을 대조한다.
현재 prep inventory는 unit289/integration34/reference39, 총362다. 명령이 실제 각 target의
올바른 tests를 등록했는지 확인하며 합계나 leftover JSON의 존재만으로 통과하지 않는다.
Generated suffix가 바뀌면 actual build graph의 각 target 매핑을 기록하고 임의 파일을 만들지 않는다.
새 `src/fesa/Debug/fesa_solver.lib`와 세 executable의 hash/mtime를 기록한다.
이 새 binary에 대한 required manual full CTest의 fresh reference solve, 입력 복사/hash와
결과 생성 시각을 확인하고 그 뒤 registered same-binary full의 정상 cache 재사용과 구분한다.
Manual fresh solve를 cached-only 자동 실행으로 대체하지 않는다. 각 raw log를 덮어쓰기 전에
새 attempt evidence로 보존한다. No-op 관찰·단독 진단·합산 duration은 full gate가 아니다.
E7의 classifier37/focused136/full pytest 대상과 모든 assertion은 유지한다. 새 invocation에서
각 필수 검증을 수행하되 현재 code에서 한 번 통과한 focused136을 근거 없이 반복하지 않는다.
과거 attempt4의 focused136 실제 세 반복이 모두 exit0이었다는 사실도 보존한다.
이미 GREEN인 source를 RED로 되돌리지 않으며 두 번째 source Correction을 시작하지 않는다.
기존 전체 Python, native configure/build/discovery/CTest와 실제 registered Stop 성공이 모두
필수다. Full pytest/CTest의 미실행 상태를 다음 owner가 실제 evidence로 해소한다.
Implementation owner는 보고서의 역사적 reference 미사용 문장을 정정해 full CTest의 기존
static reference 읽기·비교, 원본 직접 수정 없음과 새 eigen 비교 미실행을 구분한다.
과거 manual failure와 registered pwsh1/Python0/CTest0도 각각 보존한다.
Result payload는 manual 결과와 아직 미관찰인 Stop을 구분하며 Coordinator가 실제 종료 후
검토한다. Active Executor 동안 Root/Coordinator는 non-owned edits/commits를 멈춘다.
Main은 prep 전체 gate 뒤 별도 workspace-write invocation만 허용하며 E9의 병렬도1을
같이 사용한다. 새로운 동일 실패는 원시 evidence와 기존 retry key로 반환한다.
## E10. 부모 환경과 독립적인 workspace user preset
[Root Stop 실패](../../.harness/build/mitc4-eigen-evidence/prep/attempt4/root-stop-after-decision-docs/observation.json)는
parent env4 미설정과 public preset의 cache4 빈 값 overwrite/configure1을 보존한다.
현재 실패 cache hash는 `125a4dbd163c5a0a6d94bd03d3186790c41e6a0d8e0ed7b9c72f18fdf619d66e`다.
이는 MFE-OQ-004 승인 답변이 아니다. Canonical Hook·재신뢰는 계속 미승인이고 새 native/
status reset도 미할당이다. 아래는 별도 Root 실행을 위한 문서 계획이며 현재 setup은 미실행이다.
[Toolchain](../../scripts/msvc_harness/toolchain.py)은 executable 경로만 찾으며
[Stop](../../scripts/hooks/stop_validation.py)은 parent env를 복사한다. 따라서 root
`CMakeUserPresets.json`의 `mfe-workspace-msvc-ninja-debug`가 public preset을 상속하고
dependency4 literal cache와 최소 MSVC INCLUDE/LIB/tool PATH를 제공하도록 한다.
Installed CMake4.4 `Help/manual/cmake-presets.7.rst`의 user/common 자동 include,
`$penv{PATH}`, build/test `inheritConfigureEnvironment`를 사용한다. Public preset,
Harness API/config schema와 source/test/Hook은 바꾸지 않는다.
Build/test는 configure 환경을 상속한다. 같은 Ninja binaryDir·MSVC Hostx64/x64·Debug,
실제 Debug archive 경로와 직렬 병렬도1을 유지한다. Root의 정상 DevShell에서 INCLUDE/LIB,
cl/rc/mt/Ninja directory만 capture하고 전체 env·비밀값은 preset에 저장하지 않는다.
Parent process는 finally에서 복원한다. 이후 arbitrary Root process에도 CMake child가
자체 preset 환경을 적용하므로 parent env4/DevShell 지속성을 전제로 삼지 않는다.
Preset TEMP/TMP는 setup에서 만든 새80자 이하 ignored 디렉터리를 **설정 수명 동안**
사용한다. CMake child에서는 이 값이 E7/E9의 매회 parent TEMP를 대체한다. 기존 model/test의
고유 child directory 생성은 유지하고 동시 build/test가 없는지 Root가 setup 전에 실제 확인한다.
Python/pytest는 기존 per-invocation 새 짧은 TEMP와 basetemp를 유지한다. 기존 디렉터리/
evidence 삭제, 권한·실행 정책 변경이나 Hook 우회는 없다.
### E10.1. Root의 exact setup과 configure-only 확인
순서는 Planning3 동결 → Root docs checkpoint → 아래 setup → 실제 observation/최소 문서
갱신과 Root exact commit이다. 현재 UserPresets는 미존재·미ignore다. Root가 별도 소유하는
tracked 변경은 [.gitignore](../../.gitignore)의 `/CMakeUserPresets.json` 한 줄뿐이다.
그 줄을 Root가 검토·commit하며 public preset과 Implementation owned9/phase 범위는 유지한다.
UserPresets/local config/TEMP/backup/Ninja configure output은 ignored로 둔다.
```powershell
$ErrorActionPreference = 'Stop'
$mfeRoot=(Get-Location).Path
$mfePython=Join-Path $mfeRoot '.harness/build/mitc4-eigen-evidence/prep/classifier-correction/pytest-venv/Scripts/python.exe'
$mfeCmake=(Get-Command cmake.exe -ErrorAction Stop).Source
$mfePreset='mfe-workspace-msvc-ninja-debug'
$mfeGitRoot=git rev-parse --show-toplevel
if ($LASTEXITCODE -ne 0 -or [IO.Path]::GetFullPath($mfeGitRoot) -ne [IO.Path]::GetFullPath($mfeRoot)) { throw 'Repository mismatch' }
$mfeDirty=git status --porcelain
if ($LASTEXITCODE -ne 0 -or $mfeDirty) { throw 'Root docs checkpoint and clean required' }
if (Test-Path -LiteralPath 'CMakeUserPresets.json') { throw 'Preserve existing UserPresets' }
$mfeInputs=@{
'.gitignore'='b81d422ee0185f16714e3218369689b808701e5e398a6f687a22784da2290842'
'.harness/config.json'='3ebdfa80124f1eb96909b2738d6f2a96fecd55cce61150a9cd7ef27d7c5fd892'
'.harness/build-ninja-msvc-debug/CMakeCache.txt'='125a4dbd163c5a0a6d94bd03d3186790c41e6a0d8e0ed7b9c72f18fdf619d66e'
}
foreach ($mfePath in $mfeInputs.Keys) {
if ((Get-FileHash -LiteralPath $mfePath).Hash.ToLower() -ne $mfeInputs[$mfePath]) { throw "Changed input: $mfePath" }
}
$mfeBackup=Join-Path $mfeRoot ('.harness/build/mfe-preset-'+[guid]::NewGuid().ToString('N').Substring(0,8))
$mfeTemp=Join-Path $mfeRoot ('.harness/build/t-'+[guid]::NewGuid().ToString('N').Substring(0,8))
if ($mfeTemp.Length -gt 80 -or (Test-Path -LiteralPath $mfeTemp) -or (Test-Path -LiteralPath $mfeBackup)) { throw 'Fresh short paths required' }
$null=New-Item -ItemType Directory -Path $mfeBackup
$null=New-Item -ItemType Directory -Path $mfeTemp
Copy-Item -LiteralPath '.gitignore' -Destination (Join-Path $mfeBackup 'gitignore.before')
Copy-Item -LiteralPath '.harness/config.json' -Destination (Join-Path $mfeBackup 'config.before.json')
Copy-Item -LiteralPath '.harness/build-ninja-msvc-debug/CMakeCache.txt' -Destination (Join-Path $mfeBackup 'CMakeCache.before.txt')
$mfeUtf8=[Text.UTF8Encoding]::new($false)
[IO.File]::WriteAllText((Join-Path $mfeBackup 'before.json'),(@{hashes=$mfeInputs; userPresetsExisted=$false; temp=$mfeTemp}|ConvertTo-Json -Depth 6),$mfeUtf8)
$mfeProtected=@('CMakePresets.json','.codex/hooks.json','scripts/execute.py','tests/test_execute.py','phases/index.json','phases/mitc4-shell-feast-eigen-execution-prep/index.json','phases/mitc4-shell-feast-eigen-execution-prep/step0-output.json','.harness/build/CMakeCache.txt')
$mfeHashes=@{}
foreach ($mfePath in $mfeProtected) { $mfeHashes[$mfePath]=(Get-FileHash -LiteralPath $mfePath).Hash }
$mfeBeforeEnv=[Environment]::GetEnvironmentVariables('Process')
try {
$mfeVs='C:/Program Files/Microsoft Visual Studio/18/Community'
& (Join-Path $mfeVs 'Common7/Tools/Launch-VsDevShell.ps1') -VsInstallationPath $mfeVs -Arch amd64 -HostArch amd64 -SkipAutomaticLocation -NoLogo
$mfeCl=(Get-Command cl.exe -ErrorAction Stop).Source
$mfeRc=(Get-Command rc.exe -ErrorAction Stop).Source
$mfeMt=(Get-Command mt.exe -ErrorAction Stop).Source
$mfeNinja=Join-Path $mfeVs 'Common7/IDE/CommonExtensions/Microsoft/CMake/Ninja/ninja.exe'
if ($env:VSCMD_ARG_TGT_ARCH -notin @('x64','amd64') -or $mfeCl -notmatch '[\\/]Hostx64[\\/]x64[\\/]cl\.exe$') { throw 'MSVC x64 required' }
if (-not(Test-Path -LiteralPath $mfeNinja) -or [string]::IsNullOrWhiteSpace($env:INCLUDE) -or [string]::IsNullOrWhiteSpace($env:LIB)) { throw 'Toolchain incomplete' }
$mfeDirs=@((Split-Path $mfeCl),(Split-Path $mfeNinja),(Split-Path $mfeRc),(Split-Path $mfeMt))|Select-Object -Unique
$mfeEnv=@{INCLUDE=$env:INCLUDE; LIB=$env:LIB; PATH=($mfeDirs -join ';')+';$penv{PATH}'; TEMP=$mfeTemp.Replace('\','/'); TMP=$mfeTemp.Replace('\','/'); CMAKE_BUILD_PARALLEL_LEVEL='1'}
$mfeCache=@{
FESA_GTEST_SOURCE_DIR='C:/git/googletest'
MKL_DIR='C:/Program Files (x86)/Intel/oneAPI/mkl/2026.1/lib/cmake/mkl'
TBB_DIR='C:/Program Files (x86)/Intel/oneAPI/tbb/2023.1/lib/cmake/tbb'
HDF5_DIR='C:/Program Files/HDF_Group/HDF5/2.1.1/cmake'
CMAKE_CXX_COMPILER=$mfeCl.Replace('\','/')
CMAKE_MAKE_PROGRAM=$mfeNinja.Replace('\','/')
CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG=('$'+'{sourceDir}/.harness/build-ninja-msvc-debug/src/fesa/Debug')
}
$mfeUser=@{
version=3
configurePresets=@(@{name=$mfePreset; inherits='windows-msvc-ninja-debug'; cacheVariables=$mfeCache; environment=$mfeEnv})
buildPresets=@(@{name=$mfePreset; inherits='windows-msvc-ninja-debug'; configurePreset=$mfePreset; inheritConfigureEnvironment=$true})
testPresets=@(@{name=$mfePreset; inherits='windows-msvc-ninja-debug'; configurePreset=$mfePreset; inheritConfigureEnvironment=$true})
}
}
finally {
foreach ($mfeName in @([Environment]::GetEnvironmentVariables('Process').Keys)) {
if (-not $mfeBeforeEnv.Contains($mfeName)) { [Environment]::SetEnvironmentVariable($mfeName,$null,'Process') }
}
foreach ($mfeName in $mfeBeforeEnv.Keys) { [Environment]::SetEnvironmentVariable($mfeName,$mfeBeforeEnv[$mfeName],'Process') }
}
$mfeOriginalIgnore=[IO.File]::ReadAllBytes((Join-Path $mfeRoot '.gitignore'))
if ($mfeOriginalIgnore[-1] -ne 10) { throw 'Unexpected ignore EOF' }
$mfeSuffix=$mfeUtf8.GetBytes('/CMakeUserPresets.json'+[Environment]::NewLine)
[IO.File]::WriteAllBytes((Join-Path $mfeRoot '.gitignore'),[byte[]]($mfeOriginalIgnore+$mfeSuffix))
git check-ignore --quiet CMakeUserPresets.json
if ($LASTEXITCODE -ne 0) { throw 'UserPresets must be ignored' }
[IO.File]::WriteAllText((Join-Path $mfeRoot 'CMakeUserPresets.json'),($mfeUser|ConvertTo-Json -Depth 10)+[Environment]::NewLine,$mfeUtf8)
$mfeConfig=Get-Content -LiteralPath '.harness/config.json' -Raw|ConvertFrom-Json
$mfeConfig.cmake.configurePreset=$mfePreset
$mfeConfig.cmake.buildPreset=$mfePreset
$mfeConfig.cmake.testPreset=$mfePreset
[IO.File]::WriteAllText((Join-Path $mfeRoot '.harness/config.json'),($mfeConfig|ConvertTo-Json -Depth 10)+[Environment]::NewLine,$mfeUtf8)
foreach ($mfeName in @('FESA_GTEST_SOURCE_DIR','MKL_DIR','TBB_DIR','HDF5_DIR')) {
if (-not [string]::IsNullOrEmpty([Environment]::GetEnvironmentVariable($mfeName,'Process'))) { throw 'Separate env4-unset configure verification required' }
}
$mfeExit=$null
try {
& $mfeCmake --list-presets=all
$mfeExit=$LASTEXITCODE
if ($null -eq $mfeExit) { throw 'Preset validation exit unavailable' }
if ($mfeExit -ne 0) { exit $mfeExit }
& $mfePython -B -X utf8 -c "from pathlib import Path; from scripts.msvc_harness.config import load_config; c=load_config(Path.cwd()); assert c.cmake.configure_preset==c.cmake.build_preset==c.cmake.test_preset=='mfe-workspace-msvc-ninja-debug'; assert c.cmake.binary_dir==Path.cwd()/'.harness/build-ninja-msvc-debug'; print('Local config valid')"
$mfeExit=$LASTEXITCODE
if ($null -eq $mfeExit) { throw 'Config validation exit unavailable' }
if ($mfeExit -ne 0) { exit $mfeExit }
& $mfeCmake --preset mfe-workspace-msvc-ninja-debug
$mfeExit=$LASTEXITCODE
if ($null -eq $mfeExit) { throw 'Configure exit unavailable' }
}
finally {
$mfeAfter=@{}
foreach ($mfePath in @('.gitignore','CMakeUserPresets.json','.harness/config.json','.harness/build-ninja-msvc-debug/CMakeCache.txt')) {
$mfeAfter[$mfePath]=(Get-FileHash -LiteralPath $mfePath).Hash.ToLower()
}
[IO.File]::WriteAllText((Join-Path $mfeBackup 'after.json'),(@{hashes=$mfeAfter; lastNativeExit=$mfeExit}|ConvertTo-Json -Depth 6),$mfeUtf8)
foreach ($mfePath in $mfeProtected) {
if ((Get-FileHash -LiteralPath $mfePath).Hash -ne $mfeHashes[$mfePath]) { throw "Non-owned change: $mfePath" }
}
}
$mfeFinalDirty=git status --porcelain
if ($LASTEXITCODE -ne 0 -or @($mfeFinalDirty).Count -ne 1 -or $mfeFinalDirty -ne ' M .gitignore') { throw 'Unexpected tracked changes' }
git diff --check -- .gitignore
if ($LASTEXITCODE -ne 0) { throw 'Ignore whitespace check failed' }
Write-Output ("Setup evidence: {0}; configure exit {1}" -f $mfeBackup,$mfeExit)
exit $mfeExit
```
Original3파일 bytes/hash와 UserPresets 부재를 mutation 전에 backup하고, 실제 CMake
`--list-presets=all` schema/inheritance 해석 및 `load_config` 뒤 **env4 unset 상태의
configure만** 수행한다. Root는 after cache의 dependency4, Ninja/Debug, compiler 경로·
pointer size8, archive 경로와 새 user/config/cache hash를 기록한다. Actual adapter plan의
configure/build/discovery/test 네 명령도 같은 local preset으로 대조한다.
Library/build/CTest 성공은 configure evidence가 아니다. HEAD/phase history4/source/
reference/old VS cache 불변, tracked diff가 ignore1줄뿐인지 감사한 뒤 Root가 exact commit한다.
자동 Root outer Stop이 발생하면 실제 결과를 따로 관찰하며 manual Hook으로 대체하지 않는다.
### E10.2. 기존 명령의 명시 대체와 수명
Setup 성공을 별도 감사한 뒤 E8/E9 및 모든 prep/main 명령의 preset 인수를 아래로 대체한다.
Configure의 archive는 user cache가 고정하므로 예전 추가 `-D`가 필요 없다.
| 목적 | 정확한 새 명령 |
| --- | --- |
| Configure | `cmake --preset mfe-workspace-msvc-ninja-debug` |
| E9 최초 실제 직렬 재검증 build | `cmake --build --preset mfe-workspace-msvc-ninja-debug --clean-first --verbose` |
| 일반 full/registered build | `cmake --build --preset mfe-workspace-msvc-ninja-debug` |
| Main focused target build | `cmake --build --preset mfe-workspace-msvc-ninja-debug --target fesa_tests` |
| Discovery | `ctest --preset mfe-workspace-msvc-ninja-debug --show-only=json-v1` |
| Full CTest | `ctest --preset mfe-workspace-msvc-ninja-debug --output-on-failure` |
Focused CTest는 원 Step의 `-R` 문자열을 그대로 유지하고 preset 인수만 위 이름으로 바꾼다.
E9 세 discovery의 실제 실행/등록 identity·새 binary hash/mtime·fresh manual solve 및
registered same-binary full 구분, 전체 Python/CTest, 수치/reference 계약은 유지한다.
CMake 검증에 한해 E8/E9의 외부 shell DevShell 재호출·cl/Ninja 사전 검색 조건을 user preset과
actual compiler/cache evidence로 대체한다. Python/UV recipe와 finite14400/7500/7200
계약, workspace-write 및 모든 Hook/retry/ownership guard는 그대로다.
실제 canonical outer1800·재신뢰 문제는 이 local setup으로 해결됐다고 주장하지 않는다.
[Preset contract test](../../tests/test_harness_preset_contract.py)는 tracked public preset/
config.example과 tmp_path에 복사한 example을 검사하며 실제 local config/UserPresets를
읽지 않는다. Public/example 불변과 기존 ignore 항목 보존 시 이 선택과 모순되지 않는다.
테스트 소스/대상/assertions는 수정하지 않았고 이 Planning에서 테스트를 실행하지 않았다.
Rollback은 active 실행이 없고 after hash와 현재 파일이 동일한 경우에 Root가 수행한다.
`.gitignore`와 local config는 backup bytes로 복원하고, 원래 없었던 정확한
`CMakeUserPresets.json`만 `Remove-Item -LiteralPath`로 비재귀 제거한다.
필요 시 `CMakeCache.before.txt`를 원래 exact cache 경로에 복사하되 **기존 실패 cache
복원**임을 기록한다. Generated outputs/TEMP/evidence는 삭제하지 않고 graph 유효성이나
검증 성공을 주장하지 않는다. 이미 ignore1줄을 commit했다면 Root의 exact inverse commit을
사용하고 history reset은 하지 않는다. 타 owner 변경이 있으면 덮어쓰지 않고 반환한다.
설정은 해당 workspace에 유지하며 feature 종료·설치 경로 변경 때 Root가 수명을 검토한다.