Hash stamps with git hash-object, not shasum - #1
Merged
cleiter merged 1 commit intoSep 8, 2026
Conversation
stamp_digest was the one place still shelling out to shasum. decision_key already carries the reason not to: git hash-object rather than shasum/sha1sum/openssl: it is the one hashing tool guaranteed to be present, since desvio cannot run at all without git. worktree_fingerprint follows that rule too. This applies it to the third site. shasum ships with macOS Perl but is absent on stock Arch and on minimal container images, and its absence is silent rather than loud: the command substitution yields an empty digest, a missing stamp file also reads as the empty string, so stamp_changed compares "" against "" and reports 'unchanged'. The hook then skips its work on every build — including the first one on a virgin worktree, where the cache announces a hit before it has ever been populated. Observed on Arch with the examples/paseo config: desvio_install printed 'dependencies current - skipping npm ci' into a build tree with no node_modules at all, and the build failed four steps later in packages/protocol with 'Cannot find module zod-aot'. Before, with no stamp file: digest '', stamp_changed -> unchanged After, with no stamp file: digest 3996615849, stamp_changed -> changed git hash-object --stdin rather than passing the path, so the bytes are hashed as-is and no clean filter can change the digest. Existing stamps are invalidated once by the algorithm change, costing one extra run of each guarded hook. tests/run.sh: 464 passed, 0 failed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Changes recommended
stamp_changed still discards stamp_digest’s exit status, so a failed digest can collapse to an empty string and produce a false “unchanged” result.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Updates the stamp hashing mechanism in lib/common.sh to avoid relying on shasum, which can be absent on some Linux distros / minimal images and fail silently, leading to incorrect cache “hits” and skipped work.
Changes:
- Replace
shasum -a 256usage instamp_digestwithgit hash-object --stdin. - Expand inline documentation explaining why
git hash-objectis used for stamps and the failure mode being addressed.
File summaries
| File | Description |
|---|---|
| lib/common.sh | Switches stamp digest computation from shasum to git hash-object and documents the rationale/failure mode. |
Review details
- Files reviewed: 1/1 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
308
to
+313
| if [ -f "$f" ]; then | ||
| printf '%s\0%s\0' "$f" "$(shasum -a 256 < "$f" | cut -d' ' -f1)" | ||
| printf '%s\0%s\0' "$f" "$(git hash-object --stdin < "$f")" | ||
| else | ||
| printf '%s\0absent\0' "$f" | ||
| fi | ||
| done | shasum -a 256 | cut -d' ' -f1 ) | ||
| done | git hash-object --stdin ) |
Comment on lines
+295
to
+296
| # git hash-object rather than shasum, for the reason already given above | ||
| # decision_key: it is the one hashing tool guaranteed to be present, since |
Owner
|
Thanks for your contribution. Sorry for the delay, seems like I missed the notification. |
cleiter
added a commit
that referenced
this pull request
Sep 8, 2026
…lsof Issue #2, companion to #1: desvio_preflight resolved a running daemon's cwd with lsof alone. lsof is absent by default on Arch and on most minimal images, and its absence is silent — the command substitution yields an empty digest, the case statement matches no branch, and the hook returns success. The guard whose entire job is to refuse a build under a live daemon then permits it quietly, starting with the first build on a virgin worktree, and a rebuild rewrites dist/ beneath a process that lazily requires from it. Two more sites in start.sh share the lookup and are not merely degraded reporting: the "already serving this tree" check falls through to a swap it shouldn't make, and the post-start proof kills the daemon it just started and blames "something else won the port" when the real cause is a missing lsof. Fix: paseo_daemon_cwd(), duplicated in desvio.conf and start.sh per the existing convention for the pidfile parse (a shared source would break the "lift one script at a time" design these files already document) — /proc/$pid/cwd first, lsof as the fallback, probing /proc/self/cwd rather than the target's own entry so a daemon owned by another user still reaches lsof instead of being misread as "no procfs". desvio_preflight now treats a live daemon with an undeterminable cwd as a refusal, with a message naming both remedies (install lsof, or stop the daemon) — no escape-hatch env var, since a documented way to turn the guard off is a way to leave it off. start.sh had a second, independent bug at the same root: the confirmation prompt and the actual `daemon stop` were gated on "do we know its cwd" instead of "is a daemon live". With lsof missing that skipped the stop entirely, started a second daemon against a port the first one still held, and then read the OLD daemon's pid back out of the pidfile for the proof. Introduced CURRENT_PID alongside CURRENT_CWD so liveness and tree-knowledge are tracked separately, and moved the prompt, the live-agent listing, and the stop itself onto CURRENT_PID. The post-start proof, when the new daemon's tree can't be verified, now warns and keeps it rather than killing a daemon that answered `daemon status` just fine — killing it over an unrelated introspection gap would leave the user with nothing and the wrong diagnosis. install.sh is untouched: it's macOS-only by construction (ships a .app into /Applications, reads Info.plist via plutil), and lsof is part of the macOS base system, so the absence this issue describes can't occur there. tests/test-daemon-cwd.sh: paseo_daemon_cwd against real processes, unstubbed, so CI's matrix exercises both branches (/proc on Linux, lsof on macOS) without a conditional in the test; desvio_preflight's four outcomes with the helper stubbed — cwd inside the tree, cwd elsewhere, cwd undeterminable (the regression this fixes), and no pidfile — plus the <tree>-other sibling-prefix guard. Verified: 538 passed/0 failed (527 baseline + 11 new), both under default bash and under /bin/bash (macOS system 3.2.57); shellcheck clean. End to end against this machine's real, live daemon: shadowing lsof with a failing stub (no /proc on macOS, so this is the full Arch-shaped failure) now makes desvio_preflight die with the install-lsof message where before it silently returned 0; pointing DESVIO_WORKTREE at the daemon's own tree still dies with the original "serving from this tree" message. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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.
stamp_digest is the one place still shelling out to
shasum.decision_keyalready carries the reason not to:worktree_fingerprintfollows that rule too. This applies it to the third site.Why it matters
shasumships with macOS Perl. It is absent on stock Arch and on minimal container images — and its absence is silent, not loud:stamp_changedcompares""against""and reports unchangedThe guarded hook then skips its work on every build, starting with the first one on a virgin worktree — a cache announcing a hit before it has ever been populated.
How I hit it
Arch, using
examples/paseounmodified.desvio_installprinted:into a build tree with no
node_modulesat all. The build then failed four steps later insidepackages/protocol:which reads as a Paseo problem rather than a desvio one. That indirection is most of the cost.
Before / after
With no stamp file present:
stamp_changed3996615849f1116480b0e9ee7390769bafdc8f1fNotes
git hash-object --stdin < "$f"rather than passing the path, so the bytes are hashed as-is and no clean filter can alter the digest.stamp_changed— with git guaranteed present the empty-digest path disappears, and the comment abovestamp_digestsays turning a cache miss into a failed build is deliberately the wrong trade. Happy to add a guard if you want belt and braces.tests/run.sh: 464 passed, 0 failed, unchanged from baseline. Linux only here — I have no macOS box to confirm on.🤖 Generated with Claude Code