fix(sync): bound the fetch without coreutils, and cover the hook - #67
Merged
Merged
Conversation
`scripts/sync-checkout.sh` bounded its fetch with `timeout 15`, which is GNU coreutils and is not on a stock macOS. There it exited 127 — command not found — which the guard could not tell from a failed fetch, so every session on a Mac reported "could not reach origin (offline?)" while the network was fine, and then worked from a stale `main`. That is the exact outcome the script exists to prevent, and it hid in the one place nothing looks: the SessionStart hook runs before anyone is watching, and an "offline" line reads as a blip. `fetch_bounded` now prefers `timeout`, falls back to Homebrew's `gtimeout`, and otherwise runs a portable watchdog — polling rather than `wait -n`, since macOS ships bash 3.2. The bound is kept: an unbounded fetch in a session-start hook would hang the session open. The timeout is named once, as FETCH_TIMEOUT_SECS. `scripts/sync-selftest.sh` is new, and is why this could ship: sync-checkout was the only script in scripts/ with no coverage. It runs every case against a throwaway origin on disk under a PATH holding only the tools the script may use, so it reproduces the stock-macOS condition on any platform and needs no network. Beyond the regression it holds the contract around it — a real outage is still reported, each of the three refusals changes no tracked state, a fast-forward that brings instructions raises restart-lead, and the hook always gets one parseable JSON object and exit 0. Wired into `./scripts/check.sh` as `sync`, beside the other five selftests. Co-Authored-By: Claude Opus 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.
Intent
scripts/sync-checkout.shbounded its fetch withtimeout 15, which is GNUcoreutils and is not on a stock macOS. There it exits 127 — command not
found — which the guard cannot tell from a failed fetch. So every session on a
Mac reported
could not reach origin (offline?)while the network was fine,and then worked from a stale
main.That is the exact outcome the script exists to prevent, and it hid in the one
place nothing looks: the
SessionStarthook runs before anyone is watching,and an "offline" line reads as a blip, so nobody investigates.
Observed on a Mac where
git ls-remoteanswered in 0.27s while the hookreported the origin unreachable, and the checkout sat a commit behind for days.
What changed
fetch_boundedpreferstimeout, falls back to Homebrew'sgtimeout, andotherwise runs a portable watchdog — polling rather than
wait -n, since macOSships bash 3.2. The bound is kept deliberately: an unbounded fetch in a
session-start hook would hang the session open. The timeout is named once, as
FETCH_TIMEOUT_SECS.scripts/sync-selftest.shis new, and is why this could ship in the firstplace —
sync-checkout.shwas the only script inscripts/with no coverage.Each case runs against a throwaway origin on disk under a PATH holding only the
tools the script may use, so it reproduces the stock-macOS condition on any
platform and needs no network. That sandbox has a second effect worth keeping:
a tool the script starts reaching for without declaring shows up as a failure
rather than as a silent dependency on whatever the developer happened to have.
Beyond the regression it holds the contract around it:
reported and changes no tracked state;
INSTRUCTION_PATHraisesrestart-lead, and onethat does not, does not;
Wired into
./scripts/check.shassync, beside the other five selftests.Verification
Written test-first. The selftest failed for the right reason before the fix —
the false "offline" swallows the script's entire behaviour, so all 11
assertions fell:
After the fix, all green, and
check.sh shellis shellcheck-clean. Thecoreutils case declares itself skipped rather than failed on a host with
neither
timeoutnorgtimeout— which is every stock macOS, and exactly thehost this bug came from.
Locally green:
sync,shell,yaml,profiles,skills,voice. The fullcheck.shwas not completed in one piece on this machine —queue-selftest.shruns long and the session was restarted under it more than once — and
paneand
markdownskip for want ofluaandrumdl. CI runs the whole thing.🤖 Generated with Claude Code