Skip to content

Recurring schedules with spawn leases #1196

Description

@SK-DEV-AI

Recurring schedules with spawn leases

Problem

The schedule tool is one-shot: a wake fires once and is gone. Recurrence is
emulated by agents rescheduling themselves, which breaks permanently when a
wake is missed, delivery fails, or the chain errors — the next occurrence
only exists if the previous run successfully creates it. There is also no
guard against overlap: nothing stops occurrence N+1 from spawning while
occurrence N's worker is still running against the same memory graph.

Proposal

Interval recurrence plus a per-series spawn lease, additive to the existing
queue:

  • ScheduleRequest gains repeat: {every_minutes, max_iterations?}.
    Interval-based, no new dependency. Direct targets (resume/spawn) only:
    ambient cycles already re-read queued items every run, so repeating there
    would double-fire — scheduling it is a validation error, not silent
    behavior. every_minutes < 1 and max_iterations == 0 are rejected.
  • Each item carries RepeatState {every_minutes, remaining, recurrence_id, active_run?}, all serde-defaulted so old queue files load unchanged.
    recurrence_id is stable per series, so a whole series cancels at once
    (cancel_recurrence; the tool accepts recur_* IDs in action=cancel).
  • The next occurrence is re-queued before delivery runs, in the same save:
    the chain survives delivery failures, crashes, and missed wakes. A stale
    process coming back finds the next occurrence due, never a burst —
    scheduling is anchored at max(now, previous_due) + interval, so there is
    no catch-up storm.
  • Spawn lease: after a spawn delivery, the series is stamped with
    {owner_session, started_at}. A due occurrence whose owner is still live
    defers in place (due time pushed one interval, iteration count untouched).
    Dead owners (session file gone, closed/crashed/error status) and stale
    leases (older than three intervals) read as free, so a hung run blocks only
    its own series, and only boundedly. Session-target reminders take no lease:
    overlap there is harmless.
  • Tool surface: repeat_every_minutes / repeat_max_iterations on create,
    series shown in list output, debug queue JSON carries the new fields.

Reference implementation

Built and verified on origin/master (983b532). Two commits on the fork:

  • SK-DEV-AI/jcode@6562596 — the feature
  • SK-DEV-AI/jcode@7a4921b — zero-iteration rejection, import cleanup

Full diff:

SK-DEV-AI/jcode@1jehuang:master...SK-DEV-AI:feat/recurring-schedules

Files: ambient.rs (schema), ambient/persistence.rs (requeue + lease +
shared pop path), ambient/manager.rs (validation, series cancel, lease
stamp), ambient/runner.rs (stamp after spawn), tool/ambient.rs (params,
list, cancel), tests in ambient_tests.rs + tool/ambient/tests.rs.

Verification

  • 16 new tests pass: chain countdown, exhaustion at last iteration,
    unbounded mode, both pop paths, series cancel, both validation rejections
    plus the zero-iteration case, lease free/stale/live/closed transitions,
    stamp targeting, tool create→list→cancel round-trip.
  • Full jcode-app-core suite baselined against clean origin/master via
    worktree: identical pre-existing failure sets, zero regressions.
  • cargo fmt clean; no new clippy warnings in touched code.
  • One honest boundary: mechanics are verified against real queue and session
    files, but the long-horizon loop (occurrences days apart) cannot be
    compressed into a test — that part rides on the requeue-before-delivery
    ordering, which is tested.

Happy for this to land rewritten in your own shape, as with #1174.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions