Repository navigation
fix(sync): reload the scheduler unit on resume - #411
Merged
Merged
Conversation
`dot sync resume` rewrote a unit installed before #394 to `--store`, but neither service manager reads a rewritten unit file until it is told to. launchd keeps running the command line it cached when `load` finds the job already loaded (which a failing job usually is), and systemd keeps the ExecStart it read until `daemon-reload`. So the migration alone healed nothing, and the `pause && resume` recovery hint that `dot sync status` prints for a spawn-failed job did not work on either platform. ResumeKind now unloads the plist before loading it on macOS, ignoring the unload error the way InstallKind does, and runs a user-scoped `daemon-reload` before starting the timer on Linux. The lifecycle test helper pins the two-step resume, so a resume that skips the reload fails the tests that record service-manager argv. `dot sync target` no longer says a local target is recorded in the global user state for future workspaces: since #409 the target is written only to this workspace's store, and a workspace without one uses the detected cloud folder. Part of #394.
Codex Review Summary: ✅ CompletedThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
entelecheia
added a commit
that referenced
this pull request
Nov 9, 2026
Name the resume reload fix (#411), the peer pair upgrade, the exact anchor scope, the dot sync target change and the dot admit wait. Claude-Session: https://claude.ai/code/session_01Vb48Vt5iGMfc7MJWPTCWCL
entelecheia
added a commit
that referenced
this pull request
Nov 9, 2026
* chore(release): add v2.81.0 release notes Covers #399, #400, #401, #402, #404, #405, #407 and #409. #406 (spec docs) and #408 (test isolation) are left out per the release-note rules. Claude-Session: https://claude.ai/code/session_01Vb48Vt5iGMfc7MJWPTCWCL * chore(release): v2.81.0 notes, apply review findings Name the resume reload fix (#411), the peer pair upgrade, the exact anchor scope, the dot sync target change and the dot admit wait. Claude-Session: https://claude.ai/code/session_01Vb48Vt5iGMfc7MJWPTCWCL
The lifecycle row that injects a failure asserted every resume command for the kind. On Linux that is wrong: a failed `daemon-reload` returns before the timer is started, so `start` never runs. macOS differs only because launchd's unload error is ignored, which puts the failure on the load. The expectation now stops at the failing command on the platforms whose resume returns there, which is what the two implementations do. Product code is unchanged; the row is the only thing that was wrong, and only the ubuntu unit job can execute it.
The installed-kind check treats a row that recorded no lifecycle action as a vacuous fixture. The Linux resume row under an injected failure records `--user daemon-reload` alone, which the counter did not recognise, so the comparison passed and the row still failed on the counter (ubuntu unit CI). The prefix is now counted, and a small table test pins the counter on both platforms so this Mac notices an unrecognised command next time instead of the ubuntu job.
The repo's misspell linter runs with locale US, so the comment I added over TestSchedulerLifecycleActionCount failed `make lint` on one word. Comment text only.
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.
Part of #394.
What changes
dot sync resumerewrote a unit installed before #394 to--store, but neither servicemanager reads a rewritten unit file until it is told to:
launchctl loadfinds the job already loaded (a job that keeps failing isstill loaded) and leaves launchd running the command line it cached.
ResumeKindnowruns
launchctl unload <plist>first and ignores its error, the wayInstallKinddoes.ExecStartit read untildaemon-reload.ResumeKindnowruns
systemctl --user daemon-reloadbeforestart <timer>.So plain
dot sync resume --store=<s>heals a migrated unit, and thepause && resumerecovery hint that
dot sync statusprints for a spawn-failed job works on both platforms.The scheduler lifecycle test helper now pins resume as two commands (unload then load;
daemon-reload then start), so a resume that skips the reload fails the tests that record
service-manager argv.
Stale help
dot sync targetsaid a local target is also recorded in the global user state for futureworkspaces to inherit. Since #409 the target is written only to this workspace's store
(
<workspace>/.dotfiles/sync/config.yaml); a workspace without one syncs to the detectedcloud folder. The sentence and
docs/commands/dot_sync_target.md(make docs) say that now.Verification
dot admit -- env -u CLAUDE_CONFIG_DIR GOMAXPROCS=2 go test -p 2 -parallel 2 ./internal/syncer/... ./internal/cli/...passes.dot admit -- make lint: 0 issues.unloadstep fromResumeKindfailsTestSyncSchedulerLifecycle_InstalledKinds(every resume row),TestSyncPauseResumeReloadsSpawnFailedJobsand
TestSyncResumeMigratesAUnitInstalledWithTheRetiredStoreFlag; restored, they pass.same shared helper and is exercised by the CI test run.
Release note:
dot sync resumenow reloads the scheduler unit, so a unit that still ran the removed--profileflag is healed by it on both platforms.Round 2
CI's
unit (ubuntu-latest)job caught a wrong expectation, not a wrong product: on LinuxResumeKindreturns thedaemon-reloaderror beforestartruns, so the injected-failurerow could not end with
--user start.schedulerLifecycleCommandsnow stops the expectedlist at the failing command on platforms whose resume returns there (macOS keeps both because
the unload error is ignored and the failure lands on the load). Product code unchanged.
Verification for this round:
GOOS=linux go vet ./internal/syncer/and the nativego vet ./internal/syncer/both pass. The focusedgo testrow andmake lintweredeferred three times by the host admission gate ("memory pressure warning", five-minute
recovery streak not completed), so they are unverified on this host this round; the ubuntu
unit job is the executable proof for the Linux expectation.
Round 3
CI's ubuntu unit job was still red at the same row, one assertion later:
TestSyncSchedulerLifecycle_InstalledKinds/resume_stops_after_push_failurenow passed theargv comparison and failed the non-vacuity guard, because
schedulerLifecycleActionCountdid not recognise
--user daemon-reload— which is the only command a failed Linux resumerecords.
schedulerLifecycleActionCountcounts that prefix now, andTestSchedulerLifecycleActionCountpins the counter as a small table test that runs on every platform, so an unrecognised
command fails here instead of in the ubuntu job. Test-only; no product change.
Verification for this round:
gofmtclean, native andGOOS=linuxgo vet ./internal/syncer/pass. The focused
go testrun andmake lintwere deferred again by the host admission gate(memory-pressure episode still open after ~40 minutes, both attempts refused with the same
Retry after: 23s), so they are unverified on this host and the ubuntu unit job carries theproof of the Linux expectation.
Round 4
make lint(misspell, locale US) failed on one word:recognise->recognizein thecounter's comment. That word only; no code, no test logic.