templater
SkillDev toolsThe 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.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the templater skill
What this skill tells your AI
The instructions your AI receives, as published by alecs5am/ralphy in .agents/skills/templater/SKILL.md and read by ahel’s review.
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-webrepository.
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) andpostmortem/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 eachunits/<slug>/unit.json. These are the Units. Readpostmortem/06-units.mdif present — it pre-classifies the provenance blocks (NEW vs REUSED). NEVER fabricate units from the rawartifacts/dump. -
Read
BRIEF.md+ (if present)scenario.jsonheaders 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, fromindex.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. Fromindex.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 asub(character/location/prop/music). Multi-value.
- Template — from
-
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'sscripts/*.shbake scripts + the capturedblueprint.jsonrecipes[].commandfor bakes,index.htmlfor HyperFrames overlays, the gen-log /prompts/**for prompt recipes. When kept, author the FULL enriched payload:recipeKind(ffmpeg/encode/overlay/bake/hyperframes/prompt) +body(markdown how-to) +artifact(the real code) +params(named knobs) +demo(a self-contained runnabledemo.htmlfor HyperFrames recipes; before/after media for ffmpeg recipes when real frames exist — never fabricated). - Tag (NOT a block, no detail page) if it is a pure textual descriptor with NO extractable artifact (e.g. "rain overlay" / "soft bloom" applied only as a vibe). It becomes a
tags[]entry on the Unit(s) that used it, carried inunit.json. - The failure mode to kill: never publish an EMPTY recipe block (refs:0, no
body, noartifact). That empty chip is the "tag cloud" anti-pattern. If you cannot author a real artifact, it is a tag.
- 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:
-
Match existing blocks FIRST (de-dup before you author). Before proposing ANY new block — and before authoring a kept recipe's
body/artifact— check whether an existing library block already covers it. Read../ralphy-web/lib/library-v2/library.json, and useralphy template list/ralphy template suggestfor template/style candidates andralphy assets list --kind <kind>for asset candidates. Ifpostmortem/06-units.mdalready marked a block REUSED, trust that. Only propose a NEW block for a genuine gap. Worked cautionary example:choose-path-xfade-masterwas published as a NEW recipe when the canonicalffmpeg-xfade-masteralready carried that exact artifact — a duplicate that #081/#083 had to delete and repoint. Reuse + cite the existing slug; never publish a second copy. Over-creating duplicate blocks is the failure mode this step prevents. -
Classify slots + tags + descriptions (LLM, via
callLLM()). Throughcli/lib/providers/llm.ts → callLLM():- Slots — extract
{{slots}}(brand / product / character names / location keys / target language) fromprompts.jsonperreferences/slot-detection.md, so the Style block's prompt cookbook is reusable across subjects. - Format — confirm each Unit's
format(it is inunit.json; validate it against the eight library formats). - Unit tags — the descriptors demoted from step 4 land in each Unit's
unit.jsontags[](filter-only labels for the feed'sTAGSfacet). - Category + description — per
references/category-classifier.md, for any localtemplates/<category>/<slug>/artifact. - English-only on disk. ANY captured prose — storyboards, prompts, VO lines, block bodies, tags, descriptions — must land in English. Translate folklore / foreign terms before they touch a file (the swamp lesson: the Russian word nechist → "the unclean"). The user can chat in any language; the on-disk artifact is English. This is
docs/developing-ralphy.md's hard rule — the publish gaterg '\p{Cyrillic}'must come back empty.
- Slots — extract
-
Capture each Unit's Blueprint (#080 — local + free). For EACH Unit, run
ralphy blueprint create <project-id> --unit <slug>. This is the per-unit reproduction recipe: it reads the (gitignored) project files and writes a self-containedunits/<slug>/blueprint/payload (blueprint.json+ copiedindex.html/ prompt files / hard-asset files), validated againstBlueprintSchema. It is extraction only — reads local files, makes no network call, costs nothing, and is append-only (a re-run on a slug that already has ablueprint/writesblueprint.v2/, never overwrites). Record the result in the bundle asunits[].blueprint(statusNEWif this run created the firstblueprint/,REUSEDif a prior capture already existed, with the payload pathunits/<slug>/blueprint/). Seereferences/extraction-rules.md→ "Blueprint capture (per Unit)". -
Emit the entity bundle. The primary output is a classified bundle: the Units (with resolved provenance, demoted
tags[], AND each Unit's captured Blueprint), plus the NEW blocks worth keeping — each recipe carrying its fullrecipeKind/body/artifact/params/demo. Mark every block NEW vs REUSED. This bundle is what the publish primitive consumes. Print it as JSON (see Output). -
(Optional) Write a local
templates/<category>/<slug>/artifact. When the user wants a downloadable repo template (thevibe-reference/vibe-styleform), write it undertemplates/<category>/<slug>/soralphy template list / show / suggest / usepick it up. This is ONE optional output, not the skill's reason for being — the entity publish is the #056 primitive. Schema is the onecli/commands/template.tsconsumes. Never modify the source project. -
Print the publish runbook (#056), do NOT push. Print the exact ordered
publish-entity.tscommands the user runs — dry-run first, then--push— for the NEW blocks (--block-file, recipes carrying the enriched fields), the Units (--unit, carryingunit.json.tags), and each Unit's Blueprint (--blueprint). See Publish runbook for the full ordered set + the DB-migration prerequisite. Then stop. The user (ordev-publish-template) drives the actual push.
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": "NEW" }],
"assets": [{ "slug": "free-air-mascot", "sub": "character", "status": "NEW" }]
},
"tags": ["white die-cut outline", "jelly mascot"],
"blueprint": {
"status": "NEW",
"path": ".ralphy/workspaces/<ws>/projects/free-air-vpn-stickerpack/units/stickers-outline/blueprint",
"publish_cmd": "cd ../ralphy-web && bun run scripts/publish-entity.ts --blueprint <absolute-blueprint-dir>"
},
"publish_cmd": "cd ../ralphy-web && bun run scripts/publish-entity.ts --unit <absolute-unit-dir>"
}
],
"new_blocks": [
{ "kind": "style", "slug": "free-air-jelly-pure", "blurb": "...", "publish_cmd": "cd ../ralphy-web && bun run scripts/publish-entity.ts --block-file <spec.json>" },
{
"kind": "recipe",
"slug": "floodfill-diecut-cutout",
"blurb": "...",
"recipeKind": "ffmpeg",
"body": "## What it is ...",
"artifact": "colorkey=0xffffff:0.1:0.0,despill=...",
"params": { "key": "0xffffff", "similarity": 0.1 },
"demo": { "kind": "media", "beforeUrl": "...", "afterUrl": "..." }
},
{ "kind": "asset", "slug": "free-air-mascot", "sub": "character", "blurb": "..." }
],
"reused_blocks": [{ "kind": "template", "slug": "sticker-set" }],
"demoted_tags": ["white die-cut outline", "jelly mascot"],
"local_template_artifact": null,
"scenario_present": false,
"warnings": []
}
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> # dry-run (per NEW block)
cd ../ralphy-web && bun run scripts/publish-entity.ts --block-file <block-spec.json> --push # push
2. Units next (carrying unit.json.tags + provenance links)
cd ../ralphy-web && bun run scripts/publish-entity.ts --unit <absolute-unit-dir> # dry-run (per Unit)
cd ../ralphy-web && bun run scripts/publish-entity.ts --unit <absolute-unit-dir> --push # 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> # dry-run (per Unit)
cd ../ralphy-web && bun run scripts/publish-entity.ts --blueprint <absolute-blueprint-dir> --push # 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 inasset-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, suggestralphy 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 fromindex.html/ slide layout, skip the scene table. - No
postmortem/→ proceed but warn. Block classification leans harder onprompts.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. (Thechoose-path-xfade-master==ffmpeg-xfade-masterdup is the canonical cautionary case — seereferences/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 emptyrefs:0recipe block. Seereferences/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 toralphy-assets/pool/(for Asset blocks / the local artifact).cli/lib/schemas/unit.ts— theunit.jsonZod schema (the Unit source of truth, #069).cli/commands/blueprint.ts— theralphy blueprint create|show|list|usesurface (#076/#079); templater runsblueprint createper Unit in step 7.cli/lib/schemas/blueprint.ts— theBlueprintSchemaZod 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'sartifact.../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), andUnit.tags.supabase/migrations/0001_init_library_v2.sql— the schema (theblueprintstable +blocks.recipe_kind/blocks.data/units.tagscolumns) 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..agents/skills/dev-publish-template/SKILL.md— the maintainer one-shot that runs the publish for you.
Signals
- GitHub stars
- 133
- Forks
- 13
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
templater- Source
- github.com/alecs5am/ralphy