Skip to content

pv update incurs a 10-second launchd throttle after switching app binary #300

Description

@munezaclovis

Summary

Updating the PV application can add an avoidable ~10-second delay while restarting the daemon. During a local update from PV 0.2.0 to 0.2.2, the first launchd spawn of the new binary failed with a launch-constraint violation. launchd then throttled the service for exactly ten seconds before a retry succeeded.

This was not caused by Managed Resource downloads. The Managed Resource phase refreshed/checked the manifest, found every installed resource current, and completed in approximately one second.

Environment

  • PV update: 0.2.0 → 0.2.2
  • macOS: 26.5.1 (25F80)
  • Darwin: 25.5.0

Observed timeline

22:30:19        PV 0.2.2 binary activated; daemon stopped
22:30:19.768    xpcproxy rejected the first daemon spawn
22:30:19.768    launchd deferred the next spawn by 10 seconds
22:30:21.337    launchd reported the service was still throttled
22:30:28.084    launchd reported the service was still throttled
22:30:29.871    daemon spawned successfully
22:30:29–30     Managed Resource update job completed with summary `current`

Relevant unified-log entries:

xpcproxy exited due to OS_REASON_CODESIGNING | Launch Constraint Violation,
error info: c[5]p[1]m[1]e[0], (Constraint not matched) ...
/Users/clovismuneza/.pv/bin/releases/0.2.2/pv

service spawn deferred by 10 seconds due to throttle

Successfully spawned pv[...] because inefficient

The update command took approximately 11 seconds overall.

Resource-update evidence

  • The daemon update job completed with summary current.
  • Installed Managed Resource release directories and binaries had no mtimes near the update.
  • No new Managed Resource artifact was installed.
  • A remote resource-manifest refresh/comparison still occurred, as expected.

Likely cause

This portion is strongly supported but not yet reproduced in an isolated test:

  1. The LaunchAgent uses the stable ~/.pv/bin/pv symlink.
  2. Self-update switches that symlink from the previous release binary to the new release binary.
  3. normalize_launch_agent considers the plist current because its text/path did not change.
  4. restart_daemon_without_reconciliation therefore runs only launchctl kickstart -k; it does not run bootout and bootstrap.
  5. The old and new binaries have different ad-hoc signing identifiers and CDHashes.
  6. macOS rejects the first spawn of the new symlink target against its launch constraint, then launchd's normal failure throttle delays the retry by ten seconds.

Confidence:

  • High: the ten-second launchd throttle caused the delay.
  • High: no Managed Resource binaries were downloaded.
  • Medium-high: reusing the existing launchd registration after changing the symlink target caused the constraint mismatch.

Expected behavior

After activating a new PV release, the daemon should start successfully on its first launch attempt. pv update should not silently depend on launchd's throttled retry.

Proposed narrow solution

When the PV application binary changes, reload the owned LaunchAgent registration before starting the daemon:

  1. launchctl bootout the existing PV service, tolerating the existing already-unloaded condition.
  2. launchctl bootstrap the current PV LaunchAgent plist.
  3. launchctl kickstart -k the newly registered service.
  4. Keep the current daemon health wait and rollback behavior.

The plist does not need to be rewritten if its contents are already current; only the launchd registration needs refreshing after activation.

Acceptance criteria

  • An app update reloads the LaunchAgent registration after switching the active binary.
  • The updated daemon starts on the first attempt without a launch-constraint violation or ten-second throttle.
  • The current rollback path still restores and starts the previous release if the updated daemon fails health checks.
  • Integration coverage asserts the app-update restart sequence and preserves the existing current-app/no-update behavior.
  • No changes are made to Managed Resource update/download behavior as part of this fix.

Notes

TLS/project reconciliation began later and was not responsible for the foreground update duration.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingpriority-mediumP2 medium priorityready-for-agentFully specified, ready for an AFK agentruntimeRuntime and toolchainvalue-mediumMedium value

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions