From 0bd9c1ddf7e58a5bc6500d1adb55fcaace9a7e1b Mon Sep 17 00:00:00 2001 From: Albert Date: Sun, 26 Apr 2026 22:10:50 +0800 Subject: [PATCH] feat(space): surface review attention queue --- CLAUDE.md | 2 +- docs/PMFLOW_PRODUCT_DELIVERY_STANDARD.md | 26 +++ docs/architecture/ARCHITECTURE_INVENTORY.md | 4 +- docs/architecture/CODE_REFERENCE_INDEX.md | 1 + frontend/src/api/space.ts | 140 +++++++++++- .../personal-space/PersonalSpacePage.test.tsx | 211 +++++++++++++++++- .../personal-space/PersonalSpacePage.tsx | 9 +- 7 files changed, 385 insertions(+), 8 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 4f376ed9..9f37bc78 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -132,7 +132,7 @@ frontend/ - 页面目录化、Mounted AI Console、Inbox、AI Steward 与 Recipes/Projects/Users 等模块化拆分的详细库存见库存文档。 - 顶层路由已改为 `React.lazy + Suspense`,统一 fallback 使用 `frontend/src/components/layout/LazyBoundary.tsx`;AI Steward / AI Config 页面按 active tab 只渲染当前可见区块,Projects/Users/图表页则把重对话框与图表从同步入口拆出。 - `frontend/src/features/project-launch/` 是新项目启动台独立产品域:`types.ts / useProjectLaunchState.ts` 承载状态合同和真实 API 编排,`mockData.ts` 只在显式 demo 模式下使用;默认路径调用 `POST /pmflow-launch/drafts` 生成可审查草案,确认后调用 `POST /pmflow-launch/drafts/{draft_id}/confirm` 创建真实项目并跳转 `/pmflow-os?projectId=`。`LaunchShell` 桌面端复用 `frontend/src/features/personal-space/components/SpaceChrome.tsx` 的空间左侧栏和顶部条,主画布仍由 `IntentInputPanel / AIProgressCanvas / HumanReviewCanvas / DraftEditCanvas / FailureStatePanel / SuccessTransitionPanel` 承接,移动端仍由 `MobileLaunchLayout` 承接;`frontend/src/pages/ProjectLaunchPage.tsx` 仅做薄入口。 -- `frontend/src/features/personal-space/` 是空间层入口:`PersonalSpacePage.tsx` 承接桌面个人工作面与独立移动单手布局,`frontend/src/api/space.ts` 只读聚合 `projects` summary、PMFlow OS `truth-state`、`execution-summary` 并生成 `nextActionQueue` 我的下一步队列、继续推进、待确认、发布关注、发布后反馈关注、下一轮迭代草稿关注、AI 正在推进、草案箱、最近项目摘要;Space P6 的下一步队列按高风险待确认、发布阻塞/失败证据、handoff consumed 后缺少后续 progress、下一轮 draft、发布后反馈/收入/合规信号排序,点击只跳 `/change-review?projectId=&changeSetId=`、`/execution-bridge?projectId=` 或 `/release-growth?projectId=`;Space P3 的发布关注优先消费 `truth-state.releases` 的 release validation 投影,没有验证投影时才回落 `execution-summary.releaseGate`;Space P4/P5 的发布后反馈/下一轮草稿关注只读消费 `execution-summary.releaseSignals`;不在 Space 写 Project Memory、ProgressEvent、ReleaseValidationRun、FeedbackIngest、RevenueEvent、ComplianceCheckRun、ChangeSet 状态或发布执行。`components/SpaceChrome.tsx` 承接空间层左侧栏、顶部条与空间用量组件,并被 `/project-launch` 桌面首次新建壳复用;`frontend/src/pages/PersonalSpacePage.tsx` 仅做薄入口。 +- `frontend/src/features/personal-space/` 是空间层入口:`PersonalSpacePage.tsx` 承接桌面个人工作面与独立移动单手布局,`frontend/src/api/space.ts` 只读聚合 `projects` summary、PMFlow OS `truth-state`、`execution-summary` 并生成 `nextActionQueue` 我的下一步队列、继续推进、待确认、review attention、发布关注、发布后反馈关注、下一轮迭代草稿关注、AI 正在推进、草案箱、最近项目摘要;Space P6 的下一步队列按高风险待确认、发布阻塞/失败证据、handoff consumed 后缺少后续 progress、下一轮 draft、发布后反馈/收入/合规信号排序;Space P7 新增 `review_attention`,只读消费 `truth-state.reviewerRouting`,按高风险待我审、`request_changes / rejected` 后待处理、`reviewer_missing` warning、普通 pending review 排序进入“我的下一步”,点击只跳 `/change-review?projectId=&changeSetId=`、已有 `/team-layer?projectId=`、`/execution-bridge?projectId=` 或 `/release-growth?projectId=`;Space P3 的发布关注优先消费 `truth-state.releases` 的 release validation 投影,没有验证投影时才回落 `execution-summary.releaseGate`;Space P4/P5 的发布后反馈/下一轮草稿关注只读消费 `execution-summary.releaseSignals`;不在 Space 写 Project Memory、PMFlowApproval、ProgressEvent、ReleaseValidationRun、FeedbackIngest、RevenueEvent、ComplianceCheckRun、ChangeSet 状态或发布执行,不虚构 reviewer、审查意见或待审 ChangeSet。`components/SpaceChrome.tsx` 承接空间层左侧栏、顶部条与空间用量组件,并被 `/project-launch` 桌面首次新建壳复用;`frontend/src/pages/PersonalSpacePage.tsx` 仅做薄入口。 - `frontend/src/features/account-settings/` 是 Account Settings 独立产品域:`AccountSettingsPanel.tsx` 承接 `/me` 桌面设置页、移动端分组列表与 bottom sheet,`frontend/src/api/accountSettings.ts` 定义个人资料、默认语言、AI 回答深度、证据展示偏好和四类通知偏好合同;当前已有账号同步 API fallback、本地 UI 通知消费模型、Release/Growth 偏好 helper、post-release / next change review 偏好 helper,以及 `getAccountReminderConsumption(settings, event)` 提醒强度消费合同,支持 `reminderIntensity=silent|normal|prominent` 与 `evidenceDisplay=compact|expanded`;`releaseGate / failureAlert / pendingConfirmation / executionWriteback` 只影响页面内提醒表达强度和证据展开方式,不代表真实邮件、桌面推送或系统通知中心已接入,也不得隐藏真实业务证据或审计状态,不改 OAuth/JWT/auth core;`frontend/src/features/pmflow-os/pages/AccountPage.tsx` 仅做薄入口并复用既有退出登录 bottom sheet。 - `frontend/src/features/pmflow-os/` 当前承载新的对话驱动前端产品域:`store.tsx` 提供共享 truth store;真实模式通过 `?projectId=` 调用 `frontend/src/api/pmflowOs.ts` 读取后端 `ProjectTruthState` projection,并调用真实 message、`proposed_items` extraction、ChangeItem `accept/edit/defer`、ChangeSet `commit`、Execution Handoff create/consume、execution progress writeback、release validation run create、release feedback ingest、单条 iteration recommendation -> ChangeSet draft 与同组 iteration recommendation 批量 -> ChangeSet draft API,成功后 refetch truth-state / execution-summary;显式 `?demo=1` 才启用 `command_idle -> ai_extracting -> extraction_ready -> review_open -> writeback_success` 本地演示状态机,以及本地 `promoteAcceptedToGraph` 等演示动作。`pages/` 下包含 `CommandCenter / StructuredExtraction / ChangeReview / MemoryWritebackResult / WorkGraph / ExecutionBridge / ReleaseGrowth / TeamLayer / ProjectMemory / Account` 主页面;`TeamLayerPage` 真实模式读取 `state.reviewerRouting` 展示 ready/empty/warning、待审 ChangeSet、来源、风险等级、建议审查人或 fallback role、审查原因、待审 item 数和证据数,动作只跳转 `/change-review?projectId=&changeSetId=`,不自动分派 reviewer;`ExecutionBridgePage` 已支持创建 handoff、运行端领取确认和执行进度回写;`ReleaseGrowthPage` 真实模式展示 `execution-summary.releaseGate`、execution evidence、阻塞原因、下一步动作、`latestValidationRun` 和 `releaseSignals`,可创建 release validation run、摄取发布后 feedback、展示 recommendation quality grouping、从真实 recommendation 单条或同组批量生成下一轮待确认 ChangeSet draft,并消费 Account notification preference 静默强调型提醒,但不隐藏真实证据、审计状态或发布后信号;`ChangeReviewPage` 会展示来自 release feedback recommendation group 的组、反馈、风险、影响和审查优先级,帮助 reviewer 判断为什么生成这一组待确认变更;静态发布/增长原型仅保留在显式 demo 模式;后端只记录 progress/release gate/release validation/release feedback/recommendation draft 元数据,不执行本机命令、真实发布命令、外部支付、真实推送、自动 Project Memory commit 或正式任务创建;`ProjectMemoryPage` 在真实模式消费后端 `truth_nodes + semantic_facts` 的 `projectMemory` 投影,真实模式下 accepted/edited ChangeItem 只有 commit 成功后才进入 Project Memory。`components/` 下除了 `ProjectStageRail / ThreadComposer / ExtractionRail / ChangeGroupPanel / ObjectGraphSection / ContextPacketPanel / ExecutionTimeline / ReleaseGatePanel / TeamFeedPanel` 之外,新增 `ProjectStructuredLoop / PMFlowOsFrame / AccountControls / FeedbackPrimitives` 等行为组件,用来承接项目指挥台、结构化提取、待确认审查、项目记忆写入结果、项目内面包屑、项目切换器、账号退出登录交互,以及 `PageBanner / InlineNotice / HumanDecisionBar / ConfirmDialog / MobileBottomSheet` 这组契约化提示提醒组件;`components/MobileBottomNav.tsx` 是正式应用壳唯一移动底部导航,固定为 `空间 / 指挥台 / 待确认 / 动态 / 我的`。 - 旧登录实现 `frontend/src/components/ui/sign-in.tsx` 与 `frontend/src/components/ui/demo.tsx` 已移除,`frontend/src/components/login/` 是当前唯一登录真源。 diff --git a/docs/PMFLOW_PRODUCT_DELIVERY_STANDARD.md b/docs/PMFLOW_PRODUCT_DELIVERY_STANDARD.md index 190b62e3..b616727e 100644 --- a/docs/PMFLOW_PRODUCT_DELIVERY_STANDARD.md +++ b/docs/PMFLOW_PRODUCT_DELIVERY_STANDARD.md @@ -817,6 +817,32 @@ P8 可以三条 lane 并行,但共享高冲突文件必须由 Main Flow 或 Sh ## 8. 进度台账 +### 2026-04-26 / space-p7-review-attention-queue / implemented + +完成内容: + +- `/space` 的 `nextActionQueue` 新增 `review_attention` 类型,只读消费 `ProjectTruthState.reviewerRouting(status/warningCode/summary/items)`。 +- Team Review 待审事项按“高风险待我审 -> request_changes/rejected 后待处理 -> reviewer missing warning -> 普通 pending review”排序进入“我的下一步”。 +- 高风险、普通待审与 review decision 后待处理项只跳 `/change-review?projectId=&changeSetId=`;reviewer 缺失 warning 只跳已有 `/team-layer?projectId=`。 +- `PersonalSpacePage` 仅补充待审查的队列表达、icon、CTA 与空态文案,不新增模块,不写 ChangeSet、`PMFlowApproval`、ProgressEvent 或 PMFlow OS store。 +- reviewerRouting 为空时保持空态,不虚构 reviewer、审查意见或待审 ChangeSet。 + +涉及文件: + +- `frontend/src/api/space.ts` +- `frontend/src/features/personal-space/PersonalSpacePage.tsx` +- `frontend/src/features/personal-space/PersonalSpacePage.test.tsx` +- `CLAUDE.md` +- `docs/PMFLOW_PRODUCT_DELIVERY_STANDARD.md` +- `docs/architecture/CODE_REFERENCE_INDEX.md` +- `docs/architecture/ARCHITECTURE_INVENTORY.md` + +验证: + +- `cd frontend && npm run test -- src/features/personal-space/PersonalSpacePage.test.tsx` 通过:`3 passed`;仍有既有 React Router v7 future flag warning。 +- `cd frontend && npm run typecheck` 通过。 +- `cd frontend && npm run build` 通过;Vite 仍提示既有 manual chunk circular warning(`ai-steward / sstc / charts / governance` chunk 环),构建成功。 + ### 2026-04-26 / shell-p8-rebaseline-next-plan / done 完成内容: diff --git a/docs/architecture/ARCHITECTURE_INVENTORY.md b/docs/architecture/ARCHITECTURE_INVENTORY.md index a96c8327..c03af65d 100644 --- a/docs/architecture/ARCHITECTURE_INVENTORY.md +++ b/docs/architecture/ARCHITECTURE_INVENTORY.md @@ -195,9 +195,9 @@ - 登录页与营销页仍共享 `frontend/src/components/marketing/` 的部分视觉基线;公开登录页现改由 `frontend/src/components/login/` 目录承载,其中 `LoginExperience.tsx` 负责登录壳层、`LoginWorkflowVisual.tsx` 负责左侧场景、`loginScene.ts` / `loginMath.ts` 拆出展示配置与路径几何、`login.css` 负责登录页专属样式边界,`frontend/src/pages/login/LoginPage.tsx` 继续负责真实鉴权、错误态和 redirect 提示;未登录访问 `/` 会经 `frontend/src/router/index.tsx` 直接重定向到 `/login`,无显式 `redirect` 的成功登录现在默认进入 `/command-center`。 - 前端主包治理已切到“同步首屏最小集 + route/page-section lazy”模式:`frontend/src/router/index.tsx` 保留 `LoginPage / Layout` 这组同步入口,其余后台重页面通过 `React.lazy + Suspense` 懒加载;统一 fallback 在 `frontend/src/components/layout/LazyBoundary.tsx`。P0 公共壳冻结后,大模块 route 归属为:`/space` 归 Space lane,`/project-launch` 是启动层入口,`/pmflow-os` 与 `/command-center / /change-review / /execution-bridge / /release-growth` 归 Main Flow lane,`/me` 归 Account Settings lane,`/settings` 当前仅为 reserved route、尚未实现,新增必须由 Shell / Integration owner 串行处理。`frontend/src/features/personal-space/` 作为空间层入口,通过 `/space` 提供可直接打开的本地预览入口,承载继续推进、待我确认、AI 正在推进、暂存想法、草案箱与新建项目入口。`frontend/src/features/project-launch/` 作为首次进入页 / 新项目启动台产品域,通过 `/project-launch` 提供可直接打开的入口;Phase B 后默认调用真实 `pmflow-launch` draft/confirm API,创建成功态主动作跳转后端返回的 `/pmflow-os?projectId=`,只有显式 `?demo=1` 或 `?dataSource=demo` 才启用本地 mock 状态机。`frontend/src/features/account-settings/` 作为 Account Settings 产品域,通过 `/me` 提供个人资料、默认语言、AI 回答深度、证据展示偏好、待确认/执行回写/发布门禁/失败提醒通知偏好和账户操作入口;该域通过 `frontend/src/api/accountSettings.ts` 建立前端合同,有 token 时优先调用已挂入 `backend/app/api/router.py` 的 `backend/app/api/account_settings_api/` `GET /account-settings/me` / `PUT /account-settings/me` 同步当前账号偏好,后端不可用时明确回落本机暂存,不修改 OAuth/JWT/auth core。`frontend/src/features/pmflow-os/` 作为新产品域,通过 `PMFlowOsProvider` 在应用根部挂载共享 truth store,并提供 `CommandCenter / ChangeReview / WorkGraph / ExecutionBridge / ReleaseGrowth / TeamLayer / ProjectMemory / Account` 主入口;当前 PMFlow OS 页面已切到专属轻壳,桌面端默认隐藏旧 TopBar,并由 `frontend/src/features/pmflow-os/components/PMFlowOsFrame.tsx` 统一承接“左侧稳定导航 + 顶部上下文条 + 中央视图 + 右侧 AI 副驾”的全局骨架,同时承接项目内 `个人空间 / 当前项目名` 面包屑和项目切换器;账号退出登录由 `frontend/src/features/pmflow-os/components/AccountControls.tsx` 统一承接桌面头像下拉、退出确认与移动端 bottom sheet,`frontend/src/features/pmflow-os/pages/AccountPage.tsx` 提供 `/me` 入口并委托 Account Settings 域渲染设置页;正式应用壳移动端底部导航统一由 `frontend/src/features/pmflow-os/components/MobileBottomNav.tsx` 承接,固定为 `空间 / 指挥台 / 待确认 / 动态 / 我的`,其中 `动态` 当前仍是 `/command-center` 占位。`/command-center` 额外提供免登录产品预览壳,`/pmflow-os` 是真实项目上下文 alias。主视觉按 `#2463EB + 白色` 的 Linear-like 高密度原型展开。`UsersPage` 把创建/删除/重置/导入/批量分配对话框拆为按需 chunk,`UserSkillsPage` 与 `WorkloadDetailPage` 的图表也分别拆到了 `frontend/src/pages/user-skills/components/UserSkillsRadarPanel.tsx` 与 `frontend/src/pages/workload-detail/components/WorkloadTrendChart.tsx`。 - PMFlow P8 当前推进真源已从已完成的 Team Review P1 重定为 Review Decision Loop:下一阶段基于 `ProjectTruthState.reviewerRouting` 只读模型推进 `review request -> reviewer decision -> approval audit -> personal attention`,建议并行边界为 `main-flow-p8-review-decision-loop`、`space-p7-review-attention-queue`、`account-p8-review-decision-preference`,最终由 `big-module-integration-p8` 收口;P8 不新增战略页面、不改 router/nav、不接真实通知中心,继续使用既有 `/change-review / /team-layer / /space / /me` 表面。 -- `frontend/src/features/personal-space/PersonalSpacePage.tsx` 是空间首页视图层,`frontend/src/api/space.ts` 是空间工作面只读聚合 API:从 `projects` summary、PMFlow OS `truth-state` 与 `execution-summary` 生成 `nextActionQueue` 我的下一步队列、继续推进、待确认、发布关注、发布后反馈关注、下一轮迭代草稿关注、执行中 / AI 正在推进、暂存想法、草案箱、最近项目摘要;Space P6 的 `nextActionQueue` 按高风险 ChangeSet 待确认、release blocked / failed evidence、handoff consumed 但无后续 progress、iteration draft 待确认、feedback/revenue/compliance 信号待处理排序,并只提供 `/change-review`、`/execution-bridge`、`/release-growth` 跳转。Space P3 的 `releaseAttention` 优先消费 `truth-state.releases` 中由 release validation run 投影出的 release 状态与检查项,把“发布验证阻塞 / 发布验证待确认”显示到 `/space`,没有发布验证投影时才回落到 `execution-summary.releaseGate` 的发布候选或发布门禁阻塞;Space P4/P5 的 `feedbackAttention` 只读消费 `execution-summary.releaseSignals`,把发布后 feedback、revenue、ROI、compliance、iteration recommendation 与已生成的下一轮 ChangeSet draft 关注项显示到 `/space`。发布验证、发布后信号或下一轮 draft 缺失时显示明确空态,不虚构验证、反馈、收入、ROI、合规、建议或 ChangeSet draft 结果。项目主动作进入 `/pmflow-os?projectId=`,待确认项进入 `/change-review?projectId=&changeSetId=`,发布关注和发布后反馈关注项进入 `/release-growth?projectId=`,下一轮 draft 关注项进入 `/change-review?projectId=&changeSetId=`,执行中项进入 `/execution-bridge?projectId=`;暂存想法后端只读聚合尚未接入时只显示 empty/warning,不使用 mock 数据冒充真实内容;移动端为空间首页独立 App 布局,不直接压缩桌面卡片,并保留最近项目区块;`frontend/src/pages/PersonalSpacePage.tsx` 是薄入口。该域只表达个人空间和主流程导航,不做团队空间强切换、企业组织管理、Project Memory 写入、ChangeSet 状态写入、ProgressEvent 写入、ReleaseValidationRun 写入、FeedbackIngest 写入或发布执行。 +- `frontend/src/features/personal-space/PersonalSpacePage.tsx` 是空间首页视图层,`frontend/src/api/space.ts` 是空间工作面只读聚合 API:从 `projects` summary、PMFlow OS `truth-state` 与 `execution-summary` 生成 `nextActionQueue` 我的下一步队列、继续推进、待确认、发布关注、发布后反馈关注、下一轮迭代草稿关注、执行中 / AI 正在推进、暂存想法、草案箱、最近项目摘要;Space P6 的 `nextActionQueue` 按高风险 ChangeSet 待确认、release blocked / failed evidence、handoff consumed 但无后续 progress、iteration draft 待确认、feedback/revenue/compliance 信号待处理排序,并只提供 `/change-review`、`/execution-bridge`、`/release-growth` 跳转;Space P7 新增 `review_attention`,只读消费 `truth-state.reviewerRouting`,把高风险待我审、`request_changes / rejected` 后待处理、`reviewer_missing` warning 与普通 pending review 排入“我的下一步”,待审/退回/拒绝项只跳 `/change-review?projectId=&changeSetId=`,reviewer 缺失 warning 只跳已有 `/team-layer?projectId=`。Space P3 的 `releaseAttention` 优先消费 `truth-state.releases` 中由 release validation run 投影出的 release 状态与检查项,把“发布验证阻塞 / 发布验证待确认”显示到 `/space`,没有发布验证投影时才回落到 `execution-summary.releaseGate` 的发布候选或发布门禁阻塞;Space P4/P5 的 `feedbackAttention` 只读消费 `execution-summary.releaseSignals`,把发布后 feedback、revenue、ROI、compliance、iteration recommendation 与已生成的下一轮 ChangeSet draft 关注项显示到 `/space`。reviewerRouting、发布验证、发布后信号或下一轮 draft 缺失时显示明确空态,不虚构 reviewer、审查意见、待审 ChangeSet、验证、反馈、收入、ROI、合规、建议或 ChangeSet draft 结果。项目主动作进入 `/pmflow-os?projectId=`,待确认项进入 `/change-review?projectId=&changeSetId=`,发布关注和发布后反馈关注项进入 `/release-growth?projectId=`,下一轮 draft 关注项进入 `/change-review?projectId=&changeSetId=`,执行中项进入 `/execution-bridge?projectId=`;暂存想法后端只读聚合尚未接入时只显示 empty/warning,不使用 mock 数据冒充真实内容;移动端为空间首页独立 App 布局,不直接压缩桌面卡片,并保留最近项目区块;`frontend/src/pages/PersonalSpacePage.tsx` 是薄入口。该域只表达个人空间和主流程导航,不做团队空间强切换、企业组织管理、Project Memory 写入、ChangeSet 状态写入、PMFlowApproval 写入、ProgressEvent 写入、ReleaseValidationRun 写入、FeedbackIngest 写入或发布执行。 - 前端主包治理已切到“同步首屏最小集 + route/page-section lazy”模式:`frontend/src/router/index.tsx` 保留 `LoginPage / Layout` 这组同步入口,其余后台重页面通过 `React.lazy + Suspense` 懒加载;统一 fallback 在 `frontend/src/components/layout/LazyBoundary.tsx`。P0 公共壳冻结后,大模块 route 归属为:`/space` 归 Space lane,`/project-launch` 是启动层入口,`/pmflow-os` 与 `/command-center / /change-review / /execution-bridge / /release-growth` 归 Main Flow lane,`/me` 归 Account Settings lane,`/settings` 当前仅为 reserved route、尚未实现,新增必须由 Shell / Integration owner 串行处理。`frontend/src/features/personal-space/` 作为空间层入口,通过 `/space` 提供可直接打开的本地预览入口,承载继续推进、待我确认、AI 正在推进、暂存想法、草案箱与新建项目入口。`frontend/src/features/project-launch/` 作为首次进入页 / 新项目启动台产品域,通过 `/project-launch` 提供可直接打开的入口;Phase B 后默认调用真实 `pmflow-launch` draft/confirm API,创建成功态主动作跳转后端返回的 `/pmflow-os?projectId=`,只有显式 `?demo=1` 或 `?dataSource=demo` 才启用本地 mock 状态机。`frontend/src/features/account-settings/` 作为 Account Settings 产品域,通过 `/me` 提供个人资料、默认语言、AI 回答深度、证据展示偏好、待确认/执行回写/发布门禁/失败提醒通知偏好和账户操作入口;该域通过 `frontend/src/api/accountSettings.ts` 建立前端合同,有 token 时优先调用已挂入 `backend/app/api/router.py` 的 `backend/app/api/account_settings_api/` `GET /account-settings/me` / `PUT /account-settings/me` 同步当前账号偏好,后端不可用时明确回落本机暂存,不修改 OAuth/JWT/auth core。`frontend/src/features/pmflow-os/` 作为新产品域,通过 `PMFlowOsProvider` 在应用根部挂载共享 truth store,并提供 `CommandCenter / ChangeReview / WorkGraph / ExecutionBridge / ReleaseGrowth / TeamLayer / ProjectMemory / Account` 主入口;当前 PMFlow OS 页面已切到专属轻壳,桌面端默认隐藏旧 TopBar,并由 `frontend/src/features/pmflow-os/components/PMFlowOsFrame.tsx` 统一承接“左侧稳定导航 + 顶部上下文条 + 中央视图 + 右侧 AI 副驾”的全局骨架,同时承接项目内 `个人空间 / 当前项目名` 面包屑和项目切换器;`TeamLayerPage.tsx` 现在消费 `ProjectTruthState.reviewerRouting(status/summary/items)` 展示待审 ChangeSet、来源、风险等级、建议审查人或 fallback role、审查原因、待审 item 数、证据数和 warning,动作只跳 `/change-review?projectId=&changeSetId=`,不自动 assign reviewer;账号退出登录由 `frontend/src/features/pmflow-os/components/AccountControls.tsx` 统一承接桌面头像下拉、退出确认与移动端 bottom sheet,`frontend/src/features/pmflow-os/pages/AccountPage.tsx` 提供 `/me` 入口并委托 Account Settings 域渲染设置页;正式应用壳移动端底部导航统一由 `frontend/src/features/pmflow-os/components/MobileBottomNav.tsx` 承接,固定为 `空间 / 指挥台 / 待确认 / 动态 / 我的`,其中 `动态` 当前仍是 `/command-center` 占位。`/command-center` 额外提供免登录产品预览壳,`/pmflow-os` 是真实项目上下文 alias。主视觉按 `#2463EB + 白色` 的 Linear-like 高密度原型展开。`UsersPage` 把创建/删除/重置/导入/批量分配对话框拆为按需 chunk,`UserSkillsPage` 与 `WorkloadDetailPage` 的图表也分别拆到了 `frontend/src/pages/user-skills/components/UserSkillsRadarPanel.tsx` 与 `frontend/src/pages/workload-detail/components/WorkloadTrendChart.tsx`。 -- `frontend/src/features/personal-space/PersonalSpacePage.tsx` 是空间首页视图层,`frontend/src/api/space.ts` 是空间工作面只读聚合 API:从 `projects` summary、PMFlow OS `truth-state` 与 `execution-summary` 生成继续推进、待确认、发布关注、发布后反馈关注、下一轮迭代草稿关注、执行中 / AI 正在推进、暂存想法、草案箱、最近项目摘要;Space P3 的 `releaseAttention` 优先消费 `truth-state.releases` 中由 release validation run 投影出的 release 状态与检查项,把“发布验证阻塞 / 发布验证待确认”显示到 `/space`,没有发布验证投影时才回落到 `execution-summary.releaseGate` 的发布候选或发布门禁阻塞;Space P4/P5 的 `feedbackAttention` 只读消费 `execution-summary.releaseSignals`,把发布后 feedback、revenue、ROI、compliance、iteration recommendation 与已生成的下一轮 ChangeSet draft 关注项显示到 `/space`。发布验证、发布后信号或下一轮 draft 缺失时显示明确空态,不虚构验证、反馈、收入、ROI、合规、建议或 ChangeSet draft 结果。项目主动作进入 `/pmflow-os?projectId=`,待确认项进入 `/change-review?projectId=&changeSetId=`,发布关注和发布后反馈关注项进入 `/release-growth?projectId=`,下一轮 draft 关注项进入 `/change-review?projectId=&changeSetId=`,执行中项进入 `/execution-bridge?projectId=`;暂存想法后端只读聚合尚未接入时只显示 empty/warning,不使用 mock 数据冒充真实内容;移动端为空间首页独立 App 布局,不直接压缩桌面卡片,并保留最近项目区块;`frontend/src/pages/PersonalSpacePage.tsx` 是薄入口。该域只表达个人空间和主流程导航,不做团队空间强切换、企业组织管理、Project Memory 写入、ChangeSet 状态写入、ProgressEvent 写入、ReleaseValidationRun 写入、FeedbackIngest 写入或发布执行。 +- `frontend/src/features/personal-space/PersonalSpacePage.tsx` 是空间首页视图层,`frontend/src/api/space.ts` 是空间工作面只读聚合 API:从 `projects` summary、PMFlow OS `truth-state` 与 `execution-summary` 生成继续推进、待确认、review attention、发布关注、发布后反馈关注、下一轮迭代草稿关注、执行中 / AI 正在推进、暂存想法、草案箱、最近项目摘要;`review_attention` 只读消费 `truth-state.reviewerRouting`,用于把高风险待我审、审查退回/拒绝、reviewer 缺失 warning 与普通 pending review 加入“我的下一步”。发布验证、发布后信号、reviewerRouting 或下一轮 draft 缺失时显示明确空态,不虚构 reviewer、审查意见、待审 ChangeSet、验证、反馈、收入、ROI、合规、建议或 ChangeSet draft 结果。项目主动作进入 `/pmflow-os?projectId=`,待确认和待审项进入 `/change-review?projectId=&changeSetId=`,reviewer 缺失 warning 进入已有 `/team-layer?projectId=`,发布关注和发布后反馈关注项进入 `/release-growth?projectId=`,下一轮 draft 关注项进入 `/change-review?projectId=&changeSetId=`,执行中项进入 `/execution-bridge?projectId=`;暂存想法后端只读聚合尚未接入时只显示 empty/warning,不使用 mock 数据冒充真实内容;移动端为空间首页独立 App 布局,不直接压缩桌面卡片,并保留最近项目区块;`frontend/src/pages/PersonalSpacePage.tsx` 是薄入口。该域只表达个人空间和主流程导航,不做团队空间强切换、企业组织管理、Project Memory 写入、ChangeSet 状态写入、PMFlowApproval 写入、ProgressEvent 写入、ReleaseValidationRun 写入、FeedbackIngest 写入或发布执行。 - `frontend/src/features/project-launch/types.ts`、`mockData.ts`、`useProjectLaunchState.ts` 是新项目启动台状态层;默认真实模式通过 `frontend/src/api/pmflowLaunch.ts` 生成草案并确认创建真实项目,API 失败进入明确失败态,不静默 fallback 成 mock 成功;`mockData.ts` 仅在显式 `?demo=1` 或 `?dataSource=demo` 下作为演示状态机使用。`components/LaunchShell.tsx` 负责桌面/移动分流,桌面端复用 `frontend/src/features/personal-space/components/SpaceChrome.tsx` 的空间左侧栏和顶部条,`LaunchCanvases.tsx` 和 `MobileLaunchLayout.tsx` 承接高保真图中的意图输入、AI 工作、人类确认、草案修改、生成失败、创建成功过渡与移动端 bottom sheet 交互。 - `frontend/src/features/account-settings/AccountSettingsPanel.tsx` 是 `/me` 设置页实现:桌面端为个人资料摘要 + 分区设置面板,移动端为分组列表 + `MobileBottomSheet` 编辑;当前覆盖个人资料、默认语言、AI 回答深度、证据展示偏好、待确认、执行回写、发布门禁和失败提醒通知偏好,并在通知偏好区展示页面内提醒消费说明。`frontend/src/api/accountSettings.ts` 是前端合同层;有 token 时优先调用 `backend/app/api/account_settings_api/`,失败时回落浏览器本机暂存;P2 新增 `shouldShowAccountNotification` 与 `getAccountNotificationConsumption`,将四类通知偏好映射为本地 UI 的待确认、Execution Bridge、Release/Growth 和失败提醒显示/静默状态;P3 新增 `getReleaseGateNotificationPreference(settings, event)`,把 Release/Growth 的 `release_gate_* / release_validation_*` 事件映射到 `releaseGate` 偏好,把 `failed_event` 映射到 `failureAlert` 偏好;P5/P6 的 `getPostReleaseNotificationPreference(settings, event)` 把发布后 `feedback_received / revenue_signal / compliance_blocked` 映射到 `releaseGate` 偏好,把 `iteration_recommendation / next_change_review` 映射到 `pendingConfirmation` 偏好和 `change_review` surface,并返回 `spaceReadable / evidencePolicy / reminderPolicy` 供 Main Flow 与 Space 消费。该合同只控制页面内提醒表达,不代表邮件、桌面推送或系统通知中心已接入;偏好关闭也不得隐藏业务证据、recommendation 或 ChangeSet。`backend/app/schemas/account_settings.py` 定义账号偏好合同,`backend/app/services/account_settings_service.py` 当前复用既有 `integrations` 表按 `tenant_id + user_id` 保存 `account_settings_preferences` 配置包,`backend/app/api/router.py` 已挂载该 API;未改用户表、OAuth/JWT/auth core 或 PMFlow OS truth store。 - `frontend/src/features/pmflow-os/store.tsx` 当前是 PMFlow OS 共享 truth store:`?projectId=` 时通过 React Query 调用 `frontend/src/api/pmflowOs.ts` 读取真实 `GET /pmflow-os/projects/{project_id}/truth-state` 与 `GET /pmflow-os/projects/{project_id}/execution-summary`,真实模式下项目内 message、`proposed_items` extraction、ChangeItem `accept/edit/defer`、ChangeSet commit、Execution Handoff create/consume、execution progress writeback、release validation run create、release feedback ingest、单条 iteration recommendation -> ChangeSet draft 与同组 iteration recommendation 批量 -> ChangeSet draft 均调用 `/pmflow-os` 后端并 refetch;只有显式 `?demo=1` 或 `?dataSource=demo` 时才启用本地 mock 演示状态机。真实读取/动作失败会进入错误/空态,不静默 fallback 成 mock 成功。 diff --git a/docs/architecture/CODE_REFERENCE_INDEX.md b/docs/architecture/CODE_REFERENCE_INDEX.md index 770a1887..c1b35169 100644 --- a/docs/architecture/CODE_REFERENCE_INDEX.md +++ b/docs/architecture/CODE_REFERENCE_INDEX.md @@ -112,6 +112,7 @@ - PMFlow OS 产品页入口:参考 `frontend/src/features/pmflow-os/pages/CommandCenterPage.tsx`、`StructuredExtractionPage.tsx`、`ChangeReviewPage.tsx`、`MemoryWritebackResultPage.tsx`、`WorkGraphPage.tsx`、`ExecutionBridgePage.tsx`、`ReleaseGrowthPage.tsx`、`TeamLayerPage.tsx`、`ProjectMemoryPage.tsx`、`AccountPage.tsx`;`TeamLayerPage.tsx` 真实模式读取 `state.reviewerRouting` 展示 ready/empty/warning、待审 ChangeSet、来源(下一轮迭代 / AI 提取 / 手动)、风险等级、建议审查人或 fallback role、审查原因、待审 item 数、证据数,并只跳转 `/change-review?projectId=&changeSetId=`,不自动分派 reviewer;`ReleaseGrowthPage.tsx` 真实模式展示 `execution-summary.releaseGate`、execution evidence、阻塞原因、下一步动作、`latestValidationRun` 和 `releaseSignals`,缺证据或缺发布后信号时显示 empty/warning,不展示 mock 发布结果;页面可调用真实 API 创建 release validation run、摄取 release feedback,从真实 recommendation 单条或同组批量生成下一轮待确认 ChangeSet draft,并解释 `iteration_recommendation_group` evidence 的组、反馈、风险、影响和审查优先级;生成后仍进入 Change Review accept/edit/defer,不自动 commit、不写 Project Memory、不执行部署命令、不接外部支付;同时消费 Account notification preference helper,`releaseGate` 关闭时静默发布门禁强调提醒,`failureAlert` 关闭时静默失败强调提醒,所有真实证据和审计状态仍可见;静态高保真发布/增长数据仅保留在显式 demo 模式 - PMFlow OS 行为组件:参考 `frontend/src/features/pmflow-os/components/ProjectStructuredLoop.tsx`、`ProjectStageRail.tsx`、`ThreadComposer.tsx`、`ExtractionRail.tsx`、`ChangeGroupPanel.tsx`、`ObjectGraphSection.tsx`、`ContextPacketPanel.tsx`、`ExecutionTimeline.tsx`、`ReleaseGatePanel.tsx`、`TeamFeedPanel.tsx`、`ChangeReviewGroup.tsx`、`WorkbenchPanel.tsx`、`ExecutionBridgeCards.tsx`、`PMFlowOsFrame.tsx`、`AccountControls.tsx`、`FeedbackPrimitives.tsx`、`MobileBottomNav.tsx`;其中 `ProjectStructuredLoop.tsx` 承接 `StatusLabel / EvidenceSnippet / ObjectChangeCard / ProjectConversationPanel / StructuredExtractionPanel / ProjectAICopilotPanel`,`PMFlowOsFrame.tsx` 承接项目内面包屑和项目切换器,`AccountControls.tsx` 承接桌面头像菜单、退出登录确认与移动端 bottom sheet,`FeedbackPrimitives.tsx` 承接 `PageBanner / InlineNotice / HumanDecisionBar / ConfirmDialog / MobileBottomSheet`,`MobileBottomNav.tsx` 承接正式应用壳移动端固定底部导航 - PMFlow OS 原型状态回归测试:参考 `frontend/src/features/pmflow-os/store.test.ts`;真实 API 动作回归参考 `frontend/src/features/pmflow-os/store.api.test.tsx` +- Space 工作面回归测试:参考 `frontend/src/features/personal-space/PersonalSpacePage.test.tsx`,覆盖空态、导航只读约束、发布/反馈/迭代草案队列,以及 reviewerRouting -> `review_attention` 的 next action 聚合排序与 `/change-review`、`/team-layer` 跳转合同。 - Project Launch 真实/演示分支回归测试:参考 `frontend/src/features/project-launch/useProjectLaunchState.test.ts` - Account Settings 前端合同回归测试:参考 `frontend/src/features/account-settings/accountSettings.test.ts`,覆盖默认偏好、账号同步 fallback、通知偏好消费模型、提醒强度/证据展示合同、Release/Growth 通知消费合同、失败事件映射和缺字段 fallback。 - 网页问答显式锚点与 AI Steward URL 构造:参考 `frontend/src/components/ai-steward/qaPageAnchor.ts` diff --git a/frontend/src/api/space.ts b/frontend/src/api/space.ts index d400eda9..28bb2f26 100644 --- a/frontend/src/api/space.ts +++ b/frontend/src/api/space.ts @@ -5,6 +5,7 @@ import type { MetricSignal, PMFlowExecutionStatus, PMFlowObjectStatus, + PMFlowReviewerRoutingItem, Release, ProjectTruthState, } from "@/features/pmflow-os/types"; @@ -95,6 +96,7 @@ export interface SpaceIterationDraftAttentionItem { export type SpaceNextActionKind = | "pending_confirmation" + | "review_attention" | "release_blocked" | "execution_attention" | "iteration_draft" @@ -245,6 +247,10 @@ function releaseGrowthHref(projectId: string): string { return `/release-growth?projectId=${encodeURIComponent(projectId)}`; } +function teamLayerHref(projectId: string): string { + return `/team-layer?projectId=${encodeURIComponent(projectId)}`; +} + function pendingChangeItems(truth: ProjectTruthState | null): ChangeItem[] { if (!truth) return []; return truth.changeItems.filter((item) => item.status === "pending_review"); @@ -728,6 +734,134 @@ function nextActionPriorityLabel(severity: "low" | "medium" | "high"): string { return "低优先级"; } +function reviewRoutingHref(projectId: string, item: PMFlowReviewerRoutingItem, preferTeamLayer = false): string { + const targetUrl = String(item.targetUrl || ""); + if (targetUrl.startsWith("/change-review") || targetUrl.startsWith("/team-layer")) { + return targetUrl; + } + if (preferTeamLayer) return teamLayerHref(projectId); + return reviewHref(projectId, item.changeSetId); +} + +function reviewDecisionState(item: PMFlowReviewerRoutingItem): "request_changes" | "rejected" | null { + const record = item as unknown as Record; + const raw = String( + record.reviewDecision || + record.review_decision || + record.approvalState || + record.approval_state || + record.approvalStatus || + record.approval_status || + "" + ).toLowerCase(); + if (raw === "request_changes" || raw === "changes_requested" || raw === "needs_changes") return "request_changes"; + if (raw === "rejected" || raw === "reject") return "rejected"; + return null; +} + +function isReviewerMissing(item: PMFlowReviewerRoutingItem): boolean { + return !item.suggestedReviewer && !item.fallbackReviewerRole; +} + +function reviewAttentionPriorityRank(item: PMFlowReviewerRoutingItem): number { + if (item.riskLevel === "high" && item.priority === "urgent") return 8; + if (item.riskLevel === "high") return 9; + if (reviewDecisionState(item)) return 14; + if (isReviewerMissing(item)) return 24; + return 32; +} + +function reviewAttentionSeverity(item: PMFlowReviewerRoutingItem): "low" | "medium" | "high" { + if (item.riskLevel === "high" || item.priority === "urgent" || reviewDecisionState(item) === "rejected") return "high"; + if (item.riskLevel === "medium" || item.priority === "elevated" || reviewDecisionState(item)) return "medium"; + return "low"; +} + +function reviewAttentionTitle(item: PMFlowReviewerRoutingItem): string { + const decisionState = reviewDecisionState(item); + if (decisionState === "request_changes") return "审查退回待处理"; + if (decisionState === "rejected") return "审查拒绝待处理"; + if (isReviewerMissing(item)) return "审查人缺失"; + if (item.riskLevel === "high") return "高风险 ChangeSet 待我审"; + return "ChangeSet 待我审"; +} + +function reviewAttentionCategoryLabel(item: PMFlowReviewerRoutingItem): string { + const decisionState = reviewDecisionState(item); + if (decisionState) return decisionState === "request_changes" ? "审查退回" : "审查拒绝"; + if (isReviewerMissing(item)) return "审查人缺失"; + if (item.riskLevel === "high") return "高风险待审"; + return "待审查"; +} + +function reviewAttentionSummary(item: PMFlowReviewerRoutingItem): string { + const reviewer = + item.suggestedReviewer?.displayName || + item.suggestedReviewer?.role || + (item.fallbackReviewerRole ? `${item.fallbackReviewerRole} 角色` : ""); + const decisionState = reviewDecisionState(item); + if (decisionState === "request_changes") { + return `${item.changeSetTitle} 已被要求修改,需要回到 Change Review 处理审查意见。`; + } + if (decisionState === "rejected") { + return `${item.changeSetTitle} 已被拒绝,需要回到 Change Review 判断重做、延期或关闭。`; + } + if (isReviewerMissing(item)) { + return `${item.changeSetTitle} 缺少可用审查人,项目需要先补齐 reviewer routing 后再推进。`; + } + return reviewer + ? `${item.changeSetTitle} 需要 ${reviewer} 审查;${item.reviewReason}` + : `${item.changeSetTitle} 需要审查;${item.reviewReason}`; +} + +function deriveReviewAttentionActions(snapshot: ProjectSnapshot): SpaceNextActionQueueItem[] { + const { project, truth } = snapshot; + const routing = truth?.reviewerRouting; + if (!routing) return []; + + const projectId = String(project.id); + const projectTitle = project.title || truth.project.name || `项目 ${projectId}`; + const routedItems = routing.items.map((item): SpaceNextActionQueueItem => { + const severity = reviewAttentionSeverity(item); + const missingReviewer = isReviewerMissing(item) && !reviewDecisionState(item); + return { + id: `next-action-review-${projectId}-${item.changeSetId}`, + projectId, + projectTitle, + kind: "review_attention", + title: reviewAttentionTitle(item), + summary: reviewAttentionSummary(item), + categoryLabel: reviewAttentionCategoryLabel(item), + priorityLabel: nextActionPriorityLabel(severity), + priorityRank: reviewAttentionPriorityRank(item), + severity, + updatedAt: item.updatedAt || item.createdAt || project.updated_at, + href: reviewRoutingHref(projectId, item, missingReviewer), + }; + }); + + if (routedItems.length || routing.status !== "warning" || routing.summary.missingReviewerCount <= 0) { + return routedItems; + } + + return [ + { + id: `next-action-review-warning-${projectId}`, + projectId, + projectTitle, + kind: "review_attention", + title: "审查路由待处理", + summary: `${routing.summary.missingReviewerCount} 个待审 ChangeSet 缺少 reviewer routing,需要回到团队层查看。`, + categoryLabel: "审查人缺失", + priorityLabel: "中优先级", + priorityRank: 24, + severity: "medium", + updatedAt: project.updated_at, + href: teamLayerHref(projectId), + }, + ]; +} + function deriveStaleExecutionActions(snapshot: ProjectSnapshot): SpaceNextActionQueueItem[] { const { project, truth, execution } = snapshot; const events = execution?.recentProgressEvents ?? []; @@ -774,6 +908,7 @@ function buildNextActionQueue({ releaseAttention, feedbackAttention, iterationDraftAttention, + reviewAttentionActions, staleExecutionActions, }: { pendingConfirmations: SpacePendingConfirmation[]; @@ -781,6 +916,7 @@ function buildNextActionQueue({ releaseAttention: SpaceReleaseAttentionItem[]; feedbackAttention: SpaceFeedbackAttentionItem[]; iterationDraftAttention: SpaceIterationDraftAttentionItem[]; + reviewAttentionActions: SpaceNextActionQueueItem[]; staleExecutionActions: SpaceNextActionQueueItem[]; }): SpaceNextActionQueueItem[] { const pendingActions = pendingConfirmations.map((item): SpaceNextActionQueueItem => { @@ -864,7 +1000,7 @@ function buildNextActionQueue({ })); const seen = new Set(); - return [...pendingActions, ...releaseBlockedActions, ...staleExecutionActions, ...activeExecutionActions, ...iterationActions, ...feedbackActions] + return [...reviewAttentionActions, ...pendingActions, ...releaseBlockedActions, ...staleExecutionActions, ...activeExecutionActions, ...iterationActions, ...feedbackActions] .filter((item) => { const key = `${item.kind}:${item.href}:${item.title}`; if (seen.has(key)) return false; @@ -905,6 +1041,7 @@ export const spaceApi = { const releaseAttention = sortByDate(snapshots.flatMap(deriveReleaseAttention)).slice(0, 8); const feedbackAttention = sortByDate(snapshots.flatMap(deriveFeedbackAttention)).slice(0, 8); const iterationDraftAttention = sortByDate(snapshots.flatMap(deriveIterationDraftAttention)).slice(0, 8); + const reviewAttentionActions = snapshots.flatMap(deriveReviewAttentionActions); const staleExecutionActions = sortByDate(snapshots.flatMap(deriveStaleExecutionActions)).slice(0, 4); return { projects: workbenchProjects.slice(0, 4), @@ -914,6 +1051,7 @@ export const spaceApi = { releaseAttention, feedbackAttention, iterationDraftAttention, + reviewAttentionActions, staleExecutionActions, }), pendingConfirmations, diff --git a/frontend/src/features/personal-space/PersonalSpacePage.test.tsx b/frontend/src/features/personal-space/PersonalSpacePage.test.tsx index 72f953fe..f54197ea 100644 --- a/frontend/src/features/personal-space/PersonalSpacePage.test.tsx +++ b/frontend/src/features/personal-space/PersonalSpacePage.test.tsx @@ -1,6 +1,8 @@ import { screen } from "@testing-library/react"; -import { describe, expect, it, vi } from "vitest"; +import { beforeEach, describe, expect, it, vi } from "vitest"; import PersonalSpacePage from "./PersonalSpacePage"; +import { getPMFlowExecutionSummary, getPMFlowTruthState } from "@/api/pmflowOs"; +import { projectsApi } from "@/api/projects"; import { spaceApi, type SpaceWorkbench } from "@/api/space"; import { renderWithProviders } from "@/test/renderWithProviders"; @@ -10,6 +12,17 @@ vi.mock("@/api/space", () => ({ }, })); +vi.mock("@/api/projects", () => ({ + projectsApi: { + getSummary: vi.fn(), + }, +})); + +vi.mock("@/api/pmflowOs", () => ({ + getPMFlowTruthState: vi.fn(), + getPMFlowExecutionSummary: vi.fn(), +})); + vi.mock("@/features/pmflow-os/components/MobileBottomNav", () => ({ MobileBottomNav: () =>