Files
ai/backend/src/production-kernel/contracts/production-contracts.ts
T

655 lines
17 KiB
TypeScript

export const PRODUCTION_CONTRACT_VERSIONS = {
sourceAnalysis: 'source_analysis_v1',
adaptationBible: 'adaptation_bible_v1',
episodePlan: 'episode_plan_v1',
sceneScript: 'scene_script_v1',
assetPlan: 'asset_plan_v1',
sceneGeography: 'scene_geography_v1',
shotExecution: 'shot_execution_v1',
stageQuality: 'stage_quality_v1',
stageReviewDelta: 'stage_review_delta_v1',
shotQcDelta: 'shot_qc_delta_v1',
promptRule: 'prompt_rule_v1'
} as const;
export type ProductionContractVersion =
(typeof PRODUCTION_CONTRACT_VERSIONS)[keyof typeof PRODUCTION_CONTRACT_VERSIONS];
export interface SourceRefV1 {
source_type: 'novel_snapshot' | 'chapter' | 'story_bible' | 'memory' | 'approved_decision';
source_id: string;
locator?: string;
quote?: string;
}
export interface SourceFactRefV1 {
fact: string;
source_refs: SourceRefV1[];
}
export interface SourceCharacterFactV1 {
character_id?: string;
name: string;
role: string;
goal: string;
fear?: string;
secrets: string[];
state: string;
source_refs: SourceRefV1[];
}
export interface SourceEventV1 {
id: string;
order: number;
event: string;
participants: string[];
consequence: string;
source_refs: SourceRefV1[];
}
export interface SourceAnalysisSpecV1 {
schema_version: typeof PRODUCTION_CONTRACT_VERSIONS.sourceAnalysis;
source_snapshot_id: string;
premise: string;
genre_promises: string[];
timeline: SourceEventV1[];
characters: SourceCharacterFactV1[];
relationships: SourceFactRefV1[];
world_rules: SourceFactRefV1[];
conflicts: SourceFactRefV1[];
mysteries: SourceFactRefV1[];
emotional_assets: SourceFactRefV1[];
visual_assets: SourceFactRefV1[];
immutable_facts: SourceFactRefV1[];
uncertain_facts: SourceFactRefV1[];
source_refs: SourceRefV1[];
}
export interface AdaptationDecisionV1 {
id: string;
operation: 'keep' | 'compress' | 'merge' | 'reorder' | 'remove' | 'add';
source_refs: SourceRefV1[];
target: string;
reason: string;
impact: string[];
continuity_risk: string;
approval_status: 'draft' | 'approved' | 'rejected';
}
export interface HookPolicyV1 {
mode: 'aggressive_0_3s' | 'suspense_0_5s' | 'cinematic_0_8s';
max_setup_ms: number;
require_visible_question: boolean;
require_episode_payoff: boolean;
max_repeated_hook_type: number;
}
export interface DialoguePolicyV1 {
language: string;
target_chars_per_second: number;
preserve_confirmed_lines: boolean;
require_listener_reaction: boolean;
}
export interface AdaptationBibleSpecV1 {
schema_version: typeof PRODUCTION_CONTRACT_VERSIONS.adaptationBible;
source_analysis_version_id: string;
format: {
orientation: '16:9';
target_episode_seconds: number;
episode_count_range: [number, number];
audience: string;
};
genre_contract: string;
tone_contract: string;
protagonist_contract: string;
central_dramatic_question: string;
main_arc: string;
character_arcs: Array<{ character_id: string; arc: string }>;
season_beats: Array<{ id: string; beat: string; consequence: string }>;
adaptation_decisions: AdaptationDecisionV1[];
hook_policy: HookPolicyV1;
dialogue_policy: DialoguePolicyV1;
visual_policy: string[];
sound_policy: string[];
prohibited_changes: string[];
source_refs: SourceRefV1[];
}
export type HookTypeV1 =
| 'crisis'
| 'result_first'
| 'identity_contrast'
| 'relationship_break'
| 'impossible_event'
| 'secret_exposure'
| 'countdown'
| 'forced_choice'
| 'high_value_promise'
| 'previous_hook_payoff';
export interface HookTimelineBeatV1 {
start_ms: number;
end_ms: number;
visual: string;
audio: string;
information_gain: string;
}
export interface OpeningHookSpecV1 {
type: HookTypeV1;
secondary_type?: HookTypeV1;
viewer_question: string;
character_at_risk: string[];
stakes: string;
visual_event: string;
audio_event: string;
timeline: HookTimelineBeatV1[];
relation_to_episode_conflict: string;
payoff_beat_id: string;
source_refs: SourceRefV1[];
anti_clickbait_check: string;
}
export interface EndingHookSpecV1 {
hook: string;
new_information: string;
next_episode_question: string;
state_change: string;
source_refs: SourceRefV1[];
}
export interface StoryStateRefV1 {
character_states: Record<string, string>;
active_conflicts: string[];
known_information: string[];
location_state?: string;
}
export interface EpisodeBeatV1 {
id: string;
type: 'setup' | 'escalation' | 'reversal' | 'choice' | 'climax' | 'aftermath' | 'hook';
beat: string;
character_action: string;
consequence: string;
source_refs: SourceRefV1[];
}
export interface EpisodePlanSpecV1 {
schema_version: typeof PRODUCTION_CONTRACT_VERSIONS.episodePlan;
adaptation_bible_version_id: string;
episode_number: number;
target_duration_ms: number;
hook_policy: HookPolicyV1;
opening_hook: OpeningHookSpecV1;
protagonist_goal: string;
obstacle: string;
stakes: string;
dramatic_question: string;
beats: EpisodeBeatV1[];
midpoint_change: string;
climax_choice: string;
irreversible_change: string;
ending_hook: EndingHookSpecV1;
entry_state: StoryStateRefV1;
exit_state: StoryStateRefV1;
source_refs: SourceRefV1[];
adaptation_decision_refs: string[];
}
export interface DialogueBeatV1 {
id: string;
character_id: string;
line: string;
intention: string;
subtext: string;
reaction_target?: string;
estimated_duration_ms: number;
}
export interface ActionBeatV1 {
id: string;
character_id?: string;
action: string;
trigger: string;
result: string;
estimated_duration_ms: number;
}
export interface SceneSpecV1 {
id: string;
location_asset_ref: string;
time_of_day: string;
entry_state: StoryStateRefV1;
scene_goal: string;
active_characters: string[];
character_objectives: Array<{ character_id: string; objective: string }>;
obstacle: string;
tactics: Array<{ character_id: string; tactic: string }>;
action_beats: ActionBeatV1[];
dialogue_beats: DialogueBeatV1[];
subtext: string;
turn: string;
exit_state: StoryStateRefV1;
estimated_duration_ms: number;
source_refs: SourceRefV1[];
}
export interface SceneScriptSpecV1 {
schema_version: typeof PRODUCTION_CONTRACT_VERSIONS.sceneScript;
episode_plan_version_id: string;
episode_number: number;
target_duration_ms: number;
scenes: SceneSpecV1[];
opening_hook_scene_id: string;
climax_scene_id: string;
ending_hook_scene_id: string;
dialogue_duration_estimate_ms: number;
total_duration_estimate_ms: number;
character_voice_checks: Array<{ character_id: string; check?: string; passed: boolean; issue?: string }>;
continuity_checks: Array<{ check?: string; passed: boolean; issue?: string }>;
}
export type AssetRequirementKindV1 =
| 'character_identity'
| 'character_state'
| 'crowd'
| 'location'
| 'prop'
| 'vfx';
export type AssetEntityTypeV1 =
| 'project_character'
| 'character_state'
| 'project_visual_asset'
| 'global_character'
| 'asset';
export interface AssetEntityRefV1 {
entity_type: AssetEntityTypeV1;
id: string;
}
export interface AssetCandidateRefV1 extends AssetEntityRefV1 {
reason: string;
approval_status: 'manual_review_required' | 'approved' | 'rejected';
}
export interface AssetRequirementV1 {
id: string;
kind: AssetRequirementKindV1;
name: string;
source_character_id?: string;
source_location_ref?: string;
reuse_decision: 'reuse' | 'create' | 'upgrade' | 'manual_review';
existing_ref: AssetEntityRefV1 | null;
candidate_refs: AssetCandidateRefV1[];
applies_to_scene_ids: string[];
priority: 'blocking' | 'high' | 'normal';
visual_brief: string;
continuity_locks: string[];
deliverables: string[];
acceptance_criteria: string[];
dependencies: string[];
source_refs: SourceRefV1[];
}
export interface AssetSceneBindingV1 {
scene_id: string;
location_requirement_ref: string;
character_requirement_refs: string[];
crowd_requirement_refs: string[];
prop_requirement_refs: string[];
vfx_requirement_refs: string[];
}
export interface AssetPlanSpecV1 {
schema_version: typeof PRODUCTION_CONTRACT_VERSIONS.assetPlan;
scene_script_version_id: string;
episode_number: number;
format: {
orientation: '16:9';
keyframe_resolution: '2560x1440';
video_resolution: '1920x1080';
};
inventory_snapshot: {
project_character_ids: string[];
project_visual_asset_ids: string[];
matching_global_character_ids: string[];
legacy_candidate_asset_ids: string[];
};
requirements: AssetRequirementV1[];
scene_bindings: AssetSceneBindingV1[];
creation_order: string[];
blocking_requirement_refs: string[];
quality_checks: Array<{ check: string; passed: boolean; issue?: string }>;
source_refs: SourceRefV1[];
}
export interface SpatialVectorV1 {
x: number;
y: number;
z: number;
}
export interface SceneGeographyZoneV1 {
id: string;
name: string;
purpose: string;
center: SpatialVectorV1;
bounds: string;
adjacent_zone_ids: string[];
}
export interface SceneGeographyPlacementV1 {
subject_ref: string;
subject_type: 'character' | 'faction' | 'crowd' | 'prop' | 'vfx_origin' | 'vfx_target';
faction_ref?: string;
zone_id: string;
world_position: SpatialVectorV1;
facing_vector: SpatialVectorV1;
screen_side: 'left' | 'center' | 'right' | 'background' | 'foreground';
eyeline_target_ref?: string;
vertical_relation: 'ground' | 'below' | 'above' | 'overhead';
}
export interface SceneGeographyAxisV1 {
id: string;
endpoint_a_ref: string;
endpoint_b_ref: string;
description: string;
screen_left_ref: string;
screen_right_ref: string;
safe_camera_side: string;
forbidden_camera_side: string;
crossing_policy: 'forbidden' | 'neutral_bridge_required' | 'motivated_crossing_allowed';
}
export interface SceneGeographyCameraPositionV1 {
id: string;
name: string;
world_position: SpatialVectorV1;
viewing_direction: SpatialVectorV1;
axis_side: 'safe' | 'on_axis' | 'forbidden';
allowed: boolean;
purpose: string;
preserves_screen_relationship: string;
}
export interface SceneGeographyActionVectorV1 {
id: string;
source_ref: string;
target_ref: string;
origin_zone_id: string;
target_zone_id: string;
world_direction: SpatialVectorV1;
screen_direction: string;
trajectory: string;
must_keep_origin_and_target_visible: boolean;
}
export interface SceneGeographyBlockingBeatV1 {
id: string;
trigger: string;
actor_ref: string;
start_zone_id: string;
end_zone_id: string;
facing_target_ref: string;
eyeline_target_ref: string;
action_vector_ref?: string;
continuity_result: string;
}
export interface SceneGeographySceneV1 {
scene_id: string;
location_requirement_ref: string;
world_layout: string;
coordinate_system: {
origin: string;
x_axis: string;
y_axis: string;
z_axis: string;
};
zones: SceneGeographyZoneV1[];
placements: SceneGeographyPlacementV1[];
primary_axis: SceneGeographyAxisV1;
camera_positions: SceneGeographyCameraPositionV1[];
action_vectors: SceneGeographyActionVectorV1[];
blocking_beats: SceneGeographyBlockingBeatV1[];
continuity_locks: string[];
forbidden_outcomes: string[];
required_spatial_anchor_refs: string[];
acceptance_criteria: string[];
source_refs: SourceRefV1[];
}
export interface SceneGeographySpecV1 {
schema_version: typeof PRODUCTION_CONTRACT_VERSIONS.sceneGeography;
asset_plan_version_id: string;
scene_script_version_id: string;
episode_number: number;
format: {
orientation: '16:9';
world_unit: 'meter';
coordinate_handedness: 'right_handed';
};
scene_geographies: SceneGeographySceneV1[];
global_continuity_locks: string[];
global_forbidden_outcomes: string[];
quality_checks: Array<{ check: string; passed: boolean; issue?: string }>;
source_refs: SourceRefV1[];
}
export interface FrameStateV1 {
composition: string;
character_positions: string;
eyelines: string;
action_state: string;
emotional_state: string;
camera_axis: string;
screen_direction: string;
lighting_state: string;
}
export interface ContinuityAnchorV1 {
type: 'character' | 'action' | 'eyeline' | 'camera_axis' | 'screen_direction' | 'sound' | 'prop' | 'light';
value: string;
}
export interface PerformanceBeatV1 {
character_id: string;
start_ms: number;
end_ms: number;
start_state: string;
trigger: string;
visible_action: string;
end_state: string;
}
export interface DialogueTimingV1 {
character_id: string;
start_ms: number;
end_ms: number;
spoken_text: string;
delivery: string;
listener_reaction?: string;
}
export interface ShotExecutionSpecV1 {
schema_version: typeof PRODUCTION_CONTRACT_VERSIONS.shotExecution;
scene_script_version_id: string;
scene_geography_version_id: string;
scene_id: string;
shot_number: number;
dramatic_purpose: string;
generation_unit: 'single_shot' | 'continuous_shot' | 'montage_unit';
target_duration_ms: number;
assets: {
character_state_refs: string[];
location_asset_ref: string;
prop_asset_refs: string[];
wardrobe_refs: string[];
vfx_asset_refs: string[];
};
blocking: {
focus: string;
pressure_source: string;
reaction_receiver: string;
positions: string;
movement_path: string;
};
geography_binding: {
axis_id: string;
camera_position_id: string;
placement_subject_refs: string[];
action_vector_refs: string[];
zone_ids: string[];
};
performance_timeline: PerformanceBeatV1[];
dialogue_timeline: DialogueTimingV1[];
camera: {
shot_size: string;
angle: string;
lens_intent: string;
movement: string;
movement_motivation: string;
camera_axis: string;
screen_direction: string;
landing_point: string;
};
lighting: {
key_light: string;
contrast: string;
dynamic_change: string;
};
vfx: Array<{
effect: string;
trigger: string;
interaction: string;
intensity_arc: string;
}>;
sound: {
ambience: string[];
dialogue_source: 'native_audio' | 'post_tts' | 'none';
sfx_hits: Array<{ at_ms: number; cue: string }>;
music_cue?: string;
sound_bridge?: string;
};
start_state: FrameStateV1;
end_state: FrameStateV1;
continuity_from_previous: ContinuityAnchorV1[];
continuity_to_next: ContinuityAnchorV1[];
edit_relation_to_previous:
| 'new_scene_cut'
| 'motivated_hard_cut'
| 'reaction_cut'
| 'match_cut'
| 'sound_bridge'
| 'continuous_motion';
cut_motivation: string;
generation_strategy: {
mode: 'text_only' | 'first_frame' | 'first_last_frame' | 'multi_reference';
reference_assets: Array<{
asset_id: string;
responsibility:
| 'character_identity'
| 'wardrobe_state'
| 'location_layout'
| 'prop_identity'
| 'start_frame'
| 'end_frame'
| 'action_pose'
| 'style_reference';
priority: number;
}>;
};
provider_requirements: {
native_audio_required: boolean;
dialogue_required: boolean;
max_reference_images?: number;
allowed_duration_ms?: number[];
};
acceptance_criteria: Array<{ field_path: string; expected: string; severity: 'minor' | 'major' | 'fatal' }>;
forbidden_outcomes: string[];
}
export interface QualityIssueV1 {
code: string;
field_path: string;
message: string;
severity: 'minor' | 'major' | 'fatal';
}
export interface StageQualityResultV1 {
schema_version: typeof PRODUCTION_CONTRACT_VERSIONS.stageQuality;
hard_gate_status: 'pass' | 'blocked';
hard_gate_issues: QualityIssueV1[];
dimension_scores: Record<string, number>;
weighted_score: number;
confidence: number;
fatal_issues: QualityIssueV1[];
repair_instructions: Array<{ field_path: string; instruction: string }>;
rubric_version: string;
}
export interface StageReviewDeltaV1 {
schema_version: typeof PRODUCTION_CONTRACT_VERSIONS.stageReviewDelta;
contract_type: 'source_analysis' | 'adaptation_bible' | 'episode_plan' | 'scene_script' | 'asset_plan' | 'scene_geography';
expected_contract_id: string;
reviewer_version: string;
verdict: 'pass' | 'repair_required';
issues: QualityIssueV1[];
repairs: Array<{
field_path: string;
instruction: string;
evidence_refs: SourceRefV1[];
}>;
reviewer_notes: string[];
}
export interface ShotQcDeltaV1 {
schema_version: typeof PRODUCTION_CONTRACT_VERSIONS.shotQcDelta;
expected_spec_version: string;
observed_asset_id: string;
matched: string[];
mismatches: Array<{
field_path: string;
expected: string;
observed: string;
severity: 'minor' | 'major' | 'fatal';
}>;
repair_scope: 'prompt' | 'reference' | 'duration' | 'route' | 'script';
repair_instructions: string[];
}
export type PromptRuleStageV1 =
| 'source'
| 'adaptation'
| 'episode'
| 'script'
| 'assets'
| 'geography'
| 'shot'
| 'qc'
| 'timeline';
export interface PromptRuleCandidateV1 {
id: string;
source_prompt_refs: string[];
stage: PromptRuleStageV1;
scope: 'global' | 'genre' | 'provider' | 'project';
genre_tags: string[];
provider_codes: string[];
original_text: string;
normalized_rule: string;
activation_condition: string;
expected_improvement: string;
evidence_refs: string[];
positive_examples: string[];
negative_examples: string[];
conflicts_with: string[];
status: 'candidate' | 'testing' | 'approved' | 'deprecated';
reviewer_id?: string;
rule_version: typeof PRODUCTION_CONTRACT_VERSIONS.promptRule;
}