run-provenance · git:20260921.9b3de7c · 2026-09-21 · sha256 6125bdce639a9dac
run-provenance git:20260921.9b3de7cA
Immutable. This exact content is served forever at /api/v1/blob/6125bdce639a9dac.
--- name: run-provenance description: "Decide how much provenance a computational run owes, record it before the first expensive step, and write a README that says how to remake the output. Use when launching a run, publishing an artifact another run will consume, freezing a checkout for a queued job, or deciding whether a branch, worktree, or recovery copy can be deleted." --- # Run Provenance Provenance exists so a later reader can tell what produced an artifact and remake it. Everything below scales with what the artifact is for: a probe nobody will reuse owes very little, and a parent that other runs consume owes all of it. ## Choose The Tier Before You Launch Use three tiers so iteration stays fast without weakening durable evidence. 1. **Developer checks.** Compile/import checks, config validation, dry runs, and disposable local mechanics probes may use uncommitted code and need no provenance artifact. A read-only diagnostic that consumes immutable inputs and writes nothing anything else will consume is this tier by default; it needs no deliberation about which tier applies. 2. **Exploratory scheduled smokes.** Uncommitted code is allowed when the run root automatically records the full base commit SHA, exact command, config hash, environment identity, immutable data/parent hashes, working-tree status, and an exact binary patch or source snapshot covering every dirty or untracked execution path. Report these results only as preliminary, with that provenance. They may not become reusable parents or decision-grade evidence. 3. **Decision-grade, expensive, or reusable runs.** Long training, final comparisons, promotion/retirement evidence, and artifacts consumed as parents must execute from a clean checkout pinned to a full 40-character commit SHA, with every invoked code and config path tracked and committed. A dirty shared checkout should use a clean commit-pinned worktree rather than blocking unrelated development. For tier 3, generate the run README and machine-readable provenance **before the first scientific kernel or optimizer update**. Record the full commit SHA, exact command and config hash, environment identity, immutable data/parent manifest hashes, and clean execution status; the commit already determines the tree and transitive source contents. Fail closed when a field is absent or the execution checkout differs from the commit. Resume with the same commit and bound hashes; a code change requires a new commit and a new semantic run slug or explicitly versioned attempt. Generate this automatically rather than spending agent effort on long narrative prose. Before re-preparing a provenance-hashed run after an owner change, inspect its existing-artifact policy and either archive the prior immutable result or choose a new semantic run slug before resubmission. ## Write A README That Says How To Remake The Output Every new output/run directory needs a launcher-written `README.md` before it is reported ready, recording the differentiator, launcher, config snapshot, queue/job IDs, run/fold paths, environment, commit, dirty-state summary, and important results. Capture provenance on the submitting host and pass it into jobs whose images lack `git`; a dependency-pending job from a mutable checkout must receive the intended commit and dirty state at submission or load an immutable snapshot, never claim a later `HEAD`. That README must also say how to remake the output, not only what produced it. Recording a commit and a config hash establishes provenance; it does not tell the next reader what to run. Include: - the exact command with every input path; - the shard or fan-out count, and whether rerunning is idempotent; - what each emitted array or column holds, with its indexing; - the producing commit, whether the tree was dirty, and a **checkout of that commit as the first rebuild step** — a command without the version it belongs to stops reproducing the artifact the moment the producer changes, and the commit is the only way back; - a digest of every input table, because inputs can move underneath a fixed commit and a digest mismatch is what tells the next reader which of the two changed. Generate it from the arguments the run actually received rather than writing it by hand, so it cannot drift from what it describes, and write it from the first unit of work rather than only from a reducer, so an interrupted run is still documented. A cache or export nobody can rebuild is one nobody can trust. Scale this to what the artifact is for. The full form is owed by anything that could become a parent, a reusable cache, an export, or decision-grade evidence. A disposable probe whose whole output is a log, and which one printed command regenerates, owes the command, the commit, and what it measured, and nothing else: input digests exist so a later reader can tell whether an input moved, and there is no later reader for an artifact nobody will consume. When in doubt, write the full form — a probe is sometimes promoted, and the long form costs minutes. ## Freeze A Checkout By Execution Pin, Not For Its Lifetime A worktree may advance through many commits while no queued or running job is bound to it. At submission, record the exact worktree and full commit together, and do not move, update, or modify that checkout until every job bound to that pin has finished or been withdrawn; the queued job's pin must still match when the scheduler starts it. Multiple jobs may share one frozen worktree only when they bind the same commit. Give simultaneously active different commits separate worktrees. Once the last bound job is terminal, the clean worktree may be advanced, reused for the same bounded experiment, or removed. The pin binds the recorded identity, not only the executed source. Each array task stamps the commit it sees when it starts, so any commit to that checkout mid-array splits one run's provenance across two SHAs, and a consumer that requires a single commit will refuse the result. A documentation-only commit is enough to do it. Repair that by re-running from a pinned checkout, not by relaxing the consumer's check. ## Do Not Let Branches And Worktrees Accumulate - Treat development branches and worktrees as temporary integration state, not indefinite forks. As soon as a result is available and no queued or running job requires the checkout frozen, either integrate the bounded validated implementation promptly or explicitly record that the branch is not intended for merge, with the reason and any authoritative result artifact. A not-for-merge label is a disposition, not a reason to keep the checkout forever. - At each clean development boundary, refresh active merge-bound work against the current upstream default branch. Never rewrite a commit pinned by a queued or running job. - Retain unmerged code, branches, worktrees, or recovery copies only for a current merge plan, a concrete named future use, necessary scientific or provenance evidence, or unresolved ownership. Name the reason and the owner in existing task state; speculative usefulness and absence from version control are not retention reasons. During authorized cleanup, after checking live consumers and execution pins, delete obsolete work rather than manufacturing a backup branch, recovery commit, or archive. Reassess retained work when its stated use ends. - Do not rewrite or forcibly purge history merely to remove obsolete working copies or references.