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 server was updated. Reloading the page to pick up the latest version.