diff --git a/contracts/artifact.schema.json b/contracts/artifact.schema.json new file mode 100644 index 0000000..e32780b --- /dev/null +++ b/contracts/artifact.schema.json @@ -0,0 +1,1767 @@ +{ + "type": "object", + "properties": { + "schema_version": { + "const": "1.0" + }, + "project_id": { + "type": "string", + "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$" + }, + "stage_id": { + "enum": [ + "01-topic", + "02-research", + "03-script", + "04-title", + "05-tts-split", + "06-tts", + "07-cuts", + "08-scenes", + "09-entities", + "10-references", + "11-images", + "12-motion", + "13-compose", + "14-review" + ] + }, + "revision": { + "type": "integer", + "minimum": 1 + }, + "inputs": { + "type": "array", + "items": { + "$ref": "#/$defs/stage_ref" + }, + "uniqueItems": true + }, + "payload": { + "type": "object" + } + }, + "required": [ + "schema_version", + "project_id", + "stage_id", + "revision", + "inputs", + "payload" + ], + "additionalProperties": false, + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$defs": { + "stage_ref": { + "type": "object", + "properties": { + "stage_id": { + "enum": [ + "01-topic", + "02-research", + "03-script", + "04-title", + "05-tts-split", + "06-tts", + "07-cuts", + "08-scenes", + "09-entities", + "10-references", + "11-images", + "12-motion", + "13-compose", + "14-review" + ] + }, + "revision": { + "type": "integer", + "minimum": 1 + } + }, + "required": [ + "stage_id", + "revision" + ], + "additionalProperties": false + }, + "issue": { + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "message": { + "type": "string" + }, + "blocking": { + "type": "boolean" + } + }, + "required": [ + "code", + "message", + "blocking" + ], + "additionalProperties": false + }, + "media": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "kind": { + "enum": [ + "audio", + "image", + "video" + ] + }, + "verified": { + "type": "boolean" + }, + "duration_ms": { + "type": [ + "integer", + "null" + ], + "minimum": 0 + }, + "width": { + "type": [ + "integer", + "null" + ], + "minimum": 1 + }, + "height": { + "type": [ + "integer", + "null" + ], + "minimum": 1 + } + }, + "required": [ + "path", + "kind", + "verified", + "duration_ms", + "width", + "height" + ], + "additionalProperties": false, + "allOf": [ + { + "if": { + "properties": { + "kind": { + "const": "audio" + } + } + }, + "then": { + "properties": { + "width": { + "type": "null" + }, + "height": { + "type": "null" + } + } + } + }, + { + "if": { + "properties": { + "kind": { + "const": "image" + } + } + }, + "then": { + "properties": { + "duration_ms": { + "type": "null" + } + } + } + }, + { + "if": { + "properties": { + "verified": { + "const": true + } + } + }, + "then": { + "properties": { + "path": { + "type": "string", + "minLength": 1 + } + }, + "allOf": [ + { + "if": { + "properties": { + "kind": { + "enum": [ + "audio", + "video" + ] + } + } + }, + "then": { + "properties": { + "duration_ms": { + "type": "integer", + "minimum": 1 + } + } + } + }, + { + "if": { + "properties": { + "kind": { + "enum": [ + "image", + "video" + ] + } + } + }, + "then": { + "properties": { + "width": { + "type": "integer", + "minimum": 1 + }, + "height": { + "type": "integer", + "minimum": 1 + } + } + } + } + ] + } + } + ] + }, + "unit": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "block_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "display_text": { + "type": "string" + }, + "spoken_text": { + "type": "string" + }, + "pronunciation_notes": { + "type": "string" + } + }, + "required": [ + "id", + "block_ids", + "display_text", + "spoken_text", + "pronunciation_notes" + ], + "additionalProperties": false + }, + "caption": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "start_ms": { + "type": [ + "integer", + "null" + ], + "minimum": 0 + }, + "end_ms": { + "type": [ + "integer", + "null" + ], + "minimum": 0 + }, + "text": { + "type": "string" + } + }, + "required": [ + "id", + "start_ms", + "end_ms", + "text" + ], + "additionalProperties": false + }, + "01-topic": { + "type": "object", + "properties": { + "candidates": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "angle": { + "type": "string" + }, + "summary": { + "type": "string" + } + }, + "required": [ + "id", + "angle", + "summary" + ], + "additionalProperties": false + } + }, + "selected_id": { + "anyOf": [ + { + "type": "null" + }, + { + "type": "string" + } + ] + }, + "selection_reason": { + "type": "string" + } + }, + "required": [ + "candidates", + "selected_id", + "selection_reason" + ], + "additionalProperties": false + }, + "02-research": { + "type": "object", + "properties": { + "sources": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "title": { + "type": "string" + }, + "url": { + "type": "string" + }, + "publisher": { + "type": "string" + }, + "accessed_at": { + "type": "string", + "format": "date" + } + }, + "required": [ + "id", + "title", + "url", + "publisher", + "accessed_at" + ], + "additionalProperties": false + } + }, + "claims": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "text": { + "type": "string" + }, + "source_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "assessment": { + "enum": [ + "verified", + "uncertain" + ] + } + }, + "required": [ + "id", + "text", + "source_ids", + "assessment" + ], + "additionalProperties": false + } + }, + "open_questions": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "sources", + "claims", + "open_questions" + ], + "additionalProperties": false + }, + "03-script": { + "type": "object", + "properties": { + "blocks": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "text": { + "type": "string" + }, + "claim_ids": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "id", + "text", + "claim_ids" + ], + "additionalProperties": false + } + }, + "full_text": { + "type": "string" + }, + "estimated_duration_ms": { + "type": [ + "integer", + "null" + ], + "minimum": 0 + } + }, + "required": [ + "blocks", + "full_text", + "estimated_duration_ms" + ], + "additionalProperties": false + }, + "04-title": { + "type": "object", + "properties": { + "candidates": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "text": { + "type": "string" + }, + "rationale": { + "type": "string" + } + }, + "required": [ + "id", + "text", + "rationale" + ], + "additionalProperties": false + } + }, + "selected_id": { + "anyOf": [ + { + "type": "null" + }, + { + "type": "string" + } + ] + } + }, + "required": [ + "candidates", + "selected_id" + ], + "additionalProperties": false + }, + "05-tts-split": { + "type": "object", + "properties": { + "units": { + "type": "array", + "items": { + "$ref": "#/$defs/unit" + } + } + }, + "required": [ + "units" + ], + "additionalProperties": false + }, + "06-tts": { + "type": "object", + "properties": { + "request": { + "type": "object", + "properties": { + "engine": { + "const": "Qwen3-TTS" + }, + "conda_env": { + "const": "qwen3-tts" + }, + "language": { + "type": "string" + }, + "units": { + "type": "array", + "items": { + "$ref": "#/$defs/unit" + } + } + }, + "required": [ + "engine", + "conda_env", + "language", + "units" + ], + "additionalProperties": false + }, + "result": { + "anyOf": [ + { + "type": "null" + }, + { + "type": "object", + "properties": { + "engine": { + "const": "Qwen3-TTS" + }, + "model": { + "type": "string" + }, + "voice": { + "type": "string" + }, + "files": { + "type": "array", + "items": { + "type": "object", + "properties": { + "unit_id": { + "type": "string" + }, + "media": { + "allOf": [ + { + "$ref": "#/$defs/media" + }, + { + "properties": { + "kind": { + "const": "audio" + } + } + } + ] + } + }, + "required": [ + "unit_id", + "media" + ], + "additionalProperties": false + } + }, + "narration": { + "allOf": [ + { + "$ref": "#/$defs/media" + }, + { + "properties": { + "kind": { + "const": "audio" + } + } + } + ] + }, + "unit_ranges": { + "type": "array", + "items": { + "type": "object", + "properties": { + "unit_id": { + "type": "string" + }, + "start_ms": { + "type": [ + "integer", + "null" + ], + "minimum": 0 + }, + "end_ms": { + "type": [ + "integer", + "null" + ], + "minimum": 0 + } + }, + "required": [ + "unit_id", + "start_ms", + "end_ms" + ], + "additionalProperties": false + } + } + }, + "required": [ + "engine", + "model", + "voice", + "files", + "narration", + "unit_ranges" + ], + "additionalProperties": false + } + ] + } + }, + "required": [ + "request", + "result" + ], + "additionalProperties": false + }, + "07-cuts": { + "type": "object", + "properties": { + "narration_duration_ms": { + "type": [ + "integer", + "null" + ], + "minimum": 0 + }, + "timing_evidence": { + "type": "string" + }, + "cuts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "start_ms": { + "type": [ + "integer", + "null" + ], + "minimum": 0 + }, + "end_ms": { + "type": [ + "integer", + "null" + ], + "minimum": 0 + }, + "unit_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "narration_text": { + "type": "string" + } + }, + "required": [ + "id", + "start_ms", + "end_ms", + "unit_ids", + "narration_text" + ], + "additionalProperties": false + } + }, + "captions": { + "type": "array", + "items": { + "$ref": "#/$defs/caption" + } + } + }, + "required": [ + "narration_duration_ms", + "timing_evidence", + "cuts", + "captions" + ], + "additionalProperties": false + }, + "08-scenes": { + "type": "object", + "properties": { + "scenes": { + "type": "array", + "items": { + "type": "object", + "properties": { + "cut_id": { + "type": "string" + }, + "description": { + "type": "string" + }, + "start_state": { + "type": "string" + }, + "end_state": { + "type": "string" + }, + "camera": { + "type": "string" + } + }, + "required": [ + "cut_id", + "description", + "start_state", + "end_state", + "camera" + ], + "additionalProperties": false + } + }, + "connections": { + "type": "array", + "items": { + "type": "object", + "properties": { + "from_cut_id": { + "type": "string" + }, + "to_cut_id": { + "type": "string" + }, + "mode": { + "enum": [ + "continuous", + "cut" + ] + }, + "reason": { + "type": "string" + } + }, + "required": [ + "from_cut_id", + "to_cut_id", + "mode", + "reason" + ], + "additionalProperties": false + } + } + }, + "required": [ + "scenes", + "connections" + ], + "additionalProperties": false + }, + "09-entities": { + "type": "object", + "properties": { + "characters": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "description": { + "type": "string" + }, + "fixed_features": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "id", + "description", + "fixed_features" + ], + "additionalProperties": false + } + }, + "backgrounds": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "description": { + "type": "string" + }, + "fixed_features": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "id", + "description", + "fixed_features" + ], + "additionalProperties": false + } + }, + "appearances": { + "type": "array", + "items": { + "type": "object", + "properties": { + "cut_id": { + "type": "string" + }, + "character_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "background_ids": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "cut_id", + "character_ids", + "background_ids" + ], + "additionalProperties": false + } + } + }, + "required": [ + "characters", + "backgrounds", + "appearances" + ], + "additionalProperties": false + }, + "10-references": { + "type": "object", + "properties": { + "request": { + "type": "object", + "properties": { + "model": { + "const": "gpt-image-2.5-sunburst" + }, + "style_reference": { + "const": "references/1.png" + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "kind": { + "enum": [ + "style", + "character", + "background" + ] + }, + "entity_id": { + "anyOf": [ + { + "type": "null" + }, + { + "type": "string" + } + ] + }, + "prompt": { + "type": "string" + } + }, + "required": [ + "id", + "kind", + "entity_id", + "prompt" + ], + "additionalProperties": false, + "allOf": [ + { + "if": { + "properties": { + "kind": { + "const": "style" + } + } + }, + "then": { + "properties": { + "entity_id": { + "type": "null" + } + } + }, + "else": { + "properties": { + "entity_id": { + "type": "string" + } + } + } + } + ] + } + } + }, + "required": [ + "model", + "style_reference", + "items" + ], + "additionalProperties": false + }, + "result": { + "anyOf": [ + { + "type": "null" + }, + { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "reference_id": { + "type": "string" + }, + "media": { + "allOf": [ + { + "$ref": "#/$defs/media" + }, + { + "properties": { + "kind": { + "const": "image" + } + } + } + ] + } + }, + "required": [ + "reference_id", + "media" + ], + "additionalProperties": false + } + } + }, + "required": [ + "items" + ], + "additionalProperties": false + } + ] + } + }, + "required": [ + "request", + "result" + ], + "additionalProperties": false + }, + "11-images": { + "type": "object", + "properties": { + "request": { + "type": "object", + "properties": { + "model": { + "const": "gpt-image-2.5-sunburst" + }, + "frames": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "cut_id": { + "type": "string" + }, + "position": { + "enum": [ + "start", + "end" + ] + }, + "prompt": { + "type": "string" + }, + "reference_ids": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "id", + "cut_id", + "position", + "prompt", + "reference_ids" + ], + "additionalProperties": false + } + } + }, + "required": [ + "model", + "frames" + ], + "additionalProperties": false + }, + "result": { + "anyOf": [ + { + "type": "null" + }, + { + "type": "object", + "properties": { + "frames": { + "type": "array", + "items": { + "type": "object", + "properties": { + "frame_id": { + "type": "string" + }, + "media": { + "allOf": [ + { + "$ref": "#/$defs/media" + }, + { + "properties": { + "kind": { + "const": "image" + } + } + } + ] + } + }, + "required": [ + "frame_id", + "media" + ], + "additionalProperties": false + } + } + }, + "required": [ + "frames" + ], + "additionalProperties": false + } + ] + } + }, + "required": [ + "request", + "result" + ], + "additionalProperties": false + }, + "12-motion": { + "type": "object", + "properties": { + "request": { + "type": "object", + "properties": { + "clips": { + "type": "array", + "items": { + "type": "object", + "properties": { + "cut_id": { + "type": "string" + }, + "start_frame_id": { + "type": "string" + }, + "end_frame_id": { + "type": "string" + }, + "duration_ms": { + "type": [ + "integer", + "null" + ], + "minimum": 0 + }, + "motion_prompt": { + "type": "string" + } + }, + "required": [ + "cut_id", + "start_frame_id", + "end_frame_id", + "duration_ms", + "motion_prompt" + ], + "additionalProperties": false + } + } + }, + "required": [ + "clips" + ], + "additionalProperties": false + }, + "result": { + "anyOf": [ + { + "type": "null" + }, + { + "type": "object", + "properties": { + "clips": { + "type": "array", + "items": { + "type": "object", + "properties": { + "cut_id": { + "type": "string" + }, + "media": { + "allOf": [ + { + "$ref": "#/$defs/media" + }, + { + "properties": { + "kind": { + "const": "video" + } + } + } + ] + } + }, + "required": [ + "cut_id", + "media" + ], + "additionalProperties": false + } + } + }, + "required": [ + "clips" + ], + "additionalProperties": false + } + ] + } + }, + "required": [ + "request", + "result" + ], + "additionalProperties": false + }, + "13-compose": { + "type": "object", + "properties": { + "request": { + "type": "object", + "properties": { + "timeline": { + "type": "array", + "items": { + "type": "object", + "properties": { + "cut_id": { + "type": "string" + }, + "video_path": { + "type": "string" + }, + "start_ms": { + "type": [ + "integer", + "null" + ], + "minimum": 0 + }, + "end_ms": { + "type": [ + "integer", + "null" + ], + "minimum": 0 + } + }, + "required": [ + "cut_id", + "video_path", + "start_ms", + "end_ms" + ], + "additionalProperties": false + } + }, + "narration_path": { + "type": "string" + }, + "captions": { + "type": "array", + "items": { + "$ref": "#/$defs/caption" + } + }, + "title": { + "type": "string" + } + }, + "required": [ + "timeline", + "narration_path", + "captions", + "title" + ], + "additionalProperties": false + }, + "result": { + "anyOf": [ + { + "type": "null" + }, + { + "type": "object", + "properties": { + "video": { + "allOf": [ + { + "$ref": "#/$defs/media" + }, + { + "properties": { + "kind": { + "const": "video" + } + } + } + ] + }, + "captions_path": { + "type": "string" + } + }, + "required": [ + "video", + "captions_path" + ], + "additionalProperties": false + } + ] + } + }, + "required": [ + "request", + "result" + ], + "additionalProperties": false + }, + "14-review": { + "type": "object", + "properties": { + "checks": { + "type": "array", + "items": { + "type": "object", + "properties": { + "area": { + "enum": [ + "story", + "visual", + "audio", + "captions", + "timing", + "consistency" + ] + }, + "verdict": { + "enum": [ + "pass", + "fail", + "not_checked" + ] + }, + "evidence": { + "type": "string" + } + }, + "required": [ + "area", + "verdict", + "evidence" + ], + "additionalProperties": false + } + }, + "findings": { + "type": "array", + "items": { + "type": "object", + "properties": { + "severity": { + "enum": [ + "blocking", + "minor" + ] + }, + "description": { + "type": "string" + }, + "stage_id": { + "enum": [ + "01-topic", + "02-research", + "03-script", + "04-title", + "05-tts-split", + "06-tts", + "07-cuts", + "08-scenes", + "09-entities", + "10-references", + "11-images", + "12-motion", + "13-compose", + "14-review" + ] + }, + "cut_id": { + "anyOf": [ + { + "type": "null" + }, + { + "type": "string" + } + ] + } + }, + "required": [ + "severity", + "description", + "stage_id", + "cut_id" + ], + "additionalProperties": false + } + }, + "recommendation": { + "enum": [ + "pass", + "revise", + "incomplete" + ] + } + }, + "required": [ + "checks", + "findings", + "recommendation" + ], + "additionalProperties": false, + "allOf": [ + { + "if": { + "properties": { + "recommendation": { + "const": "pass" + } + } + }, + "then": { + "properties": { + "checks": { + "type": "array", + "items": { + "type": "object", + "properties": { + "area": { + "enum": [ + "story", + "visual", + "audio", + "captions", + "timing", + "consistency" + ] + }, + "verdict": { + "const": "pass" + }, + "evidence": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "area", + "verdict", + "evidence" + ], + "additionalProperties": false + }, + "minItems": 6, + "maxItems": 6, + "allOf": [ + { + "contains": { + "properties": { + "area": { + "const": "story" + } + }, + "required": [ + "area" + ] + }, + "minContains": 1, + "maxContains": 1 + }, + { + "contains": { + "properties": { + "area": { + "const": "visual" + } + }, + "required": [ + "area" + ] + }, + "minContains": 1, + "maxContains": 1 + }, + { + "contains": { + "properties": { + "area": { + "const": "audio" + } + }, + "required": [ + "area" + ] + }, + "minContains": 1, + "maxContains": 1 + }, + { + "contains": { + "properties": { + "area": { + "const": "captions" + } + }, + "required": [ + "area" + ] + }, + "minContains": 1, + "maxContains": 1 + }, + { + "contains": { + "properties": { + "area": { + "const": "timing" + } + }, + "required": [ + "area" + ] + }, + "minContains": 1, + "maxContains": 1 + }, + { + "contains": { + "properties": { + "area": { + "const": "consistency" + } + }, + "required": [ + "area" + ] + }, + "minContains": 1, + "maxContains": 1 + } + ] + }, + "findings": { + "not": { + "contains": { + "properties": { + "severity": { + "const": "blocking" + } + }, + "required": [ + "severity" + ] + } + } + } + } + } + } + ] + } + }, + "oneOf": [ + { + "properties": { + "stage_id": { + "const": "01-topic" + }, + "payload": { + "$ref": "#/$defs/01-topic" + } + } + }, + { + "properties": { + "stage_id": { + "const": "02-research" + }, + "payload": { + "$ref": "#/$defs/02-research" + } + } + }, + { + "properties": { + "stage_id": { + "const": "03-script" + }, + "payload": { + "$ref": "#/$defs/03-script" + } + } + }, + { + "properties": { + "stage_id": { + "const": "04-title" + }, + "payload": { + "$ref": "#/$defs/04-title" + } + } + }, + { + "properties": { + "stage_id": { + "const": "05-tts-split" + }, + "payload": { + "$ref": "#/$defs/05-tts-split" + } + } + }, + { + "properties": { + "stage_id": { + "const": "06-tts" + }, + "payload": { + "$ref": "#/$defs/06-tts" + } + } + }, + { + "properties": { + "stage_id": { + "const": "07-cuts" + }, + "payload": { + "$ref": "#/$defs/07-cuts" + } + } + }, + { + "properties": { + "stage_id": { + "const": "08-scenes" + }, + "payload": { + "$ref": "#/$defs/08-scenes" + } + } + }, + { + "properties": { + "stage_id": { + "const": "09-entities" + }, + "payload": { + "$ref": "#/$defs/09-entities" + } + } + }, + { + "properties": { + "stage_id": { + "const": "10-references" + }, + "payload": { + "$ref": "#/$defs/10-references" + } + } + }, + { + "properties": { + "stage_id": { + "const": "11-images" + }, + "payload": { + "$ref": "#/$defs/11-images" + } + } + }, + { + "properties": { + "stage_id": { + "const": "12-motion" + }, + "payload": { + "$ref": "#/$defs/12-motion" + } + } + }, + { + "properties": { + "stage_id": { + "const": "13-compose" + }, + "payload": { + "$ref": "#/$defs/13-compose" + } + } + }, + { + "properties": { + "stage_id": { + "const": "14-review" + }, + "payload": { + "$ref": "#/$defs/14-review" + } + } + } + ] +} diff --git a/contracts/project.schema.json b/contracts/project.schema.json new file mode 100644 index 0000000..3cd1a3a --- /dev/null +++ b/contracts/project.schema.json @@ -0,0 +1,501 @@ +{ + "type": "object", + "properties": { + "schema_version": { + "const": "1.0" + }, + "project_id": { + "type": "string", + "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$" + }, + "brief": { + "type": "object", + "properties": { + "topic": { + "type": "string" + }, + "language": { + "type": "string" + } + }, + "required": [ + "topic", + "language" + ], + "additionalProperties": false + }, + "settings": { + "type": "object", + "properties": { + "aspect_ratio": { + "const": "9:16" + }, + "max_duration_ms": { + "const": 180000 + }, + "max_cut_duration_ms": { + "const": 5000 + }, + "style_reference": { + "const": "references/1.png" + }, + "tts_engine": { + "const": "Qwen3-TTS" + }, + "conda_env": { + "const": "qwen3-tts" + }, + "image_model": { + "const": "gpt-image-2.5-sunburst" + } + }, + "required": [ + "aspect_ratio", + "max_duration_ms", + "max_cut_duration_ms", + "style_reference", + "tts_engine", + "conda_env", + "image_model" + ], + "additionalProperties": false + }, + "stages": { + "type": "array", + "items": { + "type": "object", + "properties": { + "stage_id": { + "enum": [ + "01-topic", + "02-research", + "03-script", + "04-title", + "05-tts-split", + "06-tts", + "07-cuts", + "08-scenes", + "09-entities", + "10-references", + "11-images", + "12-motion", + "13-compose", + "14-review" + ] + }, + "status": { + "enum": [ + "pending", + "draft", + "awaiting_input", + "awaiting_review", + "complete", + "stale", + "failed" + ] + }, + "revision": { + "anyOf": [ + { + "type": "null" + }, + { + "type": "integer", + "minimum": 1 + } + ] + }, + "artifact_path": { + "anyOf": [ + { + "type": "null" + }, + { + "type": "string" + } + ] + }, + "issues": { + "type": "array", + "items": { + "$ref": "#/$defs/issue" + } + } + }, + "required": [ + "stage_id", + "status", + "revision", + "artifact_path", + "issues" + ], + "additionalProperties": false + }, + "minItems": 14, + "maxItems": 14, + "allOf": [ + { + "contains": { + "properties": { + "stage_id": { + "const": "01-topic" + } + }, + "required": [ + "stage_id" + ] + }, + "minContains": 1, + "maxContains": 1 + }, + { + "contains": { + "properties": { + "stage_id": { + "const": "02-research" + } + }, + "required": [ + "stage_id" + ] + }, + "minContains": 1, + "maxContains": 1 + }, + { + "contains": { + "properties": { + "stage_id": { + "const": "03-script" + } + }, + "required": [ + "stage_id" + ] + }, + "minContains": 1, + "maxContains": 1 + }, + { + "contains": { + "properties": { + "stage_id": { + "const": "04-title" + } + }, + "required": [ + "stage_id" + ] + }, + "minContains": 1, + "maxContains": 1 + }, + { + "contains": { + "properties": { + "stage_id": { + "const": "05-tts-split" + } + }, + "required": [ + "stage_id" + ] + }, + "minContains": 1, + "maxContains": 1 + }, + { + "contains": { + "properties": { + "stage_id": { + "const": "06-tts" + } + }, + "required": [ + "stage_id" + ] + }, + "minContains": 1, + "maxContains": 1 + }, + { + "contains": { + "properties": { + "stage_id": { + "const": "07-cuts" + } + }, + "required": [ + "stage_id" + ] + }, + "minContains": 1, + "maxContains": 1 + }, + { + "contains": { + "properties": { + "stage_id": { + "const": "08-scenes" + } + }, + "required": [ + "stage_id" + ] + }, + "minContains": 1, + "maxContains": 1 + }, + { + "contains": { + "properties": { + "stage_id": { + "const": "09-entities" + } + }, + "required": [ + "stage_id" + ] + }, + "minContains": 1, + "maxContains": 1 + }, + { + "contains": { + "properties": { + "stage_id": { + "const": "10-references" + } + }, + "required": [ + "stage_id" + ] + }, + "minContains": 1, + "maxContains": 1 + }, + { + "contains": { + "properties": { + "stage_id": { + "const": "11-images" + } + }, + "required": [ + "stage_id" + ] + }, + "minContains": 1, + "maxContains": 1 + }, + { + "contains": { + "properties": { + "stage_id": { + "const": "12-motion" + } + }, + "required": [ + "stage_id" + ] + }, + "minContains": 1, + "maxContains": 1 + }, + { + "contains": { + "properties": { + "stage_id": { + "const": "13-compose" + } + }, + "required": [ + "stage_id" + ] + }, + "minContains": 1, + "maxContains": 1 + }, + { + "contains": { + "properties": { + "stage_id": { + "const": "14-review" + } + }, + "required": [ + "stage_id" + ] + }, + "minContains": 1, + "maxContains": 1 + } + ] + }, + "reviews": { + "type": "array", + "items": { + "type": "object", + "properties": { + "review_id": { + "enum": [ + "topic", + "story", + "scenes", + "final" + ] + }, + "decision": { + "enum": [ + "pending", + "approved", + "changes_requested", + "stale" + ] + }, + "artifact_refs": { + "type": "array", + "items": { + "$ref": "#/$defs/stage_ref" + }, + "uniqueItems": true + }, + "feedback": { + "type": "string" + } + }, + "required": [ + "review_id", + "decision", + "artifact_refs", + "feedback" + ], + "additionalProperties": false + }, + "minItems": 4, + "maxItems": 4, + "allOf": [ + { + "contains": { + "properties": { + "review_id": { + "const": "topic" + } + }, + "required": [ + "review_id" + ] + }, + "minContains": 1, + "maxContains": 1 + }, + { + "contains": { + "properties": { + "review_id": { + "const": "story" + } + }, + "required": [ + "review_id" + ] + }, + "minContains": 1, + "maxContains": 1 + }, + { + "contains": { + "properties": { + "review_id": { + "const": "scenes" + } + }, + "required": [ + "review_id" + ] + }, + "minContains": 1, + "maxContains": 1 + }, + { + "contains": { + "properties": { + "review_id": { + "const": "final" + } + }, + "required": [ + "review_id" + ] + }, + "minContains": 1, + "maxContains": 1 + } + ] + } + }, + "required": [ + "schema_version", + "project_id", + "brief", + "settings", + "stages", + "reviews" + ], + "additionalProperties": false, + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$defs": { + "stage_ref": { + "type": "object", + "properties": { + "stage_id": { + "enum": [ + "01-topic", + "02-research", + "03-script", + "04-title", + "05-tts-split", + "06-tts", + "07-cuts", + "08-scenes", + "09-entities", + "10-references", + "11-images", + "12-motion", + "13-compose", + "14-review" + ] + }, + "revision": { + "type": "integer", + "minimum": 1 + } + }, + "required": [ + "stage_id", + "revision" + ], + "additionalProperties": false + }, + "issue": { + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "message": { + "type": "string" + }, + "blocking": { + "type": "boolean" + } + }, + "required": [ + "code", + "message", + "blocking" + ], + "additionalProperties": false + } + } +} diff --git a/docs/workflow-contracts.md b/docs/workflow-contracts.md new file mode 100644 index 0000000..a2bc522 --- /dev/null +++ b/docs/workflow-contracts.md @@ -0,0 +1,117 @@ +# 제작 단계의 전달과 완료 기준 + +## 문서와 양식 + +이 문서는 제작 입력, 단계별 JSON, 진행 상태, 검토·수정 규칙의 공통 기준이다. 내용과 화면을 작성할 때는 [제작 지침서](short-video-guide.md)의 담당 단계에 필요한 절을 함께 읽는다. 양식·스키마는 실제 생성 프로그램이 아니며, 요청서를 작성한 것과 결과 파일을 받은 것을 구분한다. + +- [진행 기록 스키마](../contracts/project.schema.json)와 [빈 진행 기록](../templates/project.json) +- [단계 결과 스키마](../contracts/artifact.schema.json)와 [14단계 빈 양식](../templates/stages/) +- [사용자 검토 양식](../templates/review.md) + +스키마는 JSON Schema Draft 2020-12다. 파서는 필드·형식·열거값을 검사한다. 완료 판단에는 아래 의미·참조·실제 파일·사용자 응답 확인이 추가로 필요하다. 빈 문자열·배열·null은 미작성 상태를 표현하며, 형식 검사를 통과해도 완료가 아니다. 사용하지 않는 필드를 임의로 추가하지 않는다. + +## 제작 입력과 공통 설정 + +필수 입력은 `topic`, `language`이며 공백만 있거나 빠졌으면 보완한다. 언어 이름·코드를 의미 확인 후 보존한다. 입력 언어로 대본·제목·내레이션·자막을 작성하며 지원되지 않는 언어를 다른 언어로 바꾸지 않는다. 새로운 요청은 저장소에서 유일한 소문자 영숫자·하이픈 `project_id`로 시작한다. 빈 양식의 video-001은 복사 후 실제 식별자로 바꾼다. + +공통 설정은 세로 9:16, 최종 180000ms 이내, 각 컷 0ms 초과 5000ms 이하, 스타일 기준 `references/1.png`, TTS `Qwen3-TTS`, conda 환경 `qwen3-tts`, 이미지 모델 `gpt-image-2.5-sunburst`다. 그림체만 참고하고 캠핑 소재·인물·상표를 강제하지 않는다. 쉬운 표현은 대상 시청자를 어린이로 제한하는 규칙이 아니다. + +## 저장과 전달 + +```text +projects// + project.json + stages//r/artifact.json + stages//r/review.md + media/ +``` + +모든 데이터 경로는 저장소 루트 기준 상대 경로다. 현재 작업 폴더나 검토 문서의 위치를 기준으로 해석하지 않는다. 외부 실행파일 경로·키·인증정보를 양식에 기록하지 않는다. + +단계 결과는 `schema_version`="1.0", `project_id`, `stage_id`, `revision`, `inputs`, `payload`를 가진다. 버전은 1부터 시작하고 이전 결과를 덮어쓰지 않는다. `inputs`는 실제 사용한 선행 결과의 `{stage_id, revision}` 배열이다. 중복·자기 참조·다른 제작물 참조를 금지한다. 14는 13과 검수에서 직접 사용한 유효 선행 결과를 기록한다. + +`project.json`의 필드는 `schema_version`, `project_id`, `brief`, `settings`, `stages`, `reviews`다. stages는 아래 14개를 각각 한 번 포함하는 `{stage_id, status, revision, artifact_path, issues}` 배열이다. 결과가 없으면 revision/artifact_path는 null이다. issues는 `{code, message, blocking}` 배열이다. reviews는 네 검토를 각각 한 번 포함하는 `{review_id, decision, artifact_refs, feedback}` 배열이다. artifact_refs는 검토한 `{stage_id, revision}` 배열이고 feedback은 실제 선택·피드백을 기록한 문자열이다. + +총괄만 진행 기록의 상태·현재 결과 포인터·승인을 바꾼다. 담당은 자기 단계의 새 버전 결과와 review.md를 제출하고, 준비 여부·미확인 내용·문제를 총괄에 보고한다. 같은 단계·버전을 동시에 작성하지 않는다. review.md는 JSON의 의미를 보존하며 별도로 승인 내용을 바꾸지 않는다. + +## 단계별 내용과 완료 기준 + +필드의 정확한 구조와 타입은 해당 양식·스키마를 사용한다. 배열 순서가 이야기·음성·컷·합성 순서다. 배열 안의 식별자는 유일해야 하고 모든 참조는 같은 제작물의 사용 버전에 존재해야 한다. 빈 필수 텍스트와 미선택 값은 완료 전에 채운다. + +| 단계와 양식 | 선행 결과 | 필요한 내용과 완료 확인 | +|---|---|---| +| [01-topic](../templates/stages/01-topic.json) | 제작 입력 | 주제 안의 후보·선택 ID·이유. 선택은 후보에 존재하며 topic 승인 필요 | +| [02-research](../templates/stages/02-research.json) | 01 | 출처·주장·미해결 질문. verified 주장은 실제 확인한 출처를 하나 이상 참조. 불확실한 주장은 uncertain | +| [03-script](../templates/stages/03-script.json) | 01,02 | 블록별 텍스트·claim_ids·전체 대본·예상 길이. 순서와 내용 일치, 사실 주장은 verified. story 승인 필요 | +| [04-title](../templates/stages/04-title.json) | 03 | 제목 후보·이유·선택. 대본의 약속과 일치하며 story 승인 필요 | +| [05-tts-split](../templates/stages/05-tts-split.json) | 03 | 단위별 block_ids·표시/발화 텍스트·발음 메모. 순서·의미·누락·중복 확인 | +| [06-tts](../templates/stages/06-tts.json) | 05 | 도구·환경·언어·단위 요청. 실제 모델·화자·단위 WAV·통합 WAV·실측 unit_ranges 결과. 전체 파일과 시간 확인 | +| [07-cuts](../templates/stages/07-cuts.json) | 05,06 | 실측 통합 길이·확인 방법·연속 컷·별도 자막. 전체 음성을 덮고 각 컷 5초 이내. scenes 승인 필요 | +| [08-scenes](../templates/stages/08-scenes.json) | 03,07 | 각 컷의 설명·시작/끝 상태·카메라·인접 연결. 누락·중복 없음, 연결 이유·상태 일치. scenes 승인 필요 | +| [09-entities](../templates/stages/09-entities.json) | 03,08 | 인물·배경·고정 특징·컷별 등장 참조. 모든 컷을 정확히 한 번 포함. scenes 승인 필요 | +| [10-references](../templates/stages/10-references.json) | 02,08,09 | 모델·그림체·스타일/인물/배경 요청, 실제 PNG 결과. 모든 요청 ID와 고정 특징을 다룸. 스타일만 entity_id=null | +| [11-images](../templates/stages/11-images.json) | 02,03,07,08,09,10 | 컷별 start/end 요청·기준 ID·실제 PNG. 각 컷의 두 위치가 정확히 하나씩 존재, 장면·근거·연속성 일치 | +| [12-motion](../templates/stages/12-motion.json) | 07,08,11 | 각 컷의 프레임·실제 길이·움직임 요청과 MP4 결과. 모든 컷과 길이·상태 일치 | +| [13-compose](../templates/stages/13-compose.json) | 03,04,06,07,12 | timeline·통합 음성·자막·제목 요청, 실제 MP4·SRT 결과. 세로 비율·전체 길이·파일·시간·순서 확인 | +| [14-review](../templates/stages/14-review.json) | 13 및 검수에 사용한 결과 | 6영역 checks·발견·추천. 근거와 수정할 단계·컷. 검수자의 pass와 사용자의 final 승인 구분 | + +`*_ms`는 0 이상 정수이고 아직 정하지 않은 시간은 null이다. 완료한 길이는 양수다. 고정 특징·open_questions·각종 *_ids는 문자열 배열이다. accessed_at은 ISO 8601 날짜, blocking/verified는 불리언이다. 그 밖의 텍스트는 문자열이며 selected_id는 미선택 시 null, findings.cut_id는 특정 컷이 아니면 null이다. 실제 사용한 모델·화자는 06 result.model/voice에 기록한다. + +## 실제 파일과 시간 확인 + +media는 `{path, kind, verified, duration_ms, width, height}`다. kind는 audio/image/video, 미확인 값은 null, 검증하지 않은 파일은 verified=false다. 실제 파일 존재·내용 형식·메타데이터를 확인한 경우만 true다. 확장자나 요청 길이만으로 실제 결과를 확인했다고 쓰지 않는다. + +- 음성은 WAV, width/height=null, 실제 duration_ms 필요. +- 이미지는 PNG, duration_ms=null, 실제 width/height 필요. +- 영상은 MP4, 실제 duration_ms·width·height 필요. 최종 자막은 SRT이며 파일·언어·내용·정렬을 확인한다. + +외부 단계의 result=null이면 awaiting_input이다. 부분 결과는 보존하되 요청 항목을 모두 받기 전 complete로 처리하지 않는다. 결과의 unit_id/reference_id/frame_id/cut_id는 요청에 존재해야 한다. 가상 예제 경로는 검증된 실제 파일로 기록하지 않는다. + +Qwen3-TTS의 설치 탐색, 모델 로딩, 실제 추론 성공은 다른 확인이다. 기존 환경을 재설치하지 않고 실제 제작 시 모델·화자·언어 지원을 확인한다. 이미지 모델·세로 출력의 지원 여부도 실제 제작에서 확인하며, 확인하지 않은 API 파라미터나 다른 모델을 고정하지 않는다. 영상 생성·합성 엔진은 이번 범위에서 선택하지 않는다. + +06의 통합 음성이 시간 기준이다. unit_ranges는 해당 단위의 실제 통합 음성 구간을 기록하며 순서·전체 포함·겹침을 확인한다. 07 cuts는 0부터 통합 음성 끝까지 빈 구간·중복 없이 이어진다. 각 컷은 start_ms < end_ms, end_ms-start_ms <= 5000이다. 긴 문장을 화면 컷으로 나눌 수 있지만 단어·의미를 누락하거나 순서를 바꾸지 않는다. + +captions는 컷과 다른 단위다. 여러 컷을 걸칠 수 있으며 0 <= start_ms < end_ms <= 통합 길이, 텍스트·표시 순서를 보존한다. 글자 수 기반 예상 정렬을 실측으로 표시하지 않는다. timing_evidence에 파일 측정·청취·정렬 확인 방법을 적는다. 실제 음성이 180000ms를 넘으면 대본 수정과 story 재승인으로 돌아간다. + +## 상태와 네 사용자 검토 + +| 단계 상태 | 의미 | +|---|---| +| pending | 시작 전 | +| draft | 작성 중 | +| awaiting_input | 필수 자료·실제 결과·파일 부재 | +| awaiting_review | 검토 결과 버전을 제시하고 사용자 결정 대기 | +| complete | 완료 조건과 필요한 사용자 승인 충족 | +| stale | 사용한 선행 버전의 변경으로 다시 확인해야 함 | +| failed | 실행·검증 실패가 보고됨 | + +기본 흐름은 pending→draft, 자료 부재 시 draft→awaiting_input, 결과 준비 시 draft/awaiting_input→complete 또는 awaiting_review다. 승인 후 complete, 변경 요청·실패 복구는 draft에서 새 버전을 만든다. 그 밖의 전이는 현재 기록을 먼저 확인하고 issues에 이유를 남긴다. + +| 검토 ID | 사용자가 확인하는 결과 | 승인 전 진행하지 않는 곳 | +|---|---|---| +| topic | 01 각도·범위 | 02 자료 조사 | +| story | 02 근거와 03 대본·04 제목 | 05 TTS 분할 | +| scenes | 07 실제 컷·08 연결·09 인물·배경 | 10 기준 이미지 | +| final | 13 실제 완성본·14 보고 | 제작 완료 선언 | + +검토 결정은 pending/approved/changes_requested/stale다. 승인 버전을 artifact_refs로 고정하고 실제 사용자 응답을 feedback에 기록한다. 응답 없음·담당의 추천은 승인이 아니다. 같은 승인 버전으로 재개할 때는 다시 묻지 않는다. 한 검토 묶음 안에서는 준비된 유효 결과로 묶음을 완성할 수 있다. 예를 들어 07 결과로 08·09를 준비한 뒤 scenes 검토를 요청하되, 승인 전에 10으로 넘어가지 않는다. + +## 재개와 수정 + +재개는 brief·현재 포인터·버전별 결과·승인·실제 파일·참조의 유효성을 확인한 뒤 가장 앞선 미해결 작업부터 이어간다. 오래된 버전·다른 제작물의 결과를 섞지 않는다. 같은 주제의 새 제작과 기존 제작의 재개를 구분한다. + +수정은 새 r을 만든다. inputs의 실제 관계를 따라 영향을 받는 후속 결과를 stale로 표시하고, 해당 결과를 승인한 검토도 stale로 표시한다. 변경과 관계없는 결과는 유지한다. 가령 04 제목만 변경하면 이를 쓰는 13·14와 story/final 검토는 재확인하지만, 05–12의 기존 음성·장면·이미지를 무조건 재생성하지 않는다. story 승인을 받은 뒤 기존 미디어와 새 제목을 연결할 수 있다. + +언어 변경은 해당 언어를 사용하는 조사·대본·제목·발화·자막과 후속 결과를 재검토한다. 단순한 표시 수정이 의미·음성을 바꾸는지 확인하고 영향 범위와 보존할 결과를 보고한다. 승인 경계가 다시 열리면 새 후속 작업 전 재승인을 받는다. 이전 검토와 결과는 버전별 review.md에 보존한다. + +## 검수의 판정과 근거 + +14의 area는 story/visual/audio/captions/timing/consistency, verdict는 pass/fail/not_checked다. 모든 영역의 실제 근거가 있어야 하며 미확인 영역을 pass로 만들지 않는다. findings는 blocking/minor, 설명, 수정할 stage_id와 cut_id를 기록한다. 추천은 pass/revise/incomplete다. + +pass는 여섯 영역 모두 pass이고 blocking 발견이 없을 때만 가능하다. 실제 완성본이 없거나 볼 수 없으면 incomplete다. 제작 지침의 [최종 영상 검토](short-video-guide.md#최종-영상-검토) 질문을 기존 checks.evidence에 연결하며 새 필드·승인 지점을 만들지 않는다. + +## 형식 검사와 의미 확인의 경계 + +스키마는 잘못된 필드·타입·단계·상태, 진행 기록의 14단계/4검토 구성, 다른 도구명, 음성·이미지의 해당 없는 메타데이터, 근거 없는 pass 구조를 검사한다. 주제·언어의 실사용 값, 배열별 식별자·참조의 유효성, 선택 ID, 대본 일치, 파일 실존·언어 지원, 승인 진위, 실제 시간·길이·화면 판독성, 종속 결과의 stale 처리는 담당과 총괄이 별도로 확인한다. + +파싱·스키마 검사만으로 Codex의 실제 에이전트·스킬 탐색이나 미디어 생성을 확인했다고 쓰지 않는다. [공식 에이전트 형식](https://learn.chatgpt.com/docs/agent-configuration/subagents#custom-agents)과 [스킬 위치](https://learn.chatgpt.com/docs/build-skills#where-to-save-skills)를 따른다. 실제 호출 환경에서 목록에 보이는지 확인하고, 보이지 않으면 프로젝트 루트에서 새 세션으로 탐색한다. diff --git a/templates/project.json b/templates/project.json new file mode 100644 index 0000000..81b3ddc --- /dev/null +++ b/templates/project.json @@ -0,0 +1,143 @@ +{ + "schema_version": "1.0", + "project_id": "video-001", + "brief": { + "topic": "", + "language": "" + }, + "settings": { + "aspect_ratio": "9:16", + "max_duration_ms": 180000, + "max_cut_duration_ms": 5000, + "style_reference": "references/1.png", + "tts_engine": "Qwen3-TTS", + "conda_env": "qwen3-tts", + "image_model": "gpt-image-2.5-sunburst" + }, + "stages": [ + { + "stage_id": "01-topic", + "status": "pending", + "revision": null, + "artifact_path": null, + "issues": [] + }, + { + "stage_id": "02-research", + "status": "pending", + "revision": null, + "artifact_path": null, + "issues": [] + }, + { + "stage_id": "03-script", + "status": "pending", + "revision": null, + "artifact_path": null, + "issues": [] + }, + { + "stage_id": "04-title", + "status": "pending", + "revision": null, + "artifact_path": null, + "issues": [] + }, + { + "stage_id": "05-tts-split", + "status": "pending", + "revision": null, + "artifact_path": null, + "issues": [] + }, + { + "stage_id": "06-tts", + "status": "pending", + "revision": null, + "artifact_path": null, + "issues": [] + }, + { + "stage_id": "07-cuts", + "status": "pending", + "revision": null, + "artifact_path": null, + "issues": [] + }, + { + "stage_id": "08-scenes", + "status": "pending", + "revision": null, + "artifact_path": null, + "issues": [] + }, + { + "stage_id": "09-entities", + "status": "pending", + "revision": null, + "artifact_path": null, + "issues": [] + }, + { + "stage_id": "10-references", + "status": "pending", + "revision": null, + "artifact_path": null, + "issues": [] + }, + { + "stage_id": "11-images", + "status": "pending", + "revision": null, + "artifact_path": null, + "issues": [] + }, + { + "stage_id": "12-motion", + "status": "pending", + "revision": null, + "artifact_path": null, + "issues": [] + }, + { + "stage_id": "13-compose", + "status": "pending", + "revision": null, + "artifact_path": null, + "issues": [] + }, + { + "stage_id": "14-review", + "status": "pending", + "revision": null, + "artifact_path": null, + "issues": [] + } + ], + "reviews": [ + { + "review_id": "topic", + "decision": "pending", + "artifact_refs": [], + "feedback": "" + }, + { + "review_id": "story", + "decision": "pending", + "artifact_refs": [], + "feedback": "" + }, + { + "review_id": "scenes", + "decision": "pending", + "artifact_refs": [], + "feedback": "" + }, + { + "review_id": "final", + "decision": "pending", + "artifact_refs": [], + "feedback": "" + } + ] +} diff --git a/templates/review.md b/templates/review.md new file mode 100644 index 0000000..8caa8b5 --- /dev/null +++ b/templates/review.md @@ -0,0 +1,39 @@ +# 단계 결과 검토 + +## 검토 대상 + +- 제작 ID: +- 검토 ID와 결과 버전: topic / story / scenes / final 중 해당 검토와 stage_id·revision +- JSON 위치: +- 현재 상태와 확인하지 못한 내용: +- 사용자의 선택·피드백: 실제 응답 후 기록 + +JSON을 기준으로 요약하며 문서만 고쳐 승인 내용을 바꾸지 않는다. 이 양식은 결과·사용자 검토를 돕는 문서이고 자체 승인이 아니다. 검토 구분이 없는 중간 결과는 해당 없음으로 적는다. + +## 결과와 다음 진행 + +- 핵심 결과: +- 근거와 선행 버전: +- 완료 조건 충족 여부와 남은 문제: +- 사용자에게 필요한 결정: +- 결정에 따른 다음 작업 또는 돌아갈 단계: + +## 제작 지침 적용 + +[제작 지침서](../docs/short-video-guide.md)의 해당 단계 메모만 사용한다. 이 링크는 templates 위치 기준이다. 실제 제작 폴더로 복사할 때 저장소의 docs/short-video-guide.md를 가리키도록 문서 링크를 조정한다. JSON 내부 경로는 항상 저장소 루트 기준이다. + +- 적용한 구성 원칙과 실제 결과: +- 소재에 맞지 않아 적용하지 않은 구성과 이유: + +story 검토에는 중심 질문·답, 제목과 첫 문장의 약속, 답하는 블록, 블록 연결, 핵심 주장·출처, 비유의 한계와 덜어낸 내용을 적는다. + +scenes 검토에는 컷 ID·실측 음성 구간, 이해할 내용, 시작→끝 변화, 화면·연결 선택 이유, 인물·배경·근거 참조, 주석·숫자의 근거와 모바일 초점을 적는다. + +final 검토에는 story/visual/audio/captions/timing/consistency별 실제 파일·시간·청취·관찰 근거와 수정할 단계·컷을 적는다. JSON의 checks.evidence·findings와 일치시키고 미검수는 not_checked, 완성본 부재는 incomplete로 남긴다. + +## 수정과 보존 + +- 새 버전에서 바뀐 내용: +- 영향을 받는 후속 결과·검토: +- 그대로 사용할 기존 결과와 이유: +- 이전 버전의 위치: diff --git a/templates/stages/01-topic.json b/templates/stages/01-topic.json new file mode 100644 index 0000000..6bbd042 --- /dev/null +++ b/templates/stages/01-topic.json @@ -0,0 +1,12 @@ +{ + "schema_version": "1.0", + "project_id": "video-001", + "stage_id": "01-topic", + "revision": 1, + "inputs": [], + "payload": { + "candidates": [], + "selected_id": null, + "selection_reason": "" + } +} diff --git a/templates/stages/02-research.json b/templates/stages/02-research.json new file mode 100644 index 0000000..65c20eb --- /dev/null +++ b/templates/stages/02-research.json @@ -0,0 +1,12 @@ +{ + "schema_version": "1.0", + "project_id": "video-001", + "stage_id": "02-research", + "revision": 1, + "inputs": [], + "payload": { + "sources": [], + "claims": [], + "open_questions": [] + } +} diff --git a/templates/stages/03-script.json b/templates/stages/03-script.json new file mode 100644 index 0000000..41fbe8f --- /dev/null +++ b/templates/stages/03-script.json @@ -0,0 +1,12 @@ +{ + "schema_version": "1.0", + "project_id": "video-001", + "stage_id": "03-script", + "revision": 1, + "inputs": [], + "payload": { + "blocks": [], + "full_text": "", + "estimated_duration_ms": null + } +} diff --git a/templates/stages/04-title.json b/templates/stages/04-title.json new file mode 100644 index 0000000..49067a2 --- /dev/null +++ b/templates/stages/04-title.json @@ -0,0 +1,11 @@ +{ + "schema_version": "1.0", + "project_id": "video-001", + "stage_id": "04-title", + "revision": 1, + "inputs": [], + "payload": { + "candidates": [], + "selected_id": null + } +} diff --git a/templates/stages/05-tts-split.json b/templates/stages/05-tts-split.json new file mode 100644 index 0000000..eae37da --- /dev/null +++ b/templates/stages/05-tts-split.json @@ -0,0 +1,10 @@ +{ + "schema_version": "1.0", + "project_id": "video-001", + "stage_id": "05-tts-split", + "revision": 1, + "inputs": [], + "payload": { + "units": [] + } +} diff --git a/templates/stages/06-tts.json b/templates/stages/06-tts.json new file mode 100644 index 0000000..156b53e --- /dev/null +++ b/templates/stages/06-tts.json @@ -0,0 +1,16 @@ +{ + "schema_version": "1.0", + "project_id": "video-001", + "stage_id": "06-tts", + "revision": 1, + "inputs": [], + "payload": { + "request": { + "engine": "Qwen3-TTS", + "conda_env": "qwen3-tts", + "language": "", + "units": [] + }, + "result": null + } +} diff --git a/templates/stages/07-cuts.json b/templates/stages/07-cuts.json new file mode 100644 index 0000000..ac88f24 --- /dev/null +++ b/templates/stages/07-cuts.json @@ -0,0 +1,13 @@ +{ + "schema_version": "1.0", + "project_id": "video-001", + "stage_id": "07-cuts", + "revision": 1, + "inputs": [], + "payload": { + "narration_duration_ms": null, + "timing_evidence": "", + "cuts": [], + "captions": [] + } +} diff --git a/templates/stages/08-scenes.json b/templates/stages/08-scenes.json new file mode 100644 index 0000000..ee0da14 --- /dev/null +++ b/templates/stages/08-scenes.json @@ -0,0 +1,11 @@ +{ + "schema_version": "1.0", + "project_id": "video-001", + "stage_id": "08-scenes", + "revision": 1, + "inputs": [], + "payload": { + "scenes": [], + "connections": [] + } +} diff --git a/templates/stages/09-entities.json b/templates/stages/09-entities.json new file mode 100644 index 0000000..36309e4 --- /dev/null +++ b/templates/stages/09-entities.json @@ -0,0 +1,12 @@ +{ + "schema_version": "1.0", + "project_id": "video-001", + "stage_id": "09-entities", + "revision": 1, + "inputs": [], + "payload": { + "characters": [], + "backgrounds": [], + "appearances": [] + } +} diff --git a/templates/stages/10-references.json b/templates/stages/10-references.json new file mode 100644 index 0000000..a4d20d6 --- /dev/null +++ b/templates/stages/10-references.json @@ -0,0 +1,15 @@ +{ + "schema_version": "1.0", + "project_id": "video-001", + "stage_id": "10-references", + "revision": 1, + "inputs": [], + "payload": { + "request": { + "model": "gpt-image-2.5-sunburst", + "style_reference": "references/1.png", + "items": [] + }, + "result": null + } +} diff --git a/templates/stages/11-images.json b/templates/stages/11-images.json new file mode 100644 index 0000000..21e3288 --- /dev/null +++ b/templates/stages/11-images.json @@ -0,0 +1,14 @@ +{ + "schema_version": "1.0", + "project_id": "video-001", + "stage_id": "11-images", + "revision": 1, + "inputs": [], + "payload": { + "request": { + "model": "gpt-image-2.5-sunburst", + "frames": [] + }, + "result": null + } +} diff --git a/templates/stages/12-motion.json b/templates/stages/12-motion.json new file mode 100644 index 0000000..7ab6bd3 --- /dev/null +++ b/templates/stages/12-motion.json @@ -0,0 +1,13 @@ +{ + "schema_version": "1.0", + "project_id": "video-001", + "stage_id": "12-motion", + "revision": 1, + "inputs": [], + "payload": { + "request": { + "clips": [] + }, + "result": null + } +} diff --git a/templates/stages/13-compose.json b/templates/stages/13-compose.json new file mode 100644 index 0000000..8919f0a --- /dev/null +++ b/templates/stages/13-compose.json @@ -0,0 +1,16 @@ +{ + "schema_version": "1.0", + "project_id": "video-001", + "stage_id": "13-compose", + "revision": 1, + "inputs": [], + "payload": { + "request": { + "timeline": [], + "narration_path": "", + "captions": [], + "title": "" + }, + "result": null + } +} diff --git a/templates/stages/14-review.json b/templates/stages/14-review.json new file mode 100644 index 0000000..23f9e86 --- /dev/null +++ b/templates/stages/14-review.json @@ -0,0 +1,43 @@ +{ + "schema_version": "1.0", + "project_id": "video-001", + "stage_id": "14-review", + "revision": 1, + "inputs": [], + "payload": { + "checks": [ + { + "area": "story", + "verdict": "not_checked", + "evidence": "" + }, + { + "area": "visual", + "verdict": "not_checked", + "evidence": "" + }, + { + "area": "audio", + "verdict": "not_checked", + "evidence": "" + }, + { + "area": "captions", + "verdict": "not_checked", + "evidence": "" + }, + { + "area": "timing", + "verdict": "not_checked", + "evidence": "" + }, + { + "area": "consistency", + "verdict": "not_checked", + "evidence": "" + } + ], + "findings": [], + "recommendation": "incomplete" + } +}