Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions scripts/check_adjoint_prevalidation.py
Original file line number Diff line number Diff line change
Expand Up @@ -732,6 +732,21 @@ def carrying_lane(snapshot: dict[str, Any]) -> str | None:
return match.group(1) if match else None


def is_out_of_fleet(snapshot: dict[str, Any]) -> bool:
"""True when the pull request is carried by no fleet lane at all.

Both conditions, mirroring the `tag_required` exemption (#17713/#17715,
`variation_tag_required.py`): the head branch starts with ``claude/`` AND
the body declares « Hors flotte ». Either alone is not out of fleet -- a
fleet lane renaming its branch or copying the marker stays inside the
third-party rule (#17791 acceptance 3).
"""
head_ref = snapshot.get("headRefName") or ""
return head_ref.startswith("claude/") and "Hors flotte" in (
snapshot.get("body") or ""
)


def validate_dossier(dossier: Dossier, snapshot: dict[str, Any]) -> list[str]:
"""Validate a parsed dossier against one live PR snapshot."""
f = dossier.fields
Expand Down Expand Up @@ -779,6 +794,13 @@ def validate_dossier(dossier: Dossier, snapshot: dict[str, Any]) -> list[str]:
errors.append(
f"lane must be one of the qualifying cluster lanes, got {dossier_lane!r}"
)
elif is_out_of_fleet(snapshot):
# #17791 -- a maintainer cloud session (`claude/*` + « Hors flotte »)
# carries no fleet lane: CLAUDE.md ('À qui ce fichier s'adresse') exempts
# it from the Grain tag, so the third-party rule has no carrier to
# compare against. Every qualifying lane is third-party by construction,
# and the self-attestation check below is unreachable for it.
pass
else:
carrier = carrying_lane(snapshot)
if carrier is None:
Expand Down Expand Up @@ -982,6 +1004,7 @@ def build_result(
"ready": verdict == VERDICT_READY,
"verdict": verdict or "NO_DOSSIER",
"errors": errors,
"out_of_fleet": is_out_of_fleet(snapshot),
}
if dossier is not None and verdict:
result["dossier"] = dossier_payload(dossier)
Expand Down Expand Up @@ -1316,6 +1339,11 @@ def main() -> int:
)
elif ready:
print(f"READY -- PR #{args.pr} prevalidated by adjoint at {snapshot['headRefOid']}")
if is_out_of_fleet(snapshot):
print(
" note: hors-flotte PR (claude/* + 'Hors flotte'): no fleet "
"lane carries it, any qualifying lane is third-party (#17791)"
)
elif verdict == VERDICT_BLOCKED:
print(
f"BLOCKED-WITH-SUBSTANCE -- PR #{args.pr} has an intact adjoint dossier at "
Expand Down
56 changes: 56 additions & 0 deletions scripts/tests/test_check_adjoint_prevalidation.py
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,62 @@ def test_non_shared_github_author_cannot_satisfy_gate():
assert any(error.startswith("comment author must") for error in errors)


# --- #17791 : PR hors flotte (session cloud du mainteneur) -------------------
#
# Miroir de l'exemption `tag_required` (#17713/#17715, variation_tag_required.py) :
# branche `claude/*` ET marqueur « Hors flotte » dans le body. Une telle PR n'est
# porte par AUCUNE lane de la flotte, donc tout dossier d'une lane qualifiante
# est tiers par construction -- le refus « carrying lane cannot be established »
# y est une impasse structurelle, pas une garantie.


def test_out_of_fleet_pr_accepts_qualifying_dossier_without_grain_tag():
"""claude/* + « Hors flotte » : un dossier de lane qualifiante est tiers."""
snapshot = _snapshot_with_body(
"Session cloud du mainteneur. **Hors flotte**", lane="myia-po-2023:CoursIA"
)
snapshot["headRefName"] = "claude/affectionate-mccarthy-6dvuea"
ready, errors = mod.evaluate(snapshot)
assert ready, errors
assert errors == []


def test_claude_branch_without_marker_is_still_refused():
"""Le prefixe seul n'exempte pas : sans le marqueur, le refus est intact."""
snapshot = _snapshot_with_body(
"Session cloud du mainteneur.", lane="myia-po-2023:CoursIA"
)
snapshot["headRefName"] = "claude/affectionate-mccarthy-6dvuea"
errors = _errors(snapshot)
assert any(
error.startswith("carrying lane cannot be established") for error in errors
)


def test_fleet_branch_with_marker_is_still_refused():
"""Recopier le marqueur sur une branche de flotte ne sort pas de la regle."""
snapshot = _snapshot_with_body(
"Session cloud du mainteneur. **Hors flotte**", lane="myia-po-2023:CoursIA"
)
snapshot["headRefName"] = "feature/renamed-to-escape"
errors = _errors(snapshot)
assert any(
error.startswith("carrying lane cannot be established") for error in errors
)


def test_self_prevalidation_refusal_survives_the_exemption_next_door():
"""Une PR de flotte taguee reste refusee en self-attestation : l'exemption
hors flotte n'ouvre aucune echappatoire a cote."""
carrier = "myia-po-2023:CoursIA"
snapshot = _snapshot_with_body(
"Grain: DEEP/lean -- lane %s -- prev: MED" % carrier, lane=carrier
)
snapshot["headRefName"] = "feature/ordinary-fleet-branch"
errors = _errors(snapshot)
assert any(error.startswith("self-prevalidation refused") for error in errors)


def test_blocked_preflight_is_a_valid_dossier_but_never_ready():
"""An honest BLOCKED dossier must be distinguishable from an absent one.

Expand Down
Loading