Skip to content

Loops open in a dialog like New Task, and can run every day at a set time - #276

Merged
pallaoro merged 2 commits into
mainfrom
the-plus-in-loops-sidebar-does
Oct 5, 2026
Merged

pallaoro merged 2 commits into
mainfrom
the-plus-in-loops-sidebar-does

Conversation

@pallaoro

@pallaoro pallaoro commented Oct 5, 2026

Copy link
Copy Markdown
Member

What changes

Loop dialog. Creating and editing a loop now happen in the same popup as the New Task composer, opened over whatever view you are on.

  • The sidebar's Loops + used to call a raw setView("loops"), which does nothing while a task is open full-width (the focused task covers every view). It now opens the dialog directly. A never-run loop's sidebar row had the same dead click; it now goes through goToView.
  • After a loop is created, the sidebar's Loops section opens so the loop lands somewhere visible.
  • Every dismissal (Escape, backdrop, Cancel) discards, like the task composer. The module-scope draft map existed only so the inline form survived a tab switch, which a modal can't have, so it is gone.

Schedule picker. The bare "every N min" field is now a list: every 15/30 min, 1/6/12 hours, Every day at [time], and Custom (minutes).

  • "Every day at" is stored as a 5-field cron plus the setter's IANA zone, so a UTC box still fires at their local time. croner (zero-dependency, MIT) computes slots and handles DST. It bundles into the box's cli.js, so boxes install nothing new.
  • A missed daily slot runs once on wake, the same rule intervals follow. An edit that sends no schedule keeps the existing one.
  • Protocol v13, with FEATURE_MIN_VERSION.loopSchedules. A v12 engine would drop cron on update and keep the old interval while the save looks applied, so the option is disabled for those boxes with a reason.

Bug fix. An interval longer than ~24.8 days fired immediately and then back to back, because setTimeout overflows past 2^31-1 ms. The runner now waits in hops.

Mobile. The phone shows a daily schedule read-only and no longer turns it hourly on save. It does not get the new picker yet.

Testing

  • scripts/typecheck.sh clean on the merged tree.
  • Server suite: 356 pass, 1 fail. The failure is system:hello handshake, which times out probing agent CLIs and fails identically on untouched main.
  • 13 new tests for calendar schedules, DST, slot math, mode switching, the long-timer hop and validation. The early-wake and timer-overflow tests were each checked to fail against the pre-fix code.
  • Driven in the dev app over CDP: created a daily 07:30 loop (engine scheduled tomorrow 07:30 Europe/Amsterdam), edited it through 6 hours and Custom 90, and deleted it. The dialog opens over a full-width task from both entry points.

Not verified

  • The disabled "Every day at" on an older box: the dev app could not reach boxes while the production app held the SSH forward port.
  • The mobile change was not compiled or run (Expo isn't installed in the worktree).

…time

Creating and editing a loop now happens in the same popup as the task
composer, opened over whatever view you are on. The sidebar's "+" used to
switch tabs without clearing a full-width task, so it did nothing visible;
it now opens the dialog directly, and a new loop's sidebar section expands
so the loop lands somewhere you can see it.

The schedule is a list instead of a bare minutes field: every 15/30 min,
1/6/12 hours, every day at a time, or custom minutes. "Every day at" is a
5-field cron stored with the setter's IANA zone, so a UTC box still fires
at their local time; croner computes slots and handles DST. A missed slot
runs once on wake, like intervals. This is protocol v11, and the option is
gated off for older engines, which would silently keep the old interval.

Also fixes intervals longer than ~24.8 days firing immediately and then
back to back (setTimeout overflow); the runner now waits in hops. The phone
shows a daily schedule read-only and no longer turns it hourly on save.
…ebar-does

# Conflicts:
#	apps/desktop/src/renderer/src/App.tsx
#	packages/protocol/src/index.ts
#	packages/server/src/dispatcher.ts
@pallaoro
pallaoro merged commit baebc6d into main Oct 5, 2026
1 check passed
@pallaoro pallaoro mentioned this pull request Oct 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant