Skip to content

fix(sync): reload the scheduler unit on resume - #411

Merged
entelecheia merged 4 commits into
mainfrom
fix/394-resume-reload-unit
Nov 10, 2026
Merged

entelecheia merged 4 commits into
mainfrom
fix/394-resume-reload-unit

Conversation

@entelecheia

@entelecheia entelecheia commented Nov 9, 2026 •

Copy link
Copy Markdown
Member

Part of #394.

What changes

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:

  • macOS: launchctl load finds the job already loaded (a job that keeps failing is
    still loaded) and leaves launchd running the command line it cached. ResumeKind now
    runs launchctl unload <plist> first and ignores its error, the way InstallKind does.
  • Linux: systemd keeps the ExecStart it read until daemon-reload. ResumeKind now
    runs systemctl --user daemon-reload before start <timer>.

So plain dot sync resume --store=<s> heals a migrated unit, and the pause && resume
recovery hint that dot sync status prints 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 target said a local target is also recorded in the global user state for future
workspaces 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 detected
cloud 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.
  • The tests bite: removing the unload step from ResumeKind fails
    TestSyncSchedulerLifecycle_InstalledKinds (every resume row), TestSyncPauseResumeReloadsSpawnFailedJobs
    and TestSyncResumeMigratesAUnitInstalledWithTheRetiredStoreFlag; restored, they pass.
  • The Linux half is compiled but not run here (macOS): its argv expectation comes from the
    same shared helper and is exercised by the CI test run.

Release note: dot sync resume now reloads the scheduler unit, so a unit that still ran the removed --profile flag is healed by it on both platforms.

Round 2

CI's unit (ubuntu-latest) job caught a wrong expectation, not a wrong product: on Linux
ResumeKind returns the daemon-reload error before start runs, so the injected-failure
row could not end with --user start. schedulerLifecycleCommands now stops the expected
list 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 native
go vet ./internal/syncer/ both pass. The focused go test row and make lint were
deferred 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_failure now passed the
argv comparison and failed the non-vacuity guard, because schedulerLifecycleActionCount
did not recognise --user daemon-reload — which is the only command a failed Linux resume
records.

schedulerLifecycleActionCount counts that prefix now, and TestSchedulerLifecycleActionCount
pins 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: gofmt clean, native and GOOS=linux go vet ./internal/syncer/
pass. The focused go test run and make lint were 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 the
proof of the Linux expectation.

Round 4

make lint (misspell, locale US) failed on one word: recognise -> recognize in the
counter's comment. That word only; no code, no test logic.

`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.
Copilot AI balanced review requested due to automatic review settings November 9, 2026 19:28

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota. Try requesting a review at a lower effort level.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 9, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-09T19:29:54.947372Z c312e71 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

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.
@entelecheia
entelecheia merged commit 9baf557 into main Nov 10, 2026
26 of 28 checks passed
@entelecheia
entelecheia deleted the fix/394-resume-reload-unit branch November 10, 2026 08:16
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.

2 participants