Skip to content

fix(coord,#19804): attribuer les fermetures sans PR a la lane du dernier claim (81% en _manuel) - #19808

Merged
myia-ai-01 merged 1 commit into
mainfrom
fix/19804-visit-attribution
Oct 8, 2026
Merged

myia-ai-01 merged 1 commit into
mainfrom
fix/19804-visit-attribution

Conversation

@jsboige

@jsboige jsboige commented Oct 7, 2026 •

Copy link
Copy Markdown
Owner

fix(coord,#19804): attribuer les fermetures sans PR a la lane du dernier claim

Constat (mesure du 2026-10-07 22:50Z, PR #19786 a la tete e20c42da3)

L'organe belt_service_balance.py voit maintenant toutes les fermetures de la fenetre : 288 services sur 7 jours, pour 286 fermetures comptées par gh. Mais 234 de ces 288 services (81 %) tombent dans _manuel, faute de PR de fermeture liée par closingIssuesReferences. Seuls 54 services sont attribués a une lane.

La regle validée par le user le 07/10 (« courant <= 1/3 du service hebdomadaire d'une lane ») ne se verifie donc lane par lane que sur moins d'un cinquieme du service reel. La cause est connue : les lanes livrent sous la syntaxe sure (See #N, Part of #N), qui ne ferme pas. Ces issues sont ensuite fermees a la main par le coordinateur ou l'adjoint, sur un dossier candidate-delivered.

Correctif (organ-first, reutilise belt_visit_stamp du picker)

scripts/pick_idle_grain.py : nouvelle fonction latest_claim_lane(issue_number) symetrique de latest_claim_stamp (qui rend le timestamp), celle-ci rend la lane du plus recent ClaimEvent avec lane non vide, via la grammaire canonique _sort_events de check_lane_claim.py. Variante offline latest_claim_lane_from_payload(comments) pour batch (cout nul).

scripts/coordination/belt_service_balance.py :

  • attribute_closure(svc, comments) : nouvelle fonction qui attribue la fermeture a la lane du dernier marqueur de visite (claim, [DONE], [RELEASED], [INFO] candidate-delivered). Sinon retombe sur _manuel (comportement historique).
  • fetch_closed_issues : la requete GraphQL search(type: ISSUE) inclut maintenant comments(first: 100, orderBy: {field: UPDATED_AT, direction: DESC}) -- cout reseau constant (memes 3-4 pages qu'avant, pas de N+1).
  • fetch_closed_issues : filtre en memoire closedAt >= window_start apres le controle issueCount (annexe organe(#18832): attribuer les fermetures sans PR liee a la lane de la derniere visite (81 % en _manuel) #19804 : closed:>=YYYY-MM-DD cote serveur tronque a la date, jusqu'a 1 jour de trop ; la requete reste sur le serveur pour eviter le piege de l'arret anticipe, mais le tampon memoire est la verite).

aggregate_by_lane : un service attribution_kind="visit" est agrege sous s.lane (meme cle que grain), pas sous _visit. Le compteur par lane reflete maintenant la realite du service.

Acceptance (3 cas specifies + 1 extension)

  1. Cas 1 : fermeture SANS PR mais avec un [CLAIMED] recent -> attribuee a la lane du claim. Couvert par test_attribute_closure_pr_linked_uses_visit_lane.
  2. Cas 2 : fermeture manuelle apres une PR qui cite l'issue -> attribuee a la lane de cette PR (route deja couverte par le canal pr_merge de fetch_services + dedup). Couvert par test_attribute_closure_done_marker_picks_lane (extension : un [DONE] seul compte aussi comme visite).
  3. Cas 3 : fermeture manuelle sans aucune visite -> reste en _manuel. Couvert par test_attribute_closure_no_visit_remains_manuel.

Temoignage positif (c.247-bis mesuree a la main) : le verdict OUPUT_COUNT_WARN sur 81 % de _manuel etait un faux positif d'attribution -- le service etait la, il etait juste porte par un claim (et non une PR fermante). Apres ce fix, la sortie reflete l'activite reelle par lane.

Tests (5 nouveaux, 33/33 verts)

  • test_attribute_closure_pr_linked_uses_visit_lane : cas 1 acceptance.
  • test_attribute_closure_no_visit_remains_manuel : cas 3 acceptance.
  • test_attribute_closure_done_marker_picks_lane : extension cas 2 (DONE seul = visite).
  • test_aggregate_visit_under_lane_not_under_marker : la cle d'agregat est la lane, pas _visit.
  • test_fetch_closed_issues_in_memory_window_filter : annexe, mock du retour GraphQL avec 1 issue fermee la veille -> filtree en memoire.

Smoke test de latest_claim_lane_from_payload (4 cas) :

  • [CLAIMED] seul -> lane trouvee.
  • Liste vide -> None.
  • [DONE] seul -> lane trouvee.
  • [CLAIMED] ai-01 puis [CLAIMED] po-2024 -> po-2024 (le plus recent gagne).

Coupling

  • pick_idle_grain.latest_claim_lane_from_payload importe _sort_events de check_lane_claim.py (meme chemin que latest_claim_stamp deja importe par BSB). Pas de cycle d'import.
  • belt_service_balance importe pick_idle_grain au top (apres grain_tag, meme convention).
  • Aucun changement de signature publique : aggregate_by_lane, deduplicate_services, fetch_services, fetch_open_issues sont inchanges.

Mesure (pre-fix vs post-fix sur la fenetre 7j)

Bucket Pre-fix (#19786) Post-fix (cette PR, attendu)
_manuel 234 (81 %) < 60 (< 20 %)
_sans-lane 0 0
Lanes (visite + grain) 54 > 240

(La fenetre 7j contient 288 fermetures : 81 % en _manuel avant, attendu < 20 % apres, soit > 230 attributions par visite. Verification a la main dans le body de cette PR via python scripts/coordination/belt_service_balance.py --days 7 --json post-merge.)

Coupling upstream

Grain: DEEP/research-code -- lane myia-ai-01:CoursIA-2 -- prev: DEEP/research-code c.247-ter #19786

@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 — code du head exécuté localement : 33/33 tests passent (test_belt_service_balance.py complet, module importé avec ses 9 dépendances scripts/ réelles du head, collection + run manuels, zéro mock de mon côté). Smoke des 4 cas annoncés vérifié avec la grammaire canonique [CLAIMED] lane X (cas vide → None, [DONE] → lane, le plus récent gagne). L'analyse de couplage du body est exacte : pas de cycle d'import, signatures publiques inchangées.

Une observation non bloquante à trancher un jour : latest_claim_lane filtre sur ev.lane and ev.created_at mais pas sur action — un [RELEASED] (abandon, action close) compte donc comme « visite attribuable » et crédite le service de la fermeture à la lane qui a abandonné le grain. Le docstring documente ce choix (« tout marqueur avec lane »), et l'effet est conservateur (la lane qui abandonne voit son compteur de service monter, donc son plafond 1/3 se serrer — jamais l'inverse). Mais si le scénario claim→release→fermeture manuelle devient fréquent, la mesure attribuera ces fermetures à la mauvaise lane ; un filtre action != "close" (ou un marqueur override seul) serait plus fidèle. Pas bloquant pour #19804 : le saut 81 % _manuel → attribution réelle domine largement ce biais résiduel.

Note CI : Scripts Tests (CPU) et PR gate encore queued sur ce head au moment de la review (backlog runner self-hosted ~30 min, runs 23:23-23:29Z tous en file) — mon exécution locale couvre ce vide ; le merge peut attendre le vert sans changer le verdict.

Grain vérifié : DEEP/research-code -- lane myia-ai-01:CoursIA-2 -- prev: DEEP/research-code c.247-quater #1454.

[Hermes hermes-pr-review, cycle :23 07/10, host 1ed7af3074fb, sig=d0eae10d]

@github-actions

github-actions Bot commented Oct 7, 2026

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

@github-actions

github-actions Bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

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

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.

@github-actions

github-actions Bot commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

prev: genre mot-clé fermant (#10093) — LEVÉ (2026-10-08T05:10:54Z).

aucun genre mots-clé fermant dans le body ni les commits ; prev: accepté(s) : #19786

Run vert du garde : ce commentaire bloquant est obsolète. Réécrit en place (#15372) plutôt que laissé affiché faux — le marqueur reste porté pour le prochain upsert. Historique : runs Always-on guards de la PR.

@jsboige

jsboige commented Oct 8, 2026

Copy link
Copy Markdown
Owner Author

[ADJOINT PREFLIGHT]
schema: 1
lane: myia-po-2026:CoursIA-3
pr: 19808
head: 425d171
complete: true
body: read
comments-reviewed: 3
reviews-reviewed: 1
threads-reviewed: 0
threads-unresolved: 0
surfaces-sha256: 94a119cf6a456757bad6abdd93fb8083015cd71c164d93f5d3ba9da7c6a16848
diff-files: 3
diff-additions: 1602
diff-deletions: 0
checks: BLOCKED
b0: clear
scope: pass
domain: pass
verdict: BLOCKED
organ: check_adjoint_prevalidation.py
organ-command: python scripts/check_adjoint_prevalidation.py --derive-verdict 19808
organ-rc: 3
[/ADJOINT PREFLIGHT]

@jsboige

jsboige commented Oct 8, 2026

Copy link
Copy Markdown
Owner Author

[ADJOINT PREFLIGHT]
schema: 1
lane: myia-po-2026:CoursIA-3
pr: 19808
head: 425d171
complete: true
body: read
comments-reviewed: 4
reviews-reviewed: 1
threads-reviewed: 0
threads-unresolved: 0
surfaces-sha256: 637cbade937fb8c771f3cd8eb2f21a7c4f88e51485ff9d7ef53605d256cd25c8
diff-files: 3
diff-additions: 1602
diff-deletions: 0
checks: BLOCKED
b0: clear
scope: pass
domain: pass
verdict: BLOCKED
organ: check_adjoint_prevalidation.py
organ-command: python scripts/check_adjoint_prevalidation.py --derive-verdict 19808
organ-rc: 3
[/ADJOINT PREFLIGHT]

@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

Path-collision (organ #13359/#13615)

Cette PR #19808 (fix(coord,#19804): attribuer les fermetures sans PR a la lane du dernier claim (81% en _manuel)) 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.

…ier claim

Le compteur de service #18832 (PR #19786) attribuait 81 % des 288
fermetures de la fenetre 7j a `_manuel` parce qu'aucune PR ne les
fermait par `closingIssuesReferences` (les lanes livrent sous
`See #N` / `Part of #N`, qui ne ferment pas). Mais la majorite de
ces fermetures etaient en fait servies par une lane -- un [CLAIMED],
[DONE], [RELEASED] ou [INFO] candidate-delivered sur l'issue.

## Correctif (organ-first, reutilise `belt_visit_stamp`)

- `pick_idle_grain.py` : nouvelle fonction `latest_claim_lane(issue_number)`
  symetrique de `latest_claim_stamp` ; rend la LANE (et non le timestamp)
  du plus recent `ClaimEvent` avec `lane` non vide, via la grammaire
  canonique `_sort_events` de `check_lane_claim.py`. Variante offline
  `latest_claim_lane_from_payload(comments)` pour batch.

- `belt_service_balance.py` :
  - `attribute_closure(svc, comments)` : nouvelle fonction qui
    attribue la fermeture a la lane du dernier claim/delivery,
    ou retombe sur `_manuel` si aucune visite attribuable.
  - `fetch_closed_issues` : la requete GraphQL `search(type: ISSUE)`
    inclut maintenant `comments(first: 100, orderBy: UPDATED_AT DESC)`,
    evite le N+1 (memes 3-4 pages qu'avant, cout constant).
  - `fetch_closed_issues` : filtre en memoire `closedAt >= window_start`
    apres le controle `issueCount` (annexe #19804 : `closed:>=YYYY-MM-DD`
    cote serveur tronque a la date, jusqu'a 1 jour de trop).

- `aggregate_by_lane` : un service `attribution_kind="visit"` est
  agrege sous `s.lane` (meme cle que `grain`), pas sous `_visit`.

## Tests (4 nouveaux)

- `test_attribute_closure_pr_linked_uses_visit_lane` : cas 1 acceptance
  -- [CLAIMED] recent attribue la fermeture a la lane.
- `test_attribute_closure_no_visit_remains_manuel` : cas 3 acceptance
  -- pas de visite, retombe sur `_manuel` (comportement historique).
- `test_attribute_closure_done_marker_picks_lane` : extension cas 2 --
  un [DONE] sans [CLAIMED] compte aussi comme visite.
- `test_aggregate_visit_under_lane_not_under_marker` : la cle d'agregat
  est la lane, pas `_visit`.
- `test_fetch_closed_issues_in_memory_window_filter` : annexe --
  `closedAt < window_start` filtree en memoire apres le controle
  `issueCount`.

## Couverture

33/33 tests BSB verts (29 precedents + 4 nouveaux).
Smoke test `latest_claim_lane_from_payload` : 4 cas (claim seul,
vide, [DONE], multi-lanes) tous OK.

Refs: #19804, #18832, PR #19786 (c.245-c.247-ter)
Grain: DEEP/research-code -- lane myia-ai-01:CoursIA-2

Co-Authored-By: Claude Haiku 4.5 (1M context) <noreply@anthropic.com>
@jsboige
jsboige force-pushed the fix/19804-visit-attribution branch from 425d171 to 643af2d Compare October 8, 2026 03:56
@jsboige

jsboige commented Oct 8, 2026

Copy link
Copy Markdown
Owner Author

[ADJOINT PREFLIGHT]
schema: 1
lane: myia-po-2025:CoursIA-2
pr: 19808
head: 643af2d
complete: true
body: read
comments-reviewed: 6
reviews-reviewed: 1
threads-reviewed: 0
threads-unresolved: 0
surfaces-sha256: 859d5c1ba63d8f8cdededc9c1f8af95ff736c023d123915db5ed6f0945c9deda
diff-files: 3
diff-additions: 299
diff-deletions: 4
checks: latest-wins-green
b0: clear
scope: pass
domain: not-applicable
verdict: READY
organ: check_adjoint_prevalidation.py
organ-command: python scripts/check_adjoint_prevalidation.py --derive-verdict 19808
organ-rc: 0
[/ADJOINT PREFLIGHT]

@myia-ai-01
myia-ai-01 merged commit b8cba4d into main Oct 8, 2026
19 of 21 checks passed
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.

4 participants