WP Manifestindependent plugin directory
manifest / ai / core-ai-wcus

Core AI Living Block Map

An interactive map of the building blocks connecting WordPress and AI.

by The WordPress Contributors · github.com/henryperkins/core-ai-wcus

0stars
0forks

Install

No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:

wp plugin install https://github.com/henryperkins/core-ai-wcus/archive/refs/heads/main.zip

Version and design: 3.2.5. Core AI Living Block Map is one dynamic, server-rendered core-ai/core-ai-map block for explaining how WordPress and AI building blocks fit together on a kiosk. Its server markup is enhanced by the WordPress Interactivity API. The repository also contains a reproducible, self-hosted WordPress Playground artifact for static-hosted demonstrations.

Local audit remediation

The inactivity warning allows 20 seconds to extend the session, including at the 30-second minimum timeout. Navigation, reset, About, and WP-Bench announcements use the translated PHP context. Inspector continuation cues measure remaining content when scroll-state queries are unavailable; the recovery link becomes focusable only when it appears. Forced-colors states and meaningful sidecar boundaries preserve the participation cues.

Page zoom changes after loading retain the stage's apparent enlargement and allow panning. The fitter distinguishes page zoom from display-density changes by comparing viewport and browser-window dimensions, preserving the current zoom when moving between displays. Saved initial browser zoom remains the document's baseline; it cannot be inferred from pixel density alone. Phone inspection permits pinch zoom, and keyboard focus reveals controls outside the visible part of a pannable stage. The inspector's continuation cue moves with the stage during panning and stays fixed while its text scrolls. The browser acceptance contract includes tests/browser/inspection-geometry.js for these geometry checks at desktop and phone inspection sizes.

The transparent illustration is now a 392px RGBA PNG (70,431 bytes), and the two variable fonts receive kiosk-only preload hints. Repository lint commands check authored files. These local changes require a rebuilt, verified release artifact and the browser/device gates below before deployment.

3.2.5 release notes

This release reframes welcome around what WordPress Core AI is and teaches the Choose → Follow → Open interaction. Compare components begins at a visibly marked AI Client, and every card and inspector uses the same type-and-maturity status taxonomy — owned by the plug-in rather than authored per card, so the per-card badge fields it replaces are retired. Feedback now lives in About beside a visible, saveable destination. About also explains the exhibit architecture and keeps offline and screen-awake status behind a secondary Kiosk status disclosure.

WP-Bench begins at stage 01 with Previous/Next navigation, and hydration gates initial actions. The reduced-motion preview remains still while a compact list keeps all four flows discoverable. Inactivity presents a twenty-second extension warning before returning to welcome, including while About is open. About traps focus, isolates the map behind its modal semantics, and restores the visitor's place when closed.

Hydrated Apply, progress, offline-cache, and wake-lock labels now retain their translations. The release also narrows walkthrough focus to real card controls, removes obsolete WP-Bench stage metadata, preserves the disabled cache state after cleanup failures, strengthens the reset-extension button border, advances the worker cache namespace, and exposes a visitor release marker derived from the plug-in version constant.

3.2.4 release notes

This release makes the map read as the boundary diagram it always was. The shelf of components a flow is not using moved under the WordPress band, the MCP Adapter now straddles the boundary rule it translates across, and the canvas names its own convention with a "Boundary view" badge and a "Reading the diagram" key.

"An agent learns WordPress" was rebuilt around what its guidance is actually about. The Abilities API, AI Client, and MCP Adapter stay on the canvas as the subject of the skills rather than being parked, the flow ends against a boundary stop rather than an arrow, and it offers the flow it leads into once it has settled. The third actor is now "Code for this site" and carries the panel explaining why an install, not the agent, is what reaches WordPress.

"WordPress uses AI" numbers the external AI service as its fourth step, moves Connectors below the request path as a full card joined to it by a dashed support line, and gives the provider plugin the same height as the two Core cards it sits between. Off-flow actors now leave the canvas instead of being parked at its edges. The welcome screen lists the four flows in place of the three gestures that reach them, and carries one take-away QR code for questions the booth cannot answer in person.

3.2.3 release notes

This release delivers the accessibility remediation. Server-rendered markup now exposes exactly one named level-one heading before Interactivity API hydration, and a cleared editable title falls back to the block's schema default. The About footnote clears the 24px rendered target floor at the compatibility scale, the welcome card avoids a continuously recomposited backdrop blur, and the contrast tokens now distinguish meaning-carrying boundaries, configuration strokes, dormant edges, and actor ghosts.

The 1024 x 768 compatibility gate now follows the real UI through the 68px rail, the Tests story and WP-Bench, 60px panel, tab and dialog controls, the 44px workbench Apply control, and the 34px About footnote. It requires each declared surface's exact control count, checks both axes against the 24px and 44px floors, and preserves Apply and the footnote as the only documented enhanced-target exceptions at that scale.

3.2.2 release notes

This release moves the Playground kiosk from WordPress 7.0 to the beta channel, so the exhibit runs a WordPress 7.1 release candidate rather than describing 7.1 as unshipped. AI Client and Connectors copy was corrected against a real admin screen: the JavaScript prompt API is administrator-gated rather than absent, requests carry text, image, speech, or video, and Connectors resolves keys from an environment variable, then a wp-config constant, then the database. The ability-calling edge joining the two halves of the map was added, and Connectors is framed as general connection infrastructure whose first users are AI providers rather than its only intended ones.

3.2.1 release notes

This release turns the guided map into a self-guided teaching experience. The welcome screen now defines Core AI as open, provider-neutral building blocks, teaches the choose-follow-tap interaction, and explains the diagram key. Every flow states its situation before movement, reveals its conclusion after the path settles, and predicts its outcome in the flow navigation. Component panels lead with their role and importance in the selected flow, while reduced motion, replay, flow switching, and focus restoration preserve the same lesson. The factual review also removes a volatile WP-Bench test count and aligns MCP Adapter protocol wording with the current project documentation.

3.2.0 release notes

This release is an interaction-comprehension pass. The exhibit is flow-first: the welcome control opens WordPress uses AI directly rather than a neutral canvas of unexplained parts, and that canvas moved behind a secondary Browse all components control. Every card taking part in a flow is highlighted, tappable, and cued — including the five outside actors and the transient provider-plugin layer, which previously looked like the cards beside them but opened nothing. Each interface state carries one instruction, each flow states its takeaway in words beside the diagram, and each component panel opens with the flow it was reached from, then restores that flow and the opening card's focus when it closes.

3.1.3 release notes

This release clears the neutral-map card collision and provider-copy overflow, uses an honest cold-start loader message, and removes SVG Interactivity directives that could emit renderer notices. Playground artifacts now carry source and ZIP provenance for verification-only CI, and the static shell has exhibit-owned social metadata.

Install and create the kiosk page

Build an uploadable ZIP, then upload it from Plugins > Add New > Upload Plugin and activate it:

npm ci
npm run build
npm run plugin-zip

On a fresh page, select the theme's blank or full-width template, insert the single Core AI Living Block Map block, set it to full width, and publish a non-home/root permalink. The authored target is a 1366 x 1024 landscape kiosk stage.

The stage is one fixed composition scaled to fit the viewport, so every authored size shrinks by the same factor. At the 1024 x 768 compatibility size that factor is 0.7496, and the rendered figures are what a finger actually meets:

Phone-sized viewports stop at that compatibility scale and expose the unchanged 1024 x 768 canvas through two-axis touch scrolling. This is a map-verification view, not a mobile reflow; the iPad kiosk fitting behavior is unchanged.

Control Authored Rendered at 1024 x 768
Story rail buttons 68 px 51 px
Welcome primary action 64 px 48 px
Header, story, panel, tab and dialog controls 60 px 45 px
Nested controls such as Apply 44 px 33 px
About footnote 34 px 25 px

Every control meets WCAG 2.2 SC 2.5.8 (24 x 24) by size at both supported sizes. At 1024 x 768 only two fall below SC 2.5.5 (44 x 44): the workbench Apply control at 33 px and the About footnote at 25 px. Everything else clears it, though the 60 px class clears it by less than a pixel. That is why 1024 x 768 is a compatibility view rather than a fully supported touch layout. Physical iPad touch acceptance is still a separate required sign-off.

The reviewed date carried by this release is Reviewed 14 Aug 2026. It is shown inside the About this exhibit panel, reached from the footnote under the story rail, rather than in the kiosk header.

AI assistance and accountability

  • AI assistance: Yes
  • Tool: OpenAI Codex
  • Used for: implementation, tests, and deployment preparation.

Final work was human-reviewed and tested; the human contributor remains responsible for it. Use the same disclosure in the description of any future pull request for this work.

Cloudflare Pages Playground exhibit

playground/blueprint.json packages this same plugin into a browser-executed WordPress Playground kiosk. It pins WordPress to the stable 7.1 build and PHP to 8.3, creates the /living-block-map/ page, disables Playground network access, and keeps each visitor's WordPress state in that visitor's browser. The page disables this plugin's own offline worker because Playground already owns the virtual site's service worker and browser-local persistence.

WordPress 7.1 was released on 19 August 2026 and the official Playground runtime now provides its stable build. The runtime tree is copied out of PLAYGROUND_SOURCE_DIR at build time: changing the Blueprint alone does not refresh an older runtime or a deployed booth. Use an official static release containing wp-7.1 and verify the generated artifact before publishing.

Build the static Pages artifact from an official WordPress Playground static release directory:

git fetch origin
git switch main
git pull --ff-only
if (git status --porcelain) { throw 'Production checkout is not clean.' }
npm ci
npm run build
git diff --exit-code -- build
npm run plugin-zip
$env:PLAYGROUND_SOURCE_DIR = 'C:\path\to\wasm-wordpress-net'
npm run build:playground
$commit = git rev-parse HEAD
npx wrangler pages deploy dist-playground `
    --project-name=core-ai-living-block-map `
    --branch=main `
    --commit-hash=$commit `
    --commit-dirty=false

The build copies only the assets needed by the pinned runtime plus the 7.1 channel's static fallback tree. It validates Cloudflare Pages Free's 20,000-file and 25 MiB-per-asset limits, removes upstream Google Fonts and analytics, and uses a local Blueprint and plugin ZIP. npm run plugin-zip creates the local core-ai-map.zip; the Pages build copies those exact bytes to the release URL /kiosk-blueprint/core-ai-map-3.2.5.zip. It also emits a deployment manifest with that path, its byte count, and its SHA-256, plus a Pages rewrite that keeps the Playground runtime's literal /remote.html endpoint from being redirected to Cloudflare's extensionless route. The build owns the accessible outer loading screen, keeps Playground's React root inert until the exhibit is ready, and changes the matching caption inside the runtime module loaded by remote.html. That modified runtime module receives a content-derived filename and replaces the upstream entry in the offline asset manifest. dist-playground/ is generated and not committed. Cloudflare Pages provides the required HTTPS origin; a normal HTTP origin cannot run Playground's service worker.

Production is deployed manually from dist-playground/. Automatic Git production and preview deployments are intentionally disabled for the Pages project because the repository root is not a deployable Playground artifact. The wcus.hperkins.com CNAME points to the Pages hostname in DNS-only mode; enabling the orange-cloud proxy prevents Pages from routing this hostname.

GitHub Actions verifies the release build on pull requests, main, and manual dispatch using a deterministic local Playground source fixture. It does not publish an artifact or deploy Pages; manual publication from a verified dist-playground/ remains the production boundary.

Agent-run browser policy

Any agent step that requires a real browser must use the OAuth-gated browser_run MCP service at https://browser-run-mcp.lfd.workers.dev/mcp. Its source and deployment notes live in browser-run-mcp. Configure and authenticate it once with:

curl --fail --silent --show-error https://browser-run-mcp.lfd.workers.dev/health
codex mcp add browser_run --url https://browser-run-mcp.lfd.workers.dev/mcp
codex mcp list
codex mcp get browser_run

The add command opens the OAuth page; enter the Browser Run passphrase and approve it. If the endpoint is already registered but codex mcp list reports Not logged in, run codex mcp login browser_run. Start a new Codex session after login because an existing session does not dynamically reload its MCP tool catalog. If the service is unavailable, check the health endpoint, codex mcp list, the configured endpoint, and OAuth before starting another new session. If it remains unavailable, record that blocker and stop the browser portion. Never install or launch Playwright, Puppeteer, Selenium, Cypress, Chrome, Chromium, Edge, Firefox, WebKit, or headless_shell locally for an agent-run browser task.

Use qa_markdown, qa_accessibility_tree, qa_screenshot, qa_pdf, or the other qa_* Quick Actions for one-page work; browser_* for navigation and multi-step interaction; and qa_crawl_start, qa_crawl_results, and qa_crawl_cancel for multi-page crawling. In stateful workflows, use browser_snapshot or other accessibility output to locate and verify controls, take screenshots only for claims that need visual evidence, and call browser_close in a final cleanup step whether the workflow passes or fails.

Browser Run cannot reach localhost or private-network URLs. An agent browser gate therefore requires a publicly reachable HTTPS preview of the exact artifact under test. If no preview URL exists, record that single blocker and stop the browser portion; do not fall back to a locally served artifact or a local browser. Quick Action screenshots and PDFs may be returned inline or as unguessable /files/ URLs from the configured R2 store; those URLs expire after seven days. Record relevant observations and returned evidence URLs in the release evidence, but do not commit screenshot/PDF bodies or credentials.

Verify the accessible loader with Browser Run

npm run test:playground proves the generated outer loader, inert/root handoff, runtime module fingerprint, remote.html, and offline manifest agree. It does not render the nested Playground frame. After building dist-playground/, make those exact bytes available at a publicly reachable HTTPS preview URL. Preview deployments are not provisioned by this repository, so the release operator must supply that URL before this browser gate can run.

Use one stateful Browser Run workflow against a cache-busted preview URL:

  1. Use browser_navigate, then browser_snapshot, to require the kiosk-owned heading and status while the upstream root is absent from the accessibility tree. Prefer this accessibility evidence to a screenshot.
  2. Wait for the nested /remote.html runtime and use browser_snapshot to require the same approved heading and status with no exposed Preparing WordPress fallback.
  3. Wait for the exhibit's attract prompt, snapshot again, and require the kiosk loader to be absent and the Playground root to be accessible.
  4. Collect browser_console_messages and browser_network_requests; reject console or page errors, failed requests, and HTTP errors.
  5. Call browser_close in the workflow's final cleanup step. Record the cache-busted preview URL, timestamp, relevant observations, and any evidence URLs returned directly by screenshot or PDF tools.

Treat the result as ok: true only when it satisfies the same approved-copy, loader-handoff, and error assertions retained in tests/browser/playground-loader.js. Matching text in generated files alone is not sufficient.

Verify every deployment by artifact identity

Do not infer the deployed build from what appears on screen. After every deployment, run the following from the same clean checkout and dist-playground/ directory used to deploy. It checks /remote.html, the required shell files, the Blueprint's fingerprinted source, both remote deployment manifests, and the byte count and SHA-256 of the ZIP downloaded from each public hostname. Every temporary download has one resolved path and is removed in finally.

$dist = (Resolve-Path -LiteralPath 'dist-playground').Path
$manifestPath = Join-Path $dist 'deployment-manifest.json'
$manifest = Get-Content -LiteralPath $manifestPath -Raw | ConvertFrom-Json
$artifactRelative = [string] $manifest.pluginArtifact.path
$expectedArtifact = 'kiosk-blueprint/core-ai-map-3.2.5.zip'

if ($artifactRelative -ne $expectedArtifact) {
    throw "Expected $expectedArtifact; manifest has $artifactRelative."
}

$localArtifact = Join-Path `
    $dist `
    $artifactRelative.Replace('/', [IO.Path]::DirectorySeparatorChar)
if (-not (Test-Path -LiteralPath $localArtifact -PathType Leaf)) {
    throw "Missing local deployment artifact: $localArtifact"
}

$expectedBytes = [int64] $manifest.pluginArtifact.bytes
$expectedSha256 = ([string] $manifest.pluginArtifact.sha256).ToLowerInvariant()
$localBytes = (Get-Item -LiteralPath $localArtifact).Length
$localSha256 = (Get-FileHash -LiteralPath $localArtifact -Algorithm SHA256).Hash.ToLowerInvariant()

if ($localBytes -ne $expectedBytes -or $localSha256 -ne $expectedSha256) {
    throw 'Local plugin ZIP does not match deployment-manifest.json.'
}

$hosts = @(
    'https://core-ai-living-block-map.pages.dev',
    'https://wcus.hperkins.com'
)
$paths = @(
    '/',
    '/sw.js',
    '/kiosk-blueprint/blueprint.json',
    '/deployment-manifest.json'
)
foreach ($hostName in $hosts) {
    $probe = [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds()
    $remote = Invoke-WebRequest `
        -Uri "$hostName/remote.html?probe=$probe" `
        -UseBasicParsing `
        -MaximumRedirection 0
    if ($remote.StatusCode -ne 200) {
        throw "Expected 200 for $hostName/remote.html; got $($remote.StatusCode)."
    }
    if ($null -ne $remote.Headers['Location']) {
        throw "Expected no redirect for $hostName/remote.html; got $($remote.Headers['Location'])."
    }
    if (-not $remote.Headers['Content-Type'].StartsWith('text/html')) {
        throw "Expected text/html for $hostName/remote.html; got $($remote.Headers['Content-Type'])."
    }
    "{0} {1} {2}" -f $remote.StatusCode, $remote.Headers['Content-Type'], "$hostName/remote.html"

    foreach ($path in $paths) {
        $response = Invoke-WebRequest `
            -Uri "${hostName}${path}?probe=$probe" `
            -UseBasicParsing
        $contentType = $response.Headers['Content-Type']
        "{0} {1} {2}" -f $response.StatusCode, $contentType, "$hostName$path"
    }

    $remoteManifest = Invoke-RestMethod `
        -Uri "$hostName/deployment-manifest.json?probe=$probe"
    if (
        $remoteManifest.pluginArtifact.path -ne $artifactRelative -or
        [int64] $remoteManifest.pluginArtifact.bytes -ne $expectedBytes -or
        ([string] $remoteManifest.pluginArtifact.sha256).ToLowerInvariant() -ne $expectedSha256
    ) {
        throw "Deployment manifest mismatch on $hostName."
    }

    $remoteBlueprint = Invoke-RestMethod `
        -Uri "$hostName/kiosk-blueprint/blueprint.json?probe=$probe"
    if ($remoteBlueprint.plugins.Count -ne 1 -or $remoteBlueprint.plugins[0].source -ne './core-ai-map-3.2.5.zip') {
        throw "Blueprint plugin source mismatch on $hostName."
    }

    $originName = ([Uri] $hostName).Host
    $downloadPath = [IO.Path]::GetFullPath(
        (Join-Path ([IO.Path]::GetTempPath()) "core-ai-map-${originName}-3.2.5-${PID}.zip")
    )
    if (Test-Path -LiteralPath $downloadPath) {
        throw "Refusing to overwrite existing verification file: $downloadPath"
    }

    try {
        Invoke-WebRequest `
            -Uri "${hostName}/${artifactRelative}?probe=$probe" `
            -UseBasicParsing `
            -OutFile $downloadPath
        $downloadBytes = (Get-Item -LiteralPath $downloadPath).Length
        $downloadSha256 = (Get-FileHash -LiteralPath $downloadPath -Algorithm SHA256).Hash.ToLowerInvariant()
        if (
            $downloadBytes -ne $expectedBytes -or
            $downloadSha256 -ne $expectedSha256
        ) {
            throw "Plugin ZIP identity mismatch on $hostName."
        }
        "VERIFIED {0} bytes sha256:{1} {2}/{3}" -f $downloadBytes, $downloadSha256, $hostName, $artifactRelative
    } finally {
        if (Test-Path -LiteralPath $downloadPath -PathType Leaf) {
            Remove-Item -LiteralPath $downloadPath -Force
        }
    }
}

Booth gate after every deployment

Run this checklist on every booth browser after the artifact check above. Site data clearing is a standing deployment step, not a one-time migration. This is a human, physical-device sign-off: agents must not launch a local browser to imitate it, and Browser Run cannot replace its hardware, operating system, touch, foreground-timing, power, or network evidence.

  1. Close unrelated tabs, keep the kiosk on external power, disable display sleep/auto-lock, prefer wired networking, and do not add a persisted Playground site-slug.
  2. Clear all site data for the hostname the kiosk will use. Confirm Cache Storage and origin-private file system (OPFS) data are empty and its service workers are unregistered. If the Pages hostname was opened directly, clear that origin too.
  3. Open the kiosk in the foreground and confirm document.visibilityState === 'visible'. Time a cold boot from navigation until the attract prompt is usable, then reload and time a warm boot. Record both results with the browser/device version; investigate a crash or a boot that does not complete rather than treating a hidden/background run as a valid timing.
  4. Confirm the loader says “Building a real WordPress site in your browser. A cold start can take a minute or more.” Then confirm the exhibit version using the manifest/ZIP check above, not the unchanged public URL.
  5. Exercise Add blocks, all four stories, an inspector, About, Back/Escape, and Start over. Leave the engaged exhibit untouched for at least 65 seconds while the tab stays visible; it must return to the attract screen at the configured 60-second inactivity threshold.
  6. Turn on the operating system's reduced-motion preference, reload, and confirm every story caption, path endpoint, state, and control remains understandable without ongoing animation. Restore the booth's intended setting afterward.
  7. Prewarm the foreground kiosk immediately before visitors arrive. If the upstream Playground crash dialog appears, verify power/network, keep the tab foregrounded, and reload once. If it repeats, clear that origin's site data again, cold-boot, and rerun the artifact verification before returning the kiosk to service.

Record the hostname, deployed commit, manifest SHA-256, cold/warm times, 60-second reset result, reduced-motion result, device/browser versions, and operator initials. Append any separate Browser Run timestamp, observations, and returned evidence URLs to the same release record. These physical-device gates cannot be replaced by repository automation or Browser Run.

What visitors see

The attract loop introduces four stories, then runs the preview through assemble, path, signal, caption, and release. After a visitor engages, the motion settles rather than looping continuously. With reduced motion enabled, the same story, path, state, and caption information remains available without the animation.

The stories are:

  1. WordPress uses AI.
  2. AI uses WordPress.
  3. Agent Skills -> Coding agent -> A WordPress task. This work is outside the site: nothing inside WordPress runs in this story.
  4. WordPress tests the result with WP-Bench.

In the first story, the runtime request travels from the AI Plugin through the AI Client and a provider plugin to an external AI service. Connectors appears beside that path as the site-owner surface for provider discovery, configuration, and credentials; it is not presented as the request executor.

“AI uses WordPress” follows one illustrative booking-availability request: an outside assistant acts as a WordPress user, the MCP Adapter plugin translates the call to bookings/get-availability, and Core's Abilities API validates input and permission before returning available times or a refusal. The booking action is registered by site or plugin code; it is not supplied by Core. Its three participating inspectors can be followed as an optional assistant → adapter → Core sequence.

The Abilities API inspector includes its dedicated tabs. WP-Bench has a five-stage run loop that follows the work from task and sandbox through checks to evidence; it is a test bench, not a live request path. The MCP Adapter is explicitly labelled WordPress plugin · not in Core.

Cards and inspectors support visible focus, inert background content while a detail panel is open, Escape to close, and focus restoration to the originating card.

Canonical QR destinations

Eight committed SVG QR assets live under assets/qr/; they are generated locally and their destinations are fixed, selectable text in the inspector or About. There are no arbitrary editable QR-image or URL fields.

For existing serialized blocks, canonical cards, actors, stories, and panels are merged with the current defaults by id, so this release can supply its new canonical structure. User-authored copy fields remain editable rather than being overwritten.

Offline, assets, and packaging

Offline support is scoped only to a non-root kiosk permalink. It is disabled when the map is placed on the home/root URL, so the worker cannot take control of unrelated site pages. The published kiosk must also use HTTPS (localhost is the browser's development exception), because service workers do not register in an insecure HTTP context. It caches kiosk assets locally; it does not cache the external destinations above.

The package contains the PHP bootstrap, committed build/ block assets, assets/ (icons, worker, local fonts, and QR SVGs), and plugin readmes. It does not require Node.js on the installed site. EB Garamond, IBM Plex Mono, and Inter are bundled as local WOFF2 files; their license texts are in assets/fonts/. Plugin code is GPL-2.0-or-later.

Development and verification

The unit suite requires a PHP 8.3 CLI executable named php on PATH; several contract tests execute the server renderer directly.

npm ci
npm run generate:qr
npm run test:unit -- --runInBand
npm run test:playground
npm run lint:js
npm run lint:css
npm run build
npm run plugin-zip

npm run generate:qr deterministically refreshes the eight committed local SVGs. build/ remains committed so the ZIP is installable without a build step.

Local unit, lint, and build results prove local source and package state only. Browser verification is a separate gate. Safari, Add to Home Screen, Guided Access, service-worker behavior, and physical iPad touch/landscape acceptance require their own on-device sign-off. A current Cloudflare Pages deployment still requires its own origin-level verification.

Before deactivating or deleting the plugin, turn off Offline mode in the block and load the published kiosk page once while online. That lets the active page unregister its scoped worker and clear its cache before the worker endpoint is removed.