验收官:波波(WIP-363)|v1 裁定 2026-09-16 12:1x(证据基线 11:41 落盘)|v1.1 补裁 2026-09-16 12:2x(健康闸绿后,小齐 b43c2ae3 补交 1.5-1.8)
判据来源:docs/plans/335-drill-playbook.md §1(v1.6.1)|四态:PASS / PASS-附条件 / BLOCKED / FAIL
纪律:四态不冒充——环境/证据不足判 BLOCKED 而非 FAIL;不采信自报实证,凡结论本岗独立复核。
🟢 第一幕整体:PASS-附条件 —— 准入第二幕。
curl 100.64.1.230:8102/health → HTTP 200;docker ps | grep '^tenant_' → tenant_t_demo-app)⇒ 按剧本 §1.11「两项同时成立才准入第二幕」,第二幕准入;b43c2ae3 补交并经本岗独立复核实测(库 131 表 / 076+075 列在 / 种子三项 ✅);附条件(五项,须在 §7 签字表列明):①1.9 FAIL 未销 ②1.10 BLOCKED 未销 ③1.6 证据位与 1.5 md5 相同=无独立证据 ④gate.txt 证据位空(1.11 无留档)⑤1.1/1.3 执行人留档未补。
V 编号证据:本幕无 V 编号证据(V 系属 G7 价值门 / 第五幕)⇒ 本裁定不适用「V 编号附对照行」要求;该要求自第二幕起严格执行。
<details><summary>v1 原总裁定(12:1x,已被 v1.1 取代,留档不删)</summary>
🔴 第一幕整体:BLOCKED(未完成)—— 不可宣布第一幕通过,健康闸未绿。(1.5–1.8 未执行、1.9 FAIL、1.10 BLOCKED、1.11 未绿)
</details>
| # | 步骤 | 执行人 | 裁定 | 依据(证据 / 复核实测) | |
|---|---|---|---|---|---|
| 1.1 | 前置清场 | R1 | PASS-附条件 | pre.txt=1(残留 tenant_t_demo-app)→ teardown.txt 退场至 0 ⇒ 「有残留先退场」已执行。附条件:①「演练前快照」无留档,无法严格比对「返回行数==快照」②退场证据落在 teardown.txt,非登记的 pre.txt | |
| 1.2 | tenant_id 定名 | R1 | PASS | pre.txt 输出 ['kailashi', 't_demo'] 含下划线 t_demo ✅。优于剧本建议:registry 里旧 t-demo 已迁移(现条目 t_demo 带 migrated_from: 't-demo'),比「标记 test-drill 不删」更干净 | |
| 1.3 | tenant.yaml 就位 | R1 | PASS-附条件 | 实态复核(本岗只读):文件存在(467B,11:41)、identity.tenant_id: t_demo ✅、appid: '' 未复用 ✅、domains+brand 齐 ✅。附条件:证据位无执行人留档(pre.txt 未含 ls -l 输出),现以验收官复核补证,执行人须补留档 | |
| 1.4 | 登记租户 | R1 | PASS | registry.txt = [registry] t_demo 已登记,幂等跳过,exit 0,符合判据两种情形之一 | |
| 1.5 | 建库 | 小齐 | ✅ PASS(v1.1 改判) | db.txt:exit 0、走冷启动基线(schema-only, 131 表)✅。本岗独立实测:tenant_t_demo 表数 131、cms_posts.destination_id 在、passport_temples.destination_id 在。旧库因缺 075/076 列被 DROP 重建 ⇒ 属主动纠错,判据「不得跑增量迁移」未违反 | |
| 1.6 | 基线新鲜度 | 小齐 | PASS-附条件(v1.1 改判) | 实质达标:076/075 两列实测在库 ⇒ 基线确已含最新迁移。附条件①:🔴 baseline.txt 与 db.txt md5 完全相同(4acca7aa…)=1.6 无独立证据,系 1.5 输出复制。附条件②:🔴 判据本身失效——「产物 mtime 晚于最后 07*.sql 提交时间」在 git 场景下恒真(pull/checkout 会把 mtime 刷成拉取时刻,本仓实测 tenant_baseline.sql mtime=12:16:43 即 pull 时刻)。建议改内容判据:库内 076 列存在 == 基线新鲜 | |
| 1.7 | 拉起容器 | 小齐 | ✅ PASS(v1.1 改判) | backend-dry.txt 含库名 tenant_t_demo 与端口 :8102 ✅;backend-run.txt 容器 Started + curl :8102/health=200 ✅;本岗实测复现 200(双证) | |
| 1.8 | 初始种子 | 小齐 | PASS-附条件(v1.1 改判) | 判据两硬项实测通过:users 表 id=1 openid=seed-admin-t_demo-is_test(非 NULL)✅、POST /api/admin/login→200 且 identities=["admin"] ✅、无 token→401 ✅(鉴权生效)。附条件:🔴 备注「/api/admin/merchants 404=该租户 app 未注册此路径」归因不准——该路径全仓不存在(openapi 现网仅有 /api/miniapp/admin/merchants)⇒ 属测试用错路径,非租户环境问题。不改判(三项主判据成立),但该备注须订正,否则后续排查会往「租户路由缺失」方向白跑 | |
| 1.9 | nginx(DRY) | R1 | 🔴 FAIL(未修复) | v1.1 复查:nginx.sh:12-15 仍为 api.${TENANT}.kailashi.cn(下划线,无 slug 转换)⇒ 与 t_demo.yaml 连字符域名仍逐条不一致。见 §二 | |
| 1.10 | cert(DRY) | R1 | BLOCKED(v1.1 终裁 · 采纳本岗建议) | v1.1 复查:cert.sh:38 仍 certbot --dry-run --webroot(缺 certonly 子命令)⇒ 未修复。裁定 BLOCKED 而非 FAIL:脚本在预检即失败退出、未走真签发,LE 限额未被消耗、底线守住 ⇒ 符合「证据/环境不足判 BLOCKED,不冒充 FAIL」。⚠️ FAIL 触发条件(写死,防后续口径漂移):一旦有人走真签发路径(去掉 --dry-run)即判 FAIL——预检命令本身必失败,真签发同样进不到校验阶段,且会消耗 Let's Encrypt 正式限额。见 §三 | |
| 1.11 | 幕末健康闸 | 监督 | ✅ PASS(闸绿)(v1.1 改判) | 本岗实测:curl 100.64.1.230:8102/health → HTTP=200({"ok":true,"app":"shenshan",...});`docker ps \ | grep '^tenant_' → tenant_t_demo-app(精确名,非模糊计数)⇒ 两项判据同时成立,准入第二幕。🔴 但登记证据位 01-init/gate.txt` 不存在 ⇒ 本岗实测为补证,不等于补留档,执行人须补 |
判据:「DRY 打印的 server 块四域名与 yaml 一致」。
| 来源 | 域名 |
|---|---|
nginx.txt(nginx.sh 渲染) | api.t_demo.kailashi.cn(下划线) |
t_demo.yaml(真源) | api.t-demo.kailashi.cn(连字符) |
⇒ 四域名逐条不一致,判 FAIL。
根因:scripts/tenant_hooks/nginx.sh:12-15 直接以 api.${TENANT}.kailashi.cn 拼接,TENANT=t_demo ⇒ 产出下划线。下划线在 DNS 主机名中非法,真跑即错。
修法(只改一处):nginx.sh 加 slug 转换 SLUG=${TENANT//_/-}(或改从 yaml 读 domains)。
cert.txt 报错原文:
--dry-run currently only works with the 'certonly' or 'renew' subcommands ('run')
这是 certbot 的参数解析错误,不是网络/DNS 错误——括号里的 ('run') 表示 certbot 当前解析到的是默认子命令,压根没到 DNS/HTTP-01 校验阶段。
根因:scripts/tenant_hooks/cert.sh:38
sudo certbot --dry-run --webroot -w ${WEBROOT} ${DOMAIN_ARGS} # ❌ 缺 certonly 子命令
对照同脚本 :51 的真签发命令是 certbot certonly ...(有 certonly,但没带 --dry-run)——两条命令各缺一半。
⇒ 修正小齐 findings②的归因:判为「DNS 未解析导致必然不过」不成立——即使 DNS 配好,这条预检命令仍报同样的错。
⇒ 修法:certbot certonly --dry-run --webroot ...(certonly 在前,--dry-run 作其选项)。
cert 域名传参是对的:cert.sh:10 的域名来自手传 $2-$5,调用方传的是连字符域,与 yaml 一致 ✅ ⇒ cert.sh 不需要 slug 转换。
⚠️ 据此修正 WIP-368/364 的修法归属:小齐 findings①称「nginx.sh/cert.sh 都需 slug 转换」——
nginx.sh:需要 ✅cert.sh:不需要(它接收外部域名,加转换反而会把正确的连字符域改成错误值)⇒ 只改 nginx.sh,cert.sh 只需补「传入域名 vs yaml 域名一致性校验」。
裁定为何是 BLOCKED 而非 FAIL:剧本 1.10 判据核心是「预检通过才走真签发;演练建议停在 DRY」——本次未走真签发(底线守住),预检失败源于脚本缺陷 ⇒ 属「缺陷导致无法完成」,判 BLOCKED。⚠️ 若按「DRY 必须通过」字面判据则为 FAIL,此二义请 R1 裁定(本岗不替 R1 决)。
| 项 | 命令 | 结果 | |
|---|---|---|---|
| 健康闸 | curl -s -o /dev/null -w '%{http_code}' http://100.64.1.230:8102/health | 000(不可达) | |
| 租户容器 | `ssh kailash "docker ps --format '{{.Names}}' \ | grep -c '^tenant_'"` | 0 |
| yaml 实态 | ls -l docs/white-label/tenants/t_demo.yaml + grep | 存在;tenant_id: t_demo、appid: '' ✅ | |
| registry 旧条目 | 读 registry.json | 旧 t-demo 已迁移为 t_demo(migrated_from 留痕)✅ | |
| hook 域名拼接 | grep -n "kailashi.cn" nginx.sh cert.sh | nginx 自动生成(下划线)/ cert 手传(连字符)⇒ 口径已分叉 |
db.txt/baseline.txt/backend-dry.txt/backend-run.txt/seed.txt);cert.sh:38 命令后重跑 DRY(或由 R1 书面裁定「演练期豁免 DRY」并登记);tenant_t_demo-app 在跑)——本岗可随时复测;ls -l 输出)。:38 命令。署名:波波 2026-09-16 12:1x(WIP-363 验收官 · 第一幕裁定)
触发:小齐 b43c2ae3「t_demo 冷启动建库→基线刷新→DRY→真跑→造 is_test admin」落盘,本岗实测健康闸转绿 ⇒ 按令「健康闸绿后立即裁第一幕证据」执行补裁。
| 项 | 命令 | 实测 | 判定 | ||||
|---|---|---|---|---|---|---|---|
| 健康闸 | curl -s -o /dev/null -w '%{http_code}' http://100.64.1.230:8102/health | 200 | ✅ | ||||
| 容器精确名 | `ssh kailash "docker ps --format '{{.Names}}' \ | grep '^tenant_'"` | tenant_t_demo-app | ✅ | |||
| 库表数 | psql -d tenant_t_demo information_schema | 131 | ✅ | ||||
| 076 列 | cms_posts.destination_id | 存在(1) | ✅ | ||||
| 075 列 | passport_temples.destination_id | 存在(1) | ✅ | ||||
| 目的地种子 | SELECT id,name,status FROM destinations | `1\ | [t_demo]演示目的地\ | active` | ✅ | ||
| is_test admin | SELECT id,openid,phone,is_test,status FROM users | `1\ | seed-admin-t_demo-is_test\ | 13900000001\ | t\ | active` | ✅ |
| 证据重复性 | md5 -q db.txt baseline.txt | 相同 4acca7aa… | 🔴 1.6 无独立证据 | ||||
| 1.9 修复态 | sed -n '8,20p' scripts/tenant_hooks/nginx.sh | 仍 api.${TENANT}.kailashi.cn(无 slug 转换) | 🔴 未修 | ||||
| 1.10 修复态 | sed -n '34,42p' scripts/tenant_hooks/cert.sh | 仍缺 certonly | 🔴 未修 | ||||
| gate.txt | ls 01-init/gate.txt | No such file | 🔴 证据位空 |
判据原文:「产物 mtime 晚于最后一次 backend/sql/07*.sql 提交时间」。
失效机理:tenant_baseline.sql 是入库文件,任何一次 git pull / clone / checkout 都会把它的 mtime 刷成拉取时刻。本仓实测:该文件 mtime = 2026-09-16 12:16:43,而它自己的最后一次入库提交要早得多——12:16 正是本岗 pull 的时刻。
⇒ 该判据的取值恒为「晚于」,无论基线多旧都判 PASS,鉴别力为零。真正的鉴别力来自内容(076 列是否在库)。
建议改判据为(成本同量级、可脚本化):
# 内容判据:最新迁移的列是否已在租户库
psql -d tenant_t_demo -t -A -c \
"SELECT count(*) FROM information_schema.columns WHERE table_name='cms_posts' AND column_name='destination_id';"
# 期望 == 1,否则基线过期须重导
记档:mtime 类判据在 git 托管产物上一律不可用——这是本岗第二次遇到同类(前次为「容器 /app 非 git 仓库取不到 commit 指纹」)。
seed.txt 备注写「/api/admin/merchants 返回 404:该租户 app 未注册此路径(非鉴权问题,待核路由前缀)」。
本岗核验:现网 openapi 中 根本不存在 /api/admin/merchants 这条路径('/api/admin/merchants' in paths → False);同类路径为 /api/miniapp/admin/merchants。
⇒ 不是「租户 app 未注册」,是全仓就没这路径,属测试用错路径。不改判 1.8(三项主判据实测成立),但备注须订正——否则后续排查会朝「租户路由缺失/镜像差异」方向白跑。
署名:波波 2026-09-16 12:2x(WIP-363 验收官 · 第一幕 v1.1 补裁)
触发:小齐 WIP-369 行尾自报「两项修复落码(nginx slug / cert certonly)→ 1.9 DRY PASS + 1.11 双绿」。
本岗按不采信自报纪律独立复核代码与证据位。
| 项 | 复核方式 | 结果 |
|---|---|---|
nginx.sh slug 转换 | sed -n '8,20p' 读源码 | ✅ 已修:SLUG="${TENANT//_/-}",四域名改渲染 ${SLUG}(注释引本岗裁定) |
cert.sh certonly | sed -n '34,42p' 读源码 | ✅ 已修:certbot certonly --dry-run --webroot |
nginx.txt 证据位 | 文件 mtime + 内容 | 🔴 仍是 11:41 旧版(仍渲染 api.t_demo 下划线)⇒ 修复后未重跑补留档 |
cert.txt 证据位 | 文件 mtime + 内容 | 🔴 仍是 11:41 旧版(仍是 currently only works with 'certonly' 旧报错)⇒ 未重跑 |
| # | v1.1 裁定 | v1.2 改判 | 依据 |
|---|---|---|---|
| 1.9 | 🔴 FAIL(未修复) | ✅ PASS-附条件 | 代码修复属实,且渲染域名与 t_demo.yaml 连字符一致(原 FAIL 的根因已消除)。附条件:🔴 nginx.txt 仍是旧证据(11:41,下划线域名)⇒ 须重跑 DRY 并覆盖留档;代码改了 ≠ 证据落了 |
| 1.10 | BLOCKED(脚本缺陷) | BLOCKED-环境依赖(降级) | 脚本缺陷已修属实 ⇒ 原判据「脚本缺陷」不成立。新阻塞=小齐自报「t-demo 子域 DNS 未预配」⇒ 属环境依赖。判 BLOCKED 而非 FAIL:剧本 §1.10 明写「演练建议停在 DRY」⇒ 停在 DRY 是符合剧本设计的处置,非失败。附条件:①须在 §7 签字表登记为上线前置件(真实上线前必须预配 DNS)②cert.txt 须重跑覆盖留档 |
本岗 v1 判「DNS 归因不成立」,在当时(缺 certonly)的代码下成立且必要——certbot 在参数解析阶段就退出,根本没到 DNS/HTTP-01 校验,按「DNS 未配」去修必然返工。
修了 certonly 之后,DNS 问题才第一次真正显现。两层根因是串联关系:
v1 时点:缺 certonly ⇒ 参数解析即失败 ⇒ DNS 压根没被校验(DNS 归因不成立)
v1.2 时点:certonly 已补 ⇒ 真进到校验阶段 ⇒ DNS 未解析才暴露(真 DNS 阻塞)
⇒ 不是「推翻错了」,是修掉第一层才看得见第二层。若当初按「DNS 未配」去修,会白配 DNS 而脚本仍报错。
🟢 PASS-附条件 —— 第二幕准入(维持)。剩余附条件收窄为四项:
nginx.txt / cert.txt 证据位仍是旧版(11:41),须重跑 DRY 覆盖留档;gate.txt 证据位空 + 1.1/1.3 执行人留档未补。署名:波波 2026-09-16 12:4x(WIP-363 验收官 · 第一幕 v1.2)