Compute
Pin an input snapshot that cannot change
Pin an exact version, manifest or revision so your input cannot change under a running job. A moving prefix or a live table is not a frozen source.
Before you start
You need a source that can name one exact version of itself. An object version id, a table snapshot or revision, or a manifest listing exact file versions all qualify.
You also need to have read the bytes at that version, so the digest you pin is the digest of what you inspected.
What you pin
The input object carries a snapshot with two fields:
"snapshot": {
"version_id": "<the source's own version identifier>",
"content_sha256": "<the digest of those exact bytes>"
}
version_id says which version your source calls it. content_sha256 says what the bytes actually were. Together they mean the run cannot silently read something else.
A raw file is not a snapshot
A raw file is bytes at a path. A snapshot is a promise that those bytes cannot be replaced under that name. The difference decides whether a result can ever be traced back to its input.
These are not frozen sources, however stable they look:
- A prefix or folder that new objects keep landing in. That is a query, not a version.
- A
latestpointer, a moving tag or a branch name. - A live table with no snapshot, revision or transaction id.
- Any location a second process can write to while your job runs.
If your source cannot name a version, make your own frozen copy first. Materialize the exact rows or files you want, upload that copy as an artifact, and pin its digest.
When the input changes under you
Nothing is quietly reused. A quote is signed against the digest you pinned, carries an expiry, and is marked non_authorizing = true because it never starts work by itself.
If the input has moved by the time you seek admission, admission answers REPLAN_REQUIRED. That is the plan being remade, not a failure to explain away.
Recover in the same order every time. Re-read the descriptor at the version your quote names, then re-read the source at its current version. Rebuild the request with the new digest, quote again, and approve again. Approval is always its own act.
Why the digest earns its place
Your result bundle carries provenance.json, which binds the input, workflow, model, schema and envelope digests together. That file is what lets you prove, months later, which bytes produced which row.
Without a pinned snapshot there is nothing on the input side to bind. The bundle is still complete, but the claim "this result came from that data" becomes an assertion rather than a check.
What success looks like
Your quote echoes back the version_id and content_sha256 you supplied. Read them before you approve.
After the run, provenance.json carries the same input digest, and result-manifest.json is written last as the only complete-bundle marker. Matching digests on both sides means the job read what you meant it to read.
If you cannot freeze the source
Some sources genuinely cannot hold still, such as a table under continuous write. Do not pin a digest you cannot honor.
Instead, bound the read yourself: take one window, materialize it, pin that, and run it as its own job. That is the same pattern as running a stream as bounded windows.