Skip to content

Hand mark_done! the digest save! just returned - #59

Closed
sotashimozono wants to merge 1 commit into
mainfrom
record-the-digest
Closed

sotashimozono wants to merge 1 commit into
mainfrom
record-the-digest

Conversation

@sotashimozono

Copy link
Copy Markdown
Member

Closes #58.

save! returns (; file, sha256) naming the bytes it wrote. mark_done! takes result= and writes result_sha256 / result_file / observation from it. The call between the two threw the value away:

DataVault.save!(vault, key, payload)
DataVault.mark_done!(vault, key)

So every sweep this runner has driven wrote a .done that says unknown, and no run it produced can state that what a reader loads is what the computation wrote.

What it cost

Both ends of the chain were already built and only the middle did not pass the value along. Pinax.report hashes each result as it reads it; Archeion.provenance_from compares that against the recorded digest. Measured on a deposited revision of a real study:

[counts]
read_differs_from_result = 0
read_matches_result      = 0
result_unknown           = 12

Twelve points read and hashed, and nothing to check them against. The catalogue page built from that revision says it out loud — "read 12 points: 0 read as recorded · 12 unrecorded" — which is honest and useless. And capability.verified with criterion result-file-sha256 (Archeion SPEC §6), the event by which a revision earns the claim that it can be recomputed, cannot be earned at all while the digest is missing.

The change

saved = DataVault.save!(vault, key, payload)
DataVault.mark_done!(vault, key; result=saved)

[compat] DataVault moves to 0.8.5, where mark_done!(; result) and done_version=2 arrived — the old "0.7, 0.8" admitted versions with no such keyword, and this environment was in fact resolving 0.7.8.

The test

Written from the consumer's side, through load_recorded, because that is who suffers: nothing inside the runner notices a missing digest — the sweep succeeds, every key is :done, and only a reader two packages away can tell. It checks three things:

  • the digest is present and done_version == "2";
  • it is the digest of the file — read_sha256 == result_sha256, so a plausible-looking hash of the wrong bytes fails;
  • a result replaced behind the runner's back makes the two differ, which is the comparison this restores.

Verified to fail without the fix (both assertions, on every key) and pass with it: 25 tests.

What this does not do

Existing .done files cannot be repaired. A digest computed now would name today's bytes while claiming the computation recorded them — a worse record than unknown.

🤖 Generated with Claude Code

`save!` returns `(; file, sha256)` naming the bytes it wrote, `mark_done!` takes `result=`
and writes `result_sha256` / `result_file` / `observation` from it, and the call between
the two threw the value away. So every sweep this runner has ever driven wrote a `.done`
that says `unknown`, and no run it produced can state that what a reader loads is what the
computation wrote.

Both ends of the chain were already built. `Pinax.report` hashes each result as it reads
it, `Archeion.provenance_from` compares that against the recorded digest — and reports
`result_unknown` for every point instead. Measured on a deposited revision of a real
study: `read_matches_result = 0`, `result_unknown = 12`. `capability.verified` with
criterion `result-file-sha256` (Archeion SPEC §6), which is how a revision earns the claim
that it can be recomputed, cannot be earned at all while the digest is missing.

The fix is the value already in hand. `[compat] DataVault` accordingly moves to `0.8.5`,
where `mark_done!(; result)` and `done_version=2` arrived — the old `"0.7, 0.8"` admitted
versions with no such keyword.

The test is written from the consumer's side, through `load_recorded`, because that is who
suffers: nothing inside the runner notices the missing digest — the sweep succeeds, every
key is `:done`, and only a reader two packages away can tell. It checks that the digest is
present, that it is the digest OF THE FILE (`read_sha256 == result_sha256`, so a plausible
hash of the wrong bytes fails), and that a result replaced behind the runner's back makes
the two differ, which is the comparison this restores. Verified to fail without the fix:
both assertions, on every key.

Existing `.done` files cannot be repaired — a digest computed now would name today's bytes
while claiming the computation recorded them, which is a worse record than `unknown`.

Closes #58.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@sotashimozono

Copy link
Copy Markdown
Member Author

Superseded: main already passes save!'s digest to mark_done! (#54, 06b38d5). Since #61 it does so through the owner-checked form, mark_done!(vault, key, tok; result=saved, observation=…). This branch is based on 0.6.2, and its remaining changes are formatting.

@sotashimozono
sotashimozono deleted the record-the-digest branch September 29, 2026 04:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

mark_done! is called without the digest save! just returned, so no run can prove its results are the computed ones

1 participant