案例:
20260727-graphic-tee-couple(âme soeur 早餐狗狗短版 BOXY 短袖 tee,情侶穿搭 20 秒)執行日:2026-07-27 15:30 至 20:35
這份是一次性的量測報告,不是規則正本。功能開發維持凍結,本報告不包含任何程式修改。
這支片真的剪出來了,但不是靠這套流程剪出來的。
治理層(reels/,8,851 行)在 1 小時 47 分內產生了 13 筆 event、7 個 gate 判定、9 個 FAIL。 真正把畫面做出來的那 3 小時,它一筆紀錄都沒有,因為剪片層根本不會通知它。
而且治理層檢查的字幕是一份手維護的鏡像檔(9 條),實際出貨的草稿有 17 條。 有 8 條字幕從頭到尾沒有任何一關看過。
時間來源:events.json 的 at 欄位、檔案 mtime。標「無 event」的是 timeline 撈不到、我手記的。
| # | 步驟 | 實際區間 | 耗時 | 開拍前預測 | 差距 |
|---|---|---|---|---|---|
| 1 | knowledge snapshot | 15:30–15:36 | 6 分 | 5 分 | 準 |
| 2 | case create | 15:36–15:37 | <1 分 | 1 分 | 準 |
| 3 | 腳本(Codex 7 輪)+ script-qa 跑 6 次 | 15:39–16:20 | 41 分 | 2 分 | 低估 20 倍 |
| 4 | 等 Kim 定稿 | 16:20–17:00 | 40 分 | 依 Kim | 不計 |
| 5 | script lock | 17:00–17:01 | <1 分 | 1 分 | 準 |
| 6 | inventory 掃描 + 手補欄位 | 17:01–17:04 | 4 分 | 30 分以上 | 高估 8 倍 |
| 7 | dialogue-qa | 17:04 | <1 分 | 2 分 | 準 |
| 8 | 寫 build driver + 產草稿 | 17:04–17:23 | 19 分 | 10 分 | 低估 2 倍 |
| 9 | capcut-preflight 跑 2 次 | 17:16、17:24 | 8 分 | 2 分 | 低估 4 倍 |
| 10 | 草稿 metadata 汙染診斷與修復 | 截圖後約 40 分 | 40 分 | 沒預測到 | 新成本 |
| 11 | 預覽 MP4 render(含字級重做一次) | 至 20:35 | 約 60 分 | 不在流程裡 | 新成本 |
| 12 | playback record | 未執行 | — | 10 分 | 我不能代 Kim 做 |
| 13 | deliver confirm | 未執行(G3 擋) | — | 1 分 | 如預測 |
總計約 5 小時。 其中:
我開拍前寫死的預測是「最大成本是手改 inventory.json(30 分以上)」。錯了,只花 4 分鐘。
真正的成本在三個地方,兩個我完全沒預測到:
shutil.copytree 把 BOXY 母版的 draft_id、封面、雲端同步欄位整包帶過來,CapCut 清單顯示成 BOXY 的縮圖、347.5M、00:22。只列「這次真的擋到事情或改變了結果」的,其他一律不算。
| 步驟 | 擋到什麼 | 證據 |
|---|---|---|
| G1 素材盤點 | 逼我把 8 支素材全部看完 | 第一版腳本顏色斷點(深灰 → 白 → 兩白)就是因為我只看了前 3 支就寫。補看完才修好 |
| Kim 定稿(script lock) | 唯一擋得住「方向錯」的關卡 | 我的 hook 被你否決兩次,最後結構(第一套黑藍配同色系配件、第二套同色白)是你定的,不是我想的 |
G4 禁止寫死 \n | 真的擋到 hook 字幕 | hook 字串裡有硬換行,被退件後才改成兩張字卡 |
| G8 同一份草稿 | 防 V2 V3 V4 增生 | 同目錄下 soulmate_tee 有 v2 到 v16 共 15 個檔,就是沒有這條的下場 |
| Codex 主腦(不是 gate,但在流程裡) | hook 從我寫的爛版本改成能用的 | 7 輪 brief/reply 都在 case 資料夾留檔可查 |
共 5 項。注意其中 G1 的價值來自「人真的看了素材」,不是來自 inventory.json 有沒有填滿欄位。
| 步驟 | 成本 | 為什麼是純開銷 |
|---|---|---|
| script-qa 跑 6 次 | 每次要開檔、跑、讀 | 6 次裡 5 次 verdict=PASS,只有 1 次抓到東西(4 件,全部是禁用詞)。禁用詞用 style_lint.py 一行就檢查得到,不需要 case 系統 |
| knowledge-snapshot.md(24KB) | 6 分鐘產出 | 產出之後沒有任何一步讀它。 Codex 是我另外手寫 brief 餵的,不是餵這份。最典型的「產生了但沒人讀」 |
| dialogue-qa | 跑一次,產 43 個欄位 | 這支沒口白。12 項機械檢查對著空資料跑,產出 19 個 null 欄位(44%),FAIL 的理由是「沒有逐字稿」,不是片子有問題 |
| events.json 的 hash chain | 每筆 event 算 prevSha256、驗 seq 連續 | 這次一次都沒用到,也沒有任何情境需要驗證它。防的是「有人偷改歷史」,但這個 repo 只有我跟 Codex 在寫 |
| case.revision / revisionHash | 每次存檔重算 | 從 rev 1 bump 到 rev 11,沒有任何一個決策用到這個數字 |
| capcut-preflight 的 9 個 FAIL | 跑 2 次、讀 2 次 | 見第 4、5 節:大部分不是真問題 |
這支片沒有口白(實測音軌 mean −37 至 −46 dB,Kim 確認「沒口白」)。
| Gate | 這次狀態 | 應該是什麼 | 問題 |
|---|---|---|---|
| G2 純口白剪輯 | FAIL | NOT_APPLICABLE | FAIL 的理由是「沒有逐字稿」。一支沒有人講話的片,本來就不會有逐字稿。工具沒有「不適用」這個狀態,只能一路 FAIL |
| G7 逐句音量 | NOT_RUN、metric = none | NOT_APPLICABLE | NOT_RUN 讀起來像「還沒跑」,會讓人以為之後要補跑。實際上永遠不會跑 |
| G5 字幕可見時間同步 | NOT_IMPLEMENTED | 維持 | 三個欄位全 null。這個狀態設計是對的(不會被誤讀成通過),但它從第一天就沒實作,佔著 schema 位置 |
| G3 母版 | FAIL | 見下 | 設計本身是錯的 |
實測數字:
generatorPath : ame-soeur-video/build_graphic_tee_couple.py
generatorSha256 : 0561362617ed2931...
registrySha256 : f38a271e7b9b9b8d...
hashMatches : False
G3 的判準是「生成器的 sha256 要等於母版 registry 裡的 sha256」。
但每支新片本來就是一支新的 Python 檔,hash 永遠不可能對得上。 唯一能過的方式是「跑母版本人那支檔案」,那等於每支片都剪出一模一樣的內容。
這在開拍前的 PILOT.md §2 就預測到了,實測完全吻合。G3 用 hash 保護的是「檔案沒被改過」,但要保護的其實是「風格沒有每支重新發明」。這兩件事無關。
| 檔案 | 總欄位 | 空/null/空陣列 | 佔比 |
|---|---|---|---|
case.json | 88 | 21 | 24% |
dialogueqa.json | 43 | 19 | 44% |
scriptqa.json | 27 | 4 | 15% |
inventory.json | 15 | 0 | 0% |
case.json 空的 21 欄:historicalAcceptance、deliverableUnderCurrentRules、blockedReason、media.archivedMedia、edit.voiceWavRef、musicRef.artist、G3/G4/G6/G7/G8 的 findingCount、G5 與 G9 的 ranAt/summary/findingCount、artifacts.playback、delivery.confirmedBy、delivery.confirmedAt、kimFeedback[]。
dialogueqa.json 整段全空的:semanticReview(4 欄)、humanConfirmation(4 欄)。
inventory.json 是唯一 0 空欄的檔案,也是唯一真的改變了結果的檔案(見第 2 節)。這個對比本身就是答案。
captions.json(治理層讀的) : 9 條
draft_content.json(真的出貨的): 17 條
G4 和 G6 檢查的是我手維護的一份鏡像檔。實際草稿裡有 8 條字幕,從頭到尾沒有任何一關看過。
這代表:gate 可以判 PASS,而真正會播出去的東西是錯的。 這不是這支片的意外,是架構問題,因為治理層從設計上就不讀草稿本身,只讀人手填的 JSON。
同樣的問題也在 G3:generatorPath 只是一個字串欄位,填錯了工具不會知道。
"情侶裝一定要" px 1053.4 / 安全寬 864.0 size 19
"看兩套穿法" px 876.1 / 安全寬 864.0 size 19
"也不用穿成同一套" px 1037.3 / 安全寬 864.0 size 14
"傳給想一起穿" px 942.5 / 安全寬 864.0 size 17
這 4 條我在預覽影片裡都親眼確認過,畫面上完全在安全區內,一條都沒有超出。
根因:PX_PER_SIZE = 8.8 這個係數是拿 Staatliches(拉丁字型)量出來的,套到中文差了將近兩倍。 誤判率 4/4 = 100%。
| # | 工具 | 規模 | 輸入 | 輸出 | 斷在哪 |
|---|---|---|---|---|---|
| 1 | autocut.py + config.py + 4 個 .bat | 481 行 | input/ 的 MP4 + mp3 | output/talk_草稿.mp4 等 MP4 | 產 MP4 不產 CapCut 草稿,沒有品牌字幕美術。這支片完全沒用到 |
| 2 | capcut_draft.py(617 行)+ 34 支一次性 driver | 目錄合計 8,247 行 | driver 裡手寫的 plan + captions + 一份現成草稿當模子 | CapCut 草稿資料夾 | shutil.copytree 把來源草稿的身分整包帶過來(draft_id、tm_duration、封面、雲端欄位)。每支片要新寫一支 Python |
| 3 | reels/ 治理層 | 8,851 行 | 手填的 case.json/inventory.json/captions.json | 判定 + timeline | 完全不讀草稿本身。所有輸入都是人手填的字串 |
| 4 | Codex | 不在此 repo | 自己的路徑 | CapCut 草稿 | 不經過上面任何一套,不寫 timeline |
斷點 A:治理層 ↔ 草稿(最嚴重) 治理層看的是鏡像檔,不是實體。字幕 9 對 17。generatorPath 是字串。 後果:gate 判 PASS 不代表出貨的東西是對的。
斷點 B:剪片層 ↔ 治理層 build_graphic_tee_couple.py 跑完產出草稿,不寫任何 event。 後果:timeline 對「真正在剪片的那 3 小時」完全是盲的,記錄的全是治理動作本身。
斷點 C:人 ↔ 人 Codex 跟我用同一批素材、同一個 Drive 資料夾,彼此看不見對方在做什麼。 後果:同一支片被剪了兩次,直到你貼出 CapCut 截圖才發現。 這是這次最貴的一筆浪費,而整套流程沒有任何一關會問「這支片是不是已經有人剪過了」。
這份清單是提案,等你核准後才動手。封存一律用
git mv移到_archive/,不刪檔。
| 項目 | 理由 |
|---|---|
HARD-GATES.md 的事故紀錄段 | 7/23 那次返工的真實紀錄,是所有規則的由來,不能丟 |
| G1 素材必須全部看過 | 這次唯一真的改變結果的檢查 |
| G8 只更新同一份草稿 | 34 支 driver、15 個版本檔就是沒有這條的下場 |
G4 禁止寫死 \n | 真的擋到了 |
| G9 人工實播的原則 | 我這次就是跳過它才報錯(讀 JSON 就說剪好了) |
| Kim 定稿是硬 gate | 唯一擋得住方向錯的關卡 |
protected-drafts.json | 保護清單機制本身有效,成本近乎零 |
| 項目 | 現在 | 改成 |
|---|---|---|
| G9 實播 | session binding + 4 種 hash + 7 項旗標 + attestation ≥8 字 + STALE/VALID/UNVERIFIABLE 三態 | 一行:誰、何時、看的是哪一版(草稿 hash)、結果 |
| G3 母版 | 比對生成器 .py 的 sha256 | 改成 style preset JSON(字型/顏色/字級級距/安全區/描邊),比對「用了哪個 preset」而不是「檔案 hash」 |
| G6 字寬 | PX_PER_SIZE = 8.8(拉丁字型係數) | 中文重新量測校正,或直接拿 PIL 實測字寬 |
| G2/G7 | 沒口白就 FAIL | 由 edit.json 的 hasVoiceover 決定,false 就標 NOT_APPLICABLE |
| 字幕檢查對象 | 手維護的 captions.json | 直接讀 draft_content.json,鏡像檔取消 |
| script-qa | 獨立子系統,6 次跑出 5 次 PASS | 收成 style_lint.py 的一層薄包裝 |
_archive/,不刪)| 項目 | 行數 | 理由 |
|---|---|---|
ame-soeur-video/capcut_soulmate_tee_*.py(16 支,v2 至 v16) | 1,515 | 同一支片的 16 個版本,全部被 v16 取代 |
ame-soeur-video/capcut_dragonboat_*.py(7 支) | 1,994 | 端午專案已結束 |
ame-soeur-video/capcut_layered_shirt_ai_case_*.py(v1、v2) | 1,330 | 已被 _original_project_fix_20260723.py 取代(後者是 APPROVED 母版,保留) |
reelkit/events.py 的 hash chain 部分 | 164 | 機制本身可保留,chain 驗證這次零使用 |
reelkit/dialogue.py | 502 | 有口白的片才需要,先封存不刪 |
reelkit/scriptqa.py + schema/scriptqa.json | 423 | 簡化為 lint 包裝後,原件封存 |
| 項目 | 理由 |
|---|---|
captions.json 這個鏡像檔的設計 | 造成 9 對 17 的落差,是架構錯誤不是資料錯誤 |
case.json 的 revisionHash / revision bump | 從 1 跑到 11,零決策使用 |
case.json 那 21 個空欄位中的 14 個 | 保留 delivery.confirmedBy/At、kimFeedback、G 系列 ranAt(新流程還會用),其餘刪 |
dialogueqa.json 的 semanticReview / humanConfirmation 兩整段 | 8 欄全空 |
knowledge-snapshot.md 這一步 | 產 24KB 沒人讀。知識庫要用就直接打 /api/kb?view=prompt,不用先落檔 |
| G5 | 從第一天到現在 NOT_IMPLEMENTED,佔 schema 位置 |
預估規模變化:
| 現在 | 收斂後 | |
|---|---|---|
治理層 reels/ | 8,851 行 | 約 600 至 800 行 |
剪片層 ame-soeur-video/ | 8,247 行(含 34 支一次性 driver) | 封存 4,839 行;主線剩 capcut_draft.py 617 行 + 通用 build.py |
| 每支新片要寫的程式 | 一支 Python(81 至 300 行) | 0 行(只寫一份 edit.json) |
方向照你定的:
素材 → edit.json → 通用 build.py → CapCut 草稿 → 三項必要機械檢查 → 人工實播 → 可交付
edit.json(每支片唯一的一份檔案)取代現在的:一支 Python driver + case.json + inventory.json + captions.json。
{
"id": "20260727-graphic-tee-couple",
"title": "âme soeur|圖T情侶穿搭|兩套版",
"author": "claude", // 只允許一個剪輯作者,開檔即登記
"hasVoiceover": false, // false 時口白相關檢查一律 NOT_APPLICABLE
"preset": "layered-shirt-v2", // 風格母版,指向 presets/*.json
"music": "haul-upbeat.mp3",
"sources": [ // G1:每支素材必須有 reviewed 與 verdict
{"file": "1781668634150.MP4", "reviewed": true, "verdict": "adopt", "note": "深灰+深藍 BREAKFAST tee"}
],
"timeline": [ // 每段用哪支、切哪一段、放多久
{"src": "1781668634150.MP4", "in": 1.2, "dur": 1.5}
],
"captions": [ // 唯一一份字幕來源,build 與檢查都讀這份
{"text": "情侶裝一定要", "at": 0.0, "dur": 0.75, "size": 19, "y": 0.72}
]
}
關鍵差別:captions 只有這一份。 build.py 拿它產草稿,檢查也拿它跟草稿實體對照。不會再有 9 對 17。
build.py(一支,取代 34 支 driver)python reels/build.py cases/<id>/edit.json
draft_id、正確 tm_duration、重算 size、清空雲端欄位、用自己的畫面產封面(這次踩到的坑直接內建成步驟,不是靠人記得)draft_content.json,不讀鏡像)| # | 檢查 | 為什麼是這三項 |
|---|---|---|
| 1 | 字幕缺字(cmap) + 沒有寫死換行 + 字寬在安全區(中文係數校正後) | 機器判得準、人肉檢查很煩、出事就是整支重做 |
| 2 | 草稿身分乾淨:draft_id 不與其他草稿重複、封面是自己的、tm_duration 對得上實際長度 | 這次花了 40 分鐘的坑,而且從 CapCut 清單上一眼就會被誤判成別支片 |
| 3 | 這支片還沒有別人在做:掃 CapCut 草稿目錄找同族名稱、比對 edit.json 的 author | 補上斷點 C。同一支片被剪兩次是這次最貴的浪費 |
沒口白的片,口白相關檢查一律回 NOT_APPLICABLE,不是 FAIL、不是 NOT_RUN。
python reels/build.py playback <id> --by Kim --result ok --note "整支看過,字幕都在安全區"
寫下四件事:誰、何時、看的是哪一版(草稿 draft_content.json 的 sha256)、結果。 草稿變了就自動失效,但不需要 session binding 那一整套四種 hash。
三項機械檢查全過 + 有一筆對得上目前版本的實播紀錄 = 可交付。
誠實列出來,不假裝報告是完整的:
autocut.py 那套(481 行、4 個 .bat)這次完全沒碰,不知道它現在還能不能跑、值不值得留。要另外測。它是唯一「丟檔案進去就有東西出來」的介面,可能反而是新流程該學的對象。git mv 到 _archive/,可完全回復)在你回覆之前,我不動任何程式。
量測人:Claude|2026-07-27|資料來源:cases/20260727-graphic-tee-couple/events.json、preflight.json、檔案 mtime、CapCut 草稿實體