Memory interface
Scheduler owns timing, execution, status, and results; Memory owns long-term semantic facts. Stable references and existing Memory tools connect them, but they share neither state files nor write transactions. Agents can explicitly maintain associated Memory records while planning, executing, and closing tasks.
1. Goals and boundaries
One-time tasks
Store a timezone-aware run_at and execute once when due. Migrate dated legacy Commitments to this type.
Recurring tasks
Reuse existing five-field cron parsing, owner signatures, frozen sandbox policies, and non-interactive execution boundaries.
Monitoring tasks
Periodically execute observation prompts with cron. The first version records execution logs and state without promising cross-channel exactly-once notifications.
Explicit exclusions
The Memory writer does not infer tasks; Memory pages do not show Commitments; Scheduler service does not implicitly create or modify Memory. No cross-store Scheduler/Memory transactions, external-token permission model, placeholders or ID forwarding for deleted records, second executor, project management, task dependencies, or subtasks are introduced.
2. Target data relationships
workspace_id + memory_id.Worker tick exception boundary
If the initial @reboot tick or subsequent ordinary ticks raise Exception, the daemon writes a warning and full traceback to stderr and continues at the next minute boundary. It neither immediately replays nor compensates. After initial failure, subsequent ticks use reboot=False. Redirected stderr from the persistent OpenProgram worker goes to the global worker log; standalone foreground scheduler-worker displays it in the current terminal. Neither is an individual task execution log.run_once, KeyboardInterrupt, SystemExit and other BaseException remain fail-fast.
3. Public MemoryRef contract
| Field | Rule | Failure |
|---|---|---|
workspace_id | Must equal the stable ID of the current Memory workspace. | Reject implicit cross-workspace reads. |
memory_id | Must resolve to a unique Topic block at creation and update; reread content on every execution. | Reject missing, duplicate, cross-workspace, or malformed references on write. An invalid reference before execution fails the task without falling back to an old snapshot. |
| Write capability | MemoryRef itself grants no editing authority. Prompt tasks created by the local owner may use memory_status, memory_get and memory_update; all changes still use existing revision checks, format validation, derived views, and Git transactions. | Missing current revisions, malformed Topics, or concurrent updates fail the Memory transaction independently; Scheduler state is not rolled back. |
4. Agent lifecycle protocol
| Phase | Agent behavior | Boundary |
|---|---|---|
| Planning or creation | Only if the task itself merits long-term retention does the agent use memory_update.create_record to create a task record, then attach the returned memory_id as memory_refs to the Scheduler task. Ordinary temporary reminders need not write to Memory. | Execute the calls sequentially without treating them as one atomic transaction; explicitly report either failure to the agent. |
| Execution or state change | The prompt task receives current MemoryRef content at execution. If the record describes this task, the agent first reads its revision and original content, then uses update_record to write current status, results, or information to retain for the next execution. | Scheduler cannot use other side-effect tools such as shell, file writing, or network sending; added write capability is limited to existing Memory transaction tools. |
| Closure | If long-term value remains, update the associated record as completed, closed, or with the final result; if no retention value remains, delete it using delete_record. An interactive agent may also disable or delete the Scheduler task. | After deletion, the current Memory API no longer resolves the old ID. A Scheduler task still referencing it fails on the next resolution; remove or close the task first. |
Git recovery boundary
Commits preceding record deletion remain retrievable and recoverable from reachable history in the same Memory Git repository, provided changes were committed, the repository still exists, and history was not rewritten or pruned. Current memory_get queries only HEAD. Recoverability through Git history therefore does not mean an old memory_id can be queried directly through the current API, nor does it provide an absolute 100% guarantee.
5. Legacy Commitments migration
- Migrate only
openrecords. Dated records become enabled one-time tasks scheduled for 09:00 local time on that date. - Undated records become paused one-time drafts, retaining text and provenance until the owner sets a time.
- After successful migration, move the original
commitments.jsonlto the Scheduler migration archive. Repeated startups must not create duplicates. - Remove the
record_commitmentstool and prompts from the Memory writer. Retain the old runtime parser only for one-time migration compatibility.
6. Scheduler layout
The Scheduler uses the shared page header and theme tokens. Four compact filters occupy a horizontal row below the header instead of a full-height sidebar. Filters wrap at narrow container widths. Task rows retain their schedule, status and actions.
Empty state
The introduction and Create task action are centered within the available content area. Three suggestion cards share a centered, 960px maximum-width grid, with one column in narrow containers. Each card shows its title, schedule, prompt and a Use action that opens the existing form. Existing tasks remain a full-width list.
Acceptance: /scheduler has no dedicated category column; all four filters remain accessible at 360px, 768px and 1440px container widths; suggestions wrap without horizontal overflow; Create and Use open the existing form without creating a task; loading, search and error states retain their behavior. No scheduler API, persistence, execution or terminal TUI changes.
Implementation status: the layout passes specification and quality reviews, Web static checks, 152 unit tests, TypeScript checking, production build, and documentation link checks. Browser component previews at 360px, 768px and 1440px show no horizontal overflow; Create task and Use open the existing dialog. The default App also displays the layout correctly; Use opens the existing prefilled form and Escape returns to the Scheduler without creating a task.
7. Adoption, changes, and exclusions
| Reference | Handling |
|---|---|
| User-provided Scheduled tasks screenshot | Adopt a standalone page, Create, search, and one-time/recurring/monitoring product categories. Exclude a repeated hero title and large whitespace. Reuse OpenProgram’s ManagePageHeader, ManageRow, theme-consistent filter buttons, and theme tokens. |
| Existing OpenProgram cron | Retain execution safety and cron-compatible entry points, rename the product to Scheduler, and integrate it into the persistent worker. |
| Original Commitments/heartbeat | Do not retain it as a Memory type. Keep migration reads only, without the Memory tab, status field, or special heartbeat. |
| Automatic Scheduler writes to Memory | Reject implicit dual writes in the service layer. Let agents explicitly compose two existing tools and store the relationship in MemoryRef, avoiding new cross-store transactions and duplicate CRUD. |
8. Implementation brief and acceptance
This change is limited to the internal Memory lifecycle of Scheduler prompt tasks: retain existing Scheduler service, REST, UI, and Memory data formats; permit Scheduler-origin calls to memory_status, memory_get and memory_update, and extend Agent tool descriptions and execution-context prompts. Public RED tests must prove these three tools execute while shell, ordinary file writes, network sending, and Scheduler self-modification remain strictly denied. Memory writes continue through existing authority, revision, transaction, and Git paths. External API permissions are outside this implementation. After record deletion, current queries for the old memory_id return NOT_FOUND.
The separate worker reliability change isolates complete tick exceptions in run_forever and lets scheduler-worker and cron-worker bypass stdio redirection intended only for Ink TUI startup. It does not change Scheduler service, REST, task state formats, claim, spawn, or run_once.
| Acceptance item | Current evidence |
|---|---|
| Focused Python checks | Scheduler, permissions, cron CLI, and Memory routes/runtime total 133 passed; Ruff passes. |
| Memory lifecycle | The actual Scheduler prompt worker loads merged permission rules; local-owner tasks may use memory_status, memory_get and memory_update; deny/ask still take precedence and other side effects remain denied. An actual ScriptedProvider flow updated a Topic and created a Memory Git commit. If multiple MemoryRefs exceed the limit, retain every ID and Topic path, truncate only content, and mark truncation explicitly. Both specification and quality re-reviews passed. |
| Daemon-loop fault isolation | run_forever writes ordinary tick exceptions as warnings and full tracebacks to stderr (redirected to the global worker log for the persistent worker, displayed in the terminal for standalone foreground commands), waits for the original minute cadence, then continues with the next tick. Initial failure does not replay the @reboot tick. This output does not enter the task execution log.run_once still propagates exceptions immediately; KeyboardInterrupt and similar BaseException are not converted into retries. The canonical command, compatibility alias, and their --once/--list entry points all bypass TUI stdio redirection. Public regressions cover initial/ordinary ticks, normal waits, oversleep, the next tick, logging, CLI entry points, and both fail-fast boundaries. |
| Full Python checks | 5316 passed, 15 skipped, 2 deselected, 1 xfailed. The remaining four failures were individually reproduced on baseline commit 17db67dc and concern existing channel HTTP inventory and agentic harness scanning. |
| Web | Full static checks, Scheduler checks, and tsc --noEmit pass. The standalone page includes initial-load errors, action errors, and accessible icon-button names. Visual acceptance requires one page title, a 64px management-page topbar, search and the primary action in one row, horizontal category filters below the topbar, task numbering in visible order, and centered responsive suggestion cards in the empty state, confirmed by screenshots in the default 18100 App. |
| Independent review | Specification review passed. All quality findings concerning permissions, state claims, concurrency, MemoryRef, compatibility entry points, and UI error handling were fixed and re-review passed. The separate management-page structural revision received specification PASS. Quality review requested narrow-column reflow, distinguishable action names, and executable view-logic checks; scoped re-review passed after repair. |
Implementation status for this change: Internal-agent Scheduler–Memory lifecycle integration and ordinary daemon-tick exception isolation are implemented. Deleted records retain no placeholder or ID forwarding; current queries for the old memory_id return NOT_FOUND. The external-token permission model remains deferred as scoped.