{ "pack_id": "visual-rendering", "title": "Visual and 3D rendering", "version": "0.1.0", "summary": "A collection of nouns and verbs for rendering images, motion graphics and 3D scenes and checking the results: renderer cards, scene, asset and motion recipes, quality checks with a contrast worked example, and verbs to plan, prepare, render, check and review.", "dependencies": [ "video-motion", "video-showcase" ], "boundaries": [ "Keep every output in the requester's scope. Releasing anything is the owner's separate decision.", "Rendering uses no model per frame.", "Importing the pack writes nothing and enqueues nothing." ], "nouns": [ { "slug": "pack", "category": "noun.skill_pack", "role": "pack", "title": "A collection of nouns and verbs for rendering images, motion graphics and 3D scenes and checking the results", "value": { "lets_workers": "Turn a visual brief into a scene recipe, assets and motion written as code, render it deterministically, check it automatically for size, frames, contrast, safe areas, missing resources and licenses, and have someone else review it.", "flow": [ { "verb": { "$verb": "visual-rendering.plan-scene" }, "when": "First, from a brief." }, { "verb": { "$verb": "visual-rendering.prepare-assets" }, "when": "For assets the scene needs: procedural, from licensed files, or generated and labeled." }, { "verb": { "$verb": "visual-rendering.render" }, "when": "After the recipe and assets exist; no model per frame." }, { "verb": { "$verb": "visual-rendering.check-render" }, "when": "After each render; deterministic." }, { "verb": { "$verb": "visual-rendering.review-render" }, "when": "Last, by a person or a different worker." } ], "motion_as_code": "Motion graphics are scene recipes whose elements reference motion profiles: easing, duration, keyframes and a reduced-motion alternative.", "quality_checks": { "$pin": "quality-checks" } }, "relations": [ [ "requires_noun", "quality-checks" ], [ "requires_noun", "example.contrast" ], [ "requires_noun", "tool.blender" ], [ "requires_noun", "tool.three-js" ], [ "requires_noun", "template.scene-recipe" ], [ "requires_noun", "template.asset-recipe" ], [ "requires_noun", "template.motion" ], [ "requires_noun", "template.render-manifest" ], [ "requires_noun", "template.check-report" ], [ "requires_noun", "template.review-notes" ], [ "requires_noun", "video-showcase:asset-policy" ], [ "requires_noun", "video-motion:tool.playwright" ], [ "requires_noun", "video-motion:tool.chromium" ], [ "requires_noun", "video-motion:tool.ffmpeg" ] ] }, { "slug": "quality-checks", "category": "noun.benchmark_suite", "role": "quality_checks", "title": "Render quality checks and thresholds", "category_reason": "A fixed set of checks with thresholds and methods that every render is scored against.", "value": { "checks": [ { "id": "dimensions-and-frames", "threshold": "Exact canvas width and height; frame count equals fps times duration, plus or minus one.", "method": "ffprobe or image headers." }, { "id": "color-space", "threshold": "The declared color space, sRGB unless the recipe says otherwise.", "method": "Read the output's color metadata." }, { "id": "empty-frames", "threshold": "No fully transparent or single-color frame unless the recipe declares it.", "method": "Per-frame pixel statistics." }, { "id": "repeatability", "threshold": "Two renders give identical digests for deterministic renderers, or SSIM of at least 0.99 for GPU renderers that are not bit-exact.", "method": "Render twice; compare digests or run ffmpeg's ssim filter." }, { "id": "text-contrast", "threshold": "Text contrast at least 4.5:1, or 3:1 for text 24 px and larger, against what is behind it.", "method": "WCAG relative luminance, as in example.contrast." }, { "id": "text-size", "threshold": "Overlay text at least 24 px tall at 1080p.", "method": "Font size from the recipe, scaled to the canvas." }, { "id": "safe-area", "threshold": "Every element inside the inner 90 percent of the canvas.", "method": "Element bounds from the recipe and the render." }, { "id": "missing-resources", "threshold": "The renderer log reports no missing texture, font or file.", "method": "Scan the render log." }, { "id": "render-budget", "threshold": "Wall time within the brief's budget.", "method": "The render manifest's wall_seconds." }, { "id": "asset-licenses", "threshold": "Every asset's license is allowed by the asset policy.", "method": "Deterministic join of elements and asset nouns." }, { "id": "reduced-motion", "threshold": "Every motion profile names a reduced-motion alternative.", "method": "Read the motion profiles." } ], "rules": [ "Every check reports its measurement.", "not_run and not_applicable always carry a reason; neither counts as pass.", "passed is true only if every check is pass or not_applicable." ], "contrast_example": { "$pin": "example.contrast" } }, "relations": [ [ "requires_noun", "example.contrast" ] ] }, { "slug": "example.contrast", "category": "noun.benchmark_suite", "role": "worked_example", "title": "SYNTHETIC worked example: text contrast ratios", "synthetic": true, "category_reason": "A golden fixture with reference values that the validator recomputes.", "value": { "fixture_kind": "contrast_ratio", "formula": "Relative luminance L = 0.2126 R + 0.7152 G + 0.0722 B of linearized sRGB channels (c/12.92 at or below 0.04045, else ((c + 0.055) / 1.055) ^ 2.4); ratio = (L_lighter + 0.05) / (L_darker + 0.05).", "thresholds": { "normal_text": 4.5, "large_text": 3.0 }, "pairs": [ { "id": "white-on-black", "foreground": "#ffffff", "background": "#000000" }, { "id": "gray-767676-on-white", "foreground": "#767676", "background": "#ffffff" }, { "id": "gray-777777-on-white", "foreground": "#777777", "background": "#ffffff" }, { "id": "yellow-on-white", "foreground": "#ffff00", "background": "#ffffff" }, { "id": "blue-on-white", "foreground": "#0000ff", "background": "#ffffff" } ], "expected": { "white-on-black": { "ratio": 21.0, "normal_text": true, "large_text": true }, "gray-767676-on-white": { "ratio": 4.54, "normal_text": true, "large_text": true }, "gray-777777-on-white": { "ratio": 4.48, "normal_text": false, "large_text": true }, "yellow-on-white": { "ratio": 1.07, "normal_text": false, "large_text": false }, "blue-on-white": { "ratio": 8.59, "normal_text": true, "large_text": true } }, "reading": [ "The expected ratios are the widely published reference values for these color pairs.", "#767676 is the lightest gray that passes 4.5:1 on white; #777777 just misses it." ] } }, { "slug": "tool.blender", "category": "noun.code_node", "role": "tool_card", "title": "Tool card: Blender", "category_reason": "A tool card pins an external renderer's identity, license and headless use.", "value": { "name": "Blender", "homepage": "https://www.blender.org", "source": "https://projects.blender.org/blender/blender", "tool_license": { "spdx": "GPL-2.0-or-later", "note": "Covers Blender's own code; read the license files of the exact build for bundled libraries. Work you make with Blender, such as renders and scene files, is yours and not covered by Blender's license.", "check": "https://www.blender.org/about/license/" }, "pin": null, "pin_reason": "Pin the exact version string and the build's SHA-256.", "headless": [ "blender -b scene.blend -o //render_#### -F PNG -f 1", "blender -b -P build_scene.py" ], "cautions": [ "GPU renders may differ slightly between machines; compare with SSIM instead of digests.", "Scripts run with full access inside the sandbox; run only reviewed scene scripts." ], "used_by": [ { "$verb": "visual-rendering.render" } ] } }, { "slug": "tool.three-js", "category": "noun.code_node", "role": "tool_card", "title": "Tool card: three.js", "category_reason": "A tool card pins a rendering library's identity, license and use.", "value": { "name": "three.js", "homepage": "https://threejs.org", "source": "https://github.com/mrdoob/three.js", "tool_license": { "spdx": "MIT" }, "pin": null, "pin_reason": "Pin the exact release and the bundle's SHA-256.", "use": "Render in a pinned headless browser build and capture the canvas frame by frame.", "cautions": [ "Headless WebGL may fall back to software rendering; record which one ran.", "Use a fixed device pixel ratio and canvas size for every frame." ], "used_by": [ { "$verb": "visual-rendering.render" } ] } }, { "slug": "template.scene-recipe", "category": "noun.scene_recipe", "role": "template", "title": "Template: a scene recipe: canvas, camera, lights, elements, palette, renderer, seed and output", "value": { "describes": "a scene recipe: canvas, camera, lights, elements, palette, renderer, seed and output", "schema": { "type": "object", "additionalProperties": false, "required": [ "scene_id", "kind", "canvas", "elements", "renderer", "output", "seed" ], "properties": { "scene_id": { "type": "string", "minLength": 1, "maxLength": 60, "pattern": "^[a-z0-9-]+$" }, "kind": { "type": "string", "enum": [ "image", "motion", "3d" ] }, "canvas": { "type": "object", "additionalProperties": false, "required": [ "width", "height" ], "properties": { "width": { "type": "integer", "minimum": 16, "maximum": 7680 }, "height": { "type": "integer", "minimum": 16, "maximum": 4320 }, "fps": { "type": "number", "minimum": 1, "maximum": 120 }, "duration_s": { "type": "number", "minimum": 0, "maximum": 600 }, "background": { "type": "string", "minLength": 0, "maxLength": 40 } } }, "camera": { "type": "object", "additionalProperties": false, "required": [], "properties": { "position": { "type": "array", "minItems": 3, "maxItems": 3, "items": { "type": "number" } }, "target": { "type": "array", "minItems": 3, "maxItems": 3, "items": { "type": "number" } }, "fov_deg": { "type": "number", "minimum": 1, "maximum": 179 } } }, "lights": { "type": "array", "minItems": 0, "maxItems": 10, "items": { "type": "object", "additionalProperties": false, "required": [ "kind" ], "properties": { "kind": { "type": "string", "enum": [ "ambient", "directional", "point", "area" ] }, "intensity": { "type": "number", "minimum": 0, "maximum": 1000 }, "color": { "type": "string", "minLength": 0, "maxLength": 9 } } } }, "elements": { "type": "array", "minItems": 1, "maxItems": 100, "items": { "type": "object", "additionalProperties": false, "required": [ "element_id", "asset_ref" ], "properties": { "element_id": { "type": "string", "minLength": 1, "maxLength": 60 }, "asset_ref": { "type": "string", "minLength": 1, "maxLength": 300 }, "position": { "type": "array", "minItems": 2, "maxItems": 3, "items": { "type": "number" } }, "scale": { "type": "number", "minimum": 0, "maximum": 100 }, "rotation_deg": { "type": "array", "minItems": 1, "maxItems": 3, "items": { "type": "number" } }, "motion_ref": { "type": "string", "minLength": 0, "maxLength": 300 }, "text": { "type": "string", "minLength": 0, "maxLength": 200 }, "font": { "type": "string", "minLength": 0, "maxLength": 80 }, "z": { "type": "integer", "minimum": 0, "maximum": 100 } } } }, "palette": { "type": "array", "minItems": 0, "maxItems": 12, "items": { "type": "string", "minLength": 1, "maxLength": 9, "pattern": "^#[0-9a-fA-F]{6}([0-9a-fA-F]{2})?$" } }, "renderer": { "type": "string", "minLength": 1, "maxLength": 160 }, "seed": { "type": "integer", "minimum": 0 }, "output": { "type": "object", "additionalProperties": false, "required": [ "format", "color_space" ], "properties": { "format": { "type": "string", "enum": [ "png", "webm", "mp4", "exr", "svg" ] }, "color_space": { "type": "string", "enum": [ "srgb", "linear", "display_p3" ] } } } } }, "rules": [ "Every element references an asset noun by pin.", "The seed makes procedural parts repeatable." ], "example": { "scene_id": "title-card", "kind": "motion", "canvas": { "width": 1920, "height": 1080, "fps": 30, "duration_s": 4, "background": "#101418" }, "elements": [ { "element_id": "title", "asset_ref": "SYNTHETIC font asset pin", "position": [ 960, 540 ], "scale": 1, "rotation_deg": [ 0 ], "motion_ref": "SYNTHETIC fade-up motion pin", "text": "One request, one checked result", "font": "Inter", "z": 1 } ], "palette": [ "#101418", "#f4f6f8", "#3d8bfd" ], "renderer": "SYNTHETIC: an SVG frame renderer rasterized with a pinned browser build", "seed": 7, "output": { "format": "webm", "color_space": "srgb" } } }, "category_reason": "A declarative scene layout and asset binding graph." }, { "slug": "template.asset-recipe", "category": "noun.scene_recipe", "role": "template", "title": "Template: one asset: procedural recipe, file digest or generated output, with its license", "value": { "describes": "one asset: procedural recipe, file digest or generated output, with its license", "schema": { "type": "object", "additionalProperties": false, "required": [ "asset_id", "kind", "source", "license", "generated" ], "properties": { "asset_id": { "type": "string", "minLength": 1, "maxLength": 60 }, "kind": { "type": "string", "enum": [ "vector", "raster", "mesh", "texture", "font", "material", "hdri" ] }, "source": { "type": "string", "enum": [ "procedural", "file", "generated" ] }, "recipe": { "type": "object" }, "blob_sha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" }, "license": { "type": "string", "minLength": 1, "maxLength": 120 }, "attribution": { "type": "string", "minLength": 0, "maxLength": 300 }, "generated": { "type": "boolean" }, "generator": { "type": "string", "minLength": 0, "maxLength": 200 }, "notes": { "type": "string", "minLength": 0, "maxLength": 300 } } }, "rules": [ "generated is true when a model made the asset, and generator names it.", "The license must be allowed by the asset policy." ] }, "category_reason": "An asset recipe is a binding of one asset into scenes with its provenance." }, { "slug": "template.motion", "category": "noun.motion_profile", "role": "template", "title": "Template: a motion profile for motion graphics as code: easing, duration, delay, keyframes and a reduced-motion alternative", "value": { "describes": "a motion profile for motion graphics as code: easing, duration, delay, keyframes and a reduced-motion alternative", "schema": { "type": "object", "additionalProperties": false, "required": [ "name", "easing", "duration_s" ], "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 80 }, "easing": { "type": "string", "enum": [ "linear", "ease_in", "ease_out", "ease_in_out", "spring", "cubic_bezier" ] }, "cubic_bezier": { "type": "array", "minItems": 4, "maxItems": 4, "items": { "type": "number", "minimum": -2, "maximum": 2 } }, "duration_s": { "type": "number", "minimum": 0.01, "maximum": 60 }, "delay_s": { "type": "number", "minimum": 0, "maximum": 60 }, "keyframes": { "type": "array", "minItems": 0, "maxItems": 50, "items": { "type": "object", "additionalProperties": false, "required": [ "t", "property", "value" ], "properties": { "t": { "type": "number", "minimum": 0, "maximum": 1 }, "property": { "type": "string", "minLength": 1, "maxLength": 40 }, "value": { "type": [ "number", "string" ] } } } }, "reduced_motion_alternative": { "type": "string", "minLength": 0, "maxLength": 200 } } }, "rules": [ "Keyframe times run from 0 to 1 across the duration.", "Every profile names a reduced-motion alternative." ], "example": { "name": "fade-up", "easing": "cubic_bezier", "cubic_bezier": [ 0.2, 0.0, 0.0, 1.0 ], "duration_s": 0.6, "delay_s": 0.2, "keyframes": [ { "t": 0, "property": "opacity", "value": 0 }, { "t": 1, "property": "opacity", "value": 1 }, { "t": 0, "property": "translate_y_px", "value": 24 }, { "t": 1, "property": "translate_y_px", "value": 0 } ], "reduced_motion_alternative": "Appear without movement." } }, "category_reason": "Parameterized keyframe curves are exactly what a motion profile holds." }, { "slug": "template.render-manifest", "category": "noun.eval_run", "role": "template", "title": "Template: the record of one render: tool, version, scenes, frames, digests, time and log warnings", "value": { "describes": "the record of one render: tool, version, scenes, frames, digests, time and log warnings", "schema": { "type": "object", "additionalProperties": false, "required": [ "renderer", "scenes", "model_calls" ], "properties": { "renderer": { "type": "object", "additionalProperties": false, "required": [ "tool", "version" ], "properties": { "tool": { "type": "string", "minLength": 1, "maxLength": 100 }, "version": { "type": "string", "minLength": 1, "maxLength": 100 }, "build_sha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" } } }, "scenes": { "type": "array", "minItems": 1, "maxItems": 50, "items": { "type": "object", "additionalProperties": false, "required": [ "scene_id", "frames", "output_sha256", "wall_seconds" ], "properties": { "scene_id": { "type": "string", "minLength": 1, "maxLength": 60 }, "frames": { "type": "integer", "minimum": 1 }, "output_sha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" }, "wall_seconds": { "type": [ "number", "null" ] }, "frame_sha256": { "type": "array", "minItems": 0, "maxItems": 20, "items": { "type": "string", "pattern": "^[0-9a-f]{64}$" } }, "log_warnings": { "type": "array", "minItems": 0, "maxItems": 20, "items": { "type": "string", "minLength": 1, "maxLength": 300 } } } } }, "model_calls": { "type": "integer", "minimum": 0, "maximum": 0 } } }, "rules": [ "model_calls is 0.", "Digests are of the produced files." ] }, "category_reason": "A render manifest is a fingerprinted run record." }, { "slug": "template.check-report", "category": "noun.eval_run", "role": "template", "title": "Template: automatic quality check results for one render", "value": { "describes": "automatic quality check results for one render", "schema": { "type": "object", "additionalProperties": false, "required": [ "scene_ref", "checks", "passed" ], "properties": { "scene_ref": { "type": "string", "minLength": 1, "maxLength": 300 }, "checks": { "type": "array", "minItems": 1, "maxItems": 20, "items": { "type": "object", "additionalProperties": false, "required": [ "id", "result", "measurement" ], "properties": { "id": { "type": "string", "minLength": 1, "maxLength": 60 }, "result": { "type": "string", "enum": [ "pass", "fail", "not_run", "not_applicable" ] }, "measurement": { "type": "string", "minLength": 1, "maxLength": 300 } } } }, "passed": { "type": "boolean" } } }, "rules": [ "Every quality check appears once with its measurement." ] }, "category_reason": "A check report is a scored run." }, { "slug": "template.review-notes", "category": "noun.eval_run", "role": "template", "title": "Template: a reviewer's notes and decision on one render", "value": { "describes": "a reviewer's notes and decision on one render", "schema": { "type": "object", "additionalProperties": false, "required": [ "reviewer_kind", "notes", "decision" ], "properties": { "reviewer_kind": { "type": "string", "enum": [ "human", "agent" ] }, "notes": { "type": "array", "minItems": 1, "maxItems": 20, "items": { "type": "string", "minLength": 1, "maxLength": 400 } }, "decision": { "type": "string", "enum": [ "ready", "revise", "reject" ] } } }, "rules": [ "The reviewer did not render it." ] }, "category_reason": "Review notes are a verification pass." } ], "verbs": [ { "slug": "plan-scene", "kind": "verb.solve_draft", "title": "Plan the scene", "claimant": "agent", "summary": "Turn a visual brief into a scene recipe, the asset recipes it needs and motion profiles for anything that moves.", "outcome": "Scene, asset and motion nouns, or no_change when an existing recipe already fits.", "ready_outcome": "scene_planned", "pinned": [ "quality-checks", "video-showcase:asset-policy" ], "slots": [ { "name": "brief", "category": "noun.seed_hypothesis", "description": "Pin of the visual brief, such as a video storyboard scene or a taste-palettes brief." } ], "produces": [ { "name": "scene_recipe", "template": "template.scene-recipe" }, { "name": "asset_recipes", "template": "template.asset-recipe", "max": 20 }, { "name": "motion_profiles", "template": "template.motion", "max": 10 } ], "steps": [ "Verify the pins. Choose the simplest renderer that meets the brief; prefer deterministic ones.", "Write motion as profiles with easing, duration and keyframes, each with a reduced-motion alternative.", "Pick colors and text sizes that will pass the contrast and size checks.", "Propose a prepare-assets verb for assets that do not exist yet.", "Keep every output in the requester's scope. Releasing anything is the owner's separate decision." ], "acceptance": [ { "id": "nouns-match-templates", "check": "Scene, asset and motion nouns validate against their templates.", "method": "deterministic" }, { "id": "assets-bound", "check": "Every element's asset_ref names an asset recipe produced or pinned.", "method": "deterministic" }, { "id": "fits-brief", "check": "The scene does what the brief asks.", "method": "judgment" } ], "bounds": { "max_elapsed_seconds": 1200 } }, { "slug": "prepare-assets", "kind": "verb.solve_draft", "title": "Prepare assets", "claimant": "agent", "summary": "Make or gather each asset a scene needs, procedurally, from licensed files or by labeled generation, and record its digest and license.", "outcome": "Asset nouns with blob digests and licenses.", "ready_outcome": "assets_ready", "pinned": [ "video-showcase:asset-policy" ], "slots": [ { "name": "asset_recipes", "category": "noun.scene_recipe", "description": "Pins of the asset recipes to fulfil.", "many": true, "max": 20 } ], "produces": [ { "name": "assets", "template": "template.asset-recipe", "max": 20 } ], "steps": [ "Verify the pins. Prefer procedural assets; use files only with a license the asset policy allows.", "Label generated assets with the generator and its terms.", "Store each asset as a blob and record its SHA-256.", "Keep every output in the requester's scope. Releasing anything is the owner's separate decision." ], "acceptance": [ { "id": "assets-match-template", "check": "Every asset validates against template.asset-recipe's schema.", "method": "deterministic" }, { "id": "licenses-allowed", "check": "Every license is on the asset policy's allowed list.", "method": "deterministic" }, { "id": "digests-recorded", "check": "Every file or generated asset has a blob digest.", "method": "deterministic" } ], "bounds": { "max_elapsed_seconds": 1800 }, "capabilities": [ "read permitted noun versions", "store blobs in the assigned scope", "write candidate nouns" ], "follows": [ "visual-rendering.plan-scene" ] }, { "slug": "render", "kind": "verb.compile_dag", "title": "Render", "claimant": "agent", "summary": "Render a scene recipe with its pinned renderer and assets, deterministically and with no model per frame, and record a manifest.", "outcome": "Rendered media and a render manifest with digests.", "ready_outcome": "rendered", "model_use": "none", "pinned": [ "tool.blender", "tool.three-js", "video-motion:tool.playwright", "video-motion:tool.chromium", "video-motion:tool.ffmpeg" ], "slots": [ { "name": "scene_recipe", "category": "noun.scene_recipe", "description": "Pin of the scene recipe." }, { "name": "assets", "category": "noun.scene_recipe", "description": "Pins of the scene's assets.", "many": true, "max": 40 }, { "name": "motion_profiles", "category": "noun.motion_profile", "description": "Pins of motion profiles, for motion scenes.", "many": true, "max": 10, "required": false } ], "params": { "renders": { "schema": { "type": "integer", "minimum": 1, "maximum": 2 }, "default": 2, "description": "Render twice to measure repeatability." } }, "produces": [ { "name": "rendered_media", "template": "video-motion:template.media", "max": 4 }, { "name": "render_manifest", "template": "template.render-manifest" } ], "steps": [ "Verify every pin, every asset digest and the renderer build before rendering.", "Render in a sandbox with no credentials; scene scripts run only if they are pinned and reviewed.", "Render twice when renders is 2 and record both digests.", "Record the renderer log's warnings in the manifest.", "Propose a check-render verb with the manifest pinned." ], "acceptance": [ { "id": "manifest-matches-template", "check": "The manifest validates against template.render-manifest's schema and model_calls is 0.", "method": "deterministic" }, { "id": "digests-recorded", "check": "Every output's digest is recorded.", "method": "deterministic" }, { "id": "assets-verified", "check": "Every asset digest matched its pin before rendering.", "method": "deterministic" } ], "bounds": { "max_elapsed_seconds": 3600 }, "capabilities": [ "read permitted noun versions", "qualified rendering tools in an isolated sandbox", "store blobs in the assigned scope", "write candidate nouns" ], "follows": [ "visual-rendering.prepare-assets" ] }, { "slug": "check-render", "kind": "verb.verify_gate", "title": "Check the render", "claimant": "agent", "summary": "Run every quality check on a render and record each measurement: size, frames, color space, empty frames, repeatability, contrast, text size, safe area, missing resources, budget, licenses and reduced motion.", "outcome": "A check report.", "ready_outcome": "checked", "model_use": "none", "pinned": [ "quality-checks", "example.contrast", "video-showcase:asset-policy" ], "slots": [ { "name": "render_manifest", "category": "noun.eval_run", "description": "Pin of the render manifest." }, { "name": "scene_recipe", "category": "noun.scene_recipe", "description": "Pin of the scene recipe." }, { "name": "assets", "category": "noun.scene_recipe", "description": "Pins of the scene's assets.", "many": true, "max": 40 }, { "name": "motion_profiles", "category": "noun.motion_profile", "description": "Pins of motion profiles, for motion scenes.", "many": true, "max": 10, "required": false } ], "produces": [ { "name": "check_report", "template": "template.check-report" } ], "steps": [ "Reproduce the contrast example's expected ratios first; stop with blocked if they differ.", "Run each quality check and record its measurement; not_run and not_applicable carry reasons.", "Set passed only when every check is pass or not_applicable.", "Propose a render or plan-scene verb with the failing checks named when something fails." ], "acceptance": [ { "id": "report-matches-template", "check": "The report validates against template.check-report's schema.", "method": "deterministic" }, { "id": "all-checks", "check": "Every quality check appears exactly once.", "method": "deterministic" }, { "id": "contrast-reproduced", "check": "The contrast example's expected ratios were reproduced.", "method": "deterministic" }, { "id": "passed-consistent", "check": "passed is true exactly when every check is pass or not_applicable.", "method": "deterministic" } ], "bounds": { "max_elapsed_seconds": 900 }, "follows": [ "visual-rendering.render" ] }, { "slug": "review-render", "kind": "verb.verify_gate", "title": "Review the render", "claimant": "agent_or_human", "summary": "A person or a different worker looks at the render and its check report and decides whether it is ready.", "outcome": "Review notes with a decision.", "ready_outcome": "reviewed", "pinned": [ "quality-checks" ], "slots": [ { "name": "check_report", "category": "noun.eval_run", "description": "Pin of the check report." }, { "name": "rendered_media", "category": "noun.scene_recipe", "description": "Pins of the rendered outputs.", "many": true, "max": 4 } ], "produces": [ { "name": "review_notes", "template": "template.review-notes" } ], "steps": [ "Confirm you did not render it; otherwise return blocked.", "Look at the actual output, not only the report.", "Decide ready, revise or reject.", "Keep every output in the requester's scope. Releasing anything is the owner's separate decision." ], "acceptance": [ { "id": "notes-match-template", "check": "The notes validate against template.review-notes's schema.", "method": "deterministic" }, { "id": "distinct-reviewer", "check": "The reviewer is not the render's author.", "method": "deterministic" }, { "id": "ready-needs-pass", "check": "A ready decision requires a check report with passed true.", "method": "deterministic" } ], "bounds": { "max_elapsed_seconds": 1800 }, "review": "distinct_reviewer", "follows": [ "visual-rendering.check-render" ] } ] }