# Local creator bundle intake

The CLI is a structural validator and unserved local quarantine. The separate local HTTP service now accepts authenticated submissions through the demo Firebase emulators, described below. Neither path executes uploaded code, extracts runtime files, modifies the playable catalog or publishes. Local acceptance is not safety certification or production durability.

For a single reusable instruction to package a hosted game with a coding agent, use [agent-game-compatibility.md](agent-game-compatibility.md).

## Remote listings and hosted games

Remote listings and hosted capacity are separate. The planned remote flow accepts a link to an already-hosted game, with unlimited listings and no hosted-slot charge. Remote admission and public embedding are not available through this bundle tool. A URL alone does not establish ownership, compatibility or safety. Do not put a play URL in the package manifest's source or bundle fields.

This guide covers the hosted package flow. Local release selection supports one free hosted game, or three total with a valid server-issued VIP entitlement. Uploading drafts, awaiting review and updating the selected version do not use another hosted slot. Public hosting and subscription purchase are not open yet. Each ZIP is limited to 20,000,000 compressed bytes and 20,000,000 cumulative expanded bytes, with at most 300 runtime files; optional cover and logo files each have a 2,000,000-byte limit and count toward those totals.

The public `/developers/` page provides an on-device manifest preflight, the actual current example and the copy-paste instruction. It uses the same `parseSubmission` from `public/core.mjs` as this private intake utility. It checks declared structure only, resets reviews to pending and sends no selected file to a server. Package checks below still require a WHXTEVER tooling checkout, Node 22+ and Python 3. Creator upload and publishing are not open.

## Run

- Generate a new template: node scripts/creator-intake.mjs template <new-output.json>
- Validate and quarantine: node scripts/creator-intake.mjs validate <manifest.json> <bundle.zip>
- Prepare an already-built folder: node scripts/creator-intake.mjs prepare <manifest.json> <built-folder> <new-output-folder>
- Check without writing or reserving anything: node scripts/creator-intake.mjs check <manifest.json> <bundle.zip>

## Creator and agent quickstart

Requirements: Node 22 or newer and Python 3 available as `python`. Run from the repository root. Start with this real, attributed 2048 sample; these commands package existing files without executing game code:

```powershell
New-Item -ItemType Directory -Path .quarantine/creator-work -Force
node scripts/creator-intake.mjs template .quarantine/creator-work/metadata.json
$sample = Get-Content .quarantine/creator-work/metadata.json -Raw | ConvertFrom-Json
$sample.games[0].version = 'creator-sample-1'
$sample.games[0].bundle = 'games/2048/creator-sample-1'
$sample | ConvertTo-Json -Depth 20 | Set-Content .quarantine/creator-work/metadata.json -Encoding utf8NoBOM
node scripts/creator-intake.mjs prepare .quarantine/creator-work/metadata.json public/games/2048/478b6ec346e3-whx1 .quarantine/creator-work/prepared
node scripts/creator-intake.mjs check .quarantine/creator-work/prepared/manifest.json .quarantine/creator-work/prepared/bundle.zip
node scripts/creator-intake.mjs validate .quarantine/creator-work/prepared/manifest.json .quarantine/creator-work/prepared/bundle.zip
```

For a new game, edit the single `games[0]` in `metadata.json`: use your own ID/version, title/description, exact source commit/author/license/assets, entry, readiness marker, discovery and demonstrated support. Set `bundle` to `games/<id>/<version>`. Supply only already-built runtime assets and required notices in the folder; do not point this command at a repository, credentials, dependency tree or unrelated source. The packager includes every regular file. All paths must satisfy the current strict manifest and archive contract. It rejects links, reparse points, special files, unsafe names, case collisions and oversized bundles. Output must be a new folder outside the input folder, with an existing parent; existing output is never overwritten.

`prepare` replaces `files` with measured SHA-256 hashes and byte counts, produces deterministic `manifest.json` and `bundle.zip`, and runs the same checks as intake before creating output. It resets every review to pending even when input says passed. `checked-local` means structural checks passed without writes. `prepared-local` means local files were written. `quarantined-local` means intake reserved exact bytes locally. None means uploaded, authenticated, reviewed, approved or published. Check does not execute the runtime or establish that controls actually work. A filesystem failure during writing can leave a partial preparation folder; preserve it, inspect it and use a new output folder rather than treating it as a valid package.

Actionable recovery:

- **Missing/unexpected file or hash mismatch:** the rejection names affected paths; rerun prepare from the final built folder. After quarantine, changed bytes need a new version.
- **Invalid game fields:** match the exact v1 template. Do not add owner, score, poster or remote-embed fields.
- **Entry/license not inventoried:** correct relative paths and include nonempty runtime entry and UTF-8 license notice.
- **GPL source archive missing/invalid:** include and inventory the required corresponding-source ZIP; structural acceptance does not certify license completeness.
- **Unsafe path/link or size limit:** fix the built folder, not the validator. Maximum 300 runtime files and 20 MB compressed/expanded, including nested source bytes.
- **Existing/incomplete output or quarantine:** preserve it. Use a new preparation folder; version conflicts need a new version. Never delete a reservation to bypass conflict checks.

## Compatibility and artwork contract

`support` declares device classes, actual input methods, minimum viewport, reduced-motion support and age. `discovery` declares categories, player range and session length. These are creator claims awaiting review, not automatic admission. Supply a stable `readySelector` that exists only when the game is usable. The host's current `whx:guest:1` bridge reports ready/error; it does not certify wins, scores, pause or save. Test touch, keyboard, resize and failure behavior in the real isolated player before requesting admission.

The twelve internal mocked games in design-review/test-game-library/ are the presentation examples for complete creator, support, controls, theme, session, reviews, playtime and artwork states. They are marked test-only, nonplayable and nonproduction; use them to exercise platform UI without adding them to the admitted catalog.

Optional `branding` accepts `cover`, `horizontalLogo`, or both: `"branding":{"cover":"assets/cover.webp","horizontalLogo":"assets/logo.webp"}`. Put these PNG/WebP files in the built folder; each must be nonempty, at most 2,000,000 bytes and present in the hashed inventory. `prepare` measures that inventory. Paths are relative to the game bundle; external URLs, traversal and SVG are rejected. Omit `branding` if neither image is supplied. The on-device checker validates declarations, not image decoding or ownership.

Use a portrait cover, ideally 2:3, with important artwork near the center so cropped shelf cards still work. Covers appear in discovery, Library, Shuffle previews, local rankings and game details. Use a transparent horizontal wordmark, ideally 3:1 around 900 × 300 pixels, for rankings; otherwise the title is typeset. The compact catalog retains these paths even before a game's full metadata is requested. Images are served from the game's immutable isolated bundle after admission and remain subject to the asset-rights review. Known existing games retain their approved cover when none is declared; new games without a cover, or a failed cover load, show neutral platform artwork rather than another game's cover. A failed logo load falls back to the title. Screenshots and creator avatars remain separate review material; schema v1 has no screenshot, avatar or remote URL fields. Preserve the actual game's appearance and rights; no fabricated gameplay.

Admitted hosted games use immutable bundles on the platform's isolated game origin, loaded only after Play through the existing sandbox/bridge/export path. Source URLs provide attribution; they are not runtime embed URLs. Remote embeds and arbitrary external network dependencies are not admitted by this local tool. Do not replace `bundle` with an HTTPS URL or remove isolation to make a game load. The separate remote-listing flow still requires an origin/network/lifecycle policy and independent admission review before any public embedding. Local ZIP validation alone does not audit all JavaScript network behavior.

## Agent verification

```powershell
node --test --test-concurrency=1 test/creator-prepare.test.mjs test/creator-intake.test.mjs
npm run check
```

Reuse `parseCatalog`, `checkSubmission` and `archive.py`; do not fork a second schema. Keep review authority server-owned, all incoming reviews pending, quarantine outside served/exported roots, and publication separate. Agent-written games and fixtures must not be admitted as creator inventory: this platform does not create AI games.

Prepared downloadable template: dist/creator-intake-v1/manifest-template.json. It is generated from the current catalog's real 2048 example and validated through public/core.mjs parseCatalog, with all review values reset to pending. Replace the example's metadata and exact inventory for a different game; keep original source attribution truthful. There is no separately maintained submission schema.

Every submission declares discovery metadata used by the player settings modal: one or more approved categories, a truthful minimum/maximum player count and either a quick or standard session. These fields only narrow discovery. They never override pending review, age, device, input, viewport or reduced-motion gates.

The manifest is the existing schemaVersion 1 catalog envelope with exactly one game. ZIP entry names are relative to the game root and must match game.files exactly. Include files only, without directory entries or an extra top-level folder. Declared entry and license notice must exist and be nonempty; the license notice must be UTF-8. GPL-3.0-only also requires the current contract's safe inventoried sourceArchivePath and a structurally valid source ZIP. Supported license labels come solely from the existing parser. These are structural checks, not a determination of licensing rights or corresponding-source completeness.

## Bounds and authority

Manifest: at most 262144 UTF-8 bytes. ZIP: at most 20000000 compressed bytes, 300 runtime entries and 20000000 cumulative expanded bytes including a required nested source archive. The nested source archive is capped at 1000 entries. ZIP storage and deflate only.

The shared scripts/archive.py reader rejects traversal, absolute/drive/backslash paths, reserved device paths, control and NUL-ambiguous names, duplicate/case-colliding entries, links/special files, encrypted entries and unsupported compression before reading content. Intake then rejects undeclared/missing files and any exact size/hash mismatch. ZIP CRC failures also reject.

Creator-supplied passed or failed review assertions are normalized to pending. They have no admission authority; the normalized game remains ineligible for play. Unknown review values or extra owner fields fail the existing strict contract.

Validation completes before allocating quarantine. The default root is .quarantine/creator-intake-v1, outside public/export routes and ignored by Git. Public/dist destinations and linked root ancestors are rejected. An id/version hash names the local reservation, so creator strings never become filesystem paths. Windows inherited ACLs are not presented as authenticated private storage.

Each successful reservation contains the normalized manifest.json, exact original bundle.zip and a receipt written last. Exclusive creation preserves conflicts and interrupted work. An incomplete/active reservation produces an explicit recovery error; it is not overwritten or discarded. Identical normalized metadata and identical archive bytes return the original receipt after checking stored bytes. Repacked ZIP bytes or changed metadata under the same id/version require a new version. No uploaded content is executed or served.

Receipt status is quarantined-local, owner is null, and every review remains pending. Hashes describe normalized manifest bytes and original ZIP bytes. They do not establish creator identity or permission to publish.

## Proof

test/creator-intake.test.mjs uses the real current 2048 file inventory. It proves exact receipt/storage, normalized review rejection as authority, unchanged receipt on retry, conflicting bytes/metadata preservation, malformed/oversized manifests, forged owner fields, unsupported/missing license information, traversal/absolute/drive/backslash/device paths, symlinks, duplicates/case collisions, NUL path ambiguity, undeclared/missing files, size/hash mismatch, invalid ZIP, compressed/expanded limits, no files for rejected submissions and linked-root rejection.

Intake, corresponding-source packaging and static-export tests pass. The shared ZIP-reader refactor keeps all eight accounts and 50 guest frozen artifact files hash-identical. No provider/rules/live writes occurred.

Actual local sample: 2048 version 478b6ec346e3-whx1, 13 files and 49762 expanded bytes. Receipt ID 7e51635a7aeaa18f924d58185d68ca85f81d7816f5e4ea30ccd8b33b376c6721. Status quarantined-local, owner null, all reviews pending. Input ZIP is retained at evidence/intake-example-2048.zip; normalized receipt/files remain under the ignored quarantine root. This is a local filesystem receipt, not durable provider proof.

## Authenticated local submission service

`creator-service.mjs` adds loopback HTTP transport using the existing parser, quarantine and official Firebase Admin SDK. It refuses startup unless Auth and Firestore target project `demo-whx-accounts` with the exact local emulator addresses below. Upload, review and release selection are local-only; there is no deployed endpoint or production storage change.

Prerequisites: Node 22+, Python 3, the existing Firebase CLI/Admin SDK and the local Auth/Firestore emulators. Run from the repo root. Do not start duplicate emulators if another owner already holds the ports.

```powershell
firebase emulators:start --config firebase.accounts.json --project demo-whx-accounts --only auth,firestore
```

In another terminal:

```powershell
$env:FIREBASE_AUTH_EMULATOR_HOST = '127.0.0.1:9198'
$env:FIRESTORE_EMULATOR_HOST = '127.0.0.1:8188'
# This checkout already has Admin installed under pnpm; no package installation is needed.
$env:WHX_ADMIN_MODULE = (Resolve-Path 'node_modules/.pnpm/firebase-admin@14.2.0_@fire_c3711808f6b0caa1f73114d359183e14/node_modules/firebase-admin').Path
node creator-service.mjs
```

The API binds only `127.0.0.1:4492`. Its allowed browser origin is `http://127.0.0.1:4173`; absent Origin is allowed for authenticated tooling. Files stay under `.quarantine/creator-service-v1/<UID hash>`, outside served roots. The SDK path reflects the current checkout and must be resolved again if dependencies change.

### Private remote drafts

The same demo-only service accepts a remote game's title and existing play URL without downloading or executing it. This is a private draft, not a public listing or a completed compatibility check. Remote drafts have no hosted-game quota and never change hosted selections or entitlements. The upload page does not yet expose this flow.

Send `POST /api/creator/remote-drafts` with a non-anonymous demo account's Bearer ID token and `Content-Type: application/json`. The body is exactly these three fields:

```json
{
  "requestId": "45e61e88-4a55-4fb9-8ed4-f19091585262",
  "url": "https://your-game.com/play/",
  "title": "Your game"
}
```

Generate a new UUID for each intentional draft. Keep that UUID and the original fields for a retry whose outcome is unknown. The title is trimmed and must contain 1–80 characters. The URL must be HTTPS, at most 2,048 characters, with no credentials or whitespace. The entire JSON body is capped at 8,192 bytes. Obvious private/local addresses and reserved hostnames reject; the service does not resolve DNS, follow redirects, check availability, inspect game content or establish destination safety.

A new draft returns `201`; an identical normalized retry returns `200` and the same record. Both responses contain a `draft` object with `id`, `owner`, `delivery`, `status`, `url`, `title` and `createdAt`. The server generates the `r-` plus 32 lowercase hexadecimal ID, sets the verified account as owner, `delivery: "remote"` and `status: "unreviewed"`. This ID is a private record identifier, not a public game address. Do not display a playable link or reviewed badge from it.

`GET /api/creator/remote-drafts/<id>` returns that same response only to its owner. Unknown IDs, hosted records and other accounts receive `404`; reviewer status does not grant access. There is no list, edit, delete, review, preview or publication endpoint for remote drafts yet. Hosted upload, version, review, release and preview routes reject remote records.

An uncertain save must not be reported as successful. Retry the same request ID and fields to recover the original receipt; changing those fields with that ID returns `409`. When the draft ID is known, its owner can read it to check status. Inconsistent stored retry state returns `503` for recovery and is never silently replaced. Other errors are `400` for malformed JSON, `401` for missing/invalid identity, `403` for anonymous identity or a foreign Origin, `413` for an oversized body, `415` for the wrong media type and `422` for an invalid request or URL.

Local evidence: `O:/whxtever/core-flow-20260929/remote-draft-v1/handoff.json` records 34/34 official Auth/Firestore/HTTP upload and remote checks, 11/11 retained hosted-release checks and 15/15 installed creator checks with independent source review. Root verified its 17 evidence hashes and 39 installed source hashes in `root-acceptance.json`. These checks do not prove UI, remote execution, browser behavior, production storage or public availability. Before playable admission, remote games still need the separate ownership, content, device/input, lifecycle and frame-policy review.

### Local upload page

Start the shell with the API explicitly enabled, after reconciling any existing preview owner on ports 4173/4174:

```powershell
$env:WHX_CREATOR_API = 'http://127.0.0.1:4492'
node server.mjs
```

Open `http://127.0.0.1:4173/developers/`. Its local-preview link leads to `/developers/submissions.html`. Continue to the existing account page in a new tab and use a demo emulator account; the upload page observes the same account session. Choose a prepared manifest and ZIP, then Submit for review. The page checks manifest structure before sending and displays the server's owner-bound receipt. It retains only the latest status reference under that account's UID, never tokens or selected file contents, so reload can check the same receipt. Sign-out/account changes clear the displayed receipt and cancel pending work. Missing browser storage does not block submission; the page explains how to recover with the same files.

Stopping or leaving during an upload stops waiting, not server acceptance. The reference is saved before sending so Check status can reconcile an uncertain response. Recoverable errors preserve the selected files. The UI has a 90-second timeout and does not follow API redirects. Its requests require the demo project, an exact loopback origin and known submission routes before reading an ID token. Shared manifest and selector validation reject dot-segment versions `.` and `..`, which would otherwise change the status URL during normalization.

These three local-only assets are excluded from the static export allowlist. Without the explicit API option, the upload routes return 404, `/config.json` has no creator API and the public developer page remains a manifest preflight. Only the local submission page gains the required account/API CSP permissions; the games origin and ordinary shell retain their existing restrictions. This is implemented source with controller and HTTP checks; rendered browser acceptance is still pending under the existing browser restriction.

### Request and response

Send `POST /api/creator/submissions` with `Authorization: Bearer <demo emulator ID token>` and multipart fields `manifest` (JSON file) and `bundle` (ZIP file), exactly once each. Use a non-anonymous local emulator account and a new game ID; admitted catalog IDs are reserved. Never send a live token to this demo service or put tokens in logs. Let the HTTP client set the multipart boundary.

The server verifies the token including disabled/revoked status, derives the UID itself and rejects manifest ownership fields. An atomic Firestore transaction claims the game ID and immutable version for that UID. It returns `201` with `{submission, statusUrl}` for a new version, or `200` with the original receipt for an identical retry. The owner can read that URL with the same bearer authentication; other accounts receive `404`. A different owner or changed bytes under an existing version receives `409`. New versions retain the existing owner.

The account-attributed submission has status `in-review`, all reviews pending, exact archive/normalized-manifest digests, version, file count and expanded bytes. This means queued for review, not that review has begun. The underlying structural receipt remains `quarantined-local` with `owner: null`. An account claim is not proof of intellectual-property rights or permission to publish.

Errors: `400` malformed multipart, `401` missing/invalid/expired identity, `403` anonymous identity or foreign origin, `413` size limit, `415` wrong media type, `422` manifest/archive rejection, `503` busy service or storage needing recovery. Retry `503` later with the same files; preserve incomplete quarantine instead of deleting its reservation. No owner/version document is written for a failed package inspection. If storage succeeded but the transaction or response failed, retry reconciles the same immutable receipt.

The total multipart body is capped at 20,327,680 bytes, including chunked requests without Content-Length. Existing manifest/archive limits remain unchanged. At most two uploads are admitted per process, including incomplete bodies; reads remain available. Inspection uses asynchronous bounded Python execution rather than blocking the HTTP event loop. The server request timeout is configured at 45 seconds, checked on Node's default 30-second interval; measured slow-body rejection was 59.31 seconds, not an exact 45-second guarantee.

### Review and local release selection

The existing status URL returns the submitted version plus `activeRelease` (`null` or `{id, version}`) and `capacity` (`{limit, used, overLimit}`). These are local selection records, not public catalogue admission or served game files. Status is `in-review`, `approved` or `changes-needed`; approval never silently publishes. The underlying receipt ID, manifest/archive digests and private files remain unchanged. GET is owner-only, except a trusted reviewer can read a known submission to inspect its current review revision.

A different account with the server-issued `whxReviewer: true` custom claim can POST to `<statusUrl>/review`:

```json
{
  "receiptId": "<exact 64-character receipt digest>",
  "expectedRevision": 0,
  "checks": {"safety":"passed","license":"passed","assets":"passed","moderation":"passed","playability":"passed"},
  "reason": ""
}
```

All five checks are required, each `passed` or `failed`. Any failure requires a nonblank reason (at most 1,000 characters). Self-review, ordinary creator tokens and forged fields reject. These are explicit reviewer decisions; structural upload validation cannot produce them. A changed decision requires the current `reviewRevision`, increments it and appends an immutable decision under that version's `reviews` collection. An identical retry returns the same decision without a new revision. Failed re-review of the currently selected receipt clears its selection and frees its slot in the same transaction. Reviewing another version does not disturb the selected working version. Reviewer access provisioning and revocation remain operator responsibilities; there is no client role or entitlement setter.

The owner can POST to `<statusUrl>/release`:

```json
{
  "receiptId": "<this version's exact receipt digest>",
  "expectedActiveId": null,
  "selected": true
}
```

Only approved versions with all five checks passed can be selected. Supply the current `activeRelease.id` instead of `null` to replace a selection; `selected: false` removes it. A stale selection returns409 and must be refreshed. An identical retry is safe when the requested selection is already current. Selecting a previous approved version rolls back; versions and private packages are retained. Uploading a new version or rejecting it cannot replace the selected working version.

One `creatorAccounts/{uid}.activeGames` record serializes concurrent hosted selections across games. Free accounts get one hosted slot; a trusted `creatorEntitlements/{uid}.vipUntil` Firestore Timestamp in the future allows **three total hosted slots**. Missing, expired or invalid entitlements mean one. Remote listings never consume hosted slots. Release selection requires the server-written package evidence; a remote record cannot masquerade as a hosted package. Drafts, review and replacing a game's selected version do not consume extra slots. There is no client-written VIP flag. Synthetic emulator entitlements used in tests are not Stripe subscription proof.

Malformed account capacity records reject with `503` for operator recovery. Unknown IDs in a valid existing selection list remain counted until explicitly reconciled; the service never silently drops a selection to manufacture a free slot.

This local stage preserves existing selections when an entitlement expires, reports `overLimit` and blocks additional games while permitting updates, rollback and removal. It does **not** implement the proposed commercial downgrade policy of choosing one game then unlisting the others. That timed reconciliation and disclosure must be implemented with billing before public VIP launch. No files or history are deleted.

The upload page displays review reasons, the selected version and slot usage; approved submissions gain Select for local release and Remove selection controls. To revisit a prior version, choose its original manifest/package and submit again: the immutable retry restores that receipt. Uncertain responses and concurrent changes require Check status before trying again. All decision requests are bearer-authenticated, exact-field JSON capped at8KiB, with the existing origin and timeout restrictions.

### Private local preview

With the demo emulators running, the creator service also starts a separate game listener on `http://127.0.0.1:4493`. Start the optional shell with both `WHX_CREATOR_API=http://127.0.0.1:4492` and `WHX_CREATOR_PREVIEW=http://127.0.0.1:4493`. The existing preview must be restarted by its owner to enable this configuration. Production exports do not include the submission or preview controller.

An owner or trusted reviewer can POST `{ "receiptId": "<exact receipt>" }` to `<statusUrl>/preview`. Each request rechecks the private receipt, manifest and archive digests, then repeats the bounded inventory inspection. Files remain in memory; no submitted code executes on the host and no ZIP files are extracted. The response contains a random 256-bit capability URL that expires after ten minutes. Four previews and two concurrent inspectors are allowed per process; a new preview replaces that requester's prior capability. Expiry or process shutdown discards the in-memory bytes. Grant URLs are not logged or saved in browser storage.

Preview serves only inspected files plus the existing readiness bridge on a distinct loopback origin. The HTML entry receives the same explicit bridge insertion as hosted games; other submitted files retain their exact bytes. An entry must be HTML with a document head and must not collide with `whx-bridge.js`. Requests with cookies or Authorization reject. The frame uses `sandbox="allow-scripts"` without same-origin, no referrer, restricted content policy and the existing source/session/release readiness checks. Its message handshake contains no identity or token. Errors, ten-second readiness timeout, expiry, account changes, page departure and visibility loss remove the frame; returning requires a fresh launch.

These controls restrict resource loads and nested frames. They do not establish complete network isolation: sandboxed content can attempt to navigate its own frame externally, and load-event teardown cannot prove no request was made. Browser enforcement, rendered layout, gameplay and real creator rights review remain unverified. A capability is a short-lived bearer URL; someone given that URL can fetch that private package until expiry. Already-issued previews retain their verified snapshot if the disk package later changes; new previews fail closed. Preview never approves, selects or publishes a game.

Run `node scripts/creator-preview-proof.mjs` for actual demo Firebase/HTTP proof and `node --test test/creator-preview.test.mjs test/creator-upload.test.mjs test/server.test.mjs` for transport/controller/isolation regression. These checks do not launch a browser.

### Local proof and remaining integration

```powershell
node scripts/creator-service-proof.mjs
node scripts/creator-release-proof.mjs
node --test --test-concurrency=1 test/creator-prepare.test.mjs test/creator-intake.test.mjs
```

Both HTTP scripts require the demo emulators and create synthetic local identities. The upload/status receipt from 2026-09-28 records 20/20 passing checks (`creator-service-proof/2026-09-28T15-01-58-963Z/receipt.json` under the O: evidence root). The accepted hosted-capacity slice from 2026-09-29 records 11/11 official SDK/HTTP checks and 15/15 scoped local checks at `O:/whxtever/core-flow-20260929/hosted-quota-v1/root-acceptance.json`. It covers trusted-reviewer/owner gates, concurrent free/VIP hosted selections, remote/package separation, update/rollback/removal, failed-review deselection, immutable audit records, expired/invalid entitlement fallback and direct-client write denial. Synthetic reviewer decisions are not an actual rights or gameplay review. These dated receipts apply to their recorded file hashes, not later edits.

Use local NTFS TEMP/TMP for active test trees: O: cannot create the required archive junction fixtures. Keep retained reports on O:. No browser, Stripe, public hosting or production acceptance is implied; exact receipt hashes and custody are in docs/CORE-FLOWS.md.

Still required: browser acceptance of the local creator UI and sandbox, production deployment with durable private object storage, distributed admission limits, public hosting and publication, billing-backed entitlement and timed downgrade reconciliation. Local preview/selection/update/rollback are not yet a public hosting flow. Filesystem and Firestore writes are not one distributed atomic commit; local immutable retry recovery is demonstrated, production recovery is not. No shared rules, live provider or public hosting changes were made. Existing provider approval boundaries remain in force.

Reuse one parser and one archive inspector. Official integration references: [ID token verification](https://firebase.google.com/docs/auth/admin/verify-id-tokens), [Auth emulator connection](https://firebase.google.com/docs/emulator-suite/connect_auth), and [Firestore transactions](https://firebase.google.com/docs/firestore/manage-data/transactions).
