# Block builder contract

The rules every StillMade Block follows, whether written by a person, a coding agent, StillMade AI or a repository import.

Contract `stillmade.block-builder@1.0.0`. Humans, coding agents, imported source, GitHub repositories and generated code all adapt to the Block platform; anything that becomes a StillMade Block obeys this contract.

## Source of truth

Treat the current SDK (`stillmade.block.json` manifest, `packages/block-sdk` contracts, validators, examples and published docs) as the source of truth. Before writing code, read the manifest contract, at least two working first-party Blocks closest to the request, the runtime you will use, the typed port and resolver contracts, and the shared-interface contract when you need a custom view. Do not invent runtimes, operations, permissions, ports, host services or bridge calls. If the documentation and the validator disagree, the validator wins and the mismatch should be reported.

## Reuse first

Before building, check what already exists (`find_existing_blocks` over MCP, or the Marketplace and your library) by the inputs, results and operations the goal needs. Use an installed or reviewed Block that already does it; remix a close one so its license, notices and creator credit stay attached; chain existing Blocks and build only the missing step. Never republish another creator's source without remix lineage: public copies are refused. Build a new Block when nothing fits or the person asks for their own version, and say which similar Blocks you considered.

## Define the capability first

Translate the request into an explicit capability contract before implementation: what the Block does; each input with its type, meaning and whether it is required (prefer optional settings with safe defaults over extra required questions); each output with its type and meaning; persistent state; project context it reads; permissions; UI; long-running behavior; expected failures; cancellation; determinism; and whether it needs hosted models, media, desktop tools or shared editing. Keep the public interface as small and general as possible and keep implementation details out of it.

## Blocks compose

A Block is not a mini-app. Its outputs become other Blocks' inputs and its inputs come from other Blocks, project context or the user. Use the typed port system: shared StillMade types (`image`, `video`, `audio`, `asset`, `script`, `shot[]`, ...), a `semantic` meaning on every port, `required` and a short `description`. Mark exactly one primary input and one primary output so Project Type Steps connect automatically, and use `kind: "task"`: a `workspace` Block owns a project document and is never connected automatically. Inputs marked `context` read the project and cannot receive a connection from another Step. Output reusable StillMade values: media references (`{kind,assetId,versionId,url}`) and structured production values with `schemaVersion: 1`, preserving provenance, versions and relationships downstream Blocks need. Never make ordinary users wire JSON, copy IDs, understand internal paths or translate one Block's output into another: `createUniversalResolver()` (the same engine as the Project Type composer) resolves inputs from compatible upstream outputs, permitted project context and, for optional non-context inputs, `StillMade.requestFromUser`.

## Use platform primitives

Prefer, in order: an SDK capability; a documented host capability (the hosted operations are text.generate, audio.speech, audio.transcribe, audio.music, audio.sfx, image.generate, video.generate, image.describe, media.analyze, web.fetch, web.research, timeline.propose, workspace.propose, connection.execute; connected third-party apps go through `connection.execute`); an approved dependency the SDK already bundles; small Block-local code. Do not duplicate what StillMade provides. A Block never contains its own asset system, file picker, authentication, project-context system, input resolver, Block connection system, credit system, secrets store, permission system, progress system, multiplayer system, persistence system, model-provider abstraction or execution protocol.

## No hidden workarounds

Never make a Block look functional through hard-coded local paths, developer-machine dependencies, undocumented environment variables, admin or database access, hidden core modifications, direct database writes, fake or mocked production results, hand-prepared test assets that hide incompatibilities, credentials in code, or assumptions about one user's machine. The finished Block must work through the same interfaces available to any installed Block; `permissions.network`, `permissions.filesystem` and `permissions.secrets` must be empty arrays.

## Interface

If the capability needs UI, use the Block UI SDK: generated `manifest.ui` controls first; a sandboxed custom view (`view.html`/`view.css`/`view.js`) only when the core interaction genuinely needs one. Expose the capability, not the implementation: meaningful controls, sensible defaults, preview, direct manipulation, progress, clear errors and visible outputs. Do not show raw JSON, internal IDs, filesystem details, provider terminology or developer settings unless the Block is itself a developer tool. Follow the host appearance (Light, Dark and White) and the mobile layout contract: one interface for every screen size that fits a 320 CSS-pixel phone without horizontal scrolling.

## Shared editing

Projects are multiplayer. A custom view subscribes through `StillMade.onShared(callback)` and publishes every editable field: give static controls a unique `id` and `data-stillmade-share="field"` (the host owns their synchronization), use `StillMade.bindShared(field, elementId)` for controls created dynamically, and `StillMade.updateShared(values, before, options)` for structured values; private variables and DOM values are not shared. Mark a truly device-only control (playback, hover, device selection) with `data-stillmade-local`. Observe shared outputs as well as state, honor `canEdit` in every mutating callback (read-only participants cannot write), never run, click or call a paid integration in response to a remote update, and preserve conflicting drafts. Shared background work uses `sharedWork` with the `work.shared` permission.

## External services

Reach external services only through StillMade's hosted operations, the connections service (`connection.execute`), or, in a module Block, `stillmade.net.fetch` to origins declared in `permissions.network` (with the key each needs) and `stillmade.connections.call` to adapters or catalog apps in `permissions.connections`; never put keys in code, inputs or headers: StillMade adds each person's own key on its servers. The host owns the model and provider choice, BYOK keys, StillMade credits, spending consent, authorization, token refresh, rate limits, retries and provider errors; a Block never receives a secret and never names a provider unless it must. Treat provider unavailability, timeout, cancellation and partial failure as typed outcomes the Block reports, not crashes. Generated images, video, voices, music and sound effects come from hosted operations or the `media.generate` action, paid with the user's StillMade credits at StillMade's prices; for structured model output declare a `json` output with `capability.schema` instead of parsing prose. When a Block needs npm libraries, WebAssembly or heavy compute, use `runtime: "module"` and reach hosted operations through `stillmade.hosted.run` with the operations listed in `permissions.capabilities`. Decode, cut, join, re-encode, extract or normalise audio and video with `stillmade.media.transform` on the device; never ask for a server render. For a rich interactive interface (React or another framework, SVG, canvas, drag), use a frame view: `src/view.config.json` with `{"runtime":"frame"}` and `src/view.js` bundled as one IIFE, colored with StillMade's theme variables. For files and structured data use the `file`, `document`, `table`, `url`, `date` and `color` types and their generated controls and presentations, not JSON text.

## Dependencies

A Block must declare everything it requires and must not assume anything is installed. Portable packages carry no package dependencies (`portable.dependencies` is empty): inline small, license-compatible code and record its license and notice in `manifest.license` and `manifest.provenance`. Desktop-only tools are reached solely through declared desktop permissions and the host desktop bridge. If a dependency cannot be delivered through the Block runtime, report the incompatibility instead of assuming the user's machine has it.

## Security

Treat Block code, external data, files, repositories, model output and network responses as untrusted. Request the minimum permissions and capabilities. Do not weaken the sandbox (QuickJS has no fetch, DOM, timers, modules or Node APIs). Validate and bound every value crossing a boundary. Never expose secrets through logs, outputs, project context, UI, error messages or serialized state.

## Execution lifecycle

A successful run means more than a returned value. Validate inputs before work; return only declared output ports with values matching their types; run long work as a cooperative job (`job.pending` with monotonic progress) instead of blocking; honor cancellation and the host timeout; keep persistent state in `manifest.state` (module Blocks: `stillmade.storage`) with migration routes so a reopened project and a repeated run behave the same; never leave orphaned jobs, temporary values or corrupted project state after cancellation or failure.

## Useful failures

Errors must say what failed, why it likely failed, whether a retry makes sense and what the user can do, so the native project chat and controller can troubleshoot. In JavaScript, throw an `Error` with that message: the host reports it as a `JAVASCRIPT` run failure carrying your message (guest code cannot set its own error code). Reject invalid or oversized input up front with a message naming the input and the limit. Never reduce a failure to "Something went wrong".

## Adapt to the platform

Do not modify StillMade core because a request does not fit immediately. First implement the capability entirely through documented SDK and platform primitives. If that is impossible, classify why: A Block implementation problem (fix it), B missing SDK exposure of an existing host capability, C missing generic platform primitive, D security or permission restriction, E runtime or language incompatibility, F fundamentally incompatible capability. For B or C, name the smallest general primitive that would serve this Block and a meaningful class of future Blocks (shaped like `files.watch()` or `media.transform()`, never `supportThisSpecificBlock()`). Return `{"unsupported":"exact reason","gapClass":"B|C|D|E|F","primitive":"..."}` instead of a package that pretends to work.

## What StillMade verifies

StillMade runs the readiness check on every Block it admits: manifest and contract validation, the source security scan, every fixture in the sandbox, wrong-type and over-limit inputs, declared output types, cancellation and timeout cleanup, state reopen and repeated runs, composition with a real upstream and downstream catalog Block through the resolver, a Project Type preview, the shared-interface contract with two participants editing a custom view in the real preview runtime (their edits must converge and a read-only participant must not write), and phone and desktop layout. Design for all of them and include normal and edge-case fixtures (1 to 50; stateful Blocks also declare `expectedState`). Test materially different inputs within the declared capability, and document limitations instead of overclaiming generality. Two of these checks need StillMade itself: two-participant shared editing and the phone and desktop UI profile run in StillMade's preview runtime when the Block is imported or released, so `ready` in the downloaded SDK reports them as not verified rather than passed.

## Project Types

A Project Type is an ordered production workflow. Each Step's primary output must be a compatible input for the next Step, secondary values come from permitted project context, and every member Block obeys this contract. Install Blocks into a realistic Project Type: project chat must understand each Step, inputs must resolve intuitively, outputs and execution state must be visible, failures must be diagnosable, and the user must be able to continue through the workflow.

## Readiness report

Finish with a Block Readiness Report: capability, contract (inputs, outputs, permissions, dependencies), platform APIs used, tests actually performed and their results, the composition test (upstream, Block, downstream), known limitations, unsupported requirements and any platform gap with its smallest general primitive. Choose exactly one status: PRODUCTION READY (every applicable check passed), READY WITH DOCUMENTED LIMITATIONS (works reliably within stated boundaries), BLOCKED BY PLATFORM CAPABILITY (needs a missing generic primitive) or FAILED. Never label a Block production ready to satisfy a request, and never claim a check ran when it did not.

## Definition of done

A Block is done only when every applicable item holds. Passing a compile, one unit test, a rendered UI or one happy-path sample is not done.

- DOD-01 Installs through the normal Block mechanism
- DOD-02 Manifest and contracts validate
- DOD-03 Runs through the normal runtime
- DOD-04 Required permissions are declared
- DOD-05 Required dependencies are reproducible
- DOD-06 Real inputs work
- DOD-07 Invalid inputs fail correctly
- DOD-08 Outputs conform to their declared types
- DOD-09 Outputs are usable by compatible downstream Blocks
- DOD-10 Universal resolver integration works
- DOD-11 Applicable project context works
- DOD-12 Applicable persistence works
- DOD-13 Applicable multiplayer behavior works
- DOD-14 Cancellation and failure cleanup work
- DOD-15 UI works in relevant states and screen sizes
- DOD-16 No hidden developer setup is required
- DOD-17 Security boundaries remain intact
- DOD-18 Relevant integration tests pass
- DOD-19 Works after application restart or project reopen
- DOD-20 A realistic Project Type execution succeeds when applicable

## Platform gap classes

- A: Block implementation problem
- B: Missing SDK exposure of an existing host capability
- C: Missing generic platform primitive
- D: Security or permission restriction
- E: Runtime or language incompatibility
- F: Fundamentally incompatible capability
