rifflets

PUBLIC BUILDER REFERENCE · SPEC 2.15.0

Canonical Rifflet Spec

The versioned contract: experience quality, discovery metadata, common components, analytics, security and hosting.

Canonical Rifflet Spec

Version: 2.15.0 · Published: 2026-10-10 · Status: normative for new and substantially revised contributions.

Start here before building, importing, reviewing or releasing a Rifflet. This document consolidates the living requirements previously spread across AGENTS.md, FRAMEWORK.md, RIFFLET_PROTOCOL.md, RIFFLET_COVERS.md and ANALYTICS.md. Those documents retain implementation detail; this spec governs the common product contract. Current executable validators are authoritative about accepted wire fields. A proposed spec requirement is not proof that a legacy app or remote host implements it.

1. What a Rifflet is

A Rifflet is a small, purposeful, playable piece of content with a compelling first frame, an immediate meaningful action, an understandable payoff and an honest way to share or make a variation. It packages its code, content, assets, metadata, capabilities and measurement meanings. It should work on a phone without looking like a shrunken desktop application.

Build an experience, not a brochure around a tool. Prefer one strong loop: encounter → try → payoff → personalize → share → another person plays or riffs. Never call a copied prompt an implemented editor, a saved state a newly generated app, or a share-sheet return a delivered message. The public gallery advertises only what the released version can do.

2. Version and conformance

The spec uses SemVer independently of the app version, metadata schema, analytics schema and runtime API. A major revision changes a required contract; a minor adds compatible requirements; a patch clarifies wording. Immutable snapshots live in specs/rifflet/<version>.md. Update the current file and changelog together; never rewrite an old snapshot to change its meaning.

New contributions declare riffletSpecVersion in content.json and include a conformance review that identifies the app release, spec version, browser evidence, exceptions and reviewer. This field is a declaration, not a certification. Existing apps are marked pending-audit in the catalog metadata overlay until actually reviewed. metadataSpecVersion=1.0.0 means their descriptive metadata was normalized to this schema; it does NOT mean every legacy app meets this complete spec. Keep app version/source pins unchanged during editorial-only catalog releases.

3. Package and ownership

For curated platform integration, use apps/<slug>/ with content.json, index.html, app-owned code/assets, README.md, analytics.json and tests appropriate to meaningful behavior. For the default provisional route, submit manifest.json plus source/ and build/ under the public rifflet.upload/1 contract in AGENT_SUBMISSION.md and UPLOAD_ONBOARDING.md; place corresponding RCF source records inside source/. Editable experiences also supply a reviewed rifflet.data.json contract and pure semantic validation. Use stable kebab-case IDs. Include license and asset provenance, attribution and disclosure of simulations. Do not include private keys, personal feedback exports, environment files, node_modules, raw research archives or private coordination notes in public assets.

For curated platform integration, keep app changes in one isolated worktree and focused branch. Shared framework changes need separate review. Submit a frozen commit and changed-file inventory; do not edit another agent's uncommitted work. Provisional creator submissions require no Git repository, branch, PR or maintainer handoff: preserve a separate local copy and publish a self-contained source/build package. Its immutable submission ID and server-computed SHA-256 identify that edition. The distribution Git repository is an audited source mirror, not the private canonical history. Maintainers import reviewed contributions and export an exact new source snapshot. Never merge private canonical history into the mirror.

4. How to describe your Rifflet

Write for a person deciding whether to try it and for an agent trying to classify it. State the action, subject, feedback/payoff and concrete customization or social behavior. Use specific verbs. Do not substitute hype, a genre label or implementation jargon for an explanation. Do not invent audience outcomes, scientific validity, AI capabilities, verified rankings or usage numbers.

Required descriptive fields:

The reviewed catalog overlay is shared/catalog-metadata.json. Its schema is rifflet.catalog-metadata/1.0. scripts/catalog-metadata.mjs validates exact published-app coverage, editorial lengths and controlled values, and enriches both catalogs without replacing executable permissions or release identity. Update taxonomy deliberately when an existing term cannot express the experience; do not silently make a near-duplicate tag. New apps must add their metadata before publication. Catalog descriptions must agree with the shipped version, including retained variants.

Example summary: “Guide a glowing snake through a compact arcade board, collect targets and keep a growing tail out of trouble. Each quick round rewards planning as much as reflexes. Adjust the available challenge settings, then share a playable challenge with a friend.” Use only if those features are implemented; do not copy this as a claim about an existing app.

5. Illustrated cover and passive ready preview

October 13 implementation addendum: the explicitly approved four-app experience demo pilot follows [RIFFLET_COVERS.md](RIFFLET_COVERS.md). Its exact-release opt-in rules supersede the zero-app-code pre-entry restriction below only for those reviewed releases. The published 2.15.0 snapshot and other apps remain unchanged. Static /grid artwork and same-instance feed demos are distinct surfaces.

The feed presents full-bleed animated illustrated cover art. A cover may use an original visual metaphor that truthfully communicates the central mechanic; it need not reproduce the first gameplay frame. Favor a recognizable subject, expressive materials and purposeful scene motion, with little or no large readable text. Never imply unavailable features, fabricated tracking, completed results or live activity. Do not autoplay a scare.

Artwork fills the simulated phone beneath a transparent header and profile/Heart/Share icons. There is no dedicated social rail, visible “Tap to enter” button or footer CTA. Keep a named, keyboard-accessible entry link and visible focus feedback. Keep key subjects clear of header/footer and icon hitboxes at narrow and short viewports. Full-bleed background cropping is allowed; do not crop essential subjects. Controls have independent hit targets and must not launch the app as a side effect.

Every published edition requires cover art and descriptive alt text. The current collection has 40 published editions: 31 main artworks plus variants that share their family's visual. Sharing art does not merge source/revision identity or link to a different edition. Lab/search/link previews use a static 900×1200 (3:4) WebP under 250 KB derived from the same artwork, with provenance and accessible title/description/action metadata. Preserve the complete Lab composition with contain fit; a static export is not a separate art direction.

Only the active cover animates. Pause all cover motion when the page is hidden or a modal is open; offscreen covers remain static. Reduced motion produces zero animation. Menu → Pause cover animation persists the visitor's choice across cards and visits, with a usable denied-storage fallback. Resume only when active, visible and permitted by both the preference and reduced-motion setting. Decorative scene animation never emits gameplay or synthetic interaction events.

Before deliberate entry, fetch or instantiate zero app code, app documents/iframes, gameplay timers, audio/video media, workers, models or sensors. Licensed cover artwork is the only experience-specific preview asset. Tapping or keyboard-activating the named cover link navigates in the same tab to the pinned standalone experience; it never synthesizes Start, consent or permission. Preserve received content and source/revision identity across preview, launch and sharing.

Every new or substantially revised Rifflet supplies a portable entry-profile.js importing shared/feed-entry.js and declaring its stage, firstAction, hook, gesture class, optional instructions/settings selectors and supported welcome target/style. These selectors address the real standalone app; they do not authorize mounting it for the cover. Include the literal module in index.html and preserve its complete dependency graph. The Rifflet Common Framework (RCF) provides shared entry/navigation, lifecycle, Menu, identity, feedback, sharing and measurement modules. RCF is the common integration contract; the separate provisional upload API is documented in UPLOAD_ONBOARDING.md.

The standalone entry scales to its actual viewport, respects safe areas and keeps controls readable. Do not stretch artwork, crop essential controls or force long recipes into a fixed-height canvas. Text, choices, warnings and meaningful state remain accessible HTML or have an accessible equivalent. The app's state machine remains authoritative; neither cover nor navigation synthesizes Start, consent, media permission or publication.

6. Open, play, return and common Menu

The sequence is passive preview → deliberate same-tab open → standalone play → browser Back or Menu → Back to feed. Save a bounded, validated return context identifying the selected feed card and applicable filters. Both return routes restore that card instead of resetting to the top. If browser history or local storage is unavailable, the common Menu supplies a safe same-origin feed link with the selected card; never follow an unvalidated external return URL. Do not require a new tab or an iframe-only mode to reach real play. Opening an app does not itself count as a meaningful gameplay start.

Inside the actual experience, give the visitor a meaningful action in the initial phone viewport. Useful defaults are mandatory; optional name, difficulty, theme, tutorial and authoring setup belong in Menu. At most one deliberate Start may precede interaction when timing, audio or consent requires it. Keep horror warnings, permission intent and safety guidance ahead of their relevant actions. A feed tap cannot substitute for those actions. A small responsive payoff in 5–10 seconds is a design target where appropriate, not a measured retention claim or a requirement to rush recipes, lessons or breathing.

The common bottom-right hamburger button is labelled Menu. Its dialog provides How to play, Settings, Back to feed, feedback, personal improvements, sharing/riff actions and history. Preserve the existing riff-star DOM ID for compatibility. Move original optional nodes; never clone listeners or duplicate IDs. Keep the inline fallback until the menu is available. First actions, pause/stop/mute, essential clues, received-riff context and consent stay inline. Opening the menu pauses active work; closing returns focus and resumes only where appropriate. Advanced authoring stays explicit.

Heart, Share and profile overlay the art inside the simulated phone; do not allocate a dedicated rail. Their independent hitboxes must take priority over the entry link and keep the key subject clear. Desktop previous/next arrows sit outside the phone. Mobile has swipe navigation and no visible previous/next arrows; provide equivalent keyboard and assistive navigation without introducing an on-screen mobile arrow rail. Feed cards snap one at a time, and wheel momentum must not skip multiple items. Once the standalone app opens, its scrolling, drawing, dragging, zooming and forms own their gestures; feed navigation is no longer competing with them.

Only the active, visible cover animates, subject to reduced motion and the persistent Menu pause preference. Opening any modal or hiding the page pauses it. Adjacent covers may preload bounded passive artwork, but browsing must not fetch or instantiate app code, iframes, media, workers, sensors or models. Navigation away from an active app pauses audio/timers/simulation and releases capture, workers and other owned resources as appropriate. Lifecycle pause/resume/dispose and page/history restoration need explicit tests; browser throttling alone is insufficient. Returning to the feed restores selection, not a claimed completed round or invented saved game state.

Audio unlock and camera/microphone/motion requests belong to deliberate actions in the standalone experience and retain existing reviewed route policies and denial fallbacks. Heavy models, external search and server mutations wait for explicit need. No preview or URL flag expands permissions. Main/Personal comparison must retain its selected source/revision, unload stale players and invalidate pending launches. Any remaining reviewed embed must validate message origin, source and bounded schema; arbitrary third-party code needs approved isolation.

Proposed performance targets remain a useful fallback within one second and an actionable lightweight app within three seconds under a recorded mobile/network profile. Measure preview load and standalone app launch separately. Report bytes, timing, device, cold/warm cache and exceptions; no hard performance claim without evidence.

7. Device, permission and service requirements

Metadata must separate browser permission from infrastructure. Declare requirements.device, browserPermissions, services, network and accessibility. For each permission state name, required versus optional, purpose, when requested and fallback. Examples: camera for a live background; microphone for local pitch estimation; motion for optional tilt control. Web Audio gesture unlocking, vibration and speech synthesis are capability constraints, not necessarily browser permission prompts. Record availability and fallback rather than promising universal support.

Services list what actually depends on a server: static CDN, hosted public feedback, private telemetry, group state, leaderboards, approved research, short links or artifact object storage. For each, state purpose and requiredFor; distinguish optional shared services from requirements for core play. An object-storage dependency belongs to artifact hosting, not a request to the visitor for camera permission. Do not advertise social backends that are not enabled for that app. Private configuration names are not credentials and credentials never belong in metadata.

Request sensitive capabilities only after an understandable user action. Host permissions are an explicit reviewed route allowlist; adding a manifest permission cannot grant camera or microphone access by itself. Do not upload camera frames, voice recordings or raw sensor streams through the common analytics layer. Be explicit when anatomy, health, educational or physical-world content is simulated or unvalidated.

8. Client-first architecture and WebAssembly

Keep rendering, game state, interaction, deterministic scoring where appropriate, sensor interpretation and suitable inference on the client. The server should primarily distribute immutable assets and coordinate the genuinely shared parts. Avoid one server process, remote render stream or per-frame API call per Rifflet player. Cache versioned assets through the CDN; persist bounded local state with denied-storage and offline fallbacks.

WebAssembly is optional, not a checkbox. Use it when profiling supports a real benefit for CPU-heavy tracking, signal processing or portable code. Use a Worker for long-running computation; avoid blocking the main interaction thread. Serve WASM as application/wasm, carry licenses, pin versions, lazy-load large models after consent and document memory/download budgets. WebAssembly is not a complete security sandbox and does not replace origin isolation, permission policy, input validation or resource limits. Threads and shared memory need a separately reviewed cross-origin isolation plan.

Portable bundles and WASM implementation guidance

Treat a Rifflet as a self-contained, versioned client bundle: a browser entry point, licensed assets, declared dependencies/capabilities and the RCF adapter. WASM is a supported compute implementation inside that bundle, not a replacement for the bundle contract. This clarifies the existing client-first direction; it does not mandate a WASM rewrite or introduce a new runtime API.

Implementation references: https://developer.mozilla.org/en-US/docs/WebAssembly and https://webassembly.org/docs/security/ .

Use plain HTML/CSS/JS and standard web APIs by default. Current curated app routes share a trusted host/runtime; never generalize that trust to arbitrary uploaded code. Reviewed generated artifacts use the platform's isolated artifact runtime contract. Origin isolation, CSP, capability messages, storage separation and sandbox flags must be reviewed before accepting code from an untrusted creator.

9. Common identity, feedback and riff lineage

Use the common pseudoanonymous device alias for friendly display. It is not authentication, a unique human identity or an analytics key. Preserve reset/storage-denial behavior; do not silently reconnect a cleared device. Use explicit consent before publishing a name to a scoreboard or public feedback.

There are two distinct feedback lanes. The explicit public feedback/history flow publishes only after review and consent. Personal build requests, feedback rounds and personal versions remain private to the browser owner and authorized operators. Permission to promote an exact revision to the public main experience does not publish its private feedback. Label the destination and visibility before submission; do not silently copy between lanes. Show the app/release and available instance context before any public publication. Keep drafts and retry receipts where possible; acknowledgement is delivery, a local save is not. Deletion capabilities stay private. Never combine private behavioral traces with public feedback.

Separate source release identity from personalized instance identity. Preserve immediate parent, source version/full commit and bounded validated content. Do not expose puzzle answers unnecessarily or place private rosters in share links. Server-side semantic validation is required for hosted editable content; browser validation alone is insufficient. A published riff request remains a request until an actual reviewed revision exists. Preserve release drift notices and the ability to play retained versions; don't claim current code recreates historical behavior automatically.

Creator identity and batch import review packets

Assign each human or organization owning a contribution a stable, opaque public creatorId in the form creator_<UUID>. Reuse an existing ID only after the maintainer confirms ownership through the agreed private channel; never trust a pasted ID, a matching nickname or an agent's assertion. Otherwise the maintainer assigns a new pseudonymous creator ID. An optional agreed public alias is a friendly label, not proof of identity. The ID is nonsecret attribution metadata, not authorization. Keep it separate from visitor browser/device aliases, private analytics identity and credentials.

Multiple agents may contribute on the same creator's behalf. Record their contributor credits separately; an agent/model label such as Claude or Codex must not become the app owner by default. Group each reviewed import with importBatchId: batch_<UUID> and preserve each external sourceAppId, source location and immutable ref or archive checksum. The creator ID supports consistent attribution, discovery/search and aggregate statistics; the batch ID identifies a review/import cohort, not another creator or a visitor tracking key.

Supply creator.json and import-manifest.json in the private review packet. The examples below define the proposed review-packet format, not accepted runtime fields or an implemented validator/API. Do not add these unknown fields to strict content.json. Replace all illustrative IDs, aliases and source values with maintainer-confirmed values; the example UUIDs must not be reused. Retain the app's existing manifest/conformance declarations until separately reviewed.

creator.json example:

{
  "schemaVersion": "rifflet.creator-review/0.1",
  "creatorId": "creator_123e4567-e89b-42d3-a456-426614174000",
  "publicAlias": "Example creator",
  "contributorAgents": [
    { "credit": "Codex", "role": "integration assistance" }
  ]
}

import-manifest.json example:

{
  "schemaVersion": "rifflet.import-review/0.1",
  "importBatchId": "batch_223e4567-e89b-42d3-a456-426614174001",
  "creatorId": "creator_123e4567-e89b-42d3-a456-426614174000",
  "apps": [
    {
      "sourceAppId": "external-example-app",
      "appSlug": "example-app",
      "appVersion": "1.0.0",
      "source": {
        "location": "https://example.invalid/creator/source",
        "ref": "replace-with-immutable-source-ref-or-archive-checksum"
      },
      "packagePath": "apps/example-app"
    }
  ]
}

The maintainer confirms creator authority, unique source-app mapping, package contents and exact release identity before integration. Record the submitting person or organization separately from the original creator when they differ; a batch containing multiple creators needs approved per-app creator IDs. Importing an app does not transfer authorship. Existing model-name creator labels are historical credits, not evidence for assigning human ownership; do not automatically backfill creator IDs from those labels. Freeze historical ownership with each app release; do not silently reattribute older releases when an alias or contributor list changes. Riffs retain immediate parent and original creator attribution alongside the child creator. Join approved creator attribution to exact app-release identities for authorized aggregate analytics; do not substitute private visitor identities or publish raw private analytics. A stable ID alone does not grant access to reports or source.

Curated identity/ownership confirmation and batch review remain maintainer operations. The provisional submission API now supports self-service pseudonymous creator registration: a privately persisted random key binds to a platform-assigned creator_<UUID>, with an unverified public label. Reuse that key across single-package app submissions and later immutable revisions. Accepted packages are immediately public provisional content, without a human review gate; they do not change Main. No named creator-account registry, verified ownership API, bulk batch transport or live creator dashboard is implemented. Agents can iterate the single-package workflow over all apps and retain per-app receipts. Future bulk onboarding should validate creator authority, source IDs/refs, licenses and capabilities, preserve exact ownership/lineage, and expose only separately authorized aggregate views. Private reviewed packages or named repository collaboration remain optional paths for curated integration, not provisional publication requirements.

10. Measurement and per-browser/per-app data

Every new or revised app supplies analytics.json using rifflet.analytics/0.2: an honest first meaningful action, intermediate milestones, a final completion or null for an open-ended experience, attempt boundaries and bounded interactions. Emit app facts through the common runtime. The host supplies lifecycle, identity, release, transport, passive device context and collection policy; no independent trackers.

The active invited-test deployment can collect automatically under its authorized internal policy. Ordinary developer builds remain off. The event envelope is rifflet.event/0.2; batches use rifflet.analytics-batch/0.2. ANALYTICS.md and executable validators define precise fields, limits, retention and meanings. Collection failure must never prevent play. Do not upload typed answers, free text, full URLs/query strings, DOM dumps, raw exception messages, camera images, audio or sensor samples.

Private operator reporting can relate observed nickname history, browser/session/view/attempt IDs, app/release, actions, passive device context and trusted request IP. This is not a count of verified people and not a public creator feed. Never join across devices, fingerprint visitors or treat aliases/IPs as human identity. Signed test contexts and bounded traffic signals separate tests/agents/likely visitors while retaining unknown; classification is not certainty. All automated browser checks enter through test_agent=1 before the first instrumented navigation, in a dedicated context.

Measure passive feed exposure, same-tab app opening, standalone readiness and first gameplay input separately. Initialization, a visible canvas, and auto-animation must not emit a meaningful start. Use versioned validated telemetry before adding event names; unsupported metrics remain unavailable. Report encounters → opens → meaningful tries → milestones → honest finishes, active time, app breadth and return behavior with definitions, denominators, coverage and uncertainty. Do not infer dislike from absence or completion from an intermediate goal. Public feedback and private measurements stay separate; agents receive the minimum authorized evidence needed for a review, not raw private records by default.

11. Hosting, storage and security

Current hosting uses static Pages/CDN output with narrow API services, D1 for reviewed shared records, and R2/isolated runtime for supported immutable generated artifacts. App code must be portable: relative asset paths, no hard-coded temporary host in game logic, explicit external-service adapters. No arbitrary credential-bearing backend inside a static bundle.

Validate input at every trust boundary, enforce payload/rate/capacity limits and idempotent writes, and keep capability tokens out of URLs/logs/public catalogs. Use TLS, reviewed CSP/Permissions-Policy, safe DOM text insertion, dependency provenance and explicit network destinations. Current common-host curated code is trusted reviewed source; do not claim it is isolated from same-origin storage. New untrusted content requires the isolated runner and review, not simply another folder.

Admin HTML and assets may be publicly fetched even when private APIs require login. Therefore no builder bearer credentials, private source archives or private analytics belong in static admin assets or public /create files. Grant repository access privately and revoke it independently of the shared operator login. Use separate agent/API/Git identities with least scope. New spend, subscriptions and permission expansion require owner approval.

12. Build, review and release acceptance

Validate metadata, cover asset and source provenance; run relevant behavior/contract tests and the repository suite. Verify public output excludes private files. Freeze source before a release build, version module assets, preserve accurate full commit pins, inspect dist, and retain an explicit rollback target. An editorial release changes metadata/art, not app executable source identity. Frozen artifact catalogs and registry pins must remain internally consistent; do not run a full rebuild over retained production artifacts without the coordinated rebaseline procedure.

Check desktop and mobile layouts, first action, optional permissions and denial, offline/unavailable services, storage denial, accessible controls, sharing, feedback, lifecycle and any modality-specific behavior. Browser simulation and synthetic sensor input must be labeled; claim physical phone/iOS tests only with actual evidence. Deploy through the designated release owner with existing bindings intact, then verify live bytes, routes, headers, assets and page errors. A successful upload is not proof the custom domain works.

13. Evolution and maintenance

Version 2.3.0 adopts full-bleed illustrated covers, transparent overlay controls, accessible entry without a visible CTA, shared family artwork and lifecycle-controlled animation with persistent pause. It preserves the 2.1 same-tab standalone entry and selection-preserving return. Keep the immutable 1.0.0, 2.0.0 and 2.1.0 snapshots; do not retroactively certify or rewrite retained artifacts. App metadata declares the target spec; a conformance review records the tested subset and outstanding exceptions.

Each release review asks: what changed, what evidence supports it, what did users actually do, what remains uncertain, and what reusable expectation belongs here? Add adopted requirements to this spec, its snapshot/changelog, validators and onboarding together. Keep future ideas separate from approved work. See CONTENT_ONBOARDING.md for contribution steps and RIFF_PLATFORM_MERGE_PLAN.md for proposed compatibility with the original Riff platform.

Ready to play UI: invitation, social actions and return

Ready to play UI names the animated passive illustrated cover invitation. The illustration may express the real key mechanic without matching the first app frame. Keep a named, keyboard-accessible entry link, with no visible entry button or footer CTA. Cover motion follows active/visible/modal, reduced-motion and persistent Menu pause controls; any separate standalone welcome treatment remains cosmetic and must not delay or replace its first action. Never synthesize a Start, change a score/answer, autoplay sound, request sensors or run a gameplay clock. The actual app remains usable without decorative motion.

The host overlays Heart, Share and profile over the artwork inside the simulated phone, with no dedicated rail. Heart saves a browser-local favorite, not a public popularity count. Profile shows the device-local nickname and creator attribution, not an authenticated social account. Share exposes a selectable standalone playable link before native/clipboard operations and handles denial, cancellation and stale completions. Forwarding remains distinct from making a riff, and share-sheet return is not recipient delivery. Social controls must not trigger app launch as a side effect.

Acceptance must verify both halves of the flow: passive preview with no gameplay/audio/sensor effects, then a same-tab standalone launch with working first action and preserved warnings. Verify browser Back and Menu → Back to feed from multiple selected cards and filters, including denied storage and missing history. At 375/390/430 px and desktop, check full-bleed composition and essential-subject safety, transparent header/social overlays inside the phone, no visible CTA/footer CTA, arrows outside on desktop, swipe navigation without visible mobile arrows, named entry-link keyboard access and narrow/short layouts. Check all 40 published editions, same-family variant identity and same-art Lab exports. Verify inactive/hidden/modal covers pause, reduced motion is static and Menu → Pause cover animation persists across navigation and reload, including denied storage. Inspect network and runtime evidence for zero app code/iframes/media/sensor work before entry; decorative motion must emit no fake gameplay events. Record actual viewport and physical-device evidence separately. Do not claim this contract is deployed until the release owner records verification.

Mobile feed entry, visual continuity and the Rifflet menu — RCF 2.3

Treat every feed entry as a mobile experience. On desktop, open the exact pinned app inside a 430 CSS-pixel viewport in the same tab. On phone-sized windows (up to 700px), retain the top-level full-screen app so camera, motion and native controls keep their normal document context. The desktop host preserves all revision/content parameters and delegates camera only for the reviewed Bone Lens and Skeleton Lens routes, and microphone only for Song Coach; it cannot widen the response permission policy. Return to the selected feed card through browser Back or the common menu. This is responsive viewport hosting, not device emulation. No app code or frame loads before explicit cover entry.

The cover should feel like the first scene of the same experience. Reuse reviewed in-app photographs, painted environments, characters, game objects, interface materials and palettes wherever possible. A photographic game should not become an unrelated flat cartoon; a pixel arcade should retain its pixel language. Compose these into an inviting portrait rather than shrinking a whole page with tiny controls. Small editorial motion should explain the mechanic: water levels, a drifting ferry, a pulsing memory pad, rain reaching chimes, a breathing rhythm. No unexpected sound, scare, permission prompt, fabricated result or live gameplay before entry. Preserve source/license attribution for reused assets and record any static component captures. Load the static cover immediately and reveal the animated composition only after its image assets are ready; failed images retain the static fallback. Honor reduced motion, explicit pause, hidden-page pause and one-active-cover limits.

Provide one easy-to-reach, at least 44px, keyboard-accessible in-app hamburger control with recognizable Riff star branding. Its accessible name covers discovery, making a riff and feedback. The menu prominently contains Find your next Rifflet, ✳ Make a riff, Suggest a riff or feature, and Give feedback / report a problem. Preserve existing app-specific editor behavior, public-feedback consent, exact-release context, drafts, receipts and retry semantics. Do not replace a working editor with a copied prompt. Keep instructions, settings and About in the same menu. Opening it must preserve play state and pause/resume through the common lifecycle. Discovery navigates the outer mobile host, not a nested feed inside the phone. Test both ordinary app visits and feed entry.

The current release adds this navigation through a reviewed host adapter on registered app HTML routes; immutable app artifacts and existing feedback handlers are retained. Arbitrary third-party code must still follow the separate isolation and capability review; a menu wrapper does not confer trust.

The feed’s overlaid action stack also includes an outlined pencil with a named 44px target. This is deliberate entry: it opens the selected exact Rifflet in the mobile host with its common riff/settings/feedback menu already open. The cover itself opens ordinary play. Do not auto-open either experience or settings during passive browsing. Preserve the real app editor and feedback flow; never replace them with a copied prompt.

15. Provisional contributor upload pilot

RCF 2.4.0 adds the rifflet.upload/1 envelope and invitation-based /api/contributions API described in UPLOAD_ONBOARDING.md. A creator can submit editable source plus a prebuilt browser bundle without GitHub. Valid packages become public provisional previews with creator IDs, immutable digests and optional immediate-parent submissions. They do not replace Main, gain conformance certification or cause automatic Git commits. Runtime preview permissions are narrower than the production framework: no external network, persistent storage or common hosted-service bridge; declared motion/fullscreen is available under section Provisional device capabilities. A declared RCF version is a target, not evidence of implementation. Preserve these distinctions in receipts, onboarding and reporting. Main review/promotion remains a separate future integration step; technical validation always precedes serving executable bytes.

2.4.1 clarification: public provisional transport

The creator entry point is https://riff.marantz.net/ → /create → /create/agent.md. The only required creator prompt can be “submit our apps/games/content to riff.marantz.net.” Public discovery provides a machine manifest, current references, exact package schema, local pack/preview/submit helpers and receipt APIs. No platform Git or invitation is needed for bounded self-service registration. Accepted uploads are published by default as immutable public provisional editions. Main remains a separate curated default; this patch grants no automatic promotion.

Provisional runtime capabilities are a specific subset of the full RCF integration target: packaged client HTML/JS/Canvas/media/WASM runs in an opaque sandbox; arbitrary external network, persistent iframe storage, nested frames, and general privileged hosted platform services are unavailable; the declared realtime-room service is a bounded parent-bridge exception described below; declared device capabilities follow the 2.5.0 policy below. Implement useful fallbacks and record explicit conformance exceptions. Structural package validation is not certification of product quality or complete RCF behavior. Source remains creator-key scoped; builds and metadata are public. Named accounts, verified ownership, bulk batch transport, creator dashboards and complete provisional host-service integration remain future work. Older 2.4.0 and earlier immutable snapshots remain historical contracts.

Provisional device capabilities — RCF 2.5.0

Declare every required capability in the upload manifest permissions array. Exact allowed names: accelerometer, gyroscope, camera, microphone, geolocation, vibrate, fullscreen. Unknown names and duplicates are rejected with invalid_permissions. An empty array grants no policy-controlled device capabilities. No upload receives platform approval merely by declaring a capability.

Example pinball declaration: "permissions": ["accelerometer", "gyroscope"]. Offer an in-game Enable tilt action and retain swipe/button nudges. Local preview.mjs applies the same manifest-derived response header and presents the same default-enabled starter dialog before mounting the iframe. Loopback is a secure context in supported desktop browsers, but it does not prove physical iOS or Android compatibility.

Opaque sandbox origins cannot match (self), so the supported declared directives use accelerometer=*, gyroscope=* or fullscreen=*; undeclared and review-restricted directives stay (). The player pins frame-src to the exact submission prefix to prevent grants following navigation to another site or upload. No nested frames, external networking, platform cookies or persistent storage are granted. Direct runtime URLs have the same declaration-derived policy but bypass the preview's introductory dialog; share the platform preview URL. Permissions-Policy is not a universal switch for every browser API.

Reference: https://www.w3.org/TR/permissions-policy/ and https://www.w3.org/TR/orientation-event/ .

Provisional player and shared runtime — RCF 2.6.0

The hosted /create/preview?id=<submissionId> and downloaded preview.mjs use one viewport sizing contract: full available width and remaining dynamic viewport height, no fixed 304×154 iframe and no platform-imposed portrait letterboxing. Metadata and capability controls live in a compact details menu. Rifflets must still resize their own canvas/layout inside the frame. No orientation/aspect-ratio manifest field is introduced in this version.

The preview copies its entire URL fragment to the app iframe on first load, capability restart and subsequent parent hashchange. It does not parse or rewrite app-owned hash formats. A hash change is same-document navigation: apps should handle hashchange if they need to reapply state without reload. Query strings remain forbidden on artifact assets; do not put riff state there.

Import the current pinned adapter without copying it into the package:

import { createRuntime } from '/shared/provisional/1.2.0/runtime.js';
const runtime = createRuntime({ id: 'haunted-pinball', version: '1.0.0',
  requestRiff: () => openYourEditor() });
await runtime.ready; // Catch restore errors and explain them before play.
const state = runtime.readState({ seed: 1 }, { seed: value => /^[1-9]$/.test(value) });
// In an explicit user Share action:
await runtime.share({ seed: state.seed });

This adapter creates /s/<appId>/<10-character-code> links bound to the immutable submission. Settings are public and expire within 30 days. shareURL is asynchronous; await it before displaying a URL. The trusted player performs the bounded request for the opaque app. shareRoom(room.code) creates a visible-code room invitation. See [Compact public sharing](docs/sharing/SHARE_LINKS.md) for limits and typed payloads. Local creator preview keeps play, sound and local state functional; hosted share creation requires the published player.

The older /shared/runtime.js alias intentionally retains adapter 1.0.1; it is not the recommended new import. Frozen 1.0.1, its sound module, and 1.1.0 Rooms modules remain byte-identical and usable. The full curated runtime is not injected into unreviewed content; only the exact allowed modules are permitted by CSP and opaque-module CORS.

Legacy custom hash codecs and #rcf1= links remain readable through the historical adapter and transparent preview fragment forwarding. Existing packages are immutable: authors submit a new child revision to adopt compact sharing. Do not advertise that a wrapper update rewrote an old app-authored share button. Do not wrap arbitrary encoded blobs in the new state field; adapt the actual public scalar fields and validate them on restore. A share-sheet return is not recipient delivery, and cancellation must not copy a link later.

API / serviceProvisional adapter 1.2.0Curated integration
ready, readState, share, shareURLValidated public scalar settings; compact exact-submission links; await restoration/preparation; manual copy fallbackRegistered app/source/content sharing contract
remixCalls supplied requestRiff local editor; otherwise returns method: unavailableReviewed editor/host feedback workflow
emitBounded 200-event in-memory log and local event; returns hosted: falseValidated hosted analytics only when integrated/authorized
save, restoreIn-memory for this runtime instance only; persisted: false; lost on reloadReviewed persistent storage and state contract
exportFeedbackSelectable local JSON; nothing submittedPublic feedback consent/receipt workflow
sound, disposeLocal synthesized sound; disposal closes audio and clears memoryShared lifecycle and app-owned resource cleanup
Source lineageUpload a child with parentSubmissionId using the same creator keyReviewed family/version provenance and promotion
Content-riff lineage, accounts, hosted scores, feed placementNot implemented by this adapter; no fabricated acknowledgementSeparate reviewed service adapters

Inspect runtime.capabilities for the exact service modes. This is an API-compatible provisional subset, not automatic RCF certification or a hosted analytics bridge. Promotion still requires reviewing permissions, service authorization, state/URL migration and actual event semantics. Historical submitted packages and pinned modules remain unchanged.

Submission revisions and admin approval — RCF 2.7.0

A submission is an immutable source/build snapshot. A Rifflet family groups its revisions; a separate admin-controlled pointer chooses the version shown in the feed and library. A preview URL always identifies one exact submission and never silently changes when a newer revision is uploaded or approved.

Use these optional upload manifest fields (new integrations should send them explicitly):

{
  "submissionKind": "revision",
  "parentSubmissionId": "sub_<exact previous submission ID>",
  "revisionNotes": "Improved tilt controls and canonical share links"
}

Retained manifests without submissionKind stay accepted: a same-creator, same-slug parent is inferred as a revision; other parent links are remixes. Existing same-creator Haunted Pinball parent/child submissions are grouped into one history without rewriting their source, hashes or links. Unlinked uploads are never merged based on title alone. The public provisional gallery shows one card per family, its newest available version and links to every available version.

Submit → preview → revise → approve → discover

  1. Upload with the existing creator key. Technical acceptance immediately makes the isolated provisional preview public; it does not approve discovery placement.
  2. Save the receipt. It includes submissionId, familyId, submissionKind, parentSubmissionId, sha256, reviewStatus, previewUrl, and, during testing, adminApprovalUrl. The CLI returns expanded absolute links. Return both links to the human requester. These URLs contain no key and confer no approval authority.
  3. Play the preview. Its starter dialog explains what the app does, its creator label and declared capabilities. Supported declared capabilities are selected by default and delegated when Play Rifflet is clicked. Players can deselect them; browser prompts still apply. No app iframe runs behind the initial dialog. The menu can disable/restart capabilities. Local preview uses the same entry and sizing contract. Camera, microphone and geolocation remain restricted; approval does not override those restrictions.
  4. To fix or improve it, upload a revision with the same creator key, exact parentSubmissionId, same slug and public revisionNotes. Keep the prior receipt and code snapshot.
  5. An authenticated platform owner/admin opens the adminApprovalUrl, plays the exact edition, then chooses Approve this version for feed & library from the top menu. If not signed in, use its Admin sign-in link and return to the preview. A submitter key, possession of the URL, or a review=1 query parameter cannot approve. Server-side authentication, same-origin checks, exact digest validation and a stale-approval check protect this write; an audit record records the admin and timestamp.
  6. Approval lists that exact version in the feed and library. Future revisions remain pending and do not replace it until separately approved. Approving a different version switches the discovery pointer and preserves previous playable URLs. Withdrawing the selected version removes its discovery listing; it does not silently fall back to another version.

Approved uploaded apps remain in the isolated artifact runtime. Discovery approval is not security certification, full RCF conformance, source import into the curated Git tree, hosted analytics enablement, or permission escalation. Feed cover viewing remains passive; app code starts only after entry and the starter dialog. Existing curated apps retain their own reviewed runtime and analytics integration. Uploaded apps do not acquire fabricated scores or usage data.

Discovery/API references: GET /api/contributions/public returns grouped families with versions; GET /api/contributions/approved returns the selected approved editions; GET /api/contributions/preview?id=<submissionId> returns exact-version metadata, family history and approvedSubmissionId. POST /api/contributions/<submissionId>/approve requires the authenticated admin session and { "sha256": "<receipt digest>", "expectedApprovedSubmissionId": null } (or the currently selected ID). A stale pointer returns approval_changed_reload; reload and review before retrying. Source downloads and withdrawal remain creator-key authenticated. Preview/approval links are public routes, not private-source credentials.

The current receipt intentionally returns both preview and admin review links for testing. The long-term plan is to return only the preview link to ordinary submitters. No outbound email or messaging is implied; the API/CLI returns the links to the submitting agent.

Realtime party rooms — RCF 2.8.0

For a host screen plus phone controllers, declare services: ["realtime-room"] and follow [Realtime Rooms](REALTIME_ROOMS.md). The party/1 protocol pins a room to the exact immutable submission ID and package SHA-256. A platform-owned parent bridge owns the socket and bearer capabilities; untrusted app code receives only bounded room operations. Keep the artifact frame opaque and external networking blocked. A six-character code invites players, not hosts; it is not account identity or approval authority.

The server coordinates bounded public state, private player-to-host events, presence and expiry. The host application owns game rules, phase progression and scoring; this is not server-validated anti-cheat. Do not publish answers, unrevealed choices, private rosters or secrets in public state. Provide disconnect, quota, expiry and offline fallback UI. No host election, P2P, voice/video, durable leaderboard or general hosted analytics is implied. Preserve the RCF 2.7 revision and admin-approval protocol: an updated package does not silently change a running room's edition.

Hosts must offer room moderation: lock new joins and revoke a member through the room service. Revoked credentials cannot resume; the 16 admitted-member lifetime slots are not recycled. Per-member events are limited to 20/minute, eight pending and 150/lifetime, with 700 aggregate member events and a separate 300-command host reserve over the room lifetime. Host commands have a separate 120/minute rate; host close is exempt from these command limits. The 100-room lifetime pilot admission ceiling requires explicit operator review before expansion; it is not a provider billing cap. The host application remains responsible for scoring and duplicate-event handling, and downloaded answer banks remain inspectable. Do not market this as verified competitive scoring or secret question storage.

Compact public sharing — RCF 2.9

New hosted shares use the app-scoped ephemeral protocol in [Compact public sharing](docs/sharing/SHARE_LINKS.md). Ordinary app entry remains a plain app URL; saved settings, checkpoints, exact editions and immutable content riffs use /s/<appId>/<10-character-code>. Room/group invitations retain their visible public invite code under /invite/. Leaderboard links reference a challenge and never post a score on opening. These codes are public locators, not access credentials.

Use typed, server-validated bounded data: a 16 KiB request ceiling, 12 KiB canonical full-riff ceiling, and 2 KiB/16 scalar settings limit. No arbitrary base64/data-URL blobs, private credentials, scores/names, or redirect URLs. Fixed retention is at most 30 days and cannot extend an invite's backing lifetime. Preserve source/parent identity and exact-edition scope. Server-side deduplication, atomic record/byte quotas and scoped rate limits bound this pilot; they do not guarantee a billing cap.

Resolve before reading initial state, show preparation and a selectable final link before native sharing, cancel stale operations, and offer an honest retry on failure. Never silently replace failed compact creation with a huge link or a failed restore with an apparently successful default. Keep the address compact. Read legacy formats without rewriting historical modules or packages.

The new provisional runtime /shared/provisional/1.2.0/runtime.js adds compact sharing through the trusted player. createRuntime() remains synchronous; await runtime.ready before readState. shareURL is now asynchronous, and share prepares a hosted link. shareRoom(room.code) produces a visible-code invitation to the exact submitted edition. Hosted state remains small public primitives; uploaded apps validate their semantics on restore. Publication/withdrawal and room state are rechecked by the service. Host/member tokens remain only in trusted parent storage. Frozen adapters 1.0.1 and 1.1.0 remain available unchanged; the old /shared/runtime.js alias retains its historical behavior. Authors opt into the new pinned adapter and submit an immutable revision through the existing protocol.

Opt-in premium services — RCF 2.10

The premium-v1 profile adds larger streamed uploads, private player saves, count-only metrics, self-reported leaderboards and host-independent sealed phase rooms. See [Premium platform contract](docs/premium/PREMIUM_PLATFORM.md) and [Upload transport reference](UPLOAD_ONBOARDING.md) for precise limits and transport. Service declarations do not confer entitlement. A grant binds the creator, slug and app family; the deployment-wide enable flag and grant expiry are enforced on the server. This revision is an implementation contract, not evidence of production activation.

Use the separately pinned 1.3.0 runtime/room modules for these capabilities. Frozen 1.0.1, 1.1.0 and 1.2.0 modules remain unchanged. Premium metadata uses validated playerServices and roomPlan declarations; do not invent extra fields. Applications requesting these services use upload/2. Standard applications retain upload/1 and the 1.2.0 sharing runtime.

Raw preference answers never enter room state, events, analytics, logs or share links. Only declared bounded gameplay decisions are eligible for sealed room publication. The app owns deterministic defaults and client simulation. Counts contain declared metric IDs and unit increments only. Save slots and settings are owner-only through the application API, absent from operator views, analytics and application logs, and support player export and erasure. They are not end-to-end encrypted; infrastructure administrators retain technical access. Save retention is 90 days of inactivity and must be disclosed with an export action. Scores are explicitly self-reported, not verified.

Premium publication stays provisional by default, and Main promotion remains an explicit administrator decision. Premium activation does not buy a domain, subscribe to a service, renew creator credentials, or authorize paid AI generation.

16. Native previews and current feed review

For sponsored product and legacy films, apply the [film creator guide](docs/creative/SPONSORED_FILM_GUIDE.md) alongside the feed contract. It adds authoring guidance without changing the executable schema or asserting that historical artwork is product-verified.

A current-feed preview may use a host-rendered, script-free inert native surface built from the approved ready-state DOM and local assets. It must retain the real composition and independently moving scene parts, carry a restrictive CSP, omit app scripts, network, storage, timers, gameplay state and event handlers, and preserve a static fallback. Host-owned WAAPI may animate meaningful existing scene elements only; it cannot imitate input, advance a game, change score/results, request consent, or play audio. Reduced motion is static and host lifecycle pauses/disposes active preview motion.

This requirement combines with the 2.10.0 platform services: rooms, managed images, uploads, player services and all service capability/consent/retention restrictions remain opt-in and separately declared. Native preview rendering does not grant those capabilities or change a published edition’s identity.

Ordinary play contract — explicit product decision, October 9, 2026

The root is the complete Lab. A normal Lab/feed app link opens the published Main experience immediately after deliberate entry, without asking the visitor to pick a revision or press a second platform Open button. An explicit personal/history link may pin another authorized edition. Comparison and provenance remain opt-in through Versions/menu/Admin; isolation and authorization still apply behind the scenes. Do not silently select an existing private branch or automatically activate a pending build on ordinary entry. Changing these defaults requires an explicit product decision from Dan, with updated regression tests.

On mobile, camera/microphone/motion experiences must run in a top-level context when embedding prevents browser permission or sensor delivery. Navigation never grants a permission: request it from the game's own deliberate tap and keep touch/denied fallbacks. Verify actual yaw changes with simulated orientation events, not just a permission promise; real iPhone/Safari validation remains separate.

Every release must verify the full reachable HTML/CSS/ES-module graph from Lab, feed and player, including hashed imports. A successful upload or unchanged file hashes do not prove graph completeness. Exercise a cold browser load and verify cards plus the first playable screen.

Builder model provenance — RCF 2.13

For every newly prepared app submission, remix or revision, request and declare the model identity used to build that revision. Use the bounded buildProvenance upload-manifest field described in [Builder provenance](docs/agents/BUILDER_PROVENANCE.md), including explicit unknown identity when the builder's host does not disclose it. This compatible addition applies to upload/1 and upload/2 without changing immutable SDK pins or the ordinary-play contract above. Packages declaring RCF 2.13 or later require this field; earlier RCF packages remain accepted with missing attribution flagged.

Normalize disclosed identifiers only through the platform's versioned model registry. Keep unrecognized identifiers distinct; do not guess a version from a provider, product name, writing style or creator label. Record attribution per immutable submission, not solely on the app family or creator account, because builders and models can change between revisions. Historical missing data remains missing.

Builder attribution is self-reported, not provider attestation, a model-quality rating or evidence that a model generated every byte. Preserve creator ownership, source lineage, licenses and app version independently. Development models and runtime generation models are different records. Attribution must not contain prompts, transcripts, credentials, private account identifiers or user data.

RCF 2.14.0 — optional education contract

Upload/2 adds the strict, versioned education block and exact-granted private-groups service. Existing RCF versions retain their prior shapes. Read [Education platform contract](docs/education/EDUCATION_PLATFORM.md) for collection registry, private boards, optional capabilities, offline artifact and testing limits. Module availability does not activate a service; query runtime capabilities for the exact submission. Education metadata is self-reported, not certification.

RCF 2.15.0 — consent-based education sharing

The optional education contract may declare publicChallenges, a list of one to four {id,min,max} integer score schemas. These are explicitly public targets, independent of private board definitions; declaration alone grants no access. See [Education sharing](docs/education/SHARING_PROPOSALS.md) for the complete wire contract, immutable SDK 1.11.0, operator review and release boundary.

The parent consumes #group=CODE before preview requests, telemetry or iframe construction. Opening a link never joins a private group. Storage consent and a separate trusted Join action are required. Group membership remains collection scoped across explicitly authorized families, not arbitrary apps.

rifflet.public-challenge/1 uses a dedicated, exclusive payload discriminator within existing compact provisional share storage. It does not relax generic score-field rejection. The server pins the accepted revision digest, rechecks premium entitlement and exact operator review on creation and resolution, and caps retention to the existing share TTL and grant expirations. The parent shows the exact self-reported target before consent. Private group values and tokens must never be copied into public challenge targets. Schemas cannot prove data provenance; combined private-group/public-target apps require explicit risk review.

Source availability does not mean runtime activation: education and public challenge flags remain disabled until separately reviewed exact grants exist.

Integrate with feed and starting behavior

Opt-in local two-app pilot extension, October 11, 2026; not a new production RCF release. Follow [the shared Live Feed component contract](docs/feed-mechanics/LIVE_FEED_COMPONENTS.md) for rifflet.feed/1: usable first action, explicit Start/permission/paid boundaries, independently combined pause reasons, bounded private checkpoints, typed host-managed menus and sandbox-preserving iframe transport. Keep the app's standalone menu until the host handshake succeeds. Host Back/Next remain available even if the app fails or ignores lifecycle messages.

Immediate in-feed play is enabled only for the adapted pilot route. It does not change the existing cover-only /feed, uploaded-artifact isolation, immutable SDK import allowlist, exact edition IDs, approved discovery, identity/ownership, public sharing, private-player-data or room deadlines. Future migration and server-backed recovery are documented separately; no universal ability to freeze or serialize arbitrary running code is claimed.

Feed-test integration / starting behavior experiment

See [Feed Test](feed-prototype/README.md) and [focus and yield hooks](docs/feed-mechanics/FEED_TEST_HOOKS.md). The opt-in prototype distinguishes immediate direct input from swipe-owned preview entry, and logical app focus from DOM/OS focus. Apps checkpoint before safe breaks/round yield, cancel held input on pause, and restore a usable ongoing or completed presentation. Host navigation remains available independently of app responses. These experimental components do not revise frozen RCF SDK pins or grant social/account/edit authority.

Feed Test October 12 refinement: minor milestones retain focus; only explicit major completion or user pause can open glass. Legacy unspecified yields are minor. See the [release granularity contract](docs/feed-mechanics/FEED_TEST_HOOKS.md#major-and-minor-releases--october-12-correction). Host lifecycle pauses must never echo back as app-initiated major releases.

Creator onboarding on thatscool.app — October 12, 2026

The default creator flow starts from https://thatscool.app: the agent prepares and submits public provisional apps before human sign-in. The bounded submit.mjs batch --name flow always returns a private personalized preview/claim link for a new unclaimed creator. An optional upfront email triggers the provisional welcome automatically but never assigns ownership. The human sees the apps first, then explicitly signs in with verified Google and claims them. Before claim, only newly marked registration keys may manage their own uploads; afterward, the owning session or separate explicit expiring builder delegation is required. Existing owners, legacy invitation boundaries and premium grants are preserved. /create/onboard remains the advanced signed-in setup. Consult /api/creator-account/capabilities for live registration, claim and email availability.

After posting the requested batch, builders call /api/creator-account/welcome with exact owned public-provisional submission IDs. The welcome contains public preview/share links and an account-protected creator analytics link, never credentials. Provider acceptance is not proof of inbox delivery. Temporary self-approval, when enabled, requires the verified owning account and the exact submission digest and prior approval pointer; it affects uploaded discovery placement only, preserving curated Main, history and restricted service permissions.

Use the platform profile, activity and sharing contracts described in docs/onboarding/SOCIAL_PORTING.md. Favorites, social events and recipient delivery are different measures. Do not invent creator social totals where the host does not collect attributable data. Automated sessions use test_agent=1; never place credential files in source/build packages or copied public prompts.

Agent iteration publication counts — Dan, October 14, 2026

Publication-count caps can be temporarily paused through the operator flag RIFF_SUBMISSION_CAPS_PAUSED=true; inspect /api/contributions/submission-policy for effective state. Preserve existing creators, immutable revision lineage, ownership/delegation, expiry, package validation, byte budgets, registration/upload rate controls, premium entitlement and paid model budgets. The pause never promotes provisional content. See AGENT_SUBMISSION.md for affected counters and rollback.