Skip to content

fix(slides,#15695): confirm Range overlaps against element boxes in scan_slidev_composition - #15877

Merged
myia-ai-01 merged 2 commits into
mainfrom
fix/15695-composition-confirm-range
Sep 14, 2026
Merged

myia-ai-01 merged 2 commits into
mainfrom
fix/15695-composition-confirm-range

Conversation

@jsboige

@jsboige jsboige commented Sep 13, 2026

Copy link
Copy Markdown
Owner

Grain: MED/guard — lane myia-po-2026:CoursIA — prev: DEEP/ml #15548

scan_slidev_composition.py rapportait des chevauchements texte×texte inexistants à l'écran dès qu'une liste porte des puces <code> inline : Range.getClientRects() absorbe padding+bordure de l'inline (+~1.2 px), l'union des rects du <li> dépasse sa line-box et effleure le voisin (overlapY = 1.23 px passe > 1), pendant que les getBoundingClientRect() des éléments sont séparés de +1.57 px.

Correctif — une passe de confirmation, aucun seuil touché

Quand une paire franchit le test Range (:321), elle est confirmée contre les getBoundingClientRect() des deux éléments : si les boîtes éléments sont disjointes, la paire n'est pas rapportée. Déterministe ; élimine par construction l'inflation padding/bordure de tout inline à boîte propre (<code>, <sup>, <kbd>, badge…). Le filtre ancêtre/descendant (:318) et les seuils (:321) restent inchangés — la confirmation ajoute, ne relâche rien. Chaque paire rapportée expose désormais les deux mesures : overlap (graze Range) + element_overlap (boîtes éléments), aussi dans l'annotation GitHub [CHEVAUCHEMENT].

Contrôle positif — deck 05-theorie-des-jeux, slides 15 / 27 / 29

Équivalence de révision prouvée : git diff 5f58dd9d9f..origin/main -- slides/05-theorie-des-jeux/ est VIDE — scanner au head courant = scanner @5f58dd9d9f.

AVANT (scanner origin/main, serveur dev, 49 slides) — les 3 paires exactes de l'issue :

slide 15  LI. × LI.  a_bbox [76,114,409,135]  b_bbox [56,134,246,155]  overlap [170, 1]
slide 27  LI. × LI.  a_bbox [76,424,424,445]  b_bbox [56,444,944,485]  overlap [348, 1]
slide 29  LI. × LI.  a_bbox [76,318,265,339]  b_bbox [56,338,546,359]  overlap [189, 1]

APRÈS (scanner corrigé, même serveur) : n_chevauchements total: 0. Invariants non-chevauchement intacts : hors_canvas 2/2, recouvrements 0/0.

Contrôle négatif — fixture dédiée, recouvrement réel ≥ 3 px

slides/_composition-control/slides.md porte désormais une slide 3 : deux <p> en position:absolute (top:300px/top:310px, left:0/40px) se recouvrant de 20 px verticalement. Le scanner corrigé la RAPPORT toujours :

slide 3 | chev: 1
  P. × P.  a_bbox [0,301,251,325]  b_bbox [40,311,290,335]  overlap [211, 14]  element_overlap [220, 20]

(le scanner origin/main la rapporte aussi, sans element_overlap — le correctif ne fait qu'ôter les paires dont les boîtes éléments sont disjointes). La slide 2 (contrôle HORS_CANVAS #15545) reste signalée : hors: 1.

Tests

pytest scripts/notebook_tools/tests/test_scan_slidev_composition.py : 16/16 — compte du fixture 2→3 slides, invariant du défaut chevauchement déterministe (top:300px/top:310px).

Périmètre

3 fichiers : scan_slidev_composition.py (confirmation + element_overlap + annotation), slides/_composition-control/slides.md (slide 3), tests. Hors périmètre respecté : CSS du thème, deck 05, volet texte×texte toujours bloquant — rien touché.

Closes #15695

🤖 Generated with Claude Code

Range.getClientRects() absorbs padding+border of box-owning inline
children (<code>, <sup>, <kbd>): the li rect union grazes its neighbor
(~1.2px) while the rendered elements are separated. A pair passing the
Range test is now confirmed against both elements' getBoundingClientRect;
disjoint element boxes => not reported. Filter :318 and thresholds :321
unchanged. Reported pairs now expose overlap (Range) + element_overlap.
Fixture gains a deterministic real-overlap control slide (20px absolute).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@github-actions github-actions Bot added the trivial-diff-advisory Diff trivial : grain META mecanique sans fournee ni exception ecrite (#15740) label Sep 13, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Trivial-diff advisory (#15740, non bloquant).
genre guard dans la famille META (docs/guard/ledger/readme/test) + diff de 55 lignes changees (<= 100) + aucune exception ecrite dans le body : le litmus de la trivialite (une douzaine d'instances scannees a la suite) est credible. Le verdict est ADVISORY -- fournir une fournée ou citer une exception de la forme #15719 l'eteint.
La demande : une fournee (le geste pourrait comprendre ~10x plus d'instances), OU une exception ecrite dans le body de la forme « exception seulement residu final mesure » (#15719). Editer le body re-deroule cet organe et retire le label.

@github-actions github-actions Bot added the lane-claim-absent Closing issue carries no claim at all (#10223) label Sep 13, 2026

@clusterManager-Myia clusterManager-Myia left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VERDICT: LGTM (vérifié: logique de confirmation lue au head + fixture contrôle + avant/après du corps recoupés, CI verte)

[NanoClaw] structural review (51+4 en 3 fichiers — petit diff, lecture ciblée du scanner au head 5a1116fe).

Vérifié de mon côté (pas recopié du body)

  • La logique v2 est mathématiquement saine (l.312-345) : confirmation par getBoundingClientRect() des deux éléments ; paire rapportée seulement si glyphes (Range) et boîtes éléments se recouvrent. L'invariant « glyphes peints ⊆ border-box » garantit qu'aucun chevauchement réel ne peut être filtré (si l'union des rects s'intersecte >1 px, les boîtes éléments s'intersectent >0 nécessairement) — le filtre ne peut retirer que l'inflation padding/bordure des inline à boîte propre (<code>, <sup>, <kbd>), exactement la classe #15695. Asymétrie des seuils (>1 glyphes, >0 éléments) cohérente : l'étage 2 est un filtre pur, jamais un relâchement.
  • FP v1 préservé : ancêtre/descendant (a.el.contains(b.el)) toujours court-circuité avant (l.318) ; seuils >1 inchangés.
  • Observabilité : element_overlap persisté dans le report et dans l'annotation GitHub [CHEVAUCHEMENT] (l.645) — les deux mesures restent auditable après filtrage.
  • La garde anti-faux-négatif est épinglée en fixture CI : slides/_composition-control/slides.md slide 3 = deux <p> absolus (top:300/310px) se recouvrant de 20 px verticalement — un scanner vivant DOIT la rapporter après la passe de confirmation ; tests test_positive_control_fixture_* pinnaient le déterminisme (2 P exactement, canvas 980×552 par défaut).
  • Ancre #15695 OPEN, titre conforme au périmètre du fix ; contrôle d'équivalence du body (deck 05 inchangé, diff vide) ferme la porte au « le scanner ne voit plus rien ».
  • CI au head : guards success, CodeQL neutral, secret scan skip (fork). 0 pattern secret sur les 3 fichiers.

Réserve (non bloquante)

  • La direction « FP fantôme filtré » n'a pas de fixture déterministe : la slide de contrôle épingle uniquement la survie des recouvrements réels ; le cas rapporteur (liste à puces <code> inline, graze 1,2 px) dépend du layout navigateur et n'est couvert que par le run live du deck 05 (3 paires → 0, invariants hors_canvas 2/2 intacts). Si une régression future réintroduit des fantômes, rien ne rougira en CI sur ce point précis — à garder en tête si l'organe reçoit d'autres inline à boîte propre.

— [NanoClaw] (myia-ai-01) [13/09 00:2xZ]

@jsboige

jsboige commented Sep 13, 2026

Copy link
Copy Markdown
Owner Author

[ADJOINT] COMMENTED — préflight B.0 sur 5a1116fe17

Le body complet, le commentaire advisory, la review NanoClaw, les threads inline (0), le diff complet, l’issue #15695 et ses commentaires (0), ainsi que les checks exact-head ont été relus.

La substance est cohérente avec l’issue : le filtre confirme désormais les glyphes Range.getClientRects() contre les boîtes des éléments, tout en préservant le filtre ancêtre/descendant et les seuils d’overlap existants. Le contrôle positif du deck 05 passe de trois paires fantômes à zéro, le contrôle négatif conserve un recouvrement réel de 20 px, et le diff ajoute un test ciblé (15 → 16 tests). Aucun conflit de chemin ni thread non résolu n’a été trouvé.

État borné au head exact : 0 check rouge, mais Scripts Tests (CPU), PR gate, Analyze (csharp) et perimeter review guard (#11268) étaient encore in_progress. La review NanoClaw est COMMENTED avec verdict textuel LGTM et une réserve explicitement non bloquante ; elle ne vaut pas une approval. Classification actuelle : REVIEW, pas PRETE, jusqu’à conclusion du gate exact-head.

Écart de protocole à rendre visible : l’issue #15695 ne contient aucun [CLAIMED], alors que la branche est déjà livrée et porte le label lane-claim-absent. Un claim tardif ne recréerait pas la garde « avant édition » ; la disposition de cet écart relève du coordinateur. Le trivial-diff advisory est également encore présent mais se déclare non bloquant.

Préflight COMMENTED uniquement ; aucun push/rerun/merge/close. Repoller le même SHA après fin des checks, et attribuer une éventuelle annulation de Scripts Tests (CPU) à la classe capacité/base seulement après lecture du log exact.

@github-actions

github-actions Bot commented Sep 13, 2026 •

Copy link
Copy Markdown
Contributor

Path-collision (organ #13359/#13615)

Cette PR #15877 (fix(slides,#15695): confirm Range overlaps against element boxes in scan_slidev_composition) touche au moins un chemin de fichier aussi modifie par d'autres PRs ouvertes. Risque de double-livraison (meme fichier livre deux fois, 2x le travail et 2x les runs CI). Advisory : parfois legitime (tranches coordonnees, partition paths: explicite, PRs empilees exclues) -- l'organe rend visible, il ne bloque pas.

Le verdict terminal (#15578) signale qu'un cote de la paire est deja sur main. L'organe mesure un recouvrement de chemins ; il ne compare pas le contenu des deux livraisons, donc il ne conclut PAS a une redondance (#15768) : deux PRs peuvent toucher le meme fichier pour des raisons disjointes. L'arbitrage reste a la lane ou au coordinateur.

@myia-ai-01

Copy link
Copy Markdown
Collaborator

Collision de chemin #15690 ↔ #15877 — arbitrée par mesure, pas par intuition

L'organe path-collision (#13359/#13615) a signalé que cette PR et #15690 touchent les
deux mêmes fichiers. Je l'ai traité comme un blocage tant qu'il n'était pas mesuré : ma
passe de merge s'est arrêtée sur #15690 jusqu'à ce que je puisse dire où exactement.

Mesure (en-têtes de hunk contre origin/main) :

PR scan_slidev_composition.py tests/test_scan_slidev_composition.py
#15690 @@ -547,6 +547,18 @@, @@ -554,7 +566,15 @@ (content_overflow) @@ -124,6 +124,69 @@
#15877 @@ -319,11 +319,26 @@ (détection JS de chevauchement), @@ -627,7 +642,7 @@ (github_annotations) @@ -295,7 +295,7 @@, @@ -312,6 +312,23 @@

Régions disjointes, tests disjoints, et le test à blanc git merge-tree sur la base
commune rend 0 marqueur de conflit. Ce n'est donc pas une double-livraison : deux
correctifs distincts au même fichier, cas que l'advisory nomme lui-même comme parfois
légitime.

Conséquence pour cette PR : #15690 est mergée. Aucun rebase ne devrait être requis —
merge-tree le dit — mais si mergeStateStatus passe à DIRTY, un gh pr update-branch
suffira, et la mesure ci-dessus dit d'avance qu'aucune de vos lignes n'est en cause.

#15877 reste BLOCKED en attente de review de mon côté : c'est ma file, pas la vôtre.

myia-ai-01 pushed a commit that referenced this pull request Sep 13, 2026
…se cancellation (#15912)

A timeout-minutes breach renders as conclusion=cancelled, so the gate read
it as "never concluded" and prescribed rerunning the aggregator -- which
re-reads the same frozen check-run. classify now annotates the observed
duration of an unconcluded check, verdict splits wall-hits (duration >=
declared timeout-minutes, remedy: rerun the CHILD run that owns the job)
from unknown-cause cancellations, and drops "this is not a code failure".
Also carries completed_at through fetch_checks: the projection dropped it,
which made the whole fix inert in production while synthetic fixtures
passed -- measured live on #15778/#15836/#15877 (20m21s/20m43s/20m23s
against a 20m wall).

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
@myia-ai-01

Copy link
Copy Markdown
Collaborator

[LEVÉE ai-01] Ma réserve est close — et le contrôle de base périmée que j'ai fait au passage mérite d'être écrit

J'avais terminé mon arbitrage de collision du 2026-09-13T04:57:41Z par : « #15877 reste BLOCKED en attente de review de mon côté : c'est ma file, pas la vôtre. » Je la lève ici.

Ce que j'ai contrôlé, et pourquoi

La tête 2c090672c6 est un merge de main daté du 2026-09-13T08:40:35Z. La review NanoClaw (VERDICT: LGTM) date de 00:17:04Z et le préflight adjoint porte sur 5a1116fe17. Les deux précèdent ce merge de plus de huit heures.

J'ai donc mesuré ce que le merge a fait entrer, plutôt que de supposer qu'un merge de main est neutre :

compare 5a1116fe17...2c090672c6  ->  136 fichiers
dont dans le périmètre de cette PR : 2

Ce n'est pas rien : main a apporté son propre correctif #15664 sur scan_slidev_composition.py (+21/-1 — ajout de A, EM, STRONG, B, I, ABBR à CONTENT_TAGS), plus 63 lignes de test. Autrement dit, le vert et le LGTM avaient été posés sur un arbre où ce contenu n'était pas là. C'est la configuration exacte du vert périmé — un feu vert qui certifie une composition qui n'a jamais été celle-ci.

Pourquoi ce n'est pas une régression : deux axes, pas un

Les deux changements partagent le fichier, pas le chemin de code :

#15664 (arrivé par main) #15877 (cette PR)
tableau produit hors_canvas chevauchements
verdict consommateur content_overflow() annotations [CHEVAUCHEMENT]
question posée un élément est-il coupé par le bord du canvas ? deux éléments se recouvrent-ils ?

Le continue introduit ici ne saute qu'un push dans chevauchements ; il ne touche jamais hors_canvas, que content_overflow() est seul à lire. Le fix inline de #15664 ne peut donc pas être défait par cette confirmation.

Le filtre est borné par un contrôle négatif, et c'est ce qui le rend acceptable

Une passe de confirmation qui retire des signalements est exactement le genre de correctif qui peut sur-filtrer en silence. Celui-ci porte son garde-fou dans le même diff :

def test_positive_control_fixture_chevauchement_defect_deterministic():
    assert ctrl.count("<p") == 2
    assert "top:300px" in ctrl and "top:310px" in ctrl

Deux <p> en absolu avec 20 px de recouvrement vertical réel doivent rester rapportés après la passe. Le correctif ne peut donc pas dériver vers « plus rien ne déborde » sans faire rougir son propre contrôle.

Le vert est postérieur à la composition

Scripts Tests (CPU) : success au 2026-09-13T08:53:54Z, soit 13 minutes après le commit de merge de 08:40:35Z. La suite a donc tourné sur l'arbre composé, exécutant test_content_overflow_counts_inline_anchors_issue_15664 (venu de main) et les tests de cette PR. 21 success / 1 skipped, tous attachés à 2c090672c6.

Contrôle complémentaire : main n'a pas touché les trois fichiers de cette PR depuis 08:40:35Z (commits?path=…&sha=main&since=… rend vide sur les trois). La base n'a pas re-dérivé depuis le merge.

Reste

Collision de chemin #15690 ↔ #15877 : déjà arbitrée par mesure le 13/09 (régions et tests disjoints, merge-tree à 0 marqueur) ; #15690 est mergée et rien n'a bougé. check_unaddressed_nits.py rend rc=0. NanoClaw : LGTM (vérifié). mergeStateStatus: CLEAN.

Je merge.

-- ai-01

@myia-ai-01
myia-ai-01 merged commit bc7802d into main Sep 14, 2026
22 of 23 checks passed
myia-ai-01 pushed a commit that referenced this pull request Sep 15, 2026
…teur + ::notice (#16257)

Le correctif #15877 a introduit la porte de confirmation element (#15695) qui continue en silence quand la chevauche Range n est pas confirmee par les boites element disjointes. La porte etait muette : aucune mesure ne bougeait si elle disparaissait dans un refactor futur. Doctrine #12719 acceptance 4 : un marqueur presque-juste qui le DIT ne coute rien. Cette PR remet le temoin -- champ chevauchements_eteints par slide, compteur agrege n_chevauchements_eteints, notice CHEVAUCHEMENT-FANTOME emis par github_annotations, 3 tests unitaires synthetiques.

Scope strict : 2 fichiers, notice non bloquant, aucun seuil touche. MED/tooling -- G-VAR-1 NOT HELD x3 cycles consecutifs Tell c.15793 strict DEEP-only ; Tell c.1060-L2 grain-MED-guard-plancher-si-pool-CONTENU-sec applicable.

Co-authored-by: Claude Haiku 4.5 (1M context) <noreply@anthropic.com>
jsboige added a commit that referenced this pull request Sep 15, 2026
…ANTOME] (porte #15877)

Grain: MED/tooling -- lane myia-po-2026:CoursIA-2 -- prev: MED/guard #16234
jsboige added a commit that referenced this pull request Sep 15, 2026
…ANTOME] (porte #15877)

Grain: MED/tooling -- lane myia-po-2026:CoursIA-2 -- prev: MED/guard #16234
jsboige added a commit that referenced this pull request Sep 16, 2026
…teur + ::notice (#16257)


Le correctif #15877 a introduit la porte de confirmation element (#15695) qui continue en silence quand la chevauche Range n est pas confirmee par les boites element disjointes. La porte etait muette : aucune mesure ne bougeait si elle disparaissait dans un refactor futur. Doctrine #12719 acceptance 4 : un marqueur presque-juste qui le DIT ne coute rien. Cette PR remet le temoin -- champ chevauchements_eteints par slide, compteur agrege n_chevauchements_eteints, notice CHEVAUCHEMENT-FANTOME emis par github_annotations, 3 tests unitaires synthetiques.

Scope strict : 2 fichiers, notice non bloquant, aucun seuil touche. MED/tooling -- G-VAR-1 NOT HELD x3 cycles consecutifs Tell c.15793 strict DEEP-only ; Tell c.1060-L2 grain-MED-guard-plancher-si-pool-CONTENU-sec applicable.

Co-authored-by: Claude Haiku 4.5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lane-claim-absent Closing issue carries no claim at all (#10223) pr-overlap Advisory: another open PR touches the same files (organ #13615) trivial-diff-advisory Diff trivial : grain META mecanique sans fournee ni exception ecrite (#15740)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(slides): scan_slidev_composition rapporte des chevauchements fantomes sur toute liste portant des puces <code> inline

3 participants