[DEV-93] Add quiet hours / scheduled drain to the worker so users can limit unattended work to chosen times (e.g. nights only) - #87
Conversation
…ker so users can limit unattended work to chosen times (e.g. nights only)
…t-14 # Conflicts: # packages/code/src/lib/workspace/workspace-worker.ts
|
🔀 This branch had merge conflicts with |
…t-14 # Conflicts: # docs/code/workspaces.md
|
🔀 This branch had merge conflicts with |
…t-14 # Conflicts: # packages/code/src/dashboard-server.ts # packages/code/src/lib/dashboard-api.ts
|
🔀 This branch had merge conflicts with |
…t-14 # Conflicts: # packages/code/src/dashboard-server.ts # packages/code/src/lib/dashboard-api.ts # packages/code/src/lib/workspace/config.ts # packages/code/src/lib/workspace/workspace-worker.ts # packages/code/tests/dashboard-api.test.ts
|
🔀 This branch had merge conflicts with |
…t-14 # Conflicts: # docs/code/workspaces.md # packages/code/src/lib/worker-state.ts # packages/code/src/lib/workspace/config.ts
|
🔀 This branch had merge conflicts with |
…t-14 # Conflicts: # docs/code/workspaces.md # packages/code/src/lib/task-polling-acquirer.ts # packages/code/src/lib/workspace/workspace-worker.ts # packages/dashboard-ui/src/components/StatusStrip.tsx # packages/dashboard-ui/src/lib/api.ts
|
🔀 This branch had merge conflicts with |
Task: DEV-93
Summary: Add quiet hours / scheduled drain to the worker so users can limit unattended work to chosen times (e.g. nights only)
Implementation Details
I'll start by reviewing the details, then set up a structured plan to tackle this task efficiently.
I have the full picture. Now implementing — starting with the core schedule library.
Now fixing two flaws I spotted while re-reviewing: the broken
formatTimeWindow, the messy gap-snap loop, and the throwingOpenPickupGate.snapshot().Now the WorkerState metadata KV (for drain timestamps):
Now the acquirer gating:
Now extending workspace config parsing:
Wiring the gate into workspace-worker.ts:
Now the dashboard API + server plumbing:
Now the
run-nowCLI command:Top-level imports already cover
existsSync/writeFileSync/dirname/resolve— simplifying:Also update the worker
--helpto mention the new subcommand:Now the dashboard UI — API types and StatusStrip:
Early typecheck before writing tests:
Now writing the schedule test suite:
API semantics confirmed:
instantAtanchors on the local calendar day of the passed instant. The design is sound (per-zone day model); my test anchors were wrong. Fixing docs + tests:instantAtcurrently lands on the EDT (first) duplicate; documented policy says standard-time (second). Adding explicit ambiguity resolution:Schedule lib fully green. Now extending the existing test suites:
Now appending the gating tests at the end of the
TaskPollingAcquirerdescribe block:Full package suite:
UI builds. Now the docs — first check inbound anchor links to the section I'm replacing:
rg -rmangled the display (replacement flag) — verifying actual file contents are untouched:Now checking
cli.test.tshelp-text assertions before finalizing docs:Now the main docs rewrite — replacing the cron night recipe with the native feature:
Update the intro sentence of that doc, and the license section's cron mentions:
Now worker.md — new section plus updated cross-link:
workspaces.md — sample config, key explanations, and run-now:
Now USAGE.md:
Bump
dateModifiedon the touched docs:Full verification round — format, lint, typecheck, tests, build:
End-to-end smoke test of the new CLI against the built bundle:
Manual-run lifecycle confirmed. Final review of the core diff:
DEV-93 is implemented. All acceptance criteria are met, verified with 980 passing tests, clean lint/typecheck/format, and a green build across the monorepo.
What was built
Quiet hours / working windows for the worker's ready-task drain — gated at the
TaskPollingAcquirer.tick()level (the "window applied to the ready-task query" shape from the task notes; the automations pattern was rejected since drain ticks are already the correct seam and in-flight tasks finish naturally via sequential execution).Config (
workspace.toml)New files
packages/code/src/lib/schedule.ts— window parsing/validation (union of actives,blockedwins on overlap, midnight wrap, equal start/end rejected),WallClockbuilt on Intl (local time by default or named IANA zone), DST handling (spring-forward gap snaps forward ~within the lost hour; fall-back duplicates resolve to the standard-time pass), next-transition resolution that ignores no-op overlaps, missed-window/catch-up logic, thePickupGate(incl. run-now sentinel consumption and open/close flip events).packages/code/tests/schedule.test.ts— 34 tests incl. real US DST dates for both transitions.Changed behavior
task-polling-acquirer.ts:152): closed gate → no detect/evaluate/execute, cursors untouched; in-flight task finishes; manual/catch-up bypass one tick; schedule errors degrade to a warn-once fail-open.workspace/workspace-worker.ts): gate creation from config, startup banner (🕒 Working windows…, next flip), exactly-once-per-flip logs (🌙/[schedule]↔☀️), catch-up decision fed by persisted last-drain timestamp.worker_metaKV table inWorkerState(TASK_POLL_LAST_DRAIN_KEY).devintern worker run-now [--workspace path]writes<home>/.run-now; worker consumes it within one poll interval, drains ignoring windows./api/workernow returns aschedulesnapshot (embedded dashboard only) and the dashboard header strip shows inside/outside window + next open/close time.Docs
automated-task-processing.md: the cron-of-the-CLI "Night-only CLI runs" recipe replaced by native quiet hours (timezone/DST/catch-up/run-now); retained scheduler-agnostic guidance (PATH pinning, user services, SSH push, process cleanup, failure feedback), leaving only estimation on timers.workspaces.md+worker.md: schema, semantics, cross-links (dead#night-only-cli-runsanchors updated).USAGE.mdcron snippet replaced.Not committed — say the word if you'd like a commit/PR.
This PR was automatically created by @devintern/code