docs: prepare MSVC Ninja environment recovery

This commit is contained in:
KOKO\Mimi
2026-09-06 16:28:38 +09:00
parent 96efbffa42
commit 7d714c2713
5 changed files with 344 additions and 32 deletions
@@ -253,36 +253,7 @@ foreach ($mfeRun in $mfePytestRuns) {
}
```
## E8. Attempt3 후의 정상 backend 대안
E7의 짧은 TEMP/TMP는 actual native build shell에 적용됐지만 FileTracker 접근 거부가
재발했다. 환경 누락으로 해석하지 않는다. 현재 우선 작업은
[R8](implementation-recovery.md#r8-attempt3의-blocked-기록-보존)의 실패 보존이며,
아래는 다음 bounded delta에서 구체화할 정상 backend 대안이다. 설정·configure·build·
Executor 재실행은 이번 Planning에서 수행하지 않았다.
저장소의 [MSVC Ninja preset](../../CMakePresets.json)과
[local config 예시](../../.harness/config.example.json)는 동일 Ninja graph를 manual 검증과
registered Stop이 선택할 수 있는 기존 경로다. MSVC/x64/Debug, 테스트·Hook·유한
7200/7500/14400초 한도와 workspace-write는 유지한다. 기존 config/adapter에 없는
Ninja Multi-Config 선택 기능이나 새 public preset은 만들지 않는다.
Single-config의 실제 archive 출력에는 기존 reference helper가 확인하는
`src/fesa/Debug/fesa_solver.lib`가 필요하다. 표준 CMake cache 변수
`CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG`로 새 ignored build의 해당 디렉터리를 지정하는
대안을 검토했다. Local CMake help는 archive target 속성 초기화 의미를 확인했지만,
이 선택의 actual native configure/build/full VERIFY 성공은 아직 미검증이다.
실제 library 위치/hash/mtime와 기존 cache freshness를 확인해야 하며 파일 복사나 mtime
조작으로 cache guard를 만족시키지 않는다. 기존 VS cache와 attempt evidence는 보존한다.
다음 delta에서 Root 소유의 exact ignored config/환경 setup과 Coordinator 소유의
unchanged Executor invocation을 분리해 확정한다. Prep/main의 기존 고정 build 경로를
새 preset graph에 대응시키는 명령과 registered Stop의 같은 graph 선택까지 명시한 뒤
활성화한다. 현재 E8 요약만으로 이전 명령을 임의 대체하거나 재실행하지 않는다.
기존 named Python/UV offline, 짧은 새 TEMP와 pytest basetemp, process 환경 복원은 유지한다.
이 정상 대안에는 새 DFA 승인을 요청하지 않는다. 이전 one-time DFA 승인은 소진됐으며,
정상 대안이 실제 실패하면 그 evidence로 exact-run의 새 결정을 별도로 다룬다.
Main은 prep 전체 gate와 Coordinator의 사후 감사 전 활성화하지 않는다.
CMake/MSVC/x64/Debug configure/build와 전체 CTest discovery/run은 기존 Step0의 정확한
명령을 같은 inherited TEMP/TMP에서 별도로 실행한다. 실패 시 actual command/exit와 남은
@@ -396,3 +367,260 @@ foreach ($mfeRun in $mfePytestRuns) {
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까지 권한을 확대하지 않는다.