Repository navigation
Read worktree paths as bytes; say which commits a release is - #13
Merged
Merged
Conversation
The verifier decoded `git status -z` paths and symlink targets as strict UTF-8. On Linux a filename is any byte string, so one untracked file named with a stray 0xff byte ended `run`, `baseline` and `attest` in a UnicodeDecodeError - exit 1, which this tool uses for FAIL - where the state should have been identified or refused as BLOCKED. Paths now go through os.fsdecode and os.fsencode. That is the identity on every valid UTF-8 path, so no state id that could be computed before changes; a test pins that against the v0.3.1 verifier over non-ASCII names, a rename, symlinks and a nested repository. An undecodable byte becomes a surrogate escape that encodes back to the same byte, and a record naming such a path in drift_paths is written as a JSON escape, ASCII-safe, and loads back unchanged: the existing serialization is sufficient, as a regression shows. A path that cannot be read is now BLOCKED naming it rather than a traceback. This also closes a silent omission: os.walk skipped a directory it could not list inside an untracked nested repository, so the state id quietly left part of the tree out. The walk now raises, and that too is BLOCKED. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016m6di8xFVdwwq949mJx9U1
INSTALL.md said "There are no release tags yet" and that once releases were cut a tag would be the thing to pin. v0.3.0 and v0.3.1 exist, and a tag here can still be moved or deleted, so the first sentence was false and the second would have pointed users at a mutable name. The pin section now tells users to pin a full commit id, and names each release by the two commits it really is: the commit its tag resolves to, whose scripts install it, and the runtime commit those scripts pin. For 0.3.1 that is v0.3.1 -> 84418d8 and runtime 819b7c2. Tags become a pin only once they are protected against being moved; this change sets no tag policy. A test checks the table against the repository rather than trusting it: the tag resolves to the named commit, the installer there pins the named runtime, and that runtime reports the named version. The invariant block the installer digests is untouched (INV_SHA 282f2117...). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016m6di8xFVdwwq949mJx9U1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Work package: the two findings from the
v0.3.1review. Branched frommainat3120337. It keeps contractevidence-v3.2, and the version bump to 0.3.2 follows in its own PR, as #10 did.cf878d8bad\xffnameendedrun,baselineandattestin aUnicodeDecodeError: exit 1, which means FAIL here, not BLOCKED.os.fsdecodefor git's path bytes andos.fsencodewherever a path or link target goes into the digest. A path that can't be read is now BLOCKED (exit 2), naming the path.2e78da6INSTALL.mdsaid "There are no release tags yet" and that a tag would become the thing to pin. Tags exist and can still be moved.v0.3.1→84418d8, and the runtime it pins,819b7c2. No tag policy change.What the tests decided
fsdecode/fsencodeproduce exactly the old bytes.test_valid_utf8_state_ids_are_unchanged_from_the_strict_verifierloads thev0.3.1verifier and comparesstate_idandstate_pathsover a tree with non-ASCII names, a staged rename, symlinks and a nested repository. They are identical.drift_pathsneeds no new encoding. A gate that createsmade\xffproduces drift that is BLOCKED and named in the record, under both evidence/2 and evidence/3. The record is ASCII ("made\udcff", viajson.dumps' default escaping) and loads back unchanged.test_a_record_naming_an_undecodable_path_survives_emit_and_loadpins thatemit→load_jsonround-trip, and the bytes surviveos.fsencode.os.walkskipped any directory it could not list. With an unreadable directory inside an untracked nested repository,v0.3.1reported PASS over a state ID that left that directory out. The walk now raises, and that is BLOCKED.Regressions
tests/test_path_bytes.py, 11 cases:emit/load_jsonround-trip;v0.3.1;The last two skip as root and run in CI, whose runner is not root.
tests/test_verifier_contract.py:test_install_names_each_release_by_the_commits_it_really_ischecks each row of the release table against the repository. The tag must resolve to the named commit, the installer at that commit must pin the named runtime, and that runtime must report the named version.Verification
On
2e78da6, against a baseline captured onmainat3120337. The baseline gates were green: lint clean, 420 passed / 1 skipped.drift: false.v0.3.1verifier, 9 of 11 new tests fail. The two that pass are the equality and round-trip pins, which hold on both by design.INSTALL.mdand on a table row naming the wrong runtime pin.INV_SHAdigests (282f2117…).2e78da6): lint, tests-py311, tests-py314 PASS, imported withimport-ci. The pull_request run 36208371594 is also green. I could not read the job logs from this session (the log host is blocked by network policy), so the non-root run of the permission cases above is my evidence for those two.sha256:0c257351…eb93e2a): 0 blocked required gates, 0 stale, STATE MATCH TRUE, CLEAN CLIMB TRUE, READY FOR HUMAN GATE TRUE.After this
VERSION,pyproject, and a CHANGELOG entry, stillevidence-v3.2.main.Tag protection (a
v*ruleset) is a separate administrative step and is not part of this PR.🤖 Generated with Claude Code
https://claude.ai/code/session_016m6di8xFVdwwq949mJx9U1
Generated by Claude Code