Follow-ups to the version-aware silent-update PWA overhaul that shipped in v1.2 (D21, PR #339). The foundations — per-feature-tool semver, silent SW refresh, WASM SHA-384 integrity verification, /.well-known/mcp.json, file_handlers / share_target / shortcuts, window.loft programmatic shim, OCR pre-download — are live. This phase finishes the long tail.
Agent shim. Compare PDF already has a working window.loft.tools.invoke('compare-pdf', { a, b }) that returns a per-page diff report. PDF Editor and Gerber Viewer ship descriptors only — the actual invocation throws “not yet implemented”. Wiring them up means decoupling each tool’s render-state from its React shell so an AI agent in a headless browser can drive the tool without mounting the UI. PDF Editor needs an op-application path (file in → ops list → file out). Gerber Viewer needs a render-state extractor that returns structured JSON.
Launch queue. PWA file handlers are registered in the manifest and the global capture lives in pwa-register.ts, but no feature tool reads drainPendingLaunchedFiles() yet. When a user drags a .pdf onto the installed app icon, the URL navigates to PDF Editor but the file isn’t yet auto-loaded. Each feature tool needs the mount-effect wire.
SEO/JSON-LD. Adding potentialAction to each tool’s WebApplication JSON-LD tells crawlers what the tool does, not just that it exists. Compare PDF gets AnalyzeAction; PDF Editor gets EditAction; Gerber Viewer gets ViewAction. Lands in buildToolSchemaObjects().
Security tighten. Three independent items:
- CSP
unsafe-inline removal requires Astro’s experimental CSP support + a per-island hash sweep so every inline hydration script is allowed by hash, not blanket. Big surface area, separate workstream.
- Trusted Types wraps the changelog
set:html rendering in a sanitizing policy. The markdown is already author-sanitized at content collection time; this is defense in depth.
- Cloudflare Insights SRI is awkward because CF rotates the beacon contents unpredictably. Either ship a weekly CI job that fetches the live hash and PRs it, or simply drop the beacon and use CF server-side analytics (free, zero client cost, zero supply-chain risk).
Build provenance. build-version-manifest.mjs already records a specDigest per tool (sha256 of YAML + component file). The drift warning compares it on next build; if the digest changed but the manual tool.version didn’t bump, the script prints a warning so the maintainer remembers to bump.
Full WebMCP. The in-page window.loft.tools.* shim is forward-compatible — its descriptor follows the current MCP draft. When the spec stabilizes and Chrome 146+ ships the browser-side transport, swap in a real bridge adapter without breaking calls.
Pre-launch audit P2 polish (added 2026-05-14). A pre-launch full-code audit surfaced six small wins that didn’t make the ship cut. None are user-facing failures; they’re hardening + UX papercut fixes:
- Narrow CSP
connect-src to the specific Cloudflare Insights subdomain instead of the broad *.cloudflare.com wildcard.
- Clear the in-memory
verifiedWasmUrls set on SW activate so a manifest hash rotation isn’t shadowed by a stale memo.
- Surface OCR download progress (percent + bytes) — current spinner gives no feedback for the 25 MB Paddle-English download on slow connections.
- Replace the OCR cache-size ”—” placeholder (shown when iOS Safari strips
content-length from opaque responses) with “cache status unknown”.
- Confirm dialog on OCR Remove (easy misclick after a 100 MB download).
- Validate tool IDs at build-script boundary against the same
SAFE_TOOL_ID regex the SW enforces (defense in depth — the SW boundary already covers the actual exploit path).
Out of scope: push notifications, background sync, periodic sync (different topic; not blocked by anything in this PR), light theme (separate workstream).