-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfeature_list.json
More file actions
158 lines (158 loc) · 18.5 KB
/
Copy pathfeature_list.json
File metadata and controls
158 lines (158 loc) · 18.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
{
"$comment": [
"Source de verite machine de l'etat du projet.",
"Etats autorises: not_started | active | blocked | passing.",
"Une feature ne passe a 'passing' QUE si sa commande 'verification' s'execute avec succes.",
"Cette transition est irreversible: une feature 'passing' ne redevient jamais 'active'.",
"Si une regression casse une feature 'passing', on ouvre une NOUVELLE feature de correction.",
"Invariant: au plus une feature 'active' a la fois (WIP = 1)."
],
"project": "PixaBoost",
"goal": "Reconstruire une piece photographiee en GLB fidele, usage visualisation/catalogue.",
"features": [
{
"id": "F00",
"sprint": 0,
"behavior": "Le depot s'installe depuis zero et le gate CPU complet est vert en moins de 60 s, avec au moins un test qui s'execute reellement.",
"verification": "uv run poe setup && uv run poe check",
"state": "passing",
"evidence": "commit 63d40d7 — ruff OK, mypy strict OK (4 fichiers), pytest 2 passed, 1,07 s"
},
{
"id": "F01",
"sprint": 0,
"behavior": "docs/pixal3d-internals.md repond par oui ou non, preuves fichier:ligne a l'appui, a la question: le chemin multi-vues de Pixal3D est-il expose a l'utilisateur, et sous quelle signature ?",
"verification": "uv run pytest tests/unit/test_pixal3d_contract.py",
"state": "passing",
"evidence": "7 tests de contrat verts. Verdict: NON, le multi-vues est bloque par un assert (image_conditioned_proj.py:211). Voir docs/pixal3d-internals.md et ADR-0005.",
"note": "Inconnue n°1 levee. F10 devient une implementation, pas un wrapper."
},
{
"id": "F02",
"sprint": 0,
"behavior": "core/geometry.py fournit Sim3 (composition, inverse, application), intrinseques camera, projection et back-projection, valides contre des identites analytiques.",
"verification": "uv run pytest tests/unit/test_geometry.py",
"state": "passing",
"evidence": "commit F02 — 20 tests analytiques verts. Sim3 (compose/inverse/matrice), camera Blender, projection/back-projection inversibles, grille canonique.",
"note": "Convention Blender de F01 implementee: camera selon -Z, capteur 32 mm, focale 16/tan(fov/2)."
},
{
"id": "F03",
"sprint": 0,
"behavior": "core/metrics.py et core/render.py fournissent IoU de silhouette, F-score@tau, Chamfer bidirectionnel et rendu offscreen de vues held-out, valides contre des verites terrain analytiques.",
"verification": "uv run pytest tests/unit/test_metrics.py tests/unit/test_render.py",
"state": "passing",
"evidence": "commit F03 — 34 tests analytiques verts. Rasteriseur numpy pur (z-buffer perspective-correct), silhouette IoU, Chamfer, F-score, echantillonnage de surface pondere par l'aire.",
"note": "LPIPS deliberement absent de core/ (torch + telechargement de poids), voir ADR-0003."
},
{
"id": "F04",
"sprint": 0,
"behavior": "Un test architectural echoue si un module de core/ importe torch, ouvre une socket, ou touche au GPU.",
"verification": "uv run pytest tests/unit/test_architecture.py",
"state": "passing",
"evidence": "commit F04 — 24 tests verts. Detection statique (ast), donc les imports paresseux en corps de fonction sont couverts. Garde-fou verifie par injection d'une violation reelle dans core/: les 3 detecteurs se declenchent.",
"note": "Couvre aussi le sens des dependances (core/ n'importe pas backends/) et l'absence d'alea global implicite."
},
{
"id": "F05",
"sprint": 0,
"behavior": "Le benchmark synthetique rend des meshes CAO en 6 azimuts x 3 elevations (+45, 0, -45 deg), soit 18 vues, et produit pour chaque objet: images, masques, poses GT, intrinseques GT et mesh GT.",
"verification": "uv run poe bench-build && uv run pytest tests/integration/test_benchmark.py",
"state": "passing",
"evidence": "commit F05 — bench-build ecrit 3 pieces x 18 vues en 512 en 5,1 s; 34 tests (shapes/rig/integration) verts. Le re-rendu du mesh stocke avec la pose stockee reproduit exactement le masque stocke.",
"note": "Geometrie de prise de vue identique aux photos reelles: 6 azimuts x 3 elevations (+45/0/-45). az000_el+00 reproduit la camera canonique de Pixal3D."
},
{
"id": "F06",
"sprint": 0,
"behavior": "L'image Docker GPU (TRELLIS.2 + NATTEN + utils3d + Pixal3D) est construite par GitHub Actions et poussee sur GHCR.",
"verification": "Le run gpu-image le plus recent a conclusion == success, et le manifeste de ghcr.io/pemcode/pixalboost:gpu-latest est recuperable depuis le registre",
"state": "passing",
"evidence": "Run 31192502560 conclusion=success. Manifeste OCI recuperable anonymement depuis ghcr.io/pemcode/pixalboost: 18 couches, 6,24 Go compresses. Tags publies: gpu-latest et gpu-5ff20035eaf792065672078c19fc96f88de0c2f5.",
"note": "docker n'est pas installe en local, donc la verification porte sur le manifeste publie plutot que sur un docker pull. Trois runs: echec 1 = GHCR refuse les majuscules du login, echec 2 = bug dans notre propre verification d'imports (importlib.util), succes au 3e."
},
{
"id": "F07",
"sprint": 0,
"behavior": "Une image envoyee a un pod RunPod revient sous forme de GLB valide, mis en cache dans artifacts/ et non regenere au second appel.",
"verification": "RUNPOD_ENDPOINT_ID=<id> uv run pytest tests/e2e/test_smoke_single_view.py -m gpu",
"state": "blocked",
"evidence": null,
"blocked_on": "Le transport SSH n'est plus le blocage: F09 le fournit (backends/ssh_pod.py). Ce qui manque est l'execution reelle. tests/e2e/test_smoke_single_view.py vise encore le client serverless (RUNPOD_ENDPOINT_ID) et non le backend ssh-pod effectivement construit; la commande de verification n'a donc jamais tourne contre un Pod facture.",
"note": "La chaine est prouvee MANUELLEMENT le 2026-08-12: un GLB de 37 Mo produit de bout en bout sur RTX 4090 (voir artifacts/smoke-20260812/manifest.json). Mais la commande de verification n'a pas tourne, donc la feature n'est pas 'passing'. Prochaine etape concrete: reecrire le test e2e sur SshPodClient, puis l'executer sur un Pod actif. Le code est couvert cote CPU par F09."
},
{
"id": "F08",
"sprint": 0,
"behavior": "Une petite GUI PyQt6 lance sans bloquer les essais CPU gratuits de PixaBoost, affiche en direct la phase, la progression disponible, la commande sanitisée, le journal, le temps écoulé, le code retour et les artefacts produits, et permet d'arrêter un processus local. Les actions GPU non prises en charge restent désactivées et expliquent leur blocage.",
"verification": "uv run pytest -q tests/unit/test_gui_model.py tests/unit/test_gui_remote_adapter.py tests/integration/test_gui.py tests/integration/test_gui_remote.py tests/integration/test_gui_ux.py",
"state": "passing",
"evidence": "2026-08-13 — commande F08 verte: 73 tests PyQt6/modeles/adaptateur en 6,74 s. Le controleur a aussi execute la vraie commande benchmark par defaut: 58 evenements, succes en 3,97 s, manifeste produit. Gate global vert: ruff, mypy strict et 315 tests en 13,09 s.",
"note": "Scope MVP: check CPU, tests CPU, construction du benchmark synthetique, inspection des artefacts locaux et pilotage mono-vue cache-first. Aucun achat ni activation de credits. La commande de verification a ete elargie le 2026-08-13: l'ancienne (test_gui_model + test_gui) ne couvrait pas l'adaptateur distant, la GUI distante ni les tests d'ergonomie livres avec F09. C'est la commande declaree dans docs/gui.md qui fait desormais foi."
},
{
"id": "F09",
"sprint": 0,
"behavior": "Le backend ssh-pod et le harness d'essai mono-vue appliquent un contrat verifiable sans GPU ni reseau: le cache est resolu localement avant toute connexion, un hit ne construit aucun client SSH, un miss exige une autorisation ephemere a usage unique, un hote inconnu est refuse, la revision distante et le SHA-256 du GLB sont verifies, l'annulation ne declare jamais un arret distant sans acquittement, et chaque tentative ecrit runs/<id>/manifest.json.",
"verification": "uv run pytest -q tests/unit/test_ssh_pod.py tests/unit/test_ssh_pod_cancel_latch.py tests/unit/test_ssh_pod_cancel_race.py tests/unit/test_ssh_pod_transport_cancel.py tests/unit/test_single_view_trial.py tests/unit/test_cache.py tests/unit/test_pixal3d_backend.py tests/integration/test_cli_single_view.py",
"state": "passing",
"evidence": "2026-08-13 — 74 tests verts en 2,93 s. Couvre le refus d'hote inconnu, l'autorisation a usage unique, le latch d'annulation avant construction de session, le rejet d'un GLB structurellement invalide, la detection de corruption du cache par SHA-256 et la reservation interprocessus (un worker tue est reclame, un proprietaire vivant n'est jamais vole).",
"note": "Feature ouverte retroactivement le 2026-08-13: environ 2 300 lignes de source (ssh_pod.py, ssh_worker_source.py, trials/, gui/remote_trial.py, gui/single_view_adapter.py) etaient livrees sans aucune entree dans cette liste, donc sans commande de verification. Ne prouve rien sur un Pod reel: tous les tests substituent le transport. La preuve materielle reste F07."
},
{
"id": "F14",
"sprint": 1,
"behavior": "Depuis la GUI, un clic sur la piece produit un PNG RGBA dont le canal alpha est le masque SAM 3, pret a court-circuiter le detourage interne de Pixal3D (preprocess_image n'appelle pas rembg si l'alpha n'est pas uniformement opaque). BiRefNet ne sert que d'amorce: le point de prompt automatique est le maximum de la transformee de distance a l'interieur de son masque, jamais le centroide. En cas de desaccord, le masque de SAM fait foi.",
"verification": "uv run pytest -q tests/unit/test_segmentation.py tests/unit/test_sam3_backend.py tests/unit/test_birefnet_backend.py tests/integration/test_gui_segmentation.py",
"state": "passing",
"evidence": "2026-08-13 — 86 tests verts en 1,05 s (20 core + 20 SAM 3 + 15 BiRefNet + 31 GUI). core/segmentation.py en TDD strict: les 20 tests analytiques ont ete constates ROUGES avant implementation. Les deux adaptateurs sont testes modele substitue, dont une vraie fuite de jeton injectee pour prouver la redaction. GUI offscreen: mapping clic->pixel sous letterbox, chaine BiRefNet->prompt->SAM ou le masque de SAM gagne, boutons reellement cliques, EXIF applique au chargement, marqueurs de points. Revue visuelle: fenetre rendue avec piece_test/view01.jpg. Gate global vert: ruff, mypy strict, 404 tests en 14,81 s. PREMIERE EXECUTION REELLE 2026-08-13: facebook/sam3 charge et execute sur piece_test/upright/view01.jpg. Un clic sur la jante rend la roue entiere SANS la sangle de levage (19,4 % de l'image, score 0,976) — la these de la feature est verifiee. Un clic dans l'alesage rend 1,4 % et un clic sur la sangle 1,8 %, les deux avec un score > 0,95: le score de SAM ne mesure pas la justesse.",
"note": "LIMITE: la verification automatisee substitue les deux modeles. SAM 3 a ete execute manuellement une fois (voir evidence) et separe correctement la piece de son outillage; BiRefNet n'a jamais tourne pour de vrai. Une photo, une piece, evaluation visuelle sans verite terrain. La revue GUI du 2026-08-13 a trouve trois defauts que les tests ne voyaient pas parce qu'ils appelaient les methodes directement: le bouton d'enregistrement n'etait connecte a rien, aucun moyen de charger une photo n'existait, et BiRefNet n'etait pas cable. N'avance PAS le gate F13. Licence gated assumee en ADR-0014."
},
{
"id": "F15",
"sprint": 1,
"behavior": "Deux photos sans calibration produisent un GLB unique contenant les deux reconstructions mono-vue placees dans un repere commun. La pose relative est DERIVEE par rendu-comparaison: on cherche la rotation de l'objet qui maximise l'IoU de silhouette entre le rendu de la reconstruction A et le masque SAM de la photo B. Aucune mire, aucun plateau, aucune pose mesuree.",
"verification": "uv run pytest -q tests/unit/test_pose_search.py tests/unit/test_two_view_trial.py tests/unit/test_two_view_adapter.py tests/unit/test_images_backend.py tests/unit/test_segmentation.py tests/integration/test_gui_two_view.py",
"state": "passing",
"evidence": "2026-08-13 — 52 tests verts (23 core/pose_search en TDD strict + 13 trials/GLB + 10 GUI). core/pose_search.py: les 17 premiers tests ont ete constates ROUGES avant implementation. Recuperation d'un azimut cache a moins de 20 deg sur piece asymetrique, sans aucune calibration. Sur piece revolue, l'elevation est recuperee et l'azimut ne l'est pas — le residu est verifie comme une rotation pure autour de l'axe de symetrie. GUI: onglet 'Reconstruction 2 vues' present, bouton actif des que deux photos sont choisies. Gate global vert: ruff, mypy strict, 457 tests en 43,29 s, zero avertissement. AJOUT 2026-08-14 — prior 'faces opposees': mesure sur piece_test/front_back, les deux vues de la roue ont la MEME silhouette (un cercle), donc la recherche ne peut pas distinguer l'avant de l'arriere et renverrait l'identite. Le prior fait entrer ce bit que le photographe possede toujours. Effet mesure sur le cas degenere du L-bracket: 0,94 -> 0,83. CABLAGE 2026-08-14 — 103 tests verts en 16,6 s avec la commande ci-dessus. Le bouton a desormais un moteur: gui/two_view_adapter.py enchaine deux run_single_view_trial cache-first sur un Pod existant, et le masque vient du canal alpha des decoupes RGBA (ADR-0016). core/segmentation.mask_from_rgba en TDD strict (6 tests constates ROUGES avant implementation), seuil aligne sur celui de Pixal3D (alpha > 0,8*255). Preuves negatives, chacune un enonce sur l'argent: un JPEG est refuse en le nommant AVANT toute connexion; un cache miss sans approbation ne construit aucun client; un second essai identique n'achete rien; une approbation FRAICHE est accordee par photo (l'octroi est a usage unique et expire en 120 s alors qu'une reconstruction dure des dizaines de minutes); une annulation empeche la seconde reconstruction. Pose connue recuperee a moins de 10 deg de bout en bout transport substitue. Gate global vert sur SIX executions consecutives: ruff, mypy sur les deux plateformes, 495 tests en ~33 s.",
"note": "PAS de fusion: le GLB juxtapose les deux maillages alignes, sans moyennage, ponderation, sculptage ni score de confiance par voxel. La contrainte n°11 reste entiere; l'objet du livrable est de RENDRE VISIBLE si les deux moitiees se superposent. Retourne ADR-0007: l'axisymetrie rend la pose ambigue, mais l'ambiguite est inoffensive quand la symetrie est reelle — le test l'exige explicitement. Les deux reconstructions mono-vue viennent du cache (F09) ou d'un Pod actif; aucune n'est calculee en CPU. LIMITE MESUREE ET EPINGLEE: la silhouette seule peut etre trompee par une vue degeneree — un masque en barre score 0,94 contre un L-bracket, qui vu de chant EST une barre (test_a_bar_shaped_mask_does_NOT_collapse_the_score). pose_iou seul ne prouve rien; agreement_iou et l'inspection du GLB sont les garde-fous. Un masque topologiquement impossible (anneau) fait bien s'effondrer le score. NON VERIFIE de bout en bout: la reconstruction mono-vue de chaque photo exige un Pod actif (F07 bloquee), donc aucun GLB n'a encore ete produit depuis de vraies photos. Le prior REDUIT la degenerescence sans la supprimer: is_trustworthy reste True sur un masque en barre sans rapport, et un test l'exige explicitement. DEUX DEFAUTS TROUVES ET CORRIGES PENDANT LE CABLAGE, tous deux invisibles en une seule execution du gate: (1) un QThread d'annulation survivait au test et Qt abandonnait le processus — le gate echouait environ une fois sur six, sans rapport d'echec; le panneau expose maintenant is_busy, les tests l'attendent, et MainWindow.closeEvent refuse de fermer par-dessus un thread vivant. (2) le message 'etat du Pod inconnu' etait ecrase par le resultat du run selon l'ordre d'arrivee des deux signaux; il est desormais reapplique quel que soit l'ordre, et volontairement PAS ajoute a un run reussi (les deux reconstructions ont fini, le Pod ne travaille plus). TOUJOURS PAS DE BOUT EN BOUT REEL: le transport est substitue dans tous les tests; il manque un Pod actif (F07)."
},
{
"id": "F10",
"sprint": 1,
"behavior": "N images plus leurs poses produisent un GLB unique. Implique de sous-classer ProjGrid pour lever l'assert sur transform_matrix, recuperer valid_mask, et faire une moyenne masquee par voxel sur N vues. Inclut la variante de repli (une vue conditionne, les autres recalent).",
"verification": "uv run pytest tests/e2e/test_multiview.py -m gpu",
"state": "not_started",
"evidence": null,
"note": "Portee revue apres F01: implementation, pas wrapper. Risque OOD sur le checkpoint, voir ADR-0005."
},
{
"id": "F11",
"sprint": 1,
"behavior": "Un score de confiance de recalage est calcule, et une pose sous le seuil provoque un refus explicite plutot qu'une reconstruction degradee.",
"verification": "uv run pytest tests/unit/test_registration.py",
"state": "passing",
"evidence": "commit F11 — 25 tests verts. Umeyama closed-form, ICP tronque avec pre-alignement grossier, score de confiance = inliers x qualite x distinctness, refus par exception sous le seuil. Bague a symetrie d'ordre 12: rmse 1,3e-16 et 100% d'inliers, pourtant refusee (distinctness 0,018).",
"note": "Deux mesures actees en ADR-0006 et ADR-0007. Le bassin de convergence de l'ICP (~20 deg) dimensionne le nombre de redemarrages et conditionne la strategie d'initialisation de F10."
},
{
"id": "F12",
"sprint": 1,
"behavior": "Un tableau chiffre compare B0 (meilleure vue seule) et B1 (multi-vues natif) sur le benchmark, avec metriques P1/P2 et planche de rendus comparatifs.",
"verification": "uv run poe bench-compare",
"state": "not_started",
"evidence": null,
"note": "Cout mesure: ~25 min par inference, domine par les chargements de modeles et non par le calcul. 3 pieces x 18 vues en mono-vue = ~22 h de GPU. Il faut batcher (charger la cascade une fois, boucler sur les images) avant de lancer, sinon le benchmark est economiquement absurde."
},
{
"id": "F13",
"sprint": 1,
"behavior": "GATE DE DECISION. Une ADR dans DECISIONS.md statue: B1 passe la barre catalogue (-> Sprint 3), ou B1 echoue avec un diagnostic d'erreur qui dit si le Sprint 2 est pertinent.",
"verification": "Presence d'une ADR 'F13' dans DECISIONS.md citant runs/<ts>/metrics.json",
"state": "not_started",
"evidence": null,
"note": "Aucune feature de Sprint 2 ne peut passer a 'active' avant que F13 soit 'passing'."
}
]
}