Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LutgzyJqNhPpZy1nFsJtN3
5.8 KiB
5.8 KiB
TASK · 发布通路修复单(issue #74,第 1 次打回)
- 日期:2026-10-14
- 原任务书:
docs/tasks/TASK-release-path-production-20261014.md(产品负责人 2026-10-14 在 issue #74 再次确认授权修改四个生产工作流) - 被验 SHA:
00e6efdbfa3d97b0aad764865321bc00fb1cee13(codex/release-path-production-20261014) - 执行方:Grok,同一分支续做(先 merge 最新
origin/staging,不改写已推历史) - 红线不变:仍只改原任务书授权的四个工作流;不改 staging 工作流、
gated-paths.txt、is-docs-only-range.sh;不点任何生产按钮
1. 验收结论:发布规则本身成立,三处阻断首发
已确认成立(不需要改):正向路径要求 R 有精确 SHA 的 staging push 门禁、R 或 H 有手动发布门禁、main == staging、R..H 只含文档;变更前复核用的是 R 门禁产物里的 is-docs-only-range.sh 与 gated-paths.txt;staging 工作流零改动;AGENTS.md 只动了授权的 §2.4、§2.5、§2.7。
B1 · Python 测试在 Linux 上全红(会让发布门禁红)
tests/test_resolve_release_revision.py 第 13–14 行写死 PYTHON = ROOT/".venv"/"Scripts"/"python.exe" 和 D:\Program Files\Git\bin\bash.exe。Claude 在 Linux 全量 pytest:该文件 7 条里 6 条失败(python.exe: No such file or directory)。release-quality-gate.yml 跑全树 pytest,runbook 第 3 步会红。
- 修:用
sys.executable;bash 用shutil.which("bash"),Windows 再回落到 Git Bash。Linux 与 Windows 都要过。
B2 · 主机模式的迁移演练跑不通,且会在生产库上留下一份生产数据副本
deploy/rehearse-production-migrations.sh:
run_node_sql(约 88–137 行)在主机上跑node+import pg,连@postgres:5432(约 253 行)——这个主机名只在 Docker 网络里有;生产主机按规矩不装 node /node_modules、不构建。计数步骤失败退出 1,清理步骤(约 142 行)也用同一个函数,同样失败只打警告 →rehearse_<时间戳>库连同恢复出来的生产数据留在生产 PostgreSQL 上,与 runbook「演练库会删掉」矛盾。- 恢复用
--no-owner --no-acl,对象归POSTGRES_USER,而迁移以role=schema_owner执行,ALTER可能报 "must be owner";ACL 没恢复,第 316–325 行的权限检查看到的不是生产真实授权,「退出 4 = BUG-1063 授权缺失」不可靠。 - 修:
- 主机模式下所有 SQL 都走
docker compose exec/docker run进 postgres 或 web 容器执行(与第 271 行--entrypoint node一致),不依赖主机 node。 - 清理放
trap,并且清理失败要非零退出并大字提示「演练库未删除,请手动 DROP DATABASE rehearse_xxx」,不得只打警告。 - 恢复后先把对象属主改成
schema_owner(或以 restore 后的属主执行迁移),并说明权限检查的口径;若做不到「看到生产真实授权」,就把「退出 4」的含义在脚本头和 runbook 里改成如实描述。 - 本地模式的连接串拒绝带
?查询参数(?database=/?host=能覆盖库名守卫)。 - runbook 第 5 步列的通过标准(归档会话前后数、自镜像前后数、NOTICE 计数)脚本并不打印:要么脚本打印,要么 runbook 删掉,二者必须一致。
- 主机模式下所有 SQL 都走
- 验收:新增或扩展
tests/test_rehearse_production_migrations.py:主机模式不调用主机node(用假docker记录调用);清理失败时退出码非零;带查询参数的 URL 被拒。
B3 · runbook 的失败回退走不通
deploy-production.yml回滚分支(约 126–137 行)仍要求 main 头 H 有一次 staging push 门禁成功记录;而本单要解决的正是「H 只多了文档、没有门禁记录」的情况——此时first返回 null、jq -e失败,runbook 约 107 行依赖的回滚会被拒。- 修:回滚分支对 controller 的门禁查找改用与正向相同的规则(
resolve-release-revision.sh解析出的 R),或在 H 无门禁时用 R 的门禁产物;回滚的「目标 SHA 必须在已审 main 历史里」等原有检查不放宽。加合同测试锁住。 - runbook 约 106 行「从恢复点整库恢复」没有给出工具和步骤:写清用哪个脚本 / 命令、需要哪个文件(
recovery_reference结尾就是备份文件名)、恢复后怎么核对。
2. 非阻断但这轮一并改
frontend/tests/staging-backend-workflows.test.ts:迁移与恢复两条用例把工作流里的门禁断言换成了检查脚本文本,没有任何断言锁住三个工作流里的CHECK_RELEASE_GATE=true——删掉它会让正向路径失去发布门禁要求而测试仍绿。补上这三处断言(三栏写明)。frontend/src/modules/identity/config.ts约 101 行用原始字符串比AUTH_USER_ORIGIN,https://jyotisha.chat/(带斜杠)会漏过「生产拒绝测试验证码」。改为与readOrigin规范化后的值比较,并加一条带斜杠的测试。resolve-release-revision.sh的正向判定用的是 H 自己检出里的gated-paths.txt;虽然变更前有 R 产物复核兜底,但 recovery 工作流没有复核。请在 recovery 的判定里同样用 R 产物(或在 PROGRESS 写明为何恢复点可以接受)。
3. 验收标准
- Linux:
python3 -m pytest tests/test_resolve_release_revision.py tests/test_rehearse_production_migrations.py -q全绿;Claude 全量 pytest 与 staging 基线逐条比对无新增;前端全量npm test无新增。 bash -n全部脚本通过。- runbook 每一步与工作流 / 脚本输出逐条一致(Claude 逐条对照)。
- 仍未点任何生产按钮。
4. 让步顺序
B1、B2、B3 都不可让;第 2 节第 1、2 条不可让,第 3 条可写理由不改。