diff --git a/docs/architecture/calibration_tooling.md b/docs/architecture/calibration_tooling.md index 7ffaf86..784c669 100644 --- a/docs/architecture/calibration_tooling.md +++ b/docs/architecture/calibration_tooling.md @@ -94,11 +94,14 @@ session.l3dcal session.l3dcal.bundle/detection.json session.l3dcal.bundle/solve.json session.l3dcal.bundle/evidence.json +session.l3dcal.bundle/producer.json ``` together with the exact selected execution and optical state, then construct the bounded `Lardon3DCalibrationToolingEvidence`. +`producer.json` binds the exact solver executable SHA-256, canonical solver-configuration SHA-256, exact session SHA-256, OpenCV build identity, CPU1 policy and optical-state SHA-256. + The coordinator must verify regular bounded files, immutable digests, session/bundle identity, categorical planarity PASS, selected-execution image binding and optical-state equality. It must never manufacture missing evidence. diff --git a/docs/roadmap/roadmap.md b/docs/roadmap/roadmap.md index 633d456..aa5df5b 100644 --- a/docs/roadmap/roadmap.md +++ b/docs/roadmap/roadmap.md @@ -521,6 +521,8 @@ deterministic synthetic CPU1 self-test is validated. The solver session contract now also requires explicit measured `white_border >= 30 mm` evidence. This closes the previous gap between the physical target requirement and the future coordinator input; no default border width is inferred. +`CALIBRATION_SOLVER_PRODUCER_IDENTITY_V1=PASS/FROZEN`: the solver bundle now includes deterministic `producer.json` evidence binding the exact solver executable, canonical solver configuration, session manifest, OpenCV build, CPU1 policy and optical-state SHA-256. + A bounded Tooling correction aligned planarity handling with Calibration Science v1: Science v1 defines a categorical physical planarity attestation, not a numeric flatness threshold. Tooling therefore rejects invented finite `target_flatness_mm` values. `L3DCALB1` v1 and Calibration diff --git a/prompt/02_CURRENT_FROZEN_STATE.md b/prompt/02_CURRENT_FROZEN_STATE.md index 643f61d..25fe320 100644 --- a/prompt/02_CURRENT_FROZEN_STATE.md +++ b/prompt/02_CURRENT_FROZEN_STATE.md @@ -28,6 +28,7 @@ External SSD controller VALIDATED Calibration Solver Preflight v1 PASS Calibration Evidence Solver v1 IMPLEMENTED/VALIDATED Calibration solver white-border evidence PASS/FROZEN +Calibration solver producer identity PASS/FROZEN Calibration Tooling planarity alignment PASS/FROZEN ``` diff --git a/prompt/12_CALIBRATION.md b/prompt/12_CALIBRATION.md index adaa302..fad0fb7 100644 --- a/prompt/12_CALIBRATION.md +++ b/prompt/12_CALIBRATION.md @@ -9,6 +9,7 @@ CALIBRATION_TOOLING_PLANARITY_ALIGNMENT=PASS/FROZEN CALIBRATION_BOOTSTRAP_V1=PASS/FROZEN CALIBRATION_EVIDENCE_SOLVER_V1=IMPLEMENTED/VALIDATED CALIBRATION_SOLVER_WHITE_BORDER_V1=PASS/FROZEN +CALIBRATION_SOLVER_PRODUCER_IDENTITY_V1=PASS/FROZEN CALIBRATION_WORKFLOW=IN_PROGRESS CURRENT_CALIBRATION_NEXT=WORKFLOW_COORDINATOR ``` diff --git a/tools/calibration_evidence_solver/README.md b/tools/calibration_evidence_solver/README.md index e01e68d..f78e687 100644 --- a/tools/calibration_evidence_solver/README.md +++ b/tools/calibration_evidence_solver/README.md @@ -57,7 +57,10 @@ d'orientation, dimensions et mesures de coordonnées plutôt que d'émettre le bit `0x08` par défaut. Sur réussite, le solveur crée de manière atomique `.bundle/`, qui est -immuable si déjà présent : `detection.json`, `solve.json` et `evidence.json`. +immuable si déjà présent : `detection.json`, `solve.json`, `evidence.json` et +`producer.json`. Ce dernier lie le SHA-256 du binaire solveur en cours, le +SHA-256 de sa configuration canonique, le SHA-256 exact du manifeste de +session, la version/build OpenCV, la politique CPU1 et le SHA de l’état optique. Les documents ont un ordre déterministe et encodent les valeurs faisant autorité comme chaînes `hexfloat` binary64. `solve.json` archive les trois sorties complètes (paramètres et poses); `evidence.json` archive les vecteurs diff --git a/tools/calibration_evidence_solver/calibration_evidence_solver b/tools/calibration_evidence_solver/calibration_evidence_solver index 9f53744..8dd3539 100755 Binary files a/tools/calibration_evidence_solver/calibration_evidence_solver and b/tools/calibration_evidence_solver/calibration_evidence_solver differ diff --git a/tools/calibration_evidence_solver/calibration_evidence_solver.cpp b/tools/calibration_evidence_solver/calibration_evidence_solver.cpp index 5afeb67..34ca562 100644 --- a/tools/calibration_evidence_solver/calibration_evidence_solver.cpp +++ b/tools/calibration_evidence_solver/calibration_evidence_solver.cpp @@ -250,13 +250,33 @@ bool exact_solve(const Solve& a, const Solve& b) { return true; } std::string number(double x) { std::ostringstream o; o.imbue(std::locale::classic()); o<& views, const Solve full[3], const Solve& fit, double rms, double maximum, size_t high, size_t count, double hold_rms, double hold_max, double delta, unsigned flags, - const std::array& optical, std::string* why) { + const std::array& optical, + const std::array& solver_executable_sha256, + const std::array& solver_configuration_sha256, + const std::array& session_sha256, + std::string* why) { const std::filesystem::path final = session + ".bundle"; const std::filesystem::path temp = session + ".bundle.tmp"; std::error_code ec; if(std::filesystem::exists(final,ec)||ec||std::filesystem::exists(temp,ec)){*why="evidence bundle already exists";return false;} @@ -273,6 +293,19 @@ bool write_bundle(const std::string& session, const SessionEvidence& e, const st std::ofstream f(temp/"evidence.json",std::ios::binary);if(!f)return fail("final evidence output"); f<<"{\n\"format\":\"L3DCAL_EVIDENCE_BUNDLE_V1\",\n\"target\":{\"id\":\""<(high)/static_cast(count))<<"\",\n\"holdout\":{\"rmse_px\":\""<&v,const Solve&s,double independent_rms, @@ -288,6 +321,9 @@ void report(const std::vector&v,const Solve&s,double independent_rms, } bool run_session(const std::string& session) { std::vector v;std::array optical{};SessionEvidence evidence;std::string why; + std::array solver_executable_sha256{},session_sha256{}; + if(!file_hash("/proc/self/exe",&solver_executable_sha256)||!file_hash(session,&session_sha256)){std::cerr<<"FAIL producer/session hash\\n";return false;} + const std::array solver_configuration_sha256=text_hash(solver_configuration_record()); if(!read_session(session,&v,&optical,&evidence,&why)||!detect(&v,&why)){std::cerr<<"FAIL "< all;cv::Size size;for(size_t i=0;ifit,hold;if(!validate_and_split(&v,&fit,&hold,&why)){std::cerr<<"FAIL "< optical{};std::string bundle_why; + Solve runs[3]={a,b,c};std::array optical{},solver_executable_sha256{},solver_configuration_sha256{},session_sha256{};solver_executable_sha256.fill(0xa1);solver_configuration_sha256.fill(0xa2);session_sha256.fill(0xa3);std::string bundle_why; const std::filesystem::path left=std::filesystem::temp_directory_path()/"l3dcal_byte_identity_left"; const std::filesystem::path right=std::filesystem::temp_directory_path()/"l3dcal_byte_identity_right";std::error_code ec;std::filesystem::remove_all(left.string()+".bundle",ec);std::filesystem::remove_all(right.string()+".bundle",ec); - const bool written=write_bundle(left.string(),evidence,v,runs,f,rr,rm,rh,rc,rr,rm,delta,0x0f,optical,&bundle_why)&&write_bundle(right.string(),evidence,v,runs,f,rr,rm,rh,rc,rr,rm,delta,0x0f,optical,&bundle_why); + const bool written=write_bundle(left.string(),evidence,v,runs,f,rr,rm,rh,rc,rr,rm,delta,0x0f,optical,solver_executable_sha256,solver_configuration_sha256,session_sha256,&bundle_why)&&write_bundle(right.string(),evidence,v,runs,f,rr,rm,rh,rc,rr,rm,delta,0x0f,optical,solver_executable_sha256,solver_configuration_sha256,session_sha256,&bundle_why); auto identical=[&](const char* name){std::ifstream x(left.string()+".bundle/"+name,std::ios::binary),y(right.string()+".bundle/"+name,std::ios::binary);std::ostringstream xs,ys;xs<