收口多维系统审查(07-16-system-multidimensional-audit)

这个提交包含在:
2026-07-17 22:12:39 +08:00
父节点 8f8606c328
当前提交 b418599b0e
共修改 33 个文件,包含 2805 行新增和 0 行删除
+85
查看文件
@@ -16,6 +16,91 @@ Start from `docs/文档索引.md` and `docs/文档事实源状态矩阵.md`.
raw payloads, full account identifiers, invoice originals or confidential raw payloads, full account identifiers, invoice originals or confidential
business content. business content.
## Scenario: Finding Lifecycle And Audit Closeout
### 1. Scope / Trigger
- Trigger: a review, audit or trial-run task records findings and later code,
deployment or external evidence changes their current state.
- Applies to Trellis PRDs, review reports, finding ledgers and management
summaries.
### 2. Signatures
- Canonical ledger fields: `findingId`, `dimension`, `discoverySeverity`,
`confidence`, `discoveryStatus`, `remediationStatus`, `deploymentStatus`,
`externalVerificationStatus`, `evidenceRef`, `businessImpact`,
`controlObjective`, `safeVerification`, `followUpWorkPackage`.
- Delta fields: `discoveryCommit`, `reviewedCommit`, changed-commit list,
validation command, result and explicit environment boundary.
### 3. Contracts
- Preserve the discovery baseline, discovery severity and discovery status.
Later remediation must add a delta; it must not rewrite history as if the
finding never existed.
- Record remediation, deployment and external-behavior verification as three
independent states. A local test pass does not prove deployment, and a
deployed build marker does not prove role-specific or real-device behavior.
- A closeout task must have one canonical finding ledger. Component reports
may retain their original wording, but duplicate IDs and reclassified items
must point to the canonical ledger.
- When HEAD moves after discovery, record the exact old and new commits,
classify the delta and rerun the closeout gates. Do not relabel an old report
as evidence for the new HEAD.
- Use repo-relative evidence references and synthetic fixtures. Unknown or
unauthorized deployment, public, production-data and real-device checks stay
explicit as pending or externally gated.
### 4. Validation & Error Matrix
- Local fix tested, not deployed -> remediation may pass; deployment remains
pending and external behavior remains pending when applicable.
- Finding open with no fix artifact -> deployment is `NO_FIX_TO_DEPLOY`, not
`DEPLOYMENT_PENDING`.
- Static/tooling-only finding -> deployment may be `NOT_APPLICABLE`; the
remediation state still records whether the contract itself is fixed.
- Duplicate component finding -> preserve its source reference and map it to
exactly one canonical ledger entry.
### 5. Good/Base/Bad Cases
- Good: a historical P0 remains P0/confirmed while local remediation passes,
deployment is pending and external behavior is pending.
- Base: an open local-only QA finding uses `NO_FIX_TO_DEPLOY`,
`NOT_APPLICABLE` deployment or `NOT_REQUIRED` external verification as
appropriate.
- Bad: rename the discovery status to `RESOLVED` and omit whether the fix was
deployed or observed externally.
### 6. Tests Required
- Task validation and Trellis governance.
- Sensitive-content scan and document registry audit.
- `git diff --check` plus a manual check that the recorded HEAD matches the
evidence-producing checkout.
### 7. Wrong vs Correct
#### Wrong
```json
{"findingId":"F-001","status":"resolved"}
```
#### Correct
```json
{
"findingId": "F-001",
"discoverySeverity": "P0",
"discoveryStatus": "CONFIRMED",
"remediationStatus": "LOCAL_FIX_VERIFIED",
"deploymentStatus": "DEPLOYMENT_PENDING",
"externalVerificationStatus": "EXTERNAL_BEHAVIOR_PENDING"
}
```
## Scenario: QA Readiness Report Summaries ## Scenario: QA Readiness Report Summaries
### 1. Scope / Trigger ### 1. Scope / Trigger
+86
查看文件
@@ -18,6 +18,92 @@
redacted form. redacted form.
- `scripts/qa/check-trellis-governance.mjs` must pass before commit. - `scripts/qa/check-trellis-governance.mjs` must pass before commit.
## Scenario: Defensive Security Finding Summaries
### 1. Scope / Trigger
- Trigger: a security review or remediation task writes findings into ordinary
repository-visible Trellis material, specs, reports or management summaries.
- This contract governs the shareable summary. It does not replace focused
automated tests or the archived remediation task that carries engineering
proof.
### 2. Signatures
- Allowed summary fields: `findingId`, `riskCategory`,
`discoverySeverity`, repo-relative `codeLocations`, `businessImpact`,
`controlObjective`, `safeVerification`, `remediationStatus`,
`deploymentStatus`, `externalVerificationStatus`, `evidenceRef`.
- Forbidden summary fields: exploit procedure, payload, bypass sequence,
probability/throughput calculation, raw request/response, real-environment
identifier or secret-bearing value.
### 3. Contracts
- Keep the finding ID, risk category, discovery severity, repo-relative code
locations, business impact, control objective, safe verification result and
the separate remediation/deployment/external states.
- Do not store attack procedures, working payloads, bypass instructions,
probability calculations, composable endpoint sequences, raw provider or
browser payloads, or identifying details from a real environment.
- Phrase verification as a harmless control assertion, such as an unauthorized
request being rejected, active content being forced to a non-executable
download, or a negative-path test passing with synthetic data.
- Detailed engineering proof belongs in focused automated tests and the
archived remediation task. Ordinary finding ledgers reference that evidence
instead of copying sensitive mechanics.
- If evidence cannot be safely summarized, keep only metadata and a restricted
evidence reference; do not place the raw material in the repository or chat.
### 4. Validation & Error Matrix
- Safe code location + defensive result -> allowed.
- Real account, token, attachment, contract body, business payload or complete
environment detail -> remove and regenerate the evidence.
- Reproduction text that enables misuse -> replace with the control objective
and a synthetic negative-path assertion.
- Local remediation pass without deployment/public evidence -> keep deployment
and external behavior pending.
### 5. Good/Base/Bad Cases
- Good: identify the affected module, state the business impact and assert that
a synthetic unauthorized or active-content negative path is rejected.
- Base: an unresolved static risk keeps a safe code reference and control
objective while external behavior remains gated.
- Bad: copy a working request sequence, payload, real endpoint context or real
data fragment into a PRD or finding ledger.
### 6. Tests Required
- `node scripts/qa/check-sensitive-content.mjs`.
- Manual review of security summaries for procedural or identifying detail.
- Focused positive/negative automated tests using synthetic fixtures.
### 7. Wrong vs Correct
#### Wrong
```json
{
"findingId": "SEC-001",
"reproduction": "step-by-step operational procedure",
"rawPayload": "real or executable content"
}
```
#### Correct
```json
{
"findingId": "SEC-001",
"riskCategory": "authorization boundary",
"businessImpact": "data outside the actor's scope may be exposed",
"controlObjective": "deny by default and enforce object scope",
"safeVerification": "synthetic unauthorized request is rejected"
}
```
## Scenario: Business Attachment Governance ## Scenario: Business Attachment Governance
### 1. Scope / Trigger ### 1. Scope / Trigger
@@ -0,0 +1,4 @@
{"file":".trellis/spec/hyb-oa/docs-and-evidence.md","reason":"复核审计基线、当前 delta 和四层状态没有混写。"}
{"file":".trellis/spec/hyb-oa/security-privacy.md","reason":"复核提交材料不含攻击步骤、载荷、绕过方式或真实环境细节。"}
{"file":".trellis/tasks/07-16-system-multidimensional-audit/research/findings.md","reason":"复核唯一 finding 台账、分级校准、去重和后续工作包映射。"}
{"file":".trellis/tasks/07-16-system-multidimensional-audit/research/current-head-delta.md","reason":"复核当前 HEAD、增量提交、本地门禁与外部边界。"}
@@ -0,0 +1,101 @@
# 多维系统审查设计
## Safety Boundary
- 本任务是只读审查任务。默认允许读取代码、文档、Git 历史和本地测试输出,以及运行无凭据的本地构建、单测、静态门禁和 mock E2E。
- 禁止修改业务代码、数据库、schema、迁移、权限、页面、生产配置或历史证据;发现问题只登记 finding。
- 禁止访问带凭据公网环境、执行真实业务写入、企业微信写入、真实付款或数据库重置。
- 生成截图或测试报告时,固定本轮输出目录并在结束前清理;既有历史证据目录保持不动。
- 普通任务材料中的安全 finding 只保留代码位置、风险类别、业务影响、控制目标和无害验证结论;攻击步骤、载荷、绕过方式、概率推演、可组合接口细节和真实环境信息不进入 task、spec 或聊天摘要。
## Audit Model
审查采用三层证据模型:
1. **合同层**:当前 Trellis task、项目 spec、业务事实源和现行手册定义“应该是什么”。
2. **实现层**:路由、页面、API、service、schema、权限和状态机定义“代码实际是什么”。
3. **行为层**:单测、构建、E2E、截图和 QA runner 证明“当前本地行为是什么”。
只有三层一致时才可判为通过;缺一层时记录证据缺口或外部准入,不用历史结论补证。
## Execution Order
### 1. 冻结基线与资产清单
- 记录 `HEAD`、工作树、Node/npm/Prisma/浏览器版本和本地运行边界。
- 建立功能域、路由、页面、API、schema、测试和事实源清单。
- 标记 feature flag、冻结能力、历史兼容入口和只允许外部验证的能力。
### 2. 功能与业务闭环审查
- 按普通员工、审批人、财务/出纳、管理员等代表角色抽查核心业务链。
- 从入口追踪到 API、service、storage、状态变化、附件/证据、通知和审计。
- 对异常、重复提交、撤回、归档、无权限和外部依赖失败进行负路径审查。
### 3. 权限、状态机、数据与安全审查
- 对照导航/路由、前端动作和后端权限,定位“能看不能做”“前端隐藏但后端未拦”“后端拦截但前端误导”等不一致。
- 追踪关键枚举、字段、金额/日期、附件归属和审计信息跨层传递。
- 聚焦认证、导出、上传/下载、日志、错误响应、Secret/env 和外部集成边界。
### 4. 页面与设计审查
- 先审全局 shell、导航、搜索、通知、错误/空/加载/403/404,再审核心列表、详情和编辑流程。
- PC 以 1440/1024 为主,移动以 375 为主并抽查 320/390/430;涉及企微时只做 UA/布局证据,真机结论保持外部准入。
- 复核标题动作、筛选/表格、表单反馈、弹窗/危险操作、键盘/焦点、对比度、触控目标和横向滚动。
### 5. 测试、性能、可靠性与运维审查
- 先运行基础门禁,再按矩阵选择最小充分的 Playwright 集合;不以测试数量代替覆盖质量。
- 检查前端没有 unit spec 是否造成关键纯逻辑/组件合同缺口;检查 E2E 是否陈旧、重复、依赖选择器或环境脆弱。
- 通过静态证据、构建报告和必要的浏览器测量识别性能风险;不做无数据的泛化优化建议。
- 核对 readiness、来源追溯、迁移、回滚、备份恢复准入、巡检、文档和培训口径。
### 6. Finding 收敛与交叉复核
- 每个 finding 先与现有任务、历史证据和其他维度去重。
- P0/P1 finding 至少做一次独立只读复核;权限、状态机、迁移和审批规则 finding 必须跨层复核。
- 将共因问题归并为根因,例如权限显隐合同缺失、页面模板未约束、测试与当前交互漂移。
- 输出建议修复波次:P0 立即隔离、P1 核心闭环、P2 体验/维护、外部准入。
## Evidence Layout
- `research/baseline.md`:commit、环境和门禁结果。
- `research/inventory.md`:功能/路由/页面/API/schema/test 资产矩阵。
- `research/findings.md`:唯一 finding 台账。
- `research/page-review.md`:页面、视觉、交互、响应式和可访问性证据。
- `research/business-flow-review.md`:核心业务链和负路径证据。
- `research/engineering-review.md`:权限、数据、安全、测试、性能和运维证据。
如现有文件可承载则复用,不为每个小问题新建文档。
## Finding Schema
```text
ID | Dimension | Discovery Severity | Confidence | Discovery Status |
Remediation Status | Deployment Status | External Verification |
Evidence Ref | Business Impact | Control Objective | Safe Verification |
Follow-up Work Package
```
状态分层规则:
- `Discovery Status` 只描述发现时证据:`CONFIRMED`、`LIKELY`、`EXTERNAL_GATED`、`DUPLICATE` 或 `NOT_AN_ISSUE`。
- `Remediation Status` 只描述当前本地修复,不因部署或公网未知而回退;常用值为 `OPEN`、`ACTIVE_TASK_OPEN`、`LOCAL_FIX_VERIFIED`、`FOLLOW_UP_RECOMMENDED`。`ACTIVE_TASK_OPEN` 只用于已有活动 task 承载但本轮不重复建账的 finding。
- `Deployment Status` 独立使用 `DEPLOYMENT_PENDING`、`NO_FIX_TO_DEPLOY` 或 `NOT_APPLICABLE`。只有存在本地修复产物时才可写 `DEPLOYMENT_PENDING`。
- `External Verification` 独立使用 `EXTERNAL_BEHAVIOR_PENDING`、`EXTERNAL_GATED` 或 `NOT_REQUIRED`。
- `Safe Verification` 只写防御性断言、测试结论和浏览器尺寸结果,不写可操作的攻击复现过程。
## Exit Conditions
- P0/P1/P2 和 `EXTERNAL_GATED` 均有清晰定义与证据。
- 所有计划内维度完成覆盖或明确说明为什么未覆盖。
- 不存在因审查产生的业务代码、配置、数据或未清理生成物改动。
- 最终报告能直接支持用户决定“立即修、分波修、接受、外部补证或暂缓”。
- 原基线与当前 HEAD delta 均被保留;本地修复、部署和外部行为状态不互相替代。
## Rollback
- 审查正文和 task 记录可通过本任务提交回退。
- 本任务不产生业务实现、schema、数据库或生产配置回滚。
- 浏览器和测试生成物仅删除本轮明确生成的路径。
@@ -0,0 +1,4 @@
{"file":".trellis/spec/hyb-oa/project-governance.md","reason":"约束 Trellis-first、当前 task 执行合同与归档边界。"}
{"file":".trellis/spec/hyb-oa/docs-and-evidence.md","reason":"约束发现、修复、部署、外部验证状态分离及证据收口。"}
{"file":".trellis/spec/hyb-oa/security-privacy.md","reason":"约束安全 finding 的双层脱敏与无害验证表达。"}
{"file":".trellis/tasks/07-16-system-multidimensional-audit/research/baseline.md","reason":"保留原审查基线、环境边界和旧门禁证据。"}
@@ -0,0 +1,256 @@
# 多维系统审查与问题分级
## Goal
以 2026-07-16 当前本地 `main` 为审查对象,从业务功能、页面体验、视觉与交互、权限与状态机、数据与安全、测试与性能、运维与文档等多个角度检查已经完成的系统,形成可复现、可去重、可排序的 P0/P1/P2 finding 和后续治理路线,而不是在审查过程中边看边改。
## Scope
- 审查当前本地前端、后端、数据模型、自动化、部署/运行脚本和现行文档事实源。
- 覆盖功能与业务闭环、权限/状态机/数据、页面/设计/跨端、安全/隐私/依赖、测试/性能/可靠性、运维/交付/文档。
- 只运行本地、无凭据、无业务写入的静态检查、构建、单测、mock E2E 和浏览器复核。
- 产出证据化 finding、风险地图和建议修复波次;不在本任务实施业务修复。
## What I Already Know
- 遗留任务和运行手册已经完成收口;当前工作树在审查 task 创建前干净。
- 最后有证据的公网部署来源为 `df26922`;当前本地 `main` 包含后续修复与收口,但尚未 push、部署或完成公网复检。本任务只审查本地基线,不能代表当前公网状态。
- 技术栈为 React + TypeScript + Vite + Tailwind 前端,以及 Fastify + Prisma + PostgreSQL 后端。
- 当前仓库约有 75 个前端页面文件、60 个前端组件文件、76 个路由声明、68 个后端 route 文件、63 个 service 文件、117 个 Prisma model。
- 自动化资产约有 63 个 Playwright spec、153 个后端测试文件和 50 个 QA 脚本;前端源码目录没有独立 unit spec,页面行为主要由 E2E 覆盖。
- 页面操作区任务已登记 5 类既有 finding:付款/合同/票据权限显隐、资产旧测试交互、标题动作合同与项目/资产视觉覆盖不足。本轮应复核和去重,不重复制造同一问题。
- `ui-ux-pro-max` 对企业后台给出的高价值检查项包括键盘可达、可见焦点、表格移动适配、表单反馈、标题层级、移动端横向滚动、固定导航遮挡和响应式断点;其生成的红/金活泼视觉建议与当前成熟 OA 基线不匹配,不作为换肤或重品牌依据。
- 用户已经确认:先完成收口,再独立从功能、页面、设计等多个角度审查系统并查看问题。
## Confirmed Constraints
- 用户于 2026-07-16 选择方案 1:审查采用“先广度扫描和风险分级,再对高风险链路深挖”的方式,避免只看页面或只跑测试。
- 用户于 2026-07-16 已最终批准本 PRD 与 `design.md`,可以启动本地只读审查;外部动作仍未授权。
- 默认只使用本地、mock、静态和无凭据验证;不连接生产数据库,不运行 live spec,不执行真实 OCR、企微写入、付款、部署或公网配置动作。
- 审查输出写入当前 Trellis task 的 `research/` 和 PRD,不新建重复的业务手册或泛化审计文档。
- finding 只有在有代码位置、命令结果、截图或稳定复现步骤时才标为已确认;需要公网、真机或真实角色才能判断的事项单列为 `EXTERNAL_GATED`,不虚报 PASS/FAIL。
## Open Questions
- 无。
## Requirements (Evolving)
### 1. 功能与业务闭环
- 按实际小团队使用路径审查登录/身份、工作台、报销与 PDF OCR、审批、采购、合同、项目、资产、票据、付款批次/AP/代账交接、人员/考勤/企微边界和系统管理。
- 对每个域确认入口、创建/编辑/提交、审批/复核、异常/撤回、附件/证据、导出/归档、权限与审计是否闭环。
- 区分“代码存在”“自动化通过”“本地可用”“公网已部署”“真实业务已验证”,不得互相替代。
### 2. 权限、状态机与数据
- 对照路由显隐、前端动作显隐和后端权限校验,检查只读角色、跨人员数据、管理能力和高敏附件边界。
- 抽查核心状态机的合法迁移、幂等、并发、失败回滚、撤回/归档和审计记录。
- 检查 Prisma schema、迁移、route/service/前端类型之间的字段和枚举一致性;不执行数据库 reset 或生产 drift 修复。
### 3. 页面、信息架构与设计
- 建立 PC 页面清单、移动页面清单和企微入口映射,检查菜单、路由、标题、面包屑、列表/详情/编辑结构及空/错/加载/无权限状态。
- 检查视觉层级、间距、密度、颜色/对比度、图标、表格、表单、弹窗、危险操作、反馈、焦点与键盘可达性。
- 检查 375、768、1024、1440 等代表性视口,并按产品规则补充必要的小屏和企业微信 UA;不以普通浏览器 UA 代替真机结论。
- 设计审查尊重既有品牌与组件体系,不把外部推荐的调色板或字体直接当作改版结论。
### 4. 跨端和外部边界
- 检查 PC、移动、企业微信 deeplink/通知/只读镜像之间的功能归属与信息一致性。
- 对 OA 主办、企微主办、OA 镜像、应急接管和高敏附件归属进行抽查,避免入口存在但业务责任不清。
- 需要真实企业微信、真机、live OCR 或公网来源确认的 finding 只记录准入条件和证据缺口。
### 5. 安全、依赖与隐私
- 静态检查认证、授权、上传/下载、导出、日志、错误响应、敏感内容、Secret/env、依赖风险和外部集成 fail-closed 边界。
- 不在报告中写真实票据、合同正文、完整账号、银行资料、token、Secret、完整本地路径或原始 payload。
### 6. 测试、性能与可靠性
- 评估后端测试、前端 E2E、视觉测试、权限测试和 QA runner 对真实核心路径的覆盖与重复/陈旧/脆弱情况。
- 检查构建体积、懒加载、列表/搜索/表格、重复请求、长任务、轮询、后台 job、失败重试、超时和可观测性风险;先测量再给性能建议。
- 环境性失败、测试缺陷和产品缺陷分开归类。
### 7. 运维、交付与文档
- 检查部署来源、环境合同、迁移/回滚、readiness、备份恢复准入、试运行、告警、巡检、操作手册和培训材料是否一致。
- 不把历史 `issues/*.csv` 当作当前执行合同,不把旧试运行或旧公网证据当作当前 PASS。
### 8. Finding 合同
- 每条 finding 至少包含:ID、维度、严重级别、置信度、状态、证据、复现、影响、建议、修复边界、是否需要用户决策、是否已有 task。
- `P0`:存在现实的数据泄露/丢失、越权、高风险资金或系统不可用风险,或核心业务无法继续且没有可接受绕行。
- `P1`:核心业务链路、广泛角色权限、关键数据一致性或主要跨端体验显著受损,但存在临时绕行或影响范围可控。
- `P2`:局部一致性、可用性、维护性、覆盖率、性能观察项或文档问题,不阻断核心业务。
- 状态使用 `CONFIRMED`、`LIKELY`、`EXTERNAL_GATED`、`DUPLICATE`、`NOT_AN_ISSUE`,避免把证据不足的问题强行定级。
## Initial Acceptance Criteria(Superseded)
> 以下清单保留原始全维审查合同,用于说明发现阶段的覆盖意图。2026-07-17
> 起本任务按后文“风险收敛收口”验收,不据此声称已经完成穷尽式全仓审计。
- 固定本地审查 commit、环境边界、命令和生成物清理规则。
- 建立功能域 × 角色 × 页面/接口 × 证据的审查矩阵。
- 覆盖核心业务闭环、权限/状态机/数据、页面/设计、跨端、安全、测试/性能、运维/文档八大类。
- 运行批准范围内的基础门禁与聚焦自动化;所有失败按产品/测试/环境分类。
- 关键 PC/移动页面形成可追溯的结构或截图证据,不把旧截图当作当前基线。
- 已有页面操作区 finding 完成复核和去重。
- 所有确认 finding 具备证据、复现、影响和建议;外部受限项明确准入条件。
- 输出管理摘要、P0/P1/P2 清单、跨域根因、建议修复波次和需要用户决策的事项。
- 审查阶段不修改业务代码、schema、配置或真实数据,不自动创建一批修复实现。
- task validate、Trellis governance、敏感内容检查和 `git diff --check` 通过。
## Risk Convergence Closeout(2026-07-17)
### Closeout Decision
**Context**:原发现基线为 `bbb5c89`,当前本地目标已前移到 `8f8606c`,期间两个原 P0 已在独立任务完成本地修复与归档。继续按原始清单扩大审查会降低边际收益,也会混淆发现历史、当前修复、部署和外部行为。
**Decision**:本任务从“继续扩大全维扫描”转为“风险收敛收口”。保留原基线与分域报告,新增当前 HEAD delta、唯一 finding 台账、本地浏览器行为证据和双层脱敏规则;不修改业务代码,不创建下游修复 task,只登记推荐工作包顺序。
**Consequences**:可以稳定回答“发现过什么、当前本地修到哪里、哪些尚未部署或外部验证”,但不能据此声称当前代码已部署、公网角色可用、企微真机通过或真实业务已经完成新一轮试运行。
### Management Summary
- 唯一台账共 34 条:发现时 P0 2、P1 11、P2 21;当前开放 P0 为 0。
- 两个原 P0 均保持发现时 P0/`CONFIRMED`,当前为 `LOCAL_FIX_VERIFIED`、`DEPLOYMENT_PENDING`、`EXTERNAL_BEHAVIOR_PENDING`。
- 当前最重要的开放项是员工敏感数据范围、付款状态一致性、高敏合同附件和发布门禁可信度。
- 两个已本地修复 P0 的部署与外部行为复核是另行授权的 Gate 0;六个工作包只排序本地开放问题,不能替代 Gate 0。
- 768×1024 与 1024×768 浏览器量测确认共享表格容器隐藏超宽内容,多个核心列表的右侧操作不可达,`RESP-TABLE-001` 定为 P1。
- 375×812 归档页的路由、空态、列表和底栏通过;长详情内部约 105px 被隐藏,形成 P2 `MOB-ARCHIVE-001`。这一区分证明静态移动路由合同失败本身不直接等于产品缺陷。
- 后端正确拒绝的按钮显隐、全局搜索列表落点、通知目标缺失、可恢复动作确认和持续可访问性治理均按批准口径收敛为 P2;移动考勤入口自循环保留 P1。
- 页面工作包内部先处理不可逆/资金动作确认、核心长表单保护、移动考勤自循环和中间断点表格四条 P1,再处理权限显隐与现有 `05-31` 任务;其他页面 P2 按触达治理,不一次打成巨型实施任务。
### Revised Acceptance Mapping
| 收口验收项 | 状态 | 证据 |
| --- | --- | --- |
| 保留原基线并补当前 HEAD delta,不改写历史结论 | PASS | `research/baseline.md`、`research/current-head-delta.md` |
| 形成唯一 finding 台账,统一分级、去重和四层状态 | PASS | `research/findings.md` |
| 完成 375 移动归档与 768/1024 五个共享表格页面行为量测 | PASS | `research/browser-review.md`、`research/evidence/local-browser-8f8606c/` |
| 安全材料完成双层脱敏并沉淀稳定摘要规则 | PASS | `research/business-security-review.md`、`.trellis/spec/hyb-oa/security-privacy.md` |
| 发现/修复/部署/外部验证状态分离成为稳定证据合同 | PASS | `design.md`、`.trellis/spec/hyb-oa/docs-and-evidence.md` |
| 后续工作包按批准顺序登记且未创建下游 task | PASS | `research/findings.md#推荐工作包顺序` |
| 当前 HEAD 全量本地基础门禁与收口治理门禁 | `COMPLETE_WITH_KNOWN_FINDINGS` | `research/current-head-delta.md#最终本地收口门禁`;完整基础门禁通过,专项移动合同按预期失败,流程覆盖证据保持不可信 finding,均未被掩盖。 |
| 提交范围仅含当前 task、必要 spec 与脱敏合成截图 | PASS | `git status`、`git diff --check` 与最终只读 reviewer;0 blocker / 0 major / 0 minor。 |
### Revised Closeout Acceptance
- [x] 原发现基线 `bbb5c89` 和当前目标 `8f8606c` 均有明确、不可互相替代的证据。
- [x] 所有分域 finding 已映射到唯一台账,批准的严重度校准和拆分已执行。
- [x] 两个原 P0 分别记录本地修复、部署和外部行为状态,没有改写发现时严重度。
- [x] 浏览器证据记录 viewport、最终 URL、标题、角色 fixture、横向溢出、操作可达和底栏边界,且只使用合成数据。
- [x] 普通安全摘要不含攻击步骤、载荷、绕过方式、概率推演、可组合接口细节或真实环境资料。
- [x] 外部未覆盖项标记为 `EXTERNAL_GATED`、`EXTERNAL_BEHAVIOR_PENDING` 或后续工作包。
- [x] 全量本地基础门禁、task validate、governance、敏感扫描、文档 registry、测试资产清单和 `git diff --check` 已按当前工作树重跑。
- [x] 只读复核确认 P0/P1 当前状态、提交范围和未授权边界准确。
### Uncovered And Authorization-Gated Boundaries
- 未部署,未检查公网 build SHA、边缘响应头、真实代理链或公网角色矩阵。
- 未连接生产数据库,未执行迁移状态、drift、备份恢复、并发付款或任何真实数据写入。
- 未读取真实员工档案、合同正文、附件、票据、银行资料、导出文件或客户端设备存储。
- 未执行企业微信真机、真实通知、真实 OCR、在线依赖审计或外部服务写入。
- 旧 Day 1–Day 4 试运行不复用;未来部署和外部门禁通过后必须从新的 Day 1 开始。
- 上述边界不阻塞本地只读审查归档,但阻塞“已部署”“可公网测试”和“真实业务通过”等对外表述。
## Definition of Done
- 当前本地系统的主要能力、页面和风险有可追溯的审查覆盖,而不是只罗列已有测试。
- finding 分级一致、去重、可复现,并能直接转成后续独立 Trellis task。
- 明确哪些结论只适用于本地,哪些仍需部署、公网、真机或真实角色验证。
- 给出推荐修复顺序,但不在本任务实施业务修复。
## Feasible Approaches
### 方案 1:全维度风险优先(推荐)
- 先做全系统静态、自动化和页面矩阵扫描,快速形成 P0/P1/P2 初版;再对 P0/P1 及跨域根因深挖。
- 优点:覆盖用户要求的多个角度,较早暴露致命风险,也能控制审查范围。
- 代价:首轮每个业务域的深度有限,需要第二轮按高风险聚焦。
### 方案 2:核心业务流深挖优先
- 先深挖报销/OCR/审批、采购/合同/项目、付款/AP 和人员/企微主链,再补页面与横向治理。
- 优点:核心业务证据最扎实。
- 代价:页面、设计、文档和低频管理能力的问题会较晚出现。
### 方案 3:页面与设计优先
- 先完成全部路由、PC/移动视口、信息架构、交互和视觉一致性审查,再回到后端与业务闭环。
- 优点:用户可见问题收敛快。
- 代价:越权、状态机和数据一致性等高风险问题可能被延后。
## Decision (ADR-lite)
**Context**:系统覆盖前后端、移动/企微、财务与审批等多个域;只做页面审查会遗漏越权、状态机和数据风险,只深挖业务流又会延后用户可见问题。
**Decision**:采用方案 1。第一轮先建立全系统资产和证据矩阵,完成八大维度广度扫描并形成 P0/P1/P2 初版;随后只对 P0/P1、跨域共因和证据冲突进行第二轮深挖。
**Consequences**:可以尽早得到完整风险地图,并控制审查期间不混入修复。代价是首轮不会对每个 P2 做同等深度分析;P2 在风险地图稳定后按修复波次再展开。
## Execution Plan
### Wave 1:冻结基线与全量资产矩阵
- 记录本地 commit、环境、功能域、角色、路由、页面、API、schema、测试和事实源。
- 运行基础静态/构建/单测门禁,分类现有失败和 warning。
### Wave 2:业务、权限、状态机、数据与安全扫描
- 追踪核心业务闭环和代表角色的正/负路径。
- 对导航、动作、API 权限与数据范围做跨层对照。
- 检查状态迁移、附件/导出、审计、敏感内容和外部集成边界。
### Wave 3:页面、设计、响应式与跨端扫描
- 使用本地 Vite dev + Playwright 复核核心 PC/移动页面和企业微信 UA 布局。
- 检查信息架构、视觉层级、表单/表格/反馈、键盘/焦点、危险操作和跨端归属。
- 复核并去重页面操作区任务已有 5 类 finding。
### Wave 4:测试、性能、可靠性、运维与文档扫描
- 评估 63 个 E2E spec、153 个后端测试文件和 50 个 QA 脚本的覆盖、重复、陈旧和环境脆弱性。
- 基于构建/运行证据检查性能、任务调度、重试、可观测性、部署来源、回滚、备份和文档培训。
### Wave 5:Finding 收敛与独立复核
- 统一 finding schema,合并重复问题和跨域根因。
- 对 P0/P1、权限、状态机、迁移和审批规则 finding 做独立只读复核。
- 输出管理摘要、问题分级、修复波次、外部补证项和需要用户决策的事项。
## Test Plan (Draft)
```bash
cd server && npm run prisma:validate
cd server && npm test
cd client && npm run typecheck
cd client && npm run build
cd client && npx playwright test --list
node scripts/qa/check-process-contract.mjs --baseline --json
node scripts/qa/check-oa-permission-matrix.mjs
node scripts/qa/check-mobile-route-contract.mjs
node scripts/qa/check-sensitive-content.mjs
node scripts/qa/audit-docs.mjs --check-registry
node scripts/qa/check-trellis-governance.mjs
git diff --check
```
- Playwright 实跑范围在方案确认后冻结,优先选择本地 mock、核心流程、权限直连、跨页面一致性、项目级视觉和移动路由相关 spec;明确排除 `manual/` 和 `live-smoke.spec.ts`。
- 浏览器复核需要本地 Vite dev,避免 production preview 的 PWA Service Worker 绕过 route mock;生成物只清理本轮明确产生的目录。
## Out of Scope
- 修复审查发现的业务代码、页面、schema、测试或文档问题。
- push、部署、公网检查、生产配置、数据库 reset、真实数据、企业微信写入、真实付款和真实 OCR。
- 补造旧试运行、真机或人工业务闭环证据。
- 为追求 warning 数字归零而批量重写历史证据或无关脚本。
## Technical Notes
- 审查设计见 `design.md`。
- 基线盘点见 `research/audit-scope-baseline.md`。
- 事实源入口:`docs/文档索引.md`、`docs/文档事实源状态矩阵.md`。
- 设计事实与工程规则:`docs/产品设计规则.md`、`.trellis/spec/hyb-oa/frontend-react-vite-design.md`。
- 权限、审批、报销/OCR、企微、部署和安全分别读取 `.trellis/spec/hyb-oa/` 对应规约。
@@ -0,0 +1,52 @@
# 多维系统审查范围基线
## Repository Snapshot
- 审查 task 创建日期:2026-07-16。
- 创建 task 前工作树:clean。
- 当前技术栈:React/TypeScript/Vite/Tailwind;Fastify/Prisma/PostgreSQL。
- 前端资产:75 个 page 文件、60 个 component 文件、76 个 `<Route>` 声明。
- 后端资产:68 个 route 文件、63 个 service 文件、117 个 Prisma model。
- 验证资产:63 个 Playwright spec、153 个后端测试文件、50 个 QA 脚本;`client/src` 下没有独立 unit spec。
这些数字只用于确定审查规模,不等同于功能完成率或测试覆盖率。
## Current Truth Boundary
- 最后有证据的公网部署来源为 `df26922`。
- 当前本地 `main` 包含后续可信度修复和治理收口,尚无对应公网部署证据。
- 旧 7 天试运行只完成 Day 1–Day 4,原目标未完成;不得用于证明当前系统已经连续稳定运行。
- 本轮只读审查默认只对当前本地基线作结论。
## Existing Findings To Reconcile
- 付款页对仅有复核权限的角色仍显示新建批次。
- 合同只读角色仍看到新增、编辑、续约和归档入口。
- 票据上传按钮未使用已有上传权限判断。
- 两个资产 E2E 仍按旧“资产分类”直接按钮交互。
- 标题动作容器只有布局,没有动作数量合同;项目/资产视觉覆盖不足。
以上问题来自 `05-31-unified-page-header-actions`,新审查应复核、补证和归因,不重复登记。
## UI/UX Rubric Input
`ui-ux-pro-max` 对本任务有价值的通用检查项:
- 键盘可达、焦点可见、标题层级和颜色不能作为唯一提示。
- 表格在移动端应使用可控横向滚动或卡片化,不能撑破 viewport。
- 表单需要 loading/success/error 反馈,并在 blur 或合理时机提供校验。
- 固定导航不能遮挡内容;交互状态不应引发布局位移。
- 代表视口至少覆盖 375、768、1024、1440,并检查移动横向滚动。
- React 搜索/筛选需要测量后再决定 debounce 或 `useDeferredValue`,不凭感觉优化。
该 skill 生成的“红/金 + 活泼块状”样式与当前成熟、低认知负担的企业 OA 目标冲突,因此不作为品牌、配色或换肤建议。现有产品事实与组件设计规则优先。
## Initial Risk Hypotheses
- 前端动作显隐与后端权限校验可能存在系统性合同缺口,而不只是三个页面的局部问题。
- 大量 E2E、零前端 unit spec 可能导致纯逻辑、组件合同和测试维护成本不平衡。
- 历史页面/视觉 spec 数量多,可能存在旧交互断言、重复覆盖和环境性脆弱。
- 117 个数据模型与多个集成/试运行脚本增加跨层状态、迁移和外部依赖口径漂移风险。
- 本地与公网来源分离后,任何“正式可用”结论都必须重新标注适用环境。
这些是假设,不是 finding;只有后续证据支持时才进入正式台账。
@@ -0,0 +1,59 @@
# 本地审查基线
## Snapshot
- 审查对象:本地 `main`,`bbb5c89825b0fbee60d5f093d7b30368cb7a5104`。
- commit 时间:2026-07-16 20:52:42 +08:00。
- task 创建前工作树:clean;审查期间仅当前 task 目录为未跟踪内容,未修改业务代码、schema、迁移、部署配置或真实数据。
- 环境:Linux、Node.js `v22.22.2`、npm `10.9.7`、Prisma / `@prisma/client` `7.8.0`、Fastify `5.10.0`、Playwright `1.58.2`。
- 结论边界:所有结果仅证明该本地 commit;最后有证据的公网部署来源仍是 `df26922`,不得据此声称公网已同步或可供用户测试。
## 基础门禁结果
| 检查 | 结果 | 证据摘要 |
|---|---|---|
| `./scripts/check-minimal.sh` | PASS | 首次因当前 PRD 缺 `## Scope` 被治理门禁正确阻断;补齐任务合同后重跑通过。 |
| Process contract | PASS | baseline 模式无阻断。 |
| Trellis governance | PASS | 当前 task 和治理合同有效。 |
| 敏感内容检查 | PASS | 扫描 30 个文件,0 finding。 |
| `cd server && npm run prisma:validate` | PASS | Prisma schema 有效。 |
| Server static checks | PASS | 脚本语法/静态门禁通过。 |
| `cd server && npm test` | PASS | 153/153 文件、786/786 用例通过。 |
| `cd client && npm run typecheck` | PASS | TypeScript 无错误。 |
| `cd client && npm run build` | PASS | Vite 处理 2381 modules;PWA 生成 102 个 precache entries。 |
| Feature matrix | PASS | 无阻断。 |
| 部署环境合同 | PASS | 无阻断。 |
| UI 静态设计规则 | PASS | 扫描 137 个文件,0 finding。 |
| `cd client && npx playwright test --list` | PASS | 枚举 144 tests / 56 files;只代表清单可解析,尚不代表行为通过。 |
| 文档审计 | PASS with warnings | 0 error;22 个未登记文件、19 条既有启发式 warning,后续单独归类。 |
基础门禁观察到两个非阻断工具链提示:Node `punycode` 弃用提示、Browserslist 数据陈旧提示。二者不等同于产品故障,进入工程维护观察项。
## 已确认的门禁缺口
### 移动路由静态合同
`node scripts/qa/check-mobile-route-contract.mjs` 返回 1 个 finding:
- `client/src/router/index.tsx:336` 的 `/mobile/wecom-approval-archive` 直接挂载 PC 页面组件 `WecomApprovalArchivePage`。
- 脚本规则明确禁止 `/mobile` 路由直挂非 `Mobile*Page` 组件。
- 当前状态:`LIKELY`。静态合同失败已经确认,但最终严重级别和用户影响需结合 375px 浏览器复核后裁定。
### 真实角色权限矩阵
`node scripts/qa/check-oa-permission-matrix.mjs` 未执行:
- 脚本默认访问公网地址,并要求多个真实角色凭据;同时会写入时间戳报告目录。
- 这超出当前“无公网、无凭据、无真实数据”的批准边界。
- 当前状态:`EXTERNAL_GATED`,不能记为 PASS 或 FAIL;准入条件是另行批准公网只读检查并提供脱敏测试角色。
## 生成物所有权与清理
- `client/dist/` 由本轮前端 build 于 2026-07-16 21:33 生成,已被 `.gitignore` 忽略;任务结束前可安全删除。
- `client/playwright-report/` 于 2026-07-16 21:34 生成,已被 `.gitignore` 忽略;聚焦 E2E 完成后与本轮 `test-results/`、专用截图目录一并清理。
- `artifacts/` 下已有历史审查证据不属于本轮生成物,不删除、不改写。
## 安全边界复核记录
- 未发现 `dangerouslySetInnerHTML`、直接 `innerHTML`、`eval`、字符串代码执行或不安全 `postMessage` 等明显前端执行 sink。
- 安全结论仍需结合认证/授权、cookie/CSRF、代理信任、signed URL 和附件下载链路;“未命中关键词”不能替代这些跨层检查。
@@ -0,0 +1,87 @@
# 本地浏览器复核
## 结论摘要
- 复核基线:本地 `HEAD 8f8606c328c4754901e045c34b071ecd36215bd3`。
- `RESP-TABLE-001`:`P1 / CONFIRMED`。768×1024 与 1024×768 均没有页面级横向滚动,但共享表格容器固定为 `overflow-x: hidden`,表格右侧被裁切且用户不能横向滚动恢复;1024×768 下五个受检页面的右侧操作均不可达。
- `MOB-ARCHIVE-001`:`P2 / CONFIRMED`。375×812 下空态、列表卡片、角色范围和底栏安全间距通过;详情展开后,长标识会把“历史详情”内部内容撑宽并被 `overflow-x: hidden` 裁切,页面本身又不提供横向滚动。
- 两组桌面标题动作均保持一行、完整位于视口内,标题区无横向溢出。本轮没有确认标题动作换行故障。
- 本轮没有访问公网、真实账号或真实业务数据;所有 API 响应均为本地合成 mock,浏览器上下文阻断 Service Worker,未发现未知 API、控制台错误或页面异常。
## 验证边界
- 服务:仅监听 `127.0.0.1` 的本地 Vite。
- 浏览器:本地 Playwright Chromium,`serviceWorkers: "block"`,`prefers-reduced-motion: reduce`。
- 网络:只允许本地 Vite 同源请求;本轮外部来源请求数为 0。
- 数据:全部为 `SYN-*` 合成对象;未使用真实姓名、票据、合同正文、账号、凭据、银行信息或原始 payload。
- 角色 fixture:移动空态使用 `employee`,移动有记录/详情使用 `hr`,桌面页面使用 `admin`。
- 页面加载、截图和量测均由 `/tmp` 临时脚本执行,未修改业务代码或测试代码。
## 375×812 移动企微审批归档
### 空态与列表
| 场景 | 角色 | 最终 URL | H1 | 页面横溢 | 桌面表格 | 底栏遮挡 | 结论 |
| --- | --- | --- | --- | ---: | ---: | --- | --- |
| 空态 | `employee` | `/mobile/wecom-approval-archive` | 企微审批历史 | `375 / 375`,0px | 0 | 滚动到底后末段距底栏 18.5px | PASS |
| 有记录 | `hr` | `/mobile/wecom-approval-archive` | 企微审批历史 | `375 / 375`,0px | 0,显示 1 张移动卡片 | 滚动到底后末段距底栏 18.5px | PASS |
角色范围符合 fixture:`employee` 只显示“与我相关 / 直属团队”,`hr` 额外显示“HR/管理员全量”。长申请人名称和长单号在列表卡片中可换行,没有形成页面级横向溢出。
### 详情展开
- 最终 URL 和标题保持不变。
- 内部移动滚动容器宽度为 `375 / 375`,滚动到底后详情末段与固定底栏仍有 18.5px 净距,底栏没有遮挡末尾内容。
- “历史详情”面板 `clientWidth=351px`、`scrollWidth=457px`,内部内容向右超出约 `105px`。
- 面板计算样式为 `overflow-x: hidden`;文档和移动滚动容器都没有横向滚动,因此超出内容被裁切且用户无法恢复查看。
- 该问题只在长详情合成数据下确认;空态和列表态没有同类右侧溢出。
据此,移动路由不能整体判定为失败:空态、列表、角色范围与底栏通过;详情长标识裁切作为 `P2 / CONFIRMED` 独立保留。
## 桌面中间断点量测
所有页面最终 URL 均与目标路由一致,浏览器标题为“海音达工作台”,页面 `document.scrollWidth` 均等于 viewport;问题发生在共享表格容器内部。
| viewport | 路由 | H1 | 容器 client/scroll | 可裁切宽度 | overflow-x | 右侧操作初始可达 |
| --- | --- | --- | --- | ---: | --- | --- |
| 768×1024 | `/projects` | 项目 | 702 / 760 | 58px | hidden | 否 |
| 768×1024 | `/assets` | 资产 | 702 / 760 | 58px | hidden | 否 |
| 768×1024 | `/contracts-registry` | 合同 | 702 / 760 | 58px | hidden | 是;截图中“编辑/更多”均可见,但容器合同仍失败 |
| 768×1024 | `/payment-batches` | 付款 | 702 / 760 | 58px | hidden | 否 |
| 768×1024 | `/reimbursements` | 我的报销 | 702 / 760 | 58px | hidden | 否 |
| 1024×768 | `/projects` | 项目 | 654 / 760 | 106px | hidden | 否 |
| 1024×768 | `/assets` | 资产 | 654 / 760 | 106px | hidden | 否 |
| 1024×768 | `/contracts-registry` | 合同 | 654 / 760 | 106px | hidden | 否 |
| 1024×768 | `/payment-batches` | 付款 | 654 / 760 | 106px | hidden | 否 |
| 1024×768 | `/reimbursements` | 我的报销 | 654 / 760 | 106px | hidden | 否 |
裁切后的操作在程序化设置 `scrollLeft` 后可以重新进入容器,但 `overflow-x: hidden` 不向用户提供横向滚动,因此不视为可达。标题动作方面:五页在两个 viewport 下均为 1 行,资产页的两个标题动作也未换行;标题动作完整位于 viewport 内,标题容器横溢均为 0。
## 当前主证据
证据目录:`research/evidence/local-browser-8f8606c/`。
- 结构化量测:`metrics.json`
- 移动空态:`mobile-375x812-wecom-archive-empty-employee.png`
- 移动空态底部:`mobile-375x812-wecom-archive-empty-employee-bottom.png`
- 移动列表:`mobile-375x812-wecom-archive-record-hr.png`
- 移动列表底部:`mobile-375x812-wecom-archive-record-hr-bottom.png`
- 移动详情:`mobile-375x812-wecom-archive-detail-hr.png`
- 移动详情底部:`mobile-375x812-wecom-archive-detail-hr-bottom.png`
- 768×1024:`desktop-768x1024-projects.png`、`desktop-768x1024-assets.png`、`desktop-768x1024-contracts-registry.png`、`desktop-768x1024-payment-batches.png`、`desktop-768x1024-reimbursements.png`
- 1024×768:`desktop-1024x768-projects.png`、`desktop-1024x768-assets.png`、`desktop-1024x768-contracts-registry.png`、`desktop-1024x768-payment-batches.png`、`desktop-1024x768-reimbursements.png`
`research/screenshots/mobile-wecom-approval-archive-375.png` 已人工复核,画面本身使用 synthetic 内容,但缺少本轮精确 SHA、角色和尺寸量测合同,因此只作为旧辅助截图,不作为当前 finding 的主证据。
## 证据完整性与脱敏复核
- `metrics.json` 只记录本地 URL、当前 SHA、viewport、最终路由、页面标题、角色代码、DOM 尺寸、截图文件名和诊断计数。
- 对 `metrics.json` 进行敏感关键词复核,未发现公网域名、邮箱、密码、token、Secret、账号、银行资料、企业微信完整标识或真实业务字段。
- 两个移动上下文和两个桌面上下文均为未知 API 0、console error 0、page error 0、外部来源请求 0。
- Service Worker 在浏览器上下文层阻断;本轮页面没有发起 Service Worker 请求。
## 建议验收合同
- 共享表格修复后,两组 viewport 的容器应满足:右侧操作完整可见,或容器提供用户可操作的横向滚动;页面级 `document.scrollWidth` 继续等于 viewport。
- 移动详情修复后,“历史详情”面板应满足 `scrollWidth <= clientWidth + 2px`,长字段名、长人员摘要、长单号和 finding 类型均可换行,不通过隐藏溢出来消除页面横滚。
- 修复回归继续使用本地合成数据;真机企业微信 WebView 仍保持 `EXTERNAL_GATED`,不能由本轮 Chromium 结果替代。
@@ -0,0 +1,56 @@
# 业务、权限、数据与安全防御性审查摘要
> 本文件是可共享的防御性摘要。唯一分级与当前生命周期状态以
> `research/findings.md` 为准;原始发现基线仍为 `bbb5c89`,当前增量复核
> 见 `research/current-head-delta.md`。
## 摘要边界
- 只保留 finding ID、风险类别、发现时严重度、置信度、代码位置、业务影响、控制目标和无害验证结论。
- 不保留攻击步骤、可执行载荷、绕过方式、概率推演、可组合接口细节、真实环境信息或原始业务资料。
- 本地测试与归档修复任务承载必要工程证明;本摘要不复制敏感实现细节。
- 未访问公网、生产数据库、真实员工资料、真实附件、企业微信或付款能力。
- “本地修复通过”不等于“已部署”或“外部行为通过”。
## 防御性 finding 摘要
<a id="bsr-sec-001"></a><a id="bsr-sec-002"></a>
<a id="bsr-authz-001"></a><a id="bsr-data-001"></a>
<a id="bsr-priv-001"></a><a id="bsr-sec-003"></a>
<a id="bsr-priv-002"></a><a id="bsr-sec-004"></a>
<a id="bsr-authz-002"></a><a id="bsr-data-002"></a>
<a id="bsr-sec-005"></a><a id="bsr-ux-001"></a>
| 原 ID | 风险类别 | 发现时严重度 | 置信度 | 发现状态 | 代码与证据位置 | 业务影响 | 控制目标 | 无害验证结论 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `BSR-SEC-001` | 认证 / 代理信任 | P0 | 高 | `CONFIRMED` | `server/src/app.js`、`server/src/config.js`、`server/src/routes/auth.js`、`server/tests/unit/routes/auth.login.test.js`、归档任务 `07-17-auth-challenge-trusted-proxy-hardening` | 认证挑战边界不足可能导致账号被未授权控制。 | 挑战记录单次有效、受限速与并发约束;代理来源必须显式可信。 | 当前 HEAD 的聚焦负路径、完整服务端回归和隔离并发验证均通过;仅证明本地修复,部署与公网代理行为待授权验证。 |
| `BSR-SEC-002` | 业务附件 / 浏览器隔离 | P0 | 高 | `CONFIRMED` | `server/src/routes/purchaseRequests.js`、`server/src/services/purchaseAttachmentPolicy.js`、`client/src/api/purchaseRequests.ts`、`server/tests/unit/routes/purchase.attachments.test.js`、归档任务 `07-17-purchase-attachment-xss-hardening` | 主动内容若在 OA 同源上下文呈现,可能破坏用户会话和业务数据边界。 | 上传按实际内容验证;下载重新授权、审计并强制非执行呈现。 | 当前 HEAD 的策略、路由、完整服务端和聚焦浏览器测试均通过;仅证明本地修复,部署、边缘响应和历史资料检查待授权。 |
| `BSR-AUTHZ-001` | 员工敏感数据授权 | P1 | 高 | `CONFIRMED` | `server/src/constants/permissions.js`、`server/src/routes/employees.js`、`server/prisma/schema.prisma` | 广泛报表能力可能越过岗位所需范围读取员工敏感字段。 | 字段级授权与人员数据范围同时生效,默认最小披露,拒绝路径可审计。 | 只读跨层检查确认当前权限粒度不足;未请求或展示任何真实员工资料。 |
| `BSR-DATA-001` | 付款状态一致性 | P1 | 高 | `LIKELY` | `server/src/routes/paymentBatches.js`、`server/prisma/schema.prisma` | 并发状态变化可能形成重复成功或证据覆盖,影响资金状态可信度。 | 使用数据库条件更新/锁、幂等键和不可覆盖的审计证据保证单一结果。 | 静态事务边界检查发现并发保护证据不足;本轮未连接数据库或执行付款。 |
| `BSR-PRIV-001` | 员工合同附件 | P1 | 高 | `CONFIRMED` | `server/src/routes/employees.js`、`server/prisma/schema.prisma`、`server/tests/unit/routes/employees.attachments.test.js` | 普通人员管理能力可能覆盖高敏合同资料,且用途与下载审计不足。 | 高敏附件使用独立最小权限、对象级授权、用途记录、下载审计和安全 DTO。 | 只读路由/测试合同检查确认专用控制缺口;未读取真实合同或附件。 |
| `BSR-SEC-003` | 统一导出 | P1 | 高 | `CONFIRMED` | `server/src/routes/exports.js` 及各业务导出调用方 | 导出内容在表格软件中打开时可能被解释为活动内容,影响使用者终端边界。 | 所有 CSV/表格导出统一经过安全编码器,并以合成样例覆盖危险前缀和正常中文内容。 | 静态检查确认通用编码层缺少该类中和合同;未生成或打开真实业务导出。 |
| `BSR-PRIV-002` | 客户端数据生命周期 | P1 | 高 | `CONFIRMED` | `client/src/auth/AuthContext.tsx`、`client/src/mobile/workflowActionQueue.ts`、`client/src/pwa.ts`、`client/src/api/workflow.ts` | 退出或切换账号后残留的身份、审批详情和草稿可能被后续设备使用者读取。 | 先决定离线业务必要性;按账号隔离、最小缓存,并在退出/切换时可验证清除。 | 静态生命周期检查确认持久化与清理合同不闭合;未检查真实设备存储。 |
| `BSR-SEC-004` | 下载凭证 / 日志面 | P2 | 中高 | `LIKELY` | `server/src/routes/invoicePool/read.js`、`client/src/api/invoicePool.ts`、相关边缘日志合同 | URL 中的短期访问凭据可能进入浏览器或代理的常规记录面。 | 优先使用当前认证上下文的 API 下载、最小化 URL 敏感信息并约束日志与缓存。 | 采购附件分支已由独立修复任务关闭;当前其他业务下载链与默认日志面仍存在风险可能性。未访问真实边缘日志,因此保持 `LIKELY`。 |
| `BSR-AUTHZ-002` | 受限资料规则 | P2 | 中高 | `LIKELY` | `server/src/routes/confidentialDocRefs.js`、`server/src/routes/projects.js`、`server/prisma/schema.prisma` | 已保存但未执行的访问规则可能让用户误以为资料位置受到限制。 | 产品先确认字段是正式 ACL 还是废弃元数据;若保留,必须由后端强制执行并有拒绝测试。 | 静态检查只确认规则与执行证据不一致;未访问任何真实资料位置。 |
| `BSR-DATA-002` | 付款取消审计 | P2 | 高 | `CONFIRMED` | `server/src/routes/paymentBatches.js`、`server/prisma/schema.prisma` | 取消缺少结构化原因、并发保护和独立审计时,难以还原资金流程责任。 | 取消必须记录操作者、原因、前后状态和并发条件,且不能覆盖既有证据。 | 静态路由与模型检查确认合同缺口;未执行真实状态变化。 |
| `BSR-SEC-005` | 浏览器防御纵深 | P2 | 高 | `CONFIRMED` | `server/src/app.js`、`server/src/config.js`、部署响应头合同 | 缺少内容执行限制会降低浏览器端对未知缺陷的纵深保护。 | 基于实际资源清单分阶段收紧 CSP,先报告后阻断,并保持附件下载独立沙箱。 | 本地配置检查确认当前未启用全站策略;不据此推断单独的可利用路径。 |
| `BSR-UX-001` | 页面能力显隐 | P2(收敛校准) | 高 | `DUPLICATE` | `research/page-static-review.md`、活动任务 `05-31-unified-page-header-actions` | 后端正确拒绝但页面仍展示写操作会误导用户并造成表单后失败。 | 前端能力显隐与后端拒绝测试采用同一权限合同;后端始终保持权威。 | 只读跨层复核确认重复 finding;统一降为 P2,不作为新的下游 task。 |
## 当前状态说明
- `BSR-SEC-001`、`BSR-SEC-002` 保留发现时 P0 和 `CONFIRMED`;当前修复状态统一为 `LOCAL_FIX_VERIFIED`,部署状态为 `DEPLOYMENT_PENDING`,外部验证为 `EXTERNAL_BEHAVIOR_PENDING`。
- 其他 finding 没有在本审查任务中实施业务修复;其当前状态和去重关系只在唯一台账维护。
- 付款、权限、附件和客户端生命周期涉及状态机、数据范围或隐私边界,后续实施完成后必须进行独立只读复核。
## 后续工作包映射
Gate 0:`BSR-SEC-001`、`BSR-SEC-002` 的部署和外部行为复核须另行授权;获准后优先于下面六个本地开放问题工作包,且不会因本任务归档而视为完成。
1. 员工敏感数据范围授权:`BSR-AUTHZ-001`。
2. 付款状态一致性与审计:`BSR-DATA-001`、`BSR-DATA-002`。
3. 员工合同附件权限与访问审计:`BSR-PRIV-001`。
4. 发布门禁可信化:由工程 finding 承接,不在本文件扩写。
5. 客户端数据生命周期与统一导出:`BSR-PRIV-002`、`BSR-SEC-003`,并评估 `BSR-SEC-004`。
6. 核心页面可靠性、权限显隐及旧标题任务收口:本文件映射 `BSR-UX-001`,页面 P1/P2 内部顺序见唯一台账。
部署、公网角色、真实边缘日志、企业微信真机、历史真实资料治理和新 Day 1 试运行均保持另行授权;本任务不创建对应下游 task。
@@ -0,0 +1,65 @@
# 当前 HEAD 增量复核
## 基线与范围
- 原发现基线:`bbb5c89825b0fbee60d5f093d7b30368cb7a5104`(简称 `bbb5c89`)。
- 本次收敛复核目标:`8f8606c328c4754901e045c34b071ecd36215bd3`(简称 `8f8606c`)。
- 原 `research/baseline.md` 保持历史事实,不改写为当前 HEAD 证据。
- 本次只检查本地代码、任务材料、合成数据和无凭据门禁;未访问公网、生产数据库、真实业务资料或外部写入能力。
## 提交分类
`bbb5c89..8f8606c` 共 4 个提交:
| 提交 | 分类 | 结论 |
| --- | --- | --- |
| `6ddedf1` | 认证安全修复 | 收敛认证挑战、并发和可信代理边界。 |
| `4c5eb7e` | 认证任务归档 | 归档本地修复与验证证据。 |
| `f424492` | 采购附件安全修复 | 收敛上传内容验证、下载授权/审计和浏览器隔离边界。 |
| `8f8606c` | 采购附件任务归档 | 归档本地修复与验证证据。 |
累计影响 47 个文件,按职责分为 Trellis spec、归档任务材料、客户端适配、部署合同、QA runner、服务端/迁移/测试。该范围没有其他 finding 的完整修复证据,因此其余开放项不能因 HEAD 前移而自动标记为已解决。采购附件任务同时关闭了该模块原有的 URL 下载凭证分支;`DATA-DOWNLOAD-001` 已收窄为当前仍存在的其他业务下载链,而不是继续引用已修分支。
## 两个原 P0 的当前状态
| 原 finding | 本地修复证据 | 修复状态 | 部署状态 | 外部验证 |
| --- | --- | --- | --- | --- |
| `BSR-SEC-001` | `6ddedf1`、归档任务 `07-17-auth-challenge-trusted-proxy-hardening`;聚焦负路径、完整服务端和隔离并发验证通过。 | `LOCAL_FIX_VERIFIED` | `DEPLOYMENT_PENDING` | `EXTERNAL_BEHAVIOR_PENDING` |
| `BSR-SEC-002` | `f424492`、归档任务 `07-17-purchase-attachment-xss-hardening`;策略/路由、完整服务端和聚焦浏览器验证通过。 | `LOCAL_FIX_VERIFIED` | `DEPLOYMENT_PENDING` | `EXTERNAL_BEHAVIOR_PENDING` |
以上状态不表示当前公网来源已变化。最后有证据的公网来源仍是 `df26922`,且本轮没有获得部署、边缘响应、真实历史资料或公网角色复核授权。
## 收敛前只读门禁快照
| 检查 | 结果 | 解释 |
| --- | --- | --- |
| 当前 task validate | PASS | 命令可执行;占位上下文随后已替换为真实 spec/research 引用。 |
| Trellis governance | PASS | 当前两个活动 task 合同可识别。 |
| 敏感内容扫描 | PASS | 当前事实源扫描无 finding。 |
| 文档 registry 审计 | PASS with known warning | 0 error;保留既有未登记文档 warning。 |
| Playwright `--list` | PASS | 144 tests / 56 files;只证明资产可解析,不代表行为通过。 |
| 移动路由静态合同 | FAIL with finding | 共享归档页组件不符合脚本命名/挂载规则;该结果本身不直接认定产品页面缺陷。后续浏览器量测独立确认长详情裁切为 P2。 |
| 流程覆盖扫描 | COMMAND PASS / EVIDENCE UNTRUSTED | 68 条路由中 39 条未映射仍未形成 gap,且覆盖来源混入默认不执行的 manual 资产。 |
## 最终本地收口门禁
| 检查 | 结果 | 当前 HEAD 结论 |
| --- | --- | --- |
| `bash scripts/check-minimal.sh` | PASS | Prisma、服务端静态检查、157/157 测试文件与 896/896 用例、前端 typecheck/build、功能矩阵均通过。首次受限执行中两个需要子进程/本地 stub 的测试因 `EPERM` 失败;在允许本地子进程但仍无公网/生产访问的同一命令下全量通过,归类为环境限制。 |
| 当前 task validate | PASS | `implement.jsonl`、`check.jsonl` 各 4 条真实上下文。 |
| Trellis governance | PASS | 2 个活动 task 合同有效,无 warning/error。 |
| 敏感内容扫描 | PASS | 30 个当前事实源文件,0 finding。 |
| 文档 registry 审计 | PASS with known warning | 0 error;22 个既有未登记 docs 文件形成 1 条 warning。 |
| Playwright `--list` | PASS | 144 tests / 56 files;只证明可解析。 |
| 移动路由静态合同 | EXPECTED FAIL | 1 条共享组件挂载 finding;浏览器行为另确认 `MOB-ARCHIVE-001` 为 P2,不把命名规则直接当产品结论。 |
| 流程覆盖扫描 | COMMAND PASS / EVIDENCE UNTRUSTED | 68 条路由中 39 条未映射仍未形成 gap,52 条路由的证据引用 manual 资产;保留 `REL-GATE-001`。 |
| `git diff --check` | PASS | 无空白/补丁格式错误。 |
## 浏览器增量结论
- 375×812:归档页 URL、H1、空态、移动卡片、角色范围和底栏通过;长详情内部 351/457px,约 105px 内容被隐藏,定为 P2 `MOB-ARCHIVE-001`。
- 768×1024:共享表格容器 702/760px、隐藏 58px;五页中四页右侧操作不可达。
- 1024×768:共享表格容器 654/760px、隐藏 106px;五页右侧操作均不可达,定为 P1 `RESP-TABLE-001`。
- 所有浏览器数据为本地合成 fixture,外部请求、未知 API、console/page error 均为 0;真机企业微信仍为 `EXTERNAL_GATED`。
收口目标 HEAD 在所有证据生成期间保持 `8f8606c`。若工作提交前业务 HEAD 发生变化,必须扩大 delta 并重跑,不复用本页 PASS;收口文档自身的后续提交不改变被审查的业务代码基线。
@@ -0,0 +1,80 @@
# 工程、测试、性能与运维审查
> 本文件保留原发现基线的分域证据。当前 HEAD 增量、唯一分级和生命周期状态
> 分别以 `research/current-head-delta.md` 与 `research/findings.md` 为准。
## 测试资产与实测覆盖率
- 后端:153 个 Vitest 文件、786 个用例;普通回归与 V8 coverage 运行均为 153/153、786/786 PASS。
- coverage 实跑耗时 196.80 秒;统计范围为 198 个源码文件,lines/statements `57.34%`、functions `71.03%`、branches `57.93%`。
- 前端:Playwright 默认清单为 144 tests / 56 files;`client/src` 没有独立 unit/component spec,纯函数和组件合同主要依赖 TypeScript、静态门禁与 E2E 间接覆盖。
- `server/vitest.config.cjs` 配置 coverage 报告,但没有最低阈值;因此 `npm run test:coverage` 即使整体或关键目录覆盖下降也不会失败。
- 低覆盖集中在作业编排、部分管理/财务 route 和运行脚本。例如 delegation revoke、invoice archive、integration outbox、ERP master-data sync 等 job 的 lines 覆盖约 `6%–13%`;`src/index.js`、`src/worker.js` 及多项生产运维脚本为 0%。这不说明实现错误,但说明“全部用例通过”不能覆盖启动、调度与恢复行为。
### 候选 finding:测试通过不等于核心运行面已覆盖
- 建议级别:P2 / `CONFIRMED`。
- 影响:回归能够发现大量 route/service 逻辑问题,但无法通过阈值防止覆盖率缓慢退化;作业入口和运维脚本的回归更多依赖 mock/手册,生产调度错误可能较晚暴露。
- 建议:按风险设分层阈值而非一次追求全仓高数字;优先给认证恢复、资金状态、worker/job 注册、迁移/备份脚本设置关键文件阈值和负路径测试。前端先为权限显隐、路由映射和共享页面模板补小型 unit/component 合同,不必把所有页面复制成 unit spec。
## 流程覆盖扫描可信度
执行 `node scripts/qa/check-flow-coverage.mjs --json`:
- 报告摘要为 68 条前端路由、5 个业务域、68 个后端 route 文件、`highRiskGapCount=0`。
- 实际 68 条前端路由中只有 29 条映射到业务域,39 条标为 `unmapped`;未映射包括合同、项目、资产、票据、付款、差旅、人员及绝大多数 `/system/*` 页面。
- 矩阵使用 `/system/*`,而 `routeTokenMatches` 只处理完全相等、参数路由或 path prefix,不实现 `*` 通配,因此整组系统页面无法映射。
- `specMatchesRoute` 在精确路径之外使用整个业务域的宽泛关键词;认证域含 `login` / `mobile` 等高频词,导致每条认证/移动路由都被匹配到全部 63 个 spec,并全部显示 WeCom、visual、flow 已覆盖。
- 扫描还把默认 Playwright 配置会排除的 `manual/` live spec 纳入覆盖证据。
- `scripts/qa/run-release-gate.sh` 生成该报告时没有传 `--fail-on-gaps`;即使扫描识别出缺口也只写报告,不阻断门禁。
### 候选 finding:覆盖扫描产生系统性假阳性
- 建议级别:P1 / `CONFIRMED`。
- 影响:项目级发布证据可以在 39 条路由未映射、单路由无直接断言时仍显示“P0 缺口 0”;这会掩盖权限、视觉和业务流的真实测试缺口。
- 建议:要求所有受支持路由显式映射;实现 `*` 语义;只认精确 route/test manifest 或测试内结构化标签,不以业务域关键词推断;默认排除 manual/live;发布门禁在 unmapped 或 P0 缺口时失败。
## 项目级发布门禁可执行性
`scripts/qa/run-release-gate.sh project-wide` 的静态阶段存在当前环境可确认的阻断:
- 第 88–89 行固定要求历史 `issues/2026-05-08_23-38-37-project-wide-qa-second-pass.csv`。
- 校验器硬编码为用户目录下旧 `plan` skill 的绝对路径;当前机器该文件不存在。
- 因为脚本启用 `set -euo pipefail`,执行到该行会直接退出,后续 UI、移动路由、流程覆盖、迁移诊断和浏览器门禁均不会运行。
- 这也与当前 Trellis-first / Trellis-only 入口冲突:历史 Issue CSV 可以保留审计,但不应重新成为新发布门禁的隐含执行合同。
### 候选 finding:项目级发布门禁依赖已不存在的个人工具
- 建议级别:P1 / `CONFIRMED`。
- 影响:当前 checkout 无法从仓库自身执行标准 `project-wide` 门禁,在新机器/CI 上同样不可移植,容易迫使发布者绕过标准路径。
- 建议:删除历史 Issue/外部 skill 前置,改为仓库内 Trellis governance + 当前 task validate;需要 CSV 导出时使用仓库内受控脚本并作为显式可选步骤。
## 依赖与供应链边界
- `npm ls --depth=0 --json`:server 35 个直接依赖、client 26 个直接依赖,均 exit 0 且无 missing/invalid/extraneous problem。
- 按当前无公网边界未执行 `npm audit`,因此不能声称不存在已知 CVE。上线 checklist 要求 `npm audit --omit=dev --audit-level=high`;当前结论为 `EXTERNAL_GATED`,准入条件是允许访问 npm advisory registry 并记录锁文件对应结果。
- 运行中仍出现 Node `punycode` 弃用提示;属于依赖维护观察项,尚无证据证明影响业务。
## 前端构建与性能观察
- production build 总体约 2.8 MiB、94 个 asset 文件,页面主体采用 `React.lazy` 拆分。
- `index.html` 初始 preload 包含 React、Query、UI、app-core、app-domain-shared;最大扫码依赖 chunk `vendor-scan` 约 411 KiB,但未被首页 HTML preload,按路由延迟加载。
- 初始入口相关的未压缩 JS/CSS 约为:entry 127 KiB、React 166 KiB、UI 145 KiB、Query 41 KiB、app-core 72 KiB、app-domain-shared(较小)以及 CSS 89 KiB;另有 Material Symbols 字体约 479 KiB。当前没有基于网络/设备的 LCP、INP 或内存实测,不能据体积直接判定性能故障。
- Topbar 和移动底栏各 30 秒轮询通知;OCR pending 时 3 秒轮询。代码证据表明 OCR 轮询按 pending 条件关闭,暂无“永久 3 秒轮询”证据。
### 性能结论
- 当前无 P0/P1 性能 finding。
- P2 观察:建立 build budget 和低端移动网络测量,重点关注图标字体、初始 UI chunk、通知双入口轮询以及 OCR 多页面重复查询;先测量再优化。
## 运维、迁移与恢复
- 部署合同具备 Docker healthcheck、`/ready`、完整后端 SHA / 12 位前端 SHA、init migration、runtime smoke、external-deps smoke、live readiness、DB + 文件存储备份/恢复脚本和回滚手册。
- 仓库内确有 `scripts/backup-db.sh`、`backup-storage.sh`、`restore-db.sh`、`restore-storage.sh`;运行手册引用不是悬空命令。
- Prisma schema validate 通过;共有 58 个迁移目录。本任务未连接数据库,`migrate status` / drift、恢复演练和公网 source provenance 均为 `EXTERNAL_GATED`。
- 旧 7 日试运行只完成 Day 1–Day 4;当前本地 commit 未部署。因此恢复、真实调度、告警和公网 readiness 只能报告“合同存在、行为待外部补证”,不能记为 PASS。
## 本轮生成物
- `server/coverage/` 由本轮 coverage 运行生成且被忽略,最终汇总取证后删除。
- 不删除历史 `artifacts/` 或其他任务的证据。
二进制文件未显示。

之后

宽度:  |  高度:  |  大小: 91 KiB

二进制文件未显示。

之后

宽度:  |  高度:  |  大小: 92 KiB

二进制文件未显示。

之后

宽度:  |  高度:  |  大小: 70 KiB

二进制文件未显示。

之后

宽度:  |  高度:  |  大小: 85 KiB

二进制文件未显示。

之后

宽度:  |  高度:  |  大小: 88 KiB

二进制文件未显示。

之后

宽度:  |  高度:  |  大小: 71 KiB

二进制文件未显示。

之后

宽度:  |  高度:  |  大小: 74 KiB

二进制文件未显示。

之后

宽度:  |  高度:  |  大小: 51 KiB

二进制文件未显示。

之后

宽度:  |  高度:  |  大小: 65 KiB

二进制文件未显示。

之后

宽度:  |  高度:  |  大小: 69 KiB

二进制文件未显示。

之后

宽度:  |  高度:  |  大小: 64 KiB

二进制文件未显示。

之后

宽度:  |  高度:  |  大小: 70 KiB

二进制文件未显示。

之后

宽度:  |  高度:  |  大小: 70 KiB

@@ -0,0 +1,68 @@
# 多维系统审查唯一 Finding 台账
## 台账口径
- 本文件是当前任务唯一的分级与生命周期事实源。分域报告保留发现证据,但不再各自决定当前严重度或状态。
- 发现基线为 `bbb5c89`;当前本地增量目标为 `8f8606c`。发现时严重度不因后续修复而改写,收敛校准会在单元格中显式注明。
- 发现状态、当前修复状态、部署状态、外部验证状态彼此独立。
- `NO_FIX_TO_DEPLOY` 表示尚无修复产物,不等于待部署;`NOT_APPLICABLE` 仅用于非运行时代码/纯测试合同。
- `ACTIVE_TASK_OPEN` 表示该项仍开放但已由现有活动 task 承载;本轮只做去重,不另建 task。`FOLLOW_UP_RECOMMENDED` 表示持续治理建议,不是已修复。
- 证据和验证只保留防御性结论。安全工程细节由聚焦测试和已归档修复任务承载。
## 唯一台账
| ID | 维度 | 发现时严重度 | 置信度 | 发现状态 | 当前修复状态 | 部署状态 | 外部验证状态 | 证据引用 | 业务影响 | 控制目标 | 无害验证 | 后续工作包 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `SEC-AUTH-001` | 认证 / 代理信任 | P0 | 高 | `CONFIRMED` | `LOCAL_FIX_VERIFIED` | `DEPLOYMENT_PENDING` | `EXTERNAL_BEHAVIOR_PENDING` | `business-security-review.md#bsr-sec-001`;归档任务 `07-17-auth-challenge-trusted-proxy-hardening` | 认证边界不足可能导致账号被未授权控制。 | 挑战单次有效、并发一致、来源可信且可审计。 | 当前 HEAD 聚焦负路径、完整服务端与隔离并发验证通过。 | 另行授权:部署与公网复核 |
| `SEC-ATT-001` | 业务附件 / 浏览器隔离 | P0 | 高 | `CONFIRMED` | `LOCAL_FIX_VERIFIED` | `DEPLOYMENT_PENDING` | `EXTERNAL_BEHAVIOR_PENDING` | `business-security-review.md#bsr-sec-002`;归档任务 `07-17-purchase-attachment-xss-hardening` | 主动内容可能破坏 OA 同源会话与业务数据边界。 | 实际内容验证、重新授权/审计、非执行下载。 | 当前 HEAD 策略/路由、完整服务端与聚焦浏览器验证通过。 | 另行授权:部署与公网复核 |
| `DATA-EMP-001` | 员工敏感数据授权 | P1 | 高 | `CONFIRMED` | `OPEN` | `NO_FIX_TO_DEPLOY` | `EXTERNAL_BEHAVIOR_PENDING` | `business-security-review.md#bsr-authz-001` | 广泛能力可能读取超出岗位需要的员工敏感字段。 | 字段级授权与人员范围同时最小化。 | 只读跨层检查确认权限粒度不足,未读取真实资料。 | WP-1 员工敏感数据范围授权 |
| `PAY-STATE-001` | 付款状态机 / 并发 | P1 | 高 | `LIKELY` | `OPEN` | `NO_FIX_TO_DEPLOY` | `EXTERNAL_BEHAVIOR_PENDING` | `business-security-review.md#bsr-data-001` | 并发状态变化可能造成重复结果或证据覆盖。 | 条件更新/锁、幂等和不可覆盖审计保证单一结果。 | 静态事务检查发现保护证据不足,未连接数据库或执行付款。 | WP-2 付款状态一致性与审计 |
| `DATA-HR-ATT-001` | 员工合同附件 | P1 | 高 | `CONFIRMED` | `OPEN` | `NO_FIX_TO_DEPLOY` | `EXTERNAL_BEHAVIOR_PENDING` | `business-security-review.md#bsr-priv-001` | 高敏合同资料可能受过宽权限控制且缺访问用途审计。 | 独立最小权限、对象级授权、安全 DTO 和下载审计。 | 只读路由/测试合同确认缺口,未读取真实合同。 | WP-3 员工合同附件权限与访问审计 |
| `DATA-EXPORT-001` | 统一导出 | P1 | 高 | `CONFIRMED` | `OPEN` | `NO_FIX_TO_DEPLOY` | `NOT_REQUIRED` | `business-security-review.md#bsr-sec-003` | 导出内容可能在表格软件中形成非预期活动行为。 | 全部 CSV/表格出口统一安全编码并有合成负例。 | 静态检查确认缺少统一中和合同,未使用真实导出。 | WP-5 客户端数据生命周期与统一导出 |
| `PRIV-OFFLINE-001` | 客户端数据生命周期 | P1 | 高 | `CONFIRMED` | `OPEN` | `NO_FIX_TO_DEPLOY` | `EXTERNAL_BEHAVIOR_PENDING` | `business-security-review.md#bsr-priv-002` | 退出或换账号后残留身份、审批或草稿可能被后续使用者读取。 | 先确认离线必要性,再按账号隔离、最小缓存并可靠清除。 | 静态检查确认持久化和清理未闭合,未检查真实设备。 | WP-5 客户端数据生命周期与统一导出 |
| `DATA-DOWNLOAD-001` | 下载凭证 / 日志面 | P2 | 中高 | `LIKELY` | `OPEN` | `NO_FIX_TO_DEPLOY` | `EXTERNAL_GATED` | `business-security-review.md#bsr-sec-004` | 短期访问凭据可能进入常规浏览器或边缘记录面。 | 使用当前认证上下文下载并最小化 URL、日志和缓存暴露。 | 代码存在风险可能性;真实边缘日志未获授权检查。 | WP-5 客户端数据生命周期与统一导出 |
| `DATA-ACL-001` | 受限资料规则 | P2 | 中高 | `LIKELY` | `OPEN` | `NO_FIX_TO_DEPLOY` | `NOT_REQUIRED` | `business-security-review.md#bsr-authz-002` | 已保存但未执行的规则会造成受限资料边界误判。 | 产品明确其为 ACL 或废弃元数据;保留则由后端强制执行。 | 静态检查确认规则与执行证据不一致。 | WP-5 客户端数据生命周期与统一导出 |
| `PAY-AUDIT-001` | 付款取消审计 | P2 | 高 | `CONFIRMED` | `OPEN` | `NO_FIX_TO_DEPLOY` | `NOT_REQUIRED` | `business-security-review.md#bsr-data-002` | 取消责任与前后状态难以完整还原。 | 记录原因、操作者、前后状态与并发条件。 | 静态路由/模型检查确认缺口,未执行真实状态变化。 | WP-2 付款状态一致性与审计 |
| `WEB-DEFENSE-001` | 浏览器防御纵深 | P2 | 高 | `CONFIRMED` | `OPEN` | `NO_FIX_TO_DEPLOY` | `EXTERNAL_BEHAVIOR_PENDING` | `business-security-review.md#bsr-sec-005` | 缺少内容执行限制会降低未知缺陷的纵深保护。 | 基于资源清单分阶段收紧 CSP 并保持附件独立沙箱。 | 本地配置确认当前未启用全站策略;未推断具体利用路径。 | WP-5 客户端数据生命周期与统一导出 |
| `UI-PERM-001` | PC 写动作显隐 | P2(收敛校准;原分域报告 P1/P2 不一) | 高 | `CONFIRMED` | `ACTIVE_TASK_OPEN` | `NO_FIX_TO_DEPLOY` | `NOT_REQUIRED` | `page-static-review.md#既有-finding-复核与去重`;`BSR-UX-001`;活动任务 `05-31-unified-page-header-actions` | 后端虽拒绝写入,PC 用户仍可能进入最终失败的流程。 | PC 显隐与后端拒绝共享同一能力合同,后端保持权威。 | 付款、合同、票据三处既有 PC finding 完成只读去重;移动导航由 `MOB-PERM-001` 单列。 | WP-6 页面权限显隐及旧标题任务收口 |
| `PAY-UX-001` | 付款业务可用性 | P1 | 高 | `CONFIRMED` | `OPEN` | `NO_FIX_TO_DEPLOY` | `NOT_REQUIRED` | `page-static-review.md#page-p1-001` | 财务人员难以凭业务信息选择待付对象,可能错选或无法创建。 | 以待付池和业务编号/人员/金额选择对象,内部标识仅由系统传递。 | 静态页面与 E2E 合同确认普通流程依赖内部标识。 | WP-2 付款状态一致性与审计 |
| `UX-SAFE-001` | 不可逆证据 / 资金动作 | P1(收敛后边界) | 高 | `CONFIRMED` | `OPEN` | `NO_FIX_TO_DEPLOY` | `NOT_REQUIRED` | `page-static-review.md#page-p1-002` | 误触可能删除不可逆证据或改变资金结果。 | 强确认展示对象、后果、可逆性与必要理由。 | 静态检查确认代表性高影响动作在 mutation 前缺少强确认。 | WP-6 页面权限显隐及旧标题任务收口 |
| `UX-SAFE-002` | 可恢复管理动作 | P2(由原 PAGE-P1-002 拆分校准) | 高 | `CONFIRMED` | `OPEN` | `NO_FIX_TO_DEPLOY` | `NOT_REQUIRED` | `page-static-review.md#page-p1-002` | 普通启停等可恢复动作存在误触和反馈不清。 | 使用轻确认或可撤销反馈,按可逆性分级。 | 只读检查确认部分可恢复动作仍直接执行。 | WP-6 页面权限显隐及旧标题任务收口 |
| `UX-FORM-001` | 核心长表单可靠性 | P1 | 高 | `CONFIRMED` | `OPEN` | `NO_FIX_TO_DEPLOY` | `NOT_REQUIRED` | `page-static-review.md#page-p1-003` | 采购、报销、合同、资产等长输入可能因离开页面丢失。 | 共享 dirty-state 合同覆盖路由离开、刷新和记录切换。 | 静态检查未找到未保存保护实现。 | WP-6 页面权限显隐及旧标题任务收口 |
| `MOB-ATTEND-001` | 移动考勤入口 | P1(由原 PAGE-P1-004 拆分) | 高 | `CONFIRMED` | `OPEN` | `NO_FIX_TO_DEPLOY` | `EXTERNAL_BEHAVIOR_PENDING` | `page-static-review.md#page-p1-004` | 企业微信/触控小屏考勤入口会回到原移动首页。 | 提供明确移动镜像或允许只读响应式页,不形成自循环。 | 静态路由解析确认自循环;真机点击待另行授权。 | WP-6 页面权限显隐及旧标题任务收口 |
| `MOB-TARGET-001` | 移动通知定位 | P2(由原 PAGE-P1-004 拆分校准) | 高 | `CONFIRMED` | `OPEN` | `NO_FIX_TO_DEPLOY` | `EXTERNAL_BEHAVIOR_PENDING` | `page-static-review.md#page-p1-004` | 部分通知不能定位原业务对象,用户需重新查找。 | 每种通知保留稳定对象标识并验证最终 URL/内容。 | 静态链接映射确认目标参数不完整;真实通知待授权。 | WP-6 页面权限显隐及旧标题任务收口 |
| `SEARCH-NAV-001` | 全局搜索导航 | P2(收敛校准;原报告 P1) | 高 | `CONFIRMED` | `OPEN` | `NO_FIX_TO_DEPLOY` | `NOT_REQUIRED` | `page-static-review.md#page-p1-005` | 点击部分搜索结果只到列表,不能确认命中对象。 | 对象型结果提供稳定详情或定位/高亮合同。 | 静态 href 与现有单实体 E2E 确认覆盖不完整。 | WP-6 页面权限显隐及旧标题任务收口 |
| `RESP-TABLE-001` | 中间断点表格 | P1 | 高 | `CONFIRMED` | `OPEN` | `NO_FIX_TO_DEPLOY` | `NOT_REQUIRED` | `page-static-review.md#page-p1-006`;`browser-review.md` | 共享表格隐藏超出宽度,多个核心列表的右侧操作不可达。 | 操作列可见或容器可控横滚,页面本身不溢出。 | 768 下容器缺 58px、1024 下缺 106px 且 `overflow-x:hidden`;代表页面操作命中失败。 | WP-6 页面权限显隐及旧标题任务收口 |
| `A11Y-FORM-001` | 表单可访问性 | P2(收敛校准;原报告 P1) | 高 | `CONFIRMED` | `FOLLOW_UP_RECOMMENDED` | `NO_FIX_TO_DEPLOY` | `NOT_REQUIRED` | `page-static-review.md#page-p1-007` | 辅助技术用户较难识别字段和动态错误。 | 共享字段/错误组件提供名称、关联、错误状态和 live feedback。 | 静态样例确认语义缺口;无行为证据证明核心流程完全不可完成。 | WP-6 页面权限显隐及旧标题任务收口 |
| `NAV-META-001` | 路由 / 标题元数据 | P2 | 高 | `CONFIRMED` | `OPEN` | `NO_FIX_TO_DEPLOY` | `NOT_REQUIRED` | `page-static-review.md#page-p2-001` | 标题、最近访问和浏览器 tab 语义漂移。 | 统一 typed route manifest 与页面可控覆写。 | 静态映射确认缺失、误匹配与固定浏览器标题。 | WP-6 页面权限显隐及旧标题任务收口 |
| `ERR-RECOVERY-001` | 错误恢复 | P2 | 高 | `CONFIRMED` | `OPEN` | `NO_FIX_TO_DEPLOY` | `NOT_REQUIRED` | `page-static-review.md#page-p2-002` | 403/404/渲染错误可能把用户带向隐藏旧入口。 | 所有恢复动作回到稳定工作台或安全上一页。 | 静态链接确认仍指向旧路径。 | WP-6 页面权限显隐及旧标题任务收口 |
| `SCOPE-TRAVEL-001` | 冻结能力范围 | P2 | 高 | `CONFIRMED` | `OPEN` | `NO_FIX_TO_DEPLOY` | `NOT_REQUIRED` | `page-static-review.md#page-p2-003` | 冻结差旅仍占用付款、通知与审批认知空间。 | 用户可见入口按启用状态显式隐藏或标为只读兼容。 | 静态入口清单确认非差旅页面仍暴露选项。 | WP-6 页面权限显隐及旧标题任务收口 |
| `UX-ERROR-001` | 普通页面错误文案 | P2 | 高 | `CONFIRMED` | `OPEN` | `NO_FIX_TO_DEPLOY` | `NOT_REQUIRED` | `page-static-review.md#page-p2-004` | 用户先看到内部码/枚举,难以理解原因与下一步。 | 普通界面展示自然语言和恢复动作,诊断码仅进受控区域。 | 静态样例确认普通页面直接组合技术信息。 | WP-6 页面权限显隐及旧标题任务收口 |
| `A11Y-TABLE-001` | 表格键盘可达 | P2 | 高 | `CONFIRMED` | `FOLLOW_UP_RECOMMENDED` | `NO_FIX_TO_DEPLOY` | `NOT_REQUIRED` | `page-static-review.md#page-p2-005` | 键盘用户无法等价选择部分表格记录。 | 使用真实按钮/链接和明确选中状态。 | 静态样例确认行点击缺少键盘等价入口。 | WP-6 页面权限显隐及旧标题任务收口 |
| `A11Y-VISUAL-001` | 对比度 / 减弱动效 | P2 | 高 | `CONFIRMED` | `FOLLOW_UP_RECOMMENDED` | `NO_FIX_TO_DEPLOY` | `NOT_REQUIRED` | `page-static-review.md#page-p2-006` | 低视力与减弱动效用户体验下降。 | 共享 token 达到既定对比度并尊重 reduced motion。 | 静态 token/样式检查确认当前合同不足。 | WP-6 页面权限显隐及旧标题任务收口 |
| `QA-VISUAL-001` | 视觉证据可信度 | P2 | 高 | `CONFIRMED` | `OPEN` | `NOT_APPLICABLE` | `NOT_REQUIRED` | `page-static-review.md#page-p2-007` | 报告可能遗漏真实路由/中间断点却给出过强信心。 | 显式路由计划、最终 URL/标题/错误页断言和代表断点。 | 静态矩阵确认通配、旧路由和断点缺口。 | WP-4 发布门禁可信化 |
| `MOB-PERM-001` | 移动导航权限 | P2 | 中高 | `CONFIRMED` | `OPEN` | `NO_FIX_TO_DEPLOY` | `NOT_REQUIRED` | `page-static-review.md#page-p2-008` | 最小权限角色看到无效入口或进入 403。 | 移动入口统一复用能力谓词,未授权默认隐藏。 | 静态导航与守卫对照确认不一致。 | WP-6 页面权限显隐及旧标题任务收口 |
| `QA-ASSET-001` | 资产 E2E 漂移 | P2 | 高 | `DUPLICATE` | `ACTIVE_TASK_OPEN` | `NOT_APPLICABLE` | `NOT_REQUIRED` | `page-static-review.md#page-dup-p2-001`;活动任务 `05-31-unified-page-header-actions` | 自动化仍按旧交互执行,不能证明当前页面。 | 测试跟随当前“更多”菜单交互,不为旧测试恢复旧按钮。 | 只读测试/页面对照确认重复项。 | WP-6 页面权限显隐及旧标题任务收口 |
| `QA-HEADER-001` | 标题动作合同 / 视觉覆盖 | P2 | 高 | `DUPLICATE` | `ACTIVE_TASK_OPEN` | `NOT_APPLICABLE` | `NOT_REQUIRED` | `page-static-review.md#page-dup-p2-002`;活动任务 `05-31-unified-page-header-actions` | 标题区可能继续堆叠动作且现有回归不报警。 | 可测试的主操作/更多槽位及项目、资产代表性视觉断言。 | 静态组件与测试矩阵确认重复项。 | WP-6 页面权限显隐及旧标题任务收口 |
| `QA-COVERAGE-001` | 风险型测试覆盖 | P2 | 高 | `CONFIRMED` | `OPEN` | `NOT_APPLICABLE` | `NOT_REQUIRED` | `engineering-review.md#测试资产与实测覆盖率` | 全量用例通过仍不能防止关键 worker/脚本覆盖退化。 | 对认证、资金、worker、迁移/恢复入口设置分层防回退门禁。 | 旧基线 coverage 与入口清单确认关键运行面无阈值。 | WP-4 发布门禁可信化 |
| `REL-GATE-001` | 发布门禁可信度 | P1 | 高 | `CONFIRMED` | `OPEN` | `NOT_APPLICABLE` | `NOT_REQUIRED` | `engineering-review.md#流程覆盖扫描可信度`、`#项目级发布门禁可执行性`;`current-head-delta.md` | 标准门禁不可移植且会把大量未映射路由报告成无高风险缺口。 | 仓库内可执行、显式映射/豁免、排除 manual/live、缺口非零退出并自测反例。 | 当前 HEAD 扫描 68 路由仍有 39 未映射却无 gap;标准 runner 依赖失效外部前置。 | WP-4 发布门禁可信化 |
| `MOB-ARCHIVE-001` | 移动归档详情响应式 | P2 | 高 | `CONFIRMED` | `OPEN` | `NO_FIX_TO_DEPLOY` | `EXTERNAL_BEHAVIOR_PENDING` | `baseline.md#移动路由静态合同`;`browser-review.md` | 长标识使详情内部内容被隐藏,用户无法横向找回。 | 保持路由/卡片/底栏基线并让详情长字段安全换行或可控滚动。 | 375×812 的 URL、H1、空态、列表和底栏通过;长详情 panel 351/457,约 105px 被 `overflow-x:hidden` 裁切。 | WP-6 页面权限显隐及旧标题任务收口 |
## 不进入 Finding 的观察与外部补证
- 构建体积、通知轮询、工具链弃用提示目前没有行为故障证据,保留为观察项,不进入台账。
- 依赖在线审计、数据库迁移状态、备份恢复演练、公网来源、真实角色矩阵、企业微信真机、真实 OCR、真实付款和连续试运行属于外部补证边界,不伪装成产品 finding。
- 外部补证不阻塞本只读审查任务归档,但会阻塞“已部署”“可公网测试”或“真实业务通过”的对外结论。
## 推荐工作包顺序
Gate 0:两个已本地修复 P0 的部署与外部行为复核必须另行授权,并在获准后优先完成;它不被下面六个“本地开放问题”工作包的顺序替代。
1. WP-1 员工敏感数据范围授权。
2. WP-2 付款状态一致性与审计。
3. WP-3 员工合同附件权限与访问审计。
4. WP-4 发布门禁可信化。
5. WP-5 客户端数据生命周期与统一导出。
6. WP-6 核心页面可靠性、权限显隐及旧标题任务收口。它只是管理伞,不应一次形成 19 项巨型实施任务:先处理 `UX-SAFE-001`、`UX-FORM-001`、`MOB-ATTEND-001`、`RESP-TABLE-001` 四条 P1,再收口页面权限显隐和现有 `05-31` 任务;其余 P2 按触达持续治理。
本任务只登记顺序,不创建下游 Trellis task。部署、公网角色、企微真机和新 Day 1 试运行必须另行授权。
@@ -0,0 +1,375 @@
# 页面、信息架构与跨端静态审查
> 本文件保留 `bbb5c89` 分域静态审查的原始分级和证据,不再作为当前唯一台账。
> 2026-07-17 的严重度校准、浏览器行为结论和四层状态以 `research/findings.md`
> 与 `research/browser-review.md` 为准;不要直接汇总本文件中的旧 P1/P2 数量。
## 结论摘要
- 静态覆盖了 `client/src/pages/` 下 75 个 TSX 页面/页面子模块(约 4.15 万行)、`client/src/router/index.tsx` 的 76 个 `<Route>` 声明、PC/移动导航、全局壳、共享列表/详情/编辑模板,以及 63 个 Playwright spec 的路由与页面相关资产;对标题动作、移动路由、全局视觉、权限负例和既有 finding 对应 spec 做了重点抽读。
- 本轮只读静态审查没有发现 P0。
- 新增唯一 finding 为 **P1 7 条、P2 8 条**;其中 1 条 P1 因必须在 768/1024 浏览器中观察实际裁切而标为 `LIKELY`,其余为 `CONFIRMED`。
- 从 `05-31-unified-page-header-actions` 复核出 **P1 3 条、P2 2 条**,全部保持 `DUPLICATE`,没有重复创建新问题。
- 若把重复项也计入风险地图,当前页面维度合计为 **P0 0 / P1 10 / P2 10**;用于后续建修复 task 时,应以唯一新增的 **P0 0 / P1 7 / P2 8** 为准。
- 本文件只代表当前本地代码静态结论;没有访问公网、真实数据或企业微信真机,也没有运行浏览器。真机 WebView 结论仍属外部准入。
| 状态 | P0 | P1 | P2 | 说明 |
| --- | ---: | ---: | ---: | --- |
| `CONFIRMED`(新增) | 0 | 6 | 8 | 可由当前代码路径、DOM 结构或测试合同直接证明 |
| `LIKELY`(新增) | 0 | 1 | 0 | 表格在 768/1024 的实际裁切需浏览器量测 |
| `DUPLICATE`(既有) | 0 | 3 | 2 | 已登记在 `05-31-unified-page-header-actions` |
## 审查口径
1. 合同层:当前 PRD/design、`.trellis/spec/hyb-oa/frontend-react-vite-design.md`、`docs/产品设计规则.md`。
2. 实现层:路由、导航、页面元数据、共享模板、页面状态与动作、移动/企微路由转换。
3. 证据层:相关 E2E 的路由、视口、断言和覆盖矩阵;未用旧截图代替当前行为。
4. `ui-ux-pro-max` 只用于补充表格响应式、表单可访问性、错误反馈、焦点、对比度和 `prefers-reduced-motion` 检查。其外部配色/风格建议不用于替换当前 OA 视觉基线。
## PC、移动与企业微信入口映射
### 路由资产
| 表面 | 路由族 | 页面/模板归属 | 当前跨端归属 |
| --- | --- | --- | --- |
| 认证与兜底 | `/login`、`/forgot-password`、`/change-password`、`/403`、`*` | `AuthLayout` 或独立全屏状态页 | 普通浏览器与企微共用;不进入 PC/移动业务壳 |
| PC 工作台与协作 | `/portal`、`/announcements`、`/team`、`/weekly-reports`、`/todos`、`/dashboard`、`/notifications`、`/settings/profile` | 工作台、台账列表或个人设置 | 移动端分别映射首页、通知、个人设置;其余多折叠回移动首页 |
| PC 业务台账 | `/reimbursements`、`/purchase-requests`、`/contracts-registry`、`/projects`、`/assets`、`/payment-batches`、`/ap-operations`、`/invoice-pool`、`/budgets`、`/inventory-checks`、`/travel-requests`、`/leave-requests`、`/attendance`、`/wecom-approval-archive` | `BusinessListTemplate`、页面本地台账或记录视图 | 报销、采购、请假、票据、资产/盘点有专用移动表达;项目、合同、付款/AP、预算、差旅等折叠回移动首页 |
| PC 详情/处理 | `/projects/:projectId`、`/reimbursements/:reimbursementId`、`/reimbursements/:reimbursementId/print`、`/workflow/tasks/:taskId`,以及采购/资产/付款/票据的 `?id=` 模式 | `BusinessDetailTemplate`、`EntityOverviewHero` 或显式详情区 | 报销、审批、采购有移动详情;资产只映射扫码页;项目、合同和财务详情没有移动详情 |
| PC 管理 | `/audit-logs`、`/delegations`、`/payroll`、`/performance`、`/organization`、`/people/**`、`/system/**` | 管理配置页、审计/排障页 | 小屏/企微统一折叠到 `/mobile/me`,不复制 PC 管理台 |
| 移动工作台 | `/mobile`、`/mobile/me`、`/mobile/profile`、`/mobile/notifications`、`/mobile/wecom-approval-archive`、`/mobile/capture`、`/mobile/reimbursements/**`、`/mobile/leave/**`、`/mobile/purchase-requests/**`、`/mobile/assets/scan`、`/mobile/approvals/**` | 12 个 `Mobile*Page`,部分列表/详情复用同页 | 企业微信 WebView、钉钉容器或触控小屏优先进入该路由族 |
### 自动转换规则
- 精确映射:工作台/数据看板 → 移动首页;通知 → 移动通知;个人设置 → 移动个人设置;报销/审批/采购/请假 → 对应移动列表或详情;票据 → 移动上传;资产/盘点 → 移动扫码。
- 管理映射:`/system*`、`/people*`、`/organization*`、`/payroll*`、`/performance*` → `/mobile/me`。
- 折叠映射:项目、合同、差旅、付款、AP、周报、待办、团队、考勤、预算、公告、委派 → `/mobile`。
- 触发条件:企业微信/钉钉 UA,或 `maxTouchPoints > 0` 且视口不大于 768px。真机企业微信仍需人工复核,普通桌面浏览器缩窄视口不能替代。
- 移动底栏只固定 `首页 / 待办或进度 / 报销 / 我的`;请假、采购、通知和资产扫码从首页或“我的”进入。
### 当前设计中做得较好的部分
- PC 业务列表已普遍分离标题、命令栏、桌面表格和移动卡片;项目、资产、合同、付款、票据、采购和报销不再默认自动选择首条记录填充右侧详情。
- 报销、审批、采购和请假已有独立移动列表/详情;移动壳处理了视觉视口高度、safe area、深层详情隐藏底栏等基础问题。
- 请假/考勤页面总体明确“企微主办、OA 镜像/应急接管”,普通员工的 OA 日常请假表单已隐藏。
- 审批 PC/移动详情的通过、退回和驳回都进入 `ConfirmDialog`,没有直接从按钮瞬时执行审批动作。
- 共享 `BusinessDetailTemplate`、`BusinessEditTemplate`、空状态、骨架屏、状态徽标和确认弹窗已形成可继续治理的基础;问题主要是合同未覆盖权限、未保存、可访问性和响应式边界。
### `/mobile/wecom-approval-archive` 的 375px 专项静态预期
- 该移动路由并非独立移动页,而是直接复用 `WecomApprovalArchivePage`;路由允许具备 `leave.view_own` 或 `attendance.view_own` 的普通查看角色进入,并非仅 HR/管理员路径(`client/src/router/index.tsx:336`、`:338`、`:339`)。
- 在 375px 下,四项筛选静态上应由 `md:grid-cols-4` 回落为单列;桌面表格由 `hidden ... md:block` 隐藏,`md:hidden` 的卡片列表显示;详情主栅格到 `lg` 才切三列,字段摘要和参与人/动作到 `md` 才切多列,因此静态结构预期为单列移动表达,而不是压缩的 PC 宽表(`client/src/pages/WecomApprovalArchivePage.tsx:176`、`:227`、`:246`、`:276`、`:297`、`:307`)。
- 列表加载态虽然调用六列 `TableSkeleton`,但骨架只由可收缩的 `flex-1` 短条组成,没有 table 或 `min-width`;静态上不应制造横向滚动,不过六列在 375px 下是否显得过碎仍需截图判断(`client/src/pages/WecomApprovalArchivePage.tsx:218`;`client/src/components/common/TableSkeleton.tsx:11`、`:12`、`:18`)。
- 该路由不属于移动深层详情,底栏会保持显示;移动壳已按底栏高度、页面纵向间距和 safe area 增加内容底部 padding,静态上末尾详情不应被遮挡(`client/src/layouts/AppShellLayout.tsx:15`、`:18`、`:71`、`:79`)。
- 以上仅是 DOM/Tailwind 合同推导,不是浏览器 PASS。必须在 375px 实跑空态、加载态、有记录、打开详情、长文案和长人员名,量测页面 `scrollWidth`、末尾内容与底栏位置,并分别覆盖普通员工“与我相关”和 HR/管理员范围;真实企业微信 WebView 仍是外部准入。
## 新增 P1 Finding
### PAGE-P1-001:付款批次仍以“粘贴内部 ID”作为首屏主操作
- 维度:功能闭环 / 信息语义 / 财务页面。
- 严重级别:P1。
- 置信度:高。
- 状态:`CONFIRMED`。
- 文件与行号:`client/src/pages/PaymentBatchesPage.tsx:347`、`:356`、`:377`、`:403`;`client/tests/e2e/reimbursement-payment-batch-flow.spec.ts:107`;`.trellis/spec/hyb-oa/frontend-react-vite-design.md:777`;`docs/产品设计规则.md:110`。
- 证据:付款页把 `新建批次`放在标题主操作区;创建面板要求用户输入“待付款单据 ID”,占位文案明确要求粘贴报销或差旅单据 ID。现有 E2E 只能先从 API 取得 `reimbursementId` 再填入。当前 spec 同时规定付款首屏应先展示待复核/待付款/异常,批次创建不应成为首屏主操作;普通路径不得要求 UUID/内部 ID。
- 影响:真实财务用户通常只知道报销编号、人员、金额和付款状态,无法可靠取得 UUID;复制错误 ID 可能造成错批次、漏单或批次创建失败。当前自动化能跑通不代表人工可完成。
- 确认方式:用付款管理员打开 `/payment-batches`,点击新建后确认只有 ID 文本框而没有待付候选列表;尝试输入业务编号验证是否无法完成。静态接口与 E2E 已证明当前合同使用内部 ID。
- 建议:从 AP/待付池按业务编号、对象、人员、金额、证据状态提供可搜索多选;前端内部再提交 ID。批次创建进入待付列表的上下文动作或明确的角色工具,不作为普通首屏主 CTA。
- 修复边界:付款页、待付候选查询/API、付款批次创建 E2E;不改变付款状态机或真实付款边界。
- 是否需用户决策:是——确认以“应付账款待付池发起”还是“付款页候选选择器”为唯一主入口。
### PAGE-P1-002:危险动作确认合同没有系统性执行
- 维度:交互安全 / 证据链 / 管理操作。
- 严重级别:P1。
- 置信度:高。
- 状态:`CONFIRMED`。
- 文件与行号:`docs/产品设计规则.md:81`;`.trellis/spec/hyb-oa/frontend-react-vite-design.md:1189`;`client/src/pages/PurchaseRequestsPage.tsx:1630`;`client/src/pages/PaymentBatchesPage.tsx:595`;`client/src/pages/ReimbursementDetailPage.tsx:1537`;`client/src/pages/UserManagementPage.tsx:299`;`client/src/pages/PortalAnnouncementsPage.tsx:190`;`server/src/routes/reimbursements.js:2660`;`server/src/routes/paymentBatches.js:1062`。
- 证据:采购附件、报销附件和付款回单的“删除”按钮直接调用 delete mutation;后端执行 `deleteMany`。用户禁用/启用、重置密码、公告停用/归档也从列表按钮直接 mutation,没有对象摘要、后果、理由或确认词。
- 影响:一次误点即可删除财务/采购证据、使用户失去登录能力、停用制度公告或改变配置状态;即使后端记录审计,也不能代替前置防误触。
- 确认方式:在本地 mock 数据中点击上述动作,确认 mutation 前没有 `action-confirm-dialog`;同时检查后端是否物理删除或可恢复。
- 建议:把持久化附件删除、账号禁用/密码重置、公告发布/停用/归档等接入带类型的共享确认合同;至少展示业务编号/文件名/用户、影响、可逆性和必要理由。附件删除应明确是否物理删除、软删除或仅解除关联。
- 修复边界:共享确认组件及上述核心页面/回归测试;不在页面任务中改变后端权限和审计语义。
- 是否需用户决策:否——现行产品规则已经明确要求二次确认;仅删除/保留策略需要在具体修复 task 内确认。
### PAGE-P1-003:长表单没有未保存更改保护
- 维度:表单可靠性 / 数据丢失防护。
- 严重级别:P1。
- 置信度:高。
- 状态:`CONFIRMED`。
- 文件与行号:`.trellis/spec/hyb-oa/frontend-react-vite-design.md:920`;`client/src/components/patterns/BusinessEditTemplate.tsx:8`、`:35`;`client/src/pages/PurchaseRequestsPage.tsx:793`、`:809`;`client/src/pages/AssetRegistryPage.tsx:717`、`:732`;`client/src/pages/ContractsRegistryPage.tsx:410`。
- 证据:全前端没有 `useBlocker`、`beforeunload`、dirty-state 或“未保存”实现。共享编辑模板的返回动作是直接 `<Link>`;采购、资产在包含大量字段/明细/材料的编辑状态下点击“返回列表”会直接清空详情/创建状态。
- 影响:采购明细、资产建卡、合同登记、报销编辑等长表单可能因误点返回、侧栏跳转、浏览器返回或刷新而丢失尚未保存的输入,用户无法恢复。
- 确认方式:分别在采购草稿、资产新建和合同编辑中修改字段但不保存,点击返回列表、侧栏或浏览器返回/刷新,观察没有离开确认且输入丢失。
- 建议:新增共享 `useUnsavedChanges`/dirty 合同,覆盖路由离开、浏览器刷新、弹窗关闭和切换记录;保存成功后清除 dirty。先覆盖采购、报销、资产、合同,再覆盖管理长表单。
- 修复边界:共享 hook/编辑模板与 A/B 类长表单;不把自动保存作为本轮默认扩张。
- 是否需用户决策:否——保护原则已由 spec 明确;自动保存是否引入另行决定。
### PAGE-P1-004:移动/企微考勤入口自循环,通知直达对多类对象丢失目标
- 维度:跨端归属 / 企业微信 deeplink / 移动信息架构。
- 严重级别:P1。
- 置信度:高。
- 状态:`CONFIRMED`。
- 文件与行号:`docs/产品设计规则.md:101`、`:106`、`:116`;`client/src/pages/MobilePortalPage.tsx:348`、`:362`;`client/src/lib/notificationLinks.ts:52`、`:67`、`:75`;`client/src/lib/runtimeContext.ts:99`、`:113`;`client/src/pages/MobileNotificationsPage.tsx:124`、`:167`。
- 证据:移动首页“查看考勤汇总”链接到 `/attendance`;在企微/触控小屏中该 PC 路由又被 `resolveMobileEquivalentPath` 折叠回 `/mobile`,形成自循环。移动通知把考勤修正目标也映射为 `/attendance`;资产/盘点目标统一映射 `/mobile/assets/scan`,不携带资产 ID 或现有的 `inventoryCheckId` 参数。
- 影响:企微用户点击考勤入口回到原首页;考勤、资产和盘点通知虽然显示“直达详情”,实际无法定位原对象,用户必须重新查找,破坏消息到业务目标的闭环。
- 确认方式:用 WeCom UA 访问 `/mobile` 点击“查看考勤汇总”;再构造 `attendance_correction`、`asset`、`inventory_check` 三类通知,核对最终 URL 与目标对象是否保留。
- 建议:为考勤提供明确的移动镜像/摘要路由,或允许该只读页面在企微中响应式打开;资产目标传 `id`/查询值,盘点传 `inventoryCheckId`;每种通知类型增加“最终 URL + 目标内容”断言。
- 修复边界:移动首页、通知目标解析、移动考勤/资产/盘点入口和相关 E2E;不触碰企微真实发送或写入。
- 是否需用户决策:是——确定考勤汇总采用专用移动页,还是允许现有 PC 只读页在企微 WebView 中打开。
### PAGE-P1-005:全局搜索对多类命中只跳列表,不跳命中对象
- 维度:全局导航 / 搜索可用性。
- 严重级别:P1。
- 置信度:高。
- 状态:`CONFIRMED`。
- 文件与行号:`server/src/routes/search.js:173`、`:179`、`:201`、`:206`、`:341`、`:349`、`:378`、`:386`;`client/src/components/GlobalSearchDialog.tsx:95`、`:157`;`client/src/pages/AssetRegistryPage.tsx:233`;`client/tests/e2e/global-search.spec.ts:53`、`:59`。
- 证据:项目、报销和采购结果携带对象路由/`?id=`,但资产、请假、合同、资料引用、公告、周报和待办结果只返回通用列表路径。搜索弹窗点击时原样导航 `item.href`;资产页本可接收 `?id=`,合同页只有 `projectId`,请假 PC 页没有命中 ID 处理。现有 E2E 只点击并断言报销结果。
- 影响:用户明确点击一个资产、合同或请假搜索结果后,只看到普通列表且无法确认命中项,数据量稍大时“全局搜索”失去直接导航价值。
- 确认方式:搜索一条非首屏资产、合同和请假记录,点击结果后检查是否打开/高亮该对象;当前静态 href 已足以证明目标参数缺失。
- 建议:所有对象型结果统一返回稳定详情路由或 `?id=/highlight=`;页面接收参数并打开/高亮对象。公告/周报/待办若无详情路由,至少滚动定位并高亮;补每种实体的点击回归。
- 修复边界:搜索 API href 合同、对应页面 deeplink 处理和搜索 E2E;不扩大搜索数据权限。
- 是否需用户决策:否。
### PAGE-P1-006:共享业务表格在 768/1024 断点存在右侧列被裁切风险
- 维度:响应式 / 表格可用性。
- 严重级别:P1。
- 置信度:高。
- 状态:`LIKELY`。
- 文件与行号:`client/src/components/patterns/BusinessListTemplate.tsx:24`、`:34`;`client/src/components/patterns/ListWorkspace.tsx:92`;`client/src/index.css:318`、`:547`;`client/src/layouts/AppShellLayout.tsx:65`;`client/src/components/navigation/Sidebar.tsx:6`。
- 证据:`BusinessTable` 从 `md` 开始显示、内部表格最小宽度 760px;外层 `.table-container` 是 `overflow-hidden` 而不是横向滚动。768px 下内容区扣除页面边距后不足 760px;1024px 时侧栏开始占 288px,主内容区同样不足 760px。项目、资产、合同、付款、票据、报销六个核心列表复用该结构。
- 影响:常见 768/1024 视口可能裁掉最右“状态/操作”列,且容器没有横向滚动,直接影响查看详情或处理入口。
- 确认方式:在 768×1024 与 1024×768 分别打开 `/projects`、`/assets`、`/contracts-registry`、`/payment-batches`、`/invoice-pool`、`/reimbursements`,量测 table/container 的 `clientWidth`、`scrollWidth`、`overflowX`,确认操作列是否可见和可滚动。
- 建议:让共享表格容器 `overflow-x-auto`,或把桌面表格断点提高到内容真正容得下的宽度并继续显示卡片;在 768/1024 加操作列可见断言。
- 修复边界:共享表格 CSS/模板及六页响应式回归;不逐页打补丁。
- 是否需用户决策:否。
### PAGE-P1-007:核心表单标签与动态错误缺少可访问性语义
- 维度:可访问性 / 表单反馈。
- 严重级别:P1。
- 置信度:高。
- 状态:`CONFIRMED`。
- 文件与行号:`client/src/pages/ContractsRegistryPage.tsx:416`、`:425`、`:456`;`client/src/pages/ProjectsPage.tsx:201`;`client/src/components/patterns/BusinessEditTemplate.tsx:131`;`client/src/pages/PurchaseRequestsPage.tsx:830`、`:966`;`client/src/pages/MobileNotificationsPage.tsx:218`。
- 证据:合同和项目等核心编辑表单大量使用独立 `<Label>`,但没有 `htmlFor`,相邻 Input/select 也没有 `id`,因此视觉标签没有程序化关联。共享验证摘要和采购/移动通知动态错误只渲染红色块,没有 `role="alert"`、`aria-live`、`aria-invalid` 或字段级 `aria-describedby`。启发式扫描发现页面/组件约 529 个表单控件、375 个 label,但只有 177 个 `htmlFor`;整个页面/组件范围只有登录与忘记密码两处 `aria-live`。数量仅用于说明规模,finding 以列出的确定样例为准。
- 影响:屏幕阅读器无法可靠读出字段名称或获知异步错误;键盘/辅助技术用户在合同、项目、采购和移动流程中可能无法定位失败字段,核心流程并非等价可用。
- 确认方式:对合同/项目创建表单取 accessibility snapshot 或运行 axe;触发采购列表/表单错误,确认 live region 是否产生公告;逐字段核对 accessible name。
- 建议:建立共享 Field/FieldError 合同,强制稳定 id、`htmlFor`、`aria-invalid`、`aria-describedby`;动态错误使用适当的 `role=alert`/`aria-live` 并把焦点移动到摘要或首个错误字段。
- 修复边界:共享表单/验证组件和核心表单分波迁移;不以批量字符串替换代替逐页验证。
- 是否需用户决策:否。
## 新增 P2 Finding
### PAGE-P2-001:路由元数据、页面标题与浏览器标题存在系统性漂移
- 维度:信息架构 / 标题与导航。
- 严重级别:P2。
- 置信度:高。
- 状态:`CONFIRMED`。
- 文件与行号:`client/src/features/navigation/schema.ts:5`、`:53`;`client/src/components/navigation/Topbar.tsx:56`、`:168`;`client/src/layouts/AppShellLayout.tsx:49`;`client/index.html:7`;`client/src/pages/ReimbursementDashboardPage.tsx:138`;`client/src/pages/NotificationsPage.tsx:82`;`client/src/pages/TeamPresencePage.tsx:262`。
- 证据:`PAGE_TITLES` 只覆盖部分 PC 路由,未命中时把原始 pathname 当标题并写入最近访问。`/reimbursements/dashboard` 被较长的 `/reimbursements/` 误识别成“报销详情”;`/system/users`、`/system/departments`、`/system/master-data`、`/delegations`、`/payroll`、`/performance` 等会显示原始路径。已有映射也存在“通知/通知中心”“团队/团队状态”“数据看板/仪表盘”等漂移。浏览器 `<title>` 始终是“海音达工作台”,没有按路由更新。
- 影响:顶部标题、页面 H1、最近访问和浏览器 tab/back 历史语义不一致;管理页可能直接暴露内部路由文本。
- 确认方式:依次打开统计、系统用户、通知、团队页面,对比 Topbar、H1、最近访问和 `document.title`。
- 建议:建立唯一 typed route manifest,统一标题、分组、权限、导航和可选详情标题;路由变化时同步 `document.title`。详情标题可由页面覆写,但不得回落原始 pathname。
- 修复边界:前端路由元数据、Topbar、最近访问和路由标题测试;不改业务路由 URL。
- 是否需用户决策:否。
### PAGE-P2-002:403、404 和渲染错误的恢复按钮仍指向隐藏旧仪表盘
- 维度:错误恢复 / 信息架构。
- 严重级别:P2。
- 置信度:高。
- 状态:`CONFIRMED`。
- 文件与行号:`client/src/pages/ForbiddenPage.tsx:9`、`:13`;`client/src/pages/NotFoundPage.tsx:9`、`:11`;`client/src/components/common/ErrorBoundary.tsx:33`、`:50`;`client/src/router/index.tsx:154`;`server/src/services/workspaceSettings.js:136`。
- 证据:三类兜底都把“返回首页/工作台”链接写成 `/dashboard`;当前默认工作台是 `/portal`,Dashboard 在默认工作区中为 hidden,且普通用户进入后可能再次看到看板无权限状态。
- 影响:发生 403/404/渲染错误时,用户没有被带回稳定可用入口,容易进入第二个无权限/空错误页面;文案与实际去向也不一致。
- 确认方式:普通员工访问无权限路由、未知路由,并触发 ErrorBoundary 测试 fallback,点击返回按钮核对最终页面。
- 建议:统一使用 `/portal` 或 `resolveDefaultWorkspacePath()`;错误页提供“返回上一页 + 回到工作台”,不要显示原始错误 message 作为普通用户主文案。
- 修复边界:共享错误/403/404 页面及聚焦 E2E。
- 是否需用户决策:否。
### PAGE-P2-003:冻结的差旅能力仍散落在非差旅页面和入口合同中
- 维度:范围收口 / 兼容残留 / 跨页面一致性。
- 严重级别:P2。
- 置信度:高。
- 状态:`CONFIRMED`。
- 文件与行号:`docs/产品设计规则.md:151`;`.trellis/spec/hyb-oa/frontend-react-vite-design.md:189`、`:241`;`client/src/router/index.tsx:244`;`server/src/services/workspaceSettings.js:99`、`:138`;`client/src/pages/PaymentBatchesPage.tsx:387`;`client/src/pages/MobileApprovalsPage.tsx:84`;`client/src/pages/NotificationsPage.tsx:17`;`client/src/pages/MobileNotificationsPage.tsx:17`。
- 证据:当前事实源要求差旅不进入主线、冻结模块不静默恢复;但差旅路由仍注册为管理/更多入口,付款批次无条件提供“差旅订单付款”,PC/移动通知筛选和移动审批筛选持续显示差旅。
- 影响:冻结能力继续占用普通流程的认知空间,测试和维护容易误把差旅当作启用主线;付款用户甚至能选择一个当前不应扩展的实体类型。
- 确认方式:使用付款管理员和普通移动角色检查上述入口是否在没有独立恢复 task/feature gate 时可见。
- 建议:先移除非差旅页面的差旅选项和筛选;差旅路由保持隐藏或显式冻结边界。若为历史数据查询必须保留,只在独立只读/诊断入口通过明确 feature flag 开放。
- 修复边界:页面入口、导航/feature gate、相关旧 E2E;数据模型/API 删除应另做兼容与迁移评估。
- 是否需用户决策:是——确认“仅隐藏/冻结兼容”还是在独立清理 task 中删除前端和无消费者代码;结合用户已表达偏好,推荐先删用户可见入口,再审计后端消费者。
### PAGE-P2-004:普通路径仍暴露内部错误码与技术状态文案
- 维度:文案语义 / 错误恢复。
- 严重级别:P2。
- 置信度:高。
- 状态:`CONFIRMED`。
- 文件与行号:`.trellis/spec/hyb-oa/frontend-react-vite-design.md:688`、`:1151`;`client/src/pages/MobilePortalPage.tsx:36`;`client/src/pages/MobileApprovalsPage.tsx:194`;`client/src/pages/PurchaseRequestsPage.tsx:78`;`client/src/pages/ContractsRegistryPage.tsx:94`;`client/src/components/GlobalSearchDialog.tsx:118`;`client/src/components/common/ErrorBoundary.tsx:33`;`client/src/pages/WecomApprovalArchivePage.tsx:40`、`:47`、`:124`、`:222`、`:258`、`:328`、`:332`;`client/src/router/index.tsx:336`、`:338`。
- 证据:静态扫描有 56 个页面/组件文件使用 `${error.code}: ${mapApiErrorMessage(...)}` 一类模式,覆盖移动首页、移动审批、采购、合同、搜索等普通路径;ErrorBoundary 直接展示 `error.message`。移动企微审批归档页也在空态暴露 `PARTIAL_ARCHIVE_NEEDS_DATA`,在筛选、卡片和详情中直接使用英文 `finding`,并把 `findingType`、`status`、`severity` 原值直接输出。该路由允许 `leave.view_own` / `attendance.view_own` 查看角色进入,不是仅管理员可见的排障页。当前 spec 明确普通用户不得看到错误码、内部状态码或技术 dump。
- 影响:异常或对账信息出现时,用户先看到 `PERM_001`、`VALID_001`、`finding`、内部验收状态或原始枚举等平台语言,弱化“原因 + 影响 + 下一步”,也可能暴露不必要的内部实现信息;375px 卡片上的技术文案更直接占用有限空间。
- 确认方式:分别 mock 权限、校验和网络错误,检查普通页面/移动页可见文本;在 375px 企微归档页覆盖空态、含 finding 的卡片和详情,并核对普通员工角色可见文本;管理员审计/排障页可作为允许显示技术摘要的对照组。
- 建议:共享错误解析返回 `userMessage` 与可选 `diagnosticCode`;普通页面只显示自然中文和恢复动作,技术码只在管理/审计折叠区或日志中显示。企微归档页把 `finding` 统一翻译为“对账问题”,为类型、状态和严重度建立中文映射,并移除面向验收脚本的空态 token。
- 修复边界:共享错误解析与普通页面调用点分波迁移;不改变服务端错误合同。
- 是否需用户决策:否。
### PAGE-P2-005:多处桌面表格行只能鼠标点击,缺少键盘等价入口
- 维度:键盘可达 / 表格交互。
- 严重级别:P2。
- 置信度:高。
- 状态:`CONFIRMED`。
- 文件与行号:`client/src/pages/InventoryChecksPage.tsx:323`;`client/src/pages/AssetRegistryPage.tsx:856`;`client/src/pages/PurchaseRequestsPage.tsx:1046`;`client/src/pages/PerformancePage.tsx:207`;`client/src/components/master-data/HolidayCalendarTab.tsx:368`。
- 证据:上述 `<TableRow>/<tr>` 直接挂 `onClick`,但没有可聚焦语义、键盘处理或统一行内操作。盘点和部分管理表格依赖点击整行选择记录。
- 影响:只用键盘或开关设备的用户无法选择盘点、资产分类、绩效周期等记录;鼠标悬停样式无法提供等价可达性。
- 确认方式:从页面标题开始只按 Tab/Enter/Space,尝试选择上述行并进入后续操作。
- 建议:优先在操作列放真实 `<button>/<Link>`;确需整行选择时仍保留显式可聚焦主操作,并给选中状态 `aria-selected`,不要只给 `tr` 增加伪按钮语义。
- 修复边界:共享表格行操作约定和列出的五处页面。
- 是否需用户决策:否。
### PAGE-P2-006:共享视觉 token 与动效未达到当前可访问性基线
- 维度:视觉设计 / 低视力与动态效果。
- 严重级别:P2。
- 置信度:高。
- 状态:`CONFIRMED`。
- 文件与行号:`client/src/design/theme.css:8`、`:10`;`client/src/index.css:64`、`:318`、`:327`、`:559`、`:1098`。
- 证据:`--color-text-tertiary: #94a3b8` 在白色背景的 WCAG 对比度约 2.56:1,却被用于 12px 表头/元信息;`text-zinc-400` 也约 2.56:1。所有 `.page-scaffold` 固定执行 320ms 位移动画,代码没有 `prefers-reduced-motion` 分支。
- 影响:低视力用户难以识别表头和关键元信息;明确请求减少动态效果的用户仍会看到页面位移动画。
- 确认方式:在 100%/200% 缩放下检查表头/元信息对比度,并在系统启用 reduce motion 时记录 computed animation。
- 建议:语义正文/表头至少使用达到 AA 的色阶,浅色只保留非必要装饰;添加 `@media (prefers-reduced-motion: reduce)` 关闭页面位移及非必要过渡。
- 修复边界:共享主题/CSS token 与全局视觉回归,不逐页改色。
- 是否需用户决策:是——若项目尚无可访问性等级,建议把 WCAG 2.1 AA 设为普通文字与交互的验收基线。
### PAGE-P2-007:“全页面视觉巡检”矩阵与路由已漂移,且缺关键中间断点
- 维度:视觉验证可信度 / 页面覆盖。
- 严重级别:P2。
- 置信度:高。
- 状态:`CONFIRMED`。
- 文件与行号:`docs/trialrun/项目级流程覆盖矩阵.csv:2`、`:6`;`client/tests/e2e/project-wide-visual.spec.ts:129`、`:363`、`:390`、`:405`;`client/tests/e2e/r106-visual-audit.spec.ts:506`、`:532`;`client/tests/e2e/r103-cross-page-ui-consistency.spec.ts:339`;`client/src/router/index.tsx:336`;`client/tests/e2e/oafront3-pc-readonly-visual.spec.ts:118`。
- 证据:项目级覆盖矩阵只有 5 个业务行,系统管理写为 `/system/*`;runner 只替换少数动态 ID,不展开 wildcard,会把 `/system/*` 原样导航成 404,同时只审 1440/390。R106 仍把 PC 请假写成不存在的 `/leave`,审查函数没有校验预期 H1/404,可能给 404 截图记“PC 请假”。R103 只有采购、票据和移动票据三项,未覆盖项目/资产,也没有 768/1024。仓库已注册 `/mobile/wecom-approval-archive`,但当前只找到 PC `/wecom-approval-archive` 的 HR 视觉条目,没有该移动路由的 375/390 覆盖。
- 影响:测试名称和报告容易给出“全页面已看”的错误信心;正好遗漏共享表格最危险的中间断点和多个管理/业务页面。
- 确认方式:运行 `--list` 后打印最终 route plans,断言不得含 `*`,每页必须核对最终 URL、预期 H1 且不得出现 403/404;统计与真实路由清单的差集。
- 建议:从 typed route manifest 生成显式审查矩阵;动态详情由 fixture 解析,冻结/无权限明确标注;增加 768/1024,失败条件包含错误页、4xx 页面和标题不匹配。
- 修复边界:视觉矩阵、项目级/R106/R103 E2E 和报告 schema;不在测试 task 中改页面实现。
- 是否需用户决策:否。
### PAGE-P2-008:移动导航和“我的”仍显示无权限或未校验权限的入口
- 维度:权限显隐 / 移动信息架构。
- 严重级别:P2。
- 置信度:中高。
- 状态:`CONFIRMED`。
- 文件与行号:`.trellis/spec/hyb-oa/frontend-react-vite-design.md:632`、`:644`;`client/src/components/mobile/MobileBottomNav.tsx:39`、`:46`;`client/src/pages/MobileMePage.tsx:54`、`:112`;`client/src/router/index.tsx:353`、`:371`。
- 证据:移动底栏对无报销权限角色仍渲染 disabled“报销不可用”,而不是隐藏;移动“我的”只检查采购/资产/审批,却无条件显示“请假记录”和“报销”,对应路由本身又有权限守卫。
- 影响:特殊只读、停用或最小权限角色会看到不可用入口,点击“我的”中的未校验链接可能直接进入 403;与“未授权动作通常隐藏”合同不一致。
- 确认方式:用不含 `expense.view_*`、`leave.view_*` 的最小权限 fixture 打开 `/mobile` 与 `/mobile/me`,检查底栏/入口和点击结果。
- 建议:入口清单统一使用同一 permission predicate,未授权业务入口直接隐藏;只有确有申请权限/配置缺失说明时才显示 disabled 状态和真实下一步。
- 修复边界:移动导航 schema、BottomNav、MobileMe 与角色矩阵 E2E。
- 是否需用户决策:否。
## 既有 Finding 复核与去重
以下 5 条均已在 `.trellis/tasks/05-31-unified-page-header-actions/prd.md:108` 登记,本轮不重复建账。
### PAGE-DUP-P1-001:复核角色仍看到“新建批次”
- 维度:付款权限显隐。
- 严重级别:P1。
- 置信度:高。
- 状态:`DUPLICATE`。
- 文件与行号:`client/src/pages/PaymentBatchesPage.tsx:110`、`:347`、`:356`、`:372`;既有记录 `.trellis/tasks/05-31-unified-page-header-actions/prd.md:110`。
- 证据:页面已计算 `canManageBatch`,但列表标题“新建批次”没有按它隐藏;复核用户点击后才看到只读说明。
- 影响:误导复核角色进入无法完成的写流程;服务端 403 虽防止写入,但不修复页面合同。
- 确认方式:仅授予 `risk.review.manage` 的角色打开付款列表,检查标题按钮。
- 建议:按钮和创建面板都以 `canManageBatch` 为条件;增加 review-only 负例。
- 修复边界:独立权限显隐修复 task。
- 是否需用户决策:否。
### PAGE-DUP-P1-002:合同只读角色仍看到完整写动作
- 维度:合同权限显隐。
- 严重级别:P1。
- 置信度:高。
- 状态:`DUPLICATE`。
- 文件与行号:`client/src/pages/ContractsRegistryPage.tsx:200`、`:284`、`:310`、`:334`、`:798`、`:861`;既有记录 `.trellis/tasks/05-31-unified-page-header-actions/prd.md:111`。
- 证据:页面只对人员目录做权限判断,没有合同写权限布尔值;新增、编辑、续约和归档对 `project.view_own/view_all` 只读用户可见,最终依赖 API 403。
- 影响:只读用户无法判断真实能力,可能填写整张合同后才失败。
- 确认方式:`project.view_own`/`project.view_all` 且无 manage/settings 的角色打开合同页。
- 建议:建立合同 view/manage 权限动作矩阵,写动作默认隐藏,保留只读详情/台账。
- 修复边界:独立合同权限 task,需跨层 reviewer。
- 是否需用户决策:否。
### PAGE-DUP-P1-003:票据上传按钮没有使用已计算的上传权限
- 维度:票据权限显隐。
- 严重级别:P1。
- 置信度:高。
- 状态:`DUPLICATE`。
- 文件与行号:`client/src/pages/InvoicePoolPage.tsx:56`、`:59`、`:475`、`:485`;既有记录 `.trellis/tasks/05-31-unified-page-header-actions/prd.md:112`。
- 证据:`canUploadInvoice` 已计算,但标题“上传票据”和上传面板没有引用它。
- 影响:只读/管理范围角色可能进入最终失败的上传流程。
- 确认方式:无 `invoice.upload`、有只读入口权限的 fixture 检查标题动作和面板。
- 建议:按上传权限隐藏标题动作和上传面板,并补直连负例。
- 修复边界:独立票据权限 task。
- 是否需用户决策:否。
### PAGE-DUP-P2-001:两条资产 E2E 仍点击旧的直接“资产分类”按钮
- 维度:测试与交互漂移。
- 严重级别:P2。
- 置信度:高。
- 状态:`DUPLICATE`。
- 文件与行号:`client/tests/e2e/assets-flow.spec.ts:88`;`client/tests/e2e/manual/asset1d-public-trial.spec.ts:165`;`client/src/pages/AssetRegistryPage.tsx:743`、`:754`;既有记录 `.trellis/tasks/05-31-unified-page-header-actions/prd.md:113`。
- 证据:页面已把“资产分类”放入“更多”,两条测试仍按直接按钮点击。
- 影响:自动化不能验证当前真实交互,并可能在恢复公网试录时直接失败。
- 确认方式:运行对应 mock E2E;manual spec 只做静态修复后在另行批准的公网场景验证。
- 建议:测试先打开“更多”再选“资产分类”;不要为旧测试恢复直接按钮。
- 修复边界:测试修复 task;不改资产页面。
- 是否需用户决策:否。
### PAGE-DUP-P2-002:标题动作容器没有数量合同,项目/资产视觉覆盖仍缺
- 维度:共享组件合同 / 视觉覆盖。
- 严重级别:P2。
- 置信度:高。
- 状态:`DUPLICATE`。
- 文件与行号:`client/src/components/patterns/ListWorkspace.tsx:36`;`client/tests/e2e/r103-cross-page-ui-consistency.spec.ts:339`;既有记录 `.trellis/tasks/05-31-unified-page-header-actions/prd.md:114`。
- 证据:`HeaderActionGroup` 仅是 flex 布局,不限制“一主操作 + 最多一个更多”;R103 只覆盖采购、票据和移动票据,未覆盖项目、资产或七页面统一动作断言。
- 影响:页面后续可继续在标题区堆按钮,而现有回归不报警。
- 确认方式:静态枚举七个页面标题动作,并在 1440/1024 截图核对。
- 建议:用可测试的 slot/variant 合同表达 primary/more,增加七页面统一断言和项目/资产截图。
- 修复边界:共享标题动作组件及聚焦测试。
- 是否需用户决策:否。
## 建议主线程优先做的浏览器复核
按风险收益排序,均应使用本地 Vite dev、mock/无凭据数据,不使用公网或真实资料:
1. **768/1024 共享表格**:`/projects`、`/assets`、`/contracts-registry`、`/payment-batches`、`/invoice-pool`、`/reimbursements`;检查右侧操作列、横向滚动、标题动作换行和页面整体 `scrollWidth`。
2. **付款角色与创建**:review-only 与 manage 两种角色;检查“新建批次”显隐、候选来源、内部 ID 文案和表单 accessible name。
3. **合同/票据只读角色**:复核新增/编辑/续约/归档、上传票据是否隐藏,避免只在 API 403 时才告知权限不足。
4. **375px 企微审批归档专项**:以 375×812(可补 375×667)和 WeCom UA 打开 `/mobile/wecom-approval-archive`,分别 mock 空态、加载态、有记录和打开详情;覆盖长页面文案、长人员名、含对账问题记录,断言桌面表格隐藏、卡片/详情单列、`documentElement.scrollWidth <= clientWidth`、末尾内容不被底栏遮挡。至少使用普通员工查看权限与 HR/管理员两类角色,核对范围选项和可见数据;真机 WebView 另记 `EXTERNAL_GATED`。
5. **移动/企微直达**:390px WeCom UA 打开 `/mobile` 点击考勤汇总;在 `/mobile/notifications` 注入考勤、资产、盘点通知,检查最终目标和返回语义。
6. **全局搜索**:从 `/portal` 搜索并点击资产、合同、请假、公告、周报和待办;检查是否打开/高亮唯一结果,以及状态是否仍显示 `in_use/reviewing` 等原始码。
7. **未保存保护**:采购、资产、合同、报销分别修改字段后点击返回、侧栏、浏览器返回和刷新。
8. **键盘与可访问性**:合同/项目表单 accessible name,采购错误 live announcement,盘点/资产分类/绩效表格的 Tab/Enter,移动导航最小权限角色;建议输出 accessibility snapshot 或 axe 结果。
9. **错误恢复**:普通员工走 403、404、ErrorBoundary 和典型 API 错误,确认回到工作台且不展示内部码。
## 建议修复波次
1. **页面 P1 闭环**:付款候选选择、危险确认、未保存保护、移动 deeplink、全局搜索直达、核心表单可访问性。
2. **共享响应式与权限显隐**:确认 768/1024 后修 BusinessTable;同时处理 3 条既有权限显隐 finding。
3. **范围收口与语义**:差旅用户可见残留、路由/标题 manifest、错误文案与 recovery 入口。
4. **视觉/测试治理**:对比度与 reduce motion、键盘表格、显式全路由视觉矩阵和已漂移 E2E。
审查阶段不应在同一 task 中混改上述页面;每波先建独立 Trellis task,并对权限、付款、审批和状态动作安排只读 reviewer。
二进制文件未显示。

之后

宽度:  |  高度:  |  大小: 60 KiB

@@ -0,0 +1,38 @@
{
"id": "system-multidimensional-audit",
"name": "system-multidimensional-audit",
"title": "多维系统审查与问题分级",
"description": "在不修改业务实现和不访问生产的前提下,从功能、业务闭环、页面、设计、权限、数据、安全、性能、测试、运维和文档等维度审查当前本地系统,并输出证据化 P0/P1/P2 finding。",
"status": "in_progress",
"dev_type": null,
"scope": "audit/full-system",
"package": null,
"priority": "P1",
"creator": "shunli",
"assignee": "shunli",
"createdAt": "2026-07-16",
"completedAt": null,
"branch": null,
"base_branch": "main",
"worktree_path": null,
"commit": null,
"pr_url": null,
"subtasks": [],
"children": [],
"parent": null,
"relatedFiles": [
".trellis/tasks/archive/2026-07/07-16-legacy-task-runbook-closeout",
".trellis/tasks/05-31-unified-page-header-actions",
".trellis/spec/hyb-oa",
"docs/文档索引.md",
"docs/文档事实源状态矩阵.md",
"docs/产品设计规则.md",
"client/src/router/index.tsx",
"server/prisma/schema.prisma",
"client/tests/e2e",
"server/tests/unit",
"scripts/qa"
],
"notes": "用户已批准风险收敛收口:保留发现基线 bbb5c89,并对当前本地 HEAD 8f8606c 补充 delta。两个原 P0 已完成本地修复验证,但均未获部署或外部行为验证;不得据此声称公网已同步。本轮只更新当前 task、必要 spec 与合成浏览器证据,不修改业务代码、API、schema、生产配置或真实数据,也不创建下游修复 task。后续仅按员工敏感数据、付款一致性、高敏合同附件、发布门禁、客户端数据生命周期/导出、页面权限显隐与旧标题任务的顺序登记建议;部署、公网角色、企微真机和新 Day 1 试运行须另行授权。",
"meta": {}
}