feat(projects): server-managed blocksMajor marker for Blocks v8 sites

Draft
TL
4d ago
+566-47

Part of the v8 Studio stack (base: #7796). First of the v8 Fast Preview + Localhost track, on top of #7843. PO decisions of 2026-10-11, requirements R1–R5.

Projects now learn they are Blocks v8 before any thread exists. Today v8 is known only from an async probe that runs only for cms threads, and a thread is cms only when metadata.fastPreview is on: a circle. The server now writes a project marker, metadata.blocksMajor: 8, which the next PRs use to make v8 always Fast Preview (R2/R3) without touching v7 (R1).

What

  • isBlocksV8Project(meta) (packages/shared/src/thread/session-runtime.ts): meta?.blocksMajor === 8.
  • Server-managed: blocksMajor joins sandboxMap in stripServerManagedMetadata, so the settings form or a tool call can neither set nor clobber it; metadataKeptOnClear keeps it (with sandboxMap and siteSlug) when a client sends metadata: null.
  • Written only for orgs with site_editor_content_protocol and only when main's .deco/schema.gen.json is v8; cleared when main is no longer v8. A v7 site never has it.
  • Where: hosted/blocks-major.ts (syncBlocksMajorMarker, markBlocksV8OnCreate):
    • project create / deco import (tools/virtual/create.ts), so a new v8 project's first thread already opens in Fast Preview;
    • the admin site-claims backfill on real runs. Run it once before release so existing v8 projects get the marker;
    • write-through wherever mainIsV8 already runs: decofile GET and publish, site-token issue, and the hosted /git/* routes (loadHostedDraft).
  • Single-key patchMetadata (no read-merge-write over sandboxMap), best-effort: a failed write is logged, never fails the request. The request-cached project storage now drops its cache on patchMetadata too.

No visible behaviour change in this PR.

R1 (v7 = main)

v7 projects never get the marker, so nothing keyed on it changes for them. Create in a flagged org does one extra read of main's schema (best-effort, errors swallowed).

Tests

  • apps/api/src/hosted/blocks-major.test.ts: sets, clears, no-op when it already matches, write errors swallowed; on create: v8 repo marked, site folder honoured, v7 never marked, flag off means no read and no write, no repository means nothing happens, repo errors swallowed.
  • apps/api/src/tools/strip-server-managed-metadata.test.ts: blocksMajor stripped; kept on clear.
  • tsc --noEmit for apps/api and packages/shared, oxlint, and biome format are clean. bun test src/hosted src/api/routes/decofile.test.ts src/api/routes/sandbox-proxy*.test.ts src/tools/virtual → 152 pass. The 1 failure is studio-pack.integration, which needs a database and fails on the base too.

Stack

v8 Fast Preview track: previous: #7843 · next: #7872

Review fixes (f735723f1)

  • The marker is written on create only when the hosted CMS can serve the project: the project owns its site id and a delivery bucket is configured, the same checks the hosted routes make. A marked project never polls a hosted route that answers 404 or 503.
  • COLLECTION_VIRTUAL_MCP_CREATE no longer waits for the marker (void markBlocksV8OnCreate(...), which never throws), so creating or importing a site never waits on GitHub. If it misses, the write-through and the backfill set it.
  • projectPackagePath replaces the inline copies in admin.ts, hosted.ts, decofile.ts and draft-publish-status.ts.

🤖 Generated with Claude Code

https://claude.ai/code/session_01WNwbSEePYNcY5YCgqZURig