# E2E Skipped-Tests Loop — Handoff for the next agent ## ✅ GREEN — 4th agent (2026-06-02 ~20:15 UTC) **Full "everything-on" chromium run against boiler (web image `944ef476`, freshly reseeded): `203 passed / 0 failed / 5 skipped` (9.3m).** The 5 skips are correctly operator-gated and justifiably impossible here: real-Stripe `subscription-success` ×3 (need an externally-acquired Stripe session), the registration email-verification inverse pair (env runs with `E2E_REGISTRATION_EXPECTS_EMAIL_VERIFICATION=0`), and the `E2E_REQUIRE_PLUGIN_HOOK_AUDIT` hook-audit lane (operator-gated). A second clean full run was used to confirm stability. **The 12 remaining failures from the previous handoff, all fixed this session:** 1. **plugin cluster (49/120/173/201)** — cross-spec pollution. `plugin-hook-lifecycle` now targets the dedicated, fully-provisioned `com.toolctrl.e2e-lifecycle` plugin instead of mutating the shared `e2e-readiness` plugin's audit-missing baseline. 2. **plugin-settings 244** — the grant/revoke + restore reloaded the page before the in-flight single-fetch POST landed (stale read). Now waits for RR7 revalidation. 3. **plugin-settings 587** — TWO bugs. (a) PRODUCT: the settings-iframe write posted to `fetch(".")` (resolves to the parent plugins-list route) and expected raw JSON; RR7 returns a turbo-stream document for non-`.data` fetches, so every write failed. Fixed to post the detail route's `.data` endpoint and gate on the HTTP status. (b) TEST: the post-reload iframe `get` is non-deterministic (the host frame quarantines its window + rotates the channel on a late `load`), so persistence is now verified by reading the settings loader's `.data` payload after reload. 4. **admin-email-sequences ×2** — logged in as the shared tenant user (not an admin). Seed now provisions a dedicated `e2e.admin@example.test` (AdminUser + global SuperAdmin, no TOTP) and emits `E2E_ADMIN_EMAIL/PASSWORD`; spec logs in as it. 5. **mobile-shell 570 (run/manual SSR 500)** — `MonacoEditor` mounted its browser-only `./MonacoEditor.client` during SSR, so the streaming renderer set HTTP 500 on EVERY editor page (run/manual, webhook-run, **api-docs too** — the prior handoff was wrong that api-docs was fine; only this test asserts `status < 500`). Fixed by gating the editor behind a client mount (server + first client render emit an identical placeholder → no hydration mismatch). 6. **portal-management** — operator chose to SHIP portals. Un-ignored the portal routes (`app.$tenant/__portals/**`, `admin/portals.tsx`), gated the global enable behind `PORTALS_ENABLED` (boilerplate default stays off; **boiler Coolify env set to `true`**), seeded a minimal published Portal fixture (subdomain `e2e-readiness`), and fixed 2 pre-existing type-drift errors the route-tree typecheck surfaced. 7. **subscription:394, workflow-builder-run 528/816** — were timing flakes; green on clean reseeded runs (CI `retries:1` absorbs them). **Commits (all on `main`, deployed):** `44e30fb3` (plugin fixture sep + races), `147d92e4` (plugin settings .data fix), `011c6958` (admin user), `13edf803`+`944ef476` (portals + type fixes), the Monaco fix, and `a9556041` (587 loader read-back, test-only). **Coolify (boiler web):** added `PORTALS_ENABLED=true` (single row — deduped). **Gotchas observed:** (a) a local-machine `net::ERR_INTERNET_DISCONNECTED` mid-run cascades ~70 false failures from one point onward — re-run, it's not real. (b) loader `.data` payloads embed the iframe HTML (~25KB) — search the FULL body, not a prefix. (c) the plugin settings iframe quarantines its window on late `load`s; drive it via the loader, not a post-reload `get`. --- **Written:** 2026-06-02 00:10 UTC · **Repo HEAD at handoff:** `494f52de` (branch `main`, also deployed to boiler staging) · **Companion docs:** `docs/testing/e2e-readiness-handoff.md` (full bug-fix history), agent memory `boiler-e2e-remaining-tail.md`, `boiler-deploy-seed.md`, `e2e-singlefetch-interceptor-contract.md`. --- ## 🚀 START HERE — NEXT AGENT (handoff from the 3rd agent, 2026-06-02 ~16:35 UTC) **Current state:** `main` HEAD `d8aaeacb`, deployed to boiler (web image `3cbb12d3`; the two commits after it are test-only/docs and don't change the runtime image). Full chromium run, freshly reseeded: **192 pass / 11 fail / 5 skip**. The original 26 failures are mostly fixed; the 11 are the hard tail below. The 5 skips are correctly-gated (real-Stripe success + email-verification inverse). **What's already done (don't redo):** subscription/api-docs/golden-path/registration/workflow-builder-actions/ mobile-shell-drawer race+selector fixes; seed prunes leaked accounts; seed fully-provisions the LIFECYCLE plugin; seed DELETES any operator-admin grant from the tenant user; host-aware admin path `/email-sequences`. **Coolify env on boiler (set this session, persist):** `PLUGIN_RUNTIME_PRODUCTION_ENABLED=true` (web + worker-general + worker-plugin) and `PLUGIN_HOOK_FANOUT_REQUIRED=true` (worker-general + worker-plugin). Coolify API: `https://saas-ctrl.com/api/v1`, token `~/.cli-tokens/coolify-token`, web uuid `mrf8lj70kmy9hqprx9dd6w7q` (see `boiler-deploy-seed.md`). **How to run (everything-on):** `bash /tmp/e2e-cr.sh ` — it sources `.env.e2e-readiness.boiler` and sets every opt-in + `REQUIRE_E2E_READINESS=1` (recreate it if /tmp was wiped; see §2/§4). **RESEED before every run** (after every deploy too — /tmp plugin bundles are ephemeral): `WEB=$(ssh boilerplate-staging "docker ps --format '{{.Names}}' | grep mrf8lj | head -1"); ssh boilerplate-staging "docker exec -w /tmp -e E2E_SEED_ALLOW_PRODUCTION=1 -e E2E_LOCAL_READINESS_SEED=1 -e PLUGIN_BINARY_BASE_PATH=/tmp/e2e-plugins $WEB node /myapp/packages/db/dist/e2e-readiness-seed.server.js"`. Seed source is `scripts/seed-e2e-readiness.ts`, built to that dist on deploy → **seed changes need a deploy** (push main → ~20min; watch the web image tag flip, NOT the commit-status API). DB inspection is now permitted (recipe in §4); you can also just psql the readiness plugin/grant state. **The 11 remaining, in priority order (full root-cause + fixes in the "third agent" ITERATION LOG below):** 1. **[BIGGEST WIN — ~6 tests] Plugin cluster needs FIXTURE SEPARATION.** plugin-settings is **14/0 in isolation** but fails ~5 in the serial run as CROSS-SPEC POLLUTION: `plugin-hook-lifecycle.*` sorts first, mutates the SHARED `com.toolctrl.e2e-readiness` plugin (grants audit / deactivates), and the "enabled + audit-missing" baseline is UI-unrestorable (revoke auto-disables; `plugins.tsx:415` hides the enable form for disabled+missing-cap). **Fix:** seed a SECOND, fully-provisioned readiness plugin WITH a settings UI (clone `buildE2eReadinessPluginManifest` → new id e.g. `com.toolctrl.e2e-hooklifecycle`, grant ALL caps incl `hook:workflow.executed`), and repoint `plugin-hook-lifecycle.mobile-a11y.spec.ts` (146/201) + the lifecycle/iframe tests at it; keep the audit-missing `com.toolctrl.e2e-readiness` ONLY for the review/grant/deny tests (49/120/173/244). Then nothing mutates the audit-missing plugin's audit state. Also fix hook-lifecycle's `isCapabilityGranted` — after grantAndEnable the grant lands server-side (DB-confirmed) but the revoke-form detection misses it (inspect the rendered DOM for the audit revoke control). 2. **[2 tests] admin-email-sequences** — seed a DEDICATED admin user (`e2e.admin@example.test`, `AdminUser` + global `SuperAdmin` role, reuse `E2E_READINESS_USER_PASSWORD`); emit `E2E_ADMIN_EMAIL`; make `admin-email-sequences.mobile-a11y.spec.ts` log in as THAT user (not the tenant user). DO NOT grant the shared tenant user operator-admin — it redirects to the admin host on login and breaks auth-flow/registration (the seed now actively deletes that grant). The host-aware `/email-sequences` path is already wired and the cookie reaches the admin host (verified). 3. **[1 test] run/manual SSR 500 (`mobile-authenticated-shell.spec.ts:570`)** — product bug: the page renders but `entry.server.tsx:121` sets HTTP 500 on a swallowed streaming error. Add temporary onError logging (even pre-shell), deploy, reproduce the direct GET of `wf_123/run/manual`, capture the error (suspect a deferred/Suspense boundary), fix. SPA nav works; only direct SSR 500s. 4. **[1 test] portal-management** — PRODUCT DECISION (ask the operator): `__portals` is build-disabled in `routes.ts` `defaultDisabledPluginRouteFilePatterns`. Either ship the portals module, or accept not-shipped (then stop emitting the portal env paths and make the test skip). 5. **[1 test, likely flake] workflow-builder-run:816** — webhook manual-run result doesn't render in 15s (worker-drain/SSE timing; target `https://postman-echo.com/post`). Bump the result wait or fix the streaming; CI `retries:1` may already absorb it. **Gotchas:** (a) the deploy `coolify-ensure-worker-groups.mjs` drift check FAILS "Stage or promote" if `PLUGIN_RUNTIME_PRODUCTION_ENABLED=true` on a worker without `PLUGIN_HOOK_FANOUT_REQUIRED=true` — both are already set, keep them paired. (b) Re-trigger a stalled/flaky deploy with an empty commit. (c) a11y `color-contrast` failures on toasts/loaders mid-fade are transient (CI retries absorb; `axe.ts` settles animations). (d) Don't weaken/skip to fake green (§3). Reserve the last ~20 min for the handoff + memory update. --- ## ⏱️ ITERATION LOG — 2026-06-02 (third agent — root-caused & mostly fixed) **Commits `894ba2a5` + `8bd11f9c` (empty re-trigger) DEPLOYED to boiler (web image `8bd11f9c`, reseeded). Plugin runtime env + companion set on Coolify. Outcome: 26 originally-failing tests → ~6 remaining, all genuine product/design issues (root-caused below).** **The previous "account-leakage" leading hypothesis was mostly WRONG** (verified by reading all 26 traces, per §3). The 26 split into distinct, mostly-independent causes. Status per cluster: CONFIRMED GREEN post-deploy (full a11y-strict env, chromium): plugin-settings full suite (14/0, incl. the 244/344/380/473/587 cluster), plugin hook-audit (worker fanout env), lifecycle-plugin enable/disable (431), registration family (35/1-skip, leak pruned), subscription ×3+nav, workflow-builder-actions (incl. delete-block), api-docs, golden-path, mobile-shell drawer. **ADMIN GRANT REVERTED (commit `5ed35fb5`):** granting the shared e2e-readiness tenant user `AdminUser` + global SuperAdmin DID make admin-email-sequences ×2 pass, but it is an OPERATOR admin → on login the app redirects to the **admin host** (`admin.boiler…/dashboard`), which broke `auth-flow` + `registration` login-redirect assertions AND added a "Switch to admin" affordance + teal `#00bba7` 2.42:1 text to the roles page (tenant-roles-permissions a11y ×3). Net regression, so reverted. The host-aware `/email-sequences` PATH fix is kept (correct). **admin-email-sequences now needs a DEDICATED admin user** (seed a separate `e2e.admin@…` AdminUser+SuperAdmin and point the spec at it via new creds) — the shared tenant user cannot be both. - **GREEN (validated individually on boiler, chromium):** - workflow-builder ×2 (+3 siblings) — one-shot `count()` raced the React Flow node mount. - subscription ×3 (`:80/:144/:711`) — SPA-nav race + the `:144` error test used a raw-JSON interceptor that breaks RR7 single-fetch; switched to `singleFetchActionError`. - readiness-golden-path (1) — mobile settings/nav links live in the "more" popover + a section `