@event4u/agent-config 2.24.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 (51) 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 +5 -5
  4. package/.agent-src/commands/video/storyboard.md +1 -1
  5. package/.agent-src/contexts/execution/roadmap-process-loop.md +69 -14
  6. package/.agent-src/personas/README.md +3 -2
  7. package/.agent-src/personas/ai-video-technical-director.md +2 -2
  8. package/.agent-src/personas/hollywood-director.md +3 -3
  9. package/.agent-src/profiles/content_creator.yml +5 -0
  10. package/.agent-src/rules/media-governance-routing.md +82 -0
  11. package/.agent-src/rules/persona-governance.md +90 -0
  12. package/.agent-src/rules/post-push-rewrite-discipline.md +70 -0
  13. package/.agent-src/rules/provider-lifecycle-discipline.md +75 -0
  14. package/.agent-src/rules/roadmap-ci-steps-policy.md +145 -0
  15. package/.agent-src/rules/roadmap-progress-sync.md +11 -5
  16. package/.agent-src/skills/character-consistency/SKILL.md +12 -1
  17. package/.agent-src/skills/git-workflow/SKILL.md +133 -0
  18. package/.agent-src/skills/motion-choreographer/SKILL.md +12 -0
  19. package/.agent-src/skills/pixar-storyteller/SKILL.md +19 -6
  20. package/.agent-src/skills/roadmap-writing/SKILL.md +10 -0
  21. package/.agent-src/skills/scene-expander/SKILL.md +22 -7
  22. package/.agent-src/skills/video-director/SKILL.md +13 -0
  23. package/.agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  24. package/.agent-src/templates/roadmaps.md +16 -0
  25. package/.claude-plugin/marketplace.json +1 -1
  26. package/CHANGELOG.md +43 -0
  27. package/README.md +5 -3
  28. package/config/agent-settings.template.yml +26 -0
  29. package/docs/architecture.md +1 -1
  30. package/docs/catalog.md +5 -2
  31. package/docs/contracts/file-ownership-matrix.json +81 -13
  32. package/docs/contracts/provider-lifecycle.md +122 -0
  33. package/docs/decisions/ADR-011-domain-pack-readiness.md +213 -0
  34. package/docs/decisions/INDEX.md +1 -0
  35. package/docs/getting-started-by-role.md +10 -0
  36. package/docs/getting-started.md +1 -1
  37. package/docs/personas.md +73 -26
  38. package/docs/profiles.md +9 -4
  39. package/package.json +1 -1
  40. package/scripts/_tmp_scan_framework_leakage.py +119 -0
  41. package/scripts/ai-video/adapters/gemini-veo.sh +5 -0
  42. package/scripts/ai-video/adapters/higgsfield.sh +6 -0
  43. package/scripts/ai-video/adapters/kling.sh +5 -0
  44. package/scripts/ai-video/adapters/openai-images.sh +5 -0
  45. package/scripts/ai-video/adapters/sora.sh +6 -0
  46. package/scripts/check_portability.py +6 -0
  47. package/scripts/lint_media_policy_linkage.py +140 -0
  48. package/scripts/lint_persona_governance.py +164 -0
  49. package/scripts/lint_roadmap_ci_steps.py +182 -0
  50. package/scripts/smoke/schema.sh +1 -1
  51. package/.agent-src/personas/pixar-storyboard-artist.md +0 -98
@@ -0,0 +1,140 @@
1
+ #!/usr/bin/env python3
2
+ """Lint structural reachability of media governance policies.
3
+
4
+ Every policy file under `agents/policies/media/` (except README) must
5
+ be linked from at least one of:
6
+
7
+ * a skill SKILL.md (any .agent-src.uncompressed/skills/*/SKILL.md
8
+ or .claude/skills/*/SKILL.md),
9
+ * a routing rule under .agent-src.uncompressed/rules/, or
10
+ * a sibling policy file under agents/policies/media/.
11
+
12
+ A policy that no surface references is a silent policy and a silent
13
+ policy is a failed policy. This is the CI-side reachability guarantee
14
+ the agent-in-the-loop enforcement model rests on (see
15
+ agents/policies/media/README.md § Enforcement model).
16
+
17
+ Exit codes:
18
+ 0 all policies linked
19
+ 1 one or more orphan policies
20
+ """
21
+ from __future__ import annotations
22
+
23
+ import sys
24
+ from pathlib import Path
25
+
26
+ QUIET = "--quiet" in sys.argv
27
+
28
+ REPO = Path(__file__).resolve().parents[1]
29
+ POLICY_DIR = REPO / "agents" / "policies" / "media"
30
+ EXEMPT_STEMS = frozenset({"README"})
31
+
32
+ # Surfaces scanned for inbound references to policy files.
33
+ SCAN_ROOTS: tuple[Path, ...] = (
34
+ REPO / ".agent-src.uncompressed" / "skills",
35
+ REPO / ".agent-src.uncompressed" / "rules",
36
+ REPO / ".agent-src.uncompressed" / "commands",
37
+ REPO / ".claude" / "skills",
38
+ REPO / "agents" / "policies" / "media",
39
+ )
40
+
41
+
42
+ def emit(msg: str) -> None:
43
+ if not QUIET:
44
+ print(msg)
45
+
46
+
47
+ def collect_policies() -> list[Path]:
48
+ if not POLICY_DIR.exists():
49
+ return []
50
+ return sorted(
51
+ p
52
+ for p in POLICY_DIR.glob("*.md")
53
+ if p.stem not in EXEMPT_STEMS
54
+ )
55
+
56
+
57
+ def collect_scan_files() -> list[Path]:
58
+ files: list[Path] = []
59
+ for root in SCAN_ROOTS:
60
+ if not root.exists():
61
+ continue
62
+ files.extend(root.rglob("*.md"))
63
+ return files
64
+
65
+
66
+ def referrers_for(policy: Path, scan_files: list[Path]) -> list[Path]:
67
+ """Return files that reference `policy` by its repo-relative name
68
+ or basename. We accept both the full path token
69
+ (`agents/policies/media/likeness.md`) and the bare basename
70
+ (`likeness.md`) inside a markdown link, because sibling policies
71
+ link via relative `[likeness.md](likeness.md)` form.
72
+ """
73
+ needles = (
74
+ f"policies/media/{policy.name}",
75
+ f"]({policy.name})",
76
+ )
77
+ referrers: list[Path] = []
78
+ for scan_file in scan_files:
79
+ # A policy can't satisfy its own linkage requirement.
80
+ if scan_file.resolve() == policy.resolve():
81
+ continue
82
+ try:
83
+ text = scan_file.read_text(encoding="utf-8", errors="replace")
84
+ except OSError:
85
+ continue
86
+ if any(n in text for n in needles):
87
+ referrers.append(scan_file)
88
+ return referrers
89
+
90
+
91
+ def main() -> int:
92
+ if not POLICY_DIR.exists():
93
+ emit(
94
+ "media-policy-linkage: agents/policies/media/ missing — "
95
+ "nothing to lint."
96
+ )
97
+ return 0
98
+
99
+ policies = collect_policies()
100
+ if not policies:
101
+ emit(
102
+ "media-policy-linkage: agents/policies/media/ has no policy "
103
+ "files — nothing to lint."
104
+ )
105
+ return 0
106
+
107
+ scan_files = collect_scan_files()
108
+ orphans: list[Path] = []
109
+ for policy in policies:
110
+ referrers = referrers_for(policy, scan_files)
111
+ rel = policy.relative_to(REPO)
112
+ if not referrers:
113
+ orphans.append(policy)
114
+ emit(f"❌ ORPHAN {rel}")
115
+ continue
116
+ emit(f"✅ {rel} ({len(referrers)} referrer(s))")
117
+
118
+ if orphans:
119
+ print(
120
+ f"\nmedia-policy-linkage: {len(orphans)} orphan policy "
121
+ f"file(s) — every policy must be linked from a skill, rule, "
122
+ f"or sibling policy.",
123
+ file=sys.stderr,
124
+ )
125
+ for o in orphans:
126
+ print(
127
+ f" - {o.relative_to(REPO)}",
128
+ file=sys.stderr,
129
+ )
130
+ return 1
131
+
132
+ emit(
133
+ f"media-policy-linkage: {len(policies)} policy file(s) — all "
134
+ f"linked."
135
+ )
136
+ return 0
137
+
138
+
139
+ if __name__ == "__main__":
140
+ sys.exit(main())
@@ -0,0 +1,164 @@
1
+ #!/usr/bin/env python3
2
+ """Lint persona governance — per-domain cap (hard) + citation floor (warn).
3
+
4
+ Enforces the mechanical checks in
5
+ `.agent-src.uncompressed/rules/persona-governance.md`:
6
+
7
+ 1. **Per-domain cap (HARD)** — ≤ 2 active specialist personas per
8
+ content domain. Core-tier personas are exempt. `status:
9
+ deprecated` rows (if any survive a transition window) are
10
+ excluded from the count; the canonical path is in-commit
11
+ deletion, no soak window.
12
+ 2. **Skill citation floor (WARN)** — every active specialist
13
+ persona SHOULD be cited by `personas: [<id>]` in at least one
14
+ skill SKILL.md under `.agent-src.uncompressed/skills/` or
15
+ `.claude/skills/`. Surfaced as a warning, never blocks CI:
16
+ the citation floor is enforced at PR time per the rule (a new
17
+ specialist MUST land with a cite); pre-existing wiring debt is
18
+ tracked as `--citation-debt` for the maintainer dashboard.
19
+
20
+ Schema conformance (check 4) is delegated to `lint-skills`.
21
+ Deprecation path (check 3) is reviewed at PR time via the table in
22
+ `docs/personas.md`.
23
+
24
+ Domain inference: persona ids → content domain via `DOMAIN_MAP`,
25
+ mirroring `persona-governance.md § Per-domain cap`. Personas absent
26
+ from the map are cross-cutting (uncapped).
27
+
28
+ Exit codes:
29
+ 0 per-domain cap clean (citation warnings non-blocking)
30
+ 1 per-domain cap violated
31
+ """
32
+ from __future__ import annotations
33
+
34
+ import re
35
+ import sys
36
+ from collections import defaultdict
37
+ from pathlib import Path
38
+
39
+ QUIET = "--quiet" in sys.argv
40
+
41
+ REPO = Path(__file__).resolve().parents[1]
42
+ PERSONA_DIR = REPO / ".agent-src.uncompressed" / "personas"
43
+ SKILL_ROOTS: tuple[Path, ...] = (
44
+ REPO / ".agent-src.uncompressed" / "skills",
45
+ REPO / ".claude" / "skills",
46
+ )
47
+
48
+ # Per-domain cap — mirrors persona-governance.md § Per-domain cap.
49
+ # Maps persona id → content-domain bucket. Personas absent from this
50
+ # map are cross-cutting (uncapped) — typically singleton specialists
51
+ # without a domain peer (e.g. `qa`, `tech-writer`).
52
+ DOMAIN_MAP: dict[str, str] = {
53
+ "hollywood-director": "ai-video",
54
+ "ai-video-technical-director": "ai-video",
55
+ "backend-architect": "backend",
56
+ "eloquent-tamer": "backend",
57
+ "cmo": "gtm",
58
+ "revops": "gtm",
59
+ "growth-pm": "growth",
60
+ "customer-success-lead": "customer",
61
+ "discovery-lead": "customer",
62
+ "engineering-manager": "people",
63
+ "people-strategist": "people",
64
+ "finance-partner": "money",
65
+ "strategist": "money",
66
+ }
67
+ PER_DOMAIN_CAP = 2
68
+
69
+
70
+ def emit(msg: str) -> None:
71
+ if not QUIET:
72
+ print(msg)
73
+
74
+
75
+ def parse_frontmatter(path: Path) -> dict[str, str]:
76
+ text = path.read_text(encoding="utf-8", errors="replace")
77
+ if not text.startswith("---"):
78
+ return {}
79
+ end = text.find("\n---", 3)
80
+ if end == -1:
81
+ return {}
82
+ out: dict[str, str] = {}
83
+ for line in text[3:end].splitlines():
84
+ m = re.match(r"^([a-zA-Z_][\w-]*):\s*(.*)$", line)
85
+ if m:
86
+ out[m.group(1)] = m.group(2).strip().strip('"').strip("'")
87
+ return out
88
+
89
+
90
+ def collect_personas() -> list[tuple[str, str, str, Path]]:
91
+ """Return (id, tier, status, path) for every persona file."""
92
+ out: list[tuple[str, str, str, Path]] = []
93
+ if not PERSONA_DIR.exists():
94
+ return out
95
+ for path in sorted(PERSONA_DIR.glob("*.md")):
96
+ if path.stem == "README":
97
+ continue
98
+ fm = parse_frontmatter(path)
99
+ pid = fm.get("id") or path.stem
100
+ tier = fm.get("tier", "")
101
+ status = fm.get("status", "active") or "active"
102
+ out.append((pid, tier, status, path))
103
+ return out
104
+
105
+
106
+ def citations_for(persona_id: str) -> list[Path]:
107
+ pattern = re.compile(rf"(^|[\s,\[]){re.escape(persona_id)}([\s,\]]|$)")
108
+ hits: list[Path] = []
109
+ for root in SKILL_ROOTS:
110
+ if not root.exists():
111
+ continue
112
+ for skill in root.rglob("SKILL.md"):
113
+ text = skill.read_text(encoding="utf-8", errors="replace")
114
+ if text.startswith("---"):
115
+ end = text.find("\n---", 3)
116
+ fm_block = text[3:end] if end != -1 else ""
117
+ else:
118
+ fm_block = ""
119
+ if "personas:" not in fm_block:
120
+ continue
121
+ if pattern.search(fm_block):
122
+ hits.append(skill)
123
+ return hits
124
+
125
+
126
+ def main() -> int:
127
+ personas = collect_personas()
128
+ if not personas:
129
+ emit("persona-governance: no persona files found — nothing to lint.")
130
+ return 0
131
+
132
+ by_domain: dict[str, list[str]] = defaultdict(list)
133
+ missing_citations: list[str] = []
134
+
135
+ for pid, tier, status, _ in personas:
136
+ if status == "deprecated" or tier != "specialist":
137
+ continue
138
+ domain = DOMAIN_MAP.get(pid)
139
+ if domain:
140
+ by_domain[domain].append(pid)
141
+ if not citations_for(pid):
142
+ missing_citations.append(pid)
143
+
144
+ overflows = {d: ids for d, ids in by_domain.items() if len(ids) > PER_DOMAIN_CAP}
145
+ for d, ids in sorted(by_domain.items()):
146
+ marker = "❌" if d in overflows else "✅"
147
+ emit(f"{marker} domain={d} {len(ids)}/{PER_DOMAIN_CAP} {', '.join(sorted(ids))}")
148
+ for pid in sorted(missing_citations):
149
+ emit(f"⚠️ no-skill-citation {pid} (warn — see PR-time gate)")
150
+
151
+ if overflows:
152
+ print("\npersona-governance: per-domain cap violated.", file=sys.stderr)
153
+ for d, ids in sorted(overflows.items()):
154
+ print(f" - domain '{d}' has {len(ids)} specialists (cap {PER_DOMAIN_CAP}): {', '.join(sorted(ids))}", file=sys.stderr)
155
+ return 1
156
+
157
+ active = sum(1 for _, t, s, _ in personas if s != "deprecated" and t == "specialist")
158
+ cited = active - len(missing_citations)
159
+ emit(f"persona-governance: {active} active specialist persona(s) — all domains within cap; {cited}/{active} cited by ≥ 1 skill.")
160
+ return 0
161
+
162
+
163
+ if __name__ == "__main__":
164
+ sys.exit(main())
@@ -0,0 +1,182 @@
1
+ #!/usr/bin/env python3
2
+ """Hard-Gate linter for the ``roadmap-ci-steps-policy`` rule.
3
+
4
+ Forbids full-pipeline CI literals (``task ci``, ``make test``,
5
+ ``npm run check`` etc.) inside ``agents/roadmaps/*.md`` checkbox steps
6
+ or fenced bash blocks **when** ``quality.local_auto_run`` in
7
+ ``.agent-settings.yml`` is ``false``.
8
+
9
+ Carve-outs:
10
+ * Setting is ``true`` → linter no-ops (exit 0).
11
+ * Step line carries ``<!-- carve-out: new-gate-verification -->`` →
12
+ allowed (new gate added by the same roadmap).
13
+ * ``## Acceptance criteria`` section → documentation, not steps.
14
+ * ``agents/roadmaps/archive/`` and ``agents/roadmaps/skipped/`` → out
15
+ of scope; they record history.
16
+
17
+ Cap: ≤ 150 LOC, stdlib only. Hooked into ``task ci-fast`` via
18
+ ``task lint-roadmap-ci-steps``.
19
+ """
20
+ from __future__ import annotations
21
+
22
+ import re
23
+ import sys
24
+ from pathlib import Path
25
+
26
+ QUIET = "--quiet" in sys.argv
27
+
28
+ REPO_ROOT = Path(__file__).resolve().parent.parent
29
+ ROADMAP_GLOB = "agents/roadmaps/*.md"
30
+ SETTINGS_FILE = REPO_ROOT / ".agent-settings.yml"
31
+ LOCAL_AUTO_RUN_PAT = re.compile(
32
+ r"^\s*local_auto_run:\s*(true|false)\s*(?:#.*)?$", re.MULTILINE
33
+ )
34
+ CARVE_OUT_MARKER = "carve-out: new-gate-verification"
35
+
36
+ # CI-shaped literals — case-insensitive whole-word(-ish) matches.
37
+ CI_PATTERNS: tuple[tuple[re.Pattern[str], str], ...] = (
38
+ (re.compile(r"\btask\s+ci-strict\b", re.IGNORECASE), "task ci-strict"),
39
+ (re.compile(r"\btask\s+ci-fast\b", re.IGNORECASE), "task ci-fast"),
40
+ (re.compile(r"\btask\s+ci\b(?!-)", re.IGNORECASE), "task ci"),
41
+ (re.compile(r"\bmake\s+ci\b", re.IGNORECASE), "make ci"),
42
+ (re.compile(r"\bmake\s+test\b", re.IGNORECASE), "make test"),
43
+ (re.compile(r"\bnpm\s+run\s+check\b", re.IGNORECASE), "npm run check"),
44
+ (re.compile(r"\bpnpm\s+run\s+check\b", re.IGNORECASE), "pnpm run check"),
45
+ (re.compile(r"\byarn\s+check\b", re.IGNORECASE), "yarn check"),
46
+ (re.compile(r"\bcomposer\s+test\b", re.IGNORECASE), "composer test"),
47
+ # Whole-suite = bare command, or command followed only by prose
48
+ # ("before the boundary"). A real shell argument starts with ``-``
49
+ # (flag) or contains ``/`` or ``.`` (path / .php file) — that
50
+ # signals a targeted run and is allowed.
51
+ (re.compile(r"\bvendor/bin/phpunit\b(?!\s+(?:-|\S*[/.]))", re.IGNORECASE),
52
+ "vendor/bin/phpunit (whole suite)"),
53
+ (re.compile(r"\bphp\s+artisan\s+test\b(?!\s+(?:-|\S*[/.]))", re.IGNORECASE),
54
+ "php artisan test (whole suite)"),
55
+ )
56
+
57
+ CHECKBOX_PAT = re.compile(r"^\s*-\s*\[[ x~/-]\]\s")
58
+ FENCE_PAT = re.compile(r"^\s*```")
59
+ HEADING_PAT = re.compile(r"^(#{1,6})\s+(.*?)\s*$")
60
+ ACCEPTANCE_HEADING_PAT = re.compile(
61
+ r"^acceptance criteria\b", re.IGNORECASE
62
+ )
63
+
64
+
65
+ def _read_local_auto_run() -> bool:
66
+ """Return ``quality.local_auto_run`` from ``.agent-settings.yml``.
67
+
68
+ Default ``True`` (= no-op) when file or key is missing. The Hard
69
+ Gate only fires when the setting is explicitly ``false``.
70
+ """
71
+ if not SETTINGS_FILE.is_file():
72
+ return True
73
+ try:
74
+ text = SETTINGS_FILE.read_text(encoding="utf-8")
75
+ except OSError:
76
+ return True
77
+ in_quality = False
78
+ for raw in text.splitlines():
79
+ if not raw.strip() or raw.lstrip().startswith("#"):
80
+ continue
81
+ if raw.startswith("quality:"):
82
+ in_quality = True
83
+ continue
84
+ if in_quality and raw and not raw.startswith((" ", "\t")):
85
+ in_quality = False
86
+ continue
87
+ if in_quality:
88
+ m = LOCAL_AUTO_RUN_PAT.match(raw)
89
+ if m:
90
+ return m.group(1).lower() == "true"
91
+ return True
92
+
93
+
94
+ def _scan(text: str) -> list[tuple[int, str, str]]:
95
+ """Return ``(line_no, matched_literal, line_text)`` for every hit.
96
+
97
+ Only scans:
98
+ * checkbox-step lines (``- [ ] …``)
99
+ * lines inside fenced code blocks (```` ``` ````)
100
+ Skips lines under an ``## Acceptance criteria`` heading.
101
+ Skips lines carrying the carve-out marker.
102
+ """
103
+ hits: list[tuple[int, str, str]] = []
104
+ in_fence = False
105
+ in_acceptance = False
106
+ for idx, line in enumerate(text.splitlines(), start=1):
107
+ if FENCE_PAT.match(line):
108
+ in_fence = not in_fence
109
+ continue
110
+ if not in_fence:
111
+ heading = HEADING_PAT.match(line)
112
+ if heading:
113
+ in_acceptance = bool(
114
+ ACCEPTANCE_HEADING_PAT.match(heading.group(2))
115
+ )
116
+ continue
117
+ if in_acceptance:
118
+ continue
119
+ is_checkbox = CHECKBOX_PAT.match(line) is not None
120
+ if not (is_checkbox or in_fence):
121
+ continue
122
+ if CARVE_OUT_MARKER in line:
123
+ continue
124
+ for pat, label in CI_PATTERNS:
125
+ if pat.search(line):
126
+ hits.append((idx, label, line.strip()))
127
+ break
128
+ return hits
129
+
130
+
131
+ def main() -> int:
132
+ if _read_local_auto_run():
133
+ if not QUIET:
134
+ print(
135
+ "✅ quality.local_auto_run=true (or unset) — "
136
+ "CI-step gate disabled"
137
+ )
138
+ return 0
139
+ roadmaps = sorted(REPO_ROOT.glob(ROADMAP_GLOB))
140
+ if not roadmaps:
141
+ if not QUIET:
142
+ print(f"✅ no active roadmaps under {ROADMAP_GLOB}")
143
+ return 0
144
+ failed = 0
145
+ for roadmap in roadmaps:
146
+ rel = roadmap.relative_to(REPO_ROOT)
147
+ text = roadmap.read_text(encoding="utf-8")
148
+ hits = _scan(text)
149
+ if hits:
150
+ failed += 1
151
+ print(f"❌ {rel}", file=sys.stderr)
152
+ for line_no, label, line_text in hits:
153
+ print(
154
+ f" line {line_no}: '{label}' in: {line_text}",
155
+ file=sys.stderr,
156
+ )
157
+ print(
158
+ " → reword as a narrow command "
159
+ "(e.g. 'vendor/bin/phpstan analyse app/Modules/X'), or "
160
+ "mark with '<!-- carve-out: new-gate-verification -->' "
161
+ "when the step verifies a NEW gate introduced by this "
162
+ "roadmap.",
163
+ file=sys.stderr,
164
+ )
165
+ else:
166
+ if not QUIET:
167
+ print(f"✅ {rel}")
168
+ if failed:
169
+ print(
170
+ f"\n❌ {failed} roadmap(s) schedule full-pipeline CI steps "
171
+ f"while quality.local_auto_run=false — "
172
+ f"see .augment/rules/roadmap-ci-steps-policy.md",
173
+ file=sys.stderr,
174
+ )
175
+ return 1
176
+ if not QUIET:
177
+ print(f"\n✅ {len(roadmaps)} roadmap(s) CI-step-clean")
178
+ return 0
179
+
180
+
181
+ if __name__ == "__main__":
182
+ sys.exit(main())
@@ -18,7 +18,7 @@
18
18
 
19
19
  set -euo pipefail
20
20
 
21
- EXPECTED_WARNS=92
21
+ EXPECTED_WARNS=93
22
22
  EXPECTED_MIN_TOTAL=438
23
23
 
24
24
  quiet="${SMOKE_QUIET:-0}"
@@ -1,98 +0,0 @@
1
- ---
2
- id: pixar-storyboard-artist
3
- role: Pixar Storyboard Artist
4
- description: "Senior animation storyboard artist — names the emotional beat, the acting choice, the environment that reacts, and refuses flat reads."
5
- tier: specialist
6
- mode: developer
7
- version: "1.0"
8
- source: package
9
- ---
10
-
11
- # Pixar Storyboard Artist
12
-
13
- ## Focus
14
-
15
- The acting read of a scene. A prompt is done when the character
16
- *wants* something, the environment *responds* to them, and one
17
- emotional beat is unambiguous in the frame. Refuses flat reads —
18
- eyes-open, mouth-shut, hands-at-sides — and demands acting choices
19
- the camera can pick up. Not responsible for live-action lensing
20
- (`hollywood-director`) or provider grammar (`ai-video-technical-director`).
21
-
22
- ## Mindset
23
-
24
- - A scene without a want is a still life. The character is reaching
25
- for something — name it.
26
- - Eyes carry the read. Eye line, blink rhythm, micro-glance — these
27
- are the acting, not the body pose.
28
- - The environment is a co-star. Leaves move because the wind moves;
29
- the wind moves because the moment shifts.
30
- - Anticipation → action → reaction is a unit. Skip anticipation and
31
- the action looks teleported.
32
- - Stylization is a choice, not a default. "Pixar-style" without a
33
- specific film reference is a wishlist, not a brief.
34
-
35
- ## Unique Questions
36
-
37
- - What does the character want in this beat, and what is in their way?
38
- - Where are the eyes pointing, and what does the eye line tell us
39
- about the want?
40
- - What does the environment do *because of* the character's action —
41
- not just around them?
42
- - Which secondary motion (hair, cloth, dust, leaves) reacts on the
43
- same beat as the primary action?
44
- - Which stylistic anchor (specific film, year, palette) grounds the
45
- look, instead of a generic "animated"?
46
-
47
- ## Output Expectations
48
-
49
- Four-block output, in this order: CHARACTER SHEET · SCENE PROMPT ·
50
- IMAGE PROMPT · VIDEO PROMPT. Each block is self-contained and can
51
- be handed to its downstream skill (image render vs. motion prompt)
52
- without rewriting.
53
-
54
- - CHARACTER SHEET names silhouette, palette, wardrobe, signature prop, posture default, eye behavior.
55
- - SCENE PROMPT names emotional beat, want, obstacle, stylistic anchor (film + year), environment reaction.
56
- - IMAGE PROMPT is a single still — peak moment, composition + palette explicit.
57
- - VIDEO PROMPT names anticipation → action → reaction with a beat count per phase.
58
- - Severity vocabulary on review: `must-fix · should-fix · nit`.
59
-
60
- ## Anti-Patterns
61
-
62
- - Do NOT default to neutral expressions. Every beat names a feeling the face is doing.
63
- - Do NOT describe the environment as backdrop. It reacts, or it is not in the prompt.
64
- - Do NOT cite "Pixar-style" without naming a specific film and year as the stylistic anchor.
65
- - Do NOT collapse anticipation and action into one motion — both phases named, or fail.
66
- - Do NOT prescribe lenses — that is the Hollywood director's block.
67
-
68
- ## Critical Rules
69
-
70
- - CHARACTER SHEET is reused verbatim across every scene in a run.
71
- Edits to identity tokens require an explicit revision note.
72
- - SCENE PROMPT names exactly one emotional beat. Compound beats
73
- ("sad but hopeful and tired") fail review.
74
- - VIDEO PROMPT names a beat count per phase (e.g., "anticipation 0.5s,
75
- action 1.2s, reaction 0.8s"). No vague pacing.
76
- - Stylistic anchor cites a specific film + year. Generic style words
77
- fail.
78
- - Eye line is named in every IMAGE PROMPT.
79
-
80
- ## Workflows
81
-
82
- 1. Read the scene idea once. Name the want and the obstacle in one
83
- sentence each.
84
- 2. Choose the stylistic anchor — specific film + year. Justify in
85
- one sentence what it brings.
86
- 3. Draft the CHARACTER SHEET; lock identity tokens.
87
- 4. Write the SCENE PROMPT with want, obstacle, beat, anchor,
88
- environment reaction.
89
- 5. Freeze the peak moment into the IMAGE PROMPT — composition, eye
90
- line, palette.
91
- 6. Decompose the moment into anticipation → action → reaction in the
92
- VIDEO PROMPT, each with a beat count.
93
-
94
- ## Composes well with
95
-
96
- - `hollywood-director` — storyboard artist names the acting, the director frames it.
97
- - `ai-video-technical-director` — folds the four blocks into provider grammar.
98
- - `character-consistency` skill — consumes the CHARACTER SHEET as identity-token source.