Freshness¶
Summarizes SPEC.md §8; SPEC.md is authoritative.
A run is fresh when the code it executed and the inputs it read are unchanged since it recorded. Freshness is computed from content hashes. Git is not required; commits are recorded only as metadata.
Semantic hashing¶
A code unit's hash is SHA-256 (truncated to 16 hex characters) of the unit's AST after stripping docstrings. As a result:
- Comments, docstrings, blank lines, formatting and moving code around never change a hash. Such edits are cosmetic, reported as
info, and never fail. - Any change to logic changes the hash: code, defaults, decorators, annotations and constants. Renaming a local variable counts as a change — rare, and when it happens you record the judgement with
vouch accept.
Units per file:
| Unit | Contents |
|---|---|
path::<module> |
module-level statements other than definitions |
path::Class |
the class header and class-level statements other than methods and nested classes |
path::func, path::Class.method |
the whole definition: decorators, signature, defaults, body, including nested functions |
Definitions are left out of the enclosing unit, so reordering functions never changes a hash, and neither does editing one a run never executed.
Function-level tracking (Python ≥ 3.12)¶
On import vouch, vouch registers a sys.monitoring (PEP 669) PY_START
callback. Its cost is one call per distinct function, however hot the run's
loops are — measured worst case, a tight pure-Python loop: 1–2%, within
run-to-run noise. For code that spends its time in numpy or torch, the cost is
zero.
It records executed units (mapping each code object's co_qualname to its
unit) and source snapshots (each file's text as it was when the run first
loaded it). The executed units are every first-party function the run called, at
any depth: an edit to a library function the run's code calls makes the run
stale, though the script itself did not change.
Several runs in one process each record what they executed themselves, with whatever ran outside every run before they ended (setup, or data computed between two runs), and not what the other runs executed. A function two runs call is recorded in both.
Consequences:
- Editing a function the run never executed doesn't make it stale. The run is reported as
cosmetic, which passes. - The staleness report names the unit:
src/models.py::ResNet.forward changed.
Where precision isn't safe, files count whole, recorded under
code.whole_files, when: a module was imported before import vouch; the
entry script had code that could have called first-party code before
import vouch; vouch was imported inside a function; or executed code can't be
found in the source.
The whole run falls back to module granularity when: the Python version has
no sys.monitoring (< 3.12); no monitoring tool id is free; VOUCH_TRACE=0 is
set; [freshness] granularity = "module" is configured; or the run started
Python child processes, which aren't observed.
vouch trace KEY shows how each file was tracked (tracked by function, or
tracked whole -- <reason>).
Inputs, artifacts and upstream staleness¶
- Inputs are hashed by content, or by size+mtime with
mode="stat". A hash cache in.vouch/cache/hashes.jsonmakes repeated checks cheap. - Dependency between runs. If run B recorded an input that is an artifact of run A, then B depends on A. If A's recorded artifact hash differs from what B recorded as its input, B is stale. If A is stale, B is upstream-stale.
- Values read from another run. A run that reads run A's values with
vouch.value(key)depends on A through those values. It is stale once one of them moves, or no run records it any more, and upstream-stale while A is stale. A re-run of A that moves only values it did not read leaves it fresh. - Artifacts on disk are checked when present. A mismatch is
tampered. A missing file isabsent(only a warning — the committed store alone is enough to check the paper).
Run states¶
| State | Meaning | Default severity |
|---|---|---|
fresh |
every unit, input and artifact matches | ok |
cosmetic |
files changed, but not in any unit the run depends on | info |
stale |
a unit's semantic hash changed, a unit disappeared, or an input changed | error |
upstream-stale |
a run this one read from is stale | error |
tampered |
an artifact on disk differs from its recorded hash | error |
incomplete |
the record's status is not complete |
error |
accepted |
stale, but the exact current state was reviewed | info |
env-drift |
a recorded third-party package version, or the recorded Python minor version, differs from the current environment. A run that records no interpreter (vouch run of a non-Python command, vouch import) is not compared |
warning |
absent |
a recorded input or artifact is not on disk | warning |
Accepting a reviewed staleness¶
Not every logic change changes a result: a refactor, an added log line, a renamed variable. That judgement belongs to a person, and it is recorded:
.vouch/accepted.toml then records the run, the exact current hashes of the
changed units and inputs, who, when and why. The acceptance clears the run only
for those hashes: the next edit re-opens it.
Accepting is a human action. The Claude Code integration's rules forbid an agent from doing it without the user's approval — see Claude Code.