cadabra Docs

AI designer and Tweak loop

The AI in Cadabra is a game designer, not a programmer. It produces a GameSpec: data that composes blocks from the game kit, a physics world and a look. The TV's engine runs that data. Nothing the model writes is ever executed.

Why specs, not code

The tools

The designer runs in the ForgeFn Lambda (apps/backend/src/ai/designer.ts and tools.ts). Its system prompt lists the block kit, the four looks, the physics world schema (generated from the zod .describe() text) and a few fun rules: a clear goal, risk and reward, a comeback, physical comedy. Design and tweak jobs share the system prompt and tools, so they hit the same prompt cache.

ToolWhat it does
list_blocksThe kit: block id, summary, controls, arena kinds
get_block_schemaOne block's params as JSON Schema with defaults, plus the controller widgets it needs
validate_specSchema check, returns {ok, errors}
playtest_specValidates, then plays a full round with 4 bots (skills 0.4 to 0.9). Returns {ok, reasons, stats}
patch_specRFC 6902 JSON Patch on the latest spec. Tweak jobs also pass a summary and changes tags
submit_specKept for compatibility. A passing playtest of the latest spec already counts as the submission

The playtest gate

playtest() in packages/engine/src/playtest.ts rejects a spec when:

Each reason says what to change, such as "only 1 of 4 bots scored; the hazard penalised them", so the model fixes the spec in its next tool call. The same gate checks every Tweak result and every local chip preset, at the real seat count.

The Forge, step by step

  1. The TV sends forge.pitch with the Idea Storm ingredients, the seats and the settings. WsFn takes a forge lock with a conditional DynamoDB write and invokes ForgeFn asynchronously.
  2. Guardrails screens the ingredients. Blocked ones are dropped and that player's phone shows a "blocked" screen.
  3. Claude Haiku 5.5 writes three pitches, each with a title, a hook, blocks and a look. Budget: 8 s, else fallback pitches. Measured: about 6 s.
  4. All three designs start in parallel as soon as the pitches go out, with Claude Sonnet 5.5. Progress streams to the TV as forge.progress.
  5. The room votes. The winner is usually ready 4 to 7 s after the vote.
  6. If the winner is not ready 25 s after the vote, a built-in game is reskinned from the pitch (title, hook, look, palette) and sent as forge.ready {fallback: true}. The room never waits on the AI.
  7. Losing pitches that finish are saved to the household Arcade.

The Tweak loop

After a round, phones get a 10 s window with three request slots. Each phone can tap a chip or type up to 80 characters.

Chips: Low gravity, Faster, Bigger arena, Bouncier, More chaos, Harder for leader.

  1. The TV sends forge.tweak with the current spec, the requests, the seat count and the mode.
  2. Typed text goes through Guardrails, which fails closed.
  3. The model gets a tweak brief plus a hint from the matching chip preset, and answers with patch_spec and playtest_spec in one reply. One repair reply is allowed.
  4. The first passing patched spec wins. The TV shows a "What changed" card (for example Gravity x0.4, Bounce up, Chaos up) and replays.

Live timings over 16 runs: p50 5.7 s, p90 8.5 s.

Remix uses the same flow from the Home row or the Arcade, and saves the result as a new Arcade entry. A changed spec gets a new id with origin.parentId, so the Arcade keeps the lineage.

Bedrock first, Anthropic API as fallback

apps/backend/src/ai/bedrock.ts exposes one converse() for two roles: designer (Sonnet 5.5) and fast (Haiku 5.5). Each role has an ordered chain of model ids and inference profiles (anthropic., in., global.).

Guardrails

Edit this page on GitHubLast updated 23 October 2026