(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
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.tsstarts the REAL worker bundle (.worker/worker.mjs) fromglobalSetupand stops it inglobalTeardown. 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.tsis how a spec moves a job between lanes MID-RUN.lib/jobs/engine/cutover.tsreadsMOTIR_POSTGRES_JOB_IDS_FILEfresh 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.tsalready drive the index fleet toindexedthrough 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.tsis the engine's existing spec and the file this one sits beside.
What to assert
- 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.
- 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.
- The whole supervision, on the engine. Boot → poll → settle through the fake orchestrator, ending in ONE
job_runper repo,succeeded, carrying itsoutput.repoRef— the ledger contract MOTIR-3417 refuses to let this story change. - THE RESTART. Mid-index, kill the worker process and bring it back. The index completes, on the SAME container, with one
job_runrow 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. - The dashboard still reads it.
/settings/workspace/jobsshows 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 seams — the 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-refreshrouted to the engine throughtests/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_runper repo,succeeded, with oneoutput.repoRef, and/settings/workspace/jobsrenders 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_runrow, 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.tsandjobs-postgres-engine.spec.tsstill pass unchanged, proving the lane still serves both engines from one server.- No new
webServerentry, 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 fromglobalSetuptests/e2e/_helpers/job-routing.ts— the mid-run cutover seamtests/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 passingtests/e2e/migrate-index-fleet.spec.ts·tests/e2e/_helpers/migrate-index-seed.ts— the fleet already driven toindexedin this lanelib/orchestrator/adapters/fake/index.ts— the fake container backendlib/jobs/engine/cutover.ts—MOTIR_POSTGRES_JOB_IDS_FILEand why it is read freshlib/github/indexEnqueue.ts— the push-webhook enqueue this spec drivesmotir-core/CLAUDE.md§ E2E — the authoritative-wait discipline