| name | templater |
| namespace | user |
| description | The full extract → classify → blueprint → de-dup → publish pipeline for a finished `.ralphy/workspaces/<ws>/projects/<id>/`. Reads its `units/*/unit.json` (#069) as the Unit source of truth, then factors the project into ALL FIVE entities — Unit + the four typed blocks Template (structure), Style (look), Recipe (effect/treatment), Asset (reusable media). Applies the recipe-vs-tag split (#082/#083): a candidate is a Recipe only if it carries an extractable artifact (ffmpeg filtergraph / HyperFrames snippet / encode recipe / prompt technique); otherwise it is a Tag on the Unit. Captures a per-unit Blueprint (#080), de-dups every block against the live library FIRST, then prints the exact ordered `publish-entity.ts` commands (dry-run → --push). Publishing itself is the #056 path.
USE WHEN the user says any of: "save this as a template", "turn the project into a template", "templatify <project-id>", "extract a template from <project>", "decompose this project into blocks", "what units/blocks did this project produce", "I want others to reproduce this", "make a reusable version of <project>", "extract the entities from <project>", "classify this project", "freeze this project". ALSO FIRE proactively after a successful render + postmortem the user rates 8+/10 — the experience is most reusable while still fresh.
DO NOT FIRE for scaffolding a new project, one-off renders, or quality evaluation — see the DO NOT FIRE section in the body. |
templater
You decompose a finished project into the content-entity model (#063) and classify its pieces into reusable blocks. The contract is: a future agent should be able to reproduce the work — same units, same blocks (Template / Style / Recipe / Asset) — without re-deriving any of it from the raw artifacts/ dump.
Where this sits in the Unit lifecycle. templater runs AFTER Unit formation (phase 17) in the canonical Unit lifecycle — it reads each units/<slug>/unit.json. Only extract/publish a project whose Unit is polished: the render must have passed the native-video final gate (ralphy project status <id> --contract → polished === true, #411). A keyframe-only eval does not qualify.
Two exits: library entities vs workspace bundle
Reusable know-how leaves the repo through TWO distinct doors. Pick the door before running anything:
- Project → library entities (THIS skill). The user wants the work browsable and reproducible per unit — "publish this to the library", "others should reproduce this style", "extract the blocks". Scope: ONE finished project, factored into Unit + Template/Style/Recipe/Asset blocks. The publishing implementation lives in the sibling
ralphy-web repository.
DO NOT FIRE
- Scaffolding a new project — that is
ralphy template use <existing-slug>, not this skill.
- One-off renders — that is the producer playbook.
- Quality evaluation — that is
/evaluator.
- Pushing to the live library — that is the #056 publish path in
../ralphy-web/scripts/publish-entity.ts; templater stops at printing the ordered commands. See HARD INVARIANTS below.
This skill is the full maximal-detail pipeline: one invocation, followed end-to-end, reproduces what a careful maintainer does by hand. The six stages are EXTRACT (read the finished deliverables) → CLASSIFY (factor into the five entities, incl. the recipe-vs-tag split) → BLUEPRINT (capture a per-unit reproduction payload) → DE-DUP (match every candidate against the live library first) → EMIT the entity bundle → PRINT the publish runbook (the exact ordered publish-entity.ts commands).
templater does the extraction, classification, blueprint capture, and de-dup itself. It does not invoke the live push (--push) — that is the user's deliberate step. It prints the full, ordered command set (dry-run first, then --push) and hands control back. Publishing to the live library is owned by the sibling ralphy-web repository — see Publish runbook.
The five entities (read this first)
The library model (../ralphy-web/lib/library-v2/types.ts) has five entities. Every project decomposes into them:
- Unit — a finished deliverable in a Format (
video, carousel, sticker-pack, podcast-cuts, fb-creative, motion-design, poster, image), holding 1..N ordered media items. A Unit = exactly 1 Template + 1 Style + N Recipes + M Assets. That ingredient list is the Unit's provenance.
- Template (block, single-per-unit) — the STRUCTURE / skeleton only, style-agnostic. The beat structure, the slide count + slot layout, the composition skeleton.
- Style (block, single-per-unit) — the visual look / register. The aesthetic + its anchor reference images.
- Recipe (block, multi-per-unit) — a composable effect / treatment. A VFX layer, an encode recipe, an overlay pass, a caption style.
- Asset (block, multi-per-unit, has a
sub: character / location / prop / music) — concrete reusable media: a locked character master, a location plate, a prop, a music bed.
The Unit source of truth is .ralphy/workspaces/<ws>/projects/<id>/units/*/unit.json (formed by ralphy unit create, #069). Each unit.json already carries format, ordered media, and a provenance block (template / style / recipes[] / assets[] slugs) — so much of the decomposition is reading, not inventing. The postmortem/06-units.md record (if the project has one) is the second-highest-signal input: it already marks each provenance block NEW vs. REUSED.
What this skill is NOT
- Not a project scaffolder. That is
ralphy template use <slug> --project <new-id>.
- Not a postmortem generator. That is
/postmortem. Run it first if the source has none — postmortem/02-lessons.md (lessons) and postmortem/06-units.md (units + provenance) are the highest-signal inputs here.
- Not the library publisher. The publish step is the #056 primitive in
../ralphy-web/scripts/publish-entity.ts. templater produces the classified bundle; the web repository publishes it.
- Not a single-template extractor. The old
create-from-project "one template per project" framing is retired. A project produces a SET of entities (often several units + several blocks), not one template.
Source-of-truth files in the source project
Every extraction is keyed to a known file. If a file is missing, the skill degrades gracefully (it derives what it can, never invents). scenario.json is PREFERRED, not required — scenario-less still / HyperFrames projects (sticker packs, FB packs, poster sets) are valid; derive structure from asset-manifest.json + units/ + index.html and skip the scene table (the #062 fix).
Source file in .ralphy/workspaces/<ws>/projects/<id>/ | Required? | What gets pulled | Maps to entity |
|---|
units/*/unit.json | REQUIRED | Finished deliverables + ordered media + provenance block slugs (#069) | Unit (+ seeds all four block axes via provenance) |
asset-manifest.json | REQUIRED | Locked refs, music beds, character masters, location plates; final slot list | Asset blocks; backstop for Units when units/ is thin |
scenario.json | PREFERRED | Scene skeleton: beats + durations + speaker map + VO/SFX flags | Template block (structure only) |
prompts.json | PREFERRED | Per-stage prompts (image / video / VO / music / captions) | Style block prompt cookbook (with {{slots}}) + Recipe hints |
index.html (HyperFrames) | PREFERRED | Composition skeleton + per-template defaults; encode / overlay recipes | Template block (composition); Recipe blocks (VFX / encode / overlay) |
logs/generations.jsonl | PREFERRED | Per-stage model picks + params + cost rollup | Model-stack notes on the Style / Template blocks |
BRIEF.md | PREFERRED | Original user brief — sentence-level intent | Drives Format + category classification + descriptions |
postmortem/06-units.md | PREFERRED | Units shipped + provenance, NEW vs REUSED per block | The classification spine — read it before re-deriving anything |
postmortem/02-lessons.md | PREFERRED | Top rules + anti-patterns | "Key rules" on the relevant blocks |
postmortem/04-models-and-cost.md | PREFERRED | Spend rollup per stage | Cost ballpark on the blocks |
If units/ is genuinely empty AND asset-manifest.json has finished deliverables, surface that gap: the project has shippable media but no curated Units — suggest ralphy unit create <id> --slug <s> --format <f> --from '<glob>' first, then re-run. You can still classify candidate blocks from the manifest, but a clean per-unit publish needs the units formed.
See references/extraction-rules.md for the per-file extraction details and edge cases.
The workflow
-
Resolve the project + read its Units. ralphy unit list <project-id> and read each units/<slug>/unit.json. These are the Units. Read postmortem/06-units.md if present — it pre-classifies the provenance blocks (NEW vs REUSED). NEVER fabricate units from the raw artifacts/ dump.
-
Read BRIEF.md + (if present) scenario.json headers for the elevator pitch. This grounds the classification passes.
-
Decompose into the five entities. For each Unit, resolve its provenance into concrete block candidates:
- Template — from
scenario.json (beats) or, for scenario-less projects, from index.html (composition skeleton) / the slide-slot layout. Structure only, style-agnostic.
- Style — from
prompts.json + the locked anchor refs. The look + its reference images.
- Recipe candidates — VFX layers, encode recipes (
-tune grain, CRF), overlay passes, caption styles, baked transitions, prompt techniques. From index.html + prompts.json + bake scripts + postmortem lessons. Each candidate is then split recipe-vs-tag in step 4.
- Assets — locked character masters, location plates, props, music beds from
asset-manifest.json. Each carries a sub (character/location/prop/music). Multi-value.
-
Recipe-vs-tag split (the #082/#083 discipline — read references/recipe-vs-tag.md). For EACH recipe candidate from step 3, decide:
- Recipe (stays a block, earns a detail page) ONLY if you can author a real, copyable artifact — an ffmpeg filtergraph, an encode/bake command, a HyperFrames snippet, or a concrete prompt-style technique — sourced from the project's own files, never invented:
cli/lib/ffmpeg-recipes.ts (the canonical builders) for ffmpeg/encode/overlay, the project's scripts/*.sh bake scripts + the captured blueprint.json recipes[].command for bakes, index.html for HyperFrames overlays, the gen-log / for prompt recipes. When kept, author the FULL enriched payload: (/////) + (markdown how-to) + (the real code) + (named knobs) + (a self-contained runnable for HyperFrames recipes; before/after media for ffmpeg recipes — never fabricated).
Output
JSON, pipe-friendly:
{
"project": "free-air-vpn-stickerpack",
"units": [
{
"slug": "stickers-outline",
"format": "sticker-pack",
"media_count": 32,
"provenance": {
"template": { "slug": "sticker-set", "status": "REUSED" },
"style": { "slug": "free-air-jelly-pure", "status": "NEW" },
"recipes": [{ "slug": "floodfill-diecut-cutout", "status":
Publish runbook (#056)
templater extracts + classifies + blueprints + de-dups; the publish to library is the #056 primitive in ../ralphy-web/scripts/publish-entity.ts. Print commands in the required order (blocks → units → blueprints), each as a dry-run line then the --push line. templater never runs --push — it prints the runbook and stops; the user (or dev-publish-template) runs it from the web repository.
0. Web repository prerequisite
Before ANY --push, verify that ../ralphy-web is present, clean, on its expected branch, and has its publishing credentials configured. Dry-runs touch nothing remote, so they are always safe to print/run first.
1. NEW blocks first (so unit provenance + tag facets resolve)
Each NEW block is a --block-file <spec.json>. A block spec is { kind, id, name, blurb, sub?, refs?[] }; a recipe spec ALSO carries the enriched payload { recipeKind, body, artifact, params, demo } (#082) — which pack into the blocks.recipe_kind column + the blocks.data jsonb. The recipe's demo (demo.html for HyperFrames, before/after media for ffmpeg) + any refs ride along to Storage so the library page is interactive (live recipe demo, audio player for a music asset). Tags are NOT blocks — they carry no --block-file line; they ride on the Unit.
cd ../ralphy-web && bun run scripts/publish-entity.ts --block-file <block-spec.json>
cd ../ralphy-web && bun run scripts/publish-entity.ts --block-file <block-spec.json> --push
2. Units next (carrying unit.json.tags + provenance links)
cd ../ralphy-web && bun run scripts/publish-entity.ts --unit <absolute-unit-dir>
cd ../ralphy-web && bun run scripts/publish-entity.ts --unit <absolute-unit-dir> --push
The unit's media uploads to the configured CDN and the committed library entry receives its provenance and tags. A missing provenance block is warned and skipped, never fabricated — which is why blocks publish FIRST.
3. Blueprints last (the per-unit reproduction payload, one per Unit)
cd ../ralphy-web && bun run scripts/publish-entity.ts --blueprint <absolute-blueprint-dir>
cd ../ralphy-web && bun run scripts/publish-entity.ts --blueprint <absolute-blueprint-dir> --push
The --blueprint dir is the units/<slug>/blueprint/ payload step 7 captured (blueprint.json + copied index.html / prompts / hard assets). It uploads the payload to Storage under blueprints/<unitId>/, upserts the 1:1 blueprints row, and appends to PUBLISHED_BLUEPRINTS.
Every mode updates the committed library data and uploads media through the web repository's configured storage path (idempotent by id / unitId, append-only). Default run is DRY-RUN. The maintainer one-shot that runs the whole runbook for you is dev-publish-template (#056).
Edge cases & refusals
- No
units/ AND no finished deliverables in asset-manifest.json → the project isn't done. Refuse, point at what's missing (run /producer or finish the pipeline).
- No
units/ but finished media exists → DO NOT refuse. Surface the gap, suggest ralphy unit create, classify candidate blocks from the manifest anyway. (This is the #062 fix in spirit — never hard-block scenario-less / unit-thin projects.)
- No
scenario.json → DO NOT refuse (the #062 fix). Derive the Template block's structure from index.html / slide layout, skip the scene table.
- No
postmortem/ → proceed but warn. Block classification leans harder on prompts.json + asset-manifest.json; offer to /postmortem (now a 7-file set incl. 06-units.md) first for a cleaner NEW/REUSED split.
- A block looks like an existing one → match it, mark REUSED, do NOT create a duplicate. When genuinely unsure, prefer REUSED and flag the uncertainty in
warnings. (The choose-path-xfade-master == ffmpeg-xfade-master dup is the canonical cautionary case — see references/recipe-vs-tag.md.)
- A recipe candidate has no extractable artifact → it is a TAG, not a block. Demote it to the Unit's
tags[]; never publish an empty refs:0 recipe block. See references/recipe-vs-tag.md.
- Captured prose contains non-English (folklore, foreign brand copy, RU/other-script VO) → translate before writing it to any file. English-only on disk; the
rg '\p{Cyrillic}' gate must come back empty.
- Slug collision in
templates/ (only if writing the optional local artifact) → refuse unless --force, show the diff first.
Why this skill exists
A finished project's postmortem captures the expensive lessons; units/*/unit.json captures the finished deliverables and their provenance. Without templater, those sit unindexed — the next agent re-derives the structure, the look, the effects from scratch. templater is the compression + classification layer: it factors the project into the five reusable entities and matches them against the library so the next project starts at the library's wisdom level. The publish primitive (#056) then makes those entities discoverable. This skill is the bridge between "we shipped deliverables" and "we shipped reusable blocks."
References
references/recipe-vs-tag.md — the #082/#083 split: the decision rule, the enriched-recipe payload to author, where to source artifacts, the de-dup cautionary example.
references/extraction-rules.md — per-source-file extraction details + edge cases.
references/slot-detection.md — LLM prompt + heuristics for {{slots}} in prompts.json.
references/kind-decision.md — vibe-reference vs vibe-style decision tree (for the optional local artifact).
references/category-classifier.md — the five segment-persona categories + LLM classification prompt.
references/pool-migration.md — heavy-asset migration to ralphy-assets/pool/ (for Asset blocks / the local artifact).
cli/lib/schemas/unit.ts — the unit.json Zod schema (the Unit source of truth, #069).
cli/commands/blueprint.ts — the ralphy blueprint create|show|list|use surface (#076/#079); templater runs blueprint create per Unit in step 7.
cli/lib/schemas/blueprint.ts — the BlueprintSchema Zod shape (#074): the six reproduction axes a captured Blueprint carries.
cli/lib/ffmpeg-recipes.ts — the canonical ffmpeg/encode/overlay builders (buildVhsFilter, buildColorGradeFilter, buildSidechainFilter, buildMixMusicFilter, CRF helpers); the source-of-truth for a kept recipe's artifact.
../ralphy-web/lib/library-v2/types.ts — the five-entity shapes (Format / Unit / Block kinds), the enriched-Recipe fields (#082: recipeKind/body/artifact/params/demo), and Unit.tags.
supabase/migrations/0001_init_library_v2.sql — the schema (the blueprints table + blocks.recipe_kind / blocks.data / units.tags columns) that must exist before any --push.
../ralphy-web/scripts/publish-entity.ts — the publish primitive (#056); templater hands off to it.
docs/skills-vs-templates.md — templater = extract/classify; #056 = the web library writer.