# Step 2: domain-model-entities ## 읽어야 할 파일 먼저 아래 파일들을 읽고 프로젝트의 아키텍처와 이전 step 산출물을 파악하라: - `/AGENTS.md` - `/docs/PRD.md` - `/docs/ARCHITECTURE.md` - `/docs/ADR.md` - `/src/fesa/core/ids.hpp` - `/src/fesa/core/status.hpp` - `/tests/unit/core_primitives_test.cpp` 이전 step에서 만들어진 core primitive를 꼼꼼히 읽고, ID ownership과 diagnostic convention을 유지하라. ## 작업 입력 파일에서 생성된 전체 semantic model을 소유하는 최소 model layer를 `/src/fesa/model/`에 구현한다. 필수 파일: - `/src/fesa/model/node.hpp` - `/src/fesa/model/element.hpp` - `/src/fesa/model/material.hpp` - `/src/fesa/model/property.hpp` - `/src/fesa/model/boundary_condition.hpp` - `/src/fesa/model/load.hpp` - `/src/fesa/model/analysis_step.hpp` - `/src/fesa/model/domain.hpp` - `/tests/unit/model_domain_test.cpp` 필수 interface: ```cpp namespace fesa::model { class Node { public: Node(core::NodeId id, std::array coordinates); core::NodeId id() const; const std::array& coordinates() const; }; enum class ElementTopology { truss2, bar2, unknown }; class Element { public: Element(core::ElementId id, ElementTopology topology, std::vector node_ids, core::PropertyId property_id); core::ElementId id() const; ElementTopology topology() const; const std::vector& node_ids() const; core::PropertyId property_id() const; }; class Material { public: Material(core::MaterialId id, std::string name); core::MaterialId id() const; const std::string& name() const; }; class Property { public: Property(core::PropertyId id, std::string name, core::MaterialId material_id); core::PropertyId id() const; const std::string& name() const; core::MaterialId material_id() const; }; enum class DofComponent { ux, uy, uz, rx, ry, rz, temperature }; class BoundaryCondition { public: BoundaryCondition(core::NodeId node_id, DofComponent component, double value); core::NodeId node_id() const; DofComponent component() const; double value() const; }; class Load { public: Load(core::NodeId node_id, DofComponent component, double value); core::NodeId node_id() const; DofComponent component() const; double value() const; }; class AnalysisStep { public: AnalysisStep(core::StepId id, std::string name); core::StepId id() const; const std::string& name() const; void add_boundary_condition(BoundaryCondition bc); void add_load(Load load); const std::vector& boundary_conditions() const; const std::vector& loads() const; }; class Domain { public: void add_node(Node node); void add_element(Element element); void add_material(Material material); void add_property(Property property); void add_step(AnalysisStep step); const std::vector& nodes() const; const std::vector& elements() const; const std::vector& materials() const; const std::vector& properties() const; const std::vector& steps() const; const Node* find_node(core::NodeId id) const; const Element* find_element(core::ElementId id) const; const Material* find_material(core::MaterialId id) const; const Property* find_property(core::PropertyId id) const; const AnalysisStep* find_step(core::StepId id) const; }; } // namespace fesa::model ``` 구현 규칙: - Domain은 semantic model 객체를 소유한다. - `nodes()`, `elements()`, `materials()`, `properties()`, `steps()`는 const reference를 반환한다. - `find_*`는 없으면 `nullptr`을 반환한다. - Node와 Element 내부에 equation id, constrained/free equation mapping, sparse pattern 정보를 저장하지 않는다. - `DofComponent`는 아직 equation number가 아니라 물리 DOF component만 표현한다. - Abaqus keyword 문자열이나 parser detail을 model object에 저장하지 않는다. ## Tests To Write First - `/tests/unit/model_domain_test.cpp` - Node가 id와 3D coordinates를 보존한다. - Element가 topology, connectivity, property id를 보존한다. - Material과 Property가 id/name/material link를 보존한다. - AnalysisStep이 boundary condition과 load를 저장한다. - Domain이 add/find를 통해 각 객체를 조회한다. - 없는 id는 `nullptr`을 반환한다. - Node/Element public interface에 equation id를 노출하지 않는다는 점을 테스트 코드 사용 방식으로 확인한다. RED 확인: 1. 테스트 파일을 먼저 작성한다. 2. targeted CTest를 실행해 missing model headers로 실패함을 확인한다. 3. 그 뒤 production headers를 작성한다. ## Acceptance Criteria ```powershell python -m unittest discover -s scripts -p "test_*.py" python scripts/validate_workspace.py ctest --test-dir build/msvc-debug --output-on-failure -C Debug -R model_domain_test ``` ## 검증 절차 1. 위 AC 커맨드를 실행한다. 2. 아키텍처 체크리스트를 확인한다: - Domain이 전체 모델 정의를 소유하는가? - model layer가 parser keyword 문자열이나 analysis state를 소유하지 않는가? - Node/Element에 equation id가 분산 저장되지 않는가? 3. 결과에 따라 `phases/solver-core-skeleton/index.json`의 step 2를 업데이트한다: - 성공: `"status": "completed"`, `"summary": "Model entities and Domain ownership API added with tests"` - 3회 수정 시도 후 실패: `"status": "error"`, `"error_message": "구체적 에러 내용"` - 사용자 개입 필요: `"status": "blocked"`, `"blocked_reason": "구체적 사유"` 후 중단 ## 금지사항 - Parser, assembler, solver backend를 만들지 마라. - `Domain`을 실행 중 state container로 사용하지 마라. - JavaScript/TypeScript/npm fallback을 추가하지 마라.