feat(atelier): post-render review hook + reviewer enforcement (F6 + F7)

Closes the two biggest gaps from the atelier audit: hero bespoke renders
were skipping every automated quality gate, and the templated→atelier
inversion was advisory rather than enforced.

F6 — _render_via_atelier now runs the full _run_final_review (technical
probe + visual spotcheck + audio spotcheck + duration drift + transcript
comparison) plus two atelier-specific doctrine checks:

  - stock_reuse_check: rglobs the bespoke project tree for imports from
    the stock creative registry (src/components, src/Explainer, etc.).
    Any hit fails the render with status="fail" and names the offending
    file — the doctrine bypass is now mechanically caught.
  - art_direction_check: requires edit_decisions.bespoke.art_direction.
    Missing → warning surfaced in final_review.issues_found; forcing
    function for step 1 of skills/meta/bespoke-composition.md.

Verified end-to-end on the compound-snowball proof render: positive case
status=pass, atelier checks clean; negative case (deliberate stock import
+ missing art_direction) returns status=fail with both findings named.

F7 — skills/meta/reviewer.md gets a "Composition Authoring Mode Review"
section that enforces the inversion at every stage:

  - proposal: composition_mode must be a presented decision with both
    options; hero briefs default to atelier; templated for hero requires
    an explicit sanctioned reason.
  - scene_plan/edit: any stock cut.type in an atelier piece is CRITICAL;
    bespoke.{entry, composition_id, art_direction} required.
  - compose: consumes final_review.checks.atelier from F6; mandates a
    recorded distinctness review (the human taste call the tool can't
    automate — its absence is itself CRITICAL).
  - publish: blocks ship on any unresolved atelier finding.

Engine knowledge stays reusable; creative components don't.
This commit is contained in:
calesthio
2026-06-27 11:01:00 -07:00
parent 578f0f1bb1
commit 5481226faf
2 changed files with 152 additions and 10 deletions
+30
View File
@@ -305,3 +305,33 @@ Run at **compose** and **publish** stages. Ensures the agent reviewed the actual
1. Verify that `final_review` was passed through as a required artifact
2. If `final_review.status` is not `pass`: **CRITICAL** — "Cannot publish with a non-passing self-review"
3. If `final_review.issues_found` is non-empty and `recommended_action` is not `present_to_user`: **SUGGESTION** — "Self-review found issues; verify they were resolved before publishing"
## Composition Authoring Mode Review
The templated→atelier inversion (`AGENT_GUIDE.md` → "Composition Authoring Mode" + `skills/meta/bespoke-composition.md`) is governance, not a suggestion. The reviewer is the enforcement point: without these checks, the next agent quietly defaults back to the stock cut-schema and every video starts looking the same again.
### At proposal stage:
1. `decision_log` must contain a `composition_mode` decision with `options_considered: ["templated","atelier"]` and a `selected` value with a real reason tied to the brief.
- Missing `composition_mode` decision entirely: **CRITICAL** — "Proposal missing composition_mode choice. Atelier vs templated is a mandatory presented decision (see AGENT_GUIDE.md → Composition Authoring Mode)."
- Decision logged with only one option considered: **CRITICAL** — "composition_mode decision logged without presenting both templated and atelier alternatives."
2. For **hero work** (brief tagged marketing / launch / brand piece / explainer-with-quality-bar / any single-deliverable where quality is the point) where `selected == "templated"`: **CRITICAL** — "Hero brief locked composition_mode='templated'. Default is atelier per doctrine; templated requires an explicit reason in `decision_log.<entry>.reason` (e.g. localization variant, batch, time-boxed draft)." Only suppress if the reason field names a sanctioned exception.
3. If `composition_mode == "atelier"` and `proposal_packet` lacks an `art_direction` declaration (palette, type, motion, signature device): **CRITICAL** — "Atelier proposal missing art-direction commitment. Per `skills/meta/bespoke-composition.md` step 1, art direction must be written down *before* authoring scenes."
### At scene_plan / edit stage (when composition_mode == "atelier"):
1. `edit_decisions.composition_mode` must equal `"atelier"` and `edit_decisions.bespoke.{entry, composition_id, art_direction}` must all be set.
- Missing any of `entry`/`composition_id`: **CRITICAL** — "Atelier compose contract incomplete; render will be rejected by `_render_via_atelier`."
- Missing `art_direction`: **CRITICAL** — "Atelier without an art-direction declaration; reviewer cannot evaluate distinctness."
2. Any presence of stock `cut.type` scene-types (`text_card`, `stat_card`, `bar_chart`, `kpi_grid`, `callout`, `comparison`, `hero_title`, `terminal_scene`, `anime_scene`, `progress_bar`, `pie_chart`, `line_chart`) in `edit_decisions.cuts`: **CRITICAL** — "Atelier piece reaches for stock cut.type {name}. Hand-author the scene; the stock registry is a mechanics codex, not a parts bin (`skills/meta/bespoke-composition.md`)."
### At compose stage (when composition_mode == "atelier"):
1. The compose stage's `final_review.checks.atelier` block must exist. If absent: **CRITICAL** — "Atelier render skipped doctrine checks — `_render_via_atelier` returned without `atelier` checks; investigate tool wiring."
2. If `final_review.checks.atelier.stock_reuse_detected == true`: **CRITICAL** — "Stock-registry import inside bespoke project ({offending_imports[0].file} → {offending_imports[0].import}). Hand-author the scene; do not import from the stock src/."
3. If `final_review.checks.atelier.art_direction_declared == false`: **CRITICAL** — "Atelier render with no art-direction declaration. Set `edit_decisions.bespoke.art_direction` before re-render."
4. **Distinctness review (human-judged, mandatory).** Before approving the render, the reviewer must explicitly answer in the review record:
- *"Could this video be any other product's video?"* (yes ⇒ CRITICAL, re-author art direction)
- *"Does its visual language reuse a look from a prior piece I've made?"* (yes ⇒ CRITICAL, re-author)
- *"Is the signature device named in `art_direction` actually present in the rendered frames?"* (no ⇒ CRITICAL, re-author or update the declaration to match what was actually built)
Distinctness is taste-call territory the tool can't automate; reviewer absence on this question is itself a **CRITICAL** finding ("distinctness review not recorded").
### At publish stage (when composition_mode == "atelier"):
1. All four atelier compose-stage findings above must show `resolved` in the review record. Any unresolved: **CRITICAL** — "Cannot publish atelier piece with unresolved doctrine or distinctness findings."
+122 -10
View File
@@ -789,18 +789,130 @@ class VideoCompose(BaseTool):
error=f"Atelier render completed but output file missing: {output_path}",
)
return ToolResult(
success=True,
data={
"operation": "render",
"composition_mode": "atelier",
"entry": str(entry_path),
"composition_id": comp_id,
"output": str(output_path),
},
artifacts=[str(output_path)],
# --- Atelier post-render review -------------------------------------
# The cut-schema paths run _run_final_review (technical/visual/audio
# probes + transcript-vs-script). Atelier MUST do the same so hero
# renders aren't shipped without the safety net — and additionally
# enforce the bespoke doctrine: no stock-registry imports, an
# art-direction declaration must exist. The distinctness review
# ("could this be any other product's video?") stays human; what we
# automate here is the *doctrine bypass*, not the taste call.
final_review = self._run_final_review(
output_path=output_path,
edit_decisions=edit_decisions,
proposal_packet=inputs.get("proposal_packet"),
narration_transcript_path=inputs.get("narration_transcript_path"),
script_text=inputs.get("script_text"),
)
atelier_checks = self._run_atelier_checks(entry_path, bespoke)
final_review.setdefault("checks", {})["atelier"] = atelier_checks
final_review["issues_found"] = list(final_review.get("issues_found", [])) + atelier_checks.get("issues", [])
# Escalate atelier-critical issues (stock reuse) to the overall status.
# Missing art-direction is a warning, not a fail — it shows in issues_found.
if atelier_checks.get("stock_reuse_detected"):
final_review["status"] = "fail"
final_review["recommended_action"] = "re_author"
data: dict[str, Any] = {
"operation": "render",
"composition_mode": "atelier",
"entry": str(entry_path),
"composition_id": comp_id,
"output": str(output_path),
"final_review": final_review,
"final_review_status": final_review.get("status"),
}
if final_review.get("status") == "fail":
return ToolResult(
success=False,
error=(
"Atelier render produced an invalid output:\n"
+ "\n".join(f"{i}" for i in final_review.get("issues_found", []))
),
data=data,
artifacts=[str(output_path)],
)
return ToolResult(success=True, data=data, artifacts=[str(output_path)])
# Stock-registry import patterns that violate the atelier doctrine.
# Any of these inside a bespoke project tree means a creative component
# was reused instead of hand-stitched. Engine knowledge (the `remotion`
# package, `@remotion/*`, project-local files) is fine.
_ATELIER_STOCK_IMPORT_RE = (
r"""from\s+["']("""
# parent-traversed paths into the stock src/
r"""(?:\.\./)+src/(?:components|Explainer|CinematicRenderer|"""
r"""TitledVideo|TalkingHead|CollageBurst|LyricOverlay|cinematic|crucix|phantom)"""
# or absolute-ish paths into the same
r"""|remotion-composer/src/(?:components|Explainer|CinematicRenderer|"""
r"""TitledVideo|TalkingHead|CollageBurst|LyricOverlay|cinematic|crucix|phantom)"""
r""")"""
)
def _run_atelier_checks(self, entry_path: Path, bespoke: dict[str, Any]) -> dict[str, Any]:
"""Doctrine-enforcement checks specific to atelier mode.
Returns a dict with two checks:
- stock_reuse_detected (bool) + offending_imports (list) — CRITICAL,
fails the render. Catches `import X from "../../src/components/..."`
and similar reuse of stock creative components.
- art_direction_declared (bool) + art_direction (str|None) — WARNING.
Forces step 1 of the bespoke-composition skill (commit to a fresh
art direction per video) to be written down rather than skipped.
"""
import re as _re
issues: list[str] = []
offending: list[dict[str, str]] = []
project_dir = entry_path.parent
pat = _re.compile(self._ATELIER_STOCK_IMPORT_RE)
try:
for f in project_dir.rglob("*"):
if not f.is_file() or f.suffix.lower() not in {".tsx", ".ts", ".jsx", ".js"}:
continue
try:
txt = f.read_text(encoding="utf-8", errors="replace")
except Exception:
continue
for m in pat.finditer(txt):
offending.append({"file": str(f.relative_to(project_dir)), "import": m.group(1)})
except Exception as e: # pragma: no cover — never let the check itself break a render
issues.append(f"atelier stock-reuse scan errored: {e}")
stock_reuse_detected = bool(offending)
if stock_reuse_detected:
issues.append(
"atelier doctrine violation: bespoke project imports from the stock "
"creative registry. Hand-author the scene instead — the registry is "
"a mechanics codex, not a parts bin. Offending imports: "
+ ", ".join(f"{o['file']}{o['import']}" for o in offending[:5])
+ ("" if len(offending) > 5 else "")
)
art_direction = bespoke.get("art_direction") or bespoke.get("art_direction_note")
art_direction_declared = bool(art_direction and str(art_direction).strip())
if not art_direction_declared:
issues.append(
"atelier warning: no bespoke.art_direction declared. Per "
"skills/meta/bespoke-composition.md step 1, every atelier piece must "
"commit to a fresh art direction (palette, type, motion, signature "
"device) before authoring. Pass edit_decisions.bespoke.art_direction "
"as a short note or a path to art-direction.md."
)
return {
"stock_reuse_detected": stock_reuse_detected,
"offending_imports": offending,
"art_direction_declared": art_direction_declared,
"art_direction": str(art_direction) if art_direction else None,
"issues": issues,
}
@staticmethod
def _build_theme_from_playbook(
playbook_name: str | None,