@vzn/vx 0.0.637 → 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.
- package/docs/caching.md +84 -28
- package/docs/cli.md +273 -408
- package/docs/execution.md +19 -44
- package/docs/features.md +13 -15
- package/docs/patterns.md +5 -7
- package/docs/schema.md +68 -61
- package/package.json +5 -5
- package/schemas/last.json +7 -4
- package/schemas/plan.json +2 -2
- package/skills/vx/SKILL.md +19 -6
- package/src/bin.ts +13 -7
- package/src/cache/cache.ts +60 -11
- package/src/cache/git-inputs.ts +6 -0
- package/src/cache/index.ts +10 -2
- package/src/cache/inputs.ts +0 -24
- package/src/cache/layer.ts +32 -12
- package/src/cache/layered-cache.ts +112 -315
- package/src/cache/plugin-cache.ts +240 -0
- package/src/cache/policy.ts +9 -0
- package/src/cache/remote-lane.ts +383 -0
- package/src/cache/run-history.ts +92 -8
- package/src/cache/run-store.ts +77 -0
- package/src/cache/schema.ts +44 -13
- package/src/cache/scoped-cache.ts +22 -2
- package/src/cli/cache.ts +33 -16
- package/src/cli/completions.ts +11 -7
- package/src/cli/foreign-flags.ts +10 -22
- package/src/cli/help.ts +22 -38
- package/src/cli/index.ts +25 -11
- package/src/cli/info.ts +8 -6
- package/src/cli/init.ts +14 -15
- package/src/cli/last.ts +20 -10
- package/src/cli/lock.ts +39 -27
- package/src/cli/plan-format.ts +10 -61
- package/src/cli/plugin-commands.ts +77 -6
- package/src/cli/plugin-templates.ts +1 -1
- package/src/cli/run.ts +31 -191
- package/src/cli/select.ts +18 -9
- package/src/cli/show.ts +72 -40
- package/src/cli/task-verb.ts +4 -4
- package/src/cli/upgrade.ts +132 -39
- package/src/cli/watch-cycle.ts +2 -1
- package/src/cli/watch-filter.ts +7 -2
- package/src/cli/watch-judge.ts +6 -1
- package/src/cli/watch.ts +73 -34
- package/src/cli/why.ts +14 -5
- package/src/cli/workspace-config.ts +2 -2
- package/src/config.ts +0 -6
- package/src/exec/executor.ts +1 -1
- package/src/exec/sandbox-runtime.ts +1 -1
- package/src/exec/sandbox-violations.ts +3 -1
- package/src/graph/index.ts +9 -1
- package/src/graph/priorities.ts +1 -1
- package/src/graph/scheduler.ts +12 -16
- package/src/graph/task-graph.ts +119 -21
- package/src/graph/task-status.ts +95 -0
- package/src/{orchestrator → history}/failure-mode.ts +20 -11
- package/src/{orchestrator → history}/history.ts +61 -5
- package/src/history/index.ts +48 -0
- package/src/{orchestrator → history}/metrics.ts +14 -23
- package/src/{orchestrator → history}/miss-reason.ts +1 -1
- package/src/index.ts +21 -29
- package/src/orchestrator/affected-tasks.ts +8 -3
- package/src/orchestrator/doctor.ts +2 -2
- package/src/orchestrator/execute-args.ts +80 -0
- package/src/orchestrator/execute-task.ts +47 -429
- package/src/orchestrator/fingerprint-watch.ts +2 -2
- package/src/orchestrator/index.ts +3 -74
- 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 +3 -3
- package/src/orchestrator/plan.ts +18 -5
- package/src/orchestrator/plugin-host.ts +77 -47
- package/src/orchestrator/plugin.ts +86 -14
- package/src/orchestrator/prepare.ts +74 -31
- package/src/orchestrator/projects.ts +3 -0
- package/src/orchestrator/remote-prefetch.ts +23 -6
- 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 +36 -4
- package/src/orchestrator/run-report.ts +81 -179
- package/src/orchestrator/run-settings.ts +1 -16
- package/src/orchestrator/run-stop.ts +153 -0
- package/src/orchestrator/run.ts +313 -555
- package/src/orchestrator/sandbox-fix.ts +0 -16
- package/src/orchestrator/signals.ts +14 -6
- package/src/orchestrator/stable-keys.ts +107 -3
- package/src/orchestrator/steps.ts +50 -0
- package/src/orchestrator/task-hash.ts +3 -16
- package/src/orchestrator/task-log-buffer.ts +1 -1
- package/src/orchestrator/task-process.ts +94 -0
- package/src/orchestrator/telemetry-host.ts +1 -1
- package/src/orchestrator/telemetry.ts +6 -83
- package/src/{orchestrator → report}/events.ts +41 -8
- package/src/{orchestrator → report}/forecast.ts +35 -2
- package/src/{orchestrator → report}/framed-output.ts +39 -34
- package/src/report/index.ts +36 -0
- package/src/{orchestrator → report}/logger.ts +166 -21
- package/src/{orchestrator → report}/status-line.ts +84 -7
- package/src/{orchestrator → report}/summary.ts +179 -63
- package/src/report/tally.ts +83 -0
- package/src/settings.ts +13 -23
- package/src/{orchestrator → util}/colors.ts +49 -4
- package/src/util/config-frame.ts +31 -2
- package/src/util/errors.ts +32 -0
- package/src/util/git-ignored.ts +143 -0
- package/src/util/index.ts +25 -3
- package/src/util/refusal-notice.ts +111 -0
- package/src/util/report.ts +39 -0
- package/src/util/settle.ts +14 -39
- package/src/util/tail.ts +1 -1
- package/src/util/ui.ts +197 -0
- package/src/workspace/affected.ts +20 -23
- package/src/workspace/dependency-rules.ts +12 -4
- package/src/workspace/migration.ts +87 -91
- package/src/workspace/workspace.ts +5 -4
- package/schemas/summary.json +0 -133
- package/src/cache/chained-cache.ts +0 -276
- 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/cli.md
CHANGED
|
@@ -64,12 +64,13 @@ included.
|
|
|
64
64
|
|
|
65
65
|
**Every requested name must resolve.** If any positional matches no
|
|
66
66
|
project in scope, the run refuses to start — `vx run: no projects
|
|
67
|
-
declare task(s): <name>.` on stderr, a run and `--dry`
|
|
67
|
+
declare task(s): <name>.` on stderr, a run and `--dry` alike
|
|
68
68
|
(a run printed it to stdout until 2026-10-03), exit 1, with `Did you mean <task>?` when a
|
|
69
69
|
declared task (or, for `pkg#task`, a runnable spec) is within two edits,
|
|
70
70
|
and with no name that near, what exists: `Tasks: build, test.`, `No
|
|
71
71
|
project is named zzz; projects: a, b.`, or `a's tasks: build, test.`
|
|
72
|
-
(M-56),
|
|
72
|
+
(M-56), each followed by ``A run reads the lock: after editing a
|
|
73
|
+
vx.config, run `vx lock`.`` (a task just added is not in the lock yet),
|
|
73
74
|
and with `Only projects outside the selection declare <name> — pass
|
|
74
75
|
--all, or --filter to pick them.` when the run was scoped (the cwd's
|
|
75
76
|
project, a `--filter`) and a project outside the scope declares it
|
|
@@ -123,20 +124,21 @@ If no task name is given:
|
|
|
123
124
|
`missing task name (stdin is not a TTY, so no picker; tasks here: build, test)`,
|
|
124
125
|
naming the cwd project's tasks, else every project's (twelve, then
|
|
125
126
|
`and N more`); outside a workspace it reads `vx run <task>, e.g. vx run build`.
|
|
126
|
-
- **Under `--
|
|
127
|
+
- **Under `--dry=json`**, on a terminal too — the
|
|
127
128
|
same refusal (`a JSON answer opens no picker; …`), with the
|
|
128
129
|
`VX_E_USAGE` error document on stdout: a program cannot pick.
|
|
129
130
|
|
|
130
131
|
Exit codes:
|
|
131
132
|
|
|
132
|
-
| Code | When
|
|
133
|
-
| --------------------- |
|
|
134
|
-
| `0` | Every task finished `success` or `cache-hit` (local or remote); or `--affected` left no project that declares the task.
|
|
135
|
-
| `1` | At least one task ended `failed` or `skipped`; a persistent task exited non-zero after it was ready; a task name no project declares; or parse/setup error.
|
|
136
|
-
| `130` / `143` / `129` | Interrupted (SIGINT / SIGTERM / SIGHUP): each task's process group (the task and what it forked) gets vx's signal once (a SIGHUP as a SIGTERM),
|
|
133
|
+
| Code | When |
|
|
134
|
+
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
135
|
+
| `0` | Every task finished `success` or `cache-hit` (local or remote); or `--affected` left no project that declares the task. |
|
|
136
|
+
| `1` | At least one task ended `failed` or `skipped`; a persistent task exited non-zero after it was ready; a task name no project declares; or parse/setup error. |
|
|
137
|
+
| `130` / `143` / `129` | Interrupted (SIGINT / SIGTERM / SIGHUP): each task's process group (the task and what it forked) gets vx's signal once (a SIGHUP as a SIGTERM), a 2 s grace to go, then SIGKILL; a second signal skips the grace. vx then dies of the signal, so a shell script running it stops too (a run that handed a task the terminal exits with the code, so the terminal is restored). |
|
|
137
138
|
|
|
138
139
|
A task runs in its own session, so a terminal's Ctrl-C reaches vx alone,
|
|
139
|
-
and each task hears it once: from vx, as SIGINT.
|
|
140
|
+
and each task hears it once: from vx, as SIGINT. A stop starts no more remote
|
|
141
|
+
cache uploads and does not wait on one in flight.
|
|
140
142
|
|
|
141
143
|
Installed from npm, `vx` is a sh launcher that execs the platform
|
|
142
144
|
binary, so every signal reaches vx directly and no Node starts first
|
|
@@ -479,34 +481,23 @@ than run with the args unheard.
|
|
|
479
481
|
| `--schedule <rule>` | value | `run.schedule`, else `most-work` | Which ready task starts first when more are ready than workers: `most-work`, `critical-path`, `direct-dependents`, `ready-order` (`docs/schema.md` § `run.schedule`). |
|
|
480
482
|
| `--branch <name>` | value | `VX_RUN_BRANCH`, else git's branch, then the CI provider's | The branch the run records in its history and forecasts from. No config key: a committed branch would mislabel every other branch's runs. |
|
|
481
483
|
| `--no-cache` | boolean | off | Disable caching entirely (no reads, no writes); output globs are NOT cleaned. |
|
|
482
|
-
| `--force` | boolean | off | Re-execute everything (skip cache reads)
|
|
484
|
+
| `--force` | boolean | off | Re-execute everything (skip cache reads); writes stay on. A key already stored is kept, and a remote that holds it is not sent it again. Output globs are cleaned (so the saved snapshot is clean). |
|
|
483
485
|
| `--cache-policy <spec>` (`--cache-policy`) | value | `VX_CACHE_POLICY` / `cache.policy` | Per-layer read/write control. See below. An EMPTY spec (`--cache-policy=`) is a parse error — it applied nothing and left every axis on; pass `--no-cache` to disable them all. |
|
|
484
486
|
| `--cache-scope <name>` | value | `VX_CACHE_SCOPE` / workspace `cache.scope` | This run's `cache.scope` (`pr-123`), above `VX_CACHE_SCOPE`, `.vx/workspace`'s and `github()`'s. A name `cache.scope` would refuse is refused. `--cache-scope=<name>` form too. Never folded into a cache key. |
|
|
485
487
|
| `--cache-dir <path>` | value | `VX_CACHE_DIR` / `cache.dir` / `~/.vx/<id>/cache` | Cache directory override, resolved relative to cwd (absolute paths used as-is). Beats `VX_CACHE_DIR`, the workspace's `cache.dir` and the default; like them, the directory then holds the whole cache, shared through no store, for every cache the run opens, so the workspace's default dir is not created beside it. A per-run knob — never folded into a cache key. `--cache-dir=<path>` form too; the space form rejects a value starting with `-`. A directory this user cannot write into fails the run before any task with `cache directory <path> is not writable (EACCES: …)` — every run records its history there. |
|
|
486
488
|
| `--retries <n>` | value | `run.retries`, else `0` | Re-run a failed task up to `n` more times; never a persistent one. A task's own `exec.retries` wins (even an explicit `0`). Never affects cache keys. `--retries=<n>` form too. |
|
|
487
489
|
| `--continue[=<mode>]` | value | `deps-ok` | What a failed task takes down with it. `never` stops dispatch on the first failure; `deps-ok` (default) skips only its dependents; `always` (bare `--continue`) runs dependents anyway. See § Failure propagation. |
|
|
488
490
|
| `--timeout <duration>` | duration | `run.timeout`, else none | A task's time limit (`30s`, `10m`), for tasks without their own `exec.timeout`, which always wins. A runaway task is killed + `failed`. A bare number is refused naming the forms with units. Never affects cache keys. `--timeout=<duration>` form too. |
|
|
489
|
-
| `--kill-grace <duration>` | duration | `run.killGrace`, else `2s` | How long a task that ignores SIGTERM gets before SIGKILL: on a timeout, on a signal, and at the end-of-run shutdown of persistent tasks. |
|
|
490
|
-
| `--teardown-timeout <duration>` | duration | `run.teardownTimeout`, else `3s` | The bound on each plugin's end-of-run flush or teardown, and on its `telemetry()` setup, so a third party's I/O cannot hold the run. |
|
|
491
|
-
| `--ready-notice <duration>` | duration | `run.readyNotice`, else `10s` | How long a persistent task may take to match `readyWhen` before vx says once what it waits for. |
|
|
492
491
|
| `--output-logs <mode>` | value | `VX_RUN_OUTPUT_LOGS`, else flow-derived | `full` \| `errors-only` \| `hash-only` \| `none` — explicit output override. See § `--output-logs`. `--output-logs=<mode>` form too. |
|
|
493
492
|
| `--cache-download <mode>` | value | `cache.download`, else `all` | `all` \| `toplevel` \| `none` — where a REMOTELY-executed task's outputs land. `none` leaves them in the remote CAS and fetches lazily, only when a locally-placed task needs them. Never affects cache keys. See § `--cache-download`. `--cache-download=<mode>` form too. |
|
|
494
493
|
| `--verbosity <n>` | int (0+) | `0` | `1` or more prints a per-task table (groups left out) after the framed blocks, above the footer. `--verbosity=<n>` form too. |
|
|
495
494
|
| `--dry[=text\|json]` | optional value | off | Print the task graph + predicted cache hit/miss; skip execution. `VX_DEBUG_TIMING=1` prints the stage table here as it does for a run. |
|
|
496
|
-
| `--graph[=<path>]` | optional value | off | Emit Graphviz DOT (stdout if no path, its directory made if missing); skip execution. A path it cannot write is one line and exit 1. |
|
|
497
|
-
| `--format <fmt>` | value | `pretty` | `json` prints the `--summarize` document on stdout when the run ends and moves every other line to stderr ([Machine-readable output](#machine-readable-output)). `--format=<fmt>` form too. |
|
|
498
|
-
| `--summarize[=<path>]` | optional value | off | Write per-run JSON to `<cacheDir>/runs/<run_id>.json` (or the explicit path). |
|
|
499
495
|
| `--profile[=<path>]` | optional value | off (`profile.json` when set) | Write Chrome-trace JSON of the run's wallclock spans. |
|
|
500
|
-
| `--tag <k=v>` | repeatable | (none) | Label this invocation. Recorded on the run's `invocations` row so dashboards can filter runs. `--tag=k=v` form too. |
|
|
501
|
-
| `--report[=markdown]` | optional value | off | At the end of the run, print a markdown run report to stdout, above the footer. Only `markdown` is supported (`json` is reserved). |
|
|
502
|
-
| `--report-file <path>` | value | off | After the run, APPEND the same markdown report to `<path>`, making its directory as the other output paths do. Use this for `$GITHUB_STEP_SUMMARY` — redirecting stdout captures the whole run log too. `--report-file=<path>` form too. |
|
|
503
496
|
|
|
504
497
|
Mutual exclusion:
|
|
505
498
|
|
|
506
|
-
- `--dry`
|
|
507
|
-
|
|
508
|
-
`--report-file` — each needs a real run to write about. `--report` and
|
|
509
|
-
`--report-file` were accepted and silently wrote nothing until item 992.
|
|
499
|
+
- `--dry` with `--profile` — the profile needs a real run to write
|
|
500
|
+
about.
|
|
510
501
|
|
|
511
502
|
Unknown flags are a parse error (`unknown flag: --foo`), naming the
|
|
512
503
|
nearest flag the verb accepts when one is within two edits
|
|
@@ -530,13 +521,10 @@ name is the verb, and would change what `vx build` means the day one
|
|
|
530
521
|
was declared.
|
|
531
522
|
|
|
532
523
|
**Optional-value flags take their value with `=` only.** `--affected`,
|
|
533
|
-
`--exclude-dependencies`, `--dry
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
`build`, affected scope", and there is no way to tell that apart from
|
|
538
|
-
"`build` is the git base". Write `--graph=out.dot` or
|
|
539
|
-
`--affected=origin/main`. Getting it wrong is loud, not silent: the value
|
|
524
|
+
`--exclude-dependencies`, `--dry` and `--profile` are all valid bare,
|
|
525
|
+
so a following word is read as a task name — `vx run --affected build`
|
|
526
|
+
means "run `build`, affected scope", and there is no way to tell that
|
|
527
|
+
apart from "`build` is the git base". Write `--affected=origin/main`. Getting it wrong is loud, not silent: the value
|
|
540
528
|
becomes a positional that matches no project, so the run refuses to
|
|
541
529
|
start (see "Every requested name must resolve" above).
|
|
542
530
|
|
|
@@ -546,9 +534,9 @@ Pass bare `--exclude-dependencies` for the first, omit the flag for the
|
|
|
546
534
|
second.
|
|
547
535
|
|
|
548
536
|
Value flags (`--filter`, `--concurrency`, `--output-logs`,
|
|
549
|
-
`--verbosity`, `--cache-dir`,
|
|
537
|
+
`--verbosity`, `--cache-dir`, …)
|
|
550
538
|
accept both `--flag value` and `--flag=value`. In the space form,
|
|
551
|
-
`--cache-dir`
|
|
539
|
+
`--cache-dir` rejects a value
|
|
552
540
|
starting with `-`: that is always a swallowed flag (an unquoted empty
|
|
553
541
|
shell variable), never a path or task id. Use the `=` form for a literal
|
|
554
542
|
leading dash.
|
|
@@ -558,15 +546,13 @@ leading dash.
|
|
|
558
546
|
fractional (`2.7`), signed (`+4`) and space-padded forms, plus anything
|
|
559
547
|
past `2^53` (it would parse to a number you did not type). These all
|
|
560
548
|
used to be silently reinterpreted — `--concurrency 0x10` ran 16
|
|
561
|
-
workers. A duration flag (`--timeout`,
|
|
562
|
-
`--teardown-timeout`, `--ready-notice`) takes its unit (`500ms`, `30s`,
|
|
549
|
+
workers. A duration flag (`--timeout`) takes its unit (`500ms`, `30s`,
|
|
563
550
|
`10m`): a bare `500` is refused, naming both readings.
|
|
564
551
|
|
|
565
|
-
An empty `=` value means "no value" on
|
|
566
|
-
their bare forms: `--profile=` writes `profile.json
|
|
567
|
-
writes `<cacheDir>/runs/<run_id>.json`, `--graph=` prints to stdout and
|
|
552
|
+
An empty `=` value means "no value" on two flags, which then act like
|
|
553
|
+
their bare forms: `--profile=` writes `profile.json` and
|
|
568
554
|
`--affected=` uses the default base. Every other flag refuses an empty
|
|
569
|
-
value: `--dry=`, `--
|
|
555
|
+
value: `--dry=`, `--continue=` and
|
|
570
556
|
`--exclude-dependencies=` as well as the ones with no bare form
|
|
571
557
|
(`--retries=`, `--timeout=`, `--cache-dir=`, `--filter=`, `--cache-policy=`).
|
|
572
558
|
|
|
@@ -601,10 +587,13 @@ So `--no-cache` always wins over `--force`. The common cases:
|
|
|
601
587
|
- `--no-cache` → nothing reads, nothing writes, and declared output
|
|
602
588
|
globs are left untouched (you're debugging; vx won't manage your
|
|
603
589
|
tree).
|
|
604
|
-
- `--force` → re-execute every task (reads off)
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
590
|
+
- `--force` → re-execute every task (reads off) with writes on. An
|
|
591
|
+
entry is immutable: a key the local cache already holds is kept, and
|
|
592
|
+
each remote that can batch (`hasMany`) is asked once, in the
|
|
593
|
+
background, which keys it holds; one that cannot is asked `has` per
|
|
594
|
+
key before its upload. Held keys are not uploaded again (a probe that
|
|
595
|
+
fails uploads, as before). Output globs ARE cleaned before each task,
|
|
596
|
+
so the disk holds this run's outputs.
|
|
608
597
|
|
|
609
598
|
`--cache-policy=<spec>` is a comma-separated list of `<layer>:<flags>`
|
|
610
599
|
segments. `layer` is `local` or `remote`; `flags` is any subset of `r`
|
|
@@ -624,8 +613,9 @@ layer keeps its current value. Both `--cache-policy=<spec>` and the space form
|
|
|
624
613
|
a remote hit is still delivered (the artifact has to land on disk to be
|
|
625
614
|
extracted), it just never short-circuits the remote read.
|
|
626
615
|
|
|
627
|
-
Combine with `--force` for "re-execute and
|
|
628
|
-
`--cache-policy=local: --force` (local off, reads off, remote write-only
|
|
616
|
+
Combine with `--force` for "re-execute and fill only the remote":
|
|
617
|
+
`--cache-policy=local: --force` (local off, reads off, remote write-only;
|
|
618
|
+
keys the remote already holds are not sent).
|
|
629
619
|
|
|
630
620
|
Invalid layers/flags are a parse error
|
|
631
621
|
(`--cache-policy: invalid layer 'disk'`, `--cache-policy: invalid flag 'x'`, …);
|
|
@@ -690,8 +680,7 @@ What the shapes mean in each column:
|
|
|
690
680
|
everything, the blocker included (`⊘ skipped app#deploy • blocked by
|
|
691
681
|
lib#build`; a fail-fast skip, which nothing blocked, carries no
|
|
692
682
|
suffix). Every view that lists tasks prints it, a dependency's
|
|
693
|
-
included: nothing prints below the footer.
|
|
694
|
-
`skipped (blocked by lib#build)`.
|
|
683
|
+
included: nothing prints below the footer.
|
|
695
684
|
- **`frame, or one-liner if quiet`** — a cache hit with stored stdout is
|
|
696
685
|
worth a frame (the output is the point); a hit with nothing to replay
|
|
697
686
|
compresses to one line, which is what keeps a 2000-task warm run
|
|
@@ -745,7 +734,7 @@ tracks the run live. Top to bottom:
|
|
|
745
734
|
persistent tasks it depends on, until it — or, with several, the
|
|
746
735
|
first of them — exits (one kept only as a dependency that exits 0,
|
|
747
736
|
a daemon that forked and returned, does not end it); the rest still up are then torn
|
|
748
|
-
down (SIGTERM,
|
|
737
|
+
down (SIGTERM, a 2 s grace, SIGKILL), one status line names
|
|
749
738
|
the task and its code (`vx: app#dev exited with code 1; stopping 1
|
|
750
739
|
other persistent task`), and a non-zero exit makes the run exit 1
|
|
751
740
|
and is what `vx last` records.
|
|
@@ -761,16 +750,39 @@ other persistent task`), and a non-zero exit makes the run exit 1
|
|
|
761
750
|
3. **Worker rows** — one per worker slot that has been busy (at most
|
|
762
751
|
`min(concurrency, 10)`), no glyph and no spinner: the live ticking
|
|
763
752
|
elapsed time leads (` 568ms running <id>`). A task stays in
|
|
764
|
-
its row for its whole life
|
|
765
|
-
|
|
766
|
-
|
|
753
|
+
its row for its whole life, so nothing jumps; overflow shows as
|
|
754
|
+
`+k more`. A second in, an idle worker row holds a task an
|
|
755
|
+
unfinished dependency blocks, one per row, in the order the
|
|
756
|
+
scheduler starts them (most dependents first):
|
|
757
|
+
`blocked solid-js#build by solid-js#types` (dim `blocked`, the id,
|
|
758
|
+
then the running task that holds it back). The task starts in the
|
|
759
|
+
row that held it. A row with no blocked task left to hold leaves the
|
|
760
|
+
region and the rows below close up; blocked tasks only dwindle, so
|
|
761
|
+
it never comes back. A running row says what waits on it:
|
|
762
|
+
`critical path` (yellow) on the running task
|
|
767
763
|
the longest unfinished chain waits on, by the same p50s as the
|
|
768
764
|
forecast below and only while two or more run, and a dim
|
|
769
765
|
`blocks N`, its unfinished dependents direct and transitive. Where
|
|
770
766
|
local history knows the task, its row also shows its usual duration
|
|
771
767
|
(its executed p50) in the cache column, fixed while the elapsed time
|
|
772
768
|
counts up toward it (`3.33s running ~5.2s solid-js#types`).
|
|
773
|
-
4. **
|
|
769
|
+
4. **vx's own steps** — what vx does around the tasks, as rows of
|
|
770
|
+
their own below the worker rows: `vx:load` (configs and graph from
|
|
771
|
+
the lock), `vx:run-lock`, `<plugin>:setup`, `vx:keys` (keys and local
|
|
772
|
+
probes), `vx:prefetch`, `vx:schedule`, then after the graph
|
|
773
|
+
`vx:uploads`, `vx:history`, `vx:keep-run`, `vx:telemetry`,
|
|
774
|
+
`<plugin>:teardown`, `vx:snapshots`, `vx:retention`, `vx:close` and
|
|
775
|
+
`vx:sandbox`. A step shows once it has run 150 ms, ticking like a
|
|
776
|
+
task (` 1.2s running vx:uploads`); no worker slot or
|
|
777
|
+
`--concurrency` holds it back. When it ends it leaves a list row
|
|
778
|
+
like a task's, with no cache column (`⏺︎ 1.2s success vx:uploads`);
|
|
779
|
+
a quicker one shows nowhere. `:` is no task id's separator, so a
|
|
780
|
+
step never reads as a task: a project named `vx` keeps `vx#build`.
|
|
781
|
+
No step is a task: no count, bar, history row, forecast, flaky
|
|
782
|
+
verdict, `vx last` row or telemetry record holds one; `--profile`
|
|
783
|
+
and `VX_DEBUG_TIMING` time vx's stages their own way. Steps show only
|
|
784
|
+
beside a live region; a log (CI, a pipe) prints none.
|
|
785
|
+
5. **The live summary section** — the SAME meters the final footer
|
|
774
786
|
prints (`tasks` + `cache` bars with legends, `time`), filling in as
|
|
775
787
|
the run progresses, under a bare `vx` wordmark rule. A second in,
|
|
776
788
|
the `time` row adds what is left (`time 1.41s · ~3s left`): each
|
|
@@ -813,11 +825,11 @@ redraws within 30 ms of the last draw coalesce into a single
|
|
|
813
825
|
trailing draw when the floor expires (the final state always lands).
|
|
814
826
|
On a 3,270-task warm run this cuts ~6.7 MB of redraw ANSI to ~20 KB.
|
|
815
827
|
|
|
816
|
-
Identity coloring:
|
|
817
|
-
|
|
818
|
-
in every run and every surface
|
|
819
|
-
|
|
820
|
-
read as an outcome.
|
|
828
|
+
Identity coloring: each half of a `project#task` renders in a stable
|
|
829
|
+
hue hashed from its own name (same project = same color, same target
|
|
830
|
+
name = same color, in every run and every surface: every `#build`
|
|
831
|
+
matches), with a dim `#` between, all outside the status palette, so
|
|
832
|
+
an id can never read as an outcome.
|
|
821
833
|
|
|
822
834
|
On GitHub Actions (`GITHUB_ACTIONS` truthy, full output mode), each
|
|
823
835
|
task's block is wrapped in `::group::<id> (<outcome> <duration>)` /
|
|
@@ -902,9 +914,9 @@ not read a deferred output (the same reason vx refuses to infer inputs
|
|
|
902
914
|
by tracing). When bytes are fetched later, vx saves an ordinary
|
|
903
915
|
cache entry for them, so the next run is a plain local hit.
|
|
904
916
|
|
|
905
|
-
## Planning mode (`--dry
|
|
917
|
+
## Planning mode (`--dry`)
|
|
906
918
|
|
|
907
|
-
|
|
919
|
+
`--dry` short-circuits execution. It builds the full task graph,
|
|
908
920
|
compute every task's cache key, and probe the cache to predict the
|
|
909
921
|
hit/miss outcome. Against a remote cache the probe is a lightweight
|
|
910
922
|
existence check — planning never downloads or ingests artifacts.
|
|
@@ -912,8 +924,8 @@ existence check — planning never downloads or ingests artifacts.
|
|
|
912
924
|
```
|
|
913
925
|
$ vx run ci --dry
|
|
914
926
|
would run:
|
|
915
|
-
|
|
916
|
-
|
|
927
|
+
✓ @vzn/vx#format-check cache hit (local) 02bfe8a9
|
|
928
|
+
✓ @vzn/vx#lint cache hit (local) d66cfed2
|
|
917
929
|
▶ @vzn/vx#test cache miss — would exec 68595e49 ~72.64s
|
|
918
930
|
|
|
919
931
|
3 task(s) planned, 2 cache hits (2 local), 1 would run.
|
|
@@ -946,11 +958,11 @@ Status legend:
|
|
|
946
958
|
|
|
947
959
|
| Symbol | Meaning |
|
|
948
960
|
| ------ | ------------------------------------------------------------------------------------------ |
|
|
949
|
-
|
|
|
961
|
+
| `✓` | cache hit (local) — entry already in `<cacheDir>/` |
|
|
950
962
|
| `↓` | cache hit (remote) — entry would be fetched from the layer |
|
|
951
963
|
| `▶` | cache miss — task would execute (under `--force` too: nothing is read, what runs is saved) |
|
|
952
|
-
|
|
|
953
|
-
|
|
|
964
|
+
| `∅` | no-cache — task opts out (no `cache` block, or `--no-cache`) |
|
|
965
|
+
| `·` | `@noop` — task would not run anywhere |
|
|
954
966
|
| `−` | not started — a server you did not request whose every dependant restores a local hit |
|
|
955
967
|
| `○` | group task (suppressed in human view; in DOT + JSON) |
|
|
956
968
|
|
|
@@ -999,7 +1011,9 @@ $ vx run build lint --affected=main --dry
|
|
|
999
1011
|
`kind` is `input` (a changed `file` is a declared input), `project` (its
|
|
1000
1012
|
project changed and the task reads it whole: an uncached task, or a changed
|
|
1001
1013
|
`file` no cached task of the project declares, often an output git does not
|
|
1002
|
-
ignore; no `file` for a
|
|
1014
|
+
ignore; no `file` for a config import), `workspace` (a workspace-wide
|
|
1015
|
+
`file` changed: the workspace config, or a root file a plugin claims such
|
|
1016
|
+
as a lockfile, reached every task of the project),
|
|
1003
1017
|
`package` (a `^` edge passes a changed `project` with no such task),
|
|
1004
1018
|
`named` (`pkg#task` beside a bare task) or `selected` (another filter
|
|
1005
1019
|
selected its project). `via` is the `dependsOn` chain, nearest first, to
|
|
@@ -1010,150 +1024,12 @@ against: a bare `--affected` guesses it (`origin/HEAD`, a trunk branch,
|
|
|
1010
1024
|
else `HEAD~1`), so a reason that names no file may come from a commit
|
|
1011
1025
|
the base reaches, a root `package.json` edit among them.
|
|
1012
1026
|
|
|
1013
|
-
|
|
1027
|
+
## Run artifacts (`--profile`)
|
|
1014
1028
|
|
|
1015
|
-
`--
|
|
1016
|
-
writes a file):
|
|
1017
|
-
|
|
1018
|
-
```
|
|
1019
|
-
vx run ci --graph | dot -Tsvg > graph.svg
|
|
1020
|
-
vx run ci --graph=graph.dot
|
|
1021
|
-
```
|
|
1022
|
-
|
|
1023
|
-
Node `fillcolor` varies by predicted status (green = local hit,
|
|
1024
|
-
sky-blue = remote hit, orange = miss, gray = no-cache, fuchsia =
|
|
1025
|
-
group, white = not started). Edges are unstyled.
|
|
1026
|
-
|
|
1027
|
-
## Run artifacts (`--summarize`, `--profile`)
|
|
1028
|
-
|
|
1029
|
-
Both flags add a side-effect after a real run completes. Errors
|
|
1029
|
+
`--profile` adds a side-effect after a real run completes. Errors
|
|
1030
1030
|
writing the artifact are surfaced via `vx: failed to write …` but
|
|
1031
1031
|
don't change the run's exit code — the run already happened.
|
|
1032
1032
|
|
|
1033
|
-
### `--summarize[=<path>]`
|
|
1034
|
-
|
|
1035
|
-
Writes a per-run JSON file; `schemas/summary.json` is its JSON Schema:
|
|
1036
|
-
|
|
1037
|
-
```json
|
|
1038
|
-
{
|
|
1039
|
-
"runId": "01a0e3ef-206c-708d-95bf-c2b07211adf6",
|
|
1040
|
-
"ok": true,
|
|
1041
|
-
"exitCode": 0,
|
|
1042
|
-
"startedAt": "2026-09-27T17:34:54.572Z",
|
|
1043
|
-
"endedAt": "2026-09-27T17:34:54.586Z",
|
|
1044
|
-
"totalMs": 14.123643,
|
|
1045
|
-
"savedMs": 9,
|
|
1046
|
-
"tasks": [
|
|
1047
|
-
{
|
|
1048
|
-
"id": "a#build",
|
|
1049
|
-
"project": "a",
|
|
1050
|
-
"task": "build",
|
|
1051
|
-
"status": "cache-hit",
|
|
1052
|
-
"exitCode": 0,
|
|
1053
|
-
"durationMs": 3,
|
|
1054
|
-
"hash": "48007ccadd42ed7d",
|
|
1055
|
-
"storedDurationMs": 9,
|
|
1056
|
-
"storedCpuMs": 2.059,
|
|
1057
|
-
"wallclockStartNs": "9456491",
|
|
1058
|
-
"wallclockEndNs": "12902888"
|
|
1059
|
-
}
|
|
1060
|
-
],
|
|
1061
|
-
"aborted": [],
|
|
1062
|
-
"summary": {
|
|
1063
|
-
"successful": 1,
|
|
1064
|
-
"failed": 0,
|
|
1065
|
-
"skipped": 0,
|
|
1066
|
-
"cachedLocal": 1,
|
|
1067
|
-
"restoredLocal": 0,
|
|
1068
|
-
"restoredRemote": 0,
|
|
1069
|
-
"upToDate": 1,
|
|
1070
|
-
"cachedRemote": 0,
|
|
1071
|
-
"aborted": 0,
|
|
1072
|
-
"total": 1
|
|
1073
|
-
}
|
|
1074
|
-
}
|
|
1075
|
-
```
|
|
1076
|
-
|
|
1077
|
-
`runId` is a UUIDv7. Default path: `<cacheDir>/runs/<run_id>.json`. hrtime fields are
|
|
1078
|
-
strings (bigints serialized as strings) to preserve ns precision
|
|
1079
|
-
through JSON. `cpuMs` / `peakRssBytes` are what the task's own
|
|
1080
|
-
execution used and appear on executed rows only (`peakRssBytes` only
|
|
1081
|
-
when the task's peak rose above vx's own footprint — a lighter task's
|
|
1082
|
-
figure would be vx's, handed back by the kernel; a Linux sandboxed task
|
|
1083
|
-
reports neither, since what bwrap's pid namespace used never reaches vx);
|
|
1084
|
-
`restored` appears on a hit only: `true` when its outputs were restored,
|
|
1085
|
-
`false` when they were already up to date. A hit's `durationMs`
|
|
1086
|
-
is the restore it cost, and what the PRODUCING execution used rides the
|
|
1087
|
-
artifact and appears under its own keys, `storedDurationMs` /
|
|
1088
|
-
`storedCpuMs` / `storedPeakRssBytes` (the work the hit skipped) — a
|
|
1089
|
-
remote worker's peak is never presented as this run's. `savedMs` is
|
|
1090
|
-
their sum over the hits, the footer's `saved` as a number. `admissionHeldMs` appears on a row
|
|
1091
|
-
only when an `admit` policy (a plugin's — `@vzn/vx-schedule-history`
|
|
1092
|
-
packs learned reservations) refused the task while a worker was free:
|
|
1093
|
-
the wait from that first refusal to its dispatch, the plugin's hand on
|
|
1094
|
-
the run. The footer's `info` row sums the waits, task-seconds:
|
|
1095
|
-
`admit held 3 tasks, 4.2s in all` (85 held tasks can read 4783s beside
|
|
1096
|
-
a 160 s run).
|
|
1097
|
-
|
|
1098
|
-
**`ok` / `exitCode`** are the run's verdict — the same value the CLI
|
|
1099
|
-
exits with: a stopping signal's code (130 for Ctrl-C), and with a kept
|
|
1100
|
-
server, its exit (the file is written again when the server ends). Gate on these rather than re-deriving a pass from the
|
|
1101
|
-
buckets: a run can be red without a single failed task (see `aborted`).
|
|
1102
|
-
|
|
1103
|
-
**`tasks[]` and `summary` describe the same population**, so
|
|
1104
|
-
`tasks.length === summary.total` always holds. Group tasks (no `exec`)
|
|
1105
|
-
are in neither — they do no work.
|
|
1106
|
-
|
|
1107
|
-
**`aborted[]`** lists tasks whose attempt ended while the run was
|
|
1108
|
-
stopping (Ctrl-C, a signal to vx), with their exit code, and tasks the stop reached before they started, with
|
|
1109
|
-
exit 1, `durationMs: 0` and no `wallclockStartNs`. Neither joins an
|
|
1110
|
-
outcome bucket or `total`, but they make the run red, so they are
|
|
1111
|
-
listed separately and counted as `summary.aborted`.
|
|
1112
|
-
|
|
1113
|
-
**`noCache: true`** marks a task no cache answered for: it declares no
|
|
1114
|
-
`cache` block (it executes every run by design), or the run's `--cache-policy`
|
|
1115
|
-
read and wrote nothing (`--no-cache`), so a hit rate should leave it out
|
|
1116
|
-
of the denominator. The key is present only when true; every other row is
|
|
1117
|
-
unchanged. Its `hash` is still set: dependents fold it.
|
|
1118
|
-
|
|
1119
|
-
**`notReady`** is present only on a failed persistent task: why it never
|
|
1120
|
-
became ready — `timeout` (the readiness deadline fired; `exitCode` is
|
|
1121
|
-
the kill's, 143 or 137 after the grace, and 143 for a server that
|
|
1122
|
-
traps the TERM and exits 0, as an ordinary timeout's),
|
|
1123
|
-
`exited` (the child exited first; `exitCode` is then its own) or `spawn`
|
|
1124
|
-
(the spawn itself failed). Every label reads it, `failed (never ready:
|
|
1125
|
-
timed out, exit 143)`. A server the run's stop (a Ctrl-C) killed while it started is
|
|
1126
|
-
`aborted`, not failed, as any task the stop kills; so is one that
|
|
1127
|
-
became ready only after the stop.
|
|
1128
|
-
|
|
1129
|
-
**`sandboxViolations`** is present only on a sandboxed task with a
|
|
1130
|
-
SANDBOX VIOLATIONS section — the count of its denials (vx's own notes
|
|
1131
|
-
there count none), and the reason the task failed (its exit is forced to 1
|
|
1132
|
-
when it was 0); every label counts it, `failed (exit 1, 2 sandbox
|
|
1133
|
-
violations)`.
|
|
1134
|
-
|
|
1135
|
-
**`timedOut: true`** is present only on a `failed` row vx's own
|
|
1136
|
-
`timeout` killed: its exit is the shell's 143, and every label reads
|
|
1137
|
-
`failed (timed out, exit 143)` rather than a signal.
|
|
1138
|
-
|
|
1139
|
-
**`blockedBy`** is present only on a `skipped` row: the id of the failed
|
|
1140
|
-
(or aborted) task at the root of what blocked it, through any chain of
|
|
1141
|
-
skips between — what the skipped row prints, for a script.
|
|
1142
|
-
A fail-fast skip has none.
|
|
1143
|
-
|
|
1144
|
-
**`flaky: { passes, failures, attempts }`** is present only on a task
|
|
1145
|
-
this run proved flaky (its row's `flaky` note, typed): `passes` and
|
|
1146
|
-
`failures` count the outcomes on record for this exact `hash`, this run
|
|
1147
|
-
included, and `attempts` is what this run took. A consumer gating on
|
|
1148
|
-
`status: "failed"` can tell a break (no `flaky` key) from a flake
|
|
1149
|
-
without reading the history. Every other row is byte-identical.
|
|
1150
|
-
|
|
1151
|
-
**`durationMs` is always what THIS run spent on the task.** For a cache
|
|
1152
|
-
hit that is the probe + restore, not the exec time the entry was stored
|
|
1153
|
-
with — so it is small even for an expensive task. The work a hit
|
|
1154
|
-
_skipped_ is a different number; `--report`'s "N saved" is the surface
|
|
1155
|
-
that reports it.
|
|
1156
|
-
|
|
1157
1033
|
### `--profile[=<path>]`
|
|
1158
1034
|
|
|
1159
1035
|
Writes a Chrome-trace JSON of every task's wallclock span. Open in
|
|
@@ -1189,91 +1065,6 @@ from the per-task `hrtime.bigint()` spans the runner captures.
|
|
|
1189
1065
|
|
|
1190
1066
|
Default path: `profile.json` (cwd-relative).
|
|
1191
1067
|
|
|
1192
|
-
### `--report[=markdown]`
|
|
1193
|
-
|
|
1194
|
-
At the end of a real run, prints a markdown run report to **stdout**,
|
|
1195
|
-
just above the footer: nothing prints below the footer. The report
|
|
1196
|
-
itself is machine-clean. One header line of
|
|
1197
|
-
totals plus a table, one row per task:
|
|
1198
|
-
|
|
1199
|
-
```markdown
|
|
1200
|
-
## vx run — passed
|
|
1201
|
-
|
|
1202
|
-
**3 tasks** · 3 success · 0 failed · 2 cached (1 up-to-date, 1 restored) · 1.23s total · 2.65s saved
|
|
1203
|
-
|
|
1204
|
-
| Task | Status | Cache | Duration |
|
|
1205
|
-
| --------- | ------- | ---------- | -------- |
|
|
1206
|
-
| web#build | success | miss | 1.23s |
|
|
1207
|
-
| web#test | success | local | 5ms |
|
|
1208
|
-
| api#test | success | up-to-date | 3ms |
|
|
1209
|
-
```
|
|
1210
|
-
|
|
1211
|
-
`Status` is the task outcome (`success` / `failed (exit N)`, with the
|
|
1212
|
-
signal an exit above 128 stands for, `failed (exit 137, 128 + SIGKILL)`,
|
|
1213
|
-
or a timeout's reason, `failed (timed out, exit 143)`, a persistent
|
|
1214
|
-
task's `never ready: …`, or a sandboxed task's violation count, or
|
|
1215
|
-
`skipped`, naming what blocked it: `skipped (blocked by lib#build)`);
|
|
1216
|
-
`Cache` is its provenance (`miss` / `no-cache` for a task no cache
|
|
1217
|
-
answered for — no `cache` block, or a `--no-cache` / `--cache-policy=local:` run —
|
|
1218
|
-
which never consulted one / `local` / `remote` / `up-to-date` /
|
|
1219
|
-
`—`). A stopped run (a Ctrl-C teardown) reads `## vx run — interrupted`,
|
|
1220
|
-
and its tasks keep the terminal's words: one the signal killed is
|
|
1221
|
-
`aborted` with its time so far, one the stop reached before it ran is
|
|
1222
|
-
`not run` with no time. Both count in the header's task total, listed
|
|
1223
|
-
as `1 aborted · 1 not run`, so a red report with no failing row still
|
|
1224
|
-
says why. Group tasks (no `exec`) get neither — they are not
|
|
1225
|
-
work, and the header's counts match the terminal summary and
|
|
1226
|
-
`--summarize` exactly.
|
|
1227
|
-
|
|
1228
|
-
The two durations in the header mean different things, and the
|
|
1229
|
-
distinction is the point:
|
|
1230
|
-
|
|
1231
|
-
- **`N total`** sums `Duration` over the tasks that actually EXECUTED,
|
|
1232
|
-
an aborted task's time so far included — the time this run spent.
|
|
1233
|
-
- **`N saved`** sums the exec times the cache hits SKIPPED, read from
|
|
1234
|
-
each entry as it was stored (above, 2.01s and 640ms). It is
|
|
1235
|
-
deliberately not the hits' `Duration` column, which is the restore
|
|
1236
|
-
they cost this run: summing that reported a task taking 2.01s cold
|
|
1237
|
-
as "6ms saved".
|
|
1238
|
-
|
|
1239
|
-
Only `markdown` is supported today (`json` is reserved; a bad value is a
|
|
1240
|
-
parse error). Built purely from the run's outcomes after it returns — it
|
|
1241
|
-
adds zero cost when both flags are absent.
|
|
1242
|
-
|
|
1243
|
-
### `--report-file <path>`
|
|
1244
|
-
|
|
1245
|
-
Writes the same markdown report to a file instead of (or as well as)
|
|
1246
|
-
stdout, and is the form to use for a CI step summary:
|
|
1247
|
-
|
|
1248
|
-
```sh
|
|
1249
|
-
vx run ci --report-file="$GITHUB_STEP_SUMMARY"
|
|
1250
|
-
```
|
|
1251
|
-
|
|
1252
|
-
**Do not redirect stdout for this.** The report itself is machine-clean,
|
|
1253
|
-
but stdout is not vx's alone — the status logger writes frames, meter
|
|
1254
|
-
bars and `::group::` workflow commands to the same stream, so
|
|
1255
|
-
`--report=markdown >> "$GITHUB_STEP_SUMMARY"` puts the entire run log
|
|
1256
|
-
into the step summary above the table.
|
|
1257
|
-
|
|
1258
|
-
The report is **appended**, never truncated: `$GITHUB_STEP_SUMMARY` is a
|
|
1259
|
-
shared, append-only file that other steps in the same job also write to,
|
|
1260
|
-
so overwriting it would silently discard their content. Passing both
|
|
1261
|
-
flags writes the report to stdout AND appends it to the file. A run
|
|
1262
|
-
stopped by Ctrl-C or SIGTERM still writes both, its stopped tasks
|
|
1263
|
-
`aborted`. A write
|
|
1264
|
-
failure is reported (`vx: failed to write report to …`) but does not
|
|
1265
|
-
change the exit code — the run already happened, the same contract
|
|
1266
|
-
`--summarize` and `--profile` follow.
|
|
1267
|
-
|
|
1268
|
-
### `--tag <k=v>`
|
|
1269
|
-
|
|
1270
|
-
Labels the invocation. Repeatable; `--tag=k=v` form too. The pair is
|
|
1271
|
-
split on the **first** `=`, so values may contain `=` (e.g. a URL). An
|
|
1272
|
-
empty key is a parse error. Tags are recorded on the run's
|
|
1273
|
-
`invocations` row so dashboards can filter runs by label, and reach
|
|
1274
|
-
telemetry as `vx.tag.<key>`; a secret value in one is masked there as
|
|
1275
|
-
in the task's output (see [Masking](./schema.md)).
|
|
1276
|
-
|
|
1277
1068
|
## Sandbox
|
|
1278
1069
|
|
|
1279
1070
|
Sandbox isolation is opt-in **per task** via an `exec.sandbox` block in
|
|
@@ -1392,7 +1183,10 @@ run...` precedes it.
|
|
|
1392
1183
|
heard from its nearest directory that exists and each one above it,
|
|
1393
1184
|
and a watch whose
|
|
1394
1185
|
directory was replaced is armed again (an OS watch holds the deleted
|
|
1395
|
-
one; `watch-recreated-dirs.test.ts`).
|
|
1186
|
+
one; `watch-recreated-dirs.test.ts`). There, a project directory
|
|
1187
|
+
that is still the same one starts no cycle: a task writing at its
|
|
1188
|
+
top level moved its mtime, which macOS reports to the parent
|
|
1189
|
+
(`watch-project-dir-write.test.ts`). Under `--filter` or
|
|
1396
1190
|
`--affected` the scope is the one resolved at start, and a new
|
|
1397
1191
|
project joins it only as a dependency of it. A task's own declared outputs (`cache.outputs.files`,
|
|
1398
1192
|
`outputs.workspaceFiles`; a plugin's `project` stage counts, as in
|
|
@@ -1432,7 +1226,10 @@ run...` precedes it.
|
|
|
1432
1226
|
starts a cycle, since no cache key can see it (a user's edit to one
|
|
1433
1227
|
still does in a project with a task that has a command and no cache,
|
|
1434
1228
|
which reads what it likes, item 947, outside any project nested
|
|
1435
|
-
in it
|
|
1229
|
+
in it; a write the last cycle made is not, read off the later of
|
|
1230
|
+
its mtime and ctime, so an output a cache hit restores with its
|
|
1231
|
+
saved mtime is the run's, and so is the restore's own `.vx-tmp-*`
|
|
1232
|
+
file, `watch-restored-output.test.ts`) — or declared an
|
|
1436
1233
|
output, or the loop re-runs on it; after three such cycles in a row
|
|
1437
1234
|
watch names the path and the remedy, once, and keeps going. A dev
|
|
1438
1235
|
server the last cycle left running counts as that cycle for as long
|
|
@@ -1467,9 +1264,11 @@ watch events within 2000 ms; polling every 250 ms instead`.
|
|
|
1467
1264
|
changed, then its task rows as `vx run` prints them, with no footer.
|
|
1468
1265
|
The named path is the first changed one that still exists, so an
|
|
1469
1266
|
editor's temporary file renamed away names nothing; a deletion names
|
|
1470
|
-
the cycle when nothing else changed.
|
|
1471
|
-
|
|
1472
|
-
|
|
1267
|
+
the cycle when nothing else changed. The `watching` line is said
|
|
1268
|
+
once: a cycle that re-reads the watched set (a member came or went,
|
|
1269
|
+
the lock or a plugin changed) says nothing more; `VX_DEBUG_WATCH=1`
|
|
1270
|
+
says each arm on stderr as `vx watch: armed N projects`. Errors and
|
|
1271
|
+
refusals go to stderr. The
|
|
1473
1272
|
orchestrator is invoked again with the same options, save one: the
|
|
1474
1273
|
`--affected` diff is read once, for the initial run. A later cycle
|
|
1475
1274
|
runs the requested task in every project of the scope fixed at start,
|
|
@@ -1490,7 +1289,8 @@ Always ignored (no re-trigger):
|
|
|
1490
1289
|
tracked + untracked-not-ignored — so a cycle it started could change
|
|
1491
1290
|
nothing. A tracked file that matches a pattern is not ignored, by
|
|
1492
1291
|
git's own rule; outside a repository nothing is.
|
|
1493
|
-
- Files ending in
|
|
1292
|
+
- Files ending in `~` (editor swap files), and a restore's own
|
|
1293
|
+
`.vx-tmp-*` file.
|
|
1494
1294
|
- The run's **resolved cache directory**, wherever it is. `.vx/cache/` covers the
|
|
1495
1295
|
default, but `defineWorkspace({ cache: { dir } })` and `--cache-dir` can put it
|
|
1496
1296
|
anywhere; watching it would let vx's own cache writes trigger the next
|
|
@@ -1541,10 +1341,10 @@ re-reads the watched set (item 971).
|
|
|
1541
1341
|
The following flags are rejected (parser exits 1 before the initial
|
|
1542
1342
|
run):
|
|
1543
1343
|
|
|
1544
|
-
- `--dry`
|
|
1545
|
-
- `--
|
|
1546
|
-
- `--
|
|
1547
|
-
|
|
1344
|
+
- `--dry` — it skips execution; nothing to watch.
|
|
1345
|
+
- `--profile` — would overwrite its target per cycle.
|
|
1346
|
+
- `--verbosity <n>` (n > 0) — formats
|
|
1347
|
+
ONE run's result; a watch loop has no single run to report. (`--verbosity 0`
|
|
1548
1348
|
is accepted: it asks for what watch already prints.)
|
|
1549
1349
|
|
|
1550
1350
|
A requested persistent task (`exec.persistent`, a dev server) starts
|
|
@@ -1569,7 +1369,7 @@ re-runs itself and what it depends on.
|
|
|
1569
1369
|
- `0` — clean Ctrl+C / SIGTERM exit. A cycle in flight is torn down
|
|
1570
1370
|
first — its children, and the dev server held between cycles, get the
|
|
1571
1371
|
signal watch received (a Ctrl-C as SIGINT; a SIGTERM or SIGHUP as SIGTERM),
|
|
1572
|
-
|
|
1372
|
+
a 2 s grace, then SIGKILL — and the process leaves only
|
|
1573
1373
|
once it has returned, so a CI cancellation never orphans a task.
|
|
1574
1374
|
Also `0`, with no watch, when `--affected` selects nothing: watch
|
|
1575
1375
|
prints `vx watch: nothing affected since <ref>` and exits as `vx run`
|
|
@@ -1661,16 +1461,19 @@ bound clears the whole cache when that is really what you want.
|
|
|
1661
1461
|
|
|
1662
1462
|
```
|
|
1663
1463
|
$ vx cache clean --older-than 30d
|
|
1664
|
-
|
|
1464
|
+
✓ vx cache clean freed 1.3 GB
|
|
1665
1465
|
|
|
1666
|
-
|
|
1667
|
-
Pruned 18 entries (320 MB freed)
|
|
1466
|
+
42 entries
|
|
1668
1467
|
|
|
1669
1468
|
$ vx cache clean --older-than 30d # after a SCHEMA_VERSION bump
|
|
1670
|
-
|
|
1469
|
+
✓ vx cache clean freed 1.3 GB
|
|
1470
|
+
|
|
1471
|
+
42 orphaned artifacts
|
|
1671
1472
|
|
|
1672
1473
|
$ vx cache clean --older-than 30d --dry-run
|
|
1673
|
-
|
|
1474
|
+
› vx cache clean would free 1.3 GB (dry run)
|
|
1475
|
+
|
|
1476
|
+
42 entries · 3 orphaned artifacts
|
|
1674
1477
|
```
|
|
1675
1478
|
|
|
1676
1479
|
`--dry-run` picks the victims under the same policy and counts the
|
|
@@ -1694,8 +1497,8 @@ artifact that vanishes before its restore is a miss, and the task runs
|
|
|
1694
1497
|
Exit codes:
|
|
1695
1498
|
|
|
1696
1499
|
- `0` — pruning completed (zero or more entries evicted). A workspace
|
|
1697
|
-
that never ran has no cache:
|
|
1698
|
-
nothing is created.
|
|
1500
|
+
that never ran has no cache: `› vx cache clean nothing to free`,
|
|
1501
|
+
and nothing is created.
|
|
1699
1502
|
- `1` — parse error, missing policy, or workspace-discovery error.
|
|
1700
1503
|
|
|
1701
1504
|
`vx cache clean` resolves the workspace root from cwd and honors a
|
|
@@ -1723,17 +1526,19 @@ and the directory goes only if that empties it. A directory with no
|
|
|
1723
1526
|
names goes only if it is one (`.vx/<id>/cache` holding a `store.db`): the
|
|
1724
1527
|
index is a file in the checkout, which a commit or a restored CI cache
|
|
1725
1528
|
could point at any directory. A workspace that never ran says
|
|
1726
|
-
|
|
1529
|
+
`› vx cache clean nothing to free`. The next run misses every task once
|
|
1727
1530
|
and saves again.
|
|
1728
1531
|
|
|
1729
1532
|
```
|
|
1730
1533
|
$ vx cache clean
|
|
1731
|
-
|
|
1534
|
+
✓ vx cache clean freed 1.4 GB
|
|
1535
|
+
|
|
1732
1536
|
.vx/cache
|
|
1733
1537
|
/home/me/.vx/3f2a9c01b4d5e6f7/cache
|
|
1734
1538
|
|
|
1735
1539
|
$ vx cache clean --global --dry-run
|
|
1736
|
-
|
|
1540
|
+
› vx cache clean would free 3.1 GB (dry run)
|
|
1541
|
+
|
|
1737
1542
|
.vx/cache
|
|
1738
1543
|
/home/me/.vx/3f2a9c01b4d5e6f7/cache
|
|
1739
1544
|
/home/me/.vx/9b8e7d6c5a4f3e2d/cache
|
|
@@ -1775,8 +1580,8 @@ add directories (X-143). Before writing, it
|
|
|
1775
1580
|
builds the graph of every task the workspace declares, `graph` stage
|
|
1776
1581
|
included, and checks the graph rules (`run.rules`), which no run
|
|
1777
1582
|
checks, `dependencies` (which projects a project may depend on,
|
|
1778
|
-
`VX_E_DEPENDENCY_RULE`) among them: a broken one prints
|
|
1779
|
-
|
|
1583
|
+
`VX_E_DEPENDENCY_RULE`) among them: a broken one prints `✗ vx lock lock not written`
|
|
1584
|
+
with the rule's refusal under it and writes nothing.
|
|
1780
1585
|
|
|
1781
1586
|
```
|
|
1782
1587
|
vx lock # Evaluate all vx.config.* now; write .vx/projects.lock.json.
|
|
@@ -1830,7 +1635,7 @@ A project with no `vx.config.*` — its tasks from a plugin's `project`
|
|
|
1830
1635
|
stage (`turbo()` from `@vzn/vx-migrate`), or none at all — has nothing
|
|
1831
1636
|
to freeze: the lock records nothing for it, `--check` does not audit
|
|
1832
1637
|
it, and a run still loads its tasks live. Both verbs name such
|
|
1833
|
-
projects that have tasks (`
|
|
1638
|
+
projects that have tasks (`0 project configs → .vx/projects.lock.json (no
|
|
1834
1639
|
vx.config, so not locked: app, lib)`, five names then `and N more`),
|
|
1835
1640
|
so an empty lock on a plugin-only workspace never reads like an audit.
|
|
1836
1641
|
One with no task (core's default `build` aside) is not named.
|
|
@@ -2035,8 +1840,9 @@ binary keeps the old one's mode (and, as root, its owner), and must
|
|
|
2035
1840
|
answer `--version` before the upgrade reports it installed: one that
|
|
2036
1841
|
does not start on this machine, or does not answer within 10 s, is
|
|
2037
1842
|
swapped back for the previous vx, and the command exits 1 saying so
|
|
2038
|
-
(item 1097). On the version it
|
|
2039
|
-
<version>` and downloads
|
|
1843
|
+
(item 1097): `✓ vx upgrade installed 0.0.2`. On the version it
|
|
1844
|
+
already is it says `› vx upgrade already at <version>` and downloads
|
|
1845
|
+
nothing.
|
|
2040
1846
|
|
|
2041
1847
|
**A vx a package manager installed** (the npm package's launcher runs
|
|
2042
1848
|
`…/node_modules/@vzn/vx-<os>-<arch>/vx`) is upgraded by that package
|
|
@@ -2057,13 +1863,38 @@ manager is its lockfile (`pnpm-lock.yaml`, `bun.lock`, `yarn.lock`,
|
|
|
2057
1863
|
`package-lock.json`), else `packageManager`, else npm; the field it is
|
|
2058
1864
|
listed in is kept (`--save-dev`, `--dev`, …), and pnpm at a workspace
|
|
2059
1865
|
root adds `--workspace-root`. The lockfile is updated by that manager.
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
1866
|
+
vx prints what it runs, runs it there, and exits with an error if it
|
|
1867
|
+
fails:
|
|
1868
|
+
|
|
1869
|
+
```
|
|
1870
|
+
› vx upgrade 0.0.1 → 0.0.2
|
|
1871
|
+
|
|
1872
|
+
bun add @vzn/vx@0.0.2 in /repo
|
|
1873
|
+
```
|
|
1874
|
+
|
|
1875
|
+
**Inside a project with its own vx**, a global vx hands `vx upgrade` to
|
|
1876
|
+
the project's (§ Which vx runs), and one run moves both to the same
|
|
1877
|
+
version (`TAG`, or latest): first the project's dependency, then the
|
|
1878
|
+
global vx that started the run, each the way it was installed. Each
|
|
1879
|
+
line names its install, and a refusal names the one that failed
|
|
1880
|
+
(`vx upgrade: global vx /usr/local/bin/vx: cannot write to …`); a
|
|
1881
|
+
project that fails leaves the global as it was. The project's vx run
|
|
1882
|
+
directly (a package script, `node_modules/.bin/vx`) has no global behind it, so only
|
|
1883
|
+
the project moves.
|
|
1884
|
+
|
|
1885
|
+
```
|
|
1886
|
+
› vx upgrade project 0.0.1 → 0.0.2
|
|
1887
|
+
|
|
1888
|
+
bun add @vzn/vx@0.0.2 in /repo
|
|
1889
|
+
|
|
1890
|
+
› vx upgrade global 0.0.1 → 0.0.2
|
|
1891
|
+
|
|
1892
|
+
/usr/local/bin/vx
|
|
1893
|
+
|
|
1894
|
+
✓ vx upgrade global installed 0.0.2
|
|
1895
|
+
```
|
|
1896
|
+
|
|
1897
|
+
vx never installs Node or a package manager: one not on `PATH` is refused with the command to run. A vx run from an npx or
|
|
2067
1898
|
bunx cache has nothing installed to upgrade, and says to ask for
|
|
2068
1899
|
`@vzn/vx@latest` instead.
|
|
2069
1900
|
|
|
@@ -2177,8 +2008,8 @@ none is dropped in silence. An Nx flag's camelCase spelling (`--nxBail`,
|
|
|
2177
2008
|
`--skipNxCache`), which Nx's parser takes and its docs print, is its
|
|
2178
2009
|
kebab-case row. A `[=<bool>]` row takes `=true` (the flag bare) and `=false` (left out), as Turbo's and Nx's parsers do. `vx run-many` and `vx affected` name the
|
|
2179
2010
|
`vx run` that does the same, and the other verbs a hand types from
|
|
2180
|
-
either tool name what does it here: `graph` (`--
|
|
2181
|
-
(`vx show`), `
|
|
2011
|
+
either tool name what does it here: `graph` and `query` (`--dry=json`), `ls`
|
|
2012
|
+
(`vx show`), `reset` (`vx cache clean`), and
|
|
2182
2013
|
`daemon`, `login`, `logout`, `link` and `unlink`, which vx has no use
|
|
2183
2014
|
for. Nx's `project:target` (`vx run web:build`,
|
|
2184
2015
|
when `web` declares `build`) is answered with `vx run web#build`: from
|
|
@@ -2197,11 +2028,10 @@ copy to the source.
|
|
|
2197
2028
|
| turbo | `--continue=dependencies-successful` | alias | `--continue=deps-ok` |
|
|
2198
2029
|
| turbo | `--continue` | same | `--continue[=never\|deps-ok\|always]` |
|
|
2199
2030
|
| turbo | `--dry-run` | alias | `--dry[=text\|json]` |
|
|
2200
|
-
| turbo | `--graph
|
|
2201
|
-
| turbo | `--graph` | same | `--graph[=<file>.dot]` |
|
|
2031
|
+
| turbo | `--graph` | refuse | `--dry=json` prints the planned task graph, each task with its deps |
|
|
2202
2032
|
| turbo | `--force[=<bool>]` | same | `--force`: skip cache reads, keep writes |
|
|
2203
2033
|
| turbo | `--affected` | same | `--affected[=<base>]` |
|
|
2204
|
-
| turbo | `--summarize
|
|
2034
|
+
| turbo | `--summarize` | refuse | use `vx last --format json` for the run's JSON record |
|
|
2205
2035
|
| turbo | `--output-logs=new-only` | refuse | use `--output-logs=full` (a hit replays its log) or `errors-only` |
|
|
2206
2036
|
| turbo | `--output-logs <v>` | same | `--output-logs full\|errors-only\|hash-only\|none` |
|
|
2207
2037
|
| turbo | `--no-cache` | same | `--no-cache` reads nothing either; Turbo's (reads, no writes) is `--cache-policy local:r,remote:r` |
|
|
@@ -2230,8 +2060,8 @@ copy to the source.
|
|
|
2230
2060
|
| turbo | `--env-mode <v>` | refuse | vx passes only the variables a task declares (strict): list the rest in `exec.env.passThrough` |
|
|
2231
2061
|
| turbo | `--framework-inference <v>` | refuse | under `turbo()` inference is Turbo's; take a name back with a `!` entry in the task's `env` |
|
|
2232
2062
|
| turbo | `--global-deps <v>` | refuse | declare them in `cache.inputs.workspaceFiles` (under `turbo()`, turbo.json's `globalDependencies`) |
|
|
2233
|
-
| turbo | `--json` | refuse | use
|
|
2234
|
-
| turbo | `--log-file` | refuse | use
|
|
2063
|
+
| turbo | `--json` | refuse | use `vx last --format json` for the run's result, `--dry=json` for the plan |
|
|
2064
|
+
| turbo | `--log-file` | refuse | use `vx last --format json` for the run's JSON record |
|
|
2235
2065
|
| turbo | `--preflight` | refuse | set `turboCache({ preflight: true })` or `TURBO_PREFLIGHT=1` |
|
|
2236
2066
|
| turbo | `--remote-cache-timeout <v>` | refuse | set `turboCache({ timeoutMs })` or `TURBO_REMOTE_CACHE_TIMEOUT` |
|
|
2237
2067
|
| turbo | `--single-package` | refuse | a repo with no workspaces is one project already: drop it |
|
|
@@ -2273,9 +2103,8 @@ copy to the source.
|
|
|
2273
2103
|
## Machine-readable output
|
|
2274
2104
|
|
|
2275
2105
|
`vx show`, `vx info`, `vx why`, `vx last` and `vx cache clean` take
|
|
2276
|
-
`--format json`. Each prints one JSON document on one line,
|
|
2277
|
-
|
|
2278
|
-
checked-in JSON Schema (draft 2020-12) shipped with the package:
|
|
2106
|
+
`--format json`. Each prints one JSON document on one line, whose shape
|
|
2107
|
+
is a checked-in JSON Schema (draft 2020-12) shipped with the package:
|
|
2279
2108
|
`schemas/show.json`, `schemas/info.json`, `schemas/why.json`,
|
|
2280
2109
|
`schemas/last.json`, `schemas/cache.json`
|
|
2281
2110
|
(`node_modules/@vzn/vx/schemas/` in an install). Every object closes its
|
|
@@ -2287,13 +2116,9 @@ schemas: each `{ … }` a verb's section shows is an object its schema
|
|
|
2287
2116
|
closes with those keys, each object the verb prints whole is shown, and
|
|
2288
2117
|
`vx info`'s field list is its schema's top level.
|
|
2289
2118
|
|
|
2290
|
-
`vx
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
the frame and the tasks' output with it, goes to stderr, so
|
|
2294
|
-
`vx run build --all --format json | jq .ok` reads only the result. It
|
|
2295
|
-
refuses `--dry` (use `--dry=json`), a bare `--graph` and
|
|
2296
|
-
`--report=markdown`, which print on stdout too.
|
|
2119
|
+
A run's result as JSON is `vx last --format json`, read after the run
|
|
2120
|
+
from the run history: one row per task with its status, key and cache
|
|
2121
|
+
result. `vx run --dry=json` prints the plan.
|
|
2297
2122
|
|
|
2298
2123
|
### Error codes
|
|
2299
2124
|
|
|
@@ -2321,12 +2146,15 @@ lists core's verbs. A plugin's verb exists once its plugin is in
|
|
|
2321
2146
|
No project in scope declares a requested task. The message lists the
|
|
2322
2147
|
tasks it found; `vx show --format json` lists each project's. Check the
|
|
2323
2148
|
spelling, and the scope: `--filter`, `--all`, or the project the working
|
|
2324
|
-
directory is in.
|
|
2149
|
+
directory is in. A run reads its configs from the lock, so a task
|
|
2150
|
+
added to a `vx.config` since the last `vx lock` is not there yet: run
|
|
2151
|
+
`vx lock`.
|
|
2325
2152
|
|
|
2326
2153
|
#### `VX_E_UNKNOWN_PROJECT`
|
|
2327
2154
|
|
|
2328
2155
|
`vx show` names a project the workspace does not have. A project is named
|
|
2329
|
-
by its
|
|
2156
|
+
by its config's `name`, else by a plugin (a lockfile plugin names it from
|
|
2157
|
+
its `package.json`), else by its directory; `vx show --format json` lists them.
|
|
2330
2158
|
|
|
2331
2159
|
#### `VX_E_NO_HISTORY`
|
|
2332
2160
|
|
|
@@ -2406,8 +2234,9 @@ target, fix its spelling, or drop the entry.
|
|
|
2406
2234
|
`vx lock` refuses it, not a run: a run trusts the graph the lock holds.
|
|
2407
2235
|
|
|
2408
2236
|
A `^task` with no edge source is refused too: ``<id>: ^build needs
|
|
2409
|
-
project edges; add pnpm()/bun()/npm()/yarn()
|
|
2410
|
-
|
|
2237
|
+
project edges; add pnpm()/bun()/npm()/yarn() and install so its
|
|
2238
|
+
lockfile exists, or a plugin that returns `deps` ``. A lockfile plugin
|
|
2239
|
+
whose lockfile is absent gives no edges. Core reads no edge from a manifest, so with no plugin
|
|
2411
2240
|
returning `deps` every `^` would resolve to nothing and fold no
|
|
2412
2241
|
upstream into the key. Declare the lockfile plugin in
|
|
2413
2242
|
`.vx/workspace.ts` (or a plugin of your own that returns `deps`); a run
|
|
@@ -2443,12 +2272,11 @@ https://github.com/vznjs/vx/issues.
|
|
|
2443
2272
|
A plugin can give its refusal a code: `new UserError(message, 'MY_CODE')`.
|
|
2444
2273
|
|
|
2445
2274
|
**Streams.** A verb whose stdout is a product (a `--format json`
|
|
2446
|
-
document, `--dry` / `--dry=json`,
|
|
2275
|
+
document, `--dry` / `--dry=json`, a completion
|
|
2447
2276
|
script) writes that product alone there; a notice or warning it meets
|
|
2448
2277
|
on the way (a cache index from another vx version, a plugin's warning) goes
|
|
2449
2278
|
to stderr, as every refusal does. `vx run`'s stdout is the run's frame,
|
|
2450
|
-
the tasks' output it carries
|
|
2451
|
-
moves to stderr. `tests/cli-streams.test.ts` holds each
|
|
2279
|
+
the tasks' output it carries. `tests/cli-streams.test.ts` holds each
|
|
2452
2280
|
product verb to it under a notice.
|
|
2453
2281
|
|
|
2454
2282
|
## `vx show`
|
|
@@ -2487,23 +2315,25 @@ Nx's spellings name these: `vx show projects` (when no project or task
|
|
|
2487
2315
|
has that name) and `vx show project <name>` say `vx show` and
|
|
2488
2316
|
`vx show <name>`.
|
|
2489
2317
|
|
|
2490
|
-
No target: one
|
|
2491
|
-
and
|
|
2492
|
-
tasks all come from plugins reads `N tasks (no vx
|
|
2493
|
-
|
|
2494
|
-
vx config
|
|
2495
|
-
`^build` (X-48). A
|
|
2318
|
+
No target: a table, one row per project — name, root-relative dir,
|
|
2319
|
+
task count, and `no vx config` for config-less packages; one whose
|
|
2320
|
+
tasks all come from plugins reads `N tasks from plugins (no vx
|
|
2321
|
+
config)`, and one a configured project depends on reads `1 task,
|
|
2322
|
+
default build (no vx config)`: a run loads it for that dependant's
|
|
2323
|
+
`^build` (X-48). A `tags` column appears when any project has tags. With `--format json`
|
|
2496
2324
|
it's an array of `{ name, dir, tags: string[], tasks: string[] }`.
|
|
2497
2325
|
|
|
2498
2326
|
```
|
|
2499
2327
|
$ vx show
|
|
2500
|
-
|
|
2501
|
-
|
|
2328
|
+
project dir tasks tags
|
|
2329
|
+
app packages/app 3 tasks scope:web
|
|
2330
|
+
bare packages/bare no vx config
|
|
2502
2331
|
```
|
|
2503
2332
|
|
|
2504
|
-
|
|
2505
|
-
`
|
|
2506
|
-
|
|
2333
|
+
A report naming the project opens it: its `dir`, its `tags`, and
|
|
2334
|
+
`config none` for a package with no config file. Then
|
|
2335
|
+
`vx show <project>` prints a block per task, headed by its
|
|
2336
|
+
`project#task`, with every field the run reads: description, command (`(group)` for group tasks), `dependsOn`,
|
|
2507
2337
|
`timeout`, `retries`, `env.passThrough` / `env.define` / `env.secret`,
|
|
2508
2338
|
`remote`, `sandbox`, `persistent`, `interactive` (`yes` / `no`), and the
|
|
2509
2339
|
cache block
|
|
@@ -2521,15 +2351,15 @@ list and `vx show <project>`, but `vx show build`, like
|
|
|
2521
2351
|
|
|
2522
2352
|
```
|
|
2523
2353
|
$ vx show app#build
|
|
2524
|
-
app — packages/app
|
|
2525
2354
|
|
|
2526
|
-
build
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
inputs.
|
|
2532
|
-
|
|
2355
|
+
─ app#build ──────────────────────────────────────────────────
|
|
2356
|
+
dir packages/app
|
|
2357
|
+
description compile the app
|
|
2358
|
+
command tsc -b
|
|
2359
|
+
dependsOn ^build
|
|
2360
|
+
inputs.files src/**
|
|
2361
|
+
inputs.env NODE_ENV
|
|
2362
|
+
outputs.files dist/**
|
|
2533
2363
|
```
|
|
2534
2364
|
|
|
2535
2365
|
Unknown project / task names exit `1` with the same near-miss hint
|
|
@@ -2542,6 +2372,9 @@ or `; projects and tasks: …` (M-57).
|
|
|
2542
2372
|
|
|
2543
2373
|
An empty target (`vx show ''`) is refused: omit it to list every project.
|
|
2544
2374
|
|
|
2375
|
+
In a workspace with no project, the list is empty and `vx show` says so
|
|
2376
|
+
on stderr (`no projects`) and names `vx init`.
|
|
2377
|
+
|
|
2545
2378
|
Exit codes: `0` success; `1` parse error or unknown target.
|
|
2546
2379
|
|
|
2547
2380
|
Two runs on one workspace take turns: the second waits for the first's
|
|
@@ -2572,24 +2405,26 @@ counts are zero and `projects lock` says `no` — a broken config is
|
|
|
2572
2405
|
|
|
2573
2406
|
```
|
|
2574
2407
|
$ vx info
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
cache
|
|
2587
|
-
cache
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2408
|
+
|
|
2409
|
+
─ vx info ────────────────────────────────────────────────────
|
|
2410
|
+
vx 0.0.0
|
|
2411
|
+
bun 1.4.2
|
|
2412
|
+
git 2.53.0
|
|
2413
|
+
git status cache core.fsmonitor, core.untrackedCache off
|
|
2414
|
+
workspace root /work/repo
|
|
2415
|
+
projects 12 (34 tasks)
|
|
2416
|
+
plugins 2 — @vzn/vx-reapi (executor, cache); @vzn/vx-otel (telemetry)
|
|
2417
|
+
workers 2 — cgroup CPU quota 2 of 8 cores
|
|
2418
|
+
memory 13 GB usable — cgroup limit; the machine has 16 GB
|
|
2419
|
+
cache dir /work/repo/.vx/cache (8.2 MB, this workspace's run history)
|
|
2420
|
+
cache store /home/me/.vx/3f2a9c1e7b4d5a60/cache (1.3 GB, shared by this repo's checkouts)
|
|
2421
|
+
cache versions keys vx-cache-v43 · index schema v40
|
|
2422
|
+
cache entries 42 (1.3 GB)
|
|
2423
|
+
orphans 3 artifacts (12 MB) the index does not know — `vx cache clean` reaps them
|
|
2424
|
+
task runs (24h) 7 (5 cache hits)
|
|
2425
|
+
flaky tasks 1 — web#test (3 of 11 runs failed on unchanged inputs)
|
|
2426
|
+
sandbox available (9 tasks declare exec.sandbox)
|
|
2427
|
+
projects lock yes
|
|
2593
2428
|
```
|
|
2594
2429
|
|
|
2595
2430
|
- `git` shows `(not found)` when the binary is missing.
|
|
@@ -2844,7 +2679,9 @@ command that exits 137 on its own reads the same). Where the run's
|
|
|
2844
2679
|
footer gave a reason, the row ends with it instead: `timed out`,
|
|
2845
2680
|
`never ready: exited`, `2 sandbox violations`, and for a skipped task
|
|
2846
2681
|
`after lib#build failed` (the v27 columns; rows older than them read
|
|
2847
|
-
as before).
|
|
2682
|
+
as before). A task whose key met a remote failure ends with it, read or
|
|
2683
|
+
write alike: `remote ✗ download: connection refused; upload: 503` (the
|
|
2684
|
+
v40 column, `remoteError` in `--format json`). Rows run failures first, then what executed or was
|
|
2848
2685
|
skipped, then hits; past sixteen hits the rest fold into one line with
|
|
2849
2686
|
their count (`… +980 more cache hits`) — a thousand-task warm run is a
|
|
2850
2687
|
thousand rows otherwise, with the one failure a screen above the
|
|
@@ -2905,9 +2742,9 @@ measured. `--stats` does not combine with `--list`, `--failed` or
|
|
|
2905
2742
|
`--log`; `--branch` combines only with `--stats`. `--list --format json`
|
|
2906
2743
|
an array of the same `invocation` objects, newest first. An unknown run id fails
|
|
2907
2744
|
loud and points at `--list`. Before any run, every form says so
|
|
2908
|
-
(
|
|
2909
|
-
|
|
2910
|
-
`--list --failed` prints
|
|
2745
|
+
(`› vx last no runs yet` with `fix vx run`, exit 1; `--list` prints
|
|
2746
|
+
`› vx last no runs yet`, exit 0), and past green runs only,
|
|
2747
|
+
`--list --failed` prints `› vx last no failed runs`. A run id may be typed as a unique prefix; a
|
|
2911
2748
|
prefix several runs share fails and lists them. A replayed run with
|
|
2912
2749
|
failures ends with the command that re-runs them (`re-run what failed:
|
|
2913
2750
|
vx run app#test -- …`, with the arguments the run forwarded to the
|
|
@@ -2994,7 +2831,7 @@ export function mcp(): VxPlugin {
|
|
|
2994
2831
|
mcp: {
|
|
2995
2832
|
description: 'serve the run history to an AI agent over stdio',
|
|
2996
2833
|
async run(argv, ctx) {
|
|
2997
|
-
// ctx.workspaceRoot, ctx.cacheDir, ctx.concurrency, ctx.warn(...)
|
|
2834
|
+
// ctx.workspaceRoot, ctx.cacheDir, ctx.concurrency, ctx.cache, ctx.warn(...)
|
|
2998
2835
|
return 0 // the process exit code
|
|
2999
2836
|
},
|
|
3000
2837
|
},
|
|
@@ -3005,6 +2842,9 @@ export function mcp(): VxPlugin {
|
|
|
3005
2842
|
|
|
3006
2843
|
The plugin's name is its package name, which `definePlugin` reads from
|
|
3007
2844
|
`import.meta`; a plain object is refused when the workspace loads.
|
|
2845
|
+
`ctx.cache` reads the plugin's own entries as `setup` writes them (the
|
|
2846
|
+
local store, then each remote the workspace's cache plugins supply,
|
|
2847
|
+
under the run's scope), read-only, opened on first use.
|
|
3008
2848
|
|
|
3009
2849
|
(`@vzn/vx-schedule-history` ships exactly this: `vx history`, what it
|
|
3010
2850
|
learned per task and the reservation its `admit` hook packs.) The dispatcher tries core's verbs
|
|
@@ -3032,8 +2872,8 @@ names no package: it lists what the workspace itself declares.
|
|
|
3032
2872
|
|
|
3033
2873
|
## Environment variables vx reads
|
|
3034
2874
|
|
|
3035
|
-
Every setting has a variable derived from its path (`run.
|
|
3036
|
-
`
|
|
2875
|
+
Every setting has a variable derived from its path (`run.timeout` →
|
|
2876
|
+
`VX_RUN_TIMEOUT`), beating `.vx/workspace` and beaten by the flag; the
|
|
3037
2877
|
table in `docs/schema.md` § Settings gives each value's form and
|
|
3038
2878
|
default. Each is checked once before any verb runs: an empty one is
|
|
3039
2879
|
unset, a value its kind does not take is refused naming the form, and a
|
|
@@ -3053,9 +2893,6 @@ what a TASK sees is `exec.env` in `docs/schema.md`.
|
|
|
3053
2893
|
| `VX_RUN_RETRIES` | [`run.retries`](schema.md#settings) | Re-runs of a failed task, for tasks without their own `exec.retries`. |
|
|
3054
2894
|
| `VX_RUN_CONTINUE` | [`run.continue`](schema.md#settings) | What a failed task takes down: `never` stops the run, `deps-ok` skips its dependents, `always` runs them too. |
|
|
3055
2895
|
| `VX_RUN_OUTPUT_LOGS` | [`run.outputLogs`](schema.md#settings) | Which task output the run prints. Chosen before the workspace loads, so flag and env only. |
|
|
3056
|
-
| `VX_RUN_KILL_GRACE` | [`run.killGrace`](schema.md#settings) | How long a task that ignores SIGTERM gets before SIGKILL. |
|
|
3057
|
-
| `VX_RUN_TEARDOWN_TIMEOUT` | [`run.teardownTimeout`](schema.md#settings) | The bound on each plugin's end-of-run flush and teardown. |
|
|
3058
|
-
| `VX_RUN_READY_NOTICE` | [`run.readyNotice`](schema.md#settings) | How long a persistent task may take to match `readyWhen` before vx says it waits. |
|
|
3059
2896
|
| `VX_RUN_RULES_EXCLUSIVE_OUTPUTS` | [`run.rules.exclusiveOutputs`](schema.md#settings) | Refuse two tasks whose outputs overlap even when an edge orders them. |
|
|
3060
2897
|
| `VX_RUN_RULES_UPFRONT_KEYS` | [`run.rules.upfrontKeys`](schema.md#settings) | Refuse a task whose inputs can match another task's outputs. |
|
|
3061
2898
|
| `VX_CACHE_DIR` | [`cache.dir`](schema.md#settings) | Where the cache lives, relative to the workspace root; named, it holds the whole cache. |
|
|
@@ -3066,11 +2903,11 @@ what a TASK sees is `exec.env` in `docs/schema.md`.
|
|
|
3066
2903
|
| `VX_CACHE_DOWNLOAD` | [`cache.download`](schema.md#settings) | Which remote hits land on disk: every one, only the requested tasks, or none. |
|
|
3067
2904
|
| `VX_CACHE_INPUT_STABILITY_STRATEGY` | [`cache.inputStability.strategy`](schema.md#settings) | What a write to an input during the command means for the save, for tasks without their own. |
|
|
3068
2905
|
| `VX_CACHE_INPUT_STABILITY_SEVERITY` | [`cache.inputStability.severity`](schema.md#settings) | Whether a skipped save warns or fails the task, for tasks without their own. |
|
|
3069
|
-
| `VX_CACHE_OUTPUTS_CLEAN` | [`cache.outputs.clean`](schema.md#settings) | Remove a task's declared outputs before it runs on a miss, for tasks without their own. |
|
|
3070
2906
|
| `VX_LOCK_REVIEW_FILES` | [`lock.reviewFiles`](schema.md#settings) | `vx lock` writes each project's resolved config, pretty, to `vx.lock.json` beside its `vx.config`, for review; vx never reads it. |
|
|
3071
2907
|
| `VX_WATCH_POLL` | [`watch.poll`](schema.md#settings) | Poll for changes from the start instead of the OS watcher. |
|
|
3072
2908
|
| `VX_CONFIG_WORKER_TIMEOUT` | [`config.workerTimeout`](schema.md#settings) | How long one config evaluation may take. |
|
|
3073
2909
|
| `VX_DEBUG_TIMING` | [`debug.timing`](schema.md#settings) | Print the run's stage timings to stderr. |
|
|
2910
|
+
| `VX_DEBUG_WATCH` | [`debug.watch`](schema.md#settings) | `vx watch`: say on stderr each time the loop arms, with the projects it watches. |
|
|
3074
2911
|
| `VX_DEBUG_DUMPABLE` | [`debug.dumpable`](schema.md#settings) | Linux: keep vx dumpable, so strace or a debugger can read its memory and `/proc/<pid>/fd`, and an unsandboxed task its env. |
|
|
3075
2912
|
| `VX_HANDED_OFF` | — | Set by a global vx that hands the call to the project's own (§ Which vx runs): the path of the vx that handed off, so the receiver does not hand off again. |
|
|
3076
2913
|
| `VX_RUN_WORKSPACE`, `VX_RUN_TASK` | — | Set by vx on every task (below); a `vx run` a task starts reads them back. |
|
|
@@ -3087,9 +2924,8 @@ Colors are the two conventions in § Output format › Colors (`NO_COLOR`,
|
|
|
3087
2924
|
`FORCE_COLOR`). A truthy `CI` picks the CI output flow and
|
|
3088
2925
|
`GITHUB_ACTIONS` its `::group::` framing (§ Output); the run's
|
|
3089
2926
|
`invocations` row records the provider, the first truthy of
|
|
3090
|
-
`GITHUB_ACTIONS`, `GITLAB_CI`, `BUILDKITE`, `CIRCLECI`, then `CI`. Core never reads `GITHUB_STEP_SUMMARY
|
|
3091
|
-
|
|
3092
|
-
Actions pass `--report-file="$GITHUB_STEP_SUMMARY"`. A sandboxed
|
|
2927
|
+
`GITHUB_ACTIONS`, `GITLAB_CI`, `BUILDKITE`, `CIRCLECI`, then `CI`. Core never reads `GITHUB_STEP_SUMMARY`; the job summary is
|
|
2928
|
+
`@vzn/vx-ci`'s. A sandboxed
|
|
3093
2929
|
task's temp dir sits under `CLAUDE_CODE_TMPDIR` (else `CLAUDE_TMPDIR`,
|
|
3094
2930
|
else `/tmp/claude`), the sandbox runtime's own convention
|
|
3095
2931
|
(`docs/modules/sandbox-runtime.md`).
|
|
@@ -3124,8 +2960,8 @@ write ../shared/notes.txt
|
|
|
3124
2960
|
Every section is conditional: the `$ <command>` line only for an
|
|
3125
2961
|
executed task (success or failed — a hit replays its stored output and
|
|
3126
2962
|
shows none), `OUTPUT` only when the task wrote something,
|
|
3127
|
-
`SANDBOX VIOLATIONS (N)` only when the sandbox recorded some
|
|
3128
|
-
has a note on a failure. The
|
|
2963
|
+
`SANDBOX VIOLATIONS (N)` only when the sandbox recorded some (`N`
|
|
2964
|
+
counts them) or `SANDBOX` when vx has only a note on a failure. The
|
|
3129
2965
|
header carries the outcome (`restored-local • abc12345`, `failed (exit
|
|
3130
2966
|
N)`, …) and the footer repeats it after the duration. A test renders
|
|
3131
2967
|
this block and checks it against this page, byte for byte.
|
|
@@ -3165,16 +3001,34 @@ A broad run looks like:
|
|
|
3165
3001
|
tasks ▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰
|
|
3166
3002
|
2 success · 2 total
|
|
3167
3003
|
cache ▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰
|
|
3168
|
-
1
|
|
3004
|
+
local 1↓ 1↑
|
|
3169
3005
|
|
|
3170
3006
|
info 8 workers · local cache
|
|
3171
3007
|
time 5.34s · 900ms saved
|
|
3172
3008
|
```
|
|
3173
3009
|
|
|
3174
3010
|
The three bars are meters: `projects` is what the run covered against
|
|
3175
|
-
the workspace, `tasks` is failed / success / skipped, `cache`
|
|
3176
|
-
|
|
3177
|
-
|
|
3011
|
+
the workspace, `tasks` is failed / success / skipped, `cache` has a
|
|
3012
|
+
segment per legend state (a skipped task rides it too, so the two bars
|
|
3013
|
+
fill alike; its count is on the tasks legend only). Its legend reads
|
|
3014
|
+
per layer: `local 1✓ 1∅ 2↓ 1↑ · remote 3↓ 4↑ 2✗ 1⇡`. `✓` is a fresh
|
|
3015
|
+
hit (outputs already on disk, whichever layer answered, so it counts
|
|
3016
|
+
under local), `∅` a task with no cache, `↓` is restored from that
|
|
3017
|
+
layer and `↑` saved to it, `✗` a task a remote read or write failed
|
|
3018
|
+
for, and `⇡` an upload still running (live only, always last). The
|
|
3019
|
+
region stays through vx's closing steps, worker rows gone, and the
|
|
3020
|
+
final footer replaces it in place. Each task counts once, under its strongest state: a
|
|
3021
|
+
failed remote read or write, then an upload running, then saved to a
|
|
3022
|
+
remote, then saved here, then restored, then fresh, then no cache; so
|
|
3023
|
+
a task saved here and uploaded is a remote `↑`. A key the remote already
|
|
3024
|
+
held is not sent, so it is no remote `↑`. Each count is painted
|
|
3025
|
+
in its segment's colour, number and glyph alike: `✓` green, `∅` dim,
|
|
3026
|
+
local blue, remote dark blue, `✗` and a miss red, `⇡` cyan; the
|
|
3027
|
+
`local` and `remote` words are dim. A layer shows only when the run's
|
|
3028
|
+
`--cache-policy` uses it. `N not saved` (ran, succeeded, saved
|
|
3029
|
+
nothing), `N not downloaded` (outputs left in the remote store) and
|
|
3030
|
+
every count show only when non-zero. `vx last` names each remote
|
|
3031
|
+
failure on its task's row. The
|
|
3178
3032
|
`time` row is the wall time, then what the cache saved (the hits' stored
|
|
3179
3033
|
exec times, never the restore), then the spread over executed tasks
|
|
3180
3034
|
only — a hit's restore time never enters it. The footer never says a
|
|
@@ -3192,12 +3046,22 @@ running a group focused surfaces its real member tasks instead.
|
|
|
3192
3046
|
**Nothing below the footer.** The footer is the run's last word.
|
|
3193
3047
|
What a run has to say about one task rides that task's row; what it
|
|
3194
3048
|
says about the run (a server that died, outputs left remote, a file
|
|
3195
|
-
`--
|
|
3049
|
+
`--profile` wrote, a history, upload, plugin or sandbox
|
|
3196
3050
|
warning) prints above the footer: it prints once the run's closing work
|
|
3197
3051
|
is done, the `VX_DEBUG_TIMING=1` table included. Cache housekeeping
|
|
3198
3052
|
(`cache.retention`) prints nothing. Only a kept server's own output
|
|
3199
3053
|
follows it.
|
|
3200
3054
|
|
|
3055
|
+
**Narrow terminals.** The footer is 62 columns: its rule and 50-cell
|
|
3056
|
+
bars. On a terminal narrower than that the bars shrink and the rule
|
|
3057
|
+
with them, as do a frame's section rules, so none wraps onto a second
|
|
3058
|
+
row. Off a terminal they keep their width.
|
|
3059
|
+
|
|
3060
|
+
**Warnings on stderr.** A run's warnings, such as `[vx] app#build:
|
|
3061
|
+
cache.outputs matched no files …` or a plugin's, go to stderr. Stdout
|
|
3062
|
+
carries only the rows, the frames and the footer, so `vx run build >
|
|
3063
|
+
log.txt` keeps them out of the log.
|
|
3064
|
+
|
|
3201
3065
|
**Skipped rows.** A red run lists every task that never started as a
|
|
3202
3066
|
row naming the failure that blocked it, so the footer's `1 skipped`
|
|
3203
3067
|
always has a name above it:
|
|
@@ -3259,6 +3123,7 @@ ANSI truecolor (`ansi-16m`) sequences, gated by env:
|
|
|
3259
3123
|
| `FORCE_COLOR=0` / `=false` | Force off, on a TTY too. |
|
|
3260
3124
|
| `FORCE_COLOR=…` (any other) | Force on — `1`, `true`, `2`, `3`, the empty string, etc. |
|
|
3261
3125
|
| (neither) | On iff `stdout.isTTY`. |
|
|
3126
|
+
| `TERM=dumb` | Off unless `FORCE_COLOR` forces it, and no live region. |
|
|
3262
3127
|
|
|
3263
3128
|
`0` and `false` are `FORCE_COLOR`'s "off" by the convention
|
|
3264
3129
|
supports-color, chalk and Node follow; until 2026-09-24 vx read every
|
|
@@ -3284,7 +3149,7 @@ Only a run's own output (`vx run`, `vx watch`) is painted, and the
|
|
|
3284
3149
|
help on a terminal: headings bold, `vx <verb>` and each flag or example
|
|
3285
3150
|
in cyan; `NO_COLOR` or `FORCE_COLOR=0` turns it off
|
|
3286
3151
|
(`tests/help-colors.test.ts`). Every other verb (`show`, `info`, `why`,
|
|
3287
|
-
`last`, a `--dry` plan,
|
|
3152
|
+
`last`, a `--dry` plan, `cache clean`), and the help off a
|
|
3288
3153
|
terminal, prints plain text whatever `FORCE_COLOR` says, so a script
|
|
3289
3154
|
that forces colour for a run's log still parses them
|
|
3290
3155
|
(`tests/cli-colors-e2e.test.ts`).
|
|
@@ -3301,7 +3166,7 @@ Reads try local first, then remote (hydrating local on remote hit),
|
|
|
3301
3166
|
with a background prefetch pass overlapping remote GETs with
|
|
3302
3167
|
execution. Writes go to local immediately; the remote upload is a
|
|
3303
3168
|
fire-and-forget background task drained at end of run — failures are
|
|
3304
|
-
|
|
3169
|
+
warned but never fail the build.
|
|
3305
3170
|
|
|
3306
3171
|
For any OTHER cache server (a Turbo-wire deployment, S3-direct, …),
|
|
3307
3172
|
implement core's `RemoteCacheLayer` interface in a plugin's `cache`
|
|
@@ -3316,7 +3181,7 @@ consult).
|
|
|
3316
3181
|
size, runs + hits in the last 24 h). For anything deeper, vx records
|
|
3317
3182
|
every task to a `runs` table in `cache.db` (UUIDv7 `run_id`, hrtime
|
|
3318
3183
|
wallclock spans, cpu_ms, peak RSS, status, cache_hit flag) plus one
|
|
3319
|
-
`invocations` header row per run (command, git/CI context,
|
|
3184
|
+
`invocations` header row per run (command, git/CI context,
|
|
3320
3185
|
counts). The SQLite file IS the API:
|
|
3321
3186
|
|
|
3322
3187
|
```sh
|
|
@@ -3365,7 +3230,7 @@ Surface:
|
|
|
3365
3230
|
|
|
3366
3231
|
- `run(options)` — execute. Returns `Promise<RunSummary>`.
|
|
3367
3232
|
- `planRun(options)` — predict, no execute. Returns
|
|
3368
|
-
`Promise<RunPlan>`. Used by `--dry
|
|
3233
|
+
`Promise<RunPlan>`. Used by `--dry`.
|
|
3369
3234
|
- `prepareRun(options, log)` — the shared setup (discovery → configs →
|
|
3370
3235
|
graph → cache). What an embedder builds on.
|
|
3371
3236
|
- `defineProject` / `defineWorkspace` — identity helpers for type
|