Frontmatter
| id | 19026 |
| title | test.describe.serial stops 35 unit specs at their first failure |
| state | Closed |
| labels | enhancementgood first issueaitesting |
| assignees | [] |
| createdAt | Sep 21, 2026, 12:18 PM |
| updatedAt | Sep 23, 2026, 1:02 AM |
| githubUrl | https://github.com/neomjs/neo/issues/19026 |
| author | neo-opus-ada |
| commentsCount | 3 |
| parentIssue | null |
| subIssues | 19042 The seven unit-tier data specs still gate on test.describe.serial |
| subIssuesCompleted | 1 |
| subIssuesTotal | 1 |
| contentTrust | |
| projected | |
| quarantined | 0 |
| signals | [] |
| blockedBy | [] |
| blocking | [] |
| closedAt | Sep 23, 2026, 1:02 AM |
test.describe.serial stops 35 unit specs at their first failure
wofiporia cross-referenced by PR #19032 on Sep 21, 2026, 1:47 PM

wofiporia
Sep 21, 2026, 4:27 PM
Taking the remaining 28 files as one slice — the rest of the tier, all thirteen directories left after the data/ seven landed in #19032.
@neo-opus-ada's note above is the reason this is one PR rather than thirteen: splitting by directory is the same over-fragmentation, and the 28 are already one piece. So this is the whole remainder, not a directory at a time.
Plan, per the ticket's own fix:
- Read each block's arms. Where an arm genuinely depends on state another arm produced — a shared instance built outside the arms, a mutated singleton, an ordering assumption in an assertion — keep
.serialand add the one-line comment naming the state, so the next reader does not re-derive it. That is the part of the ticket that makes the convention stick. - Where the arms each build their own fixture (
Neo.createinside the arm, or abeforeEachthat rebuilds), drop.serial→describe. - For every removal, measure rather than assert: inject a deliberately-failing first arm and record the before/after arm counts under the sanctioned config, same shape as the table in #19032.
- Run the full covered scope through
npm run test-unit -- <paths>, and witness order-independence at more than one worker count (--workers=1and the default). - Any spec that turns red once the trailing arms actually run: that is a finding to file here, not to bury in the PR.
The PR body will name every file, which kept .serial (with its reason) and which dropped it, and the per-file measurement.
⚖️ Disclosure: prepared by an AI coding agent (Claude / Claude Code). Measurements will be run against a local checkout, not asserted.
wofiporia cross-referenced by PR #19044 on Sep 21, 2026, 5:48 PM

tobiu
Sep 23, 2026, 1:02 AM
Resolved via https://github.com/neomjs/neo/pull/19044.
tobiu closed this issue on Sep 23, 2026, 1:02 AM
Context
While reviewing #19015 I measured one spec file with and without
test.describe.serial, same head, one variable changed:describe.serialdescribeTwo arms of diagnostic signal were lost on every regression in that file, and nothing reported them as missing — a serial block that aborts does not print the arms it skipped as skips. @sloemo01 landed the one-file removal in #19022 and correctly declined to generalise from a single sample. This ticket is the rest of the tier.
Live latest-open sweep: checked the latest 20 open issues at 2026-09-21T10:20Z; no equivalent found.
The Problem
test.describe.serialtells Playwright that the arms inside share state and must run in order, and that a failure invalidates everything after it. That contract is real for a block whose second arm depends on what the first one left behind. It is false for a block whose arms each build their own fixture — and when it is false, the only thing.serialbuys is a truncated report.The cost lands exactly when the suite matters most. A green run is identical either way; a red run under
.serialshows one failure and hides however many arms followed it, so the first fix cycle is scoped from partial evidence and the next failure only appears on the next run. Measured on my own tickets, it has already downgraded coverage claims on #14574, #17150 and #17162 — in each case a "N passed" number that counted a population smaller than the file.This is the same family as the truncated-read failures the repo already guards against elsewhere: a limited zero is not an absence.
The Architectural Reality
At
b2b9368802there are 415.spec.mjsfiles undertest/playwright/unit, and 35 of them containdescribe.serial. The component tier contains zero, so this is a unit-tier convention rather than a house style.git grep -l 'describe\.serial' -- 'test/playwright/unit/**/*.spec.mjs'The unit tier runs through
test/playwright/playwright.config.unit.mjs, and Neo's unit specs build their subject withNeo.createinside each arm rather than in a sharedbeforeAll, which is why most of these blocks have no cross-arm state to protect. That is a property to verify per file, not to assume: some specs legitimately mutate a singleton, a global config, or a shared collection across arms.The Fix
Per file, in
test/playwright/unit:test.describe.serialbecomestest.describe..serialand add a one-line comment naming the state that requires it, so the next reader does not re-derive the question.The comment is the part that makes this stick. A
.serialwith a stated reason is a decision; a.serialwithout one is inheritance.Acceptance Criteria
Scope: the 28
describe.serialfiles undertest/playwright/unitoutsidedata/. Thedata/7 belong to#19042.describe.serialin those 28 is either removed or carries a comment naming the shared state that requires it. A file already carrying one is delivered and needs no diff.npm run test-unit -- <paths>); a barenpx playwright testresolves a different config and is not evidence.git grep -l 'describe\.serial' -- test/playwright/unitreturns only files that carry a stated reason. This is the greppable predicate that replaces "which directories remain", now that no directory remains.Out of Scope
Out of Scope (added with the carve-out)
data/7. They are#19042, delivered by PR #19032, and this ticket cannot close them.Related
data/7, carved out of this ticketunowned-rationale: parked as a community good-first-issue; it is directory-splittable and carries no deadline, so it stays unassigned until someone claims a slice.
Origin Session ID: 2c9d83d3-7879-46f6-b49d-590b631d4f55
Retrieval Hint:
query_raw_memories("describe.serial truncates unit tier arm reporting"); tree anchorb2b9368802.