diff --git a/docs/superpowers/plans/2026-07-29-fesa-phase-1.md b/docs/superpowers/plans/2026-07-29-fesa-phase-1.md index f4934b5..09d372f 100644 --- a/docs/superpowers/plans/2026-07-29-fesa-phase-1.md +++ b/docs/superpowers/plans/2026-07-29-fesa-phase-1.md @@ -73,7 +73,7 @@ intentionally differs. | Order | Harness phase | Plan tasks | Independent deliverable | | ---: | --- | --- | --- | -| 0 | `solver-bootstrap` | 1-2 | Reproducible C++20 build, dependency smoke tests, core IDs and diagnostics | +| 0 | `solver-bootstrap` | Harness baseline, 1-2 | Harness self-tests, reproducible C++20 build, dependency smoke tests, core IDs and diagnostics | | 1 | `domain-and-input-skeleton` | 3-4 | Flat or single-Instance B31 input becomes an immutable normalized `Domain` | | 2 | `fem-and-beam-kernel` | 5-6 | Real Timoshenko Beam local stiffness with analytical sanity tests | | 3 | `equation-and-linear-solve` | 7-8 | Deterministic serial CSR system solved by PARDISO | @@ -123,6 +123,9 @@ docs/VALIDATION.md benchmark matrix and qualification result **Files:** +- Create before CMake bootstrap: `tests/harness/test_config.py` +- Create before CMake bootstrap: `tests/harness/test_discovery.py` +- Create before CMake bootstrap: `tests/harness/test_process.py` - Create: `CMakeLists.txt` - Create: `CMakePresets.json` - Create: `cmake/FesaDependencies.cmake` @@ -141,6 +144,11 @@ docs/VALIDATION.md benchmark matrix and qualification result - Produces presets: `windows-debug`, `windows-release` - Later tasks consume the common warning and include-directory policies. +Before the CMake task, add characterization tests for the existing Harness +configuration, project discovery, and validation-result contracts. This +precondition makes the repository-level pytest command collect at least one +test without changing production Harness behavior. + - [ ] **Step 1: Verify required installed packages without changing the machine** Run: diff --git a/phases/solver-bootstrap/index.json b/phases/solver-bootstrap/index.json index dd78e5d..42a85e3 100644 --- a/phases/solver-bootstrap/index.json +++ b/phases/solver-bootstrap/index.json @@ -4,16 +4,21 @@ "steps": [ { "step": 0, - "name": "cmake-project-scaffold", + "name": "harness-self-tests", "status": "pending" }, { "step": 1, - "name": "dependency-smoke-tests", + "name": "cmake-project-scaffold", "status": "pending" }, { "step": 2, + "name": "dependency-smoke-tests", + "status": "pending" + }, + { + "step": 3, "name": "core-ids-and-diagnostics", "status": "pending" } diff --git a/phases/solver-bootstrap/step0.md b/phases/solver-bootstrap/step0.md index e5a264a..11ddcc7 100644 --- a/phases/solver-bootstrap/step0.md +++ b/phases/solver-bootstrap/step0.md @@ -1,62 +1,65 @@ -# Step 0: CMake Project Scaffold +# Step 0: Harness Self Tests ## 읽어야 할 파일 -먼저 아래 파일을 모두 읽고 저장소 계약을 파악하라. +먼저 아래 파일을 모두 읽고 현재 Harness 계약을 파악하라. - `/AGENTS.md` -- `/docs/PRD.md` -- `/docs/ARCHITECTURE.md` -- `/docs/ADR.md` - `/docs/HARNESS.md` -- `/docs/superpowers/plans/2026-07-29-fesa-phase-1.md` - `/.harness/config.example.json` +- `/pytest.ini` +- `/scripts/msvc_harness/config.py` +- `/scripts/msvc_harness/discovery.py` +- `/scripts/msvc_harness/models.py` +- `/scripts/msvc_harness/process.py` +- `/scripts/msvc_harness/tdd_policy.py` +- `/scripts/hooks/stop_validation.py` ## 작업 -C++20/MSVC x64 프로젝트의 최소 실행 가능한 build/test 뼈대를 만든다. +현재 Harness Python 검증이 테스트 0개 수집으로 실패하는 기준선 문제를 해결한다. +production Harness 동작은 변경하지 않고, 이미 문서화된 핵심 계약을 검증하는 최소 +자가 테스트를 작성한다. -- `CMakeLists.txt`, `CMakePresets.json`, `cmake/FesaDependencies.cmake`, - `.harness/config.json`, `tests/CMakeLists.txt`를 만든다. -- 실제 target은 `fesa_core` 정적 라이브러리와 `fesa` CLI 두 개만 만든다. -- `include/fesa/core/version.hpp`, `src/fesa/core/version.cpp`, - `src/fesa/cli/main.cpp`에 다음 계약을 구현한다. +- 다음 파일을 만든다. + - `tests/harness/test_config.py` + - `tests/harness/test_discovery.py` + - `tests/harness/test_process.py` +- `test_config.py`는 기본 설정 로드, repository 밖 경로 거부, CMake preset 필드의 + all-or-none 규칙을 실제 임시 디렉터리로 검증한다. +- `test_discovery.py`는 빈 저장소 건너뛰기, CMake 우선 감지, C/C++ 파일만 존재하는 + orphan 프로젝트 거부를 실제 임시 디렉터리로 검증한다. +- `test_process.py`는 CTest JSON에 테스트가 0개이면 실패하고 하나 이상이면 통과하는 + 결과 검사를 안전한 fake command runner로 검증한다. subprocess shell을 사용하지 + 않는 계약도 유지한다. +- 각 테스트는 공개 함수인 `load_config`, `discover_project`, `execute_plan`을 통해 + 관찰 가능한 동작만 확인한다. -```cpp -namespace fesa { -[[nodiscard]] std::string_view version() noexcept; -} -``` - -- CLI는 이 step에서 `fesa --version`만 처리한다. -- 먼저 `VersionCommand` CTest를 등록해 실패를 확인한 뒤 최소 구현한다. -- `windows-debug`, `windows-release` configure/build/test preset을 정의한다. -- build 산출물은 `out/build/` 아래에만 둔다. -- MSVC가 아니거나 x64가 아니면 configure 단계에서 명확히 실패시킨다. +이 step은 기존 production 동작의 characterization test만 추가한다. 테스트가 기존 +문서 계약과 다르게 실패하면 production 코드를 임의로 고치지 말고 step을 `blocked`로 +표시하고 불일치를 기록한다. ## Acceptance Criteria ```powershell uv run --with pytest python -m pytest -v -rs -cmake --preset windows-debug -cmake --build --preset windows-debug -ctest --preset windows-debug -R VersionCommand --output-on-failure -ctest --preset windows-debug --output-on-failure ``` -`fesa --version`은 비어 있지 않은 FESA 버전을 출력하고 종료 코드 0을 반환해야 한다. -CTest는 0개가 아니어야 한다. +pytest가 테스트를 한 개 이상 수집하고 모든 테스트가 통과해야 한다. ## 검증 절차 -1. 테스트가 구현 전 실패하는 것을 확인한다. -2. Acceptance Criteria 명령을 새로 실행한다. -3. MSVC/C++20/x64와 산출물 경로를 확인한다. -4. 성공 시 index의 step을 `completed`로 바꾸고 생성 파일을 summary에 기록한다. -5. 도구가 없으면 자동 설치하지 말고 `blocked`와 정확한 누락 항목을 기록한다. +1. 변경 전 명령이 테스트 0개 수집으로 실패한다는 기존 기준선 기록을 확인한다. +2. 위 세 테스트 파일만 추가한다. +3. Acceptance Criteria 명령을 새로 실행한다. +4. production Harness 파일에 변경이 없는지 `git diff`로 확인한다. +5. 성공 시 index의 step을 `completed`로 바꾸고 테스트 파일을 summary에 기록한다. ## 금지사항 -- 외부 패키지를 다운로드하지 마라. 이유: 사전 설치 의존성 정책을 위반한다. -- MKL, TBB, HDF5 기능을 구현하지 마라. 이유: 다음 step의 독립 범위다. -- 빈 미래 모듈을 만들지 마라. 이유: Phase 1 최소 실체화 원칙을 위반한다. +- production Harness 코드를 변경하지 마라. 이유: 이 step은 기존 계약의 기준선 + 테스트만 마련한다. +- C++ 프로젝트 파일이나 `.harness/config.json`을 만들지 마라. 이유: 다음 step의 + 독립 범위다. +- subprocess로 실제 CMake, MSBuild 또는 CTest를 실행하지 마라. 이유: self-test가 + 개발 머신 도구 설치 상태에 의존하게 된다. diff --git a/phases/solver-bootstrap/step1.md b/phases/solver-bootstrap/step1.md index c83ca01..30e020d 100644 --- a/phases/solver-bootstrap/step1.md +++ b/phases/solver-bootstrap/step1.md @@ -1,55 +1,65 @@ -# Step 1: Dependency Smoke Tests +# Step 1: CMake Project Scaffold ## 읽어야 할 파일 +먼저 아래 파일을 모두 읽고 저장소 계약을 파악하라. + - `/AGENTS.md` - `/docs/PRD.md` - `/docs/ARCHITECTURE.md` - `/docs/ADR.md` - `/docs/HARNESS.md` - `/docs/superpowers/plans/2026-07-29-fesa-phase-1.md` -- `/CMakeLists.txt` -- `/CMakePresets.json` -- `/cmake/FesaDependencies.cmake` -- `/.harness/config.json` -- `/tests/CMakeLists.txt` - -이전 step의 CMake target과 preset을 그대로 확장하라. +- `/.harness/config.example.json` +- `/tests/harness/test_config.py` +- `/tests/harness/test_discovery.py` +- `/tests/harness/test_process.py` ## 작업 -사전 설치된 oneMKL, oneTBB, HDF5 C API, GoogleTest/GoogleMock을 CMake imported -target으로 찾고 링크 계약을 검증한다. +C++20/MSVC x64 프로젝트의 최소 실행 가능한 build/test 뼈대를 만든다. -- `tests/unit/dependencies/dependency_smoke_test.cpp`를 먼저 작성한다. -- 테스트는 MKL의 작은 vector 연산, TBB의 제한된 parallel loop, HDF5 임시 파일 - 생성·닫기, GoogleTest 실행을 확인한다. -- `FesaDependencies.cmake`는 `MKL::MKL`, TBB imported target, HDF5 C target, - GoogleTest target을 제공해야 한다. -- oneMKL은 LP64, dynamic link, TBB threading 조합을 사용한다. -- runtime DLL 또는 architecture 불일치는 configure diagnostic으로 보고한다. +- `CMakeLists.txt`, `CMakePresets.json`, `cmake/FesaDependencies.cmake`, + `.harness/config.json`, `tests/CMakeLists.txt`를 만든다. +- 실제 target은 `fesa_core` 정적 라이브러리와 `fesa` CLI 두 개만 만든다. +- `include/fesa/core/version.hpp`, `src/fesa/core/version.cpp`, + `src/fesa/cli/main.cpp`에 다음 계약을 구현한다. + +```cpp +namespace fesa { +[[nodiscard]] std::string_view version() noexcept; +} +``` + +- CLI는 이 step에서 `fesa --version`만 처리한다. +- 먼저 `VersionCommand` CTest를 등록해 실패를 확인한 뒤 최소 구현한다. +- `windows-debug`, `windows-release` configure/build/test preset을 정의한다. +- build 산출물은 `out/build/` 아래에만 둔다. +- MSVC가 아니거나 x64가 아니면 configure 단계에서 명확히 실패시킨다. ## Acceptance Criteria ```powershell +uv run --with pytest python -m pytest -v -rs cmake --preset windows-debug cmake --build --preset windows-debug -ctest --preset windows-debug -R DependencySmoke --output-on-failure +ctest --preset windows-debug -R VersionCommand --output-on-failure ctest --preset windows-debug --output-on-failure ``` -네 의존성을 실제 호출하는 smoke test가 통과해야 하며 새 MSVC 경고가 없어야 한다. +`fesa --version`은 비어 있지 않은 FESA 버전을 출력하고 종료 코드 0을 반환해야 한다. +CTest는 0개가 아니어야 한다. ## 검증 절차 -1. smoke test를 먼저 추가하고 link 또는 실행 실패를 확인한다. -2. dependency discovery와 target link만 최소 수정한다. -3. 전체 configure/build/test를 새로 실행한다. -4. 성공 시 정확한 imported target과 탐색 파일을 summary에 기록한다. -5. 패키지나 MSVC가 없으면 세 차례 임의 수정하지 말고 `blocked`로 종료한다. +1. 테스트가 구현 전 실패하는 것을 확인한다. +2. Acceptance Criteria 명령을 새로 실행한다. +3. MSVC/C++20/x64와 산출물 경로를 확인한다. +4. 성공 시 index의 step을 `completed`로 바꾸고 생성 파일을 summary에 기록한다. +5. 도구가 없으면 자동 설치하지 말고 `blocked`와 정확한 누락 항목을 기록한다. ## 금지사항 -- FetchContent, vcpkg, Conan 또는 다운로드를 추가하지 마라. 이유: ADR-002 위반이다. -- vendor 절대경로를 public header에 노출하지 마라. 이유: backend 격리를 깨뜨린다. -- solver 기능을 구현하지 마라. 이유: 이 step은 build dependency 계약만 다룬다. +- 외부 패키지를 다운로드하지 마라. 이유: 사전 설치 의존성 정책을 위반한다. +- MKL, TBB, HDF5 기능을 구현하지 마라. 이유: 다음 step의 독립 범위다. +- 빈 미래 모듈을 만들지 마라. 이유: Phase 1 최소 실체화 원칙을 위반한다. diff --git a/phases/solver-bootstrap/step2.md b/phases/solver-bootstrap/step2.md index 6f867e3..5adeebd 100644 --- a/phases/solver-bootstrap/step2.md +++ b/phases/solver-bootstrap/step2.md @@ -1,4 +1,4 @@ -# Step 2: Core IDs and Diagnostics +# Step 2: Dependency Smoke Tests ## 읽어야 할 파일 @@ -6,67 +6,50 @@ - `/docs/PRD.md` - `/docs/ARCHITECTURE.md` - `/docs/ADR.md` +- `/docs/HARNESS.md` - `/docs/superpowers/plans/2026-07-29-fesa-phase-1.md` - `/CMakeLists.txt` +- `/CMakePresets.json` +- `/cmake/FesaDependencies.cmake` +- `/.harness/config.json` - `/tests/CMakeLists.txt` -- `/include/fesa/core/version.hpp` + +이전 step의 CMake target과 preset을 그대로 확장하라. ## 작업 -외부 라이브러리에 의존하지 않는 `core` 값 타입을 TDD로 구현한다. +사전 설치된 oneMKL, oneTBB, HDF5 C API, GoogleTest/GoogleMock을 CMake imported +target으로 찾고 링크 계약을 검증한다. -- 생성 파일: - `include/fesa/core/entity_id.hpp`, `vec3.hpp`, `source_location.hpp`, - `diagnostic.hpp`, `status.hpp`와 대응 테스트 -- 인터페이스: - -```cpp -template -class EntityId final { -public: - explicit constexpr EntityId(std::int64_t value); - [[nodiscard]] constexpr std::int64_t value() const noexcept; - auto operator<=>(const EntityId&) const = default; -}; - -struct Vec3 final { double x; double y; double z; }; -struct SourceLocation final { - std::filesystem::path file; - std::size_t line; - std::size_t column; -}; -enum class DiagnosticStage { io, syntax, semantic, model, equation, solver, results, validation }; -enum class Severity { warning, error }; -struct Diagnostic final { - DiagnosticStage stage; - Severity severity; - std::string code; - std::string message; - std::optional source; -}; -``` - -- typed ID의 잘못된 암시 변환, 음수 ID, nonfinite vector와 diagnostic source 보존을 - 실패 테스트로 먼저 고정한다. +- `tests/unit/dependencies/dependency_smoke_test.cpp`를 먼저 작성한다. +- 테스트는 MKL의 작은 vector 연산, TBB의 제한된 parallel loop, HDF5 임시 파일 + 생성·닫기, GoogleTest 실행을 확인한다. +- `FesaDependencies.cmake`는 `MKL::MKL`, TBB imported target, HDF5 C target, + GoogleTest target을 제공해야 한다. +- oneMKL은 LP64, dynamic link, TBB threading 조합을 사용한다. +- runtime DLL 또는 architecture 불일치는 configure diagnostic으로 보고한다. ## Acceptance Criteria ```powershell +cmake --preset windows-debug cmake --build --preset windows-debug -ctest --preset windows-debug -R "Core|Diagnostic|EntityId" --output-on-failure +ctest --preset windows-debug -R DependencySmoke --output-on-failure ctest --preset windows-debug --output-on-failure ``` +네 의존성을 실제 호출하는 smoke test가 통과해야 하며 새 MSVC 경고가 없어야 한다. + ## 검증 절차 -1. production header 전에 실패하는 GoogleTest를 작성한다. -2. 최소 값 타입만 구현한다. -3. focused test와 전체 CTest를 실행한다. -4. `core`가 MKL, TBB, HDF5, Abaqus header를 include하지 않는지 확인한다. -5. index와 summary를 갱신한다. +1. smoke test를 먼저 추가하고 link 또는 실행 실패를 확인한다. +2. dependency discovery와 target link만 최소 수정한다. +3. 전체 configure/build/test를 새로 실행한다. +4. 성공 시 정확한 imported target과 탐색 파일을 summary에 기록한다. +5. 패키지나 MSVC가 없으면 세 차례 임의 수정하지 말고 `blocked`로 종료한다. ## 금지사항 -- 단위 변환 시스템을 만들지 마라. 이유: FESA는 일관 단위계만 사용한다. -- 범용 reflection이나 serialization을 만들지 마라. 이유: 요구되지 않았다. -- equation ID를 정의하지 마라. 이유: `DofManager` 단계의 책임이다. +- FetchContent, vcpkg, Conan 또는 다운로드를 추가하지 마라. 이유: ADR-002 위반이다. +- vendor 절대경로를 public header에 노출하지 마라. 이유: backend 격리를 깨뜨린다. +- solver 기능을 구현하지 마라. 이유: 이 step은 build dependency 계약만 다룬다. diff --git a/phases/solver-bootstrap/step3.md b/phases/solver-bootstrap/step3.md new file mode 100644 index 0000000..c3dbb59 --- /dev/null +++ b/phases/solver-bootstrap/step3.md @@ -0,0 +1,73 @@ +# Step 3: Core IDs and Diagnostics + +## 읽어야 할 파일 + +- `/AGENTS.md` +- `/docs/PRD.md` +- `/docs/ARCHITECTURE.md` +- `/docs/ADR.md` +- `/docs/superpowers/plans/2026-07-29-fesa-phase-1.md` +- `/CMakeLists.txt` +- `/tests/CMakeLists.txt` +- `/include/fesa/core/version.hpp` +- `/tests/unit/dependencies/dependency_smoke_test.cpp` + +## 작업 + +외부 라이브러리에 의존하지 않는 `core` 값 타입을 TDD로 구현한다. + +- 생성 파일: + `include/fesa/core/entity_id.hpp`, `vec3.hpp`, `source_location.hpp`, + `diagnostic.hpp`, `status.hpp`와 대응 테스트 +- 인터페이스: + +```cpp +template +class EntityId final { +public: + explicit constexpr EntityId(std::int64_t value); + [[nodiscard]] constexpr std::int64_t value() const noexcept; + auto operator<=>(const EntityId&) const = default; +}; + +struct Vec3 final { double x; double y; double z; }; +struct SourceLocation final { + std::filesystem::path file; + std::size_t line; + std::size_t column; +}; +enum class DiagnosticStage { io, syntax, semantic, model, equation, solver, results, validation }; +enum class Severity { warning, error }; +struct Diagnostic final { + DiagnosticStage stage; + Severity severity; + std::string code; + std::string message; + std::optional source; +}; +``` + +- typed ID의 잘못된 암시 변환, 음수 ID, nonfinite vector와 diagnostic source 보존을 + 실패 테스트로 먼저 고정한다. + +## Acceptance Criteria + +```powershell +cmake --build --preset windows-debug +ctest --preset windows-debug -R "Core|Diagnostic|EntityId" --output-on-failure +ctest --preset windows-debug --output-on-failure +``` + +## 검증 절차 + +1. production header 전에 실패하는 GoogleTest를 작성한다. +2. 최소 값 타입만 구현한다. +3. focused test와 전체 CTest를 실행한다. +4. `core`가 MKL, TBB, HDF5, Abaqus header를 include하지 않는지 확인한다. +5. index와 summary를 갱신한다. + +## 금지사항 + +- 단위 변환 시스템을 만들지 마라. 이유: FESA는 일관 단위계만 사용한다. +- 범용 reflection이나 serialization을 만들지 마라. 이유: 요구되지 않았다. +- equation ID를 정의하지 마라. 이유: `DofManager` 단계의 책임이다.