feat(atelier): repo-root projects/, scaffolder, schemas (F1 + F3 + F5)

Closes the remaining three atelier audit gaps.

F1 — atelier projects live under repo-root projects/<slug>/ (the
established OpenMontage convention), not under remotion-composer/.
_render_via_atelier now auto-stages source files into
remotion-composer/projects/<slug>/ via mtime-skip copy when the entry
isn't already under the composer tree. Originally tried a directory
junction, but webpack walks up from the entry's REAL location to
resolve node_modules — junctions get dereferenced and module resolution
fails. Copy keeps source-of-truth under projects/, is invisible to the
user's project view, and is cheap (typical project is a few small .tsx
files). Output stays wherever output_path says. effective_entry is
returned in tool data for transparency.

F3 — scripts/scaffold_atelier_project.py generates the engine
boilerplate (index.tsx / Root.tsx / Composition.tsx / art-direction.md /
props template / README) ONLY. No creative content emitted — the
Composition.tsx is a deliberately blank black placeholder with prominent
TODOs. This removes the friction that nudged agents back to templated
mode while making it impossible to ship the scaffold unauthored (review
correctly flags black frames / silent audio as 'revise'). Verified
end-to-end: scaffold → render → review fires the right warnings.

F5 — schemas extended to know about atelier:
  - decision_log: 'composition_mode' added to category enum.
  - proposal_packet.production_plan: composition_mode + art_direction.
  - edit_decisions: composition_mode + bespoke{entry, composition_id,
    art_direction, props_path, public_dir, scale, crf, concurrency}.
  - final_review.checks: 'atelier' block (stock_reuse_detected,
    offending_imports[], art_direction_declared, art_direction).
  - final_review.recommended_action: 're_author' added.
  - final_review.transcript_comparison: allow null word_accuracy /
    transcript_matches_script (pre-existing latent bug surfaced here).
Both pass and fail shapes of the atelier render's final_review now
validate against the updated schema.
This commit is contained in:
calesthio
2026-06-27 11:15:36 -07:00
parent 5481226faf
commit 0f10381f2b
6 changed files with 389 additions and 15 deletions
@@ -29,6 +29,7 @@
"provider_selection",
"renderer_family_selection",
"render_runtime_selection",
"composition_mode",
"playbook_selection",
"fallback_decision",
"budget_tradeoff",
@@ -199,6 +199,26 @@
"enum": ["remotion", "hyperframes", "ffmpeg"],
"description": "Locked at proposal stage — technical runtime that realizes renderer_family. Edit MUST carry this forward unchanged unless a logged render_runtime_selection decision overrides it."
},
"composition_mode": {
"type": "string",
"enum": ["templated", "atelier"],
"description": "Locked at proposal — HOW the composition is built. Edit MUST carry forward unchanged. 'atelier' routes video_compose to _render_via_atelier (no cut-schema, no stock registry); requires `bespoke` block below."
},
"bespoke": {
"type": "object",
"description": "Required when composition_mode='atelier'. Contract for the hand-authored, project-local Remotion render. See tools/video/video_compose.py → _render_via_atelier docstring.",
"required": ["entry", "composition_id", "art_direction"],
"properties": {
"entry": { "type": "string", "description": "Path to the project-local Remotion entry .tsx (typically projects/<slug>/index.tsx). If outside remotion-composer/, auto-staged via directory junction/symlink at render time." },
"composition_id": { "type": "string", "description": "id registered in that entry's Root" },
"art_direction": { "type": "string", "description": "Short commitment to a fresh visual language (or a path to art-direction.md). REQUIRED — enforced by _run_atelier_checks." },
"props_path": { "type": "string", "description": "Absolute path to a props JSON (Remotion --props)" },
"public_dir": { "type": "string", "description": "Per-project public dir (avoids copying the bloated shared remotion-composer/public/)" },
"scale": { "type": "number", "minimum": 0.1, "maximum": 1.0 },
"crf": { "type": "integer", "minimum": 0, "maximum": 51 },
"concurrency": { "type": "integer", "minimum": 1 }
}
},
"slideshow_risk_score": {
"type": "object",
"description": "Slideshow risk assessment from lib/slideshow_risk.py",
+24 -3
View File
@@ -122,13 +122,34 @@
"type": "object",
"description": "Optional: transcribe the output and compare to source script",
"properties": {
"transcript_matches_script": { "type": "boolean" },
"word_accuracy": { "type": "number", "minimum": 0, "maximum": 1 },
"transcript_matches_script": { "type": ["boolean", "null"] },
"word_accuracy": { "type": ["number", "null"], "minimum": 0, "maximum": 1 },
"issues": {
"type": "array",
"items": { "type": "string" }
}
}
},
"atelier": {
"type": "object",
"description": "Atelier-only doctrine checks (populated when composition_mode='atelier'). Generated by tools/video/video_compose.py → _run_atelier_checks.",
"properties": {
"stock_reuse_detected": { "type": "boolean", "description": "TRUE if the bespoke project imports from the stock creative registry — fails the render." },
"offending_imports": {
"type": "array",
"description": "Files + import paths that violated the doctrine.",
"items": {
"type": "object",
"properties": {
"file": { "type": "string" },
"import": { "type": "string" }
}
}
},
"art_direction_declared": { "type": "boolean" },
"art_direction": { "type": ["string", "null"] },
"issues": { "type": "array", "items": { "type": "string" } }
}
}
},
"additionalProperties": false
@@ -140,7 +161,7 @@
},
"recommended_action": {
"type": "string",
"enum": ["present_to_user", "re_render", "revise_edit", "revise_assets", "block"],
"enum": ["present_to_user", "re_render", "revise_edit", "revise_assets", "block", "re_author"],
"description": "What the agent should do next based on the review findings"
},
"metadata": { "type": "object" }
@@ -155,6 +155,15 @@
"enum": ["remotion", "hyperframes", "ffmpeg"],
"description": "Locked at proposal stage — the technical runtime that realizes renderer_family. remotion=React scene components, hyperframes=HTML/CSS/GSAP, ffmpeg=simple concat/trim. Must be explicit and auditable; silent swaps are forbidden."
},
"composition_mode": {
"type": "string",
"enum": ["templated", "atelier"],
"description": "Locked at proposal — HOW the composition is built (orthogonal to render_runtime). templated=assemble stock cut.type scenes (Explainer/CinematicRenderer); atelier=hand-author a project-local composition from scratch with no creative-component reuse. Default atelier for hero work. See AGENT_GUIDE.md → 'Composition Authoring Mode' and skills/meta/bespoke-composition.md. The decision MUST be logged in decision_log with category='composition_mode' and BOTH options presented."
},
"art_direction": {
"type": "string",
"description": "Required when composition_mode='atelier'. Short note (or path to art-direction.md) committing to a fresh visual language for THIS piece — palette, type, motion, signature device. Per skills/meta/bespoke-composition.md step 1, written down BEFORE authoring scenes."
},
"music_source": {
"type": "object",
"description": "Resolved music plan from the proposal stage",