Summary
Deleting an entry from onebrain.yml schedule: and running onebrain schedule register --refresh leaves that entry's OS artifact installed and loaded. The job keeps firing on its old cron. Neither --refresh nor --remove can reach it: every cleanup path derives labels from the current config, so an entry that is no longer in the config has no code path that touches its artifact.
This is the "entry deleted" arm of the bug #352 already records as still open ("editing a schedule entry's args leaves the old artifact installed and firing, unreachable by --remove"). Filing it separately as a concrete, reproducible sub-case with a narrow ask, so it can ship independently of the full ownership redesign if that's the faster route.
Reproduction (onebrain 3.4.25, macOS, launchd)
onebrain.yml has six entries, including:
- cron: 30 8 * * *
skill: /digest
schedule register --status shows all six ✓; launchctl list | grep onebrain shows six loaded, including com.onebrain.digest.
- Remove the
/digest entry from onebrain.yml (five remain).
onebrain schedule register --refresh
Observed
(--refresh: re-emitting plists with current vault path)
✓ Wrote ~/Library/LaunchAgents/com.onebrain.daily.plist
✓ Wrote ~/Library/LaunchAgents/com.onebrain.weekly.plist
✓ Wrote ~/Library/LaunchAgents/com.onebrain.recap.plist
✓ Wrote ~/Library/LaunchAgents/com.onebrain.onebrain-search-reindex.plist
✓ Wrote ~/Library/LaunchAgents/com.onebrain.sh--c-cd--Users-keng-onebrain-ob-1--1ddd50b9.plist
Registered and activated 5 schedule entries with launchd.
schedule register --status → Registered schedules: 5 (looks clean)
ls ~/Library/LaunchAgents | grep onebrain → six plists, com.onebrain.digest.plist still present
launchctl list | grep onebrain → six jobs, com.onebrain.digest still loaded, exit 0
So --status reports the config, not the machine, and the removed job would have fired the next morning at 08:30 with nothing in the CLI's output hinting that it still existed.
onebrain schedule register --remove at this point would remove the five current entries and still not touch com.onebrain.digest (remove_entries iterates config.schedule).
Workaround used
launchctl bootout gui/$(id -u)/com.onebrain.digest
rm ~/Library/LaunchAgents/com.onebrain.digest.plist
Where it happens
crates/onebrain-cli/src/commands/register_schedule.rs @ v3.4.25:
Expected
schedule register (at minimum --refresh, ideally every real register) should reconcile installed artifacts vs. config: enumerate the com.onebrain.* artifacts on disk, keep the ones whose label is in current_labels, and backend::remove the rest — but only after confirming the artifact belongs to this vault, per the ownership direction in #352 (its ProgramArguments / working directory already carry --vault <path>, so the check is available without any ledger). An artifact that points at a different vault must be left alone and, ideally, mentioned in the output.
Concretely:
Related
Summary
Deleting an entry from
onebrain.ymlschedule:and runningonebrain schedule register --refreshleaves that entry's OS artifact installed and loaded. The job keeps firing on its old cron. Neither--refreshnor--removecan reach it: every cleanup path derives labels from the current config, so an entry that is no longer in the config has no code path that touches its artifact.This is the "entry deleted" arm of the bug #352 already records as still open ("editing a schedule entry's args leaves the old artifact installed and firing, unreachable by
--remove"). Filing it separately as a concrete, reproducible sub-case with a narrow ask, so it can ship independently of the full ownership redesign if that's the faster route.Reproduction (onebrain 3.4.25, macOS, launchd)
onebrain.ymlhas six entries, including:schedule register --statusshows all six✓;launchctl list | grep onebrainshows six loaded, includingcom.onebrain.digest./digestentry fromonebrain.yml(five remain).onebrain schedule register --refreshObserved
schedule register --status→Registered schedules: 5(looks clean)ls ~/Library/LaunchAgents | grep onebrain→ six plists,com.onebrain.digest.pliststill presentlaunchctl list | grep onebrain→ six jobs,com.onebrain.digeststill loaded, exit 0So
--statusreports the config, not the machine, and the removed job would have fired the next morning at 08:30 with nothing in the CLI's output hinting that it still existed.onebrain schedule register --removeat this point would remove the five current entries and still not touchcom.onebrain.digest(remove_entriesiteratesconfig.schedule).Workaround used
Where it happens
crates/onebrain-cli/src/commands/register_schedule.rs@ v3.4.25:run_with→ the install loop callscleanup_stale_labels(entry, ¤t_labels, …)per entry still in the config; it only removes that entry's legacy labels (pre-Scheduler: cron parser rejects steps/lists; command-mode plist path collides per binary; unimplemented #116 basename, pre-v3.4.21 truncation). An entry that disappeared from the config never enters the loop, so its artifact is never considered.remove_all→remove_entriesderives labels fromconfig.schedule— same blind spot, from the other direction.if entries.is_empty() { … return Ok(0) }— deleting the wholeschedule:block returns before any cleanup at all (already noted in Scheduler: redesign the installed-labels ledger around artifact OWNERSHIP, not bookkeeping #352).print_statusreports from the raw config, so it cannot show the orphan either.Expected
schedule register(at minimum--refresh, ideally every real register) should reconcile installed artifacts vs. config: enumerate thecom.onebrain.*artifacts on disk, keep the ones whose label is incurrent_labels, andbackend::removethe rest — but only after confirming the artifact belongs to this vault, per the ownership direction in #352 (itsProgramArguments/ working directory already carry--vault <path>, so the check is available without any ledger). An artifact that points at a different vault must be left alone and, ideally, mentioned in the output.Concretely:
--refreshremoves an installed artifact whose label is absent from the config and whose args point at this vault; prints✓ Removed schedule '…' (no longer in onebrain.yml).register(or document why refresh-only).schedule:block (emptyentries) still runs the sweep instead of early-returning.--statusreports the on-disk/loaded state, or at least flags⚠ installed but not in onebrain.yml: com.onebrain.digestso the drift is visible.--remove --dry-rundoes not delete (from Scheduler: redesign the installed-labels ledger around artifact OWNERSHIP, not bookkeeping #352's list; worth fixing in the same pass since the sweep makes--dry-runmore important).Related
cleanup_stale_labelshandles.