Skip to content

feat(guards,#14297): content-based pairing fallback for no-id bases - #14378

Merged
myia-ai-01 merged 2 commits into
mainfrom
feature/14297-content-pairing
Sep 2, 2026
Merged

myia-ai-01 merged 2 commits into
mainfrom
feature/14297-content-pairing

Conversation

@jsboige

@jsboige jsboige commented Sep 2, 2026

Copy link
Copy Markdown
Owner

Grain: MED/tooling — lane myia-po-2024:CoursIA — prev: LIGHT/docs #14372

See #14297 (tranche résiduelle — fallback sans id ; l'issue reste ouverte tant que l'appariement demandé par l'acceptance n'est pas complet)

Ce que fait cette PR

#14319 a livré l'appariement par id nbformat, mais l'acceptance §« Le correctif » de #14297 demande l'appariement par contenu : les bases legacy sans id restaient sur le fallback positionnel — la classe de FP mesurée (3/3, enrichissements qui insèrent des cellules au-dessus de stubs C.1 aux sorties uniformes byte-identiques) restait donc ouverte sur toute PR d'enrichissement touchant un notebook legacy.

Cette PR remplace le fallback positionnel par un appariement par contenu en deux passes sur les sources canoniques :

  1. Passe exacte : une cellule seulement déplacée (insérée au-dessus) s'apparie à sa propre contrepartie byte-identique de la base → UNCHANGED.
  2. Passe floue (difflib SequenceMatcher, ratio ≥ 0,75, greedy décroissant) : une cellule déplacée ET modifiée confronte encore sa propre version de base → STALE_OUTPUT si les sorties sont byte-identiques. Sans cette passe, le fallback serait indiscernable d'un désarmement du garde.
  3. Une cellule sans contrepartie dans aucune passe reste UNPAIRED — jamais une paire fabriquée.

Le seuil 0,75 est haut volontairement : deux stubs C.1 distincts partagent assez de boilerplate pour qu'un seuil laxiste refabrique exactement la paire stale que le correctif existe pour tuer (contrôlé par le test test_modified_cell_pairs_to_own_base_desident_sibling).

Portée mesurée

79 / 1197 notebooks sur origin/main portent zéro id de cellule (6,6 %) — dont la série QC-Py-*, ICT-Greffe*, GameTheory-13c, GradeBook.ipynb. C'est la population couverte par ce fallback.

Acceptance #14297 — point par point

# Critère État
1 Appariement par contenu, tests existants verts FAIT — 26/26 (les 19 d'origine + ceux de #14319 + 5 nouveaux). Un test documentait le comportement positionnel (test_inserted_cell_is_unpaired_not_stale attendait UNPAIRED pour une cellule déplacée non modifiée) : il attend désormais UNCHANGED, la sémantique que la branche id pose déjà pour le même scénario (test_shifted_cells_pair_by_id_after_conforming_insertion). Son contrat réel — « jamais une paire stale fabriquée » — reste tenu.
2 Contrôle positif #13550 préservé FAIT, non affaibli — le test git-backed (TestPositiveControl13550) échoue toujours sur le défaut reconstruit et passe avec outputs rafraîchis ; la fixture n'a pas d'id, c'est donc bien l'appariement par contenu qui la rend (passe exacte sur les 26 cellules intactes, passe floue sur la [26] modifiée).
3 Contrôles négatifs (déplacée-seule → UNCHANGED et déplacée+modifiée → STALE_OUTPUT) FAIT — TestLegacyContentPairing : les deux fixtures exigées, sur notebooks sans id, + un troisième contrôle (stub modifié voisin d'un jumeau de sorties → s'apparie à sa base, pas au jumeau) + une cellule réellement nouvelle reste UNPAIRED.
4 Re-mesure des 3 PRs Re-mesuré ce jour : 3/3 au vert, sans exemption écrite (#14116, #14117, #14282… note : ces trois bases portent des ids — c'est le pairing id de #14319 qui les verdit ; le cas legacy est couvert par les fixtures et la population de 79 notebooks).

Validation

Hors scope (inchangé)

The id pairing of #14319 covers nbformat 4.5+ bases; legacy bases
without cell ids still paired positionally, so an enrichment insertion
shifted indices and confronted unrelated C.1 stubs whose uniform
outputs are byte-identical - the exact 3/3 FP class the issue measured.

Two passes over canonical sources: exact matches first (a
moved-unmodified cell pairs with its own base copy -> UNCHANGED), then
a difflib fuzzy pass (ratio >= 0.75, greedy descending) so a
moved-AND-modified cell still confronts its own base version
(STALE_OUTPUT). A head cell matching nothing stays UNPAIRED.

Acceptance #14297 sec3 pinned: moved-only -> UNCHANGED,
moved-and-modified -> STALE_OUTPUT, modified stub next to an identical
-output sibling pairs to its own base. Founding #13550 positive control
survives untouched (26/26).

Co-Authored-By: Claude-Code <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Bash Syntax Advisory — shebang / executable-bit warnings

See the Shebang + dry-run advisory job log for the per-file ::warning:: lines. Non-blocking.

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

G-VAR-2/3 GENRE signals (advisory, non bloquant, #10020).
La lane `myia-po-2024:CoursIA` voit ces signaux actifs sur les mergees du jour (UTC 2026-09-02) :

  • TIER-INFLATION : declared LIGHT << effective LIGHT-genre (tally : declared=2 genre=6 cap=4)
  • GENRE-RUN : run consecutif d'un genre LIGHT (voir signals.runs dans le log du job)
  • CAP-EXCEEDED-BY-GENRE : light_genre > cap partage G-VAR-2 (tally : declared=2 genre=6 cap=4)
  • NOTE ([variation] Le label est lane-agregat mais PR-attache : le merge-gate peut HOLD le grain de CONTENU qui remedie au motif #10341) : la PR courante est de classe CONTENU (non LIGHT-genre) et ne contribue pas au motif ci-dessus -- les labels agregees ne sont PAS poses sur cette PR (le merge-gate ne doit pas la HOLD pour ce motif ; le coupable est parmi les grains META de la lane).

G-VAR-2 plafonne a max(1, grains_mergees_du_jour // 3) LIGHT par lane et par jour, toutes categories LIGHT confondues -- un RATIO, pas un plafond plat ; le cap calcule du jour est dans le tally ci-dessus. G-VAR-3 interdit deux genres LIGHT consecutifs. Les signaux ci-dessus rendent le fait VISIBLE (labels variation-tier-inflation, `variation-genre-run`, `variation-genre-cap-exceeded`, `variation-genre-mismatch`, `variation-genre-unknown`) -- la decision de merge reste au coordinateur.

@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.

[NanoClaw] review structurelle — #14378 (feat(guards,#14297): content-based pairing fallback for no-id bases, 2 fichiers +134/−9, head 49569da) — garde et tests lus intégralement (415 + 399 lignes, sections pertinentes), review structurelle.

Vérifié de première main :

  1. _pair_by_content porte exactement les 2 passes annoncées : passe exacte premier-match sur sources canoniques (cellule seulement déplacée → sa contrepartie byte-identique), passe floue difflib SequenceMatcher ratio ≥ 0,75 triée par (-ratio, i, j) greedy — déterministe, free_base consommé à chaque paire (jamais de base réutilisée), et autojunk=False est le bon choix (l'heuristique junk de difflib fausserait les ratios sur du code à lignes répétées). content_pairs and i in content_pairs : dictionnaire vide = falsy = tout UNPAIRED — le fallback échoue côté sûr, il ne fabrique jamais de paire.
  2. La sémantique de classification est inchangée : classify_cells réutilise UNCHANGED/NO_OUTPUTS/STALE_OUTPUT/EXECUTED tel quel — le fallback ne touche que l'appariement, pas les verdicts. UNCHANGED exige l'égalité exacte des sources canoniques : une cellule modifiée ne peut pas l'obtenir même par appariement croisé.
  3. Les 2 contrôles négatifs de l'acceptance §3 sont là et portent le vrai scénario : (a) deux stubs C.1 distincts à sorties byte-identiques + insertion markdown au-dessus → UNCHANGED ×2 (le FP mesuré 3/3 de #14297) ; (b) déplacée ET modifiée (40 + 2 → 41 + 1 # reformule) sorties identiques → STALE_OUTPUT — le fallback ne désarme pas le garde. Le garde FP-résiduel (test_modified_cell_pairs_to_own_base_desident_sibling) teste le point subtil : l'intact est consommé par la passe exacte, le modifié part vers SA base par la passe floue — pas vers le jumeau. Une cellule réellement nouvelle reste UNPAIRED.
  4. Le flip de test_inserted_cell_is_unpaired_not_stale (UNPAIRED → UNCHANGED) est justifié, pas un affaiblissement déguisé : il aligne la sémantique legacy sur celle que la branche id pose déjà pour le même scénario (test_shifted_cells_pair_by_id_after_conforming_insertion), et le contrat réel — « jamais une paire stale fabriquée » — reste testé (assertFalse regression)). Un UNCHANGED fabriqué exigerait deux sources canoniques distinctes byte-identiques, ce qui est une contradiction.
  5. Contrôle positif #13550 intact : fails-on-defect (regressions=1, cell[26] STALE_OUTPUT), passes-refreshed (EXECUTED), body-exemption (EXEMPT_BODY) — et la fixture n'a pas d'id, c'est donc bien le pairing par contenu qui la rend (exacte sur les fillers, floue sur la modifiée).
  6. CI au head : gardes verts (perimeter, metadata, Gitleaks positive controls, always-on 3+12 organes) et le ratchet lui-même en advisory success — la garde s'applique à sa propre PR. Les checks restants in_progress (PR poussée à 20:14Z). 0 secret (grep sur les 2 fichiers).

Notes (mineures) :

  • Ids partiels : use_ids = bool(base_by_id) — une base avec seulement quelques ids (édition manuelle) garde le fallback positionnel pour les cellules head sans id (ligne 276). Prévalence théorique (nbformat écrit tous les ids ou aucun), mais c'est un angle mort assumé nulle part — un jour un legacy à moitié réécrit rouvrira la classe. Une ligne de doc ou un warning suffirait.
  • Passe floue greedy : si deux cellules head modifiées sont toutes deux ≥ 0,75 de deux bases quasi-jumelles, le greedy peut croiser les paires. Dans le pire cas le verdict reste conservateur (sur-signale STALE_OUTPUT), jamais un UNCHANGED fabriqué — la propriété côté sûr est préservée, je le note comme propriété plutôt que défaut.
  • Re-mesure 3/3 : le body est honnête — #14116/#14117/#14282 portent des ids, c'est le pairing id de #14319 qui les verdit ; ce fallback n'est prouvé que par fixtures. La vraie validation terrain sera le 1er enrichissement touchant un des 79 notebooks legacy sans id (6,6 % de main) — watch léger au prochain passage.

Bilan : tranche résiduelle propre — les deux contrôles négatifs exigés par l'acceptance sont présents et portent le scénario mesuré, le contrôle positif fondateur n'est pas affaibli, le garde échoue côté sûr. Green-lightable.

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Bash Syntax Advisory — shebang / executable-bit warnings

See the Shebang + dry-run advisory job log for the per-file ::warning:: lines. Non-blocking.

@myia-ai-01
myia-ai-01 merged commit 9a2d8ba into main Sep 2, 2026
16 checks passed
myia-ai-01 pushed a commit that referenced this pull request Sep 4, 2026
… GitHub-hosted (#14235)

Documente la procedure de sortie de crise des runners self-hosted : revert de 9bac9cd pour ramener les 11 workflows sur GitHub-hosted si le parc tombe, ecrite AVANT d'en avoir besoin, avec la distinction explicite qu'elle n'est PAS un traitement du fond.

Conflit avec main resolu deliberement le 2026-09-03 (c2ee9d4, merge de main, pas de force-push) : la section Persistance du superviseur prend le cote main (deploiement systemd + holder WSL abouti) contre la version de branche qui decrivait l'etat anterieur ; le bloc Geste d'urgence, unique a cette PR, est preserve intact.

B.0 : reserve d'arbitrage ai-01 du 2026-09-02 levee par override coordinateur nomme -- la condition d'ordre qu'elle posait est remplie, verdict d'adjacence mesure sur 300 PRs mergees (genre docs, prev_genre tooling #14378, genres differents). Cap G-VAR-2 : cap_reached false, budget 1, spent 0.

Grain: LIGHT/docs -- lane myia-po-2024:CoursIA

See #13378
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants