Skip to content

fix: track the runtime's yoagent-state, and say which fold produced a verdict - #3

Merged
yuanhao merged 2 commits into
mainfrom
fix/2-track-runtime-state-version
Aug 28, 2026
Merged

fix: track the runtime's yoagent-state, and say which fold produced a verdict#3
yuanhao merged 2 commits into
mainfrom
fix/2-track-runtime-state-version

Conversation

@yuanhao

@yuanhao yuanhao commented Aug 28, 2026

Copy link
Copy Markdown
Collaborator

Fixes #2.

The skew is now active

The issue called this latent — graph.rs happened to be byte-identical across 0.4.0 and 0.5.0. That changed today: yoagent-state 0.5.1 ships the lenient fold from yologdev/yoagent#168, so the checker on 0.4 and the runtimes on 0.5.1 now disagree about the same store.

Three parts

1. The bumpyoagent-state = "0.4""0.5", resolving to 0.5.1.

2. Check 2 folds the way a runtime does. Since 0.5.1 an op naming a missing node is skipped rather than aborting, so a store containing one is restorable. Failing it would report non-conformance for a store every runtime can open — and it is the one verdict a user cannot act on, because an append-only log cannot have the op removed, and inserting a fix before it rewrites published history and permanently fails check 4.

The skip is reported, not swallowed. It goes to notes, which doesn't fail the check. Masking corruption is the failure mode this must not trade for.

3. The report names the fold version — the issue's second suggestion, and I agree it's worth more than the bump. The skew was previously discoverable only by reading two lockfiles:

conformance-check 0.1.0 — folding with yoagent-state 0.5.1

A build script reads the resolved version from the workspace lock, because cargo exposes no env var for a dependency's resolved version and the requirement ("0.5") isn't what was built. A missing lockfile degrades to unknown rather than failing the build.

Verified against the real store

yologdev/yoyo-gasp at 8,882 events with the one dangling UpdateNode that motivated yoagent#168 — previously failing checks 2 and 6:

conformance-check 0.1.0 — folding with yoagent-state 0.5.1
[PASS] check 1 — envelope round-trip
[PASS] check 2 — replay
       note: UpdateNode at batch index 0 skipped — node not found:
             task_run_day180_20260827T232710Z_session. The log is readable but
             malformed; a conformant runtime folds past this, and so does this check
[PASS] check 3 — vocabulary
[PASS] check 4 — append-only in git
[PASS] check 5 — causation integrity
[PASS] check 6 — restore
[PASS] check 7 — domain↔ops consistency
conformant: all checks passed

Check 4 green because nothing was rewritten — that's the point.

Regression test

a_dangling_op_is_readable_and_reported_not_failed appends a dangling op to the fixture, deriving the envelope from a real event so it tests the fold rather than the parser's required fields. Mutation-verified: reverting check 2 to the strict fold fails it with fold failed: node not found.

23 passed, clippy clean under -Dwarnings.

One judgement call worth your review

I made check 2 lenient rather than strict. My own release note for yoagent-state 0.5.1 said "conformance checkers should use replay_strict" — I now think that was wrong for this checker specifically. Its stated job is "can a conformant runtime fold and restore this store?", and strict would answer a different question than the one the badge claims.

If you'd rather check 2 assert well-formedness instead, the change is one line (replay_with_diagnosticsreplay_strict) — but then the yoyo-gasp store stays non-conformant with no legal repair, which is what #168 set out to escape.

🤖 Generated with Claude Code

yuanhao and others added 2 commits August 28, 2026 11:00
… verdict

The checker pinned yoagent-state 0.4 while the runtimes it certifies are
on 0.5. It answers "can a conformant runtime fold and restore this
store?" by folding the store itself, so a version skew makes the answer
wrong in either direction — certifying a store no runtime can read, or
failing one every runtime can.

That was latent while graph.rs happened to be byte-identical across the
two versions. yoagent-state 0.5.1 changed the fold, so it is now active.

Three parts.

The bump the issue asks for: 0.4 -> 0.5, resolving to 0.5.1.

Check 2 now folds the way a runtime does. Since 0.5.1 an op naming a
missing node is skipped rather than aborting the fold, so a store
containing one IS restorable, and failing it would report
non-conformance for a store every runtime can open. That is also the
verdict a user cannot act on: an append-only log cannot have the op
removed, and inserting a fix before it rewrites published history —
permanently failing check 4, which is the property the format exists to
guarantee.

The skip is reported, not swallowed. It lands in `notes`, which does not
fail the check. Masking corruption is the failure mode this must not
trade for, and a certificate that hides what it skipped cannot be
audited.

And the report now names the fold version, which is the issue's second
suggestion and worth more than the bump: the skew was previously
discoverable only by reading two lockfiles. A build script reads the
resolved version from the workspace lock, because cargo exposes no env
var for a dependency's resolved version and the requirement in Cargo.toml
("0.5") is not what was built. A missing lockfile degrades to "unknown"
rather than failing the build.

Verified end to end against yologdev/yoyo-gasp at 8,882 events with the
one dangling UpdateNode that motivated yoagent#168: 7/7, with the skip
named in the certificate and check 4 still green because nothing was
rewritten. Previously checks 2 and 6 failed.

Regression test included and mutation-verified: reverting check 2 to the
strict fold fails it with "fold failed: node not found".

23 passed, clippy clean under -Dwarnings.

Fixes #2
Three defects in the version line, all found by review and each verified.

Packaged builds printed 'unknown'. cargo package ships Cargo.lock at the
package root, beside build.rs, but the script looked one level up — so it
worked in the workspace and failed in every installed build, the one
place the line has to work. Cargo also regenerates the workspace lock
before build scripts run, making the failure unreachable in-tree.
Verified against an extracted .crate: now reports 0.5.1.

A two-version lockfile reported the lowest. Cargo sorts by name then
version ascending, so first-match named 0.4.0 while linking 0.5.1 — the
exact skew this line exists to expose, asserted backwards. Now reports
ambiguous(...) rather than confidently naming the wrong one.

Collecting every match then over-collected: cargo emits [[patch.unused]]
in the same shape, so a local [patch.crates-io] override — the obvious
way to test a fix — made a single resolved version report as ambiguous.
Now scoped to [[package]] sections. Verified by appending a real
patch.unused entry.

Note yoagent-state 0.5.2 adds a VERSION const, which reports what was
linked rather than what a lockfile resolved and makes this whole script
unnecessary. Switch once it publishes.

Also ignores .DS_Store — one was sitting untracked in fixture/, the
canonical artifact every runtime must restore.

23 passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@yuanhao
yuanhao merged commit 0c5c4c1 into main Aug 28, 2026
2 checks passed
@yuanhao
yuanhao deleted the fix/2-track-runtime-state-version branch August 28, 2026 10:09
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.

conformance-check pins yoagent-state 0.4 while certified runtimes are on 0.5 — yoagent#168 will make the skew produce wrong verdicts

1 participant