Found while running MOTIR-3682, whose acceptance criterion 2 cannot be met until main deploys.
highestdeploy is a job inside ci.yml gated behind the at-scale legs on push-to-main (.github/workflows/ci.yml, "Ship it — the deploy runs AFTER the gates"). That leg is red, so nothing has deployed since release v166, 2026-08-27T01:50Z.
fly releases -a motir-core shows v167–v172, all complete, all carrying the same ImageRef registry.fly.io/motir-core:deployment-01M10E9MC4EWT8YPBSMT4EKVZ8. They are secrets set restarts, not deploys. FLY_IMAGE_REF read from inside all four machines agrees.grep -rl 'system.public-follow-digest-tick' /app/worker inside the running machine returns nothing; system.attachment-gc (control) hits.MOTIR_POSTGRES_JOB_IDS today therefore point at jobs the running process does not know about, and have no timer on either lane — see MOTIR-3688 and #2351.Nothing alerts on this. It was found by reading fly releases while chasing an empty job_run.
Playwright E2E at-scale (billing-cloud), run 33061991669 on 555932db9. Two attempts, four different tests, one surface:
| attempt | spec | locator | outcome |
|---|---|---|---|
| 1 | cloud-cadence.spec.ts:131 | getByTestId('ai-planning-settings') | failed (attempt + retry) |
| 2 | cloud-cadence.spec.ts:131 and :432 | same | failed |
| 2 | cloud-lesson-recording.spec.ts:165 | getByTestId('ai-planning-record-mistakes-explanation') | failed |
| 2 | cloud-lesson-library.spec.ts:125 | — | flaky (passed on retry) |
Every one is the same error:
Error: strict mode violation: getByTestId('ai-planning-settings') resolved to 2 elements:
1) <div class="flex flex-col gap-5" data-testid="ai-planning-settings">…
aka locator('#main').getByTestId('ai-planning-settings')
2) <div class="flex flex-col gap-5" data-testid="ai-planning-settings">…
aka getByTestId('ai-planning-settings').nth(1)
The test id is rendered in exactly ONE place: app/(authed)/settings/project/ai-planning/_components/AiPlanningSettingsEditor.tsx:295. git grep 'ai-planning-settings' -- app components returns that one line. So there is no duplicated component and no second render path — the whole route SEGMENT is mounted twice, transiently, during the client-side navigation into it. Note the two locators: one resolves under #main, the other does not, which is what a second segment subtree outside the committed one looks like.
All four failures go through the same three lines, duplicated verbatim in two spec files (cloud-cadence.spec.ts:127, cloud-lesson-recording.spec.ts:112):
await page.goto('/settings/project');
await page.getByRole('link', { name: 'AI planning' }).click();
await page.waitForURL('**/settings/project/ai-planning');
await expect(page.getByTestId('ai-planning-settings')).toBeVisible(); // ← strict, no settling
waitForURL resolves on the URL, which the router writes before the old subtree unmounts, so the assertion can run while both are attached. toBeVisible() on a strict locator refuses rather than waiting for the count to settle — auto-retry cannot save it, because the expectation throws on the violation instead of retrying it. That is why the leg fails on attempt AND retry, and why a re-run only rotates which of the four specs loses the race.
This is the transient double-subtree class the flake log already tracks (board-scrum.spec.ts:114, project-square-flow.spec.ts:196, comments.spec.ts:261), at its predicted escalation: "if this recurs, the duplicate shape changed — the whole subtree is doubling, and the fix is a settling toHaveCount, not more scoping." It is NOT MOTIR-2506 / MOTIR-2600 / MOTIR-2621, which are /planning first-paint TIMEOUTS on a different route with a different signature; all three are done.
Scoping to #main is not enough on its own — the duplicate can land inside it — so settle the count before asserting, in ONE shared helper rather than in each spec:
const panel = page.getByTestId('ai-planning-settings');
await expect(panel).toHaveCount(1); // waits for the old subtree to unmount
await expect(panel).toBeVisible();
toHaveCount retries on the COUNT and never trips strict mode, which is exactly the property missing here. Then de-duplicate openAiPlanningSettings — it exists twice, byte-for-byte, and cloud-lesson-library.spec.ts has a third variant (railEntry) — so the fix lands once for all four specs.
Do NOT re-run past this. It has now failed on two consecutive runs of the same commit; the second re-run is what produced the four-spec picture above.
openAiPlanningSettings lives in ONE place, is used by cloud-cadence.spec.ts, cloud-lesson-recording.spec.ts and cloud-lesson-library.spec.ts, and settles the panel's count before any strict assertion.ai-planning-record-mistakes-explanation at cloud-lesson-recording.spec.ts:165, which failed on the identical shape.toHaveCount(1) that passes on zero would be worse than the flake, so the negative direction is exercised and its output quoted in the pull-request body.Playwright E2E at-scale (billing-cloud) is green on this card's own pull request, run with the e2e-at-scale label so the leg actually executes (it is push-only otherwise), and the run id is quoted.CLOSE-OUT, not a criterion — the reason run.md forbids a criterion that reads on post-merge state, and the reason this card exists at all: after the merge, confirm fly releases -a motir-core shows a NEW ImageRef, not merely a new version number. A green check is what everybody already had; a deploy is the thing that was missing. Do not flip this card done on the check alone.
.github/workflows/ci.yml — the deploy job and the at-scale gate above ittests/e2e/cloud-cadence.spec.ts:127 · tests/e2e/cloud-lesson-recording.spec.ts:112 — the duplicated helperapp/(authed)/settings/project/ai-planning/_components/AiPlanningSettingsEditor.tsx:295 — the single render site, which is what rules out a component-level duplicate