@event4u/agent-config 2.23.0 → 2.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/.agent-src/commands/create-pr/description-only.md +39 -11
  2. package/.agent-src/commands/create-pr.md +59 -5
  3. package/.agent-src/commands/video/from-script.md +123 -0
  4. package/.agent-src/commands/video/scene.md +92 -0
  5. package/.agent-src/commands/video/stitch.md +83 -0
  6. package/.agent-src/commands/video/storyboard.md +95 -0
  7. package/.agent-src/commands/video.md +59 -0
  8. package/.agent-src/contexts/execution/roadmap-process-loop.md +69 -14
  9. package/.agent-src/personas/README.md +5 -1
  10. package/.agent-src/personas/ai-video-technical-director.md +81 -0
  11. package/.agent-src/personas/hollywood-director.md +99 -0
  12. package/.agent-src/profiles/content_creator.yml +5 -0
  13. package/.agent-src/rules/media-governance-routing.md +82 -0
  14. package/.agent-src/rules/persona-governance.md +90 -0
  15. package/.agent-src/rules/post-push-rewrite-discipline.md +70 -0
  16. package/.agent-src/rules/provider-lifecycle-discipline.md +75 -0
  17. package/.agent-src/rules/roadmap-ci-steps-policy.md +145 -0
  18. package/.agent-src/rules/roadmap-progress-sync.md +11 -5
  19. package/.agent-src/skills/character-consistency/SKILL.md +131 -0
  20. package/.agent-src/skills/git-workflow/SKILL.md +133 -0
  21. package/.agent-src/skills/motion-choreographer/SKILL.md +161 -0
  22. package/.agent-src/skills/pixar-storyteller/SKILL.md +120 -0
  23. package/.agent-src/skills/roadmap-writing/SKILL.md +10 -0
  24. package/.agent-src/skills/scene-expander/SKILL.md +137 -0
  25. package/.agent-src/skills/scene-expander/scene-blueprint.schema.yaml +108 -0
  26. package/.agent-src/skills/subagent-orchestration/SKILL.md +17 -15
  27. package/.agent-src/skills/video-director/SKILL.md +126 -0
  28. package/.agent-src/templates/agent-settings.md +19 -0
  29. package/.agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  30. package/.agent-src/templates/roadmaps.md +16 -0
  31. package/.claude-plugin/marketplace.json +11 -1
  32. package/CHANGELOG.md +65 -0
  33. package/README.md +7 -5
  34. package/config/agent-settings.template.yml +54 -0
  35. package/docs/adrs/caveman/0001-default-off-until-bench.md +2 -2
  36. package/docs/adrs/cost/0001-hard-stop-hook.md +1 -1
  37. package/docs/adrs/smoke/0001-per-tier-smoke-scripts.md +2 -2
  38. package/docs/architecture.md +3 -3
  39. package/docs/catalog.md +18 -5
  40. package/docs/contracts/command-clusters.md +1 -0
  41. package/docs/contracts/compression-default-kill-criterion.md +1 -1
  42. package/docs/contracts/file-ownership-matrix.json +405 -0
  43. package/docs/contracts/provider-lifecycle.md +122 -0
  44. package/docs/decisions/ADR-011-domain-pack-readiness.md +213 -0
  45. package/docs/decisions/INDEX.md +1 -0
  46. package/docs/getting-started-by-role.md +10 -0
  47. package/docs/getting-started.md +2 -2
  48. package/docs/parity/ruflo.md +3 -3
  49. package/docs/personas.md +73 -26
  50. package/docs/profiles.md +9 -4
  51. package/package.json +1 -1
  52. package/scripts/_tmp_scan_framework_leakage.py +119 -0
  53. package/scripts/ai-video/adapters/gemini-veo.sh +62 -0
  54. package/scripts/ai-video/adapters/higgsfield.sh +88 -0
  55. package/scripts/ai-video/adapters/kling.sh +59 -0
  56. package/scripts/ai-video/adapters/openai-images.sh +57 -0
  57. package/scripts/ai-video/adapters/sora.sh +60 -0
  58. package/scripts/ai-video/lib/adapter-common.sh +116 -0
  59. package/scripts/ai-video/lib/adapter-contract.md +163 -0
  60. package/scripts/ai-video/lib/fixtures/gemini-veo/result.json +1 -0
  61. package/scripts/ai-video/lib/fixtures/gemini-veo/scene-0001.mp4 +1 -0
  62. package/scripts/ai-video/lib/fixtures/higgsfield/result.json +1 -0
  63. package/scripts/ai-video/lib/fixtures/higgsfield/scene-0001.mp4 +1 -0
  64. package/scripts/ai-video/lib/fixtures/kling/result.json +1 -0
  65. package/scripts/ai-video/lib/fixtures/kling/scene-0001.mp4 +1 -0
  66. package/scripts/ai-video/lib/fixtures/openai-images/result.json +1 -0
  67. package/scripts/ai-video/lib/fixtures/openai-images/scene-0001.png +3 -0
  68. package/scripts/ai-video/lib/fixtures/sora/result.json +1 -0
  69. package/scripts/ai-video/lib/fixtures/sora/scene-0001.mp4 +1 -0
  70. package/scripts/ai-video/lib/load-config.sh +140 -0
  71. package/scripts/ai-video/lib/operator-pick.sh +119 -0
  72. package/scripts/ai-video/lib/parse-blueprint.sh +122 -0
  73. package/scripts/ai-video/lib/redact.sh +85 -0
  74. package/scripts/ai-video/lib/validate-deps.sh +132 -0
  75. package/scripts/ai-video/stitch.sh +154 -0
  76. package/scripts/ai-video/test-pipeline.sh +169 -0
  77. package/scripts/check_portability.py +6 -0
  78. package/scripts/lint_media_policy_linkage.py +140 -0
  79. package/scripts/lint_persona_governance.py +164 -0
  80. package/scripts/lint_roadmap_ci_steps.py +182 -0
  81. package/scripts/schemas/command.schema.json +8 -0
  82. package/scripts/smoke/schema.sh +1 -1
@@ -0,0 +1,88 @@
1
+ #!/usr/bin/env bash
2
+ # higgsfield.sh — Higgsfield motion-preset video adapter.
3
+ #
4
+ # Capability: per-model. The provider exposes named motion presets
5
+ # (e.g. Mix, Burst, DVD) each with its own audio behaviour. Capability
6
+ # discovery via `capability --preset <name>` so the orchestrator can
7
+ # pick the right mux path.
8
+ #
9
+ # Contract: scripts/ai-video/lib/adapter-contract.md
10
+ # Provider: top-level <provider id="higgsfield" kind="video"> in
11
+ # agents/.ai-video.xml. Preset → motion-choreographer profile mapping
12
+ # is documented in agents/ai-video/prompts/motion-choreography.md
13
+ # (Phase 6).
14
+ #
15
+ # Lifecycle: experimental — capability-discovery path conformant; no
16
+ # maintainer real-API smoke trace captured yet. See
17
+ # docs/contracts/provider-lifecycle.md for promotion criteria. The
18
+ # agent must surface this tier and ask before defaulting to this
19
+ # adapter.
20
+
21
+ set -euo pipefail
22
+
23
+ # shellcheck source=../lib/adapter-common.sh
24
+ . "$(dirname "$0")/../lib/adapter-common.sh"
25
+
26
+ ADAPTER_ID="higgsfield"
27
+
28
+ # Map preset → audio capability. Conservative defaults; tuning table
29
+ # lives alongside the prompt library so creators can extend without
30
+ # touching this adapter.
31
+ higgsfield_audio_for_preset() {
32
+ case "${1:-}" in
33
+ mix|burst|dvd) printf 'none' ;;
34
+ cinematic|talk) printf 'native' ;;
35
+ *) printf 'none' ;;
36
+ esac
37
+ }
38
+
39
+ # Override capability handling so `capability --preset <name>` answers
40
+ # per-preset before falling through to the generic helper.
41
+ aiv_higgsfield_capability() {
42
+ local preset=""
43
+ while [ "$#" -gt 0 ]; do
44
+ case "$1" in
45
+ --preset) preset="${2:-}"; shift 2 ;;
46
+ *) shift ;;
47
+ esac
48
+ done
49
+ if [ -n "${preset}" ]; then
50
+ printf '{"audio":"%s","preset":"%s"}\n' \
51
+ "$(higgsfield_audio_for_preset "${preset}")" "${preset}"
52
+ return 0
53
+ fi
54
+ printf '{"audio":"per-model","presets":["mix","burst","dvd","cinematic","talk"]}\n'
55
+ }
56
+
57
+ aiv_cmd_submit() {
58
+ aiv_assert_dryrun
59
+ aiv_require_cmd curl jq
60
+ aiv_load_provider "${ADAPTER_ID}"
61
+ [ "$(aiv_key_status)" = "present" ] \
62
+ || aiv_die 6 "${ADAPTER_ID}: api key missing in agents/.ai-video.xml"
63
+ aiv_die 9 "${ADAPTER_ID}: live submit not yet wired"
64
+ }
65
+
66
+ aiv_cmd_poll() {
67
+ local job_id="${1:-}"
68
+ [ -n "${job_id}" ] || aiv_die 2 "${ADAPTER_ID}: poll <job_id> required"
69
+ aiv_assert_dryrun
70
+ aiv_die 9 "${ADAPTER_ID}: live poll not yet wired (job=${job_id})"
71
+ }
72
+
73
+ aiv_cmd_fetch() {
74
+ local job_id="${1:-}"
75
+ [ -n "${job_id}" ] || aiv_die 2 "${ADAPTER_ID}: fetch <job_id> required"
76
+ aiv_assert_dryrun
77
+ aiv_die 9 "${ADAPTER_ID}: live fetch not yet wired (job=${job_id})"
78
+ }
79
+
80
+ # Custom dispatch with capability override; falls back to the common
81
+ # router for every other subcommand.
82
+ sub="${1:-}"
83
+ if [ "${sub}" = "capability" ]; then
84
+ shift
85
+ aiv_higgsfield_capability "$@"
86
+ exit 0
87
+ fi
88
+ aiv_dispatch "${ADAPTER_ID}" "per-model" "$@"
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env bash
2
+ # kling.sh — Kuaishou Kling motion-tuned video generation adapter.
3
+ #
4
+ # Capability: audio=none by default (Kling current generation does not
5
+ # emit native audio); the orchestrator muxes operator-supplied dialogue
6
+ # / ambient at stitch time. Max duration is model-dependent; clip-time
7
+ # clamping happens in motion-choreographer (it tunes the prompt to fit).
8
+ #
9
+ # Contract: scripts/ai-video/lib/adapter-contract.md
10
+ # Provider: top-level <provider id="kling" kind="video"> in
11
+ # agents/.ai-video.xml.
12
+ #
13
+ # Lifecycle: experimental — async submit/poll/fetch contract conformant;
14
+ # no maintainer real-API smoke trace captured yet. See
15
+ # docs/contracts/provider-lifecycle.md for promotion criteria. The agent
16
+ # must surface this tier and ask before defaulting to this adapter.
17
+
18
+ set -euo pipefail
19
+
20
+ # shellcheck source=../lib/adapter-common.sh
21
+ . "$(dirname "$0")/../lib/adapter-common.sh"
22
+
23
+ ADAPTER_ID="kling"
24
+ KLING_MAX_DURATION="${KLING_MAX_DURATION:-10}"
25
+
26
+ aiv_cmd_submit() {
27
+ aiv_assert_dryrun
28
+ aiv_require_cmd curl jq
29
+ aiv_load_provider "${ADAPTER_ID}"
30
+ [ "$(aiv_key_status)" = "present" ] \
31
+ || aiv_die 6 "${ADAPTER_ID}: api key missing in agents/.ai-video.xml"
32
+
33
+ local stdin_json duration
34
+ stdin_json="$(cat)"
35
+ duration="$(printf '%s' "${stdin_json}" | jq -r '.duration // empty')"
36
+ if [ -n "${duration}" ]; then
37
+ awk -v d="${duration}" -v m="${KLING_MAX_DURATION}" \
38
+ 'BEGIN { if (d+0 > m+0) exit 1; exit 0 }' \
39
+ || aiv_die 3 "${ADAPTER_ID}: duration ${duration}s exceeds model max ${KLING_MAX_DURATION}s"
40
+ fi
41
+
42
+ aiv_die 9 "${ADAPTER_ID}: live submit not yet wired (max duration ${KLING_MAX_DURATION}s honored)"
43
+ }
44
+
45
+ aiv_cmd_poll() {
46
+ local job_id="${1:-}"
47
+ [ -n "${job_id}" ] || aiv_die 2 "${ADAPTER_ID}: poll <job_id> required"
48
+ aiv_assert_dryrun
49
+ aiv_die 9 "${ADAPTER_ID}: live poll not yet wired (job=${job_id})"
50
+ }
51
+
52
+ aiv_cmd_fetch() {
53
+ local job_id="${1:-}"
54
+ [ -n "${job_id}" ] || aiv_die 2 "${ADAPTER_ID}: fetch <job_id> required"
55
+ aiv_assert_dryrun
56
+ aiv_die 9 "${ADAPTER_ID}: live fetch not yet wired (job=${job_id})"
57
+ }
58
+
59
+ aiv_dispatch "${ADAPTER_ID}" "none" "$@"
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env bash
2
+ # openai-images.sh — image generation adapter (DALL-E / gpt-image-1).
3
+ #
4
+ # Capability: audio=none (still images only — motion handled by a
5
+ # downstream video adapter). Ref-image and seed reuse are passed
6
+ # through verbatim so character-consistency can lock a face across
7
+ # scenes by reusing the same seed.
8
+ #
9
+ # Contract: scripts/ai-video/lib/adapter-contract.md
10
+ # Provider: top-level <provider id="openai-images" kind="image"> in
11
+ # agents/.ai-video.xml.
12
+ #
13
+ # Lifecycle: experimental — structural shape conformant; no maintainer
14
+ # real-API smoke trace captured yet. See docs/contracts/provider-lifecycle.md
15
+ # for promotion criteria. The agent must surface this tier and ask
16
+ # before defaulting to this adapter.
17
+
18
+ set -euo pipefail
19
+
20
+ # shellcheck source=../lib/adapter-common.sh
21
+ . "$(dirname "$0")/../lib/adapter-common.sh"
22
+
23
+ ADAPTER_ID="openai-images"
24
+
25
+ aiv_cmd_run() {
26
+ aiv_assert_dryrun
27
+ aiv_require_cmd curl jq
28
+ aiv_load_provider "${ADAPTER_ID}"
29
+ [ "$(aiv_key_status)" = "present" ] \
30
+ || aiv_die 6 "${ADAPTER_ID}: api key missing in agents/.ai-video.xml"
31
+
32
+ # Read contract stdin JSON; compose a single text prompt from the
33
+ # eight prose blocks. Ref-image + seed are passed verbatim.
34
+ local stdin_json prompt seed ref_first
35
+ stdin_json="$(cat)"
36
+ prompt="$(printf '%s' "${stdin_json}" | jq -r '
37
+ [.prompt.style, .prompt.subject, .prompt.environment, .prompt.action,
38
+ .prompt.camera, .prompt.lens, .prompt.lighting, .prompt.mood]
39
+ | map(select(. != null and . != ""))
40
+ | join(". ")
41
+ ')"
42
+ seed="$(printf '%s' "${stdin_json}" | jq -r '.seed // empty')"
43
+ ref_first="$(printf '%s' "${stdin_json}" | jq -r '.ref_images[0] // empty')"
44
+
45
+ # Live mode is implementation-scaffolded: we do NOT yet POST to the
46
+ # OpenAI Images API from inside this adapter. The cost-floor gate
47
+ # (Phase 5 Step 6) refuses live mode unless the operator explicitly
48
+ # confirms in-turn; this branch surfaces a clear stub error so the
49
+ # contract surface stays stable while the live path is wired later.
50
+ aiv_die 9 "${ADAPTER_ID}: live mode not yet wired (prompt=${#prompt} chars, seed=${seed:-unset}, ref=${ref_first:-none})"
51
+ }
52
+
53
+ aiv_cmd_submit() { aiv_cmd_run "$@"; }
54
+ aiv_cmd_fetch() { aiv_cmd_run "$@"; }
55
+ aiv_cmd_poll() { printf '{"status":"done"}\n'; }
56
+
57
+ aiv_dispatch "${ADAPTER_ID}" "none" "$@"
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env bash
2
+ # sora.sh — OpenAI Sora structural-prompt video adapter.
3
+ #
4
+ # Capability: audio=native. Sora-class models produce muxed MP4 with
5
+ # dialogue + ambient sound; we pass the audio block straight through.
6
+ # Structural-prompt path informed by upstream `awesome-sora-prompts`
7
+ # (attribution in agents/ai-video/prompts/cinematic-blueprint.md).
8
+ #
9
+ # Contract: scripts/ai-video/lib/adapter-contract.md
10
+ # Provider: top-level <provider id="sora" kind="video"> in
11
+ # agents/.ai-video.xml.
12
+ #
13
+ # Lifecycle: experimental — structural-prompt path conformant; no
14
+ # maintainer real-API smoke trace captured yet. See
15
+ # docs/contracts/provider-lifecycle.md for promotion criteria. The
16
+ # agent must surface this tier and ask before defaulting to this
17
+ # adapter.
18
+
19
+ set -euo pipefail
20
+
21
+ # shellcheck source=../lib/adapter-common.sh
22
+ . "$(dirname "$0")/../lib/adapter-common.sh"
23
+
24
+ ADAPTER_ID="sora"
25
+
26
+ aiv_cmd_submit() {
27
+ aiv_assert_dryrun
28
+ aiv_require_cmd curl jq
29
+ aiv_load_provider "${ADAPTER_ID}"
30
+ [ "$(aiv_key_status)" = "present" ] \
31
+ || aiv_die 6 "${ADAPTER_ID}: api key missing in agents/.ai-video.xml"
32
+
33
+ local stdin_json
34
+ stdin_json="$(cat)"
35
+ # Sora's structural-prompt path expects all eight blocks present;
36
+ # fail loud if the blueprint parser dropped any.
37
+ printf '%s' "${stdin_json}" \
38
+ | jq -e '.prompt.style and .prompt.subject and .prompt.environment and
39
+ .prompt.action and .prompt.camera and .prompt.lens and
40
+ .prompt.lighting and .prompt.mood' >/dev/null \
41
+ || aiv_die 3 "${ADAPTER_ID}: stdin JSON missing one or more required prompt blocks"
42
+
43
+ aiv_die 9 "${ADAPTER_ID}: live submit not yet wired"
44
+ }
45
+
46
+ aiv_cmd_poll() {
47
+ local job_id="${1:-}"
48
+ [ -n "${job_id}" ] || aiv_die 2 "${ADAPTER_ID}: poll <job_id> required"
49
+ aiv_assert_dryrun
50
+ aiv_die 9 "${ADAPTER_ID}: live poll not yet wired (job=${job_id})"
51
+ }
52
+
53
+ aiv_cmd_fetch() {
54
+ local job_id="${1:-}"
55
+ [ -n "${job_id}" ] || aiv_die 2 "${ADAPTER_ID}: fetch <job_id> required"
56
+ aiv_assert_dryrun
57
+ aiv_die 9 "${ADAPTER_ID}: live fetch not yet wired (job=${job_id})"
58
+ }
59
+
60
+ aiv_dispatch "${ADAPTER_ID}" "native" "$@"
@@ -0,0 +1,116 @@
1
+ #!/usr/bin/env bash
2
+ # adapter-common.sh — shared boilerplate sourced by every adapter under
3
+ # scripts/ai-video/adapters/. Keeps individual adapters thin and makes
4
+ # the contract surface (subcommand dispatch, dry-run plumbing, stdout
5
+ # shape) uniform across providers.
6
+ #
7
+ # Sourced; never executed directly. Strict-mode flags are the caller's
8
+ # responsibility (every adapter starts with `set -euo pipefail`).
9
+
10
+ if [ -n "${AIV_ADAPTER_COMMON_LOADED:-}" ]; then
11
+ return 0 2>/dev/null || exit 0
12
+ fi
13
+ AIV_ADAPTER_COMMON_LOADED=1
14
+
15
+ # Resolve the lib dir from this file's location so adapters can source
16
+ # us via the canonical `$(dirname "$0")/../lib/...` path.
17
+ _aiv_lib_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
18
+ # shellcheck source=/dev/null
19
+ . "${_aiv_lib_dir}/redact.sh"
20
+ # shellcheck source=/dev/null
21
+ . "${_aiv_lib_dir}/load-config.sh"
22
+
23
+ AIV_LIB_DIR="${_aiv_lib_dir}"
24
+ AIV_FIXTURE_ROOT="${_aiv_lib_dir}/fixtures"
25
+ export AIV_LIB_DIR AIV_FIXTURE_ROOT
26
+
27
+ # aiv_die <exit-code> <message> — write redacted message to stderr.
28
+ aiv_die() {
29
+ local code="${1:-1}"; shift || true
30
+ printf 'adapter: %s\n' "$*" | aiv_redact_stream >&2
31
+ exit "${code}"
32
+ }
33
+
34
+ # aiv_require_cmd <cmd> [<cmd> ...] — fail fast on missing dependency.
35
+ aiv_require_cmd() {
36
+ local cmd
37
+ for cmd in "$@"; do
38
+ command -v "${cmd}" >/dev/null 2>&1 \
39
+ || aiv_die 3 "required tool not found: ${cmd}"
40
+ done
41
+ }
42
+
43
+ # aiv_emit_dry_run <adapter-id> — print contract-shaped stdout JSON
44
+ # pointing at the adapter's fixture directory. Used by every adapter's
45
+ # `dry-run` subcommand.
46
+ aiv_emit_dry_run() {
47
+ local adapter="${1:-}"
48
+ [ -n "${adapter}" ] || aiv_die 3 "aiv_emit_dry_run: adapter id required"
49
+ local fixture_dir="${AIV_FIXTURE_ROOT}/${adapter}"
50
+ local result="${fixture_dir}/result.json"
51
+ if [ ! -f "${result}" ]; then
52
+ aiv_die 3 "fixture missing: ${result}"
53
+ fi
54
+ cat "${result}"
55
+ }
56
+
57
+ # aiv_capability <flag> — print the capability JSON for `--help`-style
58
+ # discovery: `{"audio": "native|none|per-model"}`.
59
+ aiv_capability() {
60
+ local flag="${1:-none}"
61
+ printf '{"audio":"%s"}\n' "${flag}"
62
+ }
63
+
64
+ # aiv_assert_dryrun — refuse to run a network path unless the caller
65
+ # explicitly opted in (AIV_DRYRUN=false set in this turn).
66
+ aiv_assert_dryrun() {
67
+ case "${AIV_DRYRUN:-true}" in
68
+ false|FALSE|0|no|NO) return 0 ;;
69
+ *) aiv_die 4 "live call refused: AIV_DRYRUN=${AIV_DRYRUN:-true}; use dry-run subcommand or set AIV_DRYRUN=false" ;;
70
+ esac
71
+ }
72
+
73
+ # aiv_dispatch <adapter-id> <capability> "$@" — generic subcommand router.
74
+ # Adapters override individual subcommands by defining bash functions
75
+ # named `aiv_cmd_submit` / `aiv_cmd_poll` / `aiv_cmd_fetch` / `aiv_cmd_run`
76
+ # BEFORE calling aiv_dispatch. Anything not overridden falls through to
77
+ # a stub that exits non-zero with a clear message.
78
+ aiv_dispatch() {
79
+ local adapter="${1:-}"; shift || true
80
+ local cap="${1:-none}"; shift || true
81
+ local sub="${1:-}"; shift || true
82
+ case "${sub}" in
83
+ capability)
84
+ aiv_capability "${cap}"
85
+ ;;
86
+ dry-run)
87
+ aiv_emit_dry_run "${adapter}"
88
+ ;;
89
+ submit)
90
+ declare -F aiv_cmd_submit >/dev/null \
91
+ && aiv_cmd_submit "$@" \
92
+ || aiv_die 5 "${adapter}: submit not implemented"
93
+ ;;
94
+ poll)
95
+ declare -F aiv_cmd_poll >/dev/null \
96
+ && aiv_cmd_poll "$@" \
97
+ || aiv_die 5 "${adapter}: poll not implemented"
98
+ ;;
99
+ fetch)
100
+ declare -F aiv_cmd_fetch >/dev/null \
101
+ && aiv_cmd_fetch "$@" \
102
+ || aiv_die 5 "${adapter}: fetch not implemented"
103
+ ;;
104
+ run)
105
+ declare -F aiv_cmd_run >/dev/null \
106
+ && aiv_cmd_run "$@" \
107
+ || aiv_die 5 "${adapter}: run not implemented"
108
+ ;;
109
+ "")
110
+ aiv_die 2 "${adapter}: subcommand required (capability|dry-run|submit|poll|fetch|run)"
111
+ ;;
112
+ *)
113
+ aiv_die 2 "${adapter}: unknown subcommand '${sub}'"
114
+ ;;
115
+ esac
116
+ }
@@ -0,0 +1,163 @@
1
+ # Adapter Contract (v1)
2
+
3
+ > Single source of truth for every adapter under
4
+ > `scripts/ai-video/adapters/`. Phase 4 Step 1 finalizes any open
5
+ > point in this draft; **breaking changes bump the version pin
6
+ > and require every adapter to be rerun against the fixture set**
7
+ > (`scripts/ai-video/lib/fixtures/`).
8
+
9
+ ## Scope
10
+
11
+ Every backend (OpenAI Images, Gemini Veo, Kling, Higgsfield, Sora,
12
+ …) ships as one executable shell entry under
13
+ `scripts/ai-video/adapters/<id>.sh`. The orchestrator never speaks
14
+ to a network API directly — it speaks to one of these scripts.
15
+
16
+ ## Capability flag
17
+
18
+ Each adapter declares its audio capability via a top-of-file
19
+ comment and via the `capability` subcommand:
20
+
21
+ ```
22
+ # capability: audio=native | audio=none
23
+ ```
24
+
25
+ `audio=native` — the provider returns a muxed MP4 with synchronized
26
+ dialogue / ambient. `audio=none` — video-only output; the
27
+ orchestrator muxes the operator-supplied track at stitch time.
28
+
29
+ A per-model adapter (e.g. Higgsfield) MAY declare `audio=per-model`
30
+ and surface the capability via `capability --model <id>`.
31
+
32
+ ## Subcommands
33
+
34
+ Every adapter implements four:
35
+
36
+ | Subcommand | Purpose | I/O |
37
+ |---|---|---|
38
+ | `submit` | Submit a render job to the backend | stdin JSON · stdout `{job_id}` |
39
+ | `poll <job_id>` | Poll job status | stdout `{status: queued\|running\|done\|failed, progress?}` |
40
+ | `fetch <job_id>` | Download artifacts after `status=done` | stdout `{video_path, audio_path?, audio_embedded}` |
41
+ | `dry-run` | Return a deterministic fixture path without network | stdout same shape as `fetch` |
42
+
43
+ `submit` + `poll` + `fetch` may be collapsed into a single
44
+ `run` for synchronous backends (OpenAI Images) — the wrapper exposes
45
+ the same stdout shape on stdout and an exit code on completion.
46
+
47
+ ## Stdin JSON (consumed by `submit` / `run`)
48
+
49
+ ```json
50
+ {
51
+ "prompt": {
52
+ "style": "...",
53
+ "subject": "...",
54
+ "environment": "...",
55
+ "action": "...",
56
+ "camera": "...",
57
+ "lens": "...",
58
+ "lighting": "...",
59
+ "mood": "..."
60
+ },
61
+ "ref_images": ["/abs/path/frame.png", "..."],
62
+ "duration": 4.5,
63
+ "aspect": "16:9",
64
+ "seed": 1234567,
65
+ "audio": {
66
+ "dialogue": ["speaker: \"line\"", "..."],
67
+ "ambient": ["rain on metal", "..."],
68
+ "language": "en",
69
+ "enable_native_audio": true
70
+ },
71
+ "negative": ["centered framing", "..."]
72
+ }
73
+ ```
74
+
75
+ `ref_images`, `duration`, `aspect`, `seed`, `audio`, `negative` are
76
+ optional. `prompt.*` blocks are mandatory. Unknown top-level keys
77
+ are logged to stderr and ignored.
78
+
79
+ ## Stdout JSON (emitted by `fetch` / `run` / `dry-run`)
80
+
81
+ ```json
82
+ {
83
+ "video_path": "/abs/path/scene-0001.mp4",
84
+ "audio_path": "/abs/path/scene-0001.wav",
85
+ "audio_embedded": true
86
+ }
87
+ ```
88
+
89
+ Semantics:
90
+
91
+ - `audio_embedded: true` — `video_path` is a muxed MP4 with audio;
92
+ `audio_path` is omitted or echoes the same path. Stitcher
93
+ pass-through.
94
+ - `audio_embedded: false` with `audio_path` — separate track to mux
95
+ at stitch time via `ffmpeg`.
96
+ - `audio_embedded: false` without `audio_path` — video-only;
97
+ operator supplies the audio bed at stitch time.
98
+
99
+ ## Error contract
100
+
101
+ Adapter failure: **non-zero exit code** AND a JSON file at
102
+ `<project>/scenes/<scene_id>/error.json`:
103
+
104
+ ```json
105
+ {
106
+ "adapter": "gemini-veo",
107
+ "subcommand": "fetch",
108
+ "job_id": "...",
109
+ "exit_code": 7,
110
+ "stderr_tail": "...",
111
+ "retryable": true,
112
+ "user_action": "regenerate-prompt | retry | skip | abort"
113
+ }
114
+ ```
115
+
116
+ `retryable: false` means a prompt or input change is required —
117
+ the orchestrator MUST NOT auto-retry. `retryable: true` does **not**
118
+ authorize auto-retry either; the orchestrator surfaces a single
119
+ numbered-options block (retry · regenerate prompt · skip · abort)
120
+ per `non-destructive-by-default` and waits.
121
+
122
+ ## Dry-run
123
+
124
+ `AIV_DRYRUN=true` (set by command default; see Phase 5 Step 6) OR
125
+ the explicit `dry-run` subcommand:
126
+
127
+ - No network call.
128
+ - Stdout JSON points at a deterministic fixture under
129
+ `scripts/ai-video/lib/fixtures/<adapter-id>/`.
130
+ - Exit code 0.
131
+
132
+ Fixtures are committed and cover one happy path per adapter.
133
+ Phase 6 golden runs assert byte-identical stdout under
134
+ `AIV_DRYRUN=true`.
135
+
136
+ ## Logging & redaction
137
+
138
+ - **stderr only** for log output. Stdout is reserved for the
139
+ contract JSON.
140
+ - Every adapter MUST source `scripts/ai-video/lib/redact.sh` and
141
+ pipe network responses through `aiv_redact_stream` before any
142
+ `>&2` write. API keys, bearer tokens, and operator-registered
143
+ secrets are masked.
144
+
145
+ ## Strict-mode shell
146
+
147
+ Every adapter:
148
+
149
+ ```bash
150
+ #!/usr/bin/env bash
151
+ set -euo pipefail
152
+ . "$(dirname "$0")/../lib/redact.sh"
153
+ . "$(dirname "$0")/../lib/load-config.sh"
154
+ ```
155
+
156
+ `shellcheck` clean (Phase 4 Step 9).
157
+
158
+ ## Versioning
159
+
160
+ This contract is `v1`. Backward-incompatible changes (renamed
161
+ field, removed subcommand, changed stdout shape) bump to `v2` and
162
+ require an `adapter-contract` migration note plus a rerun of every
163
+ adapter against the fixture set.
@@ -0,0 +1 @@
1
+ {"video_path":"scripts/ai-video/lib/fixtures/gemini-veo/scene-0001.mp4","audio_embedded":true}
@@ -0,0 +1 @@
1
+ FIXTURE-gemini-veo-mp4
@@ -0,0 +1 @@
1
+ {"video_path":"scripts/ai-video/lib/fixtures/higgsfield/scene-0001.mp4","audio_embedded":false}
@@ -0,0 +1 @@
1
+ FIXTURE-higgsfield-mp4
@@ -0,0 +1 @@
1
+ {"video_path":"scripts/ai-video/lib/fixtures/kling/scene-0001.mp4","audio_embedded":false}
@@ -0,0 +1 @@
1
+ FIXTURE-kling-mp4
@@ -0,0 +1 @@
1
+ {"video_path":"scripts/ai-video/lib/fixtures/openai-images/scene-0001.png","audio_embedded":false}
@@ -0,0 +1,3 @@
1
+ �PNG
2
+ 
3
+ FIXTURE-openai-images
@@ -0,0 +1 @@
1
+ {"video_path":"scripts/ai-video/lib/fixtures/sora/scene-0001.mp4","audio_embedded":true}
@@ -0,0 +1 @@
1
+ FIXTURE-sora-mp4