perf(ci): gate QA smoke by smoke-visible changes and isolate the prompt snapshot lane (#108202)

* perf(ci): diff-gate QA smoke by CLI import graph and isolate prompt snapshots lane

* fix(ci): run prompt snapshot lane unconditionally

* fix(ci): treat QA lane orchestration files as QA-impacting
This commit is contained in:
Peter Steinberger
2026-07-15 02:51:19 -07:00
committed by GitHub
parent 580938097f
commit 9c8a006d0c
5 changed files with 76 additions and 26 deletions

View File

@@ -44,19 +44,41 @@ export function hasBuildArtifactAffectingChange(changedPaths) {
return changedPaths.some((changedPath) => !isTestOnlyPath(changedPath));
}
const QA_SMOKE_CRITICAL_RE =
/^(?:extensions\/qa-lab|qa)\/|^scripts\/(?:build-all\.mjs|package-openclaw-for-docker\.mjs)$|^(?:package\.json|pnpm-lock\.yaml|npm-shrinkwrap\.json)$|^ui\//u;
// Surfaces the CI smoke scenarios exercise outside the core runtime import
// graph: the qa-lab harness and scenario data, the packaged-CLI build inputs,
// the control UI (playwright scenario), the two channels the smoke profile
// drives (matrix, telegram), and workspace packages whose package-specifier
// imports the relative import graph cannot see. The QA lane's own
// orchestration (this planner, the CI workflow, composite actions) is also
// QA-impacting: changes to the gate must not be able to skip the gated lane.
const QA_SMOKE_SURFACE_RE =
/^(?:extensions\/(?:matrix|qa-lab|telegram)|packages|qa|ui)\/|^scripts\/(?:build-all\.mjs|package-openclaw-for-docker\.mjs)$|^scripts\/lib\/ci-changed-node-test-plan\.mjs$|^\.github\/(?:workflows\/ci\.yml$|actions\/)|^(?:openclaw\.mjs|package\.json|pnpm-lock\.yaml|npm-shrinkwrap\.json|pnpm-workspace\.yaml|tsdown\.config\.ts)$/u;
// The smoke profile runs the packaged CLI end to end, so its runtime blast
// radius is exactly the CLI entry's import graph (dynamic imports included).
const QA_SMOKE_RUNTIME_ENTRY = "src/index.ts";
/**
* True when a changed path touches the QA smoke packaging/scenario surface.
* Deliberate product tradeoff: targeted PRs outside this surface skip QA smoke
* and rely on import-selected shards plus build-artifact CLI smokes. Targeting
* only fires for narrow non-SDK-impacting diffs, and QA smoke still runs on
* every node-scoped main push, so a missed regression breaks main visibly
* instead of shipping silently.
* True when a changed path can influence the QA smoke scenarios: it touches
* the smoke surface directly, or the packaged CLI's import graph reaches it.
* Diffs outside both are invisible to the smoke profile, so the manifest may
* skip that lane regardless of whether test targeting fired.
*/
export function hasQaSmokeAffectingChange(changedPaths) {
return changedPaths.some((changedPath) => QA_SMOKE_CRITICAL_RE.test(changedPath));
export function hasQaSmokeAffectingChange(changedPaths, options = {}) {
const cwd = options.cwd ?? process.cwd();
if (changedPaths.some((changedPath) => QA_SMOKE_SURFACE_RE.test(changedPath))) {
return true;
}
const sourcePaths = changedPaths.filter(
(changedPath) => changedPath.startsWith("src/") && !isTestFileTarget(changedPath),
);
if (sourcePaths.length === 0) {
return false;
}
// Deleted sources cannot be graphed; fail safe to running the smoke lane.
if (sourcePaths.some((changedPath) => !existsSync(path.join(cwd, changedPath)))) {
return true;
}
return hasImportGraphImpactOnTargets(sourcePaths, [QA_SMOKE_RUNTIME_ENTRY], cwd);
}
function createBoundaryShard() {

View File

@@ -15,8 +15,10 @@ const POST_FORCE_KILL_WAIT_MS = 250;
const MAX_TIMER_TIMEOUT_MS = 2_147_000_000;
/** Ordered list of supplemental boundary checks used by CI sharding. */
// prompt:snapshots:check is intentionally absent: it regenerates snapshots by
// running real embedded-agent turns (~2min) and owns a dedicated CI lane
// (check-prompt-snapshots) so no boundary shard carries that wall clock.
export const BOUNDARY_CHECKS = [
["prompt:snapshots:check", "pnpm", ["prompt:snapshots:check"]],
["plugin-extension-boundary", "pnpm", ["run", "lint:plugins:no-extension-imports"]],
["lint:docker-e2e", "pnpm", ["run", "lint:docker-e2e"]],
["lint:tmp:no-random-messaging", "pnpm", ["run", "lint:tmp:no-random-messaging"]],