归档前端体验重构任务(05-28-frontend-ux-redesign)

这个提交包含在:
2026-05-28 23:28:11 +08:00
父节点 c508739688
当前提交 85c298e25a
共修改 42 个文件,包含 3 行新增和 3 行删除
@@ -0,0 +1,3 @@
{"file": ".trellis/spec/frontend/index.md", "reason": "Frontend quality checklist and linked project rules."}
{"file": ".trellis/spec/hyb-oa/frontend-react-vite-design.md", "reason": "Required frontend checks, auth/page visual rules and Playwright expectations."}
{"file": ".trellis/tasks/05-28-frontend-ux-redesign/research/ux-audit-matrix.md", "reason": "Scope and acceptance basis for Wave0-Wave2 UX review."}
@@ -0,0 +1,3 @@
{"file": ".trellis/spec/frontend/index.md", "reason": "Frontend task entrypoint and project-specific spec routing."}
{"file": ".trellis/spec/hyb-oa/frontend-react-vite-design.md", "reason": "Frontend React/Vite design and validation rules for OA pages."}
{"file": ".trellis/tasks/05-28-frontend-ux-redesign/research/ux-audit-matrix.md", "reason": "Wave0 audit findings and Wave1/Wave2 implementation direction."}
@@ -0,0 +1,48 @@
# Implementation Notes
## Scope Implemented
- Wave 0: wrote `research/ux-audit-matrix.md` with global findings, Wave 2 page audit and Wave 3-5 follow-up grouping.
- Wave 1: updated global shell, sidebar, topbar, page headers and shared layout classes.
- Wave 2: refined login, forgot password, portal, workflow tasks, reimbursements, reimbursement detail and notifications with clearer hierarchy and next-step language.
## UX Decisions
- Visual direction: precise, restrained OA operations console.
- Navigation groups now reflect user tasks: 工作、业务、财务、管理、更多.
- Search is presented as a command entry in the Topbar.
- Notifications now behave as an inbox and return to the workbench, not the legacy dashboard.
- Login and password reset retain the generic “如账号可用” copy to avoid account enumeration.
- Wave 3-5 stay as follow-up tasks because they require different validation surfaces.
## Validation
- `cd client && npm run typecheck` — PASS.
- `cd client && npm run build` — PASS.
- `cd client && npx playwright test tests/e2e/pc-core-workflow-visual-wave2.spec.ts tests/e2e/login-portal-ux.spec.ts --workers=1 --reporter=list` — PASS, 11 tests.
- `cd client && npx playwright test tests/e2e/r89-mobile-login-navigation.spec.ts --workers=1 --reporter=list` — PASS, 3 tests.
- `cd client && npx playwright test tests/e2e/visual-shell.spec.ts tests/e2e/r103-cross-page-ui-consistency.spec.ts --workers=1 --reporter=list` — PASS, 8 tests.
- `python3 ./.trellis/scripts/task.py validate 05-28-frontend-ux-redesign` — PASS.
- `node scripts/qa/check-trellis-governance.mjs` — PASS.
- `git diff --check` — PASS.
## Deployment And Public Verification
- 用户已批准将本轮 Wave0-2 前端体验重构部署到公网 OA。
- 部署命令:`SYNC_ENV=0 LIVE_SMOKE_BASE_URL=https://oa.hyinda.com bash deploy/trialrun/deploy.sh`
- 部署边界:未同步本地 `.env`,未改远端配置,未执行真实付款、ERP 写入或企业微信写入。
- 首次部署后 live smoke 暴露旧断言:测试仍查找旧导航文案“备用模块”。已修正 live/视觉测试基线为新导航“更多”,并提交 `1f13dd9`。
- 重新部署结果:远端前端构建通过,Prisma migration deploy/status 显示无待执行 migration,部署脚本内置 live smoke 3 passed,公网资源文案扫描 findings=0。
- 独立公网 readiness:
- `LIVE_SMOKE_READINESS_DIR=.trellis/tasks/05-28-frontend-ux-redesign/research/postdeploy-20260528-wave0-2-public/live-smoke-readiness LIVE_SMOKE_BASE_URL=https://oa.hyinda.com bash scripts/qa/run-live-smoke-readiness.sh` — PASS。
- 真实 OCR 样票复核返回 `duplicate_or_same_invoice` 可解释终态,测试通过;pending OCR 扫描因本地未提供 `DATABASE_URL` 记录 `SKIP`。
- 完整只读巡检:
- `OAOPS1_REPORT_DIR=.trellis/tasks/05-28-frontend-ux-redesign/research/postdeploy-20260528-wave0-2-public/oa-real-use-daily LIVE_SMOKE_BASE_URL=https://oa.hyinda.com bash scripts/qa/run-oa-real-use-daily.sh` — `PASS_OA_REAL_USE_DAILY_READY`,findingCount=0。
- 通过检查:`public-ready`、`live-smoke-env`、`live-readiness`、`oa-only-smoke-readiness`、`live-feature-flags`、`authenticated-readonly-probe`、`mobile-entry-matrix`、`wecom-rendering-matrix`、`wecom-entry`、`report-privacy-smoke`。
- 证据目录:`research/postdeploy-20260528-wave0-2-public/`。
- 非阻断提示:部署同步时远端历史 `artifacts/qa-flow/wecom-controlled-trial-evidence-20260520-205320` 目录仍无法自动删除;不影响本次部署、构建或公网检查。
## Not Done In This Task
- No backend permission, workflow, status-machine or data model changes.
- Wave 3-5 pages are recorded for follow-up tasks, not completed here.
@@ -0,0 +1,194 @@
# 全站前端体验重构
## Goal
从用户角度系统重构 hyb-oa 前端体验,让 PC 与移动端页面形成统一、克制、专业、可扫描的 OA 工作界面;降低用户理解成本、减少无效操作、统一页面布局和交互模式,并优先保障真实试运行中的核心业务路径。
## What I already know
- 用户已明确希望“重新优化改进全部页面”,从用户角度、用户交互、用户体验思考,也可参考优秀系统设计。
- 已确认前端视觉、布局、组件观感、字体、颜色、动效、间距和交互细节的最终裁决以 `frontend-design` skill 为准。
- `docs/产品设计规则.md` 已瘦身为产品体验事实源,不再作为视觉规则手册。
- 当前前端为 React + TypeScript + Vite + Tailwind,页面集中在 `client/src/pages/`,全局框架包括 `AppShellLayout`、`Sidebar`、`Topbar`、`NavLinks` 和 `components/patterns/*`。
- 现有页面数量较多,覆盖登录、工作台、审批、报销、通知、项目、合同、采购、票据池、付款、AP、资产、考勤、人员和系统管理。
- 真实试运行仍在进行,面向用户可测试的页面改动必须部署与公网检查通过后才能告知用户可测试。
## Product Direction
采用“克制、专业、可扫描的运营后台”方向:
- 不做营销站式大图、口号或装饰性卡片。
- PC 端以高效扫描、批量查看、筛选、审批、复核和导出为核心。
- 移动/企业微信端以快速提交、补材料、查状态、处理待办和消息直达为核心。
- 视觉要有品牌感和统一性,但不牺牲信息密度、可读性和业务判断效率。
## Scope
本任务实施 Wave 0 + Wave 1 + Wave 2:
- Wave 0:完成全路由 UX 审计矩阵,记录页面优先级、问题类型和后续分批依据。
- Wave 1:统一 PC 全局框架、侧边栏、Topbar、页面标题区、列表/详情基础视觉基线。
- Wave 2:优化登录、找回密码、工作台、审批中心、报销列表、报销详情和通知中心。
本任务只做前端体验重构与自动化验证;不修改后端权限模型、审批状态机、业务数据结构、生产配置或真实业务数据。
## User Roles
- 普通员工:提交报销/采购/材料、查看进度、补充附件、阅读公告、处理个人待办。
- 审批人/负责人:快速判断待办、查看关键信息、批准/驳回、追踪项目和合同状态。
- 财务/AP:复核票据、报销、付款批次、银行回单、应付台账、导出与审计。
- 管理员:维护组织、用户、审批规则、系统设置、功能开关和审计日志。
## Requirements
### R1. 全局信息架构
- 重新梳理 PC 侧导航分组:工作台、待办/审批、报销、项目/合同、财务、管理。
- 高频入口稳定可见,低频和管理员入口收纳,不让普通员工被管理功能干扰。
- 保留全局搜索、最近访问、通知/待办入口,但减少重复入口和解释文案。
### R2. 统一页面布局
- 建立统一页面骨架:标题区、关键状态区、主操作区、筛选/搜索区、主体区、辅助信息区。
- 列表页、详情页、工作台页、配置页分别形成稳定模式。
- 详情页优先呈现“当前状态、下一步动作、关键明细、附件/审计”,避免把操作埋在页面底部。
### R3. 文案和操作降噪
- 页面文案从功能说明改为用户下一步动作提示。
- 长说明收进帮助、提示、空状态或详情抽屉。
- 当前状态不可用的按钮不强行展示;必须展示时要解释原因。
- 表单字段、按钮、状态标签和错误信息使用一致的业务语言。
### R4. 核心业务流程优化
- 登录/找回密码、工作台、审批中心、报销列表、报销详情、通知中心优先优化。
- 审批页突出“我需要判断什么、依据是什么、点按钮的后果是什么”。
- 报销/票据/附件页面突出状态、重复、缺失、可补材料和审计边界。
- 财务/AP/付款页面突出批量复核、风险提示、下载用途和导出边界。
### R5. 视觉和组件系统
- 统一页面宽度、间距、标题层级、按钮层级、状态色、表格密度、卡片/面板样式。
- 复用或升级 `PageScaffold`、`ListWorkspace`、`SectionPanel`、`StatusBadge`、`MetricCard` 等公共组件。
- 避免一页一个风格,不再新增局部随意样式。
- 交互动效只服务状态变化、加载、确认和导航,不做干扰业务判断的装饰动效。
### R6. 移动和企业微信入口
- 移动端优先保证从企业微信消息或移动首页进入目标业务后,少步骤完成提交、补材料、审批或查看状态。
- 移动详情页不照搬 PC 表格;用分组信息、底部主操作和清晰状态解释。
- 企业微信只做脱敏消息和 OA deeplink,附件和审批动作仍按 OA 权限执行。
### R7. 验证和发布
- 每一批前端改动必须通过 `cd client && npm run typecheck` 和 `cd client && npm run build`。
- 高频页面需要 Playwright 视觉/交互检查,至少覆盖 PC 宽屏、常规桌面和移动视口。
- 面向用户测试前必须完成部署和公网检查。
## Proposed Delivery Waves
### Wave 0:UX 审计与页面矩阵
- 盘点所有 PC/移动路由。
- 按 A/B/C 页面等级记录布局、文案、操作、状态、权限、移动适配问题。
- 产出本任务内的整改矩阵,不新增独立噪音文档。
### Wave 1:全局框架与设计基线
- 重构 AppShell、侧边栏、Topbar、页面骨架、列表/详情/面板公共模式。
- 统一按钮、状态、空状态、加载态、错误态、表格密度和基础间距。
- 这一步是后续全站一致性的前提。
### Wave 2:高频核心页
- 登录/找回密码、工作台、审批中心、报销列表、报销详情、通知中心。
- 目标是显著减少啰嗦、减少点击、清晰展示下一步动作。
### Wave 3:业务详情和移动关键页
- 审批详情、项目详情、合同台账/详情、移动报销、移动审批、移动通知、企微入口页。
### Wave 4:财务和运营页
- 票据池、AP、付款批次、采购/费用、资产、合同归档。
- 重点是表格、筛选、批量操作、附件下载审计和状态解释。
### Wave 5:管理配置页
- 用户、组织、审批规则、流程定义、系统设置、功能开关、审计日志。
- 允许信息密度较高,但统一排版、权限提示和排障语言。
## Suggested MVP for This Task
建议本任务第一轮实施 Wave 0 + Wave 1 + Wave 2,原因:
- 先统一全局框架和高频核心页,能最快改善真实试运行体验。
- Wave 3-5 页面数量多、业务差异大,应在统一组件模式建立后批量套用。
- 一次性改完全部页面风险过高,容易产生回归、遗漏权限状态或视觉不一致。
## Test Plan
- `cd client && npm run typecheck`
- `cd client && npm run build`
- `cd client && npx playwright test tests/e2e/pc-core-workflow-visual-wave2.spec.ts tests/e2e/login-portal-ux.spec.ts --workers=1 --reporter=list`
- `cd client && npx playwright test tests/e2e/r89-mobile-login-navigation.spec.ts --workers=1 --reporter=list`
- `python3 ./.trellis/scripts/task.py validate 05-28-frontend-ux-redesign`
- `node scripts/qa/check-trellis-governance.mjs`
- `git diff --check`
- 若要交给用户公网试用,必须另行部署并完成公网 smoke/视觉检查后再勾选部署验收项。
## Follow-up Plan for Wave 3-5
Wave 3-5 必须记录在本任务中,避免 Wave 0-2 完成后遗忘;但不建议在本任务内同时实施。
推荐排期:
1. Wave 0-2 完成、通过验证并部署公网检查后,立即新建 `frontend-ux-wave3-business-mobile` 任务。
2. Wave 3 完成后,再新建 `frontend-ux-wave4-finance-ops` 任务。
3. Wave 4 完成后,再新建 `frontend-ux-wave5-admin-config` 任务。
拆开的理由:
- Wave 3 涉及业务详情页和移动/企微入口,验证重点是真实业务链路和移动交互。
- Wave 4 涉及财务、付款、AP、票据和资产,验证重点是表格、批量操作、附件下载审计和权限边界。
- Wave 5 涉及管理配置和审计,验证重点是高信息密度、排障语言、权限提示和误操作防护。
- 三类页面的用户角色、交互风险和测试路径不同,强行放在一个大任务里会降低质量。
Wave 3-5 不作为本任务验收阻断项;本任务结束时必须在交付总结中明确下一任务建议和页面清单。
## Acceptance Criteria
- [x] 已完成全路由页面 UX 审计矩阵,并按 A/B/C 级标记优先级。
- [x] 已确立统一页面骨架和公共组件使用方式。
- [x] PC 全局导航、Topbar 和内容容器明显统一,低频入口不干扰高频用户路径。
- [x] Wave 2 高频核心页完成第一轮体验重构。
- [x] Wave 3-5 后续任务计划已记录,且不被误判为本任务已完成范围。
- [x] 页面文案明显降噪,主要页面能直接说明当前状态和下一步动作。
- [x] 桌面与移动关键视口无明显文本溢出、遮挡、错位和不可点击问题。
- [x] `cd client && npm run typecheck` 通过。
- [x] `cd client && npm run build` 通过。
- [x] 高频页面完成 Playwright 截图/交互验证。
- [x] 若部署给用户测试,已完成公网检查并记录证据。
## Out of Scope
- 不改变后端权限模型、审批状态机或业务数据结构。
- 不新增真实业务写入、企业微信写入、生产配置变更或数据库迁移。
- 不把移动端改造成完整 PC 管理后台。
- 不在本任务恢复差旅主线能力。
- 不为了视觉统一删除已有业务能力;如发现业务流程不合理,记录为后续任务。
## Technical Notes
- 重点代码区域:
- `client/src/layouts/AppShellLayout.tsx`
- `client/src/components/navigation/*`
- `client/src/components/patterns/*`
- `client/src/components/common/*`
- `client/src/pages/*`
- 设计依据:
- `frontend-design` skill 决定视觉和交互方向。
- `docs/产品设计规则.md` 只提供 hyb-oa 产品体验事实、用户角色和页面职责。
- `.trellis/spec/hyb-oa/frontend-react-vite-design.md` 和 `.trellis/spec/frontend/index.md` 约束工程实现。
- 已按用户确认实施 Wave 0 + Wave 1 + Wave 2;Wave 3-5 作为后续任务拆分,不计入本任务完成范围。
@@ -0,0 +1,50 @@
{
"checkedAt": "2026-05-28T15:19:18.584Z",
"baseUrl": "https://oa.hyinda.com",
"liveSmokeReady": true,
"liveSmoke": {
"LIVE_SMOKE_ADMIN_IDENTIFIER": true,
"LIVE_SMOKE_ADMIN_PASSWORD": true,
"LIVE_SMOKE_EMPLOYEE_IDENTIFIER": true,
"LIVE_SMOKE_EMPLOYEE_PASSWORD": true
},
"liveSmokeOptional": {
"LIVE_EXPECT_WECOM_STATUS": true,
"LIVE_SMOKE_MANAGER_IDENTIFIER": true,
"LIVE_SMOKE_MANAGER_PASSWORD": true,
"LIVE_SMOKE_FINANCE_IDENTIFIER": true,
"LIVE_SMOKE_FINANCE_PASSWORD": true
},
"liveOcrReady": true,
"liveOcr": {
"userReady": true,
"invoiceSampleReady": true,
"invoiceFileProvided": true,
"invoiceFileReady": true,
"invoiceBatchDirProvided": false,
"invoiceBatchDirReady": false,
"allowDuplicate": true
},
"missing": [],
"publicReady": {
"ok": true,
"status": 200
},
"wecomConfig": {
"ok": true,
"status": 200,
"statusText": "ready",
"entryEnabled": true,
"oauthEnabled": true,
"defaultReturnTo": "/mobile",
"loginUrl": "/api/auth/wecom/login?returnTo=%2Fmobile"
},
"authCapabilities": {
"ok": true,
"status": 200,
"emailDeliveryReady": true,
"emailDeliveryReasons": [],
"emailLoginCodeEnabled": true,
"passwordResetEnabled": true
}
}
@@ -0,0 +1,7 @@
Running 1 test using 1 worker
[live-ocr] {"alias":"SAMPLE-PDF-001","status":"ocr_failed","durationMs":644,"transport":"ui","timings":{"uploadMs":644,"terminalWaitMs":0},"reasonCategory":"duplicate_or_same_invoice","fieldHits":{"invoiceId":false,"invoiceCodeOrNumber":false,"sellerName":false,"totalAmount":false,"invoiceDate":false}}
✓ 1 tests/e2e/manual/live-invoice-ocr.spec.ts:8:1 › 手工 live 腾讯 OCR 辅助复核:真实 VAT 发票上传后进入可处理状态 (5.9s)
1 passed (10.3s)
@@ -0,0 +1,14 @@
>>> 探活 live /ready: https://oa.hyinda.com/ready
>>> 执行 live smoke Playwright
> [email protected] test:e2e:live
> playwright test tests/e2e/live-smoke.spec.ts
Running 3 tests using 1 worker
✓ 1 tests/e2e/live-smoke.spec.ts:43:3 › live smoke › 公网基础探活与企微配置状态 (2.9s)
✓ 2 tests/e2e/live-smoke.spec.ts:64:3 › live smoke › 管理员只读网页 smoke (11.9s)
✓ 3 tests/e2e/live-smoke.spec.ts:106:3 › live smoke › 员工只读网页 smoke (10.3s)
3 passed (28.5s)
@@ -0,0 +1 @@
[public-bundle-copy] base=https://oa.hyinda.com scanned=91 focused=2 findings=0
@@ -0,0 +1,12 @@
{
"issueId": null,
"baseURL": "https://oa.hyinda.com",
"commit": "1f13dd9",
"checkedAt": "2026-05-28T15:20:10.205Z",
"status": "PASS",
"mode": "live-smoke-readiness",
"reportDir": ".trellis/tasks/05-28-frontend-ux-redesign/research/postdeploy-20260528-wave0-2-public/live-smoke-readiness",
"reportRef": ".trellis/tasks/05-28-frontend-ux-redesign/research/postdeploy-20260528-wave0-2-public/live-smoke-readiness",
"groups": null,
"notes": "公网 /ready、public bundle、pending OCR、live smoke 和真实 OCR 可用性检查;缺少凭据或样票时按脚本规则记录 SKIP。"
}
@@ -0,0 +1,129 @@
{
"mode": "oaops1-authenticated-readonly-probe",
"status": "PASS",
"baseUrl": "https://oa.hyinda.com",
"checkedAt": "2026-05-28T15:23:36.113Z",
"readOnly": true,
"duplicateRoleChecks": "employee/finance/payment/AP are covered by oa-only-smoke-readiness to avoid repeated login throttling",
"roles": {
"admin": {
"status": "PASS",
"reason": null,
"maskedIdentifier": "sm***om"
}
},
"checks": [
{
"id": "portal",
"role": "admin",
"path": "/api/portal/home",
"domain": "portal",
"status": "PASS",
"httpStatus": 200,
"summary": {}
},
{
"id": "workflow-tasks",
"role": "admin",
"path": "/api/workflow/tasks?page=1&pageSize=5",
"domain": "approval",
"status": "PASS",
"httpStatus": 200,
"summary": {
"itemCount": 0,
"total": 0
}
},
{
"id": "reimbursements",
"role": "admin",
"path": "/api/reimbursements?page=1&pageSize=5",
"domain": "reimbursement",
"status": "PASS",
"httpStatus": 200,
"summary": {
"itemCount": 5,
"total": 33
}
},
{
"id": "reimbursement-summary",
"role": "admin",
"path": "/api/reimbursements/stats/summary",
"domain": "reimbursement",
"status": "PASS",
"httpStatus": 200,
"summary": {
"totalCount": 33,
"avgProcessingDays": 0
}
},
{
"id": "invoice-pool",
"role": "admin",
"path": "/api/invoice-pool?page=1&pageSize=5",
"domain": "invoice_ocr",
"status": "PASS",
"httpStatus": 200,
"summary": {
"itemCount": 5,
"total": 46
}
},
{
"id": "purchase-requests",
"role": "admin",
"path": "/api/purchase-requests?page=1&pageSize=5",
"domain": "procurement",
"status": "PASS",
"httpStatus": 200,
"summary": {
"itemCount": 0,
"total": 0
}
},
{
"id": "contracts-registry",
"role": "admin",
"path": "/api/contracts-registry",
"domain": "contracts",
"status": "PASS",
"httpStatus": 200,
"summary": {
"itemCount": 6,
"total": null
}
},
{
"id": "projects",
"role": "admin",
"path": "/api/projects?page=1&pageSize=5",
"domain": "projects",
"status": "PASS",
"httpStatus": 200,
"summary": {
"itemCount": 4,
"total": null
}
},
{
"id": "audit-logs",
"role": "admin",
"path": "/api/audit-logs?page=1&pageSize=5",
"domain": "audit",
"status": "PASS",
"httpStatus": 200,
"summary": {
"itemCount": 5,
"total": 2178
}
}
],
"boundaries": {
"noBusinessWrites": true,
"noExportsByDefault": true,
"noFileUploads": true,
"noWeComWrites": true,
"noRealPaymentExecution": true
}
}
@@ -0,0 +1,129 @@
{
"mode": "oaops1-authenticated-readonly-probe",
"status": "PASS",
"baseUrl": "https://oa.hyinda.com",
"checkedAt": "2026-05-28T15:23:36.113Z",
"readOnly": true,
"duplicateRoleChecks": "employee/finance/payment/AP are covered by oa-only-smoke-readiness to avoid repeated login throttling",
"roles": {
"admin": {
"status": "PASS",
"reason": null,
"maskedIdentifier": "sm***om"
}
},
"checks": [
{
"id": "portal",
"role": "admin",
"path": "/api/portal/home",
"domain": "portal",
"status": "PASS",
"httpStatus": 200,
"summary": {}
},
{
"id": "workflow-tasks",
"role": "admin",
"path": "/api/workflow/tasks?page=1&pageSize=5",
"domain": "approval",
"status": "PASS",
"httpStatus": 200,
"summary": {
"itemCount": 0,
"total": 0
}
},
{
"id": "reimbursements",
"role": "admin",
"path": "/api/reimbursements?page=1&pageSize=5",
"domain": "reimbursement",
"status": "PASS",
"httpStatus": 200,
"summary": {
"itemCount": 5,
"total": 33
}
},
{
"id": "reimbursement-summary",
"role": "admin",
"path": "/api/reimbursements/stats/summary",
"domain": "reimbursement",
"status": "PASS",
"httpStatus": 200,
"summary": {
"totalCount": 33,
"avgProcessingDays": 0
}
},
{
"id": "invoice-pool",
"role": "admin",
"path": "/api/invoice-pool?page=1&pageSize=5",
"domain": "invoice_ocr",
"status": "PASS",
"httpStatus": 200,
"summary": {
"itemCount": 5,
"total": 46
}
},
{
"id": "purchase-requests",
"role": "admin",
"path": "/api/purchase-requests?page=1&pageSize=5",
"domain": "procurement",
"status": "PASS",
"httpStatus": 200,
"summary": {
"itemCount": 0,
"total": 0
}
},
{
"id": "contracts-registry",
"role": "admin",
"path": "/api/contracts-registry",
"domain": "contracts",
"status": "PASS",
"httpStatus": 200,
"summary": {
"itemCount": 6,
"total": null
}
},
{
"id": "projects",
"role": "admin",
"path": "/api/projects?page=1&pageSize=5",
"domain": "projects",
"status": "PASS",
"httpStatus": 200,
"summary": {
"itemCount": 4,
"total": null
}
},
{
"id": "audit-logs",
"role": "admin",
"path": "/api/audit-logs?page=1&pageSize=5",
"domain": "audit",
"status": "PASS",
"httpStatus": 200,
"summary": {
"itemCount": 5,
"total": 2178
}
}
],
"boundaries": {
"noBusinessWrites": true,
"noExportsByDefault": true,
"noFileUploads": true,
"noWeComWrites": true,
"noRealPaymentExecution": true
}
}
@@ -0,0 +1,9 @@
{
"name": "authenticated-readonly-probe",
"status": "PASS",
"kind": "finding_allowed",
"code": 0,
"detail": "ok",
"logRef": ".trellis/tasks/05-28-frontend-ux-redesign/research/postdeploy-20260528-wave0-2-public/oa-real-use-daily/authenticated-readonly-probe.log",
"checkedAt": "2026-05-28T15:23:36.221Z"
}
@@ -0,0 +1,64 @@
{
"checkedAt": "2026-05-28T15:22:29.525Z",
"baseUrl": "https://oa.hyinda.com",
"status": "PASS",
"readOnly": true,
"results": [
{
"flagKey": "payment_batch_v1",
"expectedState": "force_on",
"status": "PASS",
"item": {
"flagKey": "payment_batch_v1",
"overrideMode": "force_on",
"rolloutType": "global",
"version": 17,
"envEnabled": true,
"effectiveEnabled": true,
"effectiveReason": "flag_force_on_global"
}
},
{
"flagKey": "erp_sync",
"expectedState": "force_off",
"status": "PASS",
"item": {
"flagKey": "erp_sync",
"overrideMode": "force_off",
"rolloutType": "global",
"version": 2,
"envEnabled": true,
"effectiveEnabled": false,
"effectiveReason": "flag_force_off"
}
},
{
"flagKey": "attendance_v1",
"expectedState": "disabled",
"status": "PASS",
"item": {
"flagKey": "attendance_v1",
"overrideMode": "force_on",
"rolloutType": "global",
"version": 84,
"envEnabled": false,
"effectiveEnabled": false,
"effectiveReason": "env_hard_off"
}
},
{
"flagKey": "attendance_unified_v1",
"expectedState": "enabled",
"status": "PASS",
"item": {
"flagKey": "attendance_unified_v1",
"overrideMode": "force_on",
"rolloutType": "global",
"version": 82,
"envEnabled": true,
"effectiveEnabled": true,
"effectiveReason": "flag_force_on_global"
}
}
]
}
@@ -0,0 +1,9 @@
{
"name": "live-feature-flags",
"status": "PASS",
"kind": "finding_allowed",
"code": 0,
"detail": "ok",
"logRef": ".trellis/tasks/05-28-frontend-ux-redesign/research/postdeploy-20260528-wave0-2-public/oa-real-use-daily/live-feature-flags.log",
"checkedAt": "2026-05-28T15:22:29.595Z"
}
@@ -0,0 +1,45 @@
>>> [live-readiness] 环境与公网入口检查
[live-smoke-env] base=https://oa.hyinda.com
[live-smoke-env] publicReady=true status=200
[live-smoke-env] wecom=ready entry=true oauth=true
[live-smoke-env] authCapabilities=true emailDelivery=true
[live-smoke-env] liveSmokeReady=true
[live-smoke-env] liveOcrReady=true
>>> [live-readiness] /ready: https://oa.hyinda.com
>>> [live-readiness] 公网资源文案扫描
[public-bundle-copy] base=https://oa.hyinda.com scanned=91 focused=2 findings=0
>>> [live-readiness] pending OCR 扫描
[pending-ocr][SKIP] missing DATABASE_URL
>>> [live-readiness] 执行公网 live smoke
>>> 探活 live /ready: https://oa.hyinda.com/ready
>>> 执行 live smoke Playwright
> [email protected] test:e2e:live
> playwright test tests/e2e/live-smoke.spec.ts
Running 3 tests using 1 worker
(node:3307181) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
(node:3307181) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
✓ 1 tests/e2e/live-smoke.spec.ts:43:3 › live smoke › 公网基础探活与企微配置状态 (3.5s)
✓ 2 tests/e2e/live-smoke.spec.ts:64:3 › live smoke › 管理员只读网页 smoke (11.8s)
✓ 3 tests/e2e/live-smoke.spec.ts:106:3 › live smoke › 员工只读网页 smoke (11.1s)
3 passed (29.8s)
>>> [live-readiness] 执行真实 OCR 样本复核
Running 1 test using 1 worker
(node:3308026) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
(node:3308026) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
[live-ocr] {"alias":"SAMPLE-PDF-001","status":"ocr_failed","durationMs":949,"transport":"ui","timings":{"uploadMs":949,"terminalWaitMs":0},"reasonCategory":"duplicate_or_same_invoice","fieldHits":{"invoiceId":false,"invoiceCodeOrNumber":false,"sellerName":false,"totalAmount":false,"invoiceDate":false}}
✓ 1 tests/e2e/manual/live-invoice-ocr.spec.ts:8:1 › 手工 live 腾讯 OCR 辅助复核:真实 VAT 发票上传后进入可处理状态 (6.5s)
1 passed (10.2s)
[live-readiness][PASS] readiness 检查完成
[live-readiness] report=.trellis/tasks/05-28-frontend-ux-redesign/research/postdeploy-20260528-wave0-2-public/oa-real-use-daily/live-smoke-readiness
@@ -0,0 +1,9 @@
{
"name": "live-readiness",
"status": "PASS",
"kind": "finding_allowed",
"code": 0,
"detail": "ok",
"logRef": ".trellis/tasks/05-28-frontend-ux-redesign/research/postdeploy-20260528-wave0-2-public/oa-real-use-daily/live-readiness.log",
"checkedAt": "2026-05-28T15:21:18.716Z"
}
@@ -0,0 +1,50 @@
{
"checkedAt": "2026-05-28T15:20:25.663Z",
"baseUrl": "https://oa.hyinda.com",
"liveSmokeReady": true,
"liveSmoke": {
"LIVE_SMOKE_ADMIN_IDENTIFIER": true,
"LIVE_SMOKE_ADMIN_PASSWORD": true,
"LIVE_SMOKE_EMPLOYEE_IDENTIFIER": true,
"LIVE_SMOKE_EMPLOYEE_PASSWORD": true
},
"liveSmokeOptional": {
"LIVE_EXPECT_WECOM_STATUS": true,
"LIVE_SMOKE_MANAGER_IDENTIFIER": true,
"LIVE_SMOKE_MANAGER_PASSWORD": true,
"LIVE_SMOKE_FINANCE_IDENTIFIER": true,
"LIVE_SMOKE_FINANCE_PASSWORD": true
},
"liveOcrReady": true,
"liveOcr": {
"userReady": true,
"invoiceSampleReady": true,
"invoiceFileProvided": true,
"invoiceFileReady": true,
"invoiceBatchDirProvided": false,
"invoiceBatchDirReady": false,
"allowDuplicate": true
},
"missing": [],
"publicReady": {
"ok": true,
"status": 200
},
"wecomConfig": {
"ok": true,
"status": 200,
"statusText": "ready",
"entryEnabled": true,
"oauthEnabled": true,
"defaultReturnTo": "/mobile",
"loginUrl": "/api/auth/wecom/login?returnTo=%2Fmobile"
},
"authCapabilities": {
"ok": true,
"status": 200,
"emailDeliveryReady": true,
"emailDeliveryReasons": [],
"emailLoginCodeEnabled": true,
"passwordResetEnabled": true
}
}
@@ -0,0 +1,9 @@
{
"name": "live-smoke-env",
"status": "PASS",
"kind": "finding_allowed",
"code": 0,
"detail": "ok",
"logRef": ".trellis/tasks/05-28-frontend-ux-redesign/research/postdeploy-20260528-wave0-2-public/oa-real-use-daily/live-smoke-env.log",
"checkedAt": "2026-05-28T15:20:25.750Z"
}
@@ -0,0 +1,50 @@
{
"checkedAt": "2026-05-28T15:20:26.250Z",
"baseUrl": "https://oa.hyinda.com",
"liveSmokeReady": true,
"liveSmoke": {
"LIVE_SMOKE_ADMIN_IDENTIFIER": true,
"LIVE_SMOKE_ADMIN_PASSWORD": true,
"LIVE_SMOKE_EMPLOYEE_IDENTIFIER": true,
"LIVE_SMOKE_EMPLOYEE_PASSWORD": true
},
"liveSmokeOptional": {
"LIVE_EXPECT_WECOM_STATUS": true,
"LIVE_SMOKE_MANAGER_IDENTIFIER": true,
"LIVE_SMOKE_MANAGER_PASSWORD": true,
"LIVE_SMOKE_FINANCE_IDENTIFIER": true,
"LIVE_SMOKE_FINANCE_PASSWORD": true
},
"liveOcrReady": true,
"liveOcr": {
"userReady": true,
"invoiceSampleReady": true,
"invoiceFileProvided": true,
"invoiceFileReady": true,
"invoiceBatchDirProvided": false,
"invoiceBatchDirReady": false,
"allowDuplicate": true
},
"missing": [],
"publicReady": {
"ok": true,
"status": 200
},
"wecomConfig": {
"ok": true,
"status": 200,
"statusText": "ready",
"entryEnabled": true,
"oauthEnabled": true,
"defaultReturnTo": "/mobile",
"loginUrl": "/api/auth/wecom/login?returnTo=%2Fmobile"
},
"authCapabilities": {
"ok": true,
"status": 200,
"emailDeliveryReady": true,
"emailDeliveryReasons": [],
"emailLoginCodeEnabled": true,
"passwordResetEnabled": true
}
}
@@ -0,0 +1,7 @@
Running 1 test using 1 worker
[live-ocr] {"alias":"SAMPLE-PDF-001","status":"ocr_failed","durationMs":949,"transport":"ui","timings":{"uploadMs":949,"terminalWaitMs":0},"reasonCategory":"duplicate_or_same_invoice","fieldHits":{"invoiceId":false,"invoiceCodeOrNumber":false,"sellerName":false,"totalAmount":false,"invoiceDate":false}}
✓ 1 tests/e2e/manual/live-invoice-ocr.spec.ts:8:1 › 手工 live 腾讯 OCR 辅助复核:真实 VAT 发票上传后进入可处理状态 (6.5s)
1 passed (10.2s)
@@ -0,0 +1,14 @@
>>> 探活 live /ready: https://oa.hyinda.com/ready
>>> 执行 live smoke Playwright
> [email protected] test:e2e:live
> playwright test tests/e2e/live-smoke.spec.ts
Running 3 tests using 1 worker
✓ 1 tests/e2e/live-smoke.spec.ts:43:3 › live smoke › 公网基础探活与企微配置状态 (3.5s)
✓ 2 tests/e2e/live-smoke.spec.ts:64:3 › live smoke › 管理员只读网页 smoke (11.8s)
✓ 3 tests/e2e/live-smoke.spec.ts:106:3 › live smoke › 员工只读网页 smoke (11.1s)
3 passed (29.8s)
@@ -0,0 +1 @@
[public-bundle-copy] base=https://oa.hyinda.com scanned=91 focused=2 findings=0
@@ -0,0 +1,12 @@
{
"issueId": "OAOPS1",
"baseURL": "https://oa.hyinda.com",
"commit": "1f13dd9",
"checkedAt": "2026-05-28T15:21:18.642Z",
"status": "PASS",
"mode": "live-smoke-readiness",
"reportDir": ".trellis/tasks/05-28-frontend-ux-redesign/research/postdeploy-20260528-wave0-2-public/oa-real-use-daily/live-smoke-readiness",
"reportRef": ".trellis/tasks/05-28-frontend-ux-redesign/research/postdeploy-20260528-wave0-2-public/oa-real-use-daily/live-smoke-readiness",
"groups": null,
"notes": "公网 /ready、public bundle、pending OCR、live smoke 和真实 OCR 可用性检查;缺少凭据或样票时按脚本规则记录 SKIP。"
}
@@ -0,0 +1,20 @@
# OAOPS1 每日人工收口清单
## 员工
- 真实报销:上传 PDF 发票,补充必要附件,提交 OA 审批。
- 采购/费用:在 OA 创建申请,关联项目;如涉及合同,关联或补录合同台账。
- 合同:只录入合同摘要、编号、负责人、项目、金额、线下归档位置;涉密合同正文、敏感报价和成本细节不上传公网 OA。
## 审批人
- 从 OA 待办进入详情,确认报销、采购、合同或项目上下文后再审批。
- 退回时写清需要补充的材料,避免线下口头说明替代系统记录。
## 出纳/财务
- 审批通过后创建付款批次,线下银行渠道付款。
- 上传银行回单附件后再在 OA 标记已付款。
- 每日抽查 AP 台账、报销导出、票据状态和付款批次状态。
## 管理员
- 查看本报告 summary.json 和人工问题登记。
- 阻断项当天建修复 Issue CSV 或暂停对应流程;非阻断体验问题进入反馈池。
- 确认 payment_batch_v1=force_on,erp_sync=force_off;图片 OCR、预算、ERP、银行流水导入、真实支付直连继续冻结。
@@ -0,0 +1,10 @@
Running 1 test using 1 worker
(node:3312468) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
(node:3312468) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
✓ 1 tests/e2e/r124-mobile-entry-matrix.spec.ts:486:3 › R124 企业微信移动入口矩阵 › 底部入口、我的二级入口、PDF 上传和草稿删除不回退到 PC 体验 (14.0s)
1 passed (17.5s)
@@ -0,0 +1,9 @@
{
"name": "mobile-entry-matrix",
"status": "PASS",
"kind": "finding_allowed",
"code": 0,
"detail": "ok",
"logRef": ".trellis/tasks/05-28-frontend-ux-redesign/research/postdeploy-20260528-wave0-2-public/oa-real-use-daily/mobile-entry-matrix.log",
"checkedAt": "2026-05-28T15:23:59.908Z"
}
@@ -0,0 +1,392 @@
{
"issueId": "OSMOKE1",
"mode": "oa-only-smoke-readiness",
"status": "PASS_OA_ONLY_SMOKE_READY",
"conclusion": "PASS_OA_ONLY_SMOKE_READY",
"baseUrl": "https://oa.hyinda.com",
"checkedAt": "2026-05-28T15:21:23.528Z",
"businessReadOnly": true,
"readOnlyNote": "performs login/readiness probes only; no business data writes and no WeCom writes",
"privacy": "role/masked identifier/status/counts only; no password/token/secret/full email/full phone",
"targets": [
{
"key": "admin",
"expectedRoles": [
"admin",
"employee"
],
"maskedIdentifier": "sm***@***.com",
"login": {
"status": "PASS",
"reason": null,
"code": null
},
"roleMatch": "PASS",
"probes": [
{
"id": "me",
"status": "PASS",
"httpStatus": 200,
"summary": {
"status": "active",
"notificationPushEnabled": true,
"roleCodes": [
"admin",
"employee"
]
}
},
{
"id": "portal",
"status": "PASS",
"httpStatus": 200,
"summary": {}
},
{
"id": "assets",
"status": "PASS",
"httpStatus": 200,
"summary": {
"itemCount": 7,
"total": null
}
},
{
"id": "users",
"status": "PASS",
"httpStatus": 200,
"summary": {
"itemCount": 1,
"total": 13
}
}
],
"actualRoles": [
"admin",
"employee"
],
"missingRoles": []
},
{
"key": "employee",
"expectedRoles": [
"employee"
],
"maskedIdentifier": "sm***@***.com",
"login": {
"status": "PASS",
"reason": null,
"code": null
},
"roleMatch": "PASS",
"probes": [
{
"id": "me",
"status": "PASS",
"httpStatus": 200,
"summary": {
"status": "active",
"notificationPushEnabled": true,
"roleCodes": [
"employee"
]
}
},
{
"id": "portal",
"status": "PASS",
"httpStatus": 200,
"summary": {}
},
{
"id": "my-assets",
"status": "PASS",
"httpStatus": 200,
"summary": {
"itemCount": 0,
"total": null
}
}
],
"actualRoles": [
"employee"
],
"missingRoles": []
},
{
"key": "manager",
"expectedRoles": [
"manager",
"employee"
],
"maskedIdentifier": "sm***@***.com",
"login": {
"status": "PASS",
"reason": null,
"code": null
},
"roleMatch": "PASS",
"probes": [
{
"id": "me",
"status": "PASS",
"httpStatus": 200,
"summary": {
"status": "active",
"notificationPushEnabled": true,
"roleCodes": [
"employee",
"manager"
]
}
},
{
"id": "portal",
"status": "PASS",
"httpStatus": 200,
"summary": {}
},
{
"id": "workflow",
"status": "PASS",
"httpStatus": 200,
"summary": {
"itemCount": 1,
"total": 1
}
}
],
"actualRoles": [
"employee",
"manager"
],
"missingRoles": []
},
{
"key": "ceo",
"expectedRoles": [
"ceo",
"employee"
],
"maskedIdentifier": "sm***@***.com",
"login": {
"status": "PASS",
"reason": null,
"code": null
},
"roleMatch": "PASS",
"probes": [
{
"id": "me",
"status": "PASS",
"httpStatus": 200,
"summary": {
"status": "active",
"notificationPushEnabled": true,
"roleCodes": [
"ceo",
"employee"
]
}
},
{
"id": "portal",
"status": "PASS",
"httpStatus": 200,
"summary": {}
},
{
"id": "dashboard",
"status": "PASS",
"httpStatus": 200,
"summary": {
"totalCount": 33,
"paidCount": 8,
"reviewingCount": 3
}
}
],
"actualRoles": [
"ceo",
"employee"
],
"missingRoles": []
},
{
"key": "finance",
"expectedRoles": [
"finance",
"employee"
],
"maskedIdentifier": "sm***@***.com",
"login": {
"status": "PASS",
"reason": null,
"code": null
},
"roleMatch": "PASS",
"probes": [
{
"id": "me",
"status": "PASS",
"httpStatus": 200,
"summary": {
"status": "active",
"notificationPushEnabled": true,
"roleCodes": [
"employee",
"finance"
]
}
},
{
"id": "portal",
"status": "PASS",
"httpStatus": 200,
"summary": {}
},
{
"id": "payment-batches",
"status": "PASS",
"httpStatus": 200,
"summary": {
"itemCount": 9,
"total": null
}
},
{
"id": "ap-ledger",
"status": "PASS",
"httpStatus": 200,
"summary": {
"itemCount": 1,
"total": 33
}
}
],
"actualRoles": [
"employee",
"finance"
],
"missingRoles": []
},
{
"key": "finance_mgr",
"expectedRoles": [
"finance",
"finance_mgr",
"employee"
],
"maskedIdentifier": "sm***@***.com",
"login": {
"status": "PASS",
"reason": null,
"code": null
},
"roleMatch": "PASS",
"probes": [
{
"id": "me",
"status": "PASS",
"httpStatus": 200,
"summary": {
"status": "active",
"notificationPushEnabled": true,
"roleCodes": [
"employee",
"finance",
"finance_mgr"
]
}
},
{
"id": "portal",
"status": "PASS",
"httpStatus": 200,
"summary": {}
},
{
"id": "payment-batches",
"status": "PASS",
"httpStatus": 200,
"summary": {
"itemCount": 9,
"total": null
}
},
{
"id": "ap-ledger",
"status": "PASS",
"httpStatus": 200,
"summary": {
"itemCount": 1,
"total": 33
}
}
],
"actualRoles": [
"employee",
"finance",
"finance_mgr"
],
"missingRoles": []
},
{
"key": "hr",
"expectedRoles": [
"hr",
"employee"
],
"maskedIdentifier": "sm***@***.com",
"login": {
"status": "PASS",
"reason": null,
"code": null
},
"roleMatch": "PASS",
"probes": [
{
"id": "me",
"status": "PASS",
"httpStatus": 200,
"summary": {
"status": "active",
"notificationPushEnabled": true,
"roleCodes": [
"employee",
"hr"
]
}
},
{
"id": "portal",
"status": "PASS",
"httpStatus": 200,
"summary": {}
},
{
"id": "wecom-archive",
"status": "PASS",
"httpStatus": 200,
"summary": {
"itemCount": 1,
"total": 9
}
}
],
"actualRoles": [
"employee",
"hr"
],
"missingRoles": []
}
],
"findings": [],
"boundaries": {
"noBusinessWrites": true,
"noWeComWrites": true,
"noPasswordOutput": true,
"noRealEmployeeIdentifiers": true
}
}
[oa-only-smoke-readiness] report=.trellis/tasks/05-28-frontend-ux-redesign/research/postdeploy-20260528-wave0-2-public/oa-real-use-daily/oa-only-smoke-readiness/summary.json
@@ -0,0 +1,9 @@
{
"name": "oa-only-smoke-readiness",
"status": "PASS",
"kind": "finding_allowed",
"code": 0,
"detail": "ok",
"logRef": ".trellis/tasks/05-28-frontend-ux-redesign/research/postdeploy-20260528-wave0-2-public/oa-real-use-daily/oa-only-smoke-readiness.log",
"checkedAt": "2026-05-28T15:21:23.683Z"
}
@@ -0,0 +1,391 @@
{
"issueId": "OSMOKE1",
"mode": "oa-only-smoke-readiness",
"status": "PASS_OA_ONLY_SMOKE_READY",
"conclusion": "PASS_OA_ONLY_SMOKE_READY",
"baseUrl": "https://oa.hyinda.com",
"checkedAt": "2026-05-28T15:21:23.528Z",
"businessReadOnly": true,
"readOnlyNote": "performs login/readiness probes only; no business data writes and no WeCom writes",
"privacy": "role/masked identifier/status/counts only; no password/token/secret/full email/full phone",
"targets": [
{
"key": "admin",
"expectedRoles": [
"admin",
"employee"
],
"maskedIdentifier": "sm***@***.com",
"login": {
"status": "PASS",
"reason": null,
"code": null
},
"roleMatch": "PASS",
"probes": [
{
"id": "me",
"status": "PASS",
"httpStatus": 200,
"summary": {
"status": "active",
"notificationPushEnabled": true,
"roleCodes": [
"admin",
"employee"
]
}
},
{
"id": "portal",
"status": "PASS",
"httpStatus": 200,
"summary": {}
},
{
"id": "assets",
"status": "PASS",
"httpStatus": 200,
"summary": {
"itemCount": 7,
"total": null
}
},
{
"id": "users",
"status": "PASS",
"httpStatus": 200,
"summary": {
"itemCount": 1,
"total": 13
}
}
],
"actualRoles": [
"admin",
"employee"
],
"missingRoles": []
},
{
"key": "employee",
"expectedRoles": [
"employee"
],
"maskedIdentifier": "sm***@***.com",
"login": {
"status": "PASS",
"reason": null,
"code": null
},
"roleMatch": "PASS",
"probes": [
{
"id": "me",
"status": "PASS",
"httpStatus": 200,
"summary": {
"status": "active",
"notificationPushEnabled": true,
"roleCodes": [
"employee"
]
}
},
{
"id": "portal",
"status": "PASS",
"httpStatus": 200,
"summary": {}
},
{
"id": "my-assets",
"status": "PASS",
"httpStatus": 200,
"summary": {
"itemCount": 0,
"total": null
}
}
],
"actualRoles": [
"employee"
],
"missingRoles": []
},
{
"key": "manager",
"expectedRoles": [
"manager",
"employee"
],
"maskedIdentifier": "sm***@***.com",
"login": {
"status": "PASS",
"reason": null,
"code": null
},
"roleMatch": "PASS",
"probes": [
{
"id": "me",
"status": "PASS",
"httpStatus": 200,
"summary": {
"status": "active",
"notificationPushEnabled": true,
"roleCodes": [
"employee",
"manager"
]
}
},
{
"id": "portal",
"status": "PASS",
"httpStatus": 200,
"summary": {}
},
{
"id": "workflow",
"status": "PASS",
"httpStatus": 200,
"summary": {
"itemCount": 1,
"total": 1
}
}
],
"actualRoles": [
"employee",
"manager"
],
"missingRoles": []
},
{
"key": "ceo",
"expectedRoles": [
"ceo",
"employee"
],
"maskedIdentifier": "sm***@***.com",
"login": {
"status": "PASS",
"reason": null,
"code": null
},
"roleMatch": "PASS",
"probes": [
{
"id": "me",
"status": "PASS",
"httpStatus": 200,
"summary": {
"status": "active",
"notificationPushEnabled": true,
"roleCodes": [
"ceo",
"employee"
]
}
},
{
"id": "portal",
"status": "PASS",
"httpStatus": 200,
"summary": {}
},
{
"id": "dashboard",
"status": "PASS",
"httpStatus": 200,
"summary": {
"totalCount": 33,
"paidCount": 8,
"reviewingCount": 3
}
}
],
"actualRoles": [
"ceo",
"employee"
],
"missingRoles": []
},
{
"key": "finance",
"expectedRoles": [
"finance",
"employee"
],
"maskedIdentifier": "sm***@***.com",
"login": {
"status": "PASS",
"reason": null,
"code": null
},
"roleMatch": "PASS",
"probes": [
{
"id": "me",
"status": "PASS",
"httpStatus": 200,
"summary": {
"status": "active",
"notificationPushEnabled": true,
"roleCodes": [
"employee",
"finance"
]
}
},
{
"id": "portal",
"status": "PASS",
"httpStatus": 200,
"summary": {}
},
{
"id": "payment-batches",
"status": "PASS",
"httpStatus": 200,
"summary": {
"itemCount": 9,
"total": null
}
},
{
"id": "ap-ledger",
"status": "PASS",
"httpStatus": 200,
"summary": {
"itemCount": 1,
"total": 33
}
}
],
"actualRoles": [
"employee",
"finance"
],
"missingRoles": []
},
{
"key": "finance_mgr",
"expectedRoles": [
"finance",
"finance_mgr",
"employee"
],
"maskedIdentifier": "sm***@***.com",
"login": {
"status": "PASS",
"reason": null,
"code": null
},
"roleMatch": "PASS",
"probes": [
{
"id": "me",
"status": "PASS",
"httpStatus": 200,
"summary": {
"status": "active",
"notificationPushEnabled": true,
"roleCodes": [
"employee",
"finance",
"finance_mgr"
]
}
},
{
"id": "portal",
"status": "PASS",
"httpStatus": 200,
"summary": {}
},
{
"id": "payment-batches",
"status": "PASS",
"httpStatus": 200,
"summary": {
"itemCount": 9,
"total": null
}
},
{
"id": "ap-ledger",
"status": "PASS",
"httpStatus": 200,
"summary": {
"itemCount": 1,
"total": 33
}
}
],
"actualRoles": [
"employee",
"finance",
"finance_mgr"
],
"missingRoles": []
},
{
"key": "hr",
"expectedRoles": [
"hr",
"employee"
],
"maskedIdentifier": "sm***@***.com",
"login": {
"status": "PASS",
"reason": null,
"code": null
},
"roleMatch": "PASS",
"probes": [
{
"id": "me",
"status": "PASS",
"httpStatus": 200,
"summary": {
"status": "active",
"notificationPushEnabled": true,
"roleCodes": [
"employee",
"hr"
]
}
},
{
"id": "portal",
"status": "PASS",
"httpStatus": 200,
"summary": {}
},
{
"id": "wecom-archive",
"status": "PASS",
"httpStatus": 200,
"summary": {
"itemCount": 1,
"total": 9
}
}
],
"actualRoles": [
"employee",
"hr"
],
"missingRoles": []
}
],
"findings": [],
"boundaries": {
"noBusinessWrites": true,
"noWeComWrites": true,
"noPasswordOutput": true,
"noRealEmployeeIdentifiers": true
}
}
@@ -0,0 +1 @@
{"requestId":"ce1aa1a4-083c-4c82-869a-0de9302fbf9a","data":{"ok":true}}
@@ -0,0 +1,9 @@
{
"name": "public-ready",
"status": "PASS",
"kind": "required",
"code": 0,
"detail": "ok",
"logRef": ".trellis/tasks/05-28-frontend-ux-redesign/research/postdeploy-20260528-wave0-2-public/oa-real-use-daily/public-ready.log",
"checkedAt": "2026-05-28T15:20:25.214Z"
}
@@ -0,0 +1,9 @@
{
"name": "report-privacy-smoke",
"status": "PASS",
"kind": "required",
"code": 0,
"detail": "ok",
"logRef": ".trellis/tasks/05-28-frontend-ux-redesign/research/postdeploy-20260528-wave0-2-public/oa-real-use-daily/report-privacy-smoke.log",
"checkedAt": "2026-05-28T15:25:24.036Z"
}
@@ -0,0 +1,294 @@
{
"issueId": "OAOPS1",
"mode": "oa-real-use-daily",
"status": "PASS_OA_REAL_USE_DAILY_READY",
"conclusion": "PASS_OA_REAL_USE_DAILY_READY",
"baseURL": "https://oa.hyinda.com",
"commit": "1f13dd9",
"checkedAt": "2026-05-28T15:25:23.950Z",
"reportRef": ".trellis/tasks/05-28-frontend-ux-redesign/research/postdeploy-20260528-wave0-2-public/oa-real-use-daily",
"window": {
"durationDays": 7,
"scope": "small-team-all-employees",
"realBusinessDataAllowed": true,
"fixPolicy": "blocking_same_day_issue_contract_nonblocking_feedback_pool"
},
"oaPrimaryDomains": [
"reimbursement_pdf_ocr",
"approval_tasks",
"procurement_expense",
"contracts_registry",
"projects",
"payment_batches",
"bank_receipt_attachments",
"ap_accounting_handoff",
"mobile_wecom_entry",
"audit"
],
"frozenItems": [
"image_ocr",
"complex_bpm",
"budget_control",
"erp_write",
"bank_statement_import",
"real_payment_provider",
"wecom_hr_deep_mirror",
"emergency_takeover",
"recovery_writeback",
"ruleops"
],
"boundaries": {
"defaultReadOnlyAutomation": true,
"manualRealBusinessWritesOnly": true,
"paymentBatchExpected": "force_on",
"erpExpected": "force_off",
"actualPaymentChannel": "offline_bank_channel",
"contractSensitiveFiles": "do_not_upload_confidential_body_sensitive_quote_cost_detail_to_public_oa"
},
"childReports": {
"liveReadiness": {
"status": "PASS",
"reportRef": ".trellis/tasks/05-28-frontend-ux-redesign/research/postdeploy-20260528-wave0-2-public/oa-real-use-daily/live-smoke-readiness/summary.json",
"findingCount": 0,
"openCount": 0
},
"oaOnlySmokeReadiness": {
"status": "PASS_OA_ONLY_SMOKE_READY",
"reportRef": ".trellis/tasks/05-28-frontend-ux-redesign/research/postdeploy-20260528-wave0-2-public/oa-real-use-daily/oa-only-smoke-readiness/summary.json",
"findingCount": 0,
"openCount": 0
}
},
"steps": {
"public-ready": {
"name": "public-ready",
"status": "PASS",
"kind": "required",
"code": 0,
"detail": "ok",
"logRef": ".trellis/tasks/05-28-frontend-ux-redesign/research/postdeploy-20260528-wave0-2-public/oa-real-use-daily/public-ready.log",
"checkedAt": "2026-05-28T15:20:25.214Z"
},
"live-smoke-env": {
"name": "live-smoke-env",
"status": "PASS",
"kind": "finding_allowed",
"code": 0,
"detail": "ok",
"logRef": ".trellis/tasks/05-28-frontend-ux-redesign/research/postdeploy-20260528-wave0-2-public/oa-real-use-daily/live-smoke-env.log",
"checkedAt": "2026-05-28T15:20:25.750Z"
},
"live-readiness": {
"name": "live-readiness",
"status": "PASS",
"kind": "finding_allowed",
"code": 0,
"detail": "ok",
"logRef": ".trellis/tasks/05-28-frontend-ux-redesign/research/postdeploy-20260528-wave0-2-public/oa-real-use-daily/live-readiness.log",
"checkedAt": "2026-05-28T15:21:18.716Z"
},
"oa-only-smoke-readiness": {
"name": "oa-only-smoke-readiness",
"status": "PASS",
"kind": "finding_allowed",
"code": 0,
"detail": "ok",
"logRef": ".trellis/tasks/05-28-frontend-ux-redesign/research/postdeploy-20260528-wave0-2-public/oa-real-use-daily/oa-only-smoke-readiness.log",
"checkedAt": "2026-05-28T15:21:23.683Z"
},
"live-feature-flags": {
"name": "live-feature-flags",
"status": "PASS",
"kind": "finding_allowed",
"code": 0,
"detail": "ok",
"logRef": ".trellis/tasks/05-28-frontend-ux-redesign/research/postdeploy-20260528-wave0-2-public/oa-real-use-daily/live-feature-flags.log",
"checkedAt": "2026-05-28T15:22:29.595Z"
},
"authenticated-readonly-probe": {
"name": "authenticated-readonly-probe",
"status": "PASS",
"kind": "finding_allowed",
"code": 0,
"detail": "ok",
"logRef": ".trellis/tasks/05-28-frontend-ux-redesign/research/postdeploy-20260528-wave0-2-public/oa-real-use-daily/authenticated-readonly-probe.log",
"checkedAt": "2026-05-28T15:23:36.221Z"
},
"mobile-entry-matrix": {
"name": "mobile-entry-matrix",
"status": "PASS",
"kind": "finding_allowed",
"code": 0,
"detail": "ok",
"logRef": ".trellis/tasks/05-28-frontend-ux-redesign/research/postdeploy-20260528-wave0-2-public/oa-real-use-daily/mobile-entry-matrix.log",
"checkedAt": "2026-05-28T15:23:59.908Z"
},
"wecom-rendering-matrix": {
"name": "wecom-rendering-matrix",
"status": "PASS",
"kind": "finding_allowed",
"code": 0,
"detail": "ok",
"logRef": ".trellis/tasks/05-28-frontend-ux-redesign/research/postdeploy-20260528-wave0-2-public/oa-real-use-daily/wecom-rendering-matrix.log",
"checkedAt": "2026-05-28T15:25:09.477Z"
},
"wecom-entry": {
"name": "wecom-entry",
"status": "PASS",
"kind": "finding_allowed",
"code": 0,
"detail": "ok",
"logRef": ".trellis/tasks/05-28-frontend-ux-redesign/research/postdeploy-20260528-wave0-2-public/oa-real-use-daily/wecom-entry.log",
"checkedAt": "2026-05-28T15:25:23.708Z"
},
"report-privacy-smoke": {
"name": "report-privacy-smoke",
"status": "PASS",
"kind": "required",
"code": 0,
"detail": "ok",
"logRef": ".trellis/tasks/05-28-frontend-ux-redesign/research/postdeploy-20260528-wave0-2-public/oa-real-use-daily/report-privacy-smoke.log",
"checkedAt": "2026-05-28T15:25:23.875Z"
}
},
"readonlyProbe": {
"mode": "oaops1-authenticated-readonly-probe",
"status": "PASS",
"baseUrl": "https://oa.hyinda.com",
"checkedAt": "2026-05-28T15:23:36.113Z",
"readOnly": true,
"duplicateRoleChecks": "employee/finance/payment/AP are covered by oa-only-smoke-readiness to avoid repeated login throttling",
"roles": {
"admin": {
"status": "PASS",
"reason": null,
"maskedIdentifier": "sm***om"
}
},
"checks": [
{
"id": "portal",
"role": "admin",
"path": "/api/portal/home",
"domain": "portal",
"status": "PASS",
"httpStatus": 200,
"summary": {}
},
{
"id": "workflow-tasks",
"role": "admin",
"path": "/api/workflow/tasks?page=1&pageSize=5",
"domain": "approval",
"status": "PASS",
"httpStatus": 200,
"summary": {
"itemCount": 0,
"total": 0
}
},
{
"id": "reimbursements",
"role": "admin",
"path": "/api/reimbursements?page=1&pageSize=5",
"domain": "reimbursement",
"status": "PASS",
"httpStatus": 200,
"summary": {
"itemCount": 5,
"total": 33
}
},
{
"id": "reimbursement-summary",
"role": "admin",
"path": "/api/reimbursements/stats/summary",
"domain": "reimbursement",
"status": "PASS",
"httpStatus": 200,
"summary": {
"totalCount": 33,
"avgProcessingDays": 0
}
},
{
"id": "invoice-pool",
"role": "admin",
"path": "/api/invoice-pool?page=1&pageSize=5",
"domain": "invoice_ocr",
"status": "PASS",
"httpStatus": 200,
"summary": {
"itemCount": 5,
"total": 46
}
},
{
"id": "purchase-requests",
"role": "admin",
"path": "/api/purchase-requests?page=1&pageSize=5",
"domain": "procurement",
"status": "PASS",
"httpStatus": 200,
"summary": {
"itemCount": 0,
"total": 0
}
},
{
"id": "contracts-registry",
"role": "admin",
"path": "/api/contracts-registry",
"domain": "contracts",
"status": "PASS",
"httpStatus": 200,
"summary": {
"itemCount": 6,
"total": null
}
},
{
"id": "projects",
"role": "admin",
"path": "/api/projects?page=1&pageSize=5",
"domain": "projects",
"status": "PASS",
"httpStatus": 200,
"summary": {
"itemCount": 4,
"total": null
}
},
{
"id": "audit-logs",
"role": "admin",
"path": "/api/audit-logs?page=1&pageSize=5",
"domain": "audit",
"status": "PASS",
"httpStatus": 200,
"summary": {
"itemCount": 5,
"total": 2178
}
}
],
"boundaries": {
"noBusinessWrites": true,
"noExportsByDefault": true,
"noFileUploads": true,
"noWeComWrites": true,
"noRealPaymentExecution": true
}
},
"findingCount": 0,
"findings": [],
"manualGates": {
"dailyCloseout": "管理员每日查看 summary.json 和 manual-gates.md,阻断即修,非阻断入反馈池。",
"day7Closeout": [
"A:接受 PASS_OA_REAL_USE_READY,进入下一轮体验/效率优化。",
"B:接受 PASS_WITH_FINDINGS,但先处理指定 finding。",
"C:暂停真实使用扩展,先做整改。"
]
},
"privacy": "status/count/report-path summaries only; no phone/email/token/Secret/EncodingAESKey/full external ids/full invoice image/full bank receipt/full contract body/raw payload"
}
@@ -0,0 +1,24 @@
[oaops1] loaded masked live smoke env file
>>> [oaops1] public-ready
[oaops1][PASS] public-ready
>>> [oaops1] live-smoke-env
[oaops1][PASS] live-smoke-env
>>> [oaops1] live-readiness
[oaops1][PASS] live-readiness
>>> [oaops1] oa-only-smoke-readiness
[oaops1][PASS] oa-only-smoke-readiness
[oaops1] login cooldown 65s before live-feature-flags
>>> [oaops1] live-feature-flags
[oaops1][PASS] live-feature-flags
[oaops1] login cooldown 65s before authenticated-readonly-probe
>>> [oaops1] authenticated-readonly-probe
[oaops1][PASS] authenticated-readonly-probe
>>> [oaops1] mobile-entry-matrix
[oaops1][PASS] mobile-entry-matrix
>>> [oaops1] wecom-rendering-matrix
[oaops1][PASS] wecom-rendering-matrix
>>> [oaops1] wecom-entry
[oaops1][PASS] wecom-entry
[oaops1][PASS] report-privacy-smoke
[oaops1][PASS] report-privacy-smoke
[oaops1] report=.trellis/tasks/05-28-frontend-ux-redesign/research/postdeploy-20260528-wave0-2-public/oa-real-use-daily
@@ -0,0 +1,11 @@
Running 2 tests using 1 worker
(node:3314798) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
(node:3314798) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
✓ 1 tests/e2e/wecom-entry.spec.ts:56:1 › E2E 企微入口:普通浏览器下只提示企业微信工作台打开 (2.9s)
✓ 2 tests/e2e/wecom-entry.spec.ts:66:1 › E2E 企微入口:企业微信场景优先显示企微 CTA,并展示未绑定提示 (1.9s)
2 passed (8.5s)
@@ -0,0 +1,9 @@
{
"name": "wecom-entry",
"status": "PASS",
"kind": "finding_allowed",
"code": 0,
"detail": "ok",
"logRef": ".trellis/tasks/05-28-frontend-ux-redesign/research/postdeploy-20260528-wave0-2-public/oa-real-use-daily/wecom-entry.log",
"checkedAt": "2026-05-28T15:25:23.708Z"
}
@@ -0,0 +1,17 @@
Running 8 tests using 1 worker
(node:3313150) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
(node:3313150) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
✓ 1 tests/e2e/r6-wecom-mobile-rendering-hardening.spec.ts:588:7 › WCM6 企业微信多手机移动渲染稳定化 › 320 iPhone 企业微信 › 核心移动入口使用 SVG 图标且跨视口不挤压 (8.1s)
✓ 2 tests/e2e/r6-wecom-mobile-rendering-hardening.spec.ts:588:7 › WCM6 企业微信多手机移动渲染稳定化 › 360 Android 企业微信 › 核心移动入口使用 SVG 图标且跨视口不挤压 (7.7s)
✓ 3 tests/e2e/r6-wecom-mobile-rendering-hardening.spec.ts:588:7 › WCM6 企业微信多手机移动渲染稳定化 › 375 iPhone 企业微信 › 核心移动入口使用 SVG 图标且跨视口不挤压 (6.7s)
✓ 4 tests/e2e/r6-wecom-mobile-rendering-hardening.spec.ts:588:7 › WCM6 企业微信多手机移动渲染稳定化 › 390 iPhone12 企业微信 › 核心移动入口使用 SVG 图标且跨视口不挤压 (8.0s)
✓ 5 tests/e2e/r6-wecom-mobile-rendering-hardening.spec.ts:588:7 › WCM6 企业微信多手机移动渲染稳定化 › 393 Android 企业微信 › 核心移动入口使用 SVG 图标且跨视口不挤压 (7.2s)
✓ 6 tests/e2e/r6-wecom-mobile-rendering-hardening.spec.ts:588:7 › WCM6 企业微信多手机移动渲染稳定化 › 414 iPhone 企业微信 › 核心移动入口使用 SVG 图标且跨视口不挤压 (7.6s)
✓ 7 tests/e2e/r6-wecom-mobile-rendering-hardening.spec.ts:588:7 › WCM6 企业微信多手机移动渲染稳定化 › 430 iPhone 企业微信 › 核心移动入口使用 SVG 图标且跨视口不挤压 (7.8s)
✓ 8 tests/e2e/r6-wecom-mobile-rendering-hardening.spec.ts:604:5 › WCM6 企业微信多手机移动渲染稳定化 › 390 iPhone12 企业微信 字体失败 › Material Symbols 字体失败时核心移动入口仍稳定 (6.8s)
8 passed (1.1m)
@@ -0,0 +1,9 @@
{
"name": "wecom-rendering-matrix",
"status": "PASS",
"kind": "finding_allowed",
"code": 0,
"detail": "ok",
"logRef": ".trellis/tasks/05-28-frontend-ux-redesign/research/postdeploy-20260528-wave0-2-public/oa-real-use-daily/wecom-rendering-matrix.log",
"checkedAt": "2026-05-28T15:25:09.477Z"
}
@@ -0,0 +1,45 @@
# UX Audit Matrix
## Audit Method
- 依据 `client/src/router/index.tsx`、`client/src/features/navigation/schema.ts` 和页面源码盘点。
- 页面分级沿用 `docs/产品设计规则.md`:A 类核心流程、B 类业务运营、C 类管理配置。
- 本轮实施范围为 Wave 0-2;Wave 3-5 只记录后续任务,不作为当前验收完成范围。
## Global Findings
| Area | Finding | Impact | Current Task Action |
| --- | --- | --- | --- |
| PC navigation | 主导航、管理入口、备用模块的分类偏业务历史,财务/项目/合同等用户心智未成组。 | 用户找入口需要读说明,普通员工容易看到过多低频项。 | Wave 1 重排导航分组和侧栏信息。 |
| Topbar | 顶部展示页面组、标题和问候,信息占高较多;搜索按钮不够像命令入口。 | PC 页面有效内容首屏被压缩。 | Wave 1 做紧凑命令栏。 |
| Page headers | `PageHeader`、`WorkspaceHeader`、`WorkbenchHero`、`EntityOverviewHero` 并存,标题/说明/action 的密度不完全一致。 | 页面风格统一度不足。 | Wave 1 统一 header 视觉和行动区。 |
| Cards/panels | 大量页面已复用 `SectionPanel`、`ListWorkspace`,但局部还存在 page-local 卡片和提示块。 | 视觉统一改动需要多点修复。 | Wave 1 先统一 shared pattern class。 |
| Lists/tables | PC 操作列表已有 `ops-*` 规范,但部分空态、错误态和筛选控件仍不一致。 | 审批、报销、通知的扫描效率不稳定。 | Wave 2 聚焦核心列表。 |
| Copy | 部分页面说明偏“模块解释”,不是“下一步动作”。 | 用户需要理解系统设计而非直接完成任务。 | Wave 2 降噪。 |
| Mobile | 移动壳和卡片模式已存在;关键详情页属于 Wave 3。 | 本轮只避免 Wave 2 改动破坏移动入口。 | Wave 3 继续。 |
## Wave 2 Page Audit
| Page | Class | Current Issues | Target Change |
| --- | --- | --- | --- |
| Login | A | 功能完整,但登录方式、企微入口、邮箱验证码和密码入口信息层级仍可更清楚。 | 保留成熟功能,改为更明确的登录方式分区、辅助状态和焦点顺序。 |
| Forgot password | A | 流程可用,但验证码、有效期、密码要求和返回登录的提示仍偏散。 | 强化步骤感和状态提示。 |
| Portal home | A | 信息丰富但首屏模块多,角色摘要、待处理和常用动作可更聚焦。 | 工作台突出“待处理 + 常用动作 + 关键指标”,次要信息收纳。 |
| Workflow tasks | A | 已使用 `ListWorkspace` 和 `ops-*`,但 subtitle 太短,缺少“下一步如何处理”的上下文。 | 提升标题说明、状态指标和空态语义。 |
| Reimbursements | A | 已支持批量、导出、范围、模板,但筛选/范围/导出动作密度偏高。 | 保持功能,优化 header、command bar 和列表扫描字段。 |
| Reimbursement detail | A | 信息完整,分区较多;状态、下一步动作、票据和附件需要更强层级。 | 保留功能,优化 hero 说明、摘要和证据区语义。 |
| Notifications | A | 已改为 inbox 卡片,但返回仪表盘不符合当前主路径,筛选区和未读统计可以更统一。 | 改为通知收件箱工作区,直达业务、全部已读、类型筛选稳定。 |
## Follow-up Page Groups
| Wave | Pages | Reason To Split |
| --- | --- | --- |
| Wave 3 | 审批详情、项目详情、合同台账/详情、移动报销、移动审批、移动通知、企微入口页 | 详情和移动链路要验证业务直达和移动操作,不适合和全局框架同批扩大。 |
| Wave 4 | 票据池、AP、付款批次、采购/费用、资产、合同归档 | 财务运营页涉及批量操作、附件审计、导出和权限边界。 |
| Wave 5 | 用户、组织、审批规则、流程定义、系统设置、功能开关、审计日志 | 管理页信息密度高,重在排障语言和误操作防护。 |
## Design Direction Locked For Implementation
- Tone: 精密、克制、运营后台。
- Differentiation: 所有页面都能先回答“现在是什么状态、我下一步做什么、动作有什么后果”。
- Visual rule: 通过 shared shell、header、command bar、status strip 和 detail surface 统一,而不是每页单独美化。
@@ -0,0 +1,26 @@
{
"id": "frontend-ux-redesign",
"name": "frontend-ux-redesign",
"title": "全站前端体验重构",
"description": "统一 hyb-oa 前端 Wave0-2 体验基线:UX 审计、全局框架和高频核心页。",
"status": "completed",
"dev_type": null,
"scope": "client/frontend-ux-wave0-2",
"package": null,
"priority": "P2",
"creator": "shunli",
"assignee": "shunli",
"createdAt": "2026-05-28",
"completedAt": "2026-05-28",
"branch": null,
"base_branch": "main",
"worktree_path": null,
"commit": null,
"pr_url": null,
"subtasks": [],
"children": [],
"parent": null,
"relatedFiles": [],
"notes": "",
"meta": {}
}