@vzn/vx 0.0.638 → 0.0.640
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/docs/caching.md +152 -95
- package/docs/cli.md +225 -372
- package/docs/execution.md +22 -47
- package/docs/features.md +14 -16
- package/docs/patterns.md +6 -8
- package/docs/schema.md +268 -215
- package/docs/security.md +1 -1
- package/package.json +5 -5
- package/schemas/last.json +7 -4
- package/schemas/show.json +14 -14
- package/skills/vx/SKILL.md +10 -7
- package/src/cache/cache.ts +44 -11
- package/src/cache/file-hashes.ts +7 -12
- package/src/cache/fs-clock.ts +62 -0
- package/src/cache/git-inputs.ts +8 -2
- package/src/cache/index.ts +5 -2
- package/src/cache/inputs.ts +154 -83
- package/src/cache/key-fold.ts +23 -3
- package/src/cache/layer.ts +33 -18
- package/src/cache/layered-cache.ts +108 -315
- package/src/cache/linked-inputs.ts +87 -0
- package/src/cache/plugin-cache.ts +2 -10
- package/src/cache/remote-lane.ts +383 -0
- package/src/cache/run-history.ts +24 -8
- package/src/cache/run-store.ts +4 -4
- package/src/cache/schema.ts +6 -4
- package/src/cache/scoped-cache.ts +7 -0
- package/src/cli/completions.ts +2 -2
- package/src/cli/foreign-flags.ts +11 -23
- package/src/cli/help.ts +6 -31
- package/src/cli/info.ts +2 -1
- package/src/cli/init.ts +23 -11
- package/src/cli/last.ts +30 -4
- package/src/cli/plan-format.ts +5 -58
- package/src/cli/plugin-commands.ts +2 -2
- package/src/cli/plugin-templates.ts +2 -2
- package/src/cli/run.ts +23 -185
- package/src/cli/select.ts +16 -6
- package/src/cli/show.ts +15 -6
- package/src/cli/upgrade.ts +215 -55
- package/src/cli/watch-cycle.ts +2 -1
- package/src/cli/watch-filter.ts +22 -6
- package/src/cli/watch-judge.ts +49 -5
- package/src/cli/watch-set.ts +18 -8
- package/src/cli/watch.ts +103 -16
- package/src/cli/why.ts +9 -7
- package/src/config.ts +19 -21
- package/src/exec/executor.ts +5 -5
- package/src/exec/sandbox-runtime.ts +1 -1
- package/src/exec/sandbox-violations.ts +3 -1
- package/src/graph/dependency-spec.ts +2 -2
- package/src/graph/folds.ts +236 -0
- package/src/graph/index.ts +17 -2
- package/src/graph/priorities.ts +1 -1
- package/src/graph/scheduler.ts +45 -31
- package/src/graph/task-graph.ts +190 -58
- package/src/graph/task-status.ts +95 -0
- package/src/{orchestrator → history}/failure-mode.ts +1 -1
- package/src/{orchestrator → history}/history.ts +1 -1
- package/src/history/index.ts +48 -0
- package/src/{orchestrator → history}/metrics.ts +7 -20
- package/src/{orchestrator → history}/miss-reason.ts +1 -1
- package/src/index.ts +11 -29
- package/src/orchestrator/affected-tasks.ts +20 -19
- package/src/orchestrator/doctor.ts +1 -1
- package/src/orchestrator/download-policy.ts +16 -22
- package/src/orchestrator/excluded-keys.ts +4 -5
- package/src/orchestrator/execute-args.ts +80 -0
- package/src/orchestrator/execute-task.ts +50 -411
- package/src/orchestrator/fingerprint-watch.ts +2 -2
- package/src/orchestrator/index.ts +2 -73
- package/src/orchestrator/keyed-projects.ts +12 -51
- package/src/orchestrator/local-shortcircuit.ts +1 -1
- package/src/orchestrator/miss-save.ts +33 -3
- package/src/orchestrator/options.ts +1 -36
- package/src/orchestrator/output-host.ts +4 -3
- package/src/orchestrator/persistent-task.ts +321 -0
- package/src/orchestrator/persistent.ts +130 -3
- package/src/orchestrator/placement.ts +5 -5
- package/src/orchestrator/plan.ts +19 -6
- package/src/orchestrator/plugin-host.ts +129 -69
- package/src/orchestrator/plugin.ts +18 -8
- package/src/orchestrator/prepare.ts +40 -34
- package/src/orchestrator/projects.ts +7 -10
- package/src/orchestrator/remote-prefetch.ts +14 -1
- package/src/orchestrator/run-artifacts.ts +5 -177
- package/src/orchestrator/run-context.ts +55 -0
- package/src/orchestrator/run-failures.ts +15 -3
- package/src/orchestrator/run-records.ts +35 -3
- package/src/orchestrator/run-report.ts +151 -171
- package/src/orchestrator/run-settings.ts +1 -16
- package/src/orchestrator/run-steps.ts +57 -0
- package/src/orchestrator/run-stop.ts +153 -0
- package/src/orchestrator/run.ts +293 -604
- package/src/orchestrator/sandbox-fix.ts +1 -17
- package/src/orchestrator/sandbox-request.ts +2 -2
- package/src/orchestrator/signals.ts +14 -6
- package/src/orchestrator/stable-keys.ts +164 -27
- package/src/orchestrator/steps.ts +70 -0
- package/src/orchestrator/task-hash.ts +34 -25
- package/src/orchestrator/task-log-buffer.ts +1 -1
- package/src/orchestrator/task-process.ts +94 -0
- package/src/orchestrator/telemetry-host.ts +4 -4
- package/src/orchestrator/telemetry.ts +14 -86
- package/src/orchestrator/upstream.ts +4 -181
- package/src/{orchestrator → report}/events.ts +41 -8
- package/src/{orchestrator → report}/forecast.ts +35 -2
- package/src/{orchestrator → report}/framed-output.ts +40 -34
- package/src/report/index.ts +36 -0
- package/src/{orchestrator → report}/logger.ts +158 -20
- package/src/{orchestrator → report}/status-line.ts +88 -7
- package/src/{orchestrator → report}/summary.ts +126 -35
- package/src/report/tally.ts +83 -0
- package/src/settings.ts +12 -39
- package/src/util/colors.ts +44 -4
- package/src/util/git-ignored.ts +143 -0
- package/src/util/index.ts +19 -4
- package/src/util/paths.ts +1 -1
- package/src/util/report.ts +13 -5
- package/src/util/settle.ts +14 -39
- package/src/util/tail.ts +1 -1
- package/src/util/ui.ts +17 -6
- package/src/workspace/affected.ts +81 -2
- package/src/workspace/config-imports.ts +1 -1
- package/src/workspace/config-schema.ts +142 -143
- package/src/workspace/lockfile.ts +1 -1
- package/src/workspace/migration.ts +19 -3
- package/schemas/summary.json +0 -133
- package/src/cache/chained-cache.ts +0 -280
- package/src/orchestrator/tally.ts +0 -112
- /package/src/{orchestrator → report}/path-links.ts +0 -0
- /package/src/{orchestrator → report}/plain-output.ts +0 -0
- /package/src/{orchestrator → report}/terminal-signals.ts +0 -0
package/src/util/paths.ts
CHANGED
|
@@ -163,7 +163,7 @@ export function isLiteralPattern(glob: string): boolean {
|
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
/**
|
|
166
|
-
* The alphabet of a TASK glob (`
|
|
166
|
+
* The alphabet of a TASK glob (`inputs.files`, `cache.outputs.files`,
|
|
167
167
|
* `workspaceFiles`), behind {@link isLiteralPattern}, for the callers that
|
|
168
168
|
* need the POSITION of the first wildcard rather than a verdict. Every site
|
|
169
169
|
* that asks "must this declaration be matched?" reads this one regex: item
|
package/src/util/report.ts
CHANGED
|
@@ -5,16 +5,24 @@ import { paint, type ColorSupport } from './colors.js'
|
|
|
5
5
|
// are off.
|
|
6
6
|
const GRADIENT_FROM = [0xa7, 0x8b, 0xfa] as const
|
|
7
7
|
const GRADIENT_TO = [0xf4, 0x72, 0xb6] as const
|
|
8
|
-
//
|
|
9
|
-
const
|
|
8
|
+
// The bars' label column: 2 indent + 8 label + 2 gap.
|
|
9
|
+
const LABEL = 12
|
|
10
10
|
|
|
11
|
-
/**
|
|
11
|
+
/** A meter's cells: 50, or fewer so the bar fits a narrower terminal. */
|
|
12
|
+
export function barWidth(colors: ColorSupport): number {
|
|
13
|
+
const columns = colors.columns
|
|
14
|
+
return columns === undefined ? 50 : Math.max(10, Math.min(50, columns - LABEL))
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** `─ <mark> ` + dashes, flush with the bars. Shared by the run summary and every report. */
|
|
12
18
|
export function gradientRule(colors: ColorSupport, mark = 'vx'): string {
|
|
13
19
|
const head = `─ `
|
|
14
|
-
const
|
|
20
|
+
const width = LABEL + barWidth(colors)
|
|
21
|
+
const dashes = '─'.repeat(Math.max(8, width - 3 - mark.length))
|
|
15
22
|
if (!colors.enabled) return `${head}${mark} ${dashes}`
|
|
16
23
|
const chunks = 8
|
|
17
|
-
|
|
24
|
+
// The gradient spans the rule as if its mark were `vx`.
|
|
25
|
+
const per = Math.ceil((width - 5) / chunks)
|
|
18
26
|
let out = ''
|
|
19
27
|
for (let i = 0; i < chunks; i++) {
|
|
20
28
|
const seg = dashes.slice(i * per, (i + 1) * per)
|
package/src/util/settle.ts
CHANGED
|
@@ -7,54 +7,29 @@ import { parseDuration } from './size.js'
|
|
|
7
7
|
/** Default upper bound on one end-of-run flush/teardown await. */
|
|
8
8
|
const DEFAULT_TIMEOUT_MS = 3000
|
|
9
9
|
|
|
10
|
-
/** A bound the run resolved from its flag or `.vx/workspace`. */
|
|
11
|
-
export interface SettleBound {
|
|
12
|
-
readonly ms: number
|
|
13
|
-
readonly from: 'flag' | 'workspace'
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
let teardownSet: SettleBound | undefined
|
|
17
|
-
let killGraceSet: SettleBound | undefined
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* The run's `run.teardownTimeout` and `run.killGrace` from its flag or
|
|
21
|
-
* workspace (the orchestrator resolves them; util sits below settings).
|
|
22
|
-
* The variable, read per call, sits between the two, so a test drives the
|
|
23
|
-
* deadline instead of waiting it out.
|
|
24
|
-
*/
|
|
25
|
-
export function setSettleBounds(b: { teardown?: SettleBound; killGrace?: SettleBound }): void {
|
|
26
|
-
teardownSet = b.teardown
|
|
27
|
-
killGraceSet = b.killGrace
|
|
28
|
-
}
|
|
29
|
-
|
|
30
10
|
/**
|
|
31
|
-
* A
|
|
32
|
-
*
|
|
33
|
-
*
|
|
11
|
+
* A timer a test shortens: `VX_TEST_<name>`, a duration. Undocumented and
|
|
12
|
+
* in no settings table: the suites that prove a deadline drive it instead
|
|
13
|
+
* of waiting the real one out. Zero only where `zero` allows it.
|
|
34
14
|
*/
|
|
35
|
-
function
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (n !== null && (n > 0 || zero) && n <= MAX_TIMEOUT_MS) return n
|
|
41
|
-
}
|
|
42
|
-
return set?.ms
|
|
15
|
+
export function testTimerMs(name: string, defaultMs: number, zero = false): number {
|
|
16
|
+
const raw = process.env[`VX_TEST_${name}`]
|
|
17
|
+
if (raw === undefined || raw === '') return defaultMs
|
|
18
|
+
const n = parseDuration(raw)
|
|
19
|
+
return n !== null && (n > 0 || (zero && n === 0)) && n <= MAX_TIMEOUT_MS ? n : defaultMs
|
|
43
20
|
}
|
|
44
21
|
|
|
45
22
|
/**
|
|
46
|
-
* The bound on one end-of-run flush or teardown
|
|
47
|
-
*
|
|
48
|
-
* hold the run's exit hostage, so a timer past the platform's range (which
|
|
49
|
-
* would fire at once and drop every buffered record) is no value.
|
|
23
|
+
* The bound on one end-of-run flush or teardown: a plugin's flush cannot
|
|
24
|
+
* hold the run's exit hostage.
|
|
50
25
|
*/
|
|
51
26
|
export function teardownTimeoutMs(): number {
|
|
52
|
-
return
|
|
27
|
+
return testTimerMs('TEARDOWN_TIMEOUT', DEFAULT_TIMEOUT_MS, true)
|
|
53
28
|
}
|
|
54
29
|
|
|
55
30
|
/**
|
|
56
|
-
* The SIGTERM→SIGKILL grace a run grants a child that ignores SIGTERM
|
|
57
|
-
*
|
|
31
|
+
* The SIGTERM→SIGKILL grace a run grants a child that ignores SIGTERM:
|
|
32
|
+
* the one-shot timeout escalation (`exec/runner.ts`),
|
|
58
33
|
* the signal teardown (`orchestrator/signals.ts`) and the end-of-run
|
|
59
34
|
* persistent shutdown (`orchestrator/persistent.ts`) share it. Two seconds
|
|
60
35
|
* is generous for a real server's cleanup and is what every run waits when
|
|
@@ -63,7 +38,7 @@ export function teardownTimeoutMs(): number {
|
|
|
63
38
|
* 200 ms grace proves too.
|
|
64
39
|
*/
|
|
65
40
|
export function killGraceMs(defaultMs: number): number {
|
|
66
|
-
return
|
|
41
|
+
return testTimerMs('KILL_GRACE', defaultMs)
|
|
67
42
|
}
|
|
68
43
|
|
|
69
44
|
/**
|
package/src/util/tail.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// needs a cap, and the cap has to be honest — a truncated log that reads as
|
|
7
7
|
// complete is worse than one that says what it lost.
|
|
8
8
|
//
|
|
9
|
-
// `
|
|
9
|
+
// `report/logger.ts` is the sole holder: it registers a tail per
|
|
10
10
|
// persistent task at taskStart and keeps one for the rest of the run, so a
|
|
11
11
|
// single mechanism covers both the pre-ready window and everything after.
|
|
12
12
|
// `exec/runner.ts` used to keep a SECOND pre-ready copy fed from the same
|
package/src/util/ui.ts
CHANGED
|
@@ -3,14 +3,20 @@
|
|
|
3
3
|
// color, indent and spacing, so every surface shares one look. Colors
|
|
4
4
|
// are decided at print time, per stream.
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
detectColors,
|
|
8
|
+
ERROR,
|
|
9
|
+
identityColor,
|
|
10
|
+
paint,
|
|
11
|
+
SUCCESS,
|
|
12
|
+
WARN,
|
|
13
|
+
type ColorSupport,
|
|
14
|
+
} from './colors.js'
|
|
7
15
|
import { gradientRule } from './report.js'
|
|
8
16
|
|
|
9
17
|
export type Tone = 'ok' | 'warn' | 'error' | 'info'
|
|
10
18
|
|
|
11
19
|
const INFO = '#06b6d4'
|
|
12
|
-
const PROJECT = '#a78bfa'
|
|
13
|
-
const TASK = '#f472b6'
|
|
14
20
|
const HUE: Record<Tone, string> = { ok: SUCCESS, warn: WARN, error: ERROR, info: INFO }
|
|
15
21
|
const SYMBOL: Record<Tone, string> = { ok: '✓', warn: '!', error: '✗', info: '›' }
|
|
16
22
|
const INDENT = ' '
|
|
@@ -26,7 +32,7 @@ export interface Block {
|
|
|
26
32
|
readonly lines: (colors: ColorSupport) => string[]
|
|
27
33
|
}
|
|
28
34
|
|
|
29
|
-
/** A `project#task` id, in
|
|
35
|
+
/** A `project` or `project#task` id, each half in its name's hue, the `#` dim. */
|
|
30
36
|
export const id = (text: string): Inline => ({ style: 'id', text })
|
|
31
37
|
/** A path, glob or file: bold. */
|
|
32
38
|
export const path = (text: string): Inline => ({ style: 'bold', text })
|
|
@@ -48,8 +54,13 @@ function inline(x: Inline, c: ColorSupport): string {
|
|
|
48
54
|
return paint('', text, c, { bold: true })
|
|
49
55
|
case 'id': {
|
|
50
56
|
const hash = text.lastIndexOf('#')
|
|
51
|
-
if (hash < 0) return paint(
|
|
52
|
-
|
|
57
|
+
if (hash < 0) return paint(identityColor(text), text, c)
|
|
58
|
+
const task = text.slice(hash + 1)
|
|
59
|
+
return (
|
|
60
|
+
paint(identityColor(text.slice(0, hash)), text.slice(0, hash), c) +
|
|
61
|
+
paint('', '#', c, { dim: true }) +
|
|
62
|
+
paint(identityColor(task), task, c)
|
|
63
|
+
)
|
|
53
64
|
}
|
|
54
65
|
default:
|
|
55
66
|
return paint(HUE[style], text, c)
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// index + unstaged — so it captures everything you touched and nothing
|
|
7
7
|
// the base branch moved on with. Matches Turbo's `[<since>]` semantics.
|
|
8
8
|
|
|
9
|
-
import { realpath } from 'node:fs/promises'
|
|
9
|
+
import { realpath, stat } from 'node:fs/promises'
|
|
10
10
|
import path from 'node:path'
|
|
11
11
|
import {
|
|
12
12
|
executablePath,
|
|
@@ -75,7 +75,7 @@ export interface AffectedArgs {
|
|
|
75
75
|
since: string
|
|
76
76
|
projects: readonly ProjectMeta[]
|
|
77
77
|
/**
|
|
78
|
-
* Which projects declare a `
|
|
78
|
+
* Which projects declare a `inputs.workspaceFiles` glob matching one of
|
|
79
79
|
* these workspace-relative paths.
|
|
80
80
|
*
|
|
81
81
|
* A workspace-anchored glob is the documented escape hatch for inputs
|
|
@@ -107,6 +107,12 @@ export interface AffectedArgs {
|
|
|
107
107
|
* run then paid again (I-26).
|
|
108
108
|
*/
|
|
109
109
|
untracked?: () => Promise<readonly string[] | null>
|
|
110
|
+
/**
|
|
111
|
+
* The tracked symlinks, workspace-relative, from the run's enumeration
|
|
112
|
+
* (its index OIDs spell one `120000:`); null or absent spawns `git ls-files
|
|
113
|
+
* -s` here. A changed or untracked link is a changed path already.
|
|
114
|
+
*/
|
|
115
|
+
links?: () => Promise<readonly string[] | null>
|
|
110
116
|
}
|
|
111
117
|
|
|
112
118
|
export interface FingerprintClaims {
|
|
@@ -253,6 +259,16 @@ export async function affectedChanges(
|
|
|
253
259
|
const changed = [...diffed.paths, ...untracked.filter((s) => !isInstalledPath(s))].filter(
|
|
254
260
|
(s) => s !== LOCKFILE_NAME,
|
|
255
261
|
)
|
|
262
|
+
// A symlinked input keys the content behind it (cache/linked-inputs.ts),
|
|
263
|
+
// and the diff names that content's path, never the link's: a link whose
|
|
264
|
+
// target changed is a changed path itself.
|
|
265
|
+
changed.push(
|
|
266
|
+
...(await linkedChanges(
|
|
267
|
+
args.workspaceRoot,
|
|
268
|
+
(await args.links?.().catch(() => null)) ?? (await trackedLinks(args.workspaceRoot)),
|
|
269
|
+
changed,
|
|
270
|
+
)),
|
|
271
|
+
)
|
|
256
272
|
// The nested repositories git reports as one path: a gitlink, or an
|
|
257
273
|
// untracked embedded repository (`dir/`, the only path git prints so).
|
|
258
274
|
const nested = new Set(diffed.gitlinks)
|
|
@@ -503,6 +519,69 @@ async function bytesOrNull(file: string): Promise<Uint8Array | null> {
|
|
|
503
519
|
return (await f.exists()) ? await f.bytes() : null
|
|
504
520
|
}
|
|
505
521
|
|
|
522
|
+
/** The symlinks the index lists, workspace-relative. */
|
|
523
|
+
async function trackedLinks(workspaceRoot: string): Promise<string[]> {
|
|
524
|
+
const out: string[] = []
|
|
525
|
+
for (const record of await gitPaths(workspaceRoot, ['ls-files', '-s', '-z'])) {
|
|
526
|
+
if (record.startsWith('120000 ')) out.push(record.slice(record.indexOf('\t') + 1))
|
|
527
|
+
}
|
|
528
|
+
return out
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* The links in `links` whose target changed: a file `changed` names, or a
|
|
533
|
+
* directory holding one. A target git cannot diff is changed on every run:
|
|
534
|
+
* one outside the workspace, or git-ignored (or ignored files under a linked
|
|
535
|
+
* directory), since the key reads it whatever git says. A dangling link
|
|
536
|
+
* reads nothing; retargeting it is a change to the link itself.
|
|
537
|
+
*/
|
|
538
|
+
async function linkedChanges(
|
|
539
|
+
workspaceRoot: string,
|
|
540
|
+
links: readonly string[],
|
|
541
|
+
changed: readonly string[],
|
|
542
|
+
): Promise<string[]> {
|
|
543
|
+
if (links.length === 0) return []
|
|
544
|
+
const realRoot = await realpath(workspaceRoot).catch(() => workspaceRoot)
|
|
545
|
+
const out: string[] = []
|
|
546
|
+
const inside: Array<{ link: string; target: string; dir: boolean }> = []
|
|
547
|
+
for (const link of links) {
|
|
548
|
+
let real: string
|
|
549
|
+
let dir: boolean
|
|
550
|
+
try {
|
|
551
|
+
real = await realpath(path.join(workspaceRoot, link))
|
|
552
|
+
dir = (await stat(real)).isDirectory()
|
|
553
|
+
} catch {
|
|
554
|
+
continue
|
|
555
|
+
}
|
|
556
|
+
const rel = path.relative(realRoot, real)
|
|
557
|
+
if (rel.startsWith('..') || path.isAbsolute(rel)) out.push(link)
|
|
558
|
+
else inside.push({ link, target: rel.split(path.sep).join('/'), dir })
|
|
559
|
+
}
|
|
560
|
+
if (inside.length === 0) return out
|
|
561
|
+
const named = inside.map((l) => l.target).filter((t) => t !== '')
|
|
562
|
+
const ignored = await gitPaths(workspaceRoot, [
|
|
563
|
+
'ls-files',
|
|
564
|
+
'--others',
|
|
565
|
+
'--ignored',
|
|
566
|
+
'--exclude-standard',
|
|
567
|
+
'--directory',
|
|
568
|
+
'-z',
|
|
569
|
+
'--',
|
|
570
|
+
...(named.length === inside.length ? named : ['.']),
|
|
571
|
+
])
|
|
572
|
+
const changedSet = new Set(changed)
|
|
573
|
+
const under = (p: string, dir: string): boolean => dir === '' || p.startsWith(`${dir}/`)
|
|
574
|
+
for (const { link, target, dir } of inside) {
|
|
575
|
+
const unseen = ignored.some((i) => {
|
|
576
|
+
const at = i.replace(/\/$/, '')
|
|
577
|
+
return at === target || under(target, at) || (dir && under(at, target))
|
|
578
|
+
})
|
|
579
|
+
const hit = dir ? changed.some((c) => under(c, target)) : changedSet.has(target)
|
|
580
|
+
if (unseen || hit) out.push(link)
|
|
581
|
+
}
|
|
582
|
+
return out
|
|
583
|
+
}
|
|
584
|
+
|
|
506
585
|
/** Run a NUL-separated path-listing git command from the workspace root. */
|
|
507
586
|
async function gitPaths(workspaceRoot: string, cmd: string[]): Promise<string[]> {
|
|
508
587
|
const proc = spawnGit([...cmd], workspaceRoot)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// The third `changed file → project` channel for `--affected`.
|
|
2
2
|
//
|
|
3
3
|
// Directory containment answers "which project owns this file", and
|
|
4
|
-
// `
|
|
4
|
+
// `inputs.workspaceFiles` answers "which project declared it". Neither
|
|
5
5
|
// can see the one remaining way a file reaches a task: a project's
|
|
6
6
|
// `vx.config.*` IMPORTS it. Resolved-config hashing folds the imported values
|
|
7
7
|
// into the cache key (architecture principle #4), so editing such a file
|