@vzn/vx 0.0.638 → 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 +44 -15
- package/docs/cli.md +168 -345
- package/docs/execution.md +18 -43
- package/docs/features.md +7 -10
- package/docs/patterns.md +5 -7
- package/docs/schema.md +47 -49
- package/package.json +5 -5
- package/schemas/last.json +7 -4
- package/skills/vx/SKILL.md +6 -4
- package/src/cache/cache.ts +40 -10
- package/src/cache/git-inputs.ts +6 -0
- package/src/cache/index.ts +2 -2
- package/src/cache/layer.ts +25 -11
- package/src/cache/layered-cache.ts +108 -315
- 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 +10 -22
- package/src/cli/help.ts +6 -31
- package/src/cli/info.ts +2 -1
- package/src/cli/last.ts +6 -3
- package/src/cli/plan-format.ts +5 -58
- package/src/cli/plugin-commands.ts +2 -2
- package/src/cli/run.ts +23 -185
- package/src/cli/show.ts +6 -0
- package/src/cli/upgrade.ts +123 -39
- package/src/cli/watch-cycle.ts +2 -1
- package/src/cli/watch.ts +29 -14
- package/src/cli/why.ts +4 -4
- 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 +4 -16
- package/src/graph/task-graph.ts +87 -17
- 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 +10 -28
- package/src/orchestrator/doctor.ts +1 -1
- package/src/orchestrator/execute-args.ts +80 -0
- package/src/orchestrator/execute-task.ts +21 -386
- package/src/orchestrator/fingerprint-watch.ts +2 -2
- package/src/orchestrator/index.ts +1 -73
- 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 +67 -47
- package/src/orchestrator/plugin.ts +6 -6
- package/src/orchestrator/prepare.ts +28 -29
- 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 +81 -179
- package/src/orchestrator/run-settings.ts +1 -16
- package/src/orchestrator/run-stop.ts +153 -0
- package/src/orchestrator/run.ts +260 -601
- 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 +5 -82
- package/src/{orchestrator → report}/events.ts +41 -8
- package/src/{orchestrator → report}/forecast.ts +35 -2
- package/src/{orchestrator → report}/framed-output.ts +39 -33
- package/src/report/index.ts +36 -0
- package/src/{orchestrator → report}/logger.ts +158 -20
- package/src/{orchestrator → report}/status-line.ts +84 -7
- package/src/{orchestrator → report}/summary.ts +126 -35
- package/src/report/tally.ts +83 -0
- package/src/settings.ts +1 -22
- 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/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/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
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// A task's terminal status and what each one means: a pass, a hit, where
|
|
2
|
+
// its result came from. One vocabulary every reader (the run verdict, the
|
|
3
|
+
// logger, the history queries, telemetry, plugins) asks, so none re-lists
|
|
4
|
+
// the statuses.
|
|
5
|
+
|
|
6
|
+
export type TaskStatus =
|
|
7
|
+
| 'success'
|
|
8
|
+
| 'cache-hit'
|
|
9
|
+
| 'cache-hit-remote'
|
|
10
|
+
| 'failed'
|
|
11
|
+
| 'skipped'
|
|
12
|
+
// A child killed by a shutdown signal (SIGINT/SIGTERM — e.g. Ctrl-C):
|
|
13
|
+
// the task never finished on its own terms, so it reverts to aborted —
|
|
14
|
+
// excluded from the tally and the run history. It still PROPAGATES like
|
|
15
|
+
// a failure (see `willSkip`): the child died mid-write, so its outputs
|
|
16
|
+
// are partial.
|
|
17
|
+
| 'aborted'
|
|
18
|
+
|
|
19
|
+
/** Where a task's result came from, derived ONCE in core from the status. */
|
|
20
|
+
export type CacheSource = 'miss' | 'local' | 'remote' | 'none'
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Map a task outcome status to its cache source. `success`/`failed` ran
|
|
24
|
+
* (a miss as far as the cache read is concerned); the two cache-hit
|
|
25
|
+
* statuses restored from local/remote; `skipped`/`aborted` never engaged
|
|
26
|
+
* the cache. Pure function — the single place this mapping is decided;
|
|
27
|
+
* `isCacheHit` below is derived from it rather than re-listing the two
|
|
28
|
+
* hit statuses, so there is one decision, not two that can disagree.
|
|
29
|
+
*/
|
|
30
|
+
export function deriveCacheSource(status: TaskStatus): CacheSource {
|
|
31
|
+
switch (status) {
|
|
32
|
+
case 'cache-hit':
|
|
33
|
+
return 'local'
|
|
34
|
+
case 'cache-hit-remote':
|
|
35
|
+
return 'remote'
|
|
36
|
+
case 'success':
|
|
37
|
+
case 'failed':
|
|
38
|
+
return 'miss'
|
|
39
|
+
case 'skipped':
|
|
40
|
+
case 'aborted':
|
|
41
|
+
return 'none'
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Whether each status counts as a PASS. Written as a `Record<TaskStatus, …>`
|
|
47
|
+
* rather than a `new Set([...])` on purpose: a Record must name every member,
|
|
48
|
+
* so adding a status to the union is a COMPILE error HERE and the omission
|
|
49
|
+
* cannot ship. A Set of string literals has no such tripwire — it silently
|
|
50
|
+
* answers `false` for the new member, which is how ten hand-rolled copies of
|
|
51
|
+
* this vocabulary accumulated across core, cloud and the dashboard.
|
|
52
|
+
*/
|
|
53
|
+
const PASSES: Record<TaskStatus, boolean> = {
|
|
54
|
+
success: true,
|
|
55
|
+
'cache-hit': true,
|
|
56
|
+
'cache-hit-remote': true,
|
|
57
|
+
failed: false,
|
|
58
|
+
skipped: false,
|
|
59
|
+
aborted: false,
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Every `TaskStatus`, at runtime. Read off `PASSES`'s keys, which the
|
|
64
|
+
* `Record<TaskStatus, …>` type guarantees is exactly the union — so this is
|
|
65
|
+
* derived, not a second list that can fall behind. Exported so a consumer that
|
|
66
|
+
* cannot import the type (a test asserting a copy of this vocabulary in
|
|
67
|
+
* another package agrees) can still enumerate the real set.
|
|
68
|
+
*/
|
|
69
|
+
export const TASK_STATUSES: readonly TaskStatus[] = Object.keys(PASSES) as TaskStatus[]
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Did the task pass? A cache hit counts — it produced the same result without
|
|
73
|
+
* spending the time, which is the whole point. `skipped` and `aborted` do NOT:
|
|
74
|
+
* neither finished on its own terms, so neither can vouch for anything.
|
|
75
|
+
*
|
|
76
|
+
* Takes `string`, not `TaskStatus`, because most callers hold a status that
|
|
77
|
+
* arrived over a wire or out of a database column. An unrecognised string
|
|
78
|
+
* reads as NOT passing — the safe direction, since the alternative is calling
|
|
79
|
+
* a run green on a status this build has never heard of.
|
|
80
|
+
*/
|
|
81
|
+
export function isPassStatus(status: string): boolean {
|
|
82
|
+
return PASSES[status as TaskStatus] === true
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Did the task's result come out of the cache (either layer)? Derived from
|
|
87
|
+
* `deriveCacheSource` rather than re-listing the two hit statuses, so the two
|
|
88
|
+
* cannot disagree about what a hit is. Unknown strings read as not-a-hit.
|
|
89
|
+
*/
|
|
90
|
+
export function isCacheHit(status: string): boolean {
|
|
91
|
+
// No known-status guard: an unknown string falls through the switch to
|
|
92
|
+
// undefined, which is neither hit source (the guard survived item 654).
|
|
93
|
+
const source = deriveCacheSource(status as TaskStatus)
|
|
94
|
+
return source === 'local' || source === 'remote'
|
|
95
|
+
}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
import type { Database } from 'bun:sqlite'
|
|
12
12
|
import { KEYED_RUNS_SQL } from '../cache/index.js'
|
|
13
|
-
import { isCacheHit, TASK_STATUSES } from '
|
|
13
|
+
import { isCacheHit, TASK_STATUSES } from '../graph/index.js'
|
|
14
14
|
|
|
15
15
|
// SQL hit set derived from the predicate — never a hand-typed list and never
|
|
16
16
|
// a prefix LIKE (a prefix counts any status merely NAMED cache-hit-*). The
|
|
@@ -16,7 +16,7 @@ import { branchSource, EXECUTED_RUNS_SQL } from '../cache/index.js'
|
|
|
16
16
|
import { splitTaskId } from '../util/index.js'
|
|
17
17
|
import { failureModeOf, mixedOutcomeKeysSql } from './failure-mode.js'
|
|
18
18
|
import type { FailureMode } from './failure-mode.js'
|
|
19
|
-
import { isPassStatus, TASK_STATUSES } from '
|
|
19
|
+
import { isPassStatus, TASK_STATUSES } from '../graph/index.js'
|
|
20
20
|
|
|
21
21
|
const DEFAULT_RECENT = 50
|
|
22
22
|
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// What runs leave behind, read back: the duration table a schedule learns
|
|
2
|
+
// from, the flakiness verdict, the queries behind `vx last` / `vx why`, and
|
|
3
|
+
// what a miss's key changed. Read-only SQL over the cache's database; it
|
|
4
|
+
// never runs, renders or writes a run.
|
|
5
|
+
export {
|
|
6
|
+
EmptyHistoryProvider,
|
|
7
|
+
type HistoryProvider,
|
|
8
|
+
type HistoryTable,
|
|
9
|
+
LocalHistoryProvider,
|
|
10
|
+
PASS_STATUSES,
|
|
11
|
+
type TaskHistory,
|
|
12
|
+
type TaskResources,
|
|
13
|
+
} from './history.js'
|
|
14
|
+
export {
|
|
15
|
+
detectFlaky,
|
|
16
|
+
type FailureMode,
|
|
17
|
+
type FlakyCandidate,
|
|
18
|
+
type FlakyFinding,
|
|
19
|
+
type FlakyTask,
|
|
20
|
+
flakyTasks,
|
|
21
|
+
} from './failure-mode.js'
|
|
22
|
+
export {
|
|
23
|
+
cacheKeyDiff,
|
|
24
|
+
explainCacheKey,
|
|
25
|
+
getInvocation,
|
|
26
|
+
getRun,
|
|
27
|
+
inputWriters,
|
|
28
|
+
latestRunId,
|
|
29
|
+
listInvocations,
|
|
30
|
+
listRuns,
|
|
31
|
+
rootCauses,
|
|
32
|
+
whyDidThisRerun,
|
|
33
|
+
} from './metrics.js'
|
|
34
|
+
export type {
|
|
35
|
+
CacheEntryRow,
|
|
36
|
+
CacheKeyDiff,
|
|
37
|
+
CacheKeyExplanation,
|
|
38
|
+
InputDiffEntry,
|
|
39
|
+
InputWriter,
|
|
40
|
+
InvocationDetail,
|
|
41
|
+
ListInvocationsArgs,
|
|
42
|
+
ListRunsArgs,
|
|
43
|
+
RootCause,
|
|
44
|
+
RunDetail,
|
|
45
|
+
RunSummaryRow,
|
|
46
|
+
WhyDidThisRerun,
|
|
47
|
+
} from './metrics.js'
|
|
48
|
+
export { createMissExplainer, type MissExplainer } from './miss-reason.js'
|
|
@@ -15,11 +15,9 @@ import { statSync } from 'node:fs'
|
|
|
15
15
|
import path from 'node:path'
|
|
16
16
|
// The keyed-run predicate lives beside the schema in cache/cache.ts, so a
|
|
17
17
|
// rule written once cannot drift.
|
|
18
|
-
import { KEYED_RUNS_SQL } from '../cache/index.js'
|
|
19
|
-
import { splitTaskId } from '../graph/index.js'
|
|
18
|
+
import { KEYED_RUNS_SQL, type TaskInputComponent } from '../cache/index.js'
|
|
19
|
+
import { isPassStatus, splitTaskId } from '../graph/index.js'
|
|
20
20
|
import { clampInt, MASKED, secretNamed } from '../util/index.js'
|
|
21
|
-
import type { TaskInputComponent } from './task-hash.js'
|
|
22
|
-
import { isPassStatus } from './telemetry.js'
|
|
23
21
|
|
|
24
22
|
// A recorded `cache_policy` is the `--cache-policy` spec (`local:rw,remote:r`).
|
|
25
23
|
const readsCache = (spec: string): boolean => /(?:^|,)(?:local|remote):w?r/.test(spec)
|
|
@@ -57,6 +55,8 @@ export interface RunSummaryRow {
|
|
|
57
55
|
notReady: 'timeout' | 'exited' | 'spawn' | null
|
|
58
56
|
/** The task ran with the run's forwarded args (`-- …`): a requested one under a run that had some. */
|
|
59
57
|
forwarded: boolean
|
|
58
|
+
/** Each remote read or write of its key that failed, `upload: <message>`, `; `-joined (v40). */
|
|
59
|
+
remoteError: string | null
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
export interface ListRunsArgs {
|
|
@@ -117,7 +117,7 @@ export function listRuns(db: Database, args: ListRunsArgs = {}): RunSummaryRow[]
|
|
|
117
117
|
wallclock_start_ns AS wallclockStartNs, wallclock_end_ns AS wallclockEndNs,
|
|
118
118
|
blocked_by AS blockedBy, timed_out AS timedOut,
|
|
119
119
|
sandbox_violations AS sandboxViolations, not_ready AS notReady,
|
|
120
|
-
forward_args IS NOT NULL AS forwarded
|
|
120
|
+
forward_args IS NOT NULL AS forwarded, remote_error AS remoteError
|
|
121
121
|
FROM runs ${clause} ORDER BY id DESC LIMIT ?`,
|
|
122
122
|
)
|
|
123
123
|
.all(...params, limit) as RawRow[]
|
|
@@ -135,8 +135,7 @@ export function listRuns(db: Database, args: ListRunsArgs = {}): RunSummaryRow[]
|
|
|
135
135
|
|
|
136
136
|
/**
|
|
137
137
|
* One `vx run` invocation header — every `invocations` column, camelCased,
|
|
138
|
-
* `
|
|
139
|
-
* surfaced as booleans. The SPA's
|
|
138
|
+
* `dirty`/`ci`/`exitOk` surfaced as booleans. The SPA's
|
|
140
139
|
* existing fields (runId / startedAt / endedAt / taskCount / failedCount /
|
|
141
140
|
* hitCount / totalDurationMs) are all present, so existing views keep working.
|
|
142
141
|
*/
|
|
@@ -170,7 +169,6 @@ export interface InvocationDetail {
|
|
|
170
169
|
os: string | null
|
|
171
170
|
arch: string | null
|
|
172
171
|
vxVersion: string
|
|
173
|
-
tags: Record<string, string>
|
|
174
172
|
}
|
|
175
173
|
|
|
176
174
|
interface InvocationRawRow {
|
|
@@ -201,7 +199,6 @@ interface InvocationRawRow {
|
|
|
201
199
|
os: string | null
|
|
202
200
|
arch: string | null
|
|
203
201
|
vxVersion: string
|
|
204
|
-
tags: string
|
|
205
202
|
}
|
|
206
203
|
|
|
207
204
|
const INVOCATION_COLUMNS = `
|
|
@@ -214,7 +211,7 @@ const INVOCATION_COLUMNS = `
|
|
|
214
211
|
restored_remote_count AS restoredRemoteCount,
|
|
215
212
|
exit_ok AS exitOk,
|
|
216
213
|
commit_sha AS commitSha, branch, dirty, ci, ci_provider AS ciProvider,
|
|
217
|
-
host, os, arch, vx_version AS vxVersion
|
|
214
|
+
host, os, arch, vx_version AS vxVersion`
|
|
218
215
|
|
|
219
216
|
function mapInvocation(r: InvocationRawRow): InvocationDetail {
|
|
220
217
|
let requestedTasks: string[] = []
|
|
@@ -222,15 +219,6 @@ function mapInvocation(r: InvocationRawRow): InvocationDetail {
|
|
|
222
219
|
const parsed = JSON.parse(r.requestedTasks) as unknown
|
|
223
220
|
if (Array.isArray(parsed)) requestedTasks = parsed.map(String)
|
|
224
221
|
} catch {}
|
|
225
|
-
let tags: Record<string, string> = {}
|
|
226
|
-
try {
|
|
227
|
-
const parsed = JSON.parse(r.tags) as unknown
|
|
228
|
-
if (parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
229
|
-
tags = Object.fromEntries(
|
|
230
|
-
Object.entries(parsed as Record<string, unknown>).map(([k, v]) => [k, String(v)]),
|
|
231
|
-
)
|
|
232
|
-
}
|
|
233
|
-
} catch {}
|
|
234
222
|
return {
|
|
235
223
|
runId: r.runId,
|
|
236
224
|
command: r.command,
|
|
@@ -259,7 +247,6 @@ function mapInvocation(r: InvocationRawRow): InvocationDetail {
|
|
|
259
247
|
os: r.os,
|
|
260
248
|
arch: r.arch,
|
|
261
249
|
vxVersion: r.vxVersion,
|
|
262
|
-
tags,
|
|
263
250
|
}
|
|
264
251
|
}
|
|
265
252
|
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import type { Database } from 'bun:sqlite'
|
|
10
10
|
import type { InputChanges } from '../graph/index.js'
|
|
11
11
|
import { diffKeyComponents, loadEntryInputs } from './metrics.js'
|
|
12
|
-
import type { TaskInputComponent } from '
|
|
12
|
+
import type { TaskInputComponent } from '../cache/index.js'
|
|
13
13
|
|
|
14
14
|
/** How many changed components a miss names; `count` says how many there were. */
|
|
15
15
|
const NAMED = 10
|
package/src/index.ts
CHANGED
|
@@ -56,13 +56,9 @@ export { run, planRun, prepareRun } from './orchestrator/index.js'
|
|
|
56
56
|
// The per-task duration history a `schedule` plugin learns from
|
|
57
57
|
// (`@vzn/vx-schedule-history` does; a plugin package reaches core only
|
|
58
58
|
// through this façade, which is what put these here).
|
|
59
|
-
export { currentBranch
|
|
60
|
-
export
|
|
61
|
-
|
|
62
|
-
HistoryTable,
|
|
63
|
-
TaskHistory,
|
|
64
|
-
TaskResources,
|
|
65
|
-
} from './orchestrator/index.js'
|
|
59
|
+
export { currentBranch } from './orchestrator/index.js'
|
|
60
|
+
export { LocalHistoryProvider } from './history/index.js'
|
|
61
|
+
export type { HistoryProvider, HistoryTable, TaskHistory, TaskResources } from './history/index.js'
|
|
66
62
|
export type { PreparedRun } from './orchestrator/index.js'
|
|
67
63
|
// `RunOptions.affected`: what `--affected`'s diff changed, per project.
|
|
68
64
|
export type { AffectedChanges } from './workspace/index.js'
|
|
@@ -72,14 +68,8 @@ export type { AffectedChanges } from './workspace/index.js'
|
|
|
72
68
|
// docs/cli.md § Programmatic API listed all three (item 387, 2026-09-19).
|
|
73
69
|
export type { RunPlan, PlannedTask } from './orchestrator/index.js'
|
|
74
70
|
export type { CiContext, GitContext, HostContext, WorkspaceIdentity } from './orchestrator/index.js'
|
|
75
|
-
export type {
|
|
76
|
-
|
|
77
|
-
CachePolicy,
|
|
78
|
-
Logger,
|
|
79
|
-
OutputView,
|
|
80
|
-
RunOptions,
|
|
81
|
-
RunSummary,
|
|
82
|
-
} from './orchestrator/index.js'
|
|
71
|
+
export type { CacheFlags, CachePolicy, RunOptions, RunSummary } from './orchestrator/index.js'
|
|
72
|
+
export type { Logger, OutputView } from './report/index.js'
|
|
83
73
|
// `splitTaskId` is on the façade because the alternative is what happened:
|
|
84
74
|
// with only `taskId()` to JOIN an id and nothing exported to SPLIT one,
|
|
85
75
|
// consumers roll their own `split('#', 2)` and drift from the graph, which
|
|
@@ -178,8 +168,8 @@ export type {
|
|
|
178
168
|
RunReportTask,
|
|
179
169
|
PluginHookName,
|
|
180
170
|
PluginHookHandlers,
|
|
181
|
-
RunStartInfo,
|
|
182
171
|
} from './orchestrator/index.js'
|
|
172
|
+
export type { RunStartInfo } from './report/index.js'
|
|
183
173
|
// The per-task execution contract a plugin's `executor` capability returns.
|
|
184
174
|
// (`runCommand` / `runSandboxed`, the local executor's own primitives, left
|
|
185
175
|
// the façade 2026-09-10: no executor plugin built on them — `@vzn/vx-reapi`
|
|
@@ -211,15 +201,8 @@ export type {
|
|
|
211
201
|
// consumer rolls its own Set of status literals — and a Set has no
|
|
212
202
|
// compile-time tripwire when the union gains a member, so it silently answers
|
|
213
203
|
// "no" for the new one.
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
// task names core does, and the cloud job summary shipped without the escape.
|
|
217
|
-
export {
|
|
218
|
-
escapeMarkdownCell,
|
|
219
|
-
TELEMETRY_SCHEMA_VERSION,
|
|
220
|
-
isCacheHit,
|
|
221
|
-
isPassStatus,
|
|
222
|
-
} from './orchestrator/index.js'
|
|
204
|
+
export { type CacheSource, isCacheHit, isPassStatus } from './graph/index.js'
|
|
205
|
+
export { TELEMETRY_SCHEMA_VERSION } from './orchestrator/index.js'
|
|
223
206
|
// `TaskLogBuffer` is on the façade on the same demonstrated need: EVERY
|
|
224
207
|
// telemetry sink that ships build output has to bound it, and the retention
|
|
225
208
|
// rules (per-task tail, per-run budget, failures never evicted by successes,
|
|
@@ -231,7 +214,6 @@ export { LOG_WIRE_VERSION, TaskLogBuffer } from './orchestrator/index.js'
|
|
|
231
214
|
// say `128 + SIGKILL`; the decode is the runner's convention, one copy.
|
|
232
215
|
export { exitSignal } from './orchestrator/index.js'
|
|
233
216
|
export type {
|
|
234
|
-
CacheSource,
|
|
235
217
|
RunContextRecord,
|
|
236
218
|
RunSummaryRecord,
|
|
237
219
|
TaskLogBundle,
|
|
@@ -242,5 +224,5 @@ export type {
|
|
|
242
224
|
TelemetrySink,
|
|
243
225
|
} from './orchestrator/index.js'
|
|
244
226
|
|
|
245
|
-
// The serializable
|
|
246
|
-
export type {
|
|
227
|
+
// The serializable per-task views.
|
|
228
|
+
export type { TaskView, OutcomeView } from './report/index.js'
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
resolveCacheDir,
|
|
31
31
|
resolveStoreRoot,
|
|
32
32
|
} from '../workspace/index.js'
|
|
33
|
-
import { flakyTasks, type FlakyTask } from '
|
|
33
|
+
import { flakyTasks, type FlakyTask } from '../history/index.js'
|
|
34
34
|
import type { VxPlugin } from './plugin.js'
|
|
35
35
|
import { discoverProjects, loadProjects, loadWorkspacePlugins, lockReader } from './projects.js'
|
|
36
36
|
import { atPath, envName, resolveSetting, SETTINGS, type SettingSource } from '../settings.js'
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// What a dispatch hands executeTask: the run's half, built once, and the
|
|
2
|
+
// task's half, looked up per dispatch. The run's half is one fixed-shape
|
|
3
|
+
// object every task spreads; the task's optional fields are assigned only
|
|
4
|
+
// when present: twelve conditional spreads per task cost ~18 ms of object
|
|
5
|
+
// copying on a 1,090-package warm run (X-149).
|
|
6
|
+
|
|
7
|
+
import type { TaskNode, TaskOutcome } from '../graph/index.js'
|
|
8
|
+
import type { Logger } from '../report/index.js'
|
|
9
|
+
import type { DownloadMode } from './download-policy.js'
|
|
10
|
+
import type { ExecuteArgs } from './execute-task.js'
|
|
11
|
+
import type { ShortCircuit } from './local-shortcircuit.js'
|
|
12
|
+
import { UNPLACED_EXECUTOR, type Placements } from './placement.js'
|
|
13
|
+
|
|
14
|
+
type TaskField =
|
|
15
|
+
| 'node'
|
|
16
|
+
| 'upstream'
|
|
17
|
+
| 'log'
|
|
18
|
+
| 'executor'
|
|
19
|
+
| 'nestedProjectDirs'
|
|
20
|
+
| 'remoteOnlyNoop'
|
|
21
|
+
| 'remoteOnly'
|
|
22
|
+
| 'download'
|
|
23
|
+
| 'preProbed'
|
|
24
|
+
| 'upfrontKey'
|
|
25
|
+
| 'upfrontGroupKey'
|
|
26
|
+
| 'taintedUpstream'
|
|
27
|
+
| 'noDependants'
|
|
28
|
+
| 'terminal'
|
|
29
|
+
|
|
30
|
+
/** What every task of a run shares; an absent optional stays absent. */
|
|
31
|
+
export type RunScope = Omit<ExecuteArgs, TaskField>
|
|
32
|
+
|
|
33
|
+
/** Where the task's half is read from, per task id. */
|
|
34
|
+
export interface TaskFacts {
|
|
35
|
+
log: (node: TaskNode) => Logger
|
|
36
|
+
placements: Placements
|
|
37
|
+
nestedDirsByProject: ReadonlyMap<string, string[]>
|
|
38
|
+
downloadModeOf: ReadonlyMap<string, DownloadMode>
|
|
39
|
+
shortCircuit: Pick<ShortCircuit, 'preProbed' | 'uncachedKeys' | 'groupKeys'>
|
|
40
|
+
/** Tasks some other task depends on. */
|
|
41
|
+
dependedOn: ReadonlySet<string>
|
|
42
|
+
/** Tasks that hold the terminal (`terminalHolders`). */
|
|
43
|
+
holders: ReadonlySet<string>
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The dispatch builder. `reuseProbe: false` takes the task's key again
|
|
48
|
+
* instead of the up-front probe (admission's retry after a dropped probe);
|
|
49
|
+
* `tainted` is the run's taint verdict for this dispatch.
|
|
50
|
+
*/
|
|
51
|
+
export function executeArgsOf(
|
|
52
|
+
scope: RunScope,
|
|
53
|
+
facts: TaskFacts,
|
|
54
|
+
): (node: TaskNode, upstream: TaskOutcome[], reuseProbe: boolean, tainted: boolean) => ExecuteArgs {
|
|
55
|
+
const { placements, shortCircuit } = facts
|
|
56
|
+
return (node, upstream, reuseProbe, tainted) => {
|
|
57
|
+
const id = node.id
|
|
58
|
+
const a: ExecuteArgs = {
|
|
59
|
+
...scope,
|
|
60
|
+
node,
|
|
61
|
+
upstream,
|
|
62
|
+
log: facts.log(node),
|
|
63
|
+
executor: placements.executors.get(id) ?? UNPLACED_EXECUTOR,
|
|
64
|
+
nestedProjectDirs: facts.nestedDirsByProject.get(node.projectName) ?? [],
|
|
65
|
+
}
|
|
66
|
+
const probe = reuseProbe ? shortCircuit.preProbed.get(id) : undefined
|
|
67
|
+
const upfrontKey = shortCircuit.uncachedKeys.get(id)
|
|
68
|
+
const upfrontGroupKey = shortCircuit.groupKeys.get(id)
|
|
69
|
+
if (facts.downloadModeOf.get(id) === 'deferred') a.download = 'deferred'
|
|
70
|
+
if (placements.remoteOnlyNoop.has(id)) a.remoteOnlyNoop = true
|
|
71
|
+
if (placements.remoteOnly.has(id)) a.remoteOnly = true
|
|
72
|
+
if (probe !== undefined) a.preProbed = probe
|
|
73
|
+
if (upfrontKey !== undefined) a.upfrontKey = upfrontKey
|
|
74
|
+
if (upfrontGroupKey !== undefined) a.upfrontGroupKey = upfrontGroupKey
|
|
75
|
+
if (tainted) a.taintedUpstream = true
|
|
76
|
+
if (!facts.dependedOn.has(id)) a.noDependants = true
|
|
77
|
+
if (facts.holders.has(id)) a.terminal = true
|
|
78
|
+
return a
|
|
79
|
+
}
|
|
80
|
+
}
|