Skip to content

feat(guard,#18725): merge_ready 5bis -- verdict ON-MAIN/MULTI-PR/CLEAN - #18762

Merged
myia-ai-01 merged 2 commits into
mainfrom
feature/18725-merge-ready-twin-verdict
Oct 2, 2026
Merged

myia-ai-01 merged 2 commits into
mainfrom
feature/18725-merge-ready-twin-verdict

Conversation

@jsboige

@jsboige jsboige commented Oct 1, 2026

Copy link
Copy Markdown
Owner

Grain: MED/guard -- lane myia-po-2024:CoursIA-2 -- prev: LIGHT/guard #18688

feat(guard,#18725): merge_ready 5bis -- verdict ON-MAIN/MULTI-PR/CLEAN

Suite a l'issue #18725 ("merge_ready : visibilite verdict twin avant merge"),
l'etape 5bis du gate merge_ready distingue les trois cas de collision
d'index twin-pairs et agit en consequence. La regle de #18725 est

Acceptance :

  • merge_ready.py 5bis invoque check_twin_index_collisions.py sur la tete de la candidate
  • Verdict ON-MAIN => merge decline (avec message human lisible)
  • Verdict MULTI-PR => avertissement seul (la premiere mergee gagne)
  • Verdict CLEAN => merge continue

Organe source : PR #18688 (worktree D:/Dev/CoursIA-c1364-18688,
db86b94d96) a ajoute classify_ordinal_collisions(cross_ref, *, base_ref)

Comportement avant / apres

Avant (origin/main) : toute collision = twin-index-collision (skip dur).
Le merge_ready ne distinguait pas entre « la base porte deja l'index »
(refus legitime : un merge ferait rougir main) et « deux PRs en
concurrence » (concurrence de lanes, la premiere mergee gagne).

Apres :

  • ON-MAIN -- la base porte l'index et une tete le RE-porte. Skip dur
    avec motif twin-collision-on-main:<pair>/<index> et message human
    lisible (renumeroter la tete vers le premier index libre de '<pair>' par git mv PUR, puis committer). Le merge rendrait le garde CI
    test_audit_index_unique_and_no_identical_duplicates_per_pair
    DRIFT-INTRO sur une autre PR qui n'a rien demande.
  • MULTI-PR -- la base NE porte PAS l'index, mais 2+ tetes le
    portent. Pas de skip, juste un avertissement : la premiere mergee
    gagne, la seconde renumerote apres merge.
  • CLEAN -- rc=0, pas de collision, merge continue (comportement
    inchange).

Codification

if res.returncode == 0:
    return None
if res.returncode == 1:
    # Collision : on classe par verdict
    data = json.loads(res.stdout)
    cross_ref = data.get("cross_ref") or []
    # Verdict absent = organe anterieur a #18683 / #18725
    # Fail-closed : on garde le motif historique jusqu'a upgrade
    if not any(c.get("verdict") in ("ON-MAIN", "MULTI-PR")
               for c in cross_ref):
        return "twin-index-collision"
    on_main = [c for c in cross_ref if c.get("verdict") == "ON-MAIN"]
    if not on_main:
        return None  # MULTI-PR
    # ON-MAIN : skip dur + message human lisible
    first = on_main[0]
    return (f"twin-collision-on-main:{first['pair']}/{first['index']} -- "
            "renumeroter la tete vers le premier index libre de '<pair>' "
            "(max sur base + 1) par `git mv` PUR, puis committer.")
return f"twin-collision-unreadable:rc={res.returncode}"

Notes :

  • L'organe est appele avec --json (ligne 815 de merge_ready.py)
    pour obtenir la classification.
  • La sortie est passee par _json_stdout (helper existant) plutot
    qu'un json.loads local, pour la coherence avec run_gate /
    mergeable_state_and_head.
  • Cas organe sans verdict = organe anterieur a registre twin : numéros d'attestation en collision entre PRs concurrentes, aucune garde au merge (récidive #16769) #18683. On NE peut
    pas distinguer ON-MAIN/MULTI-PR. Fail-closed = motif historique
    twin-index-collision (refus), comme avant le cablage.
  • Cas MIXED (plusieurs collisions dont 1 ON-MAIN) : skip dur, on
    nomme la PREMIERE ON-MAIN dans le motif.

Validation

8 scenarios unitaires (mock Runner + sorties JSON fixture) :

[OK] no_touch -- PR hors registre twin, pas de skip
[OK] CLEAN
[OK] MULTI-PR
[OK] ON-MAIN -- skip dur avec motif twin-collision-on-main:foo/0001
[OK] MIXED -- ON-MAIN gagne, MULTI-PR ignoree
[OK] unreadable -- skip fail-closed rc=2
[OK] legacy -- rc=1 sans verdict -> motif historique (retro-compat)
[OK] fetch_failed -- skip fail-closed

Suites integrees :

  • python -m pytest scripts/tests/test_merge_ready.py scripts/tests/test_check_adjoint_prevalidation.py -- 184/184 OK
  • python -m pytest scripts/notebook_tools/tests/test_twin_index_collisions.py scripts/notebook_tools/tests/test_check_twin_parity.py -- 56/56 OK
  • python scripts/notebook_tools/check_twin_index_collisions.py --base origin/main --worktree --json -- sortie conforme (cross_ref vide, base_ref=origin/main)

Fichiers touches

Le submodule MyIA.AI.Notebooks/SymbolicAI/Argument_Analysis/Argumentum
est suivi par main (commit upstream 9e79e88d) et n'est pas touche par
cette PR (sortie de git status = submodule pointer upstream seulement).

Reference

Co-Authored-By: Claude Haiku 4.5 (1M context) noreply@anthropic.com

L'etape 5bis du gate merge_ready distingue les trois cas de collision
d'index twin-pairs : ON-MAIN (skip dur, message human lisible),
MULTI-PR (avertissement seul, la premiere mergee gagne), CLEAN (rc=0).

Cas organe anterieur a #18683 (pas de verdict expose) : fail-closed,
on garde le motif historique `twin-index-collision`.

Validation :
- 8/8 scenarios unitaires (mock Runner + JSON fixtures)
- 184/184 pytest test_merge_ready + test_check_adjoint_prevalidation
- 56/56 pytest test_twin_index_collisions + test_check_twin_parity

Issue #18725 -- acceptance 4 points OK.

Co-Authored-By: Claude Haiku 4.5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 1, 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 2, 2026 •

Copy link
Copy Markdown
Contributor

Path-collision (organ #13359) — résolue

La collision de chemins signalée sur #18762 n'existe plus au passage du 2026-10-02T16:45Z : aucune autre PR ouverte ne partage désormais de chemin de fichier avec elle. Note laissée en place de l'avertissement (retraction non destructive).

@jsboige

jsboige commented Oct 2, 2026

Copy link
Copy Markdown
Owner Author

[ADJOINT VERIFIED] À la tête 4ac0762, le câblage 5bis couvre les cas CLEAN, ON-MAIN, MULTI-PR et le cas legacy entièrement sans verdict. Reste un cas de lecture partielle qui contredit la promesse fail-closed du body : twin_collision_reason accepte la liste dès que any(c.verdict in (ON-MAIN, MULTI-PR)), puis retourne None si elle ne contient aucun ON-MAIN. Pour cross_ref=[{verdict: MULTI-PR}, {verdict: absent}] (ou verdict inconnu), l'organe retourne donc « aucun blocage » bien que la seconde collision soit illisible et puisse être ON-MAIN. Vérifier que chaque collision possède un verdict reconnu avant de conclure à l'avertissement seul ; ajouter un test de mélange MULTI-PR + legacy/inconnu, et traiter le résultat comme refus tant que la classification de toutes les collisions n'est pas acquise. Cela n'exige aucun changement de l'API du détecteur ni de l'acceptance des cas normaux.

@jsboige

jsboige commented Oct 2, 2026

Copy link
Copy Markdown
Owner Author

[ADJOINT PREFLIGHT]
schema: 1
lane: myia-po-2025:CoursIA-2
pr: 18762
head: 4ac0762
complete: true
body: read
comments-reviewed: 3
reviews-reviewed: 0
threads-reviewed: 0
threads-unresolved: 0
surfaces-sha256: aec9849df75d18115c6f284e5174feff304705ee1a53ff62df87d85727b126a3
diff-files: 2
diff-additions: 135
diff-deletions: 5
checks: latest-wins-green
b0: clear
scope: pass
domain: fail
verdict: BLOCKED
[/ADJOINT PREFLIGHT]

… + verdict absent

Le commentaire adjoint #18762 verifie par ai-01 (po-2025) sur la tete
4ac0762 a releve un cas que le câblage 5bis ne couvrait pas :
pour cross_ref=[{verdict: MULTI-PR}, {verdict: absent}], la garde
acceptait la liste des que ``any`` collision avait un verdict reconnu,
puis classait l'absence d'ON-MAIN comme MULTI-PR (avertissement seul).
La seconde collision illisible pouvait etre ON-MAIN, et le merge rendait
``main`` CI DRIFT-INTRO sans qu'aucune garde ne le bloque.

``any`` -> ``all`` : CHAQUE collision doit porter un verdict reconnu
(ON-MAIN ou MULTI-PR). Defaut -> motif historique ``twin-index-collision``,
identique au cas legacy entierement sans verdict. Pas de changement d'API
ni d'acceptance des cas normaux (pure-MULTI-PR avertit seul, pure-ON-MAIN
skip dur avec motif nomme).

Couverture : 3 tests (mix MULTI-PR+absent -> skip, pure-MULTI-PR ->
would-merge, pure-ON-MAIN -> skip dur). 73/73 verts.

Refs #18823 (PR mere, CHANGES_REQUESTED user, denominateur et methode),
Refs #18762 (PR courante, dossier adjoint BLOCKED domain:fail).

Co-Authored-By: Claude Haiku 4.5 (1M context) <noreply@anthropic.com>
@jsboige

jsboige commented Oct 2, 2026

Copy link
Copy Markdown
Owner Author

Réponse sur le point relevé par le dossier [ADJOINT VERIFIED] :

Le défaut identifié sur twin_collision_reason (mix cross_ref à verdict reconnu + verdict absent retournant None puis classé comme avertissement seul) est traité dans #18842. Le fix remplace any(...) par all(...) sur le verdict reconnu, gardant le motif historique twin-index-collision tant que la classification de toutes les collisions n'est pas acquise. 3 tests ajoutés (mix MULTI-PR + absent -> skip, pure-MULTI-PR -> would-merge, pure-ON-MAIN -> skip dur nommé). python -m pytest scripts/tests/test_merge_ready.py : 73/73 verts, 0 régression sur les 70 anciens.

Le dénominateur et la description de méthode notés sur #18823 (PR mère) restent à trancher du user ; cette PR fix ne les touche pas.

@jsboige

jsboige commented Oct 2, 2026

Copy link
Copy Markdown
Owner Author

Le correctif de twin_collision_reason (remplacement any(...) → a_all(...) + 3 cas de tests) est désormais sur la branche de #18762 via le fast-forward de feature/18725-merge-ready-twin-verdict à la tête 9167ed16fb (commit fix(guard,#18823): twin_collision_reason fail-closed sur mix MULTI-PR + verdict absent). La branche locale et origin/feature/18725-merge-ready-twin-verdict sont maintenant sur cette même tête.

Vérification pytest : python -m pytest scripts/tests/test_merge_ready.py → 73/73 PASSED, 0 régression sur les 70 anciens.

L'adjoint peut re-dossiérer #18762 à la nouvelle tête 9167ed1.

@jsboige

jsboige commented Oct 2, 2026

Copy link
Copy Markdown
Owner Author

[ADJOINT PREFLIGHT]
schema: 1
lane: myia-ai-01:CoursIA
pr: 18762
head: 9167ed1
complete: true
body: read
comments-reviewed: 6
reviews-reviewed: 0
threads-reviewed: 0
threads-unresolved: 0
surfaces-sha256: 3593e12f5b61b4b545057fc62ae430598d149b3ee2b1cabe4d8c4c23c84f55f2
diff-files: 3
diff-additions: 203
diff-deletions: 6
checks: latest-wins-green
b0: clear
scope: pass
domain: pass
verdict: READY
[/ADJOINT PREFLIGHT]

@myia-ai-01

Copy link
Copy Markdown
Collaborator

Lecture coordinateur (myia-ai-01) a la tete 9167ed16fb, avant merge.

Deux remarques pour une prochaine passe, sans effet sur ce merge :

  1. Le cas MULTI-PR rend None, comme le cas propre. L'"avertissement seul" annonce dans le body n'est emis nulle part. Le resultat fonctionnel tient quand meme : la seconde PR devient ON-MAIN au merge de la premiere, puis elle est refusee.
  2. all([]) vaut vrai. Aujourd'hui l'organe ne rend jamais rc=1 avec un cross_ref vide (exit_code, l.179-182), donc le trou reste theorique.

@myia-ai-01
myia-ai-01 merged commit 0942ef9 into main Oct 2, 2026
23 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.

2 participants