Skip to content

core: manage Markdown schedules through an authenticated host CLI #914

Closed

Description

@schlessera

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 list
  • brain 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

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 due only 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.
  • cancel removes a task from due. A cancelled id stays visible in list --json with 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 --json shape is documented in docs/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:

  1. 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.
  2. Verified operator reconciliation that reopens restore_pending tasks.

The original acceptance criteria are unchanged.

Activity

  1. added
    type: featA new capability or a change in user-visible behaviour.
    area: corepackages/core: the brain CLI, indexer, hybrid search, config, taxonomy, skills.
    blockedWaiting on another issue or an upstream dependency. Say which in the body.
    contractTouches the integration contract: CONTRACT: commit, contract doc updated in the same commit.
    on Oct 3, 2026
  2. added
    priority: p2Wanted, not scheduled. Pulled in when a milestone has room.
    needs: decisionBlocked on a maintainer or designer ruling, not on code.
    securityAuth, isolation, egress, secrets, or anything in the threat model.
    on Oct 3, 2026
  3. schlessera commented on Oct 3, 2026

    @schlessera
    OwnerAuthor

    Triage — 2026-10-03. Core has a generic runner, while the server also has gated autonomous execution. The proposed direct due --run path and store ownership therefore await #913 rather than being presumed approved. Added needs: decision, a concrete reconciliation handoff and nonempty real-CLI/concurrency/DST/recovery checks; preserved the blocker and native parent.

  4. schlessera commented on Oct 3, 2026

    @schlessera
    OwnerAuthor

    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.

  5. schlessera commented on Oct 3, 2026

    @schlessera
    OwnerAuthor

    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.

  6. schlessera commented on Oct 4, 2026

    @schlessera
    OwnerAuthor

    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.

  7. schlessera commented on Oct 4, 2026

    @schlessera
    OwnerAuthor

    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.

  8. schlessera commented on Oct 4, 2026

    @schlessera
    OwnerAuthor

    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.

  9. added
    agent-readyContext and acceptance criteria are complete enough for an agent to take this unattended.
    on Oct 4, 2026
  10. 63 remaining items

  11. schlessera commented on Oct 6, 2026

    @schlessera
    OwnerAuthor

    Delivered: #1045 merged as 7719bca8cf057cc485c1923cf5f075b273fcf091 (claim w19-914-r2b8). Verified that the squash commit is on main. All 12 Depot CI jobs and the contract job passed on the PR's final head 272e4bae, rebased onto the then-current main.

    The two items left open in the 2026-10-06 delivery state are done.

    1. Runtime admission boundary and fixture. createScheduleAdmission is private and not wired into the running app.
      • An occurrence is admitted once, even when separate processes race for it.
      • Admission goes through the real Queue claim, budget reservation, lifetime lock and runAutonomousTurn.
      • Budget A: the three-operation counter is shared across retries, yields and continuation items. Each attempt has a persisted 10-minute host deadline, never past the 24-hour expiry. The claim is released only after the backend unwinds.
      • Crash recovery:
        • A crash before the backend acquired its reservation retries.
        • A crash after a possible effect is unknown. It is never replayed, and the task pauses as unknown_effect.
        • When Activity holds the turn's terminal receipt, recovery uses that outcome.
      • Cancel versus start, in both orders.
      • A one-off runs once and is never due again.
      • Timing A: busy instants are skipped and a single latest catch-up runs.
      • Work is refused when the approver is revoked, the host policy has changed, or the approved endAt has passed.
    2. Operator reconciliation. brain schedule reconcile ID --key KEY and POST /api/schedules/:id/reconcile reopen tasks paused as restore_pending or unknown_effect.
      • Only an operator can do it.
      • The definition file must still match the approved bytes, the host must still run the approved execution policy, and the creator and approver must still be usable.
      • Keys work as receipts.
      • Unknown occurrences are never replayed.
      • After a restore into a new directory, the first reconciliation binds the ledger to the directory that restore recorded.

    Eight gpt-6.1-sol adversarial 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).

  12. added this to the 0.41.0 milestone on Oct 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

agent-readyContext and acceptance criteria are complete enough for an agent to take this unattended.area: corepackages/core: the brain CLI, indexer, hybrid search, config, taxonomy, skills.area: ui-serverHono app factory: turn coordinator, auth, session catalog, routes, cron.contractTouches the integration contract: CONTRACT: commit, contract doc updated in the same commit.priority: p2Wanted, not scheduled. Pulled in when a milestone has room.securityAuth, isolation, egress, secrets, or anything in the threat model.type: featA new capability or a change in user-visible behaviour.

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions