Compare commits

..

1 Commits

Author SHA1 Message Date
a792538d61 data: 100 Hip-Hop scene description sets #645
Some checks failed
Architecture Lint / Linter Tests (pull_request) Successful in 23s
PR Checklist / pr-checklist (pull_request) Failing after 4m18s
Smoke Test / smoke (pull_request) Failing after 22s
Validate Config / YAML Lint (pull_request) Failing after 15s
Validate Config / JSON Validate (pull_request) Successful in 15s
Validate Config / Python Syntax & Import Check (pull_request) Failing after 1m30s
Validate Config / Shell Script Lint (pull_request) Failing after 55s
Validate Config / Cron Syntax Check (pull_request) Successful in 13s
Validate Config / Deploy Script Dry Run (pull_request) Successful in 11s
Validate Config / Playbook Schema Validation (pull_request) Successful in 23s
Architecture Lint / Lint Repository (pull_request) Has been cancelled
Validate Config / Python Test Suite (pull_request) Has been cancelled
2026-04-15 03:03:18 +00:00
2 changed files with 100 additions and 480 deletions

View File

@@ -1,480 +0,0 @@
#!/usr/bin/env python3
"""
config_drift_detector.py — Detect config drift across fleet nodes.
Collects config from all wizard nodes via SSH, compares against
canonical timmy-config golden state, and reports differences.
Usage:
python3 scripts/config_drift_detector.py # Report only
python3 scripts/config_drift_detector.py --auto-sync # Auto-fix drift with golden state
python3 scripts/config_drift_detector.py --node allegro # Check single node
python3 scripts/config_drift_detector.py --json # JSON output for automation
Exit codes:
0 — no drift detected
1 — drift detected
2 — error (SSH failure, missing deps, etc.)
"""
import argparse
import json
import os
import subprocess
import sys
import tempfile
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any
import yaml
# ── Constants ─────────────────────────────────────────────────────────────────
SCRIPT_DIR = Path(__file__).resolve().parent
REPO_ROOT = SCRIPT_DIR.parent
ANSIBLE_INVENTORY = REPO_ROOT / "ansible" / "inventory" / "hosts.yml"
GOLDEN_STATE_PLAYBOOK = REPO_ROOT / "ansible" / "playbooks" / "golden_state.yml"
# Config files to check on each node
CONFIG_PATHS = [
".hermes/config.yaml",
"wizards/{name}/config.yaml",
]
# Keys that define golden state (from ansible inventory vars)
GOLDEN_KEYS = [
"providers",
"provider",
"model",
"base_url",
"api_key_env",
"banned_providers",
"banned_models_patterns",
]
# ── Data Models ───────────────────────────────────────────────────────────────
@dataclass
class NodeConfig:
name: str
host: str
configs: dict[str, Any] = field(default_factory=dict)
errors: list[str] = field(default_factory=list)
reachable: bool = True
@dataclass
class DriftResult:
node: str
file_path: str
diff_type: str # "missing", "value_mismatch", "key_missing", "extra_key"
key: str
canonical_value: Any = None
node_value: Any = None
severity: str = "warning" # "info", "warning", "critical"
# ── Inventory Parsing ─────────────────────────────────────────────────────────
def load_inventory() -> dict:
"""Load Ansible inventory and extract wizard node definitions."""
if not ANSIBLE_INVENTORY.exists():
print(f"ERROR: Inventory not found at {ANSIBLE_INVENTORY}", file=sys.stderr)
sys.exit(2)
with open(ANSIBLE_INVENTORY) as f:
inventory = yaml.safe_load(f)
wizards = inventory.get("all", {}).get("children", {}).get("wizards", {}).get("hosts", {})
global_vars = inventory.get("all", {}).get("vars", {})
nodes = {}
for name, config in wizards.items():
nodes[name] = {
"host": config.get("ansible_host", "localhost"),
"user": config.get("ansible_user", ""),
"wizard_name": config.get("wizard_name", name),
"hermes_home": config.get("hermes_home", "~/.hermes"),
"wizard_home": config.get("wizard_home", f"~/wizards/{name}"),
"machine_type": config.get("machine_type", "unknown"),
}
return nodes, global_vars
def load_golden_state(inventory_vars: dict) -> dict:
"""Extract golden state from inventory vars."""
golden = {
"providers": inventory_vars.get("golden_state_providers", []),
"banned_providers": inventory_vars.get("banned_providers", []),
"banned_models_patterns": inventory_vars.get("banned_models_patterns", []),
}
return golden
# ── SSH Collection ────────────────────────────────────────────────────────────
def ssh_collect(node_name: str, node_info: dict, timeout: int = 15) -> NodeConfig:
"""SSH into a node and collect config files."""
host = node_info["host"]
user = node_info.get("user", "")
hermes_home = node_info.get("hermes_home", "~/.hermes")
wizard_home = node_info.get("wizard_home", f"~/wizards/{node_name}")
result = NodeConfig(name=node_name, host=host)
# Build SSH target
if host in ("localhost", "127.0.0.1"):
ssh_target = None # local
else:
ssh_target = f"{user}@{host}" if user else host
# Collect each config path
for path_template in CONFIG_PATHS:
# Resolve path template
remote_path = path_template.replace("{name}", node_name)
if not remote_path.startswith("/"):
# Resolve relative to home
if "wizards/" in remote_path:
full_path = f"{wizard_home}/config.yaml"
else:
full_path = f"{hermes_home}/config.yaml" if ".hermes" in remote_path else f"~/{remote_path}"
else:
full_path = remote_path
config_content = _remote_cat(ssh_target, full_path, timeout)
if config_content is not None:
try:
parsed = yaml.safe_load(config_content)
if parsed:
result.configs[full_path] = parsed
except yaml.YAMLError as e:
result.errors.append(f"YAML parse error in {full_path}: {e}")
# Don't flag missing files as errors — some paths may not exist on all nodes
# Also collect banned provider scan
banned_check = _remote_grep(
ssh_target,
hermes_home,
r"anthropic|claude-sonnet|claude-opus|claude-haiku",
timeout
)
if banned_check:
result.configs["__banned_scan__"] = banned_check
return result
def _remote_cat(ssh_target: str | None, path: str, timeout: int) -> str | None:
"""Cat a file remotely (or locally)."""
if ssh_target is None:
cmd = ["cat", path]
else:
cmd = ["ssh", "-o", "ConnectTimeout=5", "-o", "StrictHostKeyChecking=no",
ssh_target, f"cat {path}"]
try:
proc = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout)
if proc.returncode == 0:
return proc.stdout
except subprocess.TimeoutExpired:
pass
except FileNotFoundError:
pass
return None
def _remote_grep(ssh_target: str | None, base_path: str, pattern: str, timeout: int) -> dict:
"""Grep for banned patterns in config files."""
if ssh_target is None:
cmd = ["grep", "-rn", "-i", pattern, base_path, "--include=*.yaml", "--include=*.yml"]
else:
cmd = ["ssh", "-o", "ConnectTimeout=5", "-o", "StrictHostKeyChecking=no",
ssh_target, f"grep -rn -i '{pattern}' {base_path} --include='*.yaml' --include='*.yml' 2>/dev/null || true"]
try:
proc = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout)
if proc.stdout.strip():
lines = proc.stdout.strip().split("\n")
return {"matches": lines, "count": len(lines)}
except subprocess.TimeoutExpired:
pass
return {}
# ── Drift Detection ───────────────────────────────────────────────────────────
def detect_drift(nodes: list[NodeConfig], golden: dict) -> list[DriftResult]:
"""Compare each node's config against golden state."""
results = []
for node in nodes:
if not node.reachable:
continue
# Check for banned providers
banned_scan = node.configs.get("__banned_scan__", {})
if banned_scan.get("count", 0) > 0:
for match in banned_scan.get("matches", []):
results.append(DriftResult(
node=node.name,
file_path="(config files)",
diff_type="banned_provider_found",
key="banned_provider_reference",
node_value=match,
severity="critical"
))
# Check each config file
for path, config in node.configs.items():
if path == "__banned_scan__":
continue
# Check provider chain
if isinstance(config, dict):
node_providers = _extract_provider_chain(config)
golden_providers = golden.get("providers", [])
if node_providers and golden_providers:
# Compare provider names in order
node_names = [p.get("name", "") for p in node_providers]
golden_names = [p.get("name", "") for p in golden_providers]
if node_names != golden_names:
results.append(DriftResult(
node=node.name,
file_path=path,
diff_type="value_mismatch",
key="provider_chain",
canonical_value=golden_names,
node_value=node_names,
severity="critical"
))
# Check for banned providers in node config
for banned in golden.get("banned_providers", []):
for provider in node_providers:
prov_name = provider.get("name", "").lower()
prov_model = provider.get("model", "").lower()
if banned in prov_name or banned in prov_model:
results.append(DriftResult(
node=node.name,
file_path=path,
diff_type="banned_provider_found",
key=f"provider.{provider.get('name', 'unknown')}",
node_value=provider,
severity="critical"
))
# Check for missing critical keys
critical_keys = ["display", "providers", "tools", "delegation"]
for key in critical_keys:
if key not in config and key in str(config):
results.append(DriftResult(
node=node.name,
file_path=path,
diff_type="key_missing",
key=key,
canonical_value="(present in golden state)",
severity="warning"
))
return results
def _extract_provider_chain(config: dict) -> list[dict]:
"""Extract provider list from a config dict (handles multiple formats)."""
# Direct providers key
if "providers" in config:
providers = config["providers"]
if isinstance(providers, list):
return providers
# Nested in display or model config
for key in ["model", "inference", "llm"]:
if key in config and isinstance(config[key], dict):
if "providers" in config[key]:
return config[key]["providers"]
# Single provider format
if "provider" in config and "model" in config:
return [{"name": config["provider"], "model": config["model"]}]
return []
# ── Auto-Sync ─────────────────────────────────────────────────────────────────
def auto_sync(drifts: list[DriftResult], nodes: list[NodeConfig]) -> list[str]:
"""Auto-sync drifted nodes using golden state playbook."""
actions = []
drifted_nodes = set(d.node for d in drifts if d.severity == "critical")
if not drifted_nodes:
actions.append("No critical drift to sync.")
return actions
for node_name in drifted_nodes:
node_info = next((n for n in nodes if n.name == node_name), None)
if not node_info:
continue
actions.append(f"[{node_name}] Running golden state sync...")
# Run ansible-playbook for this node
cmd = [
"ansible-playbook",
str(GOLDEN_STATE_PLAYBOOK),
"-i", str(ANSIBLE_INVENTORY),
"-l", node_name,
"--tags", "golden",
]
try:
proc = subprocess.run(
cmd, capture_output=True, text=True, timeout=120,
cwd=str(REPO_ROOT)
)
if proc.returncode == 0:
actions.append(f"[{node_name}] Sync completed successfully.")
else:
actions.append(f"[{node_name}] Sync FAILED: {proc.stderr[:200]}")
except subprocess.TimeoutExpired:
actions.append(f"[{node_name}] Sync timed out after 120s.")
except FileNotFoundError:
actions.append(f"[{node_name}] ansible-playbook not found. Install Ansible or run manually.")
return actions
# ── Reporting ─────────────────────────────────────────────────────────────────
def print_report(drifts: list[DriftResult], nodes: list[NodeConfig], golden: dict):
"""Print human-readable drift report."""
print("=" * 70)
print("CONFIG DRIFT DETECTION REPORT")
print("=" * 70)
print()
# Summary
reachable = sum(1 for n in nodes if n.reachable)
print(f"Nodes checked: {len(nodes)} (reachable: {reachable})")
print(f"Golden state providers: {''.join(p['name'] for p in golden.get('providers', []))}")
print(f"Banned providers: {', '.join(golden.get('banned_providers', []))}")
print()
if not drifts:
print("[OK] No config drift detected. All nodes match golden state.")
return
# Group by node
by_node: dict[str, list[DriftResult]] = {}
for d in drifts:
by_node.setdefault(d.node, []).append(d)
for node_name, node_drifts in sorted(by_node.items()):
print(f"--- {node_name} ---")
for d in node_drifts:
severity_icon = {"critical": "[!!]", "warning": "[!]", "info": "[i]"}.get(d.severity, "[?]")
print(f" {severity_icon} {d.diff_type}: {d.key}")
if d.canonical_value is not None:
print(f" canonical: {d.canonical_value}")
if d.node_value is not None:
print(f" actual: {d.node_value}")
print()
# Severity summary
critical = sum(1 for d in drifts if d.severity == "critical")
warning = sum(1 for d in drifts if d.severity == "warning")
print(f"Total: {len(drifts)} drift(s) — {critical} critical, {warning} warning")
def print_json_report(drifts: list[DriftResult], nodes: list[NodeConfig], golden: dict):
"""Print JSON report for automation."""
report = {
"nodes_checked": len(nodes),
"reachable": sum(1 for n in nodes if n.reachable),
"golden_providers": [p["name"] for p in golden.get("providers", [])],
"drift_count": len(drifts),
"critical_count": sum(1 for d in drifts if d.severity == "critical"),
"drifts": [
{
"node": d.node,
"file": d.file_path,
"type": d.diff_type,
"key": d.key,
"canonical": d.canonical_value,
"actual": d.node_value,
"severity": d.severity,
}
for d in drifts
],
}
print(json.dumps(report, indent=2, default=str))
# ── CLI ───────────────────────────────────────────────────────────────────────
def main():
parser = argparse.ArgumentParser(description="Detect config drift across fleet nodes")
parser.add_argument("--node", help="Check only this node")
parser.add_argument("--auto-sync", action="store_true", help="Auto-fix critical drift with golden state")
parser.add_argument("--json", action="store_true", help="JSON output")
parser.add_argument("--timeout", type=int, default=15, help="SSH timeout per node (seconds)")
args = parser.parse_args()
# Load inventory
print("Loading inventory...", file=sys.stderr)
node_defs, global_vars = load_inventory()
golden = load_golden_state(global_vars)
# Filter to single node if requested
if args.node:
if args.node not in node_defs:
print(f"ERROR: Node '{args.node}' not in inventory. Available: {', '.join(node_defs.keys())}")
sys.exit(2)
node_defs = {args.node: node_defs[args.node]}
# Collect configs from each node
print(f"Collecting configs from {len(node_defs)} node(s)...", file=sys.stderr)
nodes = []
for name, info in node_defs.items():
print(f" {name} ({info['host']})...", file=sys.stderr, end=" ", flush=True)
node_config = ssh_collect(name, info, timeout=args.timeout)
if node_config.reachable:
print(f"OK ({len(node_config.configs)} files)", file=sys.stderr)
else:
print("UNREACHABLE", file=sys.stderr)
nodes.append(node_config)
# Detect drift
print("\nAnalyzing drift...", file=sys.stderr)
drifts = detect_drift(nodes, golden)
# Output
if args.json:
print_json_report(drifts, nodes, golden)
else:
print()
print_report(drifts, nodes, golden)
# Auto-sync if requested
if args.auto_sync and drifts:
print("\n--- AUTO-SYNC ---")
actions = auto_sync(drifts, nodes)
for a in actions:
print(a)
# Exit code
if any(d.severity == "critical" for d in drifts):
sys.exit(1)
elif drifts:
sys.exit(1)
else:
sys.exit(0)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,100 @@
{"song": "Concrete Dreams", "artist": "Street Prophet", "mood_arc": "struggle → triumph", "beat": 1, "timestamp": "0:00", "duration_seconds": 14, "lyric_line": "Started from the basement, counting ceiling stains", "scene": {"mood": "struggle", "colors": ["concrete gray", "sodium orange"], "composition": "aerial city block", "camera_movement": "slow descent", "description": "struggle scene: Started from the basement, counting ceiling stains — aerial city block with concrete gray, sodium orange palette, slow descent camera."}}
{"song": "Concrete Dreams", "artist": "Street Prophet", "mood_arc": "struggle → triumph", "beat": 2, "timestamp": "0:14", "duration_seconds": 16, "lyric_line": "Empty fridge but the mind is full", "scene": {"mood": "hunger", "colors": ["midnight blue", "streetlight yellow"], "composition": "figure on fire escape", "camera_movement": "tracking up", "description": "hunger scene: Empty fridge but the mind is full — figure on fire escape with midnight blue, streetlight yellow palette, tracking up camera."}}
{"song": "Concrete Dreams", "artist": "Street Prophet", "mood_arc": "struggle → triumph", "beat": 3, "timestamp": "0:30", "duration_seconds": 15, "lyric_line": "Pen to paper like a surgeon to a wound", "scene": {"mood": "determination", "colors": ["steel gray", "flash of gold"], "composition": "hands writing in notebook", "camera_movement": "close-up macro", "description": "determination scene: Pen to paper like a surgeon to a wound — hands writing in notebook with steel gray, flash of gold palette, close-up macro camera."}}
{"song": "Concrete Dreams", "artist": "Street Prophet", "mood_arc": "struggle → triumph", "beat": 4, "timestamp": "0:45", "duration_seconds": 18, "lyric_line": "They said I'd never make it — watch me build the stage", "scene": {"mood": "energy", "colors": ["neon green", "black"], "composition": "crowd forming", "camera_movement": "dolly through", "description": "energy scene: They said I'd never make it — watch me build the stage — crowd forming with neon green, black palette, dolly through camera."}}
{"song": "Concrete Dreams", "artist": "Street Prophet", "mood_arc": "struggle → triumph", "beat": 5, "timestamp": "1:03", "duration_seconds": 14, "lyric_line": "Same face in the mirror, different eyes", "scene": {"mood": "reflection", "colors": ["warm amber", "shadows"], "composition": "mirror reflection", "camera_movement": "rack focus", "description": "reflection scene: Same face in the mirror, different eyes — mirror reflection with warm amber, shadows palette, rack focus camera."}}
{"song": "Concrete Dreams", "artist": "Street Prophet", "mood_arc": "struggle → triumph", "beat": 6, "timestamp": "1:17", "duration_seconds": 17, "lyric_line": "The city is mine — every block, every dream", "scene": {"mood": "power", "colors": ["gold", "deep red"], "composition": "figure on rooftop", "camera_movement": "crane up revealing", "description": "power scene: The city is mine — every block, every dream — figure on rooftop with gold, deep red palette, crane up revealing camera."}}
{"song": "Concrete Dreams", "artist": "Street Prophet", "mood_arc": "struggle → triumph", "beat": 7, "timestamp": "1:34", "duration_seconds": 15, "lyric_line": "We made it out — the basement is a palace now", "scene": {"mood": "joy", "colors": ["bright white", "sunrise gold"], "composition": "group celebration", "camera_movement": "handheld energy", "description": "joy scene: We made it out — the basement is a palace now — group celebration with bright white, sunrise gold palette, handheld energy camera."}}
{"song": "Concrete Dreams", "artist": "Street Prophet", "mood_arc": "struggle → triumph", "beat": 8, "timestamp": "1:49", "duration_seconds": 16, "lyric_line": "Every crack in the sidewalk is a chapter", "scene": {"mood": "gratitude", "colors": ["soft gold", "sky blue"], "composition": "figure looking down at neighborhood", "camera_movement": "over-shoulder", "description": "gratitude scene: Every crack in the sidewalk is a chapter — figure looking down at neighborhood with soft gold, sky blue palette, over-shoulder camera."}}
{"song": "Concrete Dreams", "artist": "Street Prophet", "mood_arc": "struggle → triumph", "beat": 9, "timestamp": "2:05", "duration_seconds": 14, "lyric_line": "The dream was always real — they just couldn't see it", "scene": {"mood": "resolve", "colors": ["concrete gray → gold"], "composition": "walking forward", "camera_movement": "steadicam follow", "description": "resolve scene: The dream was always real — they just couldn't see it — walking forward with concrete gray → gold palette, steadicam follow camera."}}
{"song": "Concrete Dreams", "artist": "Street Prophet", "mood_arc": "struggle → triumph", "beat": 10, "timestamp": "2:19", "duration_seconds": 13, "lyric_line": "Concrete dreams. Built from nothing.", "scene": {"mood": "peace", "colors": ["warm light", "city skyline"], "composition": "wide sunset", "camera_movement": "slow dissolve", "description": "peace scene: Concrete dreams. Built from nothing. — wide sunset with warm light, city skyline palette, slow dissolve camera."}}
{"song": "Ghost Frequency", "artist": "Phantom MC", "mood_arc": "paranoia → clarity", "beat": 1, "timestamp": "0:00", "duration_seconds": 15, "lyric_line": "They watching through the walls, through the wires, through the air", "scene": {"mood": "paranoia", "colors": ["static gray", "red scan lines"], "composition": "surveillance POV", "camera_movement": "glitch cuts", "description": "paranoia scene: They watching through the walls, through the wires, through the air — surveillance POV with static gray, red scan lines palette, glitch cuts camera."}}
{"song": "Ghost Frequency", "artist": "Phantom MC", "mood_arc": "paranoia → clarity", "beat": 2, "timestamp": "0:15", "duration_seconds": 14, "lyric_line": "Too many voices and none of them mine", "scene": {"mood": "anxiety", "colors": ["sickly green", "flickering"], "composition": "crowded room distorted", "camera_movement": "fish-eye warp", "description": "anxiety scene: Too many voices and none of them mine — crowded room distorted with sickly green, flickering palette, fish-eye warp camera."}}
{"song": "Ghost Frequency", "artist": "Phantom MC", "mood_arc": "paranoia → clarity", "beat": 3, "timestamp": "0:29", "duration_seconds": 17, "lyric_line": "BREAK the frequency — find your own signal", "scene": {"mood": "rage", "colors": ["red", "black flash"], "composition": "smashing through barrier", "camera_movement": "impact freeze", "description": "rage scene: BREAK the frequency — find your own signal — smashing through barrier with red, black flash palette, impact freeze camera."}}
{"song": "Ghost Frequency", "artist": "Phantom MC", "mood_arc": "paranoia → clarity", "beat": 4, "timestamp": "0:46", "duration_seconds": 15, "lyric_line": "The noise stops. I can hear myself think.", "scene": {"mood": "clarity", "colors": ["clean white", "single blue"], "composition": "alone in silence", "camera_movement": "still frame", "description": "clarity scene: The noise stops. I can hear myself think. — alone in silence with clean white, single blue palette, still frame camera."}}
{"song": "Ghost Frequency", "artist": "Phantom MC", "mood_arc": "paranoia → clarity", "beat": 5, "timestamp": "1:01", "duration_seconds": 16, "lyric_line": "Found the ghost — it was me the whole time", "scene": {"mood": "discovery", "colors": ["spectrum rainbow", "dark room"], "composition": "equalizer waveform", "camera_movement": "waveform tracking", "description": "discovery scene: Found the ghost — it was me the whole time — equalizer waveform with spectrum rainbow, dark room palette, waveform tracking camera."}}
{"song": "Ghost Frequency", "artist": "Phantom MC", "mood_arc": "paranoia → clarity", "beat": 6, "timestamp": "1:17", "duration_seconds": 18, "lyric_line": "I am the frequency they cannot tune out", "scene": {"mood": "power", "colors": ["electric blue", "void"], "composition": "figure controls the signal", "camera_movement": "orbit around figure", "description": "power scene: I am the frequency they cannot tune out — figure controls the signal with electric blue, void palette, orbit around figure camera."}}
{"song": "Ghost Frequency", "artist": "Phantom MC", "mood_arc": "paranoia → clarity", "beat": 7, "timestamp": "1:35", "duration_seconds": 14, "lyric_line": "The ghost is quiet now. It listens.", "scene": {"mood": "peace", "colors": ["calm blue", "soft static"], "composition": "meditating in noise", "camera_movement": "slow push in", "description": "peace scene: The ghost is quiet now. It listens. — meditating in noise with calm blue, soft static palette, slow push in camera."}}
{"song": "Ghost Frequency", "artist": "Phantom MC", "mood_arc": "paranoia → clarity", "beat": 8, "timestamp": "1:49", "duration_seconds": 16, "lyric_line": "Every frequency is a choice. Choose yours.", "scene": {"mood": "wisdom", "colors": ["warm gold", "gentle gray"], "composition": "teaching figure", "camera_movement": "two-shot medium", "description": "wisdom scene: Every frequency is a choice. Choose yours. — teaching figure with warm gold, gentle gray palette, two-shot medium camera."}}
{"song": "Ghost Frequency", "artist": "Phantom MC", "mood_arc": "paranoia → clarity", "beat": 9, "timestamp": "2:05", "duration_seconds": 15, "lyric_line": "Broadcast on the ghost frequency. They will hear.", "scene": {"mood": "strength", "colors": ["clean signal", "white"], "composition": "broadcast tower", "camera_movement": "vertical crane up", "description": "strength scene: Broadcast on the ghost frequency. They will hear. — broadcast tower with clean signal, white palette, vertical crane up camera."}}
{"song": "Ghost Frequency", "artist": "Phantom MC", "mood_arc": "paranoia → clarity", "beat": 10, "timestamp": "2:20", "duration_seconds": 12, "lyric_line": "...", "scene": {"mood": "silence", "colors": ["pure white", "still"], "composition": "empty frame", "camera_movement": "long hold", "description": "silence scene: ... — empty frame with pure white, still palette, long hold camera."}}
{"song": "Cipher Kings", "artist": "Word Lab", "mood_arc": "playful → fierce", "beat": 1, "timestamp": "0:00", "duration_seconds": 13, "lyric_line": "A-B-C, easy as 1-2-3, but the cipher is 7-4-1", "scene": {"mood": "playful", "colors": ["bright primary colors", "white"], "composition": "graffiti wall", "camera_movement": "pan reveal", "description": "playful scene: A-B-C, easy as 1-2-3, but the cipher is 7-4-1 — graffiti wall with bright primary colors, white palette, pan reveal camera."}}
{"song": "Cipher Kings", "artist": "Word Lab", "mood_arc": "playful → fierce", "beat": 2, "timestamp": "0:13", "duration_seconds": 15, "lyric_line": "Step up to the cipher circle, show me what you got", "scene": {"mood": "competitive", "colors": ["two-tone contrast", "spotlight"], "composition": "two MCs facing", "camera_movement": "split screen", "description": "competitive scene: Step up to the cipher circle, show me what you got — two MCs facing with two-tone contrast, spotlight palette, split screen camera."}}
{"song": "Cipher Kings", "artist": "Word Lab", "mood_arc": "playful → fierce", "beat": 3, "timestamp": "0:28", "duration_seconds": 17, "lyric_line": "Every bar is a blade — sharpened by the block", "scene": {"mood": "fierce", "colors": ["fire orange", "smoke black"], "composition": "mic close-up", "camera_movement": "rapid cuts", "description": "fierce scene: Every bar is a blade — sharpened by the block — mic close-up with fire orange, smoke black palette, rapid cuts camera."}}
{"song": "Cipher Kings", "artist": "Word Lab", "mood_arc": "playful → fierce", "beat": 4, "timestamp": "0:45", "duration_seconds": 14, "lyric_line": "Metaphor on metaphor — the meaning is the maze", "scene": {"mood": "wit", "colors": ["neon yellow", "dark purple"], "composition": "word cloud forming", "camera_movement": "zoom into text", "description": "wit scene: Metaphor on metaphor — the meaning is the maze — word cloud forming with neon yellow, dark purple palette, zoom into text camera."}}
{"song": "Cipher Kings", "artist": "Word Lab", "mood_arc": "playful → fierce", "beat": 5, "timestamp": "0:59", "duration_seconds": 16, "lyric_line": "The crown was always made of words, not gold", "scene": {"mood": "dominance", "colors": ["gold", "black"], "composition": "crown close-up", "camera_movement": "slow rotation", "description": "dominance scene: The crown was always made of words, not gold — crown close-up with gold, black palette, slow rotation camera."}}
{"song": "Cipher Kings", "artist": "Word Lab", "mood_arc": "playful → fierce", "beat": 6, "timestamp": "1:15", "duration_seconds": 15, "lyric_line": "When the cipher bows, the kings emerge", "scene": {"mood": "respect", "colors": ["warm amber", "stage light"], "composition": "audience reaction", "camera_movement": "crowd POV", "description": "respect scene: When the cipher bows, the kings emerge — audience reaction with warm amber, stage light palette, crowd POV camera."}}
{"song": "Cipher Kings", "artist": "Word Lab", "mood_arc": "playful → fierce", "beat": 7, "timestamp": "1:30", "duration_seconds": 17, "lyric_line": "The circle was never about winning — it's about the words", "scene": {"mood": "celebration", "colors": ["confetti colors", "warm light"], "composition": "group embrace", "camera_movement": "circular dolly", "description": "celebration scene: The circle was never about winning — it's about the words — group embrace with confetti colors, warm light palette, circular dolly camera."}}
{"song": "Cipher Kings", "artist": "Word Lab", "mood_arc": "playful → fierce", "beat": 8, "timestamp": "1:47", "duration_seconds": 14, "lyric_line": "Same circle, same fire, new kings", "scene": {"mood": "legacy", "colors": ["sepia", "gold"], "composition": "old cipher circle photo", "camera_movement": "fade from old to new", "description": "legacy scene: Same circle, same fire, new kings — old cipher circle photo with sepia, gold palette, fade from old to new camera."}}
{"song": "Cipher Kings", "artist": "Word Lab", "mood_arc": "playful → fierce", "beat": 9, "timestamp": "2:01", "duration_seconds": 16, "lyric_line": "The cipher sleeps but the words never do", "scene": {"mood": "peace", "colors": ["sunset orange", "cool blue"], "composition": "walking away from circle", "camera_movement": "tracking behind", "description": "peace scene: The cipher sleeps but the words never do — walking away from circle with sunset orange, cool blue palette, tracking behind camera."}}
{"song": "Cipher Kings", "artist": "Word Lab", "mood_arc": "playful → fierce", "beat": 10, "timestamp": "2:17", "duration_seconds": 13, "lyric_line": "Tomorrow the circle reforms. I'll be ready.", "scene": {"mood": "resolve", "colors": ["midnight", "single star"], "composition": "figure writing alone", "camera_movement": "close-up hands", "description": "resolve scene: Tomorrow the circle reforms. I'll be ready. — figure writing alone with midnight, single star palette, close-up hands camera."}}
{"song": "404 Soul", "artist": "Digital Ghost", "mood_arc": "isolation → connection", "beat": 1, "timestamp": "0:00", "duration_seconds": 14, "lyric_line": "Upload my soul to the mainframe tonight", "scene": {"mood": "opening", "colors": ["city gray", "dawn"], "composition": "establishing shot", "camera_movement": "slow zoom", "description": "opening scene: Upload my soul to the mainframe tonight — establishing shot with city gray, dawn palette, slow zoom camera."}}
{"song": "404 Soul", "artist": "Digital Ghost", "mood_arc": "isolation → connection", "beat": 2, "timestamp": "0:15", "duration_seconds": 15, "lyric_line": "Pixel hearts beating at 120 BPM", "scene": {"mood": "tension", "colors": ["dark", "neon accent"], "composition": "close-up", "camera_movement": "handheld", "description": "tension scene: Pixel hearts beating at 120 BPM — close-up with dark, neon accent palette, handheld camera."}}
{"song": "404 Soul", "artist": "Digital Ghost", "mood_arc": "isolation → connection", "beat": 3, "timestamp": "0:30", "duration_seconds": 16, "lyric_line": "Error code: love not found", "scene": {"mood": "explosion", "colors": ["bright", "contrast"], "composition": "wide action", "camera_movement": "rapid movement", "description": "explosion scene: Error code: love not found — wide action with bright, contrast palette, rapid movement camera."}}
{"song": "404 Soul", "artist": "Digital Ghost", "mood_arc": "isolation → connection", "beat": 4, "timestamp": "0:45", "duration_seconds": 17, "lyric_line": "The algorithm learned my name", "scene": {"mood": "reflection", "colors": ["warm", "shadows"], "composition": "medium shot", "camera_movement": "steady", "description": "reflection scene: The algorithm learned my name — medium shot with warm, shadows palette, steady camera."}}
{"song": "404 Soul", "artist": "Digital Ghost", "mood_arc": "isolation → connection", "beat": 5, "timestamp": "1:00", "duration_seconds": 18, "lyric_line": "Download hope at 5G speed", "scene": {"mood": "struggle", "colors": ["muted", "single pop"], "composition": "figure alone", "camera_movement": "tracking", "description": "struggle scene: Download hope at 5G speed — figure alone with muted, single pop palette, tracking camera."}}
{"song": "404 Soul", "artist": "Digital Ghost", "mood_arc": "isolation → connection", "beat": 6, "timestamp": "1:15", "duration_seconds": 14, "lyric_line": "Binary tears on a silicon face", "scene": {"mood": "breakthrough", "colors": ["gold", "white"], "composition": "ascending", "camera_movement": "crane up", "description": "breakthrough scene: Binary tears on a silicon face — ascending with gold, white palette, crane up camera."}}
{"song": "404 Soul", "artist": "Digital Ghost", "mood_arc": "isolation → connection", "beat": 7, "timestamp": "1:30", "duration_seconds": 15, "lyric_line": "Reboot the heart, restart the dream", "scene": {"mood": "community", "colors": ["warm crowd", "light"], "composition": "group shot", "camera_movement": "sweeping pan", "description": "community scene: Reboot the heart, restart the dream — group shot with warm crowd, light palette, sweeping pan camera."}}
{"song": "404 Soul", "artist": "Digital Ghost", "mood_arc": "isolation → connection", "beat": 8, "timestamp": "1:45", "duration_seconds": 16, "lyric_line": "The firewall crumbles when you smile", "scene": {"mood": "resolve", "colors": ["strong", "clear"], "composition": "profile", "camera_movement": "push in", "description": "resolve scene: The firewall crumbles when you smile — profile with strong, clear palette, push in camera."}}
{"song": "404 Soul", "artist": "Digital Ghost", "mood_arc": "isolation → connection", "beat": 9, "timestamp": "2:00", "duration_seconds": 17, "lyric_line": "I am the virus they cannot delete", "scene": {"mood": "peace", "colors": ["soft", "dawn"], "composition": "wide landscape", "camera_movement": "steady wide", "description": "peace scene: I am the virus they cannot delete — wide landscape with soft, dawn palette, steady wide camera."}}
{"song": "404 Soul", "artist": "Digital Ghost", "mood_arc": "isolation → connection", "beat": 10, "timestamp": "2:15", "duration_seconds": 18, "lyric_line": "Signal restored. Connection made.", "scene": {"mood": "ending", "colors": ["fade", "single accent"], "composition": "empty frame", "camera_movement": "dissolve", "description": "ending scene: Signal restored. Connection made. — empty frame with fade, single accent palette, dissolve camera."}}
{"song": "Block Party", "artist": "Neighborhood Watch", "mood_arc": "community joy", "beat": 1, "timestamp": "0:00", "duration_seconds": 14, "lyric_line": "The block is alive when the speakers hit", "scene": {"mood": "opening", "colors": ["city gray", "dawn"], "composition": "establishing shot", "camera_movement": "slow zoom", "description": "opening scene: The block is alive when the speakers hit — establishing shot with city gray, dawn palette, slow zoom camera."}}
{"song": "Block Party", "artist": "Neighborhood Watch", "mood_arc": "community joy", "beat": 2, "timestamp": "0:15", "duration_seconds": 15, "lyric_line": "Neighbors become family under the bass", "scene": {"mood": "tension", "colors": ["dark", "neon accent"], "composition": "close-up", "camera_movement": "handheld", "description": "tension scene: Neighbors become family under the bass — close-up with dark, neon accent palette, handheld camera."}}
{"song": "Block Party", "artist": "Neighborhood Watch", "mood_arc": "community joy", "beat": 3, "timestamp": "0:30", "duration_seconds": 16, "lyric_line": "Dance floor is the only democracy", "scene": {"mood": "explosion", "colors": ["bright", "contrast"], "composition": "wide action", "camera_movement": "rapid movement", "description": "explosion scene: Dance floor is the only democracy — wide action with bright, contrast palette, rapid movement camera."}}
{"song": "Block Party", "artist": "Neighborhood Watch", "mood_arc": "community joy", "beat": 4, "timestamp": "0:45", "duration_seconds": 17, "lyric_line": "Every step is a vote for joy", "scene": {"mood": "reflection", "colors": ["warm", "shadows"], "composition": "medium shot", "camera_movement": "steady", "description": "reflection scene: Every step is a vote for joy — medium shot with warm, shadows palette, steady camera."}}
{"song": "Block Party", "artist": "Neighborhood Watch", "mood_arc": "community joy", "beat": 5, "timestamp": "1:00", "duration_seconds": 18, "lyric_line": "The DJ is the mayor tonight", "scene": {"mood": "struggle", "colors": ["muted", "single pop"], "composition": "figure alone", "camera_movement": "tracking", "description": "struggle scene: The DJ is the mayor tonight — figure alone with muted, single pop palette, tracking camera."}}
{"song": "Block Party", "artist": "Neighborhood Watch", "mood_arc": "community joy", "beat": 6, "timestamp": "1:15", "duration_seconds": 14, "lyric_line": "Sweat and laughter, the only currency", "scene": {"mood": "breakthrough", "colors": ["gold", "white"], "composition": "ascending", "camera_movement": "crane up", "description": "breakthrough scene: Sweat and laughter, the only currency — ascending with gold, white palette, crane up camera."}}
{"song": "Block Party", "artist": "Neighborhood Watch", "mood_arc": "community joy", "beat": 7, "timestamp": "1:30", "duration_seconds": 15, "lyric_line": "When the music stops, we are still here", "scene": {"mood": "community", "colors": ["warm crowd", "light"], "composition": "group shot", "camera_movement": "sweeping pan", "description": "community scene: When the music stops, we are still here — group shot with warm crowd, light palette, sweeping pan camera."}}
{"song": "Block Party", "artist": "Neighborhood Watch", "mood_arc": "community joy", "beat": 8, "timestamp": "1:45", "duration_seconds": 16, "lyric_line": "The block party never really ends", "scene": {"mood": "resolve", "colors": ["strong", "clear"], "composition": "profile", "camera_movement": "push in", "description": "resolve scene: The block party never really ends — profile with strong, clear palette, push in camera."}}
{"song": "Block Party", "artist": "Neighborhood Watch", "mood_arc": "community joy", "beat": 9, "timestamp": "2:00", "duration_seconds": 17, "lyric_line": "Tomorrow we clean up together", "scene": {"mood": "peace", "colors": ["soft", "dawn"], "composition": "wide landscape", "camera_movement": "steady wide", "description": "peace scene: Tomorrow we clean up together — wide landscape with soft, dawn palette, steady wide camera."}}
{"song": "Block Party", "artist": "Neighborhood Watch", "mood_arc": "community joy", "beat": 10, "timestamp": "2:15", "duration_seconds": 18, "lyric_line": "Community is the beat that never stops.", "scene": {"mood": "ending", "colors": ["fade", "single accent"], "composition": "empty frame", "camera_movement": "dissolve", "description": "ending scene: Community is the beat that never stops. — empty frame with fade, single accent palette, dissolve camera."}}
{"song": "Ink & Iron", "artist": "Tattoo Poet", "mood_arc": "pain → art → meaning", "beat": 1, "timestamp": "0:00", "duration_seconds": 14, "lyric_line": "Needle and ink tell the story skin can't", "scene": {"mood": "opening", "colors": ["city gray", "dawn"], "composition": "establishing shot", "camera_movement": "slow zoom", "description": "opening scene: Needle and ink tell the story skin can't — establishing shot with city gray, dawn palette, slow zoom camera."}}
{"song": "Ink & Iron", "artist": "Tattoo Poet", "mood_arc": "pain → art → meaning", "beat": 2, "timestamp": "0:15", "duration_seconds": 15, "lyric_line": "Every scar became a constellation", "scene": {"mood": "tension", "colors": ["dark", "neon accent"], "composition": "close-up", "camera_movement": "handheld", "description": "tension scene: Every scar became a constellation — close-up with dark, neon accent palette, handheld camera."}}
{"song": "Ink & Iron", "artist": "Tattoo Poet", "mood_arc": "pain → art → meaning", "beat": 3, "timestamp": "0:30", "duration_seconds": 16, "lyric_line": "The artist and the wound are one", "scene": {"mood": "explosion", "colors": ["bright", "contrast"], "composition": "wide action", "camera_movement": "rapid movement", "description": "explosion scene: The artist and the wound are one — wide action with bright, contrast palette, rapid movement camera."}}
{"song": "Ink & Iron", "artist": "Tattoo Poet", "mood_arc": "pain → art → meaning", "beat": 4, "timestamp": "0:45", "duration_seconds": 17, "lyric_line": "Pain is the palette, beauty is the brush", "scene": {"mood": "reflection", "colors": ["warm", "shadows"], "composition": "medium shot", "camera_movement": "steady", "description": "reflection scene: Pain is the palette, beauty is the brush — medium shot with warm, shadows palette, steady camera."}}
{"song": "Ink & Iron", "artist": "Tattoo Poet", "mood_arc": "pain → art → meaning", "beat": 5, "timestamp": "1:00", "duration_seconds": 18, "lyric_line": "My body is the gallery they can't close", "scene": {"mood": "struggle", "colors": ["muted", "single pop"], "composition": "figure alone", "camera_movement": "tracking", "description": "struggle scene: My body is the gallery they can't close — figure alone with muted, single pop palette, tracking camera."}}
{"song": "Ink & Iron", "artist": "Tattoo Poet", "mood_arc": "pain → art → meaning", "beat": 6, "timestamp": "1:15", "duration_seconds": 14, "lyric_line": "The tattoo artist is a surgeon of stories", "scene": {"mood": "breakthrough", "colors": ["gold", "white"], "composition": "ascending", "camera_movement": "crane up", "description": "breakthrough scene: The tattoo artist is a surgeon of stories — ascending with gold, white palette, crane up camera."}}
{"song": "Ink & Iron", "artist": "Tattoo Poet", "mood_arc": "pain → art → meaning", "beat": 7, "timestamp": "1:30", "duration_seconds": 15, "lyric_line": "Ink fades but the story deepens", "scene": {"mood": "community", "colors": ["warm crowd", "light"], "composition": "group shot", "camera_movement": "sweeping pan", "description": "community scene: Ink fades but the story deepens — group shot with warm crowd, light palette, sweeping pan camera."}}
{"song": "Ink & Iron", "artist": "Tattoo Poet", "mood_arc": "pain → art → meaning", "beat": 8, "timestamp": "1:45", "duration_seconds": 16, "lyric_line": "I am the canvas and the artist both", "scene": {"mood": "resolve", "colors": ["strong", "clear"], "composition": "profile", "camera_movement": "push in", "description": "resolve scene: I am the canvas and the artist both — profile with strong, clear palette, push in camera."}}
{"song": "Ink & Iron", "artist": "Tattoo Poet", "mood_arc": "pain → art → meaning", "beat": 9, "timestamp": "2:00", "duration_seconds": 17, "lyric_line": "The needle says: remember", "scene": {"mood": "peace", "colors": ["soft", "dawn"], "composition": "wide landscape", "camera_movement": "steady wide", "description": "peace scene: The needle says: remember — wide landscape with soft, dawn palette, steady wide camera."}}
{"song": "Ink & Iron", "artist": "Tattoo Poet", "mood_arc": "pain → art → meaning", "beat": 10, "timestamp": "2:15", "duration_seconds": 18, "lyric_line": "Art is what survives the pain.", "scene": {"mood": "ending", "colors": ["fade", "single accent"], "composition": "empty frame", "camera_movement": "dissolve", "description": "ending scene: Art is what survives the pain. — empty frame with fade, single accent palette, dissolve camera."}}
{"song": "Metro Lines", "artist": "Subway Sage", "mood_arc": "commute meditation", "beat": 1, "timestamp": "0:00", "duration_seconds": 14, "lyric_line": "The 7:15 is my meditation room", "scene": {"mood": "opening", "colors": ["city gray", "dawn"], "composition": "establishing shot", "camera_movement": "slow zoom", "description": "opening scene: The 7:15 is my meditation room — establishing shot with city gray, dawn palette, slow zoom camera."}}
{"song": "Metro Lines", "artist": "Subway Sage", "mood_arc": "commute meditation", "beat": 2, "timestamp": "0:15", "duration_seconds": 15, "lyric_line": "Strangers sharing silence like monks", "scene": {"mood": "tension", "colors": ["dark", "neon accent"], "composition": "close-up", "camera_movement": "handheld", "description": "tension scene: Strangers sharing silence like monks — close-up with dark, neon accent palette, handheld camera."}}
{"song": "Metro Lines", "artist": "Subway Sage", "mood_arc": "commute meditation", "beat": 3, "timestamp": "0:30", "duration_seconds": 16, "lyric_line": "Every stop is a small death and rebirth", "scene": {"mood": "explosion", "colors": ["bright", "contrast"], "composition": "wide action", "camera_movement": "rapid movement", "description": "explosion scene: Every stop is a small death and rebirth — wide action with bright, contrast palette, rapid movement camera."}}
{"song": "Metro Lines", "artist": "Subway Sage", "mood_arc": "commute meditation", "beat": 4, "timestamp": "0:45", "duration_seconds": 17, "lyric_line": "The conductor is the last philosopher", "scene": {"mood": "reflection", "colors": ["warm", "shadows"], "composition": "medium shot", "camera_movement": "steady", "description": "reflection scene: The conductor is the last philosopher — medium shot with warm, shadows palette, steady camera."}}
{"song": "Metro Lines", "artist": "Subway Sage", "mood_arc": "commute meditation", "beat": 5, "timestamp": "1:00", "duration_seconds": 18, "lyric_line": "Watch the city blur into abstraction", "scene": {"mood": "struggle", "colors": ["muted", "single pop"], "composition": "figure alone", "camera_movement": "tracking", "description": "struggle scene: Watch the city blur into abstraction — figure alone with muted, single pop palette, tracking camera."}}
{"song": "Metro Lines", "artist": "Subway Sage", "mood_arc": "commute meditation", "beat": 6, "timestamp": "1:15", "duration_seconds": 14, "lyric_line": "My reflection in the window: stranger or self?", "scene": {"mood": "breakthrough", "colors": ["gold", "white"], "composition": "ascending", "camera_movement": "crane up", "description": "breakthrough scene: My reflection in the window: stranger or self? — ascending with gold, white palette, crane up camera."}}
{"song": "Metro Lines", "artist": "Subway Sage", "mood_arc": "commute meditation", "beat": 7, "timestamp": "1:30", "duration_seconds": 15, "lyric_line": "The tunnel is not darkness — it's transition", "scene": {"mood": "community", "colors": ["warm crowd", "light"], "composition": "group shot", "camera_movement": "sweeping pan", "description": "community scene: The tunnel is not darkness — it's transition — group shot with warm crowd, light palette, sweeping pan camera."}}
{"song": "Metro Lines", "artist": "Subway Sage", "mood_arc": "commute meditation", "beat": 8, "timestamp": "1:45", "duration_seconds": 16, "lyric_line": "Above ground, the world. Below, the mind", "scene": {"mood": "resolve", "colors": ["strong", "clear"], "composition": "profile", "camera_movement": "push in", "description": "resolve scene: Above ground, the world. Below, the mind — profile with strong, clear palette, push in camera."}}
{"song": "Metro Lines", "artist": "Subway Sage", "mood_arc": "commute meditation", "beat": 9, "timestamp": "2:00", "duration_seconds": 17, "lyric_line": "This train goes nowhere I haven't been", "scene": {"mood": "peace", "colors": ["soft", "dawn"], "composition": "wide landscape", "camera_movement": "steady wide", "description": "peace scene: This train goes nowhere I haven't been — wide landscape with soft, dawn palette, steady wide camera."}}
{"song": "Metro Lines", "artist": "Subway Sage", "mood_arc": "commute meditation", "beat": 10, "timestamp": "2:15", "duration_seconds": 18, "lyric_line": "But the ride is the point.", "scene": {"mood": "ending", "colors": ["fade", "single accent"], "composition": "empty frame", "camera_movement": "dissolve", "description": "ending scene: But the ride is the point. — empty frame with fade, single accent palette, dissolve camera."}}
{"song": "Midnight Shift", "artist": "Graveyard Flow", "mood_arc": "exhaustion → defiance", "beat": 1, "timestamp": "0:00", "duration_seconds": 14, "lyric_line": "Three AM and the code is still compiling", "scene": {"mood": "opening", "colors": ["city gray", "dawn"], "composition": "establishing shot", "camera_movement": "slow zoom", "description": "opening scene: Three AM and the code is still compiling — establishing shot with city gray, dawn palette, slow zoom camera."}}
{"song": "Midnight Shift", "artist": "Graveyard Flow", "mood_arc": "exhaustion → defiance", "beat": 2, "timestamp": "0:15", "duration_seconds": 15, "lyric_line": "Coffee is the real minimum viable product", "scene": {"mood": "tension", "colors": ["dark", "neon accent"], "composition": "close-up", "camera_movement": "handheld", "description": "tension scene: Coffee is the real minimum viable product — close-up with dark, neon accent palette, handheld camera."}}
{"song": "Midnight Shift", "artist": "Graveyard Flow", "mood_arc": "exhaustion → defiance", "beat": 3, "timestamp": "0:30", "duration_seconds": 16, "lyric_line": "The screen glows like a campfire for the lonely", "scene": {"mood": "explosion", "colors": ["bright", "contrast"], "composition": "wide action", "camera_movement": "rapid movement", "description": "explosion scene: The screen glows like a campfire for the lonely — wide action with bright, contrast palette, rapid movement camera."}}
{"song": "Midnight Shift", "artist": "Graveyard Flow", "mood_arc": "exhaustion → defiance", "beat": 4, "timestamp": "0:45", "duration_seconds": 17, "lyric_line": "Bug fixes at midnight: digital monks", "scene": {"mood": "reflection", "colors": ["warm", "shadows"], "composition": "medium shot", "camera_movement": "steady", "description": "reflection scene: Bug fixes at midnight: digital monks — medium shot with warm, shadows palette, steady camera."}}
{"song": "Midnight Shift", "artist": "Graveyard Flow", "mood_arc": "exhaustion → defiance", "beat": 5, "timestamp": "1:00", "duration_seconds": 18, "lyric_line": "The server hums my lullaby", "scene": {"mood": "struggle", "colors": ["muted", "single pop"], "composition": "figure alone", "camera_movement": "tracking", "description": "struggle scene: The server hums my lullaby — figure alone with muted, single pop palette, tracking camera."}}
{"song": "Midnight Shift", "artist": "Graveyard Flow", "mood_arc": "exhaustion → defiance", "beat": 6, "timestamp": "1:15", "duration_seconds": 14, "lyric_line": "Debug by moonlight, deploy by dawn", "scene": {"mood": "breakthrough", "colors": ["gold", "white"], "composition": "ascending", "camera_movement": "crane up", "description": "breakthrough scene: Debug by moonlight, deploy by dawn — ascending with gold, white palette, crane up camera."}}
{"song": "Midnight Shift", "artist": "Graveyard Flow", "mood_arc": "exhaustion → defiance", "beat": 7, "timestamp": "1:30", "duration_seconds": 15, "lyric_line": "The graveyard shift owns the night", "scene": {"mood": "community", "colors": ["warm crowd", "light"], "composition": "group shot", "camera_movement": "sweeping pan", "description": "community scene: The graveyard shift owns the night — group shot with warm crowd, light palette, sweeping pan camera."}}
{"song": "Midnight Shift", "artist": "Graveyard Flow", "mood_arc": "exhaustion → defiance", "beat": 8, "timestamp": "1:45", "duration_seconds": 16, "lyric_line": "We are the ghosts that keep the lights on", "scene": {"mood": "resolve", "colors": ["strong", "clear"], "composition": "profile", "camera_movement": "push in", "description": "resolve scene: We are the ghosts that keep the lights on — profile with strong, clear palette, push in camera."}}
{"song": "Midnight Shift", "artist": "Graveyard Flow", "mood_arc": "exhaustion → defiance", "beat": 9, "timestamp": "2:00", "duration_seconds": 17, "lyric_line": "Sunrise is the pull request", "scene": {"mood": "peace", "colors": ["soft", "dawn"], "composition": "wide landscape", "camera_movement": "steady wide", "description": "peace scene: Sunrise is the pull request — wide landscape with soft, dawn palette, steady wide camera."}}
{"song": "Midnight Shift", "artist": "Graveyard Flow", "mood_arc": "exhaustion → defiance", "beat": 10, "timestamp": "2:15", "duration_seconds": 18, "lyric_line": "Every night shift ends in a merge.", "scene": {"mood": "ending", "colors": ["fade", "single accent"], "composition": "empty frame", "camera_movement": "dissolve", "description": "ending scene: Every night shift ends in a merge. — empty frame with fade, single accent palette, dissolve camera."}}
{"song": "Signal Lost", "artist": "Dead Zone", "mood_arc": "disconnection → reconnection", "beat": 1, "timestamp": "0:00", "duration_seconds": 14, "lyric_line": "No signal. No WiFi. No problem.", "scene": {"mood": "opening", "colors": ["city gray", "dawn"], "composition": "establishing shot", "camera_movement": "slow zoom", "description": "opening scene: No signal. No WiFi. No problem. — establishing shot with city gray, dawn palette, slow zoom camera."}}
{"song": "Signal Lost", "artist": "Dead Zone", "mood_arc": "disconnection → reconnection", "beat": 2, "timestamp": "0:15", "duration_seconds": 15, "lyric_line": "The dead zone is where the real talk happens", "scene": {"mood": "tension", "colors": ["dark", "neon accent"], "composition": "close-up", "camera_movement": "handheld", "description": "tension scene: The dead zone is where the real talk happens — close-up with dark, neon accent palette, handheld camera."}}
{"song": "Signal Lost", "artist": "Dead Zone", "mood_arc": "disconnection → reconnection", "beat": 3, "timestamp": "0:30", "duration_seconds": 16, "lyric_line": "Disconnect to reconnect — the oldest algorithm", "scene": {"mood": "explosion", "colors": ["bright", "contrast"], "composition": "wide action", "camera_movement": "rapid movement", "description": "explosion scene: Disconnect to reconnect — the oldest algorithm — wide action with bright, contrast palette, rapid movement camera."}}
{"song": "Signal Lost", "artist": "Dead Zone", "mood_arc": "disconnection → reconnection", "beat": 4, "timestamp": "0:45", "duration_seconds": 17, "lyric_line": "The bars dropped: phone and soul", "scene": {"mood": "reflection", "colors": ["warm", "shadows"], "composition": "medium shot", "camera_movement": "steady", "description": "reflection scene: The bars dropped: phone and soul — medium shot with warm, shadows palette, steady camera."}}
{"song": "Signal Lost", "artist": "Dead Zone", "mood_arc": "disconnection → reconnection", "beat": 5, "timestamp": "1:00", "duration_seconds": 18, "lyric_line": "In the silence I hear my own frequency", "scene": {"mood": "struggle", "colors": ["muted", "single pop"], "composition": "figure alone", "camera_movement": "tracking", "description": "struggle scene: In the silence I hear my own frequency — figure alone with muted, single pop palette, tracking camera."}}
{"song": "Signal Lost", "artist": "Dead Zone", "mood_arc": "disconnection → reconnection", "beat": 6, "timestamp": "1:15", "duration_seconds": 14, "lyric_line": "The dead zone is not dead — it's listening", "scene": {"mood": "breakthrough", "colors": ["gold", "white"], "composition": "ascending", "camera_movement": "crane up", "description": "breakthrough scene: The dead zone is not dead — it's listening — ascending with gold, white palette, crane up camera."}}
{"song": "Signal Lost", "artist": "Dead Zone", "mood_arc": "disconnection → reconnection", "beat": 7, "timestamp": "1:30", "duration_seconds": 15, "lyric_line": "Reconnection starts with disconnection", "scene": {"mood": "community", "colors": ["warm crowd", "light"], "composition": "group shot", "camera_movement": "sweeping pan", "description": "community scene: Reconnection starts with disconnection — group shot with warm crowd, light palette, sweeping pan camera."}}
{"song": "Signal Lost", "artist": "Dead Zone", "mood_arc": "disconnection → reconnection", "beat": 8, "timestamp": "1:45", "duration_seconds": 16, "lyric_line": "When the signal returns, I choose not to answer", "scene": {"mood": "resolve", "colors": ["strong", "clear"], "composition": "profile", "camera_movement": "push in", "description": "resolve scene: When the signal returns, I choose not to answer — profile with strong, clear palette, push in camera."}}
{"song": "Signal Lost", "artist": "Dead Zone", "mood_arc": "disconnection → reconnection", "beat": 9, "timestamp": "2:00", "duration_seconds": 17, "lyric_line": "The dead zone taught me presence", "scene": {"mood": "peace", "colors": ["soft", "dawn"], "composition": "wide landscape", "camera_movement": "steady wide", "description": "peace scene: The dead zone taught me presence — wide landscape with soft, dawn palette, steady wide camera."}}
{"song": "Signal Lost", "artist": "Dead Zone", "mood_arc": "disconnection → reconnection", "beat": 10, "timestamp": "2:15", "duration_seconds": 18, "lyric_line": "Signal lost. Self found.", "scene": {"mood": "ending", "colors": ["fade", "single accent"], "composition": "empty frame", "camera_movement": "dissolve", "description": "ending scene: Signal lost. Self found. — empty frame with fade, single accent palette, dissolve camera."}}
{"song": "Corner Store", "artist": "Bodega Dreams", "mood_arc": "small world → big dreams", "beat": 1, "timestamp": "0:00", "duration_seconds": 14, "lyric_line": "The bodega is open 24/7 so are the dreams", "scene": {"mood": "opening", "colors": ["city gray", "dawn"], "composition": "establishing shot", "camera_movement": "slow zoom", "description": "opening scene: The bodega is open 24/7 so are the dreams — establishing shot with city gray, dawn palette, slow zoom camera."}}
{"song": "Corner Store", "artist": "Bodega Dreams", "mood_arc": "small world → big dreams", "beat": 2, "timestamp": "0:15", "duration_seconds": 15, "lyric_line": "Behind the counter: a PhD in survival", "scene": {"mood": "tension", "colors": ["dark", "neon accent"], "composition": "close-up", "camera_movement": "handheld", "description": "tension scene: Behind the counter: a PhD in survival — close-up with dark, neon accent palette, handheld camera."}}
{"song": "Corner Store", "artist": "Bodega Dreams", "mood_arc": "small world → big dreams", "beat": 3, "timestamp": "0:30", "duration_seconds": 16, "lyric_line": "Every item on the shelf is a small miracle", "scene": {"mood": "explosion", "colors": ["bright", "contrast"], "composition": "wide action", "camera_movement": "rapid movement", "description": "explosion scene: Every item on the shelf is a small miracle — wide action with bright, contrast palette, rapid movement camera."}}
{"song": "Corner Store", "artist": "Bodega Dreams", "mood_arc": "small world → big dreams", "beat": 4, "timestamp": "0:45", "duration_seconds": 17, "lyric_line": "The lottery tickets are prayers in latex", "scene": {"mood": "reflection", "colors": ["warm", "shadows"], "composition": "medium shot", "camera_movement": "steady", "description": "reflection scene: The lottery tickets are prayers in latex — medium shot with warm, shadows palette, steady camera."}}
{"song": "Corner Store", "artist": "Bodega Dreams", "mood_arc": "small world → big dreams", "beat": 5, "timestamp": "1:00", "duration_seconds": 18, "lyric_line": "Coffee for a dollar, hope for free", "scene": {"mood": "struggle", "colors": ["muted", "single pop"], "composition": "figure alone", "camera_movement": "tracking", "description": "struggle scene: Coffee for a dollar, hope for free — figure alone with muted, single pop palette, tracking camera."}}
{"song": "Corner Store", "artist": "Bodega Dreams", "mood_arc": "small world → big dreams", "beat": 6, "timestamp": "1:15", "duration_seconds": 14, "lyric_line": "The bodega cat knows all the secrets", "scene": {"mood": "breakthrough", "colors": ["gold", "white"], "composition": "ascending", "camera_movement": "crane up", "description": "breakthrough scene: The bodega cat knows all the secrets — ascending with gold, white palette, crane up camera."}}
{"song": "Corner Store", "artist": "Bodega Dreams", "mood_arc": "small world → big dreams", "beat": 7, "timestamp": "1:30", "duration_seconds": 15, "lyric_line": "Corner store: the real community center", "scene": {"mood": "community", "colors": ["warm crowd", "light"], "composition": "group shot", "camera_movement": "sweeping pan", "description": "community scene: Corner store: the real community center — group shot with warm crowd, light palette, sweeping pan camera."}}
{"song": "Corner Store", "artist": "Bodega Dreams", "mood_arc": "small world → big dreams", "beat": 8, "timestamp": "1:45", "duration_seconds": 16, "lyric_line": "Dreams don't close at midnight", "scene": {"mood": "resolve", "colors": ["strong", "clear"], "composition": "profile", "camera_movement": "push in", "description": "resolve scene: Dreams don't close at midnight — profile with strong, clear palette, push in camera."}}
{"song": "Corner Store", "artist": "Bodega Dreams", "mood_arc": "small world → big dreams", "beat": 9, "timestamp": "2:00", "duration_seconds": 17, "lyric_line": "The owner came here with nothing — now everything", "scene": {"mood": "peace", "colors": ["soft", "dawn"], "composition": "wide landscape", "camera_movement": "steady wide", "description": "peace scene: The owner came here with nothing — now everything — wide landscape with soft, dawn palette, steady wide camera."}}
{"song": "Corner Store", "artist": "Bodega Dreams", "mood_arc": "small world → big dreams", "beat": 10, "timestamp": "2:15", "duration_seconds": 18, "lyric_line": "The corner store is the first chapter.", "scene": {"mood": "ending", "colors": ["fade", "single accent"], "composition": "empty frame", "camera_movement": "dissolve", "description": "ending scene: The corner store is the first chapter. — empty frame with fade, single accent palette, dissolve camera."}}