Found during motir run MOTIR-3232 (2026-08-20), writing the story's browser E2E (MOTIR-3243).
Story MOTIR-3232 rebuilds /plans — a PAGE. It fanned out into eleven children: eight that change the page's code and its services, one vitest gate, and two E2E cards placed last, blocked by the other nine. That is the ordinary and, card-for-card, defensible shape: an E2E of a surface genuinely needs the surface.
MOTIR-3241 (the tab strip) declared the page's URL parser — ten lines, pure string → string, no hooks — inside the 'use client' component that pushes the URL, and the page, a Server Component, imported it. 'use client' marks a module, not a declaration, so every export of it is a client REFERENCE: the server got a proxy, calling it threw, and /plans answered 500 on every request.
It then survived, undetected, through five more subtask PRs on the same branch, because nothing in the story's plan opens the page:
tsc --noEmit — clean. The types are real.next build — clean. The boundary is only crossed at render.The first instrument that could see it was MOTIR-3243's Playwright walk, whose opening assertion (the tab strip is visible) found an error page. Had the E2E card been dropped, deferred, or scoped to a different surface, the story would have merged a permanently-500ing page with eleven green cards behind it.
A story whose deliverable is a rendered surface owes at least one instrument that OPENS that surface, and owes it EARLY — not only as its last card. Not because the unit tests are wrong, but because a whole class of defect lives in the server/client seam and is invisible to every one of them; the cost of finding it at the end is that N cards have shipped on top of it.
Cheap shapes worth considering rather than a second full E2E:
'use client', flag named imports from a module that HAS it whose name starts lowercase. That is a static check, it runs in seconds, and it found exactly this one instance and (after the fix) none. It would not have needed a browser at all.The third is the narrowest and might belong in motir-core rather than in the planner; the first two are planning rules. Deciding which is this card's work — this is a report with a proposal, not a settled rule.
⨯ Error: Attempted to call planStatusFromParam() from the server but planStatusFromParam is on the client. — digest: '628730192', on GET /plans, in a production build (next build && next start), acceptance lane.heading "This page couldn't load", paragraph: A server error occurred. Reload to try again.parent/MOTIR-3232-plans-surface-refined in motir-core, at the commit before MOTIR-3243's. Not a statement about main — the defect never reached it, because the story's own E2E card caught it first, which is the one thing that went right.lib/planning/planStatusFilter.ts (no directive), imported by both sides. Lesson logged as notes.html #353 (motir-meta PR #299).