Files
FESADev/docs/mitc4-shell-feast-eigen/implementation-verification.md
T
2026-09-06 05:37:30 +09:00

33 KiB
Raw Blame History

구현 검증과 계약 traceability

V1. 의미 있는 RED와 전체 suite

각 Step은 소단위별 test-first RED/expected failure 기록→최소 GREEN→focused VERIFY 뒤 full CMake/MSVC/x64/Debug CTest 및 Python regression을 완료한다. 기존 회귀가 이미 통과한 사실은 새 RED가 아니다. 부재 API로 인한 정확한 compile/link error는 RED로 기록할 수 있으나 환경/import/dependency failure와 구분한다. Mutations는 test-local data/fixture/injected backend에서 수행하며 stored reference나 production을 임시로 오염시키지 않는다.

아래 mandatory test names는 해당 Step이 완성될 때 CTest discovery inventory에 각각 존재해야 한다. 같은 combined regex가 기존 tests 몇 개만 선택했다고 성공시키지 않는다. New production basename의 대응 test 파일과 등록된 해당 behavior assertion도 함께 대조한다. 실제 test를 parameterized tests로 쪼갠 경우 원 named obligation→실제 discovered names의 일대일/일대다 대응을 step evidence에 기록한다. Case를 test 이름만 등록하고 assertions를 비워 두지 않는다.

V2. NR cases의 구체 실행 대응

Threshold와 전체 fixture inventory는 NR 원문이다. 표의 요약을 이유로 그 inventory를 축소하지 않는다. 각 row의 primary test 외 관련 단계의 preprocessing/injection/schema tests를 함께 수행한다.

Case ID Steps Mandatory primary test / file 독립 oracle·오류 검출
MFE-C01 2 Mitc4ShellMass.MfeC01FlatMomentOracle
tests/unit/elements/mitc4_shell_mass_test.cpp
4x2, rho3,t1/2의 exact C0/C1/C2; node m3,j1/16; direct energy/physical20 Cholesky/null; rho4/t2 powers. Wrong shear factor/rotary scaling을 검출.
MFE-C02 2 Mitc4ShellMass.MfeC02CurvedParticleOracle
tests/unit/elements/mitc4_shell_mass_test.cpp
NR rational X/d의 independent polynomial 또는 고차 direct particle integral. C1 zero/2-point/flat C2 mutation의 error가 기준을 넘는지 확인.
MFE-C03 2 Mitc4ShellMass.MfeC03DerivedSnapshotAndJensen
tests/unit/elements/mitc4_shell_mass_test.cpp
C1/C2 symmetric perturbation→moment snapshot/consistent/derived block을 각각 대조. Independent nodal integral, central variance, spatial first moment, four velocity families의 Jensen work.
MFE-C04 2 ShellMassAssembler.MfeC04RefinementEnergy
tests/unit/assembly/shell_mass_assembler_test.cpp
m1/2/4 square mesh, velocity(x,0,0); direct MC work4/3, ML-MC8/(3m²), constant velocity total4. Spectrum convergence 주장은 하지 않음.
MFE-C05 2 Mitc4ShellMass.MfeC05RotatedMassAndCentroid
tests/unit/elements/mitc4_shell_mass_test.cpp
NR rational proper Q+shift(7,-3,5), geometry/directors 새 생성; two mass congruence/centroid/null. d=(1,1,1)/sqrt3에서 global scalar-row lump mutation 검출.
MFE-C06 0, 2, 5 ShellMassAssembler.MfeC06SourceAndParallelOrder
tests/unit/assembly/shell_mass_assembler_test.cpp
Shared nodes와 두 rho/t, 선언/label permutation, independent source/scatter mapping. Fixed input sequential/TBB 재실행의 CSR/reduction exact order와 source-renumbered 대응 결과를 구분.
MFE-C07 0, 2 FrequencyDomain.MfeC07DensityFailures
tests/unit/io/abaqus/frequency_domain_test.cpp
Density scope/arity/duplicate/unresolved/missing/zero/negative/NaN/Inf/temperature/dependencies. Kernel 27-point invalid·geometry/t/material 거부는 Mitc4ShellMass에서 별도; density 없는 static 유지.
MFE-C08 1, 3, 5 Mitc4Covariance.MfeC08PartialBcCovariance
tests/integration/analysis/mitc4_covariance_test.cpp
NR001 rectangle/Z45에서 새 K/kd와 two mass, actual BC URZ-only. kd19/4800,Kff10369/7500,Mff3/50; Step5 actual FEAST lambda10369/450/full residual. Old min mutation 실패.
MFE-C09 1, 7 Mitc4Covariance.MfeC09StaticReactionCovariance
tests/integration/analysis/mitc4_covariance_test.cpp
Admissible physical moment (5/3)b, thetaZ7500/10369. Fresh geometry/load 재생성 후 full Kq-F의 모든 reaction component와 physical projection 기준 확인.
MFE-C10 1 Mitc4DrillingScale.MfeC10TraceInventory
tests/unit/elements/mitc4_drilling_scale_test.cpp
[0,0,2,4,0,6,0,4]→kref2, zero/negative/nonfinite/large/subnormal; node별 독립 tangent rotation angles. Positive-only mean/min/floor mutation 검출.
MFE-C11 1, 2, 3, 5 LinearFrequencyProcedure.MfeC11SixPhysicalRigidModes
tests/integration/analysis/linear_frequency_procedure_test.cpp
Free element/patch six tangent-projected rigid fields vs four massless drills. PhysicalK20 rank14/MC20PD/K24rank18, finite20·six near-zero actual modes와 mass Gram; preprocessing tests도 포함.
MFE-C12 1, 7 NonlinearMitc4Tangent.MfeC12FrozenCovariantScale
tests/unit/elements/nonlinear_mitc4_shell_tangent_test.cpp
Initial S1 scale의 trial/commit/rollback 불변, existing gamma의 full FD schedule/index3/tangent/objectivity/physical linear limit/recovery. 기존 tests는 회귀이며 새 RED로 가장하지 않음.
MFE-C13 0, 3, 5 EigenReduction.MfeC13ExactBoundaryNullIntersection
tests/unit/analysis/eigen_reduction_test.cpp
NR 세 director와 supported mask 조합, drill-only는 axis-aligned URZ만; per-node rank0를 global failure로 오인하지 않음. Global n0/N>n, source constraint exact0.
MFE-C14 3, 5 EigenReduction.MfeC14NearNullConditioning
tests/unit/analysis/eigen_reduction_test.cpp
p1/2000 analytic5 pass, p1e-8 analytic5 conditioning/Cholesky fail, p0 null1. rcond2^-32 equality/nextafter tests; tiny positive를 삭제하면 failure.
MFE-C15 3 EigenReduction.MfeC15RejectsInvalidDecoupling
tests/unit/analysis/eigen_reduction_test.cpp
Test-local K6,2],[2,4,Mdiag2,0 finite2.5/z=-p/2. Production Kpz nonzero/Kzz nonpositive injected rejection; 삭제3 또는 general Schur fallback 금지.
MFE-C16 3, 5 EigenValidation.MfeC16OriginalMassAndPairing
tests/unit/analysis/eigen_validation_test.cpp
M2,1],[1,2,K6,4],[4,6→2,10/3; triangular whitening/restoration. Wrong M/lambda/vector/null/constraint corruption을 full original-space 검사로 탐지.
MFE-C17 4, 5 MklFeastEigenSolver.MfeC17KnownLowestSpectra
tests/unit/solvers/eigen/mkl_feast_eigen_solver_test.cpp
Actual API+production search: n1; [0,0,0,2,5,9] N1/4/6; [1,2,4,8] N1/2/4; allzero n4 N1/4. Matrix-derived enclosure/count history와 independent ordered spectrum.
MFE-C18 4, 5 MklFeastEigenSolver.MfeC18RepeatedAndTightClusters
tests/unit/solvers/eigen/mkl_feast_eigen_solver_test.cpp
Actual n64 fortyfold1+2..25 N1/41; tight[1,1+2^-28,1+2^-27,8] N2; fixed Givens dense CSR. c>N/all-c Gram와 gap/8, repeated projector 기준. 개별 repeated vector equality 아님.
MFE-C19 4, 5, 6 EigenValidation.MfeC19RawSignedEigenvalues
tests/unit/analysis/eigen_validation_test.cpp
Actual [-2,0,3],[-1e-12,0,1] backend raw; procedure significant negative failure. ±tauzero 및 nextafter, frequency ID subset/finite/underflow. Small positive도 N에 포함.
MFE-C20 2, 3, 5 LinearFrequencyProcedure.MfeC20DensityAndUnits
tests/integration/analysis/linear_frequency_procedure_test.cpp
TwoM×4,lambda/4,amplitude1/2; repeated projector; length1024/rho1024^-3/E1024^-1/t1024의 lambda invariance. Baseline s와 동일 물리 BC.
MFE-C21 4, 5, 6 FeastSearch.MfeC21StatusAndHistoryFailures
tests/unit/solvers/eigen/feast_search_test.cpp
C21 전체 raw status inventory, endpoint/zero count/nonmonotonicity/total!=n/m!=c, invalidCSR/fpm/SPD/QR/pair/residual. c>N의 뒤 column 손상과 false-lowest success를 검출; 허용 retry 외 즉시 실패.
MFE-C22 4, 6 FeastSearch.MfeC22FiniteBudgetsAndCapacity
tests/unit/solvers/eigen/feast_search_test.cpp
512count/3solve inclusive와 초과,384 optimization+128reserve, certified fallback. n²/n*m0/nnz+1/byte overflow 및 allocation injected failure; 큰 allocation 실제 고갈을 유발하지 않음.
MFE-C23 0, 5, 6, 7 FrequencyDomain.MfeC23ExactReferenceSubset
tests/unit/io/abaqus/frequency_domain_test.cpp
원본729/676/Step-2/N10, density/t/S4R/warnings/source records. N invalid·fields27 nonempty0/eighth empty·unsupported enum/procedure/load/acoustic/mixed/nonzero BC; I1I6 전 inventory.
MFE-C24 5, 6, 7 EigenHdf5Schema.MfeC24RoundTripAndRejection
tests/unit/io/hdf5/eigen_hdf5_schema_test.cpp
O1~O5 independent reader/mutation, N/c/source/units/backend/ML/flags/types/ranks. FrequencyApplication failure matrix는 이전 final/state hash와 최초final부재를 각각 확인.
MFE-C25 7 EigenReferenceCases.MfeC25FreshTenValueFamilies
tests/reference/eigen_reference_cases_test.cpp
원본TXT full grammar+source join/10singleton 실제fresh eigen 비교. EigenReferenceComparison 음성/1%RMS·5%row·near-zero·S0 boundaries, Freq mutation 판정불변.
MFE-C26 1, 6, 7 Mitc4EigenStaticRegression.MfeC26FreshApprovedPortfolio
tests/reference/mitc4_eigen_static_regression_test.cpp
shell/shellNL/shellNL-meshfine와 linear-limit 역할. 원본copy→actualrun→기존 Ublocking/URwarning/localreactionreview+factor1 internalbalance. Existing meshfine-02 post-closure·pressure fullsuite 유지.
MFE-C27 0, 1, 2, 3, 4, 5, 6, 7 EigenReferenceCases.MfeC27FreshAndRepeatedEvidence
tests/reference/eigen_reference_cases_test.cpp
모든 Step RED/GREEN/full 실제exits/warnings/identity; 같은 binary/runtime/thread/input3회 CSR/row/diagnostic exact와 scalar/projector. Fresh binary/source/result evidence·canonical reports, 독립Physics/Release는 후속gate.

C07은 추가 Mitc4ShellMass.MfeC07InvalidMassGeometry, C08은 LinearFrequencyProcedure.MfeC08PartialBcEigenvalue, C24는 FrequencyApplication.MfeC24PreservesFinalAndState, comparator 음성/경계는 EigenReferenceComparison.MfeC25PrechecksAndToleranceBoundaries를 mandatory로 둔다. 각 companion은 해당 Step의 discovery에서 확인하고 record한다.

V3. 전체 must requirement 대응

전27개 requirement의 NR case mapping을 그대로 옮겨 실행 Step에 연결했다. Requirement025의 Implementation evidence는 여기서 준비하되 독립 Physics/Release gate는 후속 owner가 수행한다.

Requirement ID Required NR cases Implementation steps
FEM-REQ-MITC4-EIGEN-001 MFE-C23, MFE-C25 0, 5, 6, 7
FEM-REQ-MITC4-EIGEN-002 MFE-C01, MFE-C08, MFE-C12, MFE-C23, MFE-C26 0, 1, 2, 3, 5, 6, 7
FEM-REQ-MITC4-EIGEN-003 MFE-C06, MFE-C07, MFE-C23 0, 2, 5, 6, 7
FEM-REQ-MITC4-EIGEN-004 MFE-C01, MFE-C02, MFE-C07 0, 2
FEM-REQ-MITC4-EIGEN-005 MFE-C01, MFE-C02, MFE-C03 2
FEM-REQ-MITC4-EIGEN-006 MFE-C01, MFE-C03, MFE-C11, MFE-C13 0, 1, 2, 3, 5
FEM-REQ-MITC4-EIGEN-007 MFE-C06, MFE-C13, MFE-C27 0, 1, 2, 3, 4, 5, 6, 7
FEM-REQ-MITC4-EIGEN-008 MFE-C08, MFE-C13, MFE-C14, MFE-C15, MFE-C16 0, 1, 3, 5
FEM-REQ-MITC4-EIGEN-009 MFE-C13, MFE-C17, MFE-C18, MFE-C19, MFE-C22, MFE-C23 0, 3, 4, 5, 6, 7
FEM-REQ-MITC4-EIGEN-010 MFE-C17, MFE-C18, MFE-C25, MFE-C27 0, 1, 2, 3, 4, 5, 6, 7
FEM-REQ-MITC4-EIGEN-011 MFE-C03, MFE-C16, MFE-C17, MFE-C23, MFE-C25 0, 2, 3, 4, 5, 6, 7
FEM-REQ-MITC4-EIGEN-012 MFE-C14, MFE-C19, MFE-C21, MFE-C22, MFE-C24 3, 4, 5, 6, 7
FEM-REQ-MITC4-EIGEN-013 MFE-C11, MFE-C16, MFE-C17, MFE-C18, MFE-C21 1, 2, 3, 4, 5, 6
FEM-REQ-MITC4-EIGEN-014 MFE-C01, MFE-C02, MFE-C03, MFE-C04, MFE-C05, MFE-C08, MFE-C11, MFE-C20 1, 2, 3, 5
FEM-REQ-MITC4-EIGEN-015 MFE-C23, MFE-C25 0, 5, 6, 7
FEM-REQ-MITC4-EIGEN-016 MFE-C07, MFE-C21, MFE-C23 0, 2, 4, 5, 6, 7
FEM-REQ-MITC4-EIGEN-017 MFE-C24, MFE-C25 5, 6, 7
FEM-REQ-MITC4-EIGEN-018 MFE-C06, MFE-C13, MFE-C16, MFE-C24, MFE-C25 0, 2, 3, 5, 6, 7
FEM-REQ-MITC4-EIGEN-019 MFE-C21, MFE-C22, MFE-C24 4, 5, 6, 7
FEM-REQ-MITC4-EIGEN-020 MFE-C25, MFE-C26 1, 6, 7
FEM-REQ-MITC4-EIGEN-021 MFE-C25 7
FEM-REQ-MITC4-EIGEN-022 MFE-C25 7
FEM-REQ-MITC4-EIGEN-023 MFE-C27 0, 1, 2, 3, 4, 5, 6, 7
FEM-REQ-MITC4-EIGEN-024 MFE-C09, MFE-C12, MFE-C26, MFE-C27 0, 1, 2, 3, 4, 5, 6, 7
FEM-REQ-MITC4-EIGEN-025 MFE-C27 0, 1, 2, 3, 4, 5, 6, 7
FEM-REQ-MITC4-EIGEN-026 MFE-C01, MFE-C02, MFE-C03, MFE-C04, MFE-C05 2
FEM-REQ-MITC4-EIGEN-027 MFE-C08, MFE-C09, MFE-C10, MFE-C11, MFE-C12, MFE-C26 1, 2, 3, 5, 6, 7

V4. I/O obligation 대응

Obligation ID Steps Positive/negative evidence
IO-T01 0, 5, 6, 7 C23; 원본 unchanged semantic snapshot/source warnings→actual backend→HDF5.
IO-T02 0, 2 C01/02/06/07; material/section/rho/t 관계와 semantic/kernel negative inventory.
IO-T03 0, 6 C07/13/23; exact parameter/slot/count/procedure/BC 실패와 exit category.
IO-T04 0, 2, 3, 5 C06/13/14/24; source renumbering/ambiguity와 actual BC의 rank/복원.
IO-T05 3, 5, 6, 7 C16/19/24; O1~O5/Nvectors/all-c raw metrics, source identity와 sameML replay.
IO-T06 4, 5, 6 C17/18/19/21/22; actual API와 injected failure/budget/availability history.
IO-T07 5, 6 C24; solve/validation/log/lease/write/flush/close/reopen/selfcheck/replace 전 failure matrix.
IO-T08 7 C25; all-row parser/schema/identity finite prechecks, Freq structure-only mutation.
IO-T09 7 C25; existing policy inclusive boundaries/near-zero/S0·singleton RMS independently checked.
IO-T10 0, 1, 6, 7 C26/27; density없는static/parser/schema 보존·fresh portfolio·mode-shape reference 미요구.
IO-T11 0, 1, 2, 3, 4, 5, 6, 7 C27; step별 meaningfulRED/실제fullcounts/새warning0/3repeat·freshhash.

V5. 수학·backend·schema의 누락 방지

Upstream item Implementation / independent evidence
Mass M1~M10 Step2 C01~07; sign S=-[d]x, exact quadrature order, original moments, two matrix energy/centroid/Jensen, no artificial drill
Stabilization S1~S8 Step1 C08~12, Step3/5 partial-BC original pencil, Step7 static fresh portfolio; old formula만 대체
Eigen E1~E5 Step3 C13~16; D,C,P,Z,L과 DOF pattern owner, exact ranks/rcond/zero branches/original residual 보존
Eigen E6/E7 + C5/C6 Step4 actual FEAST·all count/status history, per-call fpm128·seed/QR·thread restore·index/allocation/capacity·유한 budgets
V1V5 + C1C4 Step5 all-c original ML residual/Gram/energy/rhoB/rigid/scale/projector; Step7 N persistent replay
Eigen E8 + IO-M2 Step5 candidate, Step6 final replacement 후 noexcept swap, all failure matrix/oldfinal hash; zero-byte log 독립
I1~I6 Step0 grammar/source/semantic positives+negatives, Step6 actual CLI exits/stable warnings; sourceN와 internalc 분리
O1/O2 Step6 discriminant/types/mandatory metadata/model identities/source_records/densities; Step7 input-byte identity
O3 Step5 raw paired N/cluster/classification/frequency subset/sign, Step6 N×nn×6 persistence/units/constraint0
O4/O5 Step4/5 all-c evidence source, Step6 exact schema/fpm128/available flags/readback/relations/self-check
IO-M3/M4 + common tolerance Step7 full-row TXT/source join10singleton/Value only; external Freq/vector gate 없음; static roles 보존

원 full K/M 및 independent particle/polynomial 기대값은 solve의 reduced A나 serializer 자기 복사에서 생성하지 않는다. Self-check는 저장 일관성 검사이며 independent physics를 대신하지 않는다. FEAST의 documented count는 무조건 completeness theorem이 아니다. Count/empty-prefix/guard/ solve-count/전체 c residual·Gram 및 known spectra를 함께 확보한다.

V6. Fresh reference 실행과 보고서

새 eigen 원본은 reference-model의 input/TXT를 그대로 사용한다. Step7의 unique generated execution directories 아래 bytes를 복사하고 FesaApplication을 실제 호출한다. Reference 전후 SHA-256/byte size/mtime, copied input exact bytes, actual binary/lib SHA-256, start/end/exit, final h5/log hash를 기록한다. Metadata source path는 실제 copied lexical input path와 맞아야 하며 원본과는 byte identity를 따로 확인한다.

Fresh eigen 세 run은 같은 input bytes/runtime/thread/settings를 사용한다. 각 run10modes, nf3750/nz625/n3125와 all-c checks/call histories, N persistent vectors의 sameML 원 residual을 확인한다. Same-input deterministic replay는 각 run을 같은 lexical input path에서 직접 solver로 재실행한 뒤 run별 결과를 고유 evidence directory로 복사해 비교할 수 있다. 복사본 schema의 source path는 원 실행 path 그대로 보존한다. 모드 벡터 외부 reference는 요구하지 않는다.

Count x의3125²×8=78,125,000bytes는 한 scratch allocation 크기다. 실제 reference test process의 peak working set/private memory를 관찰해 peak 측정 범위와 PID/command를 기록한다. 이 수를 전체 peak나 sparse shifted fill로 대체하지 않는다. Process 전체 peak는 solver-only allocation peak의 upper bound임을 구분한다. 관찰은 managed test process의 actual exit까지 연결하고 unobserved detached 실행은 하지 않는다.

Static portfolio는 기존 요구의 8개 required/review-only artifacts와 같은 input/source identity를 사용한다. Existing comparator의 artifact disposition을 적용하여 제외 stress/RM3를 새 mandatory로 만들지 않는다. shell-s4/shell-s4-linear-limit는 같은 fresh solve를 두 역할에 쓸 수 있으나 보고서는 분리한다. Nonlinear final factor1 accepted-state/U blocking/UR warning/RF1..3와 spatial RM1..2 review-only, internal force/moment balance blocking은 보존한다. Registered shellnl-meshfine-02-s4-final는 별도 post-closure 역할이며 pressure를 포함한 기존 full suite도 실행한다. 기존 reports는 수정하지 않고 현재 r2 새 evidence를 남긴다.

Future outputs는 Step7의 implementation-report.md, build-test.md, reference-comparison.md, reference-comparison-detail.md다. Step별 implementation-stepN-evidence.md와 prep evidence는 실제 실행 때만 생성한다. Final canonical3 보고서는 같은 r2의 실제 command/exit/count/수치·원본보존·실제 미실행 내역·전체 requirement trace를 연결한다. 마지막 registered Stop/actual exit가 아직 미관찰이면 확인 대기로 구분하며 Executor 종료 뒤 Coordinator의 management 사후 감사를 받는다. Physics/Release 판정과 외부 release 권한을 대신하지 않는다.

V7. 계획 정적 감사

정적 감사에서 actual StepExecutor의 read/validation methods만 호출했다. Executor 실행, CMake/CTest/Python tests, 실제 FEAST 또는 reference solve는 호출하지 않았다. 첫 감사는 Step2의 mitc4_shell.h에 대응하는 기존 C++ test의 owned 목록 누락을 찾아 exit1이었다. mitc4_shell_test.cpp를 같은 Step index/Markdown에 추가한 뒤 전체 audit exit0을 확인했다. Local link target 검사에서 발견한 I/O M1 anchor 두 곳도 실제 heading으로 수정했다.

최종 read-only command의 actual exit0 결과: prep1/main8, 계획 산출물16, owned path128회 (고유110), 모든 Step의 upstream22 입력, must27/NR27/I/O11, 필수 신규 test 이름40개, local Markdown links575개, 누락/anchor warning0. 기존 top index의5개 entry/status/history는 HEAD와 deep equality다. 모든 새 Step은 pending이며 JSON/Markdown owned 목록이 동일하다. 최초 git diff --check exit0은 unstaged diff만 검사하여 당시 untracked 신규 파일을 포함하지 않았다. 별도 line trailing whitespace 검사도 여분 EOF blank를 검출하지 못했다. Root의 checkpoint staged 검사에서 EOF blank3개를 발견하여 지정된3파일만 정리했다. 아래 전체 산출물 검사는 EOF newline/여분 blank 검사까지 보완한 뒤 actual exit0을 확인했다. Root의 재staging과 staged 검사는 별도 소유 작업이며 여기서 그 성공을 선기록하지 않는다. Hash는 아래 command가 산출한 SHA-256을 handoff에 기록한다. Bundle digest는 sorted path, NUL, raw bytes를 이어 hash하며 자기 digest를 이 문서에 삽입하지 않는다.

아래는 workspace root에서 수행한 정확한 read-only 감사 command다. Test 이름 검사는 계획 inventory/regex/등록 target의 정적 일관성이며 아직 존재하지 않는 C++ test의 실제 discovery/실행 성공을 주장하지 않는다. 해당 실제 검증은 각 Implementation Step의 AC다.

@'
import hashlib,json,re,subprocess
from pathlib import Path
from urllib.parse import unquote
from scripts.execute import StepExecutor
root=Path.cwd(); feature="mitc4-shell-feast-eigen"; doc=root/"docs"/feature
phases=[feature+"-execution-prep",feature]
artifacts=[doc/n for n in ("implementation-plan.md","implementation-environment.md","implementation-interfaces.md","implementation-verification.md")]
artifacts += [root/"phases"/"index.json"]
counts=[]; owners=[]; names=[]; steps=[]
required=["FEATURED_AGENTS.md","PRD.md","ADR.md","coordination.md","requirements.md","requirements-evidence.md","research.md","research-evidence.md","formulation.md","formulation-mass.md","formulation-eigen.md","formulation-verification.md","formulation-stabilization.md","numerical-review.md","numerical-review-evidence.md","numerical-review-controls.md","numerical-review-cases.md","reference-model.md","io.md","io-input.md","io-schema.md","io-mapping.md"]
assert all((doc/n).is_file() for n in required)
for phase in phases:
    ex=StepExecutor(phase); ex._read_top_index(); data=ex._read_phase_index()
    assert all(s["status"]=="pending" for s in data["steps"])
    artifacts.append(root/"phases"/phase/"index.json"); counts.append(len(data["steps"]))
    for step in data["steps"]:
        path=root/"phases"/phase/f'step{step["step"]}.md'
        artifacts.append(path); steps.append((phase,step,path))
        body=path.read_text(encoding="utf-8"); owned=ex._step_owned_paths(step)
        for p in owned: ex._validate_owned_file(p)
        assert list(owned)==json.loads(re.search(r"\x60{3}json\n(.*?)\n\x60{3}",body,re.S)[1]),path
        assert all(f"docs/{feature}/{p}" in body for p in required),path
        assert all(x in body for x in ("Input contract revision:","implementation-interfaces.md","RED","GREEN","Full VERIFY","error_message","blocked_reason","actual exit","사후 감사")),path
        assert len(owned)>0
        owners.extend(owned)
        if phase==feature:
            inv=body.split("### 필수 신규 test discovery inventory\n",1)[1].split("## Full VERIFY",1)[0]
            planned=re.findall(r"^- \x60([^\x60]+)\x60",inv,re.M)
            regex=re.search(r"-R '([^']+)'",body)[1]
            assert planned and all(re.search(regex,n) for n in planned),(path,planned,regex)
            names.extend(planned); assert "--target fesa_tests" in body
            for p in owned:
                if p.startswith(("src/fesa/","include/fesa/")) and Path(p).suffix in (".cpp",".h"):
                    stem=Path(p).stem+"_test.cpp"
                    assert any(Path(t).name==stem and t.startswith("tests/") for t in owned),(path,p)
base=json.loads(subprocess.check_output(["git","show","HEAD:phases/index.json"],text=True))
top=json.loads((root/"phases/index.json").read_text(encoding="utf-8"))
assert top["phases"][:-2]==base["phases"]
assert [p["dir"] for p in top["phases"][-2:]]==phases
assert counts==[1,8] and len(artifacts)==16
v=(doc/"implementation-verification.md").read_text(encoding="utf-8")
nr=(doc/"numerical-review-cases.md").read_text(encoding="utf-8")
reqmap={m[0]:re.findall(r"MFE-C\d{2}",m[1]) for m in re.findall(r"^\| (\d{3}) \| ([^|]+) \|",nr,re.M)}
planmap={m[0]:re.findall(r"MFE-C\d{2}",m[1]) for m in re.findall(r"^\| FEM-REQ-MITC4-EIGEN-(\d{3}) \| ([^|]+) \|",v,re.M)}
assert reqmap==planmap and len(planmap)==27
assert len(re.findall(r"^\| MFE-C\d{2} \|",v,re.M))==27
assert len(re.findall(r"^\| IO-T\d{2} \|",v,re.M))==11
primary=re.findall(r"^\| MFE-C\d{2} \| [^|]+\| \x60([^\x60]+)\x60",v,re.M)
assert len(primary)==27 and set(primary)<=set(names)
links=0; anchor_warnings=[]
def slug(s):
    s=s.replace(chr(96),"").strip().lower()
    s=re.sub(r"[^\w\- ]","",s)
    return s.replace(" ","-")
for p in artifacts:
    body=p.read_text(encoding="utf-8")
    assert body.endswith("\n") and not body.endswith("\n\n"),p
    assert not any(line.rstrip()!=line for line in body.splitlines()),p
    if p.suffix!=".md": continue
    for target in re.findall(r"\[[^\]]+\]\(([^)]+)\)",body):
        if re.match(r"^[a-z]+://",target): continue
        pathpart,_,anchor=unquote(target.strip("<>")).partition("#")
        linked=(p.parent/pathpart).resolve() if pathpart else p
        assert linked.is_file(),(p,target)
        links+=1
        if anchor and linked.suffix==".md":
            headings=[slug(h) for h in re.findall(r"^#+ (.+)$",linked.read_text(encoding="utf-8"),re.M)]
            if anchor not in headings: anchor_warnings.append((p.relative_to(root).as_posix(),target))
for p in ("implementation-report.md","build-test.md","reference-comparison.md","physics-evaluation.md","release.md"):
    assert not (doc/p).exists(),p
cm=(root/"tests/CMakeLists.txt").read_text(encoding="utf-8")
assert re.search(r"add_custom_target\(\s*fesa_tests\s+DEPENDS\s+fesa_unit_tests\s+fesa_integration_tests\s+fesa_reference_tests",cm)
diff=subprocess.run(["git","diff","--check","--"]+[p.relative_to(root).as_posix() for p in artifacts],capture_output=True,text=True)
assert diff.returncode==0,diff.stdout+diff.stderr
digest=hashlib.sha256()
for p in sorted(artifacts):
    digest.update(p.relative_to(root).as_posix().encode());digest.update(b"\0");digest.update(p.read_bytes())
print(json.dumps({"phase_steps":counts,"artifacts":len(artifacts),"owned_path_occurrences":len(owners),"unique_owned_paths":len(set(owners)),"required_inputs":len(required),"requirement_rows":len(planmap),"nr_cases":len(primary),"io_obligations":11,"mandatory_test_names":len(names),"local_links":links,"anchor_warnings":anchor_warnings,"git_diff_check":diff.returncode,"bundle_sha256":digest.hexdigest()},ensure_ascii=False))
for p in artifacts[:4]: print(p.name,hashlib.sha256(p.read_bytes()).hexdigest())
'@ | python -B -

관리 문서 coordination.md의 동시 owner 변경은 이 계획 patch에 포함하지 않았다. 현재 실행 준비 완료는 계획 materialization을 뜻한다. Native transport, prep 실제 full VERIFY/ 등록 Stop 및 새 main invocation의 구현·FEAST/reference 성공은 후속 execution evidence가 필요하다.