认清你的未知 · 实施中
← 全部示例

实施笔记

不要让构建中的意外消失在滚动记录里,让 agent 在发生时记录每一次偏离方案的情况——这样意外会成为你下次尝试的输入。

提示词
「在构建导出功能时维护一份实施笔记文件。若遇到迫使你偏离方案的边缘情况,选择保守选项,在「偏离」下记录,然后继续。」
Claude 的输出
docs/notes/export-feature-implementation.md 运行开始 14:02 · 最后写入 16:51 · 分支 feat/review-export
11
条记录
4
偏离
2
需你判断
14:02
方案已确认14:02

步骤 1 — 导出任务模型与队列接入,按方案执行

apps/api/src/exports/exportJob.ts 添加 ExportJob,入队到现有 BullMQ media-tasks 队列,使用专用 export 任务名。迁移 0121_export_jobs 与方案 schema 完全一致(status 枚举、requested_byreview_idformat)。

14:18
方案已确认14:18

步骤 2 — POST /api/v1/reviews/:id/export 端点

请求校验按方案复用 apps/api/src/validation/ 中的 zod schema。format 接受 mp4-burnin | csv | bundle。返回 202 及任务 id;轮询端点按方案推迟到步骤 6。

14:29
发现14:29

Review.duration_ms 已反规范化且偶尔陈旧

三个 fixture 审阅的 duration_ms 与源资产的探测数据不一致(像是重新上传后从未刷新)。导出改为从 MediaAsset.probe.duration 读取时长——与 playbackManifest.ts 已采用的做法相同。方案不变,仅为后续接触此处的人记下这个陷阱。

14:41
偏离 1/414:41

旧版批注并非总有帧时间戳

方案所述
每条批注都有 frame_ts;烧录渲染器可排序并将它们全部放在时间轴上。
代码揭示
annotations 中约 12% 的行早于迁移 0087frame_ts = NULL——它们是逐帧批注出现前的通用审阅评论。
保守选择
将无时间戳批注排除在视频烧录外;在 CSV 侧车文件中包含它们,frame_ts 留空并设 legacy_comment 标志。导出包中无任何内容被静默丢弃。
待复查
可根据 created_at 相对审阅会话的偏移插值时间戳——决定是否足够诚实以在视频中展示。
15:05
方案已确认15:05

步骤 3 — 通过 ffmpeg drawtext/drawbox 烧录批注,按方案执行

滤镜图构建器在 apps/worker/src/exports/burnin.ts。方案的逐批注叠加窗口(enable='between(t,a,b)')可用;针对 40 条批注压力 fixture 验证——在 worker 实例类型上渲染时间为实时的 1.4 倍,在方案 2 倍预算内。

15:22
偏离 2/415:22

队列将 Date 对象往返为字符串

方案所述
ExportJobPayload.requestedAt: Date,传递到 worker 并与审阅的 updated_at 比较以检测陈旧导出。
代码揭示
BullMQ 将载荷 JSON 序列化到 Redis,因此 worker 收到 ISO 字符串而非 Date。陈旧性比较在单元测试中静默强制转换并通过,但在集成运行的重新投递任务中失败。
保守选择
将载荷类型定为线性格式(requestedAt: string ISO-8601),在 exportConsumer.ts 的 worker 边界解析一次。无自定义序列化器。
待复查
其他 media-tasks 载荷可能有同样的潜在 bug——任务载荷的共享 zod codec 可修复整类问题。
15:48
偏离 3/415:48

Zip 流式传输已存在——舍弃计划的 archiver 依赖

方案所述
添加 archiver 将导出包(视频 + CSV + 缩略图)构建为 zip 流上传到 S3。
代码揭示
packages/media/src/zipStream.ts 已实现带背压处理的流式 zip——它支撑批量资产下载功能,自三月起已在生产环境运行。
保守选择
原样复用 zipStream.ts。少一个依赖,包导出继承与批量下载相同的已测试背压行为。
待复查
zipStream 位于 packages/media 但现在被两个功能使用——清理时值得提升到 packages/shared
16:07
发现16:07

WebSocket 进度通道约定已存在

转码管道发出 media:progress:{assetId} 事件。遵循相同约定——导出发出 export:progress:{jobId}——前端可复用 apps/web/src/hooks/ 中的 useProgressChannel(),而非方案草拟的轮询 UI。也保留了轮询端点作为后备。

16:20
偏离 4/416:20

访客审阅者可查看审阅但无法下载资产

方案所述
导出权限 = 审阅级 ACL:能打开审阅的人即可导出。
代码揭示
工作区级 guest_reviewer 角色授予审阅访问但 can_download_assets = false——客户用它让外部利益相关方评论而不将媒体带出。仅审阅 ACL 检查会让访客通过导出外泄源视频。
保守选择
对所有格式,guest_reviewer 的导出返回 403,镜像 assetAccess.ts 中现有资产下载检查。最严格可辩护的解读。
待复查
访客或许仍应获得 CSV(仅批注,无媒体)。这是产品决策,不是我的——记录在下方。
16:33
待人工处理16:33

决定访客审阅者导出策略

当前行为(来自偏离 4):访客在所有导出格式上均获 403。若你希望访客导出无媒体的批注 CSV,在 exportPolicy.ts 中约 10 行改动加一个测试即可——但这会改变客户对「无法下载资产」的理解,应由你决定。不阻碍合并或 QA。

16:51
待人工处理16:51

确认导出包的保留窗口

方案未指定生成包的 TTL。我将它们放在 exports/ S3 前缀下,使用 7 天生命周期规则,与分享链接过期一致——保守,且客户可见内容未承诺更久。但若合同或数据保留政策另有规定,infra/s3.tf 中的生命周期规则是唯一需改的一行。