diff --git a/.trellis/tasks/08-07-local-database-migration-readiness/check.jsonl b/.trellis/tasks/08-07-local-database-migration-readiness/check.jsonl new file mode 100644 index 00000000..9dd3234a --- /dev/null +++ b/.trellis/tasks/08-07-local-database-migration-readiness/check.jsonl @@ -0,0 +1 @@ +{"_example": "Fill with {\"file\": \"\", \"reason\": \"\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."} diff --git a/.trellis/tasks/08-07-local-database-migration-readiness/design.md b/.trellis/tasks/08-07-local-database-migration-readiness/design.md new file mode 100644 index 00000000..49b1b2ac --- /dev/null +++ b/.trellis/tasks/08-07-local-database-migration-readiness/design.md @@ -0,0 +1,150 @@ +# 设计说明 + +## 1. 设计目标 + +本任务建立一个最小、可审计的本地数据库基线恢复流程:先证明目标和迁移状态,再用唯一允许的 forward-only 命令应用仓库 migration,最后独立验证数据库、接口和上一轮被阻塞的回归证据。 + +任务不改变任何产品契约。它解决的是“本地数据库 schema 落后于当前 checkout”这一环境/数据基线问题。 + +## 2. 安全不变量 + +1. 目标必须是预期回环本地开发数据库;目标不明或非回环时零写入。 +2. 第一条数据库命令和最后一条数据库检查均为 `prisma migrate status`。 +3. schema 写入命令只有 `npm run prisma:migrate:deploy`;不存在自动 fallback。 +4. 允许集合之外的 pending migration、failed migration、history divergence 或 applied migration 异常全部进入 `PAUSED`。 +5. 不运行 reset、dev、db push、resolve、手工 DDL、seed 或数据清理。 +6. 原始 status 输出只保存在 gitignored 本地 artifacts;Trellis 只记录脱敏摘要和仓库相对引用。 +7. 数据库基线恢复和产品回归通过是两层结论,不能互相冒充。 + +## 3. 状态流 + +```text +PLANNING + -> PRE_STATUS(只读) + -> 非回环/目标不明 ----------------------> PAUSED + -> failed/diverged/未知 pending ----------> PAUSED + -> 已是最新 ------------------------------> POST_STATUS + -> 仅 allowlist pending + SQL 安全 --------> DEPLOY_ONCE + -> deploy 失败 ----------------------> READ_ONLY_RECHECK -> PAUSED + -> deploy 成功 ----------------------> POST_STATUS + -> POST_STATUS strict PASS + -> /api/me 仍是 migration/缺列 500 --------> BLOCKED + -> /api/me 200 ----------------------------> FIVE_SPEC_RUN + -> FIVE_SPEC_RUN + -> migration/数据库失败 ------------------> BLOCKED + -> 产品/夹具/环境/数据失败 ----------------> FOLLOW_UP_REQUIRED + -> 全部通过 ------------------------------> BASELINE_READY +``` + +`FOLLOW_UP_REQUIRED` 表示本地 migration 基线已经恢复,但上一轮路由化仍有独立红灯;本任务只记录,不修改代码。合同任务是否继续由后续复核决定,不能把该失败吞成 PASS。 + +## 4. 目标与迁移前置 + +### 4.1 目标确认 + +- 从 Prisma status 的 datasource 信息确认使用预期本地开发数据库和回环主机。 +- 任务记录只写“loopback=true/false、目标已人工匹配=true/false”,不复制连接 URL、用户名或密码。 +- 若 shell 环境或 Prisma 自动加载的配置无法证明目标,停止,不尝试通过连接或写入来猜测。 + +### 4.2 Pending allowlist + +| Migration | 仓库 SQL 摘要 | 本轮判断 | +| --- | --- | --- | +| `20260717120000_auth_challenge_trusted_proxy_hardening` | 增加认证挑战计数/锁定字段和索引,并失效本地未使用密码重置码、邮箱登录码 | 增量 schema + 已批准本地挑战数据更新 | +| `20260718120000_add_system_admin_role` | `RoleCode` 增加 `system_admin` | 非删除型 enum 扩展 | +| `20260718123000_small_team_payment_cancel_reason` | `payment_batches` 增加可空取消原因 | 纯增量 | +| `20260727153000_allow_employee_phone_null` | `employees.phone` 放宽为可空 | 非删除型 | +| `20260801120000_reimbursement_travel_summary` | 报销表增加四个可空差旅摘要字段 | 纯增量 | +| `20260802143000_corporate_bank_account_payment_order` | 新增银行机构/付款账户表,并增加员工及付款批次相关可空字段、索引和外键 | 增量;包含本轮缺失列 | + +`migrate deploy` 会一次应用全部 pending migration,不能选择单个 ID。因此执行前 status 返回值必须是上述六条集合的子集,并逐个复读实际 SQL。任何额外 ID 都改变获批写入范围,必须暂停。用户已明确接受第一条 migration 对本地未使用认证验证码的失效处理。 + +### 4.3 命令 allowlist / denylist + +允许: + +- `npx prisma migrate status`(只读,允许由现有 drift wrapper 调用)。 +- `npm run prisma:validate`(只读 schema 校验)。 +- `npm run prisma:migrate:deploy`(唯一 schema 写入,前置通过后一次)。 + +禁止: + +- `prisma migrate dev` +- `prisma migrate reset` +- `prisma db push` +- `prisma migrate resolve` +- 手工 SQL、迁移表修改、migration 文件改写 + +## 5. 证据设计 + +### 5.1 原始证据 + +- Pre:`artifacts/qa-flow/08-07-local-database-migration-readiness/pre/prisma-migrate-status.txt` +- Post:`artifacts/qa-flow/08-07-local-database-migration-readiness/post/prisma-migrate-status.txt` + +两份文件由现有 `check-local-migration-drift.sh` 生成并保持 gitignored。它们用于本地只读复核,不提交。 + +### 5.2 Trellis 脱敏记录 + +`implement.md` 的执行记录只写: + +- 执行时间与 HEAD; +- pre/post Prisma 实际退出状态; +- pending migration ID; +- `loopback=true/false`、目标匹配结论; +- deploy 是否执行及退出状态; +- `/api/me` 状态码; +- 五个 spec 的 passed/failed/skipped 和失败分类; +- reviewer 结论。 + +不记录 datasource URL、数据库账号、测试账号、密码、token、完整 API payload 或完整本地路径。 + +## 6. 服务与接口验证 + +- 优先复用已经运行且确认属于当前 checkout、当前本地数据库的服务。 +- 如需启动服务,先确认当前构建对应 HEAD,再以回环 host、`JOBS_ENABLED=false` 和当前本地数据库启动,避免后台任务引入无关写入。 +- `/api/me` 必须通过现有测试登录链路观察到 200;不另建凭据文件,不把认证材料写入任务证据。 +- 若 status 已对齐但 `/api/me` 仍报缺列,说明不是普通 pending migration,停止并保留现场。 + +## 7. 五个用例与数据副作用 + +| Spec | API 类型 | 数据副作用 | +| --- | --- | --- | +| `procurement-flow.spec.ts` | 当前本地真实 API | 创建并推进合成采购记录,保存归档字段 | +| `assets-flow.spec.ts` | 当前本地真实 API | 创建合成资产分类、资产和盘点/流转记录 | +| `r103-cross-page-ui-consistency.spec.ts` | mock API | 无本地业务数据写入 | +| `pc-detail-mobile-visual-wave3.spec.ts` | mock API | 无本地业务数据写入 | +| `global-search.spec.ts` | mock API | 无本地业务数据写入 | + +两个真实流 spec 使用时间戳或 `Playwright` 标记,便于识别,但当前测试没有自动清理。本任务不增加清理逻辑,不运行 reset 或批量删除;如后续需要治理测试数据,另建 task。 + +## 8. 失败分类 + +| 现象 | 分类 | 本任务处置 | +| --- | --- | --- | +| deploy 前目标非回环/不明 | 安全前置失败 | `PAUSED`,零写入 | +| status 出现 allowlist 外 ID、failed 或 divergence | migration 范围变化 | `PAUSED`,回来确认 | +| deploy 失败 | 数据库/migration | 保存输出,只读重查 status,停止 | +| post status 非 0 | 数据库基线未恢复 | `BLOCKED` | +| `/api/me` 仍因缺列 500 | migration 未解决 | `BLOCKED` | +| `/api/me` 200,但页面/选择器/断言失败 | 产品或测试桩 | 记录 `08-05-business-detail-routing` follow-up 候选 | +| 真实流缺业务前置数据 | 数据问题 | 记录缺口,不 seed、不重置 | +| 服务或端口不可用 | 环境问题 | 仅修复本地启动条件;不改产品代码 | + +## 9. 回滚与失败恢复 + +- Prisma committed migration 是 forward-only。本任务不提供“drop 回去”的自动回滚,也不把已成功 migration 标记为未应用。 +- deploy 失败后只允许保存输出并再次执行只读 status;不得用 `migrate dev/reset/resolve`、手工 DDL 或改 migration 历史继续推进。 +- deploy 成功后若必须撤销,只能通过另行批准的备份恢复或新的纠正 migration;两者均不在本任务范围。 +- Playwright 产生的合成记录不在本任务删除。测试数据清理由独立 task 评估,避免误删本地真实开发数据。 + +## 10. Review Gate + +实施完成后只读 reviewer 专门核查: + +1. pre/post 原始 status 证据存在且属于同一回环目标; +2. 实际 pending 集合未超出 allowlist; +3. 只执行了允许命令,未使用任何 reset/dev/db push/resolve/手工 DDL; +4. 没有修改 schema、migration、server/client 业务代码或测试; +5. `/api/me` 和五个 spec 结论没有把迁移通过与产品通过混为一谈; +6. 合同 task 未在基线结论形成前启动。 diff --git a/.trellis/tasks/08-07-local-database-migration-readiness/implement.jsonl b/.trellis/tasks/08-07-local-database-migration-readiness/implement.jsonl new file mode 100644 index 00000000..9dd3234a --- /dev/null +++ b/.trellis/tasks/08-07-local-database-migration-readiness/implement.jsonl @@ -0,0 +1 @@ +{"_example": "Fill with {\"file\": \"\", \"reason\": \"\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."} diff --git a/.trellis/tasks/08-07-local-database-migration-readiness/implement.md b/.trellis/tasks/08-07-local-database-migration-readiness/implement.md new file mode 100644 index 00000000..bff2613d --- /dev/null +++ b/.trellis/tasks/08-07-local-database-migration-readiness/implement.md @@ -0,0 +1,118 @@ +# 实施计划 + +> 当前状态:迁移与接口基线已完成;数据库结论 PASS,浏览器回归为 PASS_WITH_FINDINGS(16/10/0),待只读 review 和收口门禁。 + +## 1. 执行前固定现场 + +- [x] 记录当前 HEAD、当前 task、`git status --porcelain`;发现非本任务脏文件时停止确认。 +- [x] 确认 `08-05-contract-detail-workspace` 仍为 planning,未提前开始 schema 实施。 +- [x] 确认 `artifacts/qa-flow/08-07-local-database-migration-readiness/` 为 gitignored 本地证据目录。 +- [x] 复读 `prd.md`、`design.md`、`.trellis/spec/hyb-oa/backend-fastify-prisma.md` 和 `docs/试运行与自动化验证手册.md` 的 migration drift 边界。 + +## 2. Pre-status 只读基线 + +- [x] 运行: + `MIGRATION_DRIFT_REPORT_DIR=artifacts/qa-flow/08-07-local-database-migration-readiness/pre bash scripts/qa/check-local-migration-drift.sh` +- [x] 从原始输出记录 Prisma 实际退出状态;不得把 wrapper 的最终退出 0 当作“无 pending migration”。 +- [x] 只记录脱敏目标结论,确认 datasource 为预期回环本地开发数据库。 +- [x] 逐项记录 pending migration ID,并复读对应 `migration.sql`。 +- [x] pending 集合属于以下已核查 allowlist 子集;本次实际集合为全部六条: + - `20260717120000_auth_challenge_trusted_proxy_hardening` + - `20260718120000_add_system_admin_role` + - `20260718123000_small_team_payment_cancel_reason` + - `20260727153000_allow_employee_phone_null` + - `20260801120000_reimbursement_travel_summary` + - `20260802143000_corporate_bank_account_payment_order` +- [x] 运行 `cd server && npm run prisma:validate`,结果 PASS。 + +### 强制停止条件 + +- [x] 目标非回环、目标不明或不是预期本地开发库 → `PAUSED`,零写入;本次未触发,已确认 loopback=true。 +- [x] status 显示 failed migration、history divergence、applied migration 异常或 allowlist 外 pending → `PAUSED`,回来确认。— 第一次 pre-status 发现 3 条原 allowlist 外 pending 后已暂停;用户已复核 SQL并明确批准扩展到全部 6 条,继续前须再次确认 pre-status 未变化。 +- [x] 实际 SQL 包含删除表、删除列、清空数据、重置或其他未规划破坏性操作 → `PAUSED`,回来确认;六条均未命中,本次未触发。 +- [x] status 已是最新但 `/api/me` 仍缺列 → 不运行 deploy,转只读诊断并 `PAUSED`;本次 deploy 后 status current,未触发。 + +## 3. 唯一迁移写入 + +- [x] pre-status 非 current,未记录 `SKIPPED_ALREADY_CURRENT`。 +- [x] 存在允许的 pending migration,仅运行一次: + `cd server && npm run prisma:migrate:deploy` +- [x] 记录退出状态和应用的六个 migration ID,不复制数据库连接信息;命令退出码为 0。 +- [x] 若命令失败,立即保存输出并只读重跑 status;本次未失败,且未运行 `migrate dev/reset/db push/resolve`、手工 DDL 或迁移历史修改。 + +## 4. Post-status 与接口基线 + +- [x] 运行: + `MIGRATION_DRIFT_REPORT_DIR=artifacts/qa-flow/08-07-local-database-migration-readiness/post MIGRATION_DRIFT_STRICT=1 bash scripts/qa/check-local-migration-drift.sh` +- [x] 确认 strict 退出 0、同一目标、无 pending/failed/diverged migration;原始 post-status 已保存。 +- [x] `cd client && npm run build` PASS,并使用当前 checkout 的前端产物启动本地服务;第一次启动缺少必需的字段加密配置,所对应的回归结果作废,随后已用回环 host、`JOBS_ENABLED=false`、`SERVE_CLIENT_DIST=true` 重启并完成有效验证。 +- [x] 通过现有认证链路确认 `/api/me` 返回 200;employee、manager、finance、finance manager、admin 五类 fixture 均为 login=200、me=200,未再出现缺列错误。 + +## 5. 五个用例补跑 + +- [x] 运行(第二轮、有效服务配置): + `cd client && npx playwright test tests/e2e/procurement-flow.spec.ts tests/e2e/assets-flow.spec.ts tests/e2e/r103-cross-page-ui-consistency.spec.ts tests/e2e/pc-detail-mobile-visual-wave3.spec.ts tests/e2e/global-search.spec.ts --workers=1 --reporter=list` +- [x] 分别记录五个 spec: + - [x] `procurement-flow.spec.ts`:0 passed / 1 failed / 0 skipped;创建采购后审批摘要仍期待历史英文 `Employee`,实际页面已返回中文申请人摘要。 + - [x] `assets-flow.spec.ts`:0 passed / 1 failed / 0 skipped;创建 API 成功后页面仍停留 `/assets/new`,未跳到预期 canonical 详情路由。 + - [x] `r103-cross-page-ui-consistency.spec.ts`:4 passed / 7 failed / 0 skipped;失败集中在合同标题、采购/资产详情及未保存/中等宽度场景,多个上下文回到登录页。 + - [x] `pc-detail-mobile-visual-wave3.spec.ts`:9 passed / 0 failed / 0 skipped。 + - [x] `global-search.spec.ts`:3 passed / 1 failed / 0 skipped;canonical path 用例第二次回到 portal 后搜索触发器等待超时并落到登录页。 +- [x] 记录第二轮总数:16 passed / 10 failed / 0 skipped(26 tests);第一次缺少本地服务必需加密配置时的 17/9 结果明确作废,不计入最终证据;记录不含凭据、token、原始 payload 或完整本地路径。 +- [x] 明确采购、资产真实流产生的本地合成测试记录属于获批副作用;未执行清理、seed 或 reset。 + +## 6. 失败分流 + +- [x] migration/status/缺列导致的失败:本次未发生;post strict 与 `/api/me` 均通过,数据库基线层结论为 PASS。 +- [x] `/api/me` 已 200 后的路由、页面行为或选择器失败:全部归为产品路由/页面契约或测试桩会话夹具问题,登记为 `08-05-business-detail-routing` follow-up 候选;本任务未改代码/测试。 +- [x] 业务前置数据不足:本次未据此归因,也未自动 seed;真实流失败均有可复现的页面/断言首个失败证据。 +- [x] 本地服务/端口问题:第一次服务启动配置不完整,已仅恢复当前 checkout 的安全启动条件;未修改产品功能。 +- [x] 任何 spec 失败时,未把“数据库基线恢复”写成“五个用例全绿”;已分别记录数据库 PASS 与浏览器 `PASS_WITH_FINDINGS`(16/10/0)。 + +## 7. 只读 Review + +- [x] 启动只读 reviewer,明确 reviewer 不修改文件。 +- [x] Reviewer 核对:回环目标、pre/post status、pending allowlist、唯一 deploy 命令、禁用命令未执行、无业务代码/schema/test 改动、失败分类真实;结论 PASS,未修改文件、未运行长测试。 +- [x] Reviewer 核对合同任务仍未在迁移基线形成前启动;`08-05-contract-detail-workspace` 仍为 planning。 + +## 8. 收口门禁 + +- [x] `python3 ./.trellis/scripts/task.py validate 08-07-local-database-migration-readiness` PASS。 +- [x] `node scripts/qa/check-trellis-governance.mjs --json` PASS,0 errors / 0 warnings。 +- [x] `git diff --check` PASS。 +- [x] 检查 git diff:`git status --porcelain` 仅包含当前 Trellis task 目录;`artifacts/` 保持 ignored,无 server/client/schema/migration/test 改动。 +- [x] PRD Acceptance Criteria 按真实证据勾选;非数据库失败保留 `PASS_WITH_FINDINGS`,未伪装为 PASS。 +- [ ] 使用中文 commit message 并引用 `08-07-local-database-migration-readiness`;不 push、不部署。— 待用户确认提交计划后执行。 + +## 9. 执行记录 + +规划阶段留空。任务启动后按以下字段回填: + +- HEAD:`0124a2b` +- Pre status reportRef:`artifacts/qa-flow/08-07-local-database-migration-readiness/pre/prisma-migrate-status.txt` +- Pre Prisma exit/status:Prisma status `1`;wrapper 按设计返回 `0`,人工核读为 6 条 pending。 +- Loopback/target match:`loopback=true`,预期本地回归数据库匹配。 +- Pending migration IDs: + - `20260717120000_auth_challenge_trusted_proxy_hardening`(当前 allowlist 外;增加认证挑战字段/索引,并使本地未使用验证码失效) + - `20260718120000_add_system_admin_role`(当前 allowlist 外;增加 enum 值) + - `20260718123000_small_team_payment_cancel_reason`(当前 allowlist 外;增加可空字段) + - `20260727153000_allow_employee_phone_null` + - `20260801120000_reimbursement_travel_summary` + - `20260802143000_corporate_bank_account_payment_order` +- Deploy:`2026-08-07` 执行一次 `cd server && npm run prisma:migrate:deploy`,exit=0;六条 pending 全部成功应用。 +- Post status reportRef:`artifacts/qa-flow/08-07-local-database-migration-readiness/post/prisma-migrate-status.txt` +- Post Prisma exit/status:Prisma 原始 status exit=0(strict wrapper exit=0);输出为 `Database schema is up to date!`,同一 datasource、无 pending/failed/diverged。 +- `/api/me`:五类本地 fixture(employee、manager、finance、finance manager、admin)login=200、me=200;未出现 `employees.bank_institution_id` 缺列错误。 +- Five-spec result:26 tests,16 passed / 10 failed / 0 skipped;按 spec 数字见第 5 节。`pc-detail-mobile-visual-wave3.spec.ts` 全绿,其余四个含失败。 +- Failure classification:数据库迁移层 PASS;10 条失败均在迁移对齐和 `/api/me` 恢复后出现,归为路由/页面行为、E2E 断言契约或 mock 会话夹具问题,登记 `08-05-business-detail-routing` follow-up 候选;本任务未改产品代码、测试或数据。 +- Read-only review:PASS;bounded reviewer 只读完成,确认同一 loopback 目标、六条 allowlist、一次 deploy、post current、16/10/0 数字自洽、无业务代码/schema/migration/test/权限/生产配置改动;未修改文件。 +- Spec/docs update:无需新增稳定 spec;`.trellis/spec/hyb-oa/backend-fastify-prisma.md` 的 `Local Regression Migration Drift` 已覆盖 status → 显式批准 deploy → 缺列消失后将选择器/页面失败分流的完整规则,本任务仅补充当前 Trellis 证据。 +- Remaining PAUSED/NOT RUN:本地数据库迁移基线已恢复;浏览器回归为 `PASS_WITH_FINDINGS`,非迁移失败待后续 task;企业微信真机、生产/公网仍按 R10 保持 PAUSED,合同任务启动仍需独立批准。 + +## 10. 回归失败证据索引 + +- `client/test-results/assets-flow-E2E-资产流转主链路:建卡→领用归还→调拨→盘点/error-context.md`:POST 创建返回成功,但页面停留 `/assets/new`,未完成 canonical 详情跳转。 +- `client/test-results/procurement-flow-E2E-采购主链路:员工提单→经理审批→财务归档/error-context.md`:审批详情已渲染中文采购摘要,断言仍要求 `Employee`。 +- `client/test-results/global-search-E2E-全局搜索:采购与资产忽略旧-href-并使用实体-canonical-路径/error-context.md`:第二次回到 portal 后 `global-search-trigger` 等待超时,页面为登录态。 +- `client/test-results/r103-cross-page-ui-consist-2485f-务台账标题区保持一主操作加至多一个更多,列表工具不常驻/error-context.md`:合同列表标题未出现。 +- `client/test-results/r103-cross-page-ui-consist-bed95-cy-采购页使用安全格式上传并通过认证-Blob-下载/error-context.md`、`06cda-资产列表、详情各自只请求职责内数据并恢复-URL-筛选/error-context.md`、`aa8b7-与资产长表单详情对-403-404-使用同一安全错误态/error-context.md`、`c9ebc-ncy-采购长表单切回记录列表时支持取消保持与确认放弃/error-context.md`、`b8364-cy-共享未保存保护覆盖浏览器返回取消、确认与刷新拦截/error-context.md`、`b7ec9-ursements-在中等宽度下操作列可达且页面不横溢/error-context.md`:均在页面契约断言前回到登录页,归为 mock 会话/页面环境问题,未归因于 migration。 diff --git a/.trellis/tasks/08-07-local-database-migration-readiness/prd.md b/.trellis/tasks/08-07-local-database-migration-readiness/prd.md new file mode 100644 index 00000000..3a4ec444 --- /dev/null +++ b/.trellis/tasks/08-07-local-database-migration-readiness/prd.md @@ -0,0 +1,90 @@ +# 本地数据库迁移基线就绪 + +## Goal + +在启动 `08-05-contract-detail-workspace` 前,把当前回环本地开发数据库恢复到与仓库已提交 Prisma migration 一致的可验证基线,消除因 `employees.bank_institution_id` 缺列导致的 `/api/me` 500,并把上一轮被阻塞的五个采购/资产路由回归用例从 `PAUSED` 转为有证据的实际结果。 + +本任务只清理本地数据库基线,不建设产品能力,不生成迁移,不修改业务代码。 + +## Confirmed Facts + +- `server/package.json` 同时提供 `prisma:migrate:dev` 和 `prisma:migrate:deploy`;本任务唯一允许的 schema 写入入口是 `npm run prisma:migrate:deploy`,它只应用仓库中已提交且尚未执行的 migration。 +- `server/prisma/schema.prisma:601` 已定义 `Employee.bank_institution_id`;该列由 `20260802143000_corporate_bank_account_payment_order` 增加。 +- 第一次 pre-status 实际发现 6 条 pending:`20260717120000_auth_challenge_trusted_proxy_hardening`、`20260718120000_add_system_admin_role`、`20260718123000_small_team_payment_cancel_reason`、`20260727153000_allow_employee_phone_null`、`20260801120000_reimbursement_travel_summary`、`20260802143000_corporate_bank_account_payment_order`。 +- 六条 migration 已逐一核查:均无删除表、删除列、清空数据或 reset;其中 `20260717120000_auth_challenge_trusted_proxy_hardening` 会使本地尚未使用的密码重置码和邮箱登录码失效。用户已于 2026-08-07 明确批准将 allowlist 扩展到全部六条,并接受该本地认证挑战数据更新。 +- 归档任务 `08-05-business-detail-routing` 已记录:本地数据库缺少 `employees.bank_institution_id`,导致 `/api/me` 返回 500,相关真实链路回归保持 `PAUSED`。 +- `procurement-flow.spec.ts` 与 `assets-flow.spec.ts` 使用本地真实 API,并会留下带 `Playwright` 标记的合成采购、资产、分类和盘点记录;另外三个指定 spec 拦截 `/api/`,属于 mock 浏览器回归。 +- `scripts/qa/check-local-migration-drift.sh` 只读执行 `npx prisma migrate status` 并保存输出;非 strict 模式在存在待执行 migration 时仍以 0 退出,因此不能只看包装脚本退出码判断数据库已对齐。 + +## Scope + +- 仅针对当前 `DATABASE_URL` 指向的回环本地开发数据库,记录执行前、执行后两份 Prisma migration status 原始输出和脱敏结论。 +- 在目标、待执行 migration 和 SQL 风险均满足本 PRD 后,仅使用 `cd server && npm run prisma:migrate:deploy` 应用仓库已提交 migration。 +- 验证迁移状态已对齐、`/api/me` 恢复 200,并补跑以下五个既有用例: + - `client/tests/e2e/procurement-flow.spec.ts` + - `client/tests/e2e/assets-flow.spec.ts` + - `client/tests/e2e/r103-cross-page-ui-consistency.spec.ts` + - `client/tests/e2e/pc-detail-mobile-visual-wave3.spec.ts` + - `client/tests/e2e/global-search.spec.ts` +- 逐项记录五个用例的实际 PASS/FAIL/SKIP 数量和失败分类;数据库基线恢复不等于承诺把所有非数据库回归在本任务内修绿。 +- 保持 `08-05-contract-detail-workspace` 为 planning,直到本任务形成可复核的干净迁移基线证据。 + +## Requirements + +- R1:任何 schema 写入前,第一条数据库命令必须是只读 `prisma migrate status`。原始输出保存到 gitignored 的 `artifacts/qa-flow/08-07-local-database-migration-readiness/pre/`,Trellis 任务只记录仓库相对引用、退出状态、待执行 migration 名称和脱敏结论,不记录连接串、密码、token 或原始业务 payload。 +- R2:执行前必须确认 Prisma datasource 指向回环地址和预期本地开发数据库。目标不是 `localhost`、`127.0.0.1`、`::1` 等回环地址,或无法确认目标时,立即标记 `PAUSED`,不得执行任何迁移写入。 +- R3:允许继续的待执行集合仅限以下六个仓库已核查 migration 的任意子集:`20260717120000_auth_challenge_trusted_proxy_hardening`、`20260718120000_add_system_admin_role`、`20260718123000_small_team_payment_cancel_reason`、`20260727153000_allow_employee_phone_null`、`20260801120000_reimbursement_travel_summary`、`20260802143000_corporate_bank_account_payment_order`。若状态显示已失败 migration、迁移历史分叉、已应用文件缺失/变更,或出现其他待执行 migration,必须停止并回来确认。 +- R4:满足 R1-R3 后,唯一允许的 schema 写入命令是 `cd server && npm run prisma:migrate:deploy`。命令只执行一次;失败后不得改用其他 Prisma 命令重试,不得手工补列或修改 `_prisma_migrations`。 +- R5:执行后再次保存 `prisma migrate status` 原始输出,post 检查必须以 strict 方式要求退出码为 0,并确认同一数据库与仓库 migration 一致。若执行前已是最新状态,则跳过 deploy,直接进入 post 检查和接口诊断。 +- R6:迁移状态对齐后,使用当前本地服务或以 `JOBS_ENABLED=false` 启动的当前 checkout 服务验证一次已认证 `/api/me` 返回 200;证据只记录状态码和结论,不保存账号、令牌或响应 payload。 +- R7:五个指定 Playwright spec 必须全部进入执行清单并逐项记录结果。采购和资产用例对本地开发库产生的合成测试记录属于本轮获批验证副作用;不得因此运行 seed、reset、批量删除或手工清理。 +- R8:若 `/api/me` 仍因缺列或 migration 异常返回 500,本任务失败并保持合同任务阻塞;若 `/api/me` 已恢复而 spec 出现路由、选择器、夹具或产品行为失败,应归类为 `08-05-business-detail-routing` 遗留候选,单独记录并等待后续 task 决策,不在本任务顺手修改代码或测试。 +- R9:实施后必须启动只读 reviewer,确认数据库目标、命令 allowlist、前后 status 证据、无重置/手工 DDL、无产品代码改动和失败分类均符合本 PRD;reviewer 不修改文件。 +- R10:本任务不包含部署、公网、生产配置、企业微信真机或远程数据库验证,不得据此宣称线上可测试。 + +## Acceptance Criteria + +- [x] 执行前 `prisma migrate status` 原始输出已保存,任务记录包含脱敏目标结论、真实退出状态和完整待执行 migration 名称清单。 +- [x] 目标已确认是预期回环本地开发数据库;若不是或无法确认,任务保持 `PAUSED` 且没有执行 schema 写入。 +- [x] 待执行集合为空或仅为 R3 允许集合的子集;每个实际待执行 migration 的 SQL 已复核无删除表、删除列、清空数据或重置动作;本地未使用认证验证码失效副作用已获明确批准。 +- [x] 需要迁移时仅执行一次 `cd server && npm run prisma:migrate:deploy` 并成功;六条 pending 全部应用。全程未运行 `migrate dev`、`migrate reset`、`db push`、`migrate resolve`、手工 DDL 或迁移历史修改。 +- [x] 执行后 status 原始输出已另存,strict 检查退出 0,确认同一数据库与仓库 migration 一致。 +- [x] 已认证 `/api/me` 返回 200,且不再出现 `employees.bank_institution_id` 缺列错误;任务证据不含凭据、token 或原始响应。 +- [x] `procurement-flow.spec.ts` 已执行并记录结果:0 passed / 1 failed / 0 skipped。 +- [x] `assets-flow.spec.ts` 已执行并记录结果:0 passed / 1 failed / 0 skipped。 +- [x] `r103-cross-page-ui-consistency.spec.ts` 已执行并记录结果:4 passed / 7 failed / 0 skipped。 +- [x] `pc-detail-mobile-visual-wave3.spec.ts` 已执行并记录结果:9 passed / 0 failed / 0 skipped。 +- [x] `global-search.spec.ts` 已执行并记录结果:3 passed / 1 failed / 0 skipped。 +- [x] 五个用例中的任何失败均已先分类为迁移/数据库、产品代码、测试桩、环境或数据问题;本轮 10 条失败均发生在迁移对齐和 `/api/me` 200 之后,归为路由/页面契约或 mock 会话夹具问题,非迁移失败未在本任务内顺手修复,并已登记 `08-05-business-detail-routing` follow-up 候选。 +- [x] 只读 reviewer 已完成且未修改文件;确认本轮无业务代码、Prisma schema、migration 文件、权限模型或生产配置改动。 +- [x] `npm run prisma:validate`、`npm run build`、任务校验、Trellis governance 和 `git diff --check` 通过;工作区只包含本任务规划/证据文件的预期改动。 + +当前结论:数据库迁移基线 `PASS`;五个浏览器 spec 为 `PASS_WITH_FINDINGS`(26 tests:16 passed / 10 failed / 0 skipped)。这不等同于五个用例全绿,失败详情和后续归属见 `implement.md`。 + +## Out of Scope + +- `prisma migrate dev` / `npm run prisma:migrate:dev`。该命令可能在 drift 场景建议或触发数据库 reset,本任务明确禁止。 +- `prisma migrate reset`、`prisma db push`、`prisma migrate resolve`、删除/修改 `_prisma_migrations`、手工 `ALTER TABLE` 或其他直接 DDL。 +- 数据库重建、恢复备份、重新 seed、批量删除或清理既有及本轮 Playwright 测试记录;任何 reset 或恢复动作必须另行获得明确批准。 +- 新建或修改 Prisma schema、migration SQL、后端 API、前端页面、路由、权限、测试断言或夹具。 +- 为了让五个 spec 全绿而修复 `08-05-business-detail-routing` 遗留问题;如确认存在,另建最小 task。 +- 启动 `08-05-contract-detail-workspace`、新增合同 terminated 字段或执行合同迁移。 +- 生产、试运行、公网、远程数据库、企业微信真机或真实业务数据操作。 + +## Test Plan + +1. 前置只读证据: + - `MIGRATION_DRIFT_REPORT_DIR=artifacts/qa-flow/08-07-local-database-migration-readiness/pre bash scripts/qa/check-local-migration-drift.sh` + - 人工核对包装脚本输出中的 Prisma 原始退出状态、datasource 回环目标和待执行 migration;不能以包装脚本退出 0 代替状态判断。 +2. 静态校验:`cd server && npm run prisma:validate`。 +3. 唯一获准写入(仅在 R1-R3 通过且存在待执行 migration 时):`cd server && npm run prisma:migrate:deploy`。 +4. 执行后严格证据: + - `MIGRATION_DRIFT_REPORT_DIR=artifacts/qa-flow/08-07-local-database-migration-readiness/post MIGRATION_DRIFT_STRICT=1 bash scripts/qa/check-local-migration-drift.sh` + - 在当前本地服务上验证已认证 `/api/me` 为 200;必要时仅以 `JOBS_ENABLED=false` 启动当前 checkout 服务。 +5. 补跑五个用例: + - `cd client && npx playwright test tests/e2e/procurement-flow.spec.ts tests/e2e/assets-flow.spec.ts tests/e2e/r103-cross-page-ui-consistency.spec.ts tests/e2e/pc-detail-mobile-visual-wave3.spec.ts tests/e2e/global-search.spec.ts --workers=1 --reporter=list` +6. 逐项记录测试结果与失败分类;迁移无关失败不得在本任务内修复。 +7. 只读 reviewer 复核后运行: + - `python3 ./.trellis/scripts/task.py validate 08-07-local-database-migration-readiness` + - `node scripts/qa/check-trellis-governance.mjs --json` + - `git diff --check` diff --git a/.trellis/tasks/08-07-local-database-migration-readiness/task.json b/.trellis/tasks/08-07-local-database-migration-readiness/task.json new file mode 100644 index 00000000..7c303bf1 --- /dev/null +++ b/.trellis/tasks/08-07-local-database-migration-readiness/task.json @@ -0,0 +1,48 @@ +{ + "id": "local-database-migration-readiness", + "name": "local-database-migration-readiness", + "title": "本地数据库迁移基线就绪", + "description": "仅针对回环本地开发数据库,先后记录 Prisma 迁移状态,在安全前置满足时只用 migrate deploy 应用仓库已提交迁移,并复核 /api/me 与五个既有 E2E;禁止重置、db push、migrate dev 和产品代码修改。", + "status": "in_progress", + "dev_type": null, + "scope": "loopback-local-database-migration-baseline-and-paused-e2e-evidence", + "package": "server", + "priority": "P2", + "creator": "shunli", + "assignee": "shunli", + "createdAt": "2026-08-07", + "completedAt": null, + "branch": null, + "base_branch": "main", + "worktree_path": null, + "commit": null, + "pr_url": null, + "subtasks": [], + "children": [], + "parent": null, + "relatedFiles": [ + "server/package.json", + "server/prisma/schema.prisma", + "server/prisma/migrations/20260717120000_auth_challenge_trusted_proxy_hardening/migration.sql", + "server/prisma/migrations/20260718120000_add_system_admin_role/migration.sql", + "server/prisma/migrations/20260718123000_small_team_payment_cancel_reason/migration.sql", + "server/prisma/migrations/20260727153000_allow_employee_phone_null/migration.sql", + "server/prisma/migrations/20260801120000_reimbursement_travel_summary/migration.sql", + "server/prisma/migrations/20260802143000_corporate_bank_account_payment_order/migration.sql", + "scripts/qa/check-local-migration-drift.sh", + "client/tests/e2e/procurement-flow.spec.ts", + "client/tests/e2e/assets-flow.spec.ts", + "client/tests/e2e/r103-cross-page-ui-consistency.spec.ts", + "client/tests/e2e/pc-detail-mobile-visual-wave3.spec.ts", + "client/tests/e2e/global-search.spec.ts", + ".trellis/tasks/archive/2026-08/08-05-business-detail-routing/implement.md", + ".trellis/tasks/08-05-contract-detail-workspace/prd.md", + ".trellis/spec/hyb-oa/backend-fastify-prisma.md", + "docs/试运行与自动化验证手册.md", + "docs/测试规约与回归门禁.md" + ], + "notes": "已完成回环本地迁移基线恢复:pre-status 发现并核查 6 条 pending,用户于 2026-08-07 批准扩展 allowlist 并接受本地未使用认证验证码失效;仅执行一次 prisma migrate deploy,post-status strict PASS,五类 fixture 的 /api/me 均 200。五个 Playwright spec 最终 16 passed / 10 failed / 0 skipped,失败均为迁移对齐后的路由/页面契约或 mock 会话夹具问题,留作 follow-up;只读 reviewer、任务校验、governance 与 diff 门禁均 PASS,待用户确认提交。", + "meta": { + "description": "恢复本地 Prisma migration 基线并补跑上一轮被数据库缺列阻塞的五个采购/资产路由回归用例。" + } +}