Start Miri at login: check launch at login end to end and show when it will not work#201

opened9d agobySSamanyu Das

Note

🤖 Claude Opus 5.5 responding on behalf of Sam

Draft type: enhancement. Source: Sam asked on 1 November 2026 for Miri to start at login. Related: #199 (the shared edge right after a start), decisions/release-scope.md (Startup and crash recovery), decisions/setup.md, decisions/configuration.md (App settings and lifecycle).

Goal

The installed Miri app starts at login, so window management is running when the user logs in. The decisions already require this: Settings offers launch at login, on by default, stored in [miri].

What exists

  • Settings > General has a Launch at login toggle, stored as [miri].launch_at_login, on by default.
  • AppModel.syncLoginItem in Sources/Miri/AppModel.swift registers SMAppService.mainApp when the toggle is on and unregisters it when it is off.
  • It registers only when Miri runs from an Applications folder. Elsewhere, Settings shows "Launch at login takes effect after you move Miri to the Applications folder."

It has not been checked end to end with a log out and log in.

Open points

  • macOS can hold a login item for approval, or the user can turn it off in System Settings > General > Login Items. syncLoginItem does not show either state, so the toggle can read on while Miri will not start.
  • syncLoginItem runs at launch and on a toggle change, so a change made in System Settings while Miri runs does not show until the next launch.
  • A first launch from outside Applications, such as straight from the mounted disk image, registers nothing. Miri could say so in setup, not only in Settings.

Acceptance criteria

  • Miri installed in /Applications registers itself at first launch, and a log out and log in starts it with window management running and no new permission prompts.
  • Turning the toggle off removes the login item, and the next login does not start Miri.
  • When macOS holds the login item for approval, or the user turned it off in System Settings, Settings says Miri will not start at login and opens Login Items.
  • Started at login, Miri shows the shared-edge slices of parked windows (#199).

Note

🤖 Claude Opus 5.5 responding on behalf of Sam

Triage on main at ffefedfa: the root cause is that syncLoginItem treats the login item as on or off, but macOS has four states. There is no running defect to reproduce yet, because no installed copy exists on this Mac and a log out ends every live session.

Root cause

syncLoginItem in Sources/Miri/AppModel.swift compares SMAppService.mainApp.status only with .enabled. The SDK has four states: .notRegistered, .enabled, .requiresApproval, and .notFound. The macOS 27 SDK header SMAppService.h says .requiresApproval means "the user has denied execution". The same header says register() then fails with kSMErrorLaunchDeniedByUser.

From the code and the header, not observed on a running app:

  1. With the toggle on and the item turned off in System Settings, Miri calls register() again. It fails, and Settings shows "Launch at login could not be changed." followed by the macOS error text. That text does not say that Miri will not start at login, and Settings has no way to open Login Items.
  2. Miri reads the status only at launch and on a toggle change, so a change in System Settings while Miri runs never reaches Settings.
  3. With the toggle off and the status .requiresApproval, Miri skips unregister(), so the item stays listed in Login Items. It does not start Miri, but it stays in the list.

The Applications-folder guard and the .enabled and .notRegistered paths match criteria 1 and 2 on paper. They have not been run through a log out and log in.

What I checked

  • No Miri copy is in /Applications or ~/Applications, and Spotlight finds no io.github.samanyudas.miri* bundle there. The running Miri Developer is in /tmp, so syncLoginItem stops at the Applications guard.
  • sfltool dumpbtm waits for an administrator password, so I stopped it and did not read the system's login item list.
  • #199 is closed, so criterion 4 checks that its fix also holds for a start at login.

Criteria

Criteria 1, 2, and 4 need a real log out and log in. An agent cannot run that check on this Mac, because a log out ends every agent session and live test. They are hand checks for Sam. Criterion 3 can have a unit test over the status-to-message mapping, plus one live check in which the item is turned off in System Settings > General > Login Items.

Proposed wording changes, so that the criteria name the build that exists:

  • Criterion 1: "Miri Developer, copied from the DMG to /Applications, registers itself at first launch..."
  • Criterion 3: "...Settings says Miri will not start at login and shows a button that opens Login Items. Settings shows the current state when it opens, without a Miri restart."

Fix options

  1. Model the state (recommended). Map all four SMAppService.Status values to one login item state in MiriCore. The state gives the Settings message and the Open Login Items button, which calls SMAppService.openSystemSettingsLoginItems(). Read it again each time Settings opens or Miri becomes active. Turning the toggle off unregisters every registered state, not only .enabled.
  2. Special-case .requiresApproval only. This is a smaller diff, but .notFound and the stale status after a change in System Settings stay unhandled.

Setup could also warn on a first launch from outside Applications, which is open point 3. The criteria do not ask for this, so I left it out of both options.