openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
128 lines (105 loc) • 31.1 kB
Markdown
---
summary: "Changed-scope detection and the per-area rules that select CI lanes"
read_when:
- You need to know why a lane was or was not selected for a diff
- You are changing changed-scope detection
title: "Scope selection"
sidebarTitle: "Selection"
---
Changed-scope detection, native lane selection, and the per-area routing rules that decide which lanes a diff selects. Part of the [CI scope and routing](/ci/scope-and-routing) index.
Runner placement is separate from coverage selection. On automatic canonical hybrid first attempts, preflight may offload security, up to three selected Control UI unit rows, and only the browser-extension E2E row when the [complete hosted base has at most 40 rows](/ci/capacity#bounded-hybrid-hosted-offload), keeping optional additions within 45 total. Base counts above 40 keep those rows on Blacksmith with their original tests and workers; an eligible base above 45 also emits a warning while retaining the complete base manifest. Narrow PR admission reserves the full lint/type row templates plus one hosted `check-plan` job. Later row reductions do not expand that admission, so optional offloads can remain on Blacksmith even when the final selected inventory would fit.
## Scope and routing
Main-tier CI is [hourly by default](/ci/scheduled-workflows#hourly-main-ci).
Main-push lane selection below describes the opt-in `OPENCLAW_CI_ON_PUSH=true`
path. Hourly runs select the complete main tier, including Android, without
changed-path filtering. Full manual and release validation additionally select
the release-only proofs described in the scheduled-workflow guide.
Scope logic lives in `scripts/ci-changed-scope.mjs` and is covered by unit tests in `src/scripts/ci-changed-scope.test.ts`. Ordinary manual dispatch skips changed-scope detection and makes the preflight manifest act as if every scoped area changed. The exact-head `release_gate` exception evaluates the fetched pull request merge tree and retains its macOS, iOS-build, and generated-native-locale decisions while still verifying native sources.
Labeler skips PR edits without title or base-branch changes. These ignored edits use isolated per-run concurrency groups so they cannot cancel running labeling or replace useful pending work. Opened, reopened, synchronize, and title/base-edit events retain the shared per-PR group and supersede older labeling runs. Issue labeling and manual backfills retain their existing non-cancelling ref group.
Affected pull requests, `main` pushes, and exact-head `release_gate` fallbacks run one required `ios-build (smoke)` phase: the existing `pnpm ios:build` command, Swift lint, focused voice cleanup tests, and native Access client, transfer, and session-store tests, with their Xcode, Swift, and Watch Rust tooling. Smoke omits Rust tests and the broader app/notification lifecycle and Watch simulator suites. A failed, cancelled, or unexpectedly skipped smoke still fails `openclaw/ci-gate`.
Hourly main-tier CI retains `ios-build (tests)`, including Rust, voice cleanup, native Access, and every focused app/notification lifecycle test, including its Watch messaging and activation coverage. Managed attachment UI/export proof, Watch operation simulator suites, and Watch delivery UI proof belong to full-tier manual/release validation; hourly runs also exclude screenshot capture. Main-tier and smoke builds use the selected simulator's native architecture without compiler indexing; voice and lifecycle tests retain logs and xcresult bundles without Xcode's verbose diagnostic collection. Only a canceled selected scheduled `ios-build` on canonical `main` can pass the aggregate with a coalescing notice; this delegates proof to a later job rather than validating the canceled revision. Other failures, unexpected skips, and cancellations remain failures. See [scheduled iOS aggregation](/ci/scheduled-workflows#hourly-main-ci).
Screenshot-input changes on pull requests, admitted pushes, and their exact-head `release_gate` fallbacks select both device shards and the evidence reducer through `shouldRunIosScreenshots`: iOS UI/resources/UI tests and Fastlane configuration, linked Swift sources, and capture/build tooling. Native unit-test-only Swift edits do not select screenshots.
Full-tier manual CI (`workflow_dispatch` with `validation_tier=full`, `release_gate=false`, and `release_scope=full`), including full-scope release validation, retains separate Release device and Debug/native-test phases plus the full iPhone, iPad, and Watch screenshot matrix. The test phase keeps universal simulator builds, failure diagnostics, Rust, voice cleanup, native Access, lifecycle, managed attachment UI/export, Watch operation, and Watch delivery UI tests. Every case and assertion moved out of hourly CI remains in this phase. The two device shards build their own simulator apps independently of `ios-build`; scenarios stay serial within each device, with Watch evidence in the iPad shard. A hosted reducer verifies the exact evidence union and provenance before publishing the canonical screenshot artifact. Provenance binds every shard to the reducer's run, target SHA, and workflow SHA, not its attempt: rerunning one failed shard reuses the other shard's evidence from its earlier successful attempt, while GitHub starts the reducer only after each shard's latest attempt succeeded. The final gate requires both build phases, both screenshot shards, and the reducer. Frozen compatibility targets retain their existing Debug-only full-manual contract and screenshot exclusion; `npm-beta` and `npm-stable` still defer native qualification. All iOS build phases and screenshot shards use Xcode 27 on GitHub-hosted `xcode-27`, the preview macOS 27 image. A pure iOS app change does not select macOS jobs by itself.
The iPad shard captures Watch from the Watch app compiled by its own fresh screenshot build, after all four iPad captures pass. It does not rebuild Watch or transfer simulator products between jobs. The standalone `watch_screenshot` lane still prepares and builds a fresh Watch app. A missing or invalid product fails capture without a rebuild fallback; completed iOS screenshots and result bundles remain available in the failure artifact.
Separate iOS and macOS Periphery workflows enforce a zero-findings dead-code policy. Each runs only when a non-draft pull request touches its native scan scope, or when manually dispatched.
The shared PR commenter reads each producer's fixed run title to distinguish report admission, explicit `converted_to_draft` cleanup, passive draft events, and manual runs. Reports require a live open PR at the same repository and head with draft status off; draft cleanup requires draft status on. Passive runs cannot publish or supersede reports. Newer eligible runs and attempts supersede older results, including while pending. A report-admitted run that successfully detects no scan scope can clear an existing comment; draft cleanup names draft status instead of claiming scope loss. The commenter rechecks PR state, repository, head, and draft status immediately before writing, but separate REST calls are not atomic.
Runs without recognized admission metadata are logged no-ops: they neither publish nor supersede. After rollout, a new pull-request source event is needed to produce an eligible run; rerunning an old unmarked run does not recover its original admission intent.
The iOS, macOS, and both shared OpenClawKit Periphery scans use Xcode 27 on GitHub-hosted `xcode-27`. The toolchain change preserves all four hosted scan jobs, their scope, workloads, timeouts, artifacts, and rerun behavior; it changes no Blacksmith placement or registration counts. Periphery remains pinned to 3.8.0, with native compatibility required across all four consumers when its toolchain changes.
- **Max-lines baseline maintenance** uses the existing required `checks-fast-baseline-ratchets` guard on pull requests. When that guard is selected, changing only `config/max-lines-baseline.txt` does not add unrelated tooling tests; mixed changes retain their owning tests and boundary checks. The guard still verifies the exact tested merge tree against its prepared base and rejects baseline expansion or stale entries. Changes to the ratchet implementation, CI routing, or other config files retain their existing test selection.
- **CI workflow edits** validate the Node CI graph, workflow linting, and the Windows lane (`ci.yml` executes it), but do not force iOS, Android, or macOS native builds by themselves; those platform lanes stay scoped to platform source changes.
- **Native inputs beside Node changes** retain every selected macOS, iOS, or Android check and the Node tests that own, directly import, or explicitly watch those inputs. Native-only inputs do not request the complete Node inventory. Static compiler and boundary checks retain conservative coverage independently of runtime selection.
- **Compact planner policy changes** select the planner, inventory, routing, and policy tests owned by those files, plus affected import consumers, protected regressions, and the fixed smoke set. They do not select the complete compact core or every extension. Hourly main and Full Release Validation still exercise the full inventories through their existing owners.
- **Control UI source selection** preserves complete ordinary UI unit, mocked E2E, real-Gateway, and browser-extension families when their existing area owners change. The family gates remain independent, so a browser-extension change need not select Control UI jobs. Outside an affected family, protected tests and transitive owner selection can still admit precise files. Node excludes files delegated to a selected UI job.
- **Documentation beside code** keeps all `docs/` content, including navigation JSON, generated metadata, and assets, plus README files, `AGENTS.md`, and skill Markdown from widening the code's Node plan. Packaged workspace templates and test fixtures retain their runtime owners. Docs-only routing still selects no Node rows; unrelated runtime Markdown outside `docs/` retains its owner.
- **PR dependency and helper selection** combines bounded owner areas, transitive test import/re-export reachability through workspace and public SDK aliases, explicit policy watches, and protected regressions. Protected tests also follow runtime imports across owner areas. One-hop proximity alone misses observed catches. Unrelated global inputs do not request the full runtime repository; owner and protected-test policy supplies the retained coverage.
- **Erased TypeScript modules** retain their owner tests, affected runtime test consumers, and declared source watches. Type-only changes still select every consuming compiler graph through the separate static-check owner. Ambiguous compiler ownership, ambient/declaration files, and missing history retain complete static checks without widening runtime tests.
- **Filesystem source scanners** retain their declared watched inputs alongside ordinary importers. A policy watch supplements each changed source's tests and never supplies a missing ordinary owner.
- **Global and hub Node inputs** select their bounded owner tests, transitive import consumers, and protected regressions plus the fixed PR smoke set. Shared test bootstrap/configuration, root compiler resolution, workspace/install settings, patches, dependency hubs, and packing policy no longer request the complete runtime plan. Static checks retain full fallback where ownership is ambiguous. Missing changed paths or an unavailable bounded selector fail preflight instead of admitting every runtime family. Global package, lockfile, compiler, and shared Vitest inputs do not select every platform/browser family merely because Node ownership is broad; related protected tests remain selected. UI and wizard locale catalogs and translation metadata retain their dedicated localization and compiler checks without PR Node or browser rows; localization generators and runtime modules retain tests.
- **Generated Control UI boot metadata** selects its chunking and Vite tests through the shared test-target map. The existing UI performance lane still builds and checks the generated chunks; the metadata alone does not require unrelated core suites.
- **Preflight diagnostics** log `Node test plan changed-set` with the selected row count and any bounded owner-selection reason. Missing or unbounded PR plans fail preflight; they never become the full compact matrix. The logged changed-path manifest supplies replay inputs. Hourly main and ordinary manual/release validation retain complete inventories through their existing owners.
- **PR check families** select static checks and guards from their own changed-path owners, independently of whether Node test targeting finds a precise plan. Lint and formatting reuse the local changed-check owner; lint configuration changes retain full lint. Semantic lint runs changed files and their transitive import consumers, including type-only imports and workspace/path aliases, through the existing shard owner. Each row receives only its assigned file list; workers do not rediscover the graph. Affected root tests retain their central compiler-config admission. Deleted, ambient, unsupported, and oversized selections retain full lint. Hourly main and Full Release Validation continue running full repository lint. Hosted profiles keep selected files in their existing stripes, while scripts, root tests, formatting, and localization checks retain their central owner. Preflight remains dependency-free and owns admission, static-check selection, and the original row/resource templates. For admitted narrow PRs, the hosted `check-plan` job installs through the existing Node setup owner, then materializes selected-file lint and compiler facts. It validates compiler ownership and selects every graph that consumes a changed file, including erased type imports into UI, plugins, scripts, and tests. Its consumers require a successful plan; failure cannot silently select a full fallback, and `openclaw/ci-gate` requires the planner itself. Existing production, test, and stripe jobs retain their runner placement and compiler concurrency; only rows with selected graphs run. GitHub and hybrid keep changed core-test consumers in their canonical stripes; the central row owns the selected extensions, scripts, and root-test graphs. Markdown and UI styles do not widen a mixed TypeScript change. Deleted, ambient, configuration, and unclassified inputs retain all compiler graphs. Shared fixtures and scanner policy inputs retain conservative check families. Conflict-marker and wall-clock deprecation guards run after Node setup in the required planner job. Other guards retain their generated native inputs. SDK subpath exports, extension import boundaries, declaration compile/canary proof, protocol generation, schema drift, and Knip remain blocking. Plugin and channel runtime contract tests use the exact changed-owner Node plan, including their watched SDK documentation and packaged skills. Narrow code changes run coercion checks once in the guard job. Main, ordinary manual CI, and historical targets skip this additional planner and retain their full check families. Static full fallback never widens runtime families. Bundled config metadata follows the shared schema owner map. The bundled/protocol row always verifies protocol generation; its full bundled runtime suite runs only on hourly main and ordinary manual/release validation. Specialized Bun launcher proof opts in for its direct launcher and module-generation owners. Startup, plugin, and channel tests retain their canonical configs within the changed-owner plan.
- **Browser integration on PRs** retains the complete ordinary family selected by the existing Chromium/UI, Gateway/served-UI, or extension-bootstrap owner. Unrelated families remain omitted unless protected or affected tests select a precise subset. Named release-only UI compositions remain deferred unless their tests, owner selection, or protected status opts them in. Existing row caps, worker limits, serial/parallel ownership, and runner routes stay unchanged. Empty real-Gateway phase groups are removed; a desktop-only carrier keeps its required desktop proof without invoking a test phase with no files. Hourly main and Full Release Validation retain their complete tier inventories; historical manual targets retain their supported runner path.
- **Extension selection** uses exact tests from the changed plugin owner, transitive test importers, protected regressions, and explicit policy watches. Global dependency, shared-runtime, SDK, and planner inputs do not append a whole-plugin fallback. The existing Plugin Prerelease workflow owns the complete extension runtime inventory hourly and in Full Release Validation; normal CI does not append a second partial inventory.
- **PR builds** select `build-artifacts` for a dist-dependent row, an affected build/package owner test, or an individually selected built-process proof. Pipeline ownership comes from the existing changed-target map for build, declaration, package-tarball, and dist-artifact tests; generic runtime changes do not request the full artifact job. Source boundary guards and affected channel tests keep their Node owners. An artifact build does not also select an unrelated dist boundary, channel family, or every process verifier.
- **PR wrapper extraction** selects `pr-worktree-provision.test.ts` when the wrapper, its library, or a file in `scripts/pr-lib/wrapper-components.txt` changes. This manifest-derived policy watch supplements ordinary source tests because filesystem copying is invisible to the import graph. Manifest-only changes also run provisioning, including its duplicate-inventory and eager runtime import-closure checks.
- **Selector integration proofs** retain the complete real-checkout owner scenarios in their canonical config. Direct edits select them through the same owner mechanism. Bounded synthetic cases protect selection and budget behavior without expanding the whole checkout for each assertion.
- **Tooling test changes** under `test/scripts/**/*.test.ts` also select `test-projects.test.ts`, whose workflow-routing expectations depend on the test inventory. The changed tests retain their own coverage, and newly added workflow guards check routing expectations before merge.
- **Messaging test-root changes** under `src/auto-reply/` and `src/infra/outbound/`, plus core-test tsconfig inputs, select `tsgo-core-test-shards.test.ts`. This filesystem-inventory guard verifies exactly-once ownership; shard size against the advisory 720-root limit is reported as a warning only and never blocks CI. It supplements the changed tests' ordinary owners; other test-root paths retain their existing selection.
- **Codex app-server test changes** under `extensions/codex/src/app-server/**/*.test.ts` also select `test/vitest-projects-config.test.ts`, which verifies complete and unique full-suite test ownership. The changed tests retain their existing extension suite coverage.
- **Git-owner changes** to its action, base-commit policy, projection generator, lifecycle tests and support, or named owner-adopting workflows such as Workflow Sanity, QA Profile Evidence, Mantis ref validation/installers/worktrees, Docs Sync Publish Repo, OpenClaw Performance, the Linux/macOS/npm-placeholder release admission jobs, and plugin ClawHub/npm publication select the existing `macos-node` and Windows lanes. These run native checkout ownership proof without selecting Swift, iOS, or Android jobs; Mac app and shared-native changes retain their existing Mac lanes.
- **macOS Swift runner budgets** are 30 minutes per worker on GitHub-hosted `xcode-27` with Xcode 27, including automatic first attempts. Regular PR/main CI and PR `release_gate` dispatches run `tests` for Swift lint, schema checks, and app coverage build/tests, alongside `packages` for the Talk opt-out build, shared package suite, and standalone Swabble suite for current targets. Ordinary full-scope manual validation adds the independent `release` app build, moves lint/schema ownership to that phase, and retains health renders in `tests`. Every selected phase must pass the existing CI gate. At most two workers run concurrently; a failed phase does not cancel the other phases' diagnostics. The preview macOS 27 image changes neither coverage nor the Swift 6.3 source-language minimum; complete native proof is required on the new toolchain.
- **macOS fixture support** changes select the existing Mac Node gate so the shared managed-command and concurrency owner receives Darwin proof independently of Swift/app changes.
- **macOS Swift build caches** retain the original nanosecond timestamps and content hashes of their source inputs inside `apps/macos/.build`. The restore helper replays timestamps only for byte-identical regular files with matching permissions in the current input inventory; changed, missing, linked, or invalid entries keep their checkout metadata and invalidate through SwiftPM normally. The v6 archive keys include the phase, helper, toolchain, package graph, and source identities, with same-phase, same-graph prefix reuse. Each phase starts with a cold seed instead of restoring the former combined build archive, then records metadata immediately before its own trusted save. Both phases may restore the shared SwiftPM dependency cache; its sole eligible writer is `tests` in regular CI or `release` in full validation. Candidate cache trust is unchanged: cache-off validation compiles every selected phase cold. Historical targets retain their target-owned build commands.
- **Workflow Sanity** runs `actionlint`, `zizmor` over all workflow YAML files, the composite-action interpolation guard, and the conflict-marker guard. Actionlint configuration stays with this owner and does not widen Node test selection. The PR-scoped `security-fast` job also runs `zizmor` over changed workflow files so workflow security findings fail early in the main CI graph.
- **Docs on opt-in `main` pushes** are checked by the standalone `Docs` workflow with the same ClawHub docs mirror used by CI, so mixed code+docs pushes do not also queue the CI `check-docs` shard. Pull requests and manual CI still run `check-docs` from CI when docs changed.
- **TUI PTY** runs two built-CLI artifact canaries in `build-artifacts` on main, ordinary manual/release CI, and PRs selecting their existing test owner: a local model roundtrip and a real Gateway connection. The complete suite is defined in `test/vitest/vitest.tui-pty.config.ts`. Narrow pull-request plans omit the named PTY process proofs and run the source-only harness assertion test as an exact target under that same config, without requiring dist. Manual/release full plans retain the `core-runtime-tui-pty` descriptor; PRs have no broad fallback. CI consumes that descriptor only through the built-artifact selection flag, so the full suite has no executing matrix row; manual and release CI also run only the canaries. Canonical `main` push compaction omits the full descriptor while keeping the canaries.
- **SQLite session lifecycle** runs for its existing storage/session owner scope on PRs, exact-head PR fallbacks, and main; ordinary manual/release CI retains it. Owner selection covers the built-CLI migration, restart, compaction, cleanup, and session RPC proof only when the diff touches its direct storage/session owners or a reachable session path in the embedded runner. The `build-artifacts` verifier wave runs it against the runtime already built in that job, after the isolated startup-memory measurement. It overlaps independent readers on Blacksmith and stays serial on hosted runners; ordinary manual and release dispatches always select it when the target contains the proof.
- **CI routing and plugin contract helper edits** use the same changed-owner Node plan and static correctness gates as other PRs. Historical fast-only scope hints cannot bypass that plan or select whole contract families. Hourly main and ordinary manual/release validation retain their complete routing and contract inventories.
- **Release tooling fast lane** applies when a canonical pull request carries the `release-fast-lane` label and every changed path is release tooling (`.github/workflows/**`, `scripts/**`, `test/scripts/**`, `.agents/skills/release-*/**`, `docs/reference/RELEASING.md`) or independently checked documentation. The manifest keeps `security-fast`, `check-shard`, `check-docs`, and the changed Node rows, relaxes the compact packing-policy proof to those rows, and skips every other lane; the preflight step summary lists the admitted and skipped lanes under `Release fast lane`. A declined label (out-of-scope path, global Node input, fork, push, dispatch, docs-only) warns and keeps ordinary selection. Hourly full main CI still covers the merged result.
- **QA Smoke on PRs and `main`** runs when the diff touches the qa-lab harness, `qa/` scenario data, the matrix/telegram channels the smoke profile drives, Docker packaging scripts, or the gate's orchestration. Generic runtime, UI, workspace-package, and dependency changes do not select smoke by themselves. Exact-head PR fallbacks use the same owner map. Ordinary manual CI and Full Release Validation retain the complete smoke profile; missing changed paths or an older planner without the selector retain coverage when the target supports the smoke harness.
- **Docker seed on canonical `main` pushes** uses `resolveDockerSeedLanes` to select exactly `published-upgrade-survivor` on every admitted run, independently of changed paths. It runs `legacy-operator-state` with `auto-auth` against an exact published predecessor. Main prepares the complete runtime and public SDK types with `pnpm build:ci-artifacts`, then uses the canonical package helper with `--skip-build` and its unchanged tarball integrity check. Ordinary manual CI, including Full Release Validation's `normal_ci` child and its `npm-beta`/`npm-stable` scopes, retains full package generation and also selects `cron-mcp-cleanup`, `fleet-cache`, `mcp-channels`, `mcp-code-mode-gateway`, and `update-channel-switch`. Targets must declare the Docker seed capability; older targets without the tier selector retain the survivor fallback. Pull requests and exact-head `release_gate` fallbacks use `resolveChangedDockerSeedLanes` and its established per-lane owner map; unrelated changes omit Docker. This restores owner opt-in without changing main’s published-upgrade tripwire or full release inventory. Docs-only main pushes remain excluded at the workflow trigger. Expanded release history stays in Package Acceptance and the weekly Update Migration workflow.
- **Control UI performance** uses the dedicated `run_control_ui_performance` output. Production UI files, plugin browser entries, and the UI build/performance policies select it. Other modules select it when a runtime import path from a UI source or performance script reaches them; workspace aliases use the shared import-graph owner. Type-only edges remain covered by static checks. Unrelated inputs retain hourly main and ordinary manual/release validation. Test-only files remain with their changed-test owner. Historical targets retain their existing performance-script availability contract.
- **Windows Node checks** use the existing Windows-sensitive process/path/filesystem/service and test/planner owner map. A selected Windows owner retains the complete package-script-owned Windows inventory and all five existing measured rows, preserving their runner placement, packing, and estimates. Narrow Node target selection never clips that platform family. The fixed smoke set alone does not select Windows; unrelated PRs omit it. Hourly main and ordinary manual/release validation retain the complete inventory.
Owner-selected main proof gates can leave regressions outside their selected
paths undetected until manual or release validation. PR unit/boundary checks do
not replace their process proofs. Push gates compare the triggering event's
`before` commit with its head, covering
every commit in that push. They do not accumulate changes from earlier pushes
whose pending runs were coalesced away (cancelled). If a coalesced or otherwise
cancelled main run never executes a selected owner-path proof, that proof can
remain unexecuted until a later run selects it. Hourly main-tier CI covers its
retained lanes; release-only compositions require applicable full manual or
release validation unless a direct edit selects them. The published-upgrade survivor is
selected on every admitted canonical main run, so it does not depend on a later
owner-path match. Ordinary manual CI and Full Release Validation also select it
independently of changed paths, subject to the target's Docker seed capability.
## Process proof tier
Pull requests and exact-head `release_gate` fallbacks admit Docker seed, QA Smoke,
and built-process verification through their existing owner maps and selected
test targets. Unrelated PRs defer these jobs. Native-host, built Doctor, Discord
attachment, Gateway watch, SQLite lifecycle, and TUI canaries each have an
independent selection flag; choosing one never selects every process proof.
Selected built proofs reuse the one artifact build. Main and ordinary manual CI
retain their established full-proof behavior; Full Release Validation dispatches
that ordinary manual child.
The named `CI_PROOF_TEST_FILES` stay outside ordinary Node rows so their dedicated
artifact/workflow owners execute them. The pre-existing complete FRV and installer
process tours remain manual/release-only; this change does not move those tours
onto PRs. Their faster source/continuation guards retain changed-owner coverage.
Install Smoke predates this selection change as a nightly and manual/release
workflow. Its existing 03:17 schedule and Full Release Validation child are
unchanged; no new PR trigger is added. Installer-owned Node tests still opt in
through the changed-source and direct-test owners. This is an existing timing
exception to hourly main coverage, not a new hourly install proof.
`test/scripts/ci-workflow-planning.test.ts` checks proof-tier selection for PR,
main-push, ordinary manual, and PR-fallback events. The Docker scheduler guard in
`test/scripts/ci-workflow-guards.test.ts`
asserts that the selected `docker_seed_lanes` inventory reaches
`pnpm test:docker:all` through `OPENCLAW_DOCKER_ALL_LANES`, and its release-child
guard verifies that `normal_ci` dispatches `ci.yml` against the exact target.
`test/scripts/ci-workflow-evidence.test.ts` owns QA protocol, evidence-reader,
and maturity handoff checks. Fast CI-routing and cache warming include all three
files when their owners select them; YAML action-pinning checks retain their
original guard owner. The Node planner tests separately assert that hourly main
and Full Release Validation retain moved PR-exempt files, while the existing
named process-proof exclusions remain outside ordinary PR execution.