# Step 24: Linear Static Orchestration and CLI

## 담당 역할과 필수 스킬

- 담당 역할: `implementation-agent`
- 필수 스킬: `fesa-cpp-msvc-tdd`
- 모든 C++ build/test와 최종 VERIFY는 MSVC x64 Debug 기준으로 수행한다.

## 읽어야 할 파일

- `/AGENTS.md`
- `/docs/ARCHITECTURE.md`
- `/docs/ADR.md`
- `/docs/linear-static-3d-euler-beam/requirements.md`
- `/docs/linear-static-3d-euler-beam/formulation.md`
- `/docs/linear-static-3d-euler-beam/io.md`
- `/docs/linear-static-3d-euler-beam/reference-model.md`
- `/docs/linear-static-3d-euler-beam/implementation-plan.md`
- `/include/fesa/io/abaqus/input_reader.hpp`
- `/include/fesa/io/abaqus/domain_mapper.hpp`
- `/include/fesa/analysis/analysis_model.hpp`
- `/include/fesa/analysis/analysis_state.hpp`
- `/include/fesa/fem/dof_manager.hpp`
- `/include/fesa/assembly/parallel_for.hpp`
- `/include/fesa/assembly/sparse_assembler.hpp`
- `/include/fesa/constraints/essential_constraints.hpp`
- `/include/fesa/solvers/linear/linear_solver.hpp`
- `/include/fesa/assembly/load_assembler.hpp`
- `/include/fesa/results/result_recovery.hpp`
- `/include/fesa/results/results_writer.hpp`
- `/include/fesa/io/hdf5/hdf5_results_writer.hpp`
- `/src/fesa/CMakeLists.txt`
- `/tests/CMakeLists.txt`
- `/reference/cantilever beam/`의 승인된 네 파일

## 소유 파일

- Create: `/include/fesa/analysis/linear_static_analysis.hpp`
- Create: `/src/fesa/analysis/linear_static_analysis.cpp`
- Create: `/include/fesa/app/fesa_application.hpp`
- Create: `/src/fesa/app/fesa_application.cpp`
- Create: `/src/fesa/app/main.cpp`
- Create: `/tests/integration/analysis/linear_static_analysis_test.cpp`
- Create: `/tests/integration/app/fesa_application_test.cpp`
- Create: `/tests/reference/reference_comparison.hpp`
- Create: `/tests/reference/reference_comparison.cpp`
- Create: `/tests/reference/reference_comparison_test.cpp`
- Create: `/tests/reference/b33_reference_comparison_test.cpp`
- Modify: `/src/fesa/CMakeLists.txt`
- Modify: `/tests/CMakeLists.txt`

## 작업

전체 single-step lifecycle과 `fesa.exe` CLI를 구현하고 Step 26이 재사용할 read-only
reference comparison test utility를 완성하라.

```cpp
struct AnalysisRequest { std::filesystem::path inputPath; std::filesystem::path outputPath; };
class Analysis {
public:
    virtual ~Analysis() = default;
    Status run(const AnalysisRequest& request);
protected:
    virtual Status initialize(const AnalysisRequest& request) = 0;
    virtual Status buildAnalysisModel() = 0;
    virtual Status buildDofMapAndSparsePattern() = 0;
    virtual Status assembleAndPartitionStiffness() = 0;
    virtual Status factorize() = 0;
    virtual Status assembleLoadsAndEffectiveRhs() = 0;
    virtual Status substituteAndReconstruct() = 0;
    virtual Status recoverAndWriteResults() = 0;
};
class LinearStaticAnalysis final : public Analysis { /* V0 hook implementations */ };
class FesaApplication {
public:
    int run(const std::vector<std::string>& arguments);
};
```

`LinearStaticAnalysis::run`은 다음 event order를 exact하게 고정하고 각 실패를 diagnostic/
exit category에 연결한다.

```text
parse input -> Domain -> AnalysisModel -> DofManager/sparse pattern
-> parallel element stiffness -> deterministic COO-to-CSR
-> free/constrained partition -> factorize(Kff)
-> assemble full nodal load -> rhs=Ff-Kfc*dc
-> solve substitution -> reconstruct full displacement
-> residual/reaction/element recovery -> atomic HDF5 write
```

Spy/fake adapters를 사용한 orchestration test는 `factorize` event가 load assembly보다 먼저고
`solve`가 factorization을 재수행하지 않음을 검증한다. 실제 production default는
TbbParallelFor, MklPardisoSolver, Hdf5ResultsWriter를 사용한다.

CLI는 `fesa.exe <model.inp> --output <results.h5>`와 output 생략 시 current directory
`results.h5`를 지원한다. Exit code는 `0 success`, `2 usage`, `3 input syntax/semantic`,
`4 model validation`, `5 factorization/substitution`, `6 HDF5 output`이다. Error diagnostics는
stderr에 deterministic order로 쓰고 warning은 output diagnostics에도 보존한다. CMake
executable target은 내부 이름이 다르더라도 `OUTPUT_NAME fesa`를 설정해 Debug output이
실제로 `fesa.exe`가 되게 한다.

Test-only reference support를 `tests/reference/` 아래 구현하라.

- approved input으로 `.harness/build/reference/cantilever-beam-b33/results.h5`를 생성한다.
- legacy displacement/reaction/elemental-force CSV를 read-only로 parse하고 실제 CAE header
  `U-U*`, `UR-UR*`, `RF-RF*`, `RM-RM*`, `SF-SF1`, `SM-SM*`을 canonical component로 normalize한다.
- row matching 전에 expected header, B33 input, unique identity, missing/extra/nonfinite를 검사한다.
- same model/step/frame/quantity/component의 Abaqus-only scale과
  `absolute_floor + 1e-6*reference_scale`로 모든 row를 판정한다.
- section resultants는 node station과 `SF1->N`, `SM1->My`, `SM2->Mz`, `SM3->T`로 비교한다.
- Stress comparison은 N/A로 기록하되 HDF5 stress dataset/test 통과를 요구한다.
- max absolute, component-scale normalized, RMS, norm, worst row/component를
  `.harness/build/reference/cantilever-beam-b33/comparison.json`에 결정적으로 기록한다.
- 같은 JSON에 matched FESA/reference rows, full free residual norm, applied/reaction force와
  origin 기준 moment 합, endpoint consistency를 `physics_evidence`로 기록해 Step 27이
  source/test 변경 없이 물리 sanity를 독립 검토할 수 있게 한다.

Tests는 orchestration order, CLI default/explicit output, all exit codes, output-request no-op,
approved input end-to-end, reference tolerance zero/near-zero/failure cases를 포함한다.
CTest regex/label은 `LinearStaticCli`, `ReferenceComparisonContract`,
`B33ReferenceComparison`으로 실행 가능하게 한다.

## Acceptance Criteria

```powershell
cmake --build .harness/build --config Debug --target fesa_tests
ctest --test-dir .harness/build -C Debug -R "LinearStaticCli|ReferenceComparisonContract|B33ReferenceComparison" --output-on-failure
```

위 command에서 missing orchestration/CLI/reference utility RED를 확인한 후 구현한다.

```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"
cmake --build .harness/build --config Debug
ctest --test-dir .harness/build -C Debug -R "LinearStaticCli|ReferenceComparisonContract|B33ReferenceComparison" --output-on-failure
ctest --test-dir .harness/build -C Debug --show-only=json-v1
ctest --test-dir .harness/build -C Debug --output-on-failure
if (-not (Test-Path '.harness/build/reference/cantilever-beam-b33/results.h5')) { throw 'Missing reference-run HDF5' }
if (-not (Test-Path '.harness/build/reference/cantilever-beam-b33/comparison.json')) { throw 'Missing comparison metrics' }
git diff --exit-code -- reference/
```

## 검증 절차

1. orchestration/CLI/reference contract tests를 먼저 작성하고 targeted RED를 확인한다.
2. exact lifecycle과 minimal CLI/test utility로 targeted GREEN을 만든다.
3. approved input end-to-end, comparison metrics, CTest discovery/full VERIFY를 실행한다.
4. reference artifacts가 unchanged인지 확인한다.
5. `/docs/linear-static-3d-euler-beam/implementation-report.md`의
   Step 24 section에 실제 RED/GREEN/VERIFY command, exit code,
   핵심 output, 변경 파일과 generated HDF5/comparison evidence를 기록한다.
6. Step 24를 `completed`로 갱신하고 CLI path, exit codes, generated HDF5/metrics와
   RED/GREEN/VERIFY evidence를 summary에 기록한다.

## 금지사항

- load assembly를 factorization 앞으로 옮기지 마라. 이유: 승인 runtime order와 반대다.
- factorize와 substitution을 합치지 마라. 이유: solver contract와 repeated RHS 검증을 깨뜨린다.
- B31/multiple-step/transform/`*DLOAD`를 지원하지 마라. 이유: V0 범위 밖이다.
- reference CSV를 보정·rename·rewrite하지 마라. 이유: comparison baseline을 오염시킨다.
- stress reference comparison을 만들어내지 마라. 이유: 승인 범위에서 명시적 N/A다.
- 직접 commit하지 마라. 이유: Harness executor가 담당한다.
