Skip to content

ci: skip build and test jobs for documentation-only changes - #263

Merged
eric8810 merged 2 commits into
masterfrom
ci/skip-build-and-test-jobs-for-docs-only-changes
Oct 15, 2026
Merged

eric8810 merged 2 commits into
masterfrom
ci/skip-build-and-test-jobs-for-docs-only-changes

Conversation

@cunninghamcard-bit

@cunninghamcard-bit cunninghamcard-bit commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Requested by li · project thread

Before: every pull request ran all 24 jobs, five of them on macOS, whatever it changed. A documentation-only pull request or master push ran the whole workflow too, about 92 runner-minutes; the four RFC and roadmap merges on 2026-10-15 queued four full runs one after another. The free plan runs at most five macOS jobs at a time across all open pull requests, so one change to a single binding waited behind everybody's macOS legs.

After: a pull request runs only the jobs its change can affect, on Linux unless it touches something platform-specific.

The pull request changes Jobs that run
Only documentation (top-level Markdown, docs/, rfc/, assets/) none; CI result passes
Only bindings/<name>/ that binding's jobs and contract tests
aimux-ffi/ Rust core, aimux-ffi, the bindings that link the C ABI (Java, Kotlin, Go, Flutter and its example, Swift), contract tests
Anything else (Rust crates, tools, scripts, fixtures, Cargo manifests, workflows, any .gitattributes) every job

docs/api/providers.md is generated and checked by contract tests, so it counts as code. Node and Python link the Rust crates directly, not the C ABI, so an aimux-ffi/ change does not run them.

The macOS and Windows legs of the Node, aimux-ffi, Go and Flutter example jobs are added only when the change touches the C ABI, the Node, Go, Flutter or Swift bindings, a Cargo manifest, a build script, a .gitattributes, the toolchain or the workflows. The Rust code itself is platform-neutral (rustls, no platform cfg outside tools/). A change to the shared Rust code still runs the Windows x64 Node tests, the only job that runs that code on Windows. Swift contract tests are macOS-only and run whenever their row says so; they caught a stack overflow no Linux job hit.

Pushes to master run everything when code changed, as the AI SDK does, so a platform problem shows up on master at the latest. A documentation-only master push still runs nothing. A run with no base to compare with runs everything.

This follows RFC-0043 §9.2: the summary check still runs, a version-controlled path rule decides what is needed, and a skipped job never counts as a pass by itself. It is based on master so it can land before #262; #262 then merges master and applies the same rules to its reorganized jobs.

How

  • A changes job diffs the change against its base (the first parent of the test merge commit for a pull request, the previous head for a push) and writes one JSON map, job name to true or false, plus the matrices of the jobs that have macOS or Windows legs. Matrices are built there because a static exclude cannot drop a leg that include adds back.
  • Every build and test job has needs: changes and runs when its entry in the map is true.
  • CI result always runs. It passes only when changes succeeded and every job either succeeded or was skipped because the map says it is not needed. A failure, a cancellation, or any other skip fails it, and so does a job missing from the map. It is the one check to require in branch protection.

This is the pattern ruff, uv, rust-analyzer and pydantic-core use. A workflow-level paths filter, as in node or polars, would skip the whole run and leave a required check waiting, which RFC-0043 rules out.

Why skipping is safe

  • No binding reads another binding: outside its own directory a binding reads only the Rust crates, contract-tests/, aimux-ffi/ and build output; mentions of other bindings are comments. bindings/node and bindings/python are separate Cargo workspaces that depend on aimux-core, aimux-providers and aimux-provider-utils, not on aimux-ffi, and the Rust workspace does not include any binding.
  • No platform cfg in the workspace crates outside tools/ (which this workflow only ever built on Linux), and TLS is rustls. A change to the shared Rust code still runs on all three systems: every job on Linux, the Node x64 tests on Windows, and the Swift contract tests on macOS.
  • Replay of history: all 706 completed runs of this workflow from 2026-07-31 to 2026-10-12, with each pull request run's change fed through the new rule. Of the 364 jobs that failed on pull requests, 361 would still run. The other three were not caused by their pull requests: a Windows runner lost on a documentation-only pull request, and two runs on a pull request that touched only bindings/node, bindings/python and the changelog, while master was already red on a clippy error in tools/aimux-web (master's own push run 31920814130 failed the same way).
  • What a pull request no longer shows: a job turning red for reasons outside the change (a new release of a dependency, since Cargo.lock is not committed, or a runner image update) is not seen on a pull request that does not run that job. Pushes to master run everything, so it shows up there.

Checks

cunninghamcard-bit and others added 2 commits October 15, 2026 15:34
No job reads README, ROADMAP, the RFCs, docs/ or assets/, yet every
documentation pull request and every merge of one to master ran the
whole workflow: four documentation merges on 2026-10-15 queued four
full runs behind each other, after the pull requests had run it once
per push.

A small "changes" job diffs the push or pull request against its base
(the test merge commit's first parent for a pull request, the previous
head for a push) and the build and test jobs run only when something
outside the documentation paths changed. docs/api/providers.md counts
as code: contract-tests checks it against its generator. Without a base
to compare with, it counts as a code change.

A workflow-level path filter would have skipped the whole run, which
leaves a required check waiting forever. Instead "CI result" always
runs and passes only when every job succeeded or was skipped because
"changes" found no code change, as RFC-0043 §9.2 asks for
documentation-only pull requests. It is the one check to require once
branch protection requires any.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0112xDERMUyD5JALkFhnsdWS
…tform-specific

Every code pull request ran all 24 jobs, five of them on macOS, whose
runners the free plan limits to five at a time across all open pull
requests. Most pull requests change neither the platform-specific parts
nor more than one binding.

"changes" now decides per job, as one JSON map that every job and
"CI result" read:
- documentation only: nothing, as before;
- only inside bindings/<name>/: that binding's jobs (the Rust workspace
  does not read the bindings);
- aimux-ffi/: the Rust jobs and every binding that links the C ABI
  (Node and Python link the Rust crates directly);
- anything else: every job.
Contract tests run for any code change, because they check generated
files that live in every binding.

On pull requests the jobs run on Linux, and the macOS and Windows legs
of the Node, aimux-ffi, Go and Flutter example jobs are added only when
the change touches the C ABI, the Node, Go, Flutter or Swift bindings,
a Cargo manifest, a build script, .gitattributes, the toolchain or the
workflows; the Rust code itself uses rustls and no platform cfg outside
tools/. A change to the shared Rust code still runs the Windows x64
Node tests, the only job that runs that code on Windows, where I/O
errors and refused connections behave differently. Swift
contract tests, macOS-only, run whenever they are needed: they caught a
stack overflow no Linux job hit. Pushes to master run everything, as
the AI SDK does, so a platform problem surfaces on master at the latest.

"CI result" now accepts a skipped job only when "changes" marked it as
not needed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0112xDERMUyD5JALkFhnsdWS
@eric8810
eric8810 merged commit b5c97d5 into master Oct 15, 2026
26 checks passed
cunninghamcard-bit pushed a commit that referenced this pull request Oct 16, 2026
…low-ups, the RFC-0043 docs, the RFC-0036 §12.2 back-link, a test race fix and the review follow-ups

The restored lower-layer tests take this layer's types and JSON: the
trace suite's stream file is a `LanguageModelFile`, the repeated-id test
reads the tool result's `input` from the AI SDK JSON, and the FFI
recoverable-frame test finds the error part by its `type`. The Gemini
media-type check keeps this layer's `LanguageModelFile`, and the
alignment doc's decision paragraph keeps this layer's camelCase text with
the Anthropic adapter added.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants