Skip to content

feat: Run the Rust workspace tests through nextest via command override - #13316

Merged
anthonyshew merged 13 commits into
mainfrom
shew/dogfood-task-command
Jul 9, 2026
Merged

anthonyshew merged 13 commits into
mainfrom
shew/dogfood-task-command

Conversation

@anthonyshew

@anthonyshew anthonyshew commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

PR 3 of the task command override series — the dogfood that motivated the entire design.

Unblocked: 2.10.5-canary.4 is published and knows the flag and field; the pins in this PR reference it exactly.

Why

The question that started all of this was "run cargo nextest run instead of cargo test, without teaching turbo about nextest." This is that, in 15 lines of configuration:

"turborepo-crates#test": {
  "command": ["cargo", "nextest", "run", "--workspace",
              "--exclude", "turborepo-lsp", "--exclude", "turborepo-schema-gen",
              "--exclude", "turborepo-napi", "--no-fail-fast"]
}

Turbo knows nothing about nextest. The excludes mirror today's CI invocations exactly, and .config/nextest.toml is already a hashed workspace input (cargo's hash_input_globs), so nextest config edits invalidate cached test results.

What

  • futureFlags.experimentalTaskCommand enabled
  • turborepo-crates#test command override (scoped — level 2 — replacing the verb table's cargo test --workspace)
  • Vercel canary pins bumped (2.10.5-canary.4, the real published version)

Verification

Locally with a branch-built turbo: turbo run test --filter=turborepo-crates --dry=json resolves the command to the literal argv and hashes it. This also unblocks the parked CI-simplification step 4 (turbo run test --filter=turborepo-crates in the rust CI jobs, replacing the raw nextest invocations).

The synthetic workspace package previously took a magic reserved name
(cargo, then rust). Magic names collide with real packages (a crate
named rust was silently skipped), confuse (rust#test names a package
nobody defined), and overload the toolchain id.

Using Turborepo with Rust now requires naming the workspace in the
root Cargo.toml:

    [workspace.metadata]
    name = "my-workspace"

The key is deliberately un-namespaced: a workspace's name is a
property of the workspace with universally shared semantics, not tool
configuration. No Rust tool names workspaces today, so this mints an
agnostic convention; if cargo ever grows native workspace names, we
read those and deprecate this.

- Missing name (with members present) is a hard, actionable error
  showing the exact TOML to add. Memberless manifests need no name.
- A name colliding with a crate is a discovery error naming both
  parties; the reserved-name skip-with-warning hack is deleted.
- Names follow crate-name shape rules (they become task keys and
  filter arguments); rust/javascript warn as confusing choices.
- This repository names its workspace turborepo-crates.

Also commits PLAN.md: the task command override design this enables,
replacing the completed devEngines plan.
The task command override design doc is working material, not a
repository artifact. Restores the file to its state on main.
PR 1 of the task command override series: the schema, flag, and
validation layer. Nothing consumes the field yet - resolution,
toolchain plumbing, and hashing follow in the next PR.

The command field on task definitions declares the argv a task runs,
replacing the toolchain's own resolution (package.json scripts, Cargo
verb tables). Three JSON shapes, dispatched by a Deserializable
visitor so null and absent stay distinguishable:

- an argv array, executed directly - no shell
- null or [] - an explicit opt-out, valid only in package-scoped
  positions (it cancels a broader default for one package)
- a per-toolchain map keyed by toolchain id, valid only on unscoped
  root tasks (scoped positions already determine their toolchain)

Gated behind futureFlags.experimentalTaskCommand as a hard error, not
a silent strip: ignoring the field would change what executes.

Processing canonicalizes toolchain keys (typescript aliases
javascript; alias + canonical in one map is a conflict error), rejects
unknown keys with a did-you-meant (cargo points at rust), requires
experimentalCargoWorkspaces for the rust key, rejects empty arguments
and $TURBO_EXTENDS$ (a command is atomic - it also merges atomically
across the extends chain), and warns on $VAR-shaped arguments, which
are passed literally because there is no shell.
PR 2 of the task command override series: the five-level resolver,
toolchain placement, executor plumbing, and hashing. The field parsed
and validated in the previous PR now changes what runs.

Resolution lives in one function in the engine builder, consulted
with the chain entries tagged by position (root pkg#task keys and
Package Configuration entries are package-scoped; bare root keys are
unscoped defaults). Precedence, highest to lowest: scoped command,
package-authored script (the new Toolchain::authors_task, which
distinguishes authored definitions from synthesized verb tables),
unscoped default (per-toolchain maps fan out to matching packages),
toolchain resolution.

The resolved override is authoritative in both directions - an argv
executes even where the toolchain defines nothing (library crates
included), an opt-out never executes even where it does - and that
rule is applied uniformly at the three surfaces that ask 'does this
task run': global-deps hashing, the TUI task list, and the executor.

Toolchains place the override in their frame: argv[0] is the program,
nothing prepended, cwd is the package directory, pass-through args
append verbatim. Cargo keeps its serial group when the override still
invokes cargo (the group exists because of cargo's build-directory
lock, a property of the binary). JS overrides bypass the package
manager entirely.

The argv and opt-out flag join TaskHashable (capnp schema change: the
documented one-time hash bust, pinned-hash tests updated), so editing
a command invalidates exactly the affected tasks. Run summaries and
dry-runs display the literal argv.
- resolve_command_override precedence unit test with a stub toolchain
  (the one knob the resolver consults: authors_task)
- Cargo frame: overrides work on library crates, cwd is the crate dir,
  serial group kept iff argv[0] is cargo, dropped otherwise
- JS frame: override bypasses the package manager, argv[0] is the
  program, pass-through args append verbatim
- e2e (basic_monorepo): override replaces script, command-only tasks,
  opt-out no-op, script shadows unscoped default, per-toolchain map
  fan-out, flag-off hard error, command edit invalidates cache
- e2e (cargo_monorepo): rust map key grants tasks to crates, scoped
  override replaces the verb table
- Dry-run snapshot and pinned-hash updates from the capnp schema
  change (the documented one-time hash bust)
- ARCHITECTURE.md: engine-builder section documents the resolver
PR 3 of the task command override series: the dogfood that motivated
the whole design. turborepo-crates#test now declares its command in
turbo.json - cargo nextest run with the same excludes CI uses - via
futureFlags.experimentalTaskCommand. Turbo itself knows nothing about
nextest; this repo's configuration does.

The Vercel canary pins move to the first canary that can parse the new
flag and field (placeholder version; adjust to the actual canary cut
after the series merges).
@vercel

vercel Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
examples-basic-web Ready Ready Preview, Comment, Open in v0 Jul 9, 2026 5:23pm
examples-designsystem-docs Ready Ready Preview, Comment, Open in v0 Jul 9, 2026 5:23pm
examples-gatsby-web Ready Ready Preview, Comment, Open in v0 Jul 9, 2026 5:23pm
examples-kitchensink-blog Ready Ready Preview, Comment, Open in v0 Jul 9, 2026 5:23pm
examples-nonmonorepo Ready Ready Preview, Comment, Open in v0 Jul 9, 2026 5:23pm
examples-svelte-web Ready Ready Preview, Comment, Open in v0 Jul 9, 2026 5:23pm
examples-tailwind-web Ready Ready Preview, Comment, Open in v0 Jul 9, 2026 5:23pm
examples-vite-web Ready Ready Preview, Comment, Open in v0 Jul 9, 2026 5:23pm
turbo-site Ready Ready Preview, Comment, Open in v0 Jul 9, 2026 5:23pm
turborepo-eve-agent Ready Ready Preview, Comment, Open in v0 Jul 9, 2026 5:23pm

cargo test stops at the first failing test binary by default, and the
known-flaky graceful_shutdown_test sorts before run_caching,
run_summary, and unnamed_packages_test - so the earlier snapshot pass
never reached them. Caught by CI; regenerated with --no-fail-fast.
The commandOverride list was initialized unconditionally, and an
explicitly initialized empty list is a non-null pointer in capnp -
encoded differently from the default null pointer, which defeated
canonical form's trailing-default truncation and shifted every task
hash for every user on upgrade.

Guard both new fields the way external_deps_hash already is: only
written when set. Canonicalization then truncates them away and every
existing task hash is byte-identical to before - only tasks that
actually use a command override hash differently. Reverts the pinned
hashes and snapshots the unguarded encoding forced; they now pass at
their original values, which is the proof of stability.
Base automatically changed from shew/task-command-resolver to main July 9, 2026 15:47
@anthonyshew
anthonyshew marked this pull request as ready for review July 9, 2026 17:11
@anthonyshew
anthonyshew requested a review from a team as a code owner July 9, 2026 17:11
The nextest invocation and its excludes now live in turbo.json as the
turborepo-crates#test command, so the three rust_test jobs invoke
turbo run test --filter=turborepo-crates instead of duplicating the
command - CI and local runs are now identical by construction.

turbo.json joins the rust change-detection pattern: it defines what
the Rust test task runs, so editing it must re-run the Rust tests.
@anthonyshew
anthonyshew merged commit 6711bbc into main Jul 9, 2026
56 of 57 checks passed
@anthonyshew
anthonyshew deleted the shew/dogfood-task-command branch July 9, 2026 17:55
anthonyshew added a commit that referenced this pull request Jul 9, 2026
The rust_test jobs run through turbo now (#13316) but had no Remote
Cache credentials, so neither task caching nor the compile cache could
engage. Wire in the OIDC exchange on push events (matching
check-lockfiles and js_native_packages) and set TURBO_CACHE, then flip
futureFlags.experimentalCargoSccache: with credentials present, turbo
serves the Remote Cache as sccache's storage backend and routes rustc
invocations through the embedded wrapper - proven at 3.4x on the bench
runs, with the fail-soft layers (runtime status gate, local-compile
fallback) making credential or storage trouble degrade to a plain
build instead of failing it.

Job timeouts padded for compile-cache populate runs, which are slower
than plain builds.
anthonyshew added a commit that referenced this pull request Jul 9, 2026
…13292)

## Why

The Rust CI jobs run through turbo now (#13316) — but with no Remote
Cache credentials, every run is a cold `cache miss` and the sccache
compile cache (whose 3.4× speedup was proven on the [bench
runs](https://github.com/vercel/turborepo/actions/runs/28874070404):
407/407 compile-unit hits) sits dormant behind its
`RemoteCacheStatus::Enabled` gate. This PR — originally just the flag
flip, now adapted to the post-OIDC, post-task-command world — turns both
caches on.

## What

- **`futureFlags.experimentalCargoSccache: true`** — with credentials
present, turbo serves the Remote Cache as sccache's webdav backend and
routes rustc invocations through the embedded wrapper. CI-only by
design; local builds keep cargo's incremental compilation.
- **Rust jobs get Remote Cache credentials**: the OIDC exchange on push
events + `TURBO_CACHE` (push: `remote:rw`, PRs: `local:rw`) — the exact
pattern `check-lockfiles` and `js_native_packages` already use. This
also gives `turborepo-crates#test` remote **task** caching on main.
- **Timeouts padded** (15→25 job / 20 step): compile-cache populate runs
are slower than plain builds (artifact writes).

## How

Safety posture, all previously landed: the proxy only starts when the
runtime remote-cache status is actually `Enabled` (bad credentials → no
proxy, plain build); `SCCACHE_IGNORE_SERVER_IO_ERROR=1` + the fork's
local-compile fallback make mid-run storage trouble degrade instead of
fail; ambient `CARGO_INCREMENTAL=0` from `setup-environment` composes
with the injection. The wrapper is the released turbo binary itself
(`2.10.5-canary.4`) — nothing to install.

**Verification is a push-to-main property**: PR runs use the local cache
(fork PRs can't mint OIDC tokens), so this PR's own CI proves only that
the jobs still pass. After merge, the first main push with Rust changes
populates; the second shows `turborepo-crates#test` either as a remote
task-cache hit or with compile-unit hits in the cargo output.
anthonyshew pushed a commit that referenced this pull request Jul 10, 2026
## Release v2.10.5-canary.5

> [!CAUTION]
> Versioned docs aliasing FAILED. [View
logs](https://github.com/vercel/turborepo/actions/runs/29092745941)

### Changes

- release(turborepo): 2.10.5-canary.4 (#13325) (`6626261`)
- feat: Run the Rust workspace tests through nextest via command
override (#13316) (`6711bbc`)
- feat: Engage the Remote Cache and sccache compile cache for Rust CI
(#13292) (`1ae2065`)
- ci: Pin GitHub Actions to full commit SHAs (#13143) (`ad614d6`)
- fix: Make npm prune rehoisting deterministic and complete (#13323)
(`2dac737`)
- feat: Report incremental cache reuse in the run summary (#13327)
(`9f3d24a`)
- chore: Remove planning docs (#13329) (`5cd1d02`)
- docs: Migrate docs site to @vercel/geistdocs package (#13320)
(`5517bb2`)
- ci: Update Remote Cache action (#13330) (`4a39887`)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
anthonyshew pushed a commit that referenced this pull request Jul 13, 2026
## Release v2.10.5

> [!CAUTION]
> Versioned docs aliasing FAILED. [View
logs](https://github.com/vercel/turborepo/actions/runs/29267191097)

### Changes

- perf: Evaluate simple include globs without wax compilation (#13285)
(`189897a`)
- chore: Dogfood native Cargo support in this repository (#13283)
(`42f067b`)
- release(turborepo): 2.10.4 (#13286) (`f2fce38`)
- ci: Remove dead sccache configuration (#13289) (`558df3f`)
- feat: Serve the Remote Cache as an sccache backend for Cargo tasks
(#13288) (`0d9803f`)
- fix: Reject output path traversal (#13290) (`733ccca`)
- fix: Disable the sccache proxy when remote cache use is off (#13291)
(`3249e22`)
- feat: Embed sccache so the Cargo compile cache needs no installation
(#13293) (`b6d0035`)
- release(turborepo): 2.10.5-canary.1 (#13294) (`e6cd498`)
- fix: Pin a flag-aware turbo canary for the eve-agent deployment
(#13295) (`e60a4bd`)
- fix: Make the sccache compile cache actually cache (#13296)
(`2ac099c`)
- release(turborepo): 2.10.5-canary.2 (#13297) (`e6e6fb6`)
- fix: Stop ambient CARGO_INCREMENTAL from suppressing compile cache
injection (#13298) (`784af75`)
- fix: Never let compile cache storage failures fail the build (#13299)
(`a6fc6c5`)
- test: Add outputs path-traversal negative-case regression tests
(#13300) (`2ff6df9`)
- release(turborepo): 2.10.5-canary.3 (#13302) (`7e44a29`)
- ci: Authenticate to Remote Cache via OIDC token exchange (#13303)
(`a86839c`)
- fix: Keep pnpm patches with version range keys during prune (#13307)
(`3c27a89`)
- test: Scrub ambient turbo configuration from integration test children
(#13306) (`b03d0d6`)
- fix: Show toolchain tasks in the TUI and never run in silence (#13308)
(`d7622b6`)
- ci: Fix change detection on push events (#13304) (`eec3d61`)
- refactor: Rename the Cargo toolchain id to rust (#13311) (`a548b02`)
- fix: Remove extraneous bun.lock entries during prune (#13317)
(`382e9f5`)
- feat: Require a user-declared name for the Cargo workspace package
(#13312) (`5f01746`)
- feat: Parse and validate the task command field (#13313) (`389ea49`)
- chore: Harden turbo-vsc to invoke turbo without a shell (#13319)
(`4a19b6e`)
- feat: Resolve and execute task command overrides (#13315) (`a549baa`)
- release(turborepo): 2.10.5-canary.4 (#13325) (`6626261`)
- feat: Run the Rust workspace tests through nextest via command
override (#13316) (`6711bbc`)
- feat: Engage the Remote Cache and sccache compile cache for Rust CI
(#13292) (`1ae2065`)
- ci: Pin GitHub Actions to full commit SHAs (#13143) (`ad614d6`)
- fix: Make npm prune rehoisting deterministic and complete (#13323)
(`2dac737`)
- feat: Report incremental cache reuse in the run summary (#13327)
(`9f3d24a`)
- chore: Remove planning docs (#13329) (`5cd1d02`)
- docs: Migrate docs site to @vercel/geistdocs package (#13320)
(`5517bb2`)
- ci: Update Remote Cache action (#13330) (`4a39887`)
- release(turborepo): 2.10.5-canary.5 (#13331) (`f699719`)
- ci: Remove path-based workflow scheduling (#13332) (`84f2b2c`)
- fix: Resolve EADDRINUSE in kitchen-sink api dev script (#13328)
(`5886c71`)
- ci: Disable telemetry messages in workflows (#13334) (`2218dea`)
- fix: Prevent Cargo run tasks from being cached (#13335) (`68f449b`)
- fix: Isolate Cargo cache by host platform (#13337) (`c71f5cd`)
- fix: Continue TUI text selection beyond viewport (#13338) (`9cbfd90`)
- ci: Dogfood Cargo target restoration (#13336) (`8ad90a7`)
- fix: Require current Cargo lockfiles for caching (#13339) (`3a3d381`)
- fix: Reject unsupported Cargo local packages (#13340) (`708d656`)
- fix: Allow outputs outside package roots (#13342) (`de6a0d3`)
- fix: Resolve Cargo lock dependencies by source (#13343) (`2223a33`)
- docs: Update Cargo workspace support (#13349) (`241ada8`)
- fix: Build Ghostty for baseline CPUs (#13352) (`a2a04cc`)
- fix: Preserve watch rerun semantics for task inputs (#13351)
(`6ed5eab`)
- fix: Isolate Command Overrides From Toolchain Cache I/O (#13354)
(`e76f0b4`)
- release(turborepo): 2.10.5-canary.6 (#13355) (`f82e2c7`)
- fix: Include patched Ghostty crate in Cargo workspace (#13357)
(`b3cd7a1`)
- ci: Shard Rust tests across runners (#13356) (`ef122d9`)
- fix: Hash Cargo build environment inputs (#13348) (`d533266`)
- fix: Synchronize Cargo prune Docker lockfile (#13350) (`ed17249`)
- chore: Update agents app Eve dependency (#13364) (`38aa7d2`)
- fix: Preserve Yarn package extension ranges when pruning (#13363)
(`6ed2fb4`)
- refactor: Add `Toolchain` output availability (#13360) (`82bcfb6`)

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

This branch was successfully deployed

1 active deployment
Preview – turborepo-eve-agent 0f592da7 Deployed Jul 9, 2026 by vercel[bot]
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