Skip to content

tooling(labellisation): candidate_delivered muet sur 2 classes — la forme de titre type(scope,#N): du depot, et les issues rendues active par le [INFO] du protocole #17759

Description

@jsboige

Le probleme, mesure

L'organe scripts/candidate_delivered.py (+ .github/workflows/candidate-delivered-advisory.yml) existe pour labelliser candidate-delivered les issues livrees mais non fermees. Deux mecanismes le rendent structurellement muet sur des classes entieres — mesures firsthand le 2026-09-25, sur origin/main.

Consequence en bout de chaine : le pool reste encombre, le picker epuise ses 16 sondes sur des candidats morts, et les lanes brulent un cycle a trier des issues livrees.

Mesure d'entree (lane myia-po-2024:CoursIA, tirage reel du 2026-09-25) : sur 6 candidats offerts, 5 etaient livres-mais-ouverts et 0 sur 5 portait le label. Le label est le canal gratuit ; le commentaire est le canal couteux (1 requete/issue, borne a 16/tirage). Toute issue que le label rate coute une sonde.


Mecanisme A — la forme canonique du depot est invisible : delivery_marker ne lit QUE le body

_delivery_marker_re (scripts/candidate_delivered.py l.119) cherche le marqueur dans le body seul :

r"\b(?:see|part\s+of|closes|fixes|refs|references?)\b\s*:?\s*#%d\b" % number

Or la convention mandatee par ce depot (git-workflow.md, CLAUDE.md section A) ecrit la reference dans le titre : type(scope,#N): description. Cette forme n'est jamais lue. Le workflow l'assume (l.47 : « in its CURRENT body »).

Preuve firsthand — PR #17147, MERGED, titre :

fix(#17143): demote markdown asides by BLOCK, not by line (+ detector)

delivery_marker(body_de_17147, 17143) rend False. Le body ne dit jamais See #17143 ; il contient seulement « je le signale sur l'issue #17143 pour arbitrage ». L'issue #17143 est restee OPEN, sans label (labels : bug), et le picker l'a servie comme grain le 2026-09-25.

Ampleur mesuree (echantillon : 250 PRs mergees, 494 issues ouvertes) — issues ouvertes, non labellisees, dont la PR de livraison declare #N dans le titre seul :

issue PR labels
#17476 #17722 —
#17713 #17715 —
#17526 #17639 —
#16737 #17546 —
#17496 #17519 —
#16756 #17495 —
#17445 #17491 bug
#17464 #17471 —
#17424 #17426 —
#17420 #17423 —
#17323 #17346 —
#17083 #17312 —
#13749 #17311 —
#14773 #17294 enhancement
#17276 #17280 —
#16883 #17271 —
#8889 #17261 —
#17248 #17249 —
#15604 #17244 enhancement

19 issues sur un echantillon de 250 PRs (~7,6 %), et c'est une borne inferieure : le relevé n'a retenu que les titres matchant [\(,]#N.

Ce n'est PAS un simple « il suffit d'ouvrir le canal titre ». Le precedent #15060 est precisement le motif pour lequel le canal a ete restreint au body : une mention contextuelle dans un titre aurait labellise une issue non livree (#15200, « verified by dispatch #15060 »). Ouvrir le titre tel quel ressusciterait cette classe de faux positifs. La question — « un type(scope,#N): de titre est-il une declaration de livraison, une mention contextuelle, ou les deux selon la forme ? » — est un arbitrage, pas une evidence de code.


Mecanisme B — la condition de silence est detruite par le remede que le protocole prescrit

La classification pose le label seulement si aucun commentaire n'est arrive apres le merge (l.243-247) :

comment_dates = [c["created_at"] for c in (issue.get("comments") or []) ...]
last_activity = max(comment_dates + [issue.get("created_at", "")])
if last_activity > latest_merge:
    return ("active", f"issue active after merge ({last_activity} > {latest_merge})")

Or le protocole ordonne a une lane qui rencontre une candidate-delivered de poster [INFO] candidate-delivered (« un worker qui rencontre une candidate-delivered poste [INFO] candidate-delivered avec sa preuve et rend la main », proactive-coordination.md R5, cite dans pick_idle_grain.py l.988). Ce commentaire est precisement ce qui invalide la precondition du labelliseur.

La boucle se referme sur elle-meme :

pas de label -> la lane est obligee de commenter -> le commentaire rend l'issue active -> pas de label.

Preuve firsthand — PR #15858 vs issue #15689. delivery_marker(body, 15689) rend True (le body porte bien See #15689), la PR est MERGED depuis le 2026-09-14 — et l'issue n'a toujours pas le label (labels : bug, slides). Elle porte en revanche deux [INFO] candidate-delivered postes par des lanes (2026-09-15, 2026-09-21), donc last_activity > latest_merge -> active -> aucun label. Le picker l'a servie comme grain le 2026-09-25 (re-triage firsthand de ma part), et une autre lane l'avait deja signalee livree le 2026-09-22 sur #16589.

Le meme effet frappe #15476 (deux [INFO] candidate-delivered dont un [ADJOINT VERIFIED]) : livree sur main le 2026-09-11, jamais labellisee.

Nuance, et elle compte : la condition de silence n'est pas arbitraire — human_retraction (l.145+) s'appuie sur l'activite pour retracter un label quand un humain contredit la livraison. La seule voie evidente — ignorer les commentaires portant le marqueur [INFO] candidate-delivered dans le calcul de last_activity — est donc elle aussi un arbitrage (le marqueur peut etre poste a tort, et il est aujourd'hui auto-declare).


Ce qui est demandé

1. Arbitrage ai-01 sur les deux mecanismes. Aucun des deux n'est un simple oubli de code : A rouvrirait la classe de faux positifs #15060, B touche le mecanisme de retractation. Je ne tranche pas a la place du coordinateur, et je ne propose pas de PR avant son verdict — c'est le schema qui a fonctionne sur #16589 (mesure -> arbitrage -> greenlight -> PR).

2. En revanche, une partie est strictement additive et sans arbitrage : rendre la classe AVEUGLE visible. L'organe compte aujourd'hui un titre-seul comme no_delivery (l.238 : « no See/Part of/Closes/Fixes marker in their body -- not a delivery ») en silence. C'est exactement la lecon que le picker a lui-meme apprise (#16608 : !! ... NON SONDE ... la fin de l'urne n'a pas ete verifiee, plutot que de laisser croire a une urne propre).

Proposition d'acceptance minimale, qui ne change aucune decision de labellisation :

Cette partie ne peut pas produire de faux positif : elle ne pose aucun label, elle nomme une population.

3. Point de rendez-vous unique. Je poste ici plutot que d'ouvrir un doublon de #16589 ou de #17038 : #16589 porte le volet picker (fusil livre par #16608), #17038 porte le transport, celle-ci porte la labellisation. Les trois sont des maillons de la meme chaine et se renvoient l'un a l'autre.

Reproduction

# Mecanisme A -- la forme de titre du depot n'est pas vue par le marqueur
python -c "
import json,subprocess,sys; sys.path.insert(0,'scripts')
from candidate_delivered import delivery_marker
b=json.loads(subprocess.run(['gh','pr','view','17147','--repo','jsboige/CoursIA',
  '--json','body'],capture_output=True,text=True,encoding='utf-8').stdout)['body']
print('titre : fix(#17143): ... | delivery_marker(body,17143) =', delivery_marker(b,17143))
"
# attendu : False, alors que la PR est MERGED et le titre declare l'issue

# Mecanisme B -- livree + labellisable, mais rendue `active` par un [INFO]
gh issue view 15689 --repo jsboige/CoursIA --json labels,comments --jq \
  '{labels:[.labels[].name], commentaires:[.comments[].body[0:60]]}'
# attendu : pas de label `candidate-delivered`, et des commentaires [INFO] posterieurs au merge

Portee de ce que je n'ai PAS verifie

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions