@vzn/vx 0.0.638 → 0.0.640
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.
- package/README.md +1 -1
- package/docs/caching.md +152 -95
- package/docs/cli.md +225 -372
- package/docs/execution.md +22 -47
- package/docs/features.md +14 -16
- package/docs/patterns.md +6 -8
- package/docs/schema.md +268 -215
- package/docs/security.md +1 -1
- package/package.json +5 -5
- package/schemas/last.json +7 -4
- package/schemas/show.json +14 -14
- package/skills/vx/SKILL.md +10 -7
- package/src/cache/cache.ts +44 -11
- package/src/cache/file-hashes.ts +7 -12
- package/src/cache/fs-clock.ts +62 -0
- package/src/cache/git-inputs.ts +8 -2
- package/src/cache/index.ts +5 -2
- package/src/cache/inputs.ts +154 -83
- package/src/cache/key-fold.ts +23 -3
- package/src/cache/layer.ts +33 -18
- package/src/cache/layered-cache.ts +108 -315
- package/src/cache/linked-inputs.ts +87 -0
- package/src/cache/plugin-cache.ts +2 -10
- package/src/cache/remote-lane.ts +383 -0
- package/src/cache/run-history.ts +24 -8
- package/src/cache/run-store.ts +4 -4
- package/src/cache/schema.ts +6 -4
- package/src/cache/scoped-cache.ts +7 -0
- package/src/cli/completions.ts +2 -2
- package/src/cli/foreign-flags.ts +11 -23
- package/src/cli/help.ts +6 -31
- package/src/cli/info.ts +2 -1
- package/src/cli/init.ts +23 -11
- package/src/cli/last.ts +30 -4
- package/src/cli/plan-format.ts +5 -58
- package/src/cli/plugin-commands.ts +2 -2
- package/src/cli/plugin-templates.ts +2 -2
- package/src/cli/run.ts +23 -185
- package/src/cli/select.ts +16 -6
- package/src/cli/show.ts +15 -6
- package/src/cli/upgrade.ts +215 -55
- package/src/cli/watch-cycle.ts +2 -1
- package/src/cli/watch-filter.ts +22 -6
- package/src/cli/watch-judge.ts +49 -5
- package/src/cli/watch-set.ts +18 -8
- package/src/cli/watch.ts +103 -16
- package/src/cli/why.ts +9 -7
- package/src/config.ts +19 -21
- package/src/exec/executor.ts +5 -5
- package/src/exec/sandbox-runtime.ts +1 -1
- package/src/exec/sandbox-violations.ts +3 -1
- package/src/graph/dependency-spec.ts +2 -2
- package/src/graph/folds.ts +236 -0
- package/src/graph/index.ts +17 -2
- package/src/graph/priorities.ts +1 -1
- package/src/graph/scheduler.ts +45 -31
- package/src/graph/task-graph.ts +190 -58
- package/src/graph/task-status.ts +95 -0
- package/src/{orchestrator → history}/failure-mode.ts +1 -1
- package/src/{orchestrator → history}/history.ts +1 -1
- package/src/history/index.ts +48 -0
- package/src/{orchestrator → history}/metrics.ts +7 -20
- package/src/{orchestrator → history}/miss-reason.ts +1 -1
- package/src/index.ts +11 -29
- package/src/orchestrator/affected-tasks.ts +20 -19
- package/src/orchestrator/doctor.ts +1 -1
- package/src/orchestrator/download-policy.ts +16 -22
- package/src/orchestrator/excluded-keys.ts +4 -5
- package/src/orchestrator/execute-args.ts +80 -0
- package/src/orchestrator/execute-task.ts +50 -411
- package/src/orchestrator/fingerprint-watch.ts +2 -2
- package/src/orchestrator/index.ts +2 -73
- package/src/orchestrator/keyed-projects.ts +12 -51
- package/src/orchestrator/local-shortcircuit.ts +1 -1
- package/src/orchestrator/miss-save.ts +33 -3
- package/src/orchestrator/options.ts +1 -36
- package/src/orchestrator/output-host.ts +4 -3
- package/src/orchestrator/persistent-task.ts +321 -0
- package/src/orchestrator/persistent.ts +130 -3
- package/src/orchestrator/placement.ts +5 -5
- package/src/orchestrator/plan.ts +19 -6
- package/src/orchestrator/plugin-host.ts +129 -69
- package/src/orchestrator/plugin.ts +18 -8
- package/src/orchestrator/prepare.ts +40 -34
- package/src/orchestrator/projects.ts +7 -10
- package/src/orchestrator/remote-prefetch.ts +14 -1
- package/src/orchestrator/run-artifacts.ts +5 -177
- package/src/orchestrator/run-context.ts +55 -0
- package/src/orchestrator/run-failures.ts +15 -3
- package/src/orchestrator/run-records.ts +35 -3
- package/src/orchestrator/run-report.ts +151 -171
- package/src/orchestrator/run-settings.ts +1 -16
- package/src/orchestrator/run-steps.ts +57 -0
- package/src/orchestrator/run-stop.ts +153 -0
- package/src/orchestrator/run.ts +293 -604
- package/src/orchestrator/sandbox-fix.ts +1 -17
- package/src/orchestrator/sandbox-request.ts +2 -2
- package/src/orchestrator/signals.ts +14 -6
- package/src/orchestrator/stable-keys.ts +164 -27
- package/src/orchestrator/steps.ts +70 -0
- package/src/orchestrator/task-hash.ts +34 -25
- package/src/orchestrator/task-log-buffer.ts +1 -1
- package/src/orchestrator/task-process.ts +94 -0
- package/src/orchestrator/telemetry-host.ts +4 -4
- package/src/orchestrator/telemetry.ts +14 -86
- package/src/orchestrator/upstream.ts +4 -181
- package/src/{orchestrator → report}/events.ts +41 -8
- package/src/{orchestrator → report}/forecast.ts +35 -2
- package/src/{orchestrator → report}/framed-output.ts +40 -34
- package/src/report/index.ts +36 -0
- package/src/{orchestrator → report}/logger.ts +158 -20
- package/src/{orchestrator → report}/status-line.ts +88 -7
- package/src/{orchestrator → report}/summary.ts +126 -35
- package/src/report/tally.ts +83 -0
- package/src/settings.ts +12 -39
- package/src/util/colors.ts +44 -4
- package/src/util/git-ignored.ts +143 -0
- package/src/util/index.ts +19 -4
- package/src/util/paths.ts +1 -1
- package/src/util/report.ts +13 -5
- package/src/util/settle.ts +14 -39
- package/src/util/tail.ts +1 -1
- package/src/util/ui.ts +17 -6
- package/src/workspace/affected.ts +81 -2
- package/src/workspace/config-imports.ts +1 -1
- package/src/workspace/config-schema.ts +142 -143
- package/src/workspace/lockfile.ts +1 -1
- package/src/workspace/migration.ts +19 -3
- package/schemas/summary.json +0 -133
- package/src/cache/chained-cache.ts +0 -280
- package/src/orchestrator/tally.ts +0 -112
- /package/src/{orchestrator → report}/path-links.ts +0 -0
- /package/src/{orchestrator → report}/plain-output.ts +0 -0
- /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
|
|
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
|
|
@@ -154,8 +154,8 @@ terminal and a task succeeding or failing. Read it alongside
|
|
|
154
154
|
│ • SIGINT/SIGTERM/SIGHUP handlers (removed in a finally): on
|
|
155
155
|
│ signal, forward it (SIGHUP as SIGTERM) to everything
|
|
156
156
|
│ in liveChildren + persistentRegistry (and kill any
|
|
157
|
-
│ running
|
|
158
|
-
│
|
|
157
|
+
│ running inputs.runtime probe, C-65), wait
|
|
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,
|
|
@@ -243,7 +243,7 @@ terminal and a task succeeding or failing. Read it alongside
|
|
|
243
243
|
│ UserError that fails this task — its dependents skip,
|
|
244
244
|
│ independent tasks continue)
|
|
245
245
|
│ 2. hashTaskConfig (memoized per run via HashCache)
|
|
246
|
-
│ 3. filterUpstreamHashes (apply
|
|
246
|
+
│ 3. filterUpstreamHashes (apply inputs.tasks filter)
|
|
247
247
|
│ 4. cache.key({ taskId, taskConfigHash,
|
|
248
248
|
│ forwardArgs, envValues, runtimeValues,
|
|
249
249
|
│ workspaceRuntimeValues, upstreamHashes,
|
|
@@ -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 --
|
|
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,
|
|
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.
|
|
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.
|
|
@@ -435,7 +430,7 @@ broader access has cache-stability implications).
|
|
|
435
430
|
| Two projects of one name | `checkProjectNames` throws with both paths |
|
|
436
431
|
| Cycle in task graph | `detectCycle` throws with the cycle path |
|
|
437
432
|
| Malformed config | `loadProjectConfigs` throws (UserError) with file + field |
|
|
438
|
-
| `
|
|
433
|
+
| `inputs.runtime` command exits non-zero | UserError naming the command + exit code; that task is `failed`, dependents skip |
|
|
439
434
|
|
|
440
435
|
By default (`--continue=deps-ok`) failures don't kill the scheduler —
|
|
441
436
|
independent tasks already in flight finish, and unrelated tasks not yet
|
|
@@ -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
|
|
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
|
-
- `
|
|
548
|
+
- `report/framed-output.ts` — the `┌─` frames, the glyph grid
|
|
555
549
|
(`formatTaskRow`), one-liners, persistent markers.
|
|
556
|
-
- `
|
|
550
|
+
- `report/status-line.ts` — the serialized writer + the worker
|
|
557
551
|
region.
|
|
558
|
-
- `
|
|
552
|
+
- `report/logger.ts` — composes them; resolves the output view
|
|
559
553
|
and applies the per-flow visibility policy.
|
|
560
|
-
- `
|
|
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,19 +611,17 @@ 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
|
|
614
|
+
## Planning path (`--dry`)
|
|
621
615
|
|
|
622
|
-
|
|
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
|
|
626
620
|
is an existence check only (no artifact download, no ingest). The one
|
|
627
|
-
deliberate side effect: `
|
|
621
|
+
deliberate side effect: `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
|
-
|
|
643
|
-
|
|
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
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
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,12 +26,12 @@ 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**
|
|
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/)
|
|
33
33
|
- **Group tasks** (`dependsOn` with no `exec`) — a task that only runs its dependencies; `dependsOn: []` is a named no-op. [page](https://vznjs.github.io/vx/features/group-tasks/) · post: [Say exactly which tasks to run](https://vznjs.github.io/vx/blog/pick-your-tasks/)
|
|
34
|
-
- **Keyed groups** (`
|
|
34
|
+
- **Keyed groups** (`inputs` with no `exec`) — a key a dependant folds: it re-runs on those inputs, and nothing spawns or counts for the group. [page](https://vznjs.github.io/vx/features/group-tasks/) · post: [A key with nothing to run](https://vznjs.github.io/vx/blog/keyed-groups/)
|
|
35
35
|
- **Implicit keyed `build`** — a project with no `build` gets a `^build` group keyed on its files, so source-only packages still move their dependants' keys. [page](https://vznjs.github.io/vx/features/implicit-build/) · post: [The basics, done carefully](https://vznjs.github.io/vx/blog/the-basics/)
|
|
36
36
|
- **dependsOn syntax** (`^name`, `pkg#name`, `name.*`, `^name.*`) — upstream, cross-project and pattern edges; `^` follows the project edges a plugin supplies, and with none is refused naming the fix. [page](https://vznjs.github.io/vx/features/depends-on-syntax/) · post: [Say exactly which tasks to run](https://vznjs.github.io/vx/blog/pick-your-tasks/)
|
|
37
37
|
- **Every name must resolve** — `vx run lint test typecheck` refuses to start if one name matches no project. [page](https://vznjs.github.io/vx/features/names-must-resolve/) · post: [Say exactly which tasks to run](https://vznjs.github.io/vx/blog/pick-your-tasks/)
|
|
@@ -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 marked `internal`, only when the run waits on it (`vx:prefetch` runs beside the tasks and is listed by `vx last` alone); 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/)
|
|
@@ -79,8 +76,8 @@ config key or an environment variable is missing here.
|
|
|
79
76
|
|
|
80
77
|
## Cache
|
|
81
78
|
|
|
82
|
-
- **Opt-in, explicit inputs** (`
|
|
83
|
-
- **Inputs** (`
|
|
79
|
+
- **Opt-in, explicit inputs** (`inputs.files`) — a task is keyed on the inputs it names, cached or not; one with none has no key, so every task behind it misses. A gitignored path named outright (`.env.local`, `dist/**`) is an input, hashed from disk and never stored; broad globs still skip ignored files. [page](https://vznjs.github.io/vx/features/explicit-inputs/) · post: [Explicit over magical](https://vznjs.github.io/vx/blog/explicit-over-magical/)
|
|
80
|
+
- **Inputs** (`inputs.env`, `inputs.runtime`, `inputs.tasks`, `inputs.workspaceFiles`, `inputs.workspaceRuntime`) — env vars, tool versions, upstream tasks and workspace files in the key. [page](https://vznjs.github.io/vx/features/key-inputs/) · post: [What goes into a key, and what comes back](https://vznjs.github.io/vx/blog/inside-a-cache-hit/)
|
|
84
81
|
- **Outputs** (`cache.outputs.files`, `cache.outputs.workspaceFiles`) — what a hit restores. [page](https://vznjs.github.io/vx/features/cache-outputs/) · post: [What goes into a key, and what comes back](https://vznjs.github.io/vx/blog/inside-a-cache-hit/)
|
|
85
82
|
- **Input stability** (`cache.inputStability`, workspace `cache.inputStability`, `VX_CACHE_INPUT_STABILITY_STRATEGY`, `VX_CACHE_INPUT_STABILITY_SEVERITY`) — an input written while the command runs withholds the save, on CI too, and the saves of every task that ran over its outputs; `strategy: 'mutable'` accepts a task's identical rewrite of its own input, `'unchecked'` skips the check; `severity: 'error'` fails the run instead of warning. no post
|
|
86
83
|
- **Keep outputs on a miss** (`cache.outputs.clean`, workspace `cache.outputs.clean`) — `false` leaves a task's outputs in place for a tool that reads its last output (tsc's `.tsbuildinfo`) and caches everything the outputs then hold, leftovers included (an opt-out of the clean-tree guarantee). no post
|
|
@@ -89,19 +86,20 @@ config key or an environment variable is missing here.
|
|
|
89
86
|
- **Config keyed as evaluated** — the key sees the resolved config object. [page](https://vznjs.github.io/vx/features/typescript-config/) · post: [Configs are programs](https://vznjs.github.io/vx/blog/resolved-config-hashing/)
|
|
90
87
|
- **Cascade through inputs** — a task's key folds its upstream input keys, never outputs. [page](https://vznjs.github.io/vx/features/cascade/) · post: [Cascade through dependencies](https://vznjs.github.io/vx/blog/cascade-through-inputs/)
|
|
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
|
-
- **Upfront keys** (`
|
|
89
|
+
- **Upfront keys** (`vx lock`, no opt-out) — refuses an input glob another task's outputs could match, so every key is known before anything runs; only a dependency the key does not fold (left out of `inputs.tasks`) may be read, its bytes hashed after it ran. [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,
|
|
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/)
|
|
98
95
|
- **Remote outputs** (`--cache-download`, `cache.download`, `VX_CACHE_DOWNLOAD`) — all, top-level only, or none. [page](https://vznjs.github.io/vx/features/remote-downloads/) · post: [A remote server you can trust in production](https://vznjs.github.io/vx/blog/reapi-in-production/)
|
|
96
|
+
- **Failure uploads** (`cache.upload.failures`) — which runs keep a failed task's log and outputs in the remote: CI (default), local or always. no post
|
|
99
97
|
- **One store for every checkout** (`~/.vx/<id>/cache`) — clones and worktrees of a repo share cache entries. [page](https://vznjs.github.io/vx/features/shared-store/) · post: [The cache on your terms](https://vznjs.github.io/vx/blog/the-cache-on-your-terms/)
|
|
100
98
|
- **Warm hits restore nothing** — when outputs on disk already match, a hit costs a few stats. [page](https://vznjs.github.io/vx/features/warm-hits/) · post: [What goes into a key, and what comes back](https://vznjs.github.io/vx/blog/inside-a-cache-hit/)
|
|
101
99
|
- **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
100
|
- **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
101
|
- **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/)
|
|
102
|
+
- **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
103
|
- **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
104
|
|
|
107
105
|
## Correctness
|
|
@@ -119,11 +117,11 @@ config key or an environment variable is missing here.
|
|
|
119
117
|
## Daily work
|
|
120
118
|
|
|
121
119
|
- **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
|
|
120
|
+
- **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
121
|
- **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
122
|
- **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
123
|
- **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/)
|
|
124
|
+
- **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, its `@vzn/vx-*` plugins (one install, one release) 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
125
|
- **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
126
|
- **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
127
|
- **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/)
|
|
@@ -147,7 +145,7 @@ config key or an environment variable is missing here.
|
|
|
147
145
|
|
|
148
146
|
## Adoption
|
|
149
147
|
|
|
150
|
-
- **Start in a minute** (`vx init`, `--dry`, `--format json`, `--force`, `--mjs`) — write `.vx/workspace.ts`, one `vx.config.ts` per package (through `@vzn/vx-migrate
|
|
148
|
+
- **Start in a minute** (`vx init`, `--dry`, `--format json`, `--force`, `--mjs`) — write `.vx/workspace.ts`, one `vx.config.ts` per package (through the latest `@vzn/vx-migrate`, which installs the latest `@vzn/vx` and moves every listed `@vzn/vx-*` plugin with it; with no `package.json`, one empty root project) and `.vx/projects.lock.json`; `--dry --format json` hands an agent the plan as data. [page](https://vznjs.github.io/vx/features/quickstart/) · post: [Hello, vx](https://vznjs.github.io/vx/blog/hello-vx/)
|
|
151
149
|
- **Migrate from Turborepo or Nx** (`vx init`, `--native`, `--keep`, `@vzn/vx-migrate`) — native config, or keep `turbo()` / `nx()` as a start. [page](https://vznjs.github.io/vx/features/migrate/) · posts: [From Turborepo](https://vznjs.github.io/vx/blog/from-turborepo/), [From Nx](https://vznjs.github.io/vx/blog/from-nx/)
|
|
152
150
|
- **Keep a Turbo or Nx remote cache** (`turboCache()`, `nxCache()`) — reuse the cache server you have. [page](https://vznjs.github.io/vx/features/keep-remote-cache/) · post: [From Nx: keep the graph, drop the platform](https://vznjs.github.io/vx/blog/from-nx/)
|
|
153
151
|
- **One binary** — one file, nothing to install underneath. [page](https://vznjs.github.io/vx/features/one-binary/) · post: [One binary](https://vznjs.github.io/vx/blog/one-binary/)
|
|
@@ -177,4 +175,4 @@ config key or an environment variable is missing here.
|
|
|
177
175
|
- **OTel live export** (`otel({ live })`) — spans and metrics stream as tasks end, so a dashboard follows a CI run live. [page](https://vznjs.github.io/vx/features/otel-live/) · post: [Watch a CI run while it runs](https://vznjs.github.io/vx/blog/otel-live/)
|
|
178
176
|
- **Memory-aware admission** (`@vzn/vx-schedule-history`) — tasks are packed by the peak memory learned from past runs. [page](https://vznjs.github.io/vx/features/memory-admission/) · post: [A scheduler that learns from your runs](https://vznjs.github.io/vx/blog/a-scheduler-that-learns/)
|
|
179
177
|
- **CI timings file** (`@vzn/vx-schedule-history`, `file`) — one JSON file of learned task times a CI cache carries between runners, so a fresh runner orders by real times. [page](https://vznjs.github.io/vx/features/critical-path/) · post: [Carry the scheduler's memory between CI runners](https://vznjs.github.io/vx/blog/ci-timings-file/)
|
|
180
|
-
- **Shared timings** (`@vzn/vx-schedule-history`, `defaultBranch`) — CI on the default branch writes task timings to the remote cache; a branch with none imports its merge-base's. Plugins keep their own entries there through `ctx.cache`. [page](https://vznjs.github.io/vx/features/critical-path/)
|
|
178
|
+
- **Shared timings** (`@vzn/vx-schedule-history`, `defaultBranch`) — CI on the default branch writes task timings to the remote cache; a branch with none imports its merge-base's without waiting for it, re-ranking the tasks not yet started when they land. Plugins keep their own entries there through `ctx.cache`. [page](https://vznjs.github.io/vx/features/critical-path/)
|
package/docs/patterns.md
CHANGED
|
@@ -44,7 +44,7 @@ package-manager parts come from a plugin.
|
|
|
44
44
|
Core keys no package file (2026-10-14): the lockfile and each
|
|
45
45
|
project's `package.json` key a task through `@vzn/vx-lockfile`, which
|
|
46
46
|
claims the lockfile and adds `package.json` to every cached task's
|
|
47
|
-
inputs, so narrow `
|
|
47
|
+
inputs, so narrow `inputs.files` like `['src/**']` doesn't miss a
|
|
48
48
|
`package.json` dep bump.
|
|
49
49
|
|
|
50
50
|
The two **divergences** from Turbo's recipe — TypeScript-resolved
|
|
@@ -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/
|
|
175
|
-
| End-of-run summary footer (task/cache meters + time) | Turbo-adjacent (vx renders meters, not a banner) | `src/
|
|
176
|
-
| Per-task output buffered until task finishes | Turbo | `src/
|
|
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.)
|