@vzn/vx 0.0.638 → 0.0.639

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/docs/caching.md +44 -15
  2. package/docs/cli.md +168 -345
  3. package/docs/execution.md +18 -43
  4. package/docs/features.md +7 -10
  5. package/docs/patterns.md +5 -7
  6. package/docs/schema.md +47 -49
  7. package/package.json +5 -5
  8. package/schemas/last.json +7 -4
  9. package/skills/vx/SKILL.md +6 -4
  10. package/src/cache/cache.ts +40 -10
  11. package/src/cache/git-inputs.ts +6 -0
  12. package/src/cache/index.ts +2 -2
  13. package/src/cache/layer.ts +25 -11
  14. package/src/cache/layered-cache.ts +108 -315
  15. package/src/cache/plugin-cache.ts +2 -10
  16. package/src/cache/remote-lane.ts +383 -0
  17. package/src/cache/run-history.ts +24 -8
  18. package/src/cache/run-store.ts +4 -4
  19. package/src/cache/schema.ts +6 -4
  20. package/src/cache/scoped-cache.ts +7 -0
  21. package/src/cli/completions.ts +2 -2
  22. package/src/cli/foreign-flags.ts +10 -22
  23. package/src/cli/help.ts +6 -31
  24. package/src/cli/info.ts +2 -1
  25. package/src/cli/last.ts +6 -3
  26. package/src/cli/plan-format.ts +5 -58
  27. package/src/cli/plugin-commands.ts +2 -2
  28. package/src/cli/run.ts +23 -185
  29. package/src/cli/show.ts +6 -0
  30. package/src/cli/upgrade.ts +123 -39
  31. package/src/cli/watch-cycle.ts +2 -1
  32. package/src/cli/watch.ts +29 -14
  33. package/src/cli/why.ts +4 -4
  34. package/src/config.ts +0 -6
  35. package/src/exec/executor.ts +1 -1
  36. package/src/exec/sandbox-runtime.ts +1 -1
  37. package/src/exec/sandbox-violations.ts +3 -1
  38. package/src/graph/index.ts +9 -1
  39. package/src/graph/priorities.ts +1 -1
  40. package/src/graph/scheduler.ts +4 -16
  41. package/src/graph/task-graph.ts +87 -17
  42. package/src/graph/task-status.ts +95 -0
  43. package/src/{orchestrator → history}/failure-mode.ts +1 -1
  44. package/src/{orchestrator → history}/history.ts +1 -1
  45. package/src/history/index.ts +48 -0
  46. package/src/{orchestrator → history}/metrics.ts +7 -20
  47. package/src/{orchestrator → history}/miss-reason.ts +1 -1
  48. package/src/index.ts +10 -28
  49. package/src/orchestrator/doctor.ts +1 -1
  50. package/src/orchestrator/execute-args.ts +80 -0
  51. package/src/orchestrator/execute-task.ts +21 -386
  52. package/src/orchestrator/fingerprint-watch.ts +2 -2
  53. package/src/orchestrator/index.ts +1 -73
  54. package/src/orchestrator/miss-save.ts +33 -3
  55. package/src/orchestrator/options.ts +1 -36
  56. package/src/orchestrator/output-host.ts +4 -3
  57. package/src/orchestrator/persistent-task.ts +321 -0
  58. package/src/orchestrator/persistent.ts +130 -3
  59. package/src/orchestrator/placement.ts +3 -3
  60. package/src/orchestrator/plan.ts +18 -5
  61. package/src/orchestrator/plugin-host.ts +67 -47
  62. package/src/orchestrator/plugin.ts +6 -6
  63. package/src/orchestrator/prepare.ts +28 -29
  64. package/src/orchestrator/remote-prefetch.ts +14 -1
  65. package/src/orchestrator/run-artifacts.ts +5 -177
  66. package/src/orchestrator/run-context.ts +55 -0
  67. package/src/orchestrator/run-failures.ts +15 -3
  68. package/src/orchestrator/run-records.ts +35 -3
  69. package/src/orchestrator/run-report.ts +81 -179
  70. package/src/orchestrator/run-settings.ts +1 -16
  71. package/src/orchestrator/run-stop.ts +153 -0
  72. package/src/orchestrator/run.ts +260 -601
  73. package/src/orchestrator/sandbox-fix.ts +0 -16
  74. package/src/orchestrator/signals.ts +14 -6
  75. package/src/orchestrator/stable-keys.ts +107 -3
  76. package/src/orchestrator/steps.ts +50 -0
  77. package/src/orchestrator/task-hash.ts +3 -16
  78. package/src/orchestrator/task-log-buffer.ts +1 -1
  79. package/src/orchestrator/task-process.ts +94 -0
  80. package/src/orchestrator/telemetry-host.ts +1 -1
  81. package/src/orchestrator/telemetry.ts +5 -82
  82. package/src/{orchestrator → report}/events.ts +41 -8
  83. package/src/{orchestrator → report}/forecast.ts +35 -2
  84. package/src/{orchestrator → report}/framed-output.ts +39 -33
  85. package/src/report/index.ts +36 -0
  86. package/src/{orchestrator → report}/logger.ts +158 -20
  87. package/src/{orchestrator → report}/status-line.ts +84 -7
  88. package/src/{orchestrator → report}/summary.ts +126 -35
  89. package/src/report/tally.ts +83 -0
  90. package/src/settings.ts +1 -22
  91. package/src/util/colors.ts +44 -4
  92. package/src/util/git-ignored.ts +143 -0
  93. package/src/util/index.ts +19 -4
  94. package/src/util/report.ts +13 -5
  95. package/src/util/settle.ts +14 -39
  96. package/src/util/tail.ts +1 -1
  97. package/src/util/ui.ts +17 -6
  98. package/schemas/summary.json +0 -133
  99. package/src/cache/chained-cache.ts +0 -280
  100. package/src/orchestrator/tally.ts +0 -112
  101. /package/src/{orchestrator → report}/path-links.ts +0 -0
  102. /package/src/{orchestrator → report}/plain-output.ts +0 -0
  103. /package/src/{orchestrator → report}/terminal-signals.ts +0 -0
package/docs/execution.md CHANGED
@@ -24,7 +24,7 @@ terminal and a task succeeding or failing. Read it alongside
24
24
  │ PROCESS — always. (A whole-run `backend` seam existed until
25
25
  │ 2026-08; it moved the scheduler server-side, which is why it
26
26
  │ went. Per-task placement is the `executor` capability.)
27
- │ --dry / --graph short-circuit into planRun instead.
27
+ │ --dry short-circuits into planRun instead.
28
28
  │
29
29
  ├─ Prepare (src/orchestrator/prepare.ts:prepareRun — shared with planRun)
30
30
  │ 1. findWorkspaceRoot(cwd) — the directory of the nearest
@@ -155,7 +155,7 @@ terminal and a task succeeding or failing. Read it alongside
155
155
  │ signal, forward it (SIGHUP as SIGTERM) to everything
156
156
  │ in liveChildren + persistentRegistry (and kill any
157
157
  │ running cache.inputs.runtime probe, C-65), wait
158
- │ VX_RUN_KILL_GRACE (2 s) for their groups, SIGKILL
158
+ │ 2 s for their groups, SIGKILL
159
159
  │ what is still there, let the run finish its own
160
160
  │ end (flush, teardown, cache close), then
161
161
  │ die of the signal (SIGINT → 130, SIGTERM → 143,
@@ -303,11 +303,10 @@ terminal and a task succeeding or failing. Read it alongside
303
303
  2. Run summary footer (projects / tasks / cache meters + info +
304
304
  time + result) — counts only real tasks (with `exec`); group tasks
305
305
  don't pollute the totals; failure frames replay just above it.
306
- 3. Optional --summarize JSON (default <cacheDir>/runs/<run_id>.json)
307
- and --profile Chrome-trace JSON (default profile.json).
306
+ 3. Optional --profile Chrome-trace JSON (default profile.json).
308
307
  4. recordRunBundle — one transaction writing a `runs` row per real
309
308
  task plus the `invocations` header row (command, git/CI/host
310
- context, tags, counts). Group + aborted tasks skipped; a run
309
+ context, counts). Group + aborted tasks skipped; a run
311
310
  a signal stopped records nothing.
312
311
  5. Telemetry: emit the RunSummaryRecord to every active sink +
313
312
  await their flush (crash-isolated; skipped when no sink).
@@ -325,11 +324,7 @@ terminal and a task succeeding or failing. Read it alongside
325
324
  alive, Ctrl-C reaps the process group. That first exit ends the
326
325
  session: the others are torn down (SIGTERM, grace, SIGKILL) and
327
326
  a non-zero exit makes the run exit 1, so a script's `vx run dev`
328
- fails when the server it started fell over. That server then reads
329
- `failed` with its own exit in the rewritten `--summarize` and in the
330
- report `--report-file` writes; one a Ctrl-C stopped does not. The
331
- stdout `--report` prints above the footer, so it reads as the
332
- footer does, before the wait. Under
327
+ fails when the server it started fell over. Under
333
328
  `holdPersistent` (the watch loop) run() instead returns them on
334
329
  `RunSummary.persistent`, still running, for the caller to stop;
335
330
  one that dies on its own after that is said, its `stop()` is not.
@@ -543,21 +538,20 @@ Every surface uses one outcome vocabulary: task axis `success` /
543
538
  `failed` / `skipped` / `aborted` (+ `running` live), cache axis
544
539
  `miss` / `up-to-date` (fresh) / `local` / `remote`. The `--verbosity 1`
545
540
  table spells the combinations out as `success` / `restored-local` /
546
- `restored-remote` / `up-to-date`; `--report` keeps the two axes as its
547
- Status and Cache columns.
541
+ `restored-remote` / `up-to-date`.
548
542
 
549
543
  The colors / framing modules:
550
544
 
551
545
  - `util/colors.ts` — ANSI truecolor (`ansi-16m`), gated by
552
546
  `NO_COLOR` / `FORCE_COLOR` / `isTTY`. Programmatic-logger callers
553
547
  always see plain text.
554
- - `orchestrator/framed-output.ts` — the `┌─` frames, the glyph grid
548
+ - `report/framed-output.ts` — the `┌─` frames, the glyph grid
555
549
  (`formatTaskRow`), one-liners, persistent markers.
556
- - `orchestrator/status-line.ts` — the serialized writer + the worker
550
+ - `report/status-line.ts` — the serialized writer + the worker
557
551
  region.
558
- - `orchestrator/logger.ts` — composes them; resolves the output view
552
+ - `report/logger.ts` — composes them; resolves the output view
559
553
  and applies the per-flow visibility policy.
560
- - `orchestrator/summary.ts` — the closing footer (wordmark rule,
554
+ - `report/summary.ts` — the closing footer (wordmark rule,
561
555
  projects/tasks/cache meters, info and time rows).
562
556
 
563
557
  ## Concurrency
@@ -617,9 +611,9 @@ snapshot is clean). `--cache-policy=<spec>` gives per-layer control. Full
617
611
  semantics: [`cli.md` § Cache control](./cli.md#cache-control---cache-policy---no-cache---force)
618
612
  and [`caching.md` § Cache policy](./caching.md#cache-policy-readwrite-axes).
619
613
 
620
- ## Planning paths (`--dry`, `--graph`)
614
+ ## Planning path (`--dry`)
621
615
 
622
- Both short-circuit execution. The planner (`orchestrator/plan.ts`)
616
+ `--dry` short-circuits execution. The planner (`orchestrator/plan.ts`)
623
617
  runs the same setup steps — workspace discovery, config load, package
624
618
  graph, task graph — and the same per-task hash derivation, then
625
619
  probes the cache. The probe is read-only; against a remote layer it
@@ -628,8 +622,6 @@ deliberate side effect: `cache.inputs.runtime` / `workspaceRuntime`
628
622
  probe commands DO run, because predicting a key requires resolving
629
623
  them — keep runtime inputs side-effect-free.
630
624
 
631
- The two flags differ only in output format:
632
-
633
625
  - `--dry[=text|json]` → text (default) or JSON list of predicted
634
626
  outcomes per task: `hit-local`, `hit-remote`, `miss`, `no-cache`,
635
627
  `group`. Formatter: `cli/plan-format.ts:formatPlanText` /
@@ -639,36 +631,19 @@ The two flags differ only in output format:
639
631
  executors is a plugin-factory call, the same class the cache capability
640
632
  already makes at plan time; a plugin that throws costs the label, never
641
633
  the plan.
642
- - `--graph[=<path>]` → Graphviz DOT, colored by predicted status.
643
- Pipe to `dot` for SVG/PNG render. Formatter:
644
- `formatGraphDot`.
645
-
646
- Mutually exclusive: `--dry` and `--graph` together is a parse error.
647
- Combining either with `--summarize`, `--profile`, `--report` or
648
- `--report-file` is a parse error (those need a real run; the last two
649
- were accepted and wrote nothing until item 992).
634
+ Combining it with `--profile` is a parse error (the profile needs a
635
+ real run).
650
636
 
651
637
  ## Run artifacts
652
638
 
653
- - **`--summarize[=<path>]`** — per-run JSON to
654
- `<cacheDir>/runs/<run_id>.json` by default (or the explicit path).
655
- Mirrors the `runs` table shape — one task entry per real task, hits
656
- included, with status, hash, duration, cpu_ms, peak RSS, hrtime spans
657
- (bigint serialized as strings to preserve ns precision).
658
639
  - **`--profile[=<path>]`** — Chrome-trace JSON of every task's
659
640
  wallclock span. Default path: `profile.json` (cwd-relative). One
660
641
  `tid` per project so concurrent tasks render on distinct lanes.
661
642
  Open with `chrome://tracing` or https://ui.perfetto.dev.
662
- - **`--report[=markdown]`** — a markdown table to stdout
663
- above the footer (CI step summaries), through `RunOptions.beforeFooter`.
664
- - **`--report-file <path>`** — the same report APPENDED to `<path>`
665
- (`$GITHUB_STEP_SUMMARY`).
666
-
667
- Writers live in `orchestrator/run-artifacts.ts:writeRunSummary` /
668
- `writeRunProfile` and `orchestrator/run-report.ts`. A `--summarize` or
669
- `--profile` write error is surfaced via `log.status`, a `--report-file`
670
- one on stderr (`cli/run.ts`); neither changes the run's exit code — the
671
- run already happened.
643
+
644
+ The writer is `orchestrator/run-artifacts.ts:writeRunProfile`. A write error is surfaced via `log.status` and does
645
+ not change the run's exit code — the run already happened. A CI job
646
+ summary is `@vzn/vx-ci`'s.
672
647
 
673
648
  ## Why each rule
674
649
 
package/docs/features.md CHANGED
@@ -26,7 +26,7 @@ config key or an environment variable is missing here.
26
26
  - **Task picker** — `vx run` with no task in a terminal lists tasks to pick. [page](https://vznjs.github.io/vx/features/task-picker/) · post: [The small things](https://vznjs.github.io/vx/blog/the-small-things/)
27
27
  - **Typo hints** — an unknown task, project or filter suggests the nearest name. [page](https://vznjs.github.io/vx/features/filter-hints/) · post: [The small things](https://vznjs.github.io/vx/blog/the-small-things/)
28
28
  - **Turbo and Nx spellings** (`-t`, `-p`, `--exclude`, `--parallel`, `--base`, `--dry-run`, `--skip-nx-cache`) — accepted, or refused naming the vx spelling. [page](https://vznjs.github.io/vx/features/turbo-nx-flags/) · post: [Flags you already know](https://vznjs.github.io/vx/blog/flags-you-already-know/)
29
- - **Ctrl-C leaves nothing running** (`--kill-grace`, `--teardown-timeout`, `run.killGrace`, `run.teardownTimeout`, `VX_RUN_KILL_GRACE`, `VX_RUN_TEARDOWN_TIMEOUT`) — the whole process tree stops, then teardown runs. [page](https://vznjs.github.io/vx/features/ctrl-c/) · post: [Ctrl-C leaves nothing running](https://vznjs.github.io/vx/blog/ctrl-c/)
29
+ - **Ctrl-C leaves nothing running** — the whole process tree stops, then teardown runs. [page](https://vznjs.github.io/vx/features/ctrl-c/) · post: [Ctrl-C leaves nothing running](https://vznjs.github.io/vx/blog/ctrl-c/)
30
30
  - **Longest chain first** — the scheduler starts the critical path first; `@vzn/vx-schedule-history` learns it from past runs. [page](https://vznjs.github.io/vx/features/critical-path/) · post: [Bitsets, popcount, and a scheduler tick](https://vznjs.github.io/vx/blog/bitsets-and-the-scheduler/)
31
31
  - **Schedule strategy** (`--schedule`, `run.schedule`, `VX_RUN_SCHEDULE`) — pick the order with no timings: most work waiting (default), longest chain, most direct dependents, or ready order; plugins still lead. [page](https://vznjs.github.io/vx/features/critical-path/) · post: [Which task runs first?](https://vznjs.github.io/vx/blog/scheduling-strategies/#pick-the-guess)
32
32
  - **No daemon** — every run starts cold and still answers in milliseconds. [page](https://vznjs.github.io/vx/features/no-daemon/) · post: [No daemon, on purpose](https://vznjs.github.io/vx/blog/no-daemon/)
@@ -47,23 +47,20 @@ config key or an environment variable is missing here.
47
47
  - **Run summary** — one block: projects, tasks, cache, time; nothing prints below it. [page](https://vznjs.github.io/vx/features/run-summary/) · post: [A run you can read](https://vznjs.github.io/vx/blog/a-run-you-can-read/)
48
48
  - **Per-task table** (`--verbosity`) — a per-task summary after the run. [page](https://vznjs.github.io/vx/features/per-task-table/) · post: [One failure, and exactly what it takes down](https://vznjs.github.io/vx/blog/when-a-build-fails/)
49
49
  - **Failed output kept for agents** — a failure's full log is saved and pointed to. [page](https://vznjs.github.io/vx/features/failed-output-kept/) · post: [Built for the agent at the keyboard](https://vznjs.github.io/vx/blog/built-for-agents/)
50
- - **Markdown report** (`--report`, `--report-file`) — a run report for a PR or `$GITHUB_STEP_SUMMARY`. [page](https://vznjs.github.io/vx/features/markdown-report/) · post: [Output that fits the run](https://vznjs.github.io/vx/blog/output-that-fits-the-run/)
51
- - **Run JSON** (`--summarize`) — per-run JSON for scripts, with the time the cache saved (`savedMs`). [page](https://vznjs.github.io/vx/features/run-json/) · post: [See inside a run](https://vznjs.github.io/vx/blog/see-inside-a-run/)
52
50
  - **Trace profile** (`--profile`) — Chrome-trace JSON of the run. [page](https://vznjs.github.io/vx/features/profile/) · post: [Output that fits the run](https://vznjs.github.io/vx/blog/output-that-fits-the-run/)
53
51
  - **Run branch** (`--branch`, `VX_RUN_BRANCH`) — name the branch a run records in its history and forecasts from; unset, git's branch, then the CI provider's. no post
54
- - **Run tags** (`--tag`) — label a run; recorded in history. [page](https://vznjs.github.io/vx/features/run-tags/) · post: [See inside a run](https://vznjs.github.io/vx/blog/see-inside-a-run/)
55
52
  - **Stage timing** (`VX_DEBUG_TIMING`) — vx's own stage table, for performance work. [page](https://vznjs.github.io/vx/features/stage-timing/) · post: [See inside a run](https://vznjs.github.io/vx/blog/see-inside-a-run/)
56
53
  - **Output flows** — what is printed follows the run's intent (focused, broad or CI); a truthy `CI` wins. [page](https://vznjs.github.io/vx/features/output-flows/) · post: [Output that fits the run](https://vznjs.github.io/vx/blog/output-that-fits-the-run/)
57
54
  - **Cache-aware glyphs** — each task line's glyph shows ran, fresh, restored locally or remotely, failed, skipped or persistent. [page](https://vznjs.github.io/vx/features/cache-glyphs/) · post: [Output that fits the run](https://vznjs.github.io/vx/blog/output-that-fits-the-run/)
58
55
  - **GitHub Actions log groups** — on Actions, each task's block folds in a `::group::` with its outcome and time. [page](https://vznjs.github.io/vx/features/actions-log-groups/) · post: [The basics, done carefully](https://vznjs.github.io/vx/blog/the-basics/)
59
- - **Colors** (`NO_COLOR`, `FORCE_COLOR`) — truecolor output, forced on or off by env. [page](https://vznjs.github.io/vx/features/colors/) · post: [The basics, done carefully](https://vznjs.github.io/vx/blog/the-basics/)
56
+ - **Colors** (`NO_COLOR`, `FORCE_COLOR`) — truecolor output, forced on or off by env; `TERM=dumb` prints plain, with no live region. [page](https://vznjs.github.io/vx/features/colors/) · post: [The basics, done carefully](https://vznjs.github.io/vx/blog/the-basics/)
60
57
  - **Signal-named exits** — a failure reads `exit 137, 128 + SIGKILL`. [page](https://vznjs.github.io/vx/features/signal-exits/) · post: [When a task misbehaves](https://vznjs.github.io/vx/blog/tasks-that-misbehave/)
58
+ - **vx's own steps as rows** — on a terminal, what vx does around the tasks (`vx:uploads`, `<plugin>:teardown`) shows as a running row once it passes 150 ms, then a list row; never counted as a task. no post
61
59
  - **Plain output off a TTY** — no live region, and a missing task lists tasks instead of opening the picker. [page](https://vznjs.github.io/vx/features/plain-off-tty/) · post: [The basics, done carefully](https://vznjs.github.io/vx/blog/the-basics/)
62
60
 
63
61
  ## Plan and explain
64
62
 
65
63
  - **Dry run** (`--dry`) — the task graph and the predicted hits and misses, text or JSON (with the predicted wall time and its critical path), nothing runs. [page](https://vznjs.github.io/vx/features/dry-run/) · post: [See the plan before you run it](https://vznjs.github.io/vx/blog/dry-run/)
66
- - **Graph** (`--graph`) — the task graph as Graphviz DOT. [page](https://vznjs.github.io/vx/features/task-graph/) · post: [See inside a run](https://vznjs.github.io/vx/blog/see-inside-a-run/)
67
64
  - **vx why** (`vx why`, `--run`) — why a task re-ran, down to the file, env var or config that changed, and the task that wrote a moved input. [page](https://vznjs.github.io/vx/features/vx-why/) · post: [Why did this re-run?](https://vznjs.github.io/vx/blog/why-did-this-rerun/)
68
65
  - **vx show** (`vx show`) — every project, a project's or a task's resolved config, read from the lock as a run reads it. [page](https://vznjs.github.io/vx/features/vx-show/) · post: [See what a task really is](https://vznjs.github.io/vx/blog/vx-show/)
69
66
  - **vx last** (`vx last`, `--list`, `--failed`, `--log`, `--stats`, `--branch`) — replay a recorded run's summary, list recent runs, print one task's output, or per-task history stats (p50 as the live estimate forecasts it on a branch, p99, pass and hit rate, failure mode, peak RSS). [page](https://vznjs.github.io/vx/features/vx-last/) · post: [The last run, on request](https://vznjs.github.io/vx/blog/vx-last/)
@@ -91,7 +88,7 @@ config key or an environment variable is missing here.
91
88
  - **Lockfile-aware keys** (`@vzn/vx-lockfile`: `pnpm()`, `bun()`, `npm()`, `yarn()`) — a bump re-keys only the projects whose closure changed; each also adds every member with a `package.json` as a project named by its `name`, gives vx the project edges the lockfile records (core reads no `package.json`), keys each package's `package.json` as an input and folds its manager's install settings (`.npmrc`, `bunfig.toml`, `pnpm-workspace.yaml`, `.yarnrc.yml`) into every task. Core keys no package file, so without one an install re-keys nothing; `npm()` reads `npm-shrinkwrap.json` first, and `bun()` refuses a lone `bun.lockb`. [page](https://vznjs.github.io/vx/features/lockfile-keys/) · post: [A lockfile bump should re-key two tasks](https://vznjs.github.io/vx/blog/lockfile-aware-keys/)
92
89
  - **Upfront keys** (`run.rules.upfrontKeys`, `VX_RUN_RULES_UPFRONT_KEYS`) — refuses an input glob a same-project task's outputs could match, so every key is known before anything runs. [page](https://vznjs.github.io/vx/features/upfront-keys/) · post: [Cascade through dependencies by folding input keys, never outputs](https://vznjs.github.io/vx/blog/cascade-through-inputs/)
93
90
  - **Dependency rules** (`run.rules.dependencies`) — `vx lock` refuses a project depending on one a tag or name rule denies or does not allow, every broken edge named (`VX_E_DEPENDENCY_RULE`). [schema](https://vznjs.github.io/vx/schema/) · post: [Say which projects may depend on which](https://vznjs.github.io/vx/blog/dependency-rules/)
94
- - **Cache controls** (`--no-cache`, `--force`, `--cache-policy`, `--cache-policy`, `VX_CACHE_POLICY`, `cache.policy`) — off, refresh, or per-layer read/write. [page](https://vznjs.github.io/vx/features/cache-controls/) · post: [The cache on your terms](https://vznjs.github.io/vx/blog/the-cache-on-your-terms/)
91
+ - **Cache controls** (`--no-cache`, `--force`, `--cache-policy`, `--cache-policy`, `VX_CACHE_POLICY`, `cache.policy`) — off, re-run past it, or per-layer read/write. [page](https://vznjs.github.io/vx/features/cache-controls/) · post: [The cache on your terms](https://vznjs.github.io/vx/blog/the-cache-on-your-terms/)
95
92
  - **Cache location** (`--cache-dir`, `cache.dir`, `VX_CACHE_DIR`) — where the cache lives. [page](https://vznjs.github.io/vx/features/cache-location/) · post: [The cache on your terms](https://vznjs.github.io/vx/blog/the-cache-on-your-terms/)
96
93
  - **Cache scope** (`cache.scope`, `VX_CACHE_SCOPE`, `--cache-scope`) — a scope reads the default entries, then its own, and writes only its own. [page](https://vznjs.github.io/vx/features/cache-scope/) · post: [The cache on your terms](https://vznjs.github.io/vx/blog/the-cache-on-your-terms/)
97
94
  - **Cache pruning** (`vx cache clean`, `--older-than`, `--max-size`, `--global`, `--dry-run`, `cache.retention`, `maxSize`, `olderThan`, `VX_CACHE_RETENTION_MAX_SIZE`, `VX_CACHE_RETENTION_OLDER_THAN`) — evict by age or size, LRU; with no bound, clear this repo's store (shared by its checkouts) and run history; `--global` clears every repo's store. [page](https://vznjs.github.io/vx/features/cache-prune/) · post: [One command to know your workspace](https://vznjs.github.io/vx/blog/know-your-workspace/)
@@ -101,7 +98,7 @@ config key or an environment variable is missing here.
101
98
  - **Hits replay both streams** — stdout and stderr come back in the order the run printed them. [page](https://vznjs.github.io/vx/features/both-streams/) · post: [What goes into a key, and what comes back](https://vznjs.github.io/vx/blog/inside-a-cache-hit/)
102
99
  - **Restore lane** — cache restores run on their own lane, up to twice `--concurrency`. [page](https://vznjs.github.io/vx/features/restore-lane/) · post: [What goes into a key, and what comes back](https://vznjs.github.io/vx/blog/inside-a-cache-hit/)
103
100
  - **Line-ending-correct keys** — files git filters (`eol`, `core.autocrlf`) key on the bytes the build sees. [page](https://vznjs.github.io/vx/features/line-endings/) · post: [Your cache key is already in git's index](https://vznjs.github.io/vx/blog/keys-from-git/)
104
- - **Background remote uploads** — remote writes drain at the end of the run and never fail the build. [page](https://vznjs.github.io/vx/features/background-uploads/) · post: [What goes into a key, and what comes back](https://vznjs.github.io/vx/blog/inside-a-cache-hit/)
101
+ - **Background remote uploads** — remote writes drain at the end of the run and never fail the build; the footer counts them running (`⇡`) and failed (`✗`). [page](https://vznjs.github.io/vx/features/background-uploads/) · post: [What goes into a key, and what comes back](https://vznjs.github.io/vx/blog/inside-a-cache-hit/)
105
102
  - **Bring your own remote cache** (plugin `cache`) — plug any cache server in through one interface. [page](https://vznjs.github.io/vx/features/custom-remote-cache/) · post: [Extend vx in an afternoon](https://vznjs.github.io/vx/blog/extend-vx/)
106
103
 
107
104
  ## Correctness
@@ -119,11 +116,11 @@ config key or an environment variable is missing here.
119
116
  ## Daily work
120
117
 
121
118
  - **Watch mode** (`vx watch`, `--poll`, `watch.poll`, `VX_WATCH_POLL`, `VX_DEBUG_WATCH`) — re-run what a change affects, on content, not events. [page](https://vznjs.github.io/vx/features/watch/) · post: [Watch: a content gate](https://vznjs.github.io/vx/blog/watch-mode/)
122
- - **Dev servers in the graph** (`exec.persistent`, `readyWhen`, `--ready-notice`, `run.readyNotice`, `VX_RUN_READY_NOTICE`) — a server is a node; dependents start when it is ready. [page](https://vznjs.github.io/vx/features/dev-servers/) · post: [Dev servers as graph nodes](https://vznjs.github.io/vx/blog/dev-servers-in-the-graph/)
119
+ - **Dev servers in the graph** (`exec.persistent`, `readyWhen`) — a server is a node; dependents start when it is ready. [page](https://vznjs.github.io/vx/features/dev-servers/) · post: [Dev servers as graph nodes](https://vznjs.github.io/vx/blog/dev-servers-in-the-graph/)
123
120
  - **Interactive tasks** (`exec.interactive`) — a task that owns the terminal. [page](https://vznjs.github.io/vx/features/interactive-tasks/) · post: [When a task misbehaves](https://vznjs.github.io/vx/blog/tasks-that-misbehave/)
124
121
  - **Shell completions** (`vx completions`) — bash, zsh, fish. [page](https://vznjs.github.io/vx/features/completions/) · post: [The small things](https://vznjs.github.io/vx/blog/the-small-things/)
125
122
  - **Project-local vx** (`VX_HANDED_OFF`) — a global vx hands every call to the project's own `@vzn/vx`. [page](https://vznjs.github.io/vx/cli/#which-vx-runs)
126
- - **vx upgrade** (`vx upgrade`) — upgrade vx the way it was installed: a verified binary, or its package manager. [page](https://vznjs.github.io/vx/features/upgrade/) · post: [An upgrade you can trust](https://vznjs.github.io/vx/blog/upgrade-you-can-trust/)
123
+ - **vx upgrade** (`vx upgrade`) — upgrade vx the way it was installed: a verified binary, or its package manager. Inside a project, the project's vx and the global vx that ran it move together. [page](https://vznjs.github.io/vx/features/upgrade/) · post: [An upgrade you can trust](https://vznjs.github.io/vx/blog/upgrade-you-can-trust/)
127
124
  - **Help and version** (`vx help`, `vx version`) — every verb's reference. [page](https://vznjs.github.io/vx/features/help-and-version/) · post: [An upgrade you can trust](https://vznjs.github.io/vx/blog/upgrade-you-can-trust/)
128
125
  - **Did-you-mean** — a mistyped flag or verb gets the nearest valid spelling. [page](https://vznjs.github.io/vx/features/did-you-mean/) · post: [The small things](https://vznjs.github.io/vx/blog/the-small-things/)
129
126
  - **Task typed as a verb** — `vx build app` answers with the exact `vx run` command that does it. [page](https://vznjs.github.io/vx/features/task-as-verb/) · post: [The small things](https://vznjs.github.io/vx/blog/the-small-things/)
package/docs/patterns.md CHANGED
@@ -161,19 +161,17 @@ Turbo-aware cache server can transit our blobs unchanged.
161
161
  | `--concurrency <n>` | Turbo | `src/cli/run.ts` |
162
162
  | `--no-cache` (all off) / `--force` (reads off, writes on) — distinct, not synonyms | Turbo-adjacent | `src/cli/run.ts` |
163
163
  | `--dry` / `--dry=json` (plan output) | Turbo | `src/orchestrator/plan.ts` |
164
- | `--graph[=<path>]` (DOT) | Turbo + Nx | `src/cli/plan-format.ts` |
165
- | `--summarize[=<path>]` (per-run JSON) | Turbo | `src/orchestrator/run-artifacts.ts` |
166
164
  | `--profile[=<path>]` (Chrome-trace) | Turbo | `src/orchestrator/run-artifacts.ts` |
167
165
  | `--affected[=<base>]` | Turbo + Nx | `src/workspace/affected.ts` |
168
166
  | `watch <task>` subcommand | Turbo + Nx | `src/cli/watch.ts` |
169
167
 
170
168
  ### Output presentation
171
169
 
172
- | Pattern | Source | vx source |
173
- | ---------------------------------------------------- | ------------------------------------------------ | --------------------------------------------------- |
174
- | Framed per-task blocks (`┌─ … └─`) | Turbo | `src/orchestrator/framed-output.ts` |
175
- | End-of-run summary footer (task/cache meters + time) | Turbo-adjacent (vx renders meters, not a banner) | `src/orchestrator/summary.ts` |
176
- | Per-task output buffered until task finishes | Turbo | `src/orchestrator/framed-output.ts` ("Turbo-style") |
170
+ | Pattern | Source | vx source |
171
+ | ---------------------------------------------------- | ------------------------------------------------ | --------------------------------------------- |
172
+ | Framed per-task blocks (`┌─ … └─`) | Turbo | `src/report/framed-output.ts` |
173
+ | End-of-run summary footer (task/cache meters + time) | Turbo-adjacent (vx renders meters, not a banner) | `src/report/summary.ts` |
174
+ | Per-task output buffered until task finishes | Turbo | `src/report/framed-output.ts` ("Turbo-style") |
177
175
 
178
176
  (There is no `>>> FULL TURBO`-style all-cached banner — a fully-green
179
177
  cache meter carries the message.)
package/docs/schema.md CHANGED
@@ -376,7 +376,7 @@ for it to exit. Instead it considers the task "ready":
376
376
  The trailing partial line is tested too, so prompt-style banners
377
377
  without a newline (`printf 'Listening on :3000'`) count, and so does
378
378
  output read up to 250 ms after the shell exits. A server
379
- not ready after 10 s (`run.readyNotice`) is said once, naming
379
+ not ready after 10 s is said once, naming
380
380
  the pattern it waits for, and whether `exec.timeout` bounds the wait.
381
381
 
382
382
  ```ts
@@ -609,7 +609,7 @@ replays, the command a cache entry stores (what `vx why` prints and a
609
609
  remote cache receives), the `$ command` line, telemetry records,
610
610
  `vx show`, the hashes `vx why` gives for such a variable in
611
611
  `cache.inputs.env` (its value, unsalted: the row names it and its change), an executor's error, a plugin's warning or failure (a remote's reply; the failure masked in the error `run()` rejects with), a failed `cache.inputs` runtime probe's error (its command and output), and the run's own invocation line that `vx last` prints (a
612
- secret passed after `--`) and its `--tag`s. So are every string of a
612
+ secret passed after `--`). So are every string of a
613
613
  task `vx show` prints (a runtime probe, `readyWhen`, the description),
614
614
  the description `--dry` and the task picker print, a runtime probe's
615
615
  command as the run history stores it and `vx why` names its row (two
@@ -676,8 +676,8 @@ Semantics:
676
676
  edge from a `package.json`) and the cross-project `pkg#task` edges.
677
677
  With no plugin returning `deps` the workspace has no edge source and
678
678
  a `'^name'` is refused (`VX_E_DEPENDS_ON`: "`^build` needs project
679
- edges; add pnpm()/bun()/npm()/yarn() or a plugin that returns
680
- `deps`"): it would resolve to nothing and fold no upstream into the
679
+ edges; add pnpm()/bun()/npm()/yarn() and install so its lockfile
680
+ exists, or a plugin that returns `deps`"): it would resolve to nothing and fold no upstream into the
681
681
  key. With one, a project with no deps is a leaf; each path stops at the first dep
682
682
  that declares the task and an edge is added to it (Turbo/Nx
683
683
  direct-deps parity). The holder's own `dependsOn` is responsible
@@ -892,8 +892,8 @@ Always applied to every glob pass (regardless of what you wrote):
892
892
  itself via its own output. A path an output `!` entry takes back is
893
893
  no output, so it stays an input (A-44). Another task's outputs are
894
894
  not excluded for you: an input glob that can match them is refused
895
- while `rules.upfrontKeys` is on (X-54). Write the exclusion,
896
- `['**/*', '!dist/**']`. An input entry the task's OWN outputs take
895
+ while `rules.upfrontKeys` is on (X-54), unless git ignores them
896
+ whole. Write the exclusion, `['**/*', '!dist/**']`, or ignore `dist/`. An input entry the task's OWN outputs take
897
897
  back whole is always refused (X-55): vx removes outputs before a run,
898
898
  so a formatter declaring `src/**` as both would delete its sources. A
899
899
  task that rewrites files in place declares no outputs.
@@ -1261,7 +1261,9 @@ uses, so `dist` collides with `dist/app.js` (item 442). A whole subtree
1261
1261
  collides with any glob whose literal head lies inside it: `dist/**` with
1262
1262
  `dist/extra/**` or `dist/*.js` (item 941). Two globs a path built from
1263
1263
  either one matches are one too: `**/*` and `**/dist/**` both match
1264
- `x/dist/x`. Globs that only _might_
1264
+ `x/dist/x`. A pair is no overlap where one task's `!` output entry
1265
+ takes the other's glob back whole: `['**/dist', '!types/**']` beside
1266
+ `types/**` (a `!` entry is never cleaned or captured). Globs that only _might_
1265
1267
  overlap (`dist/*` and `dist/sub/**`) are let through; there, last
1266
1268
  restore wins. A workspace output is also compared with every other
1267
1269
  project's `files` outputs, read from the root: `packages/b/dist/a.txt`
@@ -1644,9 +1646,6 @@ export default defineWorkspace({
1644
1646
  affectedBase: 'origin/main',
1645
1647
  schedule: 'most-work',
1646
1648
  continue: 'deps-ok',
1647
- killGrace: '2s',
1648
- teardownTimeout: '3s',
1649
- readyNotice: '10s',
1650
1649
  rules: {
1651
1650
  exclusiveOutputs: true,
1652
1651
  upfrontKeys: true,
@@ -1687,36 +1686,33 @@ default and where it came from.
1687
1686
 
1688
1687
  <!-- settings table: generated by tests/settings.test.ts -->
1689
1688
 
1690
- | Setting | Variable | Flag | Value | Default | What it does |
1691
- | ---------------------------------------------- | ----------------------------------- | -------------------- | ----------------------------------------------------------------------- | ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- |
1692
- | `run.concurrency` | `VX_RUN_CONCURRENCY` | `--concurrency` | a positive integer, or a share of the cores like '50%' | the cores this process may use | Tasks at once: a positive integer, or a share of the cores (`'50%'`). |
1693
- | `run.schedule` | `VX_RUN_SCHEDULE` | `--schedule` | one of 'most-work', 'critical-path', 'direct-dependents', 'ready-order' | most-work | Which ready task starts first when there are more than workers. |
1694
- | `run.affectedBase` | `VX_RUN_AFFECTED_BASE` | — | a non-blank string | origin/HEAD, then the trunk, else HEAD~1 | The git ref a bare `--affected` compares with. |
1695
- | `run.branch` (variable or flag only) | `VX_RUN_BRANCH` | `--branch` | a non-blank string | git's branch, then the CI provider's | The branch a run records in its history and forecasts from. Flag and env only: a committed branch would be wrong on every other branch. |
1696
- | `run.timeout` | `VX_RUN_TIMEOUT` | `--timeout` | a duration like '500ms', '30s' or '5m', at most 24 days | none | A task's time limit, for tasks without their own `exec.timeout` (`'30m'`). A task's own `exec.timeout` beats it. |
1697
- | `run.retries` | `VX_RUN_RETRIES` | `--retries` | a non-negative integer | 0 | Re-runs of a failed task, for tasks without their own `exec.retries`. A task's own `exec.retries` beats it. |
1698
- | `run.continue` | `VX_RUN_CONTINUE` | `--continue` | one of 'never', 'deps-ok', 'always' | deps-ok | What a failed task takes down: `never` stops the run, `deps-ok` skips its dependents, `always` runs them too. |
1699
- | `run.outputLogs` (variable or flag only) | `VX_RUN_OUTPUT_LOGS` | `--output-logs` | one of 'full', 'errors-only', 'hash-only', 'none' | `full` on CI, else by what the run selects | Which task output the run prints. Chosen before the workspace loads, so flag and env only. |
1700
- | `run.killGrace` | `VX_RUN_KILL_GRACE` | `--kill-grace` | a duration like '500ms', '30s' or '5m', at most 24 days | 2s | How long a task that ignores SIGTERM gets before SIGKILL. |
1701
- | `run.teardownTimeout` | `VX_RUN_TEARDOWN_TIMEOUT` | `--teardown-timeout` | a duration like '500ms', '30s' or '5m', at most 24 days | 3s | The bound on each plugin's end-of-run flush and teardown. |
1702
- | `run.readyNotice` | `VX_RUN_READY_NOTICE` | `--ready-notice` | a duration like '500ms', '30s' or '5m', at most 24 days | 10s | How long a persistent task may take to match `readyWhen` before vx says it waits. |
1703
- | `run.rules.exclusiveOutputs` | `VX_RUN_RULES_EXCLUSIVE_OUTPUTS` | — | true or false | true | Refuse two tasks whose outputs overlap even when an edge orders them. |
1704
- | `run.rules.upfrontKeys` | `VX_RUN_RULES_UPFRONT_KEYS` | — | true or false | true | Refuse a task whose inputs can match another task's outputs. |
1705
- | `cache.dir` | `VX_CACHE_DIR` | `--cache-dir` | a non-blank string | .vx/cache, entries in ~/.vx/<repo>/cache | Where the cache lives, relative to the workspace root; named, it holds the whole cache. |
1706
- | `cache.retention.maxSize` | `VX_CACHE_RETENTION_MAX_SIZE` | — | a size like '500MB' or '10GB' | none | Evict least-recently-used entries at each run's end until the cache is under this (`'10GB'`). |
1707
- | `cache.retention.olderThan` | `VX_CACHE_RETENTION_OLDER_THAN` | — | a duration like '45s', '12h' or '30d' | none | Evict entries unused for this long at each run's end (`'30d'`). |
1708
- | `cache.policy` | `VX_CACHE_POLICY` | `--cache-policy` | a non-blank string | every layer reads and writes | Which layers a run reads and writes (`'local:rw,remote:r'`); `--no-cache` and `--force` apply on top. |
1709
- | `cache.scope` | `VX_CACHE_SCOPE` | `--cache-scope` | a non-blank string | none (the default keys) | Where cache writes land (`'pr-123'`): reads try the default key, then the scope's. |
1710
- | `cache.download` | `VX_CACHE_DOWNLOAD` | `--cache-download` | one of 'all', 'toplevel', 'none' | all | Which remote hits land on disk: every one, only the requested tasks, or none. |
1711
- | `cache.inputStability.strategy` | `VX_CACHE_INPUT_STABILITY_STRATEGY` | — | one of 'immutable', 'mutable', 'unchecked' | immutable | What a write to an input during the command means for the save, for tasks without their own. A task's own `cache.inputStability.strategy` beats it. |
1712
- | `cache.inputStability.severity` | `VX_CACHE_INPUT_STABILITY_SEVERITY` | — | one of 'warn', 'error' | warn | Whether a skipped save warns or fails the task, for tasks without their own. A task's own `cache.inputStability.severity` beats it. |
1713
- | `cache.outputs.clean` | — | — | true or false | true | Remove a task's declared outputs before it runs on a miss, for tasks without their own. A task's own `cache.outputs.clean` beats it. |
1714
- | `lock.reviewFiles` | `VX_LOCK_REVIEW_FILES` | — | true or false | true | `vx lock` writes each project's resolved config, pretty, to `vx.lock.json` beside its `vx.config`, for review; vx never reads it. |
1715
- | `watch.poll` | `VX_WATCH_POLL` | `vx watch --poll` | true or false | false | Poll for changes from the start instead of the OS watcher. |
1716
- | `config.workerTimeout` (variable or flag only) | `VX_CONFIG_WORKER_TIMEOUT` | — | a duration like '500ms', '30s' or '5m', at most 24 days | 30s | How long one config evaluation may take. |
1717
- | `debug.timing` (variable or flag only) | `VX_DEBUG_TIMING` | — | true or false | false | Print the run's stage timings to stderr. |
1718
- | `debug.watch` (variable or flag only) | `VX_DEBUG_WATCH` | — | true or false | false | `vx watch`: say on stderr each time the loop arms, with the projects it watches. |
1719
- | `debug.dumpable` (variable or flag only) | `VX_DEBUG_DUMPABLE` | — | true or false | false | Linux: keep vx dumpable, so strace or a debugger can read its memory and `/proc/<pid>/fd`, and an unsandboxed task its env. |
1689
+ | Setting | Variable | Flag | Value | Default | What it does |
1690
+ | ---------------------------------------------- | ----------------------------------- | ------------------ | ----------------------------------------------------------------------- | ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- |
1691
+ | `run.concurrency` | `VX_RUN_CONCURRENCY` | `--concurrency` | a positive integer, or a share of the cores like '50%' | the cores this process may use | Tasks at once: a positive integer, or a share of the cores (`'50%'`). |
1692
+ | `run.schedule` | `VX_RUN_SCHEDULE` | `--schedule` | one of 'most-work', 'critical-path', 'direct-dependents', 'ready-order' | most-work | Which ready task starts first when there are more than workers. |
1693
+ | `run.affectedBase` | `VX_RUN_AFFECTED_BASE` | — | a non-blank string | origin/HEAD, then the trunk, else HEAD~1 | The git ref a bare `--affected` compares with. |
1694
+ | `run.branch` (variable or flag only) | `VX_RUN_BRANCH` | `--branch` | a non-blank string | git's branch, then the CI provider's | The branch a run records in its history and forecasts from. Flag and env only: a committed branch would be wrong on every other branch. |
1695
+ | `run.timeout` | `VX_RUN_TIMEOUT` | `--timeout` | a duration like '500ms', '30s' or '5m', at most 24 days | none | A task's time limit, for tasks without their own `exec.timeout` (`'30m'`). A task's own `exec.timeout` beats it. |
1696
+ | `run.retries` | `VX_RUN_RETRIES` | `--retries` | a non-negative integer | 0 | Re-runs of a failed task, for tasks without their own `exec.retries`. A task's own `exec.retries` beats it. |
1697
+ | `run.continue` | `VX_RUN_CONTINUE` | `--continue` | one of 'never', 'deps-ok', 'always' | deps-ok | What a failed task takes down: `never` stops the run, `deps-ok` skips its dependents, `always` runs them too. |
1698
+ | `run.outputLogs` (variable or flag only) | `VX_RUN_OUTPUT_LOGS` | `--output-logs` | one of 'full', 'errors-only', 'hash-only', 'none' | `full` on CI, else by what the run selects | Which task output the run prints. Chosen before the workspace loads, so flag and env only. |
1699
+ | `run.rules.exclusiveOutputs` | `VX_RUN_RULES_EXCLUSIVE_OUTPUTS` | — | true or false | true | Refuse two tasks whose outputs overlap even when an edge orders them. |
1700
+ | `run.rules.upfrontKeys` | `VX_RUN_RULES_UPFRONT_KEYS` | — | true or false | true | Refuse a task whose inputs can match another task's outputs. |
1701
+ | `cache.dir` | `VX_CACHE_DIR` | `--cache-dir` | a non-blank string | .vx/cache, entries in ~/.vx/<repo>/cache | Where the cache lives, relative to the workspace root; named, it holds the whole cache. |
1702
+ | `cache.retention.maxSize` | `VX_CACHE_RETENTION_MAX_SIZE` | — | a size like '500MB' or '10GB' | none | Evict least-recently-used entries at each run's end until the cache is under this (`'10GB'`). |
1703
+ | `cache.retention.olderThan` | `VX_CACHE_RETENTION_OLDER_THAN` | — | a duration like '45s', '12h' or '30d' | none | Evict entries unused for this long at each run's end (`'30d'`). |
1704
+ | `cache.policy` | `VX_CACHE_POLICY` | `--cache-policy` | a non-blank string | every layer reads and writes | Which layers a run reads and writes (`'local:rw,remote:r'`); `--no-cache` and `--force` apply on top. |
1705
+ | `cache.scope` | `VX_CACHE_SCOPE` | `--cache-scope` | a non-blank string | none (the default keys) | Where cache writes land (`'pr-123'`): reads try the default key, then the scope's. |
1706
+ | `cache.download` | `VX_CACHE_DOWNLOAD` | `--cache-download` | one of 'all', 'toplevel', 'none' | all | Which remote hits land on disk: every one, only the requested tasks, or none. |
1707
+ | `cache.inputStability.strategy` | `VX_CACHE_INPUT_STABILITY_STRATEGY` | — | one of 'immutable', 'mutable', 'unchecked' | immutable | What a write to an input during the command means for the save, for tasks without their own. A task's own `cache.inputStability.strategy` beats it. |
1708
+ | `cache.inputStability.severity` | `VX_CACHE_INPUT_STABILITY_SEVERITY` | — | one of 'warn', 'error' | warn | Whether a skipped save warns or fails the task, for tasks without their own. A task's own `cache.inputStability.severity` beats it. |
1709
+ | `cache.outputs.clean` | — | — | true or false | true | Remove a task's declared outputs before it runs on a miss, for tasks without their own. A task's own `cache.outputs.clean` beats it. |
1710
+ | `lock.reviewFiles` | `VX_LOCK_REVIEW_FILES` | — | true or false | true | `vx lock` writes each project's resolved config, pretty, to `vx.lock.json` beside its `vx.config`, for review; vx never reads it. |
1711
+ | `watch.poll` | `VX_WATCH_POLL` | `vx watch --poll` | true or false | false | Poll for changes from the start instead of the OS watcher. |
1712
+ | `config.workerTimeout` (variable or flag only) | `VX_CONFIG_WORKER_TIMEOUT` | — | a duration like '500ms', '30s' or '5m', at most 24 days | 30s | How long one config evaluation may take. |
1713
+ | `debug.timing` (variable or flag only) | `VX_DEBUG_TIMING` | — | true or false | false | Print the run's stage timings to stderr. |
1714
+ | `debug.watch` (variable or flag only) | `VX_DEBUG_WATCH` | — | true or false | false | `vx watch`: say on stderr each time the loop arms, with the projects it watches. |
1715
+ | `debug.dumpable` (variable or flag only) | `VX_DEBUG_DUMPABLE` | — | true or false | false | Linux: keep vx dumpable, so strace or a debugger can read its memory and `/proc/<pid>/fd`, and an unsandboxed task its env. |
1720
1716
 
1721
1717
  <!-- /settings table -->
1722
1718
 
@@ -1737,9 +1733,6 @@ interface RunSettings {
1737
1733
  timeout?: Duration
1738
1734
  retries?: number
1739
1735
  continue?: 'never' | 'deps-ok' | 'always'
1740
- killGrace?: Duration
1741
- teardownTimeout?: Duration
1742
- readyNotice?: Duration
1743
1736
  /** Graph rules `vx lock` checks, never a run; each on unless set to false. */
1744
1737
  rules?: { exclusiveOutputs?: boolean; upfrontKeys?: boolean; dependencies?: DependencyRule[] }
1745
1738
  }
@@ -1777,11 +1770,6 @@ interface CacheSettings {
1777
1770
  - **`run.continue`** — what a failed task takes down: `never` stops
1778
1771
  dispatch, `deps-ok` (the default) skips its dependents, `always` runs
1779
1772
  them (`docs/cli.md` § Failure propagation).
1780
- - **`run.killGrace`**, **`run.teardownTimeout`**, **`run.readyNotice`**
1781
- — the SIGTERM → SIGKILL grace (2 s), the bound on each plugin's
1782
- end-of-run flush and teardown (3 s; there is no "no limit"), and how
1783
- long a persistent task may wait for `readyWhen` before vx says what it
1784
- waits for (10 s).
1785
1773
  - **`run.rules`** — checked by `vx lock` and `vx lock --check` only;
1786
1774
  no run checks them.
1787
1775
  - **`cache.dir`** — unset, the workspace keeps its index and history in
@@ -1907,7 +1895,17 @@ false in .vx/workspace.ts to let it wait for its producer.` Such a key
1907
1895
  the schedule (`caching.md` § Local restore tier). The upstream's key
1908
1896
  already reaches the reader through `dependsOn`. A `!` entry that
1909
1897
  takes the whole output back clears it, and an output the reader's
1910
- `!` entries take back no longer holds its key back either. A task's
1898
+ `!` entries take back no longer holds its key back either. Nor does
1899
+ an output git ignores whole: a key reads only what git lists, so
1900
+ `**/*` beside a gitignored `**/dist/**` passes and keys up front. Git
1901
+ is asked, never a `.gitignore` read: `git check-ignore` on the
1902
+ output's directory (for `**/dist/**`, `dist/` and a fresh
1903
+ `__vx_probe__/dist/`), and no tracked or listed file may match it,
1904
+ so a tracked output is still refused. A glob no few paths stand for
1905
+ (`*.js`) is refused as before. `vx lock` asks once for every
1906
+ overlapped output; a run asks once beside its git walk for the
1907
+ stability gate, so a rule changed after the lock keys the reader
1908
+ late until the next `vx lock` refuses it. A task's
1911
1909
  own outputs, a task with no `cache`, and a group are exempt. Off,
1912
1910
  the key waits for its producer, as before.
1913
1911
  - **`dependencies`** — which workspace projects a project may depend
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vzn/vx",
3
- "version": "0.0.638",
3
+ "version": "0.0.639",
4
4
  "description": "Task orchestration you own: correct, sandboxed, extensible, with a content-addressed build cache.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -21,10 +21,10 @@
21
21
  "node": ">=18"
22
22
  },
23
23
  "optionalDependencies": {
24
- "@vzn/vx-linux-x64": "0.0.638",
25
- "@vzn/vx-linux-arm64": "0.0.638",
26
- "@vzn/vx-darwin-x64": "0.0.638",
27
- "@vzn/vx-darwin-arm64": "0.0.638"
24
+ "@vzn/vx-linux-x64": "0.0.639",
25
+ "@vzn/vx-linux-arm64": "0.0.639",
26
+ "@vzn/vx-darwin-x64": "0.0.639",
27
+ "@vzn/vx-darwin-arm64": "0.0.639"
28
28
  },
29
29
  "dependencies": {
30
30
  "@anthropic-ai/sandbox-runtime": "^0.0.76"
package/schemas/last.json CHANGED
@@ -107,8 +107,7 @@
107
107
  "host",
108
108
  "os",
109
109
  "arch",
110
- "vxVersion",
111
- "tags"
110
+ "vxVersion"
112
111
  ],
113
112
  "properties": {
114
113
  "runId": { "type": "string" },
@@ -146,8 +145,7 @@
146
145
  "host": { "type": ["string", "null"] },
147
146
  "os": { "type": ["string", "null"] },
148
147
  "arch": { "type": ["string", "null"] },
149
- "vxVersion": { "type": "string" },
150
- "tags": { "type": "object", "additionalProperties": { "type": "string" } }
148
+ "vxVersion": { "type": "string" }
151
149
  }
152
150
  },
153
151
  "task": {
@@ -174,6 +172,7 @@
174
172
  "timedOut",
175
173
  "sandboxViolations",
176
174
  "notReady",
175
+ "remoteError",
177
176
  "forwarded"
178
177
  ],
179
178
  "properties": {
@@ -209,6 +208,10 @@
209
208
  "timedOut": { "type": ["boolean", "null"] },
210
209
  "sandboxViolations": { "type": ["integer", "null"] },
211
210
  "notReady": { "enum": ["timeout", "exited", "spawn", null] },
211
+ "remoteError": {
212
+ "type": ["string", "null"],
213
+ "description": "each failed remote read or write of the key, `; `-joined"
214
+ },
212
215
  "forwarded": { "type": "boolean" },
213
216
  "output": {
214
217
  "type": "string",
@@ -15,13 +15,13 @@ dependencies.
15
15
  vx run build --all # a task in every project that declares it
16
16
  vx run test --affected=origin/main # only what the diff since origin/main reaches
17
17
  vx run app#build # one project's task (and what it depends on)
18
- vx run build --all --format json # the result as JSON on stdout; logs go to stderr
18
+ vx last --format json # the last run's result as JSON
19
19
  ```
20
20
 
21
21
  The exit code is the verdict: `0` every task passed or hit the cache, `1` a
22
- task failed or was skipped, or the command was refused. `--format json`
23
- prints `schemas/summary.json` (`runId`, `ok`, `exitCode`, a row per task).
24
- A refusal under `--format json` is one line on stdout,
22
+ task failed or was skipped, or the command was refused. `vx last --format
23
+ json` prints the run as JSON afterwards, a row per task. A refusal under
24
+ `--format json` (or `--dry=json`) is one line on stdout,
25
25
  `{"ok":false,"error":{"code","message","docs"}}`: branch on `code`;
26
26
  `docs` (or `vx docs <code>` offline) says what to do.
27
27
  Without a TTY a missing task name never prompts: vx exits 1 and lists the
@@ -108,3 +108,5 @@ vx info --format json # vx and Bun versions, projects, config errors, plugins,
108
108
  `npx skills add vznjs/vx` installs this skill. To match the vx a workspace
109
109
  has installed, copy it from there instead:
110
110
  `cp node_modules/@vzn/vx/skills/vx/SKILL.md .claude/skills/vx/`.
111
+ `vx upgrade` inside a workspace moves the project's `@vzn/vx` and the
112
+ global vx that ran it to the same version.