Disposable-mesh e2e testing

Every MeshWeaver e2e suite follows one shape:

mesh/up.sh            # 1. BOOT a disposable instance from the platform image
npx playwright test   # 2. dev-login → install plugins + content via the Plugin Catalog →
                      #    drive the product end to end
mesh/down.sh          # 3. throw the mesh away (volumes included)

The mesh under test is never a shared instance — suites carry a CAN_MUTATE guard that refuses mutations against *.meshweaver.cloud / *.systemorph.com; only read-only smokes may point at production. The reference implementation is the education repo's course suite (Systemorph/MeshWeaver.Education → e2e/): it installs the Edu plugin and the AgenticEngineering course through the real catalog, installs the exercises as a user and executes all 27 workbenches plus all 27 solutions.

1. The harness: compose from the platform image

A four-service docker-compose.yml — postgres (pgvector) + memex-migration + memex-portal-ai + a registry stub — with throwaway volumes (down -v resets the mesh). Images come from ACR (meshweaver.azurecr.io/memex-portal-ai:main, az acr login -n meshweaver) or the GitHub registry (ghcr.io/systemorph/…). The portal env is the standard Filesystem+PostgreSql pair the Helm chart uses, plus two e2e-specific settings:

Setting Why
Authentication__EnableDevLogin=true the seedable test identity (§2)
PluginCatalog__RegistryUrl=http://registry-stub:4873 the consumer installs from the stub (§3)

2. Identity: DevLogin self-provisioning

With Authentication:EnableDevLogin on, POST /dev/signin {personId} self-provisions any user through the same UserOnboardingService dual-write the Entra flow runs — and the first user of a fresh mesh receives the platform-Admin grant (Admin/_Access). That is the entire auth story of an e2e run: the suite's global setup posts the form, captures the cookie as Playwright storage state, and the user is a global admin who can open Settings ▸ Administration ▸ Plugin Catalog. No OAuth app, no secrets, no hand-carried storage-state files. DevLogin is forced off in production builds — this only ever works on a throwaway mesh.

Two operational notes:

3. Content: install through the product, fed by a stub registry

Plugins and content enter the mesh through the real consumer path — the Plugin Catalog settings tab, PackageInstaller and the live NodeType compile — never via out-of-band SQL or bespoke import scripts. Credential-free and offline: the harness runs a ~150-line stub registry speaking the registry wire format over the local checkouts:

Endpoint Serves
GET /api/plugins one PackageManifest (kind NodeRepo) per top-level folder whose index.json is a nodeType: "Space" root — NodeRepoPackageSource's own listing rule
POST /api/plugins/files {id} every text file under that folder (binaries — videos — skipped)

The portal's PluginCatalog__RegistryUrl points at the stub, so the admin tab lists and installs the checkouts exactly like a production instance installs from memex.meshweaver.cloud.

Fixed in core — the Plugins records partition no longer needs a bootstrap package. PackageInstaller used to write its install record to Plugins/{id} without provisioning that partition, so on a fresh mesh the content imported fine but the record write died with 42P01: relation "plugins.mesh_nodes" does not exist and the card never flipped to "✓ Installed". PackageInstaller.EnsurePartitionsProvisioned now calls IPartitionStorageProvider.EnsurePartitionProvisioned for the install-record partition before writing (idempotent, promise-cached). A harness that still ships a synthetic Plugins (bootstrap) package is carrying a workaround it no longer needs.

Bootstrap installs are verified by outcome — the imported pages render — never by the catalog card's "✓ Installed" flip, whose read-back can lag on a fresh mesh.

4. What to assert

5. Timing rules (Blazor + Orleans + Roslyn + kernel)

6. CI gating

The workflow mirrors the local flow and skips cleanly where credentials are absent so forks stay green:

Job Gate Runs
static always tsc --noEmit + playwright test --list (the repo-enumeration gate)
smoke vars.E2E_SMOKE_BASE_URL the read-only smoke against a live instance
mesh vars.MW_E2E_ENABLED + registry secrets (+ a token for sibling private checkouts) registry login → mesh/up.sh → the full suite

See the education repo's .github/workflows/ci.yml for the worked example, and its .claude/skills/course-e2e/SKILL.md for the step-by-step authoring skill (mirrored on the mesh as Skill/course-e2e). Both paths are in Systemorph/MeshWeaver.Education, not in this repo — MeshWeaver's own workflows are dotnet-test.yml (CI) and main-cd.yml (CD); there is no ci.yml here.

Reconnecting…
The connection to the server was interrupted. Trying to restore it…
Trying again…
The connection could not be restored. Reloading the page…
The server was updated. Reloading the page to pick up the latest version.