v1.39.1.0 fix(release-daemon): homebrew-aware PATH + doctor subcommand + upgrade migration#37
Merged
Merged
Conversation
…nchd/systemd Release daemon's plist used launchd's default PATH (omits /opt/homebrew/bin on Apple Silicon), so spawned `gh`/`bun`/`git` hit ENOENT and the queue froze with every PR marked `blocked: gh pr view failed`. Plist + systemd unit now bake a homebrew-aware PATH; a new `gstack-build release-daemon doctor` reports queue depth, daemon load state, PATH sanity, tool resolv- ability, and last 5 log lines. A v1.39.1.0 upgrade migration prints a one-time banner with the exact re-install + reload commands when the queue has records and either no daemon is installed or the plist predates the PATH fix. Two independent touchfiles keep the install- needed and reload-needed notices separable. VERSION → 1.39.1.0. cli.ts +355, cli.test.ts +208 (9 new tests covering the PATH helper and doctor report), migration script + test suite (6 darwin + 2 linux scenarios with mocked launchctl). One follow-up captured in TODOS.md (P3 migration telemetry). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
gen-skill-docs test asserts package.json.version === VERSION. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The v1.39.1.0 commit added a test asserting chooseMergePath(null) === 'ship-and-deploy' but didn't import the function. cli.test.ts now passes 202/202 (was 201/202 with ReferenceError on the new case). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adversarial pass during /ship surfaced unbounded readFileSync on ~/.gstack/release-daemon.err.log. On a long-running daemon the log can grow without bound (no log rotation directive in the plist). Cap the read at the last 16KB before splitting — we only ever emit the last 5 non-empty lines, so 16KB is plenty. Also captures a P0 TODO for 9 pre-existing test failures observed during this branch's /ship triage. None are caused by this branch (verified by stashing the diff and reproducing on main); they should be triaged in a follow-up to restore a green baseline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
anbangr
added a commit
that referenced
this pull request
May 16, 2026
….0 (release-daemon) Both my fork and upstream chose v1.39.1.0 in parallel: - Fork (this PR #37 at 548baca): release-daemon PATH fix + doctor subcommand + migration - Upstream (PR garrytan#1512 at f589770): EXIT PLAN MODE GATE blocking checklist Per user choice (B), kept my v1.39.1.0 entry as the fork's headline release and folded upstream's release notes in as a sub-section "Upstream sync: ExitPlanMode gate" inside the same v1.39.1.0 entry. The runtime at v1.39.1.0 now contains both feature sets. A disclosure note at the top of the entry calls out the parallel-version situation explicitly. Future fork ships will bump past v1.39.1.0 cleanly. Conflicts resolved: - CHANGELOG.md: kept my v1.39.1.0 heading + body; added upstream content as an "### Upstream sync" sub-section within the same entry. - scripts/resolvers/index.ts: kept fork-local `generateBuildCliCandidates`, added upstream's `generateExitPlanModeGate` import + resolver entry. - test/gen-skill-docs.test.ts: took upstream's preamble assertion rewrite (new behavior: no `NO REVIEWS YET` in operational skills, `EXIT PLAN MODE GATE` reference instead) — verified the regenerated office-hours SKILL.md matches the new test. Side effects of upstream's preamble retoning: 53 generated SKILL.md files regenerated by `bun run gen:skill-docs --host all`. Diff is mechanical: "Plan Status Footer" section in every skill's preamble now reads as a neutral forward reference to EXIT PLAN MODE GATE instead of imposing review-report rules on operational skills. Test status: - `bun test`: 1 failure, identical to the pre-existing P0 TODO from PR #37 (Step 4.8 fork overlay test). 8 other pre-existing failures apparently fixed by upstream changes (down from 9 to 1). - cli.test.ts (release-daemon work): all 202 tests pass. - migration-v1.39.1.0.test.ts: all 8 scenarios pass (6 darwin + 2 linux skipped on darwin host). Co-Authored-By: Claude Opus 4.7 (1M context) <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.
Summary
Fixes a real-world bug on this dev box: 61 PRs queued in
~/.gstack/build-state/release-queuewith nothing draining them. Root cause: launchd's defaultPATH=/usr/bin:/bin:/usr/sbin:/sbinomits/opt/homebrew/bin, so the daemon'sgh pr listcalls returned ENOENT and every PR was markedblocked: gh pr view failed. The daemon was running, but functionally inert.Substantive changes:
releaseDaemonDefaultPath(env)merges known-good prefixes (/opt/homebrew/bin,/usr/local/bin,~/.local/bin) with the install-timeprocess.env.PATH, deduplicated, ending in system defaults. The launchd plist now emits<EnvironmentVariables>with PATH+HOME; the systemd user unit now emitsEnvironment="PATH=...".gstack-build release-daemon doctorsubcommand — reports queue depth (by status), plist/unit presence + EnvironmentVariables check, launchctl/systemctl load state, the loaded PATH, tool resolvability under that PATH, last 16KB of err.log tail, and a single-word verdict. Exit code 0 only when HEALTHY.gstack-upgrademigration v1.39.1.0 — three independent one-time banner variants gated by per-state touchfiles: Notice A (queue + no daemon installed), macOS combined Notice B (plist exists but needs reload — either not loaded, or stale without EnvironmentVariables), Linux Notice B (unit exists but lacksEnvironment="PATH=). Idempotent; banner doesn't repeat once the touchfile is set.installReleaseDaemonstdout now teaches both the first-installlaunchctl loadcommand and the re-installlaunchctl unload && launchctl loadpair (and thedaemon-reload && restartequivalent on Linux).Commits:
536817b5feat(release-daemon): doctor subcommand + homebrew-aware PATH for launchd/systemda0ce8797chore: bump package.json version to 1.39.1.0 to match VERSION0278e756fix(test): add chooseMergePath to cli.test.ts imports (formatter-applied, fixes pre-existing test)8f4e81e6fix: cap release-daemon doctor err.log read at 16KB (adversarial finding from /ship)Test Coverage
build/orchestrator/__tests__/cli.test.ts(4 PATH helper tests, 4 doctor scenarios, 1 doctor parse smoke)build/orchestrator/__tests__/migration-v1.39.1.0.test.ts(6 darwin + 2 linux, mockslaunchctl listvia fake-binary on PATH)gstack-build release-daemon doctorcorrectly reportsVerdict: DAEMON_NOT_LOADEDwith 58 queued records and tool resolvability ofgh/git/bun.Tests: 192 → 211 (+19 new). 0 new regressions.
Pre-Landing Review
No critical issues. Adversarial pass found one informational finding — unbounded
readFileSyncon err.log — and auto-fixed it (16KB cap, commit8f4e81e6).Plan Completion
10/10 plan tasks done. Plan file:
~/.claude/plans/the-release-daemon-is-distributed-orbit.md.releaseDaemonDefaultPathhelper ✓renderLaunchdReleaseDaemonPlistpatched ✓renderSystemdReleaseDaemonServicepatched ✓installReleaseDaemonstdout updated ✓doctorsubcommand added ✓TODOS
gstack-migration-logbinary that migration scripts can call to emit banner-fired and state-resolved events./shiptriage on this branch — all unrelated (5 global-discover timeouts, 2 brain-rename stale-refs, 1 fork-overlay test, 1 dev-profile test). Verified pre-existing by stashing diff and reproducing on main.Documentation
Skipped automated /document-release for this PR — the new
doctorsubcommand is documented inline ingstack-buildhelp text and in the CHANGELOG release summary. No other doc files needed updates for a single new subcommand + bug fix.Test plan
gstack-build release-daemon doctoron broken-state machine — reports DAEMON_NOT_LOADED with full diagnostic🤖 Generated with Claude Code