Rendered from specs/S09-ci-workflow-scoping.md — edit it there, not here.
S09 — CI Workflow Scoping
Overview
ci.yml's build/security/knip/integration jobs and codeql.yml's analyze job all exercise the TypeScript/JS build & test surface (lint, tsc, webpack, coverage, Trivy, knip, the two-OS integration suite, CodeQL). None of that can be broken by a docs-only or spec-only change, yet every PR — including one that only edits specs/traceability.json or a docs/** page — paid for all of it. Meanwhile the one check that does matter for exactly that kind of PR, requirement/documentation traceability drift, ran nowhere in CI at all — the opposite of what was needed.
This spec scopes the expensive jobs to diffs that could plausibly break them, while keeping the cheap traceability checks unconditional so they still catch drift on the PRs most likely to introduce it.
Requirements
Always-On Traceability
- S09-SR-01
check:traceabilityandcheck:doc-traceabilityMUST run in CI (ci.yml) on every push and pull request, independent of which paths changed — these are the checks a docs/spec-only PR is most likely to need (S07-SR-08's guarantee extends tocheck:traceabilitytoo, not just the doc checker).
Path-Scoped Jobs
- S09-SR-02
ci.yml'sbuild(lint, type-check, compile, coverage, maturity check, dependency audit),security(Trivy),knip, andintegrationjobs, andcodeql.yml'sanalyzejob, MUST be skipped when the triggering diff touches none of:src/**,scripts/**,package.json,package-lock.json,webpack.config.js,tsconfig*.json,eslint.config.js,knip.json,stryker.config.json,commitlint.config.js,playwright.e2e.config.ts, or.github/workflows/**. A diff with no resolvable base commit (new branch, history rewrite —beforeis unset or absent from history) MUST default to running everything rather than skipping. - S09-SR-03 The skip in SR-02 MUST be implemented as a job-level
if:condition fed by a sharedchangesjob's output, not as a workflow-levelon.push.paths/on.pull_request.paths(orpaths-ignore) trigger. Rationale: a path-filtered trigger never starts the workflow at all on a non-matching diff, and if that workflow's job is configured as a required status check in branch protection, GitHub shows it as permanently pending ("Expected — Waiting") instead of passing — it blocks merges rather than skipping cleanly. A job skipped viaif:still reports a (skipped, passing) conclusion for that job, so required-status-check semantics hold regardless of branch-protection configuration. - S09-SR-04
codeql.yml's weeklyschedulerun MUST always perform a full analysis regardless of SR-02's path filter — a scheduled run has no PR diff to gate against, and periodic full scans are the point of the schedule trigger.
Non-Functional Requirements
- S09-NFR-01 The diff detection in SR-02 MUST use only
git/shell commands already available on GitHub-hosted runners (no third-party path-filtering action), consistent with this project's preference for standards over added vendor tooling (AGENTS.md, "Agnosticity & standardization"). - S09-NFR-02 The path set and diff logic MUST be defined exactly once, in
scripts/ci-detect-source-changes.sh, and referenced identically by everychangesjob (ci.yml,codeql.yml) rather than duplicated inline in workflow YAML — a second workflow needing the same scoping calls the same script instead of copying the pattern.
Out of Scope
- Path-scoping workflows other than
ci.ymlandcodeql.yml(docs.yml,mutation.yml,scorecard.yml,snyk.yml,maturity-refresh.yml,refresh-gifs.yml) — these already trigger on their own schedule/workflow_dispatch/workflow_runconditions, not on every PR, so they don't have the cost problem this spec addresses. - Promoting
integrationto a required check (tracked separately inS08-SR-08).
Acceptance Criteria
- A PR touching only
docs/**,specs/**, or root*.mdfiles shows thetraceabilityjob passing andbuild/security/knip/integration(and CodeQL'sanalyze) reporting as skipped, not pending. - A PR touching
src/**runs every job as before this spec. - A PR that only edits
specs/traceability.jsonstill runscheck:traceabilityandcheck:doc-traceability. - The Monday 03:27 UTC
codeql.ymlschedule run always performs a full analysis. ci.ymlandcodeql.ymleach invokescripts/ci-detect-source-changes.shrather than defining the path pattern inline; changing the pattern is a one-file edit.
Relation to Existing Specs
- Closes the enforcement gap the investigation behind this spec found:
check:doc-traceability's CI guarantee (S07-SR-08) is extended here tocheck:traceabilityas well, and both are made structurally unconditional rather than steps inside a job that could later be scoped away. - Complements S08's CI-integration requirements (
S08-SR-08/09) for theintegrationjob specifically; this spec governs when that job (and the otherci.yml/codeql.ymljobs) run at all. - This is a CI/tooling change; per
specs/README.md's traceability workflow such changes don't strictly require a spec entry ("say so instead"), but it's specified here anyway since the design (job-levelif:vs. workflow-level path triggers) has a real correctness trade-off worth recording, not just a mechanical config tweak.
Requirement traceability
Status of each requirement in specs/traceability.json. Test coverage is auto-discovered from [ID] tags in test titles and is not listed here.
| Requirement | Status | Implementation | Note |
|---|---|---|---|
S09-NFR-01 | implemented | .github/workflows/ci.yml, .github/workflows/codeql.yml | diff detection uses plain git diff/grep, no third-party path-filter action |
S09-NFR-02 | implemented | scripts/ci-detect-source-changes.sh, .github/workflows/ci.yml, .github/workflows/codeql.yml | single script shared by both changes jobs instead of duplicated inline shell |
S09-SR-01 | implemented | .github/workflows/ci.yml | traceability job runs unconditionally on every push/PR |
S09-SR-02 | implemented | .github/workflows/ci.yml, .github/workflows/codeql.yml | build/security/knip/integration/analyze jobs gated by the changes job output |
S09-SR-03 | implemented | .github/workflows/ci.yml, .github/workflows/codeql.yml | job-level if: conditions, not workflow-level paths/paths-ignore triggers |
S09-SR-04 | implemented | .github/workflows/codeql.yml | analyze's if: always() && (schedule || changes) keeps the weekly cron unconditional |