Skip to content

Prefab system — configurable, AI-modifiable units of content

This doc names and unifies a pattern that already runs through several other docs (recipes, generators, compositions, directors, theme system). It is the noun for “the unit of reusable content in our dev experience” and the spec for the dev-tooling that lets us build, test, view, and AI-modify them.

No new architecture is introduced here. Recipe → Generator → Composition → Renderer (from scene-construction.md) is still the pipeline; director surfaces (from director-pattern.md) are still the AI loop. Prefab is the package that bundles a recipe schema + a generator + a director surface + an AI brief + a preview, presented as a single first-class concept to authors.


In Unity, a prefab is a saved object you drag into a scene; variants override prop values. Familiar mental model — adopt it, then extend.

In our system, a prefab is the full bundle that lets a human or an AI agent produce a concrete instance of a content type:

Prefab
├── Recipe schema ── Zod schema declaring the prefab's parameters
├── Generator ── pure (recipe, seed) → Composition function
├── Director surface ── recipe editor + intervention pipeline (per-type)
├── AI brief ── prompt context for the type-specific director agent
├── Preview ── thumbnail / variant grid / live render
└── Metadata ── name, tags, theme affinity, provenance, version

A prefab is always configurable (its recipe parameters), always generatable (its generator produces instances), and always AI-modifiable (its director surface accepts feedback that maps to recipe patches). These aren’t optional features layered on — they’re the definition.

A prefab instance is the Composition output: a concrete arrangement of entities, rendered to the scene, with provenance back to the source prefab + seed + intervention history.


  • Unity-familiar mental model. Most contributors will read “prefab” and have a 90% accurate intuition immediately.
  • First-class dev surface. “Prefab” is a noun authors can talk about. “Recipe / generator / composition” is internal vocabulary; prefab is the externally-visible thing.
  • Unifies otherwise-scattered concepts. Buildings, NPCs, settlements, threads, voice cards, items, world objects — they’re all prefabs in our system. Same anatomy, same tooling, different schemas.
  • Maps to the director loop directly. “Modify this prefab via prompt” is the user’s mental model; the system translates that into director-mode interventions.

Key difference from Unity prefabs: ours are recipe-driven, not instance-driven. A Unity prefab is a saved instance you spawn copies of; ours is a parametric description that generators turn into instances. Variation lives in the recipe, not in per-instance overrides.


For a hypothetical Building prefab in packages/content/prefabs/cottage/:

packages/content/prefabs/cottage/
├── schema.ts ── Recipe schema (Zod): footprint, roof, materials, …
├── generator.ts ── (recipe, seed) → Composition function
├── director.md ── AI brief for `cottage-director` subagent
├── preview.ts ── How to render a thumbnail / variant grid
├── meta.json ── name, tags, theme affinities, version, status
└── examples/ ── canonical seed instances for regression testing
├── camelot.json
├── midgard.json
└── hinterlands.json

Every prefab type lives in a folder like this. The structure is enforced by a prefab-critic subagent (when it lands) — a folder under prefabs/ without one of the six artifacts fails its PR check.

The parameters the prefab accepts. Zod-validated. Defines the configuration surface.

packages/content/prefabs/cottage/schema.ts
import { z } from 'zod';
export const CottageRecipe = z.object({
id: z.string(),
kind: z.literal('cottage'),
seed: z.number().int(),
footprint: z.object({
width: z.number().min(3).max(8),
depth: z.number().min(3).max(8),
}),
roof: z.enum(['gable', 'hip', 'shed']),
materials: z.object({
walls: z.string(), // asset key
roof: z.string(),
door: z.string(),
}),
hasChimney: z.boolean().default(true),
windowCount: z.number().int().min(0).max(6).default(2),
theme: z.string().optional(),
});
export type CottageRecipe = z.infer<typeof CottageRecipe>;

Pure function. Recipe + seed → Composition. Deterministic.

packages/content/prefabs/cottage/generator.ts
export function generateCottage(
recipe: CottageRecipe,
ctx: GenContext,
): Composition {
// … procedural composition using ctx.procgen + ctx.assets
}

Per-prefab-type director: selection modes, intervention levels, AI brief.

packages/content/prefabs/cottage/director.ts
export const cottageDirector: DirectorSurface<CottageRecipe> = {
type: 'cottage',
interventions: [
'composition-patch', // move/delete an entity
'recipe-patch', // change footprint/roof/materials
'seed-reroll', // same recipe, new seed
'generator-suggestion', // propose code change
],
agentBrief: './director.md',
fieldEditors: { /* leva spec per recipe field */ },
};

Markdown file consumed by the type-specific director agent. Defines the prompt context for AI modifications:

# cottage-director brief
Recipes describe a single residential cottage in a the chosen asset pack-aesthetic world…
## Constraints
- Footprint: 3-8m on each axis (small/medium scale only — manors and
taverns are separate prefabs)
- Roof types: gable / hip / shed; pitched, never flat
- Materials must be drawn from the asset manifest's `cottage-eligible` tag
## Tonal anchors
- the project's tonal register: a cottage is a small home for a small life
- Theme-specific material palettes (Theme-A ≠ Theme-B ≠ Theme-C)
## Common intervention shapes
- "make it bigger" → footprint +1-2m, possibly chimney removal
- "less generic" → vary window placement, add asymmetry to roof
- "doesn't fit this theme" → swap materials per theme palette

The director agent reads this brief plus the current recipe + composition when processing user feedback.

How to produce a thumbnail or live render of the prefab:

packages/content/prefabs/cottage/preview.ts
export const cottagePreview: PrefabPreview<CottageRecipe> = {
defaultRecipe: () => ({ /* a canonical "neutral" recipe */ }),
cameraFraming: 'building-3q', // standard framing preset
variantGridSeeds: [1, 7, 42, 137, 1024], // for "show me variations"
};
{
"name": "Building",
"tags": ["residential", "small", "low-poly"],
"realmAffinities": ["camelot", "hinterlands"],
"status": "stable",
"version": "1.0.0",
"author": "system",
"supersedes": null
}

What we’ll have prefabs for, roughly in build order:

TypeParameters driveFirst instanceDirector agent
BuildingFootprint, roof, materials, windowscamelot.jsoncottage-director
BuildingFootprint, anchor role, signagehinterlands-tavern.jsoninherits scene-director
BuildingMulti-storey footprint, courtyardtbdinherits scene-director
BuildingFootprint, steeple, denomination flavourtbdinherits scene-director
NPCVoice card ref, archetype, equipment, behaviourhella.jsonnpc-director
SettlementPatches, anchors, population, threadsexample-village.jsonscene-director (already exists)
RegionTerrain, settlements, POIs, scattertbdscene-director
ThreadAnchors, beats, branches, completionwelcoming.jsonthread-director
Voice cardRegister, vocabulary, quirkshella.jsonvoice-card-director
Dialogue corpusSlot variants, theme verdictshella-corpus.jsondialogue-corpus-director
ItemStats, art, flavour, raritytbditem-director
World objectMesh, interaction, lifecycletbdinherits scene-director
Ambient markerPosition, audio, particle, fadetbdscene-director

Many directors are shared (the scene-director handles anything spatial); specialists exist where the content type has its own grammar (threads, voice cards, dialogue).


Prefab composition (hierarchical embedding)

Section titled “Prefab composition (hierarchical embedding)”

A prefab’s recipe can reference other prefabs. The Settlement prefab’s recipe lists NPCs, Buildings, Landmarks — each a reference to another prefab + a specific recipe variant + a placement.

// example-village recipe (simplified)
{
kind: 'settlement',
patches: { /* … */ },
anchors: [
{
role: 'tavern',
prefabRef: { type: 'tavern', recipe: 'hinterlands-tavern' },
placement: 'center',
},
{
role: 'cottage',
prefabRef: { type: 'cottage', recipe: 'cottage-modest' },
placement: 'ring',
count: 5,
},
],
population: [
{
prefabRef: { type: 'npc', recipe: 'hella' },
placement: 'tavern-doorstep',
},
],
}

When the settlement generator runs, it ctx.subGenerate('cottage', recipe.anchors[1]) for each cottage, which produces a Composition; the settlement composition flattens those into its entity list with provenance attribution.

This is how Unity’s prefab-of-prefabs nesting works, but in our system it’s just a recipe field referencing another recipe. No special serialisation, no nested-prefab override system.


Recipes can extend other recipes via a parent field:

camelot-cottage.json
{
"parent": "cottage-base",
"materials": {
"walls": "synty/camelot/wattle-daub",
"roof": "synty/camelot/thatch",
"door": "synty/camelot/oak-plank"
},
"windowCount": 3
}

The recipe loader resolves inheritance at load time — apply parent fields, then override with child fields. Validate the merged result against the schema.

Inheritance is one level deep by default; deeper hierarchies need explicit opt-in (avoid the Unity prefab-variant labyrinth).


  1. User opens the prefab editor for Building. Sees current recipe on the right, live preview on the left, variant grid below.
  2. User adjusts a slider (window count: 2 → 4). Preview re-renders in <100ms (deterministic generator on a stable seed).
  3. User clicks a building in the preview and types: “this side of the cottage feels too plain — break it up with a small lean-to.”
  4. cottage-director agent receives: selection (the entity clicked), feedback text, current recipe, current composition slice, the AI brief, available asset tags. Emits an intervention.
  5. Intervention applied + validated: recipe patches leanToSide: 'east' (or composition patch if simpler). Tier 0 checks pass. Theme Critic approves.
  6. Diff preview shows before/after side by side. User clicks Accept.
  7. Recipe saved to disk. New variant cottage-with-lean-to.json is either an in-place edit or a new variant depending on user intent (the editor asks).
  8. Provenance log appended — recording the feedback that produced the change, for replay / audit / training-data-future.

This is the same loop the director-pattern doc describes. The prefab system is its packaging — every prefab type has its own slice of this loop, with its own director agent and editor surface.


The most-used dev surface in this whole system. Looks like Unity’s Project panel cross-bred with a 3D asset browser.

┌──────────────┬──────────────────────────────┬─────────────┐
│ Prefab types │ Browser grid │ Inspector │
│ │ ┌──┐ ┌──┐ ┌──┐ ┌──┐ │ (selected │
│ ▾ Buildings │ │ │ │ │ │ │ │ │ │ prefab's │
│ Building │ └──┘ └──┘ └──┘ └──┘ │ recipe + │
│ Building │ ┌──┐ ┌──┐ ┌──┐ ┌──┐ │ metadata) │
│ Building │ │ │ │ │ │ │ │ │ │ │
│ ▾ NPCs │ └──┘ └──┘ └──┘ └──┘ │ │
│ an example NPC │ │ │
│ ▾ Settlements│ ↑ thumbnails per variant │ │
│ Settlement A │ │ │
│ ▸ Threads │ │ │
│ ▸ Voices │ │ │
├──────────────┴──────────────────────────────┴─────────────┤
│ Search · Filter (theme, tag, status, author) · Sort │
└────────────────────────────────────────────────────────────┘

Standard dev-tooling-ui.md conventions apply (three-panel layout, Cmd-P palette, scrubbable numeric inputs, etc.).

Per-prefab actions (right-click or button row):

  • Open editor (full-screen director surface)
  • Generate variant (open editor with a forked recipe)
  • Variant grid (5×5 seed shuffles for visual variance check)
  • Add to scene (drop the prefab into the current scene at cursor)
  • Find references (which scenes / parent prefabs use this)
  • History / provenance (every intervention this prefab has been through)
  • AI: regenerate from prompt (open a quick-action prompt, send to the type-specific director, preview result)

A prefab passes when:

  1. Schema validates for every example recipe in examples/
  2. Generator is deterministic — same (recipe, seed) produces identical Composition across runs (Tier 0 probe)
  3. Generator output passes per-type Tier 0 assertions (no overlapping entities, walkable gaps, expected anchor count, tonal- flags clean, etc.)
  4. Variant grid renders cleanly at the canonical seeds (smoke test that no seed crashes)
  5. Theme Critic approves the canonical examples

Failing any of these blocks merging the prefab. The prefab-critic subagent enforces.


  • Not Unity prefab variants with override stacks. We use recipe inheritance (one level by default); overrides are recipe field changes, not per-instance.
  • Not a runtime concept. Players never see “prefab”; they see the generated world. Prefabs are an author-time abstraction.
  • Not a way to bypass the director discipline. Every prefab edit still goes through the apply pipeline (validate → Tier 0 → Theme Critic → diff preview → accept).
  • Not coupled to Three.js or Colyseus. Prefab data is JSON; rendering
    • sync are downstream concerns. (Per abstraction-discipline.md.)
  • Not a place for game balance. Stats, costs, drop rates — those live in the item / skill / economy systems, not generic prefab fields.

  1. Prefab folder convention + critic (prefab-critic subagent that validates folder structure on PR) — week one
  2. Building prefab as the first concrete instance (schema + generator + AI brief + 1 example). Validates the pattern.
  3. Prefab Browser dev surface — lists prefab types, shows the cottage, opens its editor
  4. Scene-director extension — handles “drop a cottage into the scene” via the prefab system, not as a one-off
  5. Settlement prefab — first composing prefab; tests prefab-of- prefabs flow
  6. NPC + Voice card prefabs — port from MyProject canon
  7. Thread prefab + Dialogue corpus prefab — port and adapt
  8. Variant grid + AI quick-action prompt in the browser
  9. Inheritance support for recipe extension
  10. Asset director becomes a prefab director for raw source assets (same UI, different content type)

  • scene-construction.md — the underlying Recipe → Generator → Composition → Renderer pipeline that prefabs use
  • director-pattern.md — the AI loop every prefab’s editor surface builds on
  • dev-tooling-ui.md — UI conventions every prefab editor follows
  • in-game-ui.md — separate concern; prefabs are dev-time, in-game UI is player-time
  • abstraction-discipline.md — prefab data is JSON, decoupled from any specific engine
  • subagent-roster.md — per-prefab-type director agents
  • material-system.md — material roles named in prefab recipes; framework resolves to tier-appropriate concrete materials