Merge pull request 'Use a kernel-assigned port in the static deploy test instead of a hardcoded one' (#668) from fix/svc-static-deploy-test-port-flake into main
This commit was merged in pull request #668.
This commit is contained in:
@@ -0,0 +1,204 @@
|
|||||||
|
# fix/svc-static-deploy-test-port-flake 任务卡
|
||||||
|
|
||||||
|
任务主记录:PR #668 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/668;状态:已完成(合入前闭合);owner:weichen
|
||||||
|
|
||||||
|
- 权威仓:ssh://git@192.168.3.2:2222/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= a9566a68505ac97dead15ed3b5672954f15cea1c
|
||||||
|
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-fix-svc-static-deploy-test-port-flake(worktree);分支:fix/svc-static-deploy-test-port-flake
|
||||||
|
- 分支类型:standalone;root_target:main;immediate_target:main;accepting_requirements:false
|
||||||
|
- 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=9dd8c0eec3933926486dbc7537df17ada16cbe48d6151410626f43be81914353;triage_receipt=/tmp/svc-flaky-triage.json;receipt_sha256=7e0e09ea9fd8067ba091a3b657f7930bac6d5eceb08da1a0a2c86c415a4c2c7a
|
||||||
|
|
||||||
|
- 授权与交付终点:修复 `tests/test_svc.py` 中因硬编码端口导致的偶发失败用例,standalone 直达 main(PR + 完整 CI + 独立 Review)。不含 `scripts/svc.sh` 改动、不含其它用例的端口治理、不含发布/部署。
|
||||||
|
- 交付模式:goal-first(目标、范围和交付终点一次确认;范围内连续推进)
|
||||||
|
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md)
|
||||||
|
- 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入)
|
||||||
|
- 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位)
|
||||||
|
|
||||||
|
## 目标与非目标
|
||||||
|
|
||||||
|
- 预期结果:`tests/test_svc.py` 的 `SvcDbRecoveryTests.test_static_fresh_deploy_registers_pidcheck_and_starts` 不再因端口占用而偶发失败;该用例守护的语义(static 首启探活不得把 `http.server` 判成「进程即退」)保持不变。
|
||||||
|
- 根因(host 确定性复现,非推断):用例第 989 行硬编码 `port = 18951`。只要机器上任何进程占用该端口,`scripts/svc.sh` 的 `check_port_free` 就会正确拒绝(「端口被占用(非本服务)」、退出 1),用例因此失败。host 实测:占用 18951 时该用例稳定失败(`AssertionError: 1 != 0`),不占用时 10/10 通过。
|
||||||
|
- 受影响面:仅 `tests/test_svc.py`(测试代码)。产品侧 `scripts/svc.sh` 不改——其拒绝占口的行为是正确的。
|
||||||
|
- 非目标:不改 `scripts/svc.sh`;不改 `tests/test_svc.py:753-754` 的 `register_pg("pg1", 18951, ...)` 夹具(只写 `svc.env` 文本、从不绑端口,与本问题无关);不重构测试类;不新增跨用例端口管理框架(TOCTOU 套件级解法属独立增量)。
|
||||||
|
|
||||||
|
## Agent 实施收据
|
||||||
|
|
||||||
|
路由与 fail-stop 规则见根目录 AGENT-EXECUTION.md 与 policy/delivery.md;每次尝试一行,原始日志留在源码外收据路径。
|
||||||
|
|
||||||
|
| 单元 ID | 预期范围 | 配置摘要 | 角色 | 候选顺序 | attempt | CLI/模型 | 终态 | transport | fallback | 源码外收据路径 |
|
||||||
|
|---|---|---|---|---|---|---|---|---|---|---|
|
||||||
|
| svc-static-deploy-test-port-flake | `tests/test_svc.py` 单文件:硬编码端口 → 内核分配空闲端口 | AGENT-EXECUTION.md digest ccceb3bf | implementation | cursor→agy→pi-glm→pi→codex | 1 | cursor(`cursor-agent-stable`,model auto;session f4bd5b1f-6258-48f5-bcbd-442e1f8a66f2) | success | d0b217d3(refs/agent-exec/850b1455f…/5ca3ade8a790),已 `agent-exec apply` 快进入分支 | never | /tmp/svc-flake-run.json |
|
||||||
|
| review-svc-static-deploy-test-port-flake | 只读审查 | 同上 digest | review | pi-glm→pi→agy→cursor→codex | 1 | pi-glm(`pi`,zai-coding-cn/glm-5.3-flash,session not-observable) | **failed-unknown**(worker exit 0 且只读无改动,控制器不建 transport) | 无 | never | /tmp/svc-flake-review-run.json |
|
||||||
|
|
||||||
|
> 第二次 Review 尝试以全新 `pi` CLI 进程(非 `--continue`/`--resume`,fresh session)单独执行,结论 PASS;原始输出 `/tmp/svc-flake-review-final.log`。上表 failed-unknown 一行如实保留:按 policy/delivery.md,该终态不得计为门禁 Review。
|
||||||
|
|
||||||
|
## 同步记录
|
||||||
|
|
||||||
|
| 节点 | sync_base_sha | tested_head_sha | checked_at |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 启动 | a9566a68505ac97dead15ed3b5672954f15cea1c | a9566a68505ac97dead15ed3b5672954f15cea1c | 2026-10-21T02:38:22Z |
|
||||||
|
| 合入前同步 | a496ea0f0249b2b412354677ef97612ccd692d25 | 4ebf9e1b47edb701541f57a72020b31d9e3fb323 | 2026-10-21T03:16:00Z |
|
||||||
|
|
||||||
|
## 证据(Evidence)
|
||||||
|
|
||||||
|
<!-- CMD="<命令>" make evidence 写入;手抄/转写输出视为无效证据(AGENTS.md 证据与验证门禁)。 -->
|
||||||
|
| 时间(UTC) | 命令 | 被测HEAD | exit | 来源 | sha256(16) |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
|
||||||
|
## 结果与验证
|
||||||
|
|
||||||
|
- 结果:`tests/test_svc.py:989` 的硬编码 `port = 18951` 换成 `socket.bind(("127.0.0.1", 0))` 内核分配空闲端口(3 增 1 删,单文件单 hunk)。用例的 8 条断言一条未删未弱化。
|
||||||
|
- 验证:
|
||||||
|
- PASS|目标用例在干净主机通过(被测 HEAD 4ebf9e1,SELF,收据 7dfe7b09)
|
||||||
|
- PASS|**目标用例在旧硬编码端口 18951 被外部进程占用时仍通过**——这是修复的决定性一腿;同一场景在修复前确定性失败
|
||||||
|
- PASS|`tests.test_svc` 全模块 23 项 OK(被测 HEAD 4ebf9e1,SELF,收据 7dfe7b09)
|
||||||
|
- PASS|`make test-affected BASE=main` 全量 518 项 OK(被测 HEAD d0b217d3,SELF;同步后非卡片变更触发 WARN,该收据按治理规则作废并以 4ebf9e1 重跑,见上)
|
||||||
|
- NOT_RUN|交付信任所需的精确 HEAD CI(`test`/`test-pg`/`svc-realcombo`)——合入前由 `make ci-wait` 取得 GATE
|
||||||
|
- 影响与剩余项:
|
||||||
|
- 未解决但非阻塞:释放保留端口到 `svc.sh` 真正 bind 之间存在 TOCTOU 窗口。失效模式是响亮失败(`check_port_free` 报错退出),不会静默假绿;且与本仓既有 5 处同类夹具(`test_svc_recover.py:187`、`test_svc.py:366`、`test_task_pr_check.py:100`、`test_task_triage.py:230`、`test_svc_realcombo.py:128`)风险同级。套件级解法属独立增量,不在本任务扩围。
|
||||||
|
- 本任务不解决「其它用例也硬编码端口」这一更大面上的同类脆弱性;仅记录,不在本任务处理。
|
||||||
|
|
||||||
|
## 合入与清理
|
||||||
|
|
||||||
|
- Review 记录:PASS。两次独立全新 CLI 会话:① `pi`(zai-coding-cn/glm-5.3-flash),base=origin/main a496ea0f,head=0bd5a4b / 4ebf9e1;② `pi-glm`(zai-coding-cn/glm-5.3-flash),base=2fd11effd9ccb0ae35ae5a47caccefccbbb05a8e,head=c4528653b17665885a7b8f5b5b8f612a68081cef(Backend repin 之后基线)。两次均非 host 主模型、非实施会话、未使用 `--continue`/`--resume`/既有 session id,session 身份 not-observable。核对项:动态取端口写法与仓内 5 处既有夹具惯例一致且 `socket` 已在 `:12` 导入;逐条核对 8 条断言未删未弱化;diff 仅 `tests/test_svc.py` 单 hunk,`:753-754` 夹具未动;TOCTOU 与既有惯例同级、非阻塞;无回归面。第二次 Review 给出 4 项非阻塞建议:reservation TOCTOU 窗口在实践中可忽略、用法可观察性保留、register_pg 改不改的判定一致、其他用例硬编码端口的归属明确(已记入本卡)。重放依据:host 已在 4ebf9e1 与 c4528653b 独立重跑目标用例(含占端口一腿)与全模块(`/tmp/svc-flake-finish-test-20261021.json`、`/tmp/svc-flake-svc-tests-20261021.json`);首次审阅原始输出在仓外 `/tmp/svc-flake-review-final.log`,二次复盘 `/tmp/svc-flake-review-r1-20261021.json`。
|
||||||
|
- 冲突记录:无冲突。合入前同步 `make task-sync` 纳入 origin/main a496ea0f(仅并入另一任务的归档卡与 spec,无代码变更、无组件 pin 变动);同步导致被测 HEAD 变化,关键检查已在 4ebf9e1 重跑。
|
||||||
|
- CI 记录:原 CI 失败(run 4592/4594,均为 npm registry `ECONNRESET`/`EIDLETIMEOUT` 与一次 RealCombo SQLite fixture 启动失败)已在本任务外解决:Backend 仓 `Manager.ResolveHITL` 写库顺序竞态修复(PR #237,merge `f084d10`)经 Dev 仓 gitlink 重钉(PR #674,merge `2fd11eff`)落地,CI 抖动在 2fd11eff 之后回归正常(参见 2fd11eff 之后的多次 Dev CI 全绿记录)。本任务在 2fd11eff 之后基线上重跑 PR #668 的 GATE:由 `make ci-wait` 在 fold-closed head 上单次取得(见合入核验)。
|
||||||
|
- 「与本改动无关」的依据:本次 diff 仅 `tests/test_svc.py`;三次失败分别发生在 npm registry 网络与真实 RDM 夹具启动,均不在该文件的影响面;同一窗口 `main@a496ea0f` 的 run 4587 三作业全绿,而 run 4585/4593(其它 PR)同样失败。
|
||||||
|
- 未完成:精确 HEAD 的三作业 GATE 结果。不得以本地 SELF 替代,不得合入。
|
||||||
|
- 合入记录:NOT_RUN(交付授权范围、PR、被核验 HEAD、合入 SHA、目标分支及内容核验)
|
||||||
|
- 清理记录:NOT_RUN(授权依据、写者退出确认、worktree/分支/任务卡处置)——任务卡尚未闭合,worktree `Agent-System-Dev-wt-fix-svc-static-deploy-test-port-flake` 按规则保留现场,不得回收。
|
||||||
|
|
||||||
|
## 当前阻塞与下一动作
|
||||||
|
|
||||||
|
- 阻塞:精确 HEAD 的必需 CI 三作业连续 3 次因基础设施故障红(2× npm registry `ECONNRESET`、1× 真实 RDM 夹具启动退出)。代码侧无待办:独立 Review PASS、本地关键检查在新 HEAD 全绿。
|
||||||
|
- 下一动作:平台 run 级重跑接口已耗尽;需以新提交(仅 `.task/**`)取得新 SHA 后 `make ci-wait`,或由用户决定先处理 CI 基础设施稳定性。
|
||||||
|
- 遗留发现(**不在本任务范围,未修**):`tests/test_svc_realcombo.py` 仍有硬编码端口 9471/9473/9474(`seed_real` / `http_ok` / `deploy --port`),与本次修掉的 `tests/test_svc.py:989` 属同类脆弱性;本任务只授权了后者。
|
||||||
|
|
||||||
|
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"summary_schema": 1,
|
||||||
|
"source": "SELF",
|
||||||
|
"timestamp": "2026-10-21T02:37:25.839631+00:00",
|
||||||
|
"command": "make precheck",
|
||||||
|
"exit_code": 0,
|
||||||
|
"cwd": "/home/weichen/projects/Agent-System-Dev",
|
||||||
|
"before_head": "a9566a68505ac97dead15ed3b5672954f15cea1c",
|
||||||
|
"after_head": "a9566a68505ac97dead15ed3b5672954f15cea1c",
|
||||||
|
"branch": "main",
|
||||||
|
"target_branch": "main",
|
||||||
|
"target_head": "a9566a68505ac97dead15ed3b5672954f15cea1c",
|
||||||
|
"root_target": "main",
|
||||||
|
"immediate_target": "main",
|
||||||
|
"before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||||
|
"after_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||||
|
"output_tail": [
|
||||||
|
"make[1]: Entering directory '/home/weichen/projects/Agent-System-Dev'",
|
||||||
|
"[repo] branch=main head=a9566a6850",
|
||||||
|
"[remote] origin/main=a9566a6850 ahead=0 behind=0",
|
||||||
|
"[tree] clean submodules_drift=0 uninit=0",
|
||||||
|
"[worktrees] linked=4",
|
||||||
|
"[cards] 已合入未闭合任务卡:feature-project-access-lifecycle.md——make task-close 回填归档(主检出运行)",
|
||||||
|
"[initiatives] local cards (not authoritative): none",
|
||||||
|
"[hooks] core.hooksPath=scripts/hooks(pre-commit/post-merge 实际接线)",
|
||||||
|
"[facts] policy/facts.md 6 条事实均在有效期内",
|
||||||
|
"[required-reading] core/baseline.md, workflows/delivery.md §1, policy/delivery.md",
|
||||||
|
"[R0] a9566a6850(完整:a9566a68505ac97dead15ed3b5672954f15cea1c)",
|
||||||
|
"[worktree] before-sync: recursive component checks passed",
|
||||||
|
"[verdict] READY — 保留本次 precheck receipt 后建任务工作区:PRECHECK_RECEIPT=/tmp/task-precheck.json make task-start NAME=feature/your-branch",
|
||||||
|
"make[1]: Leaving directory '/home/weichen/projects/Agent-System-Dev'"
|
||||||
|
],
|
||||||
|
"output_sha256": "a43e43203f983403f6985ffa5d4fe7de220822e00febdc8b81cf0b86389db00b",
|
||||||
|
"receipt_sha256": "1247ce47e3885b17ccae7257ba8e6fdd8760f56ef681f89967a0e313ce8142ff",
|
||||||
|
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/1247ce47e3885b17ccae7257ba8e6fdd8760f56ef681f89967a0e313ce8142ff.json"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"summary_schema": 1,
|
||||||
|
"source": "SELF",
|
||||||
|
"timestamp": "2026-10-21T03:04:28.871393+00:00",
|
||||||
|
"command": "sh -c 'set -e; echo \"[1] target test on a clean host:\"; PYTHONDONTWRITEBYTECODE=1 python3 -m unittest tests.test_svc.SvcDbRecoveryTests.test_static_fresh_deploy_registers_pidcheck_and_starts 2>&1 | tail -3; echo; echo \"[2] same test while the legacy hard-coded port is held by a foreign process:\"; bash /tmp/occupy-port.sh 18951 env PYTHONDONTWRITEBYTECODE=1 python3 -m unittest tests.test_svc.SvcDbRecoveryTests.test_static_fresh_deploy_registers_pidcheck_and_starts 2>&1 | tail -4; echo; echo \"[3] whole module tests.test_svc:\"; PYTHONDONTWRITEBYTECODE=1 python3 -m unittest tests.test_svc 2>&1 | tail -3; echo; echo \"[4] legacy port released again:\"; (ss -ltn | grep -q \":18951 \" && echo LEAK) || echo released'",
|
||||||
|
"exit_code": 0,
|
||||||
|
"cwd": "/home/weichen/projects/Agent-System-Dev-wt-fix-svc-static-deploy-test-port-flake",
|
||||||
|
"before_head": "d0b217d3a486d32db91d69539c39d07ed9dbdae0",
|
||||||
|
"after_head": "d0b217d3a486d32db91d69539c39d07ed9dbdae0",
|
||||||
|
"branch": "fix/svc-static-deploy-test-port-flake",
|
||||||
|
"target_branch": "main",
|
||||||
|
"target_head": "a496ea0f0249b2b412354677ef97612ccd692d25",
|
||||||
|
"root_target": "main",
|
||||||
|
"immediate_target": "main",
|
||||||
|
"before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||||
|
"after_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||||
|
"output_tail": [
|
||||||
|
"OK",
|
||||||
|
"",
|
||||||
|
"[2] same test while the legacy hard-coded port is held by a foreign process:",
|
||||||
|
"----------------------------------------------------------------------",
|
||||||
|
"Ran 1 test in 0.599s",
|
||||||
|
"",
|
||||||
|
"OK",
|
||||||
|
"",
|
||||||
|
"[3] whole module tests.test_svc:",
|
||||||
|
"Ran 23 tests in 29.185s",
|
||||||
|
"",
|
||||||
|
"OK",
|
||||||
|
"",
|
||||||
|
"[4] legacy port released again:",
|
||||||
|
"released"
|
||||||
|
],
|
||||||
|
"output_sha256": "d8f352a6d39b09c2fca2e868a40723780fa8c7cf3ea4ab79600d894d2069db9f",
|
||||||
|
"receipt_sha256": "314db6279eec9bfc2666b2533347df850ada748e6e99c481bc1633f3faf34c4d",
|
||||||
|
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/314db6279eec9bfc2666b2533347df850ada748e6e99c481bc1633f3faf34c4d.json"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
| 同步(记录前对象) | a496ea0f0249b2b412354677ef97612ccd692d25 | bbb9024e9718497adf0d6385f6795cb5c7d9255f | 2026-10-21T03:16:00Z |
|
||||||
|
|
||||||
|
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"summary_schema": 1,
|
||||||
|
"source": "SELF",
|
||||||
|
"timestamp": "2026-10-21T03:16:10.154890+00:00",
|
||||||
|
"command": "sh -c 'set -e; echo \"[1] target test, clean host:\"; PYTHONDONTWRITEBYTECODE=1 python3 -m unittest tests.test_svc.SvcDbRecoveryTests.test_static_fresh_deploy_registers_pidcheck_and_starts 2>&1 | tail -3; echo; echo \"[2] target test while legacy hard-coded port is held:\"; bash /tmp/occupy-port.sh 18951 env PYTHONDONTWRITEBYTECODE=1 python3 -m unittest tests.test_svc.SvcDbRecoveryTests.test_static_fresh_deploy_registers_pidcheck_and_starts 2>&1 | tail -3; echo; echo \"[3] whole module:\"; PYTHONDONTWRITEBYTECODE=1 python3 -m unittest tests.test_svc 2>&1 | tail -3; echo; echo \"[4] port released:\"; (ss -ltn | grep -q \":18951 \" && echo LEAK) || echo released'",
|
||||||
|
"exit_code": 0,
|
||||||
|
"cwd": "/home/weichen/projects/Agent-System-Dev-wt-fix-svc-static-deploy-test-port-flake",
|
||||||
|
"before_head": "4ebf9e1b47edb701541f57a72020b31d9e3fb323",
|
||||||
|
"after_head": "4ebf9e1b47edb701541f57a72020b31d9e3fb323",
|
||||||
|
"branch": "fix/svc-static-deploy-test-port-flake",
|
||||||
|
"target_branch": "main",
|
||||||
|
"target_head": "a496ea0f0249b2b412354677ef97612ccd692d25",
|
||||||
|
"root_target": "main",
|
||||||
|
"immediate_target": "main",
|
||||||
|
"before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||||
|
"after_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||||
|
"output_tail": [
|
||||||
|
"",
|
||||||
|
"OK",
|
||||||
|
"",
|
||||||
|
"[2] target test while legacy hard-coded port is held:",
|
||||||
|
"Ran 1 test in 0.574s",
|
||||||
|
"",
|
||||||
|
"OK",
|
||||||
|
"",
|
||||||
|
"[3] whole module:",
|
||||||
|
"Ran 23 tests in 26.871s",
|
||||||
|
"",
|
||||||
|
"OK",
|
||||||
|
"",
|
||||||
|
"[4] port released:",
|
||||||
|
"released"
|
||||||
|
],
|
||||||
|
"output_sha256": "41ab6880e27c3f44906b392bbe76d342d1019ae1a2aad0690c2739d691330669",
|
||||||
|
"receipt_sha256": "7dfe7b09cfcb370d00c0e9a504a8857530b8637d18e1ab7bf2d4e434093bdb33",
|
||||||
|
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/7dfe7b09cfcb370d00c0e9a504a8857530b8637d18e1ab7bf2d4e434093bdb33.json"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
| 同步(记录前对象) | 2fd11effd9ccb0ae35ae5a47caccefccbbb05a8e | c766a81277112907333ac7f1810436aed51cff21 | 2026-10-21T09:48:46Z |
|
||||||
|
|
||||||
|
## 闭合记录(合入前折叠)
|
||||||
|
|
||||||
|
- 本卡在 PR #668(https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/668)合入前闭合并归档,随该 PR 一同合入。
|
||||||
|
- 该 PR 的合并提交即本任务的落地记录。
|
||||||
|
- 合入后无需回填,也无需单独的闭合 PR。
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
# 独立代码审查:fix/svc-static-deploy-test-port-flake(static 首启用例硬编码端口致偶发失败)
|
||||||
|
|
||||||
|
- 审查对象:packet 指定为 `5c3a1d4eb8f3a5c0c0c5be5cd5`(implementation 的 transport commit `d0b217d3a486d32db91d69539c39d07ed9dbdae0`)相对同步后 base `2fd11effd9ccb0ae35ae5a47caccefccbbb05a8e` 的 diff。**对象勘误(事实陈述,见 N1)**:`5c3a1d4e…` 在本工作区不存在(`git cat-file -t` → fatal);transport commit `d0b217d3` 的父提交是任务卡提交 `409f4749`,其分支自 R0=`a9566a68`(01:58 的 main)切出,而 `2fd11ef` 是 09:30 的 main——两者互不为祖先(`git merge-base --is-ancestor` 双向否定,merge-base=`a9566a68`)。因此 `git diff 2fd11ef d0b217d3` 含约 2400 行与本单元无关的主线演进内容(tests/test_shell_pipe_hazard.py 重写、e2e-duties.sh、hooks/pre-commit、svc.sh:4360、modules/backend gitlink、删除一份 spec 文档);**本单元自身 diff(`git diff a9566a68 d0b217d3` = `git diff 409f4749 d0b217d3`)只有**:`.task/fix-svc-static-deploy-test-port-flake.md`(host 豁免路径)+ `tests/test_svc.py` 单 hunk(3 增 1 删,第 989 行 `port = 18951` → `with socket.socket() as reservation: bind(("127.0.0.1", 0)); port = reservation.getsockname()[1]`)。产品侧 `scripts/svc.sh` 本单元零改动(d0b217d3 与当前 HEAD 2841cb4 在 svc.sh 的 1 行差异是主线管道治理提交所致,位于 `db_create` 的 postgres 路径,本用例不执行)。
|
||||||
|
- 审查者身份:全新会话独立审查,未参与实现;本会话仅写入本报告,其余只读。
|
||||||
|
- 被测代码:当前 HEAD `2841cb4` 下 `tests/test_svc.py` 与 `d0b217d3` 版本逐字节一致(`git diff d0b217d3 2841cb4 -- tests/test_svc.py` 为空),故只读测试在被测 HEAD 上运行即覆盖 transport commit 的测试代码。
|
||||||
|
|
||||||
|
## 结论:PASS(无阻塞项;4 项非阻塞建议)
|
||||||
|
|
||||||
|
判定标准逐项核对:用例语义未被削弱(9 条断言逐条与 base 相同,仅端口来源一行变化);TOCTOU 未引入可静默通过的偶发失败(失效模式是 `check_port_free` 报错退出 → `self.svc(check=True)` 断言带全量 stdout 响亮失败);受保护内容(`scripts/svc.sh`、`tests/test_svc.py:753-754` register_pg 夹具、测试类结构)未被改;补丁未外溢到其他用例或脚本(单文件单 hunk,实测 `git diff --stat a9566a68..d0b217d3` 仅 2 文件且其一为 `.task/**`)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 重点 1:端口分配语义与 TOCTOU 窗口
|
||||||
|
|
||||||
|
**写法**:`socket.socket()`(默认 AF_INET/SOCK_STREAM)→ `bind(("127.0.0.1", 0))` 由内核在临时口范围内分配 → `getsockname()[1]` 取实际端口 → `with` 块退出即 close。与本仓 5 处既有夹具逐字节同构:`tests/test_svc.py:366-368`(同类同类)、`tests/test_svc_recover.py:187-190`、`tests/test_task_pr_check.py:100-102`、`tests/test_task_triage.py:230-232`、`tests/test_svc_realcombo.py:128-130`(另 `tests/test_ci_wait.py:76`)。属既有惯例,非新引入模式。
|
||||||
|
|
||||||
|
**TOCTOU 窗口评估**(分析 + 局部实测):reservation close 到 `svc.sh` 内 `python3 -m http.server` 真正 bind 之间存在窗口,内核不为已关闭的监听套接字保留端口。实践中风险可忽略:
|
||||||
|
- 本机临时口范围 `32768-60999`(`/proc/sys/net/ipv4/ip_local_port_range` 实测),与套件所有固定端口(188xx/189xx/947x)**不相交**,动态端口不可能撞上兄弟用例;碰撞只可能来自无关进程在同一瞬间抽中同一临时口。
|
||||||
|
- 「服务主动保留端口」环境(`ip_local_reserved_ports`):内核 `bind(0)` 分配本就跳过保留段,分配步不会拿到保留口,不构成偶发源【分析推断,未单独实测】。
|
||||||
|
- 「worker 池已尽/临时口耗尽」:`bind(0)` 自身抛错 → 用例报错,属环境问题的响亮失败,不会假绿【代码路径推断,未单独复现】。
|
||||||
|
- **决定性实测(run 2)**:外部监听器占住旧硬编码口 18951 期间,目标用例 PASS(0.518s)——修复前该场景确定性失败(任务卡 host 复现 `AssertionError: 1 != 0`)。
|
||||||
|
|
||||||
|
**外部进程(如 Node)占口后的可观察性**:若抽中的端口在 `check_port_free`(scripts/svc.sh:1575)时已被占,static 部署报「端口被占用(非本服务):…」并退出非零 → `self.svc(check=True)` 的 `assertEqual(returncode, 0, stdout)` 失败并打印全量 stdout,占用者 pid 可查(`port_owner`)。比修复前更可归因:从「神秘硬编码口撞车」变为「带占用者信息的明确冲突」。
|
||||||
|
|
||||||
|
## 重点 2:用例失败可观察性是否保留
|
||||||
|
|
||||||
|
对比 base(a9566a68)与 d0b217d3 的用例方法体:`self.assert*` 计数 9=9,逐条未删未弱化(assertIn「已启动」、assertNotIn「进程即退」、assertEqual kind=static、assertIsNotNone pidcheck、assertIn `--directory`、assertIn resolve 后 htdocs、assertTrue pid_alive、assertEqual 200、assertIn static-ok)。仍然可见的失败类:(a) 端口冲突 → 部署退出非零 + stdout 全文;(b) pidcheck 登记回归 → assertIsNotNone/assertIn;(c) 首启探活误判 → assertNotIn「进程即退」;(d) http.server 未起/内容不符 → 部署失败或 urlopen 断言失败。唯一语义变化是「端口被他者占用时不再假失败」——这正是修复目标,不是可观察性损失。
|
||||||
|
|
||||||
|
## 重点 3:不改 register_pg(:753-754)是否合理
|
||||||
|
|
||||||
|
**合理,且为契约要求**。实测 `register_pg`(tests/test_svc.py:231-246)仅把 kind/env/addr/tags/source/version/user[/password]/dbname/authmode/pgdata 各行 `write_text` 进 `runtime/<name>/svc.env`,从不创建或绑定任何 socket;`:753-754` 的 `18951` 只是夹具元数据,被 stub 以 DSN 字符串消费。它不可能引发端口占用型偶发失败,不改正确。修复后全仓跟踪文件中 `18951` 仅剩这两行(`git grep` 实测)。
|
||||||
|
|
||||||
|
## 重点 4:其他用例是否仍硬编码 18951 / 是否需同改
|
||||||
|
|
||||||
|
`18951` 已无其他出现点。其他用例确有硬编码端口:rdm 组 `18842/18843/18844`(经 stub 内嵌 `http.server` **真实绑口**,同属偶发脆弱类)、若干仅作 svc.env 元数据的 187xx/1889x、以及任务卡已登记的 `tests/test_svc_realcombo.py` 9471/9473/9474。**均不在本任务授权范围**(任务卡非目标明示「不含其它用例的端口治理」,并已把 realcombo 记为遗留发现);动态临时口(32768+)与它们不相交,本修复既不掩盖也不交互。属后续独立增量,非阻塞。
|
||||||
|
|
||||||
|
## 重点 5:commit message 与 PR 描述是否如实
|
||||||
|
|
||||||
|
- transport commit message 仅为传输元信息(`agent-exec transport 850b1455…/5ca3ade8a790`),不含范围声明,无夸大面。
|
||||||
|
- implementation commit `5c3a1d4e…` 对象不在本工作区,其 message **未验证**;PR #668 描述在仓外且本单元禁网,**未验证**。
|
||||||
|
- 仓内可核的诚实性证据成立:任务卡的范围陈述与实测 diff 完全一致(「单文件单 hunk」「scripts/svc.sh 不改」「TOCTOU 非阻塞、套件级解法属独立增量」「不解决其它用例硬编码端口,仅记录」)。一处文档小疵见 N3。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 非阻塞建议
|
||||||
|
|
||||||
|
- **N1 对象拓扑**:packet 字面审查对象(2fd11ef→d0b217d3 的 diff)混入分支落后于 base 的主线内容;忠实的单元 diff 应对 merge-base/R0(a9566a68)或 transport 父提交(409f4749)取。同步记录显示 c766a81/2841cb4 已并入主线且 `tests/test_svc.py` 与 d0b217d3 一致,合并视角无实际风险;仅提示后续以「相对 main 的最终 diff」复核时勿把主线反向内容误记为本单元改动。
|
||||||
|
- **N2 TOCTOU 套件级解法**:reservation-close 到 bind 的窗口是既有 5 处夹具共有的固有模式;若未来要求消除,可考虑传递已绑定的 fd/listening socket 或碰撞重试一次。属独立增量(任务卡已记),不建议在本任务扩围。
|
||||||
|
- **N3 任务卡计数笔误**:卡称「8 条断言」,实测该用例 `self.assert*` 为 9 条(base 与修复后均 9);实质主张「一条未删未弱化」经核对为真。仅文档精度问题。
|
||||||
|
- **N4 同类脆弱面**:rdm 组真实绑口的 18842/18843/18844 与 realcombo 9471/9473/9474 仍为固定端口;建议另立任务按同一惯例治理。
|
||||||
|
|
||||||
|
## 已运行的验证命令与结果(均只读;测试 3 次,≤5 次限额内)
|
||||||
|
|
||||||
|
| # | 命令 | 结果 |
|
||||||
|
|---|---|---|
|
||||||
|
| 1 | `git cat-file -t 5c3a1d4eb8f3a5c0c0c5be5cd5` | fatal: Not a valid object name(对象不在本工作区) |
|
||||||
|
| 2 | `git merge-base --is-ancestor`(2fd11ef↔d0b217d3 双向) | 均非祖先;merge-base = a9566a68 |
|
||||||
|
| 3 | `git diff 409f4749 d0b217d3` 与 `git diff --stat a9566a68 d0b217d3` | 仅 `tests/test_svc.py` 单 hunk(3+/1−)+ `.task` 卡;产品文件零改动 |
|
||||||
|
| 4 | `git diff d0b217d3 2841cb4 -- tests/test_svc.py` | 空(被测 HEAD 测试代码与 transport commit 一致);svc.sh 差异仅 db_create 1 行(主线管道治理,本用例不执行) |
|
||||||
|
| 5 | `PYTHONDONTWRITEBYTECODE=1 python3 -m unittest tests.test_svc.SvcDbRecoveryTests.test_static_fresh_deploy_registers_pidcheck_and_starts` | **OK**(Ran 1, 0.507s) |
|
||||||
|
| 6 | 同上用例,前台命令内以临时监听器占住 18951 后运行、结束时 kill 并确认释放(`ss -ltn` 前后核对) | 占口确认 LISTEN → **OK**(0.518s)→ 占用者已回收、18951 released |
|
||||||
|
| 7 | `PYTHONDONTWRITEBYTECODE=1 python3 -m unittest tests.test_svc.SvcDbRecoveryTests`(注:`-k svc_db_recovery` 因大小写不匹配该类名,改以显式类名运行同一系列) | **OK**(Ran 23, 24.607s) |
|
||||||
|
| 8 | `cat /proc/sys/net/ipv4/ip_local_port_range` | 32768 60999(与全部固定测试端口不相交) |
|
||||||
|
| 9 | `git grep -n 18951`(排除 .task) | 仅 tests/test_svc.py:753-754(受保护夹具) |
|
||||||
|
| 10 | base/transport 用例方法 `self.assert*` 计数对比、`register_pg` 函数体、`svc.sh` `static_do_start`/`check_port_free`/`write_static_reg` 通读 | 9=9 断言一致;register_pg 只写 svc.env;static 路径占口即报错退出、探活语义未变 |
|
||||||
|
|
||||||
|
## 判定标准对照
|
||||||
|
|
||||||
|
- 用例语义被削弱:未发生(断言 9=9,探活守护链完整)→ 非阻塞。
|
||||||
|
- TOCTOU 引入可观察的偶发失败:未发生(失效模式为响亮失败,run 2 决定性场景通过)→ 非阻塞。
|
||||||
|
- 未按约束改动的内容被改:未发生(svc.sh/register_pg/测试类结构均未动)→ 非阻塞。
|
||||||
|
- 补丁外溢到其他用例或脚本:未发生(单文件单 hunk 实测)→ 非阻塞。
|
||||||
|
- 结论:**PASS**。implementation commit message 与 PR #668 描述原文未验证(对象不在本地/禁网),已在重点 5 如实标注,不构成阻塞。
|
||||||
+3
-1
@@ -986,7 +986,9 @@ class SvcDbRecoveryTests(unittest.TestCase):
|
|||||||
htdocs = self.root / "static-site"
|
htdocs = self.root / "static-site"
|
||||||
htdocs.mkdir()
|
htdocs.mkdir()
|
||||||
(htdocs / "index.html").write_text("<html>static-ok</html>\n")
|
(htdocs / "index.html").write_text("<html>static-ok</html>\n")
|
||||||
port = 18951
|
with socket.socket() as reservation:
|
||||||
|
reservation.bind(("127.0.0.1", 0))
|
||||||
|
port = reservation.getsockname()[1]
|
||||||
result = self.svc(
|
result = self.svc(
|
||||||
"uiqa", "deploy", "--kind", "static",
|
"uiqa", "deploy", "--kind", "static",
|
||||||
"--dir", str(htdocs), "--addr", "127.0.0.1:%d" % port)
|
"--dir", str(htdocs), "--addr", "127.0.0.1:%d" % port)
|
||||||
|
|||||||
Reference in New Issue
Block a user