MotirBuilding in public
MOTIR · moooon
onMotir
You’re viewing a public project. Anyone can view it — no account needed. Sign in to submit, upvote, or comment on requests.View-only — you can’t edit work items
MOTIR-3473

(motir-core) Story E2E — a scheduled job fires on the engine and appears on the jobs dashboard, and a worker restart honours the declared catch-up

Done
Description

The story's Playwright E2E: the verification_recipe, automated, against a real worker process and a real browser.

The flow it drives

  1. Route a scheduled job to the engine mid-spec, with routeJobsToEngine(...) from tests/e2e/_helpers/job-routing.ts — the file-backed override lib/jobs/engine/cutover.ts documents, which exists precisely because an env var is fixed at server boot and one lane has to move a job between engines without a restart.
  2. Start a real worker with tests/e2e/_helpers/job-worker-process.ts, as tests/e2e/jobs-postgres-engine.spec.ts already does.
  3. Wait on the authoritative signal — the run row — never on a sleep.
  4. Open /settings/workspace/jobs, filter to that job, and confirm the run appears with the expected status and its scheduled.<id> provenance. That page is the story's verification recipe, and this is the step that proves an operator can actually see a scheduled run on the new engine.
  5. Restart the worker across a fire and confirm what the recorded catch-up policy says should happen: for a catch-up job, the missed fire runs on restart; for a skip job, it does not. This is the second half of the recipe and it is the only assertion in the story that exercises downtime as a real event rather than as a simulated clock.

The pacing problem, named rather than discovered

Every one of the 14 shipped cadences is too slow for a spec: the fastest is one minute. Do not re-time a production job to make the test convenient — the story's own criterion is that the schedule constants are unchanged. Choose one of the two honest routes and say in the spec's header which, and why:

  • register a test-only cron job on a fast expression, alongside the fixtures the suite already builds, and drive that; or
  • drive one of the 14 with the scheduler's injected clock advanced, which is exactly what the injectable now is for.

The first proves more of the real path; the second proves the real job. Pick deliberately.

Scope boundary

Does NOT assert production behaviour, a live cadence, or a deployed ledger — the production cutover task under the epic owns those.

Records no acceptance video. This story ships no user-observable surface of its own: /settings/workspace/jobs already exists and is unchanged, so the non-UI-story exemption applies exactly as it does on MOTIR-3415.

Does NOT change the jobs dashboard, its DTOs or its service. If the page cannot show what this spec needs to assert, that is a finding to surface, not a change to make here.

Acceptance criteria

  • The spec routes a job to the engine mid-run via the file override, starts a real worker, and asserts a succeeded scheduled run — waiting on the row, not on a timer.
  • /settings/workspace/jobs filtered to that job shows the run, and the assertion is against the rendered row rather than the database — the recipe says open the page.
  • A worker restart across a fire produces the outcome the declared catch-up policy specifies, asserted for at least one catch-up job and one skip job.
  • No production schedule constant is modified by this card, and the spec header states which of the two pacing routes it took and why.
  • The spec cleans up its routing with clearJobRouting() so a sibling spec against the same server is unaffected — the same discipline jobs-flow.spec.ts relies on.
  • It runs in the existing E2E lane with no new service and no new global setup step.

Context refs

  • tests/e2e/jobs-postgres-engine.spec.ts — the closest existing spec; extend or mirror it
  • tests/e2e/_helpers/job-routing.tsrouteJobsToEngine / clearJobRouting
  • tests/e2e/_helpers/job-worker-process.ts — starting and stopping a real worker from a spec
  • tests/e2e/jobs-dashboard.spec.ts — how the dashboard is already driven and filtered
  • tests/e2e/global-setup.ts · global-teardown.ts — the lane's worker lifecycle
  • lib/jobs/engine/cutover.ts — why the file override exists and why it is refused in production
  • CLAUDE.md § E2E — waiting on authoritative signals rather than timeouts