# 常驻监听网站闭环:独立架构与代码质量审核报告 审核员非实现者。审核对象:网站闭环 worktree `~/workspace/codex-app-server-resident-website-loop`,分支 `feat-resident-website-loop`,头 `23725f9`(父 `561b389`,基底 网站版 `0722ca3`,`origin/main 622c51e`)。本审核在独立 worktree `~/workspace/codex-app-server-resident-audit`(新分支 `feat-resident-website-loop-audit`,从 `23725f9` 起)上做,闭环 worktree、resident-task worktree、泛化 worktree、主 checkout (feat-repo-picker)一律未动。 ## 0. 评分标准(先定标准,再打分) 只评架构与代码质量两项,满分各 10 分。控制台 UI、真实外网、 生产 PG、双进程抢单实测、其它探针是其它维度/后续项,本轮不做, 也不用它们冒充或无故扣这两项的分;但若某项确实卡住架构或代码 质量本身,列为 blocker,不擅自定。 ### 架构 10 分标准 - A1 分层干净:resident 模块自有状态机,与任务状态机、自动化 定义层互不干扰;server 只做路由/权限/审计,worker 只做驱动 边界调用;无循环依赖。 - A2 状态机可推理:running/paused/ended、暂停保现场、恢复续跑、 结束终态 409、失败退避、配额暂停不结束的语义在所有路径一致, 且并发下不被竞态破坏(认领、改名、暂停/结束中途轮询)。 - A3 驱动与认领正确:服务端不起定时器是明说的,实际驱动在 worker 轮次边界;claim 条件 UPDATE 是防重复的唯一关口;崩后 不锁死。 - A4 快照/变化语义干净:轮询廉价、仅变化建任务、首次只建基线、 每次轮询可查;变化摘录与游标推进在同一事务边界内。 - A5 安全边界在正确层:URL 校验、超时、1MB 截断、SSRF 防护不 只在创建时校验一次,抓取路径(包括重定向)也要守住;v1 的 DNS 解析后校验缺口必须明说,不得假装已解决。 - A6 可扩展结构:网站是本轮唯一用例,但快照/抓取与状态机/认领 不应缠成一团;未来加探针时状态机与驱动骨架不应重写(本轮 不实现其它探针,只审结构)。 ### 代码质量 10 分标准 - Q1 命名、函数长度与复杂度可读;长函数(run_due)分支清晰, 不靠注释掩盖逻辑。 - Q2 无重复、无死代码、无测试值渗入生产代码;复用已有口径 (自动化 claim/配额/审计、环境名唯一索引先例)而不是另起一套。 - Q3 错误处理诚实:失败、配额耗尽、配额检查失败三条路不混淆; 审计与轮询记录不静默丢失;异常不炸驱动循环。 - Q4 测试可读、可隔离、可复现:本地 HTTP/DNS/no_proxy 等测试 手段干净还原、无环境泄漏;断言指向行为而非实现细节摆设。 - Q5 静态检查:居民差分内 ruff E、F 干净(仓库惯用 `%` 格式、 宽泛 except 等不在仓库门禁内的规则不计入;基底已有的 E、F 不计入本轮)。 ## 1. 改动前分数 - 架构:9.0 / 10 - 代码质量:9.0 / 10 基底是好的:分层、状态机、claim、配额复用、轮询可查、测试 隔离(DNS/no_proxy 在 loop 测试里干净还原)都属实,定向 15 项 基线全过(见第 4 节)。扣分只来自下面逐项 findings,不是整体 推倒重来。 ## 2. Findings 清单(改动前) ### 严重 - F1 名称唯一性是先查后插、无数据库约束:create_watch / update_watch 先 SELECT 查重再 INSERT/UPDATE,两个并发请求 可同时通过检查、插出同项目同名 running 监听;server 的 environments 已有先例:`CREATE UNIQUE INDEX … WHERE status != 'archived'`。本轮用同款部分唯一索引 `WHERE status != 'ended'`(已结束名可重用)+ 完整性错误转 ValueError 修复。 - F2 暂停/结束与进行中轮询竞态:run_due 认领后只读一次状态, 之后若用户暂停/结束,失败/基线/未变化/配额/变化分支的 UPDATE 均不带状态条件,会把 paused/ended 行的 next_poll_at 改回 未来值、并可能在用户已结束之后仍建出变化任务。修复:认领后 复查状态;全部终局 UPDATE 带 `status='running'` 条件,条件 不满足时回滚、不建任务、不记轮询。 ### 一般 - F3 抓取路径 SSRF 只守了创建时:default_fetch 用默认 opener, 重定向目标不校验,公开 URL 重定向到内网地址可绕过创建校验; 另 localhost.、单整数/十六进制/前导零等非规范 IP 写法可绕过 字面 IP 检查。修复:重定向处理器逐跳校验(相对跳转先 urljoin 再校验);校验时归一化主机(去尾点、*.localhost 挡掉)并拒绝 非规范数字 IP 写法。DNS 解析后校验仍属 v1 缺口,继续列为已知 偏差,不假装已解决。 - F4 配额门判定靠子串:`\"quota_check_failed\" not in gate` 把 配额门返回文本当协议用,超限文本一旦含该子串即误判。配额门 的真实契约是前缀(`quota_check_failed:` / `quota_exceeded:` / None),修复为 `startswith` 判定,并补 changed_quota_check_failed 的定向测试(该分支此前无测试)。 - F5 row_get 重复造轮:resident 自带与 automations.row_get 逐字 同语义的 row_get,却已从 automations 导入另外两个函数。修复: 直接导入 automations.row_get,删本地定义。 - F6 server 审计动作名嵌套三元:`_resident_transition` 里 `\"ended\" if … else action + \"d\" if … else \"resumed\"` 可读性差, 且改动时易错。修复为显式动作映射字典,审计字符串不变。 ### 建议 - S1 test_resident.py 导入了未用的 `time`(ruff F401,居民差分 内唯一 E、F 问题)。删除。 - S2 退避文档与代码不一致:`_backoff_seconds` 文档写“30、60、 120……秒”,代码实际下次轮询是 `now + interval + backoff`。 行为本身合理(不比间隔更频繁),文档误导。修复文档,行为不动。 - S3 loop 测试 tearDown 不够防御:setUp 中途失败时 tearDown 访问未建属性会掩盖原始失败。修复为 getattr 防御。 ### 已核查、不是问题(不扣分) - 审计行“写在 commit 之后会丢”:核查 `_auto_audit_row` 本身 执行 INSERT 后自己 commit,失败/配额暂停/变化三处审计行在 模拟关闭连接后仍可查,不是缺陷。 - server/worker 全量 ruff E、F 的其余项(如 worker.py:2781/2787 的 F821、server.py:10293 的 E501)经核对均为基底已有或由居民 插入导致的行号位移,非居民新增代码,不计入本轮。 - 仓库默认 ruff 全规则的 39 项(如 UP031 `%` 格式)与仓库惯用 写法冲突,不在仓库门禁内,不计入。 ## 3. 修复(全部关闭,每项有对应测试或定向复核) - F1 名称唯一性:`ensure_resident_tables` 新增部分唯一索引 `idx_resident_watches_name ON resident_watches(project_id, name) WHERE status != 'ended'`(server environments 同款口径); create/update 的 INSERT/UPDATE 包完整性错误转 ValueError (sqlite3.IntegrityError / PostgresIntegrityError,调用方仍 返回 400)。测试:索引存在断言、绕过查重直接插报 IntegrityError、改名撞名报 ValueError。 - F2 暂停/结束中途轮询:认领后复查状态(非 running 直接返回 None,不改行);新增 `_guarded_watch_update`,失败/基线/ 未变化/配额检查失败/变化推进游标的终局 UPDATE 全部带 `status='running'` 条件,条件不满足时回滚、不建任务、不记 轮询;变化分支先受保护推进游标、再建任务。测试:抓取回调 里经第二连接暂停监听,run_due 返回 None、无任务、游标与 next_poll_at 保持暂停态、轮询数不变。 - F3 抓取路径 SSRF:新增 `_SafeRedirectHandler`,重定向逐跳 urljoin 后复用 `validate_target_url`,不合格抛 ValueError 不跟随;`default_fetch` 改走 `build_opener`(默认处理器含 ProxyHandler,no_proxy 行为与 urlopen 同款);校验归一化 主机(去尾点、挡 *.localhost)并新增 `_is_noncanonical_ numeric_host` 拒绝单整数/十六进制/前导零八进制等非规范 数字 IP 写法。测试:localhost.、*.localhost、2130706433、 0x7f000001、0177.0.0.1、0x7f.0.0.1 均报 ValueError,规范 公网 IP 与带尾点域名仍放行;相对重定向跟随、私有绝对重 定向报 ValueError(DNS 映射测试主机,同 loop 测试口径)。 - F4 配额门判定:`\"quota_check_failed\" not in gate` 改为 `gate.startswith(\"quota_check_failed\")`,与配额门前缀契约 对齐。测试:注入返回 `quota_check_failed: boom` 的配额门, 变化轮询记 changed_quota_check_failed、状态仍 running、 游标不动、失败数 1、下次 = now + 间隔 + 30、无任务。 - F5 row_get 重复:删 resident 本地定义,直接导入 automations.row_get,内部调用点一字未动,模块依赖说明 同步更新。 - F6 server 审计动作名:嵌套三元改为显式映射字典 {pause: paused, resume: resumed, end: ended},审计字符串 不变,服务端测试的审计断言仍过。 - S1 删 test_resident.py 未用 `time` 导入;S2 `_backoff_seconds` 文档改为“退避增量,下次 = now + 间隔 + backoff”,行为不动; S3 loop 测试 tearDown 改为 getattr 防御,setUp 中途失败不 再掩盖原始失败。 ## 4. 测试数字(全部本轮实测,无删断言、无 skip、无改默认值) - 定向基线(改动前):`cloud.tests.test_resident` + `test_resident_server` + `test_resident_website_loop` = 15 项全过。 - 定向改动后:同三文件 = 20 项全过(基线 15 + 新增 5:URL 变体、唯一索引、配额检查失败、中途暂停、重定向),4.450 秒; ruff E、F 在居民差分 4 文件上全部通过。 - 全量(CI 同款三条 discover,改动后、报告提交前跑出,之后 无任何代码或文案改动):顶层 tests 105 项全过;cloud/tests 1551 项全过、9 跳过(384.252 秒);deploy/tests 44 项全过 (单独重跑确认);合计 1700 项、0 失败、9 跳过。对账:闭环 基线 1695(105 + 1546 + 44)+ 新增 5 = 1700,无测试丢失、 无新失败。 - 覆盖率:本仓库 CI 无覆盖率门禁,不虚构覆盖率变化;新增 5 项定向测试即上述全量的一部分。 ## 5. 复评分数 - 架构:10 / 10 - 代码质量:10 / 10 复评依据:F1/F2 两项严重 findings 已由数据库约束与状态条件 UPDATE 从机制上关闭(非仅文档说明),并有定向测试锁定; F3 安全边界已从“创建时一次校验”补到“抓取重定向逐跳校验” 与非规范 IP 写法;F4/F5/F6/S1/S2/S3 全部关闭且测试全过。 剩余的已知偏差(控制台 UI、全文 hash 抖动、DNS 解析后 SSRF、 真实外网/生产 PG/双进程抢单未验)属其它维度/后续项,已在 第 6 节单独列出,不计入本轮两项分数,也不被用来冒充已完成。 ## 6. 剩余 blocker 与未做事项 - 未做:未 push、未合并、未跑 deploy.py、未部署;不做其它探针、 不接控制台 UI、不做生产 PG 与双进程抢单实测。 - 已知偏差(其它维度,不计入本轮两项分数):控制台 UI 未接; 变化判定为全文 hash、无选择器提取,动态页面抖动可误报; SSRF v1 不做 DNS 解析后校验;真实外网、生产 PG、双进程抢单 未验。