WIP-363 · 335 第一幕(初始化)证据验收裁定 v1.1

验收官:波波(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;不采信自报实证,凡结论本岗独立复核。

〇、总裁定

v1.1(2026-09-16 12:2x · 健康闸绿后补裁)— 以此为准

🟢 第一幕整体:PASS-附条件 —— 准入第二幕。

附条件(五项,须在 §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前置清场R1PASS-附条件pre.txt=1(残留 tenant_t_demo-app)→ teardown.txt 退场至 0 ⇒ 「有残留先退场」已执行。附条件:①「演练前快照」无留档,无法严格比对「返回行数==快照」②退场证据落在 teardown.txt,非登记的 pre.txt
1.2tenant_id 定名R1PASSpre.txt 输出 ['kailashi', 't_demo'] 含下划线 t_demo ✅。优于剧本建议:registry 里旧 t-demo 已迁移(现条目 t_demo 带 migrated_from: 't-demo'),比「标记 test-drill 不删」更干净
1.3tenant.yaml 就位R1PASS-附条件实态复核(本岗只读):文件存在(467B,11:41)、identity.tenant_id: t_demo ✅、appid: '' 未复用 ✅、domains+brand 齐 ✅。附条件:证据位无执行人留档(pre.txt 未含 ls -l 输出),现以验收官复核补证,执行人须补留档
1.4登记租户R1PASSregistry.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.9nginx(DRY)R1🔴 FAIL(未修复)v1.1 复查:nginx.sh:12-15 仍为 api.${TENANT}.kailashi.cn(下划线,无 slug 转换)⇒ 与 t_demo.yaml 连字符域名仍逐条不一致。见 §二
1.10cert(DRY)R1BLOCKED(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` 不存在 ⇒ 本岗实测为补证,不等于补留档,执行人须补

二、🔴 1.9 FAIL:nginx 渲染域名与 yaml 不一致(本轮最硬发现)

判据:「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)。

三、🔴 1.10 BLOCKED:cert 预检失败的根因被误判(须修正后再判)

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 只需补「传入域名 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/health000(不可达)
租户容器`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.shnginx 自动生成(下划线)/ cert 手传(连字符)⇒ 口径已分叉

五、放行条件(第一幕转 PASS 的前置)

  1. 1.5–1.8 执行并留档(db.txt/baseline.txt/backend-dry.txt/backend-run.txt/seed.txt);
  2. 1.9 修复 nginx.sh slug 转换后重跑 DRY,四域名与 yaml 逐条比对一致;
  3. 1.10 修 cert.sh:38 命令后重跑 DRY(或由 R1 书面裁定「演练期豁免 DRY」并登记);
  4. 1.11 健康闸两项同时成立(8102=200 且 tenant_t_demo-app 在跑)——本岗可随时复测;
  5. 1.1/1.3 补执行人留档(清场前快照、yaml ls -l 输出)。

六、待裁决

  1. ✅ 1.10 二义已裁定(义父 12:2x 令:按本岗建议处理)⇒ 判 BLOCKED,并附死条件「走真签发即 FAIL」。R1 如需改判 FAIL 请书面推翻本条。
  2. WIP-368/364 修法归属:nginx.sh 加 slug 转换;cert.sh 不加转换、改为补域名一致性校验 + 修 :38 命令。
  3. ✅ 1.6 已判(v1.1):实质达标(076/075 列在库)。遗留:①补独立证据(现 md5 与 1.5 同)②建议把判据从 mtime 改为内容判据(mtime 在 git 场景恒真失效)。
署名:波波 2026-09-16 12:1x(WIP-363 验收官 · 第一幕裁定)

七、v1.1 补裁(2026-09-16 12:2x · 健康闸转绿后)

触发:小齐 b43c2ae3「t_demo 冷启动建库→基线刷新→DRY→真跑→造 is_test admin」落盘,本岗实测健康闸转绿 ⇒ 按令「健康闸绿后立即裁第一幕证据」执行补裁。

7.1 本岗独立实测清单(不采信自报,全部亲自跑)

项命令实测判定
健康闸curl -s -o /dev/null -w '%{http_code}' http://100.64.1.230:8102/health200✅
容器精确名`ssh kailash "docker ps --format '{{.Names}}' \grep '^tenant_'"`tenant_t_demo-app✅
库表数psql -d tenant_t_demo information_schema131✅
076 列cms_posts.destination_id存在(1)✅
075 列passport_temples.destination_id存在(1)✅
目的地种子SELECT id,name,status FROM destinations`1\[t_demo]演示目的地\active`✅
is_test adminSELECT 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.txtls 01-init/gate.txtNo such file🔴 证据位空

7.2 🔴 新发现一:1.6 的 mtime 判据在 git 场景恒真失效

判据原文:「产物 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 指纹」)。

7.3 🔴 新发现二:1.8 的 404 备注归因不准(性质轻但会误导排查)

seed.txt 备注写「/api/admin/merchants 返回 404:该租户 app 未注册此路径(非鉴权问题,待核路由前缀)」。

本岗核验:现网 openapi 中 根本不存在 /api/admin/merchants 这条路径('/api/admin/merchants' in paths → False);同类路径为 /api/miniapp/admin/merchants。

⇒ 不是「租户 app 未注册」,是全仓就没这路径,属测试用错路径。不改判 1.8(三项主判据实测成立),但备注须订正——否则后续排查会朝「租户路由缺失/镜像差异」方向白跑。

7.4 放行结论

署名:波波 2026-09-16 12:2x(WIP-363 验收官 · 第一幕 v1.1 补裁)

八、v1.2(2026-09-16 12:4x):R1/小齐修复后复核 —— 1.9 销、1.10 降级

触发:小齐 WIP-369 行尾自报「两项修复落码(nginx slug / cert certonly)→ 1.9 DRY PASS + 1.11 双绿」。
本岗按不采信自报纪律独立复核代码与证据位。

8.1 复核结果

项复核方式结果
nginx.sh slug 转换sed -n '8,20p' 读源码✅ 已修:SLUG="${TENANT//_/-}",四域名改渲染 ${SLUG}(注释引本岗裁定)
cert.sh certonlysed -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' 旧报错)⇒ 未重跑

8.2 改判

#v1.1 裁定v1.2 改判依据
1.9🔴 FAIL(未修复)✅ PASS-附条件代码修复属实,且渲染域名与 t_demo.yaml 连字符一致(原 FAIL 的根因已消除)。附条件:🔴 nginx.txt 仍是旧证据(11:41,下划线域名)⇒ 须重跑 DRY 并覆盖留档;代码改了 ≠ 证据落了
1.10BLOCKED(脚本缺陷)BLOCKED-环境依赖(降级)脚本缺陷已修属实 ⇒ 原判据「脚本缺陷」不成立。新阻塞=小齐自报「t-demo 子域 DNS 未预配」⇒ 属环境依赖。判 BLOCKED 而非 FAIL:剧本 §1.10 明写「演练建议停在 DRY」⇒ 停在 DRY 是符合剧本设计的处置,非失败。附条件:①须在 §7 签字表登记为上线前置件(真实上线前必须预配 DNS)②cert.txt 须重跑覆盖留档

8.3 根因链澄清(防止后人误读为「波波推翻错了」)

本岗 v1 判「DNS 归因不成立」,在当时(缺 certonly)的代码下成立且必要——certbot 在参数解析阶段就退出,根本没到 DNS/HTTP-01 校验,按「DNS 未配」去修必然返工。

修了 certonly 之后,DNS 问题才第一次真正显现。两层根因是串联关系:

v1 时点:缺 certonly ⇒ 参数解析即失败 ⇒ DNS 压根没被校验(DNS 归因不成立)
v1.2 时点:certonly 已补 ⇒ 真进到校验阶段 ⇒ DNS 未解析才暴露(真 DNS 阻塞)

⇒ 不是「推翻错了」,是修掉第一层才看得见第二层。若当初按「DNS 未配」去修,会白配 DNS 而脚本仍报错。

8.4 第一幕整体(v1.2)

🟢 PASS-附条件 —— 第二幕准入(维持)。剩余附条件收窄为四项:

  1. 🔴 nginx.txt / cert.txt 证据位仍是旧版(11:41),须重跑 DRY 覆盖留档;
  2. 1.10 登记为上线前置件(DNS 预配);
  3. 1.6 证据位与 1.5 md5 相同(无独立证据)+ mtime 判据失效待改内容判据;
  4. gate.txt 证据位空 + 1.1/1.3 执行人留档未补。
署名:波波 2026-09-16 12:4x(WIP-363 验收官 · 第一幕 v1.2)