Repository navigation
core: manage Markdown schedules through an authenticated host CLI #914
Description
Context
Part of #912. Nothing can store a time-scheduled unit of agent work today. Module cron entries are static manifest data (packages/core/src/lib/module-types.ts:102-108), and the host crontab is only generated at container start (packages/ui-server/src/cron/emit.ts:123). Core's existing generic headless helper is runAgent() (packages/core/src/cli/agent.ts:39). Its two callers are hardcoded slash commands (brain add, brain sync), and there is no scheduled-task CLI. The server also has gated autonomous turn plumbing; this is not the only headless execution code in the monorepo. The CLI is a supported consumer surface (docs/integration-contract.md); public commands belong in core, while #913 records the settled authoritative schedule-state ownership and host consumer boundary. Execution A now selects host Queue execution; a direct core AgentRunner execution/fallback is rejected. Store B now selects dedicated brain-root-contained Markdown definitions plus the UI server approval/runtime ledger. All supported brain schedule commands use the authenticated host API; the selected record/contracts below supply the concrete directory/schema and coherent technical contracts.
Scope
- Selected technical specification: scheduled-task decision and prepared consumer contracts. These are concrete technical deliverables under the six settled maintainer policies, not installed commands/routes/tools or runtime proof. ui-server: decide how a scheduled agent task runs while autonomous dispatch is gated #913 is delivered and verified via CONTRACT: docs: record scheduled task boundaries #980; this record dependency is cleared. All other dependencies, design needs and ui-server: verify the full async lifecycle before enabling autonomous dispatch #689 remain. No release commitment or production dispatch is inferred. Use context/scheduled-tasks/definitions/.md and retained retired/ definitions; reserve/exclude that namespace from ordinary content scans without reclassifying existing content. Implement strict cache-free parsing, canonical fingerprint/snapshot matching, file/DB publication/retirement journals and coordinated backup/restore, including uncertainty about later revocation/cancellation.
- Implement the exact add/list/cancel/read-only-due envelopes, keys, IDs, scope-file/operator review, host audience/credentials, state/zoneSource/endAt/result honesty, response bounds, signed cursors, closed error codes and 0/1/2 exits. A matched creation receipt is checked before stale first-publication timing/token validation; retired IDs never revive. Cancel retains a started bounded snapshot and reports runningOccurrences/compensationPending; it does not imply live stop.
- Under Store B, dedicated root-contained Markdown schedule definitions are outside disposable brain.db authority. The authenticated host validates/writes them with durable file/DB compensation and retains matching approval fingerprints, creator/occurrence/receipt state in the UI operational ledger. Use parseFrontmatter for definition parsing; file/frontmatter principal fields never grant authority. ui-server: decide how a scheduled agent task runs while autonomous dispatch is gated #913 specifies the exact contained directory/schema. Under approved Authority A it retains the authenticated creator and immutable approved recurring envelope, variable-input bounds and approval provenance; a client/model principal string cannot authorize it. Immutable file fields and exact retained host responses follow the selected types below: file schema/id/body prompt, materialized timing/zone/optional approved recurrence end, scope, limits and success preference; creator/approval/control state/created-at/next and last occurrence/results/counters are operational ledger data, never file authority. Occurrence counters and deadlines persist through retry/restart/continuation/new Queue items; daily admission consumes the existing shared operator-configured caps.
- brain schedule add with exactly one --at/--cron, approved zone/prompt/target/scope/notify inputs, explicit --server, the existing optional private --credential-file pattern and a stable create request key. Match the record's supported flags/JSON/receipts; host validates actual operator approval rather than trusting input provenance.`
brain schedule listbrain schedule cancel <id>- brain schedule due is read-only through the authenticated host: no claims, operations/budget changes, inference or occurrence advancement. Remove the conditional due --run proposal; only Queue/runtime dispatches work. add/list/cancel/due have no direct DB/root-runner/offline-activation fallback. Missing/unavailable host is an honest unavailable result.
- --json on every subcommand. Read success including empty due and failed-run inspection exits 0; validation/auth/conflict exits 1; unavailable/invalid-response/transient-server failure exits 2. Queued work is not a completed run. Coherent task/occurrence/result/error examples follow ui-server: decide how a scheduled agent task runs while autonomous dispatch is gated #913's selected technical contract.
- Approved Timing A: at most one outstanding/latest-missed catch-up within 24 hours; no overlap including durable human wait; occurrence expires 24 hours after original due. Keep identity/counters/authority across attempts, skip older/busy due instants and pause unknown effects for investigation. Recurring definitions continue until cancellation/revocation/explicit approved end; one-offs do not recur.
- Zone precedence and one-off/DST validation follow Timing A: explicit valid IANA zone, else validated client zone, else disclosed UTC; first-create at is an offset/Z ISO future instant, with idempotent recorded retries preserved; cron skips nonexistent wall times and runs a fold once at its earlier occurrence.
- Schedule cancel is idempotent future/unstarted cancellation, not live-attempt stop or undo. Stale Actions cannot dispatch after cancellation/expiry; running-stop behavior is a distinct supported host action.
Out of scope
- Trigger/tick/recovery wiring (the ui-server task), independent cron/core AgentRunner dispatch and autonomous enablement before ui-server: verify the full async lifecycle before enabling autonomous dispatch #689.
- Chat tools and the PWA.
- Notification intent generation/push transport (ui-server: run due scheduled tasks, record them as activity and notify the result #915), while the shared task/list state includes approved Delivery A default-off success preference and actual result/run links.
Settled policy and dependency handoff
All six #912/#913 policy rulings are selected, including Store B; remove needs: decision. Blocked by #913 remains until its durable record and technical directory/schema/CLI/HTTP/task examples are delivered, rather than treating the ruling as completed dependency work. This issue implements host-managed Markdown definitions and core authenticated CLI consumers against that branch, with authority/fingerprint validation and compensation. Keep blocked; the complete implementation brief is agent-ready subject to its dependency, without a release commitment.
Acceptance criteria
- Store B uses actual root-contained Markdown definitions and the existing parser, canonical fingerprints and server-owned immutable approval/counter/receipt records. Symlinks/escapes, externally edited/restored/missing/unapproved definitions and stale re-created files cannot activate or widen dispatch. Real interrupted file writes/cancel compensation and concurrent tick/create/cancel tests fail closed; an approved immutable snapshot is not replaced by untrusted file data.
- Authenticated CLI add/list/cancel/due and chat/PWA consumers observe the same record and receipts through the host. Wrong credential audience, expired/revoked auth, redirects, malformed host responses and unavailable server are explicit bounded errors. due is read-only with empty exit 0 and no direct runner/DB fallback; preserve retained cancelled history and idempotency conflicts.
- With a fake clock, a test proves that a one-off task is reported by
dueonly once its time has passed, runs once, and is then not due again. - A test proves that a recurring task's next due time is computed in its own time zone, including across a DST change.
cancelremoves a task fromdue. A cancelled id stays visible inlist --jsonwith its state.- A scripted actual runtime integration fixture drives the private Queue admission boundary, records an occurrence claim/outcome and admits it once under concurrent host/store callers. Public due remains read-only; there is no execute/enqueue CLI command or direct core runner. ui-server: run due scheduled tasks, record them as activity and notify the result #915 owns production trigger wiring and ui-server: verify the full async lifecycle before enabling autonomous dispatch #689 its enablement proof. Under Budget A, persist the whole-occurrence three-operation bound and host-owned 10-minute attempt deadline, including retry/continuation/new-item controls and real cancellation/unwind before claim release. Reopen after crashes around claim/effect/receipt and apply the chosen unknown-outcome recovery rather than blindly replaying an arbitrary side effect. No predicate-only claim of exactly-once execution.
- Timing A rejects invalid/past/offset-missing first-create instants and explicit invalid zones/cron before writes/admission, with validated client-zone or disclosed UTC fallback. Original matched create receipts remain idempotent after their time has passed. DST gaps skip and folds run once at the earlier due instant. Cancel is future/unstarted-only and idempotent; a cancel-versus-start race reports the actual running attempt and cannot claim undo. Cover 24-hour freshness, single coalesced catch-up and no overlap with outstanding human-wait work.
- Under Authority A, CLI creation records genuine authenticated operator approval for the immutable recurring envelope and creator linkage; model/input principal strings or ordinary chat grants cannot manufacture recurrence authority. Store/root containment and coordinated backup/restore follow Store B and the concrete selected record/contracts above. Creator revocation/expiry narrows or stops work, never broadens the approved scope; disposable-index rebuild does not delete schedules. An attempt denied by authority/budget/unavailable execution has an explicit state/error rather than a successful empty outcome.
- Each subcommand's
--jsonshape is documented indocs/integration-contract.md.
Verification
Use bun run test for actual CLI/store/selected execution integration with a fake clock, nonempty fictional tasks, scripted runtimes and real concurrent processes. Test read-only due, cancel, recurrence and persisted reopen without sleeping a minute or paid inference. Mutation receipts break the actual claim, authority or date guard and fail the named behavior assertion. Contract tests assert every JSON envelope and exit case. A later authorized host check follows the selected command; it is not a prerequisite to independent keyless work.
Contract impact
Additive: a new brain schedule command family with --json shapes. Label contract; document the selected command/JSON/exit behavior in the same CONTRACT: commit, with a minor changeset for core and other changed packages. If the choice requires a new reachable seam signature or a breaking behavior, obtain its explicit ruling and update the API report rather than hiding it as an additive CLI flag.
2026-10-03 triage: source verified at main 1d1fcc72a7e06352e8b5ee020b3910864babc5db; corrected the exclusive headless-entry/ownership claims and made the direct runner conditional on #913. Native parent #912 and blocker #913 are preserved; priority: p2 denotes unscheduled requested work.
2026-10-04 maintainer execution reconciliation: selected Execution A on #912/#913 and folded the single Queue execution branch into this brief. Independent cron/core-runner execution and interim migration are rejected; production enablement still waits for #689. Authority, budgets, timing/recovery, delivery and store/consumer contracts remain unresolved; preserve needs: decision, existing blockers and any design need, without readiness or a release commitment.
2026-10-04 maintainer authority reconciliation: selected Authority A on #912/#913, including explicit operator approval of the recurring operation/tool/input/target/egress envelope, immutable scope with current revocation/expiry checks, authentic creator provenance and durable Action/checkpoint/unwind for additional needs. Folded that ruling into this brief and required negative/continuation controls where applicable. Budgets, timing/recovery, delivery and store/consumer contracts remain open; preserve needs: decision, blockers and any design need without readiness, enablement or a release commitment.
2026-10-04 maintainer budget reconciliation: selected Budget A on #912/#913, including 10-minute host deadlines per admitted attempt, three model-bearing operations per occurrence across retry/yield/compaction/continuation/new Queue items, persistent counters, shared configured daily autonomous caps and conservative accounting/unwind. This does not enable admission or promise a provider invoice ceiling. Timing/recovery, delivery and store/consumer contracts remain open; preserve needs: decision, blockers and design needs without readiness or a release commitment.
2026-10-04 maintainer timing reconciliation: selected Timing A on #912/#913 including latest/single catch-up within 24 hours, one outstanding occurrence/no overlap, original-due 24-hour freshness including human wait, future/unstarted-only schedule cancellation with distinct live-stop behavior, unknown-effect investigation and explicit/client/UTC zone, future offset one-off and skip-gap/first-fold-once DST rules. Folded these policies into the brief; delivery and store/consumer contracts remain open. Preserve needs: decision, blockers/design needs and #689 without readiness, enablement or a release commitment.
2026-10-04 maintainer delivery reconciliation: selected Delivery A on #912/#913: per-schedule success alerts opt-in, authenticated Activity/schedule results, existing failure/stuck/Actions/completion paths, origin autonomous plus occurrence correlation, generic minimized push and existing suppression/retry/permission honesty. No unsolicited Chat insertion/new service is approved. The final authoritative definition-store/CLI boundary remains open; preserve needs: decision, blockers/design needs and #689 without readiness, enablement or a release commitment.
2026-10-04 maintainer final storage reconciliation: selected Store B on #912/#913: root-contained Markdown definitions plus the existing UI approval/runtime ledger, canonical approved-fingerprint validation, durable file/DB compensation, one authenticated host-backed CLI/chat/PWA path, read-only due, idempotent retained create/cancel history and coordinated Git/operational restore. All six policy questions are settled; removed needs: decision. Keep actual record/implementation/design/proof dependencies without claiming completed work, production enablement or a release commitment.
2026-10-04 #913 technical reconciliation: the six settled policies now have a concrete record and planned-only contract draft. Corrected stale command/origin/provenance wording where applicable; no implementation, enabled dispatch, runtime/system proof, design approval or release commitment is inferred. Dependencies remain until verified delivery.
Current record delivery and dependency state — 2026-10-04
#913 is complete: #980 merged as 38f3c10a3947b9b00574cc9123a0e64d1a1022ed, with thirteen executed current-head/live-base Depot checks, matching squash parent/tested tree/main ancestry and all twelve automatic main-push jobs verified. The durable record and future consumer contracts supply the selected specification. These are design/contract preparation, not shipped schedule commands or enabled execution.
The #913 record prerequisite is cleared; this complete implementation brief can be picked up under its existing agent-ready label. Implement the authenticated host-owned definitions/ledger and CLI consumer contracts with all original negative/compensation/backup controls. Production execution still depends on its independent #689 proof; this issue is not complete.
Earlier dated reconciliation notes record policy selection and dependency state at that time. The delivery state above supersedes their pending-record language; the original implementation scope and acceptance criteria are unchanged.
Delivery state — 2026-10-06
#1009 merged as a26209c39a258db966ad7ac5f965d267c5bb11ae. It delivers the authenticated host store and the CLI consumer: brain schedule add|list|cancel|due, six protected /api/schedules routes, root-contained Markdown definitions with an approved-snapshot ledger, file and database journals, drift quarantine, read-only due under Timing A, signed cursors, backup relation checks with restore pausing, index exclusion, and the contract docs. Details are in the delivery comment.
Remaining before this issue can close:
- The scripted runtime admission fixture. It covers:
- one admission under concurrent callers;
- persisted Budget A counters and deadlines across retry, continuation and new items, with cancellation and unwind before the claim is released;
- crash and unknown-outcome recovery;
- a one-off that runs once and is then never due again;
- the cancel-versus-start race.
- Verified operator reconciliation that reopens
restore_pendingtasks.
The original acceptance criteria are unchanged.
Activity
schlessera commentedon Oct 3, 2026
Triage — 2026-10-03. Core has a generic runner, while the server also has gated autonomous execution. The proposed direct
due --runpath and store ownership therefore await #913 rather than being presumed approved. Addedneeds: decision, a concrete reconciliation handoff and nonempty real-CLI/concurrency/DST/recovery checks; preserved the blocker and native parent.schlessera commentedon Oct 3, 2026
Execution-branch reconciliation — 2026-10-04.
The maintainer selected A for scheduled work on #912, also settling the execution branch in #913. Due occurrences use the existing Queue admission/authority/lifetime/budgets and autonomous AgentBackend boundary, triggered through its existing server runtime/tick and recovery/backstop infrastructure. One occurrence has one dispatch owner; cron may wake/recover that same path. No independent core AgentRunner/cron execution or interim runner migration is approved. Production execution waits for #689's integrated proof under docs/decisions/async-collaboration.md.
The body now follows this selected branch. Creation/future-run authority, caps, timing/recovery, delivery and schedule-definition store/consumer contracts remain open; keep needs: decision and actual dependencies, plus any design need. No readiness, enabled autonomy, release commitment or implementation is claimed. #913 already owns the durable scheduled-tasks record; record this choice there after the remaining rulings without asking for execution again.
schlessera commentedon Oct 3, 2026
Authority-branch reconciliation — 2026-10-04.
The maintainer selected A. Review and approve the exact recurring operation, tools, input bounds, targets and permitted egress once at creation, including which inputs may vary. Future occurrences use that immutable envelope intersected with current authority/revocation/expiry and admission checks. Changed prompt/scope requires cancellation and a newly approved schedule. Authenticated chat/CLI creation is not an implicit blanket grant; verified operator approval and creator provenance must be recorded without trusting a model/client principal/profile/trust string. Voice still cannot grant tools.
Additional permission or ordinary answers checkpoint to a durable Action and unwind; validated resolution creates at most one separately admitted continuation. No indefinitely live ask promise, automatic expansion of scope or blind replay of completed effects. This schedule-specific recurring approval must be recorded and reconciled with existing authority records through #913; current generic Queue/chat grants do not already implement it. Runtime containment and #689 remain production gates.
Updated the current brief. Execution A and Authority A are settled; budgets, timing/recovery, delivery and authoritative store/consumer contracts remain open. Keep needs: decision and actual dependencies/design needs without readiness, enabled autonomy or a release commitment. Concrete creator credential/approval schema/runtime enforcement details follow the selected policy rather than reopening it.
schlessera commentedon Oct 4, 2026
Budget-branch reconciliation — 2026-10-04.
The maintainer selected A, including the common budget rules. Initial dispatch, retry/redo, yielded attempts, separately dispatched compaction and approved Action continuations all share the same occurrence limit, including when a new Queue item is created. Reload/restart cannot reset counters/deadlines. The host owns the attempt deadline, aborts at expiry and waits for actual backend unwind before releasing claim/lifetime or settling charges. Lower task-specific limits remain possible; capacity is not permission to replay an unknown effect.
Scheduled work uses existing shared operator-configured daily autonomous operation/spend caps, charged usage plus active reservations. Zero/absent operations remains disabled; no implicit enablement, new schedule-only pool or privileged emergency reserve. Subscription work still consumes operation capacity; missing usage/prices remain conservatively charged, observed overruns stop later over-cap work, and admission does not promise a provider-enforced invoice ceiling.
Updated the current brief and required persistent-counter/deadline/runtime controls. Execution A, Authority A and Budget A are settled; timing/recovery, delivery and authoritative store/consumer contracts remain open. Keep needs: decision and actual dependencies/design needs without readiness, enabled autonomy or a release commitment. #913 owns the durable record after the remaining rulings.
schlessera commentedon Oct 4, 2026
Timing-branch reconciliation — 2026-10-04.
The maintainer selected A, including the common recovery rules. Keep one outstanding occurrence per schedule; otherwise catch up only the latest missed due instant within 24 hours and record older/busy instants as skipped/coalesced. Queued/running/retrying/human-wait work does not overlap. The occurrence expires 24 hours after its original due instant; retries/resolutions cannot reset freshness or revive cancelled/expired work. Existing Budget A counters/deadlines and current Authority A still bind.
Schedule cancellation stops future/unstarted work idempotently and does not stop an already-started attempt or undo effects; running stop is a distinct supported host action with honest drain. Recover charges/receipts/lifetime first. A possible effect without authoritative completion/idempotency proof pauses automatic dispatch for that schedule and creates durable investigation; no blind repeat or arbitrary exactly-once claim.
Explicit valid IANA zone wins, else validated client zone, else disclosed UTC; explicit invalid zone fails. First-create one-offs require a future ISO instant with Z/offset; recorded matching creation retries return the original receipt. Cron skips DST gaps and runs a repeated local time once at its earlier due instant. Existing tick/boot ownership remains one Queue path; due time is eligibility, not an exact-second start guarantee.
Updated the current brief and timing/cancellation/unknown-effect validation. Execution A, Authority A, Budget A and Timing A are settled; delivery and authoritative store/consumer contracts remain open. Preserve needs: decision, actual dependencies/design needs and #689 without readiness, enablement or release commitment. #913 owns the durable record after the remaining rulings.
schlessera commentedon Oct 4, 2026
Delivery-branch reconciliation — 2026-10-04.
The maintainer selected A for both one-off and recurring tasks, including the common delivery rules. Actual results/outcomes remain authenticated and durable in Activity/schedule detail; failures/stuck/needs-human Actions use the existing notice paths. Success uses existing completion intents only with explicit per-schedule opt-in. Preserve origin autonomous with additive schedule/occurrence/run correlation, deduplicate final notices across attempts/restarts, and retain generic scheduled-work push labels without prompt/title/path/result leakage. Existing watching, suppression, retry/backoff and push permissions still bind; intent/provider acceptance is not proof of user receipt/read.
No unsolicited original-Chat insertion, extra model turn, new notification kind or service is approved. Suppressed/unavailable push never erases the recorded result. Updated the current brief and related design premise. Execution A, Authority A, Budget A, Timing A and Delivery A are settled; the final policy decision is authoritative schedule-definition storage and the CLI/host consumer boundary. Keep needs: decision and actual dependencies/design gates without readiness, enablement or release commitment. #913 owns the durable record and matching contract preparation after that ruling.
63 remaining items
fix(ui-server): trust only a backend's own terminal result in schedul…
feat(ui-server): admit schedule occurrences through the Queue and reo…
CONTRACT: feat(core,ui-server): reconcile paused schedules through an…
CONTRACT: fix(ui-server): require current approver, policy and recurr…
fix(ui-server): fail refused occurrences and recheck the bound schedu…
chore(ui-server): order the schedule admission migration after main's…
fix(ui-server): keep reclaimed retries, settle stranded occurrences, …
fix(ui-server): price, recover and expire scheduled attempts from the…
fix(ui-server): keep fired deadlines and restored busy instants in re…
fix(ui-server): judge attempt deadlines by time, and settle receipts …
fix(ui-server): trust only a backend's own terminal result in schedul…
CONTRACT: feat(core,ui-server): admit schedule occurrences through th…
schlessera commentedon Oct 6, 2026
Delivered: #1045 merged as
7719bca8cf057cc485c1923cf5f075b273fcf091(claimw19-914-r2b8). Verified that the squash commit is onmain. All 12 Depot CI jobs and the contract job passed on the PR's final head272e4bae, rebased onto the then-currentmain.The two items left open in the 2026-10-06 delivery state are done.
createScheduleAdmissionis private and not wired into the running app.runAutonomousTurn.unknown. It is never replayed, and the task pauses asunknown_effect.endAthas passed.brain schedule reconcile ID --key KEYandPOST /api/schedules/:id/reconcilereopen tasks paused asrestore_pendingorunknown_effect.Eight
gpt-6.1-soladversarial review passes ran; the last found nothing actionable. The PR body lists every finding and its mutation receipt.Still owned elsewhere: production trigger wiring, Activity/notification correlation and the schedule Action path (#915); enablement (#689); bridge tools (#916); PWA review (#917).