From 6e17e77aafd911d7fd59540e504c598cde542cec Mon Sep 17 00:00:00 2001 From: Codex Date: Tue, 30 Jun 2026 13:07:20 +0800 Subject: [PATCH] chore: require migration business sampling evidence --- README.md | 2 +- docs/refactor/next-development-todo.md | 2 +- .../production-launch-evidence.template.json | 15 ++++++++++++++- scripts/production-launch-gate-test.js | 11 +++++++++++ scripts/production-launch-gate.js | 6 ++++++ 5 files changed, 33 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6ce29681..b07e49f0 100644 --- a/README.md +++ b/README.md @@ -394,7 +394,7 @@ cp docs/refactor/production-launch-evidence.template.json docs/refactor/producti npm run launch:gate -- --evidence docs/refactor/production-launch-evidence.json ``` -`production-launch-evidence.json` 不入 Git,里面只记录验收摘要、artifact 路径、审批人和时间,不保存真实 access token、支付密钥、对象存储密钥或用户隐私明细。门禁会要求以下证据全部齐备并通过:`readiness:production`、`readiness:production:db`、真实 `smoke:auth:remote`、`test:rls`、PocketBase production dry-run、`pb:import:validate`、API/worker/Taro 构建、`audit:runtime`、`@codex-security` 扫描,以及备份、回滚、真实数据抽样、生产 provider、对象存储控制、支付对账和三套 H5 `runtime-config.json` 人工确认。 +`production-launch-evidence.json` 不入 Git,里面只记录验收摘要、artifact 路径、审批人和时间,不保存真实 access token、支付密钥、对象存储密钥或用户隐私明细。门禁会要求以下证据全部齐备并通过:`readiness:production`、`readiness:production:db`、真实 `smoke:auth:remote`、`test:rls`、PocketBase production dry-run、`pb:import:validate`、`pb:import:sample`、API/worker/Taro 构建、`audit:runtime`、`@codex-security` 扫描,以及备份、回滚、真实数据抽样、生产 provider、对象存储控制、支付对账和三套 H5 `runtime-config.json` 人工确认。 模板文件: diff --git a/docs/refactor/next-development-todo.md b/docs/refactor/next-development-todo.md index dc7c9dc3..01cfb75d 100644 --- a/docs/refactor/next-development-todo.md +++ b/docs/refactor/next-development-todo.md @@ -88,7 +88,7 @@ - 生产 `.env` 模板和 `npm run readiness:production` / `npm run readiness:production:db` 已补,后续上云必须作为验收 gate。 - Auth/JWKS 上云后必须临时设置 `AUTH_SMOKE_*` 环境变量并运行 `npm run smoke:auth:remote`,真实 access token 不得写入仓库、前端配置或日志。 - 本地/预生产必须同时跑 `npm run test:rls`,它验证运行时 JWT claim 下的租户隔离,和 `readiness:production:db` 的静态 policy 检查互补。 - - 已补 `npm run launch:gate` 生产上线证据门禁和 `docs/refactor/production-launch-evidence.template.json` 模板;最终切换前必须把 readiness、远程 Auth、RLS、生产 dry-run、导入校验、API/worker/Taro、运行时审计、`@codex-security`、备份/回滚/真实抽样/生产 provider 等证据填入本地 `production-launch-evidence.json` 并通过门禁。 + - 已补 `npm run launch:gate` 生产上线证据门禁和 `docs/refactor/production-launch-evidence.template.json` 模板;最终切换前必须把 readiness、远程 Auth、RLS、生产 dry-run、导入校验、`pb:import:sample` 业务抽样、API/worker/Taro、运行时审计、`@codex-security`、备份/回滚/真实抽样/生产 provider 等证据填入本地 `production-launch-evidence.json` 并通过门禁。 - 确认数据库迁移流程、备份恢复、日志、告警。 - 准备 API 容器部署和 Supabase 云端/自托管连接方案。 - 已补 `npm run perf:api:local` 和 `docs/refactor/performance-benchmark-runbook.md`,可在本地或云端对真实迁移数据做只读混合压测;4 核 16G 正式容量报告需上云后按 6/30/50/100 阶梯并发复跑并归档到本地上线证据。 diff --git a/docs/refactor/production-launch-evidence.template.json b/docs/refactor/production-launch-evidence.template.json index e361fbca..3397bbad 100644 --- a/docs/refactor/production-launch-evidence.template.json +++ b/docs/refactor/production-launch-evidence.template.json @@ -74,6 +74,19 @@ "fail": 0 } }, + { + "id": "migration.pb-import-sample", + "status": "pass", + "command": "PB_SAMPLE_WRITE_REPORT=true npm run pb:import:sample > docs/refactor/launch-artifacts/pb-import-sample.log", + "completedAt": "2026-06-30T10:45:00+08:00", + "artifact": "launch-artifacts/pb-import-sample.log", + "summary": { + "fail": 0, + "warn": 0, + "skip": 0, + "pass": 0 + } + }, { "id": "api.integration", "status": "pass", @@ -227,7 +240,7 @@ "status": "approved", "approver": "replace-with-owner", "approvedAt": "2026-06-30T13:10:00+08:00", - "notes": "Real users, questions, orders, entitlements, assets and learning records sampled." + "notes": "Real users, questions, orders, entitlements, assets and learning records sampled; pb:import:sample warnings, skips and manual review rows have owner-approved handling conclusions." }, { "id": "provider.production-accounts", diff --git a/scripts/production-launch-gate-test.js b/scripts/production-launch-gate-test.js index 2a86e7c0..9fe9bce1 100644 --- a/scripts/production-launch-gate-test.js +++ b/scripts/production-launch-gate-test.js @@ -105,6 +105,17 @@ assert.ok( 'migration profile mismatch should be reported as a blocker', ); +const missingBusinessSampling = runGate(tempDir => { + const evidence = createEvidence(tempDir); + evidence.checks = evidence.checks.filter(item => item.id !== 'migration.pb-import-sample'); + return evidence; +}); +assert.notEqual(missingBusinessSampling.status, 0, 'missing import business sampling should fail launch gate'); +assert.ok( + missingBusinessSampling.payload.checks?.some(item => item.id === 'check.migration.pb-import-sample' && item.status === 'blocker'), + 'missing import business sampling should be reported as a blocker', +); + const missingAttestation = runGate(tempDir => { const evidence = createEvidence(tempDir); evidence.attestations = evidence.attestations.filter(item => item.id !== 'backup.snapshot'); diff --git a/scripts/production-launch-gate.js b/scripts/production-launch-gate.js index 9c397d57..db0d73a7 100644 --- a/scripts/production-launch-gate.js +++ b/scripts/production-launch-gate.js @@ -50,6 +50,12 @@ const gateChecks = [ commandIncludes: 'pb:import:validate', summary: { fail: 0 }, }, + { + id: 'migration.pb-import-sample', + label: 'PocketBase imported business sampling', + commandIncludes: 'pb:import:sample', + summary: { fail: 0 }, + }, { id: 'api.integration', label: 'API integration regression',