Repository navigation
feat(run): include nested vp run plans in --dry-run --json - #967
Merged
wan9chi merged 1 commit intoNov 9, 2026
Merged
Conversation
This was referenced Nov 7, 2026
fspy benchmarklinuxmacoswindows |
wan9chi
force-pushed
the
claude/dry-run-09-nested-plans
branch
from
November 7, 2026 11:50
7f725dd to
c78783a
Compare
wan9chi
force-pushed
the
claude/dry-run-09-nested-plans
branch
from
November 7, 2026 12:00
c78783a to
480cb60
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 480cb60. Configure here.
wan9chi
force-pushed
the
claude/dry-run-09-nested-plans
branch
from
November 7, 2026 14:54
480cb60 to
7209175
Compare
wan9chi
force-pushed
the
claude/dry-run-09-nested-plans
branch
from
November 7, 2026 15:07
7209175 to
e96fb3d
Compare
wan9chi
force-pushed
the
claude/dry-run-09-nested-plans
branch
from
November 7, 2026 17:19
e96fb3d to
968b86f
Compare
wan9chi
force-pushed
the
claude/dry-run-09-nested-plans
branch
from
November 7, 2026 18:04
968b86f to
e76f876
Compare
wan9chi
force-pushed
the
claude/dry-run-09-nested-plans
branch
from
November 7, 2026 18:38
e76f876 to
1d46e32
Compare
wan9chi
force-pushed
the
claude/dry-run-09-nested-plans
branch
from
November 9, 2026 10:11
1d46e32 to
1e22ef3
Compare
Since `vp run --dry-run --json` was added, it failed for a task whose command runs `vp run …`. The RFC shows such a step as `expandedFrom` with its own `plan`, so the run plan is a tree, and each nested plan has its own dependencies. - The layout recurses into nested graphs. Each plan's `dependsOn` is scoped to its own tasks, so the same task ID can appear at several levels. - `expandedFrom` is the item's command as written, with the args given after the task name. - A leaf's step is found by its path through the nested plans. - The error for tasks that run `vp run` goes away. A nested `vp run --dry-run` is still a spawned step, which a dry run doesn't start. The e2e fixture `run_plan_json_nested` covers plans at several levels, args forwarded to a nested run, and a nested dry run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
wan9chi
force-pushed
the
claude/dry-run-09-nested-plans
branch
from
November 9, 2026 10:44
1e22ef3 to
bc48a17
Compare
wan9chi
added a commit
that referenced
this pull request
Nov 9, 2026
## Motivation Before starting a long run, there's no way to see which tasks would come from the cache and which would run, or why one would miss, without running them (#791, voidzero-dev/vite-plus#2848). [The dry-run RFC](https://github.com/voidzero-dev/vite-task/blob/main/docs/rfcs/dry-run.md) specifies `vp run --dry-run` for this. This is the first PR of the stack that implements it: the text output. The PRs above it add every change of a miss (#961), `-v` (#963), `--json` (#966, #967), `unknown` (#968) and `-vv` (#969). This supersedes #792 by @lifeiscontent, whose write-free lookup and test scenario it builds on, so it credits them as co-author. Closes #791. ## Changes `vp run --dry-run` plans the run and checks the cache as a real run does, the remote cache included, then prints each command's line and the summary. It runs nothing and records nothing. The cache is opened as in a real run, but no entry is written, a hit's outputs aren't downloaded or restored, and `last-summary.json` isn't written, so `--last-details` still shows the last real run. ``` $ vp run --dry-run test $ vtt print-file src/a.txt ◉ cache hit $ vtt print tested --- vp run: 1/2 cache hit (50%), nothing was run. ``` - A dry run goes through the real run's lookup and spawn path, which skips each step that would write or run: recording a local hit, downloading a remote hit, replaying a hit, and spawning the command. - Each line is a real run's, without `, executing` or `, replaying`, and with no blank line between commands. `--log` is ignored, since it only shapes task output: `--dry-run` and `--log` move from `RunFlags`, which selects tasks, to `RunCommand`, and resolve to a `RunMode` of `Real { log }` or `Dry`, so a dry run can't carry a log mode. - The summary follows a real run's rules, with "nothing was run" in place of the successful count and the time saved, and without the `--last-details` hint. The detailed summary (`-v`) has no ✓ or exit codes. Real run, unchanged: ``` vp run: 1/2 cache hit (50%), 2/2 successful. (Run `vp run --last-details` for full details) │ │ │ │ │ │ │ │ │ └─ tasks │ │ │ └─ tasks that succeeded │ │ └─ cache hit rate │ └─ tasks └─ cache hits ``` Dry run: ``` vp run: 1/2 cache hit (50%), nothing was run. │ │ │ │ │ └─ cache hit rate │ └─ tasks └─ cache hits ``` - A cache lookup error is printed under its command and stops the dry run, as it stops a real run, and both exit 1. Real runs stopped but exited 0 before, since the summary only counted tasks that started; a task that fails before it starts now fails the run. Otherwise a dry run exits 0 once planning succeeds. - A nested `vp run --dry-run` isn't inlined. It's started as a process that prints its own dry run, so an enclosing dry run doesn't start it. - Without a task, `--dry-run` is an error, like `vp run -v`. After the task name, it's passed to the task, like any other flag. - RFC: the summary and cache-error rules are written down, and the sentence about the first changed file being the first in path order is removed. Without `-v`, the file check stops at whichever changed file it finds first. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Touches cache lookup, remote fetch, spawn, and exit-code paths across the runner; mistakes could skip real execution side effects or mis-report failures, though behavior is gated on `RunKind::Dry`. > > **Overview** > Adds **`vp run --dry-run`**, which plans the task graph and performs full cache lookups (including remote) but **does not** spawn processes, replay outputs, download remote archives, write cache entries, or persist `last-summary.json`. > > The CLI introduces **`RunMode`/`RunKind`**: `--dry-run` and `--log` move onto `RunCommand` (dry runs always use interleaved output). Execution and cache layers thread `RunKind` through lookup, spawn, and reporters so dry runs short-circuit after reporting cache status (`WouldSpawn` / `DryRun` not-updated reasons). > > **Output and summary** match a real run’s command lines and cache indicators, but omit `, executing` / `, replaying`, skip extra blank lines, use **“nothing was run”** instead of success/saved time, and omit the `--last-details` hint. **`-v`** detailed summaries drop exit icons and saved-time wording for hits. > > **Bugfix:** runs now **exit 1** when a task’s cache cannot be checked (e.g. DB unreadable), including failures before `start()`; nested `vp run --dry-run` is not inlined. Changelog and dry-run RFC/e2e snapshots document the behavior. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 3010605. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> Co-authored-by: Aaron Reisman <aaron@lifeiscontent.net> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
wan9chi
added a commit
that referenced
this pull request
Nov 9, 2026
…966) ## Motivation Coding agents and CI need a dry run's answer as data, without parsing text: which tasks would run, in what order, and why each would hit or miss (#791, voidzero-dev/vite-plus#2848). [The dry-run RFC](https://github.com/voidzero-dev/vite-task/blob/main/docs/rfcs/dry-run.md) specifies the run plan that `vp run --dry-run --json` prints, and this PR implements it through a new reporter. For now, a task whose command runs `vp run` fails and says so; #967 adds nested plans. ## Changes ```jsonc // vp run --dry-run --json build, after a.txt changed (indented in a terminal, one line when captured) { "$schema": "file:///tmp/vp_run-1000/run-plan_<hash>.json", "version": 1, "workspaceRoot": "/…", "plan": { "build": { "package": ".", "name": "build", "steps": [{ "program": "node_modules/.bin/vtt", "args": ["print-file", "a.txt", "b.txt"], "cache": { "status": "changed", "baseline": { "durationMs": 12 }, "changes": [{ "kind": "file", "path": "a.txt", "change": "changed" }], "changesPartial": true } }] } } } ``` - **CLI.** `--json` is no longer `exclusive`. It conflicts with an explicit `--log`, and `RunCommand::resolve` checks the rest once, with clap-style errors. `--json` alone resolves to the task graph, and with `--dry-run` to a run whose `RunMode` is `DryJson`. With a task but no `--dry-run`, the error says `--json` is only supported with `--dry-run` for now. With other flags and no task, it's the same conflict error as before. Without a task, `--dry-run --json` fails when it's planned, as `--dry-run` does. The session matches on that, so nothing re-checks `--json` later, and `plan_from_cli_run` resolves its command the same way. It's never interactive: a task that isn't found is an error on stderr, not the task list on stdout. An empty plan, such as when `--filter` matches nothing, prints `"plan": {}`. - **Cache data.** `Comparison` gains the baseline's duration and `changes_partial`. `changes_partial` is set when the file check stopped at the first changed file, or couldn't check some files. After a command, config or env change, only the previous run's key is compared, so the step is `changed` with its key changes, `changesPartial`, and no `baseline`; the RFC's `baseline` is optional for that case. Program, args and cwd changes keep what they changed from and to. vt_plan gets a program accessor on the spawn fingerprint, and name and args accessors on built-ins. - **Reporter** (`session/reporter/json/`). - When it's created, it lays out from the graph each task's ID, its sorted `dependsOn`, and its steps (`program`, `args`, `cwd`, `hook`). - Each leaf's `start` fills in its `cache`: `hit`, `changed`, `noBaseline`, `error` or `disabled`, or absent for a built-in. - At the end, it serializes the whole document and writes it once. - A run cancelled with Ctrl-C prints nothing. A failed cache lookup stops the run, as it stops a real run. As with any error that stops the document, the error goes to stderr and nothing is printed. - **Document.** Rust types with doc comments, deriving schemars under `cfg(test)`. A test regenerates the committed `schemas/run-plan.json`. `artifact!` embeds it, and each run copies it to the user's temp folder for `$schema`, as for `vp run --json`. Both documents are written by one `json::write_document`, which serializes into memory first, so an error partway prints nothing. - **Tests.** - `run_plan_json` covers the plan's layout: `-r` edges and `--parallel`, hooks, programs inside and outside the workspace, `--no-cache` and `--cache`, forwarded args, the shell, `cd` and the `cwd` setting, built-ins, and encoded task IDs. - Its `selection` case covers which tasks are planned: a task that some or all selected packages lack, an unknown package, and a filter that matches nothing, with and without `--fail-if-no-match`. - `dry_run_json` covers the cache statuses and flags that combine with `--dry-run --json`, or that are passed to the task when they come after its name. - Its `errors` case covers `--json` with a task but no `--dry-run`, with `--log`, with `--last-details`, or with flags but no task; `--dry-run --json` without a task; and a task that isn't found. - In the plan snapshots, only the expected `--json build` error message changes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Touches CLI parsing, session dispatch, and cache comparison shapes used by dry runs and reporters; behavior is well snapshot-tested but flag combinations and cache edge cases warrant review. > > **Overview** > Adds **`vp run --dry-run --json`**, which emits a single **run-plan** JSON document (tasks, `dependsOn`, per-step program/args/cwd/hook, and projected cache status) for CI and agents, with a committed **`schemas/run-plan.json`** and `$schema` materialized like the task graph. > > **CLI:** `--json` is no longer exclusive-only; with a task it **requires `--dry-run`** (otherwise a clap-style error). Bare `vp run --json` still dumps the task graph. Resolution flows through `RunCommand::resolve` / `RunMode::DryJson`, and **`--json` conflicts with `--log`**. > > **Cache / JSON payload:** `Comparison` and `Change` now carry **baseline duration**, **`changesPartial`**, and **from/to** details for program, args, and cwd so misses serialize faithfully; validation returns `Checked` with partial file lists. > > **Execution path:** A new **JSON reporter** lays out the graph up front, fills cache on each dry leaf `start`, writes once at the end (or stderr-only on lookup failure / cancel / nested `vp run`, which is rejected). Shared **`json::write_document`** and schema snapshot tests cover both JSON outputs. > > **Tests / docs:** E2E fixtures for plan layout, cache statuses, flags, and errors; dry-run RFC examples updated for optional baseline and `changesPartial`. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 220dbb0. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Motivation
Since #966,
vp run --dry-run --jsonfails for a task whose command runsvp run …. The dry-run RFC shows such a step asexpandedFromwith its ownplan, so the run plan is a tree, and each nested plan has its own dependencies.Changes
dependsOnis scoped to its own tasks, so the same task ID can appear at several levels.expandedFromis the item's command as written, with any args given after the task name.vp rungoes away.A nested
vp run --dry-runis still a spawned step, which a dry run doesn't start. The e2e fixturerun_plan_json_nestedcovers plans at several levels, args forwarded to a nested run, and a nested dry run.🤖 Generated with Claude Code
Note
Medium Risk
Touches dry-run JSON output shape and reporter layout for nested graphs; behavior is covered by new e2e snapshots but consumers parsing the old flat step model need to handle
NestedStep.Overview
vp run --dry-run --jsonno longer errors when a task runs anothervp run. Those steps are emitted as nested objects withexpandedFrom(the command as written, including args after the task name) and an embeddedplanin the same shape as the root document.The JSON reporter recursively lays out nested execution graphs before cache checks. Each nested plan keeps
dependsOnscoped to its own tasks, so the same task ID can appear at multiple levels. Cache status is applied by walking a path through nested plans rather than only top-level task/step indices.The run-plan JSON Schema splits steps into
CommandStepvsNestedStep, and the changelog documents the new behavior. E2E fixtures replace the former “not supported yet” error case with snapshots for multi-level nested plans, forwarded args, and nestedvp run --dry-run(still shown as a spawn step, not expanded).Reviewed by Cursor Bugbot for commit bc48a17. Bugbot is set up for automated code reviews on this repo. Configure here.