Skip to content

Execution flows, scenario by scenario

Companion to execution.md (prose lifecycle) and caching.md (key derivation). Each section is one end-to-end scenario as a diagram, with the source files that own each step. Diagrams are Mermaid — GitHub renders them inline.

1. Cold run — cache miss → exec → save

Section titled “1. Cold run — cache miss → exec → save”

The path a task takes the first time it runs (or after any input changed). Owners: orchestrator/execute-task.ts (sequence), cache/inputs.ts (enumeration), cache/cache.ts (key + save), exec/runner.ts (spawn).

Owners: cache/cache.ts:get + isOutputsCurrent, cache/tar.ts:extractOutputs.

3. Remote hit — download → ingest → restore

Section titled “3. Remote hit — download → ingest → restore”

Owners: cache/layered-cache.ts, cache/remote-cache.ts. Requires VX_REMOTE_CACHE_URL + VX_REMOTE_CACHE_TOKEN (orchestrator/remote-cache-setup.ts).

On any remote error (timeout, non-404 failure, corrupt body) the layered cache reports through onRemoteError and the task degrades to a miss — remote problems never fail a run.

The write side is the mirror image: LayeredCache.save writes the local artifact first, then uploads the same bytes verbatim (PUT /v8/artifacts/:hash), awaited before save() resolves, errors routed to onRemoteError.

Owner: graph/scheduler.ts. The scheduler distinguishes transitive dependents (skipped) from independent siblings (keep running) — Turbo’s middle --continue setting.

Skipped outcomes carry exit code 1 and durationMs: 0; nothing is spawned for them. The run’s ok is false; the summary lists the failed task IDs (not the skipped ones — the root cause is what you fix).

Owner: cli/watch.ts. One recursive fs.watch per project dir plus a non-recursive watch of the workspace root (lockfile edits). Path filter drops node_modules, .git, .vx, *.tsbuildinfo, editor swap files.

The pending flag is the reentrancy guard: events landing mid-cycle collapse into exactly one follow-up run, never a queue.

Owner: exec/runner.ts:runPersistent + the orchestrator’s persistentRegistry. Persistent tasks (exec.persistent) gate downstream work on readiness, then live until the rest of the graph finishes. cache + persistent is rejected at load time.

7. Sandboxed task — violation → failure

Section titled “7. Sandboxed task — violation → failure”

Owner: exec/sandbox-runtime.ts (SRT wrapper). Activation is per-task (sandbox: {...}), no workspace inheritance. Baseline policy: read = resolved cache.inputs.files, write = static prefixes of cache.outputs.files, deny-read = workspace root; user config adds explicit allow/deny lists.

Owner: cli/cache.ts + cache/cache.ts:prune. Both bounds can combine; eviction is one SQL transaction (CASCADE clears output_files) plus parallel artifact unlinks.

accessed_at is bumped on every get, so LRU reflects real use — including hits from --dry plans.

Owner: orchestrator/plan.ts + plan-format.ts. Shares prepareRun with the real path, probes the cache for predicted hits, executes nothing, and writes nothing except the accessed_at bump inherent to probing.

Because the plan path and execute-task share the same key derivation helpers, a predicted hit is exactly what the real run would see (same process, same env, same tree).