# Step 1: map-pressure-input-contract ## 실행 범위와 상태 소유권 이 Step의 단일 책임은 valid assembly-level `*SURFACE, TYPE=ELEMENT`와 sole static Step의 `*DSLOAD P`를 immutable Domain/Step semantic objects와 non-owning `AnalysisModel` view로 mapping하는 것이다. 이 Step 안에서 `RED -> observed expected failure -> minimal GREEN -> focused VERIFY -> full/Stop VERIFY`를 끝내고 green 상태로 종료한다. Implementation Agent는 Executor가 선택한 이 Step만 수행한다. 성공 시 `phases/shell-surface-pressure-load/index.json`의 Step 1 `status`와 한 줄 `summary`만, 오류/차단 시 각각 `error_message` 또는 `blocked_reason`만 기록할 수 있다. branch, retry, timestamps, commits, 다음 Step 선택, top-level phase status와 `step1-output.json`은 Harness Executor 소유다. ## 읽어야 할 파일 먼저 전부 읽는다. - `AGENTS.md` - `docs/PRD.md` - `docs/ARCHITECTURE.md` - `docs/ADR.md` - `docs/CODINGSTYLE.md` - `docs/HARNESS.md` - `docs/HARNESS_WORKFLOW.md` - `.codex/hooks.json` - `docs/shell-surface-pressure-load/requirements.md` - `docs/shell-surface-pressure-load/research.md` - `docs/shell-surface-pressure-load/formulation.md` - `docs/shell-surface-pressure-load/numerical-review.md` - `docs/shell-surface-pressure-load/reference-model.md` - `docs/shell-surface-pressure-load/io.md` - `docs/shell-surface-pressure-load/implementation-plan.md` - `include/fesa/io/abaqus/input_syntax.h` - `include/fesa/io/abaqus/input_reader.h` - `src/fesa/io/abaqus/input_reader.cpp` - `src/fesa/io/abaqus/domain_mapping_context.h` - `src/fesa/io/abaqus/domain_mapping_context.cpp` - `src/fesa/io/abaqus/topology_mapper.h` - `src/fesa/io/abaqus/topology_mapper.cpp` - `src/fesa/io/abaqus/step_definition_mapper.h` - `src/fesa/io/abaqus/step_definition_mapper.cpp` - `src/fesa/io/abaqus/domain_builder.h` - `src/fesa/io/abaqus/domain_builder.cpp` - `src/fesa/io/abaqus/domain_mapper.cpp` - `include/fesa/model/model_types.h` - `include/fesa/model/domain.h` - `src/fesa/model/domain.cpp` - `include/fesa/analysis/analysis_model.h` - `src/fesa/analysis/analysis_model.cpp` - `tests/unit/io/abaqus/input_reader_test.cpp` - `tests/unit/io/abaqus/input_syntax_test.cpp` - `tests/unit/io/abaqus/domain_mapper_test.cpp` - `tests/unit/io/abaqus/domain_mapping_components_test.cpp` - `src/fesa/CMakeLists.txt` - `tests/CMakeLists.txt` 이 Step에는 이전 Step 산출물이 없다. Existing parser/domain behavior를 먼저 이해하고 legacy beam/shell inputs를 보존한다. ## 소유 파일 먼저 test files를 생성/수정한 뒤 production files를 변경한다. - Modify: `include/fesa/io/abaqus/input_syntax.h` - Modify: `src/fesa/io/abaqus/input_reader.cpp` - Modify: `src/fesa/io/abaqus/domain_mapping_context.h` - Modify: `src/fesa/io/abaqus/domain_mapping_context.cpp` - Create: `src/fesa/io/abaqus/surface_definition_mapper.h` - Create: `src/fesa/io/abaqus/surface_definition_mapper.cpp` - Modify: `src/fesa/io/abaqus/domain_mapper.cpp` - Modify: `src/fesa/io/abaqus/step_definition_mapper.cpp` - Modify: `src/fesa/io/abaqus/domain_builder.cpp` - Modify: `include/fesa/model/model_types.h` - Modify: `include/fesa/model/domain.h` - Modify: `src/fesa/model/domain.cpp` - Modify: `include/fesa/analysis/analysis_model.h` - Modify: `src/fesa/analysis/analysis_model.cpp` - Modify/Test: `tests/unit/io/abaqus/input_reader_test.cpp` - Create/Test: `tests/unit/io/abaqus/shell_surface_pressure_domain_test.cpp` - Modify: `src/fesa/CMakeLists.txt` - Modify: `tests/CMakeLists.txt` `domain_mapper_test.cpp`와 `domain_mapping_components_test.cpp`는 focused regression 대상으로 우선 읽는다. 이 Step의 valid mapping에 직접 필요한 assertion이 새 feature test로 표현되지 않는 경우에만 최소 수정한다. ## 인터페이스와 semantic contract `include/fesa/model/model_types.h`에 다음 의미의 candidate value types를 구현한다. Existing style상 이름을 최소 조정해야 하면 upstream semantic fields와 아래 accessors를 빠짐없이 보존하고 summary에 조정 이유를 기록한다. ```cpp enum class ShellSurfaceSide { kSpos, kSneg }; enum class SurfaceMemberKind { kDirect, kElset }; struct ElementSurfaceFacetDefinition { EntityIndex element_index; std::size_t facet_ordinal; std::string part_name_raw; std::string part_name_canonical; std::string instance_name_raw; std::string instance_name_canonical; std::int64_t source_element_label; std::string source_element_label_raw; std::string source_element_type; ShellSurfaceSide side; SourceLocation first_member_location; std::size_t first_member_ordinal; SurfaceMemberKind member_kind; std::string elset_name_raw; std::string elset_name_canonical; }; struct ElementSurfaceDefinition { std::string surface_name_raw; std::string surface_name_canonical; std::vector facets; SourceLocation declaration_location; std::size_t declaration_ordinal; }; struct SurfacePressureLoadDefinition { std::string step_name_raw; std::string step_name_canonical; EntityIndex surface_index; std::string surface_name_raw; std::string surface_name_canonical; std::string load_type; double magnitude; std::string magnitude_raw; SourceLocation keyword_location; SourceLocation data_location; std::size_t data_ordinal; std::size_t load_ordinal; }; ``` Required queries: ```cpp const std::vector& Domain::ElementSurfaces() const noexcept; const std::vector& StepDefinition::SurfacePressureLoads() const noexcept; bool StepDefinition::UsesNonlinearGeometry() const noexcept; const std::vector& AnalysisModel::ActiveSurfacePressureLoads() const noexcept; ``` `ModelDefinition` owns `element_surfaces`; `StaticStepDefinition` owns `surface_pressure_loads`; committed `Domain`/`StepDefinition` remain immutable. `AnalysisModel` returns non-owning references and never copies/reorders the surface inventory; Domain must outlive it. Pressure records do not enter the existing configuration-independent nodal `Load::ComputeContributions` interface. ## RED Write and register these tests before production implementation: - `InpSyntax.AcceptsAsciiKeywordContinuationAndPreservesPhysicalSourceLines` - `ShellSurfacePressureInput.MapsDirectInstanceFacetsForS4AndS4rCaseInsensitively` - `ShellSurfacePressureInput.MapsExplicitInstanceElsetAndResultNeutralInternalParameter` - `ShellSurfacePressureInput.MapsMixedDirectAndElsetMembersAcrossTwoExplicitInstancesWithStableDataMemberAndElsetOrder` - `ShellSurfacePressureInput.PreservesSposSnegMagnitudeTextLocationsAndSourceOrder` - `ShellSurfacePressureDomain.OwnsCanonicalFacetsAndStepOwnedAdditivePressureRecords` - `ShellSurfacePressureDomain.AnalysisModelBorrowsActivePressureWithoutCopyingDomain` - `ShellSurfacePressureInput.AcceptsWhitespaceSignedZeroAndFiniteScientificNotation` The mixed-instance fixture declares `Instance-A` and `Instance-B` in distinct declaration order, interleaves differently cased direct members and flat assembly ELSET members bound by explicit `INSTANCE=`, and uses the same numeric element label in both instances. It must compare case-insensitive resolution, raw data/member ordinals, flat ELSET provenance, canonical facet order, distinct part/instance/source label/source type/side identities and first-member provenance exactly. It must not accept implicit-instance syntax. After test edits and CMake test registration, run: ```powershell cmake --fresh -S . -B .harness/build -G "Visual Studio 18 2026" -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 --target fesa_unit_tests ctest --test-dir .harness/build -C Debug --output-on-failure -R "^(InpSyntax|ShellSurfacePressureInput|ShellSurfacePressureDomain)\." ``` Expected RED: missing mapper/header/semantic accessors produces compile/link failure, or current unsupported `SURFACE`/`DSLOAD` behavior fails assertions. The mixed-instance fixture must fail because the two explicit instances/mixed direct+ELSET are not resolved or committed identity/provenance is absent. Record command, exit code, duration and meaningful output tail. An unrelated existing failure is `regression`; an unexpected pass is an `evidence-gap`. Stop before production changes until the expected RED is observed. ## Minimal GREEN - Support physical-line ASCII/256-byte policy, comma/cardinality, physical locations and keyword-parameter-only continuation exactly as `io.md` §3 defines. Do not invent inline comments or data continuation. - Parse assembly-only element surfaces and parameterless step-level DSLOAD. Surface parameters are exactly `TYPE=ELEMENT` and `NAME`; DSLOAD data is exactly `,P,`. - Run `SurfaceDefinitionMapper` after topology finalization and before Step binding. Resolve direct `.` and flat assembly ELSETs with explicit `INSTANCE=` case-insensitively. - One surface may mix direct/ELSET members and multiple explicit instances. Preserve raw data/member order and ELSET expansion provenance, then sort unique committed facets by `(instance declaration ordinal, numeric element label, SPOS before SNEG)`. - Preserve each facet's internal `element_index` plus part/instance raw+canonical identity, source label/raw token, exact `S4|S4R`, side, facet ordinal and first-member provenance. - Preserve each Step pressure record's step/surface raw+canonical identity, `load_type="P"`, signed magnitude/raw token, keyword/data locations, data ordinal and total Step load ordinal. Records remain distinct/additive. - Bind pressure surface references only after every candidate target validates. DomainBuilder commits once. Legacy `StaticStepDefinition` constructors delegate with an empty pressure vector. ## VERIFY and Acceptance Criteria If `.harness/config.json` exists at execution time, its complete configure/build/test selection takes precedence and the exact resolved commands must be recorded. Otherwise execute: ```powershell cmake --build .harness/build --config Debug --target fesa_unit_tests ctest --test-dir .harness/build -C Debug --output-on-failure -R "^(InpSyntax|ShellSurfacePressureInput|ShellSurfacePressureDomain)\." ctest --test-dir .harness/build -C Debug --output-on-failure -R "^(InpDomainMapping|DomainMappingComponents|InpSyntax)\." cmake --build .harness/build --config Debug ctest --test-dir .harness/build -C Debug --show-only=json-v1 ctest --test-dir .harness/build -C Debug --output-on-failure ``` Acceptance requires exit `0`, nonzero discovered tests, all named tests present/pass, no new MSVC `/W4 /WX` warnings, and all legacy tests green. The automatic Stop hook performs another whole-project MSVC validation; do not call hook scripts manually. ## 불변식, 실패 처리와 증거 - Supported: mixed direct/ELSET and multiple explicit instances in one surface. Unsupported: implicit-instance ELSET, one ELSET mixing instances, part-scope fallback. - Same numeric label in different instances is never merged. Source identity is not replaced by internal/equation index. - Parse/map failure returns no Domain and exposes no partial surface/load. Do not update AnalysisState or HDF5. - Evidence must record RED/GREEN/VERIFY commands, exits, durations/tails, mapped facet/load inventory, source identity/order, warnings and changed files. - Failure classification: `configure | compile | link | test | harness | environment | upstream-contract | regression | evidence-gap`. Trace: `FESA-REQ-SHELL-PRESSURE-001`–`004`, `010`, `013`; `io.md` §§2–6; `formulation.md` §3. ## 금지사항 - Do not implement pressure force/tangent or procedure assembly. 이유: Steps 3–5 ownership이다. - Do not support `*DLOAD`, implicit-instance ELSET, part surface, generalized distributed loads or additional element types. 이유: approved subset 밖이다. - Do not change HDF5 schema, CLI output contract, upstream docs, tolerance or reference artifacts. - Do not run Abaqus/reference solvers, create output JSON, invoke `scripts/execute.py`, select Step 2, commit or push.