Skip to content

feat(video): switch 0.19.0 renderer Remotion → HyperFrames (HTML-native, Apache-2.0)#314

Open
rennf93 wants to merge 156 commits into
masterfrom
feature/video-engine-hyperframes
Open

feat(video): switch 0.19.0 renderer Remotion → HyperFrames (HTML-native, Apache-2.0)#314
rennf93 wants to merge 156 commits into
masterfrom
feature/video-engine-hyperframes

Conversation

@rennf93

@rennf93 rennf93 commented Jul 6, 2026

Copy link
Copy Markdown
Owner

Summary

Switches the 0.19.0 video renderer from Remotion to HyperFrames (HeyGen, Apache 2.0) before release.

Why: HyperFrames is the better fit for RoboCo's agent-authored video pipeline — LLMs write HTML more naturally than JSX (the whole point of an AI-agent workforce), HyperFrames renders seek-driven byte-identical MP4s, and Apache 2.0 protects the CLA's future commercial-dual-license option (Remotion's source-available license requires a paid Company License for for-profits >3 employees).

Scope is bounded because the render boundary is renderer-agnostic: the orchestrator only calls renderer.render(source_dir, composition_id, input_props, orientation, render_key) over HTTP+tarball. Only the sidecar guts, the motion/ composition format (TSX→HTML), the render-client rename, and docs/tests change. The render loop, propose_video, the video_draft marker, the panel queue, CEO gates, the heartbeat mutex, and the X/TikTok posters stay byte-identical (Fork A — input_props preserved).

What changed (T1–T7)

  • T1 — Sidecar render core: render.js rewritten to @hyperframes/producer (createRenderJob + executeRenderJob); dropped the Remotion bundle-cache machinery; ensure-browser.mjs verifies Chrome Headless Shell via @hyperframes/engine.
  • T2 — Composition format: motion/ TSX → HTML. New motion/compositions/release-announcement/{vertical,square}.html + theme.css + props.js (reads window.__PROPS__ + window.__ORIENTATION__); vitest HTML-structure smoke test (4/4 green). Lint-generated index.html gitignored (sidecar renders <orientation>.html directly).
  • T3 — Render client rename: remotion_client.pyvideo_renderer_client.py; RemotionRendererVideoRenderer, NullRemotionRendererNullVideoRenderer, get_remotion_rendererget_video_renderer; config remotion_base_urlvideo_renderer_base_url; env ROBOCO_REMOTION_BASE_URLROBOCO_VIDEO_RENDERER_BASE_URL; orchestrator import + factory; test rename + monkeypatch paths.
  • T4 — Docker + compose rename: git mv remotion-renderer/ → video-renderer/; docker/remotion.Dockerfiledocker/video-renderer.Dockerfile (added system ffmpeg — HyperFrames shells out to ffmpeg on PATH, unlike @remotion/renderer which bundled it); all three compose files (.yaml + .yml + registry.yml) service/image/container/env renamed; release.yml, Makefile, .dockerignore/.gitignore path comments.
  • T5 — HTML-structure smoke test: folded into T2.
  • T6 — Docs: CLAUDE.md video-engine paragraph rewritten for HyperFrames (+ m2/m3 prose fixes); CHANGELOG [0.19.0]; docs/rag/architecture/{video-engine,minio-storage}.md; docs/map/video-engine.md added (+ complete-map, prompts-roles-taxonomy, deployment-tooling); motion/README.md; do_server.py docstring.
  • T7 — Folded hygiene (from pre-switch review): removed docs/internal/...§11.x spec refs from tiktok_client.py + x_video_client.py (parenthetical preserved); minio_client.py global+# noqa: PLW0603 → module-level dict cache (test-monkeypatch-compatible, not lru_cache); test_video_routes.py _build_app db_session: AsyncSession | None + dropped 4× # type: ignore[arg-type]. Net −6 suppressions (2 noqa + 4 type:ignore removed, none added).

Verification

  • uv run ruff format . && uv run ruff check . && uv run mypy roboco/ clean — zero new noqa/type: ignore.
  • Video pytest suite green (18 passed / 19 skipped — the 19 skipped are db-gated, pre-existing).
  • git grep -niE 'remotion' -- roboco/ motion/ video-renderer/ docker/ docker-compose*.yml .github/ Makefile → only docker/video-renderer.Dockerfile:7 (brief-mandated historical contrast comment) + motion/README.md:35 (easing-rationale prose). No live code/config refs.
  • alembic heads → single head 062_tiktok_credentials (untouched — no migration in this PR).
  • docker build unverified locally (daemon unavailable in sandbox); CEO builds on the NAS.

Known minor nits (pending final whole-branch review)

A consolidated nits list lives at .superpowers/sdd/progress.md (gitignored scratch). None block merge — all Critical/Important findings across the 6 task reviews were fixed inline; remaining items are Optional-polish (T1 outDir edge-case leak, trailing newlines, T2 redundant data-fps/non-standard data-composition-duration, T2 4th-highlight <li> DOM-by-default) or Confirmed-non-defects (HyperFrames lint false-positives structurally unavoidable per Fork A). The final whole-branch review will triage which to fix before merge.

License

AGPL-3.0 only. HyperFrames is Apache 2.0 (compatible). No MIT/license reintroduction anywhere.


Built via subagent-driven development (7 tasks, fresh implementer per task, task review after each — all approved). Branch feature/video-engine-hyperframes from cebbd73e.

@github-project-automation github-project-automation Bot moved this to Backlog in RoboCo Kanban Jul 6, 2026
@github-actions github-actions Bot added documentation Docs, README, CHANGELOG, governance files ci GitHub Actions and CI configuration area: services Touches roboco/services/ (business logic, side effects) tests Test suite changes area: orchestrator Touches roboco/runtime/ (agent spawner, dispatch loops) area: mcp Touches roboco/mcp/ (MCP server entry points) build Makefile / Docker / Docker Compose / packaging labels Jul 6, 2026
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown

Thanks for opening your first pull request on RoboCo!

Quick checklist before review (most of these are enforced by CI, but worth a glance):

  • make quality — ruff format check, ruff check, mypy, pytest (≥80% coverage), and the rest of the gate
  • Panel changes pass pnpm lint and pnpm exec tsc --noEmit (run from panel/)
  • No # noqa / # type: ignore shortcuts; pre-existing violations in touched files are fixed
  • Added an entry under ## [Unreleased] in CHANGELOG.md
  • Signed the CLA (the bot will prompt you on this PR)
  • Signed your commits — master requires verified signatures (SSH signing setup)
  • Updated any affected docs under docs/

See CONTRIBUTING.md for the full workflow and the Code of Conduct for the community standards we follow.

Welcome aboard — a maintainer will review shortly.

Comment thread video-renderer/render.js Fixed
Comment thread video-renderer/render.js Fixed
rennf93 added 3 commits July 6, 2026 02:38
…highlight

Final whole-branch review (Opus) triaged two FIX items from the SDD nits
ledger; the rest ship as-is.

- render.js: a synchronous throw from createRenderJob (post-mkdtemp, not
  awaited) left an empty outDir on disk — the outer catch only reclaimed
  extractDir. Reclaim outDir too when it exists, and correct the stale
  comment that claimed the out dir was never created.
- {vertical,square}.html: the 4th highlights <li> lived in the DOM hidden
  only by JS, so a no-JS / failed-script render would show an empty bullet.
  Start it style="display:none" and reveal on populate, so an unscripted
  render shows nothing instead.

Vitest smoke (release-announcement.test.js) 4/4 green; render.js syntax
checked. Python suite untouched by this fix (JS/HTML only).
T7 widened _build_app's db_session param to AsyncSession | None (to drop the
4x # type: ignore[arg-type] on the DB-independent _build_app(None, ...) calls)
but left the inner _override_db fixture typed AsyncIterator[AsyncSession] —
so 'yield db_session' yielded AsyncSession | None into a declared AsyncSession,
and mypy failed at test_video_routes.py:177 ('Incompatible types in yield').

The DB-independent media tests pass db_session=None deliberately: their route
uses a monkeypatched task service and never awaits the session, so yielding
None is safe at runtime. Type the override's yield as AsyncSession | None to
match — no cast, no # type: ignore, no assert, runtime behavior unchanged.
The 3 media tests (3 passed) and the 19 db-gated tests (skipped locally) hold.
reflow_md.py walks the filesystem via rglob('*.md') and skips tooling dirs
(.venv, .mypy_cache, .pytest_cache, ...) but not .superpowers/ — the
superpowers SDD workflow's scratch dir (briefs, reports, progress ledger,
all gitignored). A dev running SDD locally would hit a false markdown-prose
gate failure on those transient files. Add .superpowers to SKIP_DIRS,
consistent with the existing tooling-scratch exclusions.
@rennf93 rennf93 moved this from Backlog to In review in RoboCo Kanban Jul 6, 2026
@rennf93 rennf93 self-assigned this Jul 6, 2026
rennf93 added 4 commits July 6, 2026 03:15
compositionId flowed unvalidated from the POST body into path.join
under extractDir/motion/compositions/, so a '../..'-style value could
escape the composition dir (CodeQL: Uncontrolled data used in path
expression). Validate at the trust boundary in server.js
(/^[A-Za-z0-9_-]+$/) and add a path.resolve + startsWith containment
check in render.js so it stays safe regardless of caller.
flow_server._build_headers and do_server._build_headers constructed
only X-Agent-ID/Role/Correlation-ID, omitting X-Agent-Token and
X-Agent-Team (unlike ApiClient._get_agent_headers used by the other
MCP servers). Latent since the gateway refactor — surfaced when
ROBOCO_AGENT_AUTH_REQUIRED=true was armed on the NAS, 401-ing every
flow/do verb with 'Missing X-Agent-Token header'. Add both headers
(mirroring ApiClient) so the HMAC gate passes. Tests assert the
headers are now injected.
Mirrors ci_watch_enabled (migration 048): the global
ROBOCO_VIDEO_ENGINE_ENABLED flag arms the subsystem; the new
projects.video_engine_enabled column (migration 063) opts a repo into
authoring against its motion/ dir. VideoEngine._opted_in_project no-ops
open_video_task at the single chokepoint covering all three trigger
paths (on-release, on-spotlight, CEO on-demand) until the operator
flips it in the panel edit-project dialog. Existing projects stay
opted out (server_default=false).
… sites

The prior fix (6ed4e13) covered the flow/do MCP servers but missed four
other agent->orchestrator call sites that built the header dict by hand
and omitted X-Agent-Token and/or X-Agent-Team. With ROBOCO_AGENT_AUTH_REQUIRED
armed on the NAS, every one 401s:

- agent_sdk/server.py: the session-end post-mortem flush
  (/api/journals/me/entries), A2A persistence + offline fallback
  (/api/a2a/*), and the stopped-without-transition auto-substitute
  (/api/tasks/auto-substitute) — all sent only X-Agent-ID/Role, so each
  401'd 'Missing X-Agent-Token'. Add a shared _agent_headers() helper
  (mirroring flow_server._build_headers) and route all four through it.
- agent_sdk/secretary_driver.py: _headers() sent the token but not the
  team, so the HMAC gate 401'd with signature mismatch (secretary is
  board-team; token signed with team='board', verified with team='').
  Add the team header.
- mcp/git_readonly.py: the read-only git MCP sent only X-Agent-ID/Role
  — no token, no team — so /api/git/* 401'd once auth was armed. Convert
  the static _HEADERS to a _headers() helper with team + token.
- runtime/orchestrator.py: the cell-PM auto-submit self-API call acted
  as a PM with a hand-built {X-Agent-ID, X-Agent-Role} dict — no token,
  no team — 401ing under auth-required. Add _agent_api_headers(uuid,
  role) mirroring _system_api_headers, and use it.

Tests: _agent_headers round-trip (token + team, team-omitted when None),
_agent_api_headers carries a signed PM token + team.
rennf93 and others added 28 commits July 7, 2026 05:56
…,L27,L14,L28,H19,M13,M14,H20,M16,M45,L25,M15,L24)
…ep so live cost reflects Anthropic cache spend
[phase9] Auth hardening: bound agent tokens (iat/exp) + JWT jti revocation
[scan-fix] Phase 8: LLM providers / usage / billing
[Phase 7] schemas/conventions/MegaTask/API fixes (15 findings, scan-fix program)
Phase 6 — Panel frontend hardening (rides 0.19.0)
Phase 5 — Background-engine hardening (rides 0.19.0)
Phase 4 — Git / workflow hardening (rides 0.19.0)
Phase 3 — Data-integrity fixes (rides 0.19.0)
Phase 2 — State-machine & gateway integrity (rides 0.19.0)
[Phase 1/scan] Auth & security exposure hardening (0.19.0)
@github-actions github-actions Bot added dependencies pyproject.toml / uv.lock or panel package manifests area: gateway Touches roboco/services/gateway/ (Choreographer, verb surface) labels Jul 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: alembic Touches alembic/ (database migrations) area: api Touches roboco/api/ (FastAPI routes, schemas, app) area: db Touches roboco/db/ or roboco/models/ area: gateway Touches roboco/services/gateway/ (Choreographer, verb surface) area: mcp Touches roboco/mcp/ (MCP server entry points) area: orchestrator Touches roboco/runtime/ (agent spawner, dispatch loops) area: panel Touches panel/ (Next.js control panel) area: services Touches roboco/services/ (business logic, side effects) build Makefile / Docker / Docker Compose / packaging ci GitHub Actions and CI configuration dependencies pyproject.toml / uv.lock or panel package manifests documentation Docs, README, CHANGELOG, governance files tests Test suite changes

Projects

Status: In review

Development

Successfully merging this pull request may close these issues.

2 participants