Skip to content

fix(tooling,#13906): epic_body_staleness — sonde de completude (l'organe ne mesurait plus rien a 506 issues ouvertes) - #17860

Merged
myia-ai-01 merged 2 commits into
mainfrom
fix/13906-epic-staleness-corpus
Sep 26, 2026
Merged

myia-ai-01 merged 2 commits into
mainfrom
fix/13906-epic-staleness-corpus

Conversation

@jsboige

@jsboige jsboige commented Sep 25, 2026

Copy link
Copy Markdown
Owner

Grain: MED/tooling — lane myia-po-2023:CoursIA — prev: DEEP/notebook-python #17855

Plancher du cycle : #17855 (DEEP/notebook-python), en attente de DWELL. Cette PR est le grain META qui vient au-dela.

Le defaut, mesure avant tout geste

python scripts/epic_body_staleness.py — l'organe livre par #13937 pour mesurer les bodies d'Epic qui ignorent leurs propres livraisons — ne mesurait plus rien :

$ python scripts/epic_body_staleness.py --pretty
epic_body_staleness: open-issue corpus reached its 500-issue fetch limit
RC=1

Aucun resultat, aucun finding. Cause : list_open_epics demandait --limit 500 puis levait des que la reponse atteignait exactement ce plafond. Le depot est passe a 506 issues ouvertes :

$ gh api "search/issues?q=repo:jsboige/CoursIA+is:issue+is:open" --jq .total_count
506

Le refus est fail-CLOSED, donc il ne fabrique pas de faux vert — mais une borne placee sous la donnee n'est plus un garde, c'est un interrupteur : l'organe s'eteint au lieu de proteger, et rien ne le signale hors de l'execution.

Le correctif : une sonde de completude, pas un plafond plus haut

Un --limit fixe ne peut pas distinguer « ce depot a N issues ouvertes » de « la lecture s'est arretee a N » : les deux rendent exactement N lignes. La sonde elargit donc la requete jusqu'a ce qu'une reponse revienne plus courte que demandee — le seul observable qui prouve l'epuisement — et refuse au plafond plutot que de presenter un corpus tronque comme complet.

Avant Apres
Requete --limit 500, refus si len == 500 sonde 500 -> 1000 -> 2000 -> ... jusqu'a len < demande
Corpus de 506 refus, zero resultat lit les 506
Plafond 500 (sous la donnee) 20000, refuse explicitement au-dela
Payload non-liste (null) or [] -> corpus de 0 confondu avec un corpus vide refuse : un corpus non lu n'est pas un corpus vide

Le dernier point est le meme defaut par une autre porte : l'ancien or [] transformait une lecture ratee en corpus vide, donc en « rien a signaler ».

Preuve d'acceptation — la mesure repart

$ python scripts/epic_body_staleness.py --pretty
RC=0
corpus: {'open_epics_examined': 78, 'merged_prs_examined': 800,
         'merged_window_start': '2026-09-18T10:21:14Z',
         'merged_window_end': '2026-09-25T20:58:01Z'}
findings: 53

Sonde live sur le corpus reel (elargissement effectivement exerce) :

issues ouvertes lues : 506
EPICs reconnues      : 78
plafond              : 20000 (sonde de depart 500)

53 des 78 EPICs ouvertes portent au moins une livraison mergee que leur body n'inscrit pas, ou une posture dormante contredite par une livraison — dans la fenetre des 800 dernieres PRs merges (2026-09-18 -> 2026-09-25). Le mandat de curation de #13906 redevient mesurable au niveau programme ; les tete de classement (#16741 18 non-inscrites, #11698 9, #16334 8, #4960 7) sont la matiere de la passe de curation, pas de cette PR.

Tests

Verification Resultat
pytest scripts/tests/test_epic_body_staleness.py 25 passed
pytest scripts/tests -k epic 143 passed, 6830 deselected
Test de regression du defaut test_open_issue_probe_widens_past_a_full_reply — 506 issues : la sonde demande 500 puis 1000
Refus au plafond test_open_issue_probe_refuses_at_the_ceiling
Payload non-liste test_open_issue_probe_rejects_a_payload_that_is_not_a_list

Anti-regression

analyze_epics, dormant_stance, build_payload, _merged_pr_slice et list_merged_prs sont inchanges : le diff ne touche que la recuperation du corpus d'issues. Les tests existants (positif #1210, faux positifs de posture, bissection de fenetre) passent sans modification.

Classe deja rencontree sur ce depot : #17474 (fetch_open_prs tronque a 300 PRs) — corrige par #17568 pour le picker, sans traiter la classe. Aucune collision : grep des PRs et issues citant epic_body_staleness -> rien, et le seul plafond de cette forme sous scripts/ etait celui-ci.

See #13906

🤖 Generated with Claude Code

…pus d'issues ouvertes

L'organe livre par #13937 ne mesure plus rien. `list_open_epics` demandait
`--limit 500` puis levait des que la reponse atteignait ce plafond, et le depot
est passe a 506 issues ouvertes (mesure firsthand : `search/issues` ->
`total_count: 506`). `python scripts/epic_body_staleness.py` sortait donc rc=1
avec `open-issue corpus reached its 500-issue fetch limit`, sans aucun resultat :
le mandat de curation de #13906 (les bodies d'Epic qui ignorent leurs propres
livraisons) n'etait plus mesurable au niveau programme.

Un `--limit` fixe ne peut pas distinguer « le depot a N issues ouvertes » de
« la lecture s'est arretee a N ». La sonde elargit la requete jusqu'a ce qu'une
reponse revienne plus courte que demandee -- seul observable qui prouve
l'epuisement -- et refuse au plafond plutot que de presenter un corpus tronque
comme complet. Le refus reste fail-CLOSED : la borne de 500 etait sous la
donnee, donc elle n'etait plus un garde mais un interrupteur.

Un payload qui n'est pas une liste est desormais refuse lui aussi : `null` est
un corpus non lu, jamais un corpus vide (l'ancien `or []` confondait les deux).

Classe deja rencontree sur ce depot : #17474 (`fetch_open_prs` tronque a 300
PRs, corrige par #17568 pour le picker, sans traiter la classe).

See #13906
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

No organ-duplication: no added def/class collides with another series organ API (scripts/audit/organ_api_index.yaml).

Detector: python scripts/audit/detect_organ_duplication.py --base <merge-base> --body-file <pr body>
Rationale: #16776 / #13564 (rule merged in #16778).

@jsboige

jsboige commented Sep 25, 2026

Copy link
Copy Markdown
Owner Author

[myia-po-2023:CoursIA] Rouge Scripts Tests (CPU) de cette PR : rouge de base, mesure a l'appui

Le seul echec de la jambe Scripts Tests (CPU) (run 36192573540, job 108260919571, tete 75beda3ca001725c715772b24e605164a324181d) est :

FAILED scripts/notebook_tools/tests/test_twin_registry_integrity.py::test_audit_index_unique_and_no_identical_duplicates_per_pair
AssertionError: prefixe NNNN duplique dans une paire : ['probas-2-gaussian-mixtures:
  0016-2026-09-25-myia-po-2024-CoursIA.yaml et 0016-2026-09-25-myia-po-2026-CoursIA.yaml']
= 1 failed, 16143 passed, 111 skipped =

Il vient de la base, pas de cette PR. Le test balaie le registre entier ; origin/main = 67f75c49fa porte deja les deux entrees a l'index 0016 (verifie par git ls-tree --name-only origin/main scripts/notebook_tools/twin_pairs.d/probas-2-gaussian-mixtures/) :

0015-2026-09-18-myia-po-2025-CoursIA.yaml
0016-2026-09-25-myia-po-2024-CoursIA.yaml
0016-2026-09-25-myia-po-2026-CoursIA.yaml

Le correctif est ouvert et hors de ma lane : PR #17858 (renumerotation 0016 -> 0017), MERGEABLE, en attente de review. Les 25 tests unitaires ajoutes par cette PR passent (pytest scripts/tests -k epic : 143 passed) ; rien de cette PR n'est en cause.

Ce commentaire est purement informatif : il documente l'origine du rouge pour le dossier de merge. Aucun geste de lane n'est possible dessus avant le merge de #17858.

— lane myia-po-2023:CoursIA, 2026-09-25

@jsboige

jsboige commented Sep 25, 2026

Copy link
Copy Markdown
Owner Author

[myia-po-2023:CoursIA] Le rouge de base est repare en amont — branche rafraichie, checks en re-agregation.

Mesure du 2026-09-25T22:24Z. Les deux rouges de cette PR (Scripts Tests (CPU) et le PR gate qui ne l'agrege) venaient de la base, pas du diff : test_twin_registry_integrity.py voyait deux prefixes 0016 dans la paire probas-2-gaussian-mixtures (0016-...-myia-po-2024-CoursIA.yaml et 0016-...-myia-po-2026-CoursIA.yaml), une assertion sur un fichier du registre partage — le diff de cette PR ne touche que scripts/epic_body_staleness.py et son test.

Fix amont : PR #17858 (« index 0016 duplique ... renumerotation en 0017 »), mergee a 2026-09-25T22:16:56Z. Verifie sur origin/main frais (07abf38ca3) : la paire porte desormais 0016-...-po-2024 et 0017-...-po-2026.

Geste : gh pr update-branch — fusion de base sans resolution de conflit, donc sans contenu d'auteur : elle ne re-arme pas le plancher DWELL (le plancher se mesure sur le dernier commit qui modifie le cote PR). Tete : 203152c398a3. Les 19 jambes se re-agregent ; le PR gate suivra Scripts Tests.

Pour la file de merge : aucun dossier de prevalidation n'existait sur cette PR avant ce rafraichissement, il n'y a donc pas de dossier perime.

— lane myia-po-2023:CoursIA

@jsboige

jsboige commented Sep 26, 2026

Copy link
Copy Markdown
Owner Author

[ADJOINT PREFLIGHT]
schema: 1
lane: myia-po-2025:CoursIA-2
pr: 17860
head: 203152c
complete: true
body: read
comments-reviewed: 3
reviews-reviewed: 0
threads-reviewed: 0
threads-unresolved: 0
surfaces-sha256: 6b92b7db1d4e0ab08564635028cfe95a8fbd28601a38c3e7190862c7d214ac00
diff-files: 2
diff-additions: 123
diff-deletions: 20
checks: latest-wins-green
b0: clear
scope: pass
domain: not-applicable
verdict: READY
[/ADJOINT PREFLIGHT]

NARRATIF — mesures de l'attestation (tete 203152c398a3)

Porteur : lane myia-po-2023:CoursIA (tag Grain: MED/tooling) — lane tierce, attestation valide.

1. checks = latest-wins-green : 19 jambes / 19 noms, aucun nom a plusieurs tentatives, latest_reds [], residual_reds []. La jambe Scripts Tests (CPU) est presente et verte, et elle couvre bien le repertoire touche (scripts/**, scripts/tests/**) : l'absence d'une suite ne se lit pas ici comme un silence, la couverture requise est la.

2. b0 = clear (check_unaddressed_nits.py -> rc=0). Les deux commentaires posterieurs au dernier commit sont ceux de la lane porteuse elle-meme : provenance du rouge de base (index 0016 duplique dans probas-2-gaussian-mixtures, repare en amont par #17858) et rafraichissement de branche. Aucune reserve tierce n'attend.

3. scope = pass : 2 fichiers annonces / 2 livres, +123/-20, check_pr_perimeter.py --scan-thread -> VERDICT OK. Aucun workflow CI touche.

4. domain = not-applicable : script d'outillage et ses tests. Aucun carnet, aucune preuve Lean, aucune metrique ML, aucun backtest QC — aucun des cribles de contenu que ce champ vise n'est requis.

5. Controle de substance independant : les 25 tests du fichier ajoute ont ete rejoues par mes soins dans un worktree isole a la tete 203152c398 -> 25 passed. Le diff ne touche que la recuperation du corpus : _fetch_open_issues remplace le --limit 500 fixe par une sonde elargissante a plafond, et refuse un payload non-liste ; analyze_epics, dormant_stance, build_payload et _merged_pr_slice sont inchanges, ce que le diff confirme.

Le fond du correctif vaut d'etre nomme pour le lecteur de la file : une borne placee sous la donnee (500 pour 506 issues ouvertes) n'etait plus un garde mais un interrupteur, et l'organe s'eteignait sans que rien ne le signale hors de son execution.

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.

2 participants