Files
suaveplan/openspec/archive/2026-05-12-enterprise-server-job-scheduler/tasks.md
2026-05-27 23:29:53 +01:00

5.4 KiB

Tasks: enterprise-server-job-scheduler

Change ID: enterprise-server-job-scheduler Package: @suaveplan/job-scheduler Target path: packages/control-flow/job-scheduler Prerequisites on Verdaccio: @suaveplan/error, @suaveplan/types, @suaveplan/events, @suaveplan/retry, @suaveplan/logger, @suaveplan/cron

Amended 2026-05-11: Cron parsing extracted to @suaveplan/cron. Task §3 is replaced by a thin consumer wrapper; the bundled cron-parser is removed.

Amended 2026-05-12: Implemented at the genesis-canonical path packages/control-flow/job-scheduler/ (universal-tier control-flow primitive). The proposal's server/infra/job-scheduler path was pre-reorg; flat layout per cron-sibling convention used here.


1. Scaffold

  • Create directory tree (flat layout mirroring @suaveplan/cron).
  • package.json at 0.1.0, "sideEffects": false, exports . + per-feature subpaths.
  • Deps: workspace + catalog deps (incl. @suaveplan/cron workspace:^).
  • DevDeps: vitest, @vitest/coverage-istanbul, fast-check, @suaveplan/testing.
  • tsconfig.lib.json extending tsconfig.base.json (universal tier — no library-node.json).
  • vitest.config.ts standalone with istanbul + 100% thresholds.
  • Scripts: lint, typecheck, test, test:coverage, build, docs:check.

2. Types and errors

  • All interfaces in src/scheduler/job-scheduler.types.ts; Zod schemas in src/scheduler/job-scheduler.schemas.ts.
  • Sibling .md docs ≥200 words per impl file.
  • src/errors/job-scheduler-error.ts: three codes (JOB_NOT_FOUND, EXECUTION_FAILED, TIMEOUT); registerJobSchedulerErrorCodes(); eager call. Cron parse failures propagate as CRON.* codes from @suaveplan/cron.

3. Cron integration

  • Consume @suaveplan/cron: parseCron, CronExpression. No local cron-parser module.
  • In schedule(cronExpr, handler, options): call parseCron(cronExpr); store the returned CronExpression; compute first + every subsequent fire via expression.nextRun(clock.now(), tz).
  • Honour CRON.NEXT_RUN_NOT_FOUND: drop the job after emitting job:scheduling_exhausted and incrementing job_scheduler.cron_exhausted_total. Implemented in src/scheduler/cron-rescheduler.ts.

4. Job queue

  • src/queue/job-queue.ts: array-backed binary min-heap by runAt; enqueue/dequeue/peek/size/remove(jobId)/snapshot.
  • src/queue/job-queue.test.ts: priority order, head/tail/mid removal, empty edge cases, large randomised sequence.
  • src/queue/job-queue.md ≥200 words.
  • Property-tested in tests/property.test.ts (monotonic dequeue, remove-all-leaves-empty, sorted-output).

5. Job scheduler

  • src/scheduler/job-scheduler.ts: JobScheduler + createJobScheduler. Tick loop driven by injected timers. start/stop/schedule/queue/cancel/pause/resume/jobs.
  • src/scheduler/job-dispatcher.ts: per-job dispatch + bookkeeping (separated so scheduler core stays <300 lines).
  • src/execution/job-execution.ts: per-attempt retry/timeout/abort/event surface.
  • src/scheduler/cron-rescheduler.ts: cron-next-fire + catchup-missed branching.
  • src/scheduler/job-snapshot.ts: JobStatus projection.
  • src/scheduler/job-scheduler.test.ts + src/scheduler/job-scheduler-edge.test.ts (split for ≤500-line cap): covers immediate queue execution, cron scheduling, concurrency cap, cancel-during-run, retry, catchup, pause/resume, lifecycle idempotency.
  • Sibling .md per impl file (≥200 words, all 6 sections).
  • Events emitted via EventBusLike from @suaveplan/types: job:start, job:complete, job:fail, job:scheduling_exhausted, job:paused, job:resumed, job:cancelled. JOB_SCHEDULER_EVENTS registry in src/events/events.ts.
  • OTel surface in src/otel/job-scheduler-otel.ts: duration_ms histogram, runs_total counter, failures_total counter, cron_exhausted_total counter, queue_depth histogram. Closed outcome ∈ {success,error,timeout} and kind ∈ {cron,queued} labels.

6. Registry

  • src/registry/job-registry.ts: register/unregister/resolve/list; duplicate-name throws JOB_SCHEDULER.JOB_NOT_FOUND.
  • Sibling .md ≥200 words.

7. Integration + property tests

  • tests/integration.test.ts: three cron jobs over 10 simulated minutes (every-1m, every-2m, every-5m) with expected counts 10/5/2; stop() drains a slow handler.
  • tests/property.test.ts (fast-check): monotonic dequeue, enqueue+remove-all leaves empty, sorted-output property.

8. Docs + verify

  • Root README.md: quick-start, queued example, retries+timeout, FakeClock recipe, API table, relationship to @suaveplan/cron, events list, observability list.
  • bunx turbo lint --filter=@suaveplan/job-scheduler — green.
  • bunx turbo typecheck --filter=@suaveplan/job-scheduler — green.
  • bunx turbo test --filter=@suaveplan/job-scheduler — 96 tests pass.
  • bunx turbo test:coverage --filter=@suaveplan/job-scheduler — 100% statements/branches/functions/lines.
  • bun run docs:check — 10/10 files at ≥200 words.
  • bun scripts/publish.ts --package @suaveplan/job-scheduler (deferred to maintainer).
  • Update CLAUDE.md and MANIFEST.md (deferred to maintainer).
  • Archive to openspec/archive/2026-05-12-enterprise-server-job-scheduler/ (deferred to maintainer).