Concurrency
Grounded in flowbase/apps/frontend/e2e — 233 Playwright specs, currently scattered-flaky.
Assessed 2026-08-24 · A1 completed 2026-08-25 · MISSION.md · RESOURCES.md
Knowledge graph
Green = mastered. Amber = inner fringe (just learnt, still fragile — A1). Blue = outer fringe / zone of proximal development, lessons written and ready. Grey = locked. Click a node to open its lesson.
graph TD A[Async/await mechanics]:::got B[Playwright auto-waiting
web-first assertions]:::got C[Locator strategy]:::got D[Worker model:
process per worker]:::got E[Flake taxonomy:
async wait / concurrency / TOD]:::inner F[Symptom-first diagnosis]:::inner G[Retry policy as signal]:::inner H[The isolation ladder]:::zpd I[Fixture scope as a
sharing decision]:::zpd J[Collision surface audit:
what is actually shared]:::zpd K[Per-tenant test isolation]:::locked L[Serialisation primitives:
serial / mutex / lease]:::locked M[Cleanup contracts &
failure-safe teardown]:::locked N[Convergent assertions over
shared collections]:::locked O[Flake budgets & quarantine]:::locked A --> B --> C B --> E D --> J E --> F --> G E --> H E --> J D --> I H --> I H --> J H --> K H --> L J --> M J --> N G --> O click E "lessons/a1-naming-the-flake.html" "A1" click F "lessons/a1-naming-the-flake.html" "A1" click G "lessons/a1-naming-the-flake.html" "A1" click H "lessons/a2-the-isolation-ladder.html" "A2" click J "lessons/a3-collision-surface-audit.html" "A3" click I "lessons/a4-fixture-scope.html" "A4" classDef got fill:#dcfce7,stroke:#16a34a,color:#14532d classDef inner fill:#fed7aa,stroke:#ea580c,color:#7c2d12,stroke-width:2px classDef zpd fill:#dbeafe,stroke:#2563eb,color:#1e3a8a,stroke-width:3px classDef locked fill:#f5f5f4,stroke:#a8a29e,color:#78716c
Assessment · 2026-08-24
| Probe | Result |
|---|---|
| Q1 · slow vs. racy | Partial. Defined racy purely as inter-test contention. Missing: a solo test can race the application's own asynchrony. Conflates the ~45% category with the ~20% one. |
| Q2 · worker collision surface | Partial. Strong on the system under test. Correct instinct that separate processes share little memory. Missed the on-disk and cross-cutting surfaces: storageState, seed artefacts, ports, output dirs. |
Q3 · await expect placement | Correct and precise. Named the polling distinction unprompted. Treated as mastered. |
| Q4 · retry legitimacy | Healthy instinct, absolutist. "Never acceptable" is right about retry-as-fix, misses retry-as-instrument (counted, budgeted, ratcheted). Refined in A1 §6. |
| Q5 · isolation strategies | Blank — the clearest gap. Produced two of six-ish rungs and no cost analysis. This defines the ZPD. |
Suite findings feeding the path
- Zero
waitForTimeoutacross 233 specs; tightactionTimeout/expect.timeout. Async Wait discipline is already strong — lessons should not spend time there. - One user, one company, one
storageState, shared by all eleven projects. Codebase comment: "shared, JWT-bound company — there is no per-test company." - Guard fixtures (
companyNameGuard) and a documented virtualised-list seeding bug: existing scar tissue from shared-tenant design. retries: 3(four attempts) with no flake budget in evidence.fullyParallelunset → parallel across files, serial within. Relevant to A2.
Lessons
Taxonomy, glossary, symptom-first diagnosis, retry policy. Ends with your suite classified.
Covers: E · F · G
Seven rungs from share-everything to a tenant per test, priced. A rung per resource, not per suite. Places your six main resources on it.
Covers: H · closes the Q5 blank
Four questions, seven layers. The procedure that catches the non-database surfaces — auth artefacts, filesystem, ports, background jobs. Applied to this repo.
Covers: J · closes the Q2 gap
Run / worker / test / ambient scope as blast radius. Scope contagion, the three teardown failures, and your suite's scope map.
Covers: I
K, L, M, N, O. Pick which after A2–A4.
Findings surfaced while writing A2–A4
Read from the suite; each is used as a worked example in the lesson noted.
- Slot partitioning is unpinned.
findSeededProductresolves a worker's slot withitems[index % items.length]over a query with no explicit sort. Rung 3 in appearance, rung 0 in effect. (A2 §5, hunt A — cheapest real fix available.) seededProductis scope contagion. Declared test-scoped, resolves the same row for every test in the worker and again on retry. Safe today only by the convention that all consumers treat it as read-only. (A4 §2, §7.)authedRequestdisposes outsidefinally. A throwing test leaks the request context for the life of the worker. Low blast radius, two-line fix. (A4 §5, hunt E.)- Identity is a separate hot surface from data. One
DEFAULT_USERbehind every worker's JWT — any spec that changes its password, role or language fails the whole run at once. Highest-risk directory in the repo. (A3 §7.) - Renders are an unlisted concurrent writer.
globalSetuppoints the backend at the preview server, so a server-side headless browser works against the same shared company as the tests. (A3 §7, layer 7.)
Teaching preferences
- Answers are terse and technically literal — pitch at that register, no hand-holding prose.
- Says "don't know" cleanly rather than bluffing. Take blanks at face value; they mark real edges.
- Learning is in service of a live broken suite. Every lesson must end in something applicable this week.
- TypeScript for all code examples.