fix(service): Mac PTY wrap parity + remove DISABLE_AUTOUPDATER#16
Merged
crisandrews merged 1 commit intomainfrom Apr 17, 2026
Merged
Conversation
…TER env var Two corrections to the service install defaults introduced in #9. 1. `generatePlist` (macOS/launchd) now wraps the invocation in `/usr/bin/script -q /dev/null <claudeBin> <args>` using BSD syntax. launchd services run without a controlling TTY by default, so the same SessionEnd-hook failure mode #9 fixed on Linux systemd could in principle hit Mac too. The wrapper is a preemptive parity fix using the same mechanism, validated locally on Mac (script -q works with claude --version / --help cleanly). 2. Remove `Environment=DISABLE_AUTOUPDATER=1` from the systemd unit. JD's own PR body called this a "defense-in-depth" extra — the PTY wrap alone suffices to fix the crash-loop. Setting the env var alters Claude Code's natural behavior (disables a feature it ships intentionally), which runs against this plugin's principle of being an OS-level envelope that doesn't modify Claude Code internals. Auto-update returns to working as designed. Together these two changes give Mac and Linux equivalent protection against the original crash-loop without forcing a config change on Claude Code itself.
JD2005L
added a commit
to JD2005L/ClawCode
that referenced
this pull request
Apr 17, 2026
Follow-up to crisandrews#16 with a clarified rationale for keeping the env var. The PTY wrap from crisandrews#9 fixes the SessionEnd-hook failure that turns graceful exit into code 1 and causes restart churn. DISABLE_AUTOUPDATER=1 addresses a different problem: Claude Code's in-process auto-updater regenerates files it manages mid-run, including the resume-on-restart wrapper script generated by crisandrews#7. A long-running daemon rewriting its own ExecStart target while live is a file-integrity issue, separate from the crash loop, and the PTY wrap does nothing for it. On the "don't modify Claude Code internals" principle from crisandrews#16: the principle stands, but DISABLE_AUTOUPDATER is a documented env var Claude Code exposes for this use case. Setting it is a supported interface, not a monkey-patch. The restored comment names the env var and the specific file-regeneration scenario inline so future readers see the intent. Also relevant to crisandrews#12 (/agent:update skill): the skill's explicit-manual- update flow assumes in-process auto-update is off in service mode. With auto-update running again, the skill competes with an updater that may silently rewrite service files behind the operator.
2 tasks
crisandrews
added a commit
that referenced
this pull request
Apr 17, 2026
Supersedes #17 (conflict resolution against post-#8 main). JD's #17 targeted pre-#8 main so the block collided with the newly-added HOME/TERM Environment lines. This commit places the same 8-line block immediately after HOME/TERM so systemd sees all three env vars side by side. The rationale (verbatim from #17, which is correct): PTY wrap and DISABLE_AUTOUPDATER fix different problems: - PTY wrap: SessionEnd hook needs a controlling terminal to spawn /bin/sh at graceful shutdown. - DISABLE_AUTOUPDATER: prevents Claude Code's in-process auto-updater from regenerating daemon-relevant files (including the resume-on-restart wrapper from #7) while the daemon is running. #16 incorrectly treated DISABLE_AUTOUPDATER as redundant defense-in-depth against the crash loop. It is actually addressing the file-integrity scenario, which the PTY wrap does not cover. Setting DISABLE_AUTOUPDATER is also using a documented Claude Code env var, not monkey-patching internals. macOS plist remains unchanged (JD's #17 scope; parity can follow once the systemd-side default settles). Co-Authored-By: JD2005L <34459020+JD2005L@users.noreply.github.com>
crisandrews
added a commit
that referenced
this pull request
Apr 17, 2026
Supersedes #17 (conflict resolution against post-#8 main). JD's #17 targeted pre-#8 main so the block collided with the newly-added HOME/TERM Environment lines. This commit places the same 8-line block immediately after HOME/TERM so systemd sees all three env vars side by side. The rationale (verbatim from #17, which is correct): PTY wrap and DISABLE_AUTOUPDATER fix different problems: - PTY wrap: SessionEnd hook needs a controlling terminal to spawn /bin/sh at graceful shutdown. - DISABLE_AUTOUPDATER: prevents Claude Code's in-process auto-updater from regenerating daemon-relevant files (including the resume-on-restart wrapper from #7) while the daemon is running. #16 incorrectly treated DISABLE_AUTOUPDATER as redundant defense-in-depth against the crash loop. It is actually addressing the file-integrity scenario, which the PTY wrap does not cover. Setting DISABLE_AUTOUPDATER is also using a documented Claude Code env var, not monkey-patching internals. macOS plist remains unchanged (JD's #17 scope; parity can follow once the systemd-side default settles). Co-authored-by: crisandrews <crisandrews@users.noreply.github.com> Co-authored-by: JD2005L <34459020+JD2005L@users.noreply.github.com>
crisandrews
added a commit
that referenced
this pull request
Apr 17, 2026
Summary of changes in this release (full detail in CHANGELOG.md): Added - Resume-on-restart wrapper for service mode (#7) - Service hardening defaults: HOME/TERM env, StartLimitBurst guard, persistent log path (#8) - /agent:update skill + heartbeat version-check with day-gate and per-version dedupe (#12) Fixed - WORKSPACE resolution so memory_search hits user's project dir, not plugin dir (#6, closes #5) - Linux systemd crash loop after Claude Code auto-updates mid-run — PTY wrap in ExecStart + DISABLE_AUTOUPDATER=1 for file-integrity (#9, #17/#18) - macOS launchd PTY wrap parity (#16) - Cross-user /agent:import discovery + post-import path sanity check (#10) Performance - reconcile-crons.sh fast-path on steady-state sessions (#11) Thanks to @JD2005L for the whole batch.
crisandrews
added a commit
that referenced
this pull request
Apr 17, 2026
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.
Follow-up to #9. Two corrections to the service-install defaults:
1. macOS PTY wrap parity.
generatePlistnow wraps the invocation in/usr/bin/script -q /dev/null <claudeBin> <args>(BSD syntax). launchd services run without a controlling TTY by default, same as systemd, so the SessionEnd-hook failure mode #9 fixed on Linux could hit Mac too. This is a preemptive parity fix using the same mechanism Linux got.Validated locally:
script -q /dev/null claude --versionand--helpboth return exit 0 with clean output, so the wrapper doesn't break the happy path.2. Remove
Environment=DISABLE_AUTOUPDATER=1from the systemd unit.JD called this "defense-in-depth" in #9 — the PTY wrap alone suffices to fix the crash-loop. Setting the env var disables a Claude Code feature it ships intentionally, which runs against this plugin's principle of being an OS-level envelope that doesn't modify Claude Code internals. Auto-update returns to working as designed on both platforms.
Test plan
script -q /dev/null /bin/echo hello→ exit 0script -q /dev/null claude --version→ exit 0script -q /dev/null claude --help→ exit 0buildPlan("install", ...)for darwin emits plist with/usr/bin/script -q /dev/nullprepended in ProgramArgumentsbuildPlan("install", ...)for linux emits unit withoutDISABLE_AUTOUPDATER=1line