Test CI Routing

Audience: CI/release owners and engineers who need to know when and where a given change runs tests. Purpose: define the trigger placement for the manifest-backed testing framework.

The contract is intentionally conservative: every commit is analyzed, but broad/full execution is restricted to nightly, weekly, or manual lanes. This keeps PR feedback fast and CI cost bounded while still giving expensive, live, and Docker-heavy suites a visible home.

For the evidence standard that governs when a routed suite counts as “executed,” see TEST_CI_EVIDENCE_DASHBOARD.md. For the manifest contract behind these triggers, see TEST_MANIFEST.md.

Trigger Classes

Trigger classWorkflow placementContract
PR / per-commit.gitea/workflows/test-manifest-execution.yml targeted jobBuild test-plan.json from changed files and execute eligible selected suites/checks. Report-only and candidate failures stay visible without becoming release blockers.
Merge to main.gitea/workflows/test-manifest-execution.yml targeted jobRe-plan from the last commit and publish the same plan/run/readiness/dashboard artifacts for main.
Nightly.gitea/workflows/test-manifest-execution.yml full-sweep jobs, daily cronRun runtime shards and execute suites whose ci.nightly policy is not none; Docker/Testcontainers rows are routed to the apparmor-host host-infra job while default shards exclude them.
Weekly / release.gitea/workflows/test-manifest-execution.yml full-sweep jobs, weekly cron or workflow_dispatch mode=weeklyKeep benchmark, chaos, and extended suites visible without running them on unrelated PRs; execute non-none weekly suites with the same default-runner vs host-infra split.
Manual full sweep.gitea/workflows/test-manifest-execution.yml with workflow_dispatch full_sweep=true mode=manual execute=trueSelect every active suite by runtime shard, split into default non-Docker shards and apparmor-host Docker/Testcontainers shards. This is the current all-active-suite execution lane.
Manual / live.gitea/workflows/test-manifest-execution.yml with workflow_dispatchOperators can set execute=true for selected lanes. Live/operator rows stay manual-first unless explicitly promoted.
Manifest drift.gitea/workflows/test-manifest-plan.ymlValidate manifest, trait, fixture, and CI routing metadata plus planner/runner dry-run artifacts.

Current CI-Gap Placement

test-ci-gap-routing.csv is generated from TEST_MANIFEST.yml and covers the 23 current ci-gap suites.

FamilyPlacement
Production/operator Playwright (src/__Tests/playwright/**)Nightly/manual visible, manual-live gating, no PR/main execution by default.
src/__Tests/e2e/**Related-change PR/main candidate, report-only until stability evidence exists.
src/__Tests/Integration/**Related-change PR/main candidate, report-only until stability evidence exists.
src/Doctor/__Tests/**Related-change PR/main candidate, report-only until stability evidence exists.
BinaryIndex and performance benchmarksWeekly/manual only, never PR.
Docs samples/templatesSample-only targeted PR/main, never product-wide.

No Over-Trigger Rules

Runner Placement

Release Readiness

summarize-test-readiness.py reads test-run-summary.json and writes:

Readiness cannot be green when selected blocking or required suites are failed, skipped, cancelled, or timed out. Dry-run summaries become not_evaluated when --require-executed is set.

render-test-dashboard.py also writes the per-suite evidence matrix:

The Markdown dashboard is printed into CI logs so an outside reviewer can see selected suites, run reasons, execution counts, skipped-by-policy counts, and failure evidence without relying only on artifact download.

Commands

python .gitea/scripts/test/refresh-ci-gap-routing.py
python .gitea/scripts/test/validate-ci-routing.py \
  --out TestResults/test-ci-routing/summary.json

python .gitea/scripts/test/plan-test-run.py \
  --changed-file src/Scanner/StellaOps.Scanner.WebService/Program.cs \
  --mode pr \
  --out-dir TestResults/test-plan-targeted

python .gitea/scripts/test/plan-test-run.py \
  --select-all \
  --runtime-cost small \
  --exclude-infra docker-testcontainers \
  --exclude-infra testcontainers \
  --mode nightly \
  --out-dir TestResults/test-plan-nightly-small

python .gitea/scripts/test/plan-test-run.py \
  --select-all \
  --runtime-cost small \
  --include-infra docker-testcontainers \
  --include-infra testcontainers \
  --mode nightly \
  --out-dir TestResults/test-plan-nightly-small-host-infra

python .gitea/scripts/test/run-test-plan.py \
  --plan TestResults/test-plan-targeted/test-plan.json \
  --out-dir TestResults/test-run-targeted \
  --dry-run \
  --report-only

python .gitea/scripts/test/summarize-test-readiness.py \
  --summary TestResults/test-run-targeted/test-run-summary.json \
  --out-dir TestResults/test-readiness-targeted \
  --require-executed \
  --report-only

python .gitea/scripts/test/render-test-dashboard.py \
  --plan TestResults/test-plan-targeted/test-plan.json \
  --run-summary TestResults/test-run-targeted/test-run-summary.json \
  --readiness TestResults/test-readiness-targeted/test-readiness-summary.json \
  --out-dir TestResults/test-dashboard-targeted