@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.
Files changed (103) hide show
  1. package/docs/caching.md +44 -15
  2. package/docs/cli.md +168 -345
  3. package/docs/execution.md +18 -43
  4. package/docs/features.md +7 -10
  5. package/docs/patterns.md +5 -7
  6. package/docs/schema.md +47 -49
  7. package/package.json +5 -5
  8. package/schemas/last.json +7 -4
  9. package/skills/vx/SKILL.md +6 -4
  10. package/src/cache/cache.ts +40 -10
  11. package/src/cache/git-inputs.ts +6 -0
  12. package/src/cache/index.ts +2 -2
  13. package/src/cache/layer.ts +25 -11
  14. package/src/cache/layered-cache.ts +108 -315
  15. package/src/cache/plugin-cache.ts +2 -10
  16. package/src/cache/remote-lane.ts +383 -0
  17. package/src/cache/run-history.ts +24 -8
  18. package/src/cache/run-store.ts +4 -4
  19. package/src/cache/schema.ts +6 -4
  20. package/src/cache/scoped-cache.ts +7 -0
  21. package/src/cli/completions.ts +2 -2
  22. package/src/cli/foreign-flags.ts +10 -22
  23. package/src/cli/help.ts +6 -31
  24. package/src/cli/info.ts +2 -1
  25. package/src/cli/last.ts +6 -3
  26. package/src/cli/plan-format.ts +5 -58
  27. package/src/cli/plugin-commands.ts +2 -2
  28. package/src/cli/run.ts +23 -185
  29. package/src/cli/show.ts +6 -0
  30. package/src/cli/upgrade.ts +123 -39
  31. package/src/cli/watch-cycle.ts +2 -1
  32. package/src/cli/watch.ts +29 -14
  33. package/src/cli/why.ts +4 -4
  34. package/src/config.ts +0 -6
  35. package/src/exec/executor.ts +1 -1
  36. package/src/exec/sandbox-runtime.ts +1 -1
  37. package/src/exec/sandbox-violations.ts +3 -1
  38. package/src/graph/index.ts +9 -1
  39. package/src/graph/priorities.ts +1 -1
  40. package/src/graph/scheduler.ts +4 -16
  41. package/src/graph/task-graph.ts +87 -17
  42. package/src/graph/task-status.ts +95 -0
  43. package/src/{orchestrator → history}/failure-mode.ts +1 -1
  44. package/src/{orchestrator → history}/history.ts +1 -1
  45. package/src/history/index.ts +48 -0
  46. package/src/{orchestrator → history}/metrics.ts +7 -20
  47. package/src/{orchestrator → history}/miss-reason.ts +1 -1
  48. package/src/index.ts +10 -28
  49. package/src/orchestrator/doctor.ts +1 -1
  50. package/src/orchestrator/execute-args.ts +80 -0
  51. package/src/orchestrator/execute-task.ts +21 -386
  52. package/src/orchestrator/fingerprint-watch.ts +2 -2
  53. package/src/orchestrator/index.ts +1 -73
  54. package/src/orchestrator/miss-save.ts +33 -3
  55. package/src/orchestrator/options.ts +1 -36
  56. package/src/orchestrator/output-host.ts +4 -3
  57. package/src/orchestrator/persistent-task.ts +321 -0
  58. package/src/orchestrator/persistent.ts +130 -3
  59. package/src/orchestrator/placement.ts +3 -3
  60. package/src/orchestrator/plan.ts +18 -5
  61. package/src/orchestrator/plugin-host.ts +67 -47
  62. package/src/orchestrator/plugin.ts +6 -6
  63. package/src/orchestrator/prepare.ts +28 -29
  64. package/src/orchestrator/remote-prefetch.ts +14 -1
  65. package/src/orchestrator/run-artifacts.ts +5 -177
  66. package/src/orchestrator/run-context.ts +55 -0
  67. package/src/orchestrator/run-failures.ts +15 -3
  68. package/src/orchestrator/run-records.ts +35 -3
  69. package/src/orchestrator/run-report.ts +81 -179
  70. package/src/orchestrator/run-settings.ts +1 -16
  71. package/src/orchestrator/run-stop.ts +153 -0
  72. package/src/orchestrator/run.ts +260 -601
  73. package/src/orchestrator/sandbox-fix.ts +0 -16
  74. package/src/orchestrator/signals.ts +14 -6
  75. package/src/orchestrator/stable-keys.ts +107 -3
  76. package/src/orchestrator/steps.ts +50 -0
  77. package/src/orchestrator/task-hash.ts +3 -16
  78. package/src/orchestrator/task-log-buffer.ts +1 -1
  79. package/src/orchestrator/task-process.ts +94 -0
  80. package/src/orchestrator/telemetry-host.ts +1 -1
  81. package/src/orchestrator/telemetry.ts +5 -82
  82. package/src/{orchestrator → report}/events.ts +41 -8
  83. package/src/{orchestrator → report}/forecast.ts +35 -2
  84. package/src/{orchestrator → report}/framed-output.ts +39 -33
  85. package/src/report/index.ts +36 -0
  86. package/src/{orchestrator → report}/logger.ts +158 -20
  87. package/src/{orchestrator → report}/status-line.ts +84 -7
  88. package/src/{orchestrator → report}/summary.ts +126 -35
  89. package/src/report/tally.ts +83 -0
  90. package/src/settings.ts +1 -22
  91. package/src/util/colors.ts +44 -4
  92. package/src/util/git-ignored.ts +143 -0
  93. package/src/util/index.ts +19 -4
  94. package/src/util/report.ts +13 -5
  95. package/src/util/settle.ts +14 -39
  96. package/src/util/tail.ts +1 -1
  97. package/src/util/ui.ts +17 -6
  98. package/schemas/summary.json +0 -133
  99. package/src/cache/chained-cache.ts +0 -280
  100. package/src/orchestrator/tally.ts +0 -112
  101. /package/src/{orchestrator → report}/path-links.ts +0 -0
  102. /package/src/{orchestrator → report}/plain-output.ts +0 -0
  103. /package/src/{orchestrator → report}/terminal-signals.ts +0 -0
@@ -0,0 +1,143 @@
1
+ // Whether git ignores everything an output glob can write (M, 2026-10-15):
2
+ // a key reads only what git lists, so such an output never reaches a
3
+ // reader's key. Asked of git, never read from a `.gitignore`: git alone
4
+ // knows every rule source (nested files, `info/exclude`, `core.excludesFile`)
5
+ // and that a directory holding a tracked file is not ignored.
6
+
7
+ import { gitEnv } from './hide-env.js'
8
+ import { isLiteralPattern, normalizeGlob } from './paths.js'
9
+ import { executablePath } from './which.js'
10
+
11
+ /** A directory name no ignore rule names, standing for one a task may create. */
12
+ const FRESH = '__vx_probe__'
13
+
14
+ /**
15
+ * The paths git is asked about for a root-relative output glob: when git
16
+ * ignores each, it ignores every untracked path the glob can match. `deep`:
17
+ * the glob reaches below them (a `dist` at any depth is asked as `dist/` and
18
+ * a fresh `__vx_probe__/dist/`), so a listed file it matches must be looked
19
+ * for too (a tracked one, or one a `!` rule takes back). Null when no few paths
20
+ * stand for the glob (`*.js`): undecided, the output overlaps as before.
21
+ * `dir`: the glob's literal directory, where a listed file it matches lies.
22
+ */
23
+ export function ignoreProbes(glob: string): { paths: string[]; deep: boolean; dir: string } | null {
24
+ const segs = normalizeGlob(glob).split('/')
25
+ let i = 0
26
+ while (i < segs.length && isLiteralPattern(segs[i]!)) i++
27
+ const lit = segs.slice(0, i).join('/')
28
+ if (i === segs.length) return { paths: [lit], deep: false, dir: lit }
29
+ const at = lit === '' ? '' : `${lit}/`
30
+ const name = segs[i + 1]
31
+ if (segs[i] === '**' && name !== undefined && isLiteralPattern(name)) {
32
+ const leaf = i + 2 < segs.length ? `${name}/` : name
33
+ return { paths: [at + leaf, `${at}${FRESH}/${leaf}`], deep: true, dir: lit }
34
+ }
35
+ return lit === '' ? null : { paths: [`${lit}/`], deep: false, dir: lit }
36
+ }
37
+
38
+ const CHECK_IGNORE = ['check-ignore', '--stdin', '-z']
39
+
40
+ function ignoredOf(exitCode: number, stdout: Uint8Array): Set<string> | null {
41
+ // 0: some ignored; 1: none; anything else (no work tree): undecided.
42
+ if (exitCode === 1) return new Set()
43
+ if (exitCode !== 0) return null
44
+ return new Set(
45
+ new TextDecoder()
46
+ .decode(stdout)
47
+ .split('\0')
48
+ .filter((p) => p !== ''),
49
+ )
50
+ }
51
+
52
+ /** The `paths` (relative to `cwd`) git ignores; null when git cannot say. */
53
+ export async function checkIgnore(
54
+ cwd: string,
55
+ paths: readonly string[],
56
+ ): Promise<Set<string> | null> {
57
+ if (paths.length === 0) return new Set()
58
+ try {
59
+ const proc = Bun.spawn({
60
+ cmd: [executablePath('git'), ...CHECK_IGNORE],
61
+ env: gitEnv(),
62
+ cwd,
63
+ stdin: Buffer.from(paths.map((p) => `${p}\0`).join('')),
64
+ stdout: 'pipe',
65
+ stderr: 'ignore',
66
+ })
67
+ const stdout = await new Response(proc.stdout).bytes()
68
+ return ignoredOf(await proc.exited, stdout)
69
+ } catch {
70
+ return null
71
+ }
72
+ }
73
+
74
+ /** {@link checkIgnore} for `vx lock`, which builds its graph synchronously. */
75
+ export function checkIgnoreSync(cwd: string, paths: readonly string[]): Set<string> | null {
76
+ if (paths.length === 0) return new Set()
77
+ try {
78
+ const r = Bun.spawnSync({
79
+ cmd: [executablePath('git'), ...CHECK_IGNORE],
80
+ env: gitEnv(),
81
+ cwd,
82
+ stdin: Buffer.from(paths.map((p) => `${p}\0`).join('')),
83
+ stdout: 'pipe',
84
+ stderr: 'ignore',
85
+ })
86
+ return ignoredOf(r.exitCode, r.stdout)
87
+ } catch {
88
+ return null
89
+ }
90
+ }
91
+
92
+ /**
93
+ * What git lists under `dirs` (relative to `cwd`): tracked files and the
94
+ * untracked ones it does not ignore, the set a key filters. Null when git
95
+ * cannot say. A run has it from its enumeration; this is `vx lock`'s.
96
+ */
97
+ export function listedSync(cwd: string, dirs: readonly string[]): string[] | null {
98
+ try {
99
+ const r = Bun.spawnSync({
100
+ cmd: [
101
+ executablePath('git'),
102
+ 'ls-files',
103
+ '-z',
104
+ '--cached',
105
+ '--others',
106
+ '--exclude-standard',
107
+ '--',
108
+ ...dirs.map((d) => (d === '' ? '.' : `:(literal)${d}`)),
109
+ ],
110
+ env: gitEnv(),
111
+ cwd,
112
+ stdout: 'pipe',
113
+ stderr: 'ignore',
114
+ })
115
+ if (r.exitCode !== 0) return null
116
+ return new TextDecoder()
117
+ .decode(r.stdout)
118
+ .split('\0')
119
+ .filter((p) => p !== '')
120
+ } catch {
121
+ return null
122
+ }
123
+ }
124
+
125
+ /**
126
+ * Of `globs` (root-relative), those git ignores whole: each probe ignored
127
+ * and, for a deep glob, no listed file it matches (`listedMatch`).
128
+ */
129
+ export function wholeIgnored(
130
+ globs: Iterable<string>,
131
+ ignored: ReadonlySet<string> | null,
132
+ listedMatch: (glob: string) => boolean,
133
+ ): Set<string> {
134
+ const out = new Set<string>()
135
+ if (ignored === null) return out
136
+ for (const g of globs) {
137
+ const ask = ignoreProbes(g)
138
+ if (ask === null || !ask.paths.every((p) => ignored.has(p))) continue
139
+ if (ask.deep && listedMatch(g)) continue
140
+ out.add(g)
141
+ }
142
+ return out
143
+ }
package/src/util/index.ts CHANGED
@@ -24,8 +24,17 @@ export {
24
24
  wantsJson,
25
25
  } from './errors.js'
26
26
  export { configErrorFrame, configRefusal } from './config-frame.js'
27
- export { detectColors, ERROR, paint, SUCCESS, WARN, type ColorSupport } from './colors.js'
28
- export { gradientRule } from './report.js'
27
+ export {
28
+ detectColors,
29
+ dumbTerminal,
30
+ ERROR,
31
+ identityColor,
32
+ paint,
33
+ SUCCESS,
34
+ WARN,
35
+ type ColorSupport,
36
+ } from './colors.js'
37
+ export { barWidth, gradientRule } from './report.js'
29
38
  import * as ui from './ui.js'
30
39
  export { refusalNotice, sayLine, sayNotice } from './refusal-notice.js'
31
40
  export { ui }
@@ -67,10 +76,9 @@ export {
67
76
  claimExitForSignal,
68
77
  exitClaimedBySignal,
69
78
  killGraceMs,
70
- setSettleBounds,
71
- type SettleBound,
72
79
  settleWithin,
73
80
  teardownTimeoutMs,
81
+ testTimerMs,
74
82
  } from './settle.js'
75
83
  export { formatBytes, parseDuration, parseSize, treeBytes } from './size.js'
76
84
  export {
@@ -91,6 +99,13 @@ export { procfsIsOwn } from './procfs.js'
91
99
  export { hangupIgnored } from './hangup.js'
92
100
  export { ESSENTIAL_ENV } from './essential-env.js'
93
101
  export { gitEnv, helperEnv, hideEnvFromTasks } from './hide-env.js'
102
+ export {
103
+ checkIgnore,
104
+ checkIgnoreSync,
105
+ ignoreProbes,
106
+ listedSync,
107
+ wholeIgnored,
108
+ } from './git-ignored.js'
94
109
  export { realPath, realpathOf } from './real-path.js'
95
110
  export {
96
111
  maskedCommand,
@@ -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
- // Rule ends flush with the bars: 2 indent + 8 label + 2 gap + 50 cells.
9
- const RULE_DASHES = 57
8
+ // The bars' label column: 2 indent + 8 label + 2 gap.
9
+ const LABEL = 12
10
10
 
11
- /** Total visible rule width: `─ <mark> ` + dashes. Shared by the run summary and every report. */
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 dashes = '─'.repeat(Math.max(8, RULE_DASHES + 2 - mark.length))
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
- const per = Math.ceil(RULE_DASHES / chunks)
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)
@@ -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 flag's bound, else the variable's (a positive duration inside the
32
- * timer's range: the CLI refuses any other before a run, and an embedder's
33
- * falls back), else the workspace's.
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 bound(name: string, set: SettleBound | undefined, zero = false): number | undefined {
36
- if (set?.from === 'flag') return set.ms
37
- const raw = process.env[name]
38
- if (raw !== undefined && raw !== '') {
39
- const n = parseDuration(raw)
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 (`run.teardownTimeout`).
47
- * There is no "no limit": the deadline exists so a plugin's flush cannot
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 bound('VX_RUN_TEARDOWN_TIMEOUT', teardownSet, true) ?? DEFAULT_TIMEOUT_MS
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
- * (`run.killGrace`): the one-shot timeout escalation (`exec/runner.ts`),
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 bound('VX_RUN_KILL_GRACE', killGraceSet) ?? defaultMs
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
- // `orchestrator/logger.ts` is the sole holder: it registers a tail per
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 { detectColors, ERROR, paint, SUCCESS, WARN, type ColorSupport } from './colors.js'
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 the run's project and task hues. */
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(PROJECT, text, c)
52
- return paint(PROJECT, text.slice(0, hash), c) + paint(TASK, text.slice(hash), c)
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)
@@ -1,133 +0,0 @@
1
- {
2
- "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "title": "vx run --summarize",
4
- "description": "Every task's outcome in one run: <cacheDir>/runs/<runId>.json, or the --summarize=<path> file.",
5
- "type": "object",
6
- "additionalProperties": false,
7
- "required": [
8
- "runId",
9
- "ok",
10
- "exitCode",
11
- "startedAt",
12
- "endedAt",
13
- "totalMs",
14
- "savedMs",
15
- "tasks",
16
- "aborted",
17
- "summary"
18
- ],
19
- "properties": {
20
- "runId": { "type": "string" },
21
- "ok": { "type": "boolean", "description": "the run's verdict: exitCode is 0" },
22
- "exitCode": { "type": "integer" },
23
- "startedAt": { "type": "string", "description": "ISO 8601" },
24
- "endedAt": { "type": "string", "description": "ISO 8601" },
25
- "totalMs": { "type": "number" },
26
- "savedMs": {
27
- "type": "number",
28
- "description": "what the cache hits skipped: the sum of their storedDurationMs"
29
- },
30
- "tasks": {
31
- "type": "array",
32
- "description": "every counted task: not a group, not aborted",
33
- "items": { "$ref": "#/$defs/task" }
34
- },
35
- "aborted": {
36
- "type": "array",
37
- "description": "tasks a shutdown signal killed; in no bucket and no total",
38
- "items": { "$ref": "#/$defs/task" }
39
- },
40
- "summary": {
41
- "type": "object",
42
- "additionalProperties": false,
43
- "required": [
44
- "successful",
45
- "failed",
46
- "skipped",
47
- "cachedLocal",
48
- "restoredLocal",
49
- "restoredRemote",
50
- "upToDate",
51
- "cachedRemote",
52
- "aborted",
53
- "total"
54
- ],
55
- "properties": {
56
- "successful": { "type": "integer" },
57
- "failed": { "type": "integer" },
58
- "skipped": { "type": "integer" },
59
- "cachedLocal": { "type": "integer" },
60
- "restoredLocal": { "type": "integer" },
61
- "restoredRemote": { "type": "integer" },
62
- "upToDate": { "type": "integer" },
63
- "cachedRemote": { "type": "integer" },
64
- "aborted": { "type": "integer" },
65
- "total": { "type": "integer" }
66
- }
67
- }
68
- },
69
- "$defs": {
70
- "task": {
71
- "type": "object",
72
- "additionalProperties": false,
73
- "required": ["id", "project", "task", "status", "exitCode", "durationMs", "hash"],
74
- "properties": {
75
- "id": { "type": "string" },
76
- "project": { "type": "string" },
77
- "task": { "type": "string" },
78
- "status": {
79
- "enum": ["success", "cache-hit", "cache-hit-remote", "failed", "skipped", "aborted"]
80
- },
81
- "exitCode": { "type": "integer" },
82
- "durationMs": { "type": "number" },
83
- "hash": { "type": ["string", "null"] },
84
- "restored": {
85
- "type": "boolean",
86
- "description": "on a hit only: true when outputs were restored, false when they were already up to date"
87
- },
88
- "noCache": {
89
- "const": true,
90
- "description": "the task declares no cache block: it could never hit"
91
- },
92
- "flaky": {
93
- "type": "object",
94
- "description": "this key both passed and failed on record, or the task needed a retry",
95
- "additionalProperties": false,
96
- "required": ["passes", "failures", "attempts"],
97
- "properties": {
98
- "passes": { "type": "integer" },
99
- "failures": { "type": "integer" },
100
- "attempts": { "type": "integer" }
101
- }
102
- },
103
- "cpuMs": { "type": "number" },
104
- "peakRssBytes": { "type": "integer" },
105
- "storedDurationMs": {
106
- "type": "number",
107
- "description": "a hit: how long the producing execution ran, what the hit skipped"
108
- },
109
- "storedCpuMs": {
110
- "type": "number",
111
- "description": "a hit: what the producing execution used"
112
- },
113
- "storedPeakRssBytes": { "type": "integer" },
114
- "admissionHeldMs": {
115
- "type": "number",
116
- "description": "an admit policy held it with a worker free"
117
- },
118
- "blockedBy": {
119
- "type": "string",
120
- "description": "a skipped task: the failed task at the root of what blocked it"
121
- },
122
- "timedOut": { "const": true },
123
- "sandboxViolations": { "type": "integer" },
124
- "notReady": { "enum": ["timeout", "exited", "spawn"] },
125
- "wallclockStartNs": {
126
- "type": "string",
127
- "description": "decimal nanoseconds from the run's start"
128
- },
129
- "wallclockEndNs": { "type": "string" }
130
- }
131
- }
132
- }
133
- }