Skip to content

Ship DMG install with SMAppService and durable locks - #8

Merged
quantizor merged 1 commit into
mainfrom
feat/dmg-smappservice-locks
Jul 25, 2026
Merged

Ship DMG install with SMAppService and durable locks#8
quantizor merged 1 commit into
mainfrom
feat/dmg-smappservice-locks

Conversation

@quantizor

Copy link
Copy Markdown
Owner

Summary

  • Install from a DMG into /Applications with an in-app setup panel and Login Items (SMAppService) agent registration
  • Keep resource locks in the daemon so a mid-lock crash still resumes paused servers
  • Rebind the ad-hoc helper after upgrades instead of stalling on launchd's codesign / LWCR throttle

Test plan

  • make test
  • scripts/smoke.sh
  • Fresh install from make dmg (double-click app on volume → confirm → Applications handoff + Login Items)
  • Upgrade over an existing /Applications/devctl.app: daemon answers within a few seconds (no ~20s throttle hang)
  • Hold a devctl lock, kill devctld, confirm paused servers resume when the daemon returns
  • scripts/smoke-launchd.sh if touching agent lifecycle locally (mutates user launchd; refuses when an agent is already installed)

Users can install from a disk image into Applications with Login Items registration, and ad-hoc helper upgrades rebind instead of sitting in launchd's codesign throttle. Resource locks now live in the daemon so a mid-lock crash still resumes paused servers.
Copilot AI review requested due to automatic review settings July 25, 2026 04:09

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.

Pull request overview

This PR adds a DMG-first installation flow for devctl.app that relocates into /Applications, registers the daemon via Login Items (SMAppService), and improves daemon resilience: durable resource locks across daemon crashes plus faster helper recovery after ad-hoc upgrades (unregister/re-register rebind path). It also tightens process teardown correctness (PID reuse revalidation), improves health probing behavior, and upgrades Spotlight indexing to preserve engagement signals across incremental syncs.

Changes:

  • Add an in-app setup panel and installer plumbing (app/CLI/daemon packaging, relocation, hook offers), plus a notarized DMG build + release workflow.
  • Persist daemon resource locks (locks.json) so paused servers can be resumed after crashes, and move lock pause/resume ownership into the daemon.
  • Improve lifecycle reliability: SMAppService agent (ensure/unregister deep links, rebind policy), process-tree teardown revalidation, and health probing with a dedicated ephemeral URLSession.

Reviewed changes

Copilot reviewed 48 out of 51 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
Tests/DevCtlKitTests/WireTests.swift Adds regression test for atomic concurrent writes.
Tests/DevCtlKitTests/SpotlightLabelTests.swift Tests alternate names and ranking hint tiers.
Tests/DevCtlKitTests/SetupPlannerTests.swift Adds unit tests for setup/upgrade planning and installs.
Tests/DevCtlKitTests/DeepLinkNotificationActionTests.swift Tests default notification tap mapping to .open.
Tests/DevCtlKitTests/DaemonRecoveryPolicyTests.swift Tests recovery/rebind policies and launchd ExitTimeOut ceiling.
Tests/DevCtlDaemonCoreTests/ResourceLockTests.swift Adds durable-lock behavior tests (pause/refuse/resume/recover).
Tests/DevCtlDaemonCoreTests/ProcessTreeTests.swift Tests PID-reuse-safe identities and sweep result shapes.
Sources/DevCtlKit/Spotlight/SpotlightLabel.swift Adds Spotlight alternateNames + rankingHint helpers.
Sources/DevCtlKit/Setup/SetupPlanner.swift New installer planning + staged installs + quarantine clearing.
Sources/DevCtlKit/Protocol/Wire.swift Extends lock wire types and adds locks file/result models.
Sources/DevCtlKit/Paths/Paths.swift Adds agent.path, agent.rebind, locks.json paths; fixes temp naming.
Sources/DevCtlKit/Launchd/LaunchdAdmin.swift New shared LaunchdAdmin with app-first SMAppService path and legacy fallback.
Sources/DevCtlKit/Launchd/DaemonRecoveryPolicy.swift Adds poll-loop recovery decision logic with cooldown/intent.
Sources/DevCtlKit/Launchd/AgentRebindPolicy.swift Adds rebind heuristics for post-upgrade helper recovery.
Sources/DevCtlKit/DeepLink/DeepLinkNotificationAction.swift Supports body-tap default action id mapping to open.
Sources/DevCtlDaemonCore/Supervisor/ServerSupervisor.swift Uses descendant sweep result + PID revalidation on escalation.
Sources/DevCtlDaemonCore/Supervisor/ProcessTree.swift Introduces ProcessIdentity + result-typed sweep + revalidation.
Sources/DevCtlDaemonCore/Health/HealthProber.swift Uses dedicated ephemeral URLSession with short timeouts.
Sources/DevCtlDaemonCore/Control/ControlServer.swift Persists locks, adds crash recovery for locks, improves recoverAtStartup parallelism, and adds NW listener/connection state logging.
Sources/devctld/main.swift Applies agent.path PATH, improves fd limit handling, and unregisters power notifications on SIGTERM.
Sources/devctld/Info.plist Adds embedded Info.plist for helper identity.
Sources/DevCtlApp/SpotlightIndexer.swift Switches to incremental indexing with engagement preservation + ranking tiers.
Sources/DevCtlApp/SetupSession.swift Adds shared launch-time setup evaluation state.
Sources/DevCtlApp/SetupPerformer.swift Implements setup execution (copy/install/register hooks/relaunch).
Sources/DevCtlApp/SetupPanel.swift Adds first-run/upgrade UI with confirm flow.
Sources/DevCtlApp/DevCtlApp.swift Wires setup window + improves notification presentation + daemon-down UI.
Sources/DevCtlApp/DaemonModel.swift Adds auto-recovery state machine with intent/cooldown and SMAppService preference.
Sources/DevCtlApp/AppDeepLink.swift Adds daemon control deep links (ensure/unregister) and best-effort browser open.
Sources/DevCtlApp/AgentService.swift Implements SMAppService registration/unregister/rebind/approval handling.
Sources/devctl/LaunchdAdmin.swift Removes CLI-local LaunchdAdmin (now in DevCtlKit).
Sources/devctl/HookSupport.swift Adds CLISelf.daemonSibling for install candidates.
Sources/devctl/CLI.swift Routes daemon lifecycle through new LaunchdAdmin + updates lock to daemon-owned pause/resume.
scripts/smoke.sh Strengthens cleanup (stray grandchild reap) + validates app bundle contents.
scripts/smoke-launchd.sh Forces legacy path for deterministic launchd smoke.
scripts/notarize.sh Adds notarytool submit/staple script with safer key handling.
scripts/make-dmg.sh Adds DMG builder with optional Finder layout enforcement.
scripts/make-dmg-background.swift Generates DMG background image at build time.
scripts/make-app-bundle.sh Bundles CLI/daemon + in-bundle LaunchAgent, adds signing improvements.
README.md Updates quick start and build instructions for DMG/app flow.
Package.swift Embeds helper Info.plist via linker flags.
Makefile Adds dmg target; installs devctld sibling in make install.
docs/design.md Updates design doc for DMG/SMAppService/locks/Spotlight changes.
docs/cli-contract.md Updates lock semantics to daemon-owned + durable lock recovery.
CONTRIBUTING.md Documents DMG release workflow and required secrets.
CLAUDE.md Updates codebase map and operational notes for new installer/agent paths.
BACKLOG.md Removes now-delivered DMG item; updates remaining release work.
.gitignore Ignores dist/.
.github/workflows/release-dmg.yml Adds macOS workflow to build/sign/notarize/staple and upload DMG.
.changeset/dmg-smappservice-rebind.md Adds minor changeset for DMG + locks + rebind behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +202 to +203
proc.standardOutput = Pipe()
proc.standardError = Pipe()
Comment on lines +309 to +312
let pipe = Pipe()
proc.standardOutput = pipe
proc.standardError = Pipe()
do {
@quantizor
quantizor merged commit e503e93 into main Jul 25, 2026
4 checks passed
@quantizor
quantizor deleted the feat/dmg-smappservice-locks branch July 25, 2026 04:12
@github-actions github-actions Bot mentioned this pull request Jul 25, 2026
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