# Python 网关分支审查与修正清单 日期:2026-09-13。状态:**历史分支审查已完成;本文保留迁移前 `cmd/docker_gateway`、Docker wrapper 和容器生命周期路径作为问题证据,不是当前生产入口。当前入口与状态以 `browser_gateway`、部署说明和 native 验证记录为准。** ## 1. 范围、结论与证据边界 - 审查分支:`feat/python-gateway-douyin`。 - 基线:`origin/main@1fbf126b21a7d6de45ade70121785075542363d9`。 - 分支提交:`860ecaf7ead08bbd1af2c0d8af3604270da78095`,另包含当前未提交及未跟踪文件。不是只审查最近一次提交或 dirty diff。 - 审查前已获取远程信息;远程主线是当前分支祖先,没有待合入的主线提交。当前分支没有配置 upstream。 - 范围包含 58 个已跟踪差异文件,以及 Python 网关、事件监听、依赖锁文件等 11 个未跟踪新增文件;本报告是另行新增的交付物。 - 四个独立只读审查分别覆盖网关迁移、平台动作与事件、数据业务与采集、前端与文档;汇总时合并了重复发现,并复核关键调用链。 - **本轮修正已覆盖网关、事件、动作、采集计划、账号一致性和前端确定缺陷,并同步了部署/计划文档;未重置数据库、未提交 Git,也未进行真实平台写操作。** 下列“已修正”条目以当前代码与本地回归证据为准;真实平台、供应商和浏览器视觉证据仍不能由本地测试替代。 ### 1.1 总结 最紧急的问题不是缺少几个页面,而是已有路径会产生错误结果: 1. **没有可靠的新旧事件边界**,此前未记录的旧通知可能在重连后触发自动发送。 2. **并发创建可能误删成功容器**,超时的浏览器脚本也可能在锁释放后继续写入。 3. **代理环境不能正常走通**:Go 漏传代理标识,HTTP/HTTPS 上游代理握手又存在协议解析错误。 4. **常用页面不可用或串数据**:设置保存返回 400,工作台切换可崩溃,账号迟返请求可能覆盖另一账号资料。 5. **人工发送链路不完整**:内部 ID 未转平台 ID、刷新后丢失原操作标识、失败与不明分类及证据不足。 6. **采集计划、账号状态、租约和凭据更新存在数据一致性问题**。 7. **媒体、生产 AI、私信接收/历史及页面推送仍有实际接线缺口**,不是仅等待真机验收。 ### 1.2 优先级及状态口径 | 标记 | 含义 | | --- | --- | | P0 | 可能造成未获批准的真实平台写入;相应自动能力在修正并验证前不可视为可用 | | P1 | 阻断核心功能,或可能误删、串号、丢事件、错误判定;合并前应解决 | | P2 | 局部正确性、边界、错误反馈或可维护性问题;仍须列入修正,不默默排除 | | 确定缺陷 | 由当前代码及调用链可推导触发场景;除特别说明外,本轮没有运行复现 | | 实现缺口 | 缺少实际执行或界面接线,不能用模型、状态表或 Mock 结果替代 | | 待真机验证 | 代码之外的平台行为和运行证据不足,不据此断言平台支持或不支持 | GW01—GW11、EV01—EV14、BE01—BE19、UI01—UI09 和 T01 的代码修正已完成并有本地回归;T02、DOC01—DOC06 已按当前证据修正。GAP01—GAP10 已补齐可由本地代码完成的执行、持久化和界面闭环,但真实平台、供应商质量、浏览器视觉及小红书能力仍需外部证据;GAP11 仍明确阻断。P0 的安全门仍有效:边界不明或结果不明不得自动写入或重试。 ## 2. 网关生命周期与迁移缺陷 ### GW01 · P1:并发创建可能删除另一个请求成功创建的容器 - **证据与影响:**`cmd/docker_gateway/gateway.py:170-177,241-252,322-366`。两个相同 alias、binding_version 的停止态创建请求都在锁外查到不存在;第二个获得锁后未重查,Docker create 返回 409,清理函数在 `created_id` 为空时按相同标签删除第一个请求的容器。持久卷不会因此删除,但已成功创建的容器会消失。旧 Go 实现在锁内再次检查 alias。 - **修正:**锁内重查;明确的创建冲突不能进入“本次创建结果未知”的删除路径。所有清理必须证明资源属于本次操作。 - **验收:**线程屏障制造该竞争;仅一个创建成功,另一个 409,成功容器没有收到 DELETE。 ### GW02 · P1:HTTP/HTTPS 上游代理 CONNECT 残留响应头破坏 TLS - **证据与影响:**`cmd/docker_gateway/proxy.py:366-380,477-491,511-523`。`_read_status` 只读状态行,剩余响应头和空行随后作为隧道数据转发给浏览器。上游返回 200 不代表 HTTPS 可以使用。旧 Go 使用完整 HTTP 响应解析。 - **修正:**完整解析并消费响应头,限制大小和时间,保留响应头之后已经到达的真实隧道数据。优先采用成熟解析能力,不继续增加不完整手写分支。 - **验收:**无附加头、多头、分片头、头与首个隧道包同批到达;浏览器收到的首字节必须是真实隧道数据。现有测试只检查 socket 和认证头,不足以证明转发正确。 ### GW03 · P1:停止可被空闲连接无限阻塞,45 秒宽限不等于正常退出 - **证据与影响:**`cmd/docker_gateway/gateway.py:874-886,1018-1037,1427-1446`、`compose.yaml:52`。HTTP/1.1 非 daemon 请求线程没有空闲/读取超时;`server.shutdown()` 不关闭已有连接,`server_close()` 会等待这些线程。控制面正常的 `http.DefaultClient` 就可能留下 keep-alive 连接。半截请求体也可造成阻塞。清理顺序还先关闭订阅和代理,再等待在途请求。 - **修正:**明确停止接收、回收空闲连接、在途请求期限和依赖关闭顺序。不能仅改 daemon 线程来掩盖未完成清理。 - **验收:**隔离进程覆盖空闲连接、半截请求体、活跃请求、排队请求;记录正常退出码、耗时、未发生 SIGKILL 和 reservation 释放。此前 47 秒重启记录不能证明优雅退出,详见第 6 节。 ### GW04 · P1:冷启动拉取镜像时丢失 digest - **证据与影响:**`cmd/docker_gateway/docker_client.py:142-160,584-586`。`repo@sha256:…` 被拆开后,只将 repository 传给 `fromImage`,digest 未传出,可能拉取无关默认 tag,随后原 digest 创建失败。 - **修正:**digest 引用完整传给 Docker;普通 tag 引用才按相应契约拆分。 - **验收:**直接断言 Docker 请求 query,覆盖缺本地 digest、已存在 digest 和普通 tag,不仅测试拆字符串函数。 ### GW05 · P1:删除校验拒绝控制面的 `runtime-not-found` 清理语义 - **证据与影响:**`cmd/docker_gateway/gateway.py:1180-1205`;`cmd/control-plane/hub.go:71,140-145,196-225,1789-1794`。创建失败后只剩已知网络,控制面用 `runtime_id="runtime-not-found"` 清理;Python 对非空 ID 强制 64 位十六进制,提前返回 400,残留网络不能收敛。 - **修正:**保留现有 `/v1` 契约时,仅删除路径解释“容器不存在、清理指定网络代”;若实际存在替代容器则必须拒绝。启动和写操作仍须真实容器 ID,不放宽通用检查。 - **验收:**Go 实际生成的 payload 经过 Python HTTP 路由,覆盖仅网络、两者皆无、alias 已被新代占用。 ### GW06 · P1:网络初始化失败丢失代信息,部分副作用未回滚 - **证据与影响:**`cmd/docker_gateway/docker_client.py:393-475`;`gateway.py:190-192,316-333,474-485,929-947`。创建网络后 inspect 失败不一定携带已知 generation;调用方尚未完成赋值。`restore_proxy` 的网络初始化又位于回滚 try 之外。连接部分成功后抛错可留下半完成网络,HTTP 错误也未保留清理所需网络 ID。 - **修正:**首次副作用后立即保留已知代;所有后续错误经过统一回滚边界,返回已知网络代和清理结论。 - **验收:**逐点注入创建后 inspect 失败、runtime 已连接但 gateway 连接失败、操作生效但响应丢失;仅清理本次代,错误记录保留恢复依据。 ### GW07 · P1:单个浏览器网络异常使整个 gateway 无法列举和对账 - **证据与影响:**`cmd/docker_gateway/gateway.py:144-147`、`docker_client.py:231-252`;`cmd/control-plane/hub.go:927-970`。列表为每个 running 容器同步解析网络 IP,任何一个缺网络/IP 或中途消失就使列表整体失败,控制面跳过整个 gateway 的环境对账。 - **修正:**清单读取与 CDP 地址解析分离;异常环境仍应可识别、可单独恢复,不能返回假地址或假成功。 - **验收:**正常、缺网络、中途消失三个环境共存;正常记录可读,异常记录有真实状态,其他环境仍能对账。 ### GW08 · P1:CDP 超时释放锁后,页面脚本仍可能继续写入 - **证据与影响:**`cmd/docker_gateway/douyin.py:34,64-79,136-155,915-924`、`gateway.py:617-639`。CDP 固定 15 秒,脚本含 20 秒写请求和多个串行读,IM SDK 没有总期限。关闭 CDP 不会取消页面 Promise;Python 返回并释放 alias 锁时,旧脚本可能继续提交,后续动作与它重叠。 - **修正:**统一 HTTP/CDP/脚本预算,明确超时后仍在途的动作所有权与核验方式。不能把“连接关闭”当“写入已停止”,不明操作不得自动补发。 - **验收:**离线脚本或 CDP 替身使写入跨过 15 秒;证明旧动作未结束时后续写不会穿透互斥,结果有最终证据或明确不明状态。 ### GW09 · P2:普通 HTTP 代理丢弃 chunked 请求体 - **证据与影响:**`cmd/docker_gateway/proxy.py:226-267,307-333`。只识别 Content-Length,却保留 Transfer-Encoding 转发;合法 chunked 请求变成有声明、无正文/终止块,导致挂起或失败。 - **修正:**采用完整 HTTP 请求解析/转发能力,或正确处理分块与 trailer;不能静默丢正文。 - **验收:**分片 chunk、终止块、trailer、大小限制及异常中断,上游正文必须完整一致。 ### GW10 · P2:校验接受的默认值与执行不一致,缺字段返回 500 - **证据与影响:**`cmd/docker_gateway/gateway.py:186,1064,1082-1088,1120,1219-1220`。停止态直连创建省略 `network_exit_id` 能通过验证,但后续直接索引触发 KeyError;代理恢复缺代字段也在正式校验前直接索引,变成 500。 - **修正:**统一完成默认值归一化及必填检查,然后才允许副作用。 - **验收:**通过完整 create/HTTP 路由验证合法省略成功、缺必填返回 400,非法请求没有 Docker 调用。 ### GW11 · P2:删除时“清理待完成”的 `/v1` 响应被改变 - **证据与影响:**`cmd/docker_gateway/gateway.py:421-427,929-950`;`cmd/control-plane/hub.go:1789-1796`。旧实现对可重试网络清理失败返回 `202 runtime_cleanup_pending`,新实现传播为 502,控制面仍依赖 202 继续恢复。 - **修正:**恢复待清理语义及载荷,代冲突仍为 409。若改变契约,须另行批准并同步控制面,不能称为保持 `/v1` 不变。 - **验收:**一次暂时失败后的完整控制面请求序列;验证 202、409、真正失败分别处理。 ## 3. 平台动作与事件缺陷 ### EV01 · P0:首次及恢复监听没有可靠基线,旧通知可能触发自动发送 - **证据与影响:**`cmd/docker_gateway/douyin.py:532-553,575-581,894-906`;`cmd/control-plane/creator_events.go:248-274`;`internal/creator/actions.go:245-312`。只给 reconnected 控制消息设置 baseline,Go 又只记日志;普通 notice 未经过历史边界判断。此前未入库的旧通知,只要策略和冷却允许,就可能执行。**已入库终态事件仍有去重,不应夸大为所有旧事件都会重放。** - **修正:**建立并持久保存经平台验证的新旧边界和恢复分类;边界不明、启用前、断连期间事件仅记录,不自动写入。不能用本机启动时间或重连成功冒充平台连续性证明。 - **验收:**首次历史、停用期间、断连、缺时间、重启、冷却到期旧通知均零自动写;只有已确认新边界事件进入执行,分类理由可查。 ### EV02 · P1:事件在持久接收前被删除,错误可造成不可恢复丢失 - **证据与影响:**`cmd/docker_gateway/douyin.py:509-529,625-647,670-675,910-912`;`cmd/control-plane/creator_events.go:194-209,274-278`。浏览器 `splice`、Python `popleft` 都先删除;单条详情失败会终止批次,Go 保存失败仅记日志,重新 start 停掉旧队列,队列溢出还清空全部事件。 - **修正:**先持久接收再确认删除,可重复读取;单条失败隔离,溢出和无法恢复的缺口持久可见,不擅自补发。 - **验收:**取队列后断网、响应丢失、数据库首次失败、首条详情失败、溢出;事件可恢复或明确记录缺口,不能只以重连成功验收。 ### EV03 · P1:接收线程同步等待写动作,掩盖后续事件处理延迟 - **证据与影响:**`cmd/control-plane/creator_events.go:194-209,274`;`internal/creator/actions.go:486-503`、`migrations/017_creator.sql:192`。一批逐条等待完整自动动作,首条耗时超过 5 秒便阻塞后续;received_at 使用延后的入库时间,未保留最早接收时间。 - **修正:**快速接收和持久登记与耗时执行分离;仍按执行账号串行写。分别保存平台时间、网关接收、处理开始和结束时间。 - **验收:**首个执行器阻塞 10 秒时,第二个事件仍在收到后 5 秒内进入处理,时间记录不能后移掩盖排队。 ### EV04 · P1:后台完成动作被记录为“页面已展示” - **证据与影响:**`internal/creator/actions.go:271-275,501`、`settings.go:61-68`。后台完成时写 displayed_at,即使从未打开页面,也有“展示”记录,30 秒指标失真。 - **修正:**动作完成与页面展示分别记录;展示时间只能来自实际显示该事件的客户端确认。 - **验收:**不开页面时 displayed_at 为空;显示后才记,并与动作完成时间独立。 ### EV05 · P1:Douyin 请求漏传代理标识,非直连环境被拒绝 - **证据与影响:**`cmd/control-plane/hub.go:140-145` 的 `gatewayGenerationPayload` 不含 network_exit_id;`creator.go:685,727,740`、`creator_events.go:167` 复用它;Python `gateway.py:695-704` 强制比对代理标签,非直连请求变成 409。直连成功证据不能覆盖。 - **修正:**Douyin 请求统一携带当前有效代和 `environment.Exit.ID`,保留校验;不要顺带混入删除清理语义。 - **验收:**Go 真实 payload 穿过 Python 校验,正确代理通过,缺失/错误代理及旧代拒绝,直连继续通过。 ### EV06 · P1:每次重灌 Cookie,阻断有效人工登录并销毁监听 - **证据与影响:**`cmd/control-plane/creator.go:661-684,832,936`;`cmd/docker_gateway/gateway.py:529-538`、`douyin.py:266-318`。先强制读取存储凭据,再清 Cookie、导航、设置 Cookie;未先复用有效会话。保存凭据为空或过期时,实际已登录账号仍失败;导航销毁监听,SDK 初始化与后设 Cookie 的身份也未证明一致。 - **修正:**先核验有效会话并复用;只有确需恢复登录才注入批准的凭据,再重建并核验页面身份和监听边界。不能通过伪造 Cookie 绕过缺陷。 - **验收:**有效人工会话无存储 Cookie 也可使用;重复采集不导航/清 Cookie;必要恢复后身份、SDK 与监听一致。 ### EV07 · P1:采集与写执行器支持的凭据格式不一致 - **证据与影响:**`cmd/control-plane/creator.go:665-672,809-816,913-920`;`internal/douyin/connector.go:245-287`。采集支持 header 和 JSON bundle,写执行器仅接受 bundle;合法 header 账号到平台写入前就失败,却可能被标不明。 - **修正:**共用已批准格式的解析入口,解析错误明确属于未执行;与会话复用一起修正,不强制覆盖会话。 - **验收:**两种合法格式经过采集和写入口;非法格式零平台写调用且状态准确。 ### EV08 · P1:内部评论/作品 ID 原样下发,人工动作无法执行 - **证据与影响:**`web/src/CreatorWorkbenchPage.jsx:237-243,274-283`;`internal/creator/actions.go:599-618,716`;`cmd/control-plane/creator.go:688-690`;`cmd/docker_gateway/douyin.py:395-408`。内部 comment-/work- ID 不等于数字平台 ID;人工回复还允许缺作品 ID。数据库替身执行器不会暴露该问题。 - **修正:**共享执行边界解析 comment_key、work_key、评论所属作品,并核对平台、作者和关联关系;区分通知自带的平台 ID,不让 UI 拼猜。 - **验收:**从本地记录创建回复、评论点赞、作品点赞和转发,网关收到正确平台目标;缺关联、错作者、跨作品均零写入。 ### EV09 · P1:目标评论仅在前 50 条查找 - **证据与影响:**`cmd/docker_gateway/douyin.py:921` 的 `ACTION_SCRIPT.findComment` 固定首屏,合法目标在后续页就误报 TARGET_MISMATCH。 - **修正:**使用已验证的单条详情或正确分页定位;区分读取失败、不可获取和真正身份不匹配。 - **验收:**第二页目标可以核验;分页失败不报错身份;真正作者/作品不符则拒绝写入。 ### EV10 · P1:写后核验失败、确认字段缺失被错误归为“明确失败” - **证据与影响:**`cmd/docker_gateway/douyin.py:918-924`;`cmd/control-plane/creator.go:700-722`。点赞 POST 已成功,核验 GET 限流被设 definitive;follow/like/IM 缺确认字段也当明确拒绝。实际可能已写入,错误失败提示可误导另发。 - **修正:**区分写前失败、经验证的明确平台拒绝、写后确认不足。写后读失败不能证明未发送,缺字段不得自动等价为拒绝。 - **验收:**执行实际脚本逻辑的离线测试:写成功后核验失败、缺字段、SDK 空结果均为不明;明确拒绝才失败,全部禁止自动重发。 ### EV11 · P1:丢弃平台结果标识,评论成功证据不充分 - **证据与影响:**`cmd/docker_gateway/douyin.py:921,924`;`cmd/control-plane/creator.go:700-718`。IM 返回的消息、会话标识被 Go 解码结构丢弃。评论可能缺 ID、正文和明确作品标识,只凭发送者就成功,缺作品标识还用请求值补齐;持久结果仅固定字符串,无法查证。 - **修正:**保存最小稳定平台标识、目标及实际确认内容;不得用请求值代替响应证据,不记录无关完整私信。 - **验收:**缺评论 ID、正文错误、父评论不符不能成功;成功私信在操作记录中可查稳定消息和会话标识。 ### EV12 · P1:排队后不重核状态,已停用的动作仍可能发出 - **证据与影响:**`internal/creator/actions.go:475-486,698-716`;`cmd/control-plane/creator.go:650-693`;`cmd/docker_gateway/gateway.py:617-630`。账号业务检查、策略选择在网关锁排队前完成;锁内仅核代和平台身份。等待期间禁言、撤销授权、停策略、改关系没有最终阻止点。 - **修正:**人工和自动共用按账号执行协调,取得执行权后、实际提交前重核当前条件;自动动作还须检查原策略与关系。 - **验收:**第二个动作排队时逐项改变条件,后续零平台写;已经发出的动作保留真实结果,不能假装撤销。 ### EV13 · P1:转发确认了文案,却不发送文案 - **证据与影响:**`cmd/docker_gateway/douyin.py:409-412,921`;`cmd/control-plane/creator.go:691`。入口要求非空文案,但转发请求不使用 `p.text`,仍报 reposted/succeeded,确认内容与实际行为不符。 - **修正:**先验证对应平台带文案语义;若只有无文案推荐,明确能力差异并取得用户裁决,不静默丢文案。 - **验收:**确认文本进入真实动作且结果一致;不支持时明确阻止,不宣称带文案转发成功。 ### EV14 · P2:同页面重连累积旧事件处理器 - **证据与影响:**`cmd/docker_gateway/douyin.py:532-553,894-906`。同 key 新安装前未调用旧 dispose,关闭 CDP 不会移除页面 addEventListener;stop 只释放最新一组。 - **修正:**重装前清理旧状态,安装失败也清理本次处理器。 - **验收:**同页面重复重连后处理器数量固定,stop 后为零。 ## 4. 账号、采集、计划及持久数据缺陷 ### BE01 · P1:旧作品首次发现后,指标间隔状态错误 - **证据与影响:**`internal/creator/metrics.go:138-155,171-182`。T+2 小时首次发现,将 next_plan 推到 T+3 小时,却保留 1 小时间隔;之后推到 T+5 而非 T+7。 - **修正:**根据发布时间和当前时刻统一计算完整计划状态,不能只改下次时间。 - **验收:**T+2、T+8、T+32 首次发现后连续多个计划点及重启恢复,符合 1、3、7、15、31、55…累计时刻。 ### BE02 · P1:固定采集计划按完成时间漂移 - **证据与影响:**`cmd/control-plane/creator.go:852-853`;`internal/creator/collection.go:255,266-270`。T 开始、T+5 分钟完成的 30 分钟任务,下次变成 T+35;不符合固定起点与迟到跳到严格未来点。 - **修正:**持久保存起点及未来计划点,完成时间仅记实际执行。 - **验收:**慢任务、错过多个周期、重启、暂停恢复后不漂移、不回放历史周期。 ### BE03 · P1:修改设置没有重设已有计划 - **证据与影响:**`internal/creator/settings.go:32-57`;`metrics.go:138-141,171`。仅改 settings,账号未来时间不重设,指标计划混用旧间隔/倍数与新年龄。 - **修正:**一致重算账号、评论未来起点和作品未来计划,保留历史快照。 - **验收:**有存量计划时逐项修改间隔、倍数、上限、年龄;未来按新规则,历史不改、不补造。 ### BE04 · P1:普通自有账号不参与作品和一级评论补充采集 - **证据与影响:**`internal/creator/collection.go:259`;`cmd/control-plane/creator.go:883-891`。到期查询强制 big_account=true,将 W1 的自有账号错误缩减为大号。 - **修正:**采集资格与大号自动响应模式分离。 - **验收:**普通账号、大号、小号符合条件时均可采集,只有自动响应受大号模式控制。 ### BE05 · P1:封禁、注销账号仍可被选作采集账号 - **证据与影响:**`internal/creator/collection.go:264-270`;`cmd/control-plane/creator.go:798-804,903-908,955-964`。检查授权但不检查 banned/deleted 业务状态,自动选择与手动指定均受影响。 - **修正:**所有采集入口复用同一资格检查。 - **验收:**自有、竞品自动选择、竞品手动指定三路均拒绝封禁/注销且零平台请求;历史仍可查。 ### BE06 · P1:自有采集的登录/验证阻断仍被自动重试 - **证据与影响:**`cmd/control-plane/creator.go:913-945`;`internal/creator/collection.go:189-193,265-270,346-347`。身份失败未持久 blocked,分页 challenge/401/403 统一 failed,下轮再次选择。 - **修正:**区分可恢复网络错误和需人工处理的阻断,贯穿身份检查到分页,提供明确人工恢复。 - **验收:**身份不符、验证码、401 后下一轮零请求;网络暂时失败按批准计划恢复。 ### BE07 · P1:采集租约不续期,且未覆盖页面数据写入和结束状态 - **证据与影响:**`internal/creator/collection.go:127-136,157-186,316-342,398-408`;`content.go:94-139`。阶段超过十分钟后断点保存失败;旧持有者被接管后仍可写作品/评论/指标。MarkCompetitorSync 无所有权条件,旧请求可覆盖新结果或暂停状态。 - **修正:**一致续期;一页数据与断点更新受有效领取条件共同保护,最终状态同样核对所有者和暂停状态。 - **验收:**长任务、到期接管、旧请求迟返、运行中暂停恢复;旧持有者不能覆盖新代结果。 ### BE08 · P1:资料编辑绕过大小号关系和策略停用规则 - **证据与影响:**`internal/creator/accounts.go:124-143,173-205`;`cmd/control-plane/creator.go:65-76`。profile 更新直接设 big_account,绕过专用入口的小号晋升检查;关闭再开启或非正常再恢复,不要求重启用策略。 - **修正:**共享事务性状态变更规则,停用所有受影响策略;恢复正常不自动恢复策略。 - **验收:**资料入口和专用入口分别覆盖小号晋升、关闭开启、正常→异常→正常,非法关系拒绝且策略不偷偷恢复。 ### BE09 · P1:小号不同归属冲突被吞掉,并发关系检查有空窗 - **证据与影响:**`internal/creator/accounts.go:185-194,248-268`;`migrations/017_creator.sql:149-155`。小号已归 A,关联 B 时 ON CONFLICT DO NOTHING 仍返回成功;关联与晋升各自先查后写,可并发形成非法角色状态。 - **修正:**同关系重复可幂等,不同归属明确冲突;按一致顺序锁相关账号再检查。 - **验收:**同关系重复、不同归属、关联与晋升双事务竞争,返回结果与实际状态一致。 ### BE10 · P1:无文案动作也进入 AI,失败后占用冷却 - **证据与影响:**`internal/creator/actions.go:411-440`、`logic.go:178-179`;`cmd/control-plane/creator.go:28`。仅判断 text==空,关注/点赞无文案也调用 generator;生产为 nil,动作失败并已占冷却。 - **修正:**只为需要文本的动作选择或生成文案,不改动冷却对已开始动作的既定语义。 - **验收:**关注、作品点赞、评论点赞在 generator=nil 时执行一次且零 AI 调用;文本动作仍检查配置。 ### BE11 · P1:策略启用不校验 AI 必要条件 - **证据与影响:**`internal/creator/actions.go:82-110,152-176,366-440`。缺候选文本、有效 AI 配置或回复要求仍可启用,收到事件后才失败并可能占冷却。 - **修正:**创建、修改、专用启用入口统一校验动作实际需要的条件,不把“审批”布尔值当可用客户端。 - **验收:**三个 enabled=true 入口缺配置均拒绝且无冷却记录;无文案动作不被错误限制。 ### BE12 · P1:同一作品第二来源无法匹配对应线索规则 - **证据与影响:**`internal/creator/rules.go:138-144`;`content.go:222-247`。作品已有多来源表,但分析仍只比最初 SourceType;owned 后关联 competitor,competitor 规则被误拒,反向同样。 - **修正:**使用实际来源关联判断规则范围。 - **验收:**双来源两种入库顺序、各来源专用规则都正确,同评论仍只形成一条线索。 ### BE13 · P1:UID 登记值被当作 SecUID 查询作品 - **证据与影响:**`internal/douyin/creator_collector.go:25-54`;`cmd/control-plane/creator.go:939-943`。核验允许 UID/SecUID/UniqueID 匹配,却不保留规范映射,后续原样放进 sec_user_id。旧 connector 使用身份响应的 SecUID。 - **修正:**身份核验输出平台规范标识映射,各接口使用其要求的标识。 - **验收:**UID 和 SecUID 登记同账号,最终作品请求均用正确 SecUID;随后补真实只读证据。 ### BE14 · P1:密码更新失败可能删除原有密码 - **证据与影响:**`internal/creator/accounts.go:111-139`。新密码先覆盖固定 secret key,数据库失败后删除该 key,旧密码已经无法恢复,数据库却可能仍显示已配置。 - **修正:**新值使用独立引用,成功关联后再清理旧值;失败只清本次新建项,结果不明需核验关联。 - **验收:**已有密码时注入保存失败、数据库失败及结果不明,旧有效凭据不丢,配置标记与引用一致。 ### BE15 · P2:冷却时长没有溢出上限 - **证据与影响:**`internal/creator/accounts.go:90-93`;`actions.go:390-394`。巨大正数转 time.Duration 再乘秒可能溢出到过去,冷却失效。 - **修正:**复用已有时长边界校验。 - **验收:**上限、上限+1、int64 最大值;拒绝溢出,历史占用不改变。 ### BE16 · P2:未来发布时间作品被过滤掉,无法进入待核验状态 - **证据与影响:**`internal/creator/collection.go:317-320` 先按窗口跳过,`content.go:203-207` 的 future 标记无法到达。 - **修正:**可靠窗口外和时间无效/未来分别处理;后者保留待核验并说明范围不完整。 - **验收:**正常、窗口外、缺时间、未来时间同批输入;异常时间不丢资料、不建年龄计划。 ### BE17 · P2:AI 缺失判定字段被当作明确否定 - **证据与影响:**`internal/creator/bailian.go:110-117,127-134`。`{}`、null、缺 match 或 match=null 解码为 bool false,后续变成非线索而非分析失败。当前客户端尚未接生产,接线前也须修复。 - **修正:**区分字段缺失/null 和明确 true/false,验证必需依据。 - **验收:**上述不完整样例均失败;合法真/假按真实依据保存。 ### BE18 · P2:迟到或重复消息倒退会话最近时间 - **证据与影响:**`internal/creator/actions.go:755,804-816`。消息去重前先无条件更新 last_message_at,旧消息即使重复未插入,也改变会话排序。 - **修正:**消息写入和摘要更新一致,最近时间不得被旧/无时间消息覆盖。 - **验收:**新→旧→重复旧→缺时间消息,时间不倒退且不重复消息。 ### BE19 · P2:作品列表持有连接时再查询,低连接池下互相等待 - **证据与影响:**`internal/creator/content.go:368-384`;`store.go:56`。外层 rows 未关闭,又逐作品查询来源;并发占满池后都等待内层连接。 - **修正:**先读取并关闭外层 rows,再批量读来源,或一次查询完成,避免嵌套占用。 - **验收:**连接池设为 1 时带来源列表仍在期限内返回,再覆盖并发列表。 ## 5. 前端确定缺陷 人工目标 ID 错配统一见 EV08;AI、媒体、登录、推送缺口见第 7 节,不重复计数。 ### UI01 · P1:设置加载后原样回传只读字段,保存返回 400 - **证据与影响:**`web/src/CreatorSettingsPage.jsx:30-31,43`;`internal/creator/models.go:23-52`;`cmd/control-plane/creator.go:39-44,604-611`。GET 含 updated_at,完整合入 form 并 PUT;SettingsUpdate 无此字段,严格解码拒绝。现有 Mock fixture 漏了 updated_at。 - **修正:**仅发送可编辑字段,保持后端严格校验。 - **验收:**真实 GET 结构加载后保存,经真实编码/解码成功;请求无只读字段。 ### UI02 · P1:工作台切换页签将旧数据按新类型渲染,页面崩溃 - **证据与影响:**`web/src/CreatorWorkbenchPage.jsx:41-42,79-150,501,599-609,800`。共享 data,评论切规则时立即计算 include_keywords.join,切线索时访问 lead.comment.id;pending 包裹不能阻止 children 提前计算。 - **修正:**按页签隔离数据或明确数据归属,不匹配时不计算列表。 - **验收:**各类型非空数据逐对切换,延迟/乱序返回,始终不崩溃、不串表。 ### UI03 · P1:人工操作标识仅在页面内存,刷新恢复可变成第二次发送 - **证据与影响:**`web/src/CreatorWorkbenchPage.jsx:63-74,237-283,331-347`。响应丢失后刷新,原 key 不可恢复,再确认生成新 key。服务端同 key 去重仍正确,但无法识别原操作的恢复。不是说用户永远不能再发同内容。 - **修正:**复用持久操作记录,恢复原标识和冻结载荷;不明只查询,确需另发必须明确新确认并关联原操作。 - **验收:**创建/执行响应各自丢失,页面重挂和服务重启仍只执行一次;恢复不创建新发送。 ### UI04 · P1:局域网 HTTP 下直接调用 randomUUID 可能使发送入口失效 - **证据与影响:**`web/src/CreatorWorkbenchPage.jsx:35-36,243,331`。部署允许非 localhost HTTP,但 crypto.randomUUID 依赖安全上下文;直接调用无能力处理。该环境兼容性结论来自 API 约束,本轮未作浏览器复现。 - **修正:**使用适配批准部署方式的可靠操作标识机制,结合 UI03 持久恢复;不降为弱随机或吞错。 - **验收:**randomUUID 缺失替身及实际局域网 HTTP,回复和私信都能正常取得并恢复标识。 ### UI05 · P1:账号切换未隔离迟返请求,可显示和保存另一账号资料 - **证据与影响:**`web/src/CreatorAccountsPage.jsx:105-168,289`。A 的读取/保存等待时切 B,A 迟返仍 setForm/setStrategies;B 标题下出现 A 数据,继续保存可能覆盖 B,策略操作也可能落到 A。 - **修正:**结果绑定发起账号,失效响应不应用;提交中处理目标切换,未保存修改明确确认。 - **验收:**两个账号明显不同资料和策略,打乱读取及保存响应;任何时刻均不串用或错操作。 ### UI06 · P1:私信账号选择被当前会话平台锁住 - **证据与影响:**`web/src/CreatorWorkbenchPage.jsx:621-640`。顶层账号选项按当前会话平台过滤,有抖音会话时另一平台账号消失,无法正常跨平台切换。 - **修正:**先独立选账号,再清旧会话并读目标账号数据。 - **验收:**两平台各有会话账号双向切换,选项始终可选,请求仅属于新账号。 ### UI07 · P2:所有非成功状态都显示“结果不确定” - **证据与影响:**`web/src/CreatorWorkbenchPage.jsx:287-293,352-358`。failed、blocked、processing 被同文案覆盖,隐藏确定原因和在途状态。 - **修正:**按后端真实状态及原因展示,并关联原操作;不得给不明状态提供重新发送式重试。 - **验收:**成功、失败、阻止、处理中、不明五种结果展示准确。 ### UI08 · P2:读取失败被当作空数据 - **证据与影响:**`web/src/CreatorAccountsPage.jsx:105-114`、`CreatorCompetitorsPage.jsx:72-77`。策略/账号失败直接清空数组,误报尚无策略或可用账号。 - **修正:**错误独立保存,保留已知数据,提供只读重试,依赖数据的写入口禁用。 - **验收:**首次失败和有数据后刷新失败均不得冒充真实空列表。 ### UI09 · P2:关键词输入清理规则与后端和需求不一致 - **证据与影响:**`web/src/CreatorWorkbenchPage.jsx:28-32,213-214`;`internal/creator/logic.go:102-115`。前端 trim 去除更广 Unicode 空白,filter 静默丢空词;后端只清指定普通空白并拒绝空词。 - **修正:**统一有限清理规则,空词明确拒绝,不改变原文匹配语义。 - **验收:**全角空格、NBSP、普通空白、连续逗号;界面与直接 API 输入一致。 ## 6. 必须纠正的测试、部署和文档结论 ### T01 · P1:覆盖率统计必须排除测试代码 当前命令固定为 `python3 -m coverage run --source=cmd/docker_gateway -m unittest cmd.docker_gateway.test_gateway`,随后执行 `python3 -m coverage report --include='cmd/docker_gateway/*.py' --omit='cmd/docker_gateway/test_*.py' --fail-under=65`。最近一次本地运行 56 项测试通过,生产代码总覆盖率达到 **65%**;`.coverage` 只作为本次运行产物,不提交仓库。 - **修正:**统计分母排除测试文件,并补 CDP 超时终止、DM 规范化和网络创建异常回收回归。 - **验收:**覆盖率命令必须与同次测试生成的数据对应;逐文件低覆盖率不被总数掩盖,真实平台验收另行登记。 ### T02 · P1:常规测试通过不等于关键数据库和平台逻辑已验证 - `internal/creator/integration_test.go` 无专用数据库地址时仍会 skip;使用 `CREATORHUB_POSTGRES_TEST_URL` 时最近一次已实际运行内容、动作、消息和迁移测试。 - `creator_events_test.go` 覆盖字段/边界转换;新增 stale processing 代码路径仍须在专用数据库环境做崩溃恢复验收。 - Python 动作测试覆盖实际嵌入脚本的关键分类字符串和 CDP 超时终止;不替代真实浏览器/平台写入。 - 前端覆盖错误、忙碌、账号隔离、策略/规则编辑及消息刷新;不替代视觉、真机和外部服务验收。 - **验收口径:**记录 executed/skipped 数量及运行条件;任何 Mock 成功仅证明它覆盖的局部规则。 ### DOC01 · P1:正常退出证据仍需与实现分开记录 当前代码已配置 SIGINT/SIGTERM、请求排空和 45 秒 Compose 宽限,且本地已有特定 reservation 回收证据;这不等于所有 Docker 故障注入、并发和强杀场景都通过。部署文档保留该限制,不把单次重启时长包装成完整生命周期验收。 ### DOC02 · P1:登录要求冲突,不能自行恢复自动登录 `AGENTS.md` 与 `docs/plan01.md` 当前统一为不自动登录。**当前修正仍遵守不自动登录、不暴露密码/Cookie/二维码/验证码。** 有效会话可复用,失效后进入人工登录并由 gateway identity 核验;密码“已保存”不得表述为“登录能力已接通”。 ### DOC03 · P2:采集接线与完整性证据分开 当前文档已表述为:存在数字游标校验、分页请求、保存路径和独立计划;完整性、标识转换、分页边界和平台验收仍未由本地代码证明。接受游标参数不等于证明无漏项。 ### DOC04 · P2:完成表混杂历史与当前轮次 `docs/plan01.md` 当前已按历史文档、代码接线、实际运行证据和外部待验收项分开;新增能力继续按该三类记录,不能用代码接线代替平台成功。 ### DOC05 · P1:浏览器镜像必须由仓库源码和 immutable digest 管理 当前分支新增 `docker/browser-wrapper/`,以显式 `BROWSER_BASE_IMAGE` digest 构建 Xvfb/CDP 包装入口,并在 deployment 文档记录 base/published digest、构建提交和 Dockerfile。干净环境构建/拉取和真实身份核验仍需拥有已批准的第三方 base digest 后执行;本地 tag 不作为证据。 ### DOC06 · P1:真实证据只证明局部可达,不证明业务成功 上一轮身份读取 200、事件启动 200、空列表、重启后短期无错误,只能证明这些时点的局部可达。不得扩大为基线、无遗漏、处理时限、正常退出或全动作成功。直接 follow 返回 BUSINESS_REJECTED 且后查状态为未关注,不能证明关注能力成功;当前 EV10 已将非 2xx 和写后核验不足保留为不明。控制面 resolve credential 发生在写前,说明 EV06/EV07 所在链路缺陷,不应一概归咎于“只差用户提供 Cookie”。结果不明的原操作仍不得自动重试。 ## 7. 已确认的功能接线缺口 这些项目尚未完成实际流程,不能全部归为“缺真实平台证据”。补齐仍须遵守现有范围、供应商批准与不自动登录等约束。 | 编号/优先级 | 当前证据与缺口 | 修正方向及完成条件 | | --- | --- | --- | | GAP01 · P1 | `creator.go` 已装配独立的到期指标调度,作品发现与指标刷新分离;真实平台请求频率仍需外部验收。 | 本地已验证独立到期读取、年龄和计划边界;真实平台需继续证明过龄零请求。 | | GAP02 · P1 | Bailian adapter 已装配;缺少 key/model 明确返回 unavailable,质量样本和生产供应商仍待批准。 | 本地验证装配、配置校验和失败边界;生产质量与费用由外部验收。 | | GAP03 · P1 | 新增真实下载、FFmpeg 音轨提取和可配置转写命令;原手写 material-step 路由已删除,转写未配置时明确 failed。 | 本地验证产物驱动和失败状态;音视频、无音轨、空间、供应商质量及重试仍需样本验收。 | | GAP04 · P1 | Douyin DM notice 可规范化,入站事件和人工出站结果均写入本地会话,重复键幂等;平台实际 DM payload/history 仍待验收。 | 本地验证账号隔离、重复消息和失败状态;平台历史读取与非文本消息需外部证据。 | | GAP05 · P1 | 事件处理/缺口已持久化,工作台在打开 DM 会话时按 10 秒刷新并支持手动刷新;未新增未批准的双向推送框架。 | 本地验证持久结果恢复;平台关页监听、30 秒可见和真实断连仍待验收。 | | GAP06 · P1 | 账号页显示登录状态、原因、核验时间和凭据标记;login-result 不得直接写 logged_in,只有 gateway identity 核验可写入。 | 本地验证伪造成功被拒;人工登录入口和真机身份重核仍待外部验收。 | | GAP07 · P1 | 策略支持编辑/调序、启停;关系列表已有解除入口,更新沿用既有接口。 | 本地验证账号切换和策略更新;解除/再绑定并发效果仍需集成证据。 | | GAP08 · P1 | 规则支持来源范围、编辑、启停;评论/线索提供人工联系入口,未接自动联系。 | 本地验证来源和人工确认边界;历史判断依据展示仍需补充验收。 | | GAP09 · P1 | 同步状态、错误、下次时间已显示;链接解析预览与完整组合筛选仍未完成。 | 继续补链接解析/组合筛选,真实0与缺值需保持可区分。 | | GAP10 · P2 | 账号切换和素材文稿切换已有放弃确认,DM 草稿继续本地保留。 | 仍需覆盖工作台页签/路由切换的未保存状态。 | | GAP11 · P1 | 小红书当前在 `creator.go:792-794` 等入口明确阻断;登记或列表里有平台选项不等于业务实现。 | 独立完成同范围能力核实与接线;每类缺口单独裁决,不以抖音成功代替,不宣称平台本身不支持。 | ## 8. 建议修正顺序与交付门槛 ### 第一批:限制错误外部写入、误删和丢数据 - EV01、EV02、EV10—EV12:历史边界、持久接收、结果证据和写前条件。 - GW01、GW03、GW06、GW08:创建归属、停止排空、网络回滚、仍在途脚本互斥。 - BE07—BE09、BE14:租约、账号关系/停用及凭据更新一致性。 - 每项先有未修时失败的隔离回归,再改代码;不能用放宽代校验、吞异常、清库或无限重试替代根因修正。 ### 第二批:恢复最小可用闭环 - 代理和 `/v1`:GW02、GW04、GW05、GW07、GW09—GW11、EV05。 - 身份和人工目标:EV06—EV09、EV13、BE13。 - 页面可用性与人工标识恢复:UI01—UI06;无文案自动动作 BE10、启用校验 BE11。 - 验收先经过真实 Go 请求/Python 校验/页面响应契约,再做获批的小规模真实平台动作。结果不明的旧操作仅核验,不能拿新 key 伪装重试。 ### 第三批:计划正确性和剩余功能 - BE01—BE06、BE12、BE15—BE19、UI07—UI09、EV03、EV04、EV14。 - 按 GAP01—GAP11 完成实际缺口;AI 配置、平台差异和自动登录争议先取得必要批准。 - 保留原分阶段顺序:先抖音完整流程,再小红书,最后共存及错误恢复,不用通用框架扩大本期范围。 ### 共同验收门槛 1. 修正清单每项关联回归用例和结果;条目关闭必须有证据,不以“代码已改”关闭。 2. Python 生产代码覆盖率排除测试后至少 65%,Go 数据库测试明确实际运行,关键竞争/崩溃路径有测试。 3. 后端 test/vet/race 和控制面构建,Python 网关测试/构建、Compose 配置与生命周期通过;迁移后的网关不再要求构建已删除的 Go 可执行入口。 4. 前端 lockfile 安装、非交互测试、构建通过,补真实契约、非空切换、双账号/双平台、乱序和刷新恢复测试。 5. 不泄露凭据,不改变账号身份和操作目标,不自动重试不明写入。日志保留操作、账号、代、阶段、错误类别及时间,避免只有笼统错误而无排查线索。 6. 平台事件、恢复连续性、各动作、媒体产物、AI 质量与界面时限分别验收;HTTP 200、容器 running、空事件队列、无日志错误均不能替代。 7. 尚未完成项目必须继续标注未完成;范围调整需明确批准,不能通过修改文档把缺功能包装成已完成。 ## 9. 审查覆盖与局限 ### 9.1 覆盖清单 - **网关新增:**`cmd/__init__.py`;`cmd/docker_gateway/{__init__,docker_client,douyin,gateway,proxy,test_gateway}.py`;`requirements-gateway.lock`、`requirements-gateway-dev.lock`。 - **旧网关删除:**`cmd/docker-gateway/{main,douyin,proxy}.go` 及 `main_test.go`、`douyin_test.go`、`proxy_test.go`、`douyin_cdp_integration_test.go`。重点对照发生契约和清理/代理回归的旧实现;未把每个已删除测试逐一移植或重跑。 - **控制面:**`cmd/control-plane/creator.go`、`creator_events.go`、`creator_events_test.go`、`credential.go`、`main.go`、`hub.go`、`hub_test.go`、`douyin_test.go`;新增及改动路径沿调用链核对。 - **领域:**`internal/creator/` 全部生产 Go 文件、全部现有测试文件、017—026 十份迁移。 - **采集与凭据:**`internal/douyin/{connector,connector_test,creator_collector,creator_collector_test}.go`;`internal/phasea/store.go` 的改动与关联读取路径。 - **前端:**`web/src/Creator{Accounts,Competitors,Settings,Workbench}Page.jsx`、`CreatorPages.test.jsx`、`BrowsersPage.test.jsx`、`Layout.jsx`、`dataProvider.js`、`lib/ui.jsx`、`main.jsx`。 - **部署/文档:**`Dockerfile`、`compose.yaml`、`AGENTS.md`、`docs/plan01.md`、`docs/deployment.md`、`docs/architecture/container-control.md`、`docs/product/compliance-product-plan.md`。另对照未改动的 Compose 开发配置和相关测试。 ### 9.2 未运行或无法从本地代码证明的内容 - 本轮未进行真实平台写操作。所有本地测试、覆盖率和 Compose 配置结果均以交付前实际命令记录为准,不能证明平台业务成功。 - 未实测浏览器布局、键盘操作、局域网 HTTP、自动指纹、两账号全流程、Docker 故障注入、数据库并发、部署正常退出码和真实代理兼容性;这些仍须按原 AC 清单登记运行证据,不能将本报告未发现某项缺陷当作该项通过。 - 平台真实分页完整性、事件 ID/恢复位置、迟到边界、DM 历史范围、带文案转发能力、每类动作成功证据仍待批准的真机验证。 - 本报告不是对主线全部既有代码的全面审计;未改动部分只在解释本分支调用链和迁移契约时对照。 - 未发现问题的文件不等于证明无问题。四路审查的完整原始输出保存在本次会话的 `subagent-artifacts/outputs/8f8a3bc6-a9a0-48fc-9cf4-54a8ab4453b2/` 下,文件为 `review-gateway.md`、`review-events.md`、`review-domain.md`、`review-web-docs.md`。本文汇总后的编号是后续修正跟踪依据。