[object Object]

← back to Dw Marketing Reels

Route 9:16 catalog reels through build-reel.mjs ($0, IG-publishable, gallery-visible)

d385b43830005297369fdef6a15760e4e8faff20 · 2026-07-22 07:48:24 -0700 · Steve

Running a real render exposed that new-arrivals-reel via claude -p was slow
(~8min), cost API, produced a SILENT reel, and orphaned it in output/ (invisible
to the gallery). The repo's own build-reel.mjs renders the same reel in ~20s, $0
(local ffmpeg/Chrome), with an IG-publishable AAC track, straight into reels/.
- video-runner: new-arrivals-reel@9:16 → build-reel.mjs (no arming needed);
  1:1 + url/newsletter keep the gated claude -p skill path; retry wraps both
- server: /api/videos gallery now also scans output/ (+ serve guard) so the
  HyperFrames url/newsletter renders that land there are visible + playable

Files touched

Diff

commit d385b43830005297369fdef6a15760e4e8faff20
Author: Steve <steve@designerwallcoverings.com>
Date:   Wed Jul 22 07:48:24 2026 -0700

    Route 9:16 catalog reels through build-reel.mjs ($0, IG-publishable, gallery-visible)
    
    Running a real render exposed that new-arrivals-reel via claude -p was slow
    (~8min), cost API, produced a SILENT reel, and orphaned it in output/ (invisible
    to the gallery). The repo's own build-reel.mjs renders the same reel in ~20s, $0
    (local ffmpeg/Chrome), with an IG-publishable AAC track, straight into reels/.
    - video-runner: new-arrivals-reel@9:16 → build-reel.mjs (no arming needed);
      1:1 + url/newsletter keep the gated claude -p skill path; retry wraps both
    - server: /api/videos gallery now also scans output/ (+ serve guard) so the
      HyperFrames url/newsletter renders that land there are visible + playable
---
 data/reels.json                                  |  35 ++++++++
 data/video-jobs.json                             |  13 +--
 data/video-suggestions.json                      |   4 +-
 output/reel-20260722-073452/meta.tsv             |   7 --
 output/reel-20260722-073452/poster.png           | Bin 1953541 -> 0 bytes
 output/reel-20260722-073452/prep/base_01.png     | Bin 1877750 -> 0 bytes
 output/reel-20260722-073452/prep/base_02.png     | Bin 2148348 -> 0 bytes
 output/reel-20260722-073452/prep/base_03.png     | Bin 1899435 -> 0 bytes
 output/reel-20260722-073452/prep/base_04.png     | Bin 2618742 -> 0 bytes
 output/reel-20260722-073452/prep/base_05.png     | Bin 2226041 -> 0 bytes
 output/reel-20260722-073452/prep/base_06.png     | Bin 2752933 -> 0 bytes
 output/reel-20260722-073452/prep/base_07.png     | Bin 2879244 -> 0 bytes
 output/reel-20260722-073452/prep/frame_00.png    | Bin 117257 -> 0 bytes
 output/reel-20260722-073452/prep/frame_01.png    | Bin 1953541 -> 0 bytes
 output/reel-20260722-073452/prep/frame_02.png    | Bin 2257550 -> 0 bytes
 output/reel-20260722-073452/prep/frame_03.png    | Bin 1850205 -> 0 bytes
 output/reel-20260722-073452/prep/frame_04.png    | Bin 2834742 -> 0 bytes
 output/reel-20260722-073452/prep/frame_05.png    | Bin 2318635 -> 0 bytes
 output/reel-20260722-073452/prep/frame_06.png    | Bin 2685420 -> 0 bytes
 output/reel-20260722-073452/prep/frame_07.png    | Bin 2806367 -> 0 bytes
 output/reel-20260722-073452/prep/frame_zz.png    | Bin 106301 -> 0 bytes
 output/reel-20260722-073452/prep/scrim2_01.png   | Bin 1773820 -> 0 bytes
 output/reel-20260722-073452/prep/scrim2_02.png   | Bin 2039313 -> 0 bytes
 output/reel-20260722-073452/prep/scrim2_03.png   | Bin 1654996 -> 0 bytes
 output/reel-20260722-073452/prep/scrim2_04.png   | Bin 2488067 -> 0 bytes
 output/reel-20260722-073452/prep/scrim2_05.png   | Bin 2088377 -> 0 bytes
 output/reel-20260722-073452/prep/scrim2_06.png   | Bin 2515396 -> 0 bytes
 output/reel-20260722-073452/prep/scrim2_07.png   | Bin 2571942 -> 0 bytes
 output/reel-20260722-073452/prep/scrim_01.png    | Bin 3707 -> 0 bytes
 output/reel-20260722-073452/prep/scrim_02.png    | Bin 3707 -> 0 bytes
 output/reel-20260722-073452/prep/scrim_03.png    | Bin 3707 -> 0 bytes
 output/reel-20260722-073452/prep/scrim_04.png    | Bin 3707 -> 0 bytes
 output/reel-20260722-073452/prep/scrim_05.png    | Bin 3707 -> 0 bytes
 output/reel-20260722-073452/prep/scrim_06.png    | Bin 3707 -> 0 bytes
 output/reel-20260722-073452/prep/scrim_07.png    | Bin 3707 -> 0 bytes
 output/reel-20260722-073452/prep/scrimmed_01.png | Bin 1768813 -> 0 bytes
 output/reel-20260722-073452/prep/scrimmed_02.png | Bin 1949509 -> 0 bytes
 output/reel-20260722-073452/prep/scrimmed_03.png | Bin 1617527 -> 0 bytes
 output/reel-20260722-073452/prep/scrimmed_04.png | Bin 2472643 -> 0 bytes
 output/reel-20260722-073452/prep/scrimmed_05.png | Bin 1995370 -> 0 bytes
 output/reel-20260722-073452/prep/scrimmed_06.png | Bin 2414603 -> 0 bytes
 output/reel-20260722-073452/prep/scrimmed_07.png | Bin 2452324 -> 0 bytes
 output/reel-20260722-073452/prep/topscrim_01.png | Bin 1629 -> 0 bytes
 output/reel-20260722-073452/prep/topscrim_02.png | Bin 1629 -> 0 bytes
 output/reel-20260722-073452/prep/topscrim_03.png | Bin 1629 -> 0 bytes
 output/reel-20260722-073452/prep/topscrim_04.png | Bin 1629 -> 0 bytes
 output/reel-20260722-073452/prep/topscrim_05.png | Bin 1629 -> 0 bytes
 output/reel-20260722-073452/prep/topscrim_06.png | Bin 1629 -> 0 bytes
 output/reel-20260722-073452/prep/topscrim_07.png | Bin 1629 -> 0 bytes
 output/reel-20260722-073452/reel-9x16.mp4        | Bin 7561778 -> 0 bytes
 output/reel-20260722-073452/selection.json       | 107 -----------------------
 output/reel-20260722-073452/src/01.jpg           | Bin 317316 -> 0 bytes
 output/reel-20260722-073452/src/02.jpg           | Bin 266907 -> 0 bytes
 output/reel-20260722-073452/src/03.jpg           | Bin 306028 -> 0 bytes
 output/reel-20260722-073452/src/04.jpg           | Bin 320473 -> 0 bytes
 output/reel-20260722-073452/src/05.jpg           | Bin 256010 -> 0 bytes
 output/reel-20260722-073452/src/06.jpg           | Bin 376765 -> 0 bytes
 output/reel-20260722-073452/src/07.jpg           | Bin 383204 -> 0 bytes
 output/reel-20260722-073452/urls.tsv             |   7 --
 output/reel-20260722-073452/verify_mid.png       | Bin 2488266 -> 0 bytes
 output/reel-20260722-073452/verify_out.png       | Bin 170117 -> 0 bytes
 scripts/video-runner.mjs                         |  60 +++++++++----
 server.js                                        |   3 +-
 63 files changed, 81 insertions(+), 155 deletions(-)

diff --git a/data/reels.json b/data/reels.json
index bf85bf6..891ecf1 100644
--- a/data/reels.json
+++ b/data/reels.json
@@ -1,4 +1,39 @@
 [
+  {
+    "file": "new-arrivals-2026-07-22-14-47.mp4",
+    "created_at": "2026-07-22T14:47:43.327Z",
+    "products": 6,
+    "seconds": 19,
+    "titles": [
+      "Flower Fall / Pearl",
+      "Flower Art / Pastel",
+      "Flower Art / Green",
+      "Flower Art / Blue",
+      "Flow Mini Shallow Grey / ",
+      "Flow Mini Deep Blue / "
+    ],
+    "caption": "New arrivals just landed ✨ Flower Fall, Flower Art, Flow Mini Shallow Grey & more from Rebel Walls. Discover the latest wallcoverings — shop the collection at designerwallcoverings.com 🔗\n\n#DesignerWallcoverings #NewArrivals #Wallcovering #Wallcoverings #InteriorDesign #InteriorDecor #HomeDesign #LuxuryInteriors #DesignInspo #RebelWalls",
+    "hashtags": [
+      "#DesignerWallcoverings",
+      "#NewArrivals",
+      "#Wallcovering",
+      "#Wallcoverings",
+      "#InteriorDesign",
+      "#InteriorDecor",
+      "#HomeDesign",
+      "#LuxuryInteriors",
+      "#DesignInspo",
+      "#RebelWalls"
+    ],
+    "publish": {
+      "instagram": {
+        "status": "pending"
+      },
+      "tiktok": {
+        "status": "pending"
+      }
+    }
+  },
   {
     "file": "new-arrivals-2026-07-22-14-10.mp4",
     "created_at": "2026-07-22T14:10:32.853Z",
diff --git a/data/video-jobs.json b/data/video-jobs.json
index 145a8a4..fe51488 100644
--- a/data/video-jobs.json
+++ b/data/video-jobs.json
@@ -1,12 +1 @@
-[
-  {
-    "id": "vj-mrw6o29f",
-    "kind": "new-arrivals-reel",
-    "url": "",
-    "eml": "",
-    "format": "9:16",
-    "status": "running",
-    "created_at": "2026-07-22T14:34:11.187Z",
-    "started_at": "2026-07-22T14:34:11.248Z"
-  }
-]
\ No newline at end of file
+[]
diff --git a/data/video-suggestions.json b/data/video-suggestions.json
index 59f0402..e66e495 100644
--- a/data/video-suggestions.json
+++ b/data/video-suggestions.json
@@ -43,9 +43,9 @@
       }
     ],
     "used": true,
-    "used_at": "2026-07-22T14:34:11.191Z",
+    "used_at": "2026-07-22T14:47:18.973Z",
     "used_where": {
-      "jobId": "vj-mrw6o29f",
+      "jobId": "vj-mrw74y4a",
       "kind": "new-arrivals-reel",
       "format": "9:16",
       "surface": "video-studio-queue"
diff --git a/output/reel-20260722-073452/meta.tsv b/output/reel-20260722-073452/meta.tsv
deleted file mode 100644
index 0923448..0000000
--- a/output/reel-20260722-073452/meta.tsv
+++ /dev/null
@@ -1,7 +0,0 @@
-Flower Fall	Pearl	Rebel Walls	191.00
-Flower Art	Green	Rebel Walls	191.00
-Florida	Blue	Rebel Walls	220.75
-Floral Opulence	Red	Rebel Walls	191.00
-Floral Meadow	Multi	Rebel Walls	191.00
-Inverness Wide Width	Teal and Clay	Thibaut	58.64
-Luta Sisal Wide Width	Flax and Metallic Gold	Thibaut	58.64
diff --git a/output/reel-20260722-073452/poster.png b/output/reel-20260722-073452/poster.png
deleted file mode 100644
index d1f827c..0000000
Binary files a/output/reel-20260722-073452/poster.png and /dev/null differ
diff --git a/output/reel-20260722-073452/prep/base_01.png b/output/reel-20260722-073452/prep/base_01.png
deleted file mode 100644
index c91b326..0000000
Binary files a/output/reel-20260722-073452/prep/base_01.png and /dev/null differ
diff --git a/output/reel-20260722-073452/prep/base_02.png b/output/reel-20260722-073452/prep/base_02.png
deleted file mode 100644
index 016e098..0000000
Binary files a/output/reel-20260722-073452/prep/base_02.png and /dev/null differ
diff --git a/output/reel-20260722-073452/prep/base_03.png b/output/reel-20260722-073452/prep/base_03.png
deleted file mode 100644
index 69e33e0..0000000
Binary files a/output/reel-20260722-073452/prep/base_03.png and /dev/null differ
diff --git a/output/reel-20260722-073452/prep/base_04.png b/output/reel-20260722-073452/prep/base_04.png
deleted file mode 100644
index 2e62b6d..0000000
Binary files a/output/reel-20260722-073452/prep/base_04.png and /dev/null differ
diff --git a/output/reel-20260722-073452/prep/base_05.png b/output/reel-20260722-073452/prep/base_05.png
deleted file mode 100644
index f6a0ff1..0000000
Binary files a/output/reel-20260722-073452/prep/base_05.png and /dev/null differ
diff --git a/output/reel-20260722-073452/prep/base_06.png b/output/reel-20260722-073452/prep/base_06.png
deleted file mode 100644
index 87a0e88..0000000
Binary files a/output/reel-20260722-073452/prep/base_06.png and /dev/null differ
diff --git a/output/reel-20260722-073452/prep/base_07.png b/output/reel-20260722-073452/prep/base_07.png
deleted file mode 100644
index 7a47352..0000000
Binary files a/output/reel-20260722-073452/prep/base_07.png and /dev/null differ
diff --git a/output/reel-20260722-073452/prep/frame_00.png b/output/reel-20260722-073452/prep/frame_00.png
deleted file mode 100644
index a414f86..0000000
Binary files a/output/reel-20260722-073452/prep/frame_00.png and /dev/null differ
diff --git a/output/reel-20260722-073452/prep/frame_01.png b/output/reel-20260722-073452/prep/frame_01.png
deleted file mode 100644
index d1f827c..0000000
Binary files a/output/reel-20260722-073452/prep/frame_01.png and /dev/null differ
diff --git a/output/reel-20260722-073452/prep/frame_02.png b/output/reel-20260722-073452/prep/frame_02.png
deleted file mode 100644
index 2c8e061..0000000
Binary files a/output/reel-20260722-073452/prep/frame_02.png and /dev/null differ
diff --git a/output/reel-20260722-073452/prep/frame_03.png b/output/reel-20260722-073452/prep/frame_03.png
deleted file mode 100644
index 2996207..0000000
Binary files a/output/reel-20260722-073452/prep/frame_03.png and /dev/null differ
diff --git a/output/reel-20260722-073452/prep/frame_04.png b/output/reel-20260722-073452/prep/frame_04.png
deleted file mode 100644
index 812bb0e..0000000
Binary files a/output/reel-20260722-073452/prep/frame_04.png and /dev/null differ
diff --git a/output/reel-20260722-073452/prep/frame_05.png b/output/reel-20260722-073452/prep/frame_05.png
deleted file mode 100644
index 7a581e5..0000000
Binary files a/output/reel-20260722-073452/prep/frame_05.png and /dev/null differ
diff --git a/output/reel-20260722-073452/prep/frame_06.png b/output/reel-20260722-073452/prep/frame_06.png
deleted file mode 100644
index 264c2c5..0000000
Binary files a/output/reel-20260722-073452/prep/frame_06.png and /dev/null differ
diff --git a/output/reel-20260722-073452/prep/frame_07.png b/output/reel-20260722-073452/prep/frame_07.png
deleted file mode 100644
index c87c958..0000000
Binary files a/output/reel-20260722-073452/prep/frame_07.png and /dev/null differ
diff --git a/output/reel-20260722-073452/prep/frame_zz.png b/output/reel-20260722-073452/prep/frame_zz.png
deleted file mode 100644
index 49a08d0..0000000
Binary files a/output/reel-20260722-073452/prep/frame_zz.png and /dev/null differ
diff --git a/output/reel-20260722-073452/prep/scrim2_01.png b/output/reel-20260722-073452/prep/scrim2_01.png
deleted file mode 100644
index 20a362d..0000000
Binary files a/output/reel-20260722-073452/prep/scrim2_01.png and /dev/null differ
diff --git a/output/reel-20260722-073452/prep/scrim2_02.png b/output/reel-20260722-073452/prep/scrim2_02.png
deleted file mode 100644
index 94e7bec..0000000
Binary files a/output/reel-20260722-073452/prep/scrim2_02.png and /dev/null differ
diff --git a/output/reel-20260722-073452/prep/scrim2_03.png b/output/reel-20260722-073452/prep/scrim2_03.png
deleted file mode 100644
index 31677ed..0000000
Binary files a/output/reel-20260722-073452/prep/scrim2_03.png and /dev/null differ
diff --git a/output/reel-20260722-073452/prep/scrim2_04.png b/output/reel-20260722-073452/prep/scrim2_04.png
deleted file mode 100644
index 0cea1e9..0000000
Binary files a/output/reel-20260722-073452/prep/scrim2_04.png and /dev/null differ
diff --git a/output/reel-20260722-073452/prep/scrim2_05.png b/output/reel-20260722-073452/prep/scrim2_05.png
deleted file mode 100644
index f2a335e..0000000
Binary files a/output/reel-20260722-073452/prep/scrim2_05.png and /dev/null differ
diff --git a/output/reel-20260722-073452/prep/scrim2_06.png b/output/reel-20260722-073452/prep/scrim2_06.png
deleted file mode 100644
index cc12715..0000000
Binary files a/output/reel-20260722-073452/prep/scrim2_06.png and /dev/null differ
diff --git a/output/reel-20260722-073452/prep/scrim2_07.png b/output/reel-20260722-073452/prep/scrim2_07.png
deleted file mode 100644
index 9d16838..0000000
Binary files a/output/reel-20260722-073452/prep/scrim2_07.png and /dev/null differ
diff --git a/output/reel-20260722-073452/prep/scrim_01.png b/output/reel-20260722-073452/prep/scrim_01.png
deleted file mode 100644
index 1b76265..0000000
Binary files a/output/reel-20260722-073452/prep/scrim_01.png and /dev/null differ
diff --git a/output/reel-20260722-073452/prep/scrim_02.png b/output/reel-20260722-073452/prep/scrim_02.png
deleted file mode 100644
index b8d7348..0000000
Binary files a/output/reel-20260722-073452/prep/scrim_02.png and /dev/null differ
diff --git a/output/reel-20260722-073452/prep/scrim_03.png b/output/reel-20260722-073452/prep/scrim_03.png
deleted file mode 100644
index 8a3bd86..0000000
Binary files a/output/reel-20260722-073452/prep/scrim_03.png and /dev/null differ
diff --git a/output/reel-20260722-073452/prep/scrim_04.png b/output/reel-20260722-073452/prep/scrim_04.png
deleted file mode 100644
index 0b37f5e..0000000
Binary files a/output/reel-20260722-073452/prep/scrim_04.png and /dev/null differ
diff --git a/output/reel-20260722-073452/prep/scrim_05.png b/output/reel-20260722-073452/prep/scrim_05.png
deleted file mode 100644
index d2f2cf2..0000000
Binary files a/output/reel-20260722-073452/prep/scrim_05.png and /dev/null differ
diff --git a/output/reel-20260722-073452/prep/scrim_06.png b/output/reel-20260722-073452/prep/scrim_06.png
deleted file mode 100644
index 9f2bd21..0000000
Binary files a/output/reel-20260722-073452/prep/scrim_06.png and /dev/null differ
diff --git a/output/reel-20260722-073452/prep/scrim_07.png b/output/reel-20260722-073452/prep/scrim_07.png
deleted file mode 100644
index 8fefe3a..0000000
Binary files a/output/reel-20260722-073452/prep/scrim_07.png and /dev/null differ
diff --git a/output/reel-20260722-073452/prep/scrimmed_01.png b/output/reel-20260722-073452/prep/scrimmed_01.png
deleted file mode 100644
index c4c88ca..0000000
Binary files a/output/reel-20260722-073452/prep/scrimmed_01.png and /dev/null differ
diff --git a/output/reel-20260722-073452/prep/scrimmed_02.png b/output/reel-20260722-073452/prep/scrimmed_02.png
deleted file mode 100644
index 13609c5..0000000
Binary files a/output/reel-20260722-073452/prep/scrimmed_02.png and /dev/null differ
diff --git a/output/reel-20260722-073452/prep/scrimmed_03.png b/output/reel-20260722-073452/prep/scrimmed_03.png
deleted file mode 100644
index 0f868d1..0000000
Binary files a/output/reel-20260722-073452/prep/scrimmed_03.png and /dev/null differ
diff --git a/output/reel-20260722-073452/prep/scrimmed_04.png b/output/reel-20260722-073452/prep/scrimmed_04.png
deleted file mode 100644
index 4a13ed4..0000000
Binary files a/output/reel-20260722-073452/prep/scrimmed_04.png and /dev/null differ
diff --git a/output/reel-20260722-073452/prep/scrimmed_05.png b/output/reel-20260722-073452/prep/scrimmed_05.png
deleted file mode 100644
index 5d72563..0000000
Binary files a/output/reel-20260722-073452/prep/scrimmed_05.png and /dev/null differ
diff --git a/output/reel-20260722-073452/prep/scrimmed_06.png b/output/reel-20260722-073452/prep/scrimmed_06.png
deleted file mode 100644
index 4cf8999..0000000
Binary files a/output/reel-20260722-073452/prep/scrimmed_06.png and /dev/null differ
diff --git a/output/reel-20260722-073452/prep/scrimmed_07.png b/output/reel-20260722-073452/prep/scrimmed_07.png
deleted file mode 100644
index 027064c..0000000
Binary files a/output/reel-20260722-073452/prep/scrimmed_07.png and /dev/null differ
diff --git a/output/reel-20260722-073452/prep/topscrim_01.png b/output/reel-20260722-073452/prep/topscrim_01.png
deleted file mode 100644
index e5b1825..0000000
Binary files a/output/reel-20260722-073452/prep/topscrim_01.png and /dev/null differ
diff --git a/output/reel-20260722-073452/prep/topscrim_02.png b/output/reel-20260722-073452/prep/topscrim_02.png
deleted file mode 100644
index 17f7551..0000000
Binary files a/output/reel-20260722-073452/prep/topscrim_02.png and /dev/null differ
diff --git a/output/reel-20260722-073452/prep/topscrim_03.png b/output/reel-20260722-073452/prep/topscrim_03.png
deleted file mode 100644
index 6192395..0000000
Binary files a/output/reel-20260722-073452/prep/topscrim_03.png and /dev/null differ
diff --git a/output/reel-20260722-073452/prep/topscrim_04.png b/output/reel-20260722-073452/prep/topscrim_04.png
deleted file mode 100644
index 5fce68f..0000000
Binary files a/output/reel-20260722-073452/prep/topscrim_04.png and /dev/null differ
diff --git a/output/reel-20260722-073452/prep/topscrim_05.png b/output/reel-20260722-073452/prep/topscrim_05.png
deleted file mode 100644
index 5ac1b0a..0000000
Binary files a/output/reel-20260722-073452/prep/topscrim_05.png and /dev/null differ
diff --git a/output/reel-20260722-073452/prep/topscrim_06.png b/output/reel-20260722-073452/prep/topscrim_06.png
deleted file mode 100644
index 119a485..0000000
Binary files a/output/reel-20260722-073452/prep/topscrim_06.png and /dev/null differ
diff --git a/output/reel-20260722-073452/prep/topscrim_07.png b/output/reel-20260722-073452/prep/topscrim_07.png
deleted file mode 100644
index a480847..0000000
Binary files a/output/reel-20260722-073452/prep/topscrim_07.png and /dev/null differ
diff --git a/output/reel-20260722-073452/reel-9x16.mp4 b/output/reel-20260722-073452/reel-9x16.mp4
deleted file mode 100644
index bf3cf68..0000000
Binary files a/output/reel-20260722-073452/reel-9x16.mp4 and /dev/null differ
diff --git a/output/reel-20260722-073452/selection.json b/output/reel-20260722-073452/selection.json
deleted file mode 100644
index 0cbb36a..0000000
--- a/output/reel-20260722-073452/selection.json
+++ /dev/null
@@ -1,107 +0,0 @@
-[
-  {
-    "id": 7895904583731,
-    "handle": "flower-fall-pearl-wallcoverings-dwrw-361341",
-    "title": "Flower Fall, Pearl Wallcoverings | Rebel Walls",
-    "pattern": "Flower Fall",
-    "color": "Pearl",
-    "vendor": "Rebel Walls",
-    "product_type": "Mural",
-    "image": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/R19251_interior1.jpg?v=1784706015",
-    "image_hi": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/R19251_interior1_1200x.jpg?v=1784706015",
-    "price": "191.00",
-    "url": "https://designerwallcoverings.com/products/flower-fall-pearl-wallcoverings-dwrw-361341",
-    "created_at": "2026-07-22T00:40:09-07:00",
-    "published_at": "2026-07-22T01:25:47-07:00"
-  },
-  {
-    "id": 7895904518195,
-    "handle": "flower-art-green-wallcoverings-dwrw-361337",
-    "title": "Flower Art, Green Wallcoverings | Rebel Walls",
-    "pattern": "Flower Art",
-    "color": "Green",
-    "vendor": "Rebel Walls",
-    "product_type": "Mural",
-    "image": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/R20397_interior1.webp?v=1784706010",
-    "image_hi": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/R20397_interior1_1200x.webp?v=1784706010",
-    "price": "191.00",
-    "url": "https://designerwallcoverings.com/products/flower-art-green-wallcoverings-dwrw-361337",
-    "created_at": "2026-07-22T00:40:06-07:00",
-    "published_at": "2026-07-22T01:25:32-07:00"
-  },
-  {
-    "id": 7894970597427,
-    "handle": "florida-blue-wallcoverings-dwrw-361328",
-    "title": "Florida, Blue Wallcoverings | Rebel Walls",
-    "pattern": "Florida",
-    "color": "Blue",
-    "vendor": "Rebel Walls",
-    "product_type": "Mural",
-    "image": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/R20038_interior1.webp?v=1784619613",
-    "image_hi": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/R20038_interior1_1200x.webp?v=1784619613",
-    "price": "220.75",
-    "url": "https://designerwallcoverings.com/products/florida-blue-wallcoverings-dwrw-361328",
-    "created_at": "2026-07-21T00:40:10-07:00",
-    "published_at": "2026-07-21T00:40:11-07:00"
-  },
-  {
-    "id": 7893129789491,
-    "handle": "floral-opulence-red-wallcoverings-dwrw-361322",
-    "title": "Floral Opulence, Red Wallcoverings | Rebel Walls",
-    "pattern": "Floral Opulence",
-    "color": "Red",
-    "vendor": "Rebel Walls",
-    "product_type": "Mural",
-    "image": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/R21399_interior1.jpg?v=1784533215",
-    "image_hi": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/R21399_interior1_1200x.jpg?v=1784533215",
-    "price": "191.00",
-    "url": "https://designerwallcoverings.com/products/floral-opulence-red-wallcoverings-dwrw-361322",
-    "created_at": "2026-07-20T00:40:12-07:00",
-    "published_at": "2026-07-20T00:40:13-07:00"
-  },
-  {
-    "id": 7890539610163,
-    "handle": "floral-meadow-multi-wallcoverings-dwrw-361318",
-    "title": "Floral Meadow, Multi Wallcoverings | Rebel Walls",
-    "pattern": "Floral Meadow",
-    "color": "Multi",
-    "vendor": "Rebel Walls",
-    "product_type": "Mural",
-    "image": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/R22391_interior1.jpg?v=1784446816",
-    "image_hi": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/R22391_interior1_1200x.jpg?v=1784446816",
-    "price": "191.00",
-    "url": "https://designerwallcoverings.com/products/floral-meadow-multi-wallcoverings-dwrw-361318",
-    "created_at": "2026-07-19T00:40:12-07:00",
-    "published_at": "2026-07-19T00:40:13-07:00"
-  },
-  {
-    "id": 7891284426803,
-    "handle": "inverness-wide-width-teal-and-clay-dwtt-74388",
-    "title": "Inverness Wide Width, Teal and Clay Wallcoverings | Thibaut",
-    "pattern": "Inverness Wide Width",
-    "color": "Teal and Clay",
-    "vendor": "Thibaut",
-    "product_type": "Wallcovering",
-    "image": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/TWW10976_7c00ae0c-31e7-40c0-80d1-6f8416defc66.jpg?v=1784472010",
-    "image_hi": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/TWW10976_7c00ae0c-31e7-40c0-80d1-6f8416defc66_1200x.jpg?v=1784472010",
-    "price": "58.64",
-    "url": "https://designerwallcoverings.com/products/inverness-wide-width-teal-and-clay-dwtt-74388",
-    "created_at": "2026-07-19T07:40:06-07:00",
-    "published_at": "2026-07-19T07:40:09-07:00"
-  },
-  {
-    "id": 7891385188403,
-    "handle": "luta-sisal-wide-width-flax-and-metallic-gold-dwtt-74391",
-    "title": "Luta Sisal Wide Width, Flax and Metallic Gold Wallcoverings | Thibaut",
-    "pattern": "Luta Sisal Wide Width",
-    "color": "Flax and Metallic Gold",
-    "vendor": "Thibaut",
-    "product_type": "Wallcovering",
-    "image": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/TWW14524_9aaf6990-b346-4b4f-b671-cf05791d9cd3.jpg?v=1784475608",
-    "image_hi": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/TWW14524_9aaf6990-b346-4b4f-b671-cf05791d9cd3_1200x.jpg?v=1784475608",
-    "price": "58.64",
-    "url": "https://designerwallcoverings.com/products/luta-sisal-wide-width-flax-and-metallic-gold-dwtt-74391",
-    "created_at": "2026-07-19T08:40:05-07:00",
-    "published_at": "2026-07-19T08:40:08-07:00"
-  }
-]
\ No newline at end of file
diff --git a/output/reel-20260722-073452/src/01.jpg b/output/reel-20260722-073452/src/01.jpg
deleted file mode 100644
index 20a5b88..0000000
Binary files a/output/reel-20260722-073452/src/01.jpg and /dev/null differ
diff --git a/output/reel-20260722-073452/src/02.jpg b/output/reel-20260722-073452/src/02.jpg
deleted file mode 100644
index ae224be..0000000
Binary files a/output/reel-20260722-073452/src/02.jpg and /dev/null differ
diff --git a/output/reel-20260722-073452/src/03.jpg b/output/reel-20260722-073452/src/03.jpg
deleted file mode 100644
index 266497d..0000000
Binary files a/output/reel-20260722-073452/src/03.jpg and /dev/null differ
diff --git a/output/reel-20260722-073452/src/04.jpg b/output/reel-20260722-073452/src/04.jpg
deleted file mode 100644
index c5014a9..0000000
Binary files a/output/reel-20260722-073452/src/04.jpg and /dev/null differ
diff --git a/output/reel-20260722-073452/src/05.jpg b/output/reel-20260722-073452/src/05.jpg
deleted file mode 100644
index 096c3d8..0000000
Binary files a/output/reel-20260722-073452/src/05.jpg and /dev/null differ
diff --git a/output/reel-20260722-073452/src/06.jpg b/output/reel-20260722-073452/src/06.jpg
deleted file mode 100644
index 1eb3287..0000000
Binary files a/output/reel-20260722-073452/src/06.jpg and /dev/null differ
diff --git a/output/reel-20260722-073452/src/07.jpg b/output/reel-20260722-073452/src/07.jpg
deleted file mode 100644
index 704692d..0000000
Binary files a/output/reel-20260722-073452/src/07.jpg and /dev/null differ
diff --git a/output/reel-20260722-073452/urls.tsv b/output/reel-20260722-073452/urls.tsv
deleted file mode 100644
index 6013f4a..0000000
--- a/output/reel-20260722-073452/urls.tsv
+++ /dev/null
@@ -1,7 +0,0 @@
-1	https://cdn.shopify.com/s/files/1/0015/4117/7456/files/R19251_interior1_1200x.jpg?v=1784706015
-2	https://cdn.shopify.com/s/files/1/0015/4117/7456/files/R20397_interior1_1200x.webp?v=1784706010
-3	https://cdn.shopify.com/s/files/1/0015/4117/7456/files/R20038_interior1_1200x.webp?v=1784619613
-4	https://cdn.shopify.com/s/files/1/0015/4117/7456/files/R21399_interior1_1200x.jpg?v=1784533215
-5	https://cdn.shopify.com/s/files/1/0015/4117/7456/files/R22391_interior1_1200x.jpg?v=1784446816
-6	https://cdn.shopify.com/s/files/1/0015/4117/7456/files/TWW10976_7c00ae0c-31e7-40c0-80d1-6f8416defc66_1200x.jpg?v=1784472010
-7	https://cdn.shopify.com/s/files/1/0015/4117/7456/files/TWW14524_9aaf6990-b346-4b4f-b671-cf05791d9cd3_1200x.jpg?v=1784475608
diff --git a/output/reel-20260722-073452/verify_mid.png b/output/reel-20260722-073452/verify_mid.png
deleted file mode 100644
index 0d94e61..0000000
Binary files a/output/reel-20260722-073452/verify_mid.png and /dev/null differ
diff --git a/output/reel-20260722-073452/verify_out.png b/output/reel-20260722-073452/verify_out.png
deleted file mode 100644
index 6e6f59c..0000000
Binary files a/output/reel-20260722-073452/verify_out.png and /dev/null differ
diff --git a/scripts/video-runner.mjs b/scripts/video-runner.mjs
index fdc4fef..35ce4f6 100644
--- a/scripts/video-runner.mjs
+++ b/scripts/video-runner.mjs
@@ -6,10 +6,14 @@
 // Steve-gated action (see ~/.claude/yolo-queue). Unarmed (default), the runner simply
 // reports that jobs are waiting for a live studio session to run them — nothing fires.
 //
-// Each job maps to a video skill:
-//   new-arrivals-reel → reels-producer (9:16 IG reel from data/new-arrivals.json)
-//   url-reel          → product-launch-video (promo/tour reel from a URL)
-//   newsletter-video  → newsletter-to-video (.eml → promo video)
+// Each job maps to a renderer:
+//   new-arrivals-reel (9:16) → scripts/build-reel.mjs   ($0 local, no API/arming,
+//                              IG-publishable silent-AAC track, writes to reels/)
+//   new-arrivals-reel (1:1)  → reels-producer skill (claude -p, square)
+//   url-reel                 → product-launch-video skill (promo/tour reel from a URL)
+//   newsletter-video         → newsletter-to-video skill (.eml → promo video)
+// Only the claude -p (skill) paths need VIDEO_AUTORUN=1 + CLAUDE_BIN; the local
+// build-reel.mjs path renders even unarmed (same $0 script the /api/build button uses).
 import { readFile, writeFile } from 'node:fs/promises';
 import { existsSync } from 'node:fs';
 import { spawn } from 'node:child_process';
@@ -46,6 +50,23 @@ function runClaude(CLAUDE_BIN, pr) {
   });
 }
 
+// A catalog reel in 9:16 is rendered by the deterministic local builder — no agent.
+const isLocalReel = j => j.kind === 'new-arrivals-reel' && j.format !== '1:1';
+
+// Deterministic $0 render via scripts/build-reel.mjs (portrait New Arrivals reel with an
+// IG-publishable audio track, written to reels/). No claude, no API, no arming needed.
+function runLocalBuild() {
+  return new Promise(resolve => {
+    let err = '';
+    const env = { ...process.env, PATH: `/opt/homebrew/bin:/usr/local/bin:${process.env.PATH || ''}`,
+      HYPERFRAMES_SKIP_SKILLS: '1', REEL_COUNT: String(process.env.REEL_COUNT || 6) };
+    const ch = spawn(process.execPath, [join(ROOT, 'scripts', 'build-reel.mjs')], { cwd: ROOT, env, stdio: ['ignore', 'inherit', 'pipe'] });
+    ch.stderr.on('data', d => { err += d; process.stderr.write(d); });
+    ch.on('close', code => resolve({ ok: code === 0, code, err }));
+    ch.on('error', e => resolve({ ok: false, code: -1, err: String(e.message) }));
+  });
+}
+
 const prompt = (j) => {
   const fmt = j.format === '1:1' ? '1:1 (1080×1080)' : '9:16 Instagram Reels (1080×1920)';
   if (j.kind === 'new-arrivals-reel')
@@ -61,25 +82,26 @@ const prompt = (j) => {
   const jobs = existsSync(JOBS) ? JSON.parse(await readFile(JOBS, 'utf8')) : [];
   const queued = jobs.filter(j => j.status === 'queued');
   if (!queued.length) { console.log('video-runner: no queued jobs.'); return; }
-  if (!ARMED) {
-    console.log(`video-runner: ${queued.length} job(s) queued, waiting for a live studio session (VIDEO_AUTORUN not set).`);
-    return; // GATE: never auto-runs claude unarmed.
-  }
-  // `claude` is a shell function on this host, NOT a binary — spawn() can't run it.
-  // Require an explicit binary path and fail LOUD (not a silent detached ENOENT).
+
+  // Agent (claude -p) jobs are gated behind VIDEO_AUTORUN=1 + a real CLAUDE_BIN.
+  // Local build-reel.mjs jobs (9:16 catalog reels) are $0 and always run.
   const CLAUDE_BIN = process.env.CLAUDE_BIN || '';
-  if (!CLAUDE_BIN || !existsSync(CLAUDE_BIN)) {
-    console.error('video-runner: VIDEO_AUTORUN=1 but CLAUDE_BIN is unset / not a real file. Set CLAUDE_BIN=/abs/path/to/claude to enable autonomous rendering; jobs left queued.');
-    process.exit(1);
-  }
-  for (const j of queued) {
-    const pr = prompt(j); if (!pr) { j.status = 'error'; j.note = 'unknown kind'; continue; }
-    j.status = 'running'; j.started_at = new Date().toISOString();
+  const canAgent = ARMED && CLAUDE_BIN && existsSync(CLAUDE_BIN);
+  const agentWaiting = queued.filter(j => !isLocalReel(j));
+  if (agentWaiting.length && !canAgent)
+    console.log(`video-runner: ${agentWaiting.length} agent job(s) waiting for a live/armed studio session (VIDEO_AUTORUN + CLAUDE_BIN).`);
+  const toRun = queued.filter(j => isLocalReel(j) || canAgent);
+  if (!toRun.length) { console.log('video-runner: nothing runnable right now.'); return; }
+
+  for (const j of toRun) {
+    const local = isLocalReel(j);
+    if (!local) { const pr0 = prompt(j); if (!pr0) { j.status = 'error'; j.note = 'unknown kind'; await writeFile(JOBS, JSON.stringify(jobs, null, 2)); continue; } }
+    j.status = 'running'; j.started_at = new Date().toISOString(); j.renderer = local ? 'build-reel' : 'claude-skill';
     await writeFile(JOBS, JSON.stringify(jobs, null, 2));
     let res, tries = 0;
     for (;;) {
       tries++;
-      res = await runClaude(CLAUDE_BIN, pr);
+      res = local ? await runLocalBuild() : await runClaude(CLAUDE_BIN, prompt(j));
       j.attempts = tries;
       if (res.ok) break;
       // Only retry self-healing API throttles — a real skill/code failure fails fast.
@@ -96,7 +118,7 @@ const prompt = (j) => {
     if (res.ok) { j.status = 'done'; j.finished_at = new Date().toISOString(); delete j.note; }
     else {
       j.status = 'error'; j.finished_at = new Date().toISOString();
-      j.note = (isTransient(res.err) ? `rate-limited after ${tries} tr${tries===1?'y':'ies'} — safe to requeue` : ('claude exit ' + res.code))
+      j.note = (isTransient(res.err) ? `rate-limited after ${tries} tr${tries===1?'y':'ies'} — safe to requeue` : (`${local ? 'build-reel' : 'claude'} exit ${res.code}`))
         + (res.err ? (' · ' + res.err.replace(/\s+/g, ' ').trim().slice(0, 140)) : '');
     }
     await writeFile(JOBS, JSON.stringify(jobs, null, 2));
diff --git a/server.js b/server.js
index 1bb0408..b7e8dcd 100644
--- a/server.js
+++ b/server.js
@@ -249,6 +249,7 @@ const server = http.createServer(async (req, res) => {
       }
       await walk(join(ROOT, 'videos'), 'videos');
       await walk(join(ROOT, 'reels'), 'reels');
+      await walk(join(ROOT, 'output'), 'output');   // HyperFrames skill renders (url/newsletter) land here
       out.sort((a, b) => b.mtime - a.mtime);
       return res.writeHead(200, { 'content-type': 'application/json' }).end(JSON.stringify(out));
     }
@@ -259,7 +260,7 @@ const server = http.createServer(async (req, res) => {
       // realpathSync dereferences symlinks (normalize does NOT) — a .mp4 symlink
       // pointing at server.js/secrets must not escape videos/ or reels/.
       try { fp = realpathSync(fp); } catch { return res.writeHead(404).end('not found'); }
-      const okRoot = fp.startsWith(join(ROOT, 'videos') + sep) || fp.startsWith(join(ROOT, 'reels') + sep);
+      const okRoot = fp.startsWith(join(ROOT, 'videos') + sep) || fp.startsWith(join(ROOT, 'reels') + sep) || fp.startsWith(join(ROOT, 'output') + sep);
       if (okRoot && extname(fp) === '.mp4' && existsSync(fp)) return await serveFile(fp, req, res);
       return res.writeHead(404).end('not found');
     }

← f9fdb8f auto-save: 2026-07-22T07:42:20 (3 files) — data/video-jobs.j  ·  back to Dw Marketing Reels  ·  chore: lint, refactor, v0.8.0 (session close) ce006d6 →