Add trend-engine: predictive multi-agent trend→clip→publish workflow - #1
Draft
kaustin923 wants to merge 38 commits into
Draft
Add trend-engine: predictive multi-agent trend→clip→publish workflow#1kaustin923 wants to merge 38 commits into
kaustin923 wants to merge 38 commits into
Conversation
Self-contained package under trend-engine/ implementing a multi-agent pipeline built on the Claude Agent SDK (claude-opus-4-8): Trend Scout → Sourcing → Editor → Compliance Gate → Telegram approval → Publisher → Monitor - Trend Scout (crown jewel) built out: pulls Reddit/Google Trends/YouTube/ Hacker News signals and uses Claude to cluster + rank scored topics (momentum, longevity, saturation, opportunity). Live free APIs with DRY_RUN fixtures. - Legal-first design: every source carries license metadata; the Compliance Gate hard-blocks unknown provenance and routes all non-original content to a human Telegram approval gate before publishing. - Publisher/Editor/Monitor stubbed behind DRY_RUN with explicit throws so live mode can't silently ship a stub. - Runs offline in DRY_RUN (default); typechecks clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wUwQZt9XQTUApHoksqrFa
trend-engine is a self-contained package with its own tsconfig and tooling; keep the portfolio's `npm run lint` from scanning it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wUwQZt9XQTUApHoksqrFa
… e2e tests Addresses two problems: the product was reactive (chasing already-saturated trends), and nothing was actually verified. - Brain is now Claude Fable 5 (with server-side refusal fallback to Opus 4.8), and injectable via an LLM interface so the whole pipeline runs end-to-end offline with a deterministic mock — no API key needed to test. - Trend Scout → Trend Forecaster: fuses reactive signals with UPCOMING catalysts (scheduled events 2–8 weeks out), places each topic on its hype curve (emerging/rising/peaking/saturated/declining), and returns lead time + a concrete post window. It down-ranks and SKIPS saturated topics — e.g. it refuses to post generic World Cup content once the tournament is underway, and instead surfaces what's on the horizon. - Added upcoming-catalysts source (web-search-backed live, fixtures in DRY_RUN). - Added node:test e2e suite (7 tests, all green) exercising forecasting, saturation-skip, ranking, compliance, and the full publish pipeline. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wUwQZt9XQTUApHoksqrFa
… green Integrated the output of four Fable subagents (each on disjoint files) onto the verified forecasting core, fixed integration typing, wired the learning loop, and re-verified the whole thing end-to-end. - Editor: real ffmpeg rendering — pure buildFfmpegArgs() (unit-tested), 9:16/1:1 /16:9 reframe, ≤30s trim, drawtext caption + attribution card, spawn (no shell), http(s) source download; DRY_RUN still logs-only. - Sourcing: real Pexels (stock) + Wikimedia Commons (per-file license extraction via mapWikimediaLicense; BY-SA/NC/unclear fail closed to unknown and are dropped); best-licensed-first, unknown filtered at the boundary. - Publisher: official API clients (YouTube Data v3 resumable, TikTok Content Posting, IG Graph Reels, X v2); missing creds → skipped (never blocks the run); never publishes without an approved decision. - Monitor + learning: records outcomes.jsonl joined to the forecast; getLearning Summary() aggregates what converted and is now injected into the forecaster's prompt (loop closed). Deterministic mock metrics (no Math.random). - Fixed a generic union-narrowing error in publishers/common.ts poll(). - README updated to reflect built-out status + the predictive/forecasting model. Verification: `npm run typecheck` clean; `npm test` 32/32 pass (forecasting, saturation-skip, ranking, compliance, ffmpeg args, sourcing license mapping, publisher skip-on-missing-creds, learning aggregation, full e2e pipeline). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wUwQZt9XQTUApHoksqrFa
Self-review of the branch surfaced 6 findings; fixed the actionable ones: - trendScout: forecaster schema made `catalyst` nullable via a type-array, which is outside Anthropic's structured-output schema subset and would 400 the core live call — switched to anyOf [string, null]. (Highest-impact; only reachable in live mode, so mocked tests didn't catch it.) - config: dataDir derived a filesystem path via URL.pathname, which breaks on Windows (/C:/…) and percent-encodes spaces — use fileURLToPath. - tiktok: 50 MB base chunk could push the remainder-absorbing final chunk over TikTok's 64 MB per-chunk cap for large files — use 32 MB so the final chunk is always < 64 MB. - learning: getLearningSummary read the entire unbounded outcomes.jsonl on every forecast — bound to the most recent 500 records (recency is what we want). Left documented (not code-fixed): the Fable structured+fallbacks beta combo needs one live smoke test to confirm compatibility; the e2e test writes to the package data/ dir (low-severity test hygiene, gitignored). typecheck clean; 32/32 tests still pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wUwQZt9XQTUApHoksqrFa
… publishers, scheduler daemon + learning loop Iteration 1 (Fable-orchestrated, Codex gpt-5.6-sol xhigh builders): - Editor: real ffmpeg pipeline — download w/ cache, 9:16 1080x1920 render, ffprobe verification, capability-gated caption/attribution burn + .ass sidecar - Sourcing: live Pexels/Pixabay/NASA/Wikimedia providers with true per-file license resolution; human-review-bypass mock removed - Publisher: official API adapters (YouTube resumable upload, Instagram Graph container flow, TikTok chunked FILE_UPLOAD) — every adapter throws on DRY_RUN and on missing env keys; attribution re-appended on human caption overrides - Scheduler: multi-daily daemon w/ jitter, stale-lock handling, run-state persistence, dedupe window; Monitor writes metrics.jsonl fed back to Scout - Adversarial review: 17 findings, 4 must-fix fixed (drawtext escaping, TikTok >64MB chunking, attribution drop, DRY_RUN enforcement at call sites) - 41/41 tests green, typecheck clean, e2e DRY_RUN pipeline verified - Add .env.example covering the full env surface Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ublisher, learning module, tests) ported next Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…s from cross-line review Reconciliation of the superseded session line (cc96096) into this architecture: - New src/publish/x.ts — X v2 chunked media upload + tweet post, DRY_RUN throw, fail-loud env, attribution-preserving 280-char caption composition - New src/learning.ts + TopicOutcome records (outcomes.jsonl): monitor now joins metrics to topic domains/stage/recommendation; ranked learning summary injected into the forecaster prompt; deterministic FNV-1a dry-run metrics - Regressions caught by cross-line compare and fixed: approval gate restored inside publish() (defense-in-depth), caption burn-in actually wired into the render, Wikimedia license matching widened back to 4-field haystack with NC/ND/SA fail-closed, source downloads stream to disk instead of buffering - Instagram switched to resumable direct upload — public clip hosting (PUBLIC_VIDEO_BASE_URL) no longer required - TikTok returns real public post id; shared caption composer w/ hashtag normalization and per-platform caps; expectJson error contexts - 57/59 tests pass (2 skipped), typecheck clean, e2e dry-run verified Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The merged reorg broke two things the suite depends on:
- detectCapabilities() threw "ffmpeg not found" when ffmpeg was absent, so the
editor failed on every topic in DRY_RUN — breaking the "runs fully offline"
contract and failing 3 pipeline tests. A capability *probe* must never throw:
it now degrades to {drawtext:false, subtitles:false}. Live rendering still
fails loudly at probe()/runFfmpeg() when ffmpeg is missing, so nothing real
is silently skipped.
- Test files isolate by mutating the shared config.dataDir singleton, which
races when Node runs files concurrently (intermittent scheduler-test failure).
Serialized the filesystem-heavy suite with --test-concurrency=1.
Result: typecheck clean; 50/50 tests pass deterministically across repeated runs.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011wUwQZt9XQTUApHoksqrFa
…ls, launchd daemon Iteration 2 (5 Codex gpt-5.6-sol xhigh builders, adversarial review, 7 must-fix resolved): - Originals: macOS say TTS voiceover (temp-file input, injection-safe) + whisper.cpp word-timed captions (16kHz WAV transcode) with proportional fallback; draftOriginal assembles licensed b-roll + narration into license-type 'original' drafts with syntheticMedia flag; strict b-roll gate; 9:16 enforcement per platform - AI disclosure propagates: YouTube containsSyntheticMedia, TikTok is_aigc - Telegram: sendVideo preview cards, MarkdownV2 escaping (incl. backslash), persisted getUpdates offset, reject-with-reason, approvals.jsonl audit log, approval-gate now fails closed if the review card cannot be delivered - Live analytics: real YouTube/TikTok/IG/X stats fetchers; failed fetches skip records instead of writing zeros (learning-poison guard) - Leading signals: TheSportsDB calendar, Wikipedia pageview acceleration, GDELT news velocity, Google News RSS — keyless, fixture-backed, zero-fetch DRY_RUN - Daemon: launchd install/uninstall scripts (single-run npm start per fire, run.lock prevents overlap); scheduler jitter clamp + conditional releaseLock - 90/92 tests pass (2 env skips), typecheck clean, no-network dry-run verified Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Kept iteration-2 ffmpeg.ts (lazy probe, textfile filter, audio duration probe); applied their .catch() so the capability probe degrades instead of throwing when ffmpeg is absent. Took --test-concurrency=1 for the fs-heavy suite. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nance receipts + operating rulebook Iteration 3 (4 Codex gpt-5.6-sol xhigh builders, adversarial review, 1 must-fix resolved): - Originals publish per run (ORIGINALS_PER_RUN, default 1): sourced + original paths share compliance/approval/publish/monitor; distinct dedupe keys; mockLlm narration branch; e2e asserts the original flows end-to-end - Hardening: all 8 prior review findings fixed (Telegram caption/offset/cross- card decisions, zero-view guard on all platforms, plist XML+shell escaping, two-layer filter-filename escaping, strict audit toggle, IG token placement) - Quota preflights: per-platform daily counters in state.json, IG publishing- limit + TikTok creator_info preflights, YouTube quota-unit ledger - Provenance receipts: per-publish SHA-256 + license snapshot + approval linkage + post id → data/provenance.jsonl - docs/RULEBOOK.md + rulebook.json: 5-agent research pass distilled into tiered green/yellow/red format rules, platform requirements, enforcement reality, monetization roadmap — compliance-gate encoding queued - npm scripts load .env natively (node --env-file-if-exists) - 120/120 tests green (full ffmpeg: caption burn-in tests now active), dry-run publishes 12/12 incl. the originals path Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
First live run hung indefinitely on a source fetch (0.7s CPU, blocked on I/O). Promise.allSettled already isolates per-source failures, so a timed-out source now simply drops from the round instead of stalling the whole run. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…mplate engine, source health + events calendar Iteration 4 (4 Codex gpt-5.6-sol xhigh builders, adversarial review, 4 must-fix resolved): - Compliance now assigns green/yellow/red tiers per docs/rulebook.json: red = hard block before approval (unknown license, unlicensed music, social-CDN sources, watermarked re-exports); yellow forces human review with rulebook condition checklists; music gate fails closed on missing audio provenance; editorial-stock flags survive the originals lane - Telegram cards render tier + conditions; yellow drafts require an explicit YES reply (approve button alone won't publish); red never reaches a card; licensed-clip monthly budget gate (LICENSED_CLIP_BUDGET, default 0) - Anti-template variation: per-video structure fingerprints, n-gram similarity check vs last 20, one vary-regeneration then yellow-flag; deterministic caption-style rotation — the inauthentic-content defense - Source health per run (count/ms/error/timeout per source) + fixes from live diagnosis: Google Trends daily RSS was 404-dead → replaced with live trending RSS; Reddit 403-blocks our UA → explicit degraded status; Wikipedia derives watch terms from other sources; new keyless events-calendar source (near-term entertainment/cultural events) - 151/151 tests, dry-run 12/12 publishes, scout prints source health Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…epair, corrective re-ask First live pipeline run crashed on malformed JSON from the forecaster (fallback-path output; parse error at char 4338). structured() now retries once on truncation with doubled budget, repairs prose fringes and trailing commas, and re-asks once with the exact parse error before failing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Second live run died on the SDK's 10-min default request timeout during the xhigh forecast over 85 signals. Client now allows 20 min with 2 retries, and structured/research calls log elapsed seconds so long phases are visible. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ommand center Iteration 5 (4 Codex gpt-5.6-sol xhigh builders, adversarial review, 2 must-fix resolved): - npm run oauth:youtube — local consent flow mints YOUTUBE_REFRESH_TOKEN into .env (backup first, secrets never logged, state-validated, 5-min timeout) - Policy-drift watcher: 24h sha256 checks of YouTube/TikTok/Meta/X monetization policy pages from the daemon; drift → data/policy-drift.jsonl + Telegram alert - Claim-ops: data/claims.jsonl + CLI; takedown/strike auto-blacklists the rights holder; sourcing drops blacklisted matches on word boundaries only (review caught substring over-match nuking whole providers) - Telegram command center: /status /health /pause /resume /run from the owner chat only, offset-coexistent with approval polling (cross-process collision fixed via review), scheduler honors pause + run-now with interruptible waits - 186/186 tests, dry-run 12/12 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ox kits Iteration 6 (2 Codex gpt-5.6-sol xhigh builders + campaign research, 1 must-fix resolved): - GDELT gets its own 15s timeout + lighter query (5 terms, 3d timespan) - renderToVertical: two-pass loudnorm to -14 LUFS, capability-gated, never fails the render; silent/no-audio inputs skipped cleanly - Manual-post lane: approved drafts (and missing-credential publish rounds) write data/outbox/<date>-<topic>/ kits — MP4 + per-platform caption files + meta.json + AI-disclosure README; provenance gains outbox records - Review catch: attribution could be tail-truncated out of kit captions — now reserved before body truncation (license credit survives any cap) - 202/202 tests green Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nual-era tracking, reports + experiments Iteration 7 (4 Codex gpt-5.6-sol xhigh builds incl. one prerequisite-race rerun, 2 must-fix resolved): - Forecast calibration: every live forecast persisted; opportunity-decile vs realized platform-normalized percentile; summary injected into the forecaster prompt (the scorer reads its own report card) - Feature attribution: contentFeatures on every outcome (angle/hook/tier/ voice/hour), exponential 21d-half-life decay, platform-percentile normalization, min-sample guards; duplicate-entry dedupe (latest wins) - Manual-era tracking: npm run track register|views + Telegram /track; registered YouTube URLs auto-fetch public stats; manual metrics flow into the same outcomes pipeline — learning works from the first manual post - npm run report + /report: calibration table, top/bottom features, platform performance, manual coverage, computed recommendations → eval-report.json - Experiment scheduler: deterministic one-feature-at-a-time rotation into data/experiments.jsonl, surfaced as a soft hint to the editor - 223/223 tests green Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ELEVENLABS_API_KEY + ELEVENLABS_VOICE_ID in env → synthesis via the text-to-speech API (60s timeout, key never logged), mp3→AAC via existing ffmpeg helpers, whisper word-timing unchanged; any failure falls back to say silently. audioProvenance records elevenlabs vs macos-say so provenance and AI-disclosure stay accurate. 226/226 tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- studio/ package (Remotion 4, deterministic headless renders, offline fonts, VideoToolbox encode): ticket/receipt design system, split-flap type, animated charts, word-synced karaoke captions from whisper timestamps - Demo episode (concept proof) + Episode 1 "Your Phone Learned to Listen Offline" — educational format: six-scene curriculum, phone-mockup diagrams, ElevenLabs VO (key read from trend-engine/.env at runtime, never logged) - Review verdicts: demo "top-1% trajectory, not another template"; episode 1 "Ship — educational identity fully realized, visuals teach" - Known polish: ep1 section-03 card flight path clips a headline transiently Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two new tests in elevenlabs-tts.test.ts hard-failed in environments without ffmpeg (the silentMp3 helper spawns it) or macOS `say` (the fallback path) — unlike the rest of the suite, which skips such tests when the binary is absent. Added the same `t.skip(...)`-on-missing-binary guard used by editor-ffmpeg-render.test.ts, so the suite is green on Linux/CI too. typecheck clean; 214 pass / 0 fail / 12 skipped. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wUwQZt9XQTUApHoksqrFa
…, env binary paths, yuv420p limited range Both compositions re-rendered and verified: -14.10/-14.09 LUFS, yuv420p BT.709 TV range, previously-clipped frames confirmed clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Iteration 8 (3 Codex gpt-5.6-sol xhigh builders, 2 must-fix resolved):
- Pitch generator: 3-5 bold pitches per run with a hard quality bar
(≤9-word curiosity headlines, cross-domain angles, 'X explained' banned,
vertical diversity), owner feedback history feeds future generations
- Telegram pitch cards: natural-language replies ('1 yes, 2 no, 3 yes but…')
parsed per-pitch, feedback captured to the taste loop, owner-chat-locked,
offset-coordinated with approval polling, 4h expiry
- Studio bridge: approved pitches → script agent → studio script.json →
Remotion render with ElevenLabs VO → existing video approval card → outbox;
STUDIO_MODE default on when studio/ exists, classic path preserved
- 240/240 tests, dry-run logs the full two-gate plan
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t-date verification Studio: two-speaker dialogue (per-line ElevenLabs synthesis Jessica x George, concat + -14 LUFS loudnorm, whisper word-timing), speaker-colored karaoke captions with J/G chips, per-episode theme accent tokens (script.theme). Rendered dialogue-test.mp4 verified: 2 voices, style-switching captions, red accent, -14.2 LUFS, yuv420p. trend-engine: event-date verification — forecaster drops past/undated catalysts (past events → aftermath stage only), events-calendar recency filter, pitch verification pass (LLM checks event hasn't happened before Telegram), cards show explicit dates + days-away. Closes the wedding-pitch bug. 245/245 tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…+ label-collision polish Replaces the fake placeholder charts (hardcoded ONE/TWO/PACE bars + raw stage-direction text dumped on screen) with a typed Viz union: title, rangeBar, counter, statBig, compareBars, leaderboard, meter, grid, split — each rendered from real numbers in the script. Fixed rangeBar label collisions and counter double-minus. Spider-Man episode now renders verified box-office data (Deadline $180-190M, Toy Story 5 $160M, AMC -$632M loss, No Way Home $260M, $4.09B debt / $1.95 stock). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…s from LLM schemas Anthropic structured-output rejects array minItems/maxItems > 1 with a 400. Both the pitcher (pitches 3-5) and studio-bridge (scenes 4-6) schemas set these, so every live pitch-generation and script-generation call failed and the daemon punted to the next day — no pitches ever reached Telegram. Counts are already enforced in the prompts; dropped the schema constraints. Also ANTHROPIC_EFFORT=low (forecast ~2min vs ~10min at high). 245/245 tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… per-line concat Fixes the choppy 'reading from a slide' delivery — the whole conversation is synthesized in one /v1/text-to-dialogue request (eleven_v3) with prosody carrying across turns, per-speaker voices, and native timestamps for caption alignment. Conversational script + Eric voice (stability 0.65, less emotion). Single-voice episode-1 path unchanged. NOTE: current cut runs ~100s — script trim to ~50s pending. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…-mechanics pitches Owner feedback: pitches lacked a visceral so-what. Rewrote the quality bar to require personal, visceral stakes (viewer's money/time/what they watch), and to reject corporate-mechanics headlines unless reframed around what the viewer feels.
…tions synced, ~38s cuts
…rements Owner direction: pivot hard to sports/fantasy football (booming audience), every pitch must be non-obvious (kill anything a fan would call 'obviously') and dead simple (one clear idea, no dense stat-chains — the AMC video was too hard to follow). At least half of each batch sports/fantasy.
…rlap/empty cards + fantasy football episode Fixes: long spoken lines no longer render as a giant headline colliding with the viz; no-viz reaction lines render as a single clean centered statement (not an empty card); bigger caption bar. New sports/fantasy episode 'The August Tell' (green gridiron theme, Tuten empty-job signal, ELI5).
New src/field/ system (FieldLayer w/ yard lines+hash marks, PlayerDot, RouteArrow telestrator, FieldCard/Chip, geometry) + five shown-scene viz kinds: fieldPlay (lane opens, runner bursts through), depthFlow (starter fades, touches flow to riser), speedRace (40-yard strip race vs NFL avg), zoneHeat, riseRank. Breakouts episode re-shot so every player's situation passes the muted-viewer test — field diagrams instead of stat cards. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nt timing, silent track, proportional captions)
…ess — silent previews validated Both scripts staged (narrator format, humanized-voice settings ready, fact-checked numbers) and silent-rendered clean. Render-ready the moment ElevenLabs credits are restored.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this is
A self-contained multi-agent workflow (
trend-engine/) that forecasts trends before they peak, sources footage under a defensible license, clips + captions it, routes it through a human approval gate on Telegram, publishes to multiple platforms, and monitors performance to learn from what converted. Isolated from the Next.js portfolio app in its own package.The brain is Claude Fable 5 (with server-side refusal fallback to Opus 4.8), and it's injectable — so the whole pipeline runs end-to-end offline against a mock brain with no API key.
npm testproves it (32 tests).Pipeline
Deterministic control flow (
orchestrator.ts); Fable does the reasoning (forecasting, caption writing).Predictive, not reactive
The forecaster fuses reactive signals (Reddit/Trends/YouTube/HN) with upcoming catalysts (scheduled events 2–8 weeks out), places each topic on its hype curve (
emerging→saturated), and returns a lead time + concrete post window. It deliberately skips saturated topics — e.g. it refuses generic World Cup content once the tournament is underway, and instead surfaces what's on the horizon (meteor-shower peak, an upcoming showcase, a seasonal ramp). A test locks this behavior in.Legal-first by design
Every source clip carries
LicenseInfo. The Compliance Gate hard-blocksunknownprovenance and routes everything non-original to a human. There is deliberately no "grab an arbitrary copyrighted video" path. Sourcing pulls real licensed stock (Pexels) + Creative-Commons/public-domain (Wikimedia, with per-file license extraction that fails closed tounknown).Component status
DRY_RUN=true(default) mocks all sources and publishes nothing. Live-only stages (ffmpeg exec, official publish, generated-original render) are guarded so live mode can't silently ship a stub.Verification
npm run typecheck— cleannpm test— 32 tests pass: forecasting, saturation-skip, ranking, compliance, sourcing license mapping, ffmpeg arg-building, publisher skip-on-missing-creds, learning aggregation, and the full end-to-end pipeline (all offline against a mock brain)Known follow-ups
throw, not a stub — the next real build for fully-owned contentTry it
🤖 Generated with Claude Code
https://claude.ai/code/session_011wUwQZt9XQTUApHoksqrFa