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

(motir-core) Story gate — vitest coverage floor, the emit-to-run seams, and the totality guards a percentage cannot see

Done
Description

The story's per-repo vitest gate. It runs AFTER every code subtask of this story has merged, against real Postgres, and does three things — none of which is a static list of cases written now.

1. Coverage floor

Run coverage over this story's changed surface and, wherever a file is below the project's per-file floor (≥90% branch / function / line — motir-core/CLAUDE.md § coverage), write the missing tests to reach it. Each subtask ships its own units; this tops up the seams BETWEEN them.

⚠️ Sort the report before writing a single bullet. A coverage zero measures execution and says nothing about reachability, so nobody tested this and nothing can test this produce the same cell. Split the uncovered arms into defensive (a ?? null, an unreachable default:, a guard on a value its producer cannot emit) and rule-bearing (a filter, an early return, a status branch), and read each verdict off the PRODUCER of the value the arm tests, not off the arm. Where an arm is genuinely dead, the criterion is a test asserting the INVARIANT that makes it dead, plus an ignore directive citing that test by name — never an ignore directive alone, which moves the number and hides the gap.

2. The emit→run seam, end to end

The units mock the boundary; this drives it. For one fast-lane consumer, in one test, against real Postgres: emit through sendEvent → the dispatcher writes job_event + job_queue → the worker's claim path picks it up → the handler runs → job_run carries the ledger row /settings/workspace/jobs reads. Assert the DTO the operator surface consumes, not the table — a key drift between the writer and the reader is exactly what per-subtask units cannot see, because each side mocks the other.

Do the same for the idempotency seam: two events, one key, one delivery, read back through the same DTO.

3. The guards a percentage cannot see

  • Totality of the routing switch over the WHOLE registry. For every registered non-cron job, routing it moves it and only it: with its id in the set, dispatchEventToEngine enqueues it; with the id absent, it does not. And the split-subscriber invariant, which is what makes a partial cutover safe — hasInngestSubscribers stays true for an event while ANY of its subscribers is unrouted, and turns false only when the last one moves. Derive the job list from the registry, never from a hand-written array.
  • The import boundaries, as tree-level assertions. Nothing outside lib/jobs/** + scripts/worker.ts reaches the engine internals (JOB_ENGINE_RESTRICTION) or @/lib/jobs/client (the boundary this story widened). A lint rule is enforcement; this is the assertion that the rule still has the shape the story gave it.
  • Tenancy. The dispatcher writes job_event and job_queue through withSystemContext. Assert the rows carry the emitting event's workspace_id — including the deliberate null for a cross-workspace email.send — and that they are subject to RLS rather than written at the tenant root. Fixtures use adminDb.

Scope boundary

motir-core only — this story ships in one repo, so it has one gate card and it does not straddle.

Asserts only the ASSEMBLED surface of its merged dependencies. Nothing here tests the scheduled-job cutover, the supervisors, or any behaviour whose code has not landed. Nothing here measures latency: a CI runner has no production load and no scheduler, so a timer around a function call would pass forever and fail for unrelated reasons — the latency reading is the re-measurement task's, taken from production. tests/jobs/fast-lane-latency-budget.test.ts already draws this line for the same budget and says why; do not blur it.

Acceptance criteria

  • Every file this story changed meets the per-file coverage floor, or carries an ignore directive naming the invariant test that proves the arm dead.
  • The emit→run seam test drives a real sendEvent to a real job_run row against real Postgres, and asserts through the DTO the operator surface reads.
  • The idempotency seam test proves one delivery for two same-key events, read back through that same DTO.
  • The routing-totality guard iterates the registry rather than a literal list, and FAILS if a job is added whose event cannot be routed.
  • The split-subscriber invariant is asserted in both directions on an event with more than one subscriber.
  • The two import boundaries are asserted over the tree.
  • The tenancy assertions cover both a workspace-scoped event and the null-workspace email.send case.
  • The suite passes with no retries configured — a retry here would hide exactly the ordering flakiness this gate exists to expose.

Context refs

  • tests/jobs/engine-dispatcher.test.ts · engine-worker.test.ts · engine-ledger.test.ts · engine-story-gate.test.ts — the shipped engine suites this composes with, and the shape MOTIR-3426 established for a gate card
  • tests/jobs/rls.test.ts — the tenancy assertions to extend
  • tests/helpers/adminDb.ts — the owner client fixtures use
  • lib/jobs/engine/dispatcher.ts · lib/jobs/engine/registry.ts — the registry the guards must derive from
  • lib/services/jobRunsService.ts · lib/dto/jobs.ts — the consumer DTO the seam tests assert through
  • tests/jobs/fast-lane-latency-budget.test.ts — the precedent for what this suite must NOT claim to measure