Contextfulv0.23.0

Docs / Guides

contract run · 30-run.md · 31-pipeline.md · 32-derive.md · guide · card

Runs, pipelines and derive

The problem a run solves

A pipeline copies records from a source, such as a vendor API, into store tables. When a process dies halfway through a pull, the restart has to write each row once, keep the source position in step with those rows, and send no second paid request. A run is one execution of a pipeline over one scope.

Flowchart diagram Flowchart, top to bottom. 7 nodes, 6 edges. run contract: pipeline declaration → run plan [compile] run contract: run plan → run journal [open run, take owner] run contract: run journal → land path [land in stage order] run contract: land path → commit marker [rows and position] run contract: live projection store contract: commit marker → run record [terminal status] store contract: run record → live projection [status] run contract compile open run, take owner land in stage order pipeline declaration run plan run journal land path live projection store contract terminal status commit marker run record rows and position status

Declaring and compiling a pipeline

The example pipeline, record-sync, declares a source, one table named records and incremental = "updated_time" (pipeline-spec). That field is the stream clock: the source reports a cursor that stores the field name beside its value (incremental-field). An edit that restates a default leaves the content hash unchanged (content-hash).

The compiler turns the declaration into a plan, a flat node list holding no script (plan-node). Branches and fan-out come from declared nodes, not from data, so a replay walks the same graph (control-flow).

Each deployment supplies a durable orchestrator implementing one execution interface (role).

A run that dies and resumes

The engine writes the run row before the first pull, so a reader tells a zero-row success from no run (row-at-open). An execution owner binds the pending work to its scope, connector build and plan hash (execution-owner), and the owner renews a lease while it runs (owner-lease).

The pull admits rows at or after the stored position, so each poll re-reads the boundary instant. records declares a key, which makes those repeats idempotent; a keyless table accumulates them (inclusive-boundary).

The journal records each pulled page exactly once, although the request behind it runs at least once (step-output). Each outbound request carries an idempotency key derived from the journal entry, so a repeated request names itself (idempotency-key). A value over 1 MiB moves to a content-addressed blob (inline-cutoff).

Now the process dies after its first page. Startup marks the row partial_failure because its owner lease has expired (orphan-reap). The next fire finds no commit marker newer than the cached position, so the pending owner stays (marker-reconciles).

The plan hash matches, so the run replays. The journal hands the recorded page to the land path byte for byte (replay-lands), and the replay sends no request to the vendor (no-double-spend).

The remaining pages pull live. The commit publishes the rows and the new position together (commit-with-rows), and success releases the owner (pin-release).

Two changes stop the replay instead. A declaration whose hash changed while the owner was pending fails the open, naming both identity sets (pinned-plan-changed). A renamed updated_time fails the open before any request leaves the host (field-rename).

On D1, a callback guard holds only its own request’s owners and leases (scope), and releases unattended only when a replay is safe (replay).

Inside one batch

A batch passes a fixed stage order from pull to commit (stage-order). The secret guard runs before the recorded pull, so a replay reintroduces no credential (placement). The guard masks credential-shaped text and logs a count per column; it blocks no run (mask-only).

Normalize keeps the nesting a sink supports, or flattens it into child tables joined by a foreign key (mode). The declared transform chain then selects, renames, casts, extracts and filters columns of the root table (chain). A commit makes one table’s rows visible in one step, and a crash before it leaves a recoverable partial run (commit-visibility).

Failures, stops and waits

One tagged failure type crosses every port, and a caller branches on the tag, not on a message (failure-taxonomy). Only Transient and RateLimited retry, under the step’s schedule (retryable-classes). In the example, a vendor rate limit retries the page, and an expired credential ends the step.

A stop marks the run, and every await observes it through one cancellation token (one-token). A stop leaves landing uncut: received bytes finish landing first (land-path-uncut).

A run waiting on an outside party suspends on an awakeable, a single-use token the party posts back (awakeable). The caller supplies the deadline, and the core reads no wall clock (deadline).

A store-driven job that declares isolated items gives each item a completion receipt, so a later fire skips finished items (completion).

Loading history

A backfill splits history into chunks that workers lease. The chunk commits only while that worker’s attempt holds the row, so a stale worker’s files stay uncommitted (fenced-commit).

A seed bulk-loads consumer-held history through the same stage order as live pulls (one-land-path). A seeded row at or past the declared ceiling fails its chunk, which writes nothing (ceiling-breached).

A sweep runs one job per origin across a span, every origin under one frozen transaction-time bound (frozen-as-of). Under carried state, each origin also reads the output the preceding origin committed (carried-state).

Flowchart diagram Flowchart, top to bottom. 8 nodes, 9 edges. run contract: sweep declaration → earlier origin [one run per step]; → later origin [one run per step] run contract: frozen transaction bound → backfilled history table [bounds every read] run contract: earlier origin → sweep output table [writes stamped at origin] run contract: later origin run contract: carried state? → later origin [carried: preceding commit]; → no shared rows [fresh: frozen bound] run contract: no shared rows store contract: backfilled history table → earlier origin [rows available by origin]; → later origin [rows available by origin] store contract: sweep output table → carried state? [read by later origin] run contract one run per step one run per step carried: preceding commit fresh: frozen bound sweep declaration frozen transaction bound earlier origin later origin carried state? no shared rows store contract backfilled history table sweep output table bounds every read rows available by origin rows available by origin writes stamped at origin read by later origin

Models

A model builds a table from SQL over store tables (statement-source). build reads each input at its committed frontier and fires no pipeline (build-frontier). A model whose SQL reaches its own id fails validation before any read (model-cycle).

A build materializes into staging, so a refused build leaves the last published state serving readers (staging).

Derive

Derive runs a task over parent rows. Each tick selects parent rows holding no settled output under the current derivation key (anti-join). A binding names a registered engine; a command in the manifest fails (command-in-manifest).

An exec engine runs a pinned binary as an argument array, with no shell and a cleared environment (no-shell). A vendor step’s loopback proxy attaches its credential (exec-proxy). A fetch engine follows third-party links, and a host written as an address literal fails (address-literal).

The caption parser reads SubRip and WebVTT through one grammar (grammar). Each unit records one status: ok, empty, unavailable or failed (unit-status). The command in test-engine runs one engine against one file, outside any pipeline.

Delivering and watching

An export delivers committed rows to a declared target. Its cursor commits after the target acknowledges a batch, so a run ending between the two resends that batch (at-least-once).

An Iceberg export writes one credential’s copy of a table for a query engine outside the store (audience).

A live progress view projects runner events, and no execution step reads that view (best-effort).