diff --git a/scripts/check_unaddressed_nits.py b/scripts/check_unaddressed_nits.py index b29c6c30cf..7035ed8ff5 100644 --- a/scripts/check_unaddressed_nits.py +++ b/scripts/check_unaddressed_nits.py @@ -4542,11 +4542,41 @@ def _lift_eligible(lift_author: str, nit_author: str, # Voie 3 leve donc si le lift est voie nue (pas de prefixe # distinct), independamment du scope du nit (la garde # persona-vs-user est deja portee par voie 1). + # + # #17507 -- exception : sous `jsboige` (login partage), une + # levee voix nue NE leve PAS une reserve portant un marqueur + # persona. Voie 1 suppose lift_has_persona=True pour traiter + # le scope persona, mais un lift voie nue par une lane cross- + # poussee sous `jsboige` n'a pas voix sur la reserve `[Hermes]` + # d'une autre lane. La levee d'une reserve persona sous + # `jsboige` exige alors voie 1 (lift persona), override + # coordinateur nomme, ou re-review. Le discriminant reste sur + # le LOGIN : sous `clusterManager-Myia` (persona authentique), + # la voie 3 preserve son ancien comportement -- cf. + # `test_auteur_du_nit_leve_son_nit` (clusterManager-Myia leve + # SA reserve `[Hermes]` en voix nue, voie nue OK). + # + # Exception preservee : si le lift voie nue sous `jsboige` + # MENTIONNE explicitement sa propre reserve par un objet de + # close-the-loop (`mon concern`, `ma reserve`, `ma review`, + # `mon review`), c'est le self-close-the-loop legitime de + # l'auteur de la reserve persona sur sa propre review (cf. + # `test_12944_close_the_loop_leve_la_review_precedente` : + # Hermes self-bot `jsboige` ferme sa review REQUEST_CHANGES + # `[Hermes]` en voix nue "Mon concern est traite et ferme"). + # Sans cette exception, voie 3 deviendrait incapable de + # fermer une review persona posee par le self-bot lui-meme. stripped_lift_role = stripped_lift lift_has_role = bool(_ROLE_PREFIX_RE.search(stripped_lift_role)) + lift_self_closes_persona = bool( + re.search(r"(?i)\b(?:mon concern|ma reserve|ma review|mon review)\b", + stripped_lift_role)) if (lift_has_persona is False and lift_has_lane is False and lift_has_role is False + and (lift_author != "jsboige" + or nit_has_persona is False + or lift_self_closes_persona) and has_live_lift(lift_body or "")): return True # Voie nue par meme login, sans discriminant de ROLE ni diff --git a/scripts/tests/test_check_unaddressed_nits.py b/scripts/tests/test_check_unaddressed_nits.py index 9ce3217f60..d0c16bd5a2 100644 --- a/scripts/tests/test_check_unaddressed_nits.py +++ b/scripts/tests/test_check_unaddressed_nits.py @@ -3097,6 +3097,66 @@ def test_13316_self_lift_jsboige_sur_sa_propre_reserve_leve(): assert run([own_nit, own_lift])["blocked"] is False +# --- #17507 : voie 3 (lift voix nue par meme login) leveait une reserve +# persona sous `jsboige` (login partage) sans la nommer -- Voie 1 suppose +# lift_has_persona=True pour traiter le scope persona, mais le cas +# `not lift_has_persona and nit_has_persona` n'etait pas couvert. Reproduction : +# PR #16924, review 5258813293 `[Hermes]` (jsboige, 20/09) + commentaire adjoint +# voix nue (jsboige, myia-po-2025:CoursIA-2) `Je leve mon point du commentaire +# 5788054957.` -- avant fix, la reserve Hermes etait eteinte par un lift voix +# nue qui ne la nomme pas. + + +def test_17507_voix_nue_jsboige_ne_leve_pas_reserve_persona(): + """Critere 1 #17507 (positif) : sous `jsboige`, reserve `[Hermes]` suivie + d'une levee voix nue qui nomme un AUTRE commentaire -- la reserve Hermes + RESTE bloquante (voie 3 bloquee par `nit_has_persona`).""" + hermes_review = { + "author": {"login": "jsboige"}, "state": "COMMENTED", + "submittedAt": at(10), + "body": "[Hermes] COMMENT_WITH_CONCERNS -- la voie 3 leve sans " + "discriminer le scope de la reserve.", + } + adjoint_voice_lift = { + "author": {"login": "jsboige"}, "createdAt": at(12), + "body": "Je leve mon point du commentaire 5788054957.", + } + data = { + "number": 16924, "title": "t", "author": {"login": "jsboige"}, + "comments": [adjoint_voice_lift], "reviews": [hermes_review], + "commits": [{"committedDate": at(20)}], + } + result = mod.analyse(data, [], MERGED) + assert result["blocked"] is True, ( + f"attendu: reserve Hermes bloquee sous jsboige par lift voix nue ; " + f"observe: {result.get('blocking')}") + assert any("[Hermes]" in b.get("excerpt", "") + for b in result["blocking"]) + + +def test_17507_voix_nue_jsboige_leve_reserve_voix_nue_user(): + """Critere 2 #17507 (negatif preserve) : sous `jsboige`, reserve voix nue + user suivie d'une levee voix nue par meme login -- la voie 3 PRESERVE son + ancien comportement (lift voie nue OK sur reserve voie nue).""" + user_nit = {"author": {"login": "jsboige"}, "createdAt": at(10), + "body": "Concern: la voie 3 leve sans scope reserve."} + voice_lift = {"author": {"login": "jsboige"}, "createdAt": at(12), + "body": "Reserve levee apres correction du scope."} + assert run([user_nit, voice_lift])["blocked"] is False + + +def test_17507_voix_nue_clusterManager_leve_sa_reserve_persona(): + """Critere 3 #17507 (anti-regression #14850) : sous `clusterManager-Myia` + (persona authentique, PAS login partage), la voie 3 preserve son ancien + comportement -- clusterManager-Myia leve SA reserve `[Hermes]` par voix + nue. La garde `lift_author == "jsboige"` ne mord pas ici.""" + assert run([HERMES_NIT, { + "author": {"login": "clusterManager-Myia"}, + "createdAt": at(12), + "body": "Reserve levee.", + }])["blocked"] is False + + def test_13316_replay_12737_reel(): """Critère 3 : replay du cas réel #12737 (timestamps réels) — réserve myia-ai-01 02:37:04Z, « overrides » jsboige 02:40:01Z et 02:41:06Z : la