@event4u/agent-config 5.4.0 → 5.5.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 (49) hide show
  1. package/.agent-src/commands/knowledge/cross-repo.md +71 -0
  2. package/.agent-src/commands/knowledge.md +2 -0
  3. package/.agent-src/commands/skill/preview.md +67 -0
  4. package/.agent-src/commands/skill.md +48 -0
  5. package/.agent-src/commands/skills/discover.md +76 -0
  6. package/.agent-src/commands/skills.md +56 -0
  7. package/.agent-src/commands/video/from-song.md +317 -0
  8. package/.agent-src/commands/video.md +19 -9
  9. package/.agent-src/rules/linked-projects-onboarding-gate.md +1 -1
  10. package/.agent-src/skills/song-to-script/SKILL.md +193 -0
  11. package/.claude-plugin/marketplace.json +9 -2
  12. package/CHANGELOG.md +49 -0
  13. package/CONTRIBUTING.md +6 -0
  14. package/README.md +3 -3
  15. package/dist/cli/registry.js +1 -0
  16. package/dist/cli/registry.js.map +1 -1
  17. package/dist/discovery/deprecation-report.md +1 -1
  18. package/dist/discovery/discovery-manifest.json +171 -17
  19. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  20. package/dist/discovery/discovery-manifest.summary.md +4 -4
  21. package/dist/discovery/orphan-report.md +1 -1
  22. package/dist/discovery/packs.json +17 -10
  23. package/dist/discovery/trust-report.md +3 -3
  24. package/dist/discovery/workspaces.json +13 -6
  25. package/dist/mcp/registry-manifest.json +2 -2
  26. package/docs/architecture.md +2 -2
  27. package/docs/contracts/command-clusters.md +4 -1
  28. package/docs/contracts/cross-repo-retrieval.md +64 -0
  29. package/docs/contracts/skill-discovery.md +80 -0
  30. package/docs/contracts/skill-dry-run.md +47 -0
  31. package/docs/decisions/ADR-032-linked-projects-scope.md +7 -3
  32. package/docs/getting-started.md +1 -1
  33. package/docs/guides/cross-repo-linked-projects.md +7 -0
  34. package/docs/guides/cross-repo-retrieval.md +61 -0
  35. package/docs/guides/skill-discovery.md +71 -0
  36. package/docs/guides/skill-preview.md +71 -0
  37. package/package.json +1 -1
  38. package/scripts/__pycache__/validate_frontmatter.cpython-312.pyc +0 -0
  39. package/scripts/_dispatch.bash +10 -0
  40. package/scripts/_lib/__pycache__/__init__.cpython-312.pyc +0 -0
  41. package/scripts/_lib/__pycache__/agent_src.cpython-312.pyc +0 -0
  42. package/scripts/ai-video/lib/probe-audio.sh +181 -0
  43. package/scripts/cross_repo_retrieve.py +172 -0
  44. package/scripts/inventory_meta_layers.py +288 -0
  45. package/scripts/linked_projects_list.py +91 -0
  46. package/scripts/memory_lookup.py +53 -2
  47. package/scripts/skill_discovery.py +254 -0
  48. package/scripts/skill_linter.py +8 -4
  49. package/scripts/skill_preview.py +179 -0
@@ -0,0 +1,71 @@
1
+ ---
2
+ model_tier: inherit
3
+ name: knowledge:cross-repo
4
+ tier: 2
5
+ cluster: knowledge
6
+ sub: cross-repo
7
+ description: Targeted, read-only retrieval over opted-in linked-project siblings (ADR-032 Option A). Pulls a shared type / API contract / config without bulk-including sibling files.
8
+ skills: [file-editor]
9
+ suggestion:
10
+ eligible: true
11
+ trigger_description: "what does the frontend expect, find the shared type in the other repo, check the sibling repo's API contract, /knowledge:cross-repo <query>"
12
+ trigger_context: "user needs context that lives in an attached sibling repo without copying its files in"
13
+ workspaces:
14
+ - agent-config-maintainer
15
+ packs:
16
+ - meta
17
+ ---
18
+
19
+ # /knowledge cross-repo
20
+
21
+ Targeted, **read-only** retrieval across the IDE-attached sibling repos the user
22
+ has opted into (`linked_projects[].include: true`). Returns a bounded set of
23
+ matches — a shared type, an API contract the frontend consumes, a config the
24
+ sibling owns — **without bulk-including** any sibling file. Implements
25
+ [`cross-repo-retrieval`](../../../docs/contracts/cross-repo-retrieval.md) and
26
+ stays inside [ADR-032](../../../docs/decisions/ADR-032-linked-projects-scope.md)
27
+ Option A.
28
+
29
+ ## Prerequisites
30
+
31
+ - Python 3.10+ on the host.
32
+ - At least one sibling opted in (`agent-config linked-projects:list` shows them).
33
+ No opted-in sibling → the command is inert with a clear message.
34
+
35
+ ## Steps
36
+
37
+ ### 1. Parse the query
38
+
39
+ `/knowledge cross-repo "<query>" [--path-scope <glob>]`. The query is one
40
+ concept (≥ 1 term > 2 chars). A `--path-scope` glob narrows the search and is
41
+ **required** for `large`-flagged siblings.
42
+
43
+ ### 2. Run the retrieval
44
+
45
+ ```bash
46
+ python3 scripts/cross_repo_retrieve.py "<query>" [--path-scope <glob>] [--max-chunks 8]
47
+ ```
48
+
49
+ The script reads opted-in siblings only, runs a targeted path-glob + content
50
+ grep (never a full walk), redacts secrets/PII from every chunk, and returns the
51
+ retrieval envelope.
52
+
53
+ ### 3. Present matches
54
+
55
+ Render the table: `source_repo · path · freshness · why`. Each row names the
56
+ source sibling, the path inside it, the last-commit/mtime freshness stamp, and
57
+ why it matched. Use the chunks as *context*, never as files to copy wholesale.
58
+
59
+ ### 4. Honour the scope guards
60
+
61
+ - A `large` sibling without `--path-scope` is skipped with a note — re-run with
62
+ a scope. Do not remove the guard.
63
+ - A sibling not `include: true` is never read.
64
+
65
+ ## Rules
66
+
67
+ - **Read-only.** Never write to a sibling. Out-of-root writes still pass the
68
+ host permission gate; this surface writes nothing.
69
+ - **Opt-in only, targeted only.** No full-tree sweep; no implicit inclusion.
70
+ - **Secrets never cross repos** — the chunk redactor runs before any text is shown.
71
+ - **One concept per invocation.** Do not chain.
@@ -33,6 +33,7 @@ contract — input shapes, bounds, redaction defaults, storage layout.
33
33
  | `/knowledge ingest` | `commands/knowledge/ingest.md` | Walk a local path, redact, chunk, persist to `agents/memory/knowledge/<ingest-id>/` |
34
34
  | `/knowledge list` | `commands/knowledge/list.md` | List existing ingests (table or JSON); pin / unpin by id prefix |
35
35
  | `/knowledge forget` | `commands/knowledge/forget.md` | Drop a single ingest by id prefix (atomic, no partial state) |
36
+ | `/knowledge cross-repo` | `commands/knowledge/cross-repo.md` | Targeted read-only retrieval over opted-in linked-project siblings (ADR-032 Option A) |
36
37
 
37
38
  Sub-command names match the locked contract in
38
39
  [`docs/contracts/command-clusters.md`](../docs/contracts/command-clusters.md).
@@ -48,6 +49,7 @@ Sub-command names match the locked contract in
48
49
  > 1. ingest — point at a local folder, `.zip`, or file
49
50
  > 2. list — show what's already ingested (`--pin` / `--unpin` to flag)
50
51
  > 3. forget — drop an ingest by id prefix
52
+ > 4. cross-repo — targeted read-only retrieval over opted-in siblings
51
53
 
52
54
  ## Rules
53
55
 
@@ -0,0 +1,67 @@
1
+ ---
2
+ model_tier: inherit
3
+ name: skill:preview
4
+ tier: 2
5
+ cluster: skill
6
+ sub: preview
7
+ description: Non-destructive preview of a skill — its declared steps, execution type, allowed tools, and file/command targets — before you run it. Read-only, no execution.
8
+ skills: [file-editor]
9
+ suggestion:
10
+ eligible: true
11
+ trigger_description: "what does this skill do, preview <skill> before running, what will it change, is it safe, /skill:preview competitive-positioning"
12
+ trigger_context: "user wants to inspect a skill's declared intent before committing to run it"
13
+ workspaces:
14
+ - agent-config-maintainer
15
+ packs:
16
+ - meta
17
+ ---
18
+
19
+ # /skill preview
20
+
21
+ Renders a skill's **declared intent** — its `## Steps`, execution type, handler,
22
+ `allowed_tools`, and any file/command targets named in its body — so you can
23
+ decide whether to run it. Read-only, no network, no execution. Implements the
24
+ [`skill-dry-run`](../../../docs/contracts/skill-dry-run.md) contract.
25
+
26
+ ## Prerequisites
27
+
28
+ - Python 3.10 + PyYAML on the host.
29
+ - A skill name that resolves to `.agent-src/skills/<name>/SKILL.md`.
30
+
31
+ ## Steps
32
+
33
+ ### 1. Parse the argument
34
+
35
+ `/skill preview <name> [--technical]`. The name is the first positional
36
+ argument. Missing name → print usage and stop.
37
+
38
+ ### 2. Run the previewer
39
+
40
+ ```bash
41
+ python3 scripts/skill_preview.py <name>
42
+ ```
43
+
44
+ Add `--technical` for the raw frontmatter + numbered step list; default is the
45
+ plain-language summary. `--format json` is machine-readable.
46
+
47
+ ### 3. Present the summary
48
+
49
+ Show the plain-language preview: the skill's execution type (a manual-execution skill
50
+ renders **"instructional only — no automatic execution"**; an assisted-execution skill
51
+ renders its proposed actions), declared steps, tools, and any file/command
52
+ targets. End on the contract reminder that preview shows *declared intent*, not
53
+ a guarantee of side-effect-freeness.
54
+
55
+ ### 4. Hand back the decision
56
+
57
+ Preview never runs the skill. After showing it, let the user decide whether to
58
+ invoke the skill — that is the safe adoption loop:
59
+ `/skills:discover` → `/skill:preview` → run.
60
+
61
+ ## Rules
62
+
63
+ - **Read-only, no execution.** Preview inspects the SKILL.md; it does not run it.
64
+ - **Not a sandbox** — it cannot prove a skill is harmless; it shows what the
65
+ skill *declares* it will touch.
66
+ - **Malformed / missing SKILL.md → a structured error**, never a crash.
67
+ - **One skill per invocation.**
@@ -0,0 +1,48 @@
1
+ ---
2
+ model_tier: inherit
3
+ name: skill
4
+ tier: 2
5
+ description: Single-skill orchestrator — routes to preview. Non-destructive "what will this skill do?" before you run it.
6
+ cluster: skill
7
+ type: orchestrator
8
+ suggestion:
9
+ eligible: true
10
+ trigger_description: "what does this skill do, preview this skill before running, is this skill safe to run, what will it change, /skill:preview <name>"
11
+ trigger_context: "user wants to see a skill's declared steps + targets before committing to running it"
12
+ workspaces:
13
+ - agent-config-maintainer
14
+ packs:
15
+ - meta
16
+ ---
17
+
18
+ # /skill
19
+
20
+ Top-level orchestrator for the `/skill` family — **single-skill** operations
21
+ (singular `skill` for one target; plural `/skills` is the catalog-wide
22
+ discovery cluster). Today it carries one verb: `preview`.
23
+
24
+ Anchors: [`skill-dry-run`](../docs/contracts/skill-dry-run.md) contract —
25
+ what "preview" means, the explicit non-goals, and the surface.
26
+
27
+ ## Sub-commands
28
+
29
+ | Sub-command | Routes to | Purpose |
30
+ |---|---|---|
31
+ | `/skill preview` | `commands/skill/preview.md` | Render a skill's declared steps, execution type, tools, and file/command targets before running it |
32
+
33
+ Sub-command names match the locked contract in
34
+ [`docs/contracts/command-clusters.md`](../docs/contracts/command-clusters.md).
35
+
36
+ ## Dispatch
37
+
38
+ 1. Parse the user's argument: `/skill <sub-command> [args]`.
39
+ 2. Look up the sub-command in the table above.
40
+ 3. Load the body of the routed file and follow its `## Steps` section verbatim.
41
+ 4. Unknown / missing sub-command → route to `preview` (the only verb today).
42
+
43
+ ## Rules
44
+
45
+ - **Read-only.** Preview reads a skill's SKILL.md; it never runs the skill.
46
+ - **Not a sandbox.** Preview surfaces *declared intent*, not a guarantee of
47
+ side-effect-freeness — a contract non-goal.
48
+ - **One skill per invocation.** Do not chain.
@@ -0,0 +1,76 @@
1
+ ---
2
+ model_tier: inherit
3
+ name: skills:discover
4
+ tier: 2
5
+ cluster: skills
6
+ sub: discover
7
+ description: Recommend skills for a role — ranked by four explained classes (most-useful-for-role, related-to-current-task, recently-adopted, popular-in-role). Local-only; every result carries a why.
8
+ skills: [file-editor]
9
+ suggestion:
10
+ eligible: true
11
+ trigger_description: "which skills should I use, recommend skills for my role, what fits this work, help me find a skill, /skills:discover sales"
12
+ trigger_context: "user wants a short, explained skill shortlist instead of scanning the 220-skill catalog"
13
+ workspaces:
14
+ - agent-config-maintainer
15
+ packs:
16
+ - meta
17
+ ---
18
+
19
+ # /skills discover
20
+
21
+ Surfaces a short, explained skill shortlist for a role. Reuses existing local
22
+ signals only — the skill catalog frontmatter, the role's `skills.yml`
23
+ shortlist, and (when present and not opted out) the local-analytics JSONL.
24
+ Implements the [`skill-discovery`](../../../docs/contracts/skill-discovery.md)
25
+ contract. Local-only, read-only, no network.
26
+
27
+ ## Prerequisites
28
+
29
+ - Python 3.10 + PyYAML on the host (already a package dependency).
30
+ - A role id — passed as `[role]`, or the active role from
31
+ `.agent-settings.yml` → `roles.active_role`.
32
+
33
+ ## Steps
34
+
35
+ ### 1. Resolve the role
36
+
37
+ The user invokes `/skills discover [role]`. The role is the first positional
38
+ argument. If omitted, the recommender falls back to `roles.active_role`. If
39
+ neither resolves, it prints the available roles and stops — do not guess.
40
+
41
+ ### 2. Run the recommender
42
+
43
+ ```bash
44
+ python3 scripts/skill_discovery.py --role <role>
45
+ ```
46
+
47
+ Optional flags: `--format json` (machine-readable), `--limit N` (results per
48
+ class, default 5). The script is pure-local and writes nothing.
49
+
50
+ ### 3. Present the table
51
+
52
+ Render the recommender's Markdown table to the user:
53
+ `skill · class · why · first command`. Each row's `why` names the *signal*
54
+ (role match, domain adjacency, recent adoption, role popularity) — never a
55
+ bare score. The four classes are:
56
+
57
+ - `most-useful-for-role` — the role's priority shortlist.
58
+ - `related-to-current-task` — same-domain peers not already shortlisted.
59
+ - `recently-adopted` — used recently in this workspace (analytics) or the
60
+ shortlist tail when no usage signal exists yet.
61
+ - `popular-in-role` — launched most by this role locally (analytics) or the
62
+ shortlist when no usage signal exists yet.
63
+
64
+ ### 4. Offer the first command
65
+
66
+ Each row carries a `first command` — the natural way to start with that skill.
67
+ Suggest the user pick one and run it. Do **not** auto-invoke a skill.
68
+
69
+ ## Rules
70
+
71
+ - **Local-only, read-only.** No network, no writes, no prompt/response bodies.
72
+ - **Every result has a non-empty `why`** — a contract invariant.
73
+ - **Analytics opt-out honoured.** `AGENT_CONFIG_NO_LOCAL_ANALYTICS` env or
74
+ `analytics.local: off` → the analytics-backed classes fall back to the role
75
+ shortlist with an honest `why`; the surface never fabricates a usage signal.
76
+ - **One role per invocation.** Do not chain.
@@ -0,0 +1,56 @@
1
+ ---
2
+ model_tier: inherit
3
+ name: skills
4
+ tier: 2
5
+ description: Skill discovery orchestrator — routes to discover. Local, explained skill recommendations over the catalog + role shortlists + optional local analytics.
6
+ cluster: skills
7
+ type: orchestrator
8
+ suggestion:
9
+ eligible: true
10
+ trigger_description: "which skills should I use, recommend skills for my role, what skills fit this work, I can't find the right skill, /skills:discover"
11
+ trigger_context: "user is lost in the 220-skill catalog and wants a short, explained shortlist for their role"
12
+ workspaces:
13
+ - agent-config-maintainer
14
+ packs:
15
+ - meta
16
+ ---
17
+
18
+ # /skills
19
+
20
+ Top-level orchestrator for the `/skills` family — the **skill discovery**
21
+ cluster. Turns existing local signals (the skill catalog, the active role's
22
+ shortlist, and optional local analytics) into a short, *explained*
23
+ recommendation list. Local-only, no network, honours the analytics opt-out.
24
+
25
+ Anchors: [`skill-discovery`](../docs/contracts/skill-discovery.md) contract —
26
+ input signals, the four recommendation classes, and the non-negotiable
27
+ `why`-per-result requirement.
28
+
29
+ ## Sub-commands
30
+
31
+ | Sub-command | Routes to | Purpose |
32
+ |---|---|---|
33
+ | `/skills discover` | `commands/skills/discover.md` | Rank skills for a role by four explained classes (most-useful / related / recently-adopted / popular) |
34
+
35
+ Sub-command names match the locked contract in
36
+ [`docs/contracts/command-clusters.md`](../docs/contracts/command-clusters.md).
37
+
38
+ ## Dispatch
39
+
40
+ 1. Parse the user's argument: `/skills <sub-command> [args]`.
41
+ 2. Look up the sub-command in the table above.
42
+ 3. Load the body of the routed file and follow its `## Steps` section
43
+ verbatim with the remaining args.
44
+ 4. If the sub-command is unknown or missing, route to `discover` (the only
45
+ sub-command today) and print its menu.
46
+
47
+ ## Rules
48
+
49
+ - **Local-only.** The recommender reads local files only — the catalog, the
50
+ role `skills.yml`, and (if present and not opted out) the local-analytics
51
+ JSONL. No network, no writes.
52
+ - **Every recommendation carries a `why`.** Never surface an unexplained
53
+ score — this is a contract invariant.
54
+ - **Honours the analytics opt-out** (`AGENT_CONFIG_NO_LOCAL_ANALYTICS` env or
55
+ `analytics.local: off`); degrades to catalog + role shortlist gracefully.
56
+ - **Do NOT chain sub-commands.** One `/skills <sub>` per turn.
@@ -0,0 +1,317 @@
1
+ ---
2
+ model_tier: inherit
3
+ name: video:from-song
4
+ tier: 2
5
+ cluster: video
6
+ sub: from-song
7
+ description: Music-video from a song + reference images — accept or derive a timed scene script, optional character-lock, render, stitch, mux song as master track. Dry-run default; one batch gate for live calls.
8
+ personas: [hollywood-director, ai-video-technical-director]
9
+ skills: [song-to-script, scene-expander, video-director, character-consistency, motion-choreographer]
10
+ suggestion:
11
+ eligible: true
12
+ trigger_description: "make a music video from a song, turn a track into a video, lip-sync clip from images and audio, AI music video"
13
+ trigger_context: "user supplies an audio file plus reference images and wants a final MP4 cut to the song"
14
+ workspaces:
15
+ - agent-config-maintainer
16
+ packs:
17
+ - meta
18
+ lifecycle: experimental
19
+ trust:
20
+ level: experimental
21
+ install:
22
+ default: false
23
+ removable: true
24
+ ---
25
+
26
+ # /video:from-song
27
+
28
+ `/video:from-song <images-dir> <song-file> [--brief "<description>"] [--auto-script] [--scene-durations <list>] [--character|--no-character] [--auto-pick] [--keep-native-audio] [--max-duration <min>] [--max-scenes <n>] [--image-provider <id>] [--video-provider <id>]`
29
+
30
+ Turns a **song** plus a **folder of reference images** into a finished
31
+ music-video. The scene script is either supplied by the operator
32
+ (`--brief`) or derived from the audio itself (`--auto-script`); if
33
+ neither flag is present the command asks. After the script exists this
34
+ command reuses the same render path as
35
+ [`/video:from-script`](from-script.md) and ends by muxing the song over
36
+ the cut as the **master audio track**.
37
+
38
+ Provider flags override the `<default-image-provider>` /
39
+ `<default-video-provider>` from
40
+ [`agents/.ai-video.xml`](../../../agents/templates/.ai-video.xml.example);
41
+ absent flags fall back to the XML defaults.
42
+
43
+ **Requires `pack-ai-video`.** The declared skills
44
+ (`song-to-script`, `scene-expander`, `video-director`,
45
+ `character-consistency`, `motion-choreographer`) ship in that pack; on a
46
+ global-only install Step 1's `validate-deps.sh` fails fast with the
47
+ missing-id list instead of an opaque mid-run error — install the pack
48
+ and re-run.
49
+
50
+ **Block-on-ambiguity:** a missing/empty images directory, an unreadable
51
+ audio file, contradictory mode flags (`--brief` *and* `--auto-script`),
52
+ contradictory character flags (`--character` *and* `--no-character`), or
53
+ a contradictory provider flag halts the run with a precise message — no
54
+ silent best-guess.
55
+
56
+ ## Inputs
57
+
58
+ | Input | Required | Meaning |
59
+ |---|---|---|
60
+ | `<images-dir>` | yes | Folder of reference stills (`.png` / `.jpg`). When they contain a consistent human subject the on-screen identity is locked from them; otherwise the run is style-only (Step 6). |
61
+ | `<song-file>` | yes | Audio track (`.mp3` / `.wav` / `.m4a`). Defines total duration and, in `--auto-script` mode, the scene structure. |
62
+ | `--brief "<text>"` | one of brief/auto | Operator-written description of the video (mood, story, settings). |
63
+ | `--auto-script` | one of brief/auto | Derive the script from the song via the `song-to-script` skill. |
64
+ | `--scene-durations <list>` | no | Manual cut points (e.g. `0:00-0:15,0:15-0:30,…`). Overrides probe timing — the honest path when the track is flat (probe `method: interval`). |
65
+ | `--character` / `--no-character` | no | Force character-lock on/off. Default: auto-detect a subject in `<images-dir>`. |
66
+ | `--keep-native-audio` | no | Keep provider-generated audio instead of dropping it for the song (Step 8). |
67
+
68
+ ## Steps
69
+
70
+ ### 1. Validate dependencies
71
+
72
+ ```bash
73
+ scripts/ai-video/lib/validate-deps.sh .agent-src.uncondensed/commands/video/from-song.md
74
+ ```
75
+
76
+ Fails fast with the missing-id list if any declared persona / skill is
77
+ absent from `.agent-src/personas/` or `.agent-src/skills/`. No network
78
+ call has happened yet.
79
+
80
+ Then confirm the **runtime helper scripts** exist and are executable —
81
+ `scripts/ai-video/lib/probe-audio.sh`, `scripts/ai-video/lib/load-config.sh`,
82
+ `scripts/ai-video/stitch.sh` — so a missing script fails here (with the
83
+ path), not mid-run at Step 2/9.
84
+
85
+ **Non-interactive contexts.** When stdin is not a TTY (CI, cron, headless
86
+ harness) the command cannot prompt: it requires `--brief` or
87
+ `--auto-script`, an explicit `--character`/`--no-character`, and
88
+ `--auto-pick`, and refuses live calls outright. Missing any → fail fast
89
+ with usage, never a deadlocked prompt.
90
+
91
+ ### 2. Validate inputs + media-governance input gate
92
+
93
+ - `<images-dir>` exists and holds ≥1 `.png`/`.jpg`. Empty or missing →
94
+ halt, list what was found.
95
+ - `<song-file>` exists and is a readable audio container (`ffprobe`
96
+ returns an audio stream). Probe its length + structure now:
97
+ ```bash
98
+ scripts/ai-video/lib/probe-audio.sh <song-file>
99
+ ```
100
+ Emits `{duration, method, warning?, sections:[…]}` (deterministic, no
101
+ network). `duration` becomes the target length of the final cut. A
102
+ `method: interval` result (flat / brick-walled track) is surfaced to
103
+ the operator with the suggestion to pass `--scene-durations` for
104
+ musical cuts — never presented as beat-synced.
105
+ - **Media-governance input gate (mandatory).** Before any render,
106
+ consult the project-local media policies per
107
+ [`media-governance-routing`](../../rules/media-governance-routing.md):
108
+ - reference stills or brief depict a **real person's likeness** →
109
+ [`likeness`](../../../agents/settings/policies/media/likeness.md);
110
+ - a **recognised public figure** →
111
+ [`public-figures`](../../../agents/settings/policies/media/public-figures.md);
112
+ - the track is a **recognisable commercial song / a real artist's
113
+ voice** or the brief asks to clone one →
114
+ [`voice-cloning`](../../../agents/settings/policies/media/voice-cloning.md).
115
+ On a match: **refuse-and-surface** (one question per turn) — do not
116
+ best-guess past a likeness / rights concern.
117
+
118
+ ### 3. Cost + duration guard (theory of failure)
119
+
120
+ Refuse, with a precise message, **before** loading providers:
121
+
122
+ - a song longer than the configured cap (default 8 min) — a 45-minute
123
+ track would launch a runaway paid render;
124
+ - a derived/briefed scene count above the cap (default 40 scenes).
125
+
126
+ The operator raises a cap explicitly (`--max-duration` / `--max-scenes`)
127
+ if they really mean it; the guard never silently proceeds.
128
+
129
+ ### 4. Load config + resolve providers
130
+
131
+ Source `scripts/ai-video/lib/load-config.sh`. Resolve image / video
132
+ provider: command flag → `agents/.ai-video.xml` default → fail with the
133
+ available-providers list. A **malformed XML** or a default/flag naming a
134
+ provider with no `scripts/ai-video/adapters/<id>.sh` → fail fast here
135
+ with `provider '<id>' not found in adapters/` and the available list;
136
+ never a cryptic shell error mid-run. Surface the resolved provider's **lifecycle
137
+ tier** per
138
+ [`provider-lifecycle-discipline`](../../rules/provider-lifecycle-discipline.md);
139
+ all shipped adapters are `experimental` today, so the refuse-and-surface
140
+ path fires before any live call. For a music-video the **song is the
141
+ master track**, so a video provider with `audio-native=false` (e.g.
142
+ `kling`) is fine; native-audio providers (`gemini-veo`, `sora`) still
143
+ work — their audio is dropped at mux time in Step 8 unless
144
+ `--keep-native-audio` or a lip-sync scene needs it.
145
+
146
+ ### 5. Select script mode (block on ambiguity)
147
+
148
+ - `--brief` and `--auto-script` both present → halt: "Pick one source
149
+ for the script."
150
+ - Exactly one present → use it.
151
+ - **Neither present → ask, then stop and wait:**
152
+
153
+ ```
154
+ > How should I build the scene script?
155
+ >
156
+ > 1. From a description — I'll write the scenes to your brief
157
+ > 2. From the song — I'll derive scenes + timing from the audio
158
+ ```
159
+
160
+ ### 6. Build the timed scene script
161
+
162
+ Run the [`song-to-script`](../../skills/song-to-script/SKILL.md) skill
163
+ with the Step 2 probe result:
164
+
165
+ - **Brief mode** — the operator brief is the creative source; the audio
166
+ sections drive only the **cut timing**.
167
+ - **Auto mode** — the skill infers mood/energy per section and writes
168
+ both action and timing; vocal sections with lyrics populate
169
+ `dialogue:` for lip-sync.
170
+ - `--scene-durations` (if passed) overrides probe timing verbatim.
171
+
172
+ Output: `<project>/script.md` summing to the song length (reconciled in
173
+ Step 8). Present the script, the section→scene map, **and the probe
174
+ `method`**, then continue.
175
+
176
+ ### 7. Character lock — optional, auto-detected
177
+
178
+ Detect whether `<images-dir>` contains a consistent **human subject**.
179
+ **Consistent** = the same recognisable face recurs across the **majority**
180
+ of stills. The branch:
181
+
182
+ - **Consistent subject (or `--character`)** → run `character-consistency`
183
+ once, seeding it with `<images-dir>`. Writes `<project>/character.json`
184
+ (subject, palette, wardrobe, prop, seed) reused verbatim downstream;
185
+ the stills are passed as `ref_images` so the locked identity matches.
186
+ - **No face at all (or `--no-character`)** → **skip the lock**, tell the
187
+ operator, and run **style-only continuity**: the reference stills set
188
+ palette / setting / look, and `song-to-script` runs in style mode
189
+ (abstract / landscape / visualiser videos are first-class — a face is
190
+ never required). Zero-face input is **not** an error.
191
+ - **Ambiguous** (faces in only some stills, or several *distinct* faces
192
+ with no clear lead) → **block and ask** which subject to lock or whether
193
+ to go style-only. Never silently pick a mode on a coin-flip; the
194
+ `--character`/`--no-character` flags pre-answer this for non-interactive
195
+ runs.
196
+
197
+ ### 8. Render scenes (reuse from-script path) — ONE batch cost gate
198
+
199
+ For each scene in `<project>/script.md`, run Steps 3–7 of
200
+ [`/video:from-script`](from-script.md) verbatim: `scene-expander` →
201
+ blueprint → `video-director` eight-block image prompt → operator pick →
202
+ `motion-choreographer` → video adapter.
203
+
204
+ **Single batch COST confirmation (not per-step).** `AIV_DRYRUN=true` is
205
+ the default. Before the *first* live call, print the whole plan in one
206
+ prompt — image+video adapter, models, total scene count, and total
207
+ estimated cost — and refuse to continue without an explicit operator
208
+ confirmation (a literal yes) in this turn (mirrors
209
+ [`non-destructive-by-default`](../../rules/non-destructive-by-default.md)).
210
+ Once confirmed, the run proceeds through every scene + stitch + mux
211
+ without re-prompting **for cost**. The one remaining interactive surface
212
+ is `from-script`'s per-scene **operator-pick** (best-of-N still
213
+ selection) — a creative choice, not a spend gate; `--auto-pick` collapses
214
+ it to best-of-1 so the batch is fully unattended (required in
215
+ non-interactive contexts).
216
+
217
+ **Mid-batch failure + abort.** A per-scene adapter failure (rate-limit
218
+ `429`, provider content-policy refusal, network drop) **halts the batch**,
219
+ writes the completed-scene state to `<project>/`, and surfaces which
220
+ scene failed and why — it does not skip ahead or burn the rest of the
221
+ budget. `SIGINT` (Ctrl-C) writes state and exits clean. Re-running the
222
+ command resumes from the completed scenes (the "one project per
223
+ invocation" resume path), so a failed or aborted run is recoverable
224
+ without re-paying for finished scenes.
225
+
226
+ ### 9. Stitch + master-audio mux + duration reconciliation
227
+
228
+ 1. Build `<project>/manifest.json` with every scene as **video-only**
229
+ (`audio_embedded: false`) so the concat is silent — unless
230
+ `--keep-native-audio`, or a scene is flagged `character: talking`
231
+ (lip-sync), where dropping audio would desync mouth motion: keep that
232
+ scene's native audio and surface the mixed-audio decision rather than
233
+ silently dubbing.
234
+ 2. Concatenate:
235
+ ```bash
236
+ scripts/ai-video/stitch.sh <project>/manifest.json <project>/cut.mp4
237
+ ```
238
+ 3. **Reconcile duration explicitly** — compare the silent cut length to
239
+ the song:
240
+ - cut == song (±1.0 s) → mux straight through;
241
+ - cut **shorter** → offer `--loop-last` (hold the final scene) or
242
+ `--retime` (re-derive Step 6 timing); default = trim audio to the
243
+ cut with a short audio fade-out on the tail;
244
+ - cut **longer** → default = hard-trim video to the song length.
245
+
246
+ Never pad silently — **and never trim silently either**: whichever
247
+ default fires, report it concretely ("trimmed scene 12 from 18.0 s to
248
+ 3.4 s to match the 3:45 song" / "faded the song tail by 0.6 s"), so the
249
+ operator sees exactly what the reconciliation did. Then mux the song as the master track:
250
+ ```bash
251
+ ffmpeg -loglevel error -y -i <project>/cut.mp4 -i <song-file> \
252
+ -map 0:v:0 -map 1:a:0 -c:v copy -c:a aac -shortest <project>/final.mp4
253
+ ```
254
+ 4. **Mandatory AI-generation disclosure (non-removable).** Embed the
255
+ disclosure metadata into `final.mp4` per
256
+ [`disclosure`](../../../agents/settings/policies/media/disclosure.md)
257
+ and, where the container supports it, a provenance tag per
258
+ [`transparency`](../../../agents/settings/policies/media/transparency.md).
259
+ The run **cannot complete** without the disclosure — it is not a flag.
260
+
261
+ ### 10. Report
262
+
263
+ Print: project slug, final MP4 path, song length vs. cut length, probe
264
+ `method`, scenes rendered, scenes skipped, script mode (`brief` | `auto`),
265
+ subject mode (`character` | `style`), provider + lifecycle tier,
266
+ **media-governance gate result** (pass / refused-and-surfaced — the audit
267
+ record), **reconciliation action** taken (Step 9.3), disclosure
268
+ confirmed, estimated cost (live mode) or `dry-run` marker. No commit. No
269
+ push.
270
+
271
+ ## Rules
272
+
273
+ - **No commit, no push, no PR.** Pipeline produces artefacts; the
274
+ operator chooses what to ship.
275
+ - **Dry-run is the default.** One batch confirmation gates all live
276
+ calls — never a per-step interrogation, never a silent live run.
277
+ - **Media governance is a hard gate.** Input likeness / public-figure /
278
+ voice checks block before render; the output MP4 always carries a
279
+ non-removable AI-generation disclosure.
280
+ - **The song is the master audio track.** Provider-native audio is
281
+ dropped at mux unless `--keep-native-audio` or a lip-sync scene needs
282
+ it (surface the conflict, never silently dub).
283
+ - **Cost + duration are guarded.** Over-cap songs / scene counts are
284
+ refused before any provider loads.
285
+ - **Character lock is optional.** A no-face image folder produces a
286
+ style-only video; never abort for a missing subject.
287
+ - **Block on ambiguity** — never silently best-guess the script source,
288
+ scene timing, provider, or character mode.
289
+ - **Honest cut framing.** A flat track's `interval` cuts are never
290
+ presented as beat-synced; point the operator at `--scene-durations`.
291
+ - **One project per invocation.** Re-running on the same project resumes
292
+ from existing artefacts (skips completed scenes); a failed or aborted
293
+ batch is recoverable this way without re-paying for finished scenes.
294
+ - **Kill-switch.** Ships `lifecycle: experimental` · `install.default:
295
+ false`. Disable = remove the command + `song-to-script` skill (then
296
+ regenerate the projected tool trees); the `/video` orchestrator
297
+ degrades gracefully on an absent sub-command.
298
+
299
+ ## Policies
300
+
301
+ - [`likeness`](../../../agents/settings/policies/media/likeness.md) ·
302
+ [`public-figures`](../../../agents/settings/policies/media/public-figures.md) ·
303
+ [`voice-cloning`](../../../agents/settings/policies/media/voice-cloning.md) —
304
+ input gate (Step 2).
305
+ - [`disclosure`](../../../agents/settings/policies/media/disclosure.md) ·
306
+ [`transparency`](../../../agents/settings/policies/media/transparency.md) —
307
+ mandatory output disclosure (Step 9.4).
308
+
309
+ ## See also
310
+
311
+ - [`/video:from-script`](from-script.md) — same render path from a
312
+ hand-written script
313
+ - [`/video:scene`](scene.md) — single-scene iteration
314
+ - [`/video:stitch`](stitch.md) — re-stitch after operator edits
315
+ - [`song-to-script`](../../skills/song-to-script/SKILL.md) — audio →
316
+ timed scene script
317
+ - [`scripts/ai-video/lib/adapter-contract.md`](../../../scripts/ai-video/lib/adapter-contract.md)