(motir-core) Every E2E leg DISCARDS the shared `.next` artifact and rebuilds the app — `webServer.command` runs `next build` unconditionally, 110 s of it re-running TypeScript the `TypeScript` job already ran
Type · chore (CI configuration) · Parent · MOTIR-1464 · Repo · motir-core · Measured · run 33260551040 (PR #2457, the 8-leg E2E run) · Follows · MOTIR-3913, which measured the per-leg fixed cost and scoped this out
The finding
MOTIR-3913 recorded that each E2E leg spends ~178 s inside the Run E2E step before its first test, and called it "the dual next start webServer boot plus seeding". That description is wrong, and the truth is worth a card of its own: the leg is compiling the application from scratch.
bulk-7's boot window on run 33260551040, read off its log:
| elapsed | |
|---|---|
prisma generate | 5 s |
Creating an optimized production build … → ✓ Compiled successfully | 48 s |
Running TypeScript … → Finished TypeScript in 110s | 110 s |
| collecting page data + generating 207 static pages + finalizing | 6 s |
server up, Running N tests using 1 worker | — |
| total | 189 s |
Across the eight bulk legs the boot ran 128–189 s (mean ~170 s).
Why it happens — two correct-looking pieces that cancel each other
ci.yml'sbuildjob compiles the app once and uploads.nextas thenext-buildartifact. Its own comment says why: "Share the production build with the e2e job so each matrix leg's …"..github/actions/e2e-setupdefaultsnext-build: downloadand pulls that artifact into.next/. Its input doc calls this "the cheap path".playwright.config.ts'swebServer.commandthen throws it away:pnpm exec prisma generate && pnpm exec next build && pnpm run build:worker && pnpm exec next start --port ${PORT}next buildis unconditional. The downloaded.nextis overwritten by a fresh compile in every leg.
Neither piece is wrong on its own. The artifact is produced, transferred and then discarded, so the sharing mechanism has been paying its upload/download cost while delivering nothing.
The webServer.command comment (MOTIR-1679) explains why the suite runs against a production build rather than next dev, and that reasoning is sound and must survive: "The build runs inside this command so the flow is identical locally and in CI". That is a statement about LOCAL convenience. In CI the build already exists.
What it costs
- ~170 s × 8 bulk legs = ~1360 s of runner time per run, plus the two
@a11ylegs and (onpush) the at-scale legs — all three matrices go through the samee2e-setup+webServer. - 110 s of each leg is
Running TypeScript, which duplicates the dedicatedTypeScriptjob (2.6 min) — eight to ten more times per run. - It is ~45% of a bulk leg's 7.4–9.2 min wall, and therefore the single largest remaining item on the E2E path, which MOTIR-3913 left as CI's binding constraint.
Acceptance criteria
- A bulk leg does not compile the app.
Creating an optimized production buildandRunning TypeScriptdo not appear in its log, and its pre-test window drops from ~170 s to whatevernext start+ seeding actually costs. Quote the before/after from the logs, not the job totals. - Whatever replaces the unconditional
next buildkeeps the MOTIR-1679 contract intact: the suite still runs against a productionnext start, and a LOCALpnpm test:e2ein a fresh worktree still builds what it needs without a manual step. Say in the config comment how the two paths now differ and what selects between them. - The
next-build: buildpath (acceptance-video.yml, which is a separate workflow and cannot read this one's artifacts) still works unchanged. build:workeris accounted for explicitly — it is on the same command line and is NOT part of thenext-buildartifact, so it either stays in the webServer command or moves somewhere that runs before it. Say which and why.- The suite passes with the same spec and test counts as run
33260551040(420 across eight bulk legs), on a.nextthat came from the artifact rather than a local compile — i.e. prove the downloaded build is actually being served, not just that nothing failed.
⚠️ Measure BALANCE as well as boot, or this card will look like it regressed
MOTIR-3913's AC 3 missed: the eight legs' test-execution time spread 1.402x against a packer that predicted 1.008x, because a few specs have large run-to-run cost variance. The fixed ~261 s per leg is currently damping that — a 1.402x test spread reads as only 1.17x in job terms.
Removing ~170 s of fixed cost removes most of the damper. The same variance will then show up as a wider job-time spread, and a reader comparing only "slowest leg" before and after may conclude this made things worse. So this card must report both: the boot reduction AND the resulting leg spread, with the arithmetic that separates the two. The same caution applies to any later decision to add legs.
Out of scope
- Making
next builditself faster (e.g.typescript.ignoreBuildErrorsfor the E2E path). If the artifact is reused there is no build here to speed up. If the chosen fix keeps a build in the leg for some reason, that is a different card and needs its own argument for why the artifact could not be used. - The
TypeScriptjob. It is the right place for type-checking; the problem is the eight extra copies, not that one. - The
buildjob's own 4.1 min, which every leg waits on.
Context refs
playwright.config.ts—webServer[0].command(the unconditionalnext build) and the MOTIR-1679 comment above it explaining the production-build requirement..github/actions/e2e-setup/action.yml— thenext-buildinput,downloaddefault, and theDownload .next/ build artifactstep..github/workflows/ci.yml— thebuildjob's artifact upload and the threee2e-setupcall sites (e2e,e2e-at-scale, and the acceptance lane's sibling workflow).- Run
33260551040, jobPlaywright E2E (bulk-7)— the boot timeline quoted above.
Resolution: open.