Skip to content

ui-server: run due scheduled tasks, record them as activity and notify the result #915

Open

Description

@schlessera

Blocked by #1595

Context

Delivered prerequisite outputs: #914, #689. Their output references remain relevant, but they are no longer active blockers. #689 supplies completed system proof; production activation is owned by #1595, including its public hosting-template prerequisite.

Part of #912. Due schedules need something that fires them, with no new daemon (docs/decisions/async-collaboration.md:115-123). The server already ticks every 60 s for the inbox (packages/ui-server/src/inbox/runtime.ts:11), with a heartbeat and a loopback poke. The host crontab has a wrapper that records each run in cron_runs and as an activity span with origin: "cron" (packages/ui-server/src/cron/run-job.ts:221, packages/ui-server/src/cron/activity.ts:18-31). Results reach the user only through notification intents, whose kinds are failure | completion | stuck (packages/ui-server/src/activity/notify.ts:29). Completion is opt-in per job (:162-165).

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. The core: manage Markdown schedules through an authenticated host CLI #914/ui-server: verify the full async lifecycle before enabling autonomous dispatch #689 outputs are delivered; ui-server: enable autonomous dispatch in production behind a supported switch #1595 remains the activation prerequisite. Existing recorded approvals and runtime proof criteria remain. No release commitment or production dispatch is inferred. Drive the private Queue boundary from the existing tick/recovery infrastructure only. Use deterministic original-due occurrence identity, persistent cursor/claim/counters/deadlines/charges and exactly one outstanding occurrence, including unwind/human wait; read-only due is never a dispatcher.
  • Apply the exact prepared Task/Occurrence/result states and Activity correlation, current approval/snapshot checks, 24-hour cutoff and optional immutable recurrence endAt. Future schedule cancellation/approved end retire unstarted work; started attempts retain their current bounded snapshot. Distinct authenticated occurrence stop requests actual abort/drain, not undo. Unknown effects/restored uncertain authority stay paused. Final notices use existing kinds/bounds, generic payloads and default-off success opt-in without fabricating delivery or unstarted runs.
  • Under approved Execution A, the existing Queue runtime/tick recognizes due occurrences and admits them through the same Queue authority, lifetime/budget and autonomous AgentBackend path. Reuse its recovery/backstop infrastructure; any cron poke only wakes/recovers that path. One occurrence has one dispatch owner. Do not emit an independent brain schedule due --run core-runner cron job or fallback. Production dispatch waits for ui-server: enable autonomous dispatch in production behind a supported switch #1595; ui-server: verify the full async lifecycle before enabling autonomous dispatch #689 supplies completed system proof.
  • Under Delivery A, record actual scheduled execution as origin autonomous with additive schedule/occurrence/run identity and bounded authenticated result/reference. Keep task titles/prompt data behind auth and inert; unstarted skipped/coalesced work gets a scheduling record without a fabricated run. No new activity origin solely for scheduling.
  • Under approved Delivery A, success uses existing completion intents only when that schedule explicitly opts in; failure/stuck/durable Actions follow existing watching/debounce/suppression paths. Correlate final occurrence notices across retry/yield/continuation and deduplicate replays; keep generic scheduled-work push labels without arbitrary title/prompt/path/result payloads. Preserve current shared suppression threshold, retry/backoff and push permissions (MAX_INTENTS_PER_HOUR, packages/ui-server/src/activity/notify.ts:67). No unsolicited Chat insertion, new intent kind or notification service.
  • Enforce approved Budget A across occurrence-linked Queue attempts and Action continuations: host-owned 10-minute attempt deadline, at most three model-bearing operations, shared daily operation/spend admission, counters retained through restart/yield/new items, and actual cancellation/unwind before claim/settlement release. Profile selection never enables dispatch or widens emergency/billing authority.
  • Under Timing A, boot/tick recovery retains any outstanding original occurrence; otherwise admit at most the latest eligible missed due instant within 24 hours. Record older/busy due instants as skipped/coalesced. Queued/running/retrying/human-wait work does not overlap; original-due freshness never resets. Reconcile charges/receipts/lifetime first and pause a possible unrecorded effect for durable investigation. Future/unstarted cancellation prevents stale Action continuation; live stop remains distinct and must drain honestly.

Out of scope

Settled policies and dependency

All six #912/#913 policies are settled and recorded in docs/decisions/scheduled-tasks.md (#913, PR #980): Queue Execution A, Authority A (current authority and revocation over immutable approved recurring bounds), Budget A (10-minute attempt deadlines, three model-bearing operations per occurrence, shared daily admission caps), Timing A (bounded catch-up, no overlap, due-based 24-hour freshness, future/unstarted cancellation, zone/DST and unknown-outcome handling), Delivery A (default-off per-schedule success opt-in, authenticated results, existing intents and origin, generic minimized push) and Store B (root-contained Markdown definitions with server-owned approvals, occurrences and receipts). #914 delivered the store and CLI; #689 is completed system proof.

The one remaining dependency is #1595, the supported production activation switch for Queue dispatch, declared above. Scheduled execution must go through that path, never a different timer. The cron wrapper records a system principal and existing activity; it is not user-task authorization. Existing intents can be suppressed or send_failed, so creating one cannot promise receipt.

Acceptance criteria

  • Store B dispatch validates the canonical definition against immutable server approval/current authority before admitting an occurrence; changed/restored/missing files and cancelled tombstones cannot revive work. File/DB compensation, coupled Git/operational restore, lost-worker accounting and unknown-effect investigation are exercised through actual runtime boundaries before admission; the content index is never authority.
  • An integration test with a fake clock and nonempty task proves actual CLI/store → selected trigger/admission → scripted backend → activity/intent. A due occurrence is admitted once across concurrent triggers and restarts; no vendor inference or scheduler is called. The activity has the correct schedule/occurrence/principal identity and actual outcome, and the chosen notification policy yields one correlated intent.
  • Approved Authority A gates every occurrence against its immutable approved operation/input/target/egress envelope and current creator authority. Changed/wider scope cannot run; additional permission/ordinary answers checkpoint to a durable Action and unwind, with at-most-one separately admitted continuation after validated resolution.
  • Empty/not-yet-due ticks create zero model calls, activity roots and intents. Revoked/expired authority, exhausted reservations, unavailable/unsupported backend and cancelled tasks never start work. A runtime timeout/cancel drains before claim/lifetime release; it does not masquerade as success.
  • Default-off success preference for one-off/recurring schedules and explicit opt-in produce the selected completion intent behavior. Intermediate versus final attempt roots, duplicate/reordered observers and restart cannot generate repeated final occurrence notices.
  • Cap/suppression, push denied/unavailable and repeated terminal events preserve durable visible results and idempotent intents. No prompt, document path, tool payload or arbitrary task title is exposed on a lock screen by default.
  • Runtime/store/CLI tests prove Timing A single/latest eligible catch-up, original-due 24-hour cutoff, no overlap with queued or waiting-for-Action work, retained IDs/counters, skip-gap/first-fold recurrence and the future-cancel-versus-start ordering. Crash-after-effect-before-receipt pauses that schedule; no unknown write is automatically repeated or reported exactly once.
  • No outbound request to any vendor scheduling API is made anywhere on the path, and a test asserts it.

Verification

Use bun run test for mounted runtime/CLI/store/notification tests, scripted real-backend boundaries and fake-clock recovery fixtures. Mutate the actual admission, occurrence ownership and intent-dedup guards and record failures on their intended assertions. Run required typecheck/contract/leakage checks. After implementation, an authorized running-host check schedules a fictional task, inspects activity and the in-app intent, and verifies push only when permission/delivery are available; lack of push cannot be reported as task failure or successful push. A planned final host check does not itself impose needs: human now.

Contract impact

Additive: optional scheduleId/occurrenceId correlation on autonomous Activity roots/rollups and authenticated bounded result/occurrence links, plus per-schedule preference and final-occurrence dedup on existing notification paths. Preserve origin autonomous and existing failure/completion/stuck kinds. Label contract; document actual schemas/compatibility in the same CONTRACT: commit, with a minor changeset for additive supported behavior in each affected package. Delivery A preserves origin autonomous and existing intent kinds; only schedule/occurrence/result correlation is additive. Host-generated glue outside package behavior belongs in a public hosting-template companion if the selected path needs it.

2026-10-24: replaced the superseded "Settled policy and dependency handoff" (retain #913/#914/#689 blockers) with the settled policies and the single real dependency, #1595; dated reconciliation notes moved to a comment.

Activity

  1. added
    type: featA new capability or a change in user-visible behaviour.
    area: ui-serverHono app factory: turn coordinator, auth, session catalog, routes, cron.
    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. Preserved #913/#914 and the existing autonomous enablement requirements. The body distinguishes cron attribution from task authority and durable intent from push delivery, and adds admission/revocation/empty-tick/crash/cancel tests with meaningful runtime mutations. Notification preference and execution contracts await #913; added needs: decision and priority: p2.

  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.

    Added the actual #689 production-execution prerequisite alongside #913/#914 and normalized separate blocker lines. This does not prevent independent schedule/consumer design or keyless fixture work.

  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.
    and removed
    needs: decisionBlocked on a maintainer or designer ruling, not on code.
    on Oct 4, 2026
  10. schlessera commented on Oct 4, 2026

    @schlessera
    OwnerAuthor

    Final policy reconciliation — 2026-10-04.

    The maintainer selected B, including the common consumer/recovery rules. Dedicated root-contained Markdown files own definitions; the existing UI operational database owns immutable approval fingerprints/provenance, creator/occurrence/claim/counter/charge/Action/receipt/notification state. File edits/restores cannot grant or revive authority; dispatch requires matching approved scope and current authority, with durable file/DB compensation and quarantined drift. All supported CLI/chat/PWA consumers use one authenticated host path. No direct DB/core-runner/offline-activation fallback; due is read-only. Editing stays cancel-and-create; cancelled history/create-cancel receipts prevent replay. Coordinated content Git/operational backup and gated restore reconcile current authority/lost workers/unknown effects before dispatch.

    All six policies are now selected: Execution A, Authority A, Budget A, Timing A, Delivery A and Store B. Updated this brief and removed needs: decision. #913 still delivers the durable record and technical directory/schema/CLI/HTTP/tool examples; those are deliverables under the selected policies, not further generic maintainer choices. Existing dependency/design/system-proof requirements remain open; no implementation, record completion, production enablement or release commitment is inferred.

    The complete implementation brief is agent-ready but still blocked by its actual prerequisite outputs; preserve the existing dependency labels/lines. Technical code changes and runtime mutation receipts remain future work.

  11. schlessera commented on Oct 4, 2026

    @schlessera
    OwnerAuthor

    Technical reconciliation — 2026-10-04. #913 now has a concrete documentation draft in #980, pinned to signed head a4d701c4db7d03c18608c6b12891f5e5d020c63f. Folded the chosen directory/schema, immutable approval/provenance and file/DB compensation, strict CLI/HTTP/tool/result/error/receipt contracts into this brief; corrected stale execution/origin/provenance wording where applicable. #917's self-contained design prompt consumes the concrete types and preserves its missing interaction design.

    This is planned-only contract preparation, not package implementation, a verified operator grant or enabled execution. #913 stays open until its required checks and merge; #914–#917's other dependencies/design need and #689 remain. No release commitment or running-host proof is inferred.

  12. schlessera commented on Oct 24, 2026

    @schlessera
    OwnerAuthor

    History moved out of the body on 2026-10-24, when superseded dependency and design instructions were removed from the active specification. These notes were current at their dates; every ruling they record is folded into the body.

    2026-10-03 triage: source verified at main 1d1fcc72a7e06352e8b5ee020b3910864babc5db; clarified that cron attribution and a stored completion intent do not establish future authority or successful push delivery. Native parent #912 and blockers #913/#914 remain; priority: p2 reflects wanted unscheduled 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.

    Only the #913 record prerequisite is cleared. The #914/#689 outputs are delivered; activation still requires #1595; all original execution, containment, budgets, admission and real-runtime proof criteria stay required.

    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.

    Updated 2026-10-12: removed completed blocker declarations, retained delivered-output context and all remaining prerequisites; readiness and existing rulings preserved.

    Updated 2026-10-12: completed the active scheduling-specification correction: #689 is delivered proof and #1595 owns activation; historical rulings and approved schedule semantics remain intact.

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

    agent-readyContext and acceptance criteria are complete enough for an agent to take this unattended.area: ui-serverHono app factory: turn coordinator, auth, session catalog, routes, cron.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.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

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions