# SKG 营销内容生产平台

## 启动
- 后台启动（不弹 Terminal）：`./scripts/start-dev-background.sh`（通过 macOS launchd 后台托管；前端 4290 + 后端 4291，日志写入 `.logs/`）
- 后台停止：`./scripts/stop-dev-background.sh`
- 前端 dev：`cd web && npm run dev`（Next.js 16，端口 4290）
- 画布 dev：`cd web && npm run dev:canvas`（Vue / Vite，端口 4292；生产构建会作为根域名工作台输出）
- 后端 dev：`cd api && uvicorn main:app --host 127.0.0.1 --port 4291`（FastAPI，端口 4291，重任务用）
- 注意：后端不要带 `--reload` 跑长下载 / 抽帧 / 音频任务；reload 会等待后台任务结束，导致 4291 端口占用但新请求卡住。

## 立项决策快索引
- 详见 `CLAUDE.md` 立项决策段 + `.memory/plan.md` 七步管线拆解
- 风格：`04-Dark-Gallery-Ambient`（路径：`~/Projects/research/20260305-网页风格库/04-Dark-Gallery-Ambient.md`）
- 第一冲刺：步骤 1-4（下载 / 拆轨 / 关键帧 / ASR+翻译）
- 当前产品方向（2026-05-25 三模式 + logo-only 根域名画布版）：默认首页彻底从“信息流广告复刻管线”切换为多人通用的 SKG 营销内容生产平台入口，服务公司内部成员同时使用。`https://marketing.skg.com` 登录后直接进入个人生成画布，`/canvas/` 只作为旧链接兼容跳转到根域名。终端可见品牌位只放 SKG logo，不再把“生图生视频”“SKG 生成画布”或长系统名放在主界面上。首页和画布底部输入框都只保留三个用户能直接理解的入口：文生图、文生视频、图生视频；不再把“首帧生视频 / 首尾帧生视频”这类模型实现概念作为主入口。图生视频只显示“上传图片”，内部仍用后端 first_image 能力提交。用户选择生成方式、必要时上传图片、手写提示词并点击生成；图片模式显示尺寸选择，视频模式显示画幅和真实可用时长选择。后端 `/health` 向前端返回可选图片 / 视频模型、图片尺寸、视频画幅和视频时长，首页允许用户选择图片模型（自动、GPT Image 2、Gemini 图片兜底）和视频模型（Seedance、Kling、Veo 3 等别名；实际可用模型以环境变量映射为准）。当前 Doubao / Seedance 生产链路单条视频最长按 15 秒暴露，不在 UI 显示 30 秒；如后续要 30 秒，需要改成多段生成后合成。用户登录后仍只看到自己的任务、结果和详情页，继续沿用后端 owner 隔离；生成调用本项目后端 `/api`，每个浏览器的画布项目先保存在本地 localStorage，图片/视频资产仍按登录用户写入后端 job。旧 TK 复刻工作台、Agent Cut 一键出片和营销图文方案保留为高级/详情页能力，不再作为默认首页入口或默认理解框架。

## 部署事实
- 平台：VPS `76.13.31.179`（Ubuntu 24.04 / Docker Compose / Coolify Traefik）
- Agent Cut 独立预览服务器：`2.24.28.41`（Ubuntu 24.04 / Docker Compose / 裸端口 `4290`），部署目录 `/opt/skg-marketing-studio`，Compose 入口 `docker-compose.standalone.yml`，访问地址 `http://2.24.28.41:4290/agent/`。该入口用于“一分钟二创出片终端”预览：用户只提交 TikTok 链接和产品图，后端 `AgentRun` 状态机负责下载、抽帧、规划、生成、自动重跑、审片和合成。
- Agent Cut 独立预览验证（2026-05-21）：已在 `2.24.28.41` 的 `/opt/skg-marketing-studio` 用 `docker-compose.standalone.yml` 启动 `skg-agent-api` / `skg-agent-web`；独立 compose 通过网络别名兼容 Nginx 的 `skg-marketing-api` upstream。该裸 IP HTTP 入口的服务器 `deploy/.env.production` 需要 `WEB_AUTH_COOKIE_SECURE=false`；本次已补齐 `WEB_AUTH_*` 后重启验证通过：未登录 `/agent/` 返回 302 到 `/login/`，登录后 `/agent/` 返回 200，`/api/agent-runs` 返回数组，容器内 `/health` 返回 `ok:true` 且 `auth_configured:true`。
- 最近部署验证（2026-05-25）：`2a1ceee` 已通过 `./scripts/deploy-prod-safe.sh` 部署到 `/opt/skg-marketing-studio`，可见品牌位从文字命名收敛为 logo-only：首页、登录页和画布首页只显示 SKG logo，网页 title 和画布 title 为 `SKG`，首页入口按钮文案为“画布”。部署前脚本已备份生产私有环境、任务数据、资源库和 secrets 到 `/opt/skg-marketing-studio-backups/skg-marketing-preserve-20260525092749.tgz`；生产 Docker 重建后脚本内验证通过（web/API 容器 Up、`/` 302、`/login/` 200、缺失 `_next` 资源 404、未登录 `/api/health` 401、容器内 `api:health ok`、`api:ytdlp_cookie_args []`、未发现本地 API/dev URL 泄漏）。容器内静态产物复验：`index.html` 包含 `<title>SKG</title>` 和 `/skg-logo-black.svg`，首页入口包含“画布”，登录页只保留 logo；当前 `_next` 与 `/canvas` 产物未再命中 `SKG 生图生视频`、`SKG 生成画布`、`营销内容生产平台` 或 `内容生产画布` 等旧可见文案。
- 发布状态：已部署并验证（2026-05-20，主体元素按套图文件夹分组展示，主体生成接口提交后立即返回 queued 占位并后台逐视角生成、逐张回填；工作台外层取消 1800x1000 固定画布和应用层 `zoom` 缩放，改为正常流式桌面容器，最低操作宽度 1280px；源视频工作区主体链路为上方竖向参考帧池 + 宽幅对话式转换层、下方主体元素结果栏；转换层通过参考帧 `+` 加入、参考图分析、生图对话，英文 prompt 就绪后由发送区主按钮切换为确认生成，点击后才触发主体套图生成；转换层不再固定 640px 长高，按内容自然高度显示，仅以 560px 最大高度兜底内部滚动；下方主体元素结果栏的套图输出、轮询、文件夹分组、单张重生、删除和 hover 预览逻辑保持不变；胶片双击/拖拽加入参考帧池 + 胶片缓存复用 + 音频解析失败可重试，参考帧缩略图保持小尺寸 9:16 比例 + hover 左侧紧凑预览，旧主体模板区移出主路径 + 逐句时间轴移到原版视频下方并支持双行文案 + 波形同框时间对齐画面胶片 + 胶片密度按钮上移波形顶部 + 去分隔线 + 胶片上下错落 + body 顶层原位大放大 + 隐藏源视频工作区音频解析摘要卡 + 隐藏工作区顶部状态提示条 + 三字段候选生成工作流 + 折叠紧凑候选区）；`https://marketing.skg.com` 已启用应用内登录页，未登录 API 返回 401，认证后首页 200；容器内 `/health` 返回 `ok:true`
- 最近部署验证（2026-05-25）：`2192f15` 已通过 `./scripts/deploy-prod-safe.sh` 部署到 `/opt/skg-marketing-studio`，可见命名从“营销内容工作台 / 无限画布”改为“SKG 生图生视频 / 生成画布”。部署前脚本已备份生产私有环境、任务数据、资源库和 secrets 到 `/opt/skg-marketing-studio-backups/skg-marketing-preserve-20260525091127.tgz`；生产 Docker 重建后脚本内验证通过（web/API 容器 Up、`/` 302、`/login/` 200、缺失 `_next` 资源 404、未登录 `/api/health` 401、容器内 `api:health ok`、`api:ytdlp_cookie_args []`、未发现本地 API/dev URL 泄漏）。容器内静态产物复验：首页标题和 header 包含 `SKG 生图生视频`，首页按钮包含 `生成画布`，`/canvas/index.html` 标题为 `SKG 生成画布`，当前 `_next` 与 `/canvas` 产物未再命中旧可见命名。
- 最近部署验证（2026-05-25）：`2d19560` 已通过 `./scripts/deploy-prod-safe.sh` 部署到 `/opt/skg-marketing-studio`，新增登录保护下的 SKG 内部生成画布入口 `https://marketing.skg.com/canvas/`，并把首页“生成画布”按钮接到该路径。部署前脚本已备份生产私有环境、任务数据、资源库和 secrets 到 `/opt/skg-marketing-studio-backups/skg-marketing-preserve-20260525085342.tgz`；生产 Docker 重建后脚本内验证通过（web/API 容器 Up、`/` 302、`/login/` 200、缺失 `_next` 资源 404、未登录 `/api/health` 401、容器内 `api:health ok`、`api:ytdlp_cookie_args []`、未发现本地 API/dev URL 泄漏）。补验：未登录访问 `/canvas` 返回 308 到 `/canvas/`，未登录访问 `/canvas/` 返回 302 到 `/login/?next=/canvas/`；容器内确认 `/usr/share/nginx/html/canvas/index.html` 和 `canvas/assets` 已存在。
- 最近部署验证（2026-05-25）：`779e9b3` 已通过 `./scripts/deploy-prod-safe.sh` 部署到 `/opt/skg-marketing-studio`，视频生成改为个人公平队列：全局默认同时 2 个视频、单用户同时 1 个视频，同一用户连续提交会显示排队且不会占满所有生成通道。部署前脚本已备份生产私有环境、任务数据、资源库和 secrets 到 `/opt/skg-marketing-studio-backups/skg-marketing-preserve-20260525075706.tgz`；生产 Docker 重建后脚本内验证通过（web/API 容器 Up、`/` 302、`/login/` 200、缺失 `_next` 资源 404、未登录 `/api/health` 401、容器内 `api:health ok`、`api:ytdlp_cookie_args []`、未发现本地 API/dev URL 泄漏）。复验静态 bundle 已包含 `queue_message` 和“排队中”文案；API 容器确认 `VIDEO_QUEUE_MAX_CONCURRENT=2`、`VIDEO_QUEUE_MAX_CONCURRENT_PER_USER=1`。
- 最近部署验证（2026-05-25）：`b2d84dc` 已通过 `./scripts/deploy-prod-safe.sh` 部署到 `/opt/skg-marketing-studio`，修复首页生成视频完成后结果卡点击无反馈的问题：`MediaAssetTile` 新增可选原生视频 controls，首页仅在视频 `completed` 后开启播放控件，失败状态展示错误说明。部署前脚本已备份生产私有环境、任务数据、资源库和 secrets 到 `/opt/skg-marketing-studio-backups/skg-marketing-preserve-20260525071823.tgz`；生产 Docker 重建后脚本内验证通过（web/API 容器 Up、`/` 302、`/login/` 200、缺失 `_next` 资源 404、未登录 `/api/health` 401、容器内 `api:health ok`、`api:ytdlp_cookie_args []`、未发现本地 API/dev URL 泄漏）。复验静态 bundle 已包含 `videoControls` 和 `controls:`；生产容器内最近完成视频文件存在：`/data/jobs/16b984e804f5/storyboard_videos/ac96d8eba342/video.mp4`，大小 3687229 bytes。
- 最近部署验证（2026-05-25）：`486a682` 已通过 `./scripts/deploy-prod-safe.sh` 部署到 `/opt/skg-marketing-studio`，登录页新增飞书客户端 UA 自动发起 `/api/auth/feishu/start`，Nginx 未登录跳转改为 `/login/?next=$request_uri` 以保留回跳页面。部署前脚本已备份生产私有环境、任务数据、资源库和 secrets 到 `/opt/skg-marketing-studio-backups/skg-marketing-preserve-20260525070905.tgz`；生产 Docker 重建后脚本内验证通过（web/API 容器 Up、`/` 302、`/login/` 200、缺失 `_next` 资源 404、未登录 `/api/health` 401、容器内 `api:health ok`、`api:ytdlp_cookie_args []`、未发现本地 API/dev URL 泄漏）。复验静态 bundle 已包含 `skg-feishu-auto-login` 和 `auth/feishu/start?next`，未登录访问 `/detail/?job=test` 返回 `Location: /login/?next=/detail/?job=test`。当前生产 `auth_config()` 仍显示 `feishu_enabled=false`，说明服务器还缺 `FEISHU_APP_ID` / `FEISHU_APP_SECRET` 等飞书 OAuth 环境配置；配置补齐后该自动入口才会生效。
- 最近部署验证（2026-05-25）：`a02c5eb` 已通过 `./scripts/deploy-prod-safe.sh` 部署到 `/opt/skg-marketing-studio`，修复无首帧文生图 / 文生视频创建空白创作任务时的 `createCreativeImageJob 400 There was an error parsing the body`。部署前脚本已备份生产私有环境、任务数据、资源库和 secrets 到 `/opt/skg-marketing-studio-backups/skg-marketing-preserve-20260525064659.tgz`；生产 Docker 重建后脚本内验证通过（web/API 容器 Up、`/` 302、`/login/` 200、缺失 `_next` 资源 404、未登录 `/api/health` 401、容器内 `api:health ok`、`api:ytdlp_cookie_args []`、未发现本地 API/dev URL 泄漏）。云端容器确认图片 / 视频密钥均已配置：`image_configured=True`、`video_configured=True`、`image_base_url=https://ai.skg.com/ezlink/v1`、`video_base_url=https://ai.skg.com/doubao`；同一个缺 boundary 的空 multipart 探针已从旧版 400 变为认证层 401，说明请求体解析问题已消除。
- 最近部署验证（2026-05-25）：`e77e77f` 已通过 `./scripts/deploy-prod-safe.sh` 部署到 `/opt/skg-marketing-studio`，生产入口新增图片尺寸、视频画幅和按真实能力返回的视频时长选择。部署前脚本已备份生产私有环境、任务数据、资源库和 secrets 到 `/opt/skg-marketing-studio-backups/skg-marketing-preserve-20260525062614.tgz`；脚本内首次验证在容器刚启动 3 秒时遇到 `/` 500，随后复跑 `./scripts/verify-prod-docker.sh root@76.13.31.179` 通过（web/API 容器 Up、`/` 302、`/login/` 200、缺失 `_next` 资源 404、未登录 `/api/health` 401、容器内 `api:health ok`、`api:ytdlp_cookie_args []`、未发现本地 API/dev URL 泄漏）。容器内能力复验：`image_sizes=auto,1024x1536,1024x1024,1536x1024`，`video_sizes=720x1280,1280x720,1024x1024,960x1280`，`video_durations=5,8,10,12,15`，`video_max=15`；当前 Doubao / Seedance 单条不暴露 30 秒。
- 最近部署验证（2026-05-25）：`dcc8abc` 已通过 `./scripts/deploy-prod-safe.sh` 部署到 `/opt/skg-marketing-studio`，生产入口为单对话框四模式生成页，并接入图片 / 视频模型选择。部署前脚本已备份生产私有环境、任务数据、资源库和 secrets 到 `/opt/skg-marketing-studio-backups/skg-marketing-preserve-20260525030237.tgz`；首次脚本校验在容器刚启动时遇到 `/` 500，经日志确认是 Nginx auth 子请求早于 API 就绪导致的临时连接拒绝，随后复跑 `./scripts/verify-prod-docker.sh root@76.13.31.179` 通过（web/API 容器 Up、`/` 302、`/login/` 200、缺失 `_next` 资源 404、未登录 `/api/health` 401、容器内 `api:health ok`、`api:ytdlp_cookie_args []`、未发现本地 API/dev URL 泄漏）。容器内模型选项复验：`image_options=auto,gpt-image-2,gemini-3-pro-image-preview`，`video_options=seedance,kling,veo3,veo`，`video_configured=True`。
- 最近部署验证（2026-05-24）：`828b86d` 已通过 `./scripts/deploy-prod-safe.sh` 部署到 `/opt/skg-marketing-studio`，生产入口切换为多人通用营销内容创作平台首页，并保留 `/agent/` 作为高级复刻入口、`/detail/?job=<id>` 作为任务详情页。部署前脚本已备份生产私有环境、任务数据、资源库和 secrets 到 `/opt/skg-marketing-studio-backups/skg-marketing-preserve-20260523175306.tgz`；生产 Docker 重建后脚本内验证通过（web/API 容器 Up、`/` 302、`/login/` 200、缺失 `_next` 资源 404、未登录 `/api/health` 401、容器内 `api:health ok`、`api:ytdlp_cookie_args []`、未发现本地 API/dev URL 泄漏）。
- 最近部署验证（2026-05-22）：`6427935` 已通过 `./scripts/deploy-prod-safe.sh` 部署到 `/opt/skg-marketing-studio`；部署前备份为 `/opt/skg-marketing-studio-backups/skg-marketing-preserve-20260522012756.tgz`，生产 Docker 重建后脚本内验证通过（web/API 容器 Up、`/` 302、`/login/` 200、缺失 `_next` 资源 404、未登录 `/api/health` 401、容器内 `api:health ok`、`api:ytdlp_cookie_args []`）。部署后已把生产私有 `deploy/.env.production` 明确固定为多语言本地 ASR 路径并重启 API：`ASR_LANGUAGE=auto`、`FASTER_WHISPER_MODEL=base`、`ASR_REMOTE_ENABLED=false`、`ASR_LOCAL_FALLBACK_ENABLED=true`、`ASR_AUDIO_FALLBACK_ENABLED=false`；复验 `./scripts/verify-prod-docker.sh root@76.13.31.179` 通过，容器内 `/health` 确认 `asr_language=auto`、`faster_whisper=base`。
- 最近部署验证（2026-05-21）：`8458dac` 已按“先本地 Docker、再上传部署”流程上线。上线前在本机 Docker 构建 `skg-marketing-studio-web:latest` / `skg-marketing-studio-api:latest`，并用本地 Compose 容器验证通过：`web:/ 302`、`web:/login/ 200`、`web:/_next/does-not-exist.js 404`、`web:/api/health 401`、`api:health ok`、`api:ytdlp_cookie_args []`、静态 bundle 包含 `未来健康 · 营销内容工作台` 和 `信息流广告复刻生产`，且未发现本地 API/dev URL 泄漏。随后通过 `./scripts/deploy-prod-safe.sh` 部署到 `/opt/skg-marketing-studio`；部署前备份为 `/opt/skg-marketing-studio-backups/skg-marketing-preserve-20260521070327.tgz`，生产 Docker 重建后脚本内验证通过（web/API 容器 Up、`/` 302、`/login/` 200、缺失 `_next` 资源 404、未登录 `/api/health` 401、容器内 `api:health ok`）。
- 最近部署验证（2026-05-20）：`6597db3` 已通过 `./scripts/deploy-prod-safe.sh` 部署到 `/opt/skg-marketing-studio`；部署前备份为 `/opt/skg-marketing-studio-backups/skg-marketing-preserve-20260520151033.tgz`，生产 Docker 重建后脚本内验证通过（`web:/login/ 200`、`web:/api/health 401`、`api:health ok`）。线上登录后检查首页静态资源，当前加载 chunk `/_next/static/chunks/c48f07b9aef1cd29.js` 已包含 `min-w-[1280px]` 和 `max-w-[1920px]`，未再命中旧的 `h-[1000px]`、`w-[1800px]`、`BOARD_SCALE_PRESETS` 或 `boardScale`；对应工作台取消固定画布缩放，按浏览器正常流式布局渲染。
- 最近部署验证（2026-05-20）：`2b842fd` 已通过 `./scripts/deploy-prod-safe.sh` 部署到 `/opt/skg-marketing-studio`；部署前备份为 `/opt/skg-marketing-studio-backups/skg-marketing-preserve-20260520145223.tgz`，生产 Docker 重建后脚本内验证通过（`web:/login/ 200`、`web:/api/health 401`、`api:health ok`）。线上登录后检查首页静态资源，当前加载 chunk `/_next/static/chunks/743b82648dfa9db9.js` 已包含 `h-32`、`maxHeight:560`、`提示词就绪` 和 `确认生成`，且未再命中旧的 `height:640` / `h-40`；对应转换层取消固定长高，生成要求输入区回到 128px，底部仍由发送区主按钮确认生成。
- 最近部署验证（2026-05-20）：`ab31a98` 已通过 `./scripts/deploy-prod-safe.sh` 部署到 `/opt/skg-marketing-studio`；部署前备份为 `/opt/skg-marketing-studio-backups/skg-marketing-preserve-20260520144227.tgz`，生产 Docker 重建后脚本内验证通过（`web:/login/ 200`、`web:/api/health 401`、`api:health ok`）。线上登录后检查首页静态资源，当前加载 chunk `/_next/static/chunks/5bbecb6cf31316cb.js` 已包含 `h-40`、`提示词就绪` 和 `确认生成`，对应生成要求输入框加高到 160px，出图提示词生成后不再自动弹窗，底部主按钮直接切换为确认生成。
- 最近部署验证（2026-05-20）：`215987a` 已通过 `./scripts/deploy-prod-safe.sh` 部署到 `/opt/skg-marketing-studio`；部署前备份为 `/opt/skg-marketing-studio-backups/skg-marketing-preserve-20260520142849.tgz`，生产 Docker 重建后脚本内验证通过（`web:/login/ 200`、`web:/api/health 401`、`api:health ok`）。线上登录后检查首页静态资源，当前加载 chunk `/_next/static/chunks/54e1ee55c5019be8.js` 已包含 `height:640`，对应转换层固定高度从 560px 扩到 640px。
- 最近部署验证（2026-05-20）：`e1e9bf8` 已通过 `./scripts/deploy-prod-safe.sh` 部署到 `/opt/skg-marketing-studio`；部署前备份为 `/opt/skg-marketing-studio-backups/skg-marketing-preserve-20260520142145.tgz`，生产 Docker 重建后脚本内验证通过（`web:/login/ 200`、`web:/api/health 401`、`api:health ok`）。线上登录后 Playwright 以 2048x1060 复测生成要求 composer：文本输入区实际高约 119px，张数控件和发送按钮实际高约 42px，页面无客户端异常，验证截图 `/tmp/skg-generation-composer-expanded.png`。
- 最近部署验证（2026-05-20）：`45b25d0` 已通过 `./scripts/deploy-prod-safe.sh` 部署到 `/opt/skg-marketing-studio`；部署前备份为 `/opt/skg-marketing-studio-backups/skg-marketing-preserve-20260520140706.tgz`，生产 Docker 重建后脚本内验证通过（`web:/login/ 200`、`web:/api/health 401`、`api:health ok`）。线上登录后 Playwright 以 2048x1060 复测转换层：生成要求区不再渲染“当前要求”、“提示词已生成”和“对话记录已收起”摘要，保留元素副本也已移除；该区只保留文本输入、张数控件和发送按钮，页面无客户端异常，验证截图 `/tmp/skg-generation-composer-simplified.png`。
- 最近部署验证（2026-05-20）：`54f159b` 已通过 `./scripts/deploy-prod-safe.sh` 部署到 `/opt/skg-marketing-studio`；部署前备份为 `/opt/skg-marketing-studio-backups/skg-marketing-preserve-20260520135509.tgz`，生产 Docker 重建后脚本内验证通过（`web:/login/ 200`、`web:/api/health 401`、`api:health ok`）。线上登录后 Playwright 以 2048x1060 复测转换层：转换层和参考帧池共用高度从 500px 拉到 560px，转换层内部改为 gap 堆叠并让主要板块 `shrink-0`，超出由转换层自身滚动承接；页面无客户端异常，验证截图 `/tmp/skg-conversion-stretched.png`。
- 最近部署验证（2026-05-20）：`d1e2b17` 已通过 `./scripts/deploy-prod-safe.sh` 部署到 `/opt/skg-marketing-studio`；部署前备份为 `/opt/skg-marketing-studio-backups/skg-marketing-preserve-20260520134529.tgz`，生产 Docker 重建后脚本内验证通过（`web:/login/ 200`、`web:/api/health 401`、`api:health ok`）。线上登录后 Playwright 以 2048x1060 复测转换层：内嵌“待确认提示词”卡和黑色“确认并生成 N 张”按钮均不再渲染，页面无客户端异常；有待确认 prompt 时只在“生成要求”标题右侧显示小型“待确认 · N 张”入口，验证截图 `/tmp/skg-conversion-no-inline-confirm.png`。
- 最近部署验证（2026-05-20）：`caa7b73` 已通过 `./scripts/deploy-prod-safe.sh` 部署到 `/opt/skg-marketing-studio`；部署前备份为 `/opt/skg-marketing-studio-backups/skg-marketing-preserve-20260520132820.tgz`，生产 Docker 重建后脚本内验证通过（`web:/login/ 200`、`web:/api/health 401`、`api:health ok`）。线上登录后 Playwright 以 2048x1060 复测：页面无客户端异常，源视频工作区已撤销“布局调节”按钮和 `localStorage["skg-source-workspace-layout:v1"]` 布局读写，固定为左侧原视频列 380px、9:16 视频高 500px、逐句时间轴最大高 270px、参考帧池 140px、转换层 500px 内部滚动、主体空态 78px；验证截图 `/tmp/skg-layout-fixed-no-tuning.png`。
- 最近部署验证（2026-05-20）：`0db265f` 已通过 `./scripts/deploy-prod-safe.sh` 部署到 `/opt/skg-marketing-studio`；部署前备份为 `/opt/skg-marketing-studio-backups/skg-marketing-preserve-20260520131649.tgz`，生产 Docker 重建后脚本内验证通过（`web:/login/ 200`、`web:/api/health 401`、`api:health ok`）。线上登录后 Playwright 打开历史任务并展开“布局调节”：面板显示左列宽、视频高、时间轴高、参考池宽、转换层高、主体空态 6 个滑杆，调参值写入 `localStorage["skg-source-workspace-layout:v1"]`，供用户先在线试比例再固化默认值。
- 最近部署验证（2026-05-20）：`5bffd63` 已通过 `./scripts/deploy-prod-safe.sh` 部署到 `/opt/skg-marketing-studio`；部署前备份为 `/opt/skg-marketing-studio-backups/skg-marketing-preserve-20260520123949.tgz`，生产 Docker 重建后脚本内验证通过（`web:/login/ 200`、`web:/api/health 401`、`api:health ok`）。线上登录后 Playwright 复测 1440x900 与 2048x1060：历史任务加载后转换层占据主操作宽度，主体元素下移为转换层下方的紧凑结果栏，未再出现右侧三栏挤压；滚动到主体元素位置后仍能看到下方分镜工作台承接。
- 最近部署验证（2026-05-20）：`f0f567b` 已通过 `./scripts/deploy-prod-safe.sh` 部署到 `/opt/skg-marketing-studio`；部署前备份为 `/opt/skg-marketing-studio-backups/skg-marketing-preserve-20260520120958.tgz`，生产 Docker 重建后脚本内 `./scripts/verify-prod-docker.sh` 通过。线上登录后 Playwright 截图复测 1440x900、1728x1117、2048x1060、2560x1440：缩放后的工作台在 1440/1728/2560 这类高度有余量的窗口上下居中，2048x1060 保持顶部对齐并承接纵向内容，未出现先前的底部黑边失衡。
- 最近部署验证（2026-05-20）：`3e7c165` 已通过 `./scripts/deploy-prod-safe.sh` 部署到 `/opt/skg-marketing-studio`；部署前备份为 `/opt/skg-marketing-studio-backups/skg-marketing-preserve-20260520114759.tgz`，生产 Docker 重建后脚本内 `./scripts/verify-prod-docker.sh` 通过。线上登录后 Playwright 复测 1366x768、1440x900、1728x1117、1920x1080、2048x1060、2200x1400、2560x1440：缩放档位分别为 0.72、0.8、0.92、1.06、1.06、1.16、1.34；2048x1060 保留左右 70px 呼吸感且无横向溢出，浏览器 `pageerror` 为空。
- 最近部署验证（2026-05-20）：`e33463e` 已通过 `./scripts/deploy-prod-safe.sh` 部署到 `/opt/skg-marketing-studio`；部署前备份为 `/opt/skg-marketing-studio-backups/skg-marketing-preserve-20260520113414.tgz`，生产 Docker 重建后脚本内 `./scripts/verify-prod-docker.sh` 通过。线上登录后 Playwright 复测转换层：页面显示“生成要求”和“对话记录已收起”，不再显示旧标题“生图对话”，也不再渲染“我们将不再强制...”这类模型确认消息；最终英文 prompt 仍保留在“待确认提示词”区域，浏览器 `pageerror` 为空。
- 最近部署验证（2026-05-20）：`f35bfe0` 已通过 `./scripts/deploy-prod-safe.sh` 部署到 `/opt/skg-marketing-studio`；部署前备份为 `/opt/skg-marketing-studio-backups/skg-marketing-preserve-20260520111824.tgz`，生产 Docker 重建后脚本内 `./scripts/verify-prod-docker.sh` 通过。线上登录后 Playwright 复测 1440x900、2048x1060、2200x1400 三种窗口，工作台仍按可见宽度优先铺满且外层 wrapper 左右间隙均为 0；内层画布已改用 CSS `zoom` 渲染，三个窗口分别为 `zoom=0.8/1.138/1.222`，`transform` 均为 `none`，避免整屏 transform 小数缩放造成文字发虚，浏览器 `pageerror` 为空。
- 最近部署验证（2026-05-20）：`1d0a77b` 已通过 `./scripts/deploy-prod-safe.sh` 部署到 `/opt/skg-marketing-studio`；部署前备份为 `/opt/skg-marketing-studio-backups/skg-marketing-preserve-20260520105846.tgz`，生产 Docker 重建后脚本内 `./scripts/verify-prod-docker.sh` 通过。线上登录后 Playwright 复测 1440x900、2048x1060、2200x1400 三种窗口，工作台按可见宽度优先缩放，外层 wrapper 左右间隙均为 0；三个窗口分别缩放到 0.8、1.138、1.222，2048x1060 这类高度不足场景通过纵向滚动承接，不再为了完整高度留下左右空白，浏览器 `pageerror` 为空。
- 最近部署验证（2026-05-20）：`54eaac0` 已通过 `./scripts/deploy-prod-safe.sh` 部署到 `/opt/skg-marketing-studio`；部署前备份为 `/opt/skg-marketing-studio-backups/skg-marketing-preserve-20260520104155.tgz`，生产 Docker 重建后脚本内 `./scripts/verify-prod-docker.sh` 通过。线上登录后 Playwright 复测 1440x900、2048x1060、2200x1400 三种窗口，工作台以 1800x1000 为基准分别缩放到 0.8、1.06、1.222，主网格列宽、源视频区列宽和三栏主体管线列宽保持一致，浏览器 `pageerror` 为空。
- 最近部署验证（2026-05-20）：`64fef5a` 已通过 `./scripts/deploy-prod-safe.sh` 部署到 `/opt/skg-marketing-studio`；部署前备份为 `/opt/skg-marketing-studio-backups/skg-marketing-preserve-20260520102354.tgz`，生产 Docker 重建后脚本内 `./scripts/verify-prod-docker.sh` 通过。线上登录后 Playwright 以 1440x900 与 2200x1400 两种窗口复测，工作台内部画布固定为 1800x1000，主网格列宽、源视频区列宽和三栏主体管线列宽一致，浏览器 `pageerror` 为空。
- 最近部署验证（2026-05-20）：`40f1f28` 已推送并通过 `./scripts/deploy-prod-safe.sh` 部署到 `/opt/skg-marketing-studio`；部署前备份为 `/opt/skg-marketing-studio-backups/skg-marketing-preserve-20260520095941.tgz`，生产 Docker 重建后脚本内 `./scripts/verify-prod-docker.sh` 通过。线上登录后 Playwright 复测首页正常渲染，浏览器 `pageerror` 为空。转换层不再显示固定快捷需求按钮，生图对话空态和输入框改为中性“保留识别元素 / 补充调整要求”，由识别结果 chip 和自然语言对话承接用户意图。
- 最近部署验证（2026-05-20）：`2c0e8a0` 已推送并通过 `./scripts/deploy-prod-safe.sh` 部署到 `/opt/skg-marketing-studio`；部署前备份为 `/opt/skg-marketing-studio-backups/skg-marketing-preserve-20260520094923.tgz`，生产 Docker 重建后脚本内 `./scripts/verify-prod-docker.sh` 通过。线上登录后 Playwright 复测首页正常渲染到工作台，浏览器 `pageerror` 为空；本次修复 `selectedAgentTraitsDirty` 残留变量名导致的客户端 `ReferenceError`，恢复转换层页面首屏渲染。
- 最近部署验证（2026-05-20）：`5bdde89` 已推送并通过 `./scripts/deploy-prod-safe.sh` 部署到 `/opt/skg-marketing-studio`；部署前备份为 `/opt/skg-marketing-studio-backups/skg-marketing-preserve-20260520092721.tgz`，生产 Docker 重建后脚本内 `./scripts/verify-prod-docker.sh` 通过（web/API 容器 Up、`/` 302、`/login/` 200、缺失 `_next` 资源 404、未登录 `/api/health` 401、容器内 `api:health ok`、无本地 API 地址泄漏）。线上转换层识别结果 chip 改为本地即时切换：点亮表示保留元素、再次点击取消、清空按钮取消全部；点击 chip 不再触发 `/subject-agent/message`，保留元素随下一条“发送消息”一次性提交，避免每点一个特征都等待模型导致卡顿。
- 最近部署验证（2026-05-20）：`10d955c` 已推送并通过 `./scripts/deploy-prod-safe.sh` 部署到 `/opt/skg-marketing-studio`；部署前备份为 `/opt/skg-marketing-studio-backups/skg-marketing-preserve-20260520090750.tgz`，生产 Docker 重建后脚本内 `./scripts/verify-prod-docker.sh` 通过（web/API 容器 Up、`/` 302、`/login/` 200、缺失 `_next` 资源 404、未登录 `/api/health` 401、容器内 `api:health ok`、无本地 API 地址泄漏）。线上转换层已移除可见快捷需求 chip，复刻/创新/卡通/人物占比作为对话默认意图写入输入提示；生成张数控件移到发送消息旁边，默认 6 张、当前支持 1-10；参考输入空态和已选参考图缩略图压小并继续复用 `MediaAssetTile` hover 放大预览。
- 最近部署验证（2026-05-20）：`b9c5511` 已推送并通过 `./scripts/deploy-prod-safe.sh` 部署到 `/opt/skg-marketing-studio`；部署前备份为 `/opt/skg-marketing-studio-backups/skg-marketing-preserve-20260520085513.tgz`，生产 Docker 重建后脚本内 `./scripts/verify-prod-docker.sh` 通过（web/API 容器 Up、`/` 302、`/login/` 200、缺失 `_next` 资源 404、未登录 `/api/health` 401、容器内 `api:health ok`、无本地 API 地址泄漏）。线上转换层已更新为参考输入区支持左侧 `+`、参考帧拖拽、胶片拖拽和本地图片拖入，下方为生图对话消息 composer，右侧主体元素套图输出逻辑保持不变。
- 最近部署验证（2026-05-19）：`fd794e3` 已推送并部署到 `/opt/skg-marketing-studio`；生产 `/health` 显示 `image=gpt-image-2`、`subject_image=gpt-image-2`、`image_request_timeout_seconds=60`、`image_base_url=https://ai.skg.com/ezlink/v1`。容器内最小文字生图探针在 20 秒限制下返回 `ReadTimeout`，说明当前阻塞点是 `https://ai.skg.com/ezlink/v1` 的 `gpt-image-2` 上游通道超时，服务端不会更换图片模型。
- 最近部署验证（2026-05-19）：`3756259` 已推送并部署到 `/opt/skg-marketing-studio`；生产 `/health` 显示 `image=gpt-image-2`、`image_fallbacks=['gemini-3-pro-image-preview']`、`subject_image_fallbacks=['gpt-image-2','gemini-3-pro-image-preview']`、短时熔断阈值 2 次 / 600 秒。线上真实探针确认 `gpt-image-2` 读超时后同次调用可自动兜底到 `gemini-3-pro-image-preview` 并返回图片；模拟探针确认连续 2 次主模型失败后第三次直接走 Gemini。
- 最近部署验证（2026-05-20）：`c245bff` 已推送并部署到 `/opt/skg-marketing-studio`；本地 `python3 -m py_compile api/main.py` 和 `web/npm run build` 通过，生产 Docker 重建后 `./scripts/verify-prod-docker.sh` 通过（web 容器 Up、API 容器 Up、`/login/` 200、缺失 `_next` 资源 404、未登录 `/api/health` 401、容器内 `api:health ok`）。容器内模型偏好探针确认转换层 `image_model_preference` 路由：`auto -> ['gpt-image-2','gemini-3-pro-image-preview']`、`gpt-image-2 -> ['gpt-image-2']`、`gemini-3-pro-image-preview -> ['gemini-3-pro-image-preview']`。
- 最近部署验证（2026-05-20）：`2366662` 已推送并部署到 `/opt/skg-marketing-studio`；本地 `python3 -m py_compile api/main.py`、`web/npm run build` 通过，生产 Docker 重建后 `./scripts/verify-prod-docker.sh` 通过。容器内后处理探针确认白底小主体保存为 `1152x2048` 时有效主体高度占比从约 0.60 可放大到 `0.906`，主体 6 视图 prompt 已注入同一份 pack bible。
- 最近部署验证（2026-05-20）：`7acbfd5` 已推送并部署到 `/opt/skg-marketing-studio`；本地 `python3 -m py_compile api/main.py`、`web/npm run build` 通过，生产 Docker 重建后 `./scripts/verify-prod-docker.sh` 通过（web/API 容器 Up、`/login/` 200、缺失 `_next` 资源 404、未登录 `/api/health` 401、容器内 `api:health ok`）。转换层主体提示词记忆和生图模型偏好改为按 `job.id` 隔离；有参考帧的 `reconstruction_mode=similar` 会先生成 source brief，再把参考帧作为 `/images/edits` 的 `image[]` 参考提交；自主描述空文本切到 `reconstruction_mode=same` 源形象锁定路径。
- 最近部署验证（2026-05-20）：`e10b1a6` 已推送并部署到 `/opt/skg-marketing-studio`；本地 `python3 -m py_compile api/main.py`、`web/npm run build` 通过，生产 Docker 重建后 `./scripts/verify-prod-docker.sh` 通过（web/API 容器 Up、`/login/` 200、缺失 `_next` 资源 404、未登录 `/api/health` 401、容器内 `api:health ok`）。转换层改为项目内生图对话智能体，新增 `Job.subject_agent` 和 `/subject-agent/analyze`、`/subject-agent/message`，GPT / Gemini 改为成套控制分析、对话和生图模型，数量与要求修改进入对话状态后再调用主体套图生成；Pydantic `model_bundle` protected namespace warning 已消除。
- 最近部署验证（2026-05-20）：`d82175f` 已推送并部署到 `/opt/skg-marketing-studio`；本地 `python3 -m py_compile api/main.py`、`web/npm run build` 通过，生产 Docker 重建后 `./scripts/verify-prod-docker.sh` 通过（web/API 容器 Up、`/login/` 200、缺失 `_next` 资源 404、未登录 `/api/health` 401、容器内 `api:health ok`）。转换层去掉方向卡片、卡通风格下拉和独立数量按钮，保留单一参考区 + 生图对话；后端 `/subject-agent/message` 从对话中识别 `selected_mode` 和 `quantity` 后再驱动主体套图生成。
- 最近部署验证（2026-05-20）：`f1c710e` 已推送并部署到 `/opt/skg-marketing-studio`；本地 `web/npm run build` 通过，生产 Docker 重建后 `./scripts/verify-prod-docker.sh` 通过（web/API 容器 Up、`/login/` 200、缺失 `_next` 资源 404、未登录 `/api/health` 401、容器内 `api:health ok`）。转换层中间栏先清空为待重构占位，不再接收拖拽或触发 subject-agent / subject-assets；右侧主体元素输出逻辑保持不变。
- 最近部署验证（2026-05-20）：`7e763cf` 已推送并部署到 `/opt/skg-marketing-studio`；本地 `web/npm run build` 通过，生产 Docker 重建后 `./scripts/verify-prod-docker.sh` 通过（web/API 容器 Up、`/login/` 200、缺失 `_next` 资源 404、未登录 `/api/health` 401、容器内 `api:health ok`）。转换层改为参考帧分析 + 对话生成提示词 + 弹窗确认后再生成主体套图；右侧主体元素输出逻辑保持不变。部署时发现服务器 `WEB_AUTH_*` 环境变量缺失导致 `/auth/check` 503，已从 `/root/skg-marketing-studio-login.txt` 和新 session secret 恢复服务器 `deploy/.env.production` 后重启验证通过；后续同步生产代码必须继续排除服务器真实 `deploy/.env.production`。
- 主站 / 前端：`https://marketing.skg.com`
- 旧画布路径：`https://marketing.skg.com/canvas/`（仅兼容跳转到根域名）
- API / 后端：`https://marketing.skg.com/api`
- 代码仓库 / Gitea：`https://git.kang-kang.com/kangwan/20260512-skg-tk`
- 文档 / 解析：`docs/source-analysis.html`（项目内独立文档，不公开挂主应用路由）
- 管理后台：待定
- 服务器目录：`/opt/skg-marketing-studio`
- 生产部署唯一入口：`./scripts/deploy-prod-safe.sh`（先在服务器备份 `deploy/.env.production`、`data/jobs`、资源库和 `secrets`，再用受保护 rsync 同步代码，最后 Docker 重建并运行 `verify-prod-docker.sh`）
- 生产容器重建命令：`docker compose -f docker-compose.prod.yml --env-file deploy/.env.production up -d --build`；只允许脚本内部或明确只重启容器时使用，不允许再用裸 `rsync --delete` 手动同步。
- 独立预览容器重建命令：服务器 `/opt/skg-marketing-studio` 下执行 `docker compose -f docker-compose.standalone.yml --env-file deploy/.env.production up -d --build`；Web 暴露 `0.0.0.0:4290->80`，后端仅在 compose 内部网络暴露，`/api/` 由 Web 容器 Nginx 反代并复用应用内登录校验。
- 生产架构：`web` 容器用 Nginx 承载 Next 静态导出与根域名 Vue / Vite 画布静态应用；构建时先生成画布，再 Next 静态导出，最后用画布产物覆盖 `web/out/index.html` 和 `/assets/`，使登录后的 `/` 直接进入画布；`/canvas/` 只做 308 兼容跳转到 `/`。`/login/`、`/_next/`、`/assets/`、`/skg-logo-black.svg`、`/oasis-source/` 等登录页必需静态资源公开访问；未登录访问工作台跳转 `/login/`；`/api/` 通过 Nginx `auth_request` 校验 FastAPI 会话 Cookie 后反代到 `skg-marketing-api:4291`；Traefik 通过 `coolify` 外部网络接入 80/443
- Web 验收必须以生产 Docker 形态为准：前端是 `next export` 静态产物 + Nginx，不是 `next dev` / `next start`。任何 Web 改动部署后必须运行 `./scripts/verify-prod-docker.sh`，确认 `/login/`、`/_next/`、`/api/health`、本地 API 地址泄漏和 API 镜像 `.env` 污染检查通过；不能只用本地 `npm run build` 作为上线依据。
- 当前音频解析：`https://ai.skg.com/azure/v1` 的 `gpt-4o-transcribe` 当前返回 `DeploymentNotFound`，且官方 Azure OpenAI transcription 路径探测也未返回可用部署；生产临时复制本地成功策略，直接使用容器内多语言 `faster-whisper` 真实转写，默认语种为 `auto`，支持中文、英文和其他多语言原文识别，关闭 Gemini 多模态音频兜底。拿到真实 Azure ASR deployment 名后再恢复 `ASR_REMOTE_ENABLED=true`，并保持 `ASR_LANGUAGE` 为空或 `auto`，除非明确只想强制单一语种。
- 持久化目录：服务器 `./data/jobs` 挂载到后端 `/data/jobs`；全局资源中心持久化在 `./data/asset_library`、`./data/prompt_library` 和 `./data/_trash`
- TikTok 下载登录态：公开视频默认不带 cookies 直接下载，生产环境变量必须显式保持 `YTDLP_COOKIES_FILE=`、`YTDLP_COOKIES_FROM_BROWSER=` 为空，防止容器读取不存在的浏览器 cookies。只有 TikTok 明确要求登录态时，才使用服务器私有 cookies 文件 `./secrets/tiktok_cookies.txt` 挂载到 API 容器 `/run/secrets/tiktok_cookies.txt` 并配置 `YTDLP_COOKIES_FILE=/run/secrets/tiktok_cookies.txt`；`yt-dlp` 会在任务结束时回写 cookies，因此不要把该挂载设为只读；不要使用云端浏览器读取方案，也不要把 cookies 入库。生产容器严禁使用 `YTDLP_COOKIES_FROM_BROWSER=chrome`。
- 登录凭证：生产入口以飞书免登录为主；飞书 OAuth 的 `FEISHU_APP_ID` / `FEISHU_APP_SECRET` 只放服务器 `deploy/.env.production`，回调地址固定为 `https://marketing.skg.com/api/auth/feishu/callback` 并需要在飞书开放平台应用安全设置中登记。登录页读取 `/api/auth/config` 后，如果检测到飞书客户端并且 `feishu_enabled=true`，会自动跳转 `/api/auth/feishu/start`，普通浏览器仍保留“飞书免登录”按钮和备用账号。原账号密码登录保留为备用入口，用户名写下方快捷登录，密码明文备份只放服务器 `/root/skg-marketing-studio-login.txt`，`WEB_AUTH_PASSWORD` / `WEB_AUTH_SESSION_SECRET` 只放服务器 `deploy/.env.production`。开启 `AUTH_DATA_ISOLATION_ENABLED=true` 后，新建任务、素材任务和一键出片记录按登录用户隔离；历史无 owner 的旧任务只对备用账号可见，飞书用户互不可见。
- 禁止手动裸 `rsync --delete` 到服务器；必须使用 `./scripts/deploy-prod-safe.sh`。如遇极端情况必须手动同步，命令必须同时包含 protect/exclude：`.git`、`.memory`、`.logs`、`.pids`、`data`、`jobs`、`secrets`、`api/jobs`、`api/.env`、`api/.env.local`、`api/.env.production`、`deploy/.env.production`、`web/node_modules`、`web/.next`、`web/out`。不要把本地 `api/.env` 或 `deploy/.env.production` 覆盖到 `/opt/skg-marketing-studio`，也不要删除服务器 `data/jobs`，否则会清空案例、登录和模型配置。

## 快捷登录
- 登录地址：`https://marketing.skg.com/login/`
- 主路径：飞书免登录
- 备用用户名：`skg`
- 备用密码：见服务器 `/root/skg-marketing-studio-login.txt`（不入库）
- 说明：当前是生产入口应用内登录页；飞书 App Secret、数据库密码、API Key、服务器 root 密码不要写这里

## 元数据回写清单
- 新增或变更公网地址后，必须同步更新 `.project.json.urls`
- 如果有网页后台登录：
  - 可直接入库：写 `.project.json.quick_login`
  - 不应入库：写 `.project.json.credentials` 引用
- 部署完成后，`RULES.md` 和 `.project.json` 必须同一次任务一起更新

## Git / 开发收口
- 工作看板全局规则适用于本项目：`/Users/kangwan/Projects/code/20260317-rules-dashboard/RULES.md`、`SCHEMA.md`、`rules/03-Git约定.md`、`rules/04-版本发布规则.md`
- 主分支：`main`
- 主远端：`origin` → `ssh://git@git.kang-kang.com:22222/kangwan/20260512-skg-tk.git`
- Gitea 网页仓库：`https://git.kang-kang.com/kangwan/20260512-skg-tk`
- 每次开发结束前必须执行并汇报 `git status -sb` 和变更范围
- 代码、规则、部署或元数据变更必须形成 `feat:`、`fix:`、`docs:`、`chore:`、`release:` 等人工语义 commit；`auto-save` 只算安全快照
- 能联网和鉴权时必须 `git push origin main`；如果不能推送，最终回复必须写清楚当前分支、领先/落后数量、最新未推送 commit 和失败原因

## 环境变量
- `LLM_BASE_URL` / `LLM_API_KEY`：OpenAI 兼容网关，用于翻译、文案改写、音频分析等文本/多模态理解模型调用
- `ASR_BASE_URL` / `ASR_API_KEY`：OpenAI Audio Transcriptions 兼容网关，用于上传 `audio.wav` 做真实转写；未配置 `ASR_API_KEY` 时复用 `LLM_API_KEY`，生产默认指向 `https://ai.skg.com/azure/v1`
- `ASR_MODEL`：OpenAI Audio Transcriptions 音频转写模型；微软通道使用 Azure OpenAI 部署名 `gpt-4o-transcribe`，如果 Azure 侧实际部署名不同必须同步改这里
- `ASR_LANGUAGE`：远端和本地 ASR 的可选输入语言提示，默认空值/`auto`，由模型自动识别中文、英文和其他多语言；只有明确知道素材固定语种时才填写 ISO-639-1 代码强制识别。
- `ASR_REMOTE_ENABLED`：是否启用远端 OpenAI Audio Transcriptions；微软 ASR 验收时必须为 `true`。当前生产因 `https://ai.skg.com/azure/v1` 下 `gpt-4o-transcribe` 返回 `DeploymentNotFound`，临时设为 `false`，直接走容器内 `faster-whisper`，等真实 Azure deployment 名补齐后再恢复。
- `ASR_LOCAL_FALLBACK_ENABLED`：是否允许远端 ASR 失败后落到本机 / 容器内 ASR；当前生产为 `true`，复制本地成功路径的“本机真实转写”策略，云端用 CPU 版 `faster-whisper` 替代本机 Mac 的 `mlx_whisper`。
- `ASR_AUDIO_FALLBACK_ENABLED`：是否允许远端和本机 ASR 失败后落到多模态音频兜底；生产微软 ASR 验收设为 `false`，避免静默使用 Gemini 音频
- `FASTER_WHISPER_MODEL` / `FASTER_WHISPER_DEVICE` / `FASTER_WHISPER_COMPUTE_TYPE`：容器内本地 ASR 兜底，仅在 `ASR_LOCAL_FALLBACK_ENABLED=true` 时启用；默认用多语言 `base`，不要改回 `*.en` 英文专用模型，否则中文和多语言识别会退化。
- `ASR_FALLBACK_MODEL`：多模态音频兜底模型，仅在 `ASR_AUDIO_FALLBACK_ENABLED=true` 时用于兜底或音频画像，默认 `gemini-2.5-flash`；如果模型不能真实听到音频或返回疑似逐秒假字幕，后端必须拒绝写入时间轴
- `ASR_TIMEOUT_SECONDS`：远端 ASR / 翻译 / 音频分析单次请求超时；当前生产本地转写模式设为 45 秒，微软 ASR 重新启用时可按素材长度提高。
- `LOCAL_ASR_BIN` / `LOCAL_ASR_MODEL` / `LOCAL_ASR_TIMEOUT_SECONDS`：本机 ASR 兜底，默认使用 `/opt/homebrew/bin/mlx_whisper` + `mlx-community/whisper-tiny`，用于当前 SKG 网关 `/audio/transcriptions` 不可用时生成真实逐句时间轴
- `TRANSLATE_MODEL`：字幕翻译模型，默认 `gemini-2.5-flash`
- `GPT_TEXT_MODEL`：GPT 文本 / 视觉默认模型，默认 `gpt-4o`；用于兜底修正旧 Gemini 覆盖值
- `REWRITE_MODEL`：通用改写/分镜描述模型，默认 `gpt-4o`；如果旧环境仍写 `gemini-*`，后端会自动改用 `GPT_TEXT_MODEL`
- `VISION_MODEL`：关键帧画面理解模型，默认 `gpt-4o`；如果旧环境仍写 `gemini-*`，后端会自动改用 `GPT_TEXT_MODEL`
- `AUDIO_REWRITE_MODEL`：后续音频口播改写模型，默认跟随 `REWRITE_MODEL`；如果旧环境仍写 `gemini-*`，后端会自动改用 `REWRITE_MODEL`
- `AUDIO_PRODUCT_BRIEF`：音频口播改写时注入的 SKG 产品卖点
- `PRODUCT_VIEW_MODEL`：同一产品素材池的视角标注/自动识别模型；当前按项目要求强制使用 `gpt-image-2`
- `IMAGE_BASE_URL` / `IMAGE_API_KEY` / `IMAGE_MODEL`：OpenAI 兼容生图网关；当前所有生图入口主模型仍为 `gpt-image-2`
- `IMAGE_REQUEST_TIMEOUT_SECONDS`：单次图片网关请求超时，默认 60 秒；超时会直接把该视图标失败并继续下一张，避免主体 6 视图整包长时间无反馈
- `IMAGE_FALLBACK_ENABLED` / `IMAGE_FALLBACK_MODEL`：图片主模型故障兜底；当前允许在 `gpt-image-2` 超时、429、5xx 或网络错误时临时使用 `gemini-3-pro-image-preview`，400/401/403/404 和参数错误不兜底
- `IMAGE_CIRCUIT_FAILURE_THRESHOLD` / `IMAGE_CIRCUIT_COOLDOWN_SECONDS`：短时熔断配置，默认 `gpt-image-2` 连续 2 次上游类失败后 600 秒内直接走 Gemini 兜底；成功恢复后自动清空失败计数
- `GPT_IMAGE_MODEL` / `SUBJECT_ASSET_IMAGE_MODEL` / `SUBJECT_ASSET_IMAGE_MODELS`：保留兼容旧环境变量名；主体 6 视图在转换层默认自动使用 `gpt-image-2`，同一套图内一旦触发 Gemini 兜底，后续视图沿用 Gemini，避免一张张等待主模型超时；用户显式选择 GPT 或 Gemini 时，`image_model_preference` 会让主体套图只走所选模型
- `AI_HTTP_PROXY` / `IMAGE_HTTP_PROXY`：可选的 AI 网关出站代理；本地 launchd 后台进程不一定继承 shell 的 `http_proxy/https_proxy`，如生图报 DNS / ConnectError，可在本地 `api/.env` 配置后重启后端。`/health` 只回传是否配置代理，不回传代理地址。
- `YTDLP_COOKIES_FILE` / `YTDLP_COOKIES_FROM_BROWSER`：可选 TikTok 下载登录态；生产云端固定使用 cookies 文件 `/run/secrets/tiktok_cookies.txt`（宿主机 `./secrets/tiktok_cookies.txt` 挂载进容器），本地开发可临时用浏览器 cookies。cookies 文件属于敏感登录态，只能放本机或服务器私有路径，不允许入库。
- `VOICE_PROVIDER`：配音通道，服务端固定使用 `azure_openai`；旧环境若写 `minimax` 会被忽略
- `AZURE_OPENAI_BASE_URL` / `AZURE_OPENAI_API_KEY`：微软 Azure OpenAI 协议配音网关；本地未单独配置 Key 时回退复用 `LLM_API_KEY`
- `AZURE_TTS_MODEL` / `AZURE_TTS_VOICE_ID` / `AZURE_TTS_VOICE_POOL` / `AZURE_TTS_PATH` / `AZURE_TTS_PATHS`：Azure OpenAI TTS 模型、默认音色、音色池和 OpenAI 协议语音路径；后端会按 `AZURE_TTS_PATHS` 依次尝试，便于区分路径不对和整条语音服务不可用
- `POE_API_KEY` / `VIDEO_API_KEY`：视频生成通道 Key，只能放本地环境变量
- `WEB_AUTH_USERNAME` / `WEB_AUTH_PASSWORD` / `WEB_AUTH_SESSION_SECRET`：生产备用网页登录和会话签名配置；密码和 session secret 只放服务器环境变量，不入库。即使只开飞书免登录，也必须配置 `WEB_AUTH_SESSION_SECRET` 用于签名会话 Cookie。
- `FEISHU_APP_ID` / `FEISHU_APP_SECRET`：飞书免登录 OAuth 应用凭证；只放服务器 `deploy/.env.production` 或本地 `api/.env`，不入库。
- `FEISHU_REDIRECT_URI`：飞书 OAuth 回调地址，生产固定为 `https://marketing.skg.com/api/auth/feishu/callback`。
- `FEISHU_OAUTH_SCOPE`：飞书 OAuth 授权范围；默认空值，按飞书应用后台已开权限执行。
- `FEISHU_ALLOWED_EMAIL_DOMAINS` / `FEISHU_ALLOWED_EMAILS` / `FEISHU_ALLOWED_TENANT_KEYS`：可选飞书账号白名单；留空时由飞书应用可见范围控制。
- `AUTH_DATA_ISOLATION_ENABLED`：多用户数据隔离开关，生产保持 `true`；新建 `Job` / `AgentRun` 会写入当前登录用户 owner，列表和详情访问只返回本人数据。
- `VIDEO_QUEUE_MAX_CONCURRENT` / `VIDEO_QUEUE_MAX_CONCURRENT_PER_USER`：视频生成进程内队列并发上限，生产默认全局同时 2 个、单用户同时 1 个；同一用户连续提交会排队，其他用户仍可获得执行机会。当前队列不依赖 Redis，API 容器重启会把未完成视频标记为失败并提示重新生成。
- `FFMPEG_BIN` / `FFPROBE_BIN`：可选本地媒体二进制路径；本机 Homebrew ffmpeg 动态库损坏时，后端会自动跳过不可用的 PATH 版本并尝试本机静态 ffmpeg 备选，生产仍建议使用系统 ffmpeg/ffprobe
- 生产环境变量：服务器只使用 `deploy/.env.production`，模板为 `deploy/.env.production.example`；真实 Key 不入库
- 同步生产代码时必须排除服务器真实 `deploy/.env.production`，只同步 `deploy/.env.production.example`；网页登录密码、session secret、ASR/API Key 只保留在服务器环境文件和 `/root/skg-marketing-studio-login.txt`

## 规则
- 不允许编造不存在的部署域名、账号、密码
- 没有公网地址时，`.project.json.urls` 保持空数组
- 任何部署或域名变化，都要先改元数据，再视为任务完成
- 用户给到源码 / 下载包 / 参考实现时，默认优先按源码实现和复刻，不先自创“类似效果”；如果因安全、依赖、性能或部署限制必须改写，必须先说明差异和原因。
- 媒体素材交互为项目基底规则：任何图片、视频、抽帧、产品图、AI 生成图、首尾帧和视频候选缩略图，默认复用 `web/components/media-asset-tile.tsx`；必须支持鼠标停留顶层放大预览，可删除素材必须有删除按钮，预览不能被面板或滚动容器遮挡。

## 注意事项
- 项目内源码解析页：`docs/source-analysis.html`
- 源码解析页是给产品协作和需求描述用的独立 HTML，不接入 Next 应用路由
- 后续任何功能、节点职责、接口、数据模型或用户操作路径变更，都要同步更新 `docs/source-analysis.html` 的对应章节和变更记录
