From e32e3fa35fe9213c039cafc190be67ca7826c4b1 Mon Sep 17 00:00:00 2001 From: AlexZ005 Date: Sun, 20 Sep 2026 11:05:42 +0300 Subject: [PATCH] [fix] open-core-m1: the drawer-mount check was asserting the pre-tab drawer The one red every lane in roadmap 29 carried as "pre-existing" is a TEST fault, not a product one. No core code changes here - `git diff -- src/` is empty. - WHEN IT STARTED: the check was written at 4799994 (roadmap 14 PM, 2026-07-24), when ConnectInfoDrawer rendered `{#if $drawerSlot}` inline in its single body, so opening the drawer with the chevron showed the plugin's section straight away. The NEXT DAY 4b7b8cf ("one tabbed drawer (Info/Rooms/Toasts)") moved the cloud mount behind the Rooms tab, and nobody updated the suite. It has been red for the whole life of the tabbed drawer - about two months. - MEASURED with a throwaway probe rather than inferred: plugin loaded, chevron clicked -> {drawerOpen:true, drawerTab:"info", tabs:["Info","Rooms","Toasts"], roomsBtn:true, section:false}; the Rooms shortcut or the Rooms tab clicked -> section:true, and `.cloud-slot` count 1 -> 2. Inert build: no tabs, no Rooms button, no cloud slot. - THE PRODUCT IS RIGHT and stays untouched. The chevron opening on Info is deliberate (toggleInfo keeps the last tab; the drawer's own job is connection and server info), and the plugin's content has a first-class way in: Connect grows `#connect-rooms-button` exactly when `$drawerSlot` is set, and `openRooms` opens the drawer on that tab. - THE CHECK now drives that shortcut - the way the app offers the mount - and asserts the section renders on the Rooms tab. - NEW CHECK `M1d: no Rooms affordance in the inert build`: the Rooms shortcut exists only because a plugin mounted drawer content, which is what stops the corrected check passing vacuously. 17 checks -> 18. Nothing was deleted. - The e2e skill drops open-core-m1 from both dirty-baseline lists, says where the drawer mount now lives, and records the lesson: identical-on-base only rules out YOUR diff, so when a red is pre-existing, spend two minutes on when it started and what changed then. Counterfactuals (product broken, then restored byte-identically): - removed `` from the Rooms tab -> `PM: drawer section renders on the drawer Rooms tab` FAILS. - dropped `$drawerSlot &&` from Connect's Rooms-button gate -> `M1d: no Rooms affordance in the inert build` FAILS. - both breaks together: exactly 2 FAILURES, no others; restored -> 18/18. Gates: open-core-m1 16/1 -> 18/18; battery under the lock ALL PASS (ai-presets, approval-timeout, connect-states, dial-metadata, join-result, net-handshake, open-core-m1 - 7 suites, 203 checks, 399s); svelte-check 336/47 unchanged (identical by construction - no src change); vitest 178; build green server-down. Co-Authored-By: Claude Opus 5 (1M context) --- .claude/skills/e2e-verify/SKILL.md | 21 +++++++++++++++++---- tests/e2e/open-core-m1.test.cjs | 14 +++++++++++--- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/.claude/skills/e2e-verify/SKILL.md b/.claude/skills/e2e-verify/SKILL.md index ba77100a..e148ae03 100644 --- a/.claude/skills/e2e-verify/SKILL.md +++ b/.claude/skills/e2e-verify/SKILL.md @@ -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 — @@ -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, @@ -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 diff --git a/tests/e2e/open-core-m1.test.cjs b/tests/e2e/open-core-m1.test.cjs index 50c86801..524fbcb0 100644 --- a/tests/e2e/open-core-m1.test.cjs +++ b/tests/e2e/open-core-m1.test.cjs @@ -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')); @@ -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);