(files: list[str])
| 333 | |
| 334 | |
| 335 | def decide(files: list[str]) -> SelectorResult: |
| 336 | reasons: list[str] = [] |
| 337 | lane_reasons: dict[str, list[str]] = {} |
| 338 | lanes = LaneSelection() |
| 339 | |
| 340 | if not files: |
| 341 | lanes.full = True |
| 342 | full_reasons = ["no_changed_files_or_diff_unavailable"] |
| 343 | return SelectorResult( |
| 344 | lanes=lanes, |
| 345 | pytest_paths=[], |
| 346 | functional_scripts=[], |
| 347 | provenance=SelectionProvenance(), |
| 348 | reasons=full_reasons, |
| 349 | changed_files=[], |
| 350 | lane_reasons={"full": full_reasons}, |
| 351 | ) |
| 352 | |
| 353 | # Lint-only filtering (routing should ignore these files) |
| 354 | lint_only = [f for f in files if f in LINT_ONLY_FILES] |
| 355 | routed_files = [f for f in files if f not in LINT_ONLY_FILES] |
| 356 | |
| 357 | if lint_only: |
| 358 | reasons.append(f"lint_only_count:{len(lint_only)}") |
| 359 | |
| 360 | # If *only* lint-only files changed, skip all lanes |
| 361 | if not routed_files: |
| 362 | lanes.skip = True |
| 363 | skip_reasons = [*reasons, "lint_only"] |
| 364 | return SelectorResult( |
| 365 | lanes=lanes, |
| 366 | pytest_paths=[], |
| 367 | functional_scripts=[], |
| 368 | provenance=SelectionProvenance(), |
| 369 | reasons=skip_reasons, |
| 370 | changed_files=files, |
| 371 | lane_reasons={"skip": skip_reasons}, |
| 372 | ) |
| 373 | |
| 374 | # Docs lane is orthogonal: if any routed file matches docs, enable docs lane. |
| 375 | docs_rule = CATEGORY_RULE_BY_NAME.get("docs") |
| 376 | docs_touched = bool(docs_rule and any(_matches_any(f, docs_rule.match_any) for f in routed_files)) |
| 377 | docs_matched_files = {f for f in routed_files if docs_rule and _matches_any(f, docs_rule.match_any)} |
| 378 | non_docs_routed_files = [f for f in routed_files if f not in docs_matched_files] |
| 379 | |
| 380 | docs_pytests_sorted: list[str] = [] |
| 381 | docs_scripts_sorted: list[str] = [] |
| 382 | |
| 383 | if docs_touched: |
| 384 | lanes.docs = True |
| 385 | reasons.append("category:docs") |
| 386 | lane_reasons["docs"] = ["category:docs"] |
| 387 | docs_pytests_sorted = sorted(set(docs_rule.pytest_paths)) if docs_rule else [] |
| 388 | docs_scripts_sorted = sorted(set(docs_rule.functional_scripts)) if docs_rule else [] |
| 389 | |
| 390 | # Full-suite triggers always win over fast, but docs lane can still remain enabled. |
| 391 | triggered: list[tuple[str, str]] = [] |
| 392 | for f in routed_files: |
no test coverage detected