1仕組みと動作原理
第一の鉄則:数値こそが情報である――価格と割引率は必ず導出計算し、二重入力は絶対にしない。
1. **The number is the message — keep it exact.** Prices and percentages must be computed, never typed twice. Derive `now` and the `% off` from `was` and the discount so they can't disagree.
真のソースのみを保存:now と pct は永続化せず、was/discount から必ず派生させることで自己矛盾を構造的に排除する。
One object per product. `now` and `pct` are NOT stored — they are derived, so they can never contradict `was` and `discount`. Store only the source of truth.
取り消し線が先、値下げ表示は後――順序を間違えるとバグとして認識される。
Order matters: strike the old price *first*, then drop the new one. Striking and counting at the same instant reads as a glitch.
金額はセント(または最小通貨単位)に四捨五入してからフォーマットし、Intl を使用してレンダリングする。
Compute every displayed figure from the offer in one place. Round to cents *before* formatting so the price a viewer reads is exactly the price they pay.
一括生成前に 1 行ずつ検証し、不正行で即座に失敗(fail loud)させる:不正行のサイレントレンダリングこそが「470 ドルが 47 ドルになる」元凶である。
A batch that silently renders a bad row is how a $470 item ships labelled "$47". Validate every offer before rendering and stop the run on the first bad one.
緊急性は本物でなければならない:明確な期限またはリアルなカウントダウンを提示し、偽タイマーは絶対に使わない。
Urgency lifts conversion only when it's real. Show a concrete deadline ("Ends Sun 11:59pm") or a live-looking countdown — never a fake timer.
1 フレームにつき 1 つのフォーカス:1 つの割引、1 つのコード、1 つの CTA。2 つのオファーがある場合は 2 本の動画を制作する。
2. **One offer, one focus per frame.** A single discount, a single code, a single CTA. Competing offers kill the urgency. If there are two deals, make two videos.
価格帯に応じて表現方法を選択:高価格帯にはパーセンテージ、低価格帯には固定金額を使用する。
Choose the framing by price: percentage for higher-priced items ("40% OFF" beats "$32 off"), a flat amount for low-priced items ("$5 off" beats "8% off").
検証ループの重点は「金額が正しいか、画像が存在するか」:数値の正確性と商品画像の読み込みは妥協を許さない。
This is heavy-tier: the deliverable is an MP4, not a live page. A promo carries exact money — the verify pass is mostly *are the numbers right and is the product image actually there*.
2コア機能
3外部依存関係
| Kind | Dependency / Endpoint |
|---|---|
| package | remotion(frame/spring/interpolate/Img/Sequence) |
| cli | node fs(校验时检查图片是否存在) |
| package | Intl.NumberFormat / Intl.DateTimeFormat(运行时内置,无第三方包) |
| cli | npx remotion render(逐 offer 渲染) |
| cli | ffmpeg / ffprobe(仓库共享 contact-sheet.sh、probe-mp4.sh) |
| network | iart.ai(reference 尾部推广链接,具 utm 参数;仅读者点击才请求) |
4推奨統制措置と判定根拠
- scripts/: ローカルスクリプトまたはコマンドの実行: skill ディレクトリ内に独自スクリプトは含みません。SKILL.md/reference が agent に対し `node`(ドキュメント内にソースを含む検証・派生処理)および `npx re` の実行を指示します。
- 网络端点: 外部ネットワークアクセスの宣言: 実行時にビジネス用 API 呼び出しは行いません。価格および商品データはローカルの offers/*.json から、商品画像はローカルの public/ ディレクトリから取得します。
- 本地工作目录: ファイルの読み書きと成果物の出力: ユーザープロジェクト内の `out/<name>.mp4`(オファーごとに 1 本)、`out/f-discount.png`、`out/f-price.png` に出力します。
5二重目の独立検証
- [discrepancy] 「全部为帧纯函数、禁用 Date.now()」与 reference 实现是否一致 — SKILL.md 明确要求所有动画值为 useCurrentFrame() 纯函数(no CSS transitions, timers, Date.now()),其自身示例也写「derived from a deadline prop (deterministic per frame)」;但 promo-components.md 的 Countdown 实际用 `const remaining = Math.max(0, (end - Date.now()) / 1000 - frame / fps);`——引入挂钟时间,同一 props 在不同时刻渲染会得到不同剩余时间,不可复现。
- [discrepancy] 组件 import 名是否可运行 — promo-components.md 的 composition 写成 AbstractFill(Remotion 并无该导出,应为 AbsoluteFill),且使用处同样写 <AbstractFill …>;照抄会直接 import 失败。
- [ok] 折扣公式是否两处一致 — SKILL.md 的 Math.round((1 - now / was) * 100) 与 priceModel 的 Math.round((1 - now / o.was) * 100) 同式同精度。
- [ok] 校验清单是否与 SKILL.md 文字一致 — validateOffer 覆盖 now≥0 且 <was、discount∈(0,1)(当 now 未给)、code 非空、deadline 可解析、图片存在;与 SKILL.md 的 now < was / code present / image exists 一致且更强。
- [ok] 节拍/时长的一致性 — SKILL.md 的 10s 节拍表与 reference 的帧调度(hook 0 / badge 27 / price 60 / code 210 / cta 270)及 --frames=0-299(10s@30fps)一致。
- [ok] 「先划线后落价」顺序的实现 — PriceDrop 的 strike 从 f=0 起(0–12 帧画完),新价 spring 从 f-8 起跑;与 SKILL.md 的 strike the old price first 时序意图一致。
6結論
0c3e003bf0617b1c…