Benjamin Woodruff
90d · built 2026-07-24
90-day totals
- Commits
- 60
- Grow
- 0.1
- Maintenance
- 7.5
- Fixes
- 6.3
- Total ETV
- 13.9
Where this dev ranks
Percentile against the global top-100 leaderboard (all-time totals).
- By commits
- Top 61 %
- By Growth share
- Top 93 %
30-day trajectory
Last 30 days vs. the 30 days before. Up arrows on Growth and ETV mean improvement; up arrow on Fixes share means more time on fixes (worse).
↑+7.7 %
vs 26 prior
↑+0.2 pp
recent vs prior
↑+42.7 pp
recent vs prior
Daily performance
Daily ETV, stacked by Growth, Maintenance and Fixes.
Work-mix over time
Share of Growth / Maintenance / Fixes over a rolling 7-day window. Reads as 'where is effort flowing right now'.
Repository spread
Where this developer's commits land. Concentrated work (top1 > 80%) vs polymath spread (top1 < 30%).
Most impactful commits
Top 20 by ETV in the 90-day window.
- 6.3ETVTurbopack: Fix missing canonicalization of paths and always use verbatim paths internally for Windows (#95668) I was seeing failures on https://github.com/vercel/next.js/pull/95628 and went down this rabbit hole. - We were (incorrectly) not canonicalizing the `DiskFileSystem` root dir. This happened to work out for us because pnpm also wasn't canonicalizing junction point targets, and so the root dir prefix matched. However after `#95628`, pnpm started canonicalizing junction point targets because we added `pnpm-workspace.yaml` files. This was just coincidence and indicative of much deeper problems. - `dunce` isn't a good fit for us. It picks a win32 or verbatim representation based on path length, which would mean that long paths inside of a short root path would never work because we'd never be able to strip the prefix, since we'd be looking for a win32 path prefix. Omnipath is better for what we want to do: https://docs.rs/omnipath/latest/omnipath/windows/trait.WinPathExt.html - The windows verbatim path format works with long paths (>260 characters) and behaves a lot closer to cross-platform unix paths, which is what we want. E.g. if somebody tries to import a JS module from a Windows 8.3 short path, it should fail. This will fail with verbatim paths (good!). This PR *always* uses the verbatim path format internally within `DiskFileSystem`. - `read_link` was re-inventing `try_from_sys_path` but badly. Just call `try_from_sys_path`. - `turbopack/crates/turbo-tasks-fs/src/embed/file.rs` was dead code. - `validate_path_length_inner` was incorrectly referring to verbatim paths as UNC paths. These are two different things. - Canonicalize any absolute symlink targets outside of the `DiskFileSystem` root path, resolving any symlinks they may have, normalizing case-insensitive paths, and handling Windows 8.3 short name format. ## Some Context About Windows Path Formats: - win32 paths: These are your normal user-friendly `C:\foo\blah` paths. When calling most filesystem APIs, these are limited to 260 characters unless the user has enabled long paths on the system, and avoiding the limit with these paths also requires a [`longPathAware` application manifest](https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests#longPathAware). You can mix `/` and `\` in win32 paths, and Windows will normalize the path separator for you. - verbatim paths: This is a rust-specific name (used by the stdlib) for the "extended length" prefixed paths. These are paths starting with `\\?\`. These paths can be up to ~32,767 characters long. They do not support Windows 8.3 shortnames. They do not normalize between `/` and `\`, and do not support `.` or `..` components, however [`Path::join`](https://doc.rust-lang.org/std/path/struct.Path.html#method.join) will do this normalization if given a verbatim path. - Windows 8.3 shortnames: A legacy format where paths can be represented by 8 characters, followed by a tilde and a digit (or hash in some extreme cases where there are many collisions), followed by a 3 character file extension. NTFS supports this, [ReFS](https://learn.microsoft.com/en-us/windows-server/storage/refs/refs-overview) (used by [Windows Dev Drive](https://learn.microsoft.com/en-us/windows/dev-drive/)) does not. - UNC ("Universal Naming Convention"): This is a specific representation of paths used for remote files on network servers. - `file://` URIs: These must be formed from [win32 style paths](https://docs.rs/url/latest/url/struct.Url.html#method.from_file_path), though the normal 260 character limit typically does not apply here. - canonicalize: Corresponds to [`std::fs::canonicalize`](https://doc.rust-lang.org/stable/std/fs/fn.canonicalize.html). This involves actual fs operations (that we can't easily track), and recursively resolves symlinks. It will fail if the file path does not exist. On windows, this always returns a verbatim path. - wide format: Windows paths are utf-16. Generally we don't support paths that can't be encoded to utf-8, but we should try to fail as obviously as we can in these cases. We don't have good filesystem issue handling yet though. - case sensitivity: Generally paths are not case-sensitive on Windows or macOS, but are case-sensitive on most Linux filesystems. The exact definition of how case sensitivity works with unicode paths is highly platform and filesystem dependent. - junction points: The windows equivalent of symlinks (symlinks are also supported, but require developer mode to be turned on). These can contain an arbitrary path reference in any format (win32, verbatim, etc) with any case and with windows 8.3 short names. Junction points are always target absolute paths to directories.github.com-vercel-next.js · 3214020b · 2026-07-21
- 4.1ETVTurbopack: Split up turbo-tasks-fs/src/lib.rs into smaller modules (#96030) This file was way too huge and was wasting a ton of tokens when trying to use it with agents. `turbo-tasks-fs/src/disk.rs` is still a little bigger than I'd like, but I think this is an overall improvement.github.com-vercel-next.js · 89c94877 · 2026-07-22
- 0.5ETV[ci] split up large cache-components-errors tests (#95623) See https://github.com/vercel/next.js/pull/95553 for an explanation of why we want to split these up. TL;DR it makes CI faster and saves us some money. In CI we have to run tests at the suite/file granularity, so if a test suite is too long it limits parallelism and if it has to be retried, we have to retry the full suite.github.com-vercel-next.js · 4df37b6e · 2026-07-09
- 0.4ETV[ci] Split up large instant-validation tests (#95627) See https://github.com/vercel/next.js/pull/95553 for an explanation of why we want to split these up. TL;DR it makes CI faster and saves us some money. In CI we have to run tests at the suite/file granularity, so if a test suite is too long it limits parallelism and if it has to be retried, we have to retry the full suite.github.com-vercel-next.js · 92868446 · 2026-07-09
- 0.4ETV[ci] Enforce minimumReleaseAge in e2e tests that install external dependencies (#95628) We also mitigate the security impact of e2e tests by running everything in VMs, and by not reading caches in release jobs, but we should still be using `minimumReleaseAge` in e2e tests. Ideally we'd also use lockfiles here too, and I'll work on that next, but that is likely a lot more complicated because we probably need some way to merge lockfiles, since we can't pin the next.js and react versions in every test, and we need a reasonable/efficient way to bulk-update all the lockfiles for tests that need it. Depends on https://github.com/vercel/next.js/pull/95668 for a bunch of windows path representation fixes that this ends up exposing in CI, because adding workspace files causes pnpm to change the absolute path representation used in the Windows junction points that it creates.github.com-vercel-next.js · e630d818 · 2026-07-21
- 0.3ETV[ci] Share a single browser instance across all test suites in a single job (#95589) The idea here is that we waste a bunch of CPU setting up each test suite by spawning an entirely new Chrome process, but playwright can share the same browser across many tests. Tests are still isolated at the browser level, just not at the OS process level. Hoping to see some performance improvement in e2e tests. Compare to: https://github.com/vercel/next.js/pull/95617 Somewhat unscientific (single sample) results: ``` Comparing the head-commit build-and-test runs — PR #95589 run [28981702219 ](https://github.com/vercel/next.js/actions/runs/28981702219)vs PR #95617 run [28981686692](https://github.com/vercel/next.js/actions/runs/28981686692). Both ran 101 jobs and both succeeded. ┌─────────────────────────┬────────────────┬───────────────────────┬───────────────────┐ │ Metric │ #95617 control │ #95589 shared browser │ Delta │ ├─────────────────────────┼────────────────┼───────────────────────┼───────────────────┤ │ Raw wall-time sum │ 616.2 min │ 571.2 min │ −45.0 min (−7.3%) │ ├─────────────────────────┼────────────────┼───────────────────────┼───────────────────┤ │ Billable (ceil per job) │ 658 min │ 617 min │ −41 min (−6.2%) │ └─────────────────────────┴────────────────┴───────────────────────┴───────────────────┘ The shared-browser experiment is cheaper, and the savings land almost exactly where you'd expect — the browser-driven test suites — while non-browser jobs (rust, lint, unit, windows) are flat within noise: ┌─────────────────────────────────┬─────────┬────────┬───────┐ │ Job category │ control │ shared │ delta │ ├─────────────────────────────────┼─────────┼────────┼───────┤ │ test prod │ 130.7 │ 110.5 │ −20.2 │ ├─────────────────────────────────┼─────────┼────────┼───────┤ │ cache components dev │ 51.5 │ 42.9 │ −8.7 │ ├─────────────────────────────────┼─────────┼────────┼───────┤ │ turbopack dev │ 75.1 │ 69.5 │ −5.5 │ ├─────────────────────────────────┼─────────┼────────┼───────┤ │ turbopack production │ 86.3 │ 81.1 │ −5.2 │ ├─────────────────────────────────┼─────────┼────────┼───────┤ │ test dev │ 110.4 │ 105.5 │ −4.9 │ ├─────────────────────────────────┼─────────┼────────┼───────┤ │ cache components prod │ 48.7 │ 49.0 │ +0.3 │ ├─────────────────────────────────┼─────────┼────────┼───────┤ │ firefox and safari │ 5.3 │ 7.3 │ +2.0 │ ├─────────────────────────────────┼─────────┼────────┼───────┤ │ flake-detection jobs (combined) │ ~15 │ ~21 │ +~6 │ └─────────────────────────────────┴─────────┴────────┴───────┘ Takeaways: - Net savings of ~41 billable minutes per run (~6%), concentrated in the Chromium-driven test prod/dev, turbopack, and cache components suites — consistent with reusing one browser process instead of spawning per-suite. - The small regressions are in firefox and safari and the "new/changed tests for flakes" jobs (+~8 min combined). Worth a glance, though they may just be run-to-run variance. Caveat: this is a single run per PR, so there's real variance run-to-run. That said, the fact that the deltas track the browser-heavy jobs specifically — and not the Rust/lint/unit jobs — is a good signal the effect is genuine rather than noise. If you want a firmer number, re-running each PR 2–3× and averaging would tighten it up. ```github.com-vercel-next.js · 390eff3b · 2026-07-21
- 0.2ETVTurbopack: Perform issue filtering without turbo-task functions (#93885) The filtering logic is generally very cheap, many issues are short-lived, and filtering is unlikely to trigger more than 1-2 turbo tasks per issue. Wrapping it in another task per issue doesn't make a ton of sense.github.com-vercel-next.js · 8bc70746 · 2026-06-05
- 0.2ETV[ci] Explicitly disable package-manager-cache for uses of actions/setup-node (#93953) If this action detects that you're likely using `npm` it (by default) enables a cache for `npm`. We use `pnpm`, so it doesn't do anything (and we manually set up a pnpm cache in some places), but the implicit behavior here is a little dangerous: e.g. https://adnanthekhan.com/posts/angular-compromise-through-dev-infra/#finding-the-pivot So let's explicitly disable this everywhere. It wasn't doing anything for us anyways.github.com-vercel-next.js · 67d5f961 · 2026-05-27
- 0.2ETV[ci] Split up large rsc-build-errors development test (#95624) See https://github.com/vercel/next.js/pull/95553 for an explanation of why we want to split these up. TL;DR it makes CI faster and saves us some money. In CI we have to run tests at the suite/file granularity, so if a test suite is too long it limits parallelism and if it has to be retried, we have to retry the full suite.github.com-vercel-next.js · a87692e1 · 2026-07-09
- 0.1ETV[ci] update relay-compiler to 21.0.1 (#95217) We need to upgrade this because the ancient version of relay-compiler that we were using didn't support arm64.github.com-vercel-next.js · c518402e · 2026-06-30
- 0.1ETVTurbopack: Use Arc<PathMap> and Box<Path> to make InvalidatorMap slightly more efficient (#95987) Addresses https://github.com/vercel/next.js/pull/95951/changes#r3617631291 - We often already have an `Arc<PathBuf>` here. - The data stored in the queue is temporary, so the slight extra size of `Arc<PathBuf>` is fine, and saving the clone is nice. - The other references to the `Arc<PathBuf>` are not long-lived, so convert it into a `Box<Path>` once it goes into the locked map.github.com-vercel-next.js · 8882653a · 2026-07-22
- 0.1ETV[ci] Split up large cache-components-dev-warmup test suite (#95553) This splits two of our slowest test suites into smaller pieces that can be run in parallel and retried in isolation. ## Context Jest treats test suites as the smallest test execution unit. Slow test suites can lead to situations where a test shard is using very little CPU waiting for one last test suite to finish, and when tests fail and have to be retried, we have to retry the entire suite. Here's a particularly bad example: <img width="3116" height="2634" alt="Screenshot 2026-06-08 at 20-00-05 CI Telemetry — turbopack production tests (1)" src="https://github.com/user-attachments/assets/b5534200-4b77-40d5-a219-4b6fc552ed77" /> I instructed Claude to fetch test timing information from GitHub Actions Artifact, and to report the slowest test suites. > How timings work today > > - The `fetch-test-timings` job runs `node run-tests.js --timings --write-timings`, which pulls per-file durations from a Vercel KV store and uploads them as the test-timings artifact (test-timings.json, 5-day retention). > - Each sharded job downloads that artifact and run-tests.js -g N/M greedily bin-packs whole test files into groups by predicted duration (run-tests.js:528-549). > - I pulled the artifact from the July 7 canary run (28843797348): 1684 test files, ~61,500s total, median 20s, but max 1023s. > > Since the file is the unit of scheduling, a shard can never finish faster than its largest file. The job durations from that run confirm the imbalance this causes: test dev 4/10 took 28 min while siblings took 10–17; test cache components dev 6/6 took 13 min vs 5–6 for siblings; test turbopack production 5/7 took 20 min vs 10–11. > The slowest suites ``` ┌────────────────────┬────────────────────────────────────────────────────────────────────────────┐ │ Time │ Suite │ ├────────────────────┼────────────────────────────────────────────────────────────────────────────┤ │ 1023s + 933s │ test/development/app-dir/cache-components-dev-warmup/ (both variants) │ ├────────────────────┼────────────────────────────────────────────────────────────────────────────┤ │ 942s │ test/production/create-next-app/templates/matrix.test.ts │ ├────────────────────┼────────────────────────────────────────────────────────────────────────────┤ │ 788s + 432s + 232s │ test/e2e/app-dir/cache-components-errors/cache-components-errors.*.test.ts │ ├────────────────────┼────────────────────────────────────────────────────────────────────────────┤ │ 779s │ test/development/acceptance-app/rsc-build-errors.test.ts │ ├────────────────────┼────────────────────────────────────────────────────────────────────────────┤ │ 470s │ test/e2e/opentelemetry/instrumentation/opentelemetry.test.ts │ ├────────────────────┼────────────────────────────────────────────────────────────────────────────┤ │ 457s │ test/e2e/app-dir/actions/app-action-node-middleware.test.ts │ ├────────────────────┼────────────────────────────────────────────────────────────────────────────┤ │ 451s │ test/e2e/app-dir/next-after-app/index.test.ts │ ├────────────────────┼────────────────────────────────────────────────────────────────────────────┤ │ 447s │ test/e2e/app-dir/css-order/css-order.test.ts │ ├────────────────────┼────────────────────────────────────────────────────────────────────────────┤ │ 426s │ test/e2e/telemetry/config.test.ts │ ├────────────────────┼────────────────────────────────────────────────────────────────────────────┤ │ 402s │ test/production/debug-build-path/debug-build-paths.test.ts │ └────────────────────┴────────────────────────────────────────────────────────────────────────────┘ ``` > Reading these files, almost every one is slow for the same structural reason: a `describe.each(...)` over fixtures/modes/runtimes where each iteration boots its own `nextTestSetup` (dev server or full production build), run serially within one Jest worker: > - **cache-components-dev-warmup:** `describe.each` over 2 fixture dirs, and it restarts the dev server (stop/clean/start) before every one of ~17 tests. It's slow by design (cache-warmup isolation), so per-test cost can't shrink — but the file can be split. > - **create-next-app matrix:** `describe.each(app|pages)` × `it.each` over a ~16–48-combination flag matrix, each doing a full CNA scaffold (+ dev-server cycle for pages), fully serial. > - cache-components-errors.test.ts: 7490 lines, 129 tests, with per-describe `next build --experimental-build-mode compile` and per-path generate builds in prod mode. > - **css-order:** three sibling `describe.each` blocks over 4–6 chunking modes, each mode booting its own server, ~3 blocks × modes × page-pair orderings. > - **next-after-app, opentelemetry:** `describe.each` over runtimes/configs, again one server per iteration.github.com-vercel-next.js · c3dd28fd · 2026-07-07
- 0.1ETV[ci] Use node-version-file when we already have a repository checkout and reduce hardcoded references to node versions (#94780) I noticed the `node-version-file` option for `setup-node` while reviewing https://github.com/vercel/nextjs-react-compiler/pull/2 We should use it in places where we're already checking out the repository anyways. It should make it easier to keep node versions in sync.github.com-vercel-next.js · 36a8f4c2 · 2026-06-16
- 0.1ETV[ci] Reduce usage of turborepo cache in `build_and_deploy.yml` (#94319) We're consolidating on `actions/cache` now that we got rid of self-hosted runners. This doesn't remove all of the uses of turborepo's cache (notably sccache), but this does eliminate the remote turborepo cache for the native build cache and the docker image. These build steps use docker so that we're building against an old pinned glibc (2.31), and so that we can have access to both `aarch64` and `x86_64` musl sysroots. ## Testing **Local Build:** `node scripts/docker-native-build.js` still works (this script just exists for being able to debug these builds locally) **CI Build:** - Run an automated-preview CI job, but cancel it before the native build succeeds to test writing to the docker cache but not the native cache: https://github.com/vercel/next.js/actions/runs/26986955366/job/79638633136?pr=94319 - Re-run the job with only the docker image cache, see that we get a cache hit for the docker image: https://github.com/vercel/next.js/actions/runs/26986955366/job/79639365126?pr=94319 - Re-run the job again and see that we hit the native build cache hit and don't touch docker at all: https://github.com/vercel/next.js/actions/runs/26986955366/job/79647324952?pr=94319 - Manually trigger the job to do a full preview build: https://github.com/vercel/next.js/actions/runs/26989861549github.com-vercel-next.js · 3caee03f · 2026-06-10
- 0.1ETV[ci] Split up large create-next-app/templates/matrix test (#95555) Same motivation as #95553, but another test suite. This is a bit messier than the other PR because there's not as clear of a split point, so it uses sharding.github.com-vercel-next.js · d23d069e · 2026-07-08
- 0.1ETVClarify AI-assisted contribution policy in PR template and AGENTS.md (#95629) ## Summary Documents the policy around AI-assisted contributions, for both humans and agents: - `.github/pull_request_template.md`: notes that AI use is encouraged for researching, creating, and reviewing changes, but contributors must deeply understand their contributions, and PR descriptions from external contributors must be written by a human. - `.github/ISSUE_TEMPLATE/1.bug_report.yml`: adds the equivalent note for bug reports — issue descriptions from external contributors must be written by a human, though AI may help create reproductions. - `AGENTS.md`: replaces the old "PR Descriptions" section with two new sections. "GitHub Pull Requests" distinguishes branch PRs (agents may write descriptions) from fork PRs targeting `vercel/next.js` (agents may not). "GitHub Issues, Comments, and Discussions" restricts agent-written issues, discussions, and comments to members of the `vercel`/`vercel-labs` GitHub orgs, tells agents how to check membership via the GitHub API, lists what agents can still help non-members with (drafting details, reviewing, reproductions, translation, searching for duplicates), and carves out exceptions (commenting on the user's own PR, Vercel-operated bots, GitHub/Graphite review bots, and forks of the repo). - Renames the LLM watermark marker from `NEXT_JS_LLM_PR` to `NEXT_JS_LLM` everywhere it appears (`AGENTS.md`, `contributing/repository/pull-request-descriptions.md`, and the `create-pr`/`backport-pr` skills), since it now also applies to issues, discussions, and comments. Nothing in the repo consumes the old marker name. - `.agents/skills/create-pr/SKILL.md`: adds a "Fork PRs vs Branch PRs" section referencing the `AGENTS.md` policy. Assorted typo fixes along the way. ## Verification - `gh api /user/memberships/orgs` and `gh api orgs/vercel/members/<login>` (confirmed the documented membership checks work and return the documented statuses) - Not run: no test/build commands (docs-only change; prettier applied via the pre-commit hook) <!-- NEXT_JS_LLM -->github.com-vercel-next.js · 0cfc85ce · 2026-07-10
- 0.1ETVTurbopack: This bool could've been an enum (#95288) <img width="588" height="141" alt="Screenshot 2026-06-29 at 4 39 36 PM" src="https://github.com/user-attachments/assets/a8b0981e-fa76-433b-845a-989b840e0ac1" /> https://x.com/charliermarsh/status/2069796668506042584github.com-vercel-next.js · f84c6947 · 2026-06-30
- 0.1ETV[ci] Skip apt-installed dependencies in playwright install step (#95535) The hypothesis is that this is just installing some font stuff that isn't actually needed for Chromium to work: ``` The following additional packages will be installed: libnss3-tools xfonts-encodings xfonts-utils Recommended packages: fonts-ipafont-mincho fonts-liberation-sans-narrow fonts-tlwg-loma The following NEW packages will be installed: fonts-freefont-ttf fonts-ipafont-gothic fonts-liberation fonts-tlwg-loma-otf fonts-unifont fonts-wqy-zenhei xfonts-cyrillic xfonts-encodings xfonts-scalable xfonts-utils The following packages will be upgraded: libnss3 libnss3-tools 2 upgraded, 10 newly installed, 0 to remove and 35 not upgraded. ``` `apt` is very slow, so skipping this step should significantly reduce setup time per test shard. It seems like this isn't always true for other browsers, so only skip this for Chromium (that's fine, that's what most of our jobs use anyways). On 16 core arm runners, "Install Playwright Browsers" typically took 18-24 seconds. Now it takes 5-6s. Let's estimate that saves about 15s per Linux job that installs Chromium. Claude estimates that there are 73 chromium-only Linux jobs on this PR: <img width="891" height="148" alt="Screenshot 2026-07-06 at 10 30 16 PM" src="https://github.com/user-attachments/assets/725893fa-9f6f-4806-a3b4-9858ab0ebe66" /> So that's 15*73/60=18.25 minutes saved per PR push (the average PR gets many pushes). At the public (before any volume discounts) price of $0.026/minute for the 16 core arm runners, that comes out to $0.4745 per PR push saved. This workflow has had 3,894 runs in the last 30 days, so that could be $1,847/mo saved, but many of those workflow runs were canceled, and some likely failed before getting to these jobs, so the actual savings are probably somewhere around half of that -- scroll through https://github.com/vercel/next.js/actions/workflows/.github/workflows/build_and_test.yml and see that a little less than half of the workflow runs end up canceled.github.com-vercel-next.js · 727a3ffa · 2026-07-07
- 0.1ETV[ci] Add an `TURBO_TASKS_AVAILABLE_PARALLELISM` environment variable that overrides `std::thread::available_parallelism` callsites (#93995) We want to use this to potentially improve efficiency in e2e tests where we're running many instances of Turbopack at once. By default, turbo-tasks will try to use every available CPU core. **This environment variable does not strictly limit Turbopack to using 4 cores, it just restricts the number of main tokio worker threads and changes how we shard dashmaps.** This option will not make sense for the vast majority of users, it only makes sense when you are running *many* instances of Turbopack at once, and the exact semantics are potentially confusing, as it is not a hard limit. ## Test Plan Build vercel-site with this set to 1 and see a little over 100% CPU usage. Set it to 8 and see around 800% CPU usage. ``` TURBO_TASKS_AVAILABLE_PARALLELISM=1 pnpm next build --experimental-build-mode=compile ```github.com-vercel-next.js · 80cb3447 · 2026-05-21
- 0.1ETV[ci] Set `persist-credentials: false` for all GH actions (#94214) The actual security implications of this are likely *very* minor, but in the interest of reducing how many credentials we have, there's little reason for us not to set this to `false`. In cases where we create a new commit via GH actions, we already do that via the GitHub API, so those don't need the on-disk git credentials to push. --- From: https://github.com/actions/checkout : > The auth token is persisted in the local git config. This enables your scripts to run authenticated git commands. The token is removed during post-job cleanup. Set persist-credentials: false to opt-out. ``` # Whether to configure the token or SSH key with the local git config # Default: true persist-credentials: '' ```github.com-vercel-next.js · ef63481d · 2026-06-05