mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
Feature/video artifact verification (#537)
* fix(release): CI wait polls the prod rung; escape the header tooltip apostrophe get_latest_ci_conclusion defaults to the ladder's head rung, so wait_for_ci searched slave for a release commit that lives on master and timed out after 40 minutes with the run already green. The wait now passes the prod branch explicitly. Also fixes the react/no-unescaped-entities error that turned master's Panel CI red. * fix(panel,video): dead dialog triggers behind tooltips; dotted composition ids render HelpTip nested inside a Dialog/AlertDialog trigger puts the trigger's click handler on the Tooltip root, which renders no DOM — the agents Spawn item and the KB Reindex-All / Delete-index confirms were dead. Tooltips now wrap the triggers. The video renderer accepts interior single dots in composition ids (release-0.25.0) with '..' still unrepresentable, and propose_video refuses an unrenderable id at authoring time. * fix(dispatch): restart-safe PM review turns A leaf task in awaiting_pm_review had no periodic pickup: the closure dispatcher bailed on childless tasks and skipped PR-bearing review tasks as already-promoted, assuming the submit-time PM session was still alive — an assumption every restart breaks. Proven live on the docs-sync leaf after the 0.25.0 redeploy, which also dependency-blocked its sibling dev task. Childless awaiting_pm_review tasks now flow to the PM's review turn, and the merge turn respawns its PM when none is active. * feat(video): verify the rendered artifact, not the source The 14s release-0.25.0 cut shipped with only one of four scenes visibly registering: the dev authored DOM, the smoke asserted DOM, QA read code — nobody consumed the rendered MP4 before the CEO did. Close that loop, and the reject loop behind it: - sidecar frames mode: POST /render with frames=1..32 renders the cut, ffprobes the REAL duration, extracts midpoint-sampled keyframe PNGs (timestamps in filenames), streams a tar.gz back with X-Video-Duration - request_render do-verb (developer/QA, request_sandbox's shape): renders the caller's ACTUAL composition — dev's own worktree (head_sha/dirty provenance), QA a read-only git-archive export of the assembled branch — extracts frames to the container-shared .previews/ path, stamps the render_preview marker, returns the paths as envelope evidence - gate: i_am_done on a source=video task refuses without a stamped render_preview (Requirement.RENDER_VERIFIED; canonical source string moved to foundation as markers.VIDEO_TASK_SOURCE; mirrored in the possibilities-matrix fast path so it cannot bypass the check) - QA claim_review evidence carries video_context (composition id, the dev's preview, a re-render instruction) so review checks output - dev spawn prompt block + a 4th authoring AC order Read-every-frame verification before submitting - reject -> re-author: a CEO reject with a reason opens a fresh authoring task carrying the verbatim feedback + a revise-in-place pointer at the existing composition (best-effort, never fails the reject) — rejection feedback no longer dies on the cancelled draft E2E: rendered the committed release-0.25.0 composition through the new frames mode locally — the returned keyframes show exactly the reported failure (blank frame at 5.8s, only 'Env ladder' by 12.8s), the check the fleet was missing. --------- Co-authored-by: Renn F <rennf93@users.noreply.github.com>
This commit is contained in:
@@ -24,6 +24,10 @@ Even when enabled, distribution requires an explicit per-clip CEO approval — a
|
||||
|
||||
All three open a normal, **assigned** UX/UI authoring task (balanced across the two ux-devs) rather than a held draft — the dev builds a HyperFrames HTML composition under `motion/compositions/<id>/` (one `<id>/vertical.html` + `<id>/square.html` carrying the HyperFrames render params on `<html>`) and proposes its composition id + per-platform captions via the team-gated `propose_video` do-tool, then ships it through the standard commit/PR/QA/doc/review lifecycle.
|
||||
|
||||
## Artifact verification (request_render)
|
||||
|
||||
Authoring is gated on the RENDERED artifact, not just its source: the `request_render` do-tool (developer/QA) renders the caller's actual composition through the sidecar and extracts evenly spaced keyframe PNGs to a container-shared `.previews/` path, returning their absolute paths in the envelope's `evidence.frames`. The agent must Read every frame and verify each scene/feature from the brief appears fully and legibly — a 14-second cut that only ever shows its first scene is exactly what this catches. A developer renders their own working tree (worktree-aware, `head_sha`/`dirty` provenance stamped); QA renders a read-only `git archive` export of the assembled branch — never a working tree. A successful render stamps the task's `render_preview` marker, and `i_am_done` on a video-authoring task refuses without it (`Requirement.RENDER_VERIFIED`, mirrored in the possibilities-matrix fast path), so no video task can complete on a source-only self-review. QA's `claim_review` evidence carries a `video_context` block (composition id + the dev's stamped preview + an instruction to re-render the branch state) so the reviewer checks output, not source.
|
||||
|
||||
## Render loop and the sidecar
|
||||
|
||||
Once the authoring task completes, an orchestrator render loop (`_video_render_loop`, interval `ROBOCO_VIDEO_RENDER_INTERVAL_SECONDS`) tars the merged `motion/` source and POSTs it to the `video-renderer` sidecar (`ROBOCO_VIDEO_RENDERER_BASE_URL`). The sidecar is credential-free and git-free — it reads only what it's POSTed, bundles the composition, renders both the 9:16 and 1:1 MP4 cuts (`ROBOCO_VIDEO_RENDER_TIMEOUT_SECONDS` per render), and streams the bytes back. The orchestrator writes the MP4s to `ROBOCO_VIDEO_OUTPUT_DIR` (bind-mounted in all three compose files so renders survive container recreation) — the sidecar never writes to disk directly. The sidecar renders via `@hyperframes/producer` — agent-authored HTML rendered in headless Chrome plus system `ffmpeg` for the seek-driven, frame-deterministic MP4 cut. HyperFrames was chosen for its Apache-2.0 license (no React/TSX toolchain lock-in for the authoring agents), agent-authored HTML as the composition substrate, and seek-driven deterministic renders that match the post-pipeline shape without a node-bundled renderer.
|
||||
@@ -36,7 +40,7 @@ The render pass materializes a held `video_post` draft — mirroring the X-post/
|
||||
|----------|--------|
|
||||
| `GET /api/video/posts` | List every held video draft awaiting decision. |
|
||||
| `POST /api/video/posts/{task_id}/approve` | Post the rendered clip to X (native video, v2 media upload) and/or TikTok (inbox upload). Idempotent — approving an already-posted draft is a no-op. |
|
||||
| `POST /api/video/posts/{task_id}/reject` | Cancel the draft with a reason. Terminal. |
|
||||
| `POST /api/video/posts/{task_id}/reject` | Cancel the draft with a reason — and route the feedback back into the flow: a non-empty reason opens a fresh authoring task (same occasion, brief = the CEO's verbatim feedback + a revise-in-place pointer at the existing composition), so a rejection is a rework loop, not a dead end. |
|
||||
| `POST /api/video/request` | Open an on-demand authoring task (CEO-only). |
|
||||
|
||||
Approval runs under a Redis heartbeat-renewed lock so a double-click can't double-post; the task is marked `COMPLETED` under the same lock before it releases.
|
||||
|
||||
Reference in New Issue
Block a user