sandbox §13.1–13.2: what a park retains, and what an interval proves about waiting #582
Loading…
Reference in a new issue
No description provided.
Delete branch "sandbox/transcript-continuation"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
§13 has always let a run be suspended and resumed, recording what it retained as
a filesystem root and an optional memory image. That shape assumes the resumable
state lives on the host. For a whole class of unit it does not: where the
resumable state is the record of what the unit has already done, that record
is the continuation — restore it and the unit is where it left off, with no
process to revive. Such a run was being made to describe itself in terms of a
filesystem it did not need, and to be priced as though it had parked one.
The two forms
A snapshot reference now carries one of two, and exactly one.
fs_root, andmemorywhere resident state is suspendedNeither form, both, or a transcript beside a resident memory image is refused
with
MalformedSnapshotbefore anything is hashed, and leaves no segment behind.The third case is the one worth naming: a park claiming to retain nothing on the
host while holding an image on it. Admitting any of them would put an ambiguity
inside a chain whose whole purpose is to remove one.
The energy claim this makes, and the one it refuses
Nothing runs during a transcript park and nothing is resident, so it is tempting
to call the wait free. What it holds is bytes in storage, and storage draws power
for every hour of the wait. A host therefore MUST NOT record a transcript
park's
idle_ujas zero on the grounds that no computation happened; the figureit owes is the draw of the bytes it kept, under §6.2's provenance discipline.
The claim this form supports is that waiting costs no computation. It does
not support the claim that waiting costs nothing. A run parked for a week on a
transcript is far cheaper than one parked on a volume, and neither is costless;
§13's total — receipts plus idle — stands for both. A record that says zero for a
week of storage is not a cheaper run, only a less honest one.
Backwards compatibility, checked rather than asserted
fs_rootbecameOptionwithserde(default, skip_serializing_if), so an imagesnapshot serializes to the same JSON it always did, and
push_snapshotemitsexactly the former byte layout for that form. Two things hold the change to it:
is checked without reference to the code that produced it;
durable.jsondiffs asone added line and no changed line —
expect_runrecord_headis stillb3:mk3hGn1kCXPuMQNPGQxHsqu8TxsnGVPBTBcgcxrHYlA, and the transcript form getsits own pinned address,
b3:XKKBf5hYdrSZoJN-GHLdZ_Bbg7CjN6P4DCRVD17GyUs. Thetwo traces are identical but for the form parked, so an implementation that
confused the forms would produce one address where the other is expected.
Hashing is now total, and one latent bug went with it
Segment::chain_bytesreturnsOptionandfoldreturnsResult, so amalformed reference cannot be hashed into a chain:
suspendrefuses it, and oneedited into a record through the wire form makes
verify()false rather thanhashing to something. Separately,
resumeused to add to the run's idle totalbefore folding the segment, so a refused resume left energy attributed to a
park that was never recorded. It now folds first, and a test holds that.
Conformance
Class C14, and the pack's deliberate class count moves 13 → 14 (the guard
says to bump it only when a class is added, which is the case here).
Evidence
All of it on the build host; nothing was built on the dev Mac.
cargo clippy --workspace --all-targets -- -D warnings: clean.cargo test --workspace: passes.file-backed
durable.jsoncheck.check-comparative-prose.shclean over 4,063 files;check-no-vanity-metrics.shclean over 517 Markdown files.cargo fmtwas deliberately not run: these workspaces are notrustfmt-clean, and it would bury the change in churn.
🤖 Generated with Claude Code
§13.2 — what an interval proves about waiting (second commit)
§13 prices the wait a run spends parked. It said nothing about the wait a run
spends running, which is usually the larger number: an interval that
dispatches an outward call and then sits until the result arrives holds its
sandbox, and its receipt charges the sitting as computation. Two runs doing
identical work bill hours apart, and the record reported both the same way.
An executed segment may now carry a
CallAccount— the calls it dispatched andthe results it resolved, each by call id. A run is non-blocking when every
executed interval accounts, and every interval resolves only calls that were
outstanding before it began. One that resolves its own dispatch did not hand off
and return; it waited, and the wait is inside its receipt.
RunRecord::wait_proof()returns one of four verdicts:NonBlockingBlocked { segment }Malformed { segment }UnprovedThis is what licenses the energy claim. "Waiting costs no computation" becomes a
statement about this record rather than about the runtime that produced it,
checkable by walking the segments — no trust in the implementation, and no
instrumentation beyond the ids a host already needs to match results to calls.
Two deliberate pieces of honesty:
calls are different statements, and they do not fold to the same bytes. A unit
test and a conformance check both hold that, so the marker is not just a
comment.
returns
Unproved, neverNonBlocking. Without that rule every empty recordwould certify the section's strongest claim.
Backwards compatibility again checked rather than asserted:
callsis foldedonly when present, so an interval recorded by
record_executionhashes exactlyas before (pinned against hand-written bytes), and
durable.jsondiffs as oneadded line and no changed line — both earlier heads unchanged, the accounted
trace pinned at
b3:FINebhSRpv_1ncmpE3PaRG85MR8Ri1fDy4lmR1yI2DA. That trace isthe transcript one with the account added and nothing else, so an implementation
that dropped the account would produce the transcript head where this one is
expected. Each id set is length-prefixed, so no id boundary is ambiguous.
Conformance class C15; the pack's deliberate class count moves 13 → 15 across
the two commits.
Evidence for both commits
cargo clippy --workspace --all-targets -- -D warnings: clean.cargo test --workspace: passes.file-backed
durable.jsoncheck.platform-dependent.
cargo fmtdeliberately not run: these workspaces are not rustfmt-clean.§13 prices the wait a run spends parked. It says nothing about the wait a run spends *running*, which is usually the larger number. An interval that dispatches an outward call and then sits until the result arrives holds its sandbox, and its receipt charges the sitting as computation. A twenty-minute build, a review that comes back the next morning, an approval that never comes — each is, in a blocking runtime, an interval whose receipt is mostly waiting. Two runs doing identical work bill hours apart, and the record reported both the same way. So an executed segment may now carry a `CallAccount`: the calls it dispatched and the results it resolved, each by call id. A run is **non-blocking** when every executed interval accounts, and every interval resolves only calls that were outstanding before it began. An interval that resolves a call it dispatched itself did not hand off and return — it waited, and the wait is inside its receipt. `RunRecord::wait_proof()` walks the segments in order holding the outstanding set and returns one of: - `NonBlocking` — no receipt in the run contains a wait for an outward call, so the run's whole cost of waiting is the idle energy §13 already records, and which §13.1 makes small for a transcript park. This is the condition under which "waiting costs no computation" is a statement about the record rather than about the runtime that produced it. - `Blocked { segment }` — that interval consumed its own dispatch. - `Malformed { segment }` — the account does not add up: a result answering a call never dispatched, a call resolved twice, or one id dispatched twice. A record that cannot be read is a different problem from a run that waited, and is reported separately. - `Unproved` — some interval did not account, or the run has no accounted interval at all. Two deliberate pieces of honesty **An absent account is not an empty one.** Not accounting and accounting for no calls are different statements, and they do not fold to the same bytes — a test and a conformance check both hold that, so the marker is not merely a comment. **A property of no intervals is not a property.** A run with nothing accounted returns `Unproved`, not `NonBlocking`. Without that, every empty record would certify the strongest claim in the section. Backwards compatibility, checked rather than asserted `calls` is `Option`, `serde(default, skip_serializing_if)`, and folded into the chain only when present, so an interval recorded by `record_execution` hashes exactly as it did before — pinned by a test against bytes written out by hand. The committed vector confirms it: `durable.json` diffs as one added line and no changed line, so `expect_runrecord_head` and `expect_transcript_head` are unchanged, and the accounted trace gets its own address at `b3:FINebhSRpv_1ncmpE3PaRG85MR8Ri1fDy4lmR1yI2DA`. That trace is the transcript one with the account added and nothing else changed, so an implementation that dropped the account would produce the transcript head where this one is expected. Each id set is length-prefixed, so no id boundary is ambiguous. Conformance class C15, and the pack's deliberate class count moves 14 → 15. Evidence, on the build host: clippy `-D warnings` clean over the workspace, the workspace's tests pass, and the full pack reports all 15 classes passed with no skips. `cargo fmt` was not run: these workspaces are not rustfmt-clean. Co-Authored-By: Claude Opus 5 <[email protected]>sandbox §13.1: a park retains host state or a transcript, and says whichto sandbox §13.1–13.2: what a park retains, and what an interval proves about waiting