Skip to content

Commit c3870b2

Browse files
committed
docs: flatten media-control nav, relocate scheduler orphan cleanup
1 parent 7e8567c commit c3870b2

3 files changed

Lines changed: 15 additions & 16 deletions

File tree

docs/runtime/media-control.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Media Control (Linux + macOS + Windows)
1+
# Media Control
22

33
OS-level media controls (play/pause, next/previous, seek, metadata) for your desktop app, exposed through a single cross-platform Kotlin API:
44

docs/runtime/scheduler.md

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,6 @@ The `scheduler` module registers background tasks with the OS so they run even w
1717

1818
[^min-interval]: Enforced at request construction — see [Minimum interval](#minimum-interval) for the exact `IllegalArgumentException` behavior.
1919

20-
### Orphan cleanup after uninstall
21-
22-
Users uninstall apps without thinking about background scheduled tasks. Without explicit cleanup, the OS would keep firing schedules pointing to a missing executable forever. Nucleus handles this differently per platform:
23-
24-
- **Linux** and **Windows** — the scheduler does *not* register the application binary directly. It writes a tiny wrapper script (`<taskId>.sh` in `nucleus/scheduler/<appId>/scripts/` on Linux, `<taskId>.vbs` in `%LOCALAPPDATA%\nucleus\scheduler\<appId>\scripts\` on Windows) and registers *that* with systemd / Task Scheduler. The wrapper checks whether the application binary still exists before invoking it. If it's gone, the wrapper **self-destructs**: it disables and deletes the systemd `.timer` / `.service` units (Linux) or removes the COM tasks under `\Nucleus\<appId>\` (Windows) via the same Schedule.Service API used to create them, deletes the persisted metadata, and finally removes itself. Net result: the next time the OS triggers a task whose app has been uninstalled, the schedule cleans itself up and stops firing.
25-
- **macOS** — **no automatic cleanup.** Unlike the Linux/Windows wrapper trick, the agent's `ProgramArguments` points directly at the application binary so the entry stays visible under its real name in System Settings → "Allow in the Background". The cost: when the user trashes the .app bundle, the orphaned `.plist` in `~/Library/LaunchAgents/` is **never** reclaimed by macOS, and launchd keeps attempting to spawn the missing binary forever — throttled by `ThrottleInterval` (10 s by default), logging `cannot spawn` to `system.log` on every attempt. SMAppService (macOS 13+) does not eliminate this either, and Apple ships no guidance for "graceful uninstall of a LaunchAgent" — the macOS ecosystem treats orphaned LaunchAgents as a known limitation that an explicit cleanup step has to handle. The mitigation Nucleus offers is **in-app**: call `DesktopTaskScheduler.cancelAll()` from your app's settings ("Disable background tasks") or from any in-app sign-out / reset flow — this unloads the agents and removes the plists cleanly while the binary is still around. If the user does a plain drag-to-trash without that step, the orphan leaks; the leftover plist then has to be removed manually:
26-
27-
```bash
28-
rm ~/Library/LaunchAgents/io.github.kdroidfilter.nucleus.<appId>.<taskId>.plist
29-
```
30-
31-
The failure mode in the meantime is the well-known throttled-log-spam — not a crash and not a security or correctness issue.
32-
3320
## Installation
3421

3522
```kotlin
@@ -631,6 +618,19 @@ Creates systemd user service and timer units in `~/.config/systemd/user/` (respe
631618

632619
Registers tasks under `\Nucleus\<appId>\` via a JNI bridge (`WindowsTaskSchedulerJni`) that calls the Task Scheduler 2.0 COM API (`ITaskService`, `ITaskFolder`, `ITaskDefinition`) — no `schtasks.exe` subprocess. Supports periodic, daily, weekly, logon, and one-shot triggers natively. The task action runs the [self-destructing wrapper script](#orphan-cleanup-after-uninstall) via `wscript.exe` (Windows-subsystem host — no console window flashes when the task fires) instead of invoking the application binary directly.
633620

621+
### Orphan cleanup after uninstall
622+
623+
Users uninstall apps without thinking about background scheduled tasks. Without explicit cleanup, the OS would keep firing schedules pointing to a missing executable forever. Nucleus handles this differently per platform:
624+
625+
- **Linux** and **Windows** — the scheduler does *not* register the application binary directly. It writes a tiny wrapper script (`<taskId>.sh` in `nucleus/scheduler/<appId>/scripts/` on Linux, `<taskId>.vbs` in `%LOCALAPPDATA%\nucleus\scheduler\<appId>\scripts\` on Windows) and registers *that* with systemd / Task Scheduler. The wrapper checks whether the application binary still exists before invoking it. If it's gone, the wrapper **self-destructs**: it disables and deletes the systemd `.timer` / `.service` units (Linux) or removes the COM tasks under `\Nucleus\<appId>\` (Windows) via the same Schedule.Service API used to create them, deletes the persisted metadata, and finally removes itself. Net result: the next time the OS triggers a task whose app has been uninstalled, the schedule cleans itself up and stops firing.
626+
- **macOS** — **no automatic cleanup.** Unlike the Linux/Windows wrapper trick, the agent's `ProgramArguments` points directly at the application binary so the entry stays visible under its real name in System Settings → "Allow in the Background". The cost: when the user trashes the .app bundle, the orphaned `.plist` in `~/Library/LaunchAgents/` is **never** reclaimed by macOS, and launchd keeps attempting to spawn the missing binary forever — throttled by `ThrottleInterval` (10 s by default), logging `cannot spawn` to `system.log` on every attempt. SMAppService (macOS 13+) does not eliminate this either, and Apple ships no guidance for "graceful uninstall of a LaunchAgent" — the macOS ecosystem treats orphaned LaunchAgents as a known limitation that an explicit cleanup step has to handle. The mitigation Nucleus offers is **in-app**: call `DesktopTaskScheduler.cancelAll()` from your app's settings ("Disable background tasks") or from any in-app sign-out / reset flow — this unloads the agents and removes the plists cleanly while the binary is still around. If the user does a plain drag-to-trash without that step, the orphan leaks; the leftover plist then has to be removed manually:
627+
628+
```bash
629+
rm ~/Library/LaunchAgents/io.github.kdroidfilter.nucleus.<appId>.<taskId>.plist
630+
```
631+
632+
The failure mode in the meantime is the well-known throttled-log-spam — not a crash and not a security or correctness issue.
633+
634634
## Testing
635635

636636
The `scheduler-testing` module provides two levels of test support, inspired by Android's `work-testing`.

mkdocs.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,7 @@ nav:
9797
- macOS: runtime/launcher-macos.md
9898
- Windows: runtime/launcher-windows.md
9999
- Linux: runtime/launcher-linux.md
100-
- Media Control:
101-
- Linux + macOS + Windows: runtime/media-control.md
100+
- Media Control: runtime/media-control.md
102101
- System:
103102
- Dark Mode Detector: runtime/darkmode-detector.md
104103
- System Color: runtime/system-color.md

0 commit comments

Comments
 (0)