WOPS1 调整巡检告警降噪
Issue: WOPS1, WROAD2C, WREAL1P
这个提交包含在:
@@ -17,10 +17,10 @@ Read only what is relevant:
|
||||
- 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 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/WREAD1F have now completed real-device readability confirmation for leave and missed-punch: public report `/app/artifacts/qa-flow/wecom-approval-readability-gate-20260519T133628Z/summary.json` is `PASS_READABLE`; the user confirmed cards/lists/details are readable, reject has second confirmation, and approve is single-tap. WADM1 has completed the self-owned archive/mirror data-model design. WARCH1 has implemented and publicly verified the minimum leave/missed-punch archive slice: Prisma archive/callback/compensation/finding tables, callback DB persistence with non-200 retry on DB failure, compensation persistence behind `--persist-to-db` + `WECOM_ARCHIVE_WRITE=1`, and offline `npm run check:wecom-approval-archive`; public reports `/app/artifacts/qa-flow/wecom-approval-compensation-poc-20260519T160306Z/summary.json` and `/app/artifacts/qa-flow/wecom-approval-archive-20260519T160330Z/summary.json` are `PASS_WITH_FINDINGS`, with 2 archived histories readable and only a historical pre-WARCH1 callback gap finding. WARCH2 has now added the formal OA read-only archive API/page: `/api/wecom/approval-archive`, `/wecom-approval-archive`, and `/mobile/wecom-approval-archive` read OA DB only, do not call WeCom API or read WeCom Secrets, and enforce self/direct-report/HR-admin visibility with unmapped applicant records HR/admin-only. WROAD2+OPS is the current controlled launch gate: use test accounts `SMOKE002 -> SMOKE101 + wangshunli`, run `scripts/qa/run-wroad2-ops-daily.sh` manually once per day for 3 days, allow only compensation archive writes to OA DB via `WECOM_ARCHIVE_WRITE=1`, and do not create WeCom templates/approvals, write WeCom, install timers, or expand real users. Day 0 public report `/app/artifacts/qa-flow/wroad2-ops-20260520-041941/summary.json` is `PASS_WITH_FINDINGS`: WeCom runtime, compensation archive persistence, offline archive read, and privacy smoke pass; findings are missing live-smoke login credentials/pending OCR subcheck and historical callback gap. It still does not implement full mirror, outing/overtime, emergency mode, writeback, or RuleOps. 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. Do not start `wecom_attendance_approval_mirror_v1`, `oa_hr_emergency_mode_v1`, `wecom_recovery_writeback_v1`, or `wecom_ruleops_v1` without WROAD2+OPS, WARCH3, WCHECK1, WEXT1 decision, and the next stage's own Issue CSV and acceptance gates.
|
||||
- WARCH3/WCHECK1/WEXT1 update the current route after WROAD2 Day 0: WARCH3 adds `scripts/qa/run-warch3-ops-daily.sh`, `deploy/trialrun/install-warch3-ops-timer.sh`, and `npm run send:wecom-ops-alert` for 07:30 systemd巡检和脱敏企微告警;public report `/app/artifacts/qa-flow/warch3-ops-20260520-054841/summary.json` is `PASS_WITH_FINDINGS`, and alert report `/app/artifacts/qa-flow/wecom-ops-alert-20260520T054851Z/summary.json` is `PASS_ALERT_SENT_WITH_CONFIG_GAPS` with `HYD000006` notified and `wangshunli` kept as an alias gap. WCHECK1 adds `npm run check:wecom-checkin-summary-archive`, daily/monthly check-in summaries, and rule snapshots with `--persist-to-db` + `WECOM_CHECKIN_ARCHIVE_WRITE=1` as the only OA DB write gate; public reports `/app/artifacts/qa-flow/wecom-checkin-summary-archive-20260520T054907Z/summary.json` and `/app/artifacts/qa-flow/wecom-checkin-summary-archive-20260520T054926Z/summary.json` are `PASS_CHECKIN_ARCHIVE_READY` and `PASS_CHECKIN_ARCHIVE_OFFLINE_READY`. WEXT1 only extends approval fixtures for `--target=outing --target=overtime` dry-run; public report `/app/artifacts/qa-flow/wecom-approval-fixtures-20260520T054940Z/summary.json` is `PASS_FIXTURE_DRY_RUN_READY`. These stages still do not create WeCom templates/approvals, expand real users, or implement full mirror/emergency/writeback/RuleOps. Next real-user step is WREAL1 after WROAD2 three-day closeout and explainable WARCH3/WCHECK1/WEXT1 evidence.
|
||||
- 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/WREAD1F have now completed real-device readability confirmation for leave and missed-punch: public report `/app/artifacts/qa-flow/wecom-approval-readability-gate-20260519T133628Z/summary.json` is `PASS_READABLE`; the user confirmed cards/lists/details are readable, reject has second confirmation, and approve is single-tap. WADM1 has completed the self-owned archive/mirror data-model design. WARCH1 has implemented and publicly verified the minimum leave/missed-punch archive slice: Prisma archive/callback/compensation/finding tables, callback DB persistence with non-200 retry on DB failure, compensation persistence behind `--persist-to-db` + `WECOM_ARCHIVE_WRITE=1`, and offline `npm run check:wecom-approval-archive`; public reports `/app/artifacts/qa-flow/wecom-approval-compensation-poc-20260519T160306Z/summary.json` and `/app/artifacts/qa-flow/wecom-approval-archive-20260519T160330Z/summary.json` are `PASS_WITH_FINDINGS`, with 2 archived histories readable and only a historical pre-WARCH1 callback gap finding. WARCH2 has now added the formal OA read-only archive API/page: `/api/wecom/approval-archive`, `/wecom-approval-archive`, and `/mobile/wecom-approval-archive` read OA DB only, do not call WeCom API or read WeCom Secrets, and enforce self/direct-report/HR-admin visibility with unmapped applicant records HR/admin-only. WROAD2+OPS is the controlled launch gate: use test accounts `SMOKE002 -> SMOKE101`, observe/admin-alert via employee_no `HYD000006` (the employee id for `wangshunli`), run daily checks for 3 days, allow only compensation archive writes to OA DB via `WECOM_ARCHIVE_WRITE=1`, and do not create WeCom templates/approvals, write WeCom, or expand real users. Day 0 public report `/app/artifacts/qa-flow/wroad2-ops-20260520-041941/summary.json` is `PASS_WITH_FINDINGS`: WeCom runtime, compensation archive persistence, offline archive read, and privacy smoke pass; findings are missing live-smoke login credentials/pending OCR subcheck and historical callback gap. It still does not implement full mirror, outing/overtime, emergency mode, writeback, or RuleOps. 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. Do not start `wecom_attendance_approval_mirror_v1`, `oa_hr_emergency_mode_v1`, `wecom_recovery_writeback_v1`, or `wecom_ruleops_v1` without WROAD2-CLOSEOUT, WREAL1 question gates or explicit approval, and the next stage's own Issue CSV and acceptance gates.
|
||||
- WARCH3/WCHECK1/WEXT1 update the route after WROAD2 Day 0: WARCH3 adds `scripts/qa/run-warch3-ops-daily.sh`, `deploy/trialrun/install-warch3-ops-timer.sh`, and `npm run send:wecom-ops-alert` for 07:30 systemd巡检和脱敏企微告警;public report `/app/artifacts/qa-flow/warch3-ops-20260520-054841/summary.json` is `PASS_WITH_FINDINGS`, and WOPS1 now fixes alert noise: default recipient is `HYD000006` (the employee_no for `wangshunli`), repeated known findings are skipped via `artifacts/qa-flow/warch3-ops-alert-state.json`, and only blocked/failure/new-finding/status-worsening/force sends a message. WOPS1 public rerun `/app/artifacts/qa-flow/warch3-ops-20260520-070731/summary.json` is `PASS_WITH_FINDINGS`, and alert report `/app/artifacts/qa-flow/wecom-ops-alert-20260520T070740Z/summary.json` is `PASS_ALERT_SKIPPED_NO_FINDINGS` with `no_new_findings_or_status_worsening`. WCHECK1 adds `npm run check:wecom-checkin-summary-archive`, daily/monthly check-in summaries, and rule snapshots with `--persist-to-db` + `WECOM_CHECKIN_ARCHIVE_WRITE=1` as the only OA DB write gate; public reports `/app/artifacts/qa-flow/wecom-checkin-summary-archive-20260520T054907Z/summary.json` and `/app/artifacts/qa-flow/wecom-checkin-summary-archive-20260520T054926Z/summary.json` are `PASS_CHECKIN_ARCHIVE_READY` and `PASS_CHECKIN_ARCHIVE_OFFLINE_READY`. WEXT1 only extends approval fixtures for `--target=outing --target=overtime` dry-run; public report `/app/artifacts/qa-flow/wecom-approval-fixtures-20260520T054940Z/summary.json` is `PASS_FIXTURE_DRY_RUN_READY`. These stages still do not create WeCom templates/approvals, expand real users, or implement full mirror/emergency/writeback/RuleOps. Next real-user step is WREAL1 only after WROAD2-CLOSEOUT Day 1/2/3 evidence and WREAL1-PREP/manual question gates; WREAL1-PREP itself does not enable real employees.
|
||||
- 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`. WREAD1F then fixed duplicate OA-POC template names by adding timestamp uniqueness and created fresh leave/missed-punch approvals at `/app/artifacts/qa-flow/wecom-approval-fixtures-20260519T131958Z/summary.json`; final WREAD1 manual report `/app/artifacts/qa-flow/wecom-approval-readability-gate-20260519T133628Z/summary.json` is `PASS_READABLE`. This clears leave/missed-punch readability only; mirror data model, minimum archive, emergency mode, writeback, RuleOps, and outing/overtime sample decisions remain later gates.
|
||||
- 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. WARCH1 now supplies the minimum leave/missed-punch archive implementation, WARCH2 supplies the formal read-only history query surface, and WROAD2 supplies controlled ops daily checks. Do not start deep mirror until WROAD2+OPS, WARCH3, WCHECK1, WEXT1 decision, and the mirror stage's own contract are complete.
|
||||
- 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. WARCH1 now supplies the minimum leave/missed-punch archive implementation, WARCH2 supplies the formal read-only history query surface, and WROAD2 supplies controlled ops daily checks. Do not start deep mirror until WROAD2-CLOSEOUT, WREAL1 question gates or explicit approval, and the mirror stage's own contract are complete.
|
||||
- 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 how this skill should evolve, read `docs/Agent开发交付实践.md`.
|
||||
@@ -109,8 +109,8 @@ Final responses should include:
|
||||
- R6 scope defaults to reimbursement, leave, procurement, approval, Enterprise WeChat entry/notification, finance payment/export.
|
||||
- Do not expand to budget, ERP, AI, full travel, or complex BPM during R6; record those as follow-up Issues.
|
||||
- Enterprise WeChat personal app message is the formal approval notification channel after R5; mock and group bots are not acceptance evidence for R6 business notification.
|
||||
- After WAPI, ERP is not a future OA integration target. HR/attendance employee daily approvals (leave, missed punch, outing, overtime) should reuse mature Enterprise WeChat capabilities first, while OA provides self-owned archive, domain-based mirror, manual takeover, recovery writeback, finance closeout, and audit. Reimbursement, payment batches, AP, accounting handoff, procurement, expenses, contracts, projects, sensitive attachments, invoices, bank receipts, and accounting handoff materials remain OA-primary unless a later Issue changes that domain explicitly. OA-primary approvals may send Enterprise WeChat personal-app notifications, but messages must contain only dehydrated title/type/no/status/deeplink; do not upload procurement forms, contract bodies, quotes, supplier files, invoices, bank receipts, or accounting materials to WeCom native approvals or WeCom attachments. Current WAPI rule: contacts read with `WECOM_CONTACT_SECRET`; HR assistant/roster/check-in/approval default to `WECOM_AGENT_SECRET`, with `WECOM_CHECKIN_SECRET` only as explicit check-in override; no runtime Secret guessing. Current stage order is `WARCH3 归档稳定化自动巡检 -> WCHECK1 打卡摘要留存 -> WEXT1 外出/加班 dry-run -> WREAL1 小范围真实试用 -> wecom_attendance_approval_mirror_v1 -> oa_hr_emergency_mode_v1 -> wecom_recovery_writeback_v1 -> wecom_ruleops_v1`. UI and API work must distinguish `OA 主办`, `企微主办`, `OA 自建留存`, and `OA 应急接管`. Do not treat WEXT1 dry-run as permission to live-create outing/overtime templates or approvals; that needs a new Issue CSV and explicit user approval.
|
||||
- Current post-WROAD2 route wording is: `WARCH3 归档稳定化自动巡检 -> WCHECK1 打卡摘要留存 -> WEXT1 外出/加班 dry-run -> WREAL1 小范围真实试用 -> wecom_attendance_approval_mirror_v1`. Do not treat WEXT1 dry-run as permission to live-create outing/overtime templates or approvals; that needs a new Issue CSV and explicit user approval.
|
||||
- After WAPI, ERP is not a future OA integration target. HR/attendance employee daily approvals (leave, missed punch, outing, overtime) should reuse mature Enterprise WeChat capabilities first, while OA provides self-owned archive, domain-based mirror, manual takeover, recovery writeback, finance closeout, and audit. Reimbursement, payment batches, AP, accounting handoff, procurement, expenses, contracts, projects, sensitive attachments, invoices, bank receipts, and accounting handoff materials remain OA-primary unless a later Issue changes that domain explicitly. OA-primary approvals may send Enterprise WeChat personal-app notifications, but messages must contain only dehydrated title/type/no/status/deeplink; do not upload procurement forms, contract bodies, quotes, supplier files, invoices, bank receipts, or accounting materials to WeCom native approvals or WeCom attachments. Current WAPI rule: contacts read with `WECOM_CONTACT_SECRET`; HR assistant/roster/check-in/approval default to `WECOM_AGENT_SECRET`, with `WECOM_CHECKIN_SECRET` only as explicit check-in override; no runtime Secret guessing. Current stage order is `WOPS1 巡检告警降噪 -> WROAD2-CLOSEOUT 三天验收 -> WREAL1-PREP -> WREAL1 小范围真实试用 -> wecom_attendance_approval_mirror_v1 -> oa_hr_emergency_mode_v1 -> wecom_recovery_writeback_v1 -> wecom_ruleops_v1`. UI and API work must distinguish `OA 主办`, `企微主办`, `OA 自建留存`, and `OA 应急接管`. Do not treat WEXT1 dry-run as permission to live-create outing/overtime templates or approvals; that needs a new Issue CSV and explicit user approval.
|
||||
- Current post-WROAD2 route wording is: `WOPS1 巡检告警降噪 -> WROAD2-CLOSEOUT 三天验收 -> WREAL1-PREP -> WREAL1 小范围真实试用 -> wecom_attendance_approval_mirror_v1`. Do not treat WEXT1 dry-run as permission to live-create outing/overtime templates or approvals; that needs a new Issue CSV and explicit user approval.
|
||||
- WMSG1 has verified real Enterprise WeChat personal-app delivery and Deeplink return: report `/app/artifacts/qa-flow/wecom-runtime-ready-20260518T234431Z/summary.json` has `checks.message.status=OK`, `errcode=0`, and `recipientCount=1`; the user confirmed receiving "OA 运行就绪检查" and opening the OA notifications page from the message. This only clears message/deeplink runtime evidence; template/detail, callback evidence, compensation pull, readability, self-owned archive model design, and minimum archive implementation are covered by WAPC2FLIVE/WCBK1/WACP3R/WREAD1/WADM1/WARCH1 in the OA-POC leave/missed-punch scope, while deep mirror work remains a separate later gate.
|
||||
- WAPC1 has fixed the WeCom approval configuration preparation gate: first templates are leave, missed punch, outing, and overtime; each needs template ID, scope, fields, approver rules, visible scope, and self-built app permission; low-risk test `sp_no` must not contain procurement, contract, reimbursement, invoice, bank receipt, sensitive attachment, or real sensitive business material; callback URL/Token/EncodingAESKey are deployment-only values and must not be written to repo docs or Issue CSV. Do not implement real approval callback handling, deep mirror, emergency mode, recovery writeback, or RuleOps until a later approved Issue covers the POC or implementation.
|
||||
- WAPC2 has added the WeCom approval template/detail read-only POC runner for leave and missed punch. Use `npm run check:wecom-approval-poc`; pass template IDs with `--template=leave:<id>` / `--template=missed_punch:<id>` and low-risk test approval numbers with `--sp-no=leave:<sp_no>` / `--sp-no=missed_punch:<sp_no>`. The runner must use only `WECOM_AGENT_SECRET`, redact full template IDs and `sp_no`, and never output form values, attachments, comments, full userid, token, Secret, or raw payload.
|
||||
|
||||
+15
-2
@@ -65,7 +65,7 @@ DCL7 已删除本文件中 R70-R91 的长流水历史,避免旧环境、旧开
|
||||
- 2026-05-19 WADM1 完成企微自建留存/镜像数据模型设计:本轮只更新 Issue CSV 和事实源,不改 Prisma schema、不写业务代码、不部署、不写 OA/企微业务数据。设计固定 `WecomApprovalArchive`、参与人/动作/字段摘要、附件摘要、回调事件、补偿拉取 run/item、对账 finding、打卡/人事快照复用边界、隐私保留、权限和迁移回滚策略。WACP3R “回调证据文件未持久化”的 finding 进入 WARCH1 入库实现;同意单击即通过进入 WARCH1 历史留存、审批轨迹和操作规程提示。该阶段已由下一条 WARCH1 承接,仍不能直接进入完整审批镜像、应急接管、恢复回写或 RuleOps。
|
||||
- 2026-05-19 WARCH1 实现企微审批最小自建历史留存:新增审批留存 Prisma 表、回调事件表、补偿 run/item/finding、`wecomApprovalArchive` 规范化服务、回调 DB 入库默认路径、补偿拉取 `--persist-to-db` + `WECOM_ARCHIVE_WRITE=1` 双开关和 `npm run check:wecom-approval-archive` 离线读取 runner。已以 `SYNC_ENV=0` 部署并应用 migration;远端补偿持久化报告 `/app/artifacts/qa-flow/wecom-approval-compensation-poc-20260519T160306Z/summary.json` 为 `PASS_WITH_FINDINGS`,`archivePersistence.status=OK`、`storedCount=2`;离线读取报告 `/app/artifacts/qa-flow/wecom-approval-archive-20260519T160330Z/summary.json` 为 `PASS_WITH_FINDINGS`,可读请假/补卡 2 条已通过历史。finding 是 WARCH1 前旧回调未持久化到 DB 的历史 gap,已作为对账 finding 留存,不是当前写入链路阻断。范围仅请假/补卡;不写企微数据、不创建模板或审批单、不新增正式管理页、不进入完整镜像、应急接管、恢复回写或 RuleOps。报告和日志只输出脱敏 `sp_no`、template、userid 前缀,DB 内保存完整外部键用于审计和补偿。
|
||||
- 2026-05-20 WARCH2 实现并部署企微审批历史正式只读查询:新增 `/api/wecom/approval-archive` 列表和详情 API、PC/移动共用历史页、备用模块入口和移动考勤卡片入口。查询只读 OA DB,不调用企微 API、不读取企微 Secret;普通员工看本人相关,主管看直属下属和本人相关,HR/admin/* 看全量;`leave.view_all` / `attendance.view_all` 不单独代表全量企微归档权限;申请人未映射、callback-only 或外部-only 记录仅 HR/admin/* 可见。页面固定“企业微信主办,OA 自建留存”“仅已归档记录”,不展示审批按钮或 pending 待办语义。已以 `SYNC_ENV=0` 部署,公网 `/ready` 正常,新 API 未登录返回 401;远端离线归档报告 `/app/artifacts/qa-flow/wecom-approval-archive-20260520T013610Z/summary.json` 为 `PASS_WITH_FINDINGS`,可读 2 条请假/补卡历史,finding 仍为 WARCH1 前旧回调未入库历史 gap。
|
||||
- 2026-05-20 WROAD2+OPS 建立受控上线与每日巡检合同:当前不直接进入完整镜像,改为测试账号 `SMOKE002 -> SMOKE101 + wangshunli` 3 天受控试用,每日手动触发 `scripts/qa/run-wroad2-ops-daily.sh`。脚本复用公网 `/ready`、live smoke、feature flag、企微 runtime、审批补偿和 OA 归档离线读取;默认只读,唯一允许写入是补偿归档以进程级 `WECOM_ARCHIVE_WRITE=1` 写 OA 自建留存库,不写企业微信、不创建模板或审批单、不安装 cron/systemd timer。Day 0 公网巡检报告待 WROAD2 部署执行后回填。
|
||||
- 2026-05-20 WROAD2+OPS 建立受控上线与每日巡检合同:当前不直接进入完整镜像,改为测试账号 `SMOKE002 -> SMOKE101` 3 天受控试用,管理员观察和告警员工号使用 `HYD000006`(`wangshunli` 的员工号),每日触发 `scripts/qa/run-wroad2-ops-daily.sh` 或 WARCH3 timer。脚本复用公网 `/ready`、live smoke、feature flag、企微 runtime、审批补偿和 OA 归档离线读取;默认只读,唯一允许写入是补偿归档以进程级 `WECOM_ARCHIVE_WRITE=1` 写 OA 自建留存库,不写企业微信、不创建模板或审批单。Day 0 公网巡检报告 `/app/artifacts/qa-flow/wroad2-ops-20260520-041941/summary.json` 为 `PASS_WITH_FINDINGS`,finding 按 observation 管理。
|
||||
|
||||
## 关键证据索引
|
||||
|
||||
@@ -460,13 +460,26 @@ Go 结论边界:
|
||||
|
||||
| 项 | 当前结果 | 证据和边界 |
|
||||
| --- | --- | --- |
|
||||
| WARCH3 自动巡检 | 公网已验证,`PASS_WITH_FINDINGS` | 新增 `scripts/qa/run-warch3-ops-daily.sh`、`deploy/trialrun/install-warch3-ops-timer.sh` 和 `npm run send:wecom-ops-alert`;服务器已安装 `hyb-oa-warch3-ops.timer`,下次触发为 2026-05-21 07:30 CST;手动 service 报告 `/app/artifacts/qa-flow/warch3-ops-20260520-054841/summary.json` 为 `PASS_WITH_FINDINGS`,告警报告 `/app/artifacts/qa-flow/wecom-ops-alert-20260520T054851Z/summary.json` 为 `PASS_ALERT_SENT_WITH_CONFIG_GAPS`,已向有效管理员 `HYD000006` 发送脱敏消息,`wangshunli` 仅作为非员工号别名 gap 记录 |
|
||||
| WARCH3 自动巡检 | 公网已验证,`PASS_WITH_FINDINGS` | 新增 `scripts/qa/run-warch3-ops-daily.sh`、`deploy/trialrun/install-warch3-ops-timer.sh` 和 `npm run send:wecom-ops-alert`;服务器已安装 `hyb-oa-warch3-ops.timer`,下次触发为 2026-05-21 07:30 CST;手动 service 报告 `/app/artifacts/qa-flow/warch3-ops-20260520-054841/summary.json` 为 `PASS_WITH_FINDINGS`,告警报告 `/app/artifacts/qa-flow/wecom-ops-alert-20260520T054851Z/summary.json` 为 `PASS_ALERT_SENT_WITH_CONFIG_GAPS`,已向管理员 `wangshunli` 的员工号 `HYD000006` 发送脱敏消息;后续脚本不再把姓名字符串 `wangshunli` 当作 employee_no 使用 |
|
||||
| WCHECK1 打卡摘要留存 | 公网已验证,live/offline 均通过 | 新增打卡日/月摘要、规则快照、`wecomCheckinArchive` 服务和 `npm run check:wecom-checkin-summary-archive`;live 报告 `/app/artifacts/qa-flow/wecom-checkin-summary-archive-20260520T054907Z/summary.json` 为 `PASS_CHECKIN_ARCHIVE_READY`,本窗口 0 条打卡明细、2 条规则快照已写 OA 留存;offline 报告 `/app/artifacts/qa-flow/wecom-checkin-summary-archive-20260520T054926Z/summary.json` 为 `PASS_CHECKIN_ARCHIVE_OFFLINE_READY` |
|
||||
| WEXT1 外出/加班 | 公网 dry-run 通过,live 未批准 | `npm run check:wecom-approval-fixtures` 支持 `--target=outing --target=overtime`;公网报告 `/app/artifacts/qa-flow/wecom-approval-fixtures-20260520T054940Z/summary.json` 为 `PASS_FIXTURE_DRY_RUN_READY`;本轮只构建最小通用模板和审批申请 payload,不创建企微模板、不提交审批单 |
|
||||
| 隐私边界 | 保持 | 报告和告警只输出状态、计数、报告路径、脱敏 userid/template/sp_no 前缀;不输出手机号、邮箱、token、Secret、完整外部键、位置、人脸、设备明文、表单值、附件、评论或原始 payload |
|
||||
|
||||
上述能力仍不代表完整审批镜像、应急接管、恢复回写或 RuleOps 已完成。WREAL1 小范围真实试用需等 WROAD2 三天验收、WARCH3/WCHECK1/WEXT1 公网结果可解释后再单独问答确认。
|
||||
|
||||
## WOPS1 / WROAD2-CLOSEOUT / WREAL1-PREP 后续规划
|
||||
|
||||
执行时间:2026-05-20。基于 WARCH3/WCHECK1/WEXT1 后的受控上线结果,当前不扩大真实员工、不创建外出/加班 live 样件,先处理巡检告警降噪、三天验收和真实试用准备。
|
||||
|
||||
| 项 | 当前结果 | 证据和边界 |
|
||||
| --- | --- | --- |
|
||||
| WOPS1 巡检告警降噪 | 公网已验证 | 告警默认收件员工号修正为 `HYD000006`,即管理员 `wangshunli` 的员工号;不再把 `wangshunli` 字符串当作 employee_no。`send-wecom-ops-alert` 增加状态文件,首次 `PASS_WITH_FINDINGS` 记录为基线;远端二次触发 WARCH3 报告 `/app/artifacts/qa-flow/warch3-ops-20260520-070731/summary.json` 为 `PASS_WITH_FINDINGS`,告警报告 `/app/artifacts/qa-flow/wecom-ops-alert-20260520T070740Z/summary.json` 为 `PASS_ALERT_SKIPPED_NO_FINDINGS`,原因 `no_new_findings_or_status_worsening`。后续只有阻断、脚本失败、新增 finding、状态恶化或人工 `--force` 才发送脱敏消息。 |
|
||||
| WROAD2-CLOSEOUT 三天验收 | 待 Day 1/2/3 自动采集 | Day 0 报告 `/app/artifacts/qa-flow/wroad2-ops-20260520-041941/summary.json` 为 `PASS_WITH_FINDINGS`。已知 observation 为 live smoke 凭据、pending OCR 子检查和历史 callback gap,不阻断受控试用;企微 runtime、审批补偿归档、OA 离线读取、打卡摘要 offline 可读和隐私扫描仍是主链路。 |
|
||||
| WREAL1-PREP 真实试用准备 | 仅规划,不启用真实员工 | 固化候选员工 active/已绑定/可见、审批人非本人、回滚联系人、允许流程、暂停条件和验收标准。当前继续测试账号;首批真实试用待 WROAD2-CLOSEOUT 后再问答确认,默认只覆盖请假、补卡、打卡摘要和 OA 历史查看。 |
|
||||
| 外出/加班 | 继续暂缓 live | WEXT1 dry-run 证据保留;不创建企微外出/加班模板或审批单。若后续要 live 创建,必须另建合同并再次问答批准。 |
|
||||
|
||||
三天验收结束后的人工问答选项固定为:确认进入真实试用、先处理指定 finding、或暂停。进入真实员工试用前还需单独确认试用人数、审批人、周期、回滚联系人和暂停条件。
|
||||
|
||||
## 历史恢复
|
||||
|
||||
需要追溯 R70-R91 的详细流水时,使用:
|
||||
|
||||
+20
-11
@@ -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 / WREAD1 / WREAD1F / WADM1 / WARCH1 / WARCH2 / WROAD2 / WARCH3 / WCHECK1 / WEXT1 之后。
|
||||
**关联 Issue**:`WDRG,WCP1,WHMS,WHMSLIVE,WOUA,WAPI,WRRA,WOUAFIX,WRV1,WRA1,WMSG1,WAPC1,WAPC2,WPROC1,WPROC2,WAPC2F,WAPC2FLIVE,WCBK1,WEGOV1,WACP3,WACP3R,WREAD1,WREAD1F,WADM1,WARCH1,WARCH2,WROAD2,WARCH3,WCHECK1,WEXT1`。
|
||||
**适用时间**:WDRG / WCP1 / WHMSLIVE / WOUA / WAPI / WRRA / WOUA-FIX / WRV1 / WRA1 / WMSG1 / WAPC1 / WAPC2 / WPROC1 / WPROC2 / WAPC2F / WAPC2FLIVE / WCBK1 / WEGOV1 / WACP3 / WACP3R / WREAD1 / WREAD1F / WADM1 / WARCH1 / WARCH2 / WROAD2 / WARCH3 / WCHECK1 / WEXT1 / WOPS1 / WROAD2-CLOSEOUT / WREAL1-PREP 之后。
|
||||
**关联 Issue**:`WDRG,WCP1,WHMS,WHMSLIVE,WOUA,WAPI,WRRA,WOUAFIX,WRV1,WRA1,WMSG1,WAPC1,WAPC2,WPROC1,WPROC2,WAPC2F,WAPC2FLIVE,WCBK1,WEGOV1,WACP3,WACP3R,WREAD1,WREAD1F,WADM1,WARCH1,WARCH2,WROAD2,WARCH3,WCHECK1,WEXT1,WOPS1,WROAD2C,WREAL1P`。
|
||||
**最近审计**:2026-05-20。
|
||||
**不得误读**:本文只控制后续开发节奏、阶段顺序和门禁。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`。WREAD1F 新样例后,审批人真机确认请假和补卡消息卡片、列表、详情页均可看清;远端人工结果报告 `/app/artifacts/qa-flow/wecom-approval-readability-gate-20260519T133628Z/summary.json` 为 `PASS_READABLE`。但同意按钮单击即通过仍是操作风险,需在 WARCH1/WARCH2 的留存、审计、提示或操作规程中继续处理。WADM1 只完成自建留存/镜像数据模型设计;WARCH1 只实现请假/补卡最小审批历史留存、回调事件入库、补偿拉取入库和 QA-only 离线读取证明;WARCH2 只提供 OA DB 只读查询 API 和 PC/移动历史页,不调用企微 API、不读取企微 Secret、不代表完整镜像、外出/加班、应急接管、恢复回写或 RuleOps 已完成。WROAD2 后当前策略是测试账号受控上线和每日脚本化巡检,允许补偿归档写 OA 自建留存库,但不写企业微信、不扩大真实员工范围;WARCH3 将每日巡检升级为服务器 `systemd timer` 与脱敏企微告警,WCHECK1 增加打卡规范化明细、日/月摘要和规则快照留存,WEXT1 只完成外出/加班 dry-run 支持。上述阶段仍不代表完整深度镜像、应急接管、恢复回写或 RuleOps 已完成。
|
||||
**不得误读**:本文只控制后续开发节奏、阶段顺序和门禁。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`。WREAD1F 新样例后,审批人真机确认请假和补卡消息卡片、列表、详情页均可看清;远端人工结果报告 `/app/artifacts/qa-flow/wecom-approval-readability-gate-20260519T133628Z/summary.json` 为 `PASS_READABLE`。但同意按钮单击即通过仍是操作风险,需在 WARCH1/WARCH2 的留存、审计、提示或操作规程中继续处理。WADM1 只完成自建留存/镜像数据模型设计;WARCH1 只实现请假/补卡最小审批历史留存、回调事件入库、补偿拉取入库和 QA-only 离线读取证明;WARCH2 只提供 OA DB 只读查询 API 和 PC/移动历史页,不调用企微 API、不读取企微 Secret、不代表完整镜像、外出/加班、应急接管、恢复回写或 RuleOps 已完成。WROAD2 后当前策略是测试账号受控上线和每日脚本化巡检,允许补偿归档写 OA 自建留存库,但不写企业微信、不扩大真实员工范围;WARCH3 将每日巡检升级为服务器 `systemd timer` 与脱敏企微告警,WCHECK1 增加打卡规范化明细、日/月摘要和规则快照留存,WEXT1 只完成外出/加班 dry-run 支持。WOPS1 已公网验证巡检告警降噪:`HYD000006` 是管理员 `wangshunli` 的 employee_no,脚本不再把姓名字符串当员工号;已知 finding 不重复发送,只有阻断、脚本失败、新增 finding 或状态恶化才告警。WREAL1-PREP 只准备真实小范围试用清单,不启用真实员工。上述阶段仍不代表完整深度镜像、应急接管、恢复回写或 RuleOps 已完成。
|
||||
|
||||
## 1. 路线图原则
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
- OA 不重造企业微信完整打卡、考勤和 HR 产品;V1 目标是企微不可用时可以查看已保存历史,不是完整替代企微继续办理。
|
||||
- WROAD2 后“可受控使用”和“完整镜像增强”分离:请假/补卡已归档历史可在 OA 查询,先用测试账号做 3 天每日脚本化巡检;完整镜像、应急接管、恢复回写和 RuleOps 继续后置。
|
||||
- 外出/加班不作为当前受控上线阻断,默认延期到 `WEXT1`;打卡留存 V1 先做日/月摘要、异常摘要和规则快照,不保存完整位置、人脸、设备明文或原始 payload。
|
||||
- WOPS1 后巡检告警采用“状态记忆 + 新增异常”策略:既有 `PASS_WITH_FINDINGS` observation 进入基线后不重复发企微消息;只有 `SKIP/BLOCKED_BY_EXTERNAL_CONFIG`、脚本失败、新增 finding、状态恶化或人工 `--force` 才发送脱敏告警。
|
||||
- WREAL1-PREP 只做真实小范围试用准备,当前继续测试账号;真实员工名单、试用周期、回滚联系人和验收标准必须在 WROAD2-CLOSEOUT 后通过问答确认。
|
||||
- 企业微信自建应用不按完整隔离 sandbox 处理;后续默认使用真实企业内受控测试域、本地 synthetic 自动测试、公网 live runner 和少量真机确认。
|
||||
- 阶段未满足退出条件前不得跳阶段。线上缺陷、安全风险、正式使用阻断或真实反馈可以另开独立 Issue CSV,但不得混入企微 HR/考勤主路线。
|
||||
- ERP 未来不接入 OA;真实支付、图片 OCR、银行流水导入、复杂 BPM、审批设计器、重预算和其他 P2 大功能继续冻结,试用期只处理阻断和真实反馈。
|
||||
@@ -51,10 +53,13 @@
|
||||
| `WADM1` | 自建留存/镜像模型设计已固化 | docs-only 固定 OA 留存域数据模型:企微审批主表、参与人/节点/动作、字段摘要、附件摘要、回调事件、补偿拉取 run/item、对账 finding、打卡/人事快照边界、隐私保留、权限和 WARCH1 最小实现切片。WADM1 解决 WACP3R “回调证据文件未持久化”的模型设计缺口,但不创建迁移、不写入业务数据、不放行完整镜像。 |
|
||||
| `WARCH1` | 最小审批历史留存已实现并公网验证 | 新增 `WecomApprovalArchive` 等审批留存表、回调事件表、补偿拉取 run/item/finding 表;回调 POST 默认写 OA DB,失败返回非 200 让企微重试;补偿 runner 支持 `--persist-to-db` + `WECOM_ARCHIVE_WRITE=1` 双开关写 OA 留存;新增 `npm run check:wecom-approval-archive` 离线读取 OA DB 历史摘要。公网报告 `/app/artifacts/qa-flow/wecom-approval-compensation-poc-20260519T160306Z/summary.json` 与 `/app/artifacts/qa-flow/wecom-approval-archive-20260519T160330Z/summary.json` 均为 `PASS_WITH_FINDINGS`:请假/补卡 2 条历史可离线读取,finding 是 WARCH1 前旧回调未入 DB 的历史 gap,已作为对账 finding 留存。范围仅请假/补卡,不写企微、不做完整镜像、不做应急接管或恢复回写。 |
|
||||
| `WARCH2` | 已部署并公网只读验证 | 新增只读 `GET /api/wecom/approval-archive` 和 `GET /api/wecom/approval-archive/:id`,以及 PC/移动共用历史页 `/wecom-approval-archive`、`/mobile/wecom-approval-archive`。查询只读 OA DB,不调用企微 API、不读取企微 Secret;权限固定为本人相关、直属下属和 HR/admin/* 全量,`leave.view_all` / `attendance.view_all` 不单独代表全量企微归档权限;申请人未映射、callback-only、外部-only 记录只允许 HR/admin/* 查看。页面文案固定“企业微信主办,OA 自建留存”“仅已归档记录”,不提供审批动作。公网 `SYNC_ENV=0` 部署已完成,`/ready` 正常,新 API 未登录返回 401;远端离线归档报告 `/app/artifacts/qa-flow/wecom-approval-archive-20260520T013610Z/summary.json` 为 `PASS_WITH_FINDINGS`,可读 2 条请假/补卡历史,finding 仍是 WARCH1 前旧回调未入库历史 gap。 |
|
||||
| `WROAD2+OPS` | 受控上线和每日巡检 Day 0 已完成 | 当前进入测试账号受控上线:范围固定 `SMOKE002 -> SMOKE101 + wangshunli`,试用 3 天,每日触发一键巡检脚本。巡检复用 `/ready`、live smoke、feature flag、企微 runtime、审批补偿和 OA 归档离线读取;仅补偿归档步骤可用进程级 `WECOM_ARCHIVE_WRITE=1` 写 OA 自建留存库,不写企业微信、不创建模板或审批单。Day 0 报告 `/app/artifacts/qa-flow/wroad2-ops-20260520-041941/summary.json` 为 `PASS_WITH_FINDINGS`,finding 为缺 live smoke 登录凭据/pending OCR 子检查异常以及历史 callback gap,不阻断测试账号受控试用。 |
|
||||
| `WARCH3` | 归档稳定化自动巡检已实现 | 新增 `scripts/qa/run-warch3-ops-daily.sh` 聚合 WROAD2 子报告、归类 observation/finding、解释历史 callback gap,并新增 `deploy/trialrun/install-warch3-ops-timer.sh` 安装服务器 `systemd service/timer`,每天 07:30 触发;巡检失败或 finding 时可通过 `npm run send:wecom-ops-alert` 向 `wangshunli/HYD000006` 发送脱敏企微应用消息。仍只允许补偿归档写 OA DB,不写企业微信、不创建模板或审批单。 |
|
||||
| `WROAD2+OPS` | 受控上线和每日巡检 Day 0 已完成 | 当前进入测试账号受控上线:范围固定 `SMOKE002 -> SMOKE101`,管理员观察和告警员工号固定 `HYD000006`,即管理员 `wangshunli` 的 employee_no;脚本不再把姓名字符串 `wangshunli` 当员工号。试用 3 天,每日触发一键巡检脚本。巡检复用 `/ready`、live smoke、feature flag、企微 runtime、审批补偿和 OA 归档离线读取;仅补偿归档步骤可用进程级 `WECOM_ARCHIVE_WRITE=1` 写 OA 自建留存库,不写企业微信、不创建模板或审批单。Day 0 报告 `/app/artifacts/qa-flow/wroad2-ops-20260520-041941/summary.json` 为 `PASS_WITH_FINDINGS`,finding 为缺 live smoke 登录凭据/pending OCR 子检查异常以及历史 callback gap,按已知 observation 不阻断测试账号受控试用。 |
|
||||
| `WARCH3` | 归档稳定化自动巡检已实现 | 新增 `scripts/qa/run-warch3-ops-daily.sh` 聚合 WROAD2 子报告、归类 observation/finding、解释历史 callback gap,并新增 `deploy/trialrun/install-warch3-ops-timer.sh` 安装服务器 `systemd service/timer`,每天 07:30 触发;WOPS1 后巡检告警默认只发 `HYD000006`,由 `npm run send:wecom-ops-alert` 读取状态文件判断新增 finding 或状态恶化,已知 finding 不重复告警。仍只允许补偿归档写 OA DB,不写企业微信、不创建模板或审批单。 |
|
||||
| `WCHECK1` | 打卡摘要留存已实现 | 新增打卡日/月摘要和规则快照留存表、`wecomCheckinArchive` 服务和 `npm run check:wecom-checkin-summary-archive` runner。保存规范化打卡明细、日/月摘要、异常摘要、规则快照摘要和 hash;报告只输出计数、状态、异常摘要和脱敏 userid 前缀,不保存完整位置、人脸、设备明文或原始 payload。 |
|
||||
| `WEXT1` | 外出/加班 dry-run 已实现 | 扩展 `npm run check:wecom-approval-fixtures` 支持 `--target=outing --target=overtime`,只构建 OA-POC 外出/加班最小通用模板和低风险审批申请 payload,仍默认 dry-run。live 创建外出/加班模板或审批单前必须另起问答和 Issue CSV。 |
|
||||
| `WOPS1` | 巡检告警降噪已公网验证 | 将 WARCH3 告警从“有 finding 就发”改为“阻断、失败、新增 finding 或状态恶化才发”;默认收件员工号 `HYD000006`。远端二次触发报告 `/app/artifacts/qa-flow/wecom-ops-alert-20260520T070740Z/summary.json` 为 `PASS_ALERT_SKIPPED_NO_FINDINGS`,原因 `no_new_findings_or_status_worsening`;状态文件只保存脱敏状态、finding 摘要和报告路径。 |
|
||||
| `WROAD2-CLOSEOUT` | 三天验收采集中 | Day 1/2/3 继续由 WARCH3 timer 采集;已知 observation(live smoke 凭据、pending OCR、历史 callback gap)可接受但要保留趋势;三天结束后按问答确认 `PASS_CONTROLLED_OPS_READY`、先处理 finding 或暂停。 |
|
||||
| `WREAL1-PREP` | 真实小范围试用准备中 | 只固化候选员工条件、非自审审批人条件、回滚联系人、允许流程、暂停条件和验收标准;当前不引入真实员工,外出/加班 live 继续暂缓。 |
|
||||
|
||||
## 3. 阶段路线
|
||||
|
||||
@@ -74,13 +79,17 @@
|
||||
| 12 | `WARCH1 最小自建历史留存实现` | 先保存企微审批最小历史,满足企微 API 不可用时可查看已保存请假/补卡摘要。 | WADM1 完成并批准 schema/接口实现。 | 已新增 OA DB 审批留存、回调事件、补偿 run/item/finding、双开关补偿入库和离线读取 runner;公网已持久化现有请假/补卡详情并离线读取 2 条已通过历史。报告和日志只输出脱敏前缀;不保存 Secret、token、EncodingAESKey、原始密文或无治理原始 payload。 |
|
||||
| 13 | `WARCH2 正式只读历史查询` | 把 WARCH1 runner 级离线证明升级为正式 OA API 和 PC/移动历史页。 | WARCH1 已持久化请假/补卡历史;权限与脱敏合同批准。 | 已完成:查询 API、PC/移动页面、`SYNC_ENV=0` 公网部署、`/ready`、未登录 401、离线归档 runner 均通过。V1 仍只覆盖请假/补卡,不调用企微 API,不提供审批动作。 |
|
||||
| 14 | `WROAD2+OPS 受控上线与每日巡检` | 把 WARCH2 后的能力先放入测试账号受控使用,用每日脚本化巡检验证入口、运行、补偿归档和 OA 离线查询稳定性。 | WARCH2 公网验证完成;用户选择先受控上线、不直接深镜像;允许 `SYNC_ENV=0` 部署和补偿归档写 OA DB。 | Day 0 公网巡检已生成 `/app/artifacts/qa-flow/wroad2-ops-20260520-041941/summary.json`,结论 `PASS_WITH_FINDINGS`;后续以 WARCH3 systemd timer 承接每日巡检。脚本不写企业微信、不创建样件;发现缺配置时真实记录 `SKIP/BLOCKED_BY_EXTERNAL_CONFIG`。 |
|
||||
| 15 | `WARCH3 归档稳定化` | 稳定每日补偿、归档对账 finding、历史查询可用性、旧 callback gap 解释和报告趋势。 | WROAD2+OPS Day 0 和用户批准 systemd timer。 | 已新增 WARCH3 汇总脚本、脱敏企微告警和 07:30 systemd timer 安装脚本;每日归档报告可解释,live smoke/OCR 仅作 observation。公网安装和 Day 1/2/3 汇总仍按 WARCH3/WROAD2 证据回填。 |
|
||||
| 15 | `WARCH3 归档稳定化` | 稳定每日补偿、归档对账 finding、历史查询可用性、旧 callback gap 解释和报告趋势。 | WROAD2+OPS Day 0 和用户批准 systemd timer。 | 已新增 WARCH3 汇总脚本、脱敏企微告警和 07:30 systemd timer 安装脚本;WOPS1 后默认收件员工号为 `HYD000006`,已知 finding 不重复告警;每日归档报告可解释,live smoke/OCR 仅作 observation。公网安装和 Day 1/2/3 汇总仍按 WARCH3/WROAD2 证据回填。 |
|
||||
| 16 | `WCHECK1 打卡摘要留存` | 保存规范化打卡明细、日/月摘要、异常摘要和规则快照。 | WROAD2+OPS 或 WARCH3 证明审批归档稳定;打卡只读 API 和权限仍可用。 | 已新增摘要表、规则快照、归档服务和 QA runner;OA 可查看必要打卡摘要和异常摘要;不保存完整位置、人脸、设备明文或原始 payload。 |
|
||||
| 17 | `WEXT1 外出/加班 dry-run` | 自动生成外出/加班低风险 POC 样件 payload,但不 live 创建。 | 用户批准先做 dry-run。 | 已扩展 WAPC2F runner 支持 `outing`、`overtime` dry-run;非自审、Secret 固定和脱敏报告沿用既有门禁。live 创建模板/审批单前必须再次问答批准。 |
|
||||
| 18 | `wecom_attendance_approval_mirror_v1` | 企微打卡、请假、补卡、外出、加班、假期余额和审批历史镜像到 OA。 | WARCH3、WCHECK1 和外出/加班决策完成;运行优先门禁和用户一致性仍满足。 | OA 可查询企微 HR/考勤镜像、附件摘要、审批历史、考勤解释和审计;不把 OA 主办高敏审批迁到企微。 |
|
||||
| 19 | `oa_hr_emergency_mode_v1` | 企业微信不可用时,由管理员人工开启 OA HR/考勤应急接管。 | 自建历史留存和镜像数据可支撑对账;OA 应急入口、权限和审计方案明确。 | 管理员可填写原因、范围、预计结束时间和责任人后开启/关闭;应急单据带 `EMERGENCY` 标记。 |
|
||||
| 20 | `wecom_recovery_writeback_v1` | 企微恢复后,OA 应急数据先 dry-run,再回写企微。 | 应急接管模式完成;企微写回接口、冲突规则和回滚策略已单独批准。 | 系统生成成功、失败、跳过和冲突清单;无冲突项可自动回写,冲突进入人工确认。 |
|
||||
| 21 | `wecom_ruleops_v1` | AI 草拟企微规则、生成差异报告和 dry-run,人工确认后写入企微。 | 前述同步/回写链路稳定;企微规则 API 能力和权限已验证。 | 每次写入保存前后快照、确认人、API `errcode`、失败原因、回滚建议和审计记录。 |
|
||||
| 18 | `WOPS1 巡检告警降噪` | 修正管理员告警收件人和重复 finding 告警策略。 | WARCH3 已公网验证,用户要求告警只报新增异常。 | 已公网验证:`/app/artifacts/qa-flow/warch3-ops-20260520-070731/summary.json` 为 `PASS_WITH_FINDINGS`,告警 `/app/artifacts/qa-flow/wecom-ops-alert-20260520T070740Z/summary.json` 为 `PASS_ALERT_SKIPPED_NO_FINDINGS`;默认收件员工号 `HYD000006`,重复已知 finding 不发送。 |
|
||||
| 19 | `WROAD2-CLOSEOUT 三天验收` | 汇总 Day 0/1/2/3 巡检报告,决定是否进入真实试用。 | WOPS1 本地和公网验证通过;WARCH3 timer 正常。 | 输出 `PASS_CONTROLLED_OPS_READY`、`PASS_WITH_FINDINGS`、`PAUSED_MANUAL_CONFIRMATION` 或 `SKIP/BLOCKED_BY_EXTERNAL_CONFIG`,再由用户问答确认。 |
|
||||
| 20 | `WREAL1-PREP 真实试用准备` | 固化候选员工、审批人、允许流程、暂停条件、回滚联系人和验收标准,不启用真实员工。 | 用户选择可以并行准备但暂不引入真实员工。 | 文档和合同明确首批真实试用只覆盖请假、补卡、打卡摘要和 OA 历史查看;外出/加班 live 暂缓。 |
|
||||
| 21 | `WREAL1 小范围真实试用` | 在 1-2 名真实员工范围验证企微主办 HR/考勤和 OA 留存。 | WROAD2-CLOSEOUT 用户确认通过,并通过问答提供真实员工、审批人、试用周期、暂停条件和回滚联系人。 | 试用报告可解释通过或暂停;仍不直接全员推广。 |
|
||||
| 22 | `wecom_attendance_approval_mirror_v1` | 企微打卡、请假、补卡、外出、加班、假期余额和审批历史镜像到 OA。 | WREAL1 或用户明确批准;WARCH3、WCHECK1 和外出/加班决策完成;运行优先门禁和用户一致性仍满足。 | OA 可查询企微 HR/考勤镜像、附件摘要、审批历史、考勤解释和审计;不把 OA 主办高敏审批迁到企微。 |
|
||||
| 23 | `oa_hr_emergency_mode_v1` | 企业微信不可用时,由管理员人工开启 OA HR/考勤应急接管。 | 自建历史留存和镜像数据可支撑对账;OA 应急入口、权限和审计方案明确。 | 管理员可填写原因、范围、预计结束时间和责任人后开启/关闭;应急单据带 `EMERGENCY` 标记。 |
|
||||
| 24 | `wecom_recovery_writeback_v1` | 企微恢复后,OA 应急数据先 dry-run,再回写企微。 | 应急接管模式完成;企微写回接口、冲突规则和回滚策略已单独批准。 | 系统生成成功、失败、跳过和冲突清单;无冲突项可自动回写,冲突进入人工确认。 |
|
||||
| 25 | `wecom_ruleops_v1` | AI 草拟企微规则、生成差异报告和 dry-run,人工确认后写入企微。 | 前述同步/回写链路稳定;企微规则 API 能力和权限已验证。 | 每次写入保存前后快照、确认人、API `errcode`、失败原因、回滚建议和审计记录。 |
|
||||
|
||||
## 4. 每阶段固定门禁
|
||||
|
||||
@@ -332,7 +341,7 @@ WARCH2 把 WARCH1 的离线 runner 证明升级为 OA 内可查历史,但仍
|
||||
|
||||
WROAD2+OPS 把 WARCH2 后的可用能力先进入测试账号受控使用,不等同于完整镜像增强:
|
||||
|
||||
- 范围:`SMOKE002 -> SMOKE101 + wangshunli`,3 天试用,每日一次触发 `scripts/qa/run-wroad2-ops-daily.sh` 或 WARCH3 systemd timer。
|
||||
- 范围:测试账号 `SMOKE002 -> SMOKE101`,管理员观察和告警员工号 `HYD000006`(`wangshunli` 的员工号),3 天试用,每日一次触发 `scripts/qa/run-wroad2-ops-daily.sh` 或 WARCH3 systemd timer。
|
||||
- 巡检:复用公网 `/ready`、live smoke、feature flag、企微 runtime、审批补偿拉取和 OA 归档离线读取 runner,输出 `artifacts/qa-flow/wroad2-ops-<timestamp>/summary.json`。
|
||||
- 写入边界:巡检默认只读;仅审批补偿归档步骤可用进程级 `WECOM_ARCHIVE_WRITE=1` 与 `--persist-to-db` 写 OA 自建留存库。不写企业微信、不创建模板、不提交审批单。
|
||||
- 结果边界:结论只允许 `PASS_CONTROLLED_OPS_READY`、`PASS_WITH_FINDINGS`、`PAUSED_MANUAL_CONFIRMATION` 或真实 `SKIP/BLOCKED_BY_EXTERNAL_CONFIG`。
|
||||
|
||||
+10
-7
@@ -90,10 +90,13 @@
|
||||
| `WADM1` | `issues/2026-05-19_21-55-33-wecom-archive-mirror-data-model-design-v1.csv` | `CURRENT` | 企微自建留存/镜像数据模型设计已完成:docs-only 固定审批留存、参与人/动作/字段摘要、附件摘要、回调事件、补偿拉取 run/item、对账 finding、打卡/人事快照复用边界、隐私权限和 WARCH1 最小实现切片;不改 Prisma schema、不部署、不写 OA/企微业务数据 |
|
||||
| `WARCH1` | `issues/2026-05-19_22-32-06-wecom-approval-archive-minimum-v1.csv` | `CURRENT` | 企微审批最小自建历史留存已实现并部署验证:新增 Prisma 留存表和 migration、回调事件默认入 OA DB、补偿拉取双开关入库、对账 finding 和 `check:wecom-approval-archive` 离线读取 runner;远端补偿持久化 `/app/artifacts/qa-flow/wecom-approval-compensation-poc-20260519T160306Z/summary.json` 为 `PASS_WITH_FINDINGS`,离线读取 `/app/artifacts/qa-flow/wecom-approval-archive-20260519T160330Z/summary.json` 为 `PASS_WITH_FINDINGS`,请假/补卡 2 条已通过历史可读;finding 为 WARCH1 前旧回调未入 DB 的历史 gap;范围仅请假/补卡,不写企微、不新增正式管理页、不进入完整镜像、应急接管、恢复回写或 RuleOps |
|
||||
| `WARCH2` | `issues/2026-05-20_00-26-04-wecom-approval-archive-query-v1.csv` | `CURRENT` | 企微审批正式只读历史查询已实现并部署验证:新增列表/详情 API、PC/移动历史页、备用模块入口和移动考勤卡入口;只读 OA DB,不调用企微 API、不读取企微 Secret;分层权限为本人相关、直属下属、HR/admin/*,申请人未映射和 callback-only 记录仅 HR/admin/* 可见;公网 `/ready` 正常,新 API 未登录返回 401,远端离线归档报告 `/app/artifacts/qa-flow/wecom-approval-archive-20260520T013610Z/summary.json` 为 `PASS_WITH_FINDINGS` 且可读 2 条请假/补卡历史 |
|
||||
| `WROAD2+OPS` | `issues/2026-05-20_11-30-22-wroad2-ops-controlled-launch-v1.csv` | `CURRENT` | 受控上线与每日巡检合同已建立并完成 Day 0 公网基线:范围固定测试账号 `SMOKE002 -> SMOKE101 + wangshunli`,3 天每日脚本化触发;脚本复用公网 `/ready`、live smoke、feature flag、企微 runtime、审批补偿和 OA 归档离线读取;仅补偿归档可写 OA 留存库,不写企业微信;Day 0 报告 `/app/artifacts/qa-flow/wroad2-ops-20260520-041941/summary.json` 为 `PASS_WITH_FINDINGS`,企微 runtime、补偿归档、离线归档读取和隐私扫描通过,finding 为缺 live smoke 登录凭据/pending OCR 子检查异常以及历史 callback gap,不阻断测试账号受控试用 |
|
||||
| `WROAD2+OPS` | `issues/2026-05-20_11-30-22-wroad2-ops-controlled-launch-v1.csv` | `CURRENT` | 受控上线与每日巡检合同已建立并完成 Day 0 公网基线:范围固定测试账号 `SMOKE002 -> SMOKE101`,管理员观察和告警员工号使用 `HYD000006`(`wangshunli` 的员工号),3 天每日脚本化触发;脚本复用公网 `/ready`、live smoke、feature flag、企微 runtime、审批补偿和 OA 归档离线读取;仅补偿归档可写 OA 留存库,不写企业微信;Day 0 报告 `/app/artifacts/qa-flow/wroad2-ops-20260520-041941/summary.json` 为 `PASS_WITH_FINDINGS`,企微 runtime、补偿归档、离线归档读取和隐私扫描通过,finding 为缺 live smoke 登录凭据/pending OCR 子检查异常以及历史 callback gap,不阻断测试账号受控试用 |
|
||||
| `WARCH3` | `issues/2026-05-20_12-48-04-wecom-archive-stabilization-ops-v1.csv` | `CURRENT` | 归档稳定化自动巡检合同已实现并公网验证:新增 `scripts/qa/run-warch3-ops-daily.sh` 聚合 WROAD2 子报告、解释 observation/finding,新增 `deploy/trialrun/install-warch3-ops-timer.sh` 安装每日 07:30 systemd timer,新增 `send:wecom-ops-alert` 脱敏企微告警;公网报告 `/app/artifacts/qa-flow/warch3-ops-20260520-054841/summary.json` 为 `PASS_WITH_FINDINGS`,告警 `/app/artifacts/qa-flow/wecom-ops-alert-20260520T054851Z/summary.json` 为 `PASS_ALERT_SENT_WITH_CONFIG_GAPS` |
|
||||
| `WCHECK1` | `issues/2026-05-20_12-48-04-wecom-checkin-summary-archive-v1.csv` | `CURRENT` | 打卡摘要留存合同已实现并公网验证:新增打卡日/月摘要和规则快照 Prisma 表、`wecomCheckinArchive` 服务和 `check:wecom-checkin-summary-archive` runner;live 报告 `/app/artifacts/qa-flow/wecom-checkin-summary-archive-20260520T054907Z/summary.json` 为 `PASS_CHECKIN_ARCHIVE_READY`,offline 报告 `/app/artifacts/qa-flow/wecom-checkin-summary-archive-20260520T054926Z/summary.json` 为 `PASS_CHECKIN_ARCHIVE_OFFLINE_READY`;不保存位置、人脸、设备明文或原始 payload |
|
||||
| `WEXT1` | `issues/2026-05-20_12-48-04-wecom-outing-overtime-fixture-dryrun-v1.csv` | `CURRENT` | 外出/加班低风险 POC dry-run 合同已实现并公网验证:扩展 `check:wecom-approval-fixtures` 支持 `outing`、`overtime` payload dry-run;公网报告 `/app/artifacts/qa-flow/wecom-approval-fixtures-20260520T054940Z/summary.json` 为 `PASS_FIXTURE_DRY_RUN_READY`;不 live 创建模板或审批单,live 前必须另起问答和合同 |
|
||||
| `WOPS1` | `issues/2026-05-20_14-35-42-warch3-ops-alert-tuning-v1.csv` | `CURRENT` | 巡检告警降噪与管理员收件人修正已公网验证:默认收件员工号为 `HYD000006`(`wangshunli` 的员工号),状态文件记录上次状态和 finding 摘要;远端 WARCH3 `/app/artifacts/qa-flow/warch3-ops-20260520-070731/summary.json` 为 `PASS_WITH_FINDINGS`,告警 `/app/artifacts/qa-flow/wecom-ops-alert-20260520T070740Z/summary.json` 为 `PASS_ALERT_SKIPPED_NO_FINDINGS`;已知 finding 不重复告警,仅阻断、脚本失败、新增 finding、状态恶化或人工 `--force` 发送脱敏消息 |
|
||||
| `WROAD2-CLOSEOUT` | `issues/2026-05-20_14-35-42-wroad2-controlled-ops-closeout-v1.csv` | `CURRENT` | 三天受控巡检验收合同:Day 1/2/3 由 WARCH3 timer 采集后汇总,已知 live smoke 凭据、pending OCR 和历史 callback gap 作为 observation 不阻断,结束后需用户问答确认 |
|
||||
| `WREAL1-PREP` | `issues/2026-05-20_14-35-42-wecom-real-trial-prep-v1.csv` | `CURRENT` | 真实小范围试用准备合同:只固化候选员工、非自审审批人、回滚联系人、允许流程、暂停条件和验收标准;当前不启用真实员工,外出/加班 live 暂缓 |
|
||||
|
||||
旧 Issue CSV 保留为历史审计证据:不删除、不批量回写、不移动到 `docs/archive/` 或其他归档目录。新连续合同如替代旧合同,只在新合同 `Notes` 或文档索引中说明 supersedes。
|
||||
|
||||
@@ -101,28 +104,28 @@
|
||||
|
||||
| Doc_ID | Path | Category | Authority | Status | Owner | Related_Issues | Evidence | Supersedes | Last_Audited | Audit_Method | Risk | Notes |
|
||||
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||
| `IDX` | `docs/文档索引.md` | 入口索引 | 最高文档入口 | `CURRENT` | product/dev | `GOV8,QAOPS9,P2D,P2-ORG,P2-FIN-DECISION,P2-FIN-V1,P2-OCR-SAMPLE,P2Q,P2AUTO,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,WFOC,DBWFOC,WFDG,WDRG,WCP1,WCPC,WHMS,WHMSLIVE,WOUA,WAPI,WRRA,WOUAFIX,WRV1,WRA1,WMSG1,WAPC1,WAPC2,WPROC1,WPROC2,WAPC2F,WAPC2FLIVE,WCBK1,WEGOV1,WARCH1,WARCH2,WROAD2,WARCH3,WCHECK1,WEXT1` | `rg` | none | 2026-05-20 | `node scripts/qa/audit-docs.mjs --check-registry` | medium | 判断当前该读什么;WARCH3/WCHECK1/WEXT1 后优先读取企微主办 + OA 自建留存路线、受控上线巡检、打卡摘要、外出/加班 dry-run 和后续 WREAL1 前置 |
|
||||
| `REG` | `docs/文档事实源状态矩阵.md` | 文档台账 | 文档分类和状态事实源 | `CURRENT` | product/dev | `GOV8,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,WFOC,DBWFOC,WFDG,WDRG,WCP1,WCPC,WHMS,WHMSLIVE,WOUA,WAPI,WRRA,WOUAFIX,WRV1,WRA1,WMSG1,WAPC1,WAPC2,WPROC1,WPROC2,WAPC2F,WAPC2FLIVE,WCBK1,WEGOV1,WARCH1,WARCH2,WROAD2,WARCH3,WCHECK1,WEXT1` | `audit-docs` | none | 2026-05-20 | `audit-docs --check-registry` | medium | 判断文档权威性;登记当前受控上线、自动巡检、打卡摘要留存和外出/加班 dry-run 事实 |
|
||||
| `IDX` | `docs/文档索引.md` | 入口索引 | 最高文档入口 | `CURRENT` | product/dev | `GOV8,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,WFOC,DBWFOC,WFDG,WDRG,WCP1,WCPC,WHMS,WHMSLIVE,WOUA,WAPI,WRRA,WOUAFIX,WRV1,WRA1,WMSG1,WAPC1,WAPC2,WPROC1,WPROC2,WAPC2F,WAPC2FLIVE,WCBK1,WEGOV1,WARCH1,WARCH2,WROAD2,WARCH3,WCHECK1,WEXT1,WOPS1,WROAD2C,WREAL1P` | `rg` | none | 2026-05-20 | `node scripts/qa/audit-docs.mjs --check-registry` | medium | 判断当前该读什么;WOPS1/WROAD2C/WREAL1P 后优先读取企微主办 + OA 自建留存路线、受控上线巡检、告警降噪、打卡摘要、外出/加班 dry-run 和真实试用准备 |
|
||||
| `REG` | `docs/文档事实源状态矩阵.md` | 文档台账 | 文档分类和状态事实源 | `CURRENT` | product/dev | `GOV8,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,WFOC,DBWFOC,WFDG,WDRG,WCP1,WCPC,WHMS,WHMSLIVE,WOUA,WAPI,WRRA,WOUAFIX,WRV1,WRA1,WMSG1,WAPC1,WAPC2,WPROC1,WPROC2,WAPC2F,WAPC2FLIVE,WCBK1,WEGOV1,WARCH1,WARCH2,WROAD2,WARCH3,WCHECK1,WEXT1,WOPS1,WROAD2C,WREAL1P` | `audit-docs` | none | 2026-05-20 | `audit-docs --check-registry` | medium | 判断文档权威性;登记当前受控上线、自动巡检、告警降噪、打卡摘要留存、外出/加班 dry-run 和真实试用准备事实 |
|
||||
| `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,WARCH1,WARCH2,WROAD2,WARCH3,WCHECK1,WEXT1` | Issue CSV | `docs/requirements-specification.md` | 2026-05-20 | `rg` | high | WARCH3/WCHECK1/WEXT1 后定义 HR/考勤企微主办、OA 自建留存、受控测试域、只读历史查询、自动巡检、打卡摘要、外出/加班 dry-run、WREAL1 前置和深度镜像边界;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 自建留存策略 |
|
||||
| `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,WREAD1,WREAD1F,WADM1,WARCH1,WARCH2,WROAD2,WARCH3,WCHECK1,WEXT1` | Issue CSV | none | 2026-05-20 | `rg` | high | WARCH3/WCHECK1/WEXT1 后固定最小审批历史留存、正式只读历史查询、受控上线自动巡检、打卡摘要和外出/加班 dry-run;仍禁止直接进入深度镜像 |
|
||||
| `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,WREAD1F,WADM1,WARCH1,WARCH2,WROAD2,WARCH3,WCHECK1,WEXT1,WOPS1,WROAD2C,WREAL1P` | Issue CSV | none | 2026-05-20 | `rg` | high | WOPS1/WROAD2C/WREAL1P 后固定最小审批历史留存、正式只读历史查询、受控上线自动巡检、告警降噪、打卡摘要、外出/加班 dry-run 和真实试用准备;仍禁止直接进入深度镜像 |
|
||||
| `WECOM-API-CATALOG` | `docs/企微API能力目录.md` | 集成事实源 | 企微 API 能力目录和 Secret 矩阵事实源 | `CURRENT` | product/backend/qa | `WAPI,WRRA,WAPC1,WAPC2,WPROC1,WPROC2,WAPC2F,WAPC2FLIVE,WCBK1,WEGOV1,WACP3,WACP3R,WREAD1,WADM1,WARCH1,WARCH2,WROAD2,WARCH3,WCHECK1,WEXT1` | 官方企业微信开发者中心链接/Issue CSV | none | 2026-05-20 | `rg` | high | 企微开发先读;只保存摘要、链接和项目决策,不保存 Secret、token、完整 userid、手机号、邮箱或原始 payload;记录审批补偿、打卡摘要和巡检只写 OA 留存、不写企业微信的边界 |
|
||||
| `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 留存验收门禁 |
|
||||
| `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 | 不可违反约束 |
|
||||
| `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,WREAD1F,WADM1,WARCH1,WARCH2,WROAD2,WARCH3,WCHECK1,WEXT1` | quick_validate | none | 2026-05-20 | `quick_validate.py` | medium | 每轮交付操作入口;企微/OA 后续先读 API 能力目录、Secret 矩阵、路线图、WEGOV1/WADM1/WARCH1/WARCH2/WROAD2/WARCH3/WCHECK1/WEXT1 自建留存和受控巡检边界;不得绕过 WREAL1 或后续独立合同直接做完整考勤审批镜像 |
|
||||
| `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,WREAD1F,WADM1,WARCH1,WARCH2,WROAD2,WARCH3,WCHECK1,WEXT1,WOPS1,WROAD2C,WREAL1P` | quick_validate | none | 2026-05-20 | `quick_validate.py` | medium | 每轮交付操作入口;企微/OA 后续先读 API 能力目录、Secret 矩阵、路线图、WEGOV1/WADM1/WARCH1/WARCH2/WROAD2/WARCH3/WCHECK1/WEXT1/WOPS1 自建留存、受控巡检和告警降噪边界;不得绕过 WROAD2C/WREAL1 或后续独立合同直接做完整考勤审批镜像 |
|
||||
| `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,WREAD1F,WADM1,WARCH1` | workflow 回归 | none | 2026-05-19 | `rg` | high | OA 主办审批、企微主办 HR/考勤审批、OA 自建留存、真实回调事件证据、WACP3R 补偿拉取 findings、WREAD1 请假/补卡真机可读性结论、WARCH1 最小历史留存及 OA 应急接管边界 |
|
||||
| `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 | 报销、发票、附件细则 |
|
||||
| `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,WACP3,WACP3R,WREAD1,WADM1,WARCH1,WARCH2,WROAD2,WARCH3,WCHECK1,WEXT1` | live smoke/真机反馈/Issue CSV | none | 2026-05-20 | `rg` | high | 企微优先承担 HR/考勤员工日常审批和人事/打卡能力;WARCH2 已提供请假/补卡正式只读历史查询,WARCH3 固定受控上线自动巡检,WCHECK1 固定打卡摘要留存,WEXT1 固定外出/加班 dry-run;采购、合同、项目、报销和财务继续 OA 主办 |
|
||||
| `RUNBOOK-TRIAL` | `docs/试运行与自动化验证手册.md` | 操作手册 | 试运行验证手册 | `CURRENT` | qa/ops | `R7E,QAOPS8` | live readiness | none | 2026-05-13 | `rg` | medium | 本地、公网、企业微信和真实 OCR 验证 |
|
||||
| `MANUAL-ADMIN` | `docs/管理员手册.md` | 操作手册 | 管理员手册 | `CURRENT` | ops/admin | `ORG8,R7E,WFOC,DBWFOC,WFDG,WCPC,WHMS,WHMSLIVE,WOUA,WAPI,WRRA,WRV1,WRA1,WMSG1,WAPC1,WAPC2,WPROC1,WPROC2,WAPC2F,WAPC2FLIVE,WCBK1,WEGOV1,WACP3,WACP3R,WREAD1,WADM1,WARCH1,WARCH2,WROAD2,WARCH3,WCHECK1,WEXT1` | 人工验收 | none | 2026-05-20 | `rg` | medium | 管理员上线、企微优先边界、Secret 矩阵、WARCH3 自动巡检与脱敏告警、WCHECK1 打卡摘要留存、WEXT1 外出/加班 dry-run 和深度镜像前置关注点 |
|
||||
| `MANUAL-ADMIN` | `docs/管理员手册.md` | 操作手册 | 管理员手册 | `CURRENT` | ops/admin | `ORG8,R7E,WFOC,DBWFOC,WFDG,WCPC,WHMS,WHMSLIVE,WOUA,WAPI,WRRA,WRV1,WRA1,WMSG1,WAPC1,WAPC2,WPROC1,WPROC2,WAPC2F,WAPC2FLIVE,WCBK1,WEGOV1,WACP3,WACP3R,WREAD1,WADM1,WARCH1,WARCH2,WROAD2,WARCH3,WCHECK1,WEXT1,WOPS1,WROAD2C,WREAL1P` | 人工验收 | none | 2026-05-20 | `rg` | medium | 管理员上线、企微优先边界、Secret 矩阵、WARCH3 自动巡检与脱敏告警、WOPS1 告警降噪、WCHECK1 打卡摘要留存、WEXT1 外出/加班 dry-run、WREAL1 准备和深度镜像前置关注点 |
|
||||
| `MANUAL-EMPLOYEE` | `docs/员工快速上手.md` | 操作手册 | 员工手册 | `CURRENT` | product | `R7E,WFOC,DBWFOC,WFDG` | 人工验收 | none | 2026-05-18 | `rg` | low | 员工侧使用说明,HR/考勤优先企业微信,采购/合同/财务仍在 OA;区分企微镜像和 OA 应急接管 |
|
||||
| `R7-DRAFT` | `docs/trialrun/R7扩大试运行草案.md` | 草案 | R7 准备材料 | `DRAFT` | product/qa | `R7E` | R7EP 报告 | none | 2026-05-13 | `rg` | medium | 不代表当前 R7E 执行状态 |
|
||||
| `OPS-RUNBOOK` | `docs/trialrun/OPS7运维交接Runbook.md` | 运维草案 | OPS 准备材料 | `DRAFT` | ops | `OPS8,QAOPS8` | 只读检查 | none | 2026-05-13 | `rg` | medium | 不代表恢复演练已执行 |
|
||||
| `TRIAL-REPORT` | `docs/trialrun/人工试运行报告.md` | 证据材料 | 试运行证据入口 | `EVIDENCE` | qa | `R7EP,OPS8,QAF8,WOUA,WAPI,WOUAFIX,WRV1,WRA1,WMSG1,WAPC1,WAPC2,WAPC2F,WAPC2FLIVE,WCBK1,WEGOV1,WROAD2,WARCH3,WCHECK1,WEXT1` | artifacts/qa-flow | none | 2026-05-20 | `rg` | medium | 只证明发生过;记录 WAPC2FLIVE live 样件、WCBK1 真实回调事件、WEGOV1 路线治理、WROAD2 Day 0、WARCH3 自动巡检、WCHECK1 打卡摘要和 WEXT1 dry-run,不直接放行深度镜像 |
|
||||
| `TRIAL-REPORT` | `docs/trialrun/人工试运行报告.md` | 证据材料 | 试运行证据入口 | `EVIDENCE` | qa | `R7EP,OPS8,QAF8,WOUA,WAPI,WOUAFIX,WRV1,WRA1,WMSG1,WAPC1,WAPC2,WAPC2F,WAPC2FLIVE,WCBK1,WEGOV1,WROAD2,WARCH3,WCHECK1,WEXT1,WOPS1,WROAD2C,WREAL1P` | artifacts/qa-flow | none | 2026-05-20 | `rg` | medium | 只证明发生过;记录 WAPC2FLIVE live 样件、WCBK1 真实回调事件、WEGOV1 路线治理、WROAD2 Day 0、WARCH3 自动巡检、WOPS1 告警降噪、WCHECK1 打卡摘要、WEXT1 dry-run 和 WREAL1 准备,不直接放行深度镜像 |
|
||||
| `FBA-AUDIT` | `docs/trialrun/R7E后功能边界审计.md` | 证据材料 | R7E 后功能边界审计 | `EVIDENCE` | product/dev/qa | `FBA,R7E,QAOPS8,P2T` | Issue CSV | none | 2026-05-15 | `rg` | medium | 只作为功能边界、P2 候选和 Issue 草案证据,不直接派工 |
|
||||
| `HISTORY-MATRIX` | `docs/历史功能状态矩阵.md` | 历史材料 | 历史功能索引 | `HISTORICAL` | product | `DCL7` | git 历史 | none | 2026-05-13 | `rg` | low | 只追溯历史 |
|
||||
| `OLD-IMPL-PLAN` | `docs/implementation_plan.md` | 已移除历史 | 早期实施计划 | `REMOVED` | product | `DCL7` | git history | none | 2026-05-13 | manual | low | 从 git 历史恢复 |
|
||||
|
||||
+8
-5
@@ -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 -> WREAD1 自动汇总 -> WREAD1F 新样例 -> WREAD1 真机确认 -> WADM1 自建留存模型设计 -> WARCH1 最小审批历史留存 -> WARCH2 正式只读历史查询 -> WROAD2+OPS 受控上线与每日巡检 -> WARCH3 归档稳定化自动巡检 -> WCHECK1 打卡摘要留存 -> WEXT1 外出/加班 dry-run。
|
||||
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 自动汇总 -> WREAD1F 新样例 -> WREAD1 真机确认 -> WADM1 自建留存模型设计 -> WARCH1 最小审批历史留存 -> WARCH2 正式只读历史查询 -> WROAD2+OPS 受控上线与每日巡检 -> WARCH3 归档稳定化自动巡检 -> WCHECK1 打卡摘要留存 -> WEXT1 外出/加班 dry-run -> WOPS1 巡检告警降噪 -> WROAD2-CLOSEOUT 三天验收 -> WREAL1-PREP 真实试用准备。
|
||||
|
||||
- R6、PC6、PA6、FL6、AP6、RI6、LP6 已完成并部署。
|
||||
- ME6 自动化、公网视觉矩阵和 `ME6-3` 代表性企业微信真机门禁已完成。
|
||||
@@ -80,7 +80,7 @@ R6 -> PC6 -> PA6 -> FL6 -> AP6 -> RI6 -> LP6 -> ME6/QG6 -> DG7/MRF7 -> MIA7 -> M
|
||||
- WACP3 是 WEGOV1 后的企微审批补偿拉取 POC 合同:新增 `check:wecom-approval-compensation-poc` 和 `getapprovalinfo` 只读封装;初始公网报告缺模板 ID,WACP3R 已改为按 `SMOKE002` creator 自动发现 OA-POC 请假/补卡模板并复跑,公网报告 `/app/artifacts/qa-flow/wecom-approval-compensation-poc-20260519T115944Z/summary.json` 为 `PASS_WITH_FINDINGS`,补偿拉取和详情读取可用,finding 为回调证据文件未持久化。
|
||||
- WARCH1 是 WADM1 后的最小自建历史留存合同:新增审批留存 Prisma 表、回调事件入库、补偿拉取双开关入库和离线读取 runner;只覆盖请假/补卡历史留存,不写企微、不创建模板或审批单、不进入完整镜像、应急接管、恢复回写或 RuleOps。
|
||||
- WARCH2 是 WARCH1 后的正式只读历史查询合同:新增只读 OA DB 查询 API 和 PC/移动历史页,只覆盖请假/补卡已归档历史;页面不提供审批动作,不进入待办语境;已完成 `SYNC_ENV=0` 公网部署、`/ready`、未登录 401 和远端离线归档复跑。
|
||||
- WROAD2+OPS 是 WARCH2 后的受控上线与每日巡检合同:范围固定测试账号 `SMOKE002 -> SMOKE101 + wangshunli`,3 天每日手动触发脚本;巡检可用 `WECOM_ARCHIVE_WRITE=1` 写 OA 自建留存库,不写企业微信、不创建模板或审批单、不安装 cron/systemd timer。Day 0 公网报告 `/app/artifacts/qa-flow/wroad2-ops-20260520-041941/summary.json` 为 `PASS_WITH_FINDINGS`,主链路通过并保留可解释 finding。后续优先 `WARCH3` 归档稳定化和 `WCHECK1` 打卡摘要留存,外出/加班先延期。
|
||||
- WROAD2+OPS 是 WARCH2 后的受控上线与每日巡检合同:范围固定测试账号 `SMOKE002 -> SMOKE101`,管理员观察和告警员工号使用 `HYD000006`(`wangshunli` 的员工号),3 天每日触发脚本;巡检可用 `WECOM_ARCHIVE_WRITE=1` 写 OA 自建留存库,不写企业微信、不创建模板或审批单。Day 0 公网报告 `/app/artifacts/qa-flow/wroad2-ops-20260520-041941/summary.json` 为 `PASS_WITH_FINDINGS`,主链路通过并保留可解释 finding。后续优先 WOPS1 告警降噪、WROAD2-CLOSEOUT 三天验收和 WREAL1-PREP。
|
||||
|
||||
## 当前活跃治理流
|
||||
|
||||
@@ -151,10 +151,13 @@ R6 -> PC6 -> PA6 -> FL6 -> AP6 -> RI6 -> LP6 -> ME6/QG6 -> DG7/MRF7 -> MIA7 -> M
|
||||
| `WADM1` | `issues/2026-05-19_21-55-33-wecom-archive-mirror-data-model-design-v1.csv` | 当前模型设计:企微自建留存/镜像数据模型已固化 | 固定审批留存、回调事件、补偿拉取、对账 finding、打卡/人事快照边界、隐私权限和 WARCH1 最小实现切片;已由 WARCH1 承接实现请假/补卡最小历史留存 | docs-only;不改 Prisma schema、不部署、不写 OA/企微业务数据;完整镜像仍需单独 Issue CSV 和方案批准 |
|
||||
| `WARCH1` | `issues/2026-05-19_22-32-06-wecom-approval-archive-minimum-v1.csv` | 当前最小历史留存实现:请假/补卡审批历史可入 OA DB 并离线读取 | 新增 `WecomApprovalArchive` 等 Prisma 表、回调事件持久化、补偿 run/item/finding、`check:wecom-approval-archive` 离线 runner;补偿入库需 `--persist-to-db` + `WECOM_ARCHIVE_WRITE=1`;公网报告 `/app/artifacts/qa-flow/wecom-approval-compensation-poc-20260519T160306Z/summary.json` 持久化 2 条详情,`/app/artifacts/qa-flow/wecom-approval-archive-20260519T160330Z/summary.json` 离线读取 2 条历史,finding 为旧回调未入库历史 gap | 不写企微数据;不创建模板或审批单;不新增正式管理页;不做外出/加班、完整镜像、应急接管、恢复回写或 RuleOps |
|
||||
| `WARCH2` | `issues/2026-05-20_00-26-04-wecom-approval-archive-query-v1.csv` | 当前正式只读历史查询:企微请假/补卡已归档历史可通过 OA API 和 PC/移动页面查看 | 新增 `/api/wecom/approval-archive` 列表/详情、`/wecom-approval-archive`、`/mobile/wecom-approval-archive`;只读 OA DB,不调用企微 API;分层权限为本人相关、直属下属、HR/admin/*,申请人未映射和 callback-only 记录仅 HR/admin/* 可见;页面固定“企业微信主办,OA 自建留存”“仅已归档记录”;公网报告 `/app/artifacts/qa-flow/wecom-approval-archive-20260520T013610Z/summary.json` 可读 2 条历史 | 不写 OA/企微数据;不读取企微 Secret;不提供审批动作;不做完整镜像、外出/加班、应急接管、恢复回写或 RuleOps |
|
||||
| `WROAD2+OPS` | `issues/2026-05-20_11-30-22-wroad2-ops-controlled-launch-v1.csv` | 当前受控上线与每日巡检:先用测试账号运行 3 天脚本化巡检,不直接深镜像 | 新增 `scripts/qa/run-wroad2-ops-daily.sh`,复用公网 `/ready`、live smoke、feature flag、企微 runtime、审批补偿和 OA 归档离线读取;仅补偿归档步骤可写 OA 留存库;Day 0 报告 `/app/artifacts/qa-flow/wroad2-ops-20260520-041941/summary.json` 为 `PASS_WITH_FINDINGS` | 不写企业微信;不创建模板或审批单;外出/加班、完整镜像、应急、回写、RuleOps 和 P2 大功能继续冻结 |
|
||||
| `WARCH3` | `issues/2026-05-20_12-48-04-wecom-archive-stabilization-ops-v1.csv` | 当前归档稳定化自动巡检:把 WROAD2 巡检升级为可安装的服务器定时任务和脱敏告警 | 已安装 `hyb-oa-warch3-ops.timer`,每日 07:30 触发;手动公网报告 `/app/artifacts/qa-flow/warch3-ops-20260520-054841/summary.json` 为 `PASS_WITH_FINDINGS`,告警 `/app/artifacts/qa-flow/wecom-ops-alert-20260520T054851Z/summary.json` 为 `PASS_ALERT_SENT_WITH_CONFIG_GAPS` | 只允许补偿归档写 OA DB;不写企业微信、不创建模板或审批单;`wangshunli` 非员工号别名 gap 已记录,有效管理员 `HYD000006` 已收到脱敏告警 |
|
||||
| `WROAD2+OPS` | `issues/2026-05-20_11-30-22-wroad2-ops-controlled-launch-v1.csv` | 当前受控上线与每日巡检:先用测试账号运行 3 天脚本化巡检,不直接深镜像 | 新增 `scripts/qa/run-wroad2-ops-daily.sh`,复用公网 `/ready`、live smoke、feature flag、企微 runtime、审批补偿和 OA 归档离线读取;仅补偿归档步骤可写 OA 留存库;Day 0 报告 `/app/artifacts/qa-flow/wroad2-ops-20260520-041941/summary.json` 为 `PASS_WITH_FINDINGS` | 不写企业微信;不创建模板或审批单;管理员观察和告警员工号用 `HYD000006`;外出/加班、完整镜像、应急、回写、RuleOps 和 P2 大功能继续冻结 |
|
||||
| `WARCH3` | `issues/2026-05-20_12-48-04-wecom-archive-stabilization-ops-v1.csv` | 当前归档稳定化自动巡检:把 WROAD2 巡检升级为可安装的服务器定时任务和脱敏告警 | 已安装 `hyb-oa-warch3-ops.timer`,每日 07:30 触发;手动公网报告 `/app/artifacts/qa-flow/warch3-ops-20260520-054841/summary.json` 为 `PASS_WITH_FINDINGS`,告警 `/app/artifacts/qa-flow/wecom-ops-alert-20260520T054851Z/summary.json` 为 `PASS_ALERT_SENT_WITH_CONFIG_GAPS` | 只允许补偿归档写 OA DB;不写企业微信、不创建模板或审批单;WOPS1 后默认收件员工号为 `HYD000006`(`wangshunli` 的员工号),已知 finding 不重复告警 |
|
||||
| `WCHECK1` | `issues/2026-05-20_12-48-04-wecom-checkin-summary-archive-v1.csv` | 当前打卡摘要留存:企微打卡数据进入 OA 自建历史摘要 | 新增打卡日/月摘要和规则快照表、`wecomCheckinArchive` 服务和 `npm run check:wecom-checkin-summary-archive`;公网 live `/app/artifacts/qa-flow/wecom-checkin-summary-archive-20260520T054907Z/summary.json` 为 `PASS_CHECKIN_ARCHIVE_READY`,offline `/app/artifacts/qa-flow/wecom-checkin-summary-archive-20260520T054926Z/summary.json` 为 `PASS_CHECKIN_ARCHIVE_OFFLINE_READY` | 不写企业微信;不保存位置、人脸、设备明文、原始 payload 或密钥;本窗口 0 条打卡明细、2 条规则快照已写 OA 留存 |
|
||||
| `WEXT1` | `issues/2026-05-20_12-48-04-wecom-outing-overtime-fixture-dryrun-v1.csv` | 当前外出/加班低风险 POC dry-run | 扩展 `npm run check:wecom-approval-fixtures` 支持 `--target=outing --target=overtime`,公网报告 `/app/artifacts/qa-flow/wecom-approval-fixtures-20260520T054940Z/summary.json` 为 `PASS_FIXTURE_DRY_RUN_READY` | 不 live 创建企微模板或审批单;live 前必须另起问答和 Issue CSV;不放行完整镜像 |
|
||||
| `WOPS1` | `issues/2026-05-20_14-35-42-warch3-ops-alert-tuning-v1.csv` | 已完成并公网验证:巡检告警降噪,只报新增异常或状态恶化 | 默认收件员工号改为 `HYD000006`(管理员 `wangshunli` 的员工号);`send-wecom-ops-alert` 使用状态文件记录上次状态和 finding 摘要;远端 WARCH3 `/app/artifacts/qa-flow/warch3-ops-20260520-070731/summary.json` 为 `PASS_WITH_FINDINGS`,告警 `/app/artifacts/qa-flow/wecom-ops-alert-20260520T070740Z/summary.json` 为 `PASS_ALERT_SKIPPED_NO_FINDINGS`,重复 finding 未发送 | 不写企业微信业务数据;只发送脱敏状态消息;不扩大真实用户 |
|
||||
| `WROAD2-CLOSEOUT` | `issues/2026-05-20_14-35-42-wroad2-controlled-ops-closeout-v1.csv` | 当前三天受控巡检验收:Day 1/2/3 采集后人工确认 | 已知 observation 为 live smoke 凭据、pending OCR 和历史 callback gap,不阻断;主链必须保持企微 runtime、审批补偿归档、OA 离线读取、打卡摘要 offline 可读和隐私扫描通过 | 三天结束前不启用真实员工;结论需用户问答确认 |
|
||||
| `WREAL1-PREP` | `issues/2026-05-20_14-35-42-wecom-real-trial-prep-v1.csv` | 当前真实试用准备:不启用真实员工 | 固化候选员工、非自审审批人、回滚联系人、允许流程、暂停条件和验收标准;首批真实试用默认只覆盖请假、补卡、打卡摘要和 OA 历史查看 | 外出/加班 live 暂缓;真实员工名单不写入 Issue 或文档,后续通过问答确认 |
|
||||
|
||||
旧 Issue CSV 不删除、不批量回写、不移动到归档目录。若新治理流替代旧合同,只在新 CSV 的 `Notes` 或本索引说明 supersedes,旧文件保留历史审计语义。
|
||||
|
||||
@@ -177,7 +180,7 @@ R6 -> PC6 -> PA6 -> FL6 -> AP6 -> RI6 -> LP6 -> ME6/QG6 -> DG7/MRF7 -> MIA7 -> M
|
||||
| [海音达OA-MVP需求规格说明书](./海音达OA-MVP需求规格说明书.md) | PRD 主事实源;WARCH3/WCHECK1/WEXT1 后包含企微审批配置、只读 POC、工程化前置、process gate、请假/补卡样例、自建留存、受控上线巡检、打卡摘要、外出/加班 dry-run 和深度镜像前置口径 |
|
||||
| [文档事实源状态矩阵](./文档事实源状态矩阵.md) | 文档分类、删除恢复和冲突规则 |
|
||||
| [产品设计规则](./产品设计规则.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/WREAD1/WREAD1F/WARCH1/WARCH2/WROAD2/WARCH3/WCHECK1/WEXT1 后企微优先 HR/考勤改造的阶段顺序、准入/退出门禁、并行规则、禁止跳阶段、API/Secret 口径、运行优先、自建留存、受控上线巡检、打卡摘要、外出/加班 dry-run 和后续 WREAL1/镜像前置事实源 |
|
||||
| [企微优先开发路线图](./企微优先开发路线图.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/WREAD1F/WARCH1/WARCH2/WROAD2/WARCH3/WCHECK1/WEXT1/WOPS1/WROAD2-CLOSEOUT/WREAL1-PREP 后企微优先 HR/考勤改造的阶段顺序、准入/退出门禁、并行规则、禁止跳阶段、API/Secret 口径、运行优先、自建留存、受控上线巡检、告警降噪、打卡摘要、外出/加班 dry-run 和后续 WREAL1/镜像前置事实源 |
|
||||
| [企微 API 能力目录](./企微API能力目录.md) | WAPI/WAPC1/WAPC2/WPROC1/WPROC2/WAPC2F/WAPC2FLIVE/WCBK1/WEGOV1/WACP3/WACP3R/WARCH1/WARCH2/WROAD2/WARCH3/WCHECK1/WEXT1 后企微官方 API、Secret 矩阵、权限/可信 IP/回调模板、读写边界、运行优先、审批配置、只读 POC、回调/补偿入库、打卡摘要留存、外出/加班 dry-run 和巡检写 OA 留存边界事实源 |
|
||||
| [产品口径](./产品口径.md) | 已拍板产品边界和业务规则;WEGOV1 后包含混合主办、自建留存、受控测试域、后续阶段顺序和深度镜像前置口径 |
|
||||
| [需求 - 统一审批中心规约](./需求-统一审批中心规约.md) | OA 主办审批、企微主办 HR/考勤员工日常审批镜像、催办、超时提醒、审批效率 |
|
||||
|
||||
+12
-9
@@ -1,6 +1,6 @@
|
||||
# hyb-oa 管理员手册(10 人以下团队版)
|
||||
|
||||
**当前状态(2026-05-20 / WARCH3/WCHECK1/WEXT1 后)**: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、WREAD1/WREAD1F、WADM1、WARCH1 和 WARCH2 均已收口到当前开发阶段;WROAD2+OPS 已完成测试账号受控上线 Day 0 巡检,报告 `/app/artifacts/qa-flow/wroad2-ops-20260520-041941/summary.json` 为 `PASS_WITH_FINDINGS`;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`;WREAD1F 新样例后,用户确认请假/补卡在企业微信消息卡片、列表和详情页均可看清,远端报告 `/app/artifacts/qa-flow/wecom-approval-readability-gate-20260519T133628Z/summary.json` 为 `PASS_READABLE`。WARCH1 已新增并部署请假/补卡最小审批历史留存:回调事件默认入 OA DB,补偿拉取可在 `--persist-to-db` + `WECOM_ARCHIVE_WRITE=1` 双开关下写 OA 留存,离线 runner 可不调用企微 API 读取已保存历史摘要;公网报告 `/app/artifacts/qa-flow/wecom-approval-compensation-poc-20260519T160306Z/summary.json` 和 `/app/artifacts/qa-flow/wecom-approval-archive-20260519T160330Z/summary.json` 均为 `PASS_WITH_FINDINGS`,2 条请假/补卡历史可离线读取,finding 是旧回调未入库历史 gap。WARCH2 已新增只读历史查询 API 和 PC/移动历史页:页面入口为“企微审批历史”,文案固定“企业微信主办,OA 自建留存”和“仅已归档记录”,只读 OA DB,不调用企微 API,不提供审批动作;权限按本人相关、直属下属和 HR/admin/* 分层,申请人未映射或 callback-only 记录仅 HR/admin/* 可见。WARCH3 已新增 07:30 systemd timer 巡检和脱敏企微告警;WCHECK1 已新增打卡规范化明细、日/月摘要和规则快照留存;WEXT1 已支持外出/加班 dry-run,但未 live 创建模板或审批单。巡检只允许补偿归档和打卡摘要写 OA 留存库,不写企业微信、不扩大真实员工。同意单击即通过风险已进入归档审计事实,后续正式历史页和操作规程继续突出动作来源与补偿一致性。采购、费用、合同、项目、报销、付款、AP、代账交接和高敏附件继续 OA 主办,企业微信只做脱敏提醒和 OA deeplink。公网 `payment_batch_v1=force_on`,`erp_sync=force_off`。公司当前无 ERP/独立财务系统,且 ERP 未来不接入 OA;财务闭环以 OA 内审批、台账、付款批次、银行对公付款回单和月度代账交接为准。
|
||||
**当前状态(2026-05-20 / WOPS1/WROAD2-CLOSEOUT/WREAL1-PREP 后)**: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、WREAD1/WREAD1F、WADM1、WARCH1 和 WARCH2 均已收口到当前开发阶段;WROAD2+OPS 已完成测试账号受控上线 Day 0 巡检,报告 `/app/artifacts/qa-flow/wroad2-ops-20260520-041941/summary.json` 为 `PASS_WITH_FINDINGS`;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`;WREAD1F 新样例后,用户确认请假/补卡在企业微信消息卡片、列表和详情页均可看清,远端报告 `/app/artifacts/qa-flow/wecom-approval-readability-gate-20260519T133628Z/summary.json` 为 `PASS_READABLE`。WARCH1 已新增并部署请假/补卡最小审批历史留存:回调事件默认入 OA DB,补偿拉取可在 `--persist-to-db` + `WECOM_ARCHIVE_WRITE=1` 双开关下写 OA 留存,离线 runner 可不调用企微 API 读取已保存历史摘要;公网报告 `/app/artifacts/qa-flow/wecom-approval-compensation-poc-20260519T160306Z/summary.json` 和 `/app/artifacts/qa-flow/wecom-approval-archive-20260519T160330Z/summary.json` 均为 `PASS_WITH_FINDINGS`,2 条请假/补卡历史可离线读取,finding 是旧回调未入库历史 gap。WARCH2 已新增只读历史查询 API 和 PC/移动历史页:页面入口为“企微审批历史”,文案固定“企业微信主办,OA 自建留存”和“仅已归档记录”,只读 OA DB,不调用企微 API,不提供审批动作;权限按本人相关、直属下属和 HR/admin/* 分层,申请人未映射或 callback-only 记录仅 HR/admin/* 可见。WARCH3 已新增 07:30 systemd timer 巡检和脱敏企微告警;WOPS1 将默认告警收件员工号修正为 `HYD000006`,也就是管理员 `wangshunli` 的员工号,并把策略改为只在阻断、脚本失败、新增 finding 或状态恶化时发送,已知 observation 不重复告警。WCHECK1 已新增打卡规范化明细、日/月摘要和规则快照留存;WEXT1 已支持外出/加班 dry-run,但未 live 创建模板或审批单。巡检只允许补偿归档和打卡摘要写 OA 留存库,不写企业微信、不扩大真实员工。WREAL1-PREP 只准备真实小范围试用清单,当前不启用真实员工。同意单击即通过风险已进入归档审计事实,后续正式历史页和操作规程继续突出动作来源与补偿一致性。采购、费用、合同、项目、报销、付款、AP、代账交接和高敏附件继续 OA 主办,企业微信只做脱敏提醒和 OA deeplink。公网 `payment_batch_v1=force_on`,`erp_sync=force_off`。公司当前无 ERP/独立财务系统,且 ERP 未来不接入 OA;财务闭环以 OA 内审批、台账、付款批次、银行对公付款回单和月度代账交接为准。
|
||||
|
||||
## 1. 适用范围
|
||||
- 适用于 `small_team_internal` 工作区模式。
|
||||
@@ -205,7 +205,7 @@ WCP1/WCPC/WHMS 后续推进:
|
||||
2. WCP1 历史结论为 `PAUSE_FOR_CONFIG`;WCPC 已用既有在职测试号完成远端只读复跑,报告 `/tmp/wecom-capability-poc-wcpc-smoke002/summary.json`,结论 `GO_TO_HR_MASTER_SYNC`。
|
||||
3. WCPC 已证明 HR 主数据镜像准入项可用:通讯录成员读取、人事助手花名册读取、打卡短窗口记录/规则读取和假期余额读取均达到可复用状态;审批模板、审批实例和回调配置仍是后续考勤/审批镜像前置项。
|
||||
4. WHMS 已实现并部署验证 `wecom_hr_master_sync_v1`,包括同步批次、逐员工结果、dry-run/apply、People 管理入口和 QA-only runner;公网验证后最终保持 `force_off`。
|
||||
5. 正式开发仍按 `wecom_hr_master_sync_v1` -> `runtime_first_wecom_enablement` -> `WOUA-FIX / WECOM-RUNTIME-READY-V1` -> `WAPC1 审批配置准备` -> `WAPC2 请假/补卡只读 POC` -> `WAPC2F 自动生成 POC 样件` -> `WAPC2FLIVE live 创建与 WAPC2 自动复跑` -> `WCBK1 回调只读 POC` -> `WEGOV1 企微主办治理与自建留存路线修正` -> `WACP3/WACP3R 补偿拉取 POC` -> `WREAD1 审批可读性与误审批防护门禁` -> `WADM1 自建留存/镜像数据模型设计` -> `WARCH1 最小自建历史留存实现` -> `WARCH2 正式只读历史查询` -> `WROAD2+OPS 受控上线与每日巡检` -> `WARCH3 归档稳定化自动巡检` -> `WCHECK1 打卡摘要留存` -> `WEXT1 外出/加班 dry-run` -> `WREAL1 小范围真实试用` -> `wecom_attendance_approval_mirror_v1` -> `oa_hr_emergency_mode_v1` -> `wecom_recovery_writeback_v1` -> `wecom_ruleops_v1` 分合同实施;WOUA-FIX 已收口测试账号差异和处理决策,WRV1/WRA1/WMSG1 已验证运行入口、OAuth/绑定、脱敏消息、Deeplink、通讯录、人事助手和打卡读取主链路。WAPC2FLIVE 已生成请假/补卡低风险样例,WCBK1 已证明真实审批回调 URL、验签/解密和事件脱敏留证,WACP3R 已证明请假/补卡补偿拉取和详情读取可用,WREAD1 已确认请假/补卡可读性,WADM1 已明确自建留存模型,WARCH1 已实现最小请假/补卡历史留存,WARCH2 已实现正式只读查询;后续仍不得跳过 WROAD2+OPS、WARCH3、WCHECK1、WEXT1/WREAL1 等独立合同直接进入深度考勤/审批镜像。
|
||||
5. 正式开发仍按 `wecom_hr_master_sync_v1` -> `runtime_first_wecom_enablement` -> `WOUA-FIX / WECOM-RUNTIME-READY-V1` -> `WAPC1 审批配置准备` -> `WAPC2 请假/补卡只读 POC` -> `WAPC2F 自动生成 POC 样件` -> `WAPC2FLIVE live 创建与 WAPC2 自动复跑` -> `WCBK1 回调只读 POC` -> `WEGOV1 企微主办治理与自建留存路线修正` -> `WACP3/WACP3R 补偿拉取 POC` -> `WREAD1 审批可读性与误审批防护门禁` -> `WADM1 自建留存/镜像数据模型设计` -> `WARCH1 最小自建历史留存实现` -> `WARCH2 正式只读历史查询` -> `WROAD2+OPS 受控上线与每日巡检` -> `WARCH3 归档稳定化自动巡检` -> `WCHECK1 打卡摘要留存` -> `WEXT1 外出/加班 dry-run` -> `WOPS1 巡检告警降噪` -> `WROAD2-CLOSEOUT 三天验收` -> `WREAL1-PREP` -> `WREAL1 小范围真实试用` -> `wecom_attendance_approval_mirror_v1` -> `oa_hr_emergency_mode_v1` -> `wecom_recovery_writeback_v1` -> `wecom_ruleops_v1` 分合同实施;WOUA-FIX 已收口测试账号差异和处理决策,WRV1/WRA1/WMSG1 已验证运行入口、OAuth/绑定、脱敏消息、Deeplink、通讯录、人事助手和打卡读取主链路。WAPC2FLIVE 已生成请假/补卡低风险样例,WCBK1 已证明真实审批回调 URL、验签/解密和事件脱敏留证,WACP3R 已证明请假/补卡补偿拉取和详情读取可用,WREAD1 已确认请假/补卡可读性,WADM1 已明确自建留存模型,WARCH1 已实现最小请假/补卡历史留存,WARCH2 已实现正式只读查询;后续仍不得跳过 WROAD2-CLOSEOUT、WREAL1 问答或明确批准,直接进入深度考勤/审批镜像。
|
||||
6. 任何生产写接口、企微规则写入、应急模式开启或恢复回写都必须有单独批准、dry-run 或可审计记录。
|
||||
|
||||
WCPC 配置复跑清单:
|
||||
@@ -324,17 +324,20 @@ WAPC2 请假/补卡只读 POC 操作口径:
|
||||
|
||||
WROAD2/WARCH3 企微受控上线每日巡检:
|
||||
|
||||
1. 试用范围固定为测试账号 `SMOKE002 -> SMOKE101 + wangshunli`,试用周期 3 天;WARCH3 可通过 `hyb-oa-warch3-ops.timer` 每天 07:30 自动触发。
|
||||
1. 试用范围固定为测试账号 `SMOKE002 -> SMOKE101`,管理员观察和告警收件人为 `HYD000006`;`HYD000006` 是 `wangshunli` 的员工号,`wangshunli` 只作为姓名/账号别名记录。试用周期 3 天;WARCH3 可通过 `hyb-oa-warch3-ops.timer` 每天 07:30 自动触发。
|
||||
2. 手动执行命令:`bash scripts/qa/run-warch3-ops-daily.sh`;原始 WROAD2 编排仍可用 `bash scripts/qa/run-wroad2-ops-daily.sh`。
|
||||
3. systemd 安装/卸载:`bash deploy/trialrun/install-warch3-ops-timer.sh`;卸载用 `bash deploy/trialrun/install-warch3-ops-timer.sh uninstall`。
|
||||
4. 巡检会复用公网 `/ready`、live smoke、feature flag、企微 runtime、审批补偿拉取和 OA 归档离线读取;默认不写企业微信。
|
||||
5. 唯一允许写入的是审批补偿归档步骤:进程级 `WECOM_ARCHIVE_WRITE=1` 加 runner `--persist-to-db`,仅写 OA 自建留存库,用于补齐历史和对账证据。
|
||||
6. 每日报告位于 `artifacts/qa-flow/warch3-ops-<timestamp>/summary.json` 或 `artifacts/qa-flow/wroad2-ops-<timestamp>/summary.json`;出现 Secret、权限、可信 IP、可见范围或外部配置问题时,先记录真实阻断,不扩大范围。
|
||||
7. 告警命令为 `cd server && npm run send:wecom-ops-alert -- --summary-file=<summary.json> --send`,真实发送还需 `WARCH3_OPS_ALERT_SEND=1`;消息只包含阶段、状态、finding 数、报告路径和下一步建议。
|
||||
8. WCHECK1 打卡摘要命令:`cd server && npm run check:wecom-checkin-summary-archive -- --offline --format=json`;如要写 OA 留存,必须 `WECOM_CHECKIN_ARCHIVE_WRITE=1` 且带 `--live --persist-to-db`。
|
||||
9. WEXT1 外出/加班 dry-run:`cd server && npm run check:wecom-approval-fixtures -- --format=json --target=outing --target=overtime`;live 创建必须另起问答和合同。
|
||||
10. 外出/加班 live、完整镜像、应急接管、恢复回写、RuleOps、图片 OCR、复杂 BPM、预算、ERP 和银行流水导入在试用期继续冻结。
|
||||
11. Day 0 公网报告为 `/app/artifacts/qa-flow/wroad2-ops-20260520-041941/summary.json`,结论 `PASS_WITH_FINDINGS`:企微 runtime、补偿归档、离线归档读取和隐私扫描通过;finding 为缺 live smoke 登录凭据/pending OCR 子检查异常以及历史 callback gap,不阻断测试账号受控试用。
|
||||
7. 告警命令为 `cd server && npm run send:wecom-ops-alert -- --summary-file=<summary.json> --send`,真实发送还需 `WARCH3_OPS_ALERT_SEND=1`;默认收件员工号为 `HYD000006`,消息只包含阶段、状态、finding 数、报告路径和下一步建议。
|
||||
8. WOPS1 后告警使用状态文件 `artifacts/qa-flow/warch3-ops-alert-state.json` 记忆上次状态和 finding 摘要;已知 `PASS_WITH_FINDINGS` 或 observation 不重复发消息,只有 `SKIP/BLOCKED_BY_EXTERNAL_CONFIG`、脚本失败、新增 finding、状态恶化或人工 `--force` 才发送。
|
||||
9. WCHECK1 打卡摘要命令:`cd server && npm run check:wecom-checkin-summary-archive -- --offline --format=json`;如要写 OA 留存,必须 `WECOM_CHECKIN_ARCHIVE_WRITE=1` 且带 `--live --persist-to-db`。
|
||||
10. WEXT1 外出/加班 dry-run:`cd server && npm run check:wecom-approval-fixtures -- --format=json --target=outing --target=overtime`;live 创建必须另起问答和合同。
|
||||
11. 外出/加班 live、完整镜像、应急接管、恢复回写、RuleOps、图片 OCR、复杂 BPM、预算、ERP 和银行流水导入在试用期继续冻结。
|
||||
12. Day 0 公网报告为 `/app/artifacts/qa-flow/wroad2-ops-20260520-041941/summary.json`,结论 `PASS_WITH_FINDINGS`:企微 runtime、补偿归档、离线归档读取和隐私扫描通过;finding 为缺 live smoke 登录凭据/pending OCR 子检查异常以及历史 callback gap,不阻断测试账号受控试用。
|
||||
13. WROAD2-CLOSEOUT 三天结束后再做人工问答:确认通过、先处理指定 finding、或暂停;未确认前不启用真实员工。
|
||||
14. WREAL1-PREP 当前只准备清单,不要求提供真实员工;后续真实试用默认只覆盖请假、补卡、打卡摘要和 OA 历史查看。
|
||||
|
||||
问题登记最小字段:
|
||||
|
||||
@@ -457,7 +460,7 @@ WROAD2/WARCH3 企微受控上线每日巡检:
|
||||
- `WECOM_ALLOWED_RETURN_HOSTS=oa.hyinda.com`
|
||||
5. 先让管理员用账号密码登录,在 `个人设置` 完成一次绑定,再测试企业微信工作台直达;已绑定用户从手机企业微信进入时应直接看到移动工作台。
|
||||
6. 若启用 OA 同步企业微信打卡数据,在企业微信打卡 API「可调用接口的应用」中授权 `海音达工作台`。OA 默认可复用 `WECOM_AGENT_SECRET`;只有单独创建考勤同步应用时才额外配置 `WECOM_CHECKIN_SECRET`。
|
||||
7. HR/考勤员工日常审批后续配置企业微信原生审批模板作为主流程;WAPC2FLIVE 已自动生成请假/补卡 OA-POC 低风险样件并复跑只读 POC,WCBK1 已完成真实回调事件确认,WACP3R 已完成补偿拉取,WREAD1 已确认请假/补卡在企微中可读,WADM1 已完成自建留存/镜像模型设计,WARCH1 已完成最小请假/补卡历史留存,WARCH2 已完成正式只读历史查询。WARCH3/WCHECK1/WEXT1 后先做自动巡检、打卡摘要留存和外出/加班 dry-run;同意单击即通过需继续纳入操作提示和审计设计。外出/加班 live 模板仍需后续单独批准。报销、财务、采购、合同和项目类 OA 主办审批仍使用企业微信消息提醒并回到 OA 页面完成。
|
||||
7. HR/考勤员工日常审批后续配置企业微信原生审批模板作为主流程;WAPC2FLIVE 已自动生成请假/补卡 OA-POC 低风险样件并复跑只读 POC,WCBK1 已完成真实回调事件确认,WACP3R 已完成补偿拉取,WREAD1 已确认请假/补卡在企微中可读,WADM1 已完成自建留存/镜像模型设计,WARCH1 已完成最小请假/补卡历史留存,WARCH2 已完成正式只读历史查询。WARCH3/WCHECK1/WEXT1 后先做自动巡检、打卡摘要留存和外出/加班 dry-run;WOPS1 后告警默认发给 `HYD000006`(`wangshunli` 的员工号)且只报新增异常。同意单击即通过需继续纳入操作提示和审计设计。外出/加班 live 模板仍需后续单独批准。报销、财务、采购、合同和项目类 OA 主办审批仍使用企业微信消息提醒并回到 OA 页面完成。
|
||||
|
||||
### 员工会看到的典型状态
|
||||
- 未绑定:登录页会提示“当前企业微信账号尚未绑定”,回退到账号密码登录。
|
||||
|
||||
+2
-2
@@ -7,7 +7,7 @@
|
||||
**相关事实源**:[需求 - 统一审批中心规约](./需求-统一审批中心规约.md)、[需求 - 企业微信基础设施规约](./需求-企业微信基础设施规约.md)
|
||||
**体验总依据**:[产品设计规则基线](./产品设计规则.md)
|
||||
|
||||
本规约细化请假、补卡、外出、加班与考勤修正的产品要求。WFOC 起,企业微信作为 HR/考勤员工日常审批和打卡规则主操作端;OA 负责按域镜像、应急接管、恢复回写、查询导出和审计。WEGOV1 后进一步明确:企微成熟 HR/考勤功能优先上线,OA 不重造企业微信完整打卡/考勤/HR 产品,但必须做自建历史留存、审计、入口、消息和应急兜底。WAPC2 后,请假和补卡先通过 QA-only runner 只读验证模板详情与低风险 `sp_no` 审批详情;WAPC2FLIVE 已生成 OA-POC 样件并复跑通过;WCBK1 已完成真实回调 URL 验证和一条测试审批事件确认;WACP3R 已自动发现模板并证明补偿拉取和详情读取可用。WREAD1F 后,用户真机确认请假/补卡在企业微信消息卡片、列表和详情页均可看清,远端报告 `/app/artifacts/qa-flow/wecom-approval-readability-gate-20260519T133628Z/summary.json` 为 `PASS_READABLE`;驳回有二次确认,同意单击即通过。WARCH1 已实现请假/补卡最小审批历史留存、回调事件入库、补偿拉取入库和离线读取证明;WARCH2 已提供正式只读历史查询。WROAD2 后进入测试账号受控上线;WARCH3 已新增 07:30 systemd 自动巡检和脱敏企微告警;WCHECK1 已新增打卡规范化明细、日/月摘要和规则快照留存;WEXT1 已支持外出/加班 dry-run,但未 live 创建模板或审批单。当前仍不代表完整审批镜像、外出/加班正式样件、应急接管或恢复回写已实现。PC 和移动页面体验以产品设计规则基线为依据,不重复定义通用审批链和移动导航规则。
|
||||
本规约细化请假、补卡、外出、加班与考勤修正的产品要求。WFOC 起,企业微信作为 HR/考勤员工日常审批和打卡规则主操作端;OA 负责按域镜像、应急接管、恢复回写、查询导出和审计。WEGOV1 后进一步明确:企微成熟 HR/考勤功能优先上线,OA 不重造企业微信完整打卡/考勤/HR 产品,但必须做自建历史留存、审计、入口、消息和应急兜底。WAPC2 后,请假和补卡先通过 QA-only runner 只读验证模板详情与低风险 `sp_no` 审批详情;WAPC2FLIVE 已生成 OA-POC 样件并复跑通过;WCBK1 已完成真实回调 URL 验证和一条测试审批事件确认;WACP3R 已自动发现模板并证明补偿拉取和详情读取可用。WREAD1F 后,用户真机确认请假/补卡在企业微信消息卡片、列表和详情页均可看清,远端报告 `/app/artifacts/qa-flow/wecom-approval-readability-gate-20260519T133628Z/summary.json` 为 `PASS_READABLE`;驳回有二次确认,同意单击即通过。WARCH1 已实现请假/补卡最小审批历史留存、回调事件入库、补偿拉取入库和离线读取证明;WARCH2 已提供正式只读历史查询。WROAD2 后进入测试账号受控上线;WARCH3 已新增 07:30 systemd 自动巡检和脱敏企微告警;WCHECK1 已新增打卡规范化明细、日/月摘要和规则快照留存;WEXT1 已支持外出/加班 dry-run,但未 live 创建模板或审批单。WOPS1 后告警默认发给 `HYD000006`(`wangshunli` 的员工号)且只报新增异常;WROAD2-CLOSEOUT 三天验收完成前不启用真实员工;WREAL1-PREP 只准备真实小范围试用清单。当前仍不代表完整审批镜像、外出/加班正式样件、应急接管或恢复回写已实现。PC 和移动页面体验以产品设计规则基线为依据,不重复定义通用审批链和移动导航规则。
|
||||
|
||||
---
|
||||
|
||||
@@ -49,7 +49,7 @@ REQAUD 复核后,ATTV1 已完成统一入口实现,当前运行时完成度
|
||||
| 请假对团队状态/日历的影响 | `DONE` | 已批准请假可作为团队状态和日历依据。 |
|
||||
| 企业微信打卡作为原始采集 | `DONE` | 由企业微信侧承载定位、人脸、Wi-Fi、设备和防作弊能力;OA 不重造。 |
|
||||
| 企业微信打卡 API 同步 | `PARTIAL` | 已新增同步批次和企微打卡摘要表;有已授权调用打卡 API 的自建应用 Secret 和企微权限时同步,缺配置时记录 `SKIP`。`WECOM_CHECKIN_SECRET` 可作为单独考勤同步应用的显式覆盖;未配置时复用已授权工作台应用的 `WECOM_AGENT_SECRET`。 |
|
||||
| 企微 HR/考勤审批镜像 | `CONTROLLED_OPS_READY` | WAPC2 已新增请假/补卡模板详情和低风险 `sp_no` 审批详情只读 POC;WAPC2FLIVE 已完成自动样件与 WAPC2 自动复跑;WCBK1 已完成真实回调 URL 验证和一条测试审批事件确认;WACP3R 已证明请假/补卡补偿拉取和详情读取可用;WREAD1F 已完成请假/补卡模板级真机确认,结论 `PASS_READABLE`。WARCH1 已实现请假/补卡最小历史留存和离线读取,WARCH2 已实现正式只读历史查询。WROAD2+OPS 做测试账号巡检,WARCH3 已升级自动巡检,WCHECK1 已补打卡摘要留存,WEXT1 只完成外出/加班 dry-run;后续 `wecom_attendance_approval_mirror_v1` 仍需 WROAD2 三天验收、WREAL1 小范围真实试用或明确批准。 |
|
||||
| 企微 HR/考勤审批镜像 | `CONTROLLED_OPS_READY` | WAPC2 已新增请假/补卡模板详情和低风险 `sp_no` 审批详情只读 POC;WAPC2FLIVE 已完成自动样件与 WAPC2 自动复跑;WCBK1 已完成真实回调 URL 验证和一条测试审批事件确认;WACP3R 已证明请假/补卡补偿拉取和详情读取可用;WREAD1F 已完成请假/补卡模板级真机确认,结论 `PASS_READABLE`。WARCH1 已实现请假/补卡最小历史留存和离线读取,WARCH2 已实现正式只读历史查询。WROAD2+OPS 做测试账号巡检,WARCH3 已升级自动巡检,WCHECK1 已补打卡摘要留存,WEXT1 只完成外出/加班 dry-run,WOPS1 修正告警降噪和管理员员工号;后续 `wecom_attendance_approval_mirror_v1` 仍需 WROAD2-CLOSEOUT 三天验收、WREAL1 小范围真实试用或明确批准。 |
|
||||
| 员工补卡申请审批 | `DONE_AS_OA_FALLBACK` | 当前 OA 能提交补卡修正;WFOC 后正常入口迁往企微,OA 仅在应急模式开放并进入恢复回写。 |
|
||||
| 外出申请审批 | `DONE_AS_OA_FALLBACK` | 当前 OA 能提交外出修正;WFOC 后正常入口迁往企微,OA 仅在应急模式开放并进入恢复回写。 |
|
||||
| 加班申请审批 | `DONE_AS_OA_FALLBACK` | 当前 OA 能提交加班修正;WFOC 后正常入口迁往企微,OA 仅在应急模式开放并进入恢复回写。 |
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
"ID","Title","Description","Acceptance","Test_Method","Tools","Dev_Status","Review1_Status","Regression_Status","Files","Dependencies","Notes"
|
||||
"WOPS1-0","巡检告警降噪合同","建立 WOPS1 合同;在 WARCH3 已公网验证基础上,将告警策略从 finding 即发改为新增异常或状态恶化才发。","1) 默认不重复发送已知 finding;2) SKIP/BLOCKED_BY_EXTERNAL_CONFIG、脚本失败、新增 finding、状态恶化仍发送;3) 默认收件员工号改为 HYD000006;4) HYD000006 是管理员 wangshunli 的 employee_no,脚本不再把姓名字符串 wangshunli 当 employee_no 使用;5) 不写企微业务数据、不创建模板或审批单、不扩大真实用户。","受影响流程:WARCH3 07:30 巡检 -> 状态比较 -> 管理员脱敏企微告警;验证步骤:python3 /home/shunli/.codex/skills/plan/scripts/validate_issues_csv.py issues/2026-05-20_14-35-42-warch3-ops-alert-tuning-v1.csv;node scripts/qa/check-process-contract.mjs --issue issues/2026-05-20_14-35-42-warch3-ops-alert-tuning-v1.csv --profile wecom --json。","python3|node","DONE","DONE","DONE","issues/2026-05-20_14-35-42-warch3-ops-alert-tuning-v1.csv","[]","回滚/清理:恢复本合同和告警脚本改动即可;不清理历史巡检报告或已发送的脱敏告警。企微应用消息 Secret 来源固定 WECOM_AGENT_SECRET,不允许运行时猜 Secret。"
|
||||
"WOPS1-1","告警状态记忆与新增异常判断","为 send-wecom-ops-alert 增加状态文件,记录上次状态、finding 摘要和报告路径,用于判断新增 finding 或状态恶化。","1) 默认 state 文件位于 artifacts/qa-flow/warch3-ops-alert-state.json;2) 支持 --state-file 覆盖;3) 首次运行写入基线但不因既有 PASS_WITH_FINDINGS 重复告警;4) 新增 finding evidenceDigest 或状态等级升高时发送;5) force 仍可强制发送。","受影响流程:读取 WARCH3 summary -> 读取 alert state -> 计算 alertDecision -> 写回 state;验证步骤:cd server && node --check src/scripts/send-wecom-ops-alert.js;focused vitest 覆盖首次基线、无新增跳过、新增 finding 发送、状态恶化发送。","node|vitest","DONE","DONE","DONE","server/src/scripts/send-wecom-ops-alert.js|server/tests/unit/scripts/send-wecom-ops-alert.test.js","[""WOPS1-0""]","回滚/清理:删除状态文件支持后恢复旧行为;远端 state 文件仅含脱敏状态和 finding digest,可保留审计。远端 state `/app/artifacts/qa-flow/warch3-ops-alert-state.json` 已写入基线。"
|
||||
"WOPS1-2","默认收件人修正为 HYD000006","将巡检告警默认收件人和 WARCH3 调用参数改为 HYD000006,避免把 wangshunli 姓名字符串当员工号产生配置 gap。","1) 默认 recipientEmployeeNos 为 HYD000006;2) run-warch3-ops-daily.sh 调用不再传 wangshunli;3) 文档说明 HYD000006 是管理员 wangshunli 的 employee_no;4) 缺 HYD000006 绑定仍记录 NEEDS_CONFIG。","受影响流程:WARCH3 finding/失败 -> 告警收件人解析 -> 企微应用消息;验证步骤:cd server && npx vitest run tests/unit/scripts/send-wecom-ops-alert.test.js --reporter=verbose;rg -n ""recipient.*HYD000006|wangshunli"" scripts server/src docs .agents/skills/hyb-oa-delivery/SKILL.md。","node|vitest|rg","DONE","DONE","DONE","server/src/scripts/send-wecom-ops-alert.js|scripts/qa/run-warch3-ops-daily.sh|docs/*|.agents/skills/hyb-oa-delivery/SKILL.md","[""WOPS1-1""]","回滚/清理:恢复默认双收件人即可;不影响企微业务数据。公网报告显示 recipient `HYD000006` active 且有企微绑定。"
|
||||
"WOPS1-3","WARCH3 脚本接入降噪告警","调整 WARCH3 巡检脚本,由 send-wecom-ops-alert 负责判断是否发送;报告中写明告警策略为新增异常或状态恶化。","1) PASS_ARCHIVE_OPS_READY 无告警;2) PASS_WITH_FINDINGS 且无新增 finding 时产生 alert report 但 delivery 为 skipped;3) 新增 finding 或阻断时发送;4) WARCH3 summary 的 alertPolicy 更新为 HYD000006 与新增异常策略。","受影响流程:systemd service -> WARCH3 汇总 -> alert runner -> 脱敏报告;验证步骤:bash -n scripts/qa/run-warch3-ops-daily.sh;cd server && npx vitest run tests/unit/scripts/send-wecom-ops-alert.test.js --reporter=verbose;公网阶段再用 systemd service 真实验证降噪,不在本地发送企微消息。","bash|node|vitest","DONE","DONE","DONE","scripts/qa/run-warch3-ops-daily.sh","[""WOPS1-2""]","回滚/清理:恢复脚本调用即可;/tmp 或 artifacts 报告可保留审计。本地不发送企微消息,避免把测试告警误发到管理员。公网二次触发报告 `/app/artifacts/qa-flow/wecom-ops-alert-20260520T070740Z/summary.json` 为 PASS_ALERT_SKIPPED_NO_FINDINGS。"
|
||||
"WOPS1-4","本地门禁","执行 WOPS1 本地合同、语法、focused 单测、文档和最小门禁。","1) CSV 校验通过;2) process gate 通过;3) node --check 通过;4) focused vitest 覆盖新增异常、HYD000006 默认收件人、已知 finding 不重复发送、失败/新增 finding 仍发送和脱敏输出;5) docs audit、skill 校验、git diff --check、scripts/check-minimal.sh 通过。","受影响流程:WOPS1 实施候选 -> 本地 QA -> 可部署候选;验证步骤:python3 /home/shunli/.codex/skills/plan/scripts/validate_issues_csv.py issues/2026-05-20_14-35-42-warch3-ops-alert-tuning-v1.csv;node scripts/qa/check-process-contract.mjs --baseline --json;node scripts/qa/check-process-contract.mjs --issue issues/2026-05-20_14-35-42-warch3-ops-alert-tuning-v1.csv --profile wecom --json;cd server && node --check src/scripts/send-wecom-ops-alert.js;cd server && npx vitest run tests/unit/scripts/send-wecom-ops-alert.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;bash scripts/check-minimal.sh。","python3|node|vitest|bash|git","DONE","DONE","DONE","issues/2026-05-20_14-35-42-warch3-ops-alert-tuning-v1.csv|server/src/scripts/send-wecom-ops-alert.js|server/tests/unit/scripts/send-wecom-ops-alert.test.js|scripts/qa/run-warch3-ops-daily.sh|docs/*|.agents/skills/hyb-oa-delivery/SKILL.md","[""WOPS1-3""]","回滚/清理:门禁失败不部署;修复后重跑。已通过 CSV/process gate/node --check/bash -n/focused vitest/docs audit/skill/git diff --check/scripts/check-minimal.sh。"
|
||||
"WOPS1-5","公网部署与远端验证","本地门禁通过后以 SYNC_ENV=0 部署,远端手动触发一次降噪验证并确认 timer 仍启用。","1) 使用 SYNC_ENV=0,不同步本地 env;2) /ready 正常;3) systemd timer 仍为 07:30;4) 远端触发 WARCH3 service 生成报告;5) 既有 finding 不重复发送,或仅在新增异常时发送;6) 报告不泄露敏感字段。","受影响流程:部署 -> /ready -> timer -> 手动 service -> 降噪告警;验证步骤:SYNC_ENV=0 bash deploy/trialrun/deploy.sh;ssh 远端 systemctl list-timers hyb-oa-warch3-ops.timer;systemctl start hyb-oa-warch3-ops.service;检查 /app/artifacts/qa-flow/warch3-ops-*/summary.json 和 wecom-ops-alert-*。","bash|ssh|systemctl|npm","DONE","DONE","DONE","deploy/trialrun/deploy.sh|scripts/qa/run-warch3-ops-daily.sh|server/src/scripts/send-wecom-ops-alert.js","[""WOPS1-4""]","回滚/清理:如远端异常,重新部署上一提交或停用 timer;不清理 OA 业务数据。SYNC_ENV=0 部署通过;/ready ok;timer 下次 2026-05-21 07:30 CST;远端 WARCH3 `/app/artifacts/qa-flow/warch3-ops-20260520-070731/summary.json` 为 PASS_WITH_FINDINGS;告警 `/app/artifacts/qa-flow/wecom-ops-alert-20260520T070740Z/summary.json` 为 PASS_ALERT_SKIPPED_NO_FINDINGS。"
|
||||
"WOPS1-6","状态回填与提交","回填 WOPS1 实际结果、远端报告路径、告警状态和中文提交。","1) Issue 状态真实回填;2) 事实源同步;3) git diff --check 通过;4) 中文提交包含 WOPS1。","受影响流程:WOPS1 -> WROAD2C/WREAL1P 后续;验证步骤:git status --short;git diff --check;git log -1 --oneline。","git|manual","DONE","DONE","DONE","issues/2026-05-20_14-35-42-warch3-ops-alert-tuning-v1.csv|docs/*|.agents/skills/hyb-oa-delivery/SKILL.md","[""WOPS1-5""]","提交建议:WOPS1 调整巡检告警降噪。"
|
||||
|
@@ -0,0 +1,7 @@
|
||||
"ID","Title","Description","Acceptance","Test_Method","Tools","Dev_Status","Review1_Status","Regression_Status","Files","Dependencies","Notes"
|
||||
"WREAL1P-0","真实试用准备合同","建立 WREAL1-PREP 合同;只固化真实小范围试用前置清单,不启用真实员工。","1) 明确当前继续测试账号,不引入真实员工;2) 真实试用需等 WROAD2C 三天验收通过且用户提供人员;3) 默认建议 1 名真实员工 + 1 名审批人/管理员观察;4) 外出/加班 live 暂缓;5) 不写企业微信、不改生产配置。","受影响流程:WROAD2C 验收 -> WREAL1 准备 -> 后续真实试用问答;验证步骤:python3 /home/shunli/.codex/skills/plan/scripts/validate_issues_csv.py issues/2026-05-20_14-35-42-wecom-real-trial-prep-v1.csv;node scripts/qa/check-process-contract.mjs --issue issues/2026-05-20_14-35-42-wecom-real-trial-prep-v1.csv --profile docs-only --json。","python3|node","DONE","DONE","DONE","issues/2026-05-20_14-35-42-wecom-real-trial-prep-v1.csv","[]","回滚/清理:本合同仅文档/规划,不启用真实用户;恢复文档即可。"
|
||||
"WREAL1P-1","候选员工与审批人条件","固化真实试用人员选择条件,避免自审、权限缺失或敏感流程误入。","1) 候选员工必须 active、已绑定企微、可见范围正常;2) 审批人必须 active、已绑定企微、不是申请人本人;3) 默认不自动替换审批人;4) 员工号和真实姓名不写入 Issue 或文档,待执行时通过临时输入确认。","受影响流程:真实员工选择 -> 非自审预检 -> 试用启动;验证步骤:rg -n ""非自审|active|已绑定企微|HYD000006|真实小范围试用"" docs .agents/skills/hyb-oa-delivery/SKILL.md。","rg","DONE","DONE","DONE","docs/企微优先开发路线图.md|docs/管理员手册.md|docs/trialrun/人工试运行报告.md","[""WREAL1P-0""]","回滚/清理:恢复文档即可;不保存真实员工名单。"
|
||||
"WREAL1P-2","试用范围与暂停条件","固化 WREAL1 允许流程和暂停条件。","1) 允许流程只含请假、补卡、打卡摘要和 OA 历史查看;2) 外出/加班 live 暂缓;3) 出现企微权限、可信 IP、Secret、回调阻断、误审批、隐私泄露或 OA 留存失败时暂停;4) 暂停不删除已归档历史。","受影响流程:真实试用 -> 每日观察 -> 暂停/继续决策;验证步骤:rg -n ""请假|补卡|打卡摘要|暂停条件|外出/加班 live 暂缓|隐私泄露"" docs .agents/skills/hyb-oa-delivery/SKILL.md。","rg","DONE","DONE","DONE","docs/企微优先开发路线图.md|docs/需求-请假考勤修正规约.md|docs/管理员手册.md","[""WREAL1P-1""]","回滚/清理:恢复文档即可;不清理历史审计。"
|
||||
"WREAL1P-3","验收标准与人工问答","固化 WREAL1 启动和结束时的人工问答选项。","1) 启动前问:1 人、2 人或继续测试账号;2) 结束时问:扩大范围、先处理 finding 或暂停;3) 每次问答都说明当前状态、改了什么、测试了什么、结果是什么;4) 未确认前不扩大真实用户。","受影响流程:WREAL1 启动问答 -> 小范围试用 -> 验收问答;验证步骤:rg -n ""1 名真实员工|2 名真实员工|继续测试账号|扩大范围|暂停"" docs .agents/skills/hyb-oa-delivery/SKILL.md。","rg|manual","DONE","DONE","DONE","docs/管理员手册.md|docs/企微优先开发路线图.md|.agents/skills/hyb-oa-delivery/SKILL.md","[""WREAL1P-2""]","回滚/清理:恢复文档即可;未确认不启动真实试用。"
|
||||
"WREAL1P-4","事实源回写","更新路线图、管理员手册、试运行报告、文档索引、事实源矩阵和项目 skill,固化 WREAL1-PREP。","1) 文档明确当前只是准备,不启用真实员工;2) 后续路线仍为 WREAL1 -> 镜像 -> 应急 -> 回写 -> RuleOps;3) P2 大功能继续冻结;4) 记录 HYD000006 为巡检告警默认员工号。","受影响流程:事实源 -> 后续 WROAD2C/WREAL1/WEXT1-LIVE;验证步骤: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 ""WREAL1|真实小范围试用|暂停条件|回滚联系人|HYD000006|外出/加班 live 暂缓"" docs .agents/skills/hyb-oa-delivery/SKILL.md。","node|python3|rg","DONE","DONE","DONE","docs/企微优先开发路线图.md|docs/管理员手册.md|docs/trialrun/人工试运行报告.md|docs/文档索引.md|docs/文档事实源状态矩阵.md|.agents/skills/hyb-oa-delivery/SKILL.md","[""WREAL1P-3""]","回滚/清理:恢复文档即可。"
|
||||
"WREAL1P-5","状态回填与提交","完成 WREAL1-PREP 状态回填和中文提交。","1) Issue 状态回填;2) 文档门禁通过;3) git diff --check 通过;4) 中文提交包含 WREAL1P。","受影响流程:WREAL1-PREP -> 三天验收后可问答启动 WREAL1;验证步骤:git status --short;git diff --check;git log -1 --oneline。","git|manual","DONE","DONE","DONE","issues/2026-05-20_14-35-42-wecom-real-trial-prep-v1.csv|docs/*|.agents/skills/hyb-oa-delivery/SKILL.md","[""WREAL1P-4""]","提交建议:WREAL1P 固化真实试用准备清单。"
|
||||
|
@@ -0,0 +1,6 @@
|
||||
"ID","Title","Description","Acceptance","Test_Method","Tools","Dev_Status","Review1_Status","Regression_Status","Files","Dependencies","Notes"
|
||||
"WROAD2C-0","三天受控巡检验收合同","建立 WROAD2-CLOSEOUT 合同;延续 WROAD2/WARCH3 自动巡检,Day 1/2/3 结果出来后汇总验收。","1) 固定验收结论:PASS_CONTROLLED_OPS_READY、PASS_WITH_FINDINGS、PAUSED_MANUAL_CONFIRMATION 或 SKIP/BLOCKED_BY_EXTERNAL_CONFIG;2) 已知 observation 可接受:live smoke 凭据缺失、pending OCR 子检查、历史 callback gap;3) 企微主链必须通过:runtime、审批补偿归档、OA 离线读取、打卡摘要 offline 可读、隐私扫描;4) 不扩大真实用户、不写企业微信。","受影响流程:WARCH3 Day 1/2/3 自动巡检 -> 三天汇总 -> 人工确认验收;验证步骤:python3 /home/shunli/.codex/skills/plan/scripts/validate_issues_csv.py issues/2026-05-20_14-35-42-wroad2-controlled-ops-closeout-v1.csv;node scripts/qa/check-process-contract.mjs --issue issues/2026-05-20_14-35-42-wroad2-controlled-ops-closeout-v1.csv --profile wecom --json。","python3|node","DONE","DONE","DONE","issues/2026-05-20_14-35-42-wroad2-controlled-ops-closeout-v1.csv","[]","回滚/清理:本合同仅记录未来三天验收;如停止试用,保持 TODO 并在 Notes 写明暂停原因。企微 runtime、审批补偿和告警相关 Secret 来源固定 WECOM_AGENT_SECRET,不允许运行时猜 Secret。合同和 process gate 已通过;Day 1/2/3 仍待 timer 自动采集。"
|
||||
"WROAD2C-1","Day 1 自动巡检采集","采集 2026-05-21 07:30 CST WARCH3 timer 报告并检查主链路。","1) timer 自动执行或人工补触发;2) 记录 WARCH3 summary 路径、状态、findingCount 和告警状态;3) 主链路不为 SKIP/BLOCKED;4) 隐私扫描无命中。","受影响流程:Day 1 timer -> WARCH3 summary -> 巡检台账;验证步骤:ssh 远端 systemctl status hyb-oa-warch3-ops.service;读取最新 /app/artifacts/qa-flow/warch3-ops-*/summary.json;记录结论。","ssh|systemctl|node","TODO","TODO","TODO","external","[""WROAD2C-0""]","回滚/清理:如 timer 未执行,可人工 systemctl start 补跑;不得伪造成自动通过。"
|
||||
"WROAD2C-2","Day 2 自动巡检采集","采集 2026-05-22 07:30 CST WARCH3 timer 报告并检查趋势。","1) 记录 Day 2 报告路径和状态;2) 与 Day 1 比较状态是否恶化、finding 是否新增;3) 已知 observation 不阻断;4) 新增外部配置错误真实记录。","受影响流程:Day 2 timer -> 趋势比较 -> 巡检台账;验证步骤:读取 Day 2 WARCH3 summary;比对 Day 1 finding evidenceDigest。","ssh|node","TODO","TODO","TODO","external","[""WROAD2C-1""]","回滚/清理:如 Day 2 失败,保持失败事实并暂停 WREAL1。"
|
||||
"WROAD2C-3","Day 3 汇总验收","采集 2026-05-23 07:30 CST WARCH3 timer 报告并生成三天汇总结论。","1) 汇总 Day 0/1/2/3 报告;2) 企微主链连续可解释通过;3) 输出最终建议:进入 WREAL1、先处理 finding 或暂停;4) 等待人工确认。","受影响流程:Day 3 timer -> 三天汇总 -> 人工验收问答;验证步骤:汇总 WROAD2 Day 0 和 WARCH3 Day 1/2/3 summary 路径;形成结论并在事实源回写。","ssh|node|manual","TODO","TODO","TODO","docs/trialrun/人工试运行报告.md|docs/文档索引.md|docs/文档事实源状态矩阵.md","[""WROAD2C-2""]","回滚/清理:如结论为暂停,不进入 WREAL1;不清理历史报告。"
|
||||
"WROAD2C-4","人工验收问答","三天报告齐全后,按人工问答确认是否进入 WREAL1。","1) 向用户列出当前状态、改了什么、测试了什么、结果是什么;2) 选项为确认通过、先处理 finding、暂停;3) 未确认前不启用真实员工。","受影响流程:三天巡检 -> 用户拍板 -> WREAL1 是否启动;验证步骤:人工确认记录写入 Notes 和试运行报告。","manual","TODO","TODO","TODO","issues/2026-05-20_14-35-42-wroad2-controlled-ops-closeout-v1.csv|docs/trialrun/人工试运行报告.md","[""WROAD2C-3""]","回滚/清理:未获确认时保持 PAUSED_MANUAL_CONFIRMATION,不启动真实试用。"
|
||||
|
@@ -181,8 +181,8 @@ const summary = {
|
||||
currentRun: process.env.WARCH3_SKIP_WROAD2 === "1" ? "summary_only" : "wroad2_then_summary",
|
||||
},
|
||||
alertPolicy: {
|
||||
enabledWhen: "finding_or_failure",
|
||||
recipient: "wangshunli/HYD000006",
|
||||
enabledWhen: "blocked_failure_new_finding_or_status_worsening",
|
||||
recipient: "HYD000006",
|
||||
messageBoundary: "stage/status/finding count/report path/next step only",
|
||||
},
|
||||
boundaries: {
|
||||
@@ -218,7 +218,7 @@ maybe_send_alert() {
|
||||
return 0
|
||||
fi
|
||||
echo ">>> [warch3-ops] send dehydrated WeCom alert" | tee -a "$SUMMARY_TXT"
|
||||
if (cd "$SERVER_DIR" && npm run send:wecom-ops-alert -- --summary-file="$summary_file" --recipient-employee-no=wangshunli --recipient-employee-no=HYD000006 --send --format=json) \
|
||||
if (cd "$SERVER_DIR" && npm run send:wecom-ops-alert -- --summary-file="$summary_file" --recipient-employee-no=HYD000006 --send --format=json) \
|
||||
> >(tee "$REPORT_DIR/wecom-alert.log") 2>&1; then
|
||||
echo "[warch3-ops][PASS] alert" | tee -a "$SUMMARY_TXT"
|
||||
else
|
||||
|
||||
@@ -9,7 +9,8 @@ const { getConfig } = require("../config");
|
||||
const { sendWecomMessage } = require("../services/integrations/im/wecom");
|
||||
|
||||
const DEFAULT_REPORT_ROOT = path.resolve(__dirname, "../../../artifacts/qa-flow");
|
||||
const DEFAULT_RECIPIENTS = Object.freeze(["wangshunli", "HYD000006"]);
|
||||
const DEFAULT_ALERT_STATE_FILE = path.join(DEFAULT_REPORT_ROOT, "warch3-ops-alert-state.json");
|
||||
const DEFAULT_RECIPIENTS = Object.freeze(["HYD000006"]);
|
||||
const SEND_CONFIRM_ENV = "WARCH3_OPS_ALERT_SEND";
|
||||
|
||||
function printHelp() {
|
||||
@@ -20,9 +21,10 @@ QA-only WARCH3 企微巡检脱敏告警。
|
||||
|
||||
Options:
|
||||
--summary-file=<path> WARCH3/WROAD2 summary.json。
|
||||
--recipient-employee-no=<no> 收件员工号,可重复;默认 wangshunli + HYD000006。
|
||||
--recipient-employee-no=<no> 收件员工号,可重复;默认 HYD000006。
|
||||
--send 发送企微应用消息。
|
||||
--force PASS 且无 finding 时也发送测试消息。
|
||||
--state-file=<path> 告警状态文件,默认 artifacts/qa-flow/warch3-ops-alert-state.json。
|
||||
--format=text|json 输出格式,默认 text。
|
||||
--report-dir=<path> 报告目录,默认 artifacts/qa-flow/wecom-ops-alert-<timestamp>。
|
||||
--help 显示帮助。
|
||||
@@ -66,6 +68,7 @@ function parseArgs(argv) {
|
||||
recipientEmployeeNos: [],
|
||||
send: false,
|
||||
force: false,
|
||||
stateFile: process.env.WARCH3_OPS_ALERT_STATE_FILE || DEFAULT_ALERT_STATE_FILE,
|
||||
format: "text",
|
||||
reportDir: path.join(DEFAULT_REPORT_ROOT, `wecom-ops-alert-${timestamp()}`),
|
||||
help: false,
|
||||
@@ -89,6 +92,11 @@ function parseArgs(argv) {
|
||||
options.send = true;
|
||||
} else if (arg === "--force") {
|
||||
options.force = true;
|
||||
} else if (arg === "--state-file") {
|
||||
options.stateFile = toSafeString(argv[index + 1], 1000);
|
||||
index += 1;
|
||||
} else if (arg.startsWith("--state-file=")) {
|
||||
options.stateFile = toSafeString(arg.slice("--state-file=".length), 1000);
|
||||
} else if (arg === "--format") {
|
||||
options.format = argv[index + 1] || options.format;
|
||||
index += 1;
|
||||
@@ -144,8 +152,7 @@ function readSummary(summaryFile) {
|
||||
|
||||
function shouldSendForSummary(summary, options) {
|
||||
if (options.force) return true;
|
||||
const status = toSafeString(summary?.status || summary?.conclusion, 80);
|
||||
return status !== "PASS_ARCHIVE_OPS_READY" || Number(summary?.findingCount || 0) > 0;
|
||||
return evaluateAlertDecision(summary, options).shouldSend;
|
||||
}
|
||||
|
||||
function nextStepForStatus(summary) {
|
||||
@@ -178,6 +185,132 @@ function buildAlertMessage(summary) {
|
||||
};
|
||||
}
|
||||
|
||||
function statusRank(statusValue) {
|
||||
const status = toSafeString(statusValue, 120).toUpperCase();
|
||||
if (!status || status === "UNKNOWN") return 2;
|
||||
if (status.includes("SKIP") || status.includes("BLOCKED") || status.includes("FAIL") || status.includes("ERROR")) {
|
||||
return 4;
|
||||
}
|
||||
if (status.includes("PARTIAL") || status.includes("NEEDS_CONFIG")) {
|
||||
return 3;
|
||||
}
|
||||
if (status.includes("WITH_FINDINGS")) {
|
||||
return 2;
|
||||
}
|
||||
if (status.includes("PASS")) {
|
||||
return 1;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
|
||||
function collectFindingKeys(summary) {
|
||||
const findings = Array.isArray(summary?.findings) ? summary.findings : [];
|
||||
const keys = findings
|
||||
.map((finding) => [
|
||||
toSafeString(finding?.source, 80),
|
||||
toSafeString(finding?.status, 80),
|
||||
toSafeString(finding?.severity, 40),
|
||||
toSafeString(finding?.evidenceDigest || finding?.detail, 160),
|
||||
].filter(Boolean).join(":"))
|
||||
.filter(Boolean);
|
||||
if (!keys.length && Number(summary?.findingCount || 0) > 0) {
|
||||
keys.push(`findingCount:${Number(summary.findingCount || 0)}`);
|
||||
}
|
||||
return Array.from(new Set(keys)).sort();
|
||||
}
|
||||
|
||||
function buildAlertState(summary, generatedAt = new Date().toISOString()) {
|
||||
const status = toSafeString(summary?.status || summary?.conclusion || "UNKNOWN", 120);
|
||||
return {
|
||||
status,
|
||||
statusRank: statusRank(status),
|
||||
findingKeys: collectFindingKeys(summary),
|
||||
findingCount: Number(summary?.findingCount || 0),
|
||||
reportDir: summary?.reportDir || null,
|
||||
updatedAt: generatedAt,
|
||||
};
|
||||
}
|
||||
|
||||
function readAlertState(stateFile) {
|
||||
if (!stateFile) return null;
|
||||
try {
|
||||
if (!fs.existsSync(stateFile)) return null;
|
||||
const parsed = JSON.parse(fs.readFileSync(stateFile, "utf8"));
|
||||
return parsed && typeof parsed === "object" ? parsed : null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function writeAlertState(stateFile, state) {
|
||||
if (!stateFile) return null;
|
||||
const resolved = path.resolve(stateFile);
|
||||
fs.mkdirSync(path.dirname(resolved), { recursive: true });
|
||||
fs.writeFileSync(resolved, `${JSON.stringify(state, null, 2)}\n`, "utf8");
|
||||
return resolved;
|
||||
}
|
||||
|
||||
function evaluateAlertDecision(summary, options = {}, priorState = null) {
|
||||
const currentState = buildAlertState(summary);
|
||||
if (options.force) {
|
||||
return {
|
||||
shouldSend: true,
|
||||
reason: "force_requested",
|
||||
newFindingKeys: [],
|
||||
previousStatus: priorState?.status || null,
|
||||
currentStatus: currentState.status,
|
||||
currentState,
|
||||
};
|
||||
}
|
||||
|
||||
const currentRank = currentState.statusRank;
|
||||
const previousRank = Number(priorState?.statusRank || statusRank(priorState?.status));
|
||||
const previousKeys = new Set(Array.isArray(priorState?.findingKeys) ? priorState.findingKeys : []);
|
||||
const newFindingKeys = currentState.findingKeys.filter((key) => !previousKeys.has(key));
|
||||
|
||||
if (currentRank >= 4) {
|
||||
return {
|
||||
shouldSend: true,
|
||||
reason: "blocked_or_failed",
|
||||
newFindingKeys,
|
||||
previousStatus: priorState?.status || null,
|
||||
currentStatus: currentState.status,
|
||||
currentState,
|
||||
};
|
||||
}
|
||||
|
||||
if (priorState && currentRank > previousRank) {
|
||||
return {
|
||||
shouldSend: true,
|
||||
reason: "status_worsened",
|
||||
newFindingKeys,
|
||||
previousStatus: priorState.status || null,
|
||||
currentStatus: currentState.status,
|
||||
currentState,
|
||||
};
|
||||
}
|
||||
|
||||
if (priorState && newFindingKeys.length > 0) {
|
||||
return {
|
||||
shouldSend: true,
|
||||
reason: "new_findings",
|
||||
newFindingKeys,
|
||||
previousStatus: priorState.status || null,
|
||||
currentStatus: currentState.status,
|
||||
currentState,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
shouldSend: false,
|
||||
reason: priorState ? "no_new_findings_or_status_worsening" : "baseline_recorded",
|
||||
newFindingKeys,
|
||||
previousStatus: priorState?.status || null,
|
||||
currentStatus: currentState.status,
|
||||
currentState,
|
||||
};
|
||||
}
|
||||
|
||||
async function queryRecipientRows(employeeNos) {
|
||||
if (!process.env.DATABASE_URL) {
|
||||
throw new Error("Missing DATABASE_URL");
|
||||
@@ -280,7 +413,11 @@ async function executeOpsAlert(options, deps = {}) {
|
||||
const config = deps.config || getConfig();
|
||||
const summary = deps.summary || readSummary(options.summaryFile);
|
||||
const message = buildAlertMessage(summary);
|
||||
const shouldSend = shouldSendForSummary(summary, options);
|
||||
const priorState = Object.prototype.hasOwnProperty.call(deps, "priorState")
|
||||
? deps.priorState
|
||||
: readAlertState(options.stateFile);
|
||||
const alertDecision = evaluateAlertDecision(summary, options, priorState);
|
||||
const shouldSend = alertDecision.shouldSend;
|
||||
|
||||
let recipientRows = deps.recipientRows;
|
||||
let recipientReadError = null;
|
||||
@@ -344,6 +481,21 @@ async function executeOpsAlert(options, deps = {}) {
|
||||
conclusion = "PASS_ALERT_SKIPPED_NO_FINDINGS";
|
||||
}
|
||||
|
||||
let stateWrite = {
|
||||
status: "SKIPPED",
|
||||
stateFile: options.stateFile ? path.resolve(options.stateFile) : null,
|
||||
};
|
||||
if (options.send && (delivery.status === "OK" || delivery.status === "SKIPPED_NO_FINDINGS")) {
|
||||
const writeStateImpl = deps.writeAlertState || writeAlertState;
|
||||
stateWrite = {
|
||||
status: "OK",
|
||||
stateFile: writeStateImpl(options.stateFile, {
|
||||
...alertDecision.currentState,
|
||||
updatedAt: generatedAt,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
issueId: "WARCH3",
|
||||
mode: "wecom-ops-alert",
|
||||
@@ -353,6 +505,13 @@ async function executeOpsAlert(options, deps = {}) {
|
||||
sendRequested: Boolean(options.send),
|
||||
sendAllowed: Boolean(options.send && env[SEND_CONFIRM_ENV] === "1"),
|
||||
shouldSend,
|
||||
alertDecision: {
|
||||
reason: alertDecision.reason,
|
||||
previousStatus: alertDecision.previousStatus,
|
||||
currentStatus: alertDecision.currentStatus,
|
||||
newFindingCount: alertDecision.newFindingKeys.length,
|
||||
},
|
||||
stateWrite,
|
||||
summary: {
|
||||
issueId: summary?.issueId || null,
|
||||
status: summary?.status || summary?.conclusion || "UNKNOWN",
|
||||
@@ -381,6 +540,7 @@ function printText(report, reportPath) {
|
||||
if (report.recipientGaps.length) {
|
||||
process.stdout.write(`[wecom-ops-alert] recipientGaps=${JSON.stringify(report.recipientGaps)}\n`);
|
||||
}
|
||||
process.stdout.write(`[wecom-ops-alert] decision=${report.alertDecision.reason} newFindings=${report.alertDecision.newFindingCount}\n`);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
@@ -407,9 +567,14 @@ if (require.main === module) {
|
||||
|
||||
module.exports = {
|
||||
assertAlertSendAllowed,
|
||||
buildAlertState,
|
||||
buildAlertMessage,
|
||||
evaluateAlertDecision,
|
||||
executeOpsAlert,
|
||||
parseArgs,
|
||||
readAlertState,
|
||||
sanitizeDeliveryResponse,
|
||||
shouldSendForSummary,
|
||||
statusRank,
|
||||
writeAlertState,
|
||||
};
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
const {
|
||||
assertAlertSendAllowed,
|
||||
buildAlertMessage,
|
||||
buildAlertState,
|
||||
evaluateAlertDecision,
|
||||
executeOpsAlert,
|
||||
parseArgs,
|
||||
shouldSendForSummary,
|
||||
@@ -12,9 +14,10 @@ const SENSITIVE_TOKEN = ["ops", "alert", "token", "should", "not", "leak"].join(
|
||||
function baseOptions(overrides = {}) {
|
||||
return {
|
||||
summaryFile: "",
|
||||
recipientEmployeeNos: ["wangshunli", "HYD000006"],
|
||||
recipientEmployeeNos: ["HYD000006"],
|
||||
send: false,
|
||||
force: false,
|
||||
stateFile: "/tmp/wecom-ops-alert-state-test.json",
|
||||
format: "json",
|
||||
reportDir: "/tmp/wecom-ops-alert-test",
|
||||
help: false,
|
||||
@@ -34,12 +37,6 @@ function baseSummary(overrides = {}) {
|
||||
|
||||
function recipientRows(overrides = {}) {
|
||||
return [
|
||||
{
|
||||
employeeNo: "wangshunli",
|
||||
status: "active",
|
||||
archived: false,
|
||||
wecomExternalUserId: SENSITIVE_USER_ID,
|
||||
},
|
||||
{
|
||||
employeeNo: "HYD000006",
|
||||
status: "active",
|
||||
@@ -53,15 +50,16 @@ describe("send-wecom-ops-alert", () => {
|
||||
it("parses recipients and enforces double confirmation", () => {
|
||||
const options = parseArgs([
|
||||
"--summary-file=/tmp/summary.json",
|
||||
"--recipient-employee-no=wangshunli",
|
||||
"--recipient-employee-no",
|
||||
"HYD000006",
|
||||
"--send",
|
||||
"--state-file=/tmp/warch3-state.json",
|
||||
"--format=json",
|
||||
]);
|
||||
|
||||
expect(options.summaryFile).toBe("/tmp/summary.json");
|
||||
expect(options.recipientEmployeeNos).toEqual(["wangshunli", "HYD000006"]);
|
||||
expect(options.recipientEmployeeNos).toEqual(["HYD000006"]);
|
||||
expect(options.stateFile).toBe("/tmp/warch3-state.json");
|
||||
expect(options.send).toBe(true);
|
||||
expect(() => assertAlertSendAllowed(options, {})).toThrow(/WARCH3_OPS_ALERT_SEND=1/);
|
||||
expect(() => assertAlertSendAllowed(options, { WARCH3_OPS_ALERT_SEND: "1" })).not.toThrow();
|
||||
@@ -83,6 +81,54 @@ describe("send-wecom-ops-alert", () => {
|
||||
expect(shouldSendForSummary(summary, baseOptions({ force: true }))).toBe(true);
|
||||
});
|
||||
|
||||
it("records the first PASS_WITH_FINDINGS run as baseline without sending", () => {
|
||||
const summary = baseSummary({
|
||||
findings: [{ source: "approvalArchive", status: "PASS_WITH_FINDINGS", severity: "info", evidenceDigest: "known" }],
|
||||
});
|
||||
const decision = evaluateAlertDecision(summary, baseOptions(), null);
|
||||
|
||||
expect(decision.shouldSend).toBe(false);
|
||||
expect(decision.reason).toBe("baseline_recorded");
|
||||
});
|
||||
|
||||
it("sends when a new finding appears after baseline", () => {
|
||||
const previousSummary = baseSummary({
|
||||
findings: [{ source: "approvalArchive", status: "PASS_WITH_FINDINGS", severity: "info", evidenceDigest: "known" }],
|
||||
});
|
||||
const currentSummary = baseSummary({
|
||||
findings: [
|
||||
{ source: "approvalArchive", status: "PASS_WITH_FINDINGS", severity: "info", evidenceDigest: "known" },
|
||||
{ source: "wecomRuntimeReady", status: "PASS_WITH_FINDINGS", severity: "warn", evidenceDigest: "new" },
|
||||
],
|
||||
});
|
||||
const decision = evaluateAlertDecision(currentSummary, baseOptions(), buildAlertState(previousSummary));
|
||||
|
||||
expect(decision.shouldSend).toBe(true);
|
||||
expect(decision.reason).toBe("new_findings");
|
||||
expect(decision.newFindingKeys).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("skips repeated known findings after baseline", () => {
|
||||
const summary = baseSummary({
|
||||
findings: [{ source: "approvalArchive", status: "PASS_WITH_FINDINGS", severity: "info", evidenceDigest: "known" }],
|
||||
});
|
||||
const decision = evaluateAlertDecision(summary, baseOptions(), buildAlertState(summary));
|
||||
|
||||
expect(decision.shouldSend).toBe(false);
|
||||
expect(decision.reason).toBe("no_new_findings_or_status_worsening");
|
||||
});
|
||||
|
||||
it("sends when status worsens or blocks", () => {
|
||||
const previous = buildAlertState(baseSummary({ status: "PASS_ARCHIVE_OPS_READY", findingCount: 0, findings: [] }));
|
||||
const worsened = evaluateAlertDecision(baseSummary({ status: "PASS_WITH_FINDINGS", findingCount: 0, findings: [] }), baseOptions(), previous);
|
||||
const blocked = evaluateAlertDecision(baseSummary({ status: "SKIP/BLOCKED_BY_EXTERNAL_CONFIG", findingCount: 0, findings: [] }), baseOptions(), null);
|
||||
|
||||
expect(worsened.shouldSend).toBe(true);
|
||||
expect(worsened.reason).toBe("status_worsened");
|
||||
expect(blocked.shouldSend).toBe(true);
|
||||
expect(blocked.reason).toBe("blocked_or_failed");
|
||||
});
|
||||
|
||||
it("dry-runs alert without sending and redacts recipients", async () => {
|
||||
const sendMessageImpl = vi.fn();
|
||||
const report = await executeOpsAlert(baseOptions(), {
|
||||
@@ -94,8 +140,9 @@ describe("send-wecom-ops-alert", () => {
|
||||
generatedAt: "2026-05-20T04:00:00.000Z",
|
||||
});
|
||||
|
||||
expect(report.status).toBe("PASS_ALERT_DRY_RUN_READY");
|
||||
expect(report.delivery.status).toBe("DRY_RUN_READY");
|
||||
expect(report.status).toBe("PASS_ALERT_SKIPPED_NO_FINDINGS");
|
||||
expect(report.delivery.status).toBe("SKIPPED_NO_FINDINGS");
|
||||
expect(report.alertDecision.reason).toBe("baseline_recorded");
|
||||
expect(sendMessageImpl).not.toHaveBeenCalled();
|
||||
const text = JSON.stringify(report);
|
||||
expect(text).not.toContain(SENSITIVE_USER_ID);
|
||||
@@ -119,6 +166,8 @@ describe("send-wecom-ops-alert", () => {
|
||||
sendMessageImpl,
|
||||
env: { WARCH3_OPS_ALERT_SEND: "1" },
|
||||
config: { wecomRequestTimeoutMs: 1000 },
|
||||
priorState: buildAlertState(baseSummary({ status: "PASS_ARCHIVE_OPS_READY", findingCount: 0, findings: [] })),
|
||||
writeAlertState: vi.fn(() => "/tmp/warch3-state.json"),
|
||||
});
|
||||
|
||||
expect(report.status).toBe("PASS_ALERT_SENT");
|
||||
@@ -127,42 +176,16 @@ describe("send-wecom-ops-alert", () => {
|
||||
expect(JSON.stringify(report)).not.toContain(SENSITIVE_USER_ID);
|
||||
});
|
||||
|
||||
it("sends to the valid admin recipient while recording alias gaps", async () => {
|
||||
const sendMessageImpl = vi.fn(async ({ recipientExternalUserIds }) => {
|
||||
expect(recipientExternalUserIds).toEqual([SENSITIVE_USER_ID]);
|
||||
return {
|
||||
ok: true,
|
||||
code: "wecom_app_sent",
|
||||
message: null,
|
||||
response: { errcode: 0, recipientCount: 1 },
|
||||
};
|
||||
});
|
||||
const report = await executeOpsAlert(baseOptions({ send: true }), {
|
||||
summary: baseSummary(),
|
||||
recipientRows: recipientRows({
|
||||
wangshunli: { employeeNo: "missing", wecomExternalUserId: "" },
|
||||
}).filter((row) => row.employeeNo !== "missing"),
|
||||
sendMessageImpl,
|
||||
env: { WARCH3_OPS_ALERT_SEND: "1" },
|
||||
config: { wecomRequestTimeoutMs: 1000 },
|
||||
});
|
||||
|
||||
expect(report.status).toBe("PASS_ALERT_SENT_WITH_CONFIG_GAPS");
|
||||
expect(report.recipientGaps.map((item) => item.id)).toContain("recipient_missing");
|
||||
expect(report.delivery.status).toBe("OK");
|
||||
expect(sendMessageImpl).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("classifies missing binding as config gap", async () => {
|
||||
const report = await executeOpsAlert(baseOptions({ send: true }), {
|
||||
summary: baseSummary(),
|
||||
recipientRows: recipientRows({
|
||||
wangshunli: { wecomExternalUserId: "" },
|
||||
HYD000006: { wecomExternalUserId: "" },
|
||||
}),
|
||||
sendMessageImpl: vi.fn(),
|
||||
env: { WARCH3_OPS_ALERT_SEND: "1" },
|
||||
config: {},
|
||||
priorState: buildAlertState(baseSummary({ status: "PASS_ARCHIVE_OPS_READY", findingCount: 0, findings: [] })),
|
||||
});
|
||||
|
||||
expect(report.status).toBe("PARTIAL_ALERT_NEEDS_CONFIG");
|
||||
@@ -181,6 +204,8 @@ describe("send-wecom-ops-alert", () => {
|
||||
})),
|
||||
env: { WARCH3_OPS_ALERT_SEND: "1" },
|
||||
config: {},
|
||||
priorState: buildAlertState(baseSummary({ status: "PASS_ARCHIVE_OPS_READY", findingCount: 0, findings: [] })),
|
||||
writeAlertState: vi.fn(() => "/tmp/warch3-state.json"),
|
||||
});
|
||||
|
||||
expect(report.status).toBe("SKIP");
|
||||
|
||||
在新工单中引用
屏蔽一个用户