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-3487

(motir-core) Story E2E — a push burst coalesces into ONE refresh run that boots, polls and settles on the engine, and survives a mid-index worker restart

Done
Description

The story's end-to-end proof: MOTIR-3417's verification recipe, automated — a push burst coalesces into ONE refresh that boots a container, polls it and settles it on the Postgres engine, and a worker restart mid-index resumes rather than orphaning.

The lane already has every piece this needs — verified on origin/main@7e97e2ed

This card assembles existing scaffolding; it stands nothing new up. Each of these was read before being named:

  • tests/e2e/_helpers/job-worker-process.ts starts the REAL worker bundle (.worker/worker.mjs) from globalSetup and stops it in globalTeardown. It is a third process in the lane on purpose — a run queued onto the engine has nothing to claim it otherwise. So the lane can already execute an engine run, and can already be made to lose one: the restart assertion is a kill-and-respawn of this process, not new infrastructure.
  • tests/e2e/_helpers/job-routing.ts is how a spec moves a job between lanes MID-RUN. lib/jobs/engine/cutover.ts reads MOTIR_POSTGRES_JOB_IDS_FILE fresh on every call precisely because an env var is fixed at server boot and one spec has to prove the engine while another proves Inngest against the same server. This is the seam that lets this spec route the three supervisor ids without a second server.
  • tests/e2e/migrate-index-fleet.spec.ts + tests/e2e/_helpers/migrate-index-seed.ts already drive the index fleet to indexed through the fake orchestrator (lib/orchestrator/adapters/fake/index.ts), so no real container and no Fly credential is involved.
  • tests/e2e/jobs-postgres-engine.spec.ts is the engine's existing spec and the file this one sits beside.

What to assert

  1. Coalescing, end to end. Deliver a burst of default-branch push webhooks for ONE repo with the refresh job routed to the engine. Exactly ONE run executes, and it indexes the repo at its default branch — the LAST push's head, not the first's. Assert on the run rows, and wait on an authoritative signal (the row reaching a terminal state), never on a timer.
  2. Two repos stay independent. A burst across two repos coalesces per repo and produces two runs, so the debounce key is doing what it claims and not merging tenants.
  3. The whole supervision, on the engine. Boot → poll → settle through the fake orchestrator, ending in ONE job_run per repo, succeeded, carrying its output.repoRef — the ledger contract MOTIR-3417 refuses to let this story change.
  4. THE RESTART. Mid-index, kill the worker process and bring it back. The index completes, on the SAME container, with one job_run row and no second boot. The story's own words for this criterion are "asserted by a test that actually restarts, not by reading the code", and the worker helper is what makes that a few lines rather than a harness.
  5. The dashboard still reads it. /settings/workspace/jobs shows the coalesced run with its outcome — proving the ledger DTOs are untouched, which is the only user-visible thing this story could have broken.

Scope boundary

ENDS at: one Playwright spec, green in the existing E2E lane, with no new webServer entry and no new external service.

NO ACCEPTANCE VIDEO, and that is a scoped exemption rather than an omission. The acceptance-video rule fires for a Story whose deliverable has a user-observable surface a person watches. This story's deliverable is which engine a background job runs on; /settings/workspace/jobs is unchanged and belongs to MOTIR-3424. A non-UI story accepts on its tests — the same disposition MOTIR-3416 records for the same reason.

Uses the FAKE orchestrator only. No real container, no Fly token, no motir-ai call. A spec that needs one of those has left this story.

Does NOT assert coverage or unit-level seamsthe story gate owns those, and the two are siblings rather than a chain.

Does NOT prove PRODUCTION. Routing here is the lane's file override; production routing is the operator task, after the merge.

Does NOT touch the Inngest-side specs. jobs-flow.spec.ts proves a job still runs on the old lane against the same server, and must keep passing untouched.

Acceptance criteria

  • With system.code-graph-refresh routed to the engine through tests/e2e/_helpers/job-routing.ts, a burst of N same-repo push deliveries produces exactly ONE executed run, and the spec proves the count rather than asserting the first run's existence.
  • That run's payload is the LAST delivery's, asserted on a field that differs between the pushes.
  • A burst across TWO repos produces two runs.
  • The completed run leaves ONE job_run per repo, succeeded, with one output.repoRef, and /settings/workspace/jobs renders it.
  • The worker is killed mid-index and restarted, and the index still completes on the same container — one boot in the fake orchestrator's record, one job_run row, no orphan. The spec asserts the boot COUNT, because "it finished" is true of the double-boot case too.
  • Every wait is on an authoritative signal — a row state, a rendered outcome — and the spec contains no bare timeout as a synchronisation device.
  • jobs-flow.spec.ts and jobs-postgres-engine.spec.ts still pass unchanged, proving the lane still serves both engines from one server.
  • No new webServer entry, no new service, and the worker is the existing bundle-running helper.

Context refs

  • tests/e2e/_helpers/job-worker-process.ts — the real worker in the lane, and why it starts from globalSetup
  • tests/e2e/_helpers/job-routing.ts — the mid-run cutover seam
  • tests/e2e/jobs-postgres-engine.spec.ts · tests/e2e/jobs-flow.spec.ts · tests/e2e/jobs-dashboard.spec.ts — the neighbouring specs, and the ones that must keep passing
  • tests/e2e/migrate-index-fleet.spec.ts · tests/e2e/_helpers/migrate-index-seed.ts — the fleet already driven to indexed in this lane
  • lib/orchestrator/adapters/fake/index.ts — the fake container backend
  • lib/jobs/engine/cutover.tsMOTIR_POSTGRES_JOB_IDS_FILE and why it is read fresh
  • lib/github/indexEnqueue.ts — the push-webhook enqueue this spec drives
  • motir-core/CLAUDE.md § E2E — the authoritative-wait discipline