Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions .claude/skills/e2e-verify/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -969,7 +969,10 @@ drops the P2P session.
+ `freshReload`, then asserts the seams (`window.__stores.cloudHooks.canApply`,
`profileSlot`/`drawerSlot` are functions, mounted DOM). The flowbite avatar Dropdown
is flaky to open headlessly — assert profile mounts at the STORE level, not by
clicking `#avatar-menu`. A `transition:slide` element stays in the DOM through the
clicking `#avatar-menu`. The DRAWER mount renders on the connect drawer's ROOMS tab,
not under the chevron (which opens on Info and keeps the last tab): reach it with
`#connect-rooms-button`, the Connect-pill shortcut that exists only while `$drawerSlot`
is set — which is also what keeps that check from passing vacuously. A `transition:slide` element stays in the DOM through the
~200ms out-transition — poll with `eventually`, don't assert `count===0` immediately.
- **HMR churn makes runs LIE** (cost ~4 cycles in #16-Q5): a page that loads while
vite is still re-transforming just-edited modules gets a half-mounted app —
Expand Down Expand Up @@ -1109,7 +1112,7 @@ drops the P2P session.
- KNOWN failing suites in the localhost env (2026-07-28, proven identical across a
full old-deps/new-deps baseline comparison — treat as the dirty baseline, not
regressions): the drag-drop-SIMULATION cluster (explorer-drop, explorer,
packs-drop) + user-modules (setup crash), open-core-m1 (1 drawer check),
packs-drop) + user-modules (setup crash),
dock-sidebar-inset, layout, panels, script-nodes, and a few
two-peer timing suites (module-sdk, scene-music, physics-kinematic,
physics-discoverability, roadmap-13-notifications-notes, scene-assets,
Expand All @@ -1129,10 +1132,20 @@ drops the P2P session.
suite in a PRISTINE sibling worktree on its OWN freshly started server and diffing the
PASS/FAIL lines — the only A/B that means anything (see the day-lived-server trap):
`flow-customnode-io` (1 check — "a stale snapshot cannot resurrect the pruned edge"),
`flow-object-embed` (`locator.dblclick` timeout). `open-core-m1`'s single drawer check
was re-confirmed on that same pair: 18 identical PASS/FAIL lines both sides. Two
`flow-object-embed` (`locator.dblclick` timeout). Two
worktrees is what makes this cheap — you never touch the tree under test, so there is
no stash to pop and no chance of the "restart fixed it" confound.
- **"PRE-EXISTING" IS A DIAGNOSIS ABOUT THE ENVIRONMENT, NOT A VERDICT ON THE CHECK —
AND `open-core-m1` SPENT TWO MONTHS ON THIS LIST BECAUSE OF THE DIFFERENCE.** Its one
red was A/B'd honestly every time (identical on pristine 1.14.0, 18 identical PASS/FAIL
lines across two worktrees) and every lane correctly moved on — but identical-on-base
only rules out YOUR diff. Here the check had simply been asserting a superseded
contract since the day after it was written: it opened the connect drawer with the
chevron and demanded the plugin's section, and the very next commit (4b7b8cf) made the
drawer TABBED and moved that mount behind the Rooms tab. `git log --follow` on the
suite against `git log` on the component answered it in one look. So when a red is
pre-existing, spend the two minutes asking WHEN it started and WHAT changed then; a
red nobody reads is a suite nobody reads.
- Long full-suite runs: the Bash tool caps at 10 min — launch the runner DETACHED
(PowerShell `Start-Process node -ArgumentList 'tests\e2e\run.cjs ...'` with
output redirects) and poll/Monitor the log. A dev server started via the Bash
Expand Down
14 changes: 11 additions & 3 deletions tests/e2e/open-core-m1.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ h.run(async () => {
h.check(def.hasProvider === false, 'M1a: no capability provider installed by default');
h.check(def.auth === null, 'M1b: no auth provider installed by default');
h.check((await A.page.locator('.cloud-slot').count()) === 0, 'M1d: no cloud UI mounted by default');
// the Rooms shortcut on the Connect pill exists ONLY because a plugin mounted
// drawer content — which is what makes the Rooms-tab check further down non-vacuous.
h.check((await A.page.locator('#connect-rooms-button').count()) === 0, 'M1d: no Rooms affordance in the inert build');

// --- load the example plugin -------------------------------------------
await A.page.evaluate(() => localStorage.setItem('cloudPluginUrl', '/cloud-plugin-example.js'));
Expand Down Expand Up @@ -62,10 +65,15 @@ h.run(async () => {
});
h.check(v2.profile, 'PM: plugin installs a profile mount (mountProfile / profileSlot)');
h.check(v2.drawer, 'PM: plugin installs a Connect-drawer mount (mountConnectDrawer / drawerSlot)');
// drawer mount renders in the DOM when the (i) drawer opens
await A.page.locator('[data-testid="connect-info-button"]').click();
// The drawer mount renders on the drawer's ROOMS tab: batch CN (4b7b8cf) turned the
// info drawer into Info/Rooms/Toasts and moved the plugin's content behind the Rooms
// tab, which exists only when a plugin mounted some. The chevron deliberately opens
// on Info, so reach the mount the way the app offers it — the Rooms shortcut the
// Connect pill grows for exactly this (the inert check above pins that it is the
// plugin putting it there).
await A.page.locator('#connect-rooms-button').click();
await A.page.waitForTimeout(350);
h.check(await A.page.locator('#cloud-drawer-section').first().isVisible(), 'PM: drawer section renders in the open info drawer');
h.check(await A.page.locator('#cloud-drawer-section').first().isVisible(), 'PM: drawer section renders on the drawer Rooms tab');
await A.page.mouse.click(10, 500); // close the drawer
await A.page.waitForTimeout(350);

Expand Down
Loading