@vzn/vx 0.0.635 → 0.0.637

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/README.md +47 -19
  2. package/docs/caching.md +222 -210
  3. package/docs/cli.md +699 -664
  4. package/docs/execution.md +67 -75
  5. package/docs/features.md +44 -40
  6. package/docs/patterns.md +31 -32
  7. package/docs/schema.md +418 -202
  8. package/docs/security.md +76 -39
  9. package/launcher.sh +43 -0
  10. package/package.json +5 -5
  11. package/schemas/cache.json +41 -19
  12. package/schemas/info.json +23 -11
  13. package/schemas/last.json +48 -2
  14. package/schemas/lock.json +1 -1
  15. package/schemas/plan.json +2 -2
  16. package/schemas/show.json +13 -4
  17. package/schemas/why.json +15 -1
  18. package/skills/vx/SKILL.md +16 -12
  19. package/src/bin.ts +17 -2
  20. package/src/cache/archive.ts +11 -0
  21. package/src/cache/cache.ts +105 -53
  22. package/src/cache/git-inputs.ts +277 -63
  23. package/src/cache/index.ts +7 -1
  24. package/src/cache/inputs.ts +125 -35
  25. package/src/cache/key-fold.ts +8 -7
  26. package/src/cache/layer.ts +8 -17
  27. package/src/cache/layered-cache.ts +16 -102
  28. package/src/cache/policy.ts +25 -29
  29. package/src/cache/run-history.ts +36 -14
  30. package/src/cache/schema.ts +21 -23
  31. package/src/cache/scoped-cache.ts +224 -0
  32. package/src/cli/cache.ts +150 -28
  33. package/src/cli/completions.ts +2 -2
  34. package/src/cli/core-alias.ts +1 -1
  35. package/src/cli/foreign-flags.ts +13 -12
  36. package/src/cli/help.ts +94 -61
  37. package/src/cli/index.ts +42 -13
  38. package/src/cli/info.ts +38 -23
  39. package/src/cli/init.ts +115 -406
  40. package/src/cli/last.ts +145 -4
  41. package/src/cli/lock.ts +333 -111
  42. package/src/cli/plan-format.ts +2 -2
  43. package/src/cli/plugin-commands.ts +5 -9
  44. package/src/cli/plugin-templates.ts +10 -10
  45. package/src/cli/run.ts +123 -144
  46. package/src/cli/select.ts +33 -39
  47. package/src/cli/show.ts +17 -19
  48. package/src/cli/task-verb.ts +2 -9
  49. package/src/cli/upgrade.ts +402 -145
  50. package/src/cli/watch-cycle.ts +9 -2
  51. package/src/cli/watch-filter.ts +33 -60
  52. package/src/cli/watch-fs.ts +29 -16
  53. package/src/cli/watch-judge.ts +12 -3
  54. package/src/cli/watch-set.ts +16 -42
  55. package/src/cli/watch.ts +188 -215
  56. package/src/cli/why.ts +28 -17
  57. package/src/cli/workspace-config.ts +31 -68
  58. package/src/config.ts +191 -71
  59. package/src/exec/env.ts +1 -31
  60. package/src/exec/executor.ts +1 -5
  61. package/src/exec/kill-tree.ts +2 -1
  62. package/src/exec/sandbox-runtime.ts +12 -1
  63. package/src/exec/sandbox-violations.ts +9 -1
  64. package/src/graph/dependency-spec.ts +1 -1
  65. package/src/graph/index.ts +1 -0
  66. package/src/graph/priorities.ts +1 -1
  67. package/src/graph/scheduler.ts +19 -7
  68. package/src/graph/task-graph.ts +44 -20
  69. package/src/handoff.ts +169 -0
  70. package/src/index.ts +25 -57
  71. package/src/orchestrator/affected-tasks.ts +7 -5
  72. package/src/orchestrator/command-windows.ts +44 -0
  73. package/src/orchestrator/deferred-outputs.ts +12 -10
  74. package/src/orchestrator/doctor.ts +98 -110
  75. package/src/orchestrator/download-policy.ts +3 -3
  76. package/src/orchestrator/events.ts +19 -3
  77. package/src/orchestrator/excluded-keys.ts +0 -2
  78. package/src/orchestrator/execute-task.ts +192 -70
  79. package/src/orchestrator/fingerprint-watch.ts +18 -15
  80. package/src/orchestrator/forecast.ts +10 -2
  81. package/src/orchestrator/framed-output.ts +8 -3
  82. package/src/orchestrator/graph-stage.ts +5 -5
  83. package/src/orchestrator/history.ts +70 -30
  84. package/src/orchestrator/hit-restore.ts +15 -3
  85. package/src/orchestrator/index.ts +18 -4
  86. package/src/orchestrator/local-shortcircuit.ts +2 -1
  87. package/src/orchestrator/lockfile-claim.ts +79 -20
  88. package/src/orchestrator/logger.ts +71 -39
  89. package/src/orchestrator/metrics.ts +71 -6
  90. package/src/orchestrator/miss-save.ts +3 -0
  91. package/src/orchestrator/options.ts +43 -28
  92. package/src/orchestrator/output-host.ts +72 -0
  93. package/src/orchestrator/plan.ts +6 -8
  94. package/src/orchestrator/plugin-host.ts +87 -63
  95. package/src/orchestrator/plugin.ts +94 -60
  96. package/src/orchestrator/prepare.ts +114 -73
  97. package/src/orchestrator/projects.ts +102 -99
  98. package/src/orchestrator/remote-prefetch.ts +2 -1
  99. package/src/orchestrator/run-context.ts +25 -6
  100. package/src/orchestrator/run-failures.ts +17 -8
  101. package/src/orchestrator/run-lock.ts +1 -1
  102. package/src/orchestrator/run-report.ts +9 -9
  103. package/src/orchestrator/run-settings.ts +48 -0
  104. package/src/orchestrator/run.ts +127 -88
  105. package/src/orchestrator/sandbox-fix.ts +39 -20
  106. package/src/orchestrator/sandbox-request.ts +84 -13
  107. package/src/orchestrator/signals.ts +1 -1
  108. package/src/orchestrator/stable-keys.ts +13 -11
  109. package/src/orchestrator/status-line.ts +48 -6
  110. package/src/orchestrator/summary.ts +46 -62
  111. package/src/orchestrator/task-hash.ts +12 -83
  112. package/src/orchestrator/telemetry.ts +3 -3
  113. package/src/settings.ts +417 -0
  114. package/src/util/errors.ts +1 -1
  115. package/src/util/essential-env.ts +31 -0
  116. package/src/util/hide-env.ts +107 -0
  117. package/src/util/index.ts +8 -2
  118. package/src/util/paths.ts +10 -0
  119. package/src/util/settle.ts +49 -30
  120. package/src/util/size.ts +23 -4
  121. package/src/util/timing.ts +3 -2
  122. package/src/util/ulid.ts +1 -1
  123. package/src/util/verbs.ts +0 -15
  124. package/src/workspace/affected.ts +77 -222
  125. package/src/workspace/config-eval.ts +15 -20
  126. package/src/workspace/config-imports.ts +78 -3
  127. package/src/workspace/config-schema.ts +251 -241
  128. package/src/workspace/dependency-rules.ts +57 -0
  129. package/src/workspace/filter.ts +2 -2
  130. package/src/workspace/fingerprint.ts +11 -121
  131. package/src/workspace/index.ts +22 -27
  132. package/src/workspace/load-reads.ts +4 -4
  133. package/src/workspace/lockfile.ts +96 -140
  134. package/src/workspace/migration.ts +242 -146
  135. package/src/workspace/package-graph.ts +30 -216
  136. package/src/workspace/project-loader.ts +80 -285
  137. package/src/workspace/repo-id.ts +2 -1
  138. package/src/workspace/workspace.ts +362 -831
  139. package/src/cache/config-evals.ts +0 -130
  140. package/src/workspace/config-cache.ts +0 -661
  141. package/src/workspace/migrate-scripts.ts +0 -1121
package/src/index.ts CHANGED
@@ -9,19 +9,11 @@
9
9
  export { VERSION } from './version.js'
10
10
 
11
11
  // Clean error type — user-input failures print a message without a stack.
12
- // `clampInt` rides along for the same reason the status predicates do:
13
- // without it on the façade an integration package writes its own, and that
14
- // had already happened. A bounds helper whose floor is load-bearing (a
15
- // fractional SQL LIMIT is a datatype mismatch, not a smaller page) should
16
- // have one implementation.
17
- export { clampInt, UserError, isUserError } from './util/index.js'
12
+ export { UserError, isUserError } from './util/index.js'
18
13
  // Every hook a plugin may fill, in pipeline order — the one list the
19
14
  // loader, the host and `vx info` read; exported so a doc pin outside core
20
15
  // (the site's guides) holds its tables to the same list.
21
16
  export { PLUGIN_HOOKS, type PluginHook } from './config.js'
22
- // The workspace doctor's facts (`vx info`), for a host that answers the
23
- // same question over another surface — `@vzn/vx-mcp`'s `getWorkspaceInfo`.
24
- export { collectInfo, type CollectInfoOptions, type InfoFacts } from './orchestrator/index.js'
25
17
  // The machine as this process may use it — cores and memory capped by the
26
18
  // cgroup a container runs under. Core's default worker count reads the
27
19
  // first; a policy that budgets memory (`@vzn/vx-schedule-history`) reads
@@ -31,16 +23,15 @@ export { machineMemoryBytes, machineParallelism } from './util/index.js'
31
23
  // wildcards (`[` `]` are not; item 667) — for a plugin that turns declared
32
24
  // outputs into something else (`@vzn/vx-reapi`'s literal `output_paths`).
33
25
  export { isLiteralPattern, normalizeGlob } from './util/index.js'
34
- // A task's command as vx prints it: values of its `exec.env.secret` names
35
- // masked. `vx show` and telemetry show this; a plugin that hands a command
36
- // to someone else (`@vzn/vx-mcp`'s `listTasks`, an AI agent) does too (L-26).
37
- // `maskedLine` is the same for a line no one task owns: the error `vx mcp`
38
- // hands an agent (L-11).
39
- export { maskedCommand, maskedLine } from './util/index.js'
40
26
 
41
27
  // Schema types and helpers (used by user vx.config files and presets).
42
28
  export type {
43
29
  WorkspaceConfig,
30
+ RunSettings,
31
+ CacheSettings,
32
+ LockSettings,
33
+ WatchSettings,
34
+ Duration,
44
35
  ProjectConfig,
45
36
  TaskConfig,
46
37
  ExecConfig,
@@ -48,6 +39,7 @@ export type {
48
39
  CacheConfig,
49
40
  CacheInputs,
50
41
  CacheOutputs,
42
+ InputStability,
51
43
  SandboxConfig,
52
44
  SandboxGrants,
53
45
  SandboxDenials,
@@ -64,7 +56,7 @@ export { run, planRun, prepareRun } from './orchestrator/index.js'
64
56
  // The per-task duration history a `schedule` plugin learns from
65
57
  // (`@vzn/vx-schedule-history` does; a plugin package reaches core only
66
58
  // through this façade, which is what put these here).
67
- export { LocalHistoryProvider } from './orchestrator/index.js'
59
+ export { currentBranch, LocalHistoryProvider } from './orchestrator/index.js'
68
60
  export type {
69
61
  HistoryProvider,
70
62
  HistoryTable,
@@ -81,6 +73,7 @@ export type { AffectedChanges } from './workspace/index.js'
81
73
  export type { RunPlan, PlannedTask } from './orchestrator/index.js'
82
74
  export type { CiContext, GitContext, HostContext, WorkspaceIdentity } from './orchestrator/index.js'
83
75
  export type {
76
+ CacheFlags,
84
77
  CachePolicy,
85
78
  Logger,
86
79
  OutputView,
@@ -95,44 +88,40 @@ export { outputsOverlap, splitTaskId } from './graph/index.js'
95
88
  export type { TaskNode, TaskOutcome, TaskStatus } from './graph/index.js'
96
89
 
97
90
  // Cache classes + the layer interface (the `cache` capability's currency).
98
- export { Cache, LayeredCache } from './cache/index.js'
91
+ export { Cache } from './cache/index.js'
99
92
  export type { CacheLayer, RemoteCacheLayer, RunRecord, InvocationRecord } from './cache/index.js'
100
93
 
101
94
  // Workspace discovery + the project/config catalog surface — an
102
95
  // out-of-process service/CLI needs these. `loadProjectConfig` is the RAW
103
96
  // per-file load (`vx lock` freezes exactly that); the resolved view a run
104
97
  // or `vx show` sees — plugin stages applied — is `loadResolvedProjects`
105
- // below. Workspace's `listProjects` re-exports as `listProjectMetas` (the
106
- // bare name once belonged to a metrics query).
107
- export { findWorkspaceRoot } from './workspace/index.js'
108
- export {
109
- loadWorkspace,
110
- loadProjectConfig,
111
- listProjects as listProjectMetas,
112
- } from './workspace/index.js'
98
+ // below. `PROJECT_CONFIG_FILENAMES` are the names a project's config may
99
+ // have, in precedence order, for a tool that finds or writes one.
100
+ export { findWorkspaceRoot, PROJECT_CONFIG_FILENAMES } from './workspace/index.js'
101
+ export { loadProjectConfig } from './workspace/index.js'
113
102
  export type { ProjectMeta } from './workspace/index.js'
114
- // The package graph as a run sees it (workspace deps by manifest), for a tool
115
- // that needs a project's transitive closure the way `vx run` computes it.
116
- export { buildPackageGraph } from './workspace/index.js'
117
103
  // The run path's RESOLVED view — plugin `config` and `project` stages
118
- // applied, cached evaluations served — for a reader outside the CLI (the
119
- // MCP server's `listTasks`, an embedder's task catalog). What `vx show`
104
+ // applied — for a reader outside the CLI
105
+ // (`@vzn/vx-schedule-history`, an embedder's task catalog). What `vx show`
120
106
  // prints.
121
107
  export { loadResolvedProjects } from './orchestrator/index.js'
122
108
  export type { ProjectEntry } from './workspace/index.js'
123
109
  // The migration seam: how a generated config is planned, rendered, guarded
124
- // and written. `vx init` (package.json scripts) uses it in core;
125
- // `@vzn/vx-migrate` (Turbo, Nx) and any other adoption tool use it from here.
110
+ // and written. `@vzn/vx-migrate` (package.json scripts, Turbo, Nx) and any
111
+ // other adoption tool use it from here; `taskNameProblem` is why a name
112
+ // cannot be a task's, so a mapper leaves such a script out.
126
113
  export {
127
114
  applyMigration,
128
115
  foldScriptHooks,
129
116
  PERSISTENT_TODO,
130
117
  pruneOrphanPersistentNotes,
131
118
  quoteTsLiteral,
119
+ taskNameProblem,
132
120
  } from './workspace/index.js'
133
121
  export type {
134
122
  ApplyMigrationArgs,
135
123
  MigrationFormat,
124
+ MigrationMember,
136
125
  GeneratedProject,
137
126
  GeneratedTask,
138
127
  MigrationPlan,
@@ -142,7 +131,7 @@ export type {
142
131
  // Plugin API — the run-level extension points. Behavior capabilities
143
132
  // (executor / cache) change WHAT/HOW work runs; the observe-only `telemetry`
144
133
  // capability is the canonical data-export path and cannot change behavior.
145
- // A plugin is declared in vx.workspace.ts via defineWorkspace({ plugins }).
134
+ // A plugin is declared in .vx/workspace.ts via defineWorkspace({ plugins }).
146
135
  // See docs/design/observability-architecture-2026-06.md.
147
136
  // The claimant's shell for a lockfile plugin (`@vzn/vx-lockfile`):
148
137
  // the claim, the per-project key, the memo and the `--affected` diff around
@@ -152,6 +141,7 @@ export {
152
141
  reachDigests,
153
142
  type LockfileClaimHooks,
154
143
  type LockfileClaimOptions,
144
+ type LockfileImporter,
155
145
  type ReachGraph,
156
146
  } from './orchestrator/index.js'
157
147
  export type {
@@ -161,6 +151,7 @@ export type {
161
151
  PluginOptionKinds,
162
152
  CacheContext,
163
153
  ExecutorContext,
154
+ OutputContext,
164
155
  CommandContext,
165
156
  FingerprintChange,
166
157
  FingerprintClaim,
@@ -173,7 +164,7 @@ export type {
173
164
  AdmitContext,
174
165
  WorkspaceHookContext,
175
166
  DiscoverContext,
176
- NamedProject,
167
+ DiscoveredProject,
177
168
  PluginSetupContext,
178
169
  PluginHookName,
179
170
  PluginHookHandlers,
@@ -243,26 +234,3 @@ export type {
243
234
 
244
235
  // The serializable run result and its per-task views (what `run()` returns).
245
236
  export type { RunResult, TaskView, OutcomeView } from './orchestrator/index.js'
246
-
247
- // The run-history queries a reader outside the CLI takes today: `vx why`'s
248
- // answer, a run's failures and a task's log, which `@vzn/vx-mcp` serves. The event bus /
249
- // wire form and the other history readers (`listRuns`, `getRun`, …) left the
250
- // façade 2026-09-10 with no consumer; the telemetry seam above is the
251
- // canonical export path.
252
- export {
253
- cacheKeyDiff,
254
- latestRunId,
255
- resolveRunId,
256
- rootCauses,
257
- runFailures,
258
- taskLog,
259
- whyDidThisRerunQuery,
260
- } from './orchestrator/index.js'
261
- export type {
262
- CacheKeyDiff,
263
- OutputLocation,
264
- RootCause,
265
- RunFailures,
266
- TaskFailure,
267
- TaskLog,
268
- } from './orchestrator/index.js'
@@ -29,7 +29,7 @@ import type { VxPlugin } from './plugin.js'
29
29
  import { isDefaultBuild } from './projects.js'
30
30
 
31
31
  /** Every file in this set re-keys each of its project's tasks, whatever their inputs. */
32
- const KEYED_BY_EVERY_TASK = new Set(['package.json', ...PROJECT_CONFIG_FILENAMES])
32
+ const KEYED_BY_EVERY_TASK: ReadonlySet<string> = new Set(PROJECT_CONFIG_FILENAMES)
33
33
 
34
34
  /**
35
35
  * The projects every task of which the change reaches: the whole ones the
@@ -41,8 +41,9 @@ const KEYED_BY_EVERY_TASK = new Set(['package.json', ...PROJECT_CONFIG_FILENAMES
41
41
  function wholeProjects(
42
42
  changes: AffectedChanges,
43
43
  projects: ReadonlyMap<string, ProjectEntry>,
44
- ): Set<string> {
45
- const whole = new Set(changes.whole)
44
+ ): Map<string, string | undefined> {
45
+ // Each whole project, with the path no cached task claims when one did it.
46
+ const whole = new Map<string, string | undefined>([...changes.whole].map((n) => [n, undefined]))
46
47
  for (const [name, rels] of changes.paths) {
47
48
  if (whole.has(name)) continue
48
49
  const caches = Object.values(projects.get(name)?.config.tasks ?? {}).flatMap((t) =>
@@ -50,7 +51,8 @@ function wholeProjects(
50
51
  )
51
52
  const unclaimed = (rel: string): boolean =>
52
53
  KEYED_BY_EVERY_TASK.has(rel) || !caches.some((c) => declaresInput(c, rel, null))
53
- if (rels.some(unclaimed)) whole.add(name)
54
+ const rel = rels.find(unclaimed)
55
+ if (rel !== undefined) whole.set(name, rel)
54
56
  }
55
57
  return whole
56
58
  }
@@ -157,7 +159,7 @@ export function affectedRoots(
157
159
  return hit
158
160
  }
159
161
  if (whole.has(n.projectName)) {
160
- seeds?.set(n.id, inProject(n.projectName, undefined))
162
+ seeds?.set(n.id, inProject(n.projectName, whole.get(n.projectName)))
161
163
  return true
162
164
  }
163
165
  const rel = rels.find((r) => declaresInput(cache, r, null))
@@ -0,0 +1,44 @@
1
+ // When each command of the run ran and where it may write, so
2
+ // `inputStability.strategy: 'mutable'` can tell a task's own identical
3
+ // rewrite from another task's write and restore (STATUS item 28): a file
4
+ // whose bytes match the key after a write is the task's own only if no
5
+ // other command that may write it ran between the describe and that write.
6
+
7
+ import path from 'node:path'
8
+ import type { WriteReach } from './sandbox-request.js'
9
+
10
+ interface Window {
11
+ id: string
12
+ projectDir: string
13
+ reach: WriteReach
14
+ from: number
15
+ to: number
16
+ }
17
+
18
+ export interface CommandWindows {
19
+ /** A command starts; the returned function marks its end. A server never calls it. */
20
+ open(id: string, projectDir: string, reach: WriteReach): () => void
21
+ /** Another command that may write `file` and ran at some point in [from, to]. */
22
+ writerDuring(self: string, file: string, from: number, to: number): string | undefined
23
+ }
24
+
25
+ export function commandWindows(): CommandWindows {
26
+ const windows: Window[] = []
27
+ return {
28
+ open(id, projectDir, reach) {
29
+ if (reach === 'none') return () => {}
30
+ const w: Window = { id, projectDir, reach, from: Date.now(), to: Infinity }
31
+ windows.push(w)
32
+ return () => {
33
+ w.to = Date.now()
34
+ }
35
+ },
36
+ writerDuring(self, file, from, to) {
37
+ for (const w of windows) {
38
+ if (w.id === self || w.from > to || w.to < from) continue
39
+ if (w.reach === 'workspace' || file.startsWith(w.projectDir + path.sep)) return w.id
40
+ }
41
+ return undefined
42
+ },
43
+ }
44
+ }
@@ -1,5 +1,5 @@
1
1
  // The run-scoped registry of tasks whose outputs were left in the remote
2
- // store (`--download=none`), and the lazy materialisation that fetches
2
+ // store (`--cache-download=none`), and the lazy materialisation that fetches
3
3
  // them when a locally-placed task turns out to need them.
4
4
  //
5
5
  // See docs/design/download-policy-cas-cache-2026-08.md §5. The two
@@ -9,11 +9,12 @@
9
9
  // remote store (the executor's own record) is the entry.
10
10
  // - Materialisation CONVERGES: after the bytes land, core runs the
11
11
  // ordinary `cache.save`, so the machine is indistinguishable from a
12
- // `--download=all` run and every later reader (get, prune, verify,
12
+ // `--cache-download=all` run and every later reader (get, prune, verify,
13
13
  // metrics) sees one entry shape, not two.
14
14
 
15
15
  import {
16
16
  cleanOutputs,
17
+ committedFiles,
17
18
  resolveOutputs,
18
19
  type CacheLayer,
19
20
  type GitFilesCache,
@@ -73,7 +74,7 @@ export class DeferredOutputs {
73
74
  */
74
75
  async materializeFor(node: TaskNode): Promise<void> {
75
76
  // Nothing deferred, nothing to find: the closure walk is the size of
76
- // the graph, and every task of a run without `--download=none` paid it
77
+ // the graph, and every task of a run without `--cache-download=none` paid it
77
78
  // (1.4 s of a 3,270-task cold run's main thread).
78
79
  if (this.entries.size === 0) return
79
80
  const needed: string[] = []
@@ -116,7 +117,12 @@ export class DeferredOutputs {
116
117
  // workspace-anchored can reach this path.
117
118
  const outputs = producer.config.cache?.outputs.files ?? []
118
119
  const nestedProjectDirs = this.args.nestedDirsByProject.get(producer.projectName) ?? []
119
- const cleanArgs = { projectDir: producer.projectDir, outputs, nestedProjectDirs }
120
+ const cleanArgs = {
121
+ projectDir: producer.projectDir,
122
+ outputs,
123
+ nestedProjectDirs,
124
+ committed: committedFiles(this.args.gitFilesCache, producer.projectDir),
125
+ }
120
126
 
121
127
  // Mirror `restoreHit`'s sequencing so the two paths cannot drift: wipe
122
128
  // the declared outputs, write, then tell the git snapshot what moved.
@@ -135,14 +141,10 @@ export class DeferredOutputs {
135
141
  } catch (err) {
136
142
  const msg = err instanceof Error ? err.message : String(err)
137
143
  throw new Error(
138
- `could not fetch deferred outputs of ${taskId}: ${msg} — re-run it with --force, or use --download=all`,
144
+ `could not fetch deferred outputs of ${taskId}: ${msg} — re-run it with --force, or use --cache-download=all`,
139
145
  )
140
146
  }
141
- const outputFiles = await resolveOutputs({
142
- projectDir: producer.projectDir,
143
- outputs,
144
- nestedProjectDirs,
145
- })
147
+ const outputFiles = await resolveOutputs(cleanArgs)
146
148
  this.args.gitFilesCache?.markOutputsChanged(
147
149
  producer.projectDir,
148
150
  outputFiles.map((f) => f.slice(producer.projectDir.length + 1)),