交付出件箱

← 返回出件箱

phase83 批次五 · 生产发版交接件(给 root · 需手工解一处冲突)

生成:2026-09-26 · dev2 会话「二维码与端口绑定断开」
长官已拍板:只发 phase83 六个 MR,不带导演台(phase79.6)/企微群(phase85)


一句话背景

phase83 六个 MR 用 sudo release-to-prod 自动发发不了——cherry-pick 到生产 main 时 app/main.py 冲突,而自动工具只能「整取 test 全文」或「整取 main 全文」,两个都错:

根因:phase83 是在「已含 phase79.6 导演台」的 test 上长出来的,它改 app/main.py 的位置紧挨导演台注册行。单独摘到生产(生产没导演台)上下文对不上就冲突。只有 app/main.py 一个文件冲突,其余全部干净 cherry-pick。

需要 root 用令牌手工建 release 分支 + 建生产 MR(令牌在 root 手里,dev2 够不着)。


要发的 6 个 MR(已在 test 验收,长官真机验过)

MR 功能分支 备注
730 / 731 feature/subseat-brandcode-batch1 批次一
732 feature/subseat-batch2 批次二
733 feature/subseat-batch3 批次三(含迁移 phase83_001_seat_seq_counter.py)
760 / 764 feature/subseat-login-v2 批次五(含迁移 phase83_002_...,含冲突的 ff3f5065=phase83.15、d1e2cd11=phase83.16)

迁移祖先链完整:phase48a_002(已在生产 main) → phase83_001 → phase83_002。


冲突怎么解(核心)

app/main.py 冲突时,直接用这份解好的文件整体替换即可:

/home/dev2/reports/phase83-b5-app-main-resolved.py

它 = 生产 main 的 app/main.py 原样 + 只加 phase83 三处。已验证:

49c49
< from app.api import ...admin_brand_code, brand_code_public, brand_code_claim
---
> from app.api import ...admin_brand_code, brand_code_public, brand_code_claim, admin_org_subseat_prefix
139a140,144
>             # Phase 83.16(9-24)· 手机号查无此人要单独打个 error_type 后缀,
>             # 好让 alert_notifier._NOT_ALERTABLE 只免这一种、不误伤密码错等其它 401
>             fail_kind = getattr(request.state, "login_fail_kind", None)
>             if fail_kind:
>                 error_type = f"{error_type}_{fail_kind}"
232a238
> app.include_router(admin_org_subseat_prefix.router, tags=["Phase 83.15 · admin 子席位前缀维护"])

说明:这份文件已经是 app/main.py 的最终目标态。cherry-pick 过程中只要 app/main.py 冲突,不管在哪个 commit,一律 cp 这份文件 → git add → 继续,最终结果都正确(ff3f5065 和 d1e2cd11 两处都可能触发,都用同一份文件解)。


手工发版步骤(root 执行)

# 在主仓或干净 worktree(带 codeup 远程 + 令牌的环境)
cd /path/to/repo
git fetch codeup main test

# 1) 拿本批 29 个提交的精确有序列表(只读,不写)
python3 scripts/release_main.py plan --repo . \
  --test-ref codeup/test --main-ref codeup/main \
  --mr 730 --mr 731 --mr 732 --mr 733 --mr 760 --mr 764 --json > /tmp/phase83-plan.json
# 从 json 的 commit_ids 字段取有序提交列表(29 个)

# 2) 从生产 main 建 release 分支(分支名要短,别用 -join(mr) 那种超长名,否则 Codeup 建 MR 500)
git checkout -b release/phase83-b5-20260926 codeup/main

# 3) 按 plan 顺序 cherry-pick;app/main.py 冲突就用解好的文件替换
git cherry-pick <commit_ids 按序>
#   ── 冲突停下时 ──
cp /home/dev2/reports/phase83-b5-app-main-resolved.py app/main.py
git add app/main.py
#   若同时有 docs/superpowers/plans 进度表冲突:取任一侧解掉即可(纯文档)
git cherry-pick --continue
#   重复直到 29 个全部 pick 完

# 4) 发版前校验(release 分支上)
grep -nE "director_studio|wecom_agent|wecom_admin" app/main.py   # 期望 0 命中
grep -nE "admin_org_subseat_prefix|login_fail_kind" app/main.py  # 期望 3 命中
python3 -m py_compile app/main.py && echo "语法OK"
ls migrations/versions/phase83_001_seat_seq_counter.py \
   migrations/versions/phase83_002_subseat_prefix_and_phone_login.py  # 两个迁移都在
git diff codeup/main -- app/main.py   # 只应看到上面那 3 处 phase83 改动

# 5) push + 建生产 MR(root 令牌)
git push codeup release/phase83-b5-20260926
#   Codeup 建 MR:release/phase83-b5-20260926 → main → 评审 → 合并 → CI 部署 chat.tongban.love

发版后验收(部署到 chat.tongban.love 后)

  1. 后端起得来(最关键 —— 证明没夹带 director_studio/wecom 导致 ImportError):查真实端点存活,别只看 /health(nginx 假信号)。
  2. 生产 alembic 版本推进到 phase83_002_...。
  3. 后台「机构管理 → 更多 → 子席位前缀」能设前缀、开家长号编号带前缀。
  4. 新家长号手机号+密码能登、账号被拒;老账号照旧账号+密码登。
  5. db-prod 确认没跑 phase79.6/phase85 相关的表/迁移(它俩不该出现)。

⚠️ 红线(务必守住)


附:相关存量债(长官另行决定,勿在本次动)

phase79.6 导演台、phase75 谣言粉碎机、机构洞察重写 —— 三笔「带迁移、久未发」的存量债。这次卡住就是踩在 phase79.6 上。本次只发 phase83,这三笔长官会单独定夺,不要顺手发。