Skip to main content
A Ralphy template is a template.yaml manifest plus supporting prose (TEMPLATE.md, composition.md, hooks.md, prompt-cookbook.md). Public templates are served from the hosted content library (Supabase); your own author into .ralphy/workspaces/<ws>/templates/<slug>/ (user-local). The old repo-public templates/<category>/<slug>/ folder was retired in #084. This page is the wire-format spec: the manifest Zod schema, the two kinds, the segment categories, the slug discipline, and the version gate.

Full example

A minimal vibe-style template at .ralphy/workspaces/<ws>/templates/brand-story/template.yaml:
A vibe-reference template adds a composition.md walkthrough plus a reference/ folder of worked artifacts alongside the manifest. Both kinds use the same schema.

File layout

The five category folders are fixed. .ralphy/workspaces/<ws>/templates/ is flat (no category subdir). When a slug exists in both, workspace overrides repo — per AGENTS invariant #10.

Zod schema

cli/lib/schemas/template.ts exports TemplateYamlSchema. Field-by-field:

category — the five segment-persona folders

Each value is a directory under templates/. The lint walks only these five.

kind — vibe-reference vs vibe-style

Five vibe-reference templates ship in repo; 38 vibe-style. Both kinds are loadable by ralphy template use <slug>.

requires

diagnoseRequiredInputs() checks the user’s project inputs against this block and returns the first missing requirement. Callers map the result to E_TEMPLATE_INPUT_MISSING. The refs field is a single count for v1.0; the 3-slot {cref, sref, pref} shape lands post-launch (decision D-02).

scenes — optional scaffolding

vibe-reference templates fill this in. vibe-style templates leave it as []. A worked vibe-reference scenes block:
The scenarist consumes this scaffolding and emits a full Scenario conforming to cli/lib/schemas/scene.ts → ScenarioSchema. The full scene shape (with vo_text, camera, lighting, gesture, broll, refs, notes) is the scenarist’s output contract, not the template’s input. See Memory schemas and the scenarist playbook for the downstream shape.

Gesture vocabulary

When the template’s direction field hints at a body-language beat, the scenarist may attach a gesture to the resulting scene. The vocabulary is the finite enum in cli/lib/schemas/gestures.ts:
Niche one-off body language goes in Scene.notes (the free-text escape hatch), never in direction. The enum is closed by design (decision D-06) — adapters silently omit unknown values rather than throw, so a forward-compatible PR that adds a new gesture degrades gracefully on older binaries.

Slug discipline

Slugs are kebab-case. The regex:
No uppercase, no underscores, no leading or trailing hyphen, no single-character slugs.

Banned tokens

DENIED_SLUG_TOKENS in cli/lib/schemas/template.ts blocks real-creator and brand tokens from appearing in slugs. The match is whole-token (delimited by start, end, or hyphen) so arogant-pov is fine but joe-rogan-pod is not. Currently denied:
Creator references stay legal as prose inside TEMPLATE.md / composition.md — never in the slug. The rule is decision D-05: archetypal slugs only. validateSlug(slug) returns {ok: true} or {ok: false, reason, token?}. Callers build E_TEMPLATE_SLUG_INVALID from the rejection payload.

Version gating

Every template.yaml must declare version: 1. The loader in cli/lib/templater/loader.ts → parseTemplateManifest() reads the version field first; if it is not in SUPPORTED_VERSIONS = [1], the loader calls raiseError("E_TEMPLATE_VERSION_UNSUPPORTED", {version, id}) and exits.
When scenes[] eventually gains a required field, the loader keeps a v1 reader alive for at least one major-release cycle (decision D-03). Authors bump version: 2 only when the schema breaks compatibly.

Manifest discovery

locateTemplateManifest(dir) prefers template.yaml, then falls back to template.json for not-yet-migrated templates (02.05.04 migration window). Both shapes parse through the same Zod schema. New templates should land as .yaml only.

Slug resolution

ralphy template use <slug> resolves a slug across both tiers:
  1. Search .ralphy/workspaces/<ws>/templates/<slug>/template.yaml.
  2. If not found, look the slug up in the public content library (Supabase).
  3. If still not found, search aliases (aliases: [old-slug]).
Workspace overrides the public tier on id collision. Aliases let you rename a template without breaking existing user scripts.

Validation

The manifest parses through the v1 Zod loader (cli/lib/templater/loader.ts) on every ralphy template show / use. It enforces:
  • The category is one of TEMPLATE_CATEGORIES.
  • The slug passes validateSlug() — kebab-case, no banned tokens.
  • template.yaml (or legacy template.json) is present and parses without throwing.
(The repo-folder lint:templates script was retired with the templates/ folder in #084 — there is no longer a committed repo catalog to walk.)

Authoring checklist

  1. Pick a category. One of b2b-saas, dtc-commerce, creator-lifestyle, entertainment-viral, cinematic-narrative.
  2. Pick a slug. Kebab-case, no creator names.
  3. Decide the kind. vibe-reference if you’re shipping a full production with refs; vibe-style if you’re shipping a prompt cookbook.
  4. Write template.yaml at .ralphy/workspaces/<ws>/templates/<slug>/. Start with version: 1, fill required fields, default the rest.
  5. Write TEMPLATE.md. Human-readable overview. Creator references go here, not in the slug.
  6. For vibe-reference: add composition.md (the narrated walkthrough) and reference/ (worked artifacts).
  7. For vibe-style: add hooks.md (hook library) and prompt-cookbook.md (per-modality fragments).
  8. Run ralphy template register <slug> then ralphy template suggest "<your test brief>". Verify the new template surfaces with a sensible score.
  9. To publish to the public Library, use the templater / dev-publish-template path.