From cd6ad105a632a88b18d5e633560a290cb4750e55 Mon Sep 17 00:00:00 2001 From: fy59 Date: Mon, 10 Aug 2026 00:25:00 +0200 Subject: [PATCH] fix(reconstruction): finalize track builder v1 freeze --- docs/architecture/project_database.md | 4 +-- docs/architecture/reconstruction_pipeline.md | 6 ++-- docs/architecture/track_builder.md | 29 ++++++++-------- docs/architecture/tracks.md | 31 +++++++++-------- include/lardon3d/track_builder_task.h | 8 +++++ tests/test_project_db.c | 36 ++++++++++++++------ 6 files changed, 71 insertions(+), 43 deletions(-) diff --git a/docs/architecture/project_database.md b/docs/architecture/project_database.md index 61c23b7..c9b0183 100644 --- a/docs/architecture/project_database.md +++ b/docs/architecture/project_database.md @@ -547,8 +547,8 @@ CREATE INDEX track_observations_lookup_idx **Statut** : les tables sont créées par la migration et validées par les tests. L'API C (`create_track_set`, `load_track_set`, `find_track_set`, `list_track_sets`, `load_track`, `list_tracks`, `find_track_by_observation`) -est exposée et implémentée. Les limites réelles restent l'absence de Track -Builder algorithmique, de tâche dédiée et de triangulation. +est exposée et implémentée. Le Track Builder algorithmique et sa tâche durable +sont implémentés par les Gates A–E ; la triangulation reste hors périmètre. ## Schéma v15 — payload durable Track Builder diff --git a/docs/architecture/reconstruction_pipeline.md b/docs/architecture/reconstruction_pipeline.md index a8c08e9..089c130 100644 --- a/docs/architecture/reconstruction_pipeline.md +++ b/docs/architecture/reconstruction_pipeline.md @@ -122,9 +122,9 @@ USAC/MAGSAC avec configuration, seed et fingerprint déterministes. | **Lien avec le catalogue** | Les tracks référenceront les images par `image_id`, jamais par nom ou chemin. | | **Persistance** | Les tracks sont persistés entre les sessions de traitement. Un track ne peut être détruit que par une action explicite de l'utilisateur. | -**Statut :** PARTIAL — le Track Model v1 (persistance) est implémenté dans -Project DB v15 (`track_sets`, `tracks`, `track_observations`). Le Track -Builder, la triangulation et le Sparse SfM restent PLANNED. +**Statut :** COMPLETED/FROZEN — le Track Builder v1 direct et durable est +implémenté dans Project DB v15 (`track_sets`, `tracks`, `track_observations` et +le payload de tâche). La triangulation et le Sparse SfM restent PLANNED. --- diff --git a/docs/architecture/track_builder.md b/docs/architecture/track_builder.md index ec14d6a..4d4301e 100644 --- a/docs/architecture/track_builder.md +++ b/docs/architecture/track_builder.md @@ -2,9 +2,11 @@ ## Status -**GATE A — PASS.** This document is the scientific and algorithmic contract -for the future Track Builder. It specifies no implementation, migration or -Task. `FACT`, `DECISION` and `FROZEN` are intentionally distinguished below. +**GATE A — PASS. GATE B — PASS. GATE C — PASS. GATE D — PASS. GATE E — PASS.** +This document is the frozen scientific and algorithmic contract for Track +Builder v1. `FACT`, `DECISION` and `FROZEN` remain distinguished below. Runtime +freeze is limited to the Track Builder v1 task contract; generic Task Runtime +and Resource Governor evolution remains separate. ## Scope @@ -561,14 +563,14 @@ different-scope identity and late matching-GVR exclusion. The test-only phase seam is compiled only into the Gate C test executable. Normal and AddressSanitizer/UndefinedBehaviorSanitizer targeted runs pass for -the core and project executables. The 106,496-edge synthetic resource case -completed in 0.281 seconds with a 26,564 KiB process high-water RSS and one -Match File live at a time. C26 has no clean fault injection in the frozen DB -API and C27 belongs to the future concurrency gate. +the core and project executables. The historical 106,496-edge synthetic +resource case completed in 0.281 seconds with a 26,564 KiB fresh-process RSS +and one Match File live at a time. C26 has no clean fault injection in the +frozen DB API; C27 was closed by Gate D. -Gate C status: **PASS**. Gate A: **PASS**. Gate B: **PASS**. Gate D: -**NOT_IMPLEMENTED**. Gate E: **NOT_DONE**. Track Builder v1 is not marked -fully frozen by this document; the future runtime gates remain separate. +Gate C status: **PASS**. Gate D status: **PASS**. Gate E status: **PASS**. +Track Builder v1 is **FROZEN** by this Gate E closure. Future scientific changes +require a new Builder version and explicit identity decision. ## Gate D — durable runtime contract @@ -591,8 +593,7 @@ the task ID, payload format version, scope path, byte size and SHA-256, exact selector, builder identity, scope hash and count. The scope asset stores field- by-field little-endian uint64 GVR IDs with explicit magic/version/count/size and checksum. No native struct dump, SQL text list or arbitrary small ID limit is -allowed. A v14 database remains readable and is upgraded transactionally only -when this payload is actually implemented. +allowed. A v14 database remains readable and is upgraded transactionally to v15. ### Recovery and publication @@ -611,7 +612,7 @@ Pause and cancellation are cooperative at task boundaries and between bounded GVR units. Gate B is non-preemptible: an active invocation finishes, then pause or cancellation is observed before publication. Prepublication cancellation publishes no Track Set; a committed publication remains valid even if a late -cancellation is requested. A deterministic test seam will exercise the Gate C +cancellation is requested. A deterministic test seam exercised the Gate C late-identity collision: an exact identity conflict is re-looked-up and reused; an unrelated constraint remains an error. @@ -645,4 +646,4 @@ apparent 1,079,972 KiB result was contaminated cumulative RUSAGE from the persistent shell that launched direct runs; it was not Track Builder memory. A normal 250k hardware run completed in 0.368 seconds with 33,228 KiB peak RSS, MemAvailable loss of 14,108 KiB, no swap delta and zero PSI averages. Gate D -is **PASS**; Gate E remains responsible for final full-suite freeze. +is **PASS**; Gate E final validation and freeze are complete. diff --git a/docs/architecture/tracks.md b/docs/architecture/tracks.md index 178507e..a0c277d 100644 --- a/docs/architecture/tracks.md +++ b/docs/architecture/tracks.md @@ -7,7 +7,8 @@ géométriquement vérifiées en structures multi-view cohérentes. Il stocke de ensembles d'observations 2D liées à un même point physique supposé. Il ne calcule rien, ne triangule pas, ne contient aucune coordonnée 3D et ne résout aucun conflit. Le Track Builder, la triangulation, le Sparse SfM et le Bundle -Adjustment sont des étapes ultérieures séparées. +Adjustment sont des étapes séparées ; Gate E a gelé le Builder v1 sans +implémenter ces étapes 3D. ## Track definition @@ -22,12 +23,12 @@ triangulation appartient à une étape ultérieure. La chaîne scientifique correcte est : ```text -Matcher → Match Result → Geometric Verification → Track Builder (futur) +Matcher → Match Result → Geometric Verification → Track Builder v1 → Track Model → Sparse SfM (futur) ``` -Le Matcher ne produit pas les Tracks. Le Track Builder futur les -assemblera à partir des Geometric Verification Results. +Le Matcher ne produit pas les Tracks. Le Track Builder v1 les assemble à partir +des Geometric Verification Results. ## Observation identity @@ -140,7 +141,7 @@ différents. Le reuse est donc scoped à une seule DB projet. 1. **Minimum structurel** : un Track contient au moins 2 observations. Une seule observation ne constitue aucune relation multi-view. Le futur - Track Builder, la triangulation ou le Sparse SfM pourront appliquer des + Track Builder v1, la triangulation ou le Sparse SfM pourront appliquer des critères plus stricts. Le Model ne fixe pas de plafond de reconstruction. 2. **One observation per image** : un Track ne contient pas deux observations @@ -258,7 +259,7 @@ Track Set supprime ses tracks et observations. - cohérent avec tous les résultats publiés existants (Feature Sets, Match Results, GVRs) qui sont immutables après publication. -Le Track Builder futur construira en mémoire, puis publiera un set complet +Le Track Builder v1 construit en mémoire, puis publie un set complet dans une transaction. Aucun track n'est visible avant que le set entier soit validé. @@ -405,7 +406,8 @@ track individuel) n'est pas persistée. Les raisons : disponibles ; - une table `track_set_sources` volumineuse complexifie la DB sans bénéfice immédiat ; -- le Track Builder futur pourra l'ajouter dans une migration ultérieure. +- une future version du Track Builder pourra l'ajouter dans une migration + ultérieure. ## Invalidation @@ -437,8 +439,8 @@ et de toutes ses observations. - le `created_at` du set est le timestamp de la transaction ; - le `track_count` et `gvr_count` sont validés contre les INSERTs réels. -Le Track Builder futur utilisera le Task Runtime pour le checkpoint/reprise -et le Resource Governor pour l'admission. Le Model ne contient aucune +Le Track Builder v1 utilise le Task Runtime pour le checkpoint/reprise et le +Resource Governor pour l'admission. Le Model ne contient aucune logique d'exécution. ## Resource bounds @@ -511,13 +513,14 @@ transactionnel. ## Track rejected state Le Model v1 ne persiste pas d'état Track rejected. Le Model représente des -Tracks structurellement valides (≥ 2 observations, cohérents). Le futur -Track Builder décidera quels candidats publier. Les candidats non publiés -n'existent pas dans le Model. +Tracks structurellement valides (≥ 2 observations, cohérents). Le Track Builder +v1 décide quels candidats publier. Les candidats non publiés n'existent pas dans +le Model ; cette séparation reste la frontière scientifique figée. ## Versioning -Project DB schema version future décrira le stockage Track. `builder_version` -et `verifier_version` décrivent indépendamment les contrats scientifiques. +Project DB v14 introduced the Track storage and v15 adds only durable Track +Builder task payload persistence. `builder_version` et `verifier_version` +décrivent indépendamment les contrats scientifiques. Changer un algorithme n'impose une migration DB que si la représentation persistante change. diff --git a/include/lardon3d/track_builder_task.h b/include/lardon3d/track_builder_task.h index 66ef2c8..7e7d692 100644 --- a/include/lardon3d/track_builder_task.h +++ b/include/lardon3d/track_builder_task.h @@ -10,6 +10,10 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + #define LARDON3D_TRACK_BUILDER_TASK_KIND "track_builder.run" enum { @@ -40,4 +44,8 @@ bool lardon3d_track_builder_task_reconstruct( const Lardon3DTaskDurableSnapshot *snapshot, void *context, Lardon3DTaskKindBinding *binding); +#ifdef __cplusplus +} +#endif + #endif diff --git a/tests/test_project_db.c b/tests/test_project_db.c index 5af32fd..7ec04b9 100644 --- a/tests/test_project_db.c +++ b/tests/test_project_db.c @@ -207,18 +207,34 @@ static bool create_v13_database(const char *path) { } static bool create_v14_database(const char *path) { - Lardon3DProjectDb *database = NULL; - char error[LARDON3D_PROJECT_DB_ERROR_CAPACITY]; - if (lardon3d_project_db_open(path, &database, error) != LARDON3D_PROJECT_DB_OK) return false; - lardon3d_project_db_close(database); + if (!create_v13_database(path)) return false; sqlite3 *connection = NULL; if (sqlite3_open(path, &connection) != SQLITE_OK) return false; - bool ok = sqlite3_exec(connection, - "PRAGMA foreign_keys=OFF;BEGIN IMMEDIATE;" - "DROP TABLE track_builder_tasks;" - "UPDATE metadata SET value=14 WHERE key='schema_version';" - "COMMIT;PRAGMA foreign_keys=ON;", - NULL, NULL, NULL) == SQLITE_OK; + static const char sql[] = + "PRAGMA foreign_keys=OFF;BEGIN IMMEDIATE;" + "CREATE TABLE track_sets(track_set_id INTEGER PRIMARY KEY AUTOINCREMENT " + "CHECK(track_set_id>0),builder_kind TEXT NOT NULL CHECK(length(builder_kind)>0 AND " + "length(builder_kind)<=64),builder_version INTEGER NOT NULL CHECK(builder_version>0)," + "parameter_fingerprint BLOB NOT NULL CHECK(length(parameter_fingerprint)=32)," + "verifier_kind INTEGER NOT NULL CHECK(verifier_kind>0),verifier_version INTEGER NOT NULL " + "CHECK(verifier_version>0),verifier_fingerprint BLOB NOT NULL CHECK(length(verifier_fingerprint)=32)," + "input_scope_hash BLOB NOT NULL CHECK(length(input_scope_hash)=32),gvr_count INTEGER NOT NULL " + "CHECK(gvr_count>=1),track_count INTEGER NOT NULL CHECK(track_count>=0),created_at INTEGER NOT NULL " + "CHECK(created_at>=0),UNIQUE(builder_kind,builder_version,parameter_fingerprint,verifier_kind," + "verifier_version,verifier_fingerprint,input_scope_hash));" + "CREATE TABLE tracks(track_id INTEGER PRIMARY KEY AUTOINCREMENT CHECK(track_id>0)," + "track_set_id INTEGER NOT NULL REFERENCES track_sets(track_set_id) ON DELETE CASCADE," + "observation_count INTEGER NOT NULL CHECK(observation_count>=2));" + "CREATE INDEX tracks_set_idx ON tracks(track_set_id,track_id);" + "CREATE TABLE track_observations(track_set_id INTEGER NOT NULL,track_id INTEGER NOT NULL " + "REFERENCES tracks(track_id) ON DELETE CASCADE,feature_set_id INTEGER NOT NULL " + "REFERENCES feature_sets(feature_set_id),feature_index INTEGER NOT NULL CHECK(feature_index>=0)," + "position_in_track INTEGER NOT NULL CHECK(position_in_track>=0),PRIMARY KEY(track_set_id,feature_set_id," + "feature_index),UNIQUE(track_id,position_in_track));" + "CREATE INDEX track_observations_lookup_idx ON track_observations(feature_set_id,feature_index," + "track_set_id);UPDATE metadata SET value=14 WHERE key='schema_version';COMMIT;" + "PRAGMA foreign_keys=ON;"; + bool ok = sqlite3_exec(connection, sql, NULL, NULL, NULL) == SQLITE_OK; return sqlite3_close(connection) == SQLITE_OK && ok; }