126 lines
7.3 KiB
Markdown
126 lines
7.3 KiB
Markdown
# 常驻监听网站闭环验证摘要
|
||
|
||
## 起点与交付物(代码就绪,local only)
|
||
|
||
- 工作区:`~/workspace/codex-app-server-resident-website-loop`
|
||
- 分支:`feat-resident-website-loop`,起点为上一轮网站版
|
||
`feat-resident-task` 的 `0722ca3`(基底 `origin/main 622c51e`)。
|
||
- 实现提交:`561b389`(仅新增端到端测试文件
|
||
`cloud/tests/test_resident_website_loop.py`,364 行)。
|
||
本摘要为其后提交,工作区提交后干净。
|
||
- 范围:只做网站用例。不做 server_status、agent_status、
|
||
relay_cost 等其它探针;不把 resident-watches 改名/泛化成
|
||
通用任务;不改 API、表名与任何生产默认值。
|
||
- 泛化 worktree(`~/workspace/codex-app-server-resident-generic`)
|
||
本轮一律未碰;resident-task worktree、主 checkout
|
||
(`feat-repo-picker`)及其它 worktree 一律未动。
|
||
- **未做**:未 push、未合并到 main、未跑 `deploy.py`、未部署、
|
||
未建生产任务、未动生产/主机 env/系统 key/sub2api/
|
||
生产网关/仓库可见性/GitHub App 权限、未杀生产容器、
|
||
未连真实外部网站。
|
||
|
||
## 闭环逐环证据(全部真实链路,不是注入假抓取)
|
||
|
||
测试文件:`cloud/tests/test_resident_website_loop.py`,2 项。
|
||
内容服务是线程内真实 `http.server`(可变内容、可停服重启);
|
||
控制面是真实 `ThreadingHTTPServer` + 真实 SQLite 临时库;
|
||
驱动走真实 `default_fetch`(urllib、超时、1MB 截断)。
|
||
生产 URL 校验一字不改:测试主机名 `website-loop.test` 不是
|
||
IP 字面、不是 localhost,所以校验通过;测试进程内只把该
|
||
主机名的 DNS 指到本地服务(相当于 /etc/hosts 的一条测试
|
||
条目),并给它加 `no_proxy`(见“测试中发现的两处假断点”)。
|
||
|
||
1. 创建(API):`POST /projects/{id}/resident-watches`
|
||
(name=site1、target_url=`http://website-loop.test:<本地端口>/data`、
|
||
prompt=“变化时汇报”、interval_seconds=60)返回 201,
|
||
状态 running、interval 60、fetch_timeout 10(生产默认)、
|
||
last_content_hash 为空、next_poll_at 在位;审计含
|
||
`resident_watch_created`。状态查询 `GET` 单条可看到
|
||
running、游标、下次轮询。
|
||
2. 基线轮询:worker 驱动 `process_due_resident_watches`
|
||
(fetcher=None,即真实 default_fetch)抓到 “version one”,
|
||
记 `baseline` 轮询、游标为其 sha256,任务数为 0;
|
||
状态查询的 polls 第 1 条就是 baseline。
|
||
3. 变化建任务:内容改成 “version two”,到期轮询记
|
||
`changed`、建出 1 个 queued 任务,任务 prompt 含用户提示
|
||
“变化时汇报”与摘录 “version two”,任务行与审计行
|
||
`resident_watch_change_task_created` 均可查;游标推进到
|
||
version two,last_change_text 含 version two。
|
||
4. 暂停:API pause 后状态 paused、next_poll_at 为空;暂停
|
||
期间内容再改成 “version three”,驱动返回空、轮询数仍为
|
||
2、游标仍为 version two——暂停期间的变化没有被轮询上报。
|
||
5. 恢复:API resume 后状态 running;下一次驱动用旧游标比
|
||
对,记 `changed` 并建第 2 个任务(prompt 含 version three),
|
||
不是从头建基线;游标推进到 version three。
|
||
6. 失败环:停掉内容服务后驱动记 `failed` 轮询、
|
||
consecutive_failures=1、next_poll_at 大于 now+60+30
|
||
(间隔 + 首次退避 30 秒)、状态仍 running、游标仍为
|
||
version three——驱动不崩、监听不擅自结束。恢复服务后
|
||
(内容仍为 version three)驱动记 `unchanged`、失败清零、
|
||
任务数仍为 2。
|
||
7. 结束:API end 后状态 ended、next_poll_at 为空、游标仍为
|
||
version three;驱动返回空;pause / resume / end / PATCH
|
||
更新一律 409;状态查询显示 ended 与最后游标/变化,
|
||
polls 总数为 5(baseline、changed、changed、failed、
|
||
unchanged)。
|
||
8. 驱动接线:单独一测试把 `run_all` 等无关边界打桩,只留
|
||
resident 驱动走真实抓取,调真实 `Worker.run_loop`
|
||
(interval=0.05、max_seconds=0.4);记录到 run_loop 轮次
|
||
边界至少调了 1 次 `process_due_resident_watches`,且状态
|
||
查询里出现 1 条 baseline 轮询、游标为 version one。
|
||
这证明接线在 run_loop 里,不只是测试直接调驱动函数。
|
||
|
||
## 断点结论:生产代码无断点,无生产改动
|
||
|
||
本轮逐环均通,生产代码(`cloud/resident.py`、`cloud/server.py`、
|
||
`cloud/worker.py`)一字未改,`git diff` 为空、新增文件只有
|
||
端到端测试。第一次跑出的 2 个失败都是测试脚手架问题,
|
||
不是生产断点,修法与证据如下,不许读成“修了生产代码”:
|
||
|
||
- 假断点 1——首次真实抓取 failed:测试环境自带 egress 代理,
|
||
urllib 默认把测试主机名也送代理并被代理断开。实测对照:
|
||
不加 no_proxy 时 `default_fetch` 报 RemoteDisconnected,
|
||
加 no_proxy 后返回 “version one”。修法只在测试 setUp 里
|
||
给 `website-loop.test` 加 no_proxy(测试进程内生效,撕掉
|
||
时还原),生产抓取与代理行为一字不改。
|
||
- 假断点 2——run_loop 接线测试 TypeError:实例属性上的
|
||
recording 函数不会自动绑定 self。修法改为闭包捕获 worker,
|
||
不改 worker 代码。
|
||
|
||
## 真实测试数字
|
||
|
||
- 定向(全量后重跑,含本轮 2 项):`python3 -m unittest
|
||
cloud.tests.test_resident cloud.tests.test_resident_server
|
||
cloud.tests.test_resident_website_loop` —— 15 项全部通过。
|
||
其中上一轮 13 项在新 worktree 动工前已先复核全过。
|
||
- 全量(CI 同款三条 discover,定向之后、全量之前无生产改动,
|
||
全量后又重跑定向 15 项全过,时间关系如实写明):
|
||
- `discover -s tests`:105 项,0 失败,0 跳过。
|
||
- `discover -s cloud/tests`:1546 项,0 失败,9 跳过
|
||
(上一轮 1544 + 本轮新增 2,对账无差)。
|
||
- `discover -s deploy/tests`:44 项,0 失败。
|
||
- 合计 1695 项、0 失败、9 跳过。失败未靠删断言、加 skip、
|
||
改默认值蒙混;生产间隔下限 60 秒、抓取超时默认 10 秒、
|
||
URL 校验口径一律未动,到期只用驱动 now 注入模拟时间流逝。
|
||
|
||
## 已知偏差与未做事项(未写成已完成)
|
||
|
||
- 控制台 UI 不在本轮闭环内:只能走 API,无常驻面板。
|
||
- 真实外网抓取未验:只验了本地 HTTP 服务 + 测试 DNS 映射,
|
||
不要把本轮说成真实外网验证;生产环境下的代理/DNS/证书
|
||
行为仍属后续验证项。
|
||
- 生产 PG 表迁移未验(只验 SQLite 临时库)、双进程抢单未验
|
||
(只验条件 UPDATE 单胜者的上一轮单测与本轮单驱动链路)。
|
||
- 其它探针本轮明确不做:server_status、agent_status、
|
||
relay_cost、通用任务改名一律不在本轮范围内。
|
||
- 变化判定仍是上一轮 v1 全文 hash,无选择器/区域提取;
|
||
SSRF 仍是 v1 只挡字面私有 IP,未做 DNS 解析后校验。
|
||
- 未做:未 push、未合并、未部署;架构与代码质量的 10 分
|
||
审核是父级后续独立步骤,本轮不自评、不给自己打 10 分。
|
||
|
||
## Blocker
|
||
|
||
- 本轮无 blocker(纯本地代码与本地测试,未触及只有用户能给
|
||
的账号/密钥/商户/商店账号/宿主/维护窗口,也未发生
|
||
花真钱、删生产数据、改生产可见性的动作)。
|