# FESA Phase 1 Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Build and internally qualify a C++20/MSVC linear-static finite-element solver that reads the agreed Abaqus `.inp` subset, solves 2-node 3D Timoshenko Beam models, and writes self-contained HDF5 results.

**Architecture:** Implement one end-to-end vertical slice first, then complete the input, numerical, parallel, result, and reference-verification contracts behind the module boundaries in `docs/ARCHITECTURE.md`. Keep the semantic model independent of Abaqus syntax and isolate oneMKL, oneTBB, and HDF5 behind adapters.

**Tech Stack:** C++20, Visual Studio 2026 MSVC v145 x64, CMake/CMake Presets/CTest, GoogleTest/GoogleMock, Intel oneAPI MKL PARDISO, Intel oneAPI TBB, HDF5 C API, Python 3 Harness.

## Global Constraints

- Only the Phase 1 scope in `docs/PRD.md` may be implemented.
- Public headers live under `include/fesa/`; implementations live under `src/fesa/`; tests live under `tests/`.
- Write a failing test before every production behavior change.
- Do not add a fake stiffness matrix or a test-only solver path.
- Do not introduce empty future modules, a generic registry, MPC, iterative solvers, nonlinear state, or additional element types.
- FESA performs no unit conversion.
- Abaqus inputs may use a flat mesh or one untransformed Part/Assembly/Instance.
- When transverse shear stiffness is omitted, use \(A_{sy}=A_{sz}=5A/6\) and `SCF=0`.
- Reference comparison requests name their quantities and CSV paths; no per-model metadata file is required.
- A pipeline milestone is not a numerically qualified release.
- No new MSVC warnings are allowed.
- The existing Harness contract in `docs/HARNESS.md` and `.agents/skills/harness/SKILL.md` remains unchanged.

## Environment Audit

The planning environment currently has:

- CMake 4.4.0
- Visual Studio Community 2026 at `C:/Program Files/Microsoft Visual Studio/18/Community`
- MSVC v145 tools at `C:/Program Files/Microsoft Visual Studio/18/Community/VC/Tools/MSVC/14.51.36231`
- CMake generator `Visual Studio 18 2026`
- oneMKL CMake package at `C:/Program Files (x86)/Intel/oneAPI/2026.1/lib/cmake/mkl`
- oneTBB CMake package at `C:/Program Files (x86)/Intel/oneAPI/2026.1/lib/cmake/tbb`
- HDF5 2.1.1 CMake package at `C:/Program Files/HDF_Group/HDF5/2.1.1/cmake`
- GoogleTest/GoogleMock v1.17 VS2026/v145 x64 package at
  `C:/Users/baram/AppData/Local/FESA/dependencies/googletest-1.17.0-v145-x64-crt`
- `MSBuild.exe` at `C:/Program Files/Microsoft Visual Studio/18/Community/MSBuild/Current/Bin/MSBuild.exe`

The solver bootstrap must stop as `blocked` rather than downloading packages if a dependency
required by its current step is unavailable.

## Required Research Record

Before the related production task begins, record the relevant equations,
assumptions, API contracts, and FESA decisions from these sources:

- K. J. Bathe, *Finite Element Procedures*, 2nd edition: finite-element
  discretization, assembly, constraints, and verification.
- T. J. R. Hughes, *The Finite Element Method: Linear Static and Dynamic
  Finite Element Analysis*: variational formulation and numerical integration.
- K. J. Bathe and S. Bolourchi, “Large Displacement Analysis of
  Three-Dimensional Beam Structures,” 1979: three-dimensional isoparametric
  Beam coordinates and transformations. Phase 1 uses only the linearized
  subset.
- T. J. R. Hughes, R. L. Taylor, and W. Kanoknukulchai, “A Simple and
  Efficient Finite Element for Plate Bending,” 1977: selective reduced
  integration rationale. Do not copy its plate kinematics into the Beam
  formulation.
- [Abaqus 2024—Choosing a Beam Element](https://docs.software.vt.edu/abaqusv2024/English/SIMACAEELMRefMap/simaelm-c-beamelem.htm):
  B31 shear-flexible behavior and slenderness compensation.
- [Abaqus 2024—BEAM GENERAL SECTION](https://docs.software.vt.edu/abaqusv2024/English/SIMACAEKEYRefMap/simakey-r-beamgeneralsection.htm):
  supported general-section data and orientation.
- [Intel oneMKL PARDISO reference](https://www.intel.com/content/www/us/en/docs/onemkl/developer-reference-c/2025-0/pardiso.html):
  matrix type, CSR indexing, phases, checks, and error codes.
- [oneTBB reduction guide](https://uxlfoundation.github.io/oneTBB/main/tbb_userguide/design_patterns/Reduction.html):
  deterministic floating-point reduction.
- [HDF5 data model](https://support.hdfgroup.org/documentation/hdf5/latest/_intro_h_d_f5.html):
  groups, datasets, dataspaces, and attributes.
- [CMake FindHDF5](https://cmake.org/cmake/help/latest/module/FindHDF5.html):
  installed C-library discovery and imported targets.

`docs/formulation/timoshenko-beam-3d.md`, `docs/HDF5_SCHEMA.md`, and
`docs/VALIDATION.md` must cite the applicable source and state where FESA
intentionally differs.

## Harness Phase Map

| Order | Harness phase | Plan tasks | Independent deliverable |
| ---: | --- | --- | --- |
| 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 |
| 4 | `results-and-pipeline` | 9-10 | CLI runs one deck end-to-end and writes readable HDF5 |
| 5 | `abaqus-subset-completion` | 11 | Full agreed scoped keyword, set, material, section, load, and BC subset |
| 6 | `deterministic-parallel-assembly` | 12 | oneTBB assembly matches serial output across thread counts |
| 7 | `result-contract-completion` | 13 | Complete self-contained HDF5 schema and Beam result recovery |
| 8 | `beam-reference-qualification` | 14 | Analytical suite and available Abaqus displacement/reaction data pass tolerance |
| 9 | `internal-release` | 15 | Debug/Release validation, 100k-DOF benchmark, install tree, reports |

Create `phases/index.json`, phase indexes, and step files only after this plan and its phase split are approved.

## Planned File Map

```text
CMakeLists.txt                         root targets and project policies
CMakePresets.json                     windows-debug/windows-release workflows
cmake/FesaDependencies.cmake          installed dependency discovery
.harness/config.json                  Harness CMake preset selection
include/fesa/core/                     IDs, vectors, source locations, diagnostics
include/fesa/model/                    immutable semantic entities and Domain
include/fesa/io/abaqus/                deck records, parser, semantic mapper
include/fesa/fem/                      quadrature, shape functions, frames, DOFs
include/fesa/elements/beam/            Beam3D2 input, contribution, recovery contract
include/fesa/assembly/                 symmetric COO/CSR and assembly
include/fesa/constraints/              essential-BC elimination and reconstruction
include/fesa/solvers/linear/           backend contract and PARDISO adapter
include/fesa/results/                  step/frame/field/diagnostic result model
include/fesa/io/hdf5/                  schema constants, writer, reader
include/fesa/analysis/                 analysis lifecycle and linear-static procedure
include/fesa/validation/               comparison metrics and CSV mapping
src/fesa/                              implementations mirroring public modules
src/fesa/cli/main.cpp                  thin `fesa` command-line executable
tests/unit/                            single-module behavior
tests/integration/                     public input-to-output path
tests/reference/                       FESA HDF5 to golden CSV comparisons
tests/fixtures/                        small invalid and valid decks
reference/cantilever beam/             supplied Abaqus input and available golden CSV data
docs/formulation/                      signed-off equations and conventions
docs/HDF5_SCHEMA.md                    versioned output contract
docs/VALIDATION.md                     benchmark matrix and qualification result
```

---

### Task 1: CMake, Installed Dependencies, and Test Bootstrap

**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`
- Create: `.harness/config.json`
- Create: `include/fesa/core/version.hpp`
- Create: `src/fesa/core/version.cpp`
- Create: `src/fesa/cli/main.cpp`
- Create: `tests/CMakeLists.txt`
- Create: `tests/unit/core/version_test.cpp`
- Modify: `.gitignore`

**Interfaces:**

- Produces: `std::string_view fesa::version() noexcept`
- Produces CMake targets: `fesa_core`, `fesa_cli`, `fesa_unit_tests`
- 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:

```powershell
cmake --version
& "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" `
  -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 `
  -property installationPath
Test-Path "C:\Program Files\Microsoft Visual Studio\18\Community\MSBuild\Current\Bin\MSBuild.exe"
Get-ChildItem "C:\Program Files\Microsoft Visual Studio\18\Community\VC\Tools\MSVC" -Directory
Get-ChildItem "C:\Program Files (x86)\Intel\oneAPI" -Recurse -Filter MKLConfig.cmake
Get-ChildItem "C:\Program Files (x86)\Intel\oneAPI" -Recurse -Filter TBBConfig.cmake
Get-ChildItem "C:\Program Files" -Recurse -Filter hdf5-config.cmake
Get-ChildItem "C:\Program Files" -Recurse -Filter GTestConfig.cmake
```

Expected: Visual Studio 2026/MSVC v145, oneMKL, oneTBB, HDF5, and GoogleTest
are all discoverable before the step that consumes each dependency. If a required item is
missing, mark that Harness step `blocked` and name it; do not download it.

- [ ] **Step 2: Write the failing version test**

```cpp
#include <gtest/gtest.h>
#include <fesa/core/version.hpp>

TEST(Version, ReportsPhaseOneSemanticVersion) {
    EXPECT_EQ(fesa::version(), "0.1.0");
}
```

- [ ] **Step 3: Add configure files and verify the test fails before implementation**

`cmake/FesaDependencies.cmake` must set the oneMKL choices before package discovery:

```cmake
cmake_minimum_required(VERSION 3.30)

set(MKL_LINK dynamic)
set(MKL_THREADING tbb_thread)
set(MKL_INTERFACE lp64)
find_package(MKL CONFIG REQUIRED)
find_package(TBB CONFIG REQUIRED COMPONENTS tbb)
find_package(HDF5 REQUIRED COMPONENTS C)
find_package(GTest CONFIG REQUIRED)
```

Link `MKL::MKL`, `TBB::tbb`, `HDF5::HDF5`, and `GTest::gtest_main` only to targets that use them. Configure and build:
Compile FESA targets with `/W4 /permissive- /EHsc`; do not apply FESA warning
flags to imported targets.

```powershell
cmake --preset windows-debug
cmake --build --preset windows-debug
```

Expected: build fails because `fesa::version()` has no definition.

- [ ] **Step 4: Implement the minimum version API and thin CLI**

```cpp
namespace fesa {
std::string_view version() noexcept;
}
```

The CLI accepts only `--version` in this task. Any other command prints usage and returns a nonzero exit code.

- [ ] **Step 5: Run focused and full bootstrap validation**

```powershell
cmake --build --preset windows-debug
ctest --preset windows-debug --output-on-failure
.\out\build\windows-debug\Debug\fesa.exe --version
```

Expected: one test passes and the CLI prints `0.1.0`.

- [ ] **Step 6: Commit**

```powershell
git add CMakeLists.txt CMakePresets.json cmake .harness/config.json include/fesa/core/version.hpp src/fesa/core/version.cpp src/fesa/cli/main.cpp tests/CMakeLists.txt tests/unit/core/version_test.cpp .gitignore
git commit -m "build: bootstrap FESA CMake project"
```

---

### Task 2: Core IDs, Vectors, Source Locations, and Diagnostics

**Files:**

- Create: `include/fesa/core/entity_id.hpp`
- Create: `include/fesa/core/vec3.hpp`
- Create: `include/fesa/core/source_location.hpp`
- Create: `include/fesa/core/diagnostic.hpp`
- Create: `include/fesa/core/status.hpp`
- Create: `tests/unit/core/entity_id_test.cpp`
- Create: `tests/unit/core/vec3_test.cpp`
- Create: `tests/unit/core/diagnostic_test.cpp`
- Create: `tests/unit/core/status_test.cpp`
- Modify: `CMakeLists.txt`
- Modify: `tests/CMakeLists.txt`

**Interfaces:**

```cpp
template<class Tag>
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;
};

[[nodiscard]] bool is_finite(Vec3 value) noexcept;

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<SourceLocation> source;
};

struct Status final {
    bool succeeded;
    std::vector<Diagnostic> diagnostics;
};
```

- [ ] **Step 1: Write failing tests**

Cover negative entity-ID rejection, typed-ID non-interchangeability at compile time, finite `Vec3` validation, full diagnostic context preservation, and success/failure status preservation.

- [ ] **Step 2: Run focused tests and confirm compile or assertion failure**

```powershell
cmake --build --preset windows-debug
ctest --preset windows-debug -R "Core" --output-on-failure
```

Expected: failure because the core types do not exist.

- [ ] **Step 3: Implement only the declared core types**

Use `double` for all Phase 1 real values and `std::int64_t` for external Abaqus IDs. Do not introduce a unit library, generic error monad, matrix class, or logging framework.

- [ ] **Step 4: Run all tests**

```powershell
cmake --build --preset windows-debug
ctest --preset windows-debug --output-on-failure
```

Expected: all tests pass with no new MSVC warnings.

- [ ] **Step 5: Commit**

```powershell
git add include/fesa/core tests/unit/core CMakeLists.txt tests/CMakeLists.txt
git commit -m "feat(core): add typed IDs and diagnostics"
```

---

### Task 3: Immutable Semantic Domain

**Files:**

- Create: `include/fesa/model/ids.hpp`
- Create: `include/fesa/model/entity_origin.hpp`
- Create: `include/fesa/model/node.hpp`
- Create: `include/fesa/model/material.hpp`
- Create: `include/fesa/model/beam_section.hpp`
- Create: `include/fesa/model/beam_element.hpp`
- Create: `include/fesa/model/entity_set.hpp`
- Create: `include/fesa/model/step_definition.hpp`
- Create: `include/fesa/model/domain.hpp`
- Create: `include/fesa/model/domain_builder.hpp`
- Create: `src/fesa/model/domain.cpp`
- Create: `src/fesa/model/domain_builder.cpp`
- Create: `tests/unit/model/domain_builder_test.cpp`
- Modify: root and test CMake files

**Interfaces:**

```cpp
using NodeId = EntityId<struct NodeTag>;
using ElementId = EntityId<struct ElementTag>;
using MaterialId = EntityId<struct MaterialTag>;
using SectionId = EntityId<struct SectionTag>;

struct EntityOrigin final {
    std::string part_name;
    std::string instance_name;
    std::int64_t local_label;
};
struct Node final { NodeId id; EntityOrigin origin; Vec3 position; };
struct IsotropicElastic final { MaterialId id; std::string name; double young; double poisson; };
struct BeamSection final {
    SectionId id;
    std::string name;
    double area;
    double iy;
    double iz;
    double torsion_j;
    double shear_area_y;
    double shear_area_z;
    Vec3 orientation;
    std::vector<std::array<double, 2>> recovery_points;
};
struct BeamElement final {
    ElementId id;
    EntityOrigin origin;
    std::array<NodeId, 2> nodes;
    MaterialId material;
    SectionId section;
};
struct NodeSet final { std::string name; std::vector<NodeId> members; };
struct ElementSet final { std::string name; std::vector<ElementId> members; };
struct PrescribedDof final { NodeId node; std::uint8_t dof; double value; };
struct NodalLoad final { NodeId node; std::array<double, 6> values; };
struct StepDefinition final {
    std::string name;
    std::vector<PrescribedDof> prescribed_dofs;
    std::vector<NodalLoad> nodal_loads;
};

class Domain final {
public:
    [[nodiscard]] std::span<const Node> nodes() const noexcept;
    [[nodiscard]] std::span<const BeamElement> beam_elements() const noexcept;
    [[nodiscard]] const Node& node(NodeId id) const;
    [[nodiscard]] const Node& node(const EntityOrigin& origin) const;
};

struct DomainBuildResult final {
    std::optional<Domain> domain;
    std::vector<Diagnostic> diagnostics;
};

class DomainBuilder final {
public:
    void add_node(Node value);
    void add_material(IsotropicElastic value);
    void add_section(BeamSection value);
    void add_beam_element(BeamElement value);
    void add_node_set(NodeSet value);
    void add_element_set(ElementSet value);
    void set_step(StepDefinition value);
    [[nodiscard]] DomainBuildResult build() &&;
};
```

`DomainBuilder::build()` returns either one immutable `Domain` or a nonempty
diagnostic list. It must resolve every reference and reject duplicate internal
IDs, duplicate `(instance_name, local_label)` origins, nonfinite values, invalid
material constants, invalid section properties, zero-length elements, missing
assignments, and invalid orientation vectors. Empty Part/Instance names denote
the flat global scope.

- [ ] **Step 1: Write failing builder tests**

Write one valid two-node domain test and one test for every rejection listed above.

- [ ] **Step 2: Run the model tests and confirm failure**

```powershell
cmake --build --preset windows-debug
ctest --preset windows-debug -R "Domain" --output-on-failure
```

- [ ] **Step 3: Implement the minimum immutable storage and validation**

Preserve external IDs and create private dense lookup maps. Do not store equation numbers in `Node` or `BeamElement`.

- [ ] **Step 4: Run all tests and commit**

```powershell
cmake --build --preset windows-debug
ctest --preset windows-debug --output-on-failure
git add include/fesa/model src/fesa/model tests/unit/model CMakeLists.txt tests/CMakeLists.txt
git commit -m "feat(model): add immutable beam domain"
```

---

### Task 4: Minimal Scoped Abaqus Deck to Domain

**Files:**

- Create: `include/fesa/io/abaqus/deck_record.hpp`
- Create: `include/fesa/io/abaqus/parser.hpp`
- Create: `include/fesa/io/abaqus/semantic_mapper.hpp`
- Create: `src/fesa/io/abaqus/parser.cpp`
- Create: `src/fesa/io/abaqus/semantic_mapper.cpp`
- Create: `tests/fixtures/abaqus/minimal_cantilever.inp`
- Create: `tests/fixtures/abaqus/minimal_part_instance_cantilever.inp`
- Create: `tests/fixtures/abaqus/unsupported_keyword.inp`
- Create: `tests/unit/io/abaqus/parser_test.cpp`
- Create: `tests/integration/io/minimal_deck_to_domain_test.cpp`
- Modify: CMake files

**Interfaces:**

```cpp
struct DeckRecord final {
    std::string keyword;
    std::map<std::string, std::string, std::less<>> parameters;
    std::vector<std::vector<std::string>> data;
    SourceLocation source;
};

struct ParsedPart final {
    std::string name;
    std::vector<DeckRecord> records;
    SourceLocation source;
};

struct ParsedInstance final {
    std::string name;
    std::string part_name;
    std::vector<std::vector<std::string>> transform_data;
    SourceLocation source;
};

struct ParsedAssembly final {
    std::string name;
    std::vector<ParsedInstance> instances;
    std::vector<DeckRecord> records;
    SourceLocation source;
};

struct ParsedDeck final {
    std::vector<DeckRecord> global_records;
    std::vector<ParsedPart> parts;
    std::optional<ParsedAssembly> assembly;
};

struct ParseDeckResult final {
    std::optional<ParsedDeck> deck;
    std::vector<Diagnostic> diagnostics;
};

[[nodiscard]] ParseDeckResult parse_deck(const std::filesystem::path& path);
[[nodiscard]] DomainBuildResult map_deck_to_domain(const ParsedDeck& deck);
```

Each minimal fixture contains two nodes, one B31 element, one material, one
general section, one node set, one element set, one boundary definition, one
concentrated load, and one static step. One fixture is flat; the other contains
one Part, one Assembly, and one untransformed Instance.

- [ ] **Step 1: Write failing parser and integration tests**

Tests must call `parse_deck()` and `map_deck_to_domain()`; they may not
construct the `Domain` directly. Assert that both organizations produce
equivalent active analysis entities and that the hierarchical Domain retains
Part/Instance provenance.

- [ ] **Step 2: Confirm failure**

```powershell
cmake --build --preset windows-debug
ctest --preset windows-debug -R "Abaqus|Deck" --output-on-failure
```

- [ ] **Step 3: Implement the smallest case-insensitive keyword parser**

Support comments, blank lines, comma-separated parameters and data, UTF-8
input, exact source lines, `*PART/*END PART`, `*ASSEMBLY/*END ASSEMBLY`, and
`*INSTANCE/*END INSTANCE`. In this task, accept only the keywords used by the
two minimal fixtures. Treat `*INCLUDE` and every other keyword as an explicit
unsupported-keyword error.

- [ ] **Step 4: Implement semantic mapping for the fixture**

For hierarchical input, require exactly one Assembly and one Instance, reject
nonempty Instance transform data, activate only the referenced Part, and map
the result to the same flat `Domain` used by orphan meshes. When transverse
stiffness is omitted, set \(A_{sy}=A_{sz}=5A/6\) and `SCF=0`; supported
explicit values override the default.

- [ ] **Step 5: Validate and commit**

```powershell
cmake --build --preset windows-debug
ctest --preset windows-debug --output-on-failure
git add include/fesa/io src/fesa/io tests/fixtures tests/unit/io tests/integration/io CMakeLists.txt tests/CMakeLists.txt
git commit -m "feat(input): parse minimal Abaqus B31 deck"
```

---

### Task 5: FEM Primitives and DOF Management

**Files:**

- Create: `include/fesa/fem/gauss_rule.hpp`
- Create: `include/fesa/fem/line2_shape.hpp`
- Create: `include/fesa/fem/local_frame.hpp`
- Create: `include/fesa/fem/dof_manager.hpp`
- Create: `src/fesa/fem/local_frame.cpp`
- Create: `src/fesa/fem/dof_manager.cpp`
- Create: `tests/unit/fem/gauss_rule_test.cpp`
- Create: `tests/unit/fem/line2_shape_test.cpp`
- Create: `tests/unit/fem/local_frame_test.cpp`
- Create: `tests/unit/fem/dof_manager_test.cpp`

**Interfaces:**

```cpp
struct GaussPoint1D final { double xi; double weight; };
[[nodiscard]] std::array<GaussPoint1D, 1> gauss_rule_1();
[[nodiscard]] std::array<GaussPoint1D, 2> gauss_rule_2();
[[nodiscard]] std::array<double, 2> line2_shape(double xi);
[[nodiscard]] std::array<double, 2> line2_shape_derivative();

struct LocalFrame final { Vec3 ex; Vec3 ey; Vec3 ez; double length; };
[[nodiscard]] LocalFrame make_beam_frame(Vec3 first, Vec3 second, Vec3 orientation);

class DofManager final {
public:
    explicit DofManager(const Domain& domain);
    [[nodiscard]] std::size_t full_dof_count() const noexcept;
    [[nodiscard]] std::array<std::size_t, 12> beam_dofs(ElementId id) const;
};
```

- [ ] **Step 1: Write failing mathematical invariant tests**

Test Gauss exactness through degree 3, shape-function partition of unity, derivative sum zero, right-handed orthonormal frames, nearly parallel orientation rejection, stable external-ID ordering, and 12-DOF element maps.

- [ ] **Step 2: Confirm failure, implement, and rerun**

```powershell
cmake --build --preset windows-debug
ctest --preset windows-debug -R "Fem|Dof|Frame" --output-on-failure
```

- [ ] **Step 3: Run full validation and commit**

```powershell
ctest --preset windows-debug --output-on-failure
git add include/fesa/fem src/fesa/fem tests/unit/fem CMakeLists.txt tests/CMakeLists.txt
git commit -m "feat(fem): add quadrature frames and DOF mapping"
```

---

### Task 6: Minimal Real Timoshenko Beam Kernel

**Files:**

- Create: `docs/formulation/timoshenko-beam-3d.md`
- Create: `include/fesa/elements/beam/beam3d2.hpp`
- Create: `src/fesa/elements/beam/beam3d2.cpp`
- Create: `tests/unit/elements/beam3d2_test.cpp`
- Modify: CMake files

**Interfaces:**

```cpp
struct Beam3D2Input final {
    std::array<Vec3, 2> coordinates;
    IsotropicElastic material;
    BeamSection section;
};

struct Beam3D2Contribution final {
    std::array<double, 144> stiffness;
    std::array<double, 12> equivalent_load;
    LocalFrame frame;
};

[[nodiscard]] Beam3D2Contribution evaluate_beam3d2(const Beam3D2Input& input);
```

- [ ] **Step 1: Write and review the formulation document before production code**

The document must define DOF order, local axes, strain measures, constitutive diagonal, Jacobian, transformation, Gauss rules, matrix storage order, force/moment signs, and reference sources. It must show that axial/bending/torsion use two points and shear uses one point.

- [ ] **Step 2: Write failing kernel tests**

Test symmetry, six rigid-body modes, positive strain energy for non-rigid modes, analytical axial stiffness \(EA/L\), analytical torsional stiffness \(GJ/L\), coordinate-rotation invariance, and finite values.

- [ ] **Step 3: Confirm the tests fail**

```powershell
cmake --build --preset windows-debug
ctest --preset windows-debug -R "Beam3D2" --output-on-failure
```

- [ ] **Step 4: Implement the minimum kernel from the signed-off equations**

Use fixed-size `std::array<double, 144>` storage and small explicit loops. Do not add a dynamic matrix abstraction or copy Abaqus slenderness compensation.

- [ ] **Step 5: Validate and commit**

```powershell
cmake --build --preset windows-debug
ctest --preset windows-debug --output-on-failure
git add docs/formulation include/fesa/elements src/fesa/elements tests/unit/elements CMakeLists.txt tests/CMakeLists.txt
git commit -m "feat(elements): add 3D Timoshenko beam kernel"
```

---

### Task 7: Deterministic Serial Assembly and Essential BC Elimination

**Files:**

- Create: `include/fesa/assembly/symmetric_coo.hpp`
- Create: `include/fesa/assembly/symmetric_csr.hpp`
- Create: `include/fesa/assembly/assembler.hpp`
- Create: `src/fesa/assembly/assembler.cpp`
- Create: `include/fesa/constraints/essential_bc.hpp`
- Create: `src/fesa/constraints/essential_bc.cpp`
- Create: `tests/unit/assembly/assembler_test.cpp`
- Create: `tests/unit/constraints/essential_bc_test.cpp`

**Interfaces:**

```cpp
struct CooEntry final {
    std::size_t row;
    std::size_t column;
    ElementId source_element;
    std::size_t local_order;
    double value;
};

struct SymmetricCsr final {
    std::vector<std::int32_t> row_offsets;
    std::vector<std::int32_t> column_indices;
    std::vector<double> values;
};

struct EquationSystem final {
    SymmetricCsr stiffness;
    std::vector<double> load;
};

[[nodiscard]] EquationSystem assemble_serial(
    const Domain& domain,
    const DofManager& dofs);

struct ReducedSystem final {
    SymmetricCsr stiffness;
    std::vector<double> rhs;
    std::vector<std::size_t> free_dofs;
    std::vector<double> prescribed_full_values;
};
```

- [ ] **Step 1: Write failing assembly tests**

Cover a one-element matrix, a two-element shared-node chain, stable sort/reduction order, upper-triangle storage with every diagonal present, nonzero prescribed values, reduced RHS correction, and full-vector reconstruction.

- [ ] **Step 2: Confirm failure and implement serial baseline**

Sort COO entries by `(row, column, source_element, local_order)` and then sum. This serial result is the oracle for Task 12.

- [ ] **Step 3: Verify and commit**

```powershell
cmake --build --preset windows-debug
ctest --preset windows-debug -R "Assembly|EssentialBc" --output-on-failure
ctest --preset windows-debug --output-on-failure
git add include/fesa/assembly src/fesa/assembly include/fesa/constraints src/fesa/constraints tests/unit/assembly tests/unit/constraints CMakeLists.txt tests/CMakeLists.txt
git commit -m "feat(assembly): assemble and constrain beam systems"
```

---

### Task 8: MKL PARDISO Linear Solver Adapter

**Files:**

- Create: `include/fesa/solvers/linear/linear_solver.hpp`
- Create: `include/fesa/solvers/linear/pardiso_solver.hpp`
- Create: `src/fesa/solvers/linear/pardiso_solver.cpp`
- Create: `tests/unit/solvers/pardiso_solver_test.cpp`
- Modify: `cmake/FesaDependencies.cmake`
- Modify: CMake files

**Interfaces:**

```cpp
struct LinearSolveResult final {
    std::vector<double> solution;
    double relative_residual;
    std::vector<Diagnostic> diagnostics;
};

class LinearSolver {
public:
    virtual ~LinearSolver() = default;
    [[nodiscard]] virtual LinearSolveResult solve(
        const SymmetricCsr& matrix,
        std::span<const double> rhs) = 0;
};

class PardisoLinearSolver final : public LinearSolver {
public:
    PardisoLinearSolver();
    ~PardisoLinearSolver() override;
    PardisoLinearSolver(const PardisoLinearSolver&) = delete;
    PardisoLinearSolver& operator=(const PardisoLinearSolver&) = delete;
    [[nodiscard]] LinearSolveResult solve(
        const SymmetricCsr& matrix,
        std::span<const double> rhs) override;
};
```

- [ ] **Step 1: Write failing adapter tests**

Use a hand-calculated 3x3 SPD system, multiple RHS calls on one adapter, invalid CSR, dimension mismatch, and a singular matrix. Assert `mtype=2`, LP64-compatible index checks, 0-based indexing, and residual reporting through observable behavior.

- [ ] **Step 2: Confirm failure**

```powershell
cmake --build --preset windows-debug
ctest --preset windows-debug -R "Pardiso" --output-on-failure
```

- [ ] **Step 3: Implement RAII PARDISO phases**

Set `iparm[34]=1` for zero-based indexing and enable the matrix checker. Run symbolic analysis, numerical factorization, solve, and release. Convert every MKL error to `DiagnosticStage::solver`.

- [ ] **Step 4: Validate and commit**

```powershell
cmake --build --preset windows-debug
ctest --preset windows-debug --output-on-failure
git add include/fesa/solvers src/fesa/solvers tests/unit/solvers cmake/FesaDependencies.cmake CMakeLists.txt tests/CMakeLists.txt
git commit -m "feat(solver): add PARDISO linear backend"
```

---

### Task 9: Result Model and Minimal HDF5 Round Trip

**Files:**

- Create: `docs/HDF5_SCHEMA.md`
- Create: `include/fesa/results/result_database.hpp`
- Create: `include/fesa/io/hdf5/schema.hpp`
- Create: `include/fesa/io/hdf5/writer.hpp`
- Create: `include/fesa/io/hdf5/reader.hpp`
- Create: `src/fesa/io/hdf5/writer.cpp`
- Create: `src/fesa/io/hdf5/reader.cpp`
- Create: `tests/unit/io/hdf5_round_trip_test.cpp`
- Modify: CMake files

**Interfaces:**

```cpp
struct NodalFrame final {
    std::vector<NodeId> node_ids;
    std::vector<std::array<double, 6>> displacement;
    std::vector<std::array<double, 6>> reaction;
};

struct ResultFrame final {
    double step_time;
    NodalFrame nodal;
    std::vector<Diagnostic> diagnostics;
};

struct ResultStep final {
    std::string name;
    std::vector<ResultFrame> frames;
};

struct ResultDatabase final {
    std::string schema_version;
    std::vector<ResultStep> steps;
};

struct Hdf5ReadResult final {
    std::optional<ResultDatabase> database;
    std::vector<Diagnostic> diagnostics;
};

[[nodiscard]] std::vector<Diagnostic> write_hdf5(
    const std::filesystem::path&,
    const Domain&,
    const ResultDatabase&);
[[nodiscard]] Hdf5ReadResult read_hdf5_results(const std::filesystem::path&);
```

- [ ] **Step 1: Write schema version `1.0.0` before writer code**

Define exact group paths, dataset ranks, scalar types, dense ID mappings,
Part/Instance/local-label origins, coordinate-system attributes, applied
transverse-shear values and their input/default source, required HDF5 metadata,
and compatibility rules.

- [ ] **Step 2: Write a failing round-trip test**

Use a two-node hierarchical-origin `Domain` and one result frame. Reopen
through the FESA reader and compare every stored value, origin mapping,
transverse-shear source, and schema attribute.

- [ ] **Step 3: Confirm failure and implement minimum C-API RAII wrappers**

Do not use global HDF5 handles. Convert every failing HDF5 call into a results-stage diagnostic or exception caught at the adapter boundary.

- [ ] **Step 4: Validate with tests and HDF5 tools**

```powershell
cmake --build --preset windows-debug
ctest --preset windows-debug -R "Hdf5" --output-on-failure
h5ls -r .\out\build\windows-debug\Testing\Temporary\fesa-round-trip.h5
ctest --preset windows-debug --output-on-failure
```

- [ ] **Step 5: Commit**

```powershell
git add docs/HDF5_SCHEMA.md include/fesa/results include/fesa/io/hdf5 src/fesa/io/hdf5 tests/unit/io CMakeLists.txt tests/CMakeLists.txt
git commit -m "feat(results): add versioned HDF5 result adapter"
```

---

### Task 10: Linear Static Analysis and End-to-End CLI Slice

**Files:**

- Create: `include/fesa/analysis/analysis.hpp`
- Create: `include/fesa/analysis/linear_static_analysis.hpp`
- Create: `src/fesa/analysis/analysis.cpp`
- Create: `src/fesa/analysis/linear_static_analysis.cpp`
- Create: `include/fesa/analysis/run_solver.hpp`
- Create: `src/fesa/analysis/run_solver.cpp`
- Modify: `src/fesa/cli/main.cpp`
- Create: `tests/integration/pipeline/minimal_cantilever_test.cpp`
- Modify: CMake files

**Interfaces:**

```cpp
struct AnalysisRequest final {
    std::filesystem::path input_path;
    std::filesystem::path output_path;
};

struct AnalysisRunResult final {
    bool succeeded;
    std::vector<Diagnostic> diagnostics;
};

[[nodiscard]] AnalysisRunResult run_solver(const AnalysisRequest& request);
```

The CLI contract is:

```text
fesa solve <model.inp> --output <results.h5>
fesa --version
```

- [ ] **Step 1: Write the failing end-to-end test**

Invoke only `run_solver()` or the CLI with `tests/fixtures/abaqus/minimal_cantilever.inp`, then use the public HDF5 reader to assert node IDs, finite displacement, equilibrium residual, and result paths.

- [ ] **Step 2: Confirm the pipeline test fails**

```powershell
cmake --build --preset windows-debug
ctest --preset windows-debug -R "MinimalCantileverPipeline" --output-on-failure
```

- [ ] **Step 3: Implement the analysis lifecycle**

Connect parser, `Domain`, `DofManager`, Beam kernel, serial assembly, essential BC, PARDISO, full-vector reconstruction, reaction recovery, result model, and HDF5 writer. Keep CLI parsing out of `fesa_core`.

- [ ] **Step 4: Validate the vertical slice**

```powershell
cmake --build --preset windows-debug
ctest --preset windows-debug --output-on-failure
.\out\build\windows-debug\Debug\fesa.exe solve tests\fixtures\abaqus\minimal_cantilever.inp --output out\minimal-cantilever.h5
h5ls -r out\minimal-cantilever.h5
```

Expected: full pipeline succeeds. Do not label the Beam numerically qualified yet.

- [ ] **Step 5: Commit**

```powershell
git add include/fesa/analysis src/fesa/analysis src/fesa/cli/main.cpp tests/integration/pipeline CMakeLists.txt tests/CMakeLists.txt
git commit -m "feat(analysis): connect linear static pipeline"
```

---

### Task 11: Complete the Agreed Abaqus Input Subset

**Files:**

- Modify: Abaqus parser and mapper files from Task 4
- Create: `tests/unit/io/abaqus/set_resolution_test.cpp`
- Create: `tests/unit/io/abaqus/scope_resolution_test.cpp`
- Create: `tests/unit/io/abaqus/semantic_validation_test.cpp`
- Create: `tests/integration/io/multiple_properties_test.cpp`
- Create: `tests/integration/io/supplied_cantilever_to_domain_test.cpp`
- Create fixtures under: `tests/fixtures/abaqus/valid/`
- Create fixtures under: `tests/fixtures/abaqus/invalid/`
- Create: `docs/ABAQUS_INPUT_SUBSET.md`

**Interfaces:**

- Existing parser and mapper signatures remain unchanged.
- `docs/ABAQUS_INPUT_SUBSET.md` becomes the normative keyword/parameter/data-line contract.

- [ ] **Step 1: Write the contract document and failing fixture matrix**

Cover `*NODE`, B31 `*ELEMENT`, `*PART/*END PART`,
`*ASSEMBLY/*END ASSEMBLY`, `*INSTANCE/*END INSTANCE`, `*NSET`, `*ELSET`,
explicit members, `GENERATE`, nested set references, `INSTANCE=`,
`*MATERIAL`, `*ELASTIC`, general Beam section, optional transverse shear
stiffness, `*BOUNDARY`, `*CLOAD`, and one static step. Document
`*HEADING`, `*PREPRINT`, `*RESTART`, and `*OUTPUT` as recognized no-op
directives.

- [ ] **Step 2: Add invalid tests before parser changes**

Cover duplicate IDs and origins, missing references, set cycles, invalid
ranges, multiple section assignments, missing material, conflicting prescribed
values, unsupported options, `*INCLUDE`, multiple Assemblies, multiple
Instances, Instance translation/rotation data, instance-local mesh changes,
mixed flat/hierarchical meshes, nonzero `SCF`, multiple steps, and source-line
accuracy.

- [ ] **Step 3: Run and confirm the new tests fail**

```powershell
cmake --build --preset windows-debug
ctest --preset windows-debug -R "Abaqus|SetResolution|MultipleProperties" --output-on-failure
```

- [ ] **Step 4: Implement only the documented subset**

Resolve Part and Assembly scopes before normalizing only the active Part and
Instance. Resolve nested sets with explicit cycle detection and canonical
sorted-unique membership. Recognized no-op directives must be consumed
deliberately; do not add a general ignore-unknown path. Verify the supplied
`reference/cantilever beam/cantilever beam.inp` maps to the expected 11 nodes,
10 elements, one active material/section, six fixed DOFs, and one nodal load.

- [ ] **Step 5: Validate and commit**

```powershell
cmake --build --preset windows-debug
ctest --preset windows-debug --output-on-failure
git add docs/ABAQUS_INPUT_SUBSET.md include/fesa/io/abaqus src/fesa/io/abaqus tests/fixtures/abaqus tests/unit/io/abaqus tests/integration/io
git commit -m "feat(input): complete Phase 1 Abaqus subset"
```

---

### Task 12: Deterministic oneTBB Assembly

**Files:**

- Modify: `include/fesa/assembly/assembler.hpp`
- Modify: `src/fesa/assembly/assembler.cpp`
- Create: `tests/unit/assembly/parallel_assembler_test.cpp`
- Create: `tests/integration/assembly/thread_count_determinism_test.cpp`
- Create: `tests/performance/assembly_benchmark.cpp`
- Modify: CMake files

**Interfaces:**

```cpp
struct AssemblyOptions final {
    std::size_t max_threads;
    std::size_t grain_size;
};

[[nodiscard]] EquationSystem assemble_parallel(
    const Domain& domain,
    const DofManager& dofs,
    AssemblyOptions options);
```

- [ ] **Step 1: Write failing serial-versus-parallel tests**

Generate fixed chain and branched Beam domains. Compare CSR row offsets and column indices exactly and values bit-for-bit for thread counts 1, 2, and the available concurrency.

- [ ] **Step 2: Confirm failure**

```powershell
cmake --build --preset windows-debug
ctest --preset windows-debug -R "ParallelAssembly|ThreadCount" --output-on-failure
```

- [ ] **Step 3: Implement parallel element evaluation with deterministic merge**

Use oneTBB for independent element evaluation. Each contribution retains `(row, column, element ID, local order)`. Sort and reduce in the same order as the serial oracle. Do not perform concurrent unordered writes to CSR values.

- [ ] **Step 4: Validate correctness before measuring performance**

```powershell
cmake --build --preset windows-debug
ctest --preset windows-debug --output-on-failure
.\out\build\windows-debug\Debug\fesa_assembly_benchmark.exe
```

Record timings without asserting a speedup in unit tests.

- [ ] **Step 5: Commit**

```powershell
git add include/fesa/assembly src/fesa/assembly tests/unit/assembly tests/integration/assembly tests/performance CMakeLists.txt tests/CMakeLists.txt
git commit -m "perf(assembly): add deterministic TBB assembly"
```

---

### Task 13: Complete Beam Recovery and Self-Contained HDF5 Contract

**Files:**

- Modify: `include/fesa/elements/beam/beam3d2.hpp`
- Modify: `src/fesa/elements/beam/beam3d2.cpp`
- Modify: result-model and HDF5 files from Task 9
- Modify: `docs/HDF5_SCHEMA.md`
- Create: `tests/unit/elements/beam3d2_recovery_test.cpp`
- Create: `tests/integration/results/self_contained_hdf5_test.cpp`

**Interfaces:**

```cpp
struct BeamSectionResult final {
    double xi;
    NodeId end_node;
    std::array<double, 6> section_strain;
    std::array<double, 6> section_force;
    double centroid_sigma_xx;
    std::vector<double> sigma_xx;
};

[[nodiscard]] std::vector<BeamSectionResult> recover_beam3d2(
    const Beam3D2Input& input,
    std::span<const double, 12> element_displacement,
    std::span<const std::array<double, 2>> recovery_points);
```

- [ ] **Step 1: Write failing recovery tests**

Cover pure axial force, pure torsion, bending about each principal axis,
combined axial/biaxial bending, force sign at both element ends,
`centroid_sigma_xx=N/A`, and recovery-point ordering.

- [ ] **Step 2: Write the failing self-contained-file test**

Reopen one result file and reconstruct node coordinates, connectivity,
Part/Instance origins, sets, material, section, applied shear values and their
source, step, solver settings, local frame, nodal fields, element section
fields including centroid stress, ID maps, and diagnostics.

- [ ] **Step 3: Implement recovery and schema additions**

Do not output point shear or point torsional stress. Store section shear resultants and torsional moment as generalized quantities.

- [ ] **Step 4: Validate and commit**

```powershell
cmake --build --preset windows-debug
ctest --preset windows-debug -R "Beam3D2Recovery|SelfContainedHdf5" --output-on-failure
ctest --preset windows-debug --output-on-failure
git add include/fesa/elements src/fesa/elements include/fesa/results include/fesa/io/hdf5 src/fesa/io/hdf5 docs/HDF5_SCHEMA.md tests/unit/elements tests/integration/results
git commit -m "feat(results): recover and store beam section results"
```

---

### Task 14: Analytical and Available Abaqus 2024 Qualification

**Files:**

- Create: `include/fesa/validation/comparison.hpp`
- Create: `include/fesa/validation/reference_csv.hpp`
- Create: `src/fesa/validation/comparison.cpp`
- Create: `src/fesa/validation/reference_csv.cpp`
- Create: `src/fesa/validation/reference_compare_main.cpp`
- Create: `tests/unit/validation/comparison_test.cpp`
- Create: `tests/unit/validation/reference_csv_test.cpp`
- Create: `tests/fixtures/reference/internalforces.csv`
- Create: `tests/fixtures/reference/stresses.csv`
- Create: `tests/reference/CMakeLists.txt`
- Create: `tests/reference/cantilever_reference_test.cpp`
- Create: `docs/VALIDATION.md`
- Modify: CMake files

**Interfaces:**

```cpp
struct Tolerance final {
    double relative;
    double absolute_scale;
};

enum class ReferenceQuantity {
    displacement,
    reaction,
    internal_force,
    centroid_stress
};

struct ResultPosition final {
    std::string instance_name;
    std::int64_t entity_label;
    std::optional<std::int64_t> end_node_label;
};

struct ComparisonSample final {
    ReferenceQuantity quantity;
    ResultPosition position;
    std::vector<double> reference;
    std::vector<double> actual;
    Tolerance tolerance;
};

struct ReferenceRow final {
    ReferenceQuantity quantity;
    ResultPosition position;
    std::vector<double> values;
};

struct ComparisonReport final {
    bool passed;
    double maximum_normalized_error;
    std::vector<Diagnostic> failures;
};

[[nodiscard]] ComparisonReport compare_samples(
    std::span<const ComparisonSample> samples);

struct ReferenceCsvReadResult final {
    std::vector<ReferenceRow> rows;
    std::vector<Diagnostic> diagnostics;
};

[[nodiscard]] ReferenceCsvReadResult read_reference_csv(
    ReferenceQuantity quantity,
    const std::filesystem::path& path,
    std::string_view single_instance_name,
    Tolerance tolerance);
```

For each scalar component, define
\[
e_n=\frac{|a-r|}{a_\mathrm{scale}+r_\mathrm{tol}|r|}
\]
and pass only when \(e_n\leq1\). Reject nonfinite inputs before computing the
metric.

- [ ] **Step 1: Write failing metric and entity-matching tests**

Test the normalized error, near-zero absolute scale, nonfinite values,
duplicate positions, unknown entities, invalid element-node pairs, and
component-count mismatches.

- [ ] **Step 2: Write failing CSV adapter tests for all four quantities**

Accept the supplied displacement/reaction headers after trimming whitespace.
For a single Instance, allow the `Part Instance Name` column to be absent.
Use these exact element schemas:

```text
Part Instance Name, Element Label, Node Label,
SF-SF1, SF-SF2, SF-SF3, SM-SM1, SM-SM2, SM-SM3

Part Instance Name, Element Label, Node Label, Sxx
```

Map `SF1,SF2,SF3,SM1,SM2,SM3` to \(N,V_y,V_z,T,M_y,M_z\).
Compare `Sxx` with the element-end section-centroid value \(N/A\). The
synthetic fixtures exercise both element result schemas before real files
exist.

- [ ] **Step 3: Write the failing supplied-cantilever reference test**

Run FESA through the public parser, analysis, and HDF5 reader. Create an
explicit request selecting only:

```text
reference/cantilever beam/cantilever beam displacements.csv
reference/cantilever beam/cantilever beam reactions.csv
```

Use relative tolerance \(10^{-5}\) and test-registered absolute scales. Do not
look for metadata, internal-force CSV, or stress CSV in this reference test.
The equivalent command-line contract is:

```powershell
fesa-reference-compare `
  --results out\cantilever-beam.h5 `
  --instance Part-1-1 `
  --displacements "reference\cantilever beam\cantilever beam displacements.csv" `
  --reactions "reference\cantilever beam\cantilever beam reactions.csv" `
  --relative-tolerance 1e-5 `
  --displacement-absolute-scale 1e-10 `
  --reaction-absolute-scale 1e-8
```

- [ ] **Step 4: Confirm failures before numerical corrections**

```powershell
cmake --preset windows-debug
cmake --build --preset windows-debug
ctest --preset windows-debug -R "Reference" --output-on-failure
```

- [ ] **Step 5: Correct only evidenced formulation or recovery defects**

For every change, add or tighten the smallest analytical test that reproduces
the discrepancy. Do not widen tolerance to hide a defect. Record justified
tolerance differences in `docs/VALIDATION.md` and the CTest registration.

- [ ] **Step 6: Run the full qualification suite**

```powershell
ctest --preset windows-debug --output-on-failure
ctest --preset windows-debug -R "Reference" --output-on-failure
```

Expected: all analytical, physics-sanity, pipeline, deterministic-parallel,
CSV-adapter, and available Abaqus displacement/reaction comparisons pass.
Do not claim Abaqus qualification of element internal force or stress until
those CSV files are supplied and selected.

- [ ] **Step 7: Complete the validation report and commit**

`docs/VALIDATION.md` must list each benchmark, analytical/reference source,
Abaqus configuration, selected quantities, tolerance, maximum observed error,
and disposition. It must distinguish synthetic adapter coverage from
Abaqus-backed quantity qualification.

```powershell
git add include/fesa/validation src/fesa/validation tests/unit/validation tests/fixtures/reference tests/reference docs/VALIDATION.md CMakeLists.txt tests/CMakeLists.txt
git commit -m "test(validation): qualify Beam solver against Abaqus"
```

---

### Task 15: Internal Release Gate

**Files:**

- Create: `cmake/install.cmake`
- Create: `cmake/FESAConfig.cmake.in`
- Create: `docs/BUILDING.md`
- Create: `docs/INPUT_FORMAT.md`
- Create: `docs/RELEASE_CHECKLIST.md`
- Create: `tests/performance/phase1_scale_benchmark.cpp`
- Modify: root CMake files
- Modify: `docs/VALIDATION.md`

**Interfaces:**

- Produces install tree containing `fesa.exe`, the static library, public headers, required runtime DLL inventory, example input, schema, and validation report.
- No public ABI compatibility promise is made for Phase 1.

- [ ] **Step 1: Write the release checklist before packaging changes**

Include environment versions, Debug/Release builds, zero-warning requirement,
CTest count, selected reference quantities, synthetic four-quantity adapter
coverage, HDF5 inspection, 100k-DOF memory/time measurement, runtime DLL
inventory, example run, and clean install-tree smoke test.

- [ ] **Step 2: Add a failing install-tree smoke test**

The test configures a small consumer against installed headers and the static library, runs `fesa --version`, solves the example, and opens its HDF5 output.

- [ ] **Step 3: Implement CMake install rules**

Use `cmake --install`; do not add an installer, registry writes, package download, or external-customer SDK promise.

- [ ] **Step 4: Run Debug, Release, performance, and install validation**

```powershell
cmake --preset windows-debug
cmake --build --preset windows-debug
ctest --preset windows-debug --output-on-failure
cmake --preset windows-release
cmake --build --preset windows-release
ctest --preset windows-release --output-on-failure
.\out\build\windows-release\Release\fesa_phase1_scale_benchmark.exe
cmake --install out\build\windows-release --config Release --prefix out\package\fesa
```

Expected: all tests pass, no new MSVC warnings exist, the benchmark completes within documented resources, and the clean install-tree smoke test passes.

- [ ] **Step 5: Verify every PRD release criterion**

Map every item in `docs/PRD.md` section 8 to fresh command output or a validation-report entry. Stop the release if any item lacks evidence.

- [ ] **Step 6: Commit**

```powershell
git add cmake/install.cmake docs/BUILDING.md docs/INPUT_FORMAT.md docs/RELEASE_CHECKLIST.md docs/VALIDATION.md tests/performance CMakeLists.txt
git commit -m "chore(release): prepare FESA Phase 1 internal package"
```

## Plan Execution Gate

Before implementation:

1. Review and approve this phase split.
2. Use the Harness skill to draft `phases/index.json`, each phase index, and self-contained step files.
3. Review the phase-file draft before creating it.
4. Execute one Harness phase at a time.
5. Do not begin the next phase until its tests, review, and acceptance commands pass.
