From ed608034f62348e2488d79dfa53b5aca2b7cebc2 Mon Sep 17 00:00:00 2001 From: shunli Date: Sat, 16 May 2026 22:40:45 +0800 Subject: [PATCH] =?UTF-8?q?P2AF=20=E6=94=B6=E5=8F=A3=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E5=8C=96=E5=90=8E=E7=BB=AD=E5=92=8C=E9=BB=98=E8=AE=A4=E5=86=BB?= =?UTF-8?q?=E7=BB=93=E5=8F=A3=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/trialrun/人工试运行报告.md | 21 +- docs/文档事实源状态矩阵.md | 11 +- docs/文档索引.md | 16 +- ...10-35-22-p2-post-p2ocrs-followup-queue.csv | 6 +- ...20-09-49-p2-automated-regression-sweep.csv | 2 +- ...05-16_22-11-10-p2-automatable-followup.csv | 8 + scripts/qa/check-ocr-provider-samples.mjs | 22 +- scripts/qa/check-payment-batch-flag-off.mjs | 212 ++++++++++++++++++ scripts/qa/run-p2-safe-regression.sh | 99 ++++++++ 9 files changed, 369 insertions(+), 28 deletions(-) create mode 100644 issues/2026-05-16_22-11-10-p2-automatable-followup.csv create mode 100755 scripts/qa/check-payment-batch-flag-off.mjs create mode 100755 scripts/qa/run-p2-safe-regression.sh diff --git a/docs/trialrun/人工试运行报告.md b/docs/trialrun/人工试运行报告.md index 29c8a3be..29e718fb 100644 --- a/docs/trialrun/人工试运行报告.md +++ b/docs/trialrun/人工试运行报告.md @@ -20,9 +20,10 @@ DCL7 已删除本文件中 R70-R91 的长流水历史,避免旧环境、旧开 - 2026-05-16 P2D 旧口径已修正:live smoke 凭据、员工 OCR 凭据和授权 PDF 样票已具备,不再作为 P2-FIN/P2-OCR 的 HOLD 原因。P2-ORG 已完成只读巡检:当前连接数据源 high=0、medium=0、low=9,建议 `NO_WRITE_FIX_NEEDED`;低风险项为 CEO/root 无直属主管观察 1 和 active 员工无企业微信绑定 8。 - 2026-05-16 P2-FIN-DECISION 已完成付款批次启用决策准备:只读核对代码/flag/空态、角色账号、导出字段、审计事件、公网 readiness 和 flag off 回滚口径,结论为 `GO_TO_IMPLEMENT`,后续可另建 `P2-FIN-V1`;当前仍不启用 `payment_batch_v1`、不写付款批次业务数据、不部署。 - 2026-05-16 P2-FIN-V1 已完成付款批次启用前本地/隔离硬化:后端 focused 回归、前端 typecheck/build、隔离付款批次闭环、权限直连审计、公网 readiness 和只读 review 均通过;本轮只修补 E2E 对当前状态胶囊和二次确认 UI 的断言,不改业务 API/schema/UI,不部署,公网 `payment_batch_v1` 继续关闭。 -- 2026-05-16 P2-OCR-SAMPLE 已完成图片 OCR 样票治理和实现前置确认:当前 PDF-only 口径、`SAMPLE-IMG-*` 授权登记、脱敏证据、provider 分层和回滚设计已锁定;结论为 `HOLD_FOR_SAMPLE`,授权图片样票和真实图片 provider 回归证据形成前不进入 `P2-OCR-V1`,不开放图片 OCR。 -- 2026-05-16 P2Q 已完成 P2OCRS 后续任务队列规划:后续默认按图片样票 provider 回归门槛、UX8 真实反馈池、P2-FIN-LIVE 公网启用窗口、P2-OCR-V1、P2-WF、P2-BIZ 和持续上线门禁拆独立合同推进;本轮不改业务代码、不部署。 -- 2026-05-16 P2AUTO 已完成自动化优先回归:后端 focused、前端 typecheck/build、高风险确认、隔离核心业务流、隔离付款批次闭环、公网 readiness/PDF live OCR、公网移动矩阵和企业微信 UA/渲染自动化均通过;PDF 样票和 live smoke 凭据可用,图片 provider 回归仍需仓库外 manifest 或 `PLAYWRIGHT_OCR_IMAGE_*` 安全登记授权图片样票,不开放图片 OCR,不启用公网 `payment_batch_v1`,未部署。 +- 2026-05-16 P2-OCR-SAMPLE 已完成图片 OCR 样票治理和实现前置确认;P2AF 纠偏后当前默认结论为 `FREEZE_BY_DEFAULT`:正式能力只维护 PDF-only OCR,图片继续作为普通附件,不开放图片 OCR。 +- 2026-05-16 P2Q 已完成 P2OCRS 后续任务队列规划;P2AF 后图片 OCR 不再作为默认主线等待样票,后续默认按 UX8 真实反馈池、P2-FIN-LIVE 公网启用窗口、P2-WF、P2-BIZ 和持续上线门禁拆独立合同推进。 +- 2026-05-16 P2AUTO 已完成自动化优先回归:后端 focused、前端 typecheck/build、高风险确认、隔离核心业务流、隔离付款批次闭环、公网 readiness/PDF live OCR、公网移动矩阵和企业微信 UA/渲染自动化均通过;PDF 样票和 live smoke 凭据可用,P2AF 后 provider-only 默认结论为 `FREEZE_BY_DEFAULT`,不开放图片 OCR,不启用公网 `payment_batch_v1`,未部署。 +- 2026-05-16 P2AF 已完成可自动实施收口:图片 OCR 默认 `FREEZE_BY_DEFAULT`,PDF-only OCR 继续维护;新增 QA-only 回归聚合和付款批次公网关闭态只读门禁,公网 `payment_batch_v1` 继续 `flag_force_off`,本轮不做业务实现、不部署。 ## 关键证据索引 @@ -50,9 +51,10 @@ DCL7 已删除本文件中 R70-R91 的长流水历史,避免旧环境、旧开 | P2-ORG | 已收口:组织账号主数据只读巡检完成,高/中风险为 0,低风险观察 9,不需要写修复合同 | `issues/2026-05-16_02-04-27-p2-org-readonly-master-data-audit.csv`、`artifacts/qa-flow/p2-org-readonly-audit-20260516-020427/report.json` | | P2-FIN-DECISION | 已收口:付款批次决策准备完成,结论 `GO_TO_IMPLEMENT`;后续进入单独 `P2-FIN-V1` 合同前仍需方案批准 | `issues/2026-05-16_09-02-42-p2-fin-payment-batch-decision.csv` | | P2-FIN-V1 | 已收口:付款批次启用前本地/隔离硬化完成;隔离付款批次闭环 1 passed、权限直连审计 8 passed、公网 readiness ready | `issues/2026-05-16_09-42-43-p2-fin-payment-batch-v1.csv`、`artifacts/qa-flow/business-flow-20260516-095719/summary.txt`、`artifacts/qa-flow/business-flow-20260516-095900/summary.txt` | -| P2-OCR-SAMPLE | 已收口:图片 OCR 样票治理前置完成,结论 `HOLD_FOR_SAMPLE`;PDF live OCR 基线继续有效但不代表图片 OCR 通过 | `issues/2026-05-16_10-15-21-p2-ocr-image-sample-governance.csv` | -| P2Q | 已收口:P2OCRS 后续队列和启动门槛完成;所有实现或公网启用均需另建独立合同 | `issues/2026-05-16_10-35-22-p2-post-p2ocrs-followup-queue.csv` | -| P2AUTO | 已收口:自动化优先回归和样票 provider 门禁完成;核心隔离业务流通过、付款批次隔离闭环 1 passed、公网 readiness/PDF live OCR 通过、公网移动矩阵 6 passed、企业微信渲染矩阵 8 passed;图片 provider 回归保持 `HOLD_FOR_SAMPLE` | `issues/2026-05-16_20-09-49-p2-automated-regression-sweep.csv`、`artifacts/qa-flow/business-flow-20260516-203001/summary.txt`、`artifacts/qa-flow/business-flow-20260516-203856/summary.txt`、`artifacts/qa-flow/live-smoke-readiness-20260516-204157/` | +| P2-OCR-SAMPLE | 已收口:图片 OCR 样票治理前置完成;P2AF 后当前默认 `FREEZE_BY_DEFAULT`,PDF live OCR 基线继续有效 | `issues/2026-05-16_10-15-21-p2-ocr-image-sample-governance.csv` | +| P2Q | 已收口:P2OCRS 后续队列和启动门槛完成;图片 OCR 默认冻结,所有实现或公网启用均需另建独立合同 | `issues/2026-05-16_10-35-22-p2-post-p2ocrs-followup-queue.csv` | +| P2AUTO | 已收口:自动化优先回归和样票 provider 门禁完成;核心隔离业务流通过、付款批次隔离闭环 1 passed、公网 readiness/PDF live OCR 通过、公网移动矩阵 6 passed、企业微信渲染矩阵 8 passed;provider 默认结论改为 `FREEZE_BY_DEFAULT` | `issues/2026-05-16_20-09-49-p2-automated-regression-sweep.csv`、`artifacts/qa-flow/business-flow-20260516-203001/summary.txt`、`artifacts/qa-flow/business-flow-20260516-203856/summary.txt`、`artifacts/qa-flow/live-smoke-readiness-20260516-204157/` | +| P2AF | 已收口:可自动实施收口完成;安全回归聚合 PASS,付款批次公网关闭态只读门禁 PASS,UX8 自动矩阵通过且 UX8-1..5 保持真实反馈驱动 | `issues/2026-05-16_22-11-10-p2-automatable-followup.csv`、`artifacts/qa-flow/p2-safe-regression-20260516-222222/summary.json` | ## 当前门禁 @@ -70,9 +72,10 @@ DCL7 已删除本文件中 R70-R91 的长流水历史,避免旧环境、旧开 | P2-ORG 只读巡检 | 通过,含低风险观察项 | `server/src/scripts/audit-org-master-data.js` 在 READ ONLY 事务中执行;当前连接数据源 high=0、medium=0、low=9,未输出手机号、邮箱、token 或企业微信真实标识,报告 `artifacts/qa-flow/p2-org-readonly-audit-20260516-020427/` | | P2-FIN 决策准备 | 通过 | `LIVE_SMOKE_BASE_URL=https://oa.hyinda.com node scripts/qa/check-live-smoke-env.mjs` 显示 publicReady/liveSmokeReady/liveOcrReady 均为 true;只读核对付款批次 flag、权限、导出、审计和回滚口径;未执行付款批次业务写入 | | P2-FIN-V1 启用前硬化 | 通过,未部署 | 后端 focused 回归 8 tests passed;`client` typecheck/build 通过;隔离临时库 `reimbursement-payment-batch-flow` 1 passed,覆盖创建批次、复核、二次确认执行、报销 paid、票据 archived、AP 台账和 ERP outbox;权限直连审计 8 passed;公网只做 readiness,不启用 `payment_batch_v1` | -| P2-OCR-SAMPLE 样票治理 | HOLD,未部署 | 已确认当前发票 OCR 仍为 PDF-only,图片仅作为普通附件;腾讯云 `image_native` 只证明技术路径存在,正式图片 OCR 仍需授权图片样票、脱敏确认和真实图片 provider 回归;未调用真实图片 OCR | -| P2Q 后续队列 | 已收口,未部署 | 图片 OCR provider 回归等待授权图片样票;UX8 等待真实反馈;P2-FIN-LIVE 等待公网启用窗口批准;后续 P2-OCR-V1/P2-WF/P2-BIZ 均需独立合同 | -| P2AUTO 自动化优先回归 | 通过,未部署 | 后端 focused 43 tests passed;`client` typecheck/build 通过;核心隔离业务流 auth/reimbursement/approval/leave/procurement/governance PASS;付款批次隔离 flag-on 1 passed;公网 `/ready`、public bundle、live smoke 3 passed、PDF live OCR 1 passed;公网移动矩阵 6 passed;企业微信渲染矩阵 8 passed;provider-only 样票脚本输出 `HOLD_FOR_SAMPLE`,因为未通过安全 manifest/env 登记授权图片样票且本地无 Tencent provider 凭据 | +| P2-OCR-SAMPLE 样票治理 | 默认冻结,未部署 | 已确认当前发票 OCR 仍为 PDF-only,图片仅作为普通附件;腾讯云 `image_native` 只证明技术路径存在,不等同于当前产品能力;正式图片 OCR 默认 `FREEZE_BY_DEFAULT`,除非用户明确重启评估 | +| P2Q 后续队列 | 已收口,未部署 | 图片 OCR 不再作为默认主线等待样票;UX8 等待真实反馈;P2-FIN-LIVE 等待公网启用窗口批准;后续 P2-WF/P2-BIZ 均需独立合同 | +| P2AUTO 自动化优先回归 | 通过,未部署 | 后端 focused 43 tests passed;`client` typecheck/build 通过;核心隔离业务流 auth/reimbursement/approval/leave/procurement/governance PASS;付款批次隔离 flag-on 1 passed;公网 `/ready`、public bundle、live smoke 3 passed、PDF live OCR 1 passed;公网移动矩阵 6 passed;企业微信渲染矩阵 8 passed;P2AF 后 provider-only 默认结论为 `FREEZE_BY_DEFAULT` | +| P2AF 可自动实施收口 | 通过,未部署 | `run-p2-safe-regression.sh` PASS:后端 focused 43 tests passed,`client` typecheck/build 通过,核心隔离业务流 auth/reimbursement/approval/leave/procurement/governance PASS,公网 readiness/PDF live OCR 通过,付款批次公网关闭态 409 `payment_batch_v1_disabled / flag_force_off`,公网移动矩阵 6 passed,企业微信渲染矩阵 8 passed,OCR provider-only 默认 `FREEZE_BY_DEFAULT`;不启用公网 `payment_batch_v1`,不开放图片 OCR,不做业务实现 | ## R7EP / OPS8 / QAF8 / UX8 / P2T 预检推进 diff --git a/docs/文档事实源状态矩阵.md b/docs/文档事实源状态矩阵.md index 390e0316..7d93735b 100644 --- a/docs/文档事实源状态矩阵.md +++ b/docs/文档事实源状态矩阵.md @@ -41,9 +41,10 @@ | `P2-ORG` | `issues/2026-05-16_02-04-27-p2-org-readonly-master-data-audit.csv` | `EVIDENCE` | 组织账号主数据只读巡检已完成;当前连接数据源高/中风险为 0,仅有低风险观察项,不需要写修复合同 | | `P2-FIN-DECISION` | `issues/2026-05-16_09-02-42-p2-fin-payment-batch-decision.csv` | `EVIDENCE` | 付款批次启用决策准备已完成,结论 `GO_TO_IMPLEMENT`;后续必须另建 `P2-FIN-V1` 实现合同,当前不启用 `payment_batch_v1` | | `P2-FIN-V1` | `issues/2026-05-16_09-42-43-p2-fin-payment-batch-v1.csv` | `EVIDENCE` | 付款批次启用前本地/隔离硬化已完成;后端 focused 回归、前端构建、隔离闭环、权限审计和公网 readiness 通过,公网仍不启用 `payment_batch_v1` | -| `P2-OCR-SAMPLE` | `issues/2026-05-16_10-15-21-p2-ocr-image-sample-governance.csv` | `EVIDENCE` | 图片 OCR 样票治理和实现前置确认已完成;结论 `HOLD_FOR_SAMPLE`,当前保持 PDF-only,不开放图片 OCR | -| `P2Q` | `issues/2026-05-16_10-35-22-p2-post-p2ocrs-followup-queue.csv` | `EVIDENCE` | P2OCRS 后续任务队列已锁定;后续实现均需另建独立合同,当前不部署、不改业务代码 | -| `P2AUTO` | `issues/2026-05-16_20-09-49-p2-automated-regression-sweep.csv` | `EVIDENCE` | 自动化优先回归已收口;后端/前端/隔离业务流/公网 readiness/PDF live OCR/移动与企业微信自动化均有证据,图片 provider 回归仍需安全登记的授权图片样票和本地 provider 凭据 | +| `P2-OCR-SAMPLE` | `issues/2026-05-16_10-15-21-p2-ocr-image-sample-governance.csv` | `EVIDENCE` | 图片 OCR 样票治理和实现前置确认已完成;P2AF 纠偏后当前默认 `FREEZE_BY_DEFAULT`,保持 PDF-only,不开放图片 OCR | +| `P2Q` | `issues/2026-05-16_10-35-22-p2-post-p2ocrs-followup-queue.csv` | `EVIDENCE` | P2OCRS 后续任务队列已锁定;图片 OCR 默认冻结,后续实现均需另建独立合同,当前不部署、不改业务代码 | +| `P2AUTO` | `issues/2026-05-16_20-09-49-p2-automated-regression-sweep.csv` | `EVIDENCE` | 自动化优先回归已收口;后端/前端/隔离业务流/公网 readiness/PDF live OCR/移动与企业微信自动化均有证据,provider-only 默认决策为 `FREEZE_BY_DEFAULT` | +| `P2AF` | `issues/2026-05-16_22-11-10-p2-automatable-followup.csv` | `EVIDENCE` | 可自动实施收口已完成;纠正图片 OCR 默认冻结口径,补 QA-only 安全回归、付款批次公网关闭态只读门禁和 UX8 自动矩阵扫尾;报告 `artifacts/qa-flow/p2-safe-regression-20260516-222222/` | 旧 Issue CSV 保留为历史审计证据:不删除、不批量回写、不移动到 `docs/archive/` 或其他归档目录。新连续合同如替代旧合同,只在新合同 `Notes` 或文档索引中说明 supersedes。 @@ -51,8 +52,8 @@ | Doc_ID | Path | Category | Authority | Status | Owner | Related_Issues | Evidence | Supersedes | Last_Audited | Audit_Method | Risk | Notes | | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| `IDX` | `docs/文档索引.md` | 入口索引 | 最高文档入口 | `CURRENT` | product/dev | `GOV8,QAOPS9,P2D,P2-ORG,P2-FIN-DECISION,P2-FIN-V1,P2-OCR-SAMPLE,P2Q,P2AUTO` | `rg` | none | 2026-05-16 | `node scripts/qa/audit-docs.mjs --check-registry` | medium | 判断当前该读什么 | -| `REG` | `docs/文档事实源状态矩阵.md` | 文档台账 | 文档分类和状态事实源 | `CURRENT` | product/dev | `GOV8,QAOPS9,P2D,P2-ORG,P2-FIN-DECISION,P2-FIN-V1,P2-OCR-SAMPLE,P2Q,P2AUTO` | `audit-docs` | none | 2026-05-16 | `audit-docs --check-registry` | medium | 判断文档权威性 | +| `IDX` | `docs/文档索引.md` | 入口索引 | 最高文档入口 | `CURRENT` | product/dev | `GOV8,QAOPS9,P2D,P2-ORG,P2-FIN-DECISION,P2-FIN-V1,P2-OCR-SAMPLE,P2Q,P2AUTO,P2AF` | `rg` | none | 2026-05-16 | `node scripts/qa/audit-docs.mjs --check-registry` | medium | 判断当前该读什么 | +| `REG` | `docs/文档事实源状态矩阵.md` | 文档台账 | 文档分类和状态事实源 | `CURRENT` | product/dev | `GOV8,QAOPS9,P2D,P2-ORG,P2-FIN-DECISION,P2-FIN-V1,P2-OCR-SAMPLE,P2Q,P2AUTO,P2AF` | `audit-docs` | none | 2026-05-16 | `audit-docs --check-registry` | medium | 判断文档权威性 | | `PRD-MVP` | `docs/海音达OA-MVP需求规格说明书.md` | 需求事实源 | PRD 主事实源 | `CURRENT` | product | `REQ8,P2T,GOV8` | Issue CSV | `docs/requirements-specification.md` | 2026-05-13 | `rg` | high | 定义做什么、不做什么和验收口径 | | `PRODUCT-SCOPE` | `docs/产品口径.md` | 需求事实源 | 产品边界事实源 | `CURRENT` | product | `REQ8,P2T` | `rg` | none | 2026-05-13 | `rg` | high | 记录已拍板产品边界 | | `DESIGN-UI` | `docs/产品设计规则.md` | 设计事实源 | PC/移动/企业微信设计事实源 | `CURRENT` | design/frontend | `MIA7,MIF7,GOV8,DG8,DG9` | Playwright/视觉审查/DG8/DG9 | none | 2026-05-13 | `rg` | high | 页面改动必须读取;DG8 后飞书为主 UI/布局参考;DG9 后按页面分层和样板选型 | diff --git a/docs/文档索引.md b/docs/文档索引.md index a19e17ff..dec17695 100644 --- a/docs/文档索引.md +++ b/docs/文档索引.md @@ -20,7 +20,7 @@ ## 最近事实源链 -R6 -> PC6 -> PA6 -> FL6 -> AP6 -> RI6 -> LP6 -> ME6/QG6 -> DG7/MRF7 -> MIA7 -> MIF7/MIF7T(公网部署与带凭据公网复检已收口) -> DG8 -> DG9 -> R7E -> FBA -> QAOPS9/P2D -> P2-ORG -> P2-FIN-DECISION -> P2-FIN-V1 -> P2-OCR-SAMPLE -> P2Q -> P2AUTO。 +R6 -> PC6 -> PA6 -> FL6 -> AP6 -> RI6 -> LP6 -> ME6/QG6 -> DG7/MRF7 -> MIA7 -> MIF7/MIF7T(公网部署与带凭据公网复检已收口) -> DG8 -> DG9 -> R7E -> FBA -> QAOPS9/P2D -> P2-ORG -> P2-FIN-DECISION -> P2-FIN-V1 -> P2-OCR-SAMPLE -> P2Q -> P2AUTO -> P2AF。 - R6、PC6、PA6、FL6、AP6、RI6、LP6 已完成并部署。 - ME6 自动化、公网视觉矩阵和 `ME6-3` 代表性企业微信真机门禁已完成。 @@ -35,9 +35,10 @@ R6 -> PC6 -> PA6 -> FL6 -> AP6 -> RI6 -> LP6 -> ME6/QG6 -> DG7/MRF7 -> MIA7 -> M - P2-ORG 是 P2D 后第一个执行合同,已完成组织账号主数据只读巡检;当前连接数据源下高/中风险为 0,低风险观察项为 CEO/root 无直属主管 1 和 active 员工无企业微信绑定 8;本轮不需要写修复合同。 - P2-FIN-DECISION 已完成付款批次启用决策准备,结论为 `GO_TO_IMPLEMENT`:可进入单独 `P2-FIN-V1` 实现合同;当前仍不启用 `payment_batch_v1`、不写付款批次业务数据、不部署。 - P2-FIN-V1 已完成付款批次启用前本地/隔离硬化:后端 focused 回归、前端 typecheck/build、隔离付款批次闭环、权限直连审计和公网 readiness 均通过;本轮只硬化 E2E 断言,不新增 API/schema/feature flag/UI,不部署,公网 `payment_batch_v1` 继续关闭。 -- P2-OCR-SAMPLE 已完成图片 OCR 样票治理和实现前置确认:当前 PDF-only 口径、样票脱敏、provider 回归分层和回滚条件已锁定;结论为 `HOLD_FOR_SAMPLE`,需授权图片样票和真实图片 provider 回归证据后才可进入 `P2-OCR-V1`。 -- P2Q 是 P2OCRS 后续任务队列合同:只锁定 `P2-OCR-IMAGE-REGRESSION`、`UX8`、`P2-FIN-LIVE`、`P2-OCR-V1`、`P2-WF`、`P2-BIZ` 和持续上线门禁的启动条件,不启动实现、不部署。 -- P2AUTO 是 P2Q 后自动化优先回归合同:后端 focused、前端构建、高风险确认、隔离核心业务流、隔离付款批次闭环、公网 readiness/PDF live OCR、公网移动矩阵和企业微信 UA/渲染自动化均已完成;PDF 样票和 live smoke 凭据可用,图片样票仍需通过仓库外 manifest 或 `PLAYWRIGHT_OCR_IMAGE_*` 安全登记后才能进入真实图片 provider 回归。 +- P2-OCR-SAMPLE 已完成图片 OCR 样票治理和实现前置确认;P2AF 纠偏后当前口径为图片发票 OCR `FREEZE_BY_DEFAULT`,正式能力只维护 PDF-only OCR,图片仍按普通附件处理。 +- P2Q 是 P2OCRS 后续任务队列合同:只锁定 `UX8`、`P2-FIN-LIVE`、审批通知、轻业务增强和持续上线门禁等启动条件;图片 OCR 不进入默认主线,只有用户明确重启评估才另建 provider 回归或实现合同。 +- P2AUTO 是 P2Q 后自动化优先回归合同:后端 focused、前端构建、高风险确认、隔离核心业务流、隔离付款批次闭环、公网 readiness/PDF live OCR、公网移动矩阵和企业微信 UA/渲染自动化均已完成;PDF 样票和 live smoke 凭据可用,图片 OCR 默认冻结。 +- P2AF 是 P2AUTO 后可自动实施收口合同:纠正图片 OCR 默认冻结口径,补 QA-only provider 决策语义、安全回归聚合、付款批次公网关闭态只读门禁和 UX8 自动矩阵扫尾;不部署、不启用公网付款批次、不开放图片 OCR。 ## 当前活跃治理流 @@ -59,9 +60,10 @@ R6 -> PC6 -> PA6 -> FL6 -> AP6 -> RI6 -> LP6 -> ME6/QG6 -> DG7/MRF7 -> MIA7 -> M | `P2-ORG` | `issues/2026-05-16_02-04-27-p2-org-readonly-master-data-audit.csv` | 已收口:组织主数据只读巡检完成 | 主管链、停用审批人、角色持有人、admin-only、财务/admin 混授、企业微信绑定漂移的脱敏只读审计 | 不写生产账号数据、不执行 P2-ORG-FIX、不改 API/schema/UI、不部署 | | `P2-FIN-DECISION` | `issues/2026-05-16_09-02-42-p2-fin-payment-batch-decision.csv` | 已收口:付款批次决策准备完成,结论 `GO_TO_IMPLEMENT` | 付款批次代码/flag/空态、角色账号矩阵、导出字段、审计事件、公网验收窗口和 flag off 回滚口径 | 不启用 `payment_batch_v1`、不写付款批次、不接真实支付、不写 ERP 实时凭证、不部署 | | `P2-FIN-V1` | `issues/2026-05-16_09-42-43-p2-fin-payment-batch-v1.csv` | 已收口:付款批次启用前本地/隔离硬化完成 | 后端付款批次/AP/导出 focused 回归、前端构建、隔离闭环、权限审计、公网 readiness、flag off 回滚口径 | 不公网启用 `payment_batch_v1`、不部署、不接真实支付、不写 ERP 实时凭证 | -| `P2-OCR-SAMPLE` | `issues/2026-05-16_10-15-21-p2-ocr-image-sample-governance.csv` | 已收口:图片 OCR 样票治理前置完成,结论 `HOLD_FOR_SAMPLE` | PDF-only 当前边界、`SAMPLE-IMG-*` 授权登记口径、脱敏证据规则、mock/PDF/真实图片 provider 分层、feature flag 和回滚设计 | 不开放图片 OCR、不新增入口或 feature flag、不调用真实图片 provider、不保存真实票据、不部署 | -| `P2Q` | `issues/2026-05-16_10-35-22-p2-post-p2ocrs-followup-queue.csv` | 已收口:P2OCRS 后续队列和启动门槛完成 | 图片 OCR provider 回归、UX8、付款批次公网启用、图片 OCR V1、审批通知、轻业务增强和持续上线门禁的后续合同顺序 | 不直接启动 HOLD 项、不新建综合规划文档、不改业务代码、不部署 | -| `P2AUTO` | `issues/2026-05-16_20-09-49-p2-automated-regression-sweep.csv` | 已收口:自动化优先回归和样票 provider 门禁完成 | 凭据/样票掩码核对、后端 focused、前端构建、高风险确认、隔离业务流、隔离付款批次、公网 readiness/PDF live OCR、移动/企业微信自动化、OCR provider-only 脚本 | 不部署、不开放图片 OCR、不打开公网 `payment_batch_v1`、不自动选择未登记图片样票 | +| `P2-OCR-SAMPLE` | `issues/2026-05-16_10-15-21-p2-ocr-image-sample-governance.csv` | 已收口:图片 OCR 样票治理前置完成;P2AF 后当前默认 `FREEZE_BY_DEFAULT` | PDF-only 当前边界、`SAMPLE-IMG-*` 授权登记口径、脱敏证据规则、mock/PDF/真实图片 provider 分层、feature flag 和回滚设计 | 默认不支持图片发票 OCR;不新增入口或 feature flag、不调用真实图片 provider、不保存真实票据、不部署 | +| `P2Q` | `issues/2026-05-16_10-35-22-p2-post-p2ocrs-followup-queue.csv` | 已收口:P2OCRS 后续队列和启动门槛完成;图片 OCR 默认冻结 | UX8、付款批次公网启用、审批通知、轻业务增强和持续上线门禁的后续合同顺序;图片 OCR 仅在用户明确重启评估时另建合同 | 不直接启动 HOLD 项、不新建综合规划文档、不改业务代码、不部署 | +| `P2AUTO` | `issues/2026-05-16_20-09-49-p2-automated-regression-sweep.csv` | 已收口:自动化优先回归和样票 provider 门禁完成;P2AF 后 provider 默认结论为 `FREEZE_BY_DEFAULT` | 凭据/样票掩码核对、后端 focused、前端构建、高风险确认、隔离业务流、隔离付款批次、公网 readiness/PDF live OCR、移动/企业微信自动化、OCR provider-only 脚本 | 不部署、不开放图片 OCR、不打开公网 `payment_batch_v1`、不自动选择未登记图片样票 | +| `P2AF` | `issues/2026-05-16_22-11-10-p2-automatable-followup.csv` | 已收口:可自动实施收口完成 | 图片 OCR 默认冻结纠偏、QA 脚本语义、安全回归聚合、付款批次公网关闭态只读门禁、UX8 自动矩阵扫尾;报告 `artifacts/qa-flow/p2-safe-regression-20260516-222222/` | 不部署、不开放图片 OCR、不启用公网 `payment_batch_v1`、不做业务实现 | 旧 Issue CSV 不删除、不批量回写、不移动到归档目录。若新治理流替代旧合同,只在新 CSV 的 `Notes` 或本索引说明 supersedes,旧文件保留历史审计语义。 diff --git a/issues/2026-05-16_10-35-22-p2-post-p2ocrs-followup-queue.csv b/issues/2026-05-16_10-35-22-p2-post-p2ocrs-followup-queue.csv index bc52c87f..b8cc305c 100644 --- a/issues/2026-05-16_10-35-22-p2-post-p2ocrs-followup-queue.csv +++ b/issues/2026-05-16_10-35-22-p2-post-p2ocrs-followup-queue.csv @@ -1,9 +1,9 @@ ID,Title,Description,Acceptance,Test_Method,Tools,Dev_Status,Review1_Status,Regression_Status,Files,Dependencies,Notes -"P2Q-0","P2OCRS 后续队列建档","建立 P2OCRS 后全量后续任务队列合同,只锁定启动顺序、前置条件、后续合同 slug 和冻结边界,不执行任何业务功能开发。","1) CSV 校验通过;2) 当前事实链以 P2-OCR-SAMPLE=HOLD_FOR_SAMPLE 为基线;3) 不新增业务 API/schema/UI/feature flag;4) 不部署、不执行生产恢复、不重置数据库、不打印 secret 或远端 env;5) 后续实现均必须另建独立 Issue CSV。","受影响流程:P2 后续派工、图片 OCR、UX8、付款批次公网启用、审批通知、轻业务增强和上线门禁;验证步骤:python3 /home/shunli/.codex/skills/plan/scripts/validate_issues_csv.py issues/2026-05-16_10-35-22-p2-post-p2ocrs-followup-queue.csv;人工核对本 CSV 不含业务代码、API/schema、部署或生产操作。","python3|manual","DONE","DONE","DONE","issues/2026-05-16_10-35-22-p2-post-p2ocrs-followup-queue.csv","[]","QueueScope=DONE; no runtime effect; rollback=delete this CSV and doc status updates before commit." -"P2Q-1","图片 OCR provider 回归启动门槛","锁定 P2-OCR-IMAGE-REGRESSION 的启动条件:只有出现授权图片样票、安全别名和脱敏规则确认后,才新建 provider 回归合同。","1) 未提供授权图片样票时不启动真实图片 provider 回归;2) 仓库只记录 SAMPLE-IMG-* 别名、格式、大小、状态、字段命中摘要和失败原因;3) 不开放图片 OCR;4) 不提交真实票据或敏感字段;5) 结论枚举为 GO_TO_IMPLEMENT、HOLD_FOR_SAMPLE、FREEZE。","受影响流程:图片样票授权 -> 脱敏确认 -> 真实 provider 回归 -> P2-OCR-V1 是否启动;验证步骤:人工核对 P2-OCR-SAMPLE Notes、docs/报销发票附件规则.md 和 docs/需求-报销发票OCR规约.md;rg -n 'P2OCRS|HOLD_FOR_SAMPLE|SAMPLE-IMG|图片 OCR|image_native' docs issues server/src client/tests scripts。","manual|rg","DONE","DONE","DONE","issues/2026-05-16_10-15-21-p2-ocr-image-sample-governance.csv|docs/报销发票附件规则.md|docs/需求-报销发票OCR规约.md","[""P2Q-0""]","NextContract=issues/-p2-ocr-image-provider-regression.csv; CurrentDecision=WAIT_FOR_AUTHORIZED_IMAGE_SAMPLE; no provider call until sample evidence exists." +"P2Q-0","P2OCRS 后续队列建档","建立 P2OCRS 后全量后续任务队列合同,只锁定启动顺序、前置条件、后续合同 slug 和冻结边界,不执行任何业务功能开发。","1) CSV 校验通过;2) P2AF 后当前事实链以图片 OCR=FREEZE_BY_DEFAULT 为基线;3) 不新增业务 API/schema/UI/feature flag;4) 不部署、不执行生产恢复、不重置数据库、不打印 secret 或远端 env;5) 后续实现均必须另建独立 Issue CSV。","受影响流程:P2 后续派工、图片 OCR、UX8、付款批次公网启用、审批通知、轻业务增强和上线门禁;验证步骤:python3 /home/shunli/.codex/skills/plan/scripts/validate_issues_csv.py issues/2026-05-16_10-35-22-p2-post-p2ocrs-followup-queue.csv;人工核对本 CSV 不含业务代码、API/schema、部署或生产操作。","python3|manual","DONE","DONE","DONE","issues/2026-05-16_10-35-22-p2-post-p2ocrs-followup-queue.csv","[]","QueueScope=DONE; no runtime effect; P2AF correction=current image OCR default is FREEZE_BY_DEFAULT, not a mainline sample wait." +"P2Q-1","图片 OCR provider 回归启动门槛","锁定 P2-OCR-IMAGE-REGRESSION 的启动条件:图片 OCR 默认冻结;只有用户明确重启图片 OCR 评估并提供授权图片样票、安全别名和脱敏规则确认后,才新建 provider 回归合同。","1) 默认不启动真实图片 provider 回归;2) 仓库只记录 SAMPLE-IMG-* 别名、格式、大小、状态、字段命中摘要和失败原因;3) 不开放图片 OCR;4) 不提交真实票据或敏感字段;5) 结论枚举为 GO_TO_IMPLEMENT、HOLD_FOR_SAMPLE、FREEZE_BY_DEFAULT。","受影响流程:用户明确重启图片 OCR 评估 -> 图片样票授权 -> 脱敏确认 -> 真实 provider 回归 -> P2-OCR-V1 是否启动;验证步骤:人工核对 P2-OCR-SAMPLE Notes、docs/报销发票附件规则.md 和 docs/需求-报销发票OCR规约.md;rg -n 'P2OCRS|FREEZE_BY_DEFAULT|SAMPLE-IMG|图片 OCR|image_native' docs issues server/src client/tests scripts。","manual|rg","DONE","DONE","DONE","issues/2026-05-16_10-15-21-p2-ocr-image-sample-governance.csv|docs/报销发票附件规则.md|docs/需求-报销发票OCR规约.md","[""P2Q-0""]","NextContract=issues/-p2-ocr-image-provider-regression.csv; CurrentDecision=FREEZE_BY_DEFAULT_UNLESS_USER_RESTARTS_IMAGE_OCR; no provider call until explicit evaluation approval and sample evidence exist." "P2Q-2","UX8 真实反馈队列","确认无授权图片样票时,默认可执行队列转向 UX8 真实反馈池;继续使用既有 UX8 CSV,不新建同类 CSV。","1) UX8-1..5 只有真实截图、设备、角色、页面、复现步骤或明确频次时才实施;2) 移动采购创建仍作为新能力评估,不并入普通 UX 修复;3) 体验修复不新增后端字段、API、状态机或业务域;4) 无新反馈时只保持 TODO 和观察;5) 需要视觉验证时覆盖 320/360/375/390/393/414/430。","受影响流程:移动首页、详情扫读、财务摘要、移动采购评估、列表卡片反馈;验证步骤:人工核对 issues/2026-05-13_13-17-38-ux8-trialrun-feedback-polish.csv;rg -n 'UX8-1|UX8-2|UX8-3|UX8-4|UX8-5|移动采购创建|真实反馈' docs issues。","manual|rg|playwright","DONE","DONE","DONE","issues/2026-05-13_13-17-38-ux8-trialrun-feedback-polish.csv|docs/产品设计规则.md|docs/trialrun/人工试运行报告.md","[""P2Q-0""]","NextContract=existing UX8 CSV; CurrentDecision=KEEP_FEEDBACK_POOL; no UI change without reproducible feedback." "P2Q-3","P2-FIN-LIVE 公网启用窗口门槛","锁定付款批次公网启用窗口必须独立于 P2-FIN-V1;只有用户批准公网验收窗口、财务/复核参与人、flag 启停和回滚联系人后才启动。","1) P2-FIN-LIVE 必须另建 CSV;2) 不改变 API 契约,只受控启停公网 payment_batch_v1;3) 验收覆盖财务创建、复核、执行、审计、导出和 flag off 回滚;4) 不接真实支付、不写 ERP 实时凭证;5) 不执行生产恢复或数据库重置。","受影响流程:审批通过报销 -> 公网付款批次创建 -> 复核 -> 执行标记 -> AP 导出 -> flag off 回滚;验证步骤:人工核对 P2-FIN-V1 证据和 P2-FIN-DECISION 结论;rg -n 'P2-FIN-V1|P2-FIN-DECISION|payment_batch_v1|PAYMENT_BATCH|EXPORT_AP_OPERATIONS|flag_force_off' docs issues server/src client/tests。","manual|rg","DONE","DONE","DONE","issues/2026-05-16_09-42-43-p2-fin-payment-batch-v1.csv|issues/2026-05-16_09-02-42-p2-fin-payment-batch-decision.csv|docs/trialrun/人工试运行报告.md","[""P2Q-0""]","NextContract=issues/-p2-fin-payment-batch-live-enable.csv; CurrentDecision=WAIT_FOR_LIVE_WINDOW_APPROVAL; public payment_batch_v1 remains off." -"P2Q-4","P2-OCR-V1 最小实现门槛","锁定图片 OCR 最小实现只能在 P2-OCR-IMAGE-REGRESSION=GO_TO_IMPLEMENT 后启动,且必须独立声明入口、MIME allowlist、provider、字段、审计、flag 和 PDF-only 兼容。","1) 未满足授权图片样票和真实图片 provider 回归前不得启动;2) 不默认开放拍照 capture、OFD、多票种或 AI 自动处理;3) PDF-only 流程必须不回归;4) 实现合同必须包含权限、审计和回滚;5) 不从本队列自动继承 API/schema/UI 变化。","受影响流程:图片发票上传 -> provider 识别 -> 字段归一化 -> 查重 -> 人工复核 -> flag off 回滚;验证步骤:人工核对 P2Q-1 结论和 P2-OCR-SAMPLE 前置;rg -n 'P2-OCR-V1|图片 OCR|PDF-only|SAMPLE-IMG|feature flag|provider|字段归一化' docs issues。","manual|rg","DONE","DONE","DONE","issues/2026-05-16_10-15-21-p2-ocr-image-sample-governance.csv|docs/需求-报销发票OCR规约.md|docs/报销发票附件规则.md","[""P2Q-1""]","NextContract=issues/-p2-ocr-image-v1.csv; CurrentDecision=BLOCKED_BY_SAMPLE_REGRESSION; no implementation now." +"P2Q-4","P2-OCR-V1 最小实现门槛","锁定图片 OCR 最小实现默认冻结;只有用户明确重启评估且 P2-OCR-IMAGE-REGRESSION=GO_TO_IMPLEMENT 后才可启动,并必须独立声明入口、MIME allowlist、provider、字段、审计、flag 和 PDF-only 兼容。","1) 默认不启动图片 OCR 实现;2) 不默认开放拍照 capture、OFD、多票种或 AI 自动处理;3) PDF-only 流程必须不回归;4) 实现合同必须包含权限、审计和回滚;5) 不从本队列自动继承 API/schema/UI 变化。","受影响流程:未来显式重启时的图片发票上传 -> provider 识别 -> 字段归一化 -> 查重 -> 人工复核 -> flag off 回滚;验证步骤:人工核对 P2Q-1 结论和 P2-OCR-SAMPLE 前置;rg -n 'P2-OCR-V1|图片 OCR|PDF-only|SAMPLE-IMG|feature flag|provider|字段归一化|FREEZE_BY_DEFAULT' docs issues。","manual|rg","DONE","DONE","DONE","issues/2026-05-16_10-15-21-p2-ocr-image-sample-governance.csv|docs/需求-报销发票OCR规约.md|docs/报销发票附件规则.md","[""P2Q-1""]","NextContract=issues/-p2-ocr-image-v1.csv; CurrentDecision=FREEZE_BY_DEFAULT; no implementation now." "P2Q-5","P2-WF 审批通知扩展门槛","锁定审批通知扩展先走决策合同;只有明确通知痛点、模板清单、收件范围或转审扩展需求时才进入实现。","1) 企业微信个人应用消息和站内通知继续为正式通道;2) 邮件必须先确认 SMTP、发件域、退信处理、收件范围和脱敏策略;3) 缺邮件配置时只能 SKIP;4) 不进入复杂 BPM、审批设计器或企业微信原生审批;5) 实现项必须另建合同。","受影响流程:审批待办、催办、超时提醒、通知 deeplink、企业微信个人应用消息、可选邮件;验证步骤:人工核对 P2D-5、WF8 证据和审批/企业微信规约;rg -n 'P2-WF|通知模板|偏好|邮件|SMTP|企业微信个人应用消息|复杂 BPM|审批设计器' docs issues。","manual|rg","DONE","DONE","DONE","issues/2026-05-16_00-19-21-p2-decision-followup.csv|issues/2026-05-13_18-23-52-wf8-approval-notification.csv|docs/需求-统一审批中心规约.md|docs/需求-企业微信基础设施规约.md","[""P2Q-0""]","NextContract=issues/-p2-wf-notification-decision.csv; CurrentDecision=HOLD_FOR_REAL_PAIN_OR_CONFIG." "P2Q-6","P2-BIZ 轻业务增强门槛","锁定轻业务增强先走决策合同;供应商准入、费用提示继续优化、项目/采购摘要、合同归档细化、资产盘点体验必须明确频次、owner 和验收样例后才进入实现。","1) P2-BIZ 不牵连付款批次或 OCR;2) 是否影响 API/schema 必须先说明;3) ERP、AI、完整差旅、重预算继续冻结;4) 供应商、费用、项目、合同、资产候选各自有 owner 和验收样例后才能开发;5) 实现项另建合同。","受影响流程:采购、费用提示、项目摘要、合同归档、资产盘点;验证步骤:人工核对 P2D-6、BIZ8、采购/合同/项目规约和 FBA;rg -n 'P2-BIZ|供应商准入|费用提示|项目摘要|采购摘要|合同归档|资产盘点|ERP|AI|完整差旅|重预算' docs issues。","manual|rg","DONE","DONE","DONE","issues/2026-05-16_00-19-21-p2-decision-followup.csv|issues/2026-05-13_18-23-53-biz8-finance-ocr-project.csv|docs/需求-采购费用审批规约.md|docs/需求-合同附件归档规约.md|docs/需求-项目轻管理规约.md","[""P2Q-0""]","NextContract=issues/-p2-biz-light-enhancement-decision.csv; CurrentDecision=HOLD_FOR_FREQUENCY_OWNER_ACCEPTANCE." "P2Q-7","持续上线门禁","锁定所有后续实现合同必须自带上线门禁,不默认新建 QAOPS10;涉及公网可测代码变更时再补 /ready、public bundle、live smoke、真实 OCR 或 SKIP、移动矩阵和 Go/No-Go。","1) 非代码决策/样票阶段不部署;2) 实现类合同必须说明是否部署;3) 公网付款批次和图片 OCR 不可混入同一上线窗口;4) 不执行生产恢复、不重置数据库、不打印远端 env;5) 恢复演练必须单独批准窗口。","受影响流程:后续 P2 实现、上线硬化、真实 OCR、公网付款批次和移动矩阵;验证步骤:人工核对 docs/试运行与自动化验证手册.md、docs/trialrun/发布试运行准备清单.md、docs/trialrun/OPS7运维交接Runbook.md;rg -n 'ready|public bundle|live smoke|真实 OCR|Go/No-Go|恢复演练|重置数据库|远端 env' docs scripts issues。","manual|rg","DONE","DONE","DONE","docs/试运行与自动化验证手册.md|docs/trialrun/发布试运行准备清单.md|docs/trialrun/OPS7运维交接Runbook.md|scripts/qa","[""P2Q-0""]","RollingGate=DONE; no QAOPS10 by default; each implementation contract carries gates." diff --git a/issues/2026-05-16_20-09-49-p2-automated-regression-sweep.csv b/issues/2026-05-16_20-09-49-p2-automated-regression-sweep.csv index 602989ce..2374814b 100644 --- a/issues/2026-05-16_20-09-49-p2-automated-regression-sweep.csv +++ b/issues/2026-05-16_20-09-49-p2-automated-regression-sweep.csv @@ -7,5 +7,5 @@ "P2AUTO-5","隔离业务流自动化","在隔离临时库中运行核心业务流 flag-off 门禁,并单独运行付款批次 flag-on 闭环;付款批次结果不得解释为公网启用。","1) 核心业务流通过或失败有明确用例;2) payment_batch_v1 公网保持关闭;3) 付款批次仅隔离环境 flag-on;4) 报告路径写入 Notes;5) 不执行生产数据写入。","受影响流程:登录、企业微信入口、报销、审批、请假、采购、权限治理、付款批次隔离闭环;验证步骤:BUSINESS_FLOW_GROUPS=auth,reimbursement,approval,leave,procurement,governance BUSINESS_FLOW_EXCLUDE_SPECS=tests/e2e/reimbursement-payment-batch-flow.spec.ts bash scripts/qa/run-business-flow-gates.sh local;cd client && E2E_BASE_URL= npx playwright test tests/e2e/reimbursement-payment-batch-flow.spec.ts --workers=1 --reporter=list。","bash|playwright","DONE","DONE","DONE","scripts/qa/run-business-flow-gates.sh|client/tests/e2e/reimbursement-payment-batch-flow.spec.ts|artifacts/qa-flow","[""P2AUTO-4""]","PASS: core isolated business flow auth/reimbursement/approval/leave/procurement/governance passed, report artifacts/qa-flow/business-flow-20260516-203001/summary.txt; payment batch isolated flag-on flow 1 passed, report artifacts/qa-flow/business-flow-20260516-203856/summary.txt; public payment_batch_v1 remains off." "P2AUTO-6","公网自动化门禁","在公网只跑 /ready、public bundle、live smoke、PDF live OCR、pending OCR 扫描和 live smoke Playwright,不执行公网付款批次写入。","1) /ready 通过;2) public bundle findings=0;3) live smoke 通过;4) PDF live OCR 进入可解释终态;5) pending OCR 缺 DATABASE_URL 时只作非阻断 SKIP。","受影响流程:公网健康、登录 smoke、PDF live OCR、企业微信 config、公网资源;验证步骤:set -a && source ~/.hyb-oa/live-smoke.env && set +a;QA_ISSUE_ID=P2AUTO LIVE_SMOKE_BASE_URL=https://oa.hyinda.com bash scripts/qa/run-live-smoke-readiness.sh;cd client && E2E_BASE_URL=https://oa.hyinda.com npx playwright test tests/e2e/live-smoke.spec.ts --workers=1 --reporter=list。","bash|node|playwright","DONE","DONE","DONE","scripts/qa/run-live-smoke-readiness.sh|client/tests/e2e/live-smoke.spec.ts|artifacts/qa-flow","[""P2AUTO-5""]","PASS: live readiness report artifacts/qa-flow/live-smoke-readiness-20260516-204157; /ready ok, public bundle findings=0, live smoke 3 passed, PDF live OCR 1 passed with duplicate-invoice explainable terminal state; pending OCR missing local DATABASE_URL recorded SKIP; no public payment batch writes." "P2AUTO-7","移动与企业微信自动化门禁","执行移动矩阵、企业微信 UA、入口/绑定/deeplink 自动化;真机只保留少量真实差异确认,不阻断可自动化收口。","1) 移动矩阵通过;2) WeCom UA/入口/绑定自动化通过;3) 企业微信真机差异不被自动化替代;4) 发现移动缺陷进入 UX8;5) 不新增移动业务能力。","受影响流程:/mobile、审批、报销、企业微信入口、绑定、deeplink 和移动布局;验证步骤:cd client && E2E_BASE_URL=https://oa.hyinda.com npx playwright test tests/e2e/dg7-mobile-information-dedupe.spec.ts tests/e2e/r124-mobile-entry-matrix.spec.ts --workers=1 --reporter=list;cd client && npx playwright test tests/e2e/wecom-entry.spec.ts tests/e2e/wecom-first-bind.spec.ts tests/e2e/r6-wecom-mobile-rendering-hardening.spec.ts --workers=1 --reporter=list。","playwright|manual","DONE","DONE","DONE","client/tests/e2e|docs/trialrun/人工试运行报告.md","[""P2AUTO-6""]","PASS: public DG7/R124 mobile matrix 6 passed; local auth business-flow covered wecom-entry/wecom-first-bind; R6 WeCom rendering matrix 8 passed after subpixel test assertion normalization; real-device delta remains manual-only when user requests." -"P2AUTO-8","OCR provider 样票自动化缺口","新增并运行 provider-only 样票 QA 脚本;PDF 样票可回归,图片样票未在 manifest 或环境变量登记时记录 SKIP,不得写 PASS。","1) scripts/qa/check-ocr-provider-samples.mjs 存在且 node --check 通过;2) 无样票时输出 SKIP;3) 有样票时输出脱敏 JSON 摘要;4) 不输出票面、税号、手机号、二维码、完整号码或真实路径;5) 不开放图片 OCR。","受影响流程:PDF/image provider 样票 -> Tencent OCR -> 字段命中摘要 -> GO/HOLD/FREEZE 决策;验证步骤:node --check scripts/qa/check-ocr-provider-samples.mjs;OCR_PROVIDER_SAMPLE_MANIFEST=/safe/ocr-samples.json node scripts/qa/check-ocr-provider-samples.mjs --provider=tencent --json;无授权图片样票或 manifest 时写 SKIP。","node|manual","DONE","DONE","DONE","scripts/qa/check-ocr-provider-samples.mjs|external","[""P2AUTO-1""]","QA script added and node --check passed. Provider-only run with current safe env found PDF sample only and no image manifest/env; local Tencent provider credentials not present, so output status=SKIP decision=HOLD_FOR_SAMPLE. Do not auto-select arbitrary image candidates; image OCR remains closed." +"P2AUTO-8","OCR provider 样票自动化缺口","新增并运行 provider-only 样票 QA 脚本;PDF 样票可回归,图片 OCR 默认冻结,未显式进入图片评估时不得把缺图片样票写成主线 HOLD。","1) scripts/qa/check-ocr-provider-samples.mjs 存在且 node --check 通过;2) 默认无图片评估时输出 FREEZE_BY_DEFAULT;3) 有显式样票时输出脱敏 JSON 摘要;4) 不输出票面、税号、手机号、二维码、完整号码或真实路径;5) 不开放图片 OCR。","受影响流程:PDF provider 样票 -> Tencent OCR 可用性摘要;默认图片 OCR -> FREEZE_BY_DEFAULT;显式图片评估 -> 字段命中摘要 -> GO/HOLD 决策;验证步骤:node --check scripts/qa/check-ocr-provider-samples.mjs;node scripts/qa/check-ocr-provider-samples.mjs --provider=tencent --json;OCR_IMAGE_OCR_EVALUATION=1 node scripts/qa/check-ocr-provider-samples.mjs --provider=tencent --json。","node|manual","DONE","DONE","DONE","scripts/qa/check-ocr-provider-samples.mjs|external","[""P2AUTO-1""]","P2AF correction: default provider-only run with current safe env should report decision=FREEZE_BY_DEFAULT when image evaluation is not explicit. Do not auto-select arbitrary image candidates; image OCR remains closed." "P2AUTO-9","收口回填与中文提交","汇总 P2AUTO 自动化证据,回填 CSV、人工试运行报告、索引和事实源矩阵,完成最终校验和中文 commit。","1) 已执行项按真实 Test_Method 回填;2) 未执行外部项记录 SKIP 或后续条件;3) validate、audit-docs、rg、git diff --check 通过;4) 中文 commit 引用 P2AUTO;5) 明确未部署。","流程级验证:validate_issues_csv.py 本 CSV;node scripts/qa/audit-docs.mjs --check-registry;rg -n 'P2AUTO|PDF live OCR|图片样票|payment_batch_v1|SKIP|live smoke|企业微信' docs issues scripts;git diff --check;git status --short。","python3|node|rg|git|manual","DONE","DONE","DONE","issues/2026-05-16_20-09-49-p2-automated-regression-sweep.csv|docs/文档索引.md|docs/文档事实源状态矩阵.md|docs/trialrun/人工试运行报告.md","[""P2AUTO-2"",""P2AUTO-3"",""P2AUTO-4"",""P2AUTO-5"",""P2AUTO-6"",""P2AUTO-7"",""P2AUTO-8""]","Closeout=PASS; validate_issues_csv passed; audit-docs errors=0 warnings=1 existing unregistered docs files=24; rg keyword sweep explainable; git diff --check passed; not deployed; no business API/schema/UI/runtime change." diff --git a/issues/2026-05-16_22-11-10-p2-automatable-followup.csv b/issues/2026-05-16_22-11-10-p2-automatable-followup.csv new file mode 100644 index 00000000..dff72a72 --- /dev/null +++ b/issues/2026-05-16_22-11-10-p2-automatable-followup.csv @@ -0,0 +1,8 @@ +"ID","Title","Description","Acceptance","Test_Method","Tools","Dev_Status","Review1_Status","Regression_Status","Files","Dependencies","Notes" +"P2AF-0","P2AF 自动实施合同建档","建立 R7E 后可自动实施工作合同,只做口径纠偏、QA 脚本、自动化门禁和既有证据回填,不启动业务功能开发。","1) 本 CSV 校验通过;2) 范围限定 issues/docs/scripts/qa 和必要测试维护;3) 不新增业务 API/schema/UI/feature flag;4) 不部署、不执行生产恢复、不重置数据库、不打印 secret;5) 公网 payment_batch_v1 继续关闭,图片 OCR 默认冻结。","受影响流程:后续 P2 派工、OCR PDF-only 口径、付款批次公网关闭态、自动化回归;验证步骤:python3 /home/shunli/.codex/skills/plan/scripts/validate_issues_csv.py issues/2026-05-16_22-11-10-p2-automatable-followup.csv;人工核对不改业务运行时能力。","python3|manual","DONE","DONE","DONE","issues/2026-05-16_22-11-10-p2-automatable-followup.csv","[]","CSV validated; Scope=automatable-followup; baseline=edabce8; no deployment; no runtime capability change." +"P2AF-1","OCR 口径纠偏","把图片发票 OCR 从主线等待样票纠正为 FREEZE_BY_DEFAULT;当前正式能力只维护 PDF-only OCR,图片仅普通附件,未来只有用户明确重启评估才另建合同。","1) P2Q/P2AUTO Notes 不再表达为主线等待图片样票;2) docs/文档索引.md、docs/文档事实源状态矩阵.md、docs/trialrun/人工试运行报告.md 记录 FREEZE_BY_DEFAULT;3) PDF live OCR 样票继续记录为可用;4) 不改 OCR 业务入口;5) 不新建综合规划文档。","受影响流程:PDF 发票上传 -> OCR -> 查重/重复票据可解释终态;图片附件 -> 普通附件材料;验证步骤:rg -n 'FREEZE_BY_DEFAULT|HOLD_FOR_SAMPLE|PDF-only|图片 OCR|普通附件|P2AUTO|P2Q' docs issues scripts;node scripts/qa/audit-docs.mjs --check-registry。","rg|node|manual","DONE","DONE","DONE","issues/2026-05-16_10-35-22-p2-post-p2ocrs-followup-queue.csv|issues/2026-05-16_20-09-49-p2-automated-regression-sweep.csv|docs/文档索引.md|docs/文档事实源状态矩阵.md|docs/trialrun/人工试运行报告.md","[""P2AF-0""]","DefaultDecision=FREEZE_BY_DEFAULT; P2Q/P2AUTO and existing docs corrected; audit-docs errors=0 warnings=1 existing unregistered docs." +"P2AF-2","OCR provider QA 脚本默认冻结语义","调整 scripts/qa/check-ocr-provider-samples.mjs:默认无显式图片 OCR 评估时输出 FREEZE_BY_DEFAULT;只有 OCR_IMAGE_OCR_EVALUATION=1 或显式参数开启时,图片样票缺失才输出 HOLD_FOR_SAMPLE。","1) 默认运行不把缺图片样票视为主线阻断;2) 显式图片评估模式仍支持 manifest/env 图片样票;3) 输出不包含真实路径、税号、手机号、二维码、完整号码或票面内容;4) node --check 通过;5) 不调用图片 OCR,除非显式评估且样票和凭据具备。","受影响流程:provider-only 样票脚本 -> PDF 基线摘要 -> 默认冻结/显式图片评估决策;验证步骤:node --check scripts/qa/check-ocr-provider-samples.mjs;set -a && source ~/.hyb-oa/live-smoke.env && set +a && node scripts/qa/check-ocr-provider-samples.mjs --provider=tencent --json;OCR_IMAGE_OCR_EVALUATION=1 node scripts/qa/check-ocr-provider-samples.mjs --provider=tencent --json。","node|bash","DONE","DONE","DONE","scripts/qa/check-ocr-provider-samples.mjs","[""P2AF-1""]","node --check passed; default output decision=FREEZE_BY_DEFAULT imageEvaluation=false with PDF sample alias; explicit OCR_IMAGE_OCR_EVALUATION=1 returns HOLD_FOR_SAMPLE without image sample; no business runtime impact." +"P2AF-3","自动回归聚合脚本","新增 scripts/qa/run-p2-safe-regression.sh,串联后端 focused、前端构建、隔离核心业务流、公网 readiness、移动矩阵、企业微信 UA/渲染和 OCR 默认冻结检查,并生成 artifacts/qa-flow/p2-safe-regression-* 摘要。","1) 脚本默认不部署、不启用公网 payment_batch_v1、不开放图片 OCR;2) 支持 P2_SAFE_SKIP_LIVE=1 和 P2_SAFE_SKIP_BUILD=1;3) 失败时返回非 0 并保留摘要;4) 报告路径可追溯;5) 不打印 secret。","受影响流程:全端自动化门禁聚合;验证步骤:bash scripts/qa/run-p2-safe-regression.sh;必要时 P2_SAFE_SKIP_LIVE=1 bash scripts/qa/run-p2-safe-regression.sh 验证本地聚合;检查 artifacts/qa-flow/p2-safe-regression-*/summary.txt。","bash|npm|node|playwright","DONE","DONE","DONE","scripts/qa/run-p2-safe-regression.sh|artifacts/qa-flow","[""P2AF-2""]","bash scripts/qa/run-p2-safe-regression.sh PASS; report=artifacts/qa-flow/p2-safe-regression-20260516-222222; backend focused 43 tests passed; frontend typecheck/build passed; core isolated business flow PASS; live readiness PASS; OCR default FREEZE_BY_DEFAULT." +"P2AF-4","付款批次公网关闭态只读门禁","新增或复用 QA 脚本验证公网 payment_batch_v1 继续关闭:只允许登录和 GET/只读探测,不创建、复核、执行付款批次。","1) 公网只读检查能证明付款批次关闭态可解释;2) 不执行 POST/PATCH/DELETE 付款批次动作;3) 财务账号只读访问不泄露敏感数据;4) 普通员工/admin-only 不越权;5) 输出脱敏摘要。","受影响流程:公网财务入口/AP/payment-batches 关闭态;验证步骤:set -a && source ~/.hyb-oa/live-smoke.env && set +a;LIVE_SMOKE_BASE_URL=https://oa.hyinda.com node scripts/qa/check-payment-batch-flag-off.mjs --json;确认输出只含状态、角色、HTTP 状态和错误码摘要。","node|manual","DONE","DONE","DONE","scripts/qa/check-payment-batch-flag-off.mjs|external","[""P2AF-0""]","ReadOnly=true; PASS: finance /api/payment-batches and /api/ap-operations plus employee/admin /api/payment-batches all returned 409 payment_batch_v1_disabled reason=flag_force_off; no POST/PATCH/DELETE." +"P2AF-5","UX8 自动矩阵扫尾","运行移动/企业微信自动化矩阵,记录 UX8 当前无新真实反馈时保持 TODO;若自动化发现确定性问题,只修复测试前置或明确前端体验缺陷。","1) DG7/R124 公网移动矩阵通过或失败可归因;2) R6 WeCom 渲染矩阵通过或失败可归因;3) UX8-1..5 无新反馈时不强行改 UI;4) 移动采购创建不并入本轮;5) 结果写入 Notes。","受影响流程:移动首页、移动审批/报销/通知、企业微信 UA、UX8 反馈池;验证步骤:cd client && E2E_BASE_URL=https://oa.hyinda.com npx playwright test tests/e2e/dg7-mobile-information-dedupe.spec.ts tests/e2e/r124-mobile-entry-matrix.spec.ts --workers=1 --reporter=list;cd client && npx playwright test tests/e2e/r6-wecom-mobile-rendering-hardening.spec.ts --workers=1 --reporter=list;人工核对 UX8 CSV 不误置 DONE。","playwright|manual","DONE","DONE","DONE","client/tests/e2e|issues/2026-05-13_13-17-38-ux8-trialrun-feedback-polish.csv","[""P2AF-3""]","Public mobile matrix DG7/R124 6 passed; WeCom rendering matrix 8 passed across 320/360/375/390/393/414/430 plus font failure case; UX8-1..5 remain TODO pending real feedback." +"P2AF-6","P2AF 收口回填与提交","回填 P2AF、既有文档和历史连续合同 Notes,运行最终门禁并提交中文 commit。","1) P2AF 状态真实回填;2) validate、audit-docs、rg、git diff --check 通过;3) 中文 commit 引用 P2AF;4) 明确未部署;5) 无未解释工作区变更。","流程级验证:python3 /home/shunli/.codex/skills/plan/scripts/validate_issues_csv.py issues/2026-05-16_22-11-10-p2-automatable-followup.csv;node scripts/qa/audit-docs.mjs --check-registry;rg -n 'P2AF|FREEZE_BY_DEFAULT|HOLD_FOR_SAMPLE|PDF-only|图片 OCR|payment_batch_v1|P2AUTO|UX8' docs issues scripts;git diff --check;git status --short。","python3|node|rg|git","DONE","DONE","DONE","issues/2026-05-16_22-11-10-p2-automatable-followup.csv|docs/文档索引.md|docs/文档事实源状态矩阵.md|docs/trialrun/人工试运行报告.md","[""P2AF-1"",""P2AF-2"",""P2AF-3"",""P2AF-4"",""P2AF-5""]","Closeout=PASS; validate_issues_csv passed; audit-docs errors=0 warnings=1 existing unregistered docs files=24; keyword sweep explainable; git diff --check passed before final commit; not deployed." diff --git a/scripts/qa/check-ocr-provider-samples.mjs b/scripts/qa/check-ocr-provider-samples.mjs index 33e8c6b2..f3a12bad 100644 --- a/scripts/qa/check-ocr-provider-samples.mjs +++ b/scripts/qa/check-ocr-provider-samples.mjs @@ -26,6 +26,7 @@ function parseArgs(argv) { json: false, strict: false, manifest: process.env.OCR_PROVIDER_SAMPLE_MANIFEST || "", + imageEvaluation: process.env.OCR_IMAGE_OCR_EVALUATION === "1", }; for (const arg of argv) { @@ -33,6 +34,8 @@ function parseArgs(argv) { options.json = true; } else if (arg === "--strict") { options.strict = true; + } else if (arg === "--image-evaluation") { + options.imageEvaluation = true; } else if (arg.startsWith("--provider=")) { options.provider = arg.slice("--provider=".length); } else if (arg.startsWith("--manifest=")) { @@ -164,6 +167,16 @@ function loadEnvSamples() { return samples; } +function hasExplicitImageEnv() { + return [ + "PLAYWRIGHT_OCR_IMAGE_FILE", + "PLAYWRIGHT_OCR_IMAGE_FILES", + "PLAYWRIGHT_OCR_IMAGE_DIR", + "LIVE_OCR_IMAGE_FILE", + "LIVE_OCR_IMAGE_DIR", + ].some((key) => Boolean(process.env[key]?.trim())); +} + function dedupeSamples(samples) { const seen = new Set(); const result = []; @@ -274,7 +287,8 @@ async function checkSample(provider, config, sample) { } } -function buildDecision(results, manifestError) { +function buildDecision(results, manifestError, imageEvaluation) { + if (!imageEvaluation) return "FREEZE_BY_DEFAULT"; if (manifestError) return "HOLD_FOR_SAMPLE"; if (!results.length) return "HOLD_FOR_SAMPLE"; const imageResults = results.filter((item) => item.kind === "image"); @@ -285,7 +299,7 @@ function buildDecision(results, manifestError) { function printHuman(summary) { console.log(`[ocr-provider-samples] status=${summary.status} decision=${summary.decision}`); - console.log(`[ocr-provider-samples] provider=${summary.provider} samples=${summary.counts.total} pdf=${summary.counts.pdf} image=${summary.counts.image}`); + console.log(`[ocr-provider-samples] provider=${summary.provider} imageEvaluation=${summary.imageEvaluation} samples=${summary.counts.total} pdf=${summary.counts.pdf} image=${summary.counts.image}`); if (summary.manifestError) { console.log(`[ocr-provider-samples] manifest=${summary.manifestError}`); } @@ -307,12 +321,13 @@ async function main() { const samples = dedupeSamples([...manifest.samples, ...loadEnvSamples()]); const config = getTencentConfig(); const results = []; + const imageEvaluation = options.imageEvaluation || Boolean(options.manifest) || hasExplicitImageEnv(); for (const sample of samples) { results.push(await checkSample(options.provider, config, sample)); } - const decision = buildDecision(results, manifest.error); + const decision = buildDecision(results, manifest.error, imageEvaluation); const failed = results.some((item) => item.status === "HOLD"); const skipped = !results.length || results.some((item) => item.status === "SKIP"); const summary = { @@ -320,6 +335,7 @@ async function main() { provider: options.provider, status: failed ? "HOLD" : skipped ? "SKIP" : "PASS", decision, + imageEvaluation, manifestProvided: Boolean(options.manifest), manifestError: manifest.error, credentialReady: hasTencentCredentials(config), diff --git a/scripts/qa/check-payment-batch-flag-off.mjs b/scripts/qa/check-payment-batch-flag-off.mjs new file mode 100755 index 00000000..e32fe48c --- /dev/null +++ b/scripts/qa/check-payment-batch-flag-off.mjs @@ -0,0 +1,212 @@ +#!/usr/bin/env node +const DEFAULT_BASE_URL = "https://oa.hyinda.com"; + +function parseArgs(argv) { + const options = { + baseUrl: process.env.LIVE_SMOKE_BASE_URL || DEFAULT_BASE_URL, + json: false, + strict: false, + }; + + for (const arg of argv) { + if (arg === "--json") { + options.json = true; + } else if (arg === "--strict") { + options.strict = true; + } else if (arg.startsWith("--base-url=")) { + options.baseUrl = arg.slice("--base-url=".length); + } + } + + options.baseUrl = options.baseUrl.trim().replace(/\/$/, ""); + return options; +} + +function getCredential(role) { + const prefixByRole = { + finance: "LIVE_SMOKE_FINANCE", + employee: "LIVE_SMOKE_EMPLOYEE", + admin: "LIVE_SMOKE_ADMIN", + }; + const prefix = prefixByRole[role]; + const identifier = process.env[`${prefix}_IDENTIFIER`]?.trim(); + const password = process.env[`${prefix}_PASSWORD`]?.trim(); + + if (!identifier || !password) { + return null; + } + + return { identifier, password }; +} + +async function fetchJson(url, init = {}) { + const response = await fetch(url, { + ...init, + signal: AbortSignal.timeout(15_000), + }); + const text = await response.text(); + let json = null; + try { + json = text ? JSON.parse(text) : null; + } catch { + json = null; + } + + return { + ok: response.ok, + status: response.status, + json, + }; +} + +function summarizeError(json) { + const error = json?.error ?? {}; + const details = error.details ?? {}; + return { + code: typeof error.code === "string" ? error.code : null, + featureFlag: typeof details.featureFlag === "string" ? details.featureFlag : null, + reason: typeof details.reason === "string" ? details.reason : null, + }; +} + +async function login(baseUrl, role, credential) { + const response = await fetchJson(`${baseUrl}/api/auth/login`, { + method: "POST", + headers: { + "content-type": "application/json", + "x-device-fingerprint": `p2af-flag-off-${role}`, + }, + body: JSON.stringify({ + identifier: credential.identifier, + password: credential.password, + }), + }); + + const accessToken = response.json?.data?.accessToken; + if (!response.ok || typeof accessToken !== "string" || !accessToken) { + return { + ok: false, + role, + status: response.status, + error: summarizeError(response.json), + }; + } + + return { + ok: true, + role, + accessToken, + }; +} + +async function probeEndpoint(baseUrl, role, accessToken, endpoint) { + const response = await fetchJson(`${baseUrl}${endpoint}`, { + method: "GET", + headers: { + authorization: `Bearer ${accessToken}`, + accept: "application/json", + }, + }); + const error = summarizeError(response.json); + const featureFlagDisabled = response.status === 409 && error.featureFlag === "payment_batch_v1_disabled"; + const permissionDenied = response.status === 403 && error.code === "PERM_001"; + const unexpectedOpen = response.status === 200; + const acceptable = featureFlagDisabled || (role !== "finance" && permissionDenied); + + return { + role, + endpoint, + httpStatus: response.status, + status: acceptable ? "PASS" : "FAIL", + featureFlagDisabled, + permissionDenied, + unexpectedOpen, + error, + }; +} + +function buildRolePlan() { + return [ + { role: "finance", endpoints: ["/api/payment-batches", "/api/ap-operations"] }, + { role: "employee", endpoints: ["/api/payment-batches"] }, + { role: "admin", endpoints: ["/api/payment-batches"] }, + ]; +} + +function printHuman(summary) { + console.log(`[payment-batch-flag-off] status=${summary.status} base=${summary.baseUrl}`); + for (const missing of summary.missingCredentials) { + console.log(`[payment-batch-flag-off] ${missing.role} status=SKIP reason=missing_credentials`); + } + for (const loginFailure of summary.loginFailures) { + console.log(`[payment-batch-flag-off] ${loginFailure.role} status=FAIL loginStatus=${loginFailure.status}`); + } + for (const result of summary.results) { + const detail = result.featureFlagDisabled + ? "featureFlag=payment_batch_v1_disabled" + : result.permissionDenied + ? "permission=denied" + : `error=${result.error.code || "-"}`; + console.log( + `[payment-batch-flag-off] ${result.role} ${result.endpoint} status=${result.status} http=${result.httpStatus} ${detail}`, + ); + } +} + +async function main() { + const options = parseArgs(process.argv.slice(2)); + const missingCredentials = []; + const loginFailures = []; + const results = []; + + for (const item of buildRolePlan()) { + const credential = getCredential(item.role); + if (!credential) { + missingCredentials.push({ role: item.role }); + continue; + } + + const session = await login(options.baseUrl, item.role, credential); + if (!session.ok) { + loginFailures.push(session); + continue; + } + + for (const endpoint of item.endpoints) { + results.push(await probeEndpoint(options.baseUrl, item.role, session.accessToken, endpoint)); + } + } + + const hasFailures = loginFailures.length > 0 || results.some((result) => result.status !== "PASS"); + const status = hasFailures ? "FAIL" : missingCredentials.length ? "SKIP" : results.length ? "PASS" : "SKIP"; + const summary = { + checkedAt: new Date().toISOString(), + baseUrl: options.baseUrl, + status, + expectedFeatureFlag: "payment_batch_v1", + expectedState: "off", + readOnly: true, + missingCredentials, + loginFailures: loginFailures.map((item) => ({ + role: item.role, + status: item.status, + error: item.error, + })), + results, + }; + + if (options.json) { + console.log(JSON.stringify(summary, null, 2)); + } else { + printHuman(summary); + } + + if (status === "FAIL" || (options.strict && status !== "PASS")) { + process.exit(1); + } +} + +main().catch((error) => { + console.error(`[payment-batch-flag-off][ERROR] ${error instanceof Error ? error.message : String(error)}`); + process.exit(1); +}); diff --git a/scripts/qa/run-p2-safe-regression.sh b/scripts/qa/run-p2-safe-regression.sh new file mode 100755 index 00000000..d9bbe9f8 --- /dev/null +++ b/scripts/qa/run-p2-safe-regression.sh @@ -0,0 +1,99 @@ +#!/usr/bin/env bash +set -u -o pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +CLIENT_DIR="$ROOT_DIR/client" +SERVER_DIR="$ROOT_DIR/server" +PUBLIC_BASE_URL="${LIVE_SMOKE_BASE_URL:-https://oa.hyinda.com}" +REPORT_DIR="${P2_SAFE_REPORT_DIR:-$ROOT_DIR/artifacts/qa-flow/p2-safe-regression-$(date +%Y%m%d-%H%M%S)}" +SUMMARY_TXT="$REPORT_DIR/summary.txt" +STATUS=0 + +mkdir -p "$REPORT_DIR" +: >"$SUMMARY_TXT" + +load_default_env() { + local env_file="${P2_SAFE_ENV_FILE:-$HOME/.hyb-oa/live-smoke.env}" + if [[ "${P2_SAFE_SKIP_ENV_FILE:-0}" == "1" || ! -f "$env_file" ]]; then + return 0 + fi + + set -a + # shellcheck disable=SC1090 + source "$env_file" + set +a + PUBLIC_BASE_URL="${LIVE_SMOKE_BASE_URL:-$PUBLIC_BASE_URL}" + echo "[p2-safe] loaded masked env file" | tee -a "$SUMMARY_TXT" +} + +run_step() { + local name="$1" + shift + local log_file="$REPORT_DIR/${name}.log" + + echo ">>> [p2-safe] $name" | tee -a "$SUMMARY_TXT" + if "$@" > >(tee "$log_file") 2>&1; then + echo "[p2-safe][PASS] $name" | tee -a "$SUMMARY_TXT" + else + local code="$?" + echo "[p2-safe][FAIL] $name exit=$code" | tee -a "$SUMMARY_TXT" + STATUS=1 + fi +} + +run_shell_step() { + local name="$1" + local command="$2" + run_step "$name" bash -lc "$command" +} + +write_summary_json() { + local qa_status="PASS" + if [[ "$STATUS" -ne 0 ]]; then + qa_status="FAIL" + fi + + QA_REPORT_DIR="$REPORT_DIR" \ + QA_ISSUE_ID="${QA_ISSUE_ID:-P2AF}" \ + QA_BASE_URL="$PUBLIC_BASE_URL" \ + QA_MODE="p2-safe-regression" \ + QA_STATUS="$qa_status" \ + QA_SUMMARY_NOTES="P2AF 安全自动回归:不部署、不启用公网 payment_batch_v1、不开放图片 OCR;图片 OCR 默认 FREEZE_BY_DEFAULT。" \ + node "$ROOT_DIR/scripts/qa/write-qa-summary.mjs" >/dev/null 2>&1 || true +} + +finish() { + write_summary_json + echo "[p2-safe] report=$REPORT_DIR" | tee -a "$SUMMARY_TXT" + exit "$STATUS" +} + +trap finish EXIT + +load_default_env + +run_shell_step "backend-prisma" "cd '$SERVER_DIR' && npm run prisma:validate" +run_shell_step "backend-node-check" "cd '$SERVER_DIR' && node --check src/routes/invoicePool.js src/jobs/ocrJob.js src/services/ocr/tencent.js src/routes/paymentBatches.js src/routes/exports.js src/routes/workflow.js src/routes/wecomAuth.js" +run_shell_step "backend-focused-tests" "cd '$SERVER_DIR' && npm test -- tests/unit/services/ocr.tencent.test.js tests/unit/jobs/ocrJob.test.js tests/unit/routes/invoicePool.upload.test.js tests/unit/routes/paymentBatches.reimbursement.test.js tests/unit/routes/exports.apOperations.test.js tests/unit/routes/wecomAuth.test.js tests/unit/routes/notifications.test.js" + +if [[ "${P2_SAFE_SKIP_BUILD:-0}" == "1" ]]; then + echo "[p2-safe][SKIP] frontend-build P2_SAFE_SKIP_BUILD=1" | tee -a "$SUMMARY_TXT" +else + run_shell_step "frontend-typecheck" "cd '$CLIENT_DIR' && npm run typecheck" + run_shell_step "frontend-build" "cd '$CLIENT_DIR' && npm run build" +fi + +run_shell_step "business-flow-local" "BUSINESS_FLOW_GROUPS=auth,reimbursement,approval,leave,procurement,governance BUSINESS_FLOW_EXCLUDE_SPECS=tests/e2e/reimbursement-payment-batch-flow.spec.ts bash '$ROOT_DIR/scripts/qa/run-business-flow-gates.sh' local" + +if [[ "${P2_SAFE_SKIP_LIVE:-0}" == "1" ]]; then + echo "[p2-safe][SKIP] live-readiness P2_SAFE_SKIP_LIVE=1" | tee -a "$SUMMARY_TXT" + echo "[p2-safe][SKIP] payment-batch-flag-off P2_SAFE_SKIP_LIVE=1" | tee -a "$SUMMARY_TXT" + echo "[p2-safe][SKIP] public-mobile-matrix P2_SAFE_SKIP_LIVE=1" | tee -a "$SUMMARY_TXT" +else + run_shell_step "live-readiness" "QA_ISSUE_ID=P2AF LIVE_SMOKE_BASE_URL='$PUBLIC_BASE_URL' bash '$ROOT_DIR/scripts/qa/run-live-smoke-readiness.sh'" + run_shell_step "payment-batch-flag-off" "LIVE_SMOKE_BASE_URL='$PUBLIC_BASE_URL' node '$ROOT_DIR/scripts/qa/check-payment-batch-flag-off.mjs' --json" + run_shell_step "public-mobile-matrix" "cd '$CLIENT_DIR' && E2E_BASE_URL='$PUBLIC_BASE_URL' npx playwright test tests/e2e/dg7-mobile-information-dedupe.spec.ts tests/e2e/r124-mobile-entry-matrix.spec.ts --workers=1 --reporter=list" +fi + +run_shell_step "wecom-rendering-matrix" "cd '$CLIENT_DIR' && E2E_BASE_URL='$PUBLIC_BASE_URL' npx playwright test tests/e2e/r6-wecom-mobile-rendering-hardening.spec.ts --workers=1 --reporter=list" +run_shell_step "ocr-freeze-default" "node '$ROOT_DIR/scripts/qa/check-ocr-provider-samples.mjs' --provider=tencent --json"