# Test-selection — implementation state / handoff Working notes for the diff-based CI test-selection system, so the work can be resumed without re-deriving context. For *how the system works / how to use it*, see [`TEST_SELECTION.md`](TEST_SELECTION.md); this file is only about the **state of the implementation**. _Last updated: 2026-06-18._ ## Status: complete & locally verified; in review on PR #8077 The system is implemented end-to-end and passes local checks. Opened as [deepspeedai/DeepSpeed#8077](https://github.com/deepspeedai/DeepSpeed/pull/8077) (scoped to `modal-torch-latest` first; replicate to other workflows if accepted). It has **not** yet run a live `subset`/`deploy` on modal, so the first real runs should be watched. ### Review fixes applied (Codex bot, PR #8077) - **P1 — run the selector from trusted code.** `collect-tests` now restores `ci/` from the base branch before running the selector + self-tests, so a PR can't rewrite `tests_fetcher.py` to emit `mode=none` and skip the Required check. (A PR's `ci/` changes still show in the diff and force a full run via `ci/**`.) Includes a bootstrap fallback: if base has no `tests_fetcher.py` yet, run all. - **P1 — hidden-file artifact.** `actions/upload-artifact@v4` skips dot-prefixed paths by default; `ci/.test_selection/` is one. Added `include-hidden-files: true` so `deploy`'s download doesn't fail. > Reminder: under `pull_request_target`, `deploy` restores `ci/` from the base > branch, so changes under `ci/*` (the fetcher included) only take effect once > **merged**. To validate the live behavior before merge, trigger via a > `pull_request`-based run or the `modal` CLI. ## What was built A diff-driven selector that, on a PR, runs only the `tests/unit/v1` tests impacted by the changed files, with a fail-safe fallback to the full suite. Driven by the `modal-torch-latest` workflow. ### Files | File | State | Notes | | --- | --- | --- | | `ci/tests_fetcher.py` | new/rewritten | Config-driven `TestSelector` class; import-graph selector. | | `ci/test_tests_fetcher.py` | new | 12 stdlib self-tests over synthetic git repos. | | `ci/torch_latest.py` | modified | Reads `DS_TEST_LIST_FILE`, runs pytest on the selected targets (falls back to full `tests/unit/v1`). | | `.github/workflows/modal-torch-latest.yml` | rewritten | `collect-tests` (fetcher) gates `deploy` (modal). | | `.github/workflows/TEST_SELECTION.md` | new | Full design + usage doc. | | `.github/workflows/TEST_SELECTION_STATE.md` | new | This file. | | `CONTRIBUTING.md` | modified | "Diff-based CI test selection" section + link. | | `.gitignore` | modified | Ignores `ci/.test_selection/`. | ## Key design decisions (and why) - **Always-runs Required job, selects instead of skips.** `modal-torch-latest` is a Required check, so path filters can't skip it. `collect-tests` decides `all|subset|none`; `none` skips `deploy` while still satisfying the Required status. - **`deploy` tests PR code but restores `ci/` from base.** Selection must reflect PR code, but the orchestration that holds modal/HF secrets must not be PR-controlled. (Security trade-off accepted earlier in the work.) - **Opaque hub modules + curated run-all globs.** `deepspeed`, `deepspeed.comm`, `deepspeed.accelerator` are opaque in the graph to stop universal fan-out; core hubs (`deepspeed/__init__.py`, `runtime/engine.py`, `comm/**`, `accelerator/**`, …) instead live in the run-all globs. (Coarseness fix accepted earlier.) - **Fail-safe to `all`, never `none`.** Every uncertain case (no base, no merge-base, parse error, unexpected exception) widens to the full suite. ### Improvements implemented in the latest pass (items #3–#11) - **#3** Degrade to `all` on any selector error (top-level guard in `main()`). - **#4** `DYNAMIC_EDGES` map for runtime/dynamic deps the AST can't see (seeded: `deepspeed/module_inject/** → tests/unit/v1/moe/**`). - **#5** Deleted `.py` only forces `all` when a *surviving* file still imports it (dangling import); clean deletions no longer over-trigger. - **#6** GitHub job summary (`$GITHUB_STEP_SUMMARY`) with mode/reason/file list; `reason` also on `$GITHUB_OUTPUT`. - **#7** `--explain` prints import chains from changed files to selected tests. - **#8** Escape hatches documented in `CONTRIBUTING.md` + `TEST_SELECTION.md`. - **#9** Self-tests in `ci/test_tests_fetcher.py`, also run in `collect-tests`. - **#10** Robust merge-base: explicit check, `all` if missing; workflow keeps `fetch-depth: 0` with rationale. - **#11** Multi-workflow config: `WORKFLOWS` registry + `WorkflowConfig` + `--workflow` flag. ## Verification done locally - `python ci/tests_fetcher.py` self-tests: **12/12 pass** (narrowing, shared importers, core/comm/fixture run-all, commit tag, docs-only → none, new test file, clean vs. dangling delete, dynamic edge, missing base). - Real-repo smoke: `--base`, `--explain`, `--workflow` validation all behave. - Confirmed `$GITHUB_OUTPUT` + `$GITHUB_STEP_SUMMARY` emission and the #3 error-fallback path directly. - No linter errors. Re-run the core check any time: ```bash python ci/test_tests_fetcher.py python ci/tests_fetcher.py --base origin/master --explain ``` ## Open items / possible follow-ups - **Live validation:** watch the first real PR; confirm `collect-tests` artifact hand-off and `deploy` gating behave on Actions + modal. - **`DYNAMIC_EDGES` is minimal.** Only one seeded edge. Expand as blind spots are found (tests that fail on `master`'s full run but weren't selected on their PR are the signal). - **Single workflow wired.** The engine is config-driven (#11) but only `modal-torch-latest` is registered/wired. Other GPU workflows could adopt it by adding a `WorkflowConfig` and mirroring the `collect-tests` job. - **Run-all glob tuning.** The core-hub list is a conservative first cut; revisit if it proves too coarse (full runs on minor edits) or too narrow (missed impact). - **No telemetry.** Consider logging selection stats over time to tune globs/opaque-modules against real false-positive/negative rates.