05 — Ordre de bataille : phases → lanes
Une lane = un agent, un worktree isolé sous ~/ sur big-1 (TMPDIR=~/tmp ; /tmp est
saturé), commits par index temporaire avec des blobs construits depuis HEAD pour les
fichiers partagés, compare-and-swap sur update-ref, HEAD vérifié compilable depuis un
worktree propre, rapport en français avec des chiffres mesurés, cargo xtask prove <ids>
avant tout implemented. Six lanes en parallèle au maximum. Chaque lane cite la clause de
spécification qu'elle implémente et l'oracle face auquel elle prouve, et ajoute un
contrôle négatif.
Phase 0 — Reprendre ce que le quota hebdomadaire a interrompu (⏸ → 🔧)
| Lane | Fonctionnalité | Où en est le travail (2026-09-14) | Oracle | Sortie |
|---|---|---|---|---|
| AC texte | F-001 | ~/ac-work/wt-dev à a6b7d93 (alignement à la majorité de trois, règle de génération §7.3.10, borne /Count commités) ; 4 fichiers modifiés, non commités : crates/apdf-content/src/{cmap,font,interpret,text}.rs ; ~/ac-work/wt-after, wt-base sont du scratch |
pdftotext, mutool, pdf.js via oracle_pdfjs.rs, external_corpora.rs, oracle_text.rs |
rapports en hausse sur les trois, 0 régression, KNOWN_GAPS vidée ou classée |
| AE modificateur | F-002 | ~/wt/ae à a6b7d93 ; non commités : crates/apdf-write/src/edit.rs, crates/apdf/src/edit.rs, crates/apdf/tests/edit_oracle.rs, plus pages.rs, revision.rs, document.rs, objects_oracle.rs, support/{mod.rs,qpdf_json_diff.py} modifiés |
qpdf, pdfcpu, mutool, pdftk, pyHanko ; égalité qpdf --json sur 200 fichiers après une édition neutre | conformance-processors, prepress-boxes implémentées ; verbes CLI (pages/merge/split/rotate/boxes/annots/form/meta/attach/bookmarks) |
| AF PDF/A | F-003 | ~/wt-AF à aafc997 (check/a/mod.rs, verapdf_agreement.rs, Cargo.toml modifiés) ; ~/wt-AF-g1 (examples/ non suivi) ; ~/wt-AF-g2 à b150945 ; ~/wt-AF-g3 à 6cfcf51 (syntax.rs modifié) — réconcilier les quatre sur HEAD d'abord |
veraPDF 1.30.2 + sources ; fontTools comme troisième témoin | 0 désaccord sur les corpus veraPDF ou résidus classés ; corpus réel 3b/4 fait |
| AG performance | F-004 (petite échelle) | ~/wt-AG à f28ff11 : tests/tools/bench_corpus.sh, bench_report.py non suivis ; ~/bench-AG/{before,apdf-before,smoke} exécutions de référence |
/usr/bin/time, criterion, perf |
docs/PERFORMANCE.md, ADR-benchmarks, ≥3 points chauds corrigés avec avant/après, tests d'oracle inchangés |
| AH fuzzing | F-005 | ~/wt-AH propre à f28ff11 ; graines ~/fuzz-AH/{corpus,extract} préparées ; crate fuzz/ pas encore écrite |
libFuzzer/cargo-fuzz, ASan | docs/FUZZING.md, docs/LIMITES.md, 0 plantage ouvert |
| AI FDF/XFDF/AF/PDF-X | F-006 | ~/wt-AI propre à 66a4ae4 ; fixtures PDF/X sous ~/tmp-AB/pdfx-nutshell-fixtures/ (à copier sous ~/ d'abord) |
pdftk, xmllint, qpdf, pdf.js, veraPDF 3b, Ghent | lignes mises à jour avec les preuves |
| P0-a hygiène | F-007 | arbre partagé assaini le 2026-09-14 ; 34 fichiers mêlés restent (git status dans le checkout principal) — résoudre chacun face au worktree de la lane propriétaire, puis cargo fmt en un seul commit, puis prove all depuis un worktree propre ; supprimer les worktrees mortes (apdf-base, apdf-laneY, wt/ad-pub, wt-AF-g* une fois réconciliées) et les cibles cargo périmées |
prove all |
porte verte au nouveau HEAD ; docs/COUVERTURE.md régénéré et commité |
| P0-b amont | F-007 | branches RUSSL fix/cms-interop-and-crl-signing, lane-r-pkcs12-rsa ; CODECS laneM-jbig2-2026-09-13, laneY-jpx-2026-09-13, commits jpeg2000 de la lane F sur argon-full-corpus-2026-08-16 — fusionner dans la lignée principale une fois les sessions qui occupent ces arbres terminées (🧭 D-7) ; rien à republier (Kellnr porte déjà les crates construites) |
suites amont | branches fusionnées, tags sur les commits publiés |
Sortie de Phase 0 : prove all vert à un HEAD qui contient AC/AE/AF/AG/AH/AI, arbre
formaté, docs/PERFORMANCE.md, docs/FUZZING.md, docs/LIMITES.md présents.
État à la pause (2026-09-14, 20h45 — implémentation gelée par le fondateur tant que 06-decisions.md n'a pas de réponse)
Commité sur main entre l'arrêt du quota et la pause (aucune lane, session principale) :
89a7d7e (.gitignore /ISO_SOURCES/), e972e0a (lane AG reprise : harnais de
bancs + ligne de base docs/PERFORMANCE.md — bogue de citation CSV corrigé), 56b9277
(COUVERTURE.md régénéré), 0c5092f (repli du corpus en worktree dans six modules
tests/support + scripts/worktree-corpus.sh : un worktree lié avait un tests/corpus
vide et chaque suite d'oracle tournait sur zéro fichier — c'est ce qui expliquait les
« échecs » d'AC et d'AE, pas leur code). Checkout partagé assaini : index périmé remis à
zéro, 59 fichiers périmés + 9 de churn rustfmt restaurés depuis HEAD, 5 miroirs d'AC
restaurés, 28 patches non attribués parqués sous ~/mixed-patches-2026-09-15/
(README à l'intérieur), checkout propre.
Mesuré mais non commité (les worktrees restent en l'état ; reprendre = relire, puis commiter par index temporaire sur le HEAD de ce jour-là) :
| Worktree | Contenu | Mesuré (2026-09-14) | Note de reprise |
|---|---|---|---|
~/ac-work/wt-dev (AC, base a6b7d93) |
ordre de lecture par blocs/segments dans apdf-content/src/text.rs (+ 3 tests unitaires), ressources de formulaire fusionnées derrière celles de la page (interpret.rs, test unitaire), petits changements cmap.rs/font.rs ; apdf-doc/src/store.rs : /Length déclaré conservé quand aucun mot-clé ne suit (bad39.pdf → « Potato », D-25) ; external_corpora.rs et oracle_text.rs comparent --layout avec pdftotext -layout et l'ordre par défaut avec mutool (D-22) |
à convention égale : pdf.js en ordre ≥0,9 67,9→74,1 %, mutool 64,8→71,0 % (balayage pdf.js) ; corpus épinglé face à pdftotext -layout 85,5→86,0 % (moyenne 0,909→0,912), face à mutool 73,6→80,7 % ; corpus externes face à -layout 85,1→85,2 % ; « désaccords où les oracles s'accordent » 15→11 |
blocages : le cas 9 de robustness.rs (200 000 (x) Tj) prend 190 s — build_blocks balaie tous les blocs pour l'entrelacement (quadratique sur une entrée hostile) : borner le balayage ; apdf-content/tests/oracle_text.rs (au niveau crate) compare encore l'ordre par défaut à -layout (73,7 % < seuil) : aligner comme les tests CLI ; écarts non classés issue13211.pdf (texte thaï dans un XObject de formulaire, polices CID 22/23 avec ToUnicode Ghostscript — l'analyseur de CMap est disculpé par deux tests de scratch ; les mauvais points de code U+0E3C/0E61/0E65 viennent d'un repli ultérieur : instrumenter decode_composite) et bad39.pdf (corrigé par le changement du store, à reclasser) ; listes KNOWN_GAPS à élaguer (12 entrées corrigées) ; régression ZapfDingbats.pdf (cellules du tableau de glyphes fusionnées) à expliquer ou corriger |
~/wt-ae-new (AE rebasée sur a6b7d93+HEAD) |
apdf-write/src/edit.rs (1 569 lignes : édition de l'arbre de pages, annotations, aplatissement, Touched), apdf/src/edit.rs (1 385 lignes : Editor avec sauvegarde incrémentale par diff d'instantané, politique DocMDP/approbation, formulaires, info/XMP, signets, pièces jointes, étiquettes, préférences du visualiseur), apdf/tests/edit_oracle.rs (708 lignes), Document::refresh, doc de façade fusionnée |
les 6 tests d'oracle passent (égalité qpdf --pages/--json sur 200 fichiers du corpus, signatures pyHanko, relecture mutool/pdfdetach) une fois le corpus trouvé ; bassin de candidats pour l'édition de page élargi (40→200) ; clippy : matches! corrigé ; un avertissement reste — edit_oracle.rs:169 veut .rfind(..) au lieu de .filter(..).next_back() (~/ae-new-clippy2.log) ; tests rejoués verts (~/ae-new-tests2.log : 5 + 6 passés) |
corriger le clippy d'une ligne, commiter ; puis les lignes conformance-processors/prepress-boxes + les verbes CLI (pas encore écrits) |
~/wt-AF-new (AF réconciliée sur HEAD aed2f39) |
fichiers chiffrés ouverts avec le mot de passe utilisateur vide avant les règles PDF/A (comme veraPDF), check/a/syntax.rs : règle /ID d'un fichier linéarisé selon GFCosDocument.getlastID, le rapport d'accord imprime chaque manque |
corpus veraPDF : 1b 565/569 (était 563), 2b 960/986 (959), 3b 12/12, 4 : 455/487 (était 456 — un nouveau faux positif : 6-1-3-t02-fail-a.pdf signalé 6.1.6.1-1 par nous seuls ; causé par le changement /ID linéarisé au niveau 4 — à vérifier avant de commiter), 4e 19/19, 4f 11/11 ; Isartor 203/205 |
corriger ou expliquer le −1 de PDF/A-4, puis commiter ; les quatre anciens worktrees ~/wt-AF* peuvent être supprimés ensuite |
~/wt-corpus |
commité (0c5092f) |
— | supprimer le worktree |
| AH, AI | rien d'écrit | — | démarrer depuis les briefs (docs/roadmap F-005, F-006) |
Aussi mesuré ce jour, pour D-22 : ligne de base de performance (docs/PERFORMANCE.md) :
ouverture 6 ms contre qpdf 12 / pdfinfo 19 / pdf.js 305 ms ; texte 6 ms contre pdftotext
20 / mutool 28 / pdf.js 339 ms ; rendu 109 ms contre pdftoppm 53 / mutool 38 ms (2 à
3 fois plus lent, l'étage à optimiser en premier) ; RSS maximale 898 Mo contre
4,9 Go / 6,8 Go.
Second contrôle, le même soir (21h20–21h25) : un bug d'outillage et deux constats de
worktree. Une nouvelle passe de vérifications oracle/clippy sur les worktrees AC/AE/AF a été
enveloppée dans des boucles until ! pgrep -f "<cmd>" >/dev/null; do sleep N; done pour être
notifié à la fin. Bug : le texte de commande de chaque boucle (passé à zsh -c) contient lui-même
la chaîne de recherche de son propre pgrep -f, donc pgrep -f matchait la boucle elle-même
indéfiniment, même longtemps après que la vraie commande cargo ait terminé — les 7 boucles
étaient encore signalées « en cours » plus de 75 minutes plus tard. Diagnostiqué via pgrep -af
montrant le PID de la boucle comme son propre résultat, et via ps/uptime ne montrant plus
aucun processus cargo correspondant et une charge système normale ; tuées par PID exact (pas
un pkill large, conformément à la règle de la machine partagée), puis les vrais résultats ont
été lus directement dans les fichiers de log que ciblaient ces boucles. Leçon pour les futurs
scripts d'attente : ne jamais faire un pgrep -f sur un motif qui est une sous-chaîne de la
ligne de commande de la boucle elle-même — utiliser un fichier PID ou wait $!. Constats tirés
des logs :
- AC, dans ~/ac-work/wt-dev (toujours basé sur a6b7d93, donc antérieur à 0c5092f) :
la suite oracle apdf-content a montré un mur de tests FAILED (encodages CFF, CMaps CJK,
dictionnaires de police, dictionnaires /SH et ExtGState, comptages fill/stroke, texte,
Type 3, robustesse). C'est le symptôme de corpus vide d'avant 0c5092f qui réapparaît dans
un worktree antérieur au correctif, pas un nouveau défaut dans les changements
text.rs/store.rs/interpret.rs de cette lane — les chiffres du tableau ci-dessus (mesurés
avec un corpus accessible) restent valables ; ce worktree a besoin du correctif de repli de
corpus (ou de scripts/worktree-corpus.sh) avant de pouvoir être re-mesuré. Le cargo check
des six crates du correctif lui-même (~/wt-corpus, déjà commité en 0c5092f) est propre.
- AE, un nouveau rebase sur ~/wt/ae (distinct de ~/wt-ae-new ci-dessus) : a reproduit le
même symptôme sur page_edits_agree_with_qpdf_pages_and_render_identically (1 document /
7 pages trouvées, il en faut ≥ 4) ; épingler APDF_CORPUS sur le vrai corpus du worktree
principal a fait passer les 6 tests oracle, confirmant le diagnostic plutôt qu'une vraie
régression. Le clippy de ce rebase signale aussi de nouveau le motif matches!, cette fois
à edit.rs:1238 — en plus du edit_oracle.rs:169 déjà connu (.rfind au lieu de
.filter(..).next_back()) — donc, que ce soit ~/wt-ae-new ou ~/wt/ae qui soit repris à la
reprise, les deux correctifs clippy d'une ligne doivent être réappliqués avant commit.
- AF : cargo test -p apdf-conform --release --no-run compile proprement (binaires de test
isartor, pdfa, verapdf_agreement construits). Cela ne vérifie que la compilation, pas
l'exécution de la suite : la régression PDF/A-4 455/487 notée ci-dessus reste donc ouverte et
n'est ni confirmée ni infirmée par ce contrôle.
Phase 1 — Accessibilité depuis n'importe où (F-010…F-017)
| Lane | Contenu | Dépend de | Preuve |
|---|---|---|---|
| P1-adr | ADR-bindings (cbindgen, napi-rs, wasm-bindgen, wit-bindgen, PyO3/maturin, UniFFI, jni, csbindgen, magnus, ext-php-rs, flutter_rust_bridge, rustler — générateurs de build) et ADR-benchmarks (criterion) pour validation du fondateur | — | ADR accepté (🧭 D-6) |
| P1-a | ABI C apdf-capi + apdf.h + projet de test C |
P1-adr | projet C neuf ; abi-checker |
| P1-b | napi-rs (Node 22, Bun) + WASM (navigateur) + composant WASI 0.2 | P1-a (forme de l'API) | projets Node/Bun/Deno/navigateur neufs ; table de taille |
| P1-c | Python (wheels maturin), JVM (Maven), .NET (NuGet) | P1-a | projets neufs par langage |
| P1-d | Swift/iOS, Kotlin/Android, Go, Ruby, PHP, Dart, Elixir, en-tête C++ | P1-a | projets neufs par langage |
| P1-e | adaptateur FPDF_* de pdfium, API façon poppler-glib, alias CLI (pdftotext/pdftoppm/pdfinfo/pdfimages/pdffonts/pdfsig/qpdf/pdftk/cpdf), docs/MIGRATION.md |
P1-a | test de fumée pypdfium2 face à notre .so ; sorties de référence poppler-utils |
| P1-f | Site de documentation, matrice CI (7 cibles), builds reproductibles, paquets système | P1-b/c | site + matrice verte |
Phase 2 — Inspecteur, linter, forensique (F-020…F-027)
| Lane | Contenu | Preuve |
|---|---|---|
| P2-a | Graphe d'objets + vues + historique des révisions (apdf tree, apdf revisions) |
qpdf --json, pdfresurrect, pdfsig ; contrôles négatifs |
| P2-b | Linter + SARIF + corrections suggérées + profils d'industrie (eCTD, judiciaire, Factur-X, ZATCA, préréglages gouvernementaux) | sarif-multitool, GitHub code scanning, contrôles croisés veraPDF/callas |
| P2-c | Langage de requête + QDF + apdf json (compatible qpdf) |
qpdf --qdf, --json=2 |
| P2-d | Diff (structurel/textuel/visuel/révision) | témoins Draftable/pdftk/pdftoppm |
| P2-e | Mode forensique + apdf scrub + --rebuild (CDR) + rapport de durcissement |
peepdf/pdfid, PoC publiés, UNSAFE-DOCS |
| P2-f | GUI (egui → WASM), extension VS Code, action GitHub, extension de navigateur | action exécutée sur le dépôt ; captures d'écran |
Phase 3 — Création et confiance (F-030…F-037)
| Lane | Contenu | Preuve |
|---|---|---|
| P3-adr | ADR : moteur de façonnage, césure, OCR, sous-ensemble HTML paginé | 🧭 D-6/D-9 |
| P3-a | Constructeur de document (façonnage, césure, tableaux, SVG, Markdown, balisage automatique) | HarfBuzz, LuaTeX, veraPDF UA-2/A-4 |
| P3-b | Hybrides de facturation électronique : Factur-X/ZUGFeRD → XRechnung → ZATCA → thaïlandais → représentations PDF avec QR (CFDI, DANFE, DIAN, SII, IRN, ATCUD, KSeF, VERI*FACTU, TicketBAI, myDATA) | Mustang, KoSIT, SDK ZATCA, validateurs nationaux, Schematron Peppol |
| P3-c | C2PA 2.4 | c2patool |
| P3-d | PAdES PQ (RFC 9882, composites) — mesurer d'abord la disponibilité de RUSSL ; lane amont si manquante | OpenSSL 3.5, Bouncy Castle |
| P3-e | Trait de signataire externe + adaptateurs (PKCS#11, CNG, CTK, SKF, KMS cloud, CSC 2.2, Aadhaar eSign, SingPass), magasins de confiance (LOTL, AATL, nationaux), oracle DSS, politiques de signature (PAdES-BR, Part 11, e-Apostille, judiciaire) | DSS, SoftHSM2, bacs à sable CSC, vérificateur ITI |
| P3-f | Couche carte à puce : transport PC/SC, découverte PKCS#15/IAS-ECC/EN 14890, modules de profil carte (PIV/CAC, IAS-ECC/CPS, EstEID, BELPIC, DNIe, CNS/CIE, JPKI, OpenPGP, famille des cartes santé) | outils OpenSC, cartes de test PIV du NIST, cartes de test éditeur |
| P3-g | Formulaires : concepteur, rendu XFA (SAP/AEM/IRS/cerfa), bac à sable du sous-ensemble JS, catalogues de pré-remplissage | XFA de pdfium, Acrobat, pdf.js |
| P3-h | Écriture de TS 32004 + déclarations /ISO_ (russl-pades en amont), EA-PDF, PDF/raster, écriture PDF Declarations, conteneurs ASiC/XAdES |
pyHanko, libdigidocpp, DSS |
Phase 4 — Verticales (F-040…F-047)
Lanes P4-santé, P4-prépresse, P4-archivage, P4-accessibilité, P4-extraction,
P4-juridique-défense, P4-mrc, P4-codes-barres-géo — chacune avec les oracles listés en
03b. Ordre selon le calendrier du marché : P4-codes-barres-géo et P4-accessibilité en
premier (dépendances QR de la facturation électronique et échéances ADA Title II
2026-04/2027-04), puis P4-archivage et P4-santé, puis P4-prépresse, P4-juridique-défense,
P4-mrc, P4-extraction.
Phase 5 — Produits (F-050…F-055)
P5-visualiseur-wasm, P5-visualiseur-natif, P5-serveur, P5-saas, P5-points-ancrage-os (CUPS, IFilter, QuickLook, modules complémentaires, Moodle), P5-ocr (après l'ADR), P5-html-paginé (après D-9).
Phase 6 — Écosystème (F-060…F-067), en parallèle dès que Phase 0 sort
P6-observatoire, P6-suite-de-conformité, P6-préfixe-ASKL, P6-normalisation (PDF Association, AFNOR/ISO TC 171, liaison ETSI ESI, errata), P6-safedocs, P6-fuzzing-continu, P6-registres (après D-1).
Transversal — la performance à chaque étage
Chaque lane qui touche un étage mesure avant/après avec le harnais de F-004 (criterion + ~50 fichiers) et ne livre pas une régression au-delà de 5 % sans l'expliquer. Étages : codecs (RUSSL), filtres, analyse/xref, polices, contenu/texte, rendu, écriture, conformité. Petite échelle d'abord ; le balayage complet du corpus seulement une fois F-004 stabilisé.
Modèle de rapport de lane
Objectif (F-xx, clauses) · ce qui a été livré · chiffres face à chaque oracle avec
contrôles négatifs · défauts trouvés dans le code existant · ce qui a été délibérément
non fait et pourquoi · ce qui reste · décisions soulevées pour le fondateur
(→ 06-decisions.md) · commits (SHA, index temporaire) · emplacements du worktree et
du scratch (jamais dans le dépôt).