WREAD1 准备企微审批可读性门禁

这个提交包含在:
2026-05-19 21:09:12 +08:00
父节点 509a6bed62
当前提交 09bb202ce2
共修改 13 个文件,包含 742 行新增和 22 行删除
+3 -3
查看文件
@@ -17,9 +17,9 @@ Read only what is relevant:
- For approval workflow, roles, self-approval, notification, deeplink, reminder, or escalation work, read `docs/需求-统一审批中心规约.md`. - For approval workflow, roles, self-approval, notification, deeplink, reminder, or escalation work, read `docs/需求-统一审批中心规约.md`.
- For HR lifecycle, employee onboarding/offboarding, Enterprise WeChat directory sync, account disablement, or Enterprise WeChat API work, read `docs/需求-企业微信基础设施规约.md`, `docs/企微API能力目录.md`, `docs/管理员手册.md`, and the latest HCLT/HRWD/WAPI/WRRA Issue CSV evidence first. - For HR lifecycle, employee onboarding/offboarding, Enterprise WeChat directory sync, account disablement, or Enterprise WeChat API work, read `docs/需求-企业微信基础设施规约.md`, `docs/企微API能力目录.md`, `docs/管理员手册.md`, and the latest HCLT/HRWD/WAPI/WRRA Issue CSV evidence first.
- For any Enterprise WeChat capability, Secret, callback, approval, check-in, HR assistant, or contact-directory task, read `docs/企微API能力目录.md` before planning or coding. WAPI fixes the current Secret matrix: contacts use `WECOM_CONTACT_SECRET`; workbench/OAuth/messages/HR assistant/roster/check-in/approval use `WECOM_AGENT_SECRET`; `WECOM_CHECKIN_SECRET` is only an explicit check-in override; `WECOM_HR_ASSISTANT_SECRET` is historical compatibility and not a default. Do not write runtime code that tries multiple Secrets to guess the correct one. - For any Enterprise WeChat capability, Secret, callback, approval, check-in, HR assistant, or contact-directory task, read `docs/企微API能力目录.md` before planning or coding. WAPI fixes the current Secret matrix: contacts use `WECOM_CONTACT_SECRET`; workbench/OAuth/messages/HR assistant/roster/check-in/approval use `WECOM_AGENT_SECRET`; `WECOM_CHECKIN_SECRET` is only an explicit check-in override; `WECOM_HR_ASSISTANT_SECRET` is historical compatibility and not a default. Do not write runtime code that tries multiple Secrets to guess the correct one.
- For Enterprise WeChat-first HR/attendance runtime migration, read `docs/企微优先开发路线图.md` before planning or coding. WDRG makes the roadmap authoritative for stage order; WAPI adds the runtime-first stage before deep mirror work; WRRA states that WOUA `PASS_WITH_FINDINGS` does not clear deep mirror work; WOUA-FIX has now soft-archived the 7 OA active test accounts missing in WeCom, fixed the `T0000` seed reactivation behavior, transferred the `SMOKE201` test-manager side effects, and rerun WOUA with `oaActiveMissingInWecom=0`. WCPC has rerun the capability POC with an existing active test account and reached `GO_TO_HR_MASTER_SYNC`; WHMS/WHMSLIVE has implemented, deployed, and publicly verified `wecom_hr_master_sync_v1`, with the production flag restored to `force_off` and production real-employee batch apply not approved. WMSG1 proves real app-message delivery and Deeplink return only; WAPC1 fixes the approval configuration preparation checklist before mirror work; WAPC2 adds the leave/missed-punch approval template and low-risk `sp_no` read-only POC runner. WPROC1/WPROC2 fixed the engineering process gate; WEGOV1 is the route gate, and WACP3R has now rerun the compensation-pull POC by automatically discovering OA-POC leave/missed-punch template IDs from `SMOKE002` creator. Public report `/app/artifacts/qa-flow/wecom-approval-compensation-poc-20260519T115944Z/summary.json` is `PASS_WITH_FINDINGS`: compensation pull and detail read work, with finding that callback evidence files are not persisted across deploys. WREAD1 readability/misapproval check, WADM1 self-owned archive/mirror model design, and WARCH1 minimum history archive still remain before `wecom_attendance_approval_mirror_v1`. WPROC2 adds the executable `process gate`: before WAPC2F or other high-risk WeCom work, run `node scripts/qa/check-process-contract.mjs --baseline` and the issue-specific profile check to enforce DoR/DoD, fixed Secret source, non-self approval, live write switches, missing-config classification, and redaction boundaries. WAPC2F has implemented and deployed `npm run check:wecom-approval-fixtures`; public dry-run reached `PASS_FIXTURE_DRY_RUN_READY` at `/app/artifacts/qa-flow/wecom-approval-fixtures-20260519T045001Z/summary.json`, but no real WeCom template or approval was created because `WECOM_APPROVAL_FIXTURE_WRITE=1` was not set. Do not start `wecom_attendance_approval_mirror_v1`, `oa_hr_emergency_mode_v1`, `wecom_recovery_writeback_v1`, or `wecom_ruleops_v1` without the next stage's own Issue CSV. - For Enterprise WeChat-first HR/attendance runtime migration, read `docs/企微优先开发路线图.md` before planning or coding. WDRG makes the roadmap authoritative for stage order; WAPI adds the runtime-first stage before deep mirror work; WRRA states that WOUA `PASS_WITH_FINDINGS` does not clear deep mirror work; WOUA-FIX has now soft-archived the 7 OA active test accounts missing in WeCom, fixed the `T0000` seed reactivation behavior, transferred the `SMOKE201` test-manager side effects, and rerun WOUA with `oaActiveMissingInWecom=0`. WCPC has rerun the capability POC with an existing active test account and reached `GO_TO_HR_MASTER_SYNC`; WHMS/WHMSLIVE has implemented, deployed, and publicly verified `wecom_hr_master_sync_v1`, with the production flag restored to `force_off` and production real-employee batch apply not approved. WMSG1 proves real app-message delivery and Deeplink return only; WAPC1 fixes the approval configuration preparation checklist before mirror work; WAPC2 adds the leave/missed-punch approval template and low-risk `sp_no` read-only POC runner. WPROC1/WPROC2 fixed the engineering process gate; WEGOV1 is the route gate, and WACP3R has now rerun the compensation-pull POC by automatically discovering OA-POC leave/missed-punch template IDs from `SMOKE002` creator. Public report `/app/artifacts/qa-flow/wecom-approval-compensation-poc-20260519T115944Z/summary.json` is `PASS_WITH_FINDINGS`: compensation pull and detail read work, with finding that callback evidence files are not persisted across deploys. WREAD1 has now added and deployed `npm run check:wecom-approval-readability-gate`; public report `/app/artifacts/qa-flow/wecom-approval-readability-gate-20260519T125532Z/summary.json` is `PAUSED_MANUAL_CONFIRMATION`, so template-level real-device readability/misapproval confirmation, WADM1 self-owned archive/mirror model design, and WARCH1 minimum history archive still remain before `wecom_attendance_approval_mirror_v1`. WPROC2 adds the executable `process gate`: before WAPC2F or other high-risk WeCom work, run `node scripts/qa/check-process-contract.mjs --baseline` and the issue-specific profile check to enforce DoR/DoD, fixed Secret source, non-self approval, live write switches, missing-config classification, and redaction boundaries. WAPC2F has implemented and deployed `npm run check:wecom-approval-fixtures`; public dry-run reached `PASS_FIXTURE_DRY_RUN_READY` at `/app/artifacts/qa-flow/wecom-approval-fixtures-20260519T045001Z/summary.json`, but no real WeCom template or approval was created because `WECOM_APPROVAL_FIXTURE_WRITE=1` was not set. Do not start `wecom_attendance_approval_mirror_v1`, `oa_hr_emergency_mode_v1`, `wecom_recovery_writeback_v1`, or `wecom_ruleops_v1` without the next stage's own Issue CSV.
- WAPC2FLIVE supersedes the WAPC2F dry-run gap for OA-POC samples: after user approval, the runner was corrected to the official `template_name` + `property` controls create-template payload, deployed with `SYNC_ENV=0`, and the public live report `/app/artifacts/qa-flow/wecom-approval-fixtures-20260519T053400Z/summary.json` reached `PASS_FIXTURE_CREATED`; leave/missed-punch OA-POC templates and low-risk test approvals were created and retained for audit, with WAPC2 auto-rerun `PASS_APPROVAL_POC`. WCBK1 has added and deployed read-only `GET/POST /api/wecom/approval/callback`, `wecomCallbackCrypto`, `npm run check:wecom-approval-callback-poc`, and the `wecom-callback` process gate. After remote callback config was set, public report `/app/artifacts/qa-flow/wecom-approval-callback-poc-20260519T083422Z/summary.json` reached `PASS_CALLBACK_URL_VERIFIED`; after `SMOKE101` approved an existing OA-POC missed-punch test approval, `/app/artifacts/qa-flow/wecom-approval-callback-poc-20260519T092648Z/summary.json` reached `PASS_CALLBACK_EVENT_CONFIRMED` with one redacted event. WACP3/WACP3R has added and deployed `npm run check:wecom-approval-compensation-poc`, `POST /cgi-bin/oa/getapprovalinfo` read support, and automatic template discovery by `SMOKE002` creator; public report `/app/artifacts/qa-flow/wecom-approval-compensation-poc-20260519T115944Z/summary.json` is `PASS_WITH_FINDINGS`, because approval details were pulled but callback evidence is not persisted after deploy. This still does not clear mirror data model, emergency mode, writeback, or RuleOps. - WAPC2FLIVE supersedes the WAPC2F dry-run gap for OA-POC samples: after user approval, the runner was corrected to the official `template_name` + `property` controls create-template payload, deployed with `SYNC_ENV=0`, and the public live report `/app/artifacts/qa-flow/wecom-approval-fixtures-20260519T053400Z/summary.json` reached `PASS_FIXTURE_CREATED`; leave/missed-punch OA-POC templates and low-risk test approvals were created and retained for audit, with WAPC2 auto-rerun `PASS_APPROVAL_POC`. WCBK1 has added and deployed read-only `GET/POST /api/wecom/approval/callback`, `wecomCallbackCrypto`, `npm run check:wecom-approval-callback-poc`, and the `wecom-callback` process gate. After remote callback config was set, public report `/app/artifacts/qa-flow/wecom-approval-callback-poc-20260519T083422Z/summary.json` reached `PASS_CALLBACK_URL_VERIFIED`; after `SMOKE101` approved an existing OA-POC missed-punch test approval, `/app/artifacts/qa-flow/wecom-approval-callback-poc-20260519T092648Z/summary.json` reached `PASS_CALLBACK_EVENT_CONFIRMED` with one redacted event. WACP3/WACP3R has added and deployed `npm run check:wecom-approval-compensation-poc`, `POST /cgi-bin/oa/getapprovalinfo` read support, and automatic template discovery by `SMOKE002` creator; WREAD1 deploy reran WACP3R at `/app/artifacts/qa-flow/wecom-approval-compensation-poc-20260519T125515Z/summary.json` and then generated `/app/artifacts/qa-flow/wecom-approval-readability-gate-20260519T125532Z/summary.json` as `PAUSED_MANUAL_CONFIRMATION`. This still does not clear template-level real-device readability, mirror data model, emergency mode, writeback, or RuleOps.
- WEGOV1 changes the current WeCom route to "Enterprise WeChat owns mature HR/attendance flows; OA stores self-owned history, audit, entry, messages, and emergency fallback." Treat four domains explicitly: WeCom-owned domain is check-in, leave, missed punch, outing, overtime, leave balance, and HR assistant; OA-owned domain remains reimbursement, procurement, contracts, projects, payments, invoices, bank receipts, sensitive attachments, and operating audit; OA archive domain stores WeCom business history, full external keys, and necessary snapshots; OA emergency domain is manually enabled only when WeCom is unavailable. Do not assume a full isolated WeCom sandbox; use the controlled test domain `SMOKE002 -> SMOKE101`, `OA-POC` templates, low-risk approvals, synthetic tests, public live runners, and limited real-device confirmation. Do not start deep mirror until WACP3 compensation pull POC, WREAD1 readability/misapproval gate, WADM1 self-owned archive/mirror data model, and WARCH1 minimum archive implementation have their own contracts and evidence. - WEGOV1 changes the current WeCom route to "Enterprise WeChat owns mature HR/attendance flows; OA stores self-owned history, audit, entry, messages, and emergency fallback." Treat four domains explicitly: WeCom-owned domain is check-in, leave, missed punch, outing, overtime, leave balance, and HR assistant; OA-owned domain remains reimbursement, procurement, contracts, projects, payments, invoices, bank receipts, sensitive attachments, and operating audit; OA archive domain stores WeCom business history, full external keys, and necessary snapshots; OA emergency domain is manually enabled only when WeCom is unavailable. Do not assume a full isolated WeCom sandbox; use the controlled test domain `SMOKE002 -> SMOKE101`, `OA-POC` templates, low-risk approvals, synthetic tests, public live runners, and limited real-device confirmation. Do not start deep mirror until WACP3 compensation pull POC, WREAD1 template-level real-device readability/misapproval confirmation, WADM1 self-owned archive/mirror data model, and WARCH1 minimum archive implementation have their own contracts and evidence.
- For reimbursement, invoice, OCR, duplicate invoice, or attachment work, read `docs/报销发票附件规则.md` and `references/reimbursement.md`. - For reimbursement, invoice, OCR, duplicate invoice, or attachment work, read `docs/报销发票附件规则.md` and `references/reimbursement.md`.
- For local/WeCom/public trial-run, OCR runtime, Caddy, or DB drift work, read `docs/试运行与自动化验证手册.md` and `references/trialrun.md`. - For local/WeCom/public trial-run, OCR runtime, Caddy, or DB drift work, read `docs/试运行与自动化验证手册.md` and `references/trialrun.md`.
- For how this skill should evolve, read `docs/Agent开发交付实践.md`. - For how this skill should evolve, read `docs/Agent开发交付实践.md`.
+2
查看文件
@@ -59,6 +59,7 @@ DCL7 已删除本文件中 R70-R91 的长流水历史,避免旧环境、旧开
- 2026-05-19 WCBK1 实现并完成企微审批回调只读 POC:新增只读 `GET/POST /api/wecom/approval/callback`、企微回调验签/AES 解密/事件归一化服务、`npm run check:wecom-approval-callback-poc` 和 `wecom-callback` process gate。已以 `SYNC_ENV=0` 部署并复检 `/ready`;初始远端报告 `/app/artifacts/qa-flow/wecom-approval-callback-poc-20260519T065414Z/summary.json` 为 `PARTIAL_CALLBACK_NEEDS_CONFIG`,synthetic 回调验签、解密和审批事件解析通过。配置真实回调后,远端报告 `/app/artifacts/qa-flow/wecom-approval-callback-poc-20260519T083422Z/summary.json` 达到 `PASS_CALLBACK_URL_VERIFIED`;用户随后由 `SMOKE101` 同意一条现有 OA-POC 补卡测试审批单,复跑报告 `/app/artifacts/qa-flow/wecom-approval-callback-poc-20260519T092648Z/summary.json` 达到 `PASS_CALLBACK_EVENT_CONFIRMED`,脱敏证据记录 1 条已通过审批状态事件。本轮不建镜像表、不写 OA 业务数据、不写企微数据、不创建新模板或审批单。 - 2026-05-19 WCBK1 实现并完成企微审批回调只读 POC:新增只读 `GET/POST /api/wecom/approval/callback`、企微回调验签/AES 解密/事件归一化服务、`npm run check:wecom-approval-callback-poc` 和 `wecom-callback` process gate。已以 `SYNC_ENV=0` 部署并复检 `/ready`;初始远端报告 `/app/artifacts/qa-flow/wecom-approval-callback-poc-20260519T065414Z/summary.json` 为 `PARTIAL_CALLBACK_NEEDS_CONFIG`,synthetic 回调验签、解密和审批事件解析通过。配置真实回调后,远端报告 `/app/artifacts/qa-flow/wecom-approval-callback-poc-20260519T083422Z/summary.json` 达到 `PASS_CALLBACK_URL_VERIFIED`;用户随后由 `SMOKE101` 同意一条现有 OA-POC 补卡测试审批单,复跑报告 `/app/artifacts/qa-flow/wecom-approval-callback-poc-20260519T092648Z/summary.json` 达到 `PASS_CALLBACK_EVENT_CONFIRMED`,脱敏证据记录 1 条已通过审批状态事件。本轮不建镜像表、不写 OA 业务数据、不写企微数据、不创建新模板或审批单。
- 2026-05-19 WEGOV1 完成企微主办治理与 OA 自建留存路线修正:本轮只更新 Issue CSV 和事实源,不改业务代码、不部署、不写 OA/企微数据。结论固定为“企微成熟功能优先上线,OA 做自建数据留存、审计、入口、消息和应急兜底”;HR/考勤日常流程优先企微主办,报销、采购、合同、项目、付款、发票、银行回单、高敏附件和经营审计继续 OA 主办。后续顺序调整为 `WACP3 -> WREAD1 -> WADM1 -> WARCH1 -> wecom_attendance_approval_mirror_v1`,其中 WARCH1 先保证企微不可用时可查看已保存历史,再进入完整镜像增强。 - 2026-05-19 WEGOV1 完成企微主办治理与 OA 自建留存路线修正:本轮只更新 Issue CSV 和事实源,不改业务代码、不部署、不写 OA/企微数据。结论固定为“企微成熟功能优先上线,OA 做自建数据留存、审计、入口、消息和应急兜底”;HR/考勤日常流程优先企微主办,报销、采购、合同、项目、付款、发票、银行回单、高敏附件和经营审计继续 OA 主办。后续顺序调整为 `WACP3 -> WREAD1 -> WADM1 -> WARCH1 -> wecom_attendance_approval_mirror_v1`,其中 WARCH1 先保证企微不可用时可查看已保存历史,再进入完整镜像增强。
- 2026-05-19 WACP3/WACP3R 完成企微审批补偿拉取只读 POC:新增 `server/src/scripts/check-wecom-approval-compensation-poc.js` / `npm run check:wecom-approval-compensation-poc`,固定 `WECOM_AGENT_SECRET` 调用 `POST /cgi-bin/oa/getapprovalinfo`,按时间窗口和模板过滤拉审批单号,再复用 `getapprovaldetail` 拉详情并与 WCBK1 回调证据脱敏比对。初始公网报告因缺模板 ID 为 `PARTIAL_COMPENSATION_NEEDS_CONFIG`;WACP3R 改为按 `SMOKE002` creator 自动发现 OA-POC 请假/补卡模板并复跑,公网报告 `/app/artifacts/qa-flow/wecom-approval-compensation-poc-20260519T115944Z/summary.json` 为 `PASS_WITH_FINDINGS`:请假/补卡各拉到 1 条详情,finding 为回调证据文件未持久化。本轮未写 OA/企微数据,未保存完整模板 ID。 - 2026-05-19 WACP3/WACP3R 完成企微审批补偿拉取只读 POC:新增 `server/src/scripts/check-wecom-approval-compensation-poc.js` / `npm run check:wecom-approval-compensation-poc`,固定 `WECOM_AGENT_SECRET` 调用 `POST /cgi-bin/oa/getapprovalinfo`,按时间窗口和模板过滤拉审批单号,再复用 `getapprovaldetail` 拉详情并与 WCBK1 回调证据脱敏比对。初始公网报告因缺模板 ID 为 `PARTIAL_COMPENSATION_NEEDS_CONFIG`;WACP3R 改为按 `SMOKE002` creator 自动发现 OA-POC 请假/补卡模板并复跑,公网报告 `/app/artifacts/qa-flow/wecom-approval-compensation-poc-20260519T115944Z/summary.json` 为 `PASS_WITH_FINDINGS`:请假/补卡各拉到 1 条详情,finding 为回调证据文件未持久化。本轮未写 OA/企微数据,未保存完整模板 ID。
- 2026-05-19 WREAD1 新增企微审批可读性与误审批防护门禁:新增 `server/src/scripts/check-wecom-approval-readability-gate.js` / `npm run check:wecom-approval-readability-gate`,只读汇总 OA-POC 请假/补卡证据并输出真机确认清单。本轮以 `SYNC_ENV=0` 部署,不同步本地 env;部署后先只读复跑 WACP3R 自动发现模板并生成 `/app/artifacts/qa-flow/wecom-approval-compensation-poc-20260519T125515Z/summary.json`,再执行 WREAD1,报告 `/app/artifacts/qa-flow/wecom-approval-readability-gate-20260519T125532Z/summary.json` 为 `PAUSED_MANUAL_CONFIRMATION`。本轮未新建企微模板或审批单,未写 OA/企微数据;下一步需要审批人真机分别确认请假/补卡消息卡片、列表、详情页和快捷同意/驳回误点风险。
## 关键证据索引 ## 关键证据索引
@@ -116,6 +117,7 @@ DCL7 已删除本文件中 R70-R91 的长流水历史,避免旧环境、旧开
| WCBK1 | 已完成公网真实回调只读 POC:只读回调端点和 synthetic runner 通过本地加密链路;远端配置真实回调后达到 `PASS_CALLBACK_URL_VERIFIED`,`SMOKE101` 同意现有 OA-POC 补卡测试单后达到 `PASS_CALLBACK_EVENT_CONFIRMED`;只保存脱敏事件证据,不写业务镜像 | `issues/2026-05-19_14-11-20-wecom-approval-callback-readonly-poc-v1.csv`、`/app/artifacts/qa-flow/wecom-approval-callback-poc-20260519T092648Z/summary.json` | | WCBK1 | 已完成公网真实回调只读 POC:只读回调端点和 synthetic runner 通过本地加密链路;远端配置真实回调后达到 `PASS_CALLBACK_URL_VERIFIED`,`SMOKE101` 同意现有 OA-POC 补卡测试单后达到 `PASS_CALLBACK_EVENT_CONFIRMED`;只保存脱敏事件证据,不写业务镜像 | `issues/2026-05-19_14-11-20-wecom-approval-callback-readonly-poc-v1.csv`、`/app/artifacts/qa-flow/wecom-approval-callback-poc-20260519T092648Z/summary.json` |
| WEGOV1 | 已完成 docs-only 路线治理:固定企微主办域、OA 主办域、OA 留存域、OA 应急域、受控测试域和自动化/人工边界;明确 WCBK1 证据不直接放行完整镜像,后续先做 WACP3/WACP3R、WREAD1、WADM1 和 WARCH1 | `issues/2026-05-19_18-17-44-wecom-governance-owned-oa-archive-route-v1.csv` | | WEGOV1 | 已完成 docs-only 路线治理:固定企微主办域、OA 主办域、OA 留存域、OA 应急域、受控测试域和自动化/人工边界;明确 WCBK1 证据不直接放行完整镜像,后续先做 WACP3/WACP3R、WREAD1、WADM1 和 WARCH1 | `issues/2026-05-19_18-17-44-wecom-governance-owned-oa-archive-route-v1.csv` |
| WACP3/WACP3R | 已实现并公网只读复跑:新增补偿拉取 runner 和 `getapprovalinfo` 只读封装;WACP3R 不再要求人工填写模板 ID,改为按 `SMOKE002` creator 自动发现 OA-POC 请假/补卡模板并复跑。远端报告 `/app/artifacts/qa-flow/wecom-approval-compensation-poc-20260519T115944Z/summary.json` 为 `PASS_WITH_FINDINGS`:请假/补卡各拉到 1 条详情,finding 为回调证据文件未持久化,未写 OA/企微数据 | `issues/2026-05-19_19-03-06-wecom-approval-compensation-pull-poc-v1.csv`、`issues/2026-05-19_19-42-22-wecom-approval-compensation-config-rerun-v1.csv`、`/app/artifacts/qa-flow/wecom-approval-compensation-poc-20260519T115944Z/summary.json` | | WACP3/WACP3R | 已实现并公网只读复跑:新增补偿拉取 runner 和 `getapprovalinfo` 只读封装;WACP3R 不再要求人工填写模板 ID,改为按 `SMOKE002` creator 自动发现 OA-POC 请假/补卡模板并复跑。远端报告 `/app/artifacts/qa-flow/wecom-approval-compensation-poc-20260519T115944Z/summary.json` 为 `PASS_WITH_FINDINGS`:请假/补卡各拉到 1 条详情,finding 为回调证据文件未持久化,未写 OA/企微数据 | `issues/2026-05-19_19-03-06-wecom-approval-compensation-pull-poc-v1.csv`、`issues/2026-05-19_19-42-22-wecom-approval-compensation-config-rerun-v1.csv`、`/app/artifacts/qa-flow/wecom-approval-compensation-poc-20260519T115944Z/summary.json` |
| WREAD1 | 自动门禁已部署并公网只读汇总:新增可读性 runner,部署后复跑 WACP3R 生成 fresh 脱敏证据,再执行 WREAD1 得到 `PAUSED_MANUAL_CONFIRMATION`;等待审批人真机确认请假/补卡可读性和快捷按钮误点风险 | `issues/2026-05-19_20-39-40-wecom-approval-readability-gate-v1.csv`、`/app/artifacts/qa-flow/wecom-approval-compensation-poc-20260519T125515Z/summary.json`、`/app/artifacts/qa-flow/wecom-approval-readability-gate-20260519T125532Z/summary.json` |
## 当前门禁 ## 当前门禁
+1 -1
查看文件
@@ -107,7 +107,7 @@ WPROC1 将 WAPC2 之后的审批路线固定为:
- `WCBK1` 已完成真实回调 POC:`npm run check:wecom-approval-callback-poc -- --live` 先达到 `PASS_CALLBACK_URL_VERIFIED`,随后由 `SMOKE101` 同意一条现有 OA-POC 补卡测试审批单并复跑达到 `PASS_CALLBACK_EVENT_CONFIRMED`。报告只记录事件类型、审批类型、状态、脱敏 `sp_no`/模板前缀和幂等键摘要,不保存 Token、EncodingAESKey、原始 XML、密文、完整 `sp_no`、完整 userid、表单值、附件或评论。 - `WCBK1` 已完成真实回调 POC:`npm run check:wecom-approval-callback-poc -- --live` 先达到 `PASS_CALLBACK_URL_VERIFIED`,随后由 `SMOKE101` 同意一条现有 OA-POC 补卡测试审批单并复跑达到 `PASS_CALLBACK_EVENT_CONFIRMED`。报告只记录事件类型、审批类型、状态、脱敏 `sp_no`/模板前缀和幂等键摘要,不保存 Token、EncodingAESKey、原始 XML、密文、完整 `sp_no`、完整 userid、表单值、附件或评论。
- `WEGOV1` 固定混合架构:企微主办域为 HR/考勤成熟功能,OA 主办域为财务经营和高敏业务,OA 留存域保存企微关键历史和完整外部关联键,OA 应急域只在企微不可用时人工开启。 - `WEGOV1` 固定混合架构:企微主办域为 HR/考勤成熟功能,OA 主办域为财务经营和高敏业务,OA 留存域保存企微关键历史和完整外部关联键,OA 应急域只在企微不可用时人工开启。
- `WACP3R` 已把补偿拉取复跑改为自动发现:先按 `SMOKE002` creator 获取 OA-POC 审批单号和详情,识别请假/补卡模板,再按模板过滤拉取详情;公网报告 `/app/artifacts/qa-flow/wecom-approval-compensation-poc-20260519T115944Z/summary.json` 为 `PASS_WITH_FINDINGS`。finding 是回调证据文件未持久化,不得误读为镜像入库或自建留存已完成。 - `WACP3R` 已把补偿拉取复跑改为自动发现:先按 `SMOKE002` creator 获取 OA-POC 审批单号和详情,识别请假/补卡模板,再按模板过滤拉取详情;公网报告 `/app/artifacts/qa-flow/wecom-approval-compensation-poc-20260519T115944Z/summary.json` 为 `PASS_WITH_FINDINGS`。finding 是回调证据文件未持久化,不得误读为镜像入库或自建留存已完成。
- `WREAD1` 必须用真机或代表设备确认审批消息、审批列表和详情页的可读性;若快捷同意/驳回容易误操作,则模板调整或该模板回退 OA 主办。 - `WREAD1` 已新增只读可读性 runner 并公网生成 `/app/artifacts/qa-flow/wecom-approval-readability-gate-20260519T125532Z/summary.json`,结论 `PAUSED_MANUAL_CONFIRMATION`;仍必须用真机或代表设备确认审批消息、审批列表和详情页的可读性。若快捷同意/驳回容易误操作,则模板调整或该模板回退 OA 主办。
- `WADM1` 必须先设计自建留存和镜像字段、状态机、幂等键、索引、隐私保留和迁移回滚方案,再进入 schema 或业务代码实现。 - `WADM1` 必须先设计自建留存和镜像字段、状态机、幂等键、索引、隐私保留和迁移回滚方案,再进入 schema 或业务代码实现。
- `WARCH1` 必须先实现最小自建历史留存,让 OA 在企微 API 不可用时仍可查看已保存历史,再进入完整镜像增强。 - `WARCH1` 必须先实现最小自建历史留存,让 OA 在企微 API 不可用时仍可查看已保存历史,再进入完整镜像增强。
+5 -4
查看文件
@@ -2,10 +2,10 @@
**文档状态**:当前开发路线图事实源。 **文档状态**:当前开发路线图事实源。
**分类**:开发治理 / 路线图。 **分类**:开发治理 / 路线图。
**适用时间**:WDRG / WCP1 / WHMSLIVE / WOUA / WAPI / WRRA / WOUA-FIX / WRV1 / WRA1 / WMSG1 / WAPC1 / WAPC2 / WPROC1 / WPROC2 / WAPC2F / WAPC2FLIVE / WCBK1 / WEGOV1 / WACP3 / WACP3R 之后。 **适用时间**:WDRG / WCP1 / WHMSLIVE / WOUA / WAPI / WRRA / WOUA-FIX / WRV1 / WRA1 / WMSG1 / WAPC1 / WAPC2 / WPROC1 / WPROC2 / WAPC2F / WAPC2FLIVE / WCBK1 / WEGOV1 / WACP3 / WACP3R / WREAD1 自动汇总之后。
**关联 Issue**:`WDRG,WCP1,WHMS,WHMSLIVE,WOUA,WAPI,WRRA,WOUAFIX,WRV1,WRA1,WMSG1,WAPC1,WAPC2,WPROC1,WPROC2,WAPC2F,WAPC2FLIVE,WCBK1,WEGOV1,WACP3,WACP3R`。 **关联 Issue**:`WDRG,WCP1,WHMS,WHMSLIVE,WOUA,WAPI,WRRA,WOUAFIX,WRV1,WRA1,WMSG1,WAPC1,WAPC2,WPROC1,WPROC2,WAPC2F,WAPC2FLIVE,WCBK1,WEGOV1,WACP3,WACP3R,WREAD1`。
**最近审计**:2026-05-19。 **最近审计**:2026-05-19。
**不得误读**:本文只控制后续开发节奏、阶段顺序和门禁。WHMSLIVE 后只代表企微 HR 主数据镜像已部署并受控验证;WAPI 后进一步固定企微 API/Secret 口径并转为运行优先;WRRA 后明确 `PASS_WITH_FINDINGS` 只代表只读对账成功且存在差异,不代表企微考勤/审批镜像、OA 应急接管、恢复回写或 RuleOps 已完成运行时实现。WMSG1 只证明企业微信应用消息可达和 Deeplink 可回到 OA;WAPC1 只固化审批配置准备清单;WAPC2 只新增请假/补卡模板详情和低风险 `sp_no` 审批详情只读 POC,不代表真实回调或镜像能力已完成。WPROC1 后,WAPC2 不直接放行深度镜像,必须先完成自动样件、回调、补偿拉取和镜像数据模型设计门禁。WPROC2 只新增可执行 process gate。WAPC2FLIVE 已创建 OA-POC 请假/补卡测试模板和低风险测试审批单,并自动复跑 WAPC2 得到 `PASS_APPROVAL_POC`。WCBK1 已完成真实回调 URL 验证和一条现有 OA-POC 测试审批单事件确认,公网报告为 `PASS_CALLBACK_EVENT_CONFIRMED`;WEGOV1 后路线明确为“企微成熟功能优先上线,OA 做自建留存、审计、入口、消息和应急兜底”。WACP3R 已将补偿拉取复跑修正为自动按 `SMOKE002` creator 发现 OA-POC 请假/补卡模板,公网报告 `/app/artifacts/qa-flow/wecom-approval-compensation-poc-20260519T115944Z/summary.json` 为 `PASS_WITH_FINDINGS`:已拉到请假/补卡审批详情,但当前回调证据文件未持久化到可跨部署读取的位置,作为 findings 进入 WADM1/WARCH1。上述证据只证明企微主办 HR/考勤的可用基础,不代表可读性门禁、自建留存模型、最小历史留存或 `wecom_attendance_approval_mirror_v1` 已完成。 **不得误读**:本文只控制后续开发节奏、阶段顺序和门禁。WHMSLIVE 后只代表企微 HR 主数据镜像已部署并受控验证;WAPI 后进一步固定企微 API/Secret 口径并转为运行优先;WRRA 后明确 `PASS_WITH_FINDINGS` 只代表只读对账成功且存在差异,不代表企微考勤/审批镜像、OA 应急接管、恢复回写或 RuleOps 已完成运行时实现。WMSG1 只证明企业微信应用消息可达和 Deeplink 可回到 OA;WAPC1 只固化审批配置准备清单;WAPC2 只新增请假/补卡模板详情和低风险 `sp_no` 审批详情只读 POC,不代表真实回调或镜像能力已完成。WPROC1 后,WAPC2 不直接放行深度镜像,必须先完成自动样件、回调、补偿拉取和镜像数据模型设计门禁。WPROC2 只新增可执行 process gate。WAPC2FLIVE 已创建 OA-POC 请假/补卡测试模板和低风险测试审批单,并自动复跑 WAPC2 得到 `PASS_APPROVAL_POC`。WCBK1 已完成真实回调 URL 验证和一条现有 OA-POC 测试审批单事件确认,公网报告为 `PASS_CALLBACK_EVENT_CONFIRMED`;WEGOV1 后路线明确为“企微成熟功能优先上线,OA 做自建留存、审计、入口、消息和应急兜底”。WACP3R 已将补偿拉取复跑修正为自动按 `SMOKE002` creator 发现 OA-POC 请假/补卡模板,公网报告 `/app/artifacts/qa-flow/wecom-approval-compensation-poc-20260519T115944Z/summary.json` 为 `PASS_WITH_FINDINGS`;WREAD1 又在部署后只读复跑补偿拉取 `/app/artifacts/qa-flow/wecom-approval-compensation-poc-20260519T125515Z/summary.json`,并生成可读性门禁报告 `/app/artifacts/qa-flow/wecom-approval-readability-gate-20260519T125532Z/summary.json`,结论为 `PAUSED_MANUAL_CONFIRMATION`。上述证据只证明请假/补卡样例仍可按 API 读取,尚未得到审批人真机可读性和误点风险结论;不代表自建留存模型、最小历史留存或 `wecom_attendance_approval_mirror_v1` 已完成。
## 1. 路线图原则 ## 1. 路线图原则
@@ -45,6 +45,7 @@
| `WCBK1` | 真实回调 URL 与事件已确认 | 新增只读 `GET/POST /api/wecom/approval/callback`、`npm run check:wecom-approval-callback-poc` 和 `wecom-callback` process gate。远端配置回调 URL、Token 和 EncodingAESKey 后,live runner 报告 `/app/artifacts/qa-flow/wecom-approval-callback-poc-20260519T083422Z/summary.json` 达到 `PASS_CALLBACK_URL_VERIFIED`;用户随后由 `SMOKE101` 同意一条现有 OA-POC 补卡测试审批单,复跑报告 `/app/artifacts/qa-flow/wecom-approval-callback-poc-20260519T092648Z/summary.json` 达到 `PASS_CALLBACK_EVENT_CONFIRMED`,脱敏证据记录 `eventCount=1`、状态 `已通过`。 | | `WCBK1` | 真实回调 URL 与事件已确认 | 新增只读 `GET/POST /api/wecom/approval/callback`、`npm run check:wecom-approval-callback-poc` 和 `wecom-callback` process gate。远端配置回调 URL、Token 和 EncodingAESKey 后,live runner 报告 `/app/artifacts/qa-flow/wecom-approval-callback-poc-20260519T083422Z/summary.json` 达到 `PASS_CALLBACK_URL_VERIFIED`;用户随后由 `SMOKE101` 同意一条现有 OA-POC 补卡测试审批单,复跑报告 `/app/artifacts/qa-flow/wecom-approval-callback-poc-20260519T092648Z/summary.json` 达到 `PASS_CALLBACK_EVENT_CONFIRMED`,脱敏证据记录 `eventCount=1`、状态 `已通过`。 |
| `WEGOV1` | 路线和治理已调整 | 明确混合架构:企微成熟功能优先上线,OA 做自建数据留存、审计、入口、消息和应急兜底;固化受控测试域、自动化优先和人工边界;后续顺序调整为 WACP3、WREAD1、WADM1、WARCH1 后再评估深度镜像。 | | `WEGOV1` | 路线和治理已调整 | 明确混合架构:企微成熟功能优先上线,OA 做自建数据留存、审计、入口、消息和应急兜底;固化受控测试域、自动化优先和人工边界;后续顺序调整为 WACP3、WREAD1、WADM1、WARCH1 后再评估深度镜像。 |
| `WACP3/WACP3R` | 已实现并公网只读复跑,有 findings | 新增 QA-only `npm run check:wecom-approval-compensation-poc` 和 `POST /cgi-bin/oa/getapprovalinfo` 只读封装,固定 `WECOM_AGENT_SECRET`。WACP3R 改为按 `SMOKE002` creator 自动发现 OA-POC 请假/补卡模板 ID,再按模板过滤拉审批单号并用 `getapprovaldetail` 拉详情;公网报告 `/app/artifacts/qa-flow/wecom-approval-compensation-poc-20260519T115944Z/summary.json` 为 `PASS_WITH_FINDINGS`:请假/补卡各拉到 1 条详情,发现回调证据文件未持久化导致当前无法跨部署比对历史回调。未写 OA/企微数据,未保存完整模板 ID。 | | `WACP3/WACP3R` | 已实现并公网只读复跑,有 findings | 新增 QA-only `npm run check:wecom-approval-compensation-poc` 和 `POST /cgi-bin/oa/getapprovalinfo` 只读封装,固定 `WECOM_AGENT_SECRET`。WACP3R 改为按 `SMOKE002` creator 自动发现 OA-POC 请假/补卡模板 ID,再按模板过滤拉审批单号并用 `getapprovaldetail` 拉详情;公网报告 `/app/artifacts/qa-flow/wecom-approval-compensation-poc-20260519T115944Z/summary.json` 为 `PASS_WITH_FINDINGS`:请假/补卡各拉到 1 条详情,发现回调证据文件未持久化导致当前无法跨部署比对历史回调。未写 OA/企微数据,未保存完整模板 ID。 |
| `WREAD1` | 自动汇总已部署,等待真机确认 | 新增 QA-only `npm run check:wecom-approval-readability-gate`,只读汇总现有 OA-POC 请假/补卡证据并输出人工确认清单。部署后因容器 artifact 重建,先只读复跑 WACP3R 生成 `/app/artifacts/qa-flow/wecom-approval-compensation-poc-20260519T125515Z/summary.json`,再生成 WREAD1 报告 `/app/artifacts/qa-flow/wecom-approval-readability-gate-20260519T125532Z/summary.json`,结论 `PAUSED_MANUAL_CONFIRMATION`。本阶段不写 OA/企微数据,不新建模板或审批单;模板级结论需审批人真机确认后才能写入。 |
## 3. 阶段路线 ## 3. 阶段路线
@@ -59,7 +60,7 @@
| 7 | `WCBK1 回调只读 POC` | 证明真实回调 URL 验证、验签、解密、幂等键和重复投递处理。 | 回调 URL、Token、EncodingAESKey 由部署环境配置;不得写入仓库或聊天。 | 已完成:`PASS_CALLBACK_EVENT_CONFIRMED`。远端 live URL 验证通过后,由 `SMOKE101` 处理现有 OA-POC 补卡测试单触发真实事件;报告只记录脱敏事件类型、状态、`sp_no`/模板前缀和幂等键摘要。 | | 7 | `WCBK1 回调只读 POC` | 证明真实回调 URL 验证、验签、解密、幂等键和重复投递处理。 | 回调 URL、Token、EncodingAESKey 由部署环境配置;不得写入仓库或聊天。 | 已完成:`PASS_CALLBACK_EVENT_CONFIRMED`。远端 live URL 验证通过后,由 `SMOKE101` 处理现有 OA-POC 补卡测试单触发真实事件;报告只记录脱敏事件类型、状态、`sp_no`/模板前缀和幂等键摘要。 |
| 8 | `WEGOV1 企微主办治理与自建留存路线修正` | 固化混合主办边界、OA 自建留存策略、受控测试域和后续顺序。 | WCBK1 已确认真实回调事件;用户选择优先利用企微成熟功能并要求 OA 保存关键历史。 | docs-only 收口;文档明确企微主办域、OA 主办域、OA 留存域、OA 应急域、自动化与人工边界。 | | 8 | `WEGOV1 企微主办治理与自建留存路线修正` | 固化混合主办边界、OA 自建留存策略、受控测试域和后续顺序。 | WCBK1 已确认真实回调事件;用户选择优先利用企微成熟功能并要求 OA 保存关键历史。 | docs-only 收口;文档明确企微主办域、OA 主办域、OA 留存域、OA 应急域、自动化与人工边界。 |
| 9 | `WACP3 补偿拉取 POC` | 证明按时间窗口批量获取审批单号并可拉取审批详情,作为回调丢失补偿。 | WEGOV1 完成;WAPC2/WAPC2FLIVE/WCBK1 已提供低风险样例、详情读取和真实事件证据;官方 API 和权限明确。 | WACP3R 已自动发现 OA-POC 请假/补卡模板并复跑到 `PASS_WITH_FINDINGS`:补偿拉取和详情读取可用;findings 为当前回调证据文件未持久化,需在 WADM1/WARCH1 设计留存模型时解决。 | | 9 | `WACP3 补偿拉取 POC` | 证明按时间窗口批量获取审批单号并可拉取审批详情,作为回调丢失补偿。 | WEGOV1 完成;WAPC2/WAPC2FLIVE/WCBK1 已提供低风险样例、详情读取和真实事件证据;官方 API 和权限明确。 | WACP3R 已自动发现 OA-POC 请假/补卡模板并复跑到 `PASS_WITH_FINDINGS`:补偿拉取和详情读取可用;findings 为当前回调证据文件未持久化,需在 WADM1/WARCH1 设计留存模型时解决。 |
| 10 | `WREAD1 企微审批可读性与误审批防护门禁` | 真机确认请假、补卡、外出、加班在企微里是否看得清再批,评估快捷同意/驳回误操作风险。 | WACP3 完成或并行准备低风险样例;审批模板可在企微手机端由代表审批人查看。 | 输出 `PASS_READABLE`、`PASS_WITH_TEMPLATE_ADJUSTMENTS` 或 `FAIL_KEEP_OA_PRIMARY_FOR_THIS_TEMPLATE`;不可读模板不得进入正式镜像。 | | 10 | `WREAD1 企微审批可读性与误审批防护门禁` | 真机确认请假、补卡、外出、加班在企微里是否看得清再批,评估快捷同意/驳回误操作风险。 | WACP3 完成或并行准备低风险样例;审批模板可在企微手机端由代表审批人查看。 | 当前自动汇总为 `PAUSED_MANUAL_CONFIRMATION`;待人工按模板输出 `PASS_READABLE`、`PASS_WITH_TEMPLATE_ADJUSTMENTS` 或 `FAIL_KEEP_OA_PRIMARY_FOR_THIS_TEMPLATE`。不可读模板不得进入正式镜像。 |
| 11 | `WADM1 自建留存/镜像数据模型设计` | 先设计 OA 留存字段、状态机、幂等键、索引、隐私保留、审计和迁移回滚方案。 | WACP3 证明补偿拉取可用;WREAD1 明确模板可读性或回退策略。 | 模型覆盖审批、打卡、人事关键快照、回调事件和补偿拉取记录;设计经 Issue CSV 固化后才允许 schema 或业务代码实现。 | | 11 | `WADM1 自建留存/镜像数据模型设计` | 先设计 OA 留存字段、状态机、幂等键、索引、隐私保留、审计和迁移回滚方案。 | WACP3 证明补偿拉取可用;WREAD1 明确模板可读性或回退策略。 | 模型覆盖审批、打卡、人事关键快照、回调事件和补偿拉取记录;设计经 Issue CSV 固化后才允许 schema 或业务代码实现。 |
| 12 | `WARCH1 最小自建历史留存实现` | 只读保存企微审批、打卡、人事关键快照,先满足企微不可用时可查看历史。 | WADM1 完成并批准 schema/接口实现。 | OA 可查看已保存历史摘要和完整外部关联键;报告和日志只输出脱敏前缀;不保存 Secret、token、EncodingAESKey、原始密文或无治理原始 payload。 | | 12 | `WARCH1 最小自建历史留存实现` | 只读保存企微审批、打卡、人事关键快照,先满足企微不可用时可查看历史。 | WADM1 完成并批准 schema/接口实现。 | OA 可查看已保存历史摘要和完整外部关联键;报告和日志只输出脱敏前缀;不保存 Secret、token、EncodingAESKey、原始密文或无治理原始 payload。 |
| 13 | `wecom_attendance_approval_mirror_v1` | 企微打卡、请假、补卡、外出、加班、假期余额和审批历史镜像到 OA。 | WARCH1 稳定;外出/加班模板和打卡细项明确;运行优先门禁和用户一致性仍满足。 | OA 可查询企微 HR/考勤镜像、附件摘要、审批历史、考勤解释和审计;不把 OA 主办高敏审批迁到企微。 | | 13 | `wecom_attendance_approval_mirror_v1` | 企微打卡、请假、补卡、外出、加班、假期余额和审批历史镜像到 OA。 | WARCH1 稳定;外出/加班模板和打卡细项明确;运行优先门禁和用户一致性仍满足。 | OA 可查询企微 HR/考勤镜像、附件摘要、审批历史、考勤解释和审计;不把 OA 主办高敏审批迁到企微。 |
+6 -5
查看文件
@@ -85,6 +85,7 @@
| `WCBK1` | `issues/2026-05-19_14-11-20-wecom-approval-callback-readonly-poc-v1.csv` | `CURRENT` | 企微审批回调只读 POC 已完成公网真实事件验证:只读回调端点、验签/AES 解密/归一化服务和 runner 已部署;远端报告 `/app/artifacts/qa-flow/wecom-approval-callback-poc-20260519T083422Z/summary.json` 为 `PASS_CALLBACK_URL_VERIFIED`,`SMOKE101` 同意现有 OA-POC 补卡测试单后复跑 `/app/artifacts/qa-flow/wecom-approval-callback-poc-20260519T092648Z/summary.json` 为 `PASS_CALLBACK_EVENT_CONFIRMED`;仍不代表补偿拉取、镜像模型或深度镜像已完成 | | `WCBK1` | `issues/2026-05-19_14-11-20-wecom-approval-callback-readonly-poc-v1.csv` | `CURRENT` | 企微审批回调只读 POC 已完成公网真实事件验证:只读回调端点、验签/AES 解密/归一化服务和 runner 已部署;远端报告 `/app/artifacts/qa-flow/wecom-approval-callback-poc-20260519T083422Z/summary.json` 为 `PASS_CALLBACK_URL_VERIFIED`,`SMOKE101` 同意现有 OA-POC 补卡测试单后复跑 `/app/artifacts/qa-flow/wecom-approval-callback-poc-20260519T092648Z/summary.json` 为 `PASS_CALLBACK_EVENT_CONFIRMED`;仍不代表补偿拉取、镜像模型或深度镜像已完成 |
| `WEGOV1` | `issues/2026-05-19_18-17-44-wecom-governance-owned-oa-archive-route-v1.csv` | `CURRENT` | 企微主办治理与 OA 自建留存路线修正:docs-only 固定企微主办域、OA 主办域、OA 留存域、OA 应急域、受控测试域、自动化/人工边界和后续顺序;WCBK1 后仍需 WACP3、WREAD1、WADM1、WARCH1,不能直接进入深度镜像 | | `WEGOV1` | `issues/2026-05-19_18-17-44-wecom-governance-owned-oa-archive-route-v1.csv` | `CURRENT` | 企微主办治理与 OA 自建留存路线修正:docs-only 固定企微主办域、OA 主办域、OA 留存域、OA 应急域、受控测试域、自动化/人工边界和后续顺序;WCBK1 后仍需 WACP3、WREAD1、WADM1、WARCH1,不能直接进入深度镜像 |
| `WACP3/WACP3R` | `issues/2026-05-19_19-03-06-wecom-approval-compensation-pull-poc-v1.csv`、`issues/2026-05-19_19-42-22-wecom-approval-compensation-config-rerun-v1.csv` | `CURRENT` | 企微审批补偿拉取 POC 已实现并公网只读复跑:新增 `check:wecom-approval-compensation-poc` 和 `getapprovalinfo` 只读封装,固定 `WECOM_AGENT_SECRET`;WACP3R 改为按 `SMOKE002` creator 自动发现 OA-POC 请假/补卡模板并复跑,远端报告 `/app/artifacts/qa-flow/wecom-approval-compensation-poc-20260519T115944Z/summary.json` 为 `PASS_WITH_FINDINGS`,请假/补卡各拉到 1 条详情,finding 为回调证据文件未持久化;未写 OA/企微数据 | | `WACP3/WACP3R` | `issues/2026-05-19_19-03-06-wecom-approval-compensation-pull-poc-v1.csv`、`issues/2026-05-19_19-42-22-wecom-approval-compensation-config-rerun-v1.csv` | `CURRENT` | 企微审批补偿拉取 POC 已实现并公网只读复跑:新增 `check:wecom-approval-compensation-poc` 和 `getapprovalinfo` 只读封装,固定 `WECOM_AGENT_SECRET`;WACP3R 改为按 `SMOKE002` creator 自动发现 OA-POC 请假/补卡模板并复跑,远端报告 `/app/artifacts/qa-flow/wecom-approval-compensation-poc-20260519T115944Z/summary.json` 为 `PASS_WITH_FINDINGS`,请假/补卡各拉到 1 条详情,finding 为回调证据文件未持久化;未写 OA/企微数据 |
| `WREAD1` | `issues/2026-05-19_20-39-40-wecom-approval-readability-gate-v1.csv` | `CURRENT` | 企微审批可读性与误审批防护门禁自动汇总已实现并公网只读复跑:新增 `check:wecom-approval-readability-gate`,部署后先复跑 WACP3R 生成 `/app/artifacts/qa-flow/wecom-approval-compensation-poc-20260519T125515Z/summary.json`,再生成 `/app/artifacts/qa-flow/wecom-approval-readability-gate-20260519T125532Z/summary.json`,结论 `PAUSED_MANUAL_CONFIRMATION`;等待审批人真机确认请假/补卡模板级可读性和误点风险,未写 OA/企微数据 |
旧 Issue CSV 保留为历史审计证据:不删除、不批量回写、不移动到 `docs/archive/` 或其他归档目录。新连续合同如替代旧合同,只在新合同 `Notes` 或文档索引中说明 supersedes。 旧 Issue CSV 保留为历史审计证据:不删除、不批量回写、不移动到 `docs/archive/` 或其他归档目录。新连续合同如替代旧合同,只在新合同 `Notes` 或文档索引中说明 supersedes。
@@ -97,14 +98,14 @@
| `PRD-MVP` | `docs/海音达OA-MVP需求规格说明书.md` | 需求事实源 | PRD 主事实源 | `CURRENT` | product | `REQ8,P2T,GOV8,HR-WECOM-DIRECTORY-V1,WFOC,DBWFOC,WFDG,WDRG,WCP1,WCPC,WHMS,WHMSLIVE,WOUA,WAPI,WRRA,WAPC1,WAPC2,WAPC2FLIVE,WCBK1,WEGOV1,WACP3,WACP3R` | Issue CSV | `docs/requirements-specification.md` | 2026-05-19 | `rg` | high | WEGOV1 后定义 HR/考勤企微主办、OA 自建留存、受控测试域、WACP3/WACP3R、WREAD1/WADM1/WARCH1 前置和深度镜像边界;ERP 未来不接入 OA | | `PRD-MVP` | `docs/海音达OA-MVP需求规格说明书.md` | 需求事实源 | PRD 主事实源 | `CURRENT` | product | `REQ8,P2T,GOV8,HR-WECOM-DIRECTORY-V1,WFOC,DBWFOC,WFDG,WDRG,WCP1,WCPC,WHMS,WHMSLIVE,WOUA,WAPI,WRRA,WAPC1,WAPC2,WAPC2FLIVE,WCBK1,WEGOV1,WACP3,WACP3R` | Issue CSV | `docs/requirements-specification.md` | 2026-05-19 | `rg` | high | WEGOV1 后定义 HR/考勤企微主办、OA 自建留存、受控测试域、WACP3/WACP3R、WREAD1/WADM1/WARCH1 前置和深度镜像边界;ERP 未来不接入 OA |
| `PRODUCT-SCOPE` | `docs/产品口径.md` | 需求事实源 | 产品边界事实源 | `CURRENT` | product | `REQ8,P2T,WFDG,WDRG,WCP1,WCPC,WHMS,WHMSLIVE,WOUA,WAPI,WRRA,WAPC1,WAPC2FLIVE,WCBK1,WEGOV1` | `rg` | none | 2026-05-19 | `rg` | high | 记录已拍板产品边界、企微/OA 设计门禁、开发路线图、WCBK1 真实回调事件证据、WEGOV1 混合主办边界和 OA 自建留存策略 | | `PRODUCT-SCOPE` | `docs/产品口径.md` | 需求事实源 | 产品边界事实源 | `CURRENT` | product | `REQ8,P2T,WFDG,WDRG,WCP1,WCPC,WHMS,WHMSLIVE,WOUA,WAPI,WRRA,WAPC1,WAPC2FLIVE,WCBK1,WEGOV1` | `rg` | none | 2026-05-19 | `rg` | high | 记录已拍板产品边界、企微/OA 设计门禁、开发路线图、WCBK1 真实回调事件证据、WEGOV1 混合主办边界和 OA 自建留存策略 |
| `DESIGN-UI` | `docs/产品设计规则.md` | 设计事实源 | PC/移动/企业微信设计事实源 | `CURRENT` | design/frontend | `MIA7,MIF7,GOV8,DG8,DG9,WFDG` | Playwright/视觉审查/DG8/DG9/WFDG | none | 2026-05-18 | `rg` | high | 页面改动必须读取;DG8 后飞书为主 UI/布局参考;DG9 后按页面分层和样板选型;WFDG 后区分 OA 主办、企微镜像和 OA 应急接管 | | `DESIGN-UI` | `docs/产品设计规则.md` | 设计事实源 | PC/移动/企业微信设计事实源 | `CURRENT` | design/frontend | `MIA7,MIF7,GOV8,DG8,DG9,WFDG` | Playwright/视觉审查/DG8/DG9/WFDG | none | 2026-05-18 | `rg` | high | 页面改动必须读取;DG8 后飞书为主 UI/布局参考;DG9 后按页面分层和样板选型;WFDG 后区分 OA 主办、企微镜像和 OA 应急接管 |
| `ROADMAP-WECOM` | `docs/企微优先开发路线图.md` | 开发治理 | 企微优先开发路线图事实源 | `CURRENT` | product/dev | `WDRG,WCP1,WCPC,WHMS,WHMSLIVE,WOUA,WAPI,WRRA,WOUAFIX,WRV1,WRA1,WMSG1,WAPC1,WAPC2,WPROC1,WPROC2,WAPC2F,WAPC2FLIVE,WCBK1,WEGOV1,WACP3,WACP3R` | Issue CSV | none | 2026-05-19 | `rg` | high | WACP3R 后记录补偿拉取 `PASS_WITH_FINDINGS`、回调证据未持久化 finding、WREAD1/WADM1/WARCH1 顺序和禁止跳阶段 | | `ROADMAP-WECOM` | `docs/企微优先开发路线图.md` | 开发治理 | 企微优先开发路线图事实源 | `CURRENT` | product/dev | `WDRG,WCP1,WCPC,WHMS,WHMSLIVE,WOUA,WAPI,WRRA,WOUAFIX,WRV1,WRA1,WMSG1,WAPC1,WAPC2,WPROC1,WPROC2,WAPC2F,WAPC2FLIVE,WCBK1,WEGOV1,WACP3,WACP3R,WREAD1` | Issue CSV | none | 2026-05-19 | `rg` | high | WACP3R 后记录补偿拉取 `PASS_WITH_FINDINGS`、回调证据未持久化 finding;WREAD1 自动汇总为 `PAUSED_MANUAL_CONFIRMATION`,等待真机确认,仍禁止跳过 WADM1/WARCH1 |
| `WECOM-API-CATALOG` | `docs/企微API能力目录.md` | 集成事实源 | 企微 API 能力目录和 Secret 矩阵事实源 | `CURRENT` | product/backend/qa | `WAPI,WRRA,WAPC1,WAPC2,WPROC1,WPROC2,WAPC2F,WAPC2FLIVE,WCBK1,WEGOV1` | 官方企业微信开发者中心链接/Issue CSV | none | 2026-05-19 | `rg` | high | 企微开发先读;只保存摘要、链接和项目决策,不保存 Secret、token、完整 userid、手机号、邮箱或原始 payload;WEGOV1 后记录无完整 sandbox 假设、自建留存策略和后续 POC 顺序 | | `WECOM-API-CATALOG` | `docs/企微API能力目录.md` | 集成事实源 | 企微 API 能力目录和 Secret 矩阵事实源 | `CURRENT` | product/backend/qa | `WAPI,WRRA,WAPC1,WAPC2,WPROC1,WPROC2,WAPC2F,WAPC2FLIVE,WCBK1,WEGOV1` | 官方企业微信开发者中心链接/Issue CSV | none | 2026-05-19 | `rg` | high | 企微开发先读;只保存摘要、链接和项目决策,不保存 Secret、token、完整 userid、手机号、邮箱或原始 payload;WEGOV1 后记录无完整 sandbox 假设、自建留存策略和后续 POC 顺序 |
| `DELIVERY` | `docs/开发交付与变更管理规约.md` | 开发治理 | 开发交付事实源 | `CURRENT` | dev/qa | `GOV8,WDRG,WRRA,WAPC1,WPROC1,WPROC2,WCBK1,WEGOV1` | `rg` | none | 2026-05-19 | `rg` | high | Issue、review、commit、交付规则;WEGOV1 后采用自动化优先、受控测试域、自建留存说明和 WACP3/WREAD1/WADM1/WARCH1 前置门禁 | | `DELIVERY` | `docs/开发交付与变更管理规约.md` | 开发治理 | 开发交付事实源 | `CURRENT` | dev/qa | `GOV8,WDRG,WRRA,WAPC1,WPROC1,WPROC2,WCBK1,WEGOV1` | `rg` | none | 2026-05-19 | `rg` | high | Issue、review、commit、交付规则;WEGOV1 后采用自动化优先、受控测试域、自建留存说明和 WACP3/WREAD1/WADM1/WARCH1 前置门禁 |
| `QA-GATE` | `docs/测试规约与回归门禁.md` | 测试验收 | 测试和回归门禁事实源 | `CURRENT` | qa | `QAF8,GOV8,WPROC1,WPROC2,WAPC2FLIVE,WCBK1,WEGOV1` | 回归脚本/Playwright | none | 2026-05-19 | `rg` | high | 测试计划、DONE 门禁、企微三层门禁、自动化优先、非自审、无完整 sandbox 假设、WREAD1 真机可读性和 WARCH1 留存验收门禁 | | `QA-GATE` | `docs/测试规约与回归门禁.md` | 测试验收 | 测试和回归门禁事实源 | `CURRENT` | qa | `QAF8,GOV8,WPROC1,WPROC2,WAPC2FLIVE,WCBK1,WEGOV1` | 回归脚本/Playwright | none | 2026-05-19 | `rg` | high | 测试计划、DONE 门禁、企微三层门禁、自动化优先、非自审、无完整 sandbox 假设、WREAD1 真机可读性和 WARCH1 留存验收门禁 |
| `AGENT-PRACTICE` | `docs/Agent开发交付实践.md` | Agent 治理 | Agent 协作事实源 | `CURRENT` | dev | `GOV8,DG8` | Skill 校验 | none | 2026-05-13 | `rg` | medium | Agent 协作开发实践和设计主参考层级 | | `AGENT-PRACTICE` | `docs/Agent开发交付实践.md` | Agent 治理 | Agent 协作事实源 | `CURRENT` | dev | `GOV8,DG8` | Skill 校验 | none | 2026-05-13 | `rg` | medium | Agent 协作开发实践和设计主参考层级 |
| `AGENTS` | `AGENTS.md` | 执行硬约束 | 项目硬规则 | `CURRENT` | lead | `GOV8` | 人工核对 | none | 2026-05-13 | `rg` | high | 不可违反约束 | | `AGENTS` | `AGENTS.md` | 执行硬约束 | 项目硬规则 | `CURRENT` | lead | `GOV8` | 人工核对 | none | 2026-05-13 | `rg` | high | 不可违反约束 |
| `SKILL-HYB` | `.agents/skills/hyb-oa-delivery/SKILL.md` | Skill | 项目交付 Skill | `CURRENT` | dev | `GOV8,DG8,WFDG,WDRG,WCP1,WCPC,WHMS,WHMSLIVE,WOUA,WAPI,WRRA,WRV1,WRA1,WMSG1,WAPC1,WAPC2,WPROC1,WPROC2,WAPC2F,WAPC2FLIVE,WCBK1,WEGOV1,WACP3,WACP3R` | quick_validate | none | 2026-05-19 | `quick_validate.py` | medium | 每轮交付操作入口;企微/OA 后续先读 API 能力目录、Secret 矩阵、路线图、WEGOV1 自建留存边界、WCBK1 真实回调证据和 WACP3R 补偿拉取 findings,不得跳过 WREAD1/WADM1/WARCH1 直接做考勤审批镜像 | | `SKILL-HYB` | `.agents/skills/hyb-oa-delivery/SKILL.md` | Skill | 项目交付 Skill | `CURRENT` | dev | `GOV8,DG8,WFDG,WDRG,WCP1,WCPC,WHMS,WHMSLIVE,WOUA,WAPI,WRRA,WRV1,WRA1,WMSG1,WAPC1,WAPC2,WPROC1,WPROC2,WAPC2F,WAPC2FLIVE,WCBK1,WEGOV1,WACP3,WACP3R,WREAD1` | quick_validate | none | 2026-05-19 | `quick_validate.py` | medium | 每轮交付操作入口;企微/OA 后续先读 API 能力目录、Secret 矩阵、路线图、WEGOV1 自建留存边界、WACP3R 补偿拉取 findings 和 WREAD1 自动汇总状态,不得跳过人工可读性确认、WADM1/WARCH1 直接做考勤审批镜像 |
| `REQ-WF` | `docs/需求-统一审批中心规约.md` | 需求事实源 | 审批模块规约 | `CURRENT` | product/backend | `AP6,WF8,WFOC,DBWFOC,WFDG,WOUA,WAPI,WRRA,WRV1,WRA1,WMSG1,WAPC1,WAPC2,WPROC1,WPROC2,WAPC2F,WAPC2FLIVE,WCBK1,WEGOV1,WACP3,WACP3R` | workflow 回归 | none | 2026-05-19 | `rg` | high | OA 主办审批、企微主办 HR/考勤审批、OA 自建留存、真实回调事件证据、WACP3R 补偿拉取 findings、WREAD1/WADM1/WARCH1 前置和 OA 应急接管边界 | | `REQ-WF` | `docs/需求-统一审批中心规约.md` | 需求事实源 | 审批模块规约 | `CURRENT` | product/backend | `AP6,WF8,WFOC,DBWFOC,WFDG,WOUA,WAPI,WRRA,WRV1,WRA1,WMSG1,WAPC1,WAPC2,WPROC1,WPROC2,WAPC2F,WAPC2FLIVE,WCBK1,WEGOV1,WACP3,WACP3R,WREAD1` | workflow 回归 | none | 2026-05-19 | `rg` | high | OA 主办审批、企微主办 HR/考勤审批、OA 自建留存、真实回调事件证据、WACP3R 补偿拉取 findings、WREAD1 自动汇总和人工确认前置、WADM1/WARCH1 前置及 OA 应急接管边界 |
| `REQ-OCR` | `docs/需求-报销发票OCR规约.md` | 需求事实源 | OCR 模块规约 | `CURRENT` | product/backend | `RI6,OCR8,BIZ8` | OCR 回归 | none | 2026-05-13 | `rg` | high | OCR 和发票边界 | | `REQ-OCR` | `docs/需求-报销发票OCR规约.md` | 需求事实源 | OCR 模块规约 | `CURRENT` | product/backend | `RI6,OCR8,BIZ8` | OCR 回归 | none | 2026-05-13 | `rg` | high | OCR 和发票边界 |
| `RULE-REIMB` | `docs/报销发票附件规则.md` | 需求事实源 | 报销发票附件规则 | `CURRENT` | product/finance | `RI6,BIZ8` | 报销回归 | none | 2026-05-13 | `rg` | high | 报销、发票、附件细则 | | `RULE-REIMB` | `docs/报销发票附件规则.md` | 需求事实源 | 报销发票附件规则 | `CURRENT` | product/finance | `RI6,BIZ8` | 报销回归 | none | 2026-05-13 | `rg` | high | 报销、发票、附件细则 |
| `REQ-WECOM` | `docs/需求-企业微信基础设施规约.md` | 需求事实源 | 企业微信边界规约 | `CURRENT` | product/backend | `R7E,ORG8,WF8,ATTV1,HR-WECOM-DIRECTORY-V1,WFOC,DBWFOC,WFDG,WCP1,WCPC,WHMS,WHMSLIVE,WOUA,WAPI,WRRA,WRV1,WRA1,WMSG1,WAPC1,WAPC2,WPROC1,WPROC2,WAPC2F,WAPC2FLIVE,WCBK1,WEGOV1` | live smoke/真机反馈/Issue CSV | none | 2026-05-19 | `rg` | high | 企微优先承担 HR/考勤员工日常审批和人事/打卡能力;WEGOV1 固定 OA 自建留存和受控测试域;采购、合同、项目、报销和财务继续 OA 主办 | | `REQ-WECOM` | `docs/需求-企业微信基础设施规约.md` | 需求事实源 | 企业微信边界规约 | `CURRENT` | product/backend | `R7E,ORG8,WF8,ATTV1,HR-WECOM-DIRECTORY-V1,WFOC,DBWFOC,WFDG,WCP1,WCPC,WHMS,WHMSLIVE,WOUA,WAPI,WRRA,WRV1,WRA1,WMSG1,WAPC1,WAPC2,WPROC1,WPROC2,WAPC2F,WAPC2FLIVE,WCBK1,WEGOV1` | live smoke/真机反馈/Issue CSV | none | 2026-05-19 | `rg` | high | 企微优先承担 HR/考勤员工日常审批和人事/打卡能力;WEGOV1 固定 OA 自建留存和受控测试域;采购、合同、项目、报销和财务继续 OA 主办 |
@@ -126,14 +127,14 @@
| `docs/文档事实源状态矩阵.md` | 当前事实源 | 文档分类和恢复规则 | | `docs/文档事实源状态矩阵.md` | 当前事实源 | 文档分类和恢复规则 |
| `docs/海音达OA-MVP需求规格说明书.md` | 当前事实源 | PRD 主事实源 | | `docs/海音达OA-MVP需求规格说明书.md` | 当前事实源 | PRD 主事实源 |
| `docs/产品设计规则.md` | 当前事实源 | PC、移动、企业微信设计规则;DG8 主 UI/布局参考层级 | | `docs/产品设计规则.md` | 当前事实源 | PC、移动、企业微信设计规则;DG8 主 UI/布局参考层级 |
| `docs/企微优先开发路线图.md` | 当前事实源 | WEGOV1 后企微优先 HR/考勤改造阶段顺序、四域边界、API/Secret 口径、运行优先、审批只读 POC、自动样件、回调、补偿拉取、可读性门禁、自建留存、模型设计和禁止跳阶段 | | `docs/企微优先开发路线图.md` | 当前事实源 | WEGOV1 后企微优先 HR/考勤改造阶段顺序、四域边界、API/Secret 口径、运行优先、审批只读 POC、自动样件、回调、补偿拉取、WREAD1 可读性自动汇总、人工确认门禁、自建留存、模型设计和禁止跳阶段 |
| `docs/企微API能力目录.md` | 当前事实源 | WEGOV1 后企微官方 API、Secret 矩阵、权限/可信 IP/回调模板、读写边界、无完整 sandbox 假设、运行优先、审批只读 POC 和后续 POC 决策 | | `docs/企微API能力目录.md` | 当前事实源 | WEGOV1 后企微官方 API、Secret 矩阵、权限/可信 IP/回调模板、读写边界、无完整 sandbox 假设、运行优先、审批只读 POC 和后续 POC 决策 |
| `docs/产品口径.md` | 当前事实源 | 产品边界和业务规则 | | `docs/产品口径.md` | 当前事实源 | 产品边界和业务规则 |
| `docs/需求-统一审批中心规约.md` | 当前事实源 | 审批、催办、超时、效率摘要 | | `docs/需求-统一审批中心规约.md` | 当前事实源 | 审批、催办、超时、效率摘要 |
| `docs/报销发票附件规则.md` | 当前事实源 | 报销、发票、附件细则 | | `docs/报销发票附件规则.md` | 当前事实源 | 报销、发票、附件细则 |
| `docs/需求-报销发票OCR规约.md` | 当前事实源 | OCR 和报销模块规约 | | `docs/需求-报销发票OCR规约.md` | 当前事实源 | OCR 和报销模块规约 |
| `docs/需求-企业微信基础设施规约.md` | 当前事实源 | 企业微信入口、身份、消息、deeplink、企微主办域、OA 自建留存和应急兜底 | | `docs/需求-企业微信基础设施规约.md` | 当前事实源 | 企业微信入口、身份、消息、deeplink、企微主办域、OA 自建留存和应急兜底 |
| `docs/需求-请假考勤修正规约.md` | 当前事实源 | 请假、补卡、考勤修正、WAPC2/WAPC2FLIVE/WCBK1/WACP3R 证据、WEGOV1 自建留存路线和 WREAD1/WADM1/WARCH1 后续门禁 | | `docs/需求-请假考勤修正规约.md` | 当前事实源 | 请假、补卡、考勤修正、WAPC2/WAPC2FLIVE/WCBK1/WACP3R/WREAD1 自动汇总证据、WEGOV1 自建留存路线和 WREAD1 人工确认/WADM1/WARCH1 后续门禁 |
| `docs/需求-采购费用审批规约.md` | 当前事实源 | 采购和费用审批 | | `docs/需求-采购费用审批规约.md` | 当前事实源 | 采购和费用审批 |
| `docs/需求-合同附件归档规约.md` | 当前事实源 | 合同台账和附件归档 | | `docs/需求-合同附件归档规约.md` | 当前事实源 | 合同台账和附件归档 |
| `docs/需求-项目轻管理规约.md` | 当前事实源 | 项目轻管理 | | `docs/需求-项目轻管理规约.md` | 当前事实源 | 项目轻管理 |
+3 -2
查看文件
@@ -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 -> P2AF -> P2-FIN-LIVE-DECISION -> P2-FIN-LIVE -> PRELAUNCH -> CASHIER-VOUCHER-V1 -> LAUNCH-DEPLOY-FINANCE -> FIN-PROD-ENABLE -> ACCOUNTING-HANDOFF-V1 -> SYSTEM-COMPLETE-GATE -> LAUNCH-OPS-V1 -> REQ-COMPLETE-AUDIT -> ATTV1 -> LAUNCH-AUTO-OPS-V1 -> AUTO-REMAINING-CLOSEOUT -> HR-WECOM-DIRECTORY-V1 -> HRWD-CONTACT-LIVE-TEST -> WCFB -> WFOC -> DBWFOC -> WFDG -> WDRG -> WCP1 -> WCPC -> WHMS -> WHMSLIVE -> WOUA -> WAPI -> WRRA -> WOUA-FIX -> WRV1 -> WRA1 -> WMSG1 -> WAPC1 -> WAPC2 -> WPROC1 -> WPROC2 -> WAPC2F dry-run -> WAPC2FLIVE -> WCBK1 -> WEGOV1 -> WACP3/WACP3R。 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 -> P2-FIN-LIVE-DECISION -> P2-FIN-LIVE -> PRELAUNCH -> CASHIER-VOUCHER-V1 -> LAUNCH-DEPLOY-FINANCE -> FIN-PROD-ENABLE -> ACCOUNTING-HANDOFF-V1 -> SYSTEM-COMPLETE-GATE -> LAUNCH-OPS-V1 -> REQ-COMPLETE-AUDIT -> ATTV1 -> LAUNCH-AUTO-OPS-V1 -> AUTO-REMAINING-CLOSEOUT -> HR-WECOM-DIRECTORY-V1 -> HRWD-CONTACT-LIVE-TEST -> WCFB -> WFOC -> DBWFOC -> WFDG -> WDRG -> WCP1 -> WCPC -> WHMS -> WHMSLIVE -> WOUA -> WAPI -> WRRA -> WOUA-FIX -> WRV1 -> WRA1 -> WMSG1 -> WAPC1 -> WAPC2 -> WPROC1 -> WPROC2 -> WAPC2F dry-run -> WAPC2FLIVE -> WCBK1 -> WEGOV1 -> WACP3/WACP3R -> WREAD1 自动汇总。
- R6、PC6、PA6、FL6、AP6、RI6、LP6 已完成并部署。 - R6、PC6、PA6、FL6、AP6、RI6、LP6 已完成并部署。
- ME6 自动化、公网视觉矩阵和 `ME6-3` 代表性企业微信真机门禁已完成。 - ME6 自动化、公网视觉矩阵和 `ME6-3` 代表性企业微信真机门禁已完成。
@@ -143,6 +143,7 @@ R6 -> PC6 -> PA6 -> FL6 -> AP6 -> RI6 -> LP6 -> ME6/QG6 -> DG7/MRF7 -> MIA7 -> M
| `WCBK1` | `issues/2026-05-19_14-11-20-wecom-approval-callback-readonly-poc-v1.csv` | 当前回调只读 POC:真实 URL 和事件已确认 | 新增只读 `GET/POST /api/wecom/approval/callback`、`check:wecom-approval-callback-poc` 和 `wecom-callback` process gate;远端报告 `PASS_CALLBACK_URL_VERIFIED` 后,`SMOKE101` 处理现有 OA-POC 测试单并复跑达到 `PASS_CALLBACK_EVENT_CONFIRMED` | 不改 schema/UI;不建镜像表;不写 OA/企微业务数据;不创建新模板或审批单;后续仍需 WEGOV1/WACP3/WREAD1/WADM1/WARCH1 | | `WCBK1` | `issues/2026-05-19_14-11-20-wecom-approval-callback-readonly-poc-v1.csv` | 当前回调只读 POC:真实 URL 和事件已确认 | 新增只读 `GET/POST /api/wecom/approval/callback`、`check:wecom-approval-callback-poc` 和 `wecom-callback` process gate;远端报告 `PASS_CALLBACK_URL_VERIFIED` 后,`SMOKE101` 处理现有 OA-POC 测试单并复跑达到 `PASS_CALLBACK_EVENT_CONFIRMED` | 不改 schema/UI;不建镜像表;不写 OA/企微业务数据;不创建新模板或审批单;后续仍需 WEGOV1/WACP3/WREAD1/WADM1/WARCH1 |
| `WEGOV1` | `issues/2026-05-19_18-17-44-wecom-governance-owned-oa-archive-route-v1.csv` | 当前路线治理:企微主办 + OA 自建留存 | 固定 HR/考勤企微主办、财务经营和高敏业务 OA 主办、OA 留存关键企微历史、企业微信受控测试域、自动化与人工边界;后续新增 WREAD1 和 WARCH1 门禁 | docs-only;不改业务代码/API/schema/UI;不部署;不写 OA/企微数据;不进入深度镜像 | | `WEGOV1` | `issues/2026-05-19_18-17-44-wecom-governance-owned-oa-archive-route-v1.csv` | 当前路线治理:企微主办 + OA 自建留存 | 固定 HR/考勤企微主办、财务经营和高敏业务 OA 主办、OA 留存关键企微历史、企业微信受控测试域、自动化与人工边界;后续新增 WREAD1 和 WARCH1 门禁 | docs-only;不改业务代码/API/schema/UI;不部署;不写 OA/企微数据;不进入深度镜像 |
| `WACP3/WACP3R` | `issues/2026-05-19_19-03-06-wecom-approval-compensation-pull-poc-v1.csv`、`issues/2026-05-19_19-42-22-wecom-approval-compensation-config-rerun-v1.csv` | 当前补偿拉取 POC:runner 已部署并自动发现模板复跑 | 新增 `check:wecom-approval-compensation-poc`,固定 `WECOM_AGENT_SECRET` 只读调用 `getapprovalinfo` 和 `getapprovaldetail`;WACP3R 按 `SMOKE002` creator 自动发现 OA-POC 请假/补卡模板,远端报告为 `PASS_WITH_FINDINGS`,finding 为回调证据文件未持久化 | 不新增公开 API/schema/UI;不写 OA/企微数据;不保存完整模板 ID;不无过滤拉取高风险审批详情;不进入深度镜像 | | `WACP3/WACP3R` | `issues/2026-05-19_19-03-06-wecom-approval-compensation-pull-poc-v1.csv`、`issues/2026-05-19_19-42-22-wecom-approval-compensation-config-rerun-v1.csv` | 当前补偿拉取 POC:runner 已部署并自动发现模板复跑 | 新增 `check:wecom-approval-compensation-poc`,固定 `WECOM_AGENT_SECRET` 只读调用 `getapprovalinfo` 和 `getapprovaldetail`;WACP3R 按 `SMOKE002` creator 自动发现 OA-POC 请假/补卡模板,远端报告为 `PASS_WITH_FINDINGS`,finding 为回调证据文件未持久化 | 不新增公开 API/schema/UI;不写 OA/企微数据;不保存完整模板 ID;不无过滤拉取高风险审批详情;不进入深度镜像 |
| `WREAD1` | `issues/2026-05-19_20-39-40-wecom-approval-readability-gate-v1.csv` | 当前可读性门禁:自动汇总已部署,等待真机确认 | 新增 `check:wecom-approval-readability-gate`,只读汇总请假/补卡 OA-POC 证据;公网报告 `/app/artifacts/qa-flow/wecom-approval-readability-gate-20260519T125532Z/summary.json` 为 `PAUSED_MANUAL_CONFIRMATION` | 不新增公开 API/schema/UI;不写 OA/企微数据;不新建模板或审批单;未完成人工确认前不得进入 WADM1 或深度镜像 |
旧 Issue CSV 不删除、不批量回写、不移动到归档目录。若新治理流替代旧合同,只在新 CSV 的 `Notes` 或本索引说明 supersedes,旧文件保留历史审计语义。 旧 Issue CSV 不删除、不批量回写、不移动到归档目录。若新治理流替代旧合同,只在新 CSV 的 `Notes` 或本索引说明 supersedes,旧文件保留历史审计语义。
@@ -165,7 +166,7 @@ R6 -> PC6 -> PA6 -> FL6 -> AP6 -> RI6 -> LP6 -> ME6/QG6 -> DG7/MRF7 -> MIA7 -> M
| [海音达OA-MVP需求规格说明书](./海音达OA-MVP需求规格说明书.md) | PRD 主事实源;WAPC2FLIVE 后包含企微审批配置、只读 POC、工程化前置、process gate、请假/补卡样例和深度镜像前置口径 | | [海音达OA-MVP需求规格说明书](./海音达OA-MVP需求规格说明书.md) | PRD 主事实源;WAPC2FLIVE 后包含企微审批配置、只读 POC、工程化前置、process gate、请假/补卡样例和深度镜像前置口径 |
| [文档事实源状态矩阵](./文档事实源状态矩阵.md) | 文档分类、删除恢复和冲突规则 | | [文档事实源状态矩阵](./文档事实源状态矩阵.md) | 文档分类、删除恢复和冲突规则 |
| [产品设计规则](./产品设计规则.md) | PC、移动、企业微信 WebView 设计事实源;DG8 后飞书工作台/飞书审批为唯一主 UI/布局参考;DG9 后按页面分层和样板选型 | | [产品设计规则](./产品设计规则.md) | PC、移动、企业微信 WebView 设计事实源;DG8 后飞书工作台/飞书审批为唯一主 UI/布局参考;DG9 后按页面分层和样板选型 |
| [企微优先开发路线图](./企微优先开发路线图.md) | WDRG/WCP1/WCPC/WHMS/WHMSLIVE/WOUA/WAPI/WRRA/WOUA-FIX/WRV1/WRA1/WMSG1/WAPC1/WAPC2/WPROC1/WPROC2/WAPC2F/WAPC2FLIVE/WCBK1/WEGOV1/WACP3/WACP3R 后企微优先 HR/考勤改造的阶段顺序、准入/退出门禁、并行规则、禁止跳阶段、API/Secret 口径、运行优先、消息实发、审批配置、只读 POC、process gate、自动样件 live 证据、真实回调事件证据、自建留存、补偿拉取、可读性和模型设计事实源 | | [企微优先开发路线图](./企微优先开发路线图.md) | WDRG/WCP1/WCPC/WHMS/WHMSLIVE/WOUA/WAPI/WRRA/WOUA-FIX/WRV1/WRA1/WMSG1/WAPC1/WAPC2/WPROC1/WPROC2/WAPC2F/WAPC2FLIVE/WCBK1/WEGOV1/WACP3/WACP3R/WREAD1 后企微优先 HR/考勤改造的阶段顺序、准入/退出门禁、并行规则、禁止跳阶段、API/Secret 口径、运行优先、消息实发、审批配置、只读 POC、process gate、自动样件 live 证据、真实回调事件证据、自建留存、补偿拉取、可读性自动汇总和模型设计事实源 |
| [企微 API 能力目录](./企微API能力目录.md) | WAPI/WAPC1/WAPC2/WPROC1/WPROC2/WAPC2F/WAPC2FLIVE/WCBK1/WEGOV1/WACP3/WACP3R 后企微官方 API、Secret 矩阵、权限/可信 IP/回调模板、读写边界、运行优先、审批配置、只读 POC、WAPC2F dry-run、WAPC2FLIVE live 创建、WCBK1 回调真实事件 POC、无完整 sandbox 假设、补偿拉取自动发现和 WREAD1/WADM1/WARCH1 决策事实源 | | [企微 API 能力目录](./企微API能力目录.md) | WAPI/WAPC1/WAPC2/WPROC1/WPROC2/WAPC2F/WAPC2FLIVE/WCBK1/WEGOV1/WACP3/WACP3R 后企微官方 API、Secret 矩阵、权限/可信 IP/回调模板、读写边界、运行优先、审批配置、只读 POC、WAPC2F dry-run、WAPC2FLIVE live 创建、WCBK1 回调真实事件 POC、无完整 sandbox 假设、补偿拉取自动发现和 WREAD1/WADM1/WARCH1 决策事实源 |
| [产品口径](./产品口径.md) | 已拍板产品边界和业务规则;WEGOV1 后包含混合主办、自建留存、受控测试域、后续阶段顺序和深度镜像前置口径 | | [产品口径](./产品口径.md) | 已拍板产品边界和业务规则;WEGOV1 后包含混合主办、自建留存、受控测试域、后续阶段顺序和深度镜像前置口径 |
| [需求 - 统一审批中心规约](./需求-统一审批中心规约.md) | OA 主办审批、企微主办 HR/考勤员工日常审批镜像、催办、超时提醒、审批效率 | | [需求 - 统一审批中心规约](./需求-统一审批中心规约.md) | OA 主办审批、企微主办 HR/考勤员工日常审批镜像、催办、超时提醒、审批效率 |
+2 -2
查看文件
@@ -1,6 +1,6 @@
# hyb-oa 管理员手册(10 人以下团队版) # hyb-oa 管理员手册(10 人以下团队版)
**当前状态(2026-05-19 / WACP3R)**:R7E、PRELAUNCH、财务闭环、ATTV1、HR-WECOM-DIRECTORY-V1、WAPI、WOUA-FIX、WRV1/WRA1、WMSG1、WAPC1/WAPC2、WPROC1/WPROC2、WAPC2F dry-run、WAPC2FLIVE、WCBK1 和 WEGOV1 均已收口;SYSTEM-COMPLETE-GATE 结论为 `GO_TO_USE`。WAPI 后固定企微 API 能力目录和 Secret 矩阵:通讯录使用 `WECOM_CONTACT_SECRET`,人事助手/打卡/消息/OAuth/审批读取使用 `WECOM_AGENT_SECRET`,运行时代码不得靠多个 Secret 试错。WAPC2FLIVE 已创建 OA-POC 请假/补卡测试模板和低风险测试审批单,报告 `/app/artifacts/qa-flow/wecom-approval-fixtures-20260519T053400Z/summary.json` 为 `PASS_FIXTURE_CREATED`,自动复跑 WAPC2 为 `PASS_APPROVAL_POC`;测试模板和审批记录保留审计,不自动删除。WCBK1 已部署只读回调端点和 runner,远端完成回调 URL 验证并收到一条由 `SMOKE101` 同意现有 OA-POC 补卡测试单触发的真实审批状态事件,报告 `/app/artifacts/qa-flow/wecom-approval-callback-poc-20260519T092648Z/summary.json` 为 `PASS_CALLBACK_EVENT_CONFIRMED`。WACP3R 已新增自动发现模板 ID 路径并公网只读复跑,报告 `/app/artifacts/qa-flow/wecom-approval-compensation-poc-20260519T115944Z/summary.json` 为 `PASS_WITH_FINDINGS`:远端 `WECOM_AGENT_SECRET` 可用,已按 `SMOKE002` creator 自动发现请假/补卡 OA-POC 模板并拉取审批详情;finding 是回调证据文件未持久化,需在 WADM1/WARCH1 设计自建留存时解决。后续仍需 WREAD1 审批可读性门禁、WADM1 自建留存/镜像数据模型设计和 WARCH1 最小历史留存,不能直接进入 `wecom_attendance_approval_mirror_v1`。采购、费用、合同、项目、报销、付款、AP、代账交接和高敏附件继续 OA 主办,企业微信只做脱敏提醒和 OA deeplink。公网 `payment_batch_v1=force_on`,`erp_sync=force_off`。公司当前无 ERP/独立财务系统,且 ERP 未来不接入 OA;财务闭环以 OA 内审批、台账、付款批次、银行对公付款回单和月度代账交接为准。 **当前状态(2026-05-19 / WREAD1 自动汇总)**:R7E、PRELAUNCH、财务闭环、ATTV1、HR-WECOM-DIRECTORY-V1、WAPI、WOUA-FIX、WRV1/WRA1、WMSG1、WAPC1/WAPC2、WPROC1/WPROC2、WAPC2F dry-run、WAPC2FLIVE、WCBK1、WEGOV1 和 WACP3/WACP3R 均已收口;SYSTEM-COMPLETE-GATE 结论为 `GO_TO_USE`。WAPI 后固定企微 API 能力目录和 Secret 矩阵:通讯录使用 `WECOM_CONTACT_SECRET`,人事助手/打卡/消息/OAuth/审批读取使用 `WECOM_AGENT_SECRET`,运行时代码不得靠多个 Secret 试错。WAPC2FLIVE 已创建 OA-POC 请假/补卡测试模板和低风险测试审批单,报告 `/app/artifacts/qa-flow/wecom-approval-fixtures-20260519T053400Z/summary.json` 为 `PASS_FIXTURE_CREATED`,自动复跑 WAPC2 为 `PASS_APPROVAL_POC`;测试模板和审批记录保留审计,不自动删除。WCBK1 已部署只读回调端点和 runner,远端完成回调 URL 验证并收到一条由 `SMOKE101` 同意现有 OA-POC 补卡测试单触发的真实审批状态事件,报告 `/app/artifacts/qa-flow/wecom-approval-callback-poc-20260519T092648Z/summary.json` 为 `PASS_CALLBACK_EVENT_CONFIRMED`。WACP3R 已新增自动发现模板 ID 路径并公网只读复跑,报告 `/app/artifacts/qa-flow/wecom-approval-compensation-poc-20260519T115944Z/summary.json` 为 `PASS_WITH_FINDINGS`;WREAD1 部署后又只读复跑补偿拉取 `/app/artifacts/qa-flow/wecom-approval-compensation-poc-20260519T125515Z/summary.json` 并生成可读性报告 `/app/artifacts/qa-flow/wecom-approval-readability-gate-20260519T125532Z/summary.json`,结论 `PAUSED_MANUAL_CONFIRMATION`。后续仍需审批人真机确认请假/补卡可读性和误点风险、WADM1 自建留存/镜像数据模型设计和 WARCH1 最小历史留存,不能直接进入 `wecom_attendance_approval_mirror_v1`。采购、费用、合同、项目、报销、付款、AP、代账交接和高敏附件继续 OA 主办,企业微信只做脱敏提醒和 OA deeplink。公网 `payment_batch_v1=force_on`,`erp_sync=force_off`。公司当前无 ERP/独立财务系统,且 ERP 未来不接入 OA;财务闭环以 OA 内审批、台账、付款批次、银行对公付款回单和月度代账交接为准。
## 1. 适用范围 ## 1. 适用范围
- 适用于 `small_team_internal` 工作区模式。 - 适用于 `small_team_internal` 工作区模式。
@@ -436,7 +436,7 @@ WAPC2 请假/补卡只读 POC 操作口径:
- `WECOM_ALLOWED_RETURN_HOSTS=oa.hyinda.com` - `WECOM_ALLOWED_RETURN_HOSTS=oa.hyinda.com`
5. 先让管理员用账号密码登录,在 `个人设置` 完成一次绑定,再测试企业微信工作台直达;已绑定用户从手机企业微信进入时应直接看到移动工作台。 5. 先让管理员用账号密码登录,在 `个人设置` 完成一次绑定,再测试企业微信工作台直达;已绑定用户从手机企业微信进入时应直接看到移动工作台。
6. 若启用 OA 同步企业微信打卡数据,在企业微信打卡 API「可调用接口的应用」中授权 `海音达工作台`。OA 默认可复用 `WECOM_AGENT_SECRET`;只有单独创建考勤同步应用时才额外配置 `WECOM_CHECKIN_SECRET`。 6. 若启用 OA 同步企业微信打卡数据,在企业微信打卡 API「可调用接口的应用」中授权 `海音达工作台`。OA 默认可复用 `WECOM_AGENT_SECRET`;只有单独创建考勤同步应用时才额外配置 `WECOM_CHECKIN_SECRET`。
7. HR/考勤员工日常审批后续配置企业微信原生审批模板作为主流程;WAPC2FLIVE 已自动生成请假/补卡 OA-POC 低风险样件并复跑只读 POC,WCBK1 已完成真实回调事件确认;后续仍需按 WEGOV1 顺序做 WACP3 补偿拉取、WREAD1 可读性门禁、WADM1 自建留存/镜像模型和 WARCH1 最小历史留存。外出/加班模板仍需后续单独配置或 POC。报销、财务、采购、合同和项目类 OA 主办审批仍使用企业微信消息提醒并回到 OA 页面完成。 7. HR/考勤员工日常审批后续配置企业微信原生审批模板作为主流程;WAPC2FLIVE 已自动生成请假/补卡 OA-POC 低风险样件并复跑只读 POC,WCBK1 已完成真实回调事件确认,WACP3R 已完成补偿拉取,WREAD1 自动汇总已暂停等待真机确认。后续仍需按 WEGOV1 顺序做 WREAD1 人工可读性结论、WADM1 自建留存/镜像模型和 WARCH1 最小历史留存。外出/加班模板仍需后续单独配置或 POC。报销、财务、采购、合同和项目类 OA 主办审批仍使用企业微信消息提醒并回到 OA 页面完成。
### 员工会看到的典型状态 ### 员工会看到的典型状态
- 未绑定:登录页会提示“当前企业微信账号尚未绑定”,回退到账号密码登录。 - 未绑定:登录页会提示“当前企业微信账号尚未绑定”,回退到账号密码登录。
+2 -2
查看文件
@@ -5,7 +5,7 @@
**状态**:模块级开发规约 **状态**:模块级开发规约
**上级事实源**:[海音达OA-MVP需求规格说明书](./海音达OA-MVP需求规格说明书.md) **上级事实源**:[海音达OA-MVP需求规格说明书](./海音达OA-MVP需求规格说明书.md)
本规约细化 `hyb-oa` 审批中心和企业微信原生审批镜像的产品边界、角色权限、状态模型、异常规则、留痕要求和验收场景。WFOC 起,审批按主办方分层;DBWFOC 后边界进一步收窄:报销、财务、采购、合同和项目类审批继续由 OA 主办;请假、补卡、外出、加班等 HR/考勤员工日常审批优先由企业微信主办,OA 同步按域镜像并保留自建历史留存、人工接管和恢复回写能力。WMSG1 已验证企业微信应用消息和 Deeplink 回流,WAPC1 后审批镜像前置工作进入配置准备清单阶段,WAPC2 已提供请假/补卡只读 POC runner,WAPC2FLIVE 已自动生成请假/补卡 OA-POC 样件并复跑通过。WCBK1 已完成只读回调 URL 验证和真实审批事件确认,结论为 `PASS_CALLBACK_EVENT_CONFIRMED`。WEGOV1 后明确先利用企微成熟审批和考勤能力,同时由 OA 保存关键历史和外部关联键。WACP3R 已自动发现 OA-POC 请假/补卡模板并公网复跑到 `PASS_WITH_FINDINGS`:补偿拉取和详情读取可用,finding 是回调证据文件未持久化。后续仍必须完成 WREAD1 审批可读性与误审批防护门禁、WADM1 自建留存/镜像数据模型设计和 WARCH1 最小历史留存,不能直接放行镜像实现。后续审批相关开发必须先从本规约拆分 Issue CSV,再进入技术方案和代码实现。 本规约细化 `hyb-oa` 审批中心和企业微信原生审批镜像的产品边界、角色权限、状态模型、异常规则、留痕要求和验收场景。WFOC 起,审批按主办方分层;DBWFOC 后边界进一步收窄:报销、财务、采购、合同和项目类审批继续由 OA 主办;请假、补卡、外出、加班等 HR/考勤员工日常审批优先由企业微信主办,OA 同步按域镜像并保留自建历史留存、人工接管和恢复回写能力。WMSG1 已验证企业微信应用消息和 Deeplink 回流,WAPC1 后审批镜像前置工作进入配置准备清单阶段,WAPC2 已提供请假/补卡只读 POC runner,WAPC2FLIVE 已自动生成请假/补卡 OA-POC 样件并复跑通过。WCBK1 已完成只读回调 URL 验证和真实审批事件确认,结论为 `PASS_CALLBACK_EVENT_CONFIRMED`。WEGOV1 后明确先利用企微成熟审批和考勤能力,同时由 OA 保存关键历史和外部关联键。WACP3R 已自动发现 OA-POC 请假/补卡模板并公网复跑到 `PASS_WITH_FINDINGS`:补偿拉取和详情读取可用,finding 是回调证据文件未持久化。WREAD1 已新增只读可读性门禁 runner 并公网汇总到 `PAUSED_MANUAL_CONFIRMATION`,报告 `/app/artifacts/qa-flow/wecom-approval-readability-gate-20260519T125532Z/summary.json`;该状态只说明请假/补卡样例证据可用于真机检查,尚未形成模板级可读性结论。后续仍必须完成 WREAD1 人工确认、WADM1 自建留存/镜像数据模型设计和 WARCH1 最小历史留存,不能直接放行镜像实现。后续审批相关开发必须先从本规约拆分 Issue CSV,再进入技术方案和代码实现。
--- ---
@@ -417,7 +417,7 @@ WAPC2F 是 WAPC2 后的自动化优先补缺阶段,目标是尽量用 QA-only
- `WCBK1 回调只读 POC`:已新增只读 `GET/POST /api/wecom/approval/callback` 和 `npm run check:wecom-approval-callback-poc`。公网 synthetic 验签、解密和解析通过后,部署环境完成回调配置并达到 `PASS_CALLBACK_URL_VERIFIED`;`SMOKE101` 随后处理现有 OA-POC 补卡测试单触发真实事件,复跑结论 `PASS_CALLBACK_EVENT_CONFIRMED`,报告只保存脱敏事件证据。 - `WCBK1 回调只读 POC`:已新增只读 `GET/POST /api/wecom/approval/callback` 和 `npm run check:wecom-approval-callback-poc`。公网 synthetic 验签、解密和解析通过后,部署环境完成回调配置并达到 `PASS_CALLBACK_URL_VERIFIED`;`SMOKE101` 随后处理现有 OA-POC 补卡测试单触发真实事件,复跑结论 `PASS_CALLBACK_EVENT_CONFIRMED`,报告只保存脱敏事件证据。
- `WEGOV1 企微主办治理与自建留存路线修正`:固定企微主办域、OA 主办域、OA 留存域和 OA 应急域;明确企业微信没有完整 sandbox 假设,默认使用受控测试域和自动化 runner。 - `WEGOV1 企微主办治理与自建留存路线修正`:固定企微主办域、OA 主办域、OA 留存域和 OA 应急域;明确企业微信没有完整 sandbox 假设,默认使用受控测试域和自动化 runner。
- `WACP3 补偿拉取 POC`:已新增 `check:wecom-approval-compensation-poc` 和 `getapprovalinfo` 只读封装;WACP3R 公网复跑为 `PASS_WITH_FINDINGS`,已按 `SMOKE002` creator 自动发现请假/补卡 OA-POC 模板并拉取审批详情。finding 为回调证据文件未持久化,需在 WADM1/WARCH1 的自建留存模型中解决。 - `WACP3 补偿拉取 POC`:已新增 `check:wecom-approval-compensation-poc` 和 `getapprovalinfo` 只读封装;WACP3R 公网复跑为 `PASS_WITH_FINDINGS`,已按 `SMOKE002` creator 自动发现请假/补卡 OA-POC 模板并拉取审批详情。finding 为回调证据文件未持久化,需在 WADM1/WARCH1 的自建留存模型中解决。
- `WREAD1 审批可读性与误审批防护门禁`:真机确认审批消息卡片、审批列表和详情页是否足够清楚;若快捷同意/驳回存在高误操作风险,则要求模板调整或该模板回退 OA 主办。 - `WREAD1 审批可读性与误审批防护门禁`:已新增 `check:wecom-approval-readability-gate` 只读 runner,公网报告 `/app/artifacts/qa-flow/wecom-approval-readability-gate-20260519T125532Z/summary.json` 为 `PAUSED_MANUAL_CONFIRMATION`。下一步由审批人真机确认审批消息卡片、审批列表和详情页是否足够清楚;若快捷同意/驳回存在高误操作风险,则要求模板调整或该模板回退 OA 主办。
- `WADM1 自建留存/镜像数据模型设计`:先设计字段、状态机、幂等键、索引、隐私保留、审计和迁移回滚方案。 - `WADM1 自建留存/镜像数据模型设计`:先设计字段、状态机、幂等键、索引、隐私保留、审计和迁移回滚方案。
- `WARCH1 最小自建历史留存实现`:先只读保存企微审批、打卡和人事关键快照,保证企微不可用时可查看历史,再进入完整镜像增强。 - `WARCH1 最小自建历史留存实现`:先只读保存企微审批、打卡和人事关键快照,保证企微不可用时可查看历史,再进入完整镜像增强。
+3 -3
查看文件
@@ -7,7 +7,7 @@
**相关事实源**:[需求 - 统一审批中心规约](./需求-统一审批中心规约.md)、[需求 - 企业微信基础设施规约](./需求-企业微信基础设施规约.md) **相关事实源**:[需求 - 统一审批中心规约](./需求-统一审批中心规约.md)、[需求 - 企业微信基础设施规约](./需求-企业微信基础设施规约.md)
**体验总依据**:[产品设计规则基线](./产品设计规则.md) **体验总依据**:[产品设计规则基线](./产品设计规则.md)
本规约细化请假、补卡、外出、加班与考勤修正的产品要求。WFOC 起,企业微信作为 HR/考勤员工日常审批和打卡规则主操作端;OA 负责按域镜像、应急接管、恢复回写、查询导出和审计。WEGOV1 后进一步明确:企微成熟 HR/考勤功能优先上线,OA 不重造企业微信完整打卡/考勤/HR 产品,但必须做自建历史留存、审计、入口、消息和应急兜底。WAPC2 后,请假和补卡先通过 QA-only runner 只读验证模板详情与低风险 `sp_no` 审批详情;WAPC2FLIVE 已生成 OA-POC 样件并复跑通过;WCBK1 已完成真实回调 URL 验证和一条测试审批事件确认;WACP3R 已自动发现模板并证明补偿拉取和详情读取可用。后续还必须先完成 WREAD1 可读性与误审批防护门禁、WADM1 自建留存/镜像数据模型设计和 WARCH1 最小历史留存,这不代表审批镜像已实现。PC 和移动页面体验以产品设计规则基线为依据,不重复定义通用审批链和移动导航规则。 本规约细化请假、补卡、外出、加班与考勤修正的产品要求。WFOC 起,企业微信作为 HR/考勤员工日常审批和打卡规则主操作端;OA 负责按域镜像、应急接管、恢复回写、查询导出和审计。WEGOV1 后进一步明确:企微成熟 HR/考勤功能优先上线,OA 不重造企业微信完整打卡/考勤/HR 产品,但必须做自建历史留存、审计、入口、消息和应急兜底。WAPC2 后,请假和补卡先通过 QA-only runner 只读验证模板详情与低风险 `sp_no` 审批详情;WAPC2FLIVE 已生成 OA-POC 样件并复跑通过;WCBK1 已完成真实回调 URL 验证和一条测试审批事件确认;WACP3R 已自动发现模板并证明补偿拉取和详情读取可用。WREAD1 已部署只读可读性门禁 runner,公网报告 `/app/artifacts/qa-flow/wecom-approval-readability-gate-20260519T125532Z/summary.json` 为 `PAUSED_MANUAL_CONFIRMATION`,等待请假/补卡真机可读性和误点风险确认。后续还必须完成 WREAD1 人工结论、WADM1 自建留存/镜像数据模型设计和 WARCH1 最小历史留存,这不代表审批镜像已实现。PC 和移动页面体验以产品设计规则基线为依据,不重复定义通用审批链和移动导航规则。
--- ---
@@ -49,7 +49,7 @@ REQAUD 复核后,ATTV1 已完成统一入口实现,当前运行时完成度
| 请假对团队状态/日历的影响 | `DONE` | 已批准请假可作为团队状态和日历依据。 | | 请假对团队状态/日历的影响 | `DONE` | 已批准请假可作为团队状态和日历依据。 |
| 企业微信打卡作为原始采集 | `DONE` | 由企业微信侧承载定位、人脸、Wi-Fi、设备和防作弊能力;OA 不重造。 | | 企业微信打卡作为原始采集 | `DONE` | 由企业微信侧承载定位、人脸、Wi-Fi、设备和防作弊能力;OA 不重造。 |
| 企业微信打卡 API 同步 | `PARTIAL` | 已新增同步批次和企微打卡摘要表;有已授权调用打卡 API 的自建应用 Secret 和企微权限时同步,缺配置时记录 `SKIP`。`WECOM_CHECKIN_SECRET` 可作为单独考勤同步应用的显式覆盖;未配置时复用已授权工作台应用的 `WECOM_AGENT_SECRET`。 | | 企业微信打卡 API 同步 | `PARTIAL` | 已新增同步批次和企微打卡摘要表;有已授权调用打卡 API 的自建应用 Secret 和企微权限时同步,缺配置时记录 `SKIP`。`WECOM_CHECKIN_SECRET` 可作为单独考勤同步应用的显式覆盖;未配置时复用已授权工作台应用的 `WECOM_AGENT_SECRET`。 |
| 企微 HR/考勤审批镜像 | `POC_CHAIN_PARTIAL_READY` | WAPC2 已新增请假/补卡模板详情和低风险 `sp_no` 审批详情只读 POC;WAPC2FLIVE 已完成自动样件与 WAPC2 自动复跑;WCBK1 已完成真实回调 URL 验证和一条测试审批事件确认;WACP3R 已证明请假/补卡补偿拉取和详情读取可用,finding 为回调证据文件未持久化。后续 `wecom_attendance_approval_mirror_v1` 仍需外出/加班 POC 或处理决策、WREAD1 可读性门禁、WADM1 自建留存/镜像数据模型和 WARCH1 最小历史留存。 | | 企微 HR/考勤审批镜像 | `POC_CHAIN_PARTIAL_READY` | WAPC2 已新增请假/补卡模板详情和低风险 `sp_no` 审批详情只读 POC;WAPC2FLIVE 已完成自动样件与 WAPC2 自动复跑;WCBK1 已完成真实回调 URL 验证和一条测试审批事件确认;WACP3R 已证明请假/补卡补偿拉取和详情读取可用,finding 为回调证据文件未持久化;WREAD1 自动汇总已部署并停在 `PAUSED_MANUAL_CONFIRMATION`,等待模板级真机确认。后续 `wecom_attendance_approval_mirror_v1` 仍需外出/加班 POC 或处理决策、WREAD1 人工可读性结论、WADM1 自建留存/镜像数据模型和 WARCH1 最小历史留存。 |
| 员工补卡申请审批 | `DONE_AS_OA_FALLBACK` | 当前 OA 能提交补卡修正;WFOC 后正常入口迁往企微,OA 仅在应急模式开放并进入恢复回写。 | | 员工补卡申请审批 | `DONE_AS_OA_FALLBACK` | 当前 OA 能提交补卡修正;WFOC 后正常入口迁往企微,OA 仅在应急模式开放并进入恢复回写。 |
| 外出申请审批 | `DONE_AS_OA_FALLBACK` | 当前 OA 能提交外出修正;WFOC 后正常入口迁往企微,OA 仅在应急模式开放并进入恢复回写。 | | 外出申请审批 | `DONE_AS_OA_FALLBACK` | 当前 OA 能提交外出修正;WFOC 后正常入口迁往企微,OA 仅在应急模式开放并进入恢复回写。 |
| 加班申请审批 | `DONE_AS_OA_FALLBACK` | 当前 OA 能提交加班修正;WFOC 后正常入口迁往企微,OA 仅在应急模式开放并进入恢复回写。 | | 加班申请审批 | `DONE_AS_OA_FALLBACK` | 当前 OA 能提交加班修正;WFOC 后正常入口迁往企微,OA 仅在应急模式开放并进入恢复回写。 |
@@ -227,7 +227,7 @@ WFOC 后,正常审批规则优先以企业微信审批模板和打卡/假期
- 已完成 A0.7/A0.8/A0.9:`WAPC2F`、`WAPC2FLIVE`、`WAPC2 自动复跑` 和 `WCBK1 回调只读 POC` 已证明请假/补卡 OA-POC 样件、只读审批详情、真实回调 URL、验签/解密和一条真实事件脱敏留证。 - 已完成 A0.7/A0.8/A0.9:`WAPC2F`、`WAPC2FLIVE`、`WAPC2 自动复跑` 和 `WCBK1 回调只读 POC` 已证明请假/补卡 OA-POC 样件、只读审批详情、真实回调 URL、验签/解密和一条真实事件脱敏留证。
- 当前 A0.10:`WEGOV1 企微主办治理与自建留存路线修正`,固定企微主办域、OA 主办域、OA 留存域、OA 应急域、受控测试域和自动化/人工边界。 - 当前 A0.10:`WEGOV1 企微主办治理与自建留存路线修正`,固定企微主办域、OA 主办域、OA 留存域、OA 应急域、受控测试域和自动化/人工边界。
- 已完成 A0.11:`WACP3/WACP3R 补偿拉取 POC`,已证明按时间窗口获取审批单号并用 `getapprovaldetail` 拉详情;当前 finding 是 WCBK1 回调证据文件未持久化,需在 WADM1/WARCH1 的自建留存模型中解决。 - 已完成 A0.11:`WACP3/WACP3R 补偿拉取 POC`,已证明按时间窗口获取审批单号并用 `getapprovaldetail` 拉详情;当前 finding 是 WCBK1 回调证据文件未持久化,需在 WADM1/WARCH1 的自建留存模型中解决。
- 后续 A0.12:`WREAD1 审批可读性与误审批防护门禁`,真机确认请假、补卡、外出、加班在企业微信消息、列表和详情页中是否看得清,快捷同意/驳回是否容易误点。 - 当前 A0.12:`WREAD1 审批可读性与误审批防护门禁`,已新增只读 runner 并公网汇总请假/补卡样例证据到 `PAUSED_MANUAL_CONFIRMATION`;下一步真机确认请假、补卡在企业微信消息、列表和详情页中是否看得清,快捷同意/驳回是否容易误点。外出/加班仍需后续样例或处理决策。
- 后续 A0.13:`WADM1 自建留存/镜像数据模型设计`,先设计字段、状态机、幂等键、索引、隐私保留和迁移回滚。 - 后续 A0.13:`WADM1 自建留存/镜像数据模型设计`,先设计字段、状态机、幂等键、索引、隐私保留和迁移回滚。
- 后续 A0.14:`WARCH1 最小自建历史留存实现`,只读保存企微审批、打卡和人事关键快照,先满足企微不可用时可查看历史。 - 后续 A0.14:`WARCH1 最小自建历史留存实现`,只读保存企微审批、打卡和人事关键快照,先满足企微不可用时可查看历史。
- 后续 A1:`wecom_attendance_approval_mirror_v1`,同步企微请假、补卡、外出、加班审批、附件、历史和状态回调;该命名承接旧口径 `wecom_native_approval_mirror_v1`。WAPC2FLIVE 已生成请假/补卡 OA-POC 模板和低风险 `sp_no` 样例,WACP3R 已证明请假/补卡补偿拉取;正式开发前仍必须补齐外出/加班样例或处理决策、可读性门禁、自建留存模型、最小历史留存和必要打卡细项 POC。 - 后续 A1:`wecom_attendance_approval_mirror_v1`,同步企微请假、补卡、外出、加班审批、附件、历史和状态回调;该命名承接旧口径 `wecom_native_approval_mirror_v1`。WAPC2FLIVE 已生成请假/补卡 OA-POC 模板和低风险 `sp_no` 样例,WACP3R 已证明请假/补卡补偿拉取;正式开发前仍必须补齐外出/加班样例或处理决策、可读性门禁、自建留存模型、最小历史留存和必要打卡细项 POC。
@@ -0,0 +1,7 @@
ID,Title,Description,Acceptance,Test_Method,Tools,Dev_Status,Review1_Status,Regression_Status,Files,Dependencies,Notes
"WREAD1-0","门禁合同与边界冻结","建立 WREAD1 合同;范围锁定请假+补卡,优先使用现有 OA-POC 样例,不新建企微模板或审批单;本轮不写 OA/企微业务数据,不接新回调,不进入 wecom_attendance_approval_mirror_v1。","1) CSV 校验通过;2) 明确 WACP3R 后下一阶段为可读性与误审批防护门禁;3) 结论按模板输出;4) 缺现有样例时记录 PARTIAL_READABILITY_NEEDS_FRESH_FIXTURE 并回问;5) 不保存完整 template_id、sp_no、userid、表单值、附件、评论或原始 payload。","受影响流程:WACP3R -> WREAD1 可读性门禁 -> WADM1 准入;验证步骤:python3 /home/shunli/.codex/skills/plan/scripts/validate_issues_csv.py issues/2026-05-19_20-39-40-wecom-approval-readability-gate-v1.csv;node scripts/qa/check-process-contract.mjs --baseline --json。","python3|node|manual","DONE","DONE","DONE","issues/2026-05-19_20-39-40-wecom-approval-readability-gate-v1.csv","[]","固定边界:只读汇总和人工确认,不写 OA/企微数据,不创建模板/审批单。validate_issues_csv 与 process baseline 已通过;回滚/清理:删除本合同和事实源回写;无数据库迁移、无远端 env 修改、无 OA/企微写入。"
"WREAD1-1","只读证据汇总 runner","新增 QA-only runner check-wecom-approval-readability-gate,汇总 WAPC2FLIVE、WCBK1、WACP3R 脱敏证据,输出请假/补卡真机确认清单和可选人工结果。","1) 新增 npm script check:wecom-approval-readability-gate;2) 默认只读已有 artifacts,不访问企微写接口,不获取写入 token;3) 支持 --manual-result=leave:<status> 和 --manual-result=missed_punch:<status>;4) 无人工结果时输出 PAUSED_MANUAL_CONFIRMATION;5) 现有样例证据不足时输出 PARTIAL_READABILITY_NEEDS_FRESH_FIXTURE。","受影响流程:自动证据汇总 -> 真机人工确认;验证步骤:cd server && node --check src/scripts/check-wecom-approval-readability-gate.js;cd server && npx vitest run tests/unit/scripts/check-wecom-approval-readability-gate.test.js --reporter=verbose。","node|vitest","DONE","DONE","DONE","server/src/scripts/check-wecom-approval-readability-gate.js|server/package.json|server/tests/unit/scripts/check-wecom-approval-readability-gate.test.js","[""WREAD1-0""]","已新增 runner、npm script 和 focused 单测;测试覆盖历史 fixture artifact 不持久化但 WACP3R 详情证据可用时仍进入 PAUSED_MANUAL_CONFIRMATION。报告隐私边界不输出手机号、邮箱、token、secret、完整 userid、完整 template_id、完整 sp_no、表单值、附件、评论或原始 payload。回滚/清理:移除 runner、package script 和单测。"
"WREAD1-2","本地门禁","执行合同、process gate、语法、Prisma、focused 单测、文档审计、skill 校验和 diff 门禁。","1) validate_issues_csv 通过;2) process baseline 通过;3) node --check 通过;4) Prisma validate 通过;5) focused vitest 通过;6) 文档审计、skill 校验、git diff --check 通过。","受影响流程:WREAD1 本地质量门禁;验证步骤:python3 /home/shunli/.codex/skills/plan/scripts/validate_issues_csv.py issues/2026-05-19_20-39-40-wecom-approval-readability-gate-v1.csv;node scripts/qa/check-process-contract.mjs --baseline --json;cd server && node --check src/scripts/check-wecom-approval-readability-gate.js;cd server && npm run prisma:validate;cd server && npx vitest run tests/unit/scripts/check-wecom-approval-readability-gate.test.js --reporter=verbose;node scripts/qa/audit-docs.mjs --check-registry;python3 /home/shunli/.codex/skills/.system/skill-creator/scripts/quick_validate.py .agents/skills/hyb-oa-delivery;git diff --check。","python3|node|npm|vitest","DONE","DONE","DONE","N/A","[""WREAD1-1""]","本地门禁均通过;audit-docs 仅保留既有 unregistered docs files warning。额外通过 bash scripts/check-minimal.sh。回滚/清理:撤回本轮代码和文档改动。"
"WREAD1-3","公网只读汇总","本地门禁通过后,以 SYNC_ENV=0 部署,不同步本地 env;远端执行 check:wecom-approval-readability-gate -- --format=json,汇总现有样例证据并生成手工确认清单。","1) SYNC_ENV=0 部署通过;2) /ready 正常;3) 远端 runner 生成 summary.json;4) 若现有样例证据完整,结论为 PAUSED_MANUAL_CONFIRMATION;5) 若样例不可用,结论为 PARTIAL_READABILITY_NEEDS_FRESH_FIXTURE 并暂停问答。","受影响流程:公网证据汇总 -> 真机确认;验证步骤:SYNC_ENV=0 bash deploy/trialrun/deploy.sh;远端 npm run check:wecom-approval-readability-gate -- --format=json;curl -fsS https://oa.hyinda.com/ready。","bash|ssh|curl|npm","DONE","DONE","DONE","external","[""WREAD1-2""]","SYNC_ENV=0 部署通过,/ready 返回 ok=true。部署后容器 artifacts 重建,先只读复跑 WACP3R 生成 /app/artifacts/qa-flow/wecom-approval-compensation-poc-20260519T125515Z/summary.json,再执行 WREAD1 生成 /app/artifacts/qa-flow/wecom-approval-readability-gate-20260519T125532Z/summary.json,结论 PAUSED_MANUAL_CONFIRMATION。只读执行,不调用企微写接口,不写 OA 数据。"
"WREAD1-4","事实源回写与人工问答","回写路线图、统一审批规约、请假考勤规约、试运行报告、文档索引、事实源矩阵和项目 skill;在最终答复中按当前状态、改了什么、测试结果和需要你确认什么提问。","1) 文档记录 WREAD1 自动汇总结论和报告路径;2) 未经人工真机确认不得写 PASS_READABLE;3) 若进入 PAUSED_MANUAL_CONFIRMATION,明确等待你反馈请假/补卡卡片、列表、详情和快捷按钮风险;4) 文档审计和 skill 校验通过。","受影响流程:WREAD1 事实源和人工验收;验证步骤:node scripts/qa/audit-docs.mjs --check-registry;python3 /home/shunli/.codex/skills/.system/skill-creator/scripts/quick_validate.py .agents/skills/hyb-oa-delivery;rg -n ""WREAD1|PAUSED_MANUAL_CONFIRMATION|PARTIAL_READABILITY_NEEDS_FRESH_FIXTURE|PASS_READABLE|误审批|可读性"" docs .agents/skills/hyb-oa-delivery/SKILL.md。","node|python3|rg|manual","DONE","DONE","DONE","docs|.agents/skills/hyb-oa-delivery/SKILL.md","[""WREAD1-3""]","事实源已记录 WREAD1 自动汇总 PAUSED_MANUAL_CONFIRMATION 和报告路径;最终答复继续按问答方式等待人工真机确认。回滚/清理:git revert 本轮提交;无数据清理。"
"WREAD1-5","提交与后续准入","提交 WREAD1 自动门禁;若人工确认尚未完成,则提交记录为自动汇总和 PAUSED 状态,待你反馈后再回填最终模板级结论。","1) git diff --check 通过;2) 中文提交完成;3) 工作区干净;4) 最终答复明确 WADM1 只能在 WREAD1 模板级结论明确后进入。","受影响流程:WREAD1 -> WADM1 准入;验证步骤:git status --short;git commit -m 'WREAD1 准备企微审批可读性门禁'。","git","DONE","DONE","DONE","N/A","[""WREAD1-4""]","本提交只代表自动汇总部分和公网 PAUSED 状态;待用户真机反馈后再回填最终模板级结论。回滚/清理:git revert 本提交。"
1 ID Title Description Acceptance Test_Method Tools Dev_Status Review1_Status Regression_Status Files Dependencies Notes
2 WREAD1-0 门禁合同与边界冻结 建立 WREAD1 合同;范围锁定请假+补卡,优先使用现有 OA-POC 样例,不新建企微模板或审批单;本轮不写 OA/企微业务数据,不接新回调,不进入 wecom_attendance_approval_mirror_v1。 1) CSV 校验通过;2) 明确 WACP3R 后下一阶段为可读性与误审批防护门禁;3) 结论按模板输出;4) 缺现有样例时记录 PARTIAL_READABILITY_NEEDS_FRESH_FIXTURE 并回问;5) 不保存完整 template_id、sp_no、userid、表单值、附件、评论或原始 payload。 受影响流程:WACP3R -> WREAD1 可读性门禁 -> WADM1 准入;验证步骤:python3 /home/shunli/.codex/skills/plan/scripts/validate_issues_csv.py issues/2026-05-19_20-39-40-wecom-approval-readability-gate-v1.csv;node scripts/qa/check-process-contract.mjs --baseline --json。 python3|node|manual DONE DONE DONE issues/2026-05-19_20-39-40-wecom-approval-readability-gate-v1.csv [] 固定边界:只读汇总和人工确认,不写 OA/企微数据,不创建模板/审批单。validate_issues_csv 与 process baseline 已通过;回滚/清理:删除本合同和事实源回写;无数据库迁移、无远端 env 修改、无 OA/企微写入。
3 WREAD1-1 只读证据汇总 runner 新增 QA-only runner check-wecom-approval-readability-gate,汇总 WAPC2FLIVE、WCBK1、WACP3R 脱敏证据,输出请假/补卡真机确认清单和可选人工结果。 1) 新增 npm script check:wecom-approval-readability-gate;2) 默认只读已有 artifacts,不访问企微写接口,不获取写入 token;3) 支持 --manual-result=leave:<status> 和 --manual-result=missed_punch:<status>;4) 无人工结果时输出 PAUSED_MANUAL_CONFIRMATION;5) 现有样例证据不足时输出 PARTIAL_READABILITY_NEEDS_FRESH_FIXTURE。 受影响流程:自动证据汇总 -> 真机人工确认;验证步骤:cd server && node --check src/scripts/check-wecom-approval-readability-gate.js;cd server && npx vitest run tests/unit/scripts/check-wecom-approval-readability-gate.test.js --reporter=verbose。 node|vitest DONE DONE DONE server/src/scripts/check-wecom-approval-readability-gate.js|server/package.json|server/tests/unit/scripts/check-wecom-approval-readability-gate.test.js ["WREAD1-0"] 已新增 runner、npm script 和 focused 单测;测试覆盖历史 fixture artifact 不持久化但 WACP3R 详情证据可用时仍进入 PAUSED_MANUAL_CONFIRMATION。报告隐私边界不输出手机号、邮箱、token、secret、完整 userid、完整 template_id、完整 sp_no、表单值、附件、评论或原始 payload。回滚/清理:移除 runner、package script 和单测。
4 WREAD1-2 本地门禁 执行合同、process gate、语法、Prisma、focused 单测、文档审计、skill 校验和 diff 门禁。 1) validate_issues_csv 通过;2) process baseline 通过;3) node --check 通过;4) Prisma validate 通过;5) focused vitest 通过;6) 文档审计、skill 校验、git diff --check 通过。 受影响流程:WREAD1 本地质量门禁;验证步骤:python3 /home/shunli/.codex/skills/plan/scripts/validate_issues_csv.py issues/2026-05-19_20-39-40-wecom-approval-readability-gate-v1.csv;node scripts/qa/check-process-contract.mjs --baseline --json;cd server && node --check src/scripts/check-wecom-approval-readability-gate.js;cd server && npm run prisma:validate;cd server && npx vitest run tests/unit/scripts/check-wecom-approval-readability-gate.test.js --reporter=verbose;node scripts/qa/audit-docs.mjs --check-registry;python3 /home/shunli/.codex/skills/.system/skill-creator/scripts/quick_validate.py .agents/skills/hyb-oa-delivery;git diff --check。 python3|node|npm|vitest DONE DONE DONE N/A ["WREAD1-1"] 本地门禁均通过;audit-docs 仅保留既有 unregistered docs files warning。额外通过 bash scripts/check-minimal.sh。回滚/清理:撤回本轮代码和文档改动。
5 WREAD1-3 公网只读汇总 本地门禁通过后,以 SYNC_ENV=0 部署,不同步本地 env;远端执行 check:wecom-approval-readability-gate -- --format=json,汇总现有样例证据并生成手工确认清单。 1) SYNC_ENV=0 部署通过;2) /ready 正常;3) 远端 runner 生成 summary.json;4) 若现有样例证据完整,结论为 PAUSED_MANUAL_CONFIRMATION;5) 若样例不可用,结论为 PARTIAL_READABILITY_NEEDS_FRESH_FIXTURE 并暂停问答。 受影响流程:公网证据汇总 -> 真机确认;验证步骤:SYNC_ENV=0 bash deploy/trialrun/deploy.sh;远端 npm run check:wecom-approval-readability-gate -- --format=json;curl -fsS https://oa.hyinda.com/ready。 bash|ssh|curl|npm DONE DONE DONE external ["WREAD1-2"] SYNC_ENV=0 部署通过,/ready 返回 ok=true。部署后容器 artifacts 重建,先只读复跑 WACP3R 生成 /app/artifacts/qa-flow/wecom-approval-compensation-poc-20260519T125515Z/summary.json,再执行 WREAD1 生成 /app/artifacts/qa-flow/wecom-approval-readability-gate-20260519T125532Z/summary.json,结论 PAUSED_MANUAL_CONFIRMATION。只读执行,不调用企微写接口,不写 OA 数据。
6 WREAD1-4 事实源回写与人工问答 回写路线图、统一审批规约、请假考勤规约、试运行报告、文档索引、事实源矩阵和项目 skill;在最终答复中按当前状态、改了什么、测试结果和需要你确认什么提问。 1) 文档记录 WREAD1 自动汇总结论和报告路径;2) 未经人工真机确认不得写 PASS_READABLE;3) 若进入 PAUSED_MANUAL_CONFIRMATION,明确等待你反馈请假/补卡卡片、列表、详情和快捷按钮风险;4) 文档审计和 skill 校验通过。 受影响流程:WREAD1 事实源和人工验收;验证步骤:node scripts/qa/audit-docs.mjs --check-registry;python3 /home/shunli/.codex/skills/.system/skill-creator/scripts/quick_validate.py .agents/skills/hyb-oa-delivery;rg -n "WREAD1|PAUSED_MANUAL_CONFIRMATION|PARTIAL_READABILITY_NEEDS_FRESH_FIXTURE|PASS_READABLE|误审批|可读性" docs .agents/skills/hyb-oa-delivery/SKILL.md。 node|python3|rg|manual DONE DONE DONE docs|.agents/skills/hyb-oa-delivery/SKILL.md ["WREAD1-3"] 事实源已记录 WREAD1 自动汇总 PAUSED_MANUAL_CONFIRMATION 和报告路径;最终答复继续按问答方式等待人工真机确认。回滚/清理:git revert 本轮提交;无数据清理。
7 WREAD1-5 提交与后续准入 提交 WREAD1 自动门禁;若人工确认尚未完成,则提交记录为自动汇总和 PAUSED 状态,待你反馈后再回填最终模板级结论。 1) git diff --check 通过;2) 中文提交完成;3) 工作区干净;4) 最终答复明确 WADM1 只能在 WREAD1 模板级结论明确后进入。 受影响流程:WREAD1 -> WADM1 准入;验证步骤:git status --short;git commit -m 'WREAD1 准备企微审批可读性门禁'。 git DONE DONE DONE N/A ["WREAD1-4"] 本提交只代表自动汇总部分和公网 PAUSED 状态;待用户真机反馈后再回填最终模板级结论。回滚/清理:git revert 本提交。
+1
查看文件
@@ -32,6 +32,7 @@
"check:wecom-approval-fixtures": "node src/scripts/check-wecom-approval-fixtures.js", "check:wecom-approval-fixtures": "node src/scripts/check-wecom-approval-fixtures.js",
"check:wecom-approval-callback-poc": "node src/scripts/check-wecom-approval-callback-poc.js", "check:wecom-approval-callback-poc": "node src/scripts/check-wecom-approval-callback-poc.js",
"check:wecom-approval-compensation-poc": "node src/scripts/check-wecom-approval-compensation-poc.js", "check:wecom-approval-compensation-poc": "node src/scripts/check-wecom-approval-compensation-poc.js",
"check:wecom-approval-readability-gate": "node src/scripts/check-wecom-approval-readability-gate.js",
"check:wecom-approval-poc": "node src/scripts/check-wecom-approval-poc.js", "check:wecom-approval-poc": "node src/scripts/check-wecom-approval-poc.js",
"check:wecom-runtime-ready": "node src/scripts/check-wecom-runtime-ready.js", "check:wecom-runtime-ready": "node src/scripts/check-wecom-runtime-ready.js",
"resolve:wecom-oa-users": "node src/scripts/resolve-wecom-oa-user-consistency.js", "resolve:wecom-oa-users": "node src/scripts/resolve-wecom-oa-user-consistency.js",
@@ -0,0 +1,487 @@
#!/usr/bin/env node
const fs = require("node:fs");
const path = require("node:path");
const {
APPROVAL_POC_TARGETS,
maskApprovalIdentifier,
} = require("../services/integrations/wecomApproval");
const DEFAULT_REPORT_ROOT = path.resolve(__dirname, "../../../artifacts/qa-flow");
const READABILITY_STATUSES = new Set([
"PASS_READABLE",
"PASS_WITH_TEMPLATE_ADJUSTMENTS",
"FAIL_KEEP_OA_PRIMARY_FOR_THIS_TEMPLATE",
]);
const REPORT_PRIVACY =
"readability status, manual checklist, evidence statuses and masked prefixes only; no phone/email/token/secret/full userid/full template_id/full sp_no/form values/attachments/comments/raw payload";
function timestamp() {
return new Date().toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
}
function usage() {
return `Usage: node src/scripts/check-wecom-approval-readability-gate.js [options]
QA-only 企业微信审批可读性与误审批防护门禁。
默认只汇总现有 OA-POC 证据,不写 OA/企微数据,不创建模板或审批单。
Options:
--format=text|json 输出格式,默认 text。
--artifact-root=<path> QA 报告根目录,默认 artifacts/qa-flow。
--report-dir=<path> 输出目录,默认 artifacts/qa-flow/wecom-approval-readability-gate-<timestamp>。
--fixtures-report=<path> 指定 WAPC2FLIVE summary.json 或目录。
--callback-report=<path> 指定 WCBK1 summary.json 或目录。
--compensation-report=<path> 指定 WACP3R summary.json 或目录。
--manual-result=<target>:<status> 录入人工结论,target 为 leave 或 missed_punch。
--manual-note=<target>:<text> 录入脱敏人工备注,不写表单值、附件、评论或完整 ID。
--confirmed-by=<employeeNo> 人工确认人,仅记录员工号。
--help 显示帮助。
人工结论:
PASS_READABLE
PASS_WITH_TEMPLATE_ADJUSTMENTS
FAIL_KEEP_OA_PRIMARY_FOR_THIS_TEMPLATE
`;
}
function toSafeString(value, maxLength = 500) {
if (value == null) return "";
return String(value).trim().slice(0, maxLength);
}
function safeMessage(value) {
return toSafeString(value, 500)
.replace(/access_token=[^&\s]+/gi, "access_token=***")
.replace(/[A-Za-z0-9_-]{24,}/g, "***")
.slice(0, 240);
}
function targetKeys() {
return APPROVAL_POC_TARGETS.map((item) => item.key);
}
function parseTargetPair(value, flagName) {
const raw = toSafeString(value, 700);
const separatorIndex = raw.indexOf(":");
if (separatorIndex <= 0) {
throw new Error(`${flagName} requires <target>:<value>`);
}
const target = raw.slice(0, separatorIndex).trim();
const itemValue = raw.slice(separatorIndex + 1).trim();
if (!targetKeys().includes(target)) {
throw new Error(`${flagName} unsupported target: ${target}`);
}
if (!itemValue) {
throw new Error(`${flagName} requires non-empty value`);
}
return { target, value: itemValue };
}
function parseArgs(argv = process.argv.slice(2), env = process.env) {
const options = {
format: "text",
artifactRoot: env.WECOM_APPROVAL_READABILITY_ARTIFACT_ROOT || DEFAULT_REPORT_ROOT,
reportDir: env.WECOM_APPROVAL_READABILITY_REPORT_DIR
|| path.join(DEFAULT_REPORT_ROOT, `wecom-approval-readability-gate-${timestamp()}`),
reports: {
fixtures: "",
callback: "",
compensation: "",
},
manualResults: {},
manualNotes: {},
confirmedBy: "",
help: false,
};
for (let index = 0; index < argv.length; index += 1) {
const arg = argv[index];
if (arg === "--help" || arg === "-h") {
options.help = true;
} else if (arg === "--format") {
options.format = argv[index + 1] || options.format;
index += 1;
} else if (arg.startsWith("--format=")) {
options.format = arg.slice("--format=".length);
} else if (arg === "--artifact-root") {
options.artifactRoot = argv[index + 1] || options.artifactRoot;
index += 1;
} else if (arg.startsWith("--artifact-root=")) {
options.artifactRoot = arg.slice("--artifact-root=".length);
} else if (arg === "--report-dir") {
options.reportDir = argv[index + 1] || options.reportDir;
index += 1;
} else if (arg.startsWith("--report-dir=")) {
options.reportDir = arg.slice("--report-dir=".length);
} else if (arg === "--fixtures-report") {
options.reports.fixtures = argv[index + 1] || "";
index += 1;
} else if (arg.startsWith("--fixtures-report=")) {
options.reports.fixtures = arg.slice("--fixtures-report=".length);
} else if (arg === "--callback-report") {
options.reports.callback = argv[index + 1] || "";
index += 1;
} else if (arg.startsWith("--callback-report=")) {
options.reports.callback = arg.slice("--callback-report=".length);
} else if (arg === "--compensation-report") {
options.reports.compensation = argv[index + 1] || "";
index += 1;
} else if (arg.startsWith("--compensation-report=")) {
options.reports.compensation = arg.slice("--compensation-report=".length);
} else if (arg === "--manual-result") {
const pair = parseTargetPair(argv[index + 1], "--manual-result");
options.manualResults[pair.target] = pair.value;
index += 1;
} else if (arg.startsWith("--manual-result=")) {
const pair = parseTargetPair(arg.slice("--manual-result=".length), "--manual-result");
options.manualResults[pair.target] = pair.value;
} else if (arg === "--manual-note") {
const pair = parseTargetPair(argv[index + 1], "--manual-note");
options.manualNotes[pair.target] = pair.value;
index += 1;
} else if (arg.startsWith("--manual-note=")) {
const pair = parseTargetPair(arg.slice("--manual-note=".length), "--manual-note");
options.manualNotes[pair.target] = pair.value;
} else if (arg === "--confirmed-by") {
options.confirmedBy = toSafeString(argv[index + 1], 80);
index += 1;
} else if (arg.startsWith("--confirmed-by=")) {
options.confirmedBy = toSafeString(arg.slice("--confirmed-by=".length), 80);
} else {
throw new Error(`Unsupported argument: ${arg}`);
}
}
if (!["text", "json"].includes(options.format)) {
throw new Error(`Unsupported --format: ${options.format}`);
}
for (const [target, value] of Object.entries(options.manualResults)) {
if (!READABILITY_STATUSES.has(value)) {
throw new Error(`Unsupported manual result for ${target}: ${value}`);
}
}
return options;
}
function resolveSummaryPath(inputPath) {
const value = toSafeString(inputPath, 1000);
if (!value) return "";
if (!fs.existsSync(value)) return "";
const stat = fs.statSync(value);
if (stat.isDirectory()) {
const summaryPath = path.join(value, "summary.json");
return fs.existsSync(summaryPath) ? summaryPath : "";
}
return value;
}
function findLatestSummary(artifactRoot, prefix) {
const root = toSafeString(artifactRoot, 1000);
if (!root || !fs.existsSync(root)) return "";
const entries = fs.readdirSync(root, { withFileTypes: true })
.filter((entry) => entry.isDirectory() && entry.name.startsWith(prefix))
.map((entry) => path.join(root, entry.name, "summary.json"))
.filter((summaryPath) => fs.existsSync(summaryPath))
.sort();
return entries[entries.length - 1] || "";
}
function readJsonFile(filePath) {
const resolved = resolveSummaryPath(filePath);
if (!resolved) return { ok: false, path: "", reason: "report_not_found", data: null };
try {
const data = JSON.parse(fs.readFileSync(resolved, "utf8"));
return { ok: true, path: resolved, reason: null, data };
} catch (error) {
return { ok: false, path: resolved, reason: "report_parse_failed", message: safeMessage(error?.message || error), data: null };
}
}
function loadEvidenceReports(options, deps = {}) {
const findLatestSummaryImpl = deps.findLatestSummaryImpl || findLatestSummary;
const readJsonFileImpl = deps.readJsonFileImpl || readJsonFile;
const paths = {
fixtures: resolveSummaryPath(options.reports.fixtures)
|| findLatestSummaryImpl(options.artifactRoot, "wecom-approval-fixtures-"),
callback: resolveSummaryPath(options.reports.callback)
|| findLatestSummaryImpl(options.artifactRoot, "wecom-approval-callback-poc-"),
compensation: resolveSummaryPath(options.reports.compensation)
|| findLatestSummaryImpl(options.artifactRoot, "wecom-approval-compensation-poc-"),
};
return {
fixtures: readJsonFileImpl(paths.fixtures),
callback: readJsonFileImpl(paths.callback),
compensation: readJsonFileImpl(paths.compensation),
};
}
function sanitizePath(filePath) {
return toSafeString(filePath, 1000) || null;
}
function targetLabel(target) {
return APPROVAL_POC_TARGETS.find((item) => item.key === target)?.label || target;
}
function maskEvidencePrefix(value) {
const raw = toSafeString(value, 160);
if (!raw) return null;
return raw.endsWith("***") ? raw : maskApprovalIdentifier(raw);
}
function summarizeFixtureEvidence(readResult) {
const report = readResult.data || {};
const liveWrite = report.checks?.liveWrite || {};
const autoRerun = report.checks?.autoRerun || {};
const createdTemplatePrefixes = liveWrite.createdTemplatePrefixes || autoRerun.templateIdPrefixes || {};
const submittedApprovalPrefixes = liveWrite.submittedApprovalPrefixes || autoRerun.spNoPrefixes || {};
const targets = targetKeys().map((target) => ({
target,
label: targetLabel(target),
templateIdPrefix: maskEvidencePrefix(createdTemplatePrefixes[target]),
spNoPrefix: maskEvidencePrefix(submittedApprovalPrefixes[target]),
templateAvailable: Boolean(createdTemplatePrefixes[target]),
approvalAvailable: Boolean(submittedApprovalPrefixes[target]),
}));
const ok = readResult.ok
&& report.status === "PASS_FIXTURE_CREATED"
&& autoRerun.status === "OK"
&& targets.every((item) => item.templateAvailable && item.approvalAvailable);
return {
id: "wapc2flive_fixture_evidence",
status: ok ? "OK" : (readResult.ok ? "PARTIAL" : "MISSING"),
reportPath: sanitizePath(readResult.path),
sourceStatus: report.status || null,
sourceConclusion: report.conclusion || null,
autoRerunStatus: autoRerun.status || null,
targets,
reason: readResult.ok ? null : readResult.reason,
};
}
function summarizeCallbackEvidence(readResult) {
const report = readResult.data || {};
const eventEvidence = report.checks?.eventEvidence || {};
const ok = readResult.ok && report.status === "PASS_CALLBACK_EVENT_CONFIRMED";
return {
id: "wcbk1_callback_evidence",
status: ok ? "OK" : (readResult.ok ? "PARTIAL" : "MISSING"),
reportPath: sanitizePath(readResult.path),
sourceStatus: report.status || null,
sourceConclusion: report.conclusion || null,
eventCount: Number(eventEvidence.eventCount || 0),
lastEvent: eventEvidence.lastEvent ? {
approvalType: eventEvidence.lastEvent.approvalType || null,
statusLabel: eventEvidence.lastEvent.statusLabel || null,
spNoPrefix: eventEvidence.lastEvent.spNoPrefix || null,
templateIdPrefix: eventEvidence.lastEvent.templateIdPrefix || null,
recordedAt: eventEvidence.lastEvent.recordedAt || null,
} : null,
reason: readResult.ok ? null : readResult.reason,
};
}
function summarizeCompensationEvidence(readResult) {
const report = readResult.data || {};
const targetChecks = Array.isArray(report.checks?.targets) ? report.checks.targets : [];
const targets = targetKeys().map((target) => {
const check = targetChecks.find((item) => item.target === target || item.key === target) || {};
const details = Array.isArray(check.details) ? check.details : [];
return {
target,
label: targetLabel(target),
status: check.status || null,
detailCount: Number(check.detailCount || details.length || 0),
statusSummary: check.statusSummary || null,
detailSpNoPrefixes: details.map((item) => item.spNoPrefix).filter(Boolean).slice(0, 5),
};
});
const ok = readResult.ok
&& ["PASS_COMPENSATION_POC", "PASS_WITH_FINDINGS"].includes(report.status)
&& targets.every((item) => item.status === "OK" && item.detailCount > 0);
return {
id: "wacp3r_compensation_evidence",
status: ok ? "OK" : (readResult.ok ? "PARTIAL" : "MISSING"),
reportPath: sanitizePath(readResult.path),
sourceStatus: report.status || null,
sourceConclusion: report.conclusion || null,
templateDiscoveryStatus: report.checks?.templateDiscovery?.status || null,
findings: (report.checks?.callbackCompare?.findings || []).map((item) => ({
id: item.id,
target: item.target || null,
spNoPrefix: item.spNoPrefix || null,
})),
targets,
reason: readResult.ok ? null : readResult.reason,
};
}
function buildManualChecklist() {
return targetKeys().map((target) => ({
target,
label: targetLabel(target),
questions: [
"企微消息卡片是否能看出申请类型、申请人、时间和状态?",
"企微审批列表是否能区分请假/补卡?",
"详情页字段是否足够审批人判断?",
"快捷同意/驳回是否容易误点?",
"是否需要模板字段调整或回退 OA 主办?",
],
}));
}
function sanitizeManualNote(value) {
return safeMessage(value)
.replace(/SP[A-Za-z0-9_-]{8,}/g, "SP***")
.replace(/[A-Za-z0-9_-]{16,}/g, "***");
}
function buildManualResults(options) {
return targetKeys().map((target) => ({
target,
label: targetLabel(target),
status: options.manualResults[target] || "PENDING_MANUAL_CONFIRMATION",
note: options.manualNotes[target] ? sanitizeManualNote(options.manualNotes[target]) : null,
}));
}
function evaluateConclusion({ evidence, manualResults }) {
const sampleEvidenceOk = evidence.compensation.status === "OK";
if (!sampleEvidenceOk) {
return {
status: "PARTIAL_READABILITY_NEEDS_FRESH_FIXTURE",
conclusion: "PARTIAL_READABILITY_NEEDS_FRESH_FIXTURE",
};
}
if (manualResults.some((item) => item.status === "PENDING_MANUAL_CONFIRMATION")) {
return {
status: "PAUSED_MANUAL_CONFIRMATION",
conclusion: "PAUSED_MANUAL_CONFIRMATION",
};
}
if (manualResults.some((item) => item.status === "FAIL_KEEP_OA_PRIMARY_FOR_THIS_TEMPLATE")) {
return {
status: "FAIL_KEEP_OA_PRIMARY_FOR_THIS_TEMPLATE",
conclusion: "FAIL_KEEP_OA_PRIMARY_FOR_THIS_TEMPLATE",
};
}
if (manualResults.some((item) => item.status === "PASS_WITH_TEMPLATE_ADJUSTMENTS")) {
return {
status: "PASS_WITH_TEMPLATE_ADJUSTMENTS",
conclusion: "PASS_WITH_TEMPLATE_ADJUSTMENTS",
};
}
return {
status: "PASS_READABLE",
conclusion: "PASS_READABLE",
};
}
async function executeReadabilityGate(options, deps = {}) {
const generatedAt = deps.generatedAt || new Date().toISOString();
const reports = loadEvidenceReports(options, deps);
const evidence = {
fixtures: summarizeFixtureEvidence(reports.fixtures),
callback: summarizeCallbackEvidence(reports.callback),
compensation: summarizeCompensationEvidence(reports.compensation),
};
const manualResults = buildManualResults(options);
const conclusion = evaluateConclusion({ evidence, manualResults });
const sampleEvidenceOk = evidence.compensation.status === "OK";
return {
mode: "qa_only",
generatedAt,
status: conclusion.status,
conclusion: conclusion.conclusion,
privacy: REPORT_PRIVACY,
scope: {
issue: "WREAD1",
targets: targetKeys(),
samplePolicy: "reuse existing OA-POC leave/missed_punch samples first; do not create new WeCom templates or approvals",
writeBehavior: "read-only evidence summary only; no OA writes, no WeCom writes, no callback setup, no mirror import",
deepMirror: "not_started",
nextStage: conclusion.status === "PASS_READABLE" || conclusion.status === "PASS_WITH_TEMPLATE_ADJUSTMENTS"
? "WADM1"
: "manual_confirmation_or_fresh_fixture_decision",
},
checks: {
evidence,
sampleEvidence: {
status: sampleEvidenceOk ? "OK" : "PARTIAL",
reason: sampleEvidenceOk
? "wacp3r_compensation_detail_evidence_available"
: "existing_oa_poc_compensation_detail_evidence_not_complete",
fixtureEvidenceStatus: evidence.fixtures.status,
callbackEvidenceStatus: evidence.callback.status,
},
manualChecklist: buildManualChecklist(),
manualResults,
},
confirmedBy: options.confirmedBy || null,
configGaps: sampleEvidenceOk ? [] : [{
id: "readability_existing_sample",
status: "NEEDS_CONFIG",
reason: "existing_compensation_detail_evidence_not_complete_or_not_found",
}],
mainBlockers: [],
};
}
function ensureReportDir(reportDir) {
fs.mkdirSync(reportDir, { recursive: true });
}
function writeReport(reportDir, report) {
ensureReportDir(reportDir);
const filePath = path.join(reportDir, "summary.json");
fs.writeFileSync(filePath, `${JSON.stringify(report, null, 2)}\n`, "utf8");
return filePath;
}
function printText(report, reportPath) {
process.stdout.write(`[wecom-approval-readability-gate] status=${report.status} conclusion=${report.conclusion}\n`);
process.stdout.write(`[wecom-approval-readability-gate] report=${reportPath}\n`);
process.stdout.write(`[wecom-approval-readability-gate] privacy=${report.privacy}\n`);
process.stdout.write(`[wecom-approval-readability-gate] targets=${report.scope.targets.join(",")}\n`);
if (report.configGaps.length) {
process.stdout.write(`[wecom-approval-readability-gate] configGaps=${JSON.stringify(report.configGaps)}\n`);
}
}
async function main() {
const options = parseArgs(process.argv.slice(2));
if (options.help) {
process.stdout.write(usage());
return;
}
const report = await executeReadabilityGate(options);
const reportPath = writeReport(options.reportDir, report);
if (options.format === "json") {
process.stdout.write(`${JSON.stringify({ ...report, reportPath }, null, 2)}\n`);
} else {
printText(report, reportPath);
}
}
if (require.main === module) {
main().catch((error) => {
process.stderr.write(`[wecom-approval-readability-gate][FAIL] ${safeMessage(error?.message || error)}\n`);
process.exitCode = 1;
});
}
module.exports = {
buildManualChecklist,
buildManualResults,
evaluateConclusion,
executeReadabilityGate,
findLatestSummary,
parseArgs,
summarizeCompensationEvidence,
summarizeFixtureEvidence,
writeReport,
};
@@ -0,0 +1,220 @@
const fs = require("node:fs");
const os = require("node:os");
const path = require("node:path");
const {
executeReadabilityGate,
parseArgs,
} = require("../../../src/scripts/check-wecom-approval-readability-gate");
const SENSITIVE_TEMPLATE_ID = "tpl_readability_full_identifier_must_not_leak";
const SENSITIVE_SP_NO = "SPREADABILITYFULLIDENTIFIERMUSTNOTLEAK";
const SENSITIVE_USER_ID = "readability_userid_full_must_not_leak";
const SENSITIVE_FORM_VALUE = "这里是表单值,不应出现在报告里";
function tempDir() {
return fs.mkdtempSync(path.join(os.tmpdir(), "wread1-test-"));
}
function writeSummary(root, dirname, payload) {
const dir = path.join(root, dirname);
fs.mkdirSync(dir, { recursive: true });
const filePath = path.join(dir, "summary.json");
fs.writeFileSync(filePath, JSON.stringify(payload, null, 2));
return filePath;
}
function writeOkEvidence(root) {
writeSummary(root, "wecom-approval-fixtures-20260519T053400Z", {
status: "PASS_FIXTURE_CREATED",
conclusion: "PASS_FIXTURE_CREATED",
checks: {
liveWrite: {
createdTemplatePrefixes: {
leave: "tpl-leav***",
missed_punch: "tpl-miss***",
},
submittedApprovalPrefixes: {
leave: "SPLEAVE***",
missed_punch: "SPMISS***",
},
},
autoRerun: {
status: "OK",
},
},
rawShouldNotCopy: SENSITIVE_TEMPLATE_ID,
});
writeSummary(root, "wecom-approval-callback-poc-20260519T092648Z", {
status: "PASS_CALLBACK_EVENT_CONFIRMED",
conclusion: "PASS_CALLBACK_EVENT_CONFIRMED",
checks: {
eventEvidence: {
eventCount: 1,
lastEvent: {
approvalType: "missed_punch",
statusLabel: "已通过",
spNoPrefix: "SPMISS***",
templateIdPrefix: "tpl-miss***",
recordedAt: "2026-05-19T09:26:48.000Z",
rawShouldNotCopy: SENSITIVE_SP_NO,
},
},
},
});
writeSummary(root, "wecom-approval-compensation-poc-20260519T115944Z", {
status: "PASS_WITH_FINDINGS",
conclusion: "PASS_WITH_FINDINGS",
checks: {
templateDiscovery: { status: "OK", discovered: { leave: SENSITIVE_TEMPLATE_ID } },
targets: [
{
target: "leave",
status: "OK",
detailCount: 1,
statusSummary: { "审批中": 1 },
details: [{ spNoPrefix: "SPLEAVE***", rawShouldNotCopy: SENSITIVE_FORM_VALUE }],
},
{
target: "missed_punch",
status: "OK",
detailCount: 1,
statusSummary: { "已通过": 1 },
details: [{ spNoPrefix: "SPMISS***" }],
},
],
callbackCompare: {
findings: [
{ id: "callback_missing_for_pulled_detail", target: "leave", spNoPrefix: "SPLEAVE***" },
],
},
},
rawUserId: SENSITIVE_USER_ID,
});
}
describe("check-wecom-approval-readability-gate", () => {
it("parses manual results and report options", () => {
const options = parseArgs([
"--format=json",
"--artifact-root=/tmp/wread1-artifacts",
"--manual-result=leave:PASS_READABLE",
"--manual-result=missed_punch:PASS_WITH_TEMPLATE_ADJUSTMENTS",
"--manual-note=leave:卡片可读",
"--confirmed-by=wangshunli",
]);
expect(options.format).toBe("json");
expect(options.artifactRoot).toBe("/tmp/wread1-artifacts");
expect(options.manualResults).toEqual({
leave: "PASS_READABLE",
missed_punch: "PASS_WITH_TEMPLATE_ADJUSTMENTS",
});
expect(options.manualNotes.leave).toBe("卡片可读");
expect(options.confirmedBy).toBe("wangshunli");
expect(() => parseArgs(["--manual-result=leave:PASS"])).toThrow(/Unsupported manual result/);
});
it("pauses for manual confirmation when existing evidence is available", async () => {
const root = tempDir();
writeOkEvidence(root);
const report = await executeReadabilityGate({
...parseArgs(["--artifact-root", root]),
reportDir: path.join(root, "out"),
}, { generatedAt: "2026-05-20T00:00:00.000Z" });
expect(report.status).toBe("PAUSED_MANUAL_CONFIRMATION");
expect(report.checks.sampleEvidence.status).toBe("OK");
expect(report.checks.evidence.fixtures.status).toBe("OK");
expect(report.checks.evidence.compensation.status).toBe("OK");
expect(report.checks.manualResults.map((item) => item.status)).toEqual([
"PENDING_MANUAL_CONFIRMATION",
"PENDING_MANUAL_CONFIRMATION",
]);
});
it("accepts WACP3R compensation detail evidence when historical fixture artifacts are not persisted", async () => {
const root = tempDir();
writeSummary(root, "wecom-approval-compensation-poc-20260519T115944Z", {
status: "PASS_WITH_FINDINGS",
conclusion: "PASS_WITH_FINDINGS",
checks: {
templateDiscovery: { status: "OK" },
targets: [
{
target: "leave",
status: "OK",
details: [{ spNoPrefix: "SPLEAVE***" }],
},
{
target: "missed_punch",
status: "OK",
details: [{ spNoPrefix: "SPMISS***" }],
},
],
callbackCompare: { findings: [] },
},
});
const report = await executeReadabilityGate(parseArgs(["--artifact-root", root]), {
generatedAt: "2026-05-20T00:00:00.000Z",
});
expect(report.status).toBe("PAUSED_MANUAL_CONFIRMATION");
expect(report.checks.sampleEvidence.status).toBe("OK");
expect(report.checks.sampleEvidence.reason).toBe("wacp3r_compensation_detail_evidence_available");
expect(report.checks.evidence.fixtures.status).toBe("MISSING");
});
it("returns PASS_WITH_TEMPLATE_ADJUSTMENTS when one template needs changes", async () => {
const root = tempDir();
writeOkEvidence(root);
const report = await executeReadabilityGate(parseArgs([
"--artifact-root", root,
"--manual-result=leave:PASS_READABLE",
"--manual-result=missed_punch:PASS_WITH_TEMPLATE_ADJUSTMENTS",
"--manual-note=missed_punch:快捷按钮容易误点,需要提醒审批人点详情",
"--confirmed-by=wangshunli",
]), { generatedAt: "2026-05-20T00:00:00.000Z" });
expect(report.status).toBe("PASS_WITH_TEMPLATE_ADJUSTMENTS");
expect(report.confirmedBy).toBe("wangshunli");
expect(report.checks.manualResults.find((item) => item.target === "missed_punch").note).toContain("快捷按钮");
});
it("asks for a fresh fixture when existing sample evidence is missing", async () => {
const root = tempDir();
writeSummary(root, "wecom-approval-compensation-poc-20260519T115944Z", {
status: "PASS_WITH_FINDINGS",
checks: { targets: [] },
});
const report = await executeReadabilityGate(parseArgs(["--artifact-root", root]), {
generatedAt: "2026-05-20T00:00:00.000Z",
});
expect(report.status).toBe("PARTIAL_READABILITY_NEEDS_FRESH_FIXTURE");
expect(report.configGaps.map((item) => item.id)).toContain("readability_existing_sample");
});
it("does not leak full template, sp_no, userid or form values", async () => {
const root = tempDir();
writeOkEvidence(root);
const report = await executeReadabilityGate(parseArgs([
"--artifact-root", root,
"--manual-result=leave:PASS_READABLE",
"--manual-result=missed_punch:PASS_READABLE",
"--manual-note=leave:不要泄露 SPREADABILITYFULLIDENTIFIERMUSTNOTLEAK",
]), { generatedAt: "2026-05-20T00:00:00.000Z" });
const serialized = JSON.stringify(report);
expect(report.status).toBe("PASS_READABLE");
expect(serialized).not.toContain(SENSITIVE_TEMPLATE_ID);
expect(serialized).not.toContain(SENSITIVE_SP_NO);
expect(serialized).not.toContain(SENSITIVE_USER_ID);
expect(serialized).not.toContain(SENSITIVE_FORM_VALUE);
});
});