Appearance
Expected baseline: my reference
Not for students. This is what students should see when they run npm run ci on a fresh clone of OrbitTasks. I use it to sanity-check their numbers in Workshop 1, and to set up Workshop 2's bottleneck-ranking exercise.
The numbers below are approximate and machine-dependent. The shape matters more than the exact values: test:api should dominate everything else (it's ~95% of total).
May 2026 rebuild. The repo was extended and repaired (see the "May 2026 build update" in
workshops/REMEDIATION-PLAYBOOK.md). The pipeline now actually runs green except the documented slow/flaky tests. Previously it shipped unable to lint or run the web tests at all. The cold pipeline now runs ~13 minutes (measured 12m55s on a modern laptop; was ~1–5 min), almost all of it intest:api, which is intentional: the slow integration suite is the centerpiece students profile and then cut down.
On a clean machine (cold cache, no node_modules)
These come from a real run on a modern laptop. Hardware swings them ±40%.
| # | Stage | Expected duration | What dominates |
|---|---|---|---|
| 1 | install | 10–90 s | npm downloading ~760 packages (warm npm cache → ~10–20s; truly cold → ~90s) |
| 2 | lint | 2–10 s | ESLint over both src trees |
| 3 | typecheck | 3–10 s | tsc --noEmit, incremental: false |
| 4 | test:api | ~12 min | maxWorkers: 1 + coverage + a big suite of integration tests making real HTTP round-trips to the mock server (billing rollups, email campaigns, webhook fan-out, search, notifications) |
| 5 | test:web | 3–10 s | Vitest + coverage (single fork) |
| 6 | build:api | 1–10 s | tsc non-incremental |
| 7 | build:web | 1–5 s | Vite production build |
| 8 | deploy | 15–40 s | scripts/deploy.sh copies one file at a time |
| Total | ~12–14 min | test:api is ~95% of it |
test:api reports FAIL on roughly half of runs, not because the code is broken, but because of the documented flaky tests (see below). Re-running flips it. That's a feature, not a bug: it's the Workshop 2 lesson.
On a warm machine (node_modules already installed)
Same shape; only install shrinks (to ~10–20s). Total still ~12–14 min because test:api is unchanged.
Why ~13 minutes (and why we call it "very slow")
We picked ~13 minutes deliberately. It is uncomfortably slow on purpose: even one full run is a lot to sit through in a 60-minute session, and that's the point. The discomfort is the motivation. (I feel it too: when prepping, I won't want to run npm run ci more than I have to. That's the lived experience of a slow pipeline, in miniature.)
The pattern is what students learn from: one stage (test:api) dominating, real network slowness in integration tests, no parallelism, flaky tests producing false greens. The optimization arc is a two-stage cut: Workshop 3 (caching/parallelism/sharding/incremental build/dropping always-on coverage) brings it to ~5–7 min; Workshop 5 (mocking the SDK clients so the integration suite stops making real HTTP) collapses test:api by an order of magnitude. Students replicate the "we cut the pipeline by 70%+" story.
What students should notice in Workshop 1
Without prompting, most students identify:
test:apiutterly dominates, ~95% of total. Everything else is noise by comparison.- Within
test:api, the integration tests are the cost: each one makes dozens to hundreds of real HTTP calls to the mock server, one at a time (maxWorkers: 1). The billing rollups, email campaigns, and webhook fan-outs each take ~45 seconds. - The original slow test in
apps/api/tests/reports.test.ts("rolls up end-of-month billing") is still there, now joined by a wholetests/integration/suite of the same shape. - A handful of tests fail intermittently on re-run (the flaky tests).
If a student doesn't notice the flaky tests, don't tell them. Point them at the output and ask "anything jump out when you run it again?" The discovery is the lesson.
The flaky tests (now five)
All five use a genuine Math.random/clock race and fail ~50% of the time on re-run:
| Test | File |
|---|---|
processes async events within budget | apps/api/tests/reports.test.ts |
auto-saves a task within budget | apps/api/tests/tasks.test.ts |
renders a relative timestamp | apps/web/tests/components/CommentList.test.tsx |
signs in once the request resolves | apps/web/tests/pages/Login.test.tsx |
shows the task list shortly after load | apps/web/tests/pages/Dashboard.test.tsx |
When numbers look weird
| Symptom | Likely cause |
|---|---|
test:api is < 2 minutes | The integration tests didn't run; check the mock server started (tests/setup/globalSetup.js) and MOCK_SERVER_URL is set. |
test:api passes every single time | Possible, but unlikely; the flaky tests fail ~50% each. If it's always green, someone may have stabilised them early. |
test:web is 0 seconds | Vitest failed to find tests, or @vitest/coverage-v8 is missing; run npm test --workspace=apps/web directly. |
lint fails | Should pass now. If it errors on an unknown rule, eslint-plugin-react-hooks didn't install; re-run npm install. |
| Total is < 5 minutes | The integration suite was skipped or maxWorkers was bumped. |
Whatever the numbers, the ranking holds: test:api ≫ deploy ≈ install > everything else.