<!-- Add the related story/sub-task/bug number, like Resolves#123, or
remove if NA -->
**Related issue:** Resolves#49417
Makes an "app was open" result on a patch-when-closed policy install a
**skip** rather than a failure:
- Detects the skip in `SaveHostSoftwareInstallResult` — policy
`patch_when_closed` + empty pre-install output — and labels the
`installed_software` activity with `install_skipped_when_app_open`.
- Doesn't let the skip consume a retry attempt: stores `attempt_number =
0` (excluded from the retry-sequence count) and doesn't queue an
immediate retry — the next continuous-automation run re-fires.
- Shows "The app was open" copy in install details, distinct from the
generic pre-install-query-failed copy.
- Keys on the managed policy flag, not on empty output, so an ordinary
empty `pre_install_query` on a non-managed policy still fails and still
counts toward the retry limit.
The `changes/` entry for this feature lives on the base feature branch
per the stacked-PR convention.
# Checklist for submitter
- [x] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements).
- [x] Timeouts are implemented and retries are limited to avoid infinite
loops.
## Testing
- [x] Added/updated automated tests
- [x] Where appropriate, [automated tests simulate multiple hosts and
test for host
isolation](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/reference/patterns-backend.md#unit-testing)
(updates to one host's records do not affect another)
- [x] QA'd all new/changed functionality manually
- Drove the orbit `software_install/result` endpoint end-to-end against
a running server: app-open result → `failed_install`, activity
`install_skipped_when_app_open: true`, `attempt_number = 0`, no retry
queued, and install details showing "The app was open".
- Regression: an ordinary empty `pre_install_query` on a non-managed
policy still fails, counts (`attempt_number = 1`), and queues a retry.