@vzn/vx 0.0.606 → 0.0.608

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vzn/vx",
3
- "version": "0.0.606",
3
+ "version": "0.0.608",
4
4
  "description": "The fastest task runner and build cache for JavaScript monorepos.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -21,10 +21,10 @@
21
21
  "node": ">=18"
22
22
  },
23
23
  "optionalDependencies": {
24
- "@vzn/vx-linux-x64": "0.0.606",
25
- "@vzn/vx-linux-arm64": "0.0.606",
26
- "@vzn/vx-darwin-x64": "0.0.606",
27
- "@vzn/vx-darwin-arm64": "0.0.606"
24
+ "@vzn/vx-linux-x64": "0.0.608",
25
+ "@vzn/vx-linux-arm64": "0.0.608",
26
+ "@vzn/vx-darwin-x64": "0.0.608",
27
+ "@vzn/vx-darwin-arm64": "0.0.608"
28
28
  },
29
29
  "dependencies": {
30
30
  "@anthropic-ai/sandbox-runtime": "^0.0.76"
@@ -153,6 +153,10 @@ export async function foldKey(
153
153
  h = xxh3(`pkg:${input.projectPackageJsonHash}`, h)
154
154
  h = xxh3(`config:${input.taskConfigHash}`, h)
155
155
  if (cap) {
156
+ // Recorded so `vx why` can name a key that moved only because vx
157
+ // keys differently now (an upgrade): unrecorded, every task's miss
158
+ // read as "inputs differ" with no input changed (X-142).
159
+ cap.push({ kind: 'format', name: 'cache-version', hash: CACHE_VERSION })
156
160
  cap.push({ kind: 'workspace', name: 'fingerprint', hash: input.workspaceFingerprint })
157
161
  cap.push({ kind: 'package', name: 'package.json', hash: input.projectPackageJsonHash })
158
162
  cap.push({ kind: 'config', name: 'config', hash: input.taskConfigHash })
package/src/cli/run.ts CHANGED
@@ -11,7 +11,6 @@ import {
11
11
  run as runOrchestrator,
12
12
  shellQuote,
13
13
  type RunOptions,
14
- type RunResult,
15
14
  } from '../orchestrator/index.js'
16
15
  import type { ContinueMode, TaskOutcome } from '../graph/index.js'
17
16
  import {
@@ -721,6 +720,12 @@ export async function runCmd(args: readonly string[]): Promise<number> {
721
720
  return 1
722
721
  }
723
722
  if (plan.tasks.length === 0) {
723
+ // The run's own line: a dry run that guessed from the scope alone
724
+ // said no affected project declared a task one did (X-141).
725
+ if (plan.noneAffected !== undefined) {
726
+ process.stderr.write(`${plan.noneAffected}\n`)
727
+ return 0
728
+ }
724
729
  if (opts.selectedByDiff === true) {
725
730
  process.stderr.write(`vx run: no affected project declares task(s): ${tasks.join(', ')}.\n`)
726
731
  return 0
package/src/cli/why.ts CHANGED
@@ -49,6 +49,8 @@ export const WHAT_TO_DO: Readonly<Record<string, string>> = {
49
49
  "the task's evaluated config changed (its vx.config or a file it imports); `vx show <that task>` prints it as it is now",
50
50
  upstream: "a dependency's key moved; `vx why <that task>` says why",
51
51
  plugin: "a key plugin's material changed; that plugin decides what it folds",
52
+ format:
53
+ 'vx keys tasks differently since an upgrade; every cached task misses once, nothing to fix',
52
54
  }
53
55
 
54
56
  export function parseWhyArgs(args: readonly string[]): WhyArgs {
@@ -82,6 +82,8 @@ export interface RunPlan {
82
82
  unresolvedTasks?: readonly string[]
83
83
  /** What `vx run` appends to that refusal: the nearest declared names, or how to declare one. */
84
84
  unresolvedHint?: string
85
+ /** Set when `--affected` left nothing to run: the line the run would print, and why. */
86
+ noneAffected?: string
85
87
  /**
86
88
  * Producers `--download` WANTED to defer but could not, mapped to why.
87
89
  * Without this the gate is invisible: a user asks for `--download=none`,
@@ -404,20 +404,7 @@ async function runOnBus(
404
404
  return { ok: false, outcomes: [], refused }
405
405
  }
406
406
  if (prepared.empty === 'none-affected') {
407
- // A changed project declares the task, so `--affected`'s task walk is
408
- // what left nothing (affected-tasks.ts), not the scope.
409
- const inScope = new Set(options.projects ?? [])
410
- const reachedNone =
411
- options.affected !== undefined &&
412
- [...prepared.projects.values()].some(
413
- (p) =>
414
- inScope.has(p.name) && options.tasks.some((t) => declaredTask(p.config, t) !== undefined),
415
- )
416
- log.status(
417
- reachedNone
418
- ? `Nothing affected: the change reaches no ${options.tasks.join(', ')} task.`
419
- : `No affected project declares task(s): ${options.tasks.join(', ')}.`,
420
- )
407
+ log.status(noneAffected(prepared, options))
421
408
  await teardown()
422
409
  prepared.cache.close()
423
410
  return { ok: true, outcomes: [] }
@@ -1516,6 +1503,22 @@ async function applyCacheRetention(prepared: PreparedRun): Promise<void> {
1516
1503
  }
1517
1504
  }
1518
1505
 
1506
+ /** Why an `--affected` run has nothing to run, said alike by the run and its `--dry` plan. */
1507
+ function noneAffected(prepared: PreparedRun, options: RunOptions): string {
1508
+ // A changed project declares the task, so `--affected`'s task walk is
1509
+ // what left nothing (affected-tasks.ts), not the scope.
1510
+ const inScope = new Set(options.projects ?? [])
1511
+ const reachedNone =
1512
+ options.affected !== undefined &&
1513
+ [...prepared.projects.values()].some(
1514
+ (p) =>
1515
+ inScope.has(p.name) && options.tasks.some((t) => declaredTask(p.config, t) !== undefined),
1516
+ )
1517
+ return reachedNone
1518
+ ? `Nothing affected: the change reaches no ${options.tasks.join(', ')} task.`
1519
+ : `No affected project declares task(s): ${options.tasks.join(', ')}.`
1520
+ }
1521
+
1519
1522
  /**
1520
1523
  * Planning mode. Same setup as `run()` — workspace discovery, config
1521
1524
  * load, package graph, task graph — but stops short of execution.
@@ -1548,6 +1551,8 @@ export async function planRun(options: RunOptions): Promise<RunPlan> {
1548
1551
  unresolvedHint: elsewhereHint(prepared.declaredElsewhere),
1549
1552
  }
1550
1553
  }
1554
+ if (prepared.empty === 'none-affected')
1555
+ return { tasks: [], noneAffected: noneAffected(prepared, options) }
1551
1556
  if (prepared.empty !== null) return { tasks: [] }
1552
1557
  // Its own mark: a dry run's plan (every task's hash, the cache lookups,
1553
1558
  // the history p50s) was booked under `close`, the next mark, and read