From e85f13c4fd7b8b157eb90aa9b04f3fdb5ab17164 Mon Sep 17 00:00:00 2001 From: jsboige Date: Thu, 22 Oct 2026 18:00:04 +0200 Subject: [PATCH 1/3] feat(research,#19452): Cartier-Miller P1+ passe 2 Harvey -- 10809 stopping indices au 4e backend, 0 mismatch Backend Harvey C++/NTL du ground truth 92 execute sur Windows natif (voie run_windows.py de l'auteur : MSYS2 UCRT64 user-scope, GCC 16.2.0, NTL 11.6.0, sources hypellfrob vendoriees au pin 37a9b72). Tous les stopping indices des 92 premiers croises contre les recurrences Python independables (passe 1 #19523) : 10809 requetes, 0 mismatch ; 43 forced-big (sur-ensemble des 26 upstream), 0 mismatch ; 3/3 invalid-inputs fail-closed. Driver run_harvey_p1_plus.py + JSON au format upstream-comparable + section 7 du memo results. Schoof/BSGS passes 2 restent documentes en residuel (Python pur). Statut unrefereed draft inchange -- corroboration computationnelle, pas demonstration. Co-Authored-By: Claude Sonnet 5.5 --- .../cartier-miller-p1-plus-results.md | 35 +- .../p1_plus_harvey_validation.json | 483 ++++++++++++++++++ scripts/cartier_miller/run_harvey_p1_plus.py | 223 ++++++++ 3 files changed, 740 insertions(+), 1 deletion(-) create mode 100644 example_results/p1_plus_harvey_validation.json create mode 100644 scripts/cartier_miller/run_harvey_p1_plus.py diff --git a/docs/research/cartier-miller-p1-plus-results.md b/docs/research/cartier-miller-p1-plus-results.md index 227accfc28f..093d5239f40 100644 --- a/docs/research/cartier-miller-p1-plus-results.md +++ b/docs/research/cartier-miller-p1-plus-results.md @@ -184,4 +184,37 @@ Le script est **auto-suffisant** : il n'importe ni `pilot.py` ni `elliptic_prefi Tranche Python-only **PASS** : 92 premiers dérivés, 10809 stopping indices calculés, 2130 exact_binomial_checks vérifiés, 43 quarter_cross_checks validés, **0 désaccord** entre les deux implémentations Python indépendantes. Harvey C++ (passe 2) doit confirmer la cohérence 4-voies avec les backends Python + BSGS. -— Rapport rédigé par `myia-po-2023:CoursIA-2`, c.1101, lane worker, 2026-10-06. \ No newline at end of file +— Rapport rédigé par `myia-po-2023:CoursIA-2`, c.1101, lane worker, 2026-10-06. +--- + +## 7. Passe 2 Harvey C++ — PASS (2026-10-22, lane myia-po-2024:CoursIA, claim c.6344755635) + +**Tranche** : backend Harvey C++/NTL du P1+ ground truth 92, exécuté **sur Windows natif** via la voie documentée par l'auteur (`run_windows.py` : MSYS2 UCRT64) — le verdict RECOVERABLE-MACHINE du scoping est réduit à une installation locale (règle F). + +### 7.1 Outillage (reproductible) + +- MSYS2 UCRT64 installé **user-scope sans UAC** : `msys2-base-x86_64-latest.tar.xz` extrait vers `D:\Dev\tools\msys64`, puis `pacman -S mingw-w64-ucrt-x86_64-gcc mingw-w64-ucrt-x86_64-ntl`. +- Toolchain mesurée : GCC **16.2.0** (Rev6), NTL **11.6.0**, 1 thread (`--version` : `{"ntl":"11.6.0","compiler":"16.2.0","ntl_threads":1}`). +- Build : mêmes flags que `run_windows.py` (`-O3 -std=c++17 -pthread -I/include -L/lib`), sources = `harvey_adapter.cpp` + `upstream/recurrences_ntl.cpp` + `upstream/hypellfrob.cpp` **vendorisées au pin `37a9b72`** (aucun accès `/opt/sage` requis — le dépôt embarque les deux sources Sage hypellfrob avec leur provenance). Binaire 288 127 octets, SHA-256 `2ce5d768b430102c5efff804d08e3048be69066a746a240ce257b927668357cb` (consigné dans le JSON). +- **Piège reproductibilité (muet)** : sans `ucrt64/bin` dans le `PATH`, `cc1plus` meurt au chargement de ses DLL (libgmp/libmpfr/libisl) **sans écrire un seul octet d'erreur** — le driver g++ rend rc=1 silencieux, y compris sur erreur de syntaxe triviale. Le diagnostic passe par l'export du PATH avant tout appel. + +### 7.2 Résultats (`example_results/p1_plus_harvey_validation.json`, driver `scripts/cartier_miller/run_harvey_p1_plus.py`) + +| Critère | Attendu (upstream) | Mesuré | Verdict | +|---|---|---|---| +| Stopping-point queries | 10 809 (92 premiers) | **10 809, 92 premiers** | ✓ | +| Mismatches Harvey ↔ récurrences Python | 0 | **0** (`B`, `a`, `U` par L, tous stopping indices) | ✓ | +| Forced arbitrary-precision | 26 | **43** (quarter-points de tous les p ≤ 199, sur-ensemble), 0 mismatch | ✓ | +| Invalid inputs | 3 | **3/3** fail-closed (module composé, L > (p−1)/2, reps < 1) | ✓ | +| `status` | passed | **passed** | ✓ | + +Comparaison : pour **chaque** stopping index L ∈ [0, (p−1)/2] de **chacun** des 92 premiers, la sortie Harvey `calculate(p, L)` est confrontée aux récurrences Python indépendantes `prefix_values`/`original_values` vendorigées (passe 1, #19523) — la même croisée que `benchmark_engine.py` (clé `Harvey/Miller answer mismatch`), généralisée du seul quarter-point à tous les indices. + +### 7.3 Périmètre et résiduel + +- **Large-modulus (p ≈ 10⁸)** : non rejoués ici — déjà livrés par P1 integration #19510 côté Python ; la référence Python des récurrences coûte ~1 min/premier à cette taille. À prendre en note pour une éventuelle passe Harvey large-modulus. +- **Schoof + BSGS (§5)** : restent « à exécuter passe 2 » — backends Python purs, sans dépendance outillage ; le present document ne couvre que le backend C++. + +### 7.4 Ce que ça établit + +La cohérence **identité ↔ code ↔ validation** du manuscript est désormais croisée sur ce siège par le **backend C++/NTL de Harvey** contre les récurrences Python indépendantes, sur l'intégralité du ground truth 92 (10 809 requêtes). Rappel des gardes de l'issue : corroboration computationnelle, pas démonstration — le statut *unrefereed draft* du manuscript est inchangé. diff --git a/example_results/p1_plus_harvey_validation.json b/example_results/p1_plus_harvey_validation.json new file mode 100644 index 00000000000..da337d9bd3b --- /dev/null +++ b/example_results/p1_plus_harvey_validation.json @@ -0,0 +1,483 @@ +{ + "utc_started": "2026-10-22T15:59:51.560886+00:00", + "platform": "Windows-11-10.0.26300-SP0", + "python": "3.13.7", + "executable": "D:\\Dev\\tools\\harvey\\harvey_adapter.exe", + "executable_sha256": "2ce5d768b430102c5efff804d08e3048be69066a746a240ce257b927668357cb", + "harvey_version": { + "ntl": "11.6.0", + "compiler": "16.2.0", + "ntl_threads": 1 + }, + "validation": { + "all_stopping_indices": 10809, + "stopping_index_mismatches": 0, + "primes_all_indices": 92, + "forced_big_cross_checks": 43, + "forced_big_mismatches": 0, + "invalid_input_checks": 3, + "status": "passed" + }, + "per_prime": [ + { + "p": 7, + "stopping_indices": 4, + "mismatches": 0 + }, + { + "p": 11, + "stopping_indices": 6, + "mismatches": 0 + }, + { + "p": 13, + "stopping_indices": 7, + "mismatches": 0 + }, + { + "p": 17, + "stopping_indices": 9, + "mismatches": 0 + }, + { + "p": 19, + "stopping_indices": 10, + "mismatches": 0 + }, + { + "p": 23, + "stopping_indices": 12, + "mismatches": 0 + }, + { + "p": 29, + "stopping_indices": 15, + "mismatches": 0 + }, + { + "p": 31, + "stopping_indices": 16, + "mismatches": 0 + }, + { + "p": 37, + "stopping_indices": 19, + "mismatches": 0 + }, + { + "p": 41, + "stopping_indices": 21, + "mismatches": 0 + }, + { + "p": 43, + "stopping_indices": 22, + "mismatches": 0 + }, + { + "p": 47, + "stopping_indices": 24, + "mismatches": 0 + }, + { + "p": 53, + "stopping_indices": 27, + "mismatches": 0 + }, + { + "p": 59, + "stopping_indices": 30, + "mismatches": 0 + }, + { + "p": 61, + "stopping_indices": 31, + "mismatches": 0 + }, + { + "p": 67, + "stopping_indices": 34, + "mismatches": 0 + }, + { + "p": 71, + "stopping_indices": 36, + "mismatches": 0 + }, + { + "p": 73, + "stopping_indices": 37, + "mismatches": 0 + }, + { + "p": 79, + "stopping_indices": 40, + "mismatches": 0 + }, + { + "p": 83, + "stopping_indices": 42, + "mismatches": 0 + }, + { + "p": 89, + "stopping_indices": 45, + "mismatches": 0 + }, + { + "p": 97, + "stopping_indices": 49, + "mismatches": 0 + }, + { + "p": 101, + "stopping_indices": 51, + "mismatches": 0 + }, + { + "p": 103, + "stopping_indices": 52, + "mismatches": 0 + }, + { + "p": 107, + "stopping_indices": 54, + "mismatches": 0 + }, + { + "p": 109, + "stopping_indices": 55, + "mismatches": 0 + }, + { + "p": 113, + "stopping_indices": 57, + "mismatches": 0 + }, + { + "p": 127, + "stopping_indices": 64, + "mismatches": 0 + }, + { + "p": 131, + "stopping_indices": 66, + "mismatches": 0 + }, + { + "p": 137, + "stopping_indices": 69, + "mismatches": 0 + }, + { + "p": 139, + "stopping_indices": 70, + "mismatches": 0 + }, + { + "p": 149, + "stopping_indices": 75, + "mismatches": 0 + }, + { + "p": 151, + "stopping_indices": 76, + "mismatches": 0 + }, + { + "p": 157, + "stopping_indices": 79, + "mismatches": 0 + }, + { + "p": 163, + "stopping_indices": 82, + "mismatches": 0 + }, + { + "p": 167, + "stopping_indices": 84, + "mismatches": 0 + }, + { + "p": 173, + "stopping_indices": 87, + "mismatches": 0 + }, + { + "p": 179, + "stopping_indices": 90, + "mismatches": 0 + }, + { + "p": 181, + "stopping_indices": 91, + "mismatches": 0 + }, + { + "p": 191, + "stopping_indices": 96, + "mismatches": 0 + }, + { + "p": 193, + "stopping_indices": 97, + "mismatches": 0 + }, + { + "p": 197, + "stopping_indices": 99, + "mismatches": 0 + }, + { + "p": 199, + "stopping_indices": 100, + "mismatches": 0 + }, + { + "p": 211, + "stopping_indices": 106, + "mismatches": 0 + }, + { + "p": 223, + "stopping_indices": 112, + "mismatches": 0 + }, + { + "p": 227, + "stopping_indices": 114, + "mismatches": 0 + }, + { + "p": 229, + "stopping_indices": 115, + "mismatches": 0 + }, + { + "p": 233, + "stopping_indices": 117, + "mismatches": 0 + }, + { + "p": 239, + "stopping_indices": 120, + "mismatches": 0 + }, + { + "p": 241, + "stopping_indices": 121, + "mismatches": 0 + }, + { + "p": 251, + "stopping_indices": 126, + "mismatches": 0 + }, + { + "p": 257, + "stopping_indices": 129, + "mismatches": 0 + }, + { + "p": 263, + "stopping_indices": 132, + "mismatches": 0 + }, + { + "p": 269, + "stopping_indices": 135, + "mismatches": 0 + }, + { + "p": 271, + "stopping_indices": 136, + "mismatches": 0 + }, + { + "p": 277, + "stopping_indices": 139, + "mismatches": 0 + }, + { + "p": 281, + "stopping_indices": 141, + "mismatches": 0 + }, + { + "p": 283, + "stopping_indices": 142, + "mismatches": 0 + }, + { + "p": 293, + "stopping_indices": 147, + "mismatches": 0 + }, + { + "p": 307, + "stopping_indices": 154, + "mismatches": 0 + }, + { + "p": 311, + "stopping_indices": 156, + "mismatches": 0 + }, + { + "p": 313, + "stopping_indices": 157, + "mismatches": 0 + }, + { + "p": 317, + "stopping_indices": 159, + "mismatches": 0 + }, + { + "p": 331, + "stopping_indices": 166, + "mismatches": 0 + }, + { + "p": 337, + "stopping_indices": 169, + "mismatches": 0 + }, + { + "p": 347, + "stopping_indices": 174, + "mismatches": 0 + }, + { + "p": 349, + "stopping_indices": 175, + "mismatches": 0 + }, + { + "p": 353, + "stopping_indices": 177, + "mismatches": 0 + }, + { + "p": 359, + "stopping_indices": 180, + "mismatches": 0 + }, + { + "p": 367, + "stopping_indices": 184, + "mismatches": 0 + }, + { + "p": 373, + "stopping_indices": 187, + "mismatches": 0 + }, + { + "p": 379, + "stopping_indices": 190, + "mismatches": 0 + }, + { + "p": 383, + "stopping_indices": 192, + "mismatches": 0 + }, + { + "p": 389, + "stopping_indices": 195, + "mismatches": 0 + }, + { + "p": 397, + "stopping_indices": 199, + "mismatches": 0 + }, + { + "p": 401, + "stopping_indices": 201, + "mismatches": 0 + }, + { + "p": 409, + "stopping_indices": 205, + "mismatches": 0 + }, + { + "p": 419, + "stopping_indices": 210, + "mismatches": 0 + }, + { + "p": 421, + "stopping_indices": 211, + "mismatches": 0 + }, + { + "p": 431, + "stopping_indices": 216, + "mismatches": 0 + }, + { + "p": 433, + "stopping_indices": 217, + "mismatches": 0 + }, + { + "p": 439, + "stopping_indices": 220, + "mismatches": 0 + }, + { + "p": 443, + "stopping_indices": 222, + "mismatches": 0 + }, + { + "p": 449, + "stopping_indices": 225, + "mismatches": 0 + }, + { + "p": 457, + "stopping_indices": 229, + "mismatches": 0 + }, + { + "p": 461, + "stopping_indices": 231, + "mismatches": 0 + }, + { + "p": 463, + "stopping_indices": 232, + "mismatches": 0 + }, + { + "p": 467, + "stopping_indices": 234, + "mismatches": 0 + }, + { + "p": 479, + "stopping_indices": 240, + "mismatches": 0 + }, + { + "p": 487, + "stopping_indices": 244, + "mismatches": 0 + }, + { + "p": 491, + "stopping_indices": 246, + "mismatches": 0 + }, + { + "p": 499, + "stopping_indices": 250, + "mismatches": 0 + } + ] +} diff --git a/scripts/cartier_miller/run_harvey_p1_plus.py b/scripts/cartier_miller/run_harvey_p1_plus.py new file mode 100644 index 00000000000..9dff2c73d32 --- /dev/null +++ b/scripts/cartier_miller/run_harvey_p1_plus.py @@ -0,0 +1,223 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-or-later +"""Passe 2 Harvey C++ du P1+ ground truth 92 premiers (issue #19452). + +Drives the upstream harvey_adapter binary (compiled from the vendored +sources at pin 37a9b727df: harvey_adapter.cpp + upstream/hypellfrob.cpp + +upstream/recurrences_ntl.cpp, NTL/GMP via MSYS2 UCRT64 per run_windows.py) +and cross-checks it against the independent Python recurrences vendored in +generate_p1_plus_truth.py (passe 1, PR #19523): + +- all stopping indices L in [0, (p-1)//2] for each of the 92 primes in + [7, 499]: Harvey (B, a, U) == prefix_values(p)[L] / original_values(p)[L] + (upstream retained validation: 10,809 stopping-point queries); +- forced arbitrary-precision path (--force-big) on the quarter-point of + every prime <= 199 (superset of the upstream 26 forced-big checks); +- invalid inputs (composite modulus, L out of range, zero repetitions), + each expected to fail closed; +- --version metadata (NTL version, compiler, threads). + +Fail-closed: any mismatch or subprocess error => status != passed, exit 1. + +Large-modulus checks (p ~ 1e8) are NOT re-run here: the Python reference +(original_values/prefix_values) costs ~1 min per prime at that size and +they are already delivered by PR #19510 (P1 integration, 7 paper primes). +""" + +import argparse +import hashlib +import json +import platform +import subprocess +import sys +from datetime import datetime, timezone +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parent)) +from generate_p1_plus_truth import ( # noqa: E402 (vendored upstream, pin 37a9b72) + list_92, + original_values, + prefix_values, +) + +REPO_ROOT = Path(__file__).resolve().parents[2] + + +class HarveyWorker: + """Persistent harvey_adapter process, one query per stdin line.""" + + def __init__(self, executable: Path, force_big: bool = False): + cmd = [str(executable)] + (["--force-big"] if force_big else []) + self.process = subprocess.Popen( + cmd, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + encoding="utf-8", + errors="replace", + ) + + def query(self, p: int, L: int, reps: int = 1) -> dict: + assert self.process.stdin and self.process.stdout + self.process.stdin.write(f"{p} {L} {reps}\n") + self.process.stdin.flush() + line = self.process.stdout.readline() + if not line: + raise RuntimeError(f"harvey_adapter died: {self.process.stderr.read()}") + return json.loads(line) + + def close(self) -> None: + if self.process.stdin: + self.process.stdin.close() + self.process.wait(timeout=30) + + +def sha256_file(path: Path) -> str: + return hashlib.sha256(path.read_bytes()).hexdigest() + + +def check_prime_stopping_indices(worker: HarveyWorker, p: int) -> tuple[int, int]: + """All L in [0, (p-1)//2]: Harvey vs independent Python recurrences. + + Returns (stopping_indices_checked, mismatches). + """ + prefix = prefix_values(p) + original = original_values(p) + mismatches = 0 + for L in range((p - 1) // 2 + 1): + row = worker.query(p, L) + ref = prefix[L] + if row["B"] != ref["B"] or row["a"] != ref["a"] or row["U"] != original[L]: + mismatches += 1 + return (p - 1) // 2 + 1, mismatches + + +def expect_failure(executable: Path, p: int, L: int, reps: int) -> bool: + """One invalid-input query on a fresh process: True iff it failed closed.""" + proc = subprocess.run( + [str(executable)], + input=f"{p} {L} {reps}\n", + capture_output=True, + text=True, + encoding="utf-8", + errors="replace", + timeout=60, + ) + return proc.returncode != 0 + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "--executable", + type=Path, + default=Path(__file__).resolve().parent / "bin" / "harvey_adapter.exe", + help="path to the compiled harvey_adapter binary", + ) + parser.add_argument( + "--out", + type=Path, + default=REPO_ROOT / "example_results" / "p1_plus_harvey_validation.json", + help="output JSON path (upstream-comparable format)", + ) + parser.add_argument( + "--forced-big-max-p", + type=int, + default=199, + help="quarter-points of primes <= this value re-checked with --force-big", + ) + args = parser.parse_args() + + if not args.executable.is_file(): + print(f"harvey_adapter not found: {args.executable}", file=sys.stderr) + return 2 + + primes = list_92() + report = { + "utc_started": datetime.now(timezone.utc).isoformat(), + "platform": platform.platform(), + "python": platform.python_version(), + "executable": str(args.executable), + "executable_sha256": sha256_file(args.executable), + } + + version = json.loads( + subprocess.check_output( + [str(args.executable), "--version"], + text=True, + encoding="utf-8", + errors="replace", + timeout=30, + ) + ) + report["harvey_version"] = version + + stopping_total = 0 + mismatch_total = 0 + per_prime = [] + worker = HarveyWorker(args.executable) + try: + for p in primes: + checked, mismatches = check_prime_stopping_indices(worker, p) + stopping_total += checked + mismatch_total += mismatches + per_prime.append({"p": p, "stopping_indices": checked, "mismatches": mismatches}) + finally: + worker.close() + + forced_big_checks = 0 + forced_big_mismatches = 0 + forced = HarveyWorker(args.executable, force_big=True) + try: + for p in [q for q in primes if q <= args.forced_big_max_p]: + L = (p - 1) // 4 + row = forced.query(p, L) + ref = prefix_values(p)[L] + forced_big_checks += 1 + if ( + row["B"] != ref["B"] + or row["a"] != ref["a"] + or row["U"] != original_values(p)[L] + or row["backend"] != "ZZ_p_forced" + ): + forced_big_mismatches += 1 + finally: + forced.close() + + invalid_input_checks = sum( + [ + expect_failure(args.executable, 4, 0, 1), # composite modulus + expect_failure(args.executable, 7, 4, 1), # L > (p-1)/2 + expect_failure(args.executable, 7, 0, 0), # reps < 1 + ] + ) + + validation = { + "all_stopping_indices": stopping_total, + "stopping_index_mismatches": mismatch_total, + "primes_all_indices": len(primes), + "forced_big_cross_checks": forced_big_checks, + "forced_big_mismatches": forced_big_mismatches, + "invalid_input_checks": invalid_input_checks, + "status": "passed" + if ( + mismatch_total == 0 + and forced_big_mismatches == 0 + and stopping_total == 10809 + and invalid_input_checks == 3 + ) + else "failed", + } + report["validation"] = validation + report["per_prime"] = per_prime + + args.out.parent.mkdir(parents=True, exist_ok=True) + args.out.write_text(json.dumps(report, indent=2) + "\n", encoding="utf-8") + print(f"# wrote {args.out}") + print(json.dumps(validation, indent=2)) + return 0 if validation["status"] == "passed" else 1 + + +if __name__ == "__main__": + sys.exit(main()) From bd3d5106401059144fc50e25c72741e913414515 Mon Sep 17 00:00:00 2001 From: jsboige Date: Thu, 22 Oct 2026 19:40:03 +0200 Subject: [PATCH 2/3] Docs(research,#19452): ancre de reproductibilite du binaire Harvey Repond a la reserve non bloquante de la revue : l'empreinte SHA-256 du binaire n'etait pas situee. Ajoute ce qu'elle ancre (l'artefact exact qui a produit le JSON, re-mesure sur le siege de build le 2026-10-22 : identique) et ce qu'elle n'ancre pas (une reproduction au bit pres), avec les trois gestes qui refont la chaine : reconstruire depuis les sources au pin 37a9b72, comparer l'empreinte et la taille, rejouer la croisee comportementale. Co-Authored-By: Claude Sonnet 5.5 --- docs/research/cartier-miller-p1-plus-results.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/research/cartier-miller-p1-plus-results.md b/docs/research/cartier-miller-p1-plus-results.md index 093d5239f40..fd7e519e600 100644 --- a/docs/research/cartier-miller-p1-plus-results.md +++ b/docs/research/cartier-miller-p1-plus-results.md @@ -198,6 +198,16 @@ Tranche Python-only **PASS** : 92 premiers dérivés, 10809 stopping indices cal - Build : mêmes flags que `run_windows.py` (`-O3 -std=c++17 -pthread -I/include -L/lib`), sources = `harvey_adapter.cpp` + `upstream/recurrences_ntl.cpp` + `upstream/hypellfrob.cpp` **vendorisées au pin `37a9b72`** (aucun accès `/opt/sage` requis — le dépôt embarque les deux sources Sage hypellfrob avec leur provenance). Binaire 288 127 octets, SHA-256 `2ce5d768b430102c5efff804d08e3048be69066a746a240ce257b927668357cb` (consigné dans le JSON). - **Piège reproductibilité (muet)** : sans `ucrt64/bin` dans le `PATH`, `cc1plus` meurt au chargement de ses DLL (libgmp/libmpfr/libisl) **sans écrire un seul octet d'erreur** — le driver g++ rend rc=1 silencieux, y compris sur erreur de syntaxe triviale. Le diagnostic passe par l'export du PATH avant tout appel. +#### 7.1.1 Ce que l'empreinte du binaire ancre, et ce qu'elle n'ancre pas + +Le binaire n'est **pas** committé : il est construit localement, hors dépôt. La SHA-256 consignée n'est donc pas re-dérivable depuis le seul arbre Git — elle sert d'**ancre de comparaison** pour l'artefact exact qui a produit le JSON, pas d'une preuve de reproductibilité au bit près. Ce que la chaîne permet effectivement de refaire : + +1. **Reconstruire fonctionnellement** depuis les sources vendorisées au pin `37a9b72`, avec la commande exacte consignée ci-dessus — `build.sh` du dépôt amont émet en outre `bin/build_metadata.json` (version compilateur, commande, horodatage UTC), garde de reproductibilité que cette passe a reproduite à la main. +2. **Comparer l'empreinte** : `sha256sum harvey_adapter.exe` doit rendre `2ce5d768b430102c5efff804d08e3048be69066a746a240ce257b927668357cb` et `stat -c %s` rendre 288 127 octets. Re-mesuré firsthand sur le siège de build **le 2026-10-22** : identique au chiffre du JSON. +3. **Rejouer la validation** si l'empreinte diffère : `run_harvey_p1_plus.py` recroise les 10 809 stopping indices contre les récurrences Python indépendantes ; c'est cette croisée comportementale, et non l'empreinte, qui porte le résultat. + +Une reconstruction avec un autre jeu de correctifs GCC/MSYS2 ne sera **pas** bit-identique — c'est la raison pour laquelle l'empreinte est présentée comme une ancre et non comme un critère de reproduction. + ### 7.2 Résultats (`example_results/p1_plus_harvey_validation.json`, driver `scripts/cartier_miller/run_harvey_p1_plus.py`) | Critère | Attendu (upstream) | Mesuré | Verdict | From 87c9debbc9f224c98445f986fabf2cab4a865e20 Mon Sep 17 00:00:00 2001 From: jsboige Date: Sat, 24 Oct 2026 02:20:44 +0200 Subject: [PATCH 3/3] Fix(research,#19452): identite portable du binaire Harvey dans le JSON de resultats Le champ `executable` consignait le chemin du poste qui avait produit la mesure (`D:\Dev\tools\harvey\harvey_adapter.exe`), qu'aucun autre artefact de `example_results/` ne porte : un chemin absolu de poste dans un resultat committe n'est ni comparable ni reproductible ailleurs. Le driver ecrit desormais le nom du binaire ; l'empreinte SHA-256, deja presente a cote, reste la provenance verifiable. Le JSON est regenere par une execution reelle du driver (rc=0, MSYS2 ucrt64 dans le PATH), pas retouche a la main : 10809 stopping indices sur 92 premiers, 0 mismatch, 43 forced-big cross-checks, 0 mismatch, 3/3 invalid inputs fail-closed, status passed -- identiques a la passe precedente, ce qui rend la regeneration reproductible au bit pres hors `utc_started` et `executable`. Co-Authored-By: Claude Sonnet 5.5 --- example_results/p1_plus_harvey_validation.json | 4 ++-- scripts/cartier_miller/run_harvey_p1_plus.py | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/example_results/p1_plus_harvey_validation.json b/example_results/p1_plus_harvey_validation.json index da337d9bd3b..dd3e2ae9855 100644 --- a/example_results/p1_plus_harvey_validation.json +++ b/example_results/p1_plus_harvey_validation.json @@ -1,8 +1,8 @@ { - "utc_started": "2026-10-22T15:59:51.560886+00:00", + "utc_started": "2026-10-24T00:20:14.080592+00:00", "platform": "Windows-11-10.0.26300-SP0", "python": "3.13.7", - "executable": "D:\\Dev\\tools\\harvey\\harvey_adapter.exe", + "executable": "harvey_adapter.exe", "executable_sha256": "2ce5d768b430102c5efff804d08e3048be69066a746a240ce257b927668357cb", "harvey_version": { "ntl": "11.6.0", diff --git a/scripts/cartier_miller/run_harvey_p1_plus.py b/scripts/cartier_miller/run_harvey_p1_plus.py index 9dff2c73d32..81b69040ac2 100644 --- a/scripts/cartier_miller/run_harvey_p1_plus.py +++ b/scripts/cartier_miller/run_harvey_p1_plus.py @@ -138,7 +138,9 @@ def main() -> int: "utc_started": datetime.now(timezone.utc).isoformat(), "platform": platform.platform(), "python": platform.python_version(), - "executable": str(args.executable), + # Identite portable : le nom du binaire, jamais le chemin du poste qui a + # produit le resultat. L'empreinte ci-dessous est la provenance verifiable. + "executable": args.executable.name, "executable_sha256": sha256_file(args.executable), }