github.com-vercel-turborepo
all · 13 devs · built 2026-07-10
Repository snapshot
Monthly reports
Highlights
- Expanded *package manager* compatibility with first-class support for *Aube* [0f780067 · Anthony Shew] and *Nub* [782b168a · Colin McDonnell], including `create-turbo` integration [82b1cf13 · Anthony Shew] and JavaScript tooling support [53cddc8b · Anthony Shew].
- Major *TUI* refactoring by migrating virtual terminal emulation from `vt100` to `libghostty-vt`, significantly enhancing capabilities and user experience [b22d3b43 · Anthony Shew].
- Introduced *deferred hashing* for task inputs, providing precise control over cache key generation and improving caching reliability for generated files [4ebb50ff · Anthony Shew].
- Enhanced *package manager declaration* handling by supporting `devEngines.packageManager` across Rust [e48c56c4 · Lachlan Heywood] and JavaScript [9d94ae50 · Anthony Shew] tooling, including codemod updates [27ca9621 · Anthony Shew] and Corepack integrity hash support [6bf6a67a · 곽지욱].
- Critical bug fixes for *Windows MSVC* builds by statically linking `ghostty-vt` [e950d713 · Anthony Shew] and ensuring essential environment variables (`COMSPEC`, `PATHEXT`) are passed through [6988692e · Anthony Shew].
- Performance optimization by avoiding unnecessary *git index path normalization*, leading to a 3-5% speed improvement in `turbo run` planning [417c28dc · Anthony Shew].
- Improved *watch mode* stability with fixes for graceful shutdown [5ba8917e · Anthony Shew], correct restarting of deferred hash consumers [6dccf5ab · Anthony Shew], and respecting task inputs for interruptible tasks [0220b355 · Anthony Shew].
- Fixed *Bun lockfile pruning* to correctly preserve 3-level nested entries, preventing `bun install` failures in pruned environments [799fa5ac · Anthony Shew].
- Layed groundwork for an *automated examples maintenance agent*, establishing infrastructure for future automation of example upkeep [05ac7b5b · Anthony Shew].
Observations
- The *grow score* increased by +40% (14 current vs 10 5-month average), indicating a strong focus on new features and capabilities despite a lower commit volume.
- The *waste score* decreased by -31% (11 current vs 16 5-month average), suggesting improved code quality and reduced rework compared to the previous 5 months.
- Commit volume was significantly lower, with 139 commits this month compared to the 264-commit 5-month average (-47%), suggesting a more deliberate and impactful development cycle.
- A consistent pattern of refinement and bug fixing was observed in *package manager integration*, with multiple commits ([f469ed82 · Anthony Shew], [658f6077 · Anthony Shew], [50546e68 · Anthony Shew], [d199c53c · Anthony Shew], [6bf6a67a · 곽지욱]) addressing parsing, normalization, and lockfile support.
- The *watch mode* and *caching mechanisms* continued to be areas of active development and stabilization, evidenced by several fixes and enhancements related to graceful shutdown, deferred hashing, and task input handling ([5ba8917e · Anthony Shew], [6dccf5ab · Anthony Shew], [0220b355 · Anthony Shew], [65175fe0 · Anthony Shew]).
- Significant effort was dedicated to standardizing *package manager declaration* through `devEngines.packageManager`, involving updates across multiple packages and documentation [9d94ae50 · Anthony Shew], [e48c56c4 · Lachlan Heywood], [f8787196 · Anthony Shew], [27ca9621 · Anthony Shew], [6598f7cb · Anthony Shew].
- Several commits focused on *CI/CD stability* and *build environment consistency*, including pinning macOS Rust builds [16fe0a4b · Anthony Shew] and ensuring Zig toolchain installation for LSP builds [c1bc71f8 · Anthony Shew].
Performance over time
ETV stacked by Growth, Maintenance and Fixes — 90-day moving average, normalized to ETV / month.
Average performance per developer
ETV per active developer per month — 30-day moving average.
Active developers over time
Unique developers committing each day — 90-day moving average.
Knowledge concentration
How dependent is this repo on a small number of contributors? Higher top-1 share = higher key-person risk.
Anthony Shew owns 88.9 % of commits.
Top contributors
Most impactful commits
Top 20 by ETV in the all-time window.
- 7.6ETVfeat: Migrate TUI virtual terminals from vt100 to Ghostty (#13135) <!-- CURSOR_AGENT_PR_BODY_BEGIN --> ### Description Migrates Turborepo's TUI task output panes from the vendored `turborepo-vt100` crate to [Ghostty](https://ghostty.org)'s `libghostty-vt` terminal emulator. **Why Ghostty?** Ghostty's VT library is actively maintained, supports modern terminal features, and is the same engine powering the Ghostty terminal. This replaces the older in-repo vt100 implementation and removes the `tui-term` dependency. #### Architecture - **`turborepo-ghostty-sys`** — In-repo FFI against `libghostty-vt`. `build.rs` fetches a pinned Ghostty commit and compiles via Zig 0.15.2; `bindings.rs` is checked in for docs.rs. - **`turborepo-ghostty`** — Safe Rust wrappers (adapted from [libghostty-rs](https://github.com/Uzaaft/libghostty-rs)) plus Turborepo-specific `Parser` and ratatui `TerminalWidget` (adapted from [ratatui-ghostty](https://codeberg.org/jint/ratatui-ghostty)). - **`turborepo-ui`** — Task pane rendering, scroll, and selection updated to use the Ghostty-backed widget. TUI dependencies are gated behind a `tui` feature so lightweight consumers (e.g. `turborepo-telemetry`, `@turbo/repository`) don't pull in Ghostty/Zig. **Removed:** `crates/turborepo-vt100`, `tui-term` dependency. #### Notable fixes included - **Selection/copy** — Ghostty selection grid refs go stale as output streams; viewport endpoints are now persisted and refreshed before render/copy. Selected cells are highlighted during drag. - **`Send` for tokio** — Static Ghostty allocator objects are marked `Send + Sync` so the TUI `App` can be moved into `tokio::task::spawn`. - **CI / Zig** — `setup-zig` detects host arch (`aarch64` vs `x86_64`), uses the correct Windows `.zip`, and sets isolated `ZIG_GLOBAL_CACHE_DIR` to avoid stale linker failures on reused macOS runners. - **`@turbo/repository`** — Gating Ghostty behind `turborepo-ui`'s `tui` feature prevents the NAPI package from building `libghostty-vt` unnecessarily. #### Build requirements Zig 0.15.2+ is now required to build the `turbo` binary (documented in `CONTRIBUTING.md`). CI installs it via `.github/actions/setup-zig`. Optional env vars for local Ghostty development: `GHOSTTY_SOURCE_DIR`, `GHOSTTY_ZIG_SYSTEM_DIR`, `TURBOREPO_GHOSTTY_SYS_OPTIMIZE`. #### Attribution Vendored code is documented in the crate READMEs. Most safe wrappers and FFI patterns come from libghostty-rs; ratatui integration from ratatui-ghostty; `Parser` and Turborepo UI glue are original. ### Testing Instructions 1. Build and run the TUI: `cargo build` then `turbo run <tasks> --ui=tui` in a monorepo with long-running tasks. 2. **Selection/copy** — Click-drag across task output; verify highlight appears and copied text is correct after more output streams in. 3. **Scroll** — Mouse wheel and keyboard scrolling through task scrollback. 4. **Stdin** — Interactive tasks that read from stdin still work in the focused pane. 5. **Resize** — Resize the terminal while the TUI is open; panes should reflow cleanly. 6. **Non-TUI paths** — `turbo run` without `--ui=tui` and `@turbo/repository` tests should work without requiring a Zig build. <!-- CURSOR_AGENT_PR_BODY_END --> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Anthony Shew <anthonyshew@users.noreply.github.com>Anthony Shew · b22d3b43 · 2026-06-25
- 2.9ETVchore: Add `turborepo-log` crate to improve our logging situation (#12285) ## Summary - Introduces `turborepo-log`, a new crate for structured user-facing log events (warnings, errors, informational output), deliberately separate from `tracing` which remains for developer diagnostics. - The global `Logger` dispatches events to pluggable `LogSink` implementations. Ships with two built-in sinks: `CollectorSink` (in-memory buffer for post-run summaries) and `FileSink` (newline-delimited JSON with optional size limiting). - All user-supplied strings are sanitized against ANSI escape sequences and control characters to prevent terminal injection. ## Why Today, user-facing messages in `turbo run` are scattered across ad-hoc `println!`/`eprintln!` calls and tightly coupled to `turborepo-ui`'s terminal rendering. This makes it hard to capture warnings for post-run summaries, write them to structured log files, or route them to the TUI without touching rendering code. `turborepo-log` sits at the bottom of the dependency graph (no dependency on `turborepo-ui`) and provides a clean boundary: subsystems emit structured events, sinks decide how to present or store them. A future terminal sink in `turborepo-ui` can implement `LogSink` to bridge into the existing rendering pipeline. ## Testing ```sh cargo test -p turborepo-log ``` The crate includes unit tests for all public types and an integration test (`tests/global_init.rs`) that exercises the full global logger lifecycle including lazy handle resolution.Anthony Shew · e9434ca4 · 2026-03-13
- 2.7ETVrefactor: Split process child module (#13014) ## Why The process child module mixed public process management, platform-specific handle internals, IO, shutdown semantics, and tests in one large file. Splitting those responsibilities reduces context needed to review future process changes. ## What Moves child handle internals, IO helpers, shutdown types and processing, state manager/channel logic, PTY test guard, and tests into sibling modules under `crates/turborepo-process/src/child/` while preserving the public API from `child.rs`. ## How This is intended as a mechanical refactor. Verified with `cargo fmt -p turborepo-process`, targeted child tests, `cargo test -p turborepo-process -- --test-threads=1`, `git diff --check`, and the repository pre-push hook.Anthony Shew · 24dd7c1f · 2026-06-03
- 2.6ETVdocs: layout redesign (#10178) ### Description Newly formatted pages for docs content. ### Testing Instructions There are a great many changes in here that probably aren't useful to review as file changes. You'll want to open the preview and make sure all the design holds together.Anthony Shew · ae3db3d1 · 2025-03-21
- 2.6ETVfix: Hardening for daemon IPC endpoints (#12742) - Prevent cross-user access to local daemon RPCs by moving daemon IPC into per-user paths and enforcing owner-only socket permissions. - Reject unauthorized Unix peers and validate Windows socket ownership before clients trust a daemon endpoint.Anthony Shew · 13a9a8b1 · 2026-05-07
- 2.3ETVfix: Preserve graceful shutdown output (#12607) ## Summary - Keep signal-driven `turbo run` cleanup alive until task shutdown finishes so TUI and grouped output are not torn down early or truncated. - Route shutdown and cache-flush status through the logging pipeline, distinguish signal vs close shutdowns, and avoid signal-only force timers on normal completion. - Harden process shutdown and parent-death cleanup while adding regression coverage for TUI, process, and signal-handling edge cases.Anthony Shew · 0b5bb210 · 2026-04-13
- 2.1ETVperf: Overhaul pnpm lockfile parsing and dependency closure computation (#13228) ## Why On large pnpm monorepos, the bulk of `turbo run` startup — the window between invocation and the first task starting — is spent parsing `pnpm-lock.yaml` and computing per-workspace transitive dependency closures. Profiling a 1191-package internal monorepo (43k tracked files, 5.4MB lockfile) showed the closure walk dominated by string allocation, hashing, and map probes rather than actual resolution work, and YAML parsing spending ~75% of its time inside a general-purpose scanner that pnpm's rigid machine-generated output doesn't need. ## What This PR has grown into the full pnpm startup overhaul: five stacked changes, each merged into this branch after independent review and verification. **1. Closure walk interning + FxHash.** Resolved packages are interned to dense `u32` ids behind the existing caches: resolve-cache hits become a refcount bump instead of cloning two `String`s per closure edge, visited sets hash a `u32` instead of two heap strings, and the deps cache is keyed by id. Cache maps use FxHash — lockfile content is developer tooling input, not a hash-flooding vector. **2. pnpm hash index** (#13229). The post-parse `dependency_index` becomes an `FxHashMap` carrying membership flags and versions, making `has_package`/`package_version`/`all_dependencies` O(1) probes instead of `BTreeMap` walks over long keys. Entry dependency maps stay `BTreeMap` so closure iteration order remains deterministic. Also fixes `subgraph()` returning pruned lockfiles with an empty index. **3. Fast pnpm parse on saphyr events** (#13230). An event-driven parser (pure Rust, ~2x libyaml scanner throughput) builds `PnpmLockfile` without serde on the hot path. Deliberately conservative: multi-document input, anchors/aliases, tags, duplicate keys, and exotic numeric forms bail to the unchanged serde path. Observable serde quirks (untagged variant order, `lockfileVersion` float formatting, plain-scalar typing, unknown-field dropping) are mirrored exactly and enforced differentially. **4. Structural line scanner** (#13238). pnpm-lock.yaml is line-oriented machine-generated YAML, so a `memchr`-driven scanner specialized to exactly that subset replaces the general YAML state machine as tier 1, emitting the same event stream the semantic layer from (3) consumes — quirk handling is shared, not duplicated. Tier order: scanner → saphyr → serde; the scanner also declines anything a general parser would *reject*, so it can never fabricate a lockfile from invalid input. Roughly halves parse time again on every corpus measured. **5. Shared bitset closure DP** (#13239). Closures were recomputed per workspace, so shared dependencies were re-walked once per workspace reaching them. When the lockfile can prove every transitive edge resolves identically across workspaces (pnpm importers can shadow transitive resolution, so this is proven per edge via importer equivalence classes, not assumed), closures are computed once globally: iterative Tarjan SCC condensation, then a bottom-up bitset DP where set unions are word-parallel `u64` ORs, processed in bounded-memory chunks. Any sensitive edge, non-pnpm lockfile, or single-workspace call falls back to the unchanged legacy walk. ## Results Interleaved A/B against current `main`, 4-core Linux, telemetry disabled, on the 1191-package monorepo. Time-to-first-task is measured in-process (invocation to first task dispatch in the chrome profile). | Benchmark | main | this PR | delta | |---|---|---|---| | time-to-first-task (real run) | 1117ms | 472ms | **−58%** | | `turbo run build --dry=json` end-to-end | 1481ms | 871ms | **−41%** | | lockfile parse (5.4MB, `from_bytes`) | ~285ms | 56ms | **−80%** | | peak RSS (dry) | — | −27MB vs branch base | bitsets beat per-workspace set churn | Parse deltas hold on public corpora too: next.js lockfile (1.29MB) 30.9→15.0ms, this repo's (707KB) 15.3→8.0ms for the scanner tier alone. ## How to verify - Full `--dry=json` output diffed against current main on the internal benchmark monorepo and this repo — byte-identical modulo run id and version string. - 20 differential parse tests assert scanner == saphyr == serde (`Eq`-identical lockfiles, byte-identical `encode()` round-trips) across v5/v6/v9 fixtures, folded/literal block scalars with chomping/blank-line edges, quoting, flow collections, catalogs, patches, and this repo's own `pnpm-lock.yaml` — with scanner acceptance *required* on mainline shapes so regressions can't silently fall through to slower tiers. Each unsupported construct has explicit fallback coverage. - The closure DP is differentially tested against the legacy walk (an independent oracle — the single-workspace entry point never uses the DP), including a crafted divergent-edge fixture proving the fallback triggers. 220 crate tests pass.Anthony Shew · 669676d2 · 2026-07-04
- 2.0ETVchore: Extract turbo_json module to turborepo-turbo-json crate (#11344) ## Summary Extract ~5,400 lines of turbo.json parsing, validation, and processing code from `turborepo-lib` into a new `turborepo-turbo-json` crate. This continues the modularization effort to make `turborepo-lib` a thin orchestration layer. ## What's extracted | File | Lines | Purpose | |------|-------|---------| | `lib.rs` | ~300 | Main `TurboJson` type, constants, core API | | `raw.rs` | ~320 | Raw parsed types (`RawTurboJson`, `RawTaskDefinition`, etc.) | | `parser.rs` | ~340 | Biome-based JSON parsing | | `processed.rs` | ~620 | Processed task definitions with DSL token handling | | `extend.rs` | ~610 | Task inheritance/extension logic | | `validator.rs` | ~250 | turbo.json validation rules | | `future_flags.rs` | ~40 | Feature flags | | `error.rs` | ~320 | Error types with miette diagnostics | ## What stays in turborepo-lib - `TurboJsonLoader` - Depends on MFE (microfrontends) and task_access - `TurboJsonReader` - Reading turbo.json from disk - `TaskDefinitionFromProcessed` trait - Creates `TaskDefinition` from processed types - Re-exports for backward compatibility ## Verification - ✅ All 339 `turborepo-lib` tests pass - ✅ All 57 `turborepo-turbo-json` tests pass - ✅ All 72 engine tests pass - ✅ Binary tested with `examples/basic` - ✅ **Zero behavioral changes** for CLI users (error messages, validation, parsing all identical) ## Test Plan ```bash cargo test -p turborepo-turbo-json cargo test -p turborepo-lib cargo build --bin turbo ```Anthony Shew · e9fef6e3 · 2025-12-30
- 1.9ETVfeat(turbo_json): add `$TURBO_EXTENDS$` (#10763) ### Description This PR adds a new DSL keyword for array task definitions that alters the behavior when using `extends` in a package `turbo.json`. Usually a field from a package task definition will completely override the root field e.g. Root: `{ "env": ["FOO"] }` Package: `{ "env": ["BAR"] }` Result: `{"env": ["BAR"] }` If the package uses the new `$TURBO_EXTENDS$` the root field value will be extended from instead of overridden. e.g. Root: `{ "env": ["FOO"] }` Package: `{ "env": ["$TURBO_EXTENDS$", "BAR"] }` Result: `{"env": ["FOO", "BAR"] }` This functionality allows for greater flexibility when specializing package task definitions without needing to copy/paste the shared values and remembering to update them if the root changed. It is currently gated by `turboExtends` feature flag in the root `turbo.json`. Reviewing this each commit individually in this PR is *highly* suggested. The actual feature add is all done in the final commit, the rest are prefactors. The primary prefactors are: - Plumbing through future flags to be read at config time, but usable at task resolution time. This is necessary since this is set at root and needs to be known when reading task definitions from package `turbo.json`s - Adding a new intermediate step for task definition creation named "processed". Removes a majority of the DSL concepts and validations that are exclusive to a single field. This paves the way for future extensions to the DSL. Nerd note: Technically, we could extend `$TURBO_EXTENDS$` to additional fields that are monoids. Extension behavior is really: ``` let a = if extends { base } else { mempty } a mappend b ``` The only interesting addition we could make would be around boolean fields which has 2 different monoids: any and all. Which one to choose would depend on the default value of the flag. e.g. `cache` defaults to false so we would want an any (or) behavior. I am not suggesting we do this, it is more confusing than it is worthwhile. ### Testing Instructions Added a fair amount of unit tests for the whole task definition resolution steps.Chris Olszewski · 6ce04db4 · 2025-08-21
- 1.9ETVfix: Normalize CRLF line endings in file hashing to match git (#12572) ## Summary When `.gitattributes` marks files as `text` or `text=auto`, git normalizes CRLF→LF in blob objects. After `turbo prune` removes `.git/`, the manual hashing path was hashing raw bytes (with CRLF), producing different hashes than the git path. This caused cache misses when running `turbo run` in pruned output. This PR adds a `crlf` module to `turborepo-scm` that replicates git's CRLF→LF normalization so turbo's file hashes match git's regardless of whether the git or manual (no-git) code path is used. ## What changed - **New `crlf` module** (`crates/turborepo-scm/src/crlf.rs`): Parses `.gitattributes` via `gix-attributes`, resolves `text`/`text=auto`/`-text`/`binary` attributes per-file, and normalizes CRLF→LF during hashing. Uses a `BlobHasher` trait to write the normalization algorithm once across both the gix and sha1 hasher implementations. Single-pass for the common case (no CRLFs), two-pass only when normalization is actually needed. - **`turbo prune` preserves `.gitattributes`**: Added to `ADDITIONAL_FILES` so the manual hashing path in pruned output has the same normalization context. - **`.gitattributes` in global hash**: Changing normalization rules now invalidates all caches. - **Consolidated duplicate code**: Removed parallel `hash_file` (hash_object.rs) and `git_like_hash_file` (manual.rs) implementations. Both paths now route through the unified `BlobHasher` abstraction in `crlf.rs`, eliminating duplicate blob-header construction and file-type validation. Extracted `resolve_or_load` helper to replace 3x copy-pasted attrs-resolution boilerplate. - **CI comments updated**: Clarified that CRLF normalization is driven by `.gitattributes`, not `core.autocrlf`. ## Known limitations - Only root `.gitattributes` is loaded (nested per-directory files are not consulted) - `eol=` attribute is not handled - `core.autocrlf` from git config is not read; normalization is exclusively `.gitattributes`-driven ## Testing The test suite covers: - Ground-truth validation against `git hash-object --path` (with filters) - Cross-implementation agreement between gix and sha1 hashers - Chunk-boundary edge cases (CRLF split across 64KB buffer boundary) - Binary detection (NUL in first 8KB) - Simulated `turbo prune` → manual hash → verify hashes match git path - Dirty file re-hashing matches committed blob OID - `core.autocrlf=true` combined with `text=auto` - Package-scoped `.gitattributes` patterns Fixes #9616 Fixes #5081Anthony Shew · 1403c9ee · 2026-04-06
- 1.8ETVfeat: Task-level `extends` field (#11259) ## What? Adds task-level `extends` field to Package Configuration task definitions, allowing workspaces to exclude specific tasks from inheritance. ## Why? Enables workspaces to opt-out of inherited tasks from root/parent configs. Previously, if root defined `lint`, all workspaces inherited it. Now workspaces can exclude tasks they don't need. ## How? - `"extends": false` (no other config) → task excluded entirely for workspace - `"extends": false` (with other config) → fresh task definition, no inheritance - `extends` disallowed in root `turbo.json` (validation error added) - Exclusions propagate through extends chain - Modified engine/builder.rs to track exclusion state through inheritance resolution - Added `validate_no_task_extends_in_root` validator - Updated JSON schema with `extends` boolean field ## Testing instructions Added testing all around this functionality and have manually tested many situations to see how the API feels.Anthony Shew · 9a9033b3 · 2025-12-15
- 1.8ETVfix: Prevent cache archive symlink reads (#12813) ## Summary - Prevent cache archive creation from reading through symlink/reparse-point races before archiving task outputs. - On Unix, resolve archive inputs with fd-relative traversal so symlinked parent directories and final-component symlink swaps are rejected before reads. - On Windows, open final files with reparse-point no-follow behavior and verify the opened handle still resolves under the cache anchor before reading. Linear: TURBO-5574Anthony Shew · ab90c81e · 2026-05-16
- 1.8ETVrefactor: Split CLI module (#13013) ## Why The CLI module had grown large enough to make command parsing and execution flow hard to review in one file. Splitting type definitions and tests away from the runtime flow keeps the entrypoint focused. ## What Moves clap argument and command type definitions into `cli/args.rs`, moves CLI tests into `cli/test.rs`, and leaves `cli/mod.rs` focused on command resolution, telemetry/version setup, and execution. ## How This is intended as a mechanical refactor. Verified with `cargo fmt -p turborepo-lib`, `cargo check -p turborepo-lib`, `cargo test -p turborepo-lib cli::test`, `cargo test -p turborepo-lib`, `git diff --check`, and the repository pre-push hook.Anthony Shew · 7bd88416 · 2026-06-03
- 1.7ETVfeat: Turborepo Devtools (#11263) ### Description A new command for visualizing your Package Graphs and Task Graphs: `turbo devtools` ### Testing Instructions Working with it locally feels pretty great. Wrote some tests, as well. --------- Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com>Anthony Shew · 10b9099e · 2025-12-17
- 1.7ETVfeat: Add deferred hashing for task inputs (#13125) ## Why Some task inputs are not reliable when `turbo` starts. Generated code, generated CSS, API types, and other materialized files may be missing or stale until an upstream task runs, which can produce a cache key that does not represent the files the task actually consumes. Deferred hashing gives power users a way to finalize a task hash after its dependencies complete. Simple examples: Hashes normal package inputs at startup, using defaults while excluding `dist`: ```json { "inputs": [ { "mode": "startup", "withDefaults": true, "globs": ["!dist/**"] } ] } ``` Waits for `codegen`, then hashes generated files before checking cache: ```json { "dependsOn": ["codegen"], "inputs": [ { "mode": "startup", "withDefaults": true, "globs": ["!src/generated/**"] }, { "mode": "jit", "globs": ["src/generated/**"] } ] } ``` Hashes declared outputs from the selected `codegen` task after it runs, without also hashing generated files at startup: ```json { "dependsOn": ["codegen"], "inputs": [ { "mode": "startup", "withDefaults": true, "globs": ["!src/generated/**"] }, { "mode": "dependencyOutputs", "from": ["codegen"], "globs": ["src/generated/**"] } ] } ``` ## What Adds deferred hashing support through `startup`, `jit`, and `dependencyOutputs` input modes, including validation, hashing, dry-run summaries, schema/bindings, architecture docs, user-facing docs, and regression coverage. ## How Ton of test coverage added, and did a lot of manual testing.Anthony Shew · 4ebb50ff · 2026-06-23
- 1.7ETVdocs: Geistdocs update (#11807) Partial Geistdocs update. --------- Co-authored-by: Anthony Shew <anthony.shew@vercel.com>Hayden Bleasel · 91d0e7bb · 2026-02-12
- 1.7ETVchore: Move engine builder tests from `turborepo-lib` to `turborepo-engine` (#11353) ## Summary Move 3,196 lines of EngineBuilder integration tests from `turborepo-lib` to `turborepo-engine` where the production code lives. - Reduces `turborepo-lib` by ~3,200 lines - Tests are now colocated with the code they test - Part of ongoing modularization effort ## Testing - All 62 `turborepo-engine` tests pass - All 255 `turborepo-lib` tests passAnthony Shew · 48fb739c · 2025-12-31
- 1.7ETVfeat: Add experimental structured logging with `--json` and `--log-file` flags (#12405) ## Summary - Adds `--json` flag to stream NDJSON to stdout and `--log-file [path]` flag to write structured JSON logs to a file, both behind an experimental marker - Supports `TURBO_LOG_FILE` environment variable as an alternative to the CLI flag - Deliberately omits `logFile` from `turbo.json` — structured logging is a per-invocation concern better suited to CLI flags and env vars than checked-in config ## How to test ```bash # NDJSON to stdout turbo run build --json 2>/dev/null | head -5 # File output (default path) turbo run build --log-file cat .turbo/logs/*.json | python3 -m json.tool | head -20 # File output (custom path) turbo run build --log-file=my-log.json # Env var TURBO_LOG_FILE=1 turbo run build # Both modes simultaneously turbo run build --json --log-file=build.json ``` Each JSON entry has `timestamp`, `source`, `level`, and `text` fields. ANSI escape sequences are stripped in file output.Anthony Shew · 7ca06013 · 2026-03-20
- 1.6ETVfix: Harden token protection with `secrecy` crate and close exposure gaps (#11831) ## Summary - Migrate `SecretString` to use the `secrecy` crate for zeroize-on-drop - Convert `VerifiedSsoUser` and `VerificationResponse` token fields from `String` to `SecretString` - Change `get_token_with_refresh()` return type from `Option<String>` to `Option<SecretString>` - Replace manual `bearer_header()` with reqwest's built-in `bearer_auth()` which marks headers as sensitive ## Why The initial `SecretString` implementation had three gaps: 1. **`VerifiedSsoUser.token` and `VerificationResponse.token` were plain `String`s.** Custom `Debug` impls redacted them, but `format!("{}", user.token)` or accidental serialization could still leak. Now they use `SecretString`, and the manual `Debug` impls are replaced by derives. 2. **`get_token_with_refresh()` returned `Option<String>`.** It called `.expose().to_string()` on 4 code paths, creating unprotected `String` values that callers had to re-wrap. The token now stays as `SecretString` through the entire refresh flow. 3. **No zeroize-on-drop.** The custom wrapper left secrets in freed memory. Backing it with `secrecy::SecretBox<str>` ensures secrets are scrubbed on drop. Additionally, the manual `bearer_header()` helper used `format!("Bearer {token}")` without marking the header as sensitive. reqwest's `bearer_auth()` does this automatically via `HeaderValue::set_sensitive(true)`, preventing auth headers from appearing in tracing middleware output. <sub>CLOSES TURBO-5203</sub>Anthony Shew · 200e4c38 · 2026-02-13
- 1.6ETVfeat: Add `affectedUsingTaskInputs` future flag for task-level `--affected` detection (#12247) ## Summary - Adds `affectedUsingTaskInputs` future flag that changes `--affected` from package-level to task-level granularity - When enabled, only tasks whose declared `inputs` globs match changed files are selected (plus transitive dependents), instead of running all tasks in changed packages - Extracts shared glob matching into `turborepo-types/task_input_matching` for reuse between `turbo run --affected` and `turbo query { affectedTasks }` ## Design The task-level filtering is a post-build step on the single engine, not a separate code path. The normal `--affected` scope resolution determines affected packages and builds the engine as usual. If the future flag is enabled, `Engine::retain_affected_tasks` then filters the engine in place to only the tasks whose inputs match changed files plus their transitive dependents. This means: - One code path (no branching into a separate engine build) - One engine (mutated in place via `retain_affected_tasks`) - `--parallel` naturally works because the filter is re-applied after the parallel engine rebuild ## How it works 1. Normal `--affected` scope resolution determines affected packages 2. Engine is built with those packages (same as before) 3. SCM is queried for changed files between base and head refs 4. Each task's `inputs` globs are checked against changed files, with a compilation cache per unique (package, inputs) pair 5. `Engine::retain_affected_tasks` prunes the engine to only affected tasks + their transitive dependents via reversed DFS Global changes (root `package.json`, `turbo.json`, lockfile, or user-configured `globalDependencies`) short-circuit to running all tasks. ## Testing To verify, enable the flag in `turbo.json` and use tasks with specific `inputs` configurations: ```json { "tasks": { "build": { "dependsOn": ["^build"] }, "test": { "inputs": ["$TURBO_DEFAULT$", "!**/*.md"] }, "typecheck": { "inputs": ["$TURBO_DEFAULT$", "!**/*.md", "!**/*.test.ts"] } }, "futureFlags": { "affectedUsingTaskInputs": true } } ``` Changing a `.md` file should only trigger `build` (not `test` or `typecheck`). Changing a `.test.ts` should trigger `build` and `test` (not `typecheck`). Unit tests cover both `retain_affected_tasks` (8 tests) and `affected_task_ids` (10 tests). Integration tests cover the end-to-end `turbo run --affected --dry=json` path (5 tests). ## Known limitations - The `turbo query { affectedTasks }` path pre-filters files to the package directory, missing cross-package `$TURBO_ROOT$` inputs. TODO added in code for follow-up. --------- Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com>Anthony Shew · 36a5a6ae · 2026-03-12