Repository navigation
Conversation
A job launchd still had but whose process was not running (Driga.app moved) could not be repaired: install unloaded only a running job, and start's launchctl load refused one already loaded. The launchd controller now boots the job out before writing a changed plist, bootstraps or kickstarts it, and never uses the deprecated load/unload. Install replaces the plist whenever it differs from what it would write, including the recorded login environment. The plist keeps the job alive only while its executable exists, runs it as a Background process with a 30-second throttle, and the service exits when its executable is deleted. Builds without the release link-time marker install the dev label, so just build and go build no longer take over a release's service. Claude-Session: https://claude.ai/code/session_01XkoYQXnzjsCqogTYDHrxNJ
brew uninstall left a KeepAlive launch agent pointing at a deleted helper. The cask now uninstalls run.driga.service, signals a running app, and zaps Driga's data. Its uninstall_preflight keeps the plist so an upgrade, which runs the old uninstall, still restores the service. Release builds carry the internal/service.release marker. Claude-Session: https://claude.ai/code/session_01XkoYQXnzjsCqogTYDHrxNJ
The app spawned launchctl every two seconds. Observe now reuses the service manager's answer for 30 seconds while the lock and process are unchanged. When the login shell cannot be read, the app keeps the environment the service records instead of reinstalling it with a bare PATH. Claude-Session: https://claude.ai/code/session_01XkoYQXnzjsCqogTYDHrxNJ
Action tools now declare destructive, idempotent and open-world hints, and the generated reference lists them. README no longer calls driga mcp read-only or says the app runs the service itself, and the service docs describe dev labels, uninstalling and environment refresh. Claude-Session: https://claude.ai/code/session_01XkoYQXnzjsCqogTYDHrxNJ
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.
What changed
Fixes the macOS Driga service lifecycle and corrects the MCP and service docs. The findings came from an independent code review; each was checked against
mainbefore changing it.internal/service/launchd_darwin.gonow runs launchctl itself instead of going through kardianos. Install always runslaunchctl bootout gui/$UID/<label>(and ignores "not loaded") before it writes a changed plist. Start runsbootstrap gui/$UID <plist>on an unloaded job andkickstarton a loaded job whose process has exited. Restart useskickstart -k. Status comes fromlaunchctl print. Nothing callsloadorunloadany more. The fake launchctl models a launchd domain. In the new loaded-but-not-running test, a stale plist's executable no longer exists.docs/release.mddescribes the new sequence.uninstall launchctl: "run.driga.service", signal: [["TERM", "run.driga"]]and azap trash:list covering data, logs, config, caches, prefs, WebKit, saved state and the legacy collector plist. Homebrew skipssignalon upgrade, where the app restarts itself. I left outquitbecause it can stall Homebrew for 10 seconds with an Automation prompt. An upgrade runs the old version's uninstall,launchctlincluded, souninstall_preflightfirst saves the plist to~/Library/Caches/run.driga. The postflight reinstalls from the live plist, or from that copy if it is less than 10 minutes old, and then deletes the copy.For an app deleted without brew,
KeepAliveis nowPathState {<drigad path>: true}, so launchd stops respawning a missing helper and restarts the job when the path comes back. The service also checks its executable once a minute and exits cleanly when it is gone. The docs, the README and the cask caveats say to rundriga service uninstallbefore deleting the app by hand.-X github.com/labstack/driga/internal/service.release=true, which.goreleaser.yamlsets on all three builds. Every other build is a development build:just build,just install,go buildandgo run. It installsrun.driga.service.dev,driga-devon Linux orDriga service (dev)on Windows, and logs underdriga-dev. An executable nameddriga-<name>still uses<name>.docs/usage.md, the README source-build section and CONTRIBUTING are updated.driga mcpserves read views plus Driga dashboard edits. The README's paragraph saying the app "removes the service and runs it itself" now describes the current design and how to uninstall. The site's MCP guide and index are corrected too.All Chat action tools now have annotations, following Canon
mcp.md:run_command: destructive, open worldanswer_prompt,kill,cleanup: destructiveack_inbox: idempotent, not destructivejump,approve: not destructivesummarize: open world (it goes through the Claude CLI)The generated MCP reference now shows Destructive and Idempotent columns. A test requires every served tool to carry annotations, and every state-changing tool to set the destructive and open-world hints explicitly.
ProcessTypeBackgroundandThrottleInterval30. The app still checks the lock and process every 2 seconds, which needs no subprocess.Lifecyclereuses the service manager's answer (fromlaunchctl, systemd or Task Scheduler) for 30 seconds while those stay the same, and asks again as soon as the service starts or stops.service.RecordedEnvironment), so a bare Finder PATH never replaces it. On Linux and Windows an existing service keeps its environment, which the docs now state.Why
kardianos/service's darwin backend uses
launchctl load/unloadand checks status withlist. It cannot tell a loaded-but-not-running job from a stopped one, andloadrefuses a job launchd already has. The domain commands handle both cases. A link-time marker is the only build-independent way to tell a release from a checkout build, since nothing produced thedriga-devexecutable name.Risk and rollout
signalonly TERMs the Driga app when it is uninstalled.KeepAlive,ProcessTypeandThrottleIntervalkeys changed. That causes one service restart.run.driga.servicegets a second service under the.devlabel the next time they install. Rundriga service uninstallwith the old build first (documented in usage.md).ProcessType Backgroundapplies launchd's background resource policy (lower CPU and I/O priority) to the collector, which also serves Chat and actions. Watch whether the app feels slower;Adaptiveis the fallback.Contract changes
.dev/-devlabel.Verified
Run on macOS (darwin/arm64) on the host:
gofmt -l .is clean,go mod tidy -diff,go vet ./...(native,GOOS=windows,GOOS=linux)go run ./cmd/driga-docgen --check,go generate ./internal/configwith no.env.examplediffscripts/isolated.sh go test -count=1 ./...: all packages passscripts/isolated.sh go test -raceoninternal/service,internal/mcp,internal/desktop,scripts: pass (I did not run the fulljust race)just ui-check/design-check(UI untouched),goreleaser check/just snapshot(goreleaser is not installed locally; CI's snapshot job covers it),actionlint,govulncheckFollow-ups
SMAppService.agent(plistName:)with the plist inside the bundle) is deliberately left out of this PR. It would replace the launch agent registration and give users the Login Items UI.ProcessType Backgroundagainst real Chat and actions latency.https://claude.ai/code/session_01XkoYQXnzjsCqogTYDHrxNJ