You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Scope shrank with #109 (merge 77e64ad), which closed #95 by choosing option A: the daemon routes through the shared pass, so there is one pass and one history write path. What is left of this issue is the thin-wrapper half: file-organizer-watch is still its own cron-based daemon with catchup and scheduler-state.json, not a loop over --once, and cron/systemd is not documented as the primary path. Note that catchup reads scheduler-state.json and lives in the daemon, not the pass, so a thin loop needs somewhere for that state to go or it goes away.
The daemon keeps only the schedule. A tick now runs exactly what file-organizer-watch once <directory> --apply --source scheduled runs, and adds nothing a crontab line doesn't have: the 3-attempt retry wrapper is gone. A thrown pass is logged once and the schedule's next tick is the retry (082a118).
What already existed and is now the whole story. Since #95 the daemon and a cron once line funnel into the same runOrganizePass with source: "scheduled", the same history row, the same rollback manifest, and the same per-directory tuning (the pass looks it up for the cron path; the daemon supplies it explicitly, which is what keeps legacy autoOrganize entries behaving). The two paths already produced byte-identical passes; the duplication was in the lifecycle wrapper around the tick, and the retry was the one piece of it that had no counterpart in the cron path.
Deliberate choice: in-process shared pass, not spawning the once CLI child. "Runs --once in a loop" could be read as the daemon shelling out to the CLI per tick. Not taken: the pass module is already the one mechanism, so a child process would add bin-path resolution, stdout plumbing, and subprocess bookkeeping with zero semantic gain, and it would break the injectable-pass test seam. The tick body is now argument-for-argument the call the once CLI makes.
Kept, on purpose.noOverlap plus the in-flight guard (real overlap protection across concurrent ticks and manual triggers), tuning supply for legacy entries, startup catchup with catchup_mode, and scheduler-state.json as daemon-only bookkeeping. The last two stay until the fog item on #92 (what scheduler-state is for once --once writes history) is decided; nothing here makes that decision.
Proof. Real daemon in a sandbox home (HOME/XDG_CONFIG_HOME redirected, data inside the sandbox home): a */10 * * * * * watch organized the folder within 35 seconds, wrote 5 history rows all labelled scheduled (first carried filesProcessed: 3 and a rollback manifest id, the rest honest no-ops), and stamped scheduler-state.json with lastRunTime and the schedule. A second run with the path gate refusing the directory logged exactly one failure per tick at :20, :30, :40 with no re-attempts. Unit red-check: the new one-attempt test fails against the old retry code.
Docs. cron/systemd as the primary path was already stated in README (shipped with #121); the daemon's module header and the watch CLI header now say it too, and describe the daemon as the convenience loop it is.
Activity
kridaydave commentedon Oct 12, 2026
Scope shrank with #109 (merge
77e64ad), which closed #95 by choosing option A: the daemon routes through the shared pass, so there is one pass and one history write path. What is left of this issue is the thin-wrapper half:file-organizer-watchis still its own cron-based daemon with catchup andscheduler-state.json, not a loop over--once, and cron/systemd is not documented as the primary path. Note that catchup readsscheduler-state.jsonand lives in the daemon, not the pass, so a thin loop needs somewhere for that state to go or it goes away.kridaydave commentedon Oct 20, 2026
Resolution
The daemon keeps only the schedule. A tick now runs exactly what
file-organizer-watch once <directory> --apply --source scheduledruns, and adds nothing a crontab line doesn't have: the 3-attempt retry wrapper is gone. A thrown pass is logged once and the schedule's next tick is the retry (082a118).What already existed and is now the whole story. Since #95 the daemon and a cron
onceline funnel into the samerunOrganizePasswithsource: "scheduled", the same history row, the same rollback manifest, and the same per-directory tuning (the pass looks it up for the cron path; the daemon supplies it explicitly, which is what keeps legacyautoOrganizeentries behaving). The two paths already produced byte-identical passes; the duplication was in the lifecycle wrapper around the tick, and the retry was the one piece of it that had no counterpart in the cron path.Deliberate choice: in-process shared pass, not spawning the
onceCLI child. "Runs --once in a loop" could be read as the daemon shelling out to the CLI per tick. Not taken: the pass module is already the one mechanism, so a child process would add bin-path resolution, stdout plumbing, and subprocess bookkeeping with zero semantic gain, and it would break the injectable-pass test seam. The tick body is now argument-for-argument the call theonceCLI makes.Kept, on purpose.
noOverlapplus the in-flight guard (real overlap protection across concurrent ticks and manual triggers), tuning supply for legacy entries, startup catchup withcatchup_mode, andscheduler-state.jsonas daemon-only bookkeeping. The last two stay until the fog item on #92 (what scheduler-state is for once--oncewrites history) is decided; nothing here makes that decision.Proof. Real daemon in a sandbox home (
HOME/XDG_CONFIG_HOMEredirected, data inside the sandbox home): a*/10 * * * * *watch organized the folder within 35 seconds, wrote 5 history rows all labelledscheduled(first carriedfilesProcessed: 3and a rollback manifest id, the rest honest no-ops), and stampedscheduler-state.jsonwithlastRunTimeand the schedule. A second run with the path gate refusing the directory logged exactly one failure per tick at :20, :30, :40 with no re-attempts. Unit red-check: the new one-attempt test fails against the old retry code.Docs. cron/systemd as the primary path was already stated in README (shipped with #121); the daemon's module header and the watch CLI header now say it too, and describe the daemon as the convenience loop it is.