merge space p6 personal next action queue
# Conflicts: # CLAUDE.md # docs/PMFLOW_PRODUCT_DELIVERY_STANDARD.md # docs/architecture/ARCHITECTURE_INVENTORY.md # docs/architecture/CODE_REFERENCE_INDEX.md
This commit is contained in:
commit
4a74eea75d
|
|
@ -132,8 +132,8 @@ 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=<id>`。`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` 并生成继续推进、待确认、发布关注、发布后反馈关注、下一轮迭代草稿关注、AI 正在推进、草案箱、最近项目摘要;Space P3 的发布关注优先消费 `truth-state.releases` 的 release validation 投影,没有验证投影时才回落 `execution-summary.releaseGate`;Space P4/P5 的发布后反馈/下一轮草稿关注只读消费 `execution-summary.releaseSignals`,点击只跳 `/release-growth?projectId=<id>` 或 `/change-review?projectId=<id>&changeSetId=<id>`;不在 Space 写 Project Memory、ProgressEvent、ReleaseValidationRun、FeedbackIngest、RevenueEvent、ComplianceCheckRun、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,`releaseGate / failureAlert / pendingConfirmation` 只影响页面内提醒表达,不代表真实邮件、桌面推送或系统通知中心已接入,不改 OAuth/JWT/auth core;`frontend/src/features/pmflow-os/pages/AccountPage.tsx` 仅做薄入口并复用既有退出登录 bottom sheet。
|
||||
- `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=<id>&changeSetId=<id>`、`/execution-bridge?projectId=<id>` 或 `/release-growth?projectId=<id>`;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/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=<id>` 调用 `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` 主页面;`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/` 是当前唯一登录真源。
|
||||
|
||||
|
|
|
|||
|
|
@ -825,6 +825,62 @@ Shell / Integration owner 负责:
|
|||
- `git diff --name-only` 仅包含本 Packet 文档 write scope。
|
||||
- `rg -n "Phase D.2|当前最建议下一步|P7|Iteration Quality" docs/PMFLOW_PRODUCT_DELIVERY_STANDARD.md CLAUDE.md docs/architecture/CODE_REFERENCE_INDEX.md docs/architecture/ARCHITECTURE_INVENTORY.md` 已用于确认推荐任务真源更新。
|
||||
|
||||
### 2026-04-26 / space-p6-personal-next-action-queue / implemented
|
||||
|
||||
完成内容:
|
||||
|
||||
- 在 `frontend/src/api/space.ts` 新增 `nextActionQueue` 只读聚合模型,把高风险 ChangeSet 待确认、release blocked / failed evidence、handoff consumed 但无后续 progress、iteration draft 待确认、feedback/revenue/compliance 信号待处理收敛为统一“我的下一步”队列。
|
||||
- `/space` 桌面端和移动端新增首屏“我的下一步”队列,按优先级和更新时间排序;每个 item 只跳转 `/change-review`、`/execution-bridge` 或 `/release-growth`。
|
||||
- 保留原有继续推进、待确认、执行中、发布关注、发布后反馈、下一轮迭代建议等明细区块作为二级上下文;空态明确,不虚构项目、反馈、recommendation 或 ChangeSet。
|
||||
- 本轮不写 ChangeSet、不写 ProgressEvent、不写 ReleaseValidationRun、不写 FeedbackIngest,不改 PMFlow OS 页面、Account Settings 或 router。
|
||||
|
||||
涉及文件:
|
||||
|
||||
- `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` 通过:`2 passed`;仍有既有 React Router v7 future flag warning。
|
||||
- `cd frontend && npm run typecheck` 未通过,阻塞来自本 packet 禁止路径外的既有 Account Settings / PMFlow OS 测试类型错误:`frontend/src/features/account-settings/accountSettings.test.ts` 的 `reminderIntensity` 字面量推断为 `string`,以及 `frontend/src/features/pmflow-os/store.api.test.tsx` 的 `AccountNotificationPreference` 测试 fixture 缺少 `reminderIntensity / evidenceDisplay`。
|
||||
- `cd frontend && npm run build` 同样在 `tsc -b` 阶段被上述禁止路径外类型错误阻塞,尚未进入 Vite build。
|
||||
|
||||
未完成或风险:
|
||||
|
||||
- 当前工作区同时存在 Main Flow / Account Settings lane 的未提交改动;Space P6 提交只能 stage 本 packet 文件与文档,不能混入这些越界文件。
|
||||
- 若 integration owner 要求全量 typecheck/build 绿灯,需要先由 Account / Main Flow owner 修复对应测试 fixture 类型。
|
||||
|
||||
### 2026-04-26 / account-p7-review-notification-intensity / implemented
|
||||
|
||||
完成内容:
|
||||
|
||||
- 将 Account Settings 通知偏好从简单开关扩展为本地 UI 提醒表达合同:每类提醒继续保留 `enabled / desktop / emailDigest` 兼容字段,同时新增 `reminderIntensity=silent|normal|prominent` 与 `evidenceDisplay=compact|expanded`。
|
||||
- 新增 `getAccountReminderConsumption(settings, event)`,覆盖 `change_review_high_risk / iteration_draft_ready / release_blocked / execution_stalled`,稳定输出 `surface / notificationKey / reminderIntensity / evidenceDisplay / showInAppReminder / showProminentReminder / evidencePolicy / auditPolicy / notificationTransport=in_app_only`。
|
||||
- 默认值向后兼容 P1-P6:旧数据缺少新字段时,旧开启等价 `normal + compact`,旧关闭等价 `silent + compact`;偏好关闭只静默页面内提醒,不隐藏 ChangeSet、recommendation、release evidence、execution 状态或审计状态。
|
||||
- `AccountSettingsPanel` 通知偏好区新增提醒强度和证据展示配置;桌面提醒、邮件摘要仍只记录偏好,不代表真实邮件、桌面推送或系统通知中心已接入。
|
||||
- 本轮只扩展前端本地 helper 和页面设置,不改后端 schema、migration、router、PMFlow OS store、Space 或 Main Flow 页面。
|
||||
|
||||
涉及文件:
|
||||
|
||||
- `frontend/src/api/accountSettings.ts`
|
||||
- `frontend/src/features/account-settings/AccountSettingsPanel.tsx`
|
||||
- `frontend/src/features/account-settings/accountSettings.test.ts`
|
||||
- `CLAUDE.md`
|
||||
- `docs/architecture/CODE_REFERENCE_INDEX.md`
|
||||
- `docs/architecture/ARCHITECTURE_INVENTORY.md`
|
||||
- `docs/PMFLOW_PRODUCT_DELIVERY_STANDARD.md`
|
||||
|
||||
验证:
|
||||
|
||||
- `cd frontend && npm run test -- src/features/account-settings/accountSettings.test.ts` 通过:`11 passed`。
|
||||
- `cd frontend && npm run typecheck` 通过。
|
||||
- `cd frontend && npm run build` 待本包最终验证记录。
|
||||
|
||||
### 2026-04-26 / big-module-integration-p6 / done
|
||||
|
||||
完成内容:
|
||||
|
|
|
|||
|
|
@ -194,12 +194,16 @@
|
|||
- AI Steward 问答浮窗现分三层显性化:`FloatingQAChat.tsx` 负责即时回答/历史回放归一化,`QAConversationStatusBar.tsx` 展示对象/来源/仓库/执行通道/plan handoff,`QAResponseMetaCard.tsx` 展示 `fit_brief / external_recommendation / recommended_next_step`,对应前端回归由 `FloatingQAChat.test.tsx` 与 `QAResponseMetaCard.test.tsx` 锁定。
|
||||
- 登录页与营销页仍共享 `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=<id>`,只有显式 `?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`。
|
||||
- `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=<id>`,待确认项进入 `/change-review?projectId=<id>&changeSetId=<id>`,发布关注和发布后反馈关注项进入 `/release-growth?projectId=<id>`,下一轮 draft 关注项进入 `/change-review?projectId=<id>&changeSetId=<id>`,执行中项进入 `/execution-bridge?projectId=<id>`;暂存想法后端只读聚合尚未接入时只显示 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 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=<id>`,待确认项进入 `/change-review?projectId=<id>&changeSetId=<id>`,发布关注和发布后反馈关注项进入 `/release-growth?projectId=<id>`,下一轮 draft 关注项进入 `/change-review?projectId=<id>&changeSetId=<id>`,执行中项进入 `/execution-bridge?projectId=<id>`;暂存想法后端只读聚合尚未接入时只显示 empty/warning,不使用 mock 数据冒充真实内容;移动端为空间首页独立 App 布局,不直接压缩桌面卡片,并保留最近项目区块;`frontend/src/pages/PersonalSpacePage.tsx` 是薄入口。该域只表达个人空间和主流程导航,不做团队空间强切换、企业组织管理、Project Memory 写入、ChangeSet 状态写入、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=<id>` 时通过 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 成功。
|
||||
- `frontend/src/features/pmflow-os/store.tsx` 仍保留演示用项目内 AI 结构化闭环状态机:`command_idle / ai_extracting / extraction_ready / review_open / writeback_success`。`frontend/src/features/pmflow-os/pages/StructuredExtractionPage.tsx` 是本轮结构化提取页,`ChangeReviewPage.tsx` 是决策与待确认中心,`MemoryWritebackResultPage.tsx` 是项目记忆写入结果页,`WorkGraphPage.tsx` 是项目工作台关系总览与对象投影视图,`ExecutionBridgePage.tsx` 是执行桥上下文包、交接按钮、运行端领取确认、PR/测试与回写状态页;`ReleaseGrowthPage.tsx` 真实模式读取 `execution-summary.releaseGate`、`latestValidationRun` 与 `releaseSignals`,从 branch、commit、PR、test、failed evidence 展示 `可发布 / 阻塞 / 需要人工确认 / 缺少证据` 判断、阻塞原因、下一步动作、发布验证记录和发布后 feedback/revenue/recommendation 信号,缺少发布后信号时显示 empty/warning,不执行发布命令、不展示 mock 发布结果;页面可展示 recommendation quality grouping,把真实 recommendation 单条或同组批量生成下一轮待确认 ChangeSet draft,生成后仍进入 Change Review accept/edit/defer,不自动 commit、不写 Project Memory、不创建正式任务;`ChangeReviewPage.tsx` 会展示 `iteration_recommendation_group` evidence 的来源组、feedback 数、recommendation 数、风险、影响和审查优先级;同时读取 Account notification preference helper,用 `releaseGate` 偏好控制发布门禁强调提醒,用 `failureAlert` 偏好控制失败强调提醒,偏好关闭时只静默提醒表达强度,不隐藏 release evidence、阻塞原因、审计状态、validation run 或 releaseSignals;静态发布/增长原型只保留在显式 demo 模式。A3 已接入后端 truth-state,C1 已接入真实 message/extraction/review 状态机,C2 已接入真实 ChangeSet commit,Phase D 已接入真实 execution-summary/dev-sync,D.2 已接入 `POST /execution-handoffs` 创建 PromptPackage + handoff_created progress event,P1 已接入 `POST /execution-handoffs/{prompt_package_id}/consume` 创建 handoff_consumed progress event 并把 execution status 投影为 coding,Release Growth P1 已接入 execution evidence gate,Release Validation P1 已接入 `POST /release-validation-runs` 创建 `ReleaseValidationRun` 快照,Release Feedback P1 已接入 `POST /release-feedback-ingests` 写入 `FeedbackIngest` 并生成 `IterationRecommendation`,Iteration Loop P1 已接入 `POST /iteration-recommendations/{recommendation_id}/change-set-drafts` 把 recommendation 回流到 Change Review,Iteration Loop P2 已接入 `POST /iteration-recommendation-groups/{group_key}/change-set-drafts` 并在 `releaseSignals.recommendationGroups` 暴露分组/去重/风险/影响合同;当前仍不调用真实 AI 提取,不把 mock 当真实失败 fallback。`ProjectMemoryPage.tsx` 在真实模式下展示后端 `truth_nodes + semantic_facts` 的 `projectMemory` 投影,accepted/edited ChangeItem 只有 commit 成功后才进入 Project Memory。`frontend/src/features/pmflow-os/components/ProjectStructuredLoop.tsx` 提供 `StatusLabel / EvidenceSnippet / ObjectChangeCard / ProjectConversationPanel / StructuredExtractionPanel / ProjectAICopilotPanel` 等闭环组件,并复用契约化 `InlineNotice` 表达“未确认不得写入”的局部提示。
|
||||
- PMFlow OS 后端入口位于 `backend/app/api/pmflow_os_api/`,projection service 位于 `backend/app/services/pmflow_os_projection_service.py`,Change Review / commit 服务位于 `backend/app/services/pmflow_change_review_service.py`,Pydantic 合同位于 `backend/app/schemas/pmflow_os.py`;当前聚合 `truth_nodes / tasks / semantic_facts / ai_project_control / dev_sync`,`Thread / Message` 来自 `pmflow_threads / pmflow_thread_messages`,`ChangeSet / ChangeItem / Approval / EvidenceLink` 来自 `backend/app/models/pmflow_change_review.py` 对应真实表。C1 已实现 message、`proposed_items` extraction、ChangeSet detail、ChangeItem `accept/edit/defer`;C2 backend/frontend 已实现 `POST /pmflow-os/change-sets/{change_set_id}/commit` 与真实模式前端接入,默认 atomic,将 `Requirement / Decision / Risk` 写入 `truth_nodes`,将 `Task` 写入 `truth_nodes(node_type='task') + tasks`,高风险项强制 approval gate;D.2 已实现 `POST /pmflow-os/projects/{project_id}/execution-handoffs`,校验 task/repo project 边界,生成/复用 `PromptPackage(package_kind='execution_handoff')` 并写 `ProgressEvent(event_type='handoff_created')` 起点;P1 已实现 `POST /pmflow-os/projects/{project_id}/execution-handoffs/{prompt_package_id}/consume`,校验 execution handoff package 后更新 `PromptPackage.package_payload.latest_handoff.handoff_state=consumed` 并写 `ProgressEvent(event_type='handoff_consumed', derived_task_status='coding')`;Release Growth P1 在 `ExecutionSummaryProjection.releaseGate` 中从 `ProgressEvent` 推导发布门禁状态,失败事件 fail-closed 阻塞发布,无证据返回 empty/warning;Release Validation P1 新增 `POST /pmflow-os/projects/{project_id}/release-validation-runs`,复用现有 `ReleaseValidationRun` 模型固化当前 `releaseGate` 快照、人工决策、证据、阻塞原因和下一步动作,并在 `releaseGate.latestValidationRun` 返回最新记录;Release Feedback P1 新增 `POST /pmflow-os/projects/{project_id}/release-feedback-ingests`,复用现有 `FeedbackIngest / IterationRecommendation`,在 `ExecutionSummaryProjection.releaseSignals` 中返回真实发布后 feedback、revenue event 和 iteration recommendation 读模型;Iteration Loop P1 新增 `POST /pmflow-os/projects/{project_id}/iteration-recommendations/{recommendation_id}/change-set-drafts`,把真实 recommendation 转为 open ChangeSet、pending_review Requirement/Task/Risk/Decision items 与 `iteration_recommendation` evidence,重复调用复用既有 draft 并在 `releaseSignals` 标记 `changeSetCreated/changeSetId`;Iteration Loop P2 新增 `POST /pmflow-os/projects/{project_id}/iteration-recommendation-groups/{group_key}/change-set-drafts`,把同组 recommendation 批量转成一个 open ChangeSet draft 与 `iteration_recommendation_group` evidence,并在 `releaseSignals.recommendations / recommendationGroups` 投影 `groupKey / duplicateOf / mergedCount / impactLevel / riskLevel / suggestedChangeTypes / reviewPriority`;这些步骤都不直接执行本机命令、真实发布命令、外部支付、真实推送、自动 Project Memory commit 或正式任务创建;`ModelDecision / MetricSignal` 在 C2 V1 仍 fail-closed。
|
||||
- `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 消费;P7 将通知偏好扩展为 `reminderIntensity=silent|normal|prominent` 与 `evidenceDisplay=compact|expanded`,并新增 `getAccountReminderConsumption(settings, event)` 覆盖 `change_review_high_risk / iteration_draft_ready / release_blocked / execution_stalled`,输出 `notificationTransport=in_app_only / evidencePolicy=preserve_business_evidence / auditPolicy=preserve_audit_state`。该合同只控制页面内提醒表达,不代表邮件、桌面推送或系统通知中心已接入;偏好关闭也不得隐藏业务证据、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=<id>` 时通过 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 均调用 `/pmflow-os` 后端并 refetch;只有显式 `?demo=1` 或 `?dataSource=demo` 时才启用本地 mock 演示状态机。真实读取/动作失败会进入错误/空态,不静默 fallback 成 mock 成功。
|
||||
- `frontend/src/features/pmflow-os/store.tsx` 仍保留演示用项目内 AI 结构化闭环状态机:`command_idle / ai_extracting / extraction_ready / review_open / writeback_success`。`frontend/src/features/pmflow-os/pages/StructuredExtractionPage.tsx` 是本轮结构化提取页,`ChangeReviewPage.tsx` 是决策与待确认中心,`MemoryWritebackResultPage.tsx` 是项目记忆写入结果页,`WorkGraphPage.tsx` 是项目工作台关系总览与对象投影视图,`ExecutionBridgePage.tsx` 是执行桥上下文包、交接按钮、运行端领取确认、PR/测试与回写状态页;`ReleaseGrowthPage.tsx` 真实模式读取 `execution-summary.releaseGate`、`latestValidationRun` 与 `releaseSignals`,从 branch、commit、PR、test、failed evidence 展示 `可发布 / 阻塞 / 需要人工确认 / 缺少证据` 判断、阻塞原因、下一步动作、发布验证记录和发布后 feedback/revenue/recommendation 信号,缺少发布后信号时显示 empty/warning,不执行发布命令、不展示 mock 发布结果;页面可把真实 recommendation 生成下一轮待确认 ChangeSet draft,生成后仍进入 Change Review accept/edit/defer,不自动 commit、不写 Project Memory、不创建正式任务;同时读取 Account notification preference helper,用 `releaseGate` 偏好控制发布门禁强调提醒,用 `failureAlert` 偏好控制失败强调提醒,偏好关闭时只静默提醒表达强度,不隐藏 release evidence、阻塞原因、审计状态、validation run 或 releaseSignals;静态发布/增长原型只保留在显式 demo 模式。A3 已接入后端 truth-state,C1 已接入真实 message/extraction/review 状态机,C2 已接入真实 ChangeSet commit,Phase D 已接入真实 execution-summary/dev-sync,D.2 已接入 `POST /execution-handoffs` 创建 PromptPackage + handoff_created progress event,P1 已接入 `POST /execution-handoffs/{prompt_package_id}/consume` 创建 handoff_consumed progress event 并把 execution status 投影为 coding,Release Growth P1 已接入 execution evidence gate,Release Validation P1 已接入 `POST /release-validation-runs` 创建 `ReleaseValidationRun` 快照,Release Feedback P1 已接入 `POST /release-feedback-ingests` 写入 `FeedbackIngest` 并生成 `IterationRecommendation`,Iteration Loop P1 已接入 `POST /iteration-recommendations/{recommendation_id}/change-set-drafts` 把 recommendation 回流到 Change Review;当前仍不调用真实 AI 提取,不把 mock 当真实失败 fallback。`ProjectMemoryPage.tsx` 在真实模式下展示后端 `truth_nodes + semantic_facts` 的 `projectMemory` 投影,accepted/edited ChangeItem 只有 commit 成功后才进入 Project Memory。`frontend/src/features/pmflow-os/components/ProjectStructuredLoop.tsx` 提供 `StatusLabel / EvidenceSnippet / ObjectChangeCard / ProjectConversationPanel / StructuredExtractionPanel / ProjectAICopilotPanel` 等闭环组件,并复用契约化 `InlineNotice` 表达“未确认不得写入”的局部提示。
|
||||
- PMFlow OS 后端入口位于 `backend/app/api/pmflow_os_api/`,projection service 位于 `backend/app/services/pmflow_os_projection_service.py`,Change Review / commit 服务位于 `backend/app/services/pmflow_change_review_service.py`,Pydantic 合同位于 `backend/app/schemas/pmflow_os.py`;当前聚合 `truth_nodes / tasks / semantic_facts / ai_project_control / dev_sync`,`Thread / Message` 来自 `pmflow_threads / pmflow_thread_messages`,`ChangeSet / ChangeItem / Approval / EvidenceLink` 来自 `backend/app/models/pmflow_change_review.py` 对应真实表。C1 已实现 message、`proposed_items` extraction、ChangeSet detail、ChangeItem `accept/edit/defer`;C2 backend/frontend 已实现 `POST /pmflow-os/change-sets/{change_set_id}/commit` 与真实模式前端接入,默认 atomic,将 `Requirement / Decision / Risk` 写入 `truth_nodes`,将 `Task` 写入 `truth_nodes(node_type='task') + tasks`,高风险项强制 approval gate;D.2 已实现 `POST /pmflow-os/projects/{project_id}/execution-handoffs`,校验 task/repo project 边界,生成/复用 `PromptPackage(package_kind='execution_handoff')` 并写 `ProgressEvent(event_type='handoff_created')` 起点;P1 已实现 `POST /pmflow-os/projects/{project_id}/execution-handoffs/{prompt_package_id}/consume`,校验 execution handoff package 后更新 `PromptPackage.package_payload.latest_handoff.handoff_state=consumed` 并写 `ProgressEvent(event_type='handoff_consumed', derived_task_status='coding')`;Release Growth P1 在 `ExecutionSummaryProjection.releaseGate` 中从 `ProgressEvent` 推导发布门禁状态,失败事件 fail-closed 阻塞发布,无证据返回 empty/warning;Release Validation P1 新增 `POST /pmflow-os/projects/{project_id}/release-validation-runs`,复用现有 `ReleaseValidationRun` 模型固化当前 `releaseGate` 快照、人工决策、证据、阻塞原因和下一步动作,并在 `releaseGate.latestValidationRun` 返回最新记录;Release Feedback P1 新增 `POST /pmflow-os/projects/{project_id}/release-feedback-ingests`,复用现有 `FeedbackIngest / IterationRecommendation`,在 `ExecutionSummaryProjection.releaseSignals` 中返回真实发布后 feedback、revenue event 和 iteration recommendation 读模型;Iteration Loop P1 新增 `POST /pmflow-os/projects/{project_id}/iteration-recommendations/{recommendation_id}/change-set-drafts`,把真实 recommendation 转为 open ChangeSet、pending_review Requirement/Task/Risk/Decision items 与 `iteration_recommendation` evidence,重复调用复用既有 draft 并在 `releaseSignals` 标记 `changeSetCreated/changeSetId`;这些步骤都不直接执行本机命令、真实发布命令、外部支付、真实推送、自动 Project Memory commit 或正式任务创建;`ModelDecision / MetricSignal` 在 C2 V1 仍 fail-closed。
|
||||
- PMFlow Launch 后端 Phase B 创建链位于 `backend/app/api/pmflow_launch_api/`、`backend/app/services/pmflow_launch_service.py`、`backend/app/schemas/pmflow_launch.py` 与 `backend/app/models/pmflow_launch.py`;`POST /pmflow-launch/drafts` 只写 `pmflow_launch_drafts`,`POST /pmflow-launch/drafts/{draft_id}/confirm` 在同一事务内写 `projects / user_projects / pmflow_threads / pmflow_thread_messages / truth_nodes / tasks / semantic_facts seed / operation_logs`,并把 `launch_draft_id / thread_id / thread_message_id` 写入正式对象 metadata。
|
||||
- `frontend/src/features/pmflow-os/components/ChangeReviewGroup.tsx`、`WorkbenchPanel.tsx`、`ExecutionBridgeCards.tsx`、`PMFlowOsFrame.tsx`、`AccountControls.tsx`、`FeedbackPrimitives.tsx` 是高保真原型新增的行为组件层:分别承载变更 diff 审查块、工作台对象卡组、执行桥上下文/日志卡片、全局上下文条与 AI 副驾、账号菜单/退出登录确认交互,以及 `PageBanner / InlineNotice / HumanDecisionBar / ConfirmDialog / MobileBottomSheet` 这组提示、提醒、确认与移动端 bottom sheet 基础组件,避免再把这些业务片段内联塞回 `pages/`。
|
||||
- `frontend/vite.config.ts` 现补了固定 vendor chunk 分组(`framework / state-query / ui-radix / icons / charts / markdown`)与业务 chunk 分组(`ai-steward / sstc / governance`),目标是让登录页与基础导航不再背 AI Steward、SSTC、治理页和图表代码。
|
||||
|
|
|
|||
|
|
@ -99,21 +99,25 @@
|
|||
- PMFlow P7 当前推荐任务:参考 `docs/PMFLOW_PRODUCT_DELIVERY_STANDARD.md` 第 7 节;P6 后真实闭环已到 `feedback -> recommendation -> ChangeSet draft -> Change Review`,下一步推荐并行推进 `main-flow-p7-iteration-quality`、`main-flow-p7-reviewer-decision-desk`、`space-p7-personal-queue`,不新增战略页面、不改 router / bottom nav、不绕过 Change Review 人类确认边界
|
||||
- PMFlow OS 产品合同真源:先读 `docs/architecture/PMFLOW_OS_PRODUCT_CONTRACT.md`,再实现 `pmflow-os` 真实后端接入;该文档锁定 `ProjectTruthState / Thread / ChangeSet / ChangeItem / Evidence / Approval / Project Memory / ExecutionContext` 与 `truth_nodes / tasks / semantic_facts / ai_project_control / dev_sync` 的复用边界和后续 API 草案
|
||||
- 新增前端 API 调用:参考 `frontend/src/api/sstc.ts`;Space 工作面只读聚合参考 `frontend/src/api/space.ts`,只消费项目摘要与 PMFlow OS `truth-state / execution-summary`,不写 Project Memory、ChangeSet 状态、ProgressEvent 或 ReleaseValidationRun;Space P3 的 `releaseAttention` 优先从 `truth-state.releases` 的 release validation 投影生成“发布验证阻塞 / 发布验证待确认”,没有验证投影时才回落到 `PMFlowExecutionSummary.releaseGate` 的“发布候选 / 发布门禁阻塞”,所有动作只跳转 `/release-growth?projectId=<id>`;PMFlow OS 真实接入参考 `frontend/src/api/pmflowOs.ts` 的 `getPMFlowTruthState(projectId)`、`getPMFlowExecutionSummary(projectId)`、`createPMFlowExecutionHandoff`、`consumePMFlowExecutionHandoff`、`createPMFlowExecutionProgressEvent`、`createPMFlowReleaseValidationRun`、`createPMFlowReleaseFeedbackIngest`、`createPMFlowIterationRecommendationChangeSetDraft`、`createPMFlowIterationRecommendationGroupChangeSetDraft`、`createPMFlowThreadMessage`、`createPMFlowExtraction`、`getPMFlowChangeSet`、`acceptPMFlowChangeItem`、`editPMFlowChangeItem`、`deferPMFlowChangeItem`、`commitPMFlowChangeSet`;`PMFlowExecutionSummary.releaseGate` 是 ReleaseGrowth 真实发布门禁前端合同,`releaseGate.latestValidationRun` 是最新发布验证记录前端合同,`execution-summary.releaseSignals` 是 Release/Growth 发布后 feedback/revenue/recommendation 信号、recommendation quality grouping 与下一轮 ChangeSet draft 状态前端合同;Project Launch 创建链参考 `frontend/src/api/pmflowLaunch.ts` 的 `createPMFlowLaunchDraft` 与 `confirmPMFlowLaunchDraft`
|
||||
- 新增前端 API 调用:参考 `frontend/src/api/sstc.ts`;Space 工作面只读聚合参考 `frontend/src/api/space.ts`,只消费项目摘要与 PMFlow OS `truth-state / execution-summary`,不写 Project Memory、ChangeSet 状态、ProgressEvent 或 ReleaseValidationRun;Space P6 的 `nextActionQueue` 在 Space 本地读模型内把高风险待确认、发布阻塞/失败证据、handoff consumed 后缺少后续 progress、下一轮 draft、发布后 feedback/revenue/compliance 信号统一排序为“我的下一步”,所有 item 只跳 `/change-review`、`/execution-bridge` 或 `/release-growth`;Space P3 的 `releaseAttention` 优先从 `truth-state.releases` 的 release validation 投影生成“发布验证阻塞 / 发布验证待确认”,没有发布验证投影时才回落到 `PMFlowExecutionSummary.releaseGate` 的“发布候选 / 发布门禁阻塞”,所有动作只跳转 `/release-growth?projectId=<id>`;PMFlow OS 真实接入参考 `frontend/src/api/pmflowOs.ts` 的 `getPMFlowTruthState(projectId)`、`getPMFlowExecutionSummary(projectId)`、`createPMFlowExecutionHandoff`、`consumePMFlowExecutionHandoff`、`createPMFlowExecutionProgressEvent`、`createPMFlowReleaseValidationRun`、`createPMFlowReleaseFeedbackIngest`、`createPMFlowIterationRecommendationChangeSetDraft`、`createPMFlowThreadMessage`、`createPMFlowExtraction`、`getPMFlowChangeSet`、`acceptPMFlowChangeItem`、`editPMFlowChangeItem`、`deferPMFlowChangeItem`、`commitPMFlowChangeSet`;`PMFlowExecutionSummary.releaseGate` 是 ReleaseGrowth 真实发布门禁前端合同,`releaseGate.latestValidationRun` 是最新发布验证记录前端合同,`execution-summary.releaseSignals` 是 Release/Growth 发布后 feedback/revenue/recommendation 信号与下一轮 ChangeSet draft 状态前端合同;Project Launch 创建链参考 `frontend/src/api/pmflowLaunch.ts` 的 `createPMFlowLaunchDraft` 与 `confirmPMFlowLaunchDraft`
|
||||
- AI Steward 页面入口:参考 `frontend/src/pages/ai-steward/`
|
||||
- AI Steward 问答浮窗与上下文显性化组件:参考 `frontend/src/components/ai-steward/FloatingQAChat.tsx`、`frontend/src/components/ai-steward/QAConversationStatusBar.tsx`、`frontend/src/components/ai-steward/QAResponseMetaCard.tsx`
|
||||
- AI Steward QA contract 前端类型(含 `analysis_packet`):参考 `frontend/src/api/aiSteward.ts`
|
||||
- AI Steward handoff 元卡片回归:参考 `frontend/src/components/ai-steward/FloatingQAChat.test.tsx`、`frontend/src/components/ai-steward/QAResponseMetaCard.test.tsx`
|
||||
- AI Steward / QA session 前端 contract:参考 `frontend/src/api/aiSteward.ts`
|
||||
- PMFlow OS 前端 MVP 产品域:参考 `frontend/src/features/pmflow-os/`
|
||||
- 个人空间首页 / 空间层入口前端产品域:参考 `frontend/src/features/personal-space/`、`frontend/src/api/space.ts` 与薄入口 `frontend/src/pages/PersonalSpacePage.tsx`;本地预览路由入口为 `/space`,覆盖继续推进、待我确认、发布关注、发布后反馈关注、下一轮迭代草稿关注、执行中 / AI 正在推进、暂存想法、草案箱与最近项目,点击项目主动作进入 `/pmflow-os?projectId=<id>`,待确认项进入 `/change-review?projectId=<id>&changeSetId=<id>`,执行中项进入 `/execution-bridge?projectId=<id>`,发布关注和发布后反馈关注项进入 `/release-growth?projectId=<id>`,已生成 ChangeSet draft 的下一轮建议进入 `/change-review?projectId=<id>&changeSetId=<id>`;Space 只读聚合项目摘要、PMFlow OS `truth-state.releases / execution-summary.releaseGate / execution-summary.releaseSignals`,发布验证、发布后信号或 iteration draft 缺失时显示明确空态,不虚构验证、反馈、收入、ROI、合规、iteration recommendation 或 ChangeSet draft,不直接写 Project Memory、ChangeSet 状态、execution event、ReleaseValidationRun、FeedbackIngest、RevenueEvent 或 ComplianceCheckRun;暂存想法后端只读聚合尚未接入时必须显示 empty/warning,不得用 mock 数据冒充真实内容;空间层共享左侧栏、顶部条与空间用量组件在 `frontend/src/features/personal-space/components/SpaceChrome.tsx`,`/project-launch` 桌面首次新建壳也复用该组件
|
||||
- 个人空间首页 / 空间层入口前端产品域:参考 `frontend/src/features/personal-space/`、`frontend/src/api/space.ts` 与薄入口 `frontend/src/pages/PersonalSpacePage.tsx`;本地预览路由入口为 `/space`,首屏以 `nextActionQueue` 展示“我的下一步”,并保留继续推进、待我确认、发布关注、发布后反馈关注、下一轮迭代草稿关注、执行中 / AI 正在推进、暂存想法、草案箱与最近项目明细;点击项目主动作进入 `/pmflow-os?projectId=<id>`,待确认项进入 `/change-review?projectId=<id>&changeSetId=<id>`,执行中项进入 `/execution-bridge?projectId=<id>`,发布关注和发布后反馈关注项进入 `/release-growth?projectId=<id>`,已生成 ChangeSet draft 的下一轮建议进入 `/change-review?projectId=<id>&changeSetId=<id>`;Space 只读聚合项目摘要、PMFlow OS `truth-state.releases / execution-summary.releaseGate / execution-summary.releaseSignals`,发布验证、发布后信号或 iteration draft 缺失时显示明确空态,不虚构验证、反馈、收入、ROI、合规、iteration recommendation 或 ChangeSet draft,不直接写 Project Memory、ChangeSet 状态、execution event、ReleaseValidationRun、FeedbackIngest、RevenueEvent 或 ComplianceCheckRun;暂存想法后端只读聚合尚未接入时必须显示 empty/warning,不得用 mock 数据冒充真实内容;空间层共享左侧栏、顶部条与空间用量组件在 `frontend/src/features/personal-space/components/SpaceChrome.tsx`,`/project-launch` 桌面首次新建壳也复用该组件
|
||||
- 新项目启动台 / 首次进入页前端产品域:参考 `frontend/src/features/project-launch/` 与薄入口 `frontend/src/pages/ProjectLaunchPage.tsx`;真实模式默认通过 `frontend/src/api/pmflowLaunch.ts` 调用后端 draft/confirm API,只有显式 `?demo=1` 或 `?dataSource=demo` 才启用 `mockData.ts` 本地演示状态机;本地预览路由入口为 `/project-launch`
|
||||
- Account Settings / `/me` 前端产品域与 P1/P2/P3/P5/P6 偏好合同:参考 `frontend/src/features/account-settings/AccountSettingsPanel.tsx`、`frontend/src/features/pmflow-os/pages/AccountPage.tsx`、`frontend/src/api/accountSettings.ts`、`backend/app/api/account_settings_api/`、`backend/app/schemas/account_settings.py` 与 `backend/app/services/account_settings_service.py`;当前覆盖个人资料、默认语言、AI 回答深度、证据展示偏好、待确认、执行回写、发布门禁和失败提醒通知偏好,移动端使用分组列表与 bottom sheet,账户退出仍复用 `AccountControls.tsx`,不改 OAuth/JWT/auth core;P1 后端合同提供 `GET /account-settings/me` 与 `PUT /account-settings/me`,已由 `backend/app/api/router.py` 挂入核心路由组,按当前 `tenant_id + user_id` 保存账号偏好,前端有 token 时优先账号同步,后端不可用时明确回落 `仅本机暂存 / 保存失败`;P2 在 `frontend/src/api/accountSettings.ts` 提供 `shouldShowAccountNotification` 与 `getAccountNotificationConsumption`,把通知偏好映射为页面内提醒消费模型;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 / showInAppReminder / evidencePolicy / reminderPolicy`;ReleaseGrowthPage 当前消费 `releaseGate` 与 `failureAlert` 两个偏好,只静默强调型提醒,不隐藏 release evidence、阻塞原因、审计状态、validation run、releaseSignals 或下一轮 ChangeSet draft;这些 helper 只控制本地 UI 提醒消费合同,不代表真实邮件、桌面推送或系统通知中心已接入
|
||||
- PMFlow OS 共享 truth store 与主链状态动作:参考 `frontend/src/features/pmflow-os/store.tsx`;当前支持 `?projectId=<id>` 走 React Query 读取真实 `truth-state` 与 `execution-summary`,真实模式下 thread 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 均调后端并 refetch,不本地 promote 入 Project Memory、不伪造 execution event、发布验证记录、发布后信号或下一轮 draft;`?demo=1` 才启用本地 mock 演示状态机和本地动作;项目内 AI 结构化闭环状态机为 `command_idle / ai_extracting / extraction_ready / review_open / writeback_success`
|
||||
- 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`;`ReleaseGrowthPage.tsx` 真实模式展示 `execution-summary.releaseGate`、execution evidence、阻塞原因、下一步动作、`latestValidationRun` 和 `releaseSignals`,缺证据或缺发布后信号时显示 empty/warning,不展示 mock 发布结果;页面可调用真实 API 创建 release validation run、摄取 release feedback,从真实 recommendation 生成下一轮待确认 ChangeSet draft,并按 `recommendationGroups` 批量生成同组下一轮 ChangeSet draft;`ChangeReviewPage.tsx` 会解释来自 `iteration_recommendation_group` evidence 的组、反馈、风险、影响和审查优先级;生成后仍进入 Change Review accept/edit/defer,不自动 commit、不写 Project Memory、不执行部署命令、不接外部支付;同时消费 Account notification preference helper,`releaseGate` 关闭时静默发布门禁强调提醒,`failureAlert` 关闭时静默失败强调提醒,所有真实证据和审计状态仍可见;静态高保真发布/增长数据仅保留在显式 demo 模式
|
||||
- Account Settings / `/me` 前端产品域与 P1-P7 偏好合同:参考 `frontend/src/features/account-settings/AccountSettingsPanel.tsx`、`frontend/src/features/pmflow-os/pages/AccountPage.tsx`、`frontend/src/api/accountSettings.ts`、`backend/app/api/account_settings_api/`、`backend/app/schemas/account_settings.py` 与 `backend/app/services/account_settings_service.py`;当前覆盖个人资料、默认语言、AI 回答深度、证据展示偏好、待确认、执行回写、发布门禁和失败提醒通知偏好,移动端使用分组列表与 bottom sheet,账户退出仍复用 `AccountControls.tsx`,不改 OAuth/JWT/auth core;P1 后端合同提供 `GET /account-settings/me` 与 `PUT /account-settings/me`,已由 `backend/app/api/router.py` 挂入核心路由组,按当前 `tenant_id + user_id` 保存账号偏好,前端有 token 时优先账号同步,后端不可用时明确回落 `仅本机暂存 / 保存失败`;P2 在 `frontend/src/api/accountSettings.ts` 提供 `shouldShowAccountNotification` 与 `getAccountNotificationConsumption`,把通知偏好映射为页面内提醒消费模型;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;P7 将每类通知偏好扩展为 `reminderIntensity=silent|normal|prominent` 与 `evidenceDisplay=compact|expanded`,并新增 `getAccountReminderConsumption(settings, event)` 覆盖 `change_review_high_risk / iteration_draft_ready / release_blocked / execution_stalled`,稳定返回 `notificationTransport=in_app_only / evidencePolicy=preserve_business_evidence / auditPolicy=preserve_audit_state`;ReleaseGrowthPage 当前消费 `releaseGate` 与 `failureAlert` 两个偏好,只静默强调型提醒,不隐藏 release evidence、阻塞原因、审计状态、validation run、releaseSignals 或下一轮 ChangeSet draft;这些 helper 只控制本地 UI 提醒消费合同,不代表真实邮件、桌面推送或系统通知中心已接入
|
||||
- PMFlow OS 共享 truth store 与主链状态动作:参考 `frontend/src/features/pmflow-os/store.tsx`;当前支持 `?projectId=<id>` 走 React Query 读取真实 `truth-state` 与 `execution-summary`,真实模式下 thread 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 均调后端并 refetch,不本地 promote 入 Project Memory、不伪造 execution event、发布验证记录、发布后信号或下一轮 draft;`?demo=1` 才启用本地 mock 演示状态机和本地动作;项目内 AI 结构化闭环状态机为 `command_idle / ai_extracting / extraction_ready / review_open / writeback_success`
|
||||
- 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`;`ReleaseGrowthPage.tsx` 真实模式展示 `execution-summary.releaseGate`、execution evidence、阻塞原因、下一步动作、`latestValidationRun` 和 `releaseSignals`,缺证据或缺发布后信号时显示 empty/warning,不展示 mock 发布结果;页面可调用真实 API 创建 release validation run、摄取 release feedback,并从真实 recommendation 生成下一轮待确认 ChangeSet draft,生成后仍进入 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`
|
||||
- Project Launch 真实/演示分支回归测试:参考 `frontend/src/features/project-launch/useProjectLaunchState.test.ts`
|
||||
- Account Settings 前端合同回归测试:参考 `frontend/src/features/account-settings/accountSettings.test.ts`,覆盖默认偏好、账号同步 fallback、通知偏好消费模型、Release/Growth 通知消费合同、失败事件映射和缺字段 fallback。
|
||||
- Account Settings 前端合同回归测试:参考 `frontend/src/features/account-settings/accountSettings.test.ts`,覆盖默认偏好、账号同步 fallback、通知偏好消费模型、提醒强度/证据展示合同、Release/Growth 通知消费合同、失败事件映射和缺字段 fallback。
|
||||
- 网页问答显式锚点与 AI Steward URL 构造:参考 `frontend/src/components/ai-steward/qaPageAnchor.ts`
|
||||
- Inbox 页面入口:参考 `frontend/src/pages/inbox/`
|
||||
- Inbox `plan_mode` 展开卡片与映射:参考 `frontend/src/components/inbox/InboxListItem.tsx`、`frontend/src/lib/inbox.ts`、`frontend/src/api/aiPmo.ts`
|
||||
|
|
|
|||
|
|
@ -93,6 +93,28 @@ export interface SpaceIterationDraftAttentionItem {
|
|||
href: string;
|
||||
}
|
||||
|
||||
export type SpaceNextActionKind =
|
||||
| "pending_confirmation"
|
||||
| "release_blocked"
|
||||
| "execution_attention"
|
||||
| "iteration_draft"
|
||||
| "feedback_signal";
|
||||
|
||||
export interface SpaceNextActionQueueItem {
|
||||
id: string;
|
||||
projectId: string;
|
||||
projectTitle: string;
|
||||
kind: SpaceNextActionKind;
|
||||
title: string;
|
||||
summary: string;
|
||||
categoryLabel: string;
|
||||
priorityLabel: string;
|
||||
priorityRank: number;
|
||||
severity: "low" | "medium" | "high";
|
||||
updatedAt: string;
|
||||
href: string;
|
||||
}
|
||||
|
||||
export interface SpaceDraftItem {
|
||||
id: string;
|
||||
projectId: string;
|
||||
|
|
@ -115,6 +137,7 @@ export interface SpaceSavedIdea {
|
|||
|
||||
export interface SpaceWorkbench {
|
||||
projects: SpaceWorkbenchProject[];
|
||||
nextActionQueue: SpaceNextActionQueueItem[];
|
||||
pendingConfirmations: SpacePendingConfirmation[];
|
||||
aiProgress: SpaceAIProgressItem[];
|
||||
releaseAttention: SpaceReleaseAttentionItem[];
|
||||
|
|
@ -189,7 +212,7 @@ function objectSummary(value: unknown, fallback = "等待在主流程确认后
|
|||
function objectCreatedAt(value: unknown, fallback: string): string {
|
||||
if (!value || typeof value !== "object") return fallback;
|
||||
const record = value as Record<string, unknown>;
|
||||
return String(record.createdAt || record.created_at || record.updatedAt || record.updated_at || fallback);
|
||||
return String(record.createdAt || record.created_at || record.updatedAt || record.updated_at || record.occurredAt || record.occurred_at || fallback);
|
||||
}
|
||||
|
||||
function objectId(value: unknown, fallback: string): string {
|
||||
|
|
@ -682,16 +705,176 @@ function deriveDrafts(snapshot: ProjectSnapshot): SpaceDraftItem[] {
|
|||
}));
|
||||
}
|
||||
|
||||
function dateValue(item: { updatedAt?: string; updated_at?: string; createdAt?: string; created_at?: string }): number {
|
||||
return new Date(item.updatedAt || item.updated_at || item.createdAt || item.created_at || 0).getTime();
|
||||
function dateValue(item: {
|
||||
updatedAt?: string;
|
||||
updated_at?: string;
|
||||
createdAt?: string;
|
||||
created_at?: string;
|
||||
occurredAt?: string;
|
||||
occurred_at?: string;
|
||||
}): number {
|
||||
return new Date(item.updatedAt || item.updated_at || item.createdAt || item.created_at || item.occurredAt || item.occurred_at || 0).getTime();
|
||||
}
|
||||
|
||||
function sortByDate<T extends { updatedAt?: string; updated_at?: string; createdAt?: string; created_at?: string }>(
|
||||
function sortByDate<T extends { updatedAt?: string; updated_at?: string; createdAt?: string; created_at?: string; occurredAt?: string; occurred_at?: string }>(
|
||||
items: T[]
|
||||
): T[] {
|
||||
return [...items].sort((a, b) => dateValue(b) - dateValue(a));
|
||||
}
|
||||
|
||||
function nextActionPriorityLabel(severity: "low" | "medium" | "high"): string {
|
||||
if (severity === "high") return "高优先级";
|
||||
if (severity === "medium") return "中优先级";
|
||||
return "低优先级";
|
||||
}
|
||||
|
||||
function deriveStaleExecutionActions(snapshot: ProjectSnapshot): SpaceNextActionQueueItem[] {
|
||||
const { project, truth, execution } = snapshot;
|
||||
const events = execution?.recentProgressEvents ?? [];
|
||||
const consumedEvents = events.filter((event) => event.event_type === "handoff_consumed");
|
||||
if (!consumedEvents.length) return [];
|
||||
|
||||
const projectId = String(project.id);
|
||||
const projectTitle = project.title || truth?.project.name || `项目 ${projectId}`;
|
||||
|
||||
return consumedEvents
|
||||
.filter((event) => {
|
||||
const consumedAt = dateValue(event);
|
||||
const taskId = event.task?.id || execution?.execution.taskId || "";
|
||||
return !events.some((candidate) => {
|
||||
if (candidate.id === event.id) return false;
|
||||
const candidateTaskId = candidate.task?.id || execution?.execution.taskId || "";
|
||||
if (taskId && candidateTaskId && candidateTaskId !== taskId) return false;
|
||||
if (candidate.event_type === "handoff_created" || candidate.event_type === "handoff_consumed") return false;
|
||||
return dateValue(candidate) > consumedAt;
|
||||
});
|
||||
})
|
||||
.slice(0, 1)
|
||||
.map((event) => ({
|
||||
id: `next-action-handoff-${projectId}-${event.id}`,
|
||||
projectId,
|
||||
projectTitle,
|
||||
kind: "execution_attention" as const,
|
||||
title: "执行交接已领取,等待进度回写",
|
||||
summary: event.task?.title
|
||||
? `${event.task.title} 已被运行端领取,但还没有 branch、commit、PR、test 或 failed 等后续执行证据。`
|
||||
: "运行端已领取 handoff,但还没有 branch、commit、PR、test 或 failed 等后续执行证据。",
|
||||
categoryLabel: "执行回写",
|
||||
priorityLabel: "中优先级",
|
||||
priorityRank: 30,
|
||||
severity: "medium" as const,
|
||||
updatedAt: objectCreatedAt(event, project.updated_at),
|
||||
href: executionHref(projectId),
|
||||
}));
|
||||
}
|
||||
|
||||
function buildNextActionQueue({
|
||||
pendingConfirmations,
|
||||
aiProgress,
|
||||
releaseAttention,
|
||||
feedbackAttention,
|
||||
iterationDraftAttention,
|
||||
staleExecutionActions,
|
||||
}: {
|
||||
pendingConfirmations: SpacePendingConfirmation[];
|
||||
aiProgress: SpaceAIProgressItem[];
|
||||
releaseAttention: SpaceReleaseAttentionItem[];
|
||||
feedbackAttention: SpaceFeedbackAttentionItem[];
|
||||
iterationDraftAttention: SpaceIterationDraftAttentionItem[];
|
||||
staleExecutionActions: SpaceNextActionQueueItem[];
|
||||
}): SpaceNextActionQueueItem[] {
|
||||
const pendingActions = pendingConfirmations.map((item): SpaceNextActionQueueItem => {
|
||||
const severity = item.riskLevel;
|
||||
return {
|
||||
id: `next-action-${item.id}`,
|
||||
projectId: item.projectId,
|
||||
projectTitle: item.projectTitle,
|
||||
kind: "pending_confirmation",
|
||||
title: item.title,
|
||||
summary: item.summary,
|
||||
categoryLabel: item.source === "approval" ? "审批待确认" : "ChangeSet 待确认",
|
||||
priorityLabel: nextActionPriorityLabel(severity),
|
||||
priorityRank: severity === "high" ? 10 : severity === "medium" ? 34 : 36,
|
||||
severity,
|
||||
updatedAt: item.createdAt,
|
||||
href: item.href,
|
||||
};
|
||||
});
|
||||
|
||||
const releaseBlockedActions = releaseAttention
|
||||
.filter((item) => item.gateStatus === "blocked" || item.severity === "high")
|
||||
.map((item): SpaceNextActionQueueItem => ({
|
||||
id: `next-action-${item.id}`,
|
||||
projectId: item.projectId,
|
||||
projectTitle: item.projectTitle,
|
||||
kind: "release_blocked",
|
||||
title: item.title,
|
||||
summary: item.reasons[0] || item.summary,
|
||||
categoryLabel: item.source === "release_validation" ? "发布验证阻塞" : "发布门禁阻塞",
|
||||
priorityLabel: "高优先级",
|
||||
priorityRank: 20,
|
||||
severity: "high",
|
||||
updatedAt: item.updatedAt,
|
||||
href: item.href,
|
||||
}));
|
||||
|
||||
const activeExecutionActions = aiProgress.map((item): SpaceNextActionQueueItem => ({
|
||||
id: `next-action-${item.id}`,
|
||||
projectId: item.projectId,
|
||||
projectTitle: item.projectTitle,
|
||||
kind: "execution_attention",
|
||||
title: item.title,
|
||||
summary: item.summary,
|
||||
categoryLabel: "执行中",
|
||||
priorityLabel: "中优先级",
|
||||
priorityRank: item.status === "failed" ? 20 : 38,
|
||||
severity: item.status === "failed" ? "high" : "medium",
|
||||
updatedAt: item.updatedAt,
|
||||
href: item.href,
|
||||
}));
|
||||
|
||||
const iterationActions = iterationDraftAttention.map((item): SpaceNextActionQueueItem => ({
|
||||
id: `next-action-${item.id}`,
|
||||
projectId: item.projectId,
|
||||
projectTitle: item.projectTitle,
|
||||
kind: "iteration_draft",
|
||||
title: item.title,
|
||||
summary: item.summary,
|
||||
categoryLabel: item.source === "changeset_draft" ? "下一轮 ChangeSet" : "下一轮建议",
|
||||
priorityLabel: nextActionPriorityLabel(item.severity),
|
||||
priorityRank: 40,
|
||||
severity: item.severity,
|
||||
updatedAt: item.updatedAt,
|
||||
href: item.href,
|
||||
}));
|
||||
|
||||
const feedbackActions = feedbackAttention.map((item): SpaceNextActionQueueItem => ({
|
||||
id: `next-action-${item.id}`,
|
||||
projectId: item.projectId,
|
||||
projectTitle: item.projectTitle,
|
||||
kind: "feedback_signal",
|
||||
title: item.title,
|
||||
summary: item.summary,
|
||||
categoryLabel: item.sourceLabel,
|
||||
priorityLabel: nextActionPriorityLabel(item.severity),
|
||||
priorityRank: item.severity === "high" ? 50 : 56,
|
||||
severity: item.severity,
|
||||
updatedAt: item.updatedAt,
|
||||
href: item.href,
|
||||
}));
|
||||
|
||||
const seen = new Set<string>();
|
||||
return [...pendingActions, ...releaseBlockedActions, ...staleExecutionActions, ...activeExecutionActions, ...iterationActions, ...feedbackActions]
|
||||
.filter((item) => {
|
||||
const key = `${item.kind}:${item.href}:${item.title}`;
|
||||
if (seen.has(key)) return false;
|
||||
seen.add(key);
|
||||
return true;
|
||||
})
|
||||
.sort((a, b) => a.priorityRank - b.priorityRank || dateValue(b) - dateValue(a))
|
||||
.slice(0, 12);
|
||||
}
|
||||
|
||||
async function getProjectSnapshot(project: ProjectSummary): Promise<ProjectSnapshot> {
|
||||
const projectId = String(project.id);
|
||||
const [truthResult, executionResult] = await Promise.allSettled([
|
||||
|
|
@ -717,13 +900,27 @@ export const spaceApi = {
|
|||
const projects = sortByDate(summary.projects).slice(0, 8);
|
||||
const snapshots = await Promise.all(projects.map(getProjectSnapshot));
|
||||
const workbenchProjects = snapshots.map(deriveProject);
|
||||
const pendingConfirmations = sortByDate(snapshots.flatMap(derivePending)).slice(0, 8);
|
||||
const aiProgress = sortByDate(snapshots.flatMap(deriveAIProgress)).slice(0, 8);
|
||||
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 staleExecutionActions = sortByDate(snapshots.flatMap(deriveStaleExecutionActions)).slice(0, 4);
|
||||
return {
|
||||
projects: workbenchProjects.slice(0, 4),
|
||||
pendingConfirmations: sortByDate(snapshots.flatMap(derivePending)).slice(0, 8),
|
||||
aiProgress: sortByDate(snapshots.flatMap(deriveAIProgress)).slice(0, 8),
|
||||
releaseAttention: sortByDate(snapshots.flatMap(deriveReleaseAttention)).slice(0, 8),
|
||||
feedbackAttention: sortByDate(snapshots.flatMap(deriveFeedbackAttention)).slice(0, 8),
|
||||
iterationDraftAttention: sortByDate(snapshots.flatMap(deriveIterationDraftAttention)).slice(0, 8),
|
||||
nextActionQueue: buildNextActionQueue({
|
||||
pendingConfirmations,
|
||||
aiProgress,
|
||||
releaseAttention,
|
||||
feedbackAttention,
|
||||
iterationDraftAttention,
|
||||
staleExecutionActions,
|
||||
}),
|
||||
pendingConfirmations,
|
||||
aiProgress,
|
||||
releaseAttention,
|
||||
feedbackAttention,
|
||||
iterationDraftAttention,
|
||||
savedIdeas: [],
|
||||
drafts: sortByDate(snapshots.flatMap(deriveDrafts)).slice(0, 8),
|
||||
recentProjects: sortByDate(workbenchProjects),
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ vi.mock("@/features/pmflow-os/components/AccountControls", () => ({
|
|||
|
||||
const emptyWorkbench: SpaceWorkbench = {
|
||||
projects: [],
|
||||
nextActionQueue: [],
|
||||
pendingConfirmations: [],
|
||||
aiProgress: [],
|
||||
releaseAttention: [],
|
||||
|
|
@ -40,6 +41,8 @@ describe("PersonalSpacePage", () => {
|
|||
|
||||
expect(await screen.findAllByText("暂无可继续推进的项目")).not.toHaveLength(0);
|
||||
expect(screen.getAllByText("个人工作面")).not.toHaveLength(0);
|
||||
expect(screen.getAllByText("我的下一步")).not.toHaveLength(0);
|
||||
expect(screen.getAllByText("暂无下一步动作")).not.toHaveLength(0);
|
||||
expect(screen.getAllByText("继续推进")).not.toHaveLength(0);
|
||||
expect(screen.getAllByText("待我确认")).not.toHaveLength(0);
|
||||
expect(screen.getAllByText("发布关注")).not.toHaveLength(0);
|
||||
|
|
@ -76,6 +79,78 @@ describe("PersonalSpacePage", () => {
|
|||
href: "/pmflow-os?projectId=17",
|
||||
},
|
||||
],
|
||||
nextActionQueue: [
|
||||
{
|
||||
id: "next-action-change-high",
|
||||
projectId: "17",
|
||||
projectTitle: "真实项目",
|
||||
kind: "pending_confirmation",
|
||||
title: "高风险需求变更待确认",
|
||||
summary: "高风险 ChangeSet 需要先确认。",
|
||||
categoryLabel: "ChangeSet 待确认",
|
||||
priorityLabel: "高优先级",
|
||||
priorityRank: 10,
|
||||
severity: "high",
|
||||
updatedAt: "2026-04-26T08:00:00.000Z",
|
||||
href: "/change-review?projectId=17&changeSetId=93",
|
||||
},
|
||||
{
|
||||
id: "next-action-release-blocked",
|
||||
projectId: "17",
|
||||
projectTitle: "真实项目",
|
||||
kind: "release_blocked",
|
||||
title: "发布验证阻塞",
|
||||
summary: "测试失败,PR 未合并,不能发布。",
|
||||
categoryLabel: "发布验证阻塞",
|
||||
priorityLabel: "高优先级",
|
||||
priorityRank: 20,
|
||||
severity: "high",
|
||||
updatedAt: "2026-04-26T08:00:00.000Z",
|
||||
href: "/release-growth?projectId=17",
|
||||
},
|
||||
{
|
||||
id: "next-action-handoff",
|
||||
projectId: "17",
|
||||
projectTitle: "真实项目",
|
||||
kind: "execution_attention",
|
||||
title: "执行交接已领取,等待进度回写",
|
||||
summary: "运行端已领取 handoff,但还没有后续执行证据。",
|
||||
categoryLabel: "执行回写",
|
||||
priorityLabel: "中优先级",
|
||||
priorityRank: 30,
|
||||
severity: "medium",
|
||||
updatedAt: "2026-04-26T08:00:00.000Z",
|
||||
href: "/execution-bridge?projectId=17",
|
||||
},
|
||||
{
|
||||
id: "next-action-iteration",
|
||||
projectId: "17",
|
||||
projectTitle: "真实项目",
|
||||
kind: "iteration_draft",
|
||||
title: "下一轮迭代建议待确认",
|
||||
summary: "基于发布后反馈生成下一轮优化建议。",
|
||||
categoryLabel: "下一轮 ChangeSet",
|
||||
priorityLabel: "中优先级",
|
||||
priorityRank: 40,
|
||||
severity: "medium",
|
||||
updatedAt: "2026-04-26T08:00:00.000Z",
|
||||
href: "/change-review?projectId=17&changeSetId=92",
|
||||
},
|
||||
{
|
||||
id: "next-action-feedback",
|
||||
projectId: "17",
|
||||
projectTitle: "真实项目",
|
||||
kind: "feedback_signal",
|
||||
title: "新反馈信号",
|
||||
summary: "用户反馈集中在 onboarding 卡顿。",
|
||||
categoryLabel: "用户反馈",
|
||||
priorityLabel: "低优先级",
|
||||
priorityRank: 56,
|
||||
severity: "low",
|
||||
updatedAt: "2026-04-26T08:00:00.000Z",
|
||||
href: "/release-growth?projectId=17",
|
||||
},
|
||||
],
|
||||
pendingConfirmations: [
|
||||
{
|
||||
id: "change-91",
|
||||
|
|
@ -252,6 +327,7 @@ describe("PersonalSpacePage", () => {
|
|||
|
||||
renderWithProviders(<PersonalSpacePage />, ["/space"]);
|
||||
|
||||
expect((await screen.findAllByText("我的下一步")).length).toBeGreaterThan(0);
|
||||
expect(await screen.findByRole("link", { name: /进入主流程/ })).toHaveAttribute(
|
||||
"href",
|
||||
"/pmflow-os?projectId=17"
|
||||
|
|
@ -260,6 +336,14 @@ describe("PersonalSpacePage", () => {
|
|||
"href",
|
||||
"/change-review?projectId=17&changeSetId=91"
|
||||
);
|
||||
expect(screen.getAllByRole("link", { name: /高风险需求变更待确认/ })[0]).toHaveAttribute(
|
||||
"href",
|
||||
"/change-review?projectId=17&changeSetId=93"
|
||||
);
|
||||
expect(screen.getAllByRole("link", { name: /执行交接已领取,等待进度回写/ })[0]).toHaveAttribute(
|
||||
"href",
|
||||
"/execution-bridge?projectId=17"
|
||||
);
|
||||
expect(screen.getAllByRole("link", { name: /AI 正在推进执行链路/ })[0]).toHaveAttribute(
|
||||
"href",
|
||||
"/execution-bridge?projectId=17"
|
||||
|
|
@ -299,5 +383,6 @@ describe("PersonalSpacePage", () => {
|
|||
expect(screen.getAllByText("测试失败: typecheck 未通过")).not.toHaveLength(0);
|
||||
expect(screen.queryByRole("button", { name: /创建发布验证|运行发布验证|重新验证/ })).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole("button", { name: /创建反馈|写入反馈|记录收入|运行合规/ })).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole("button", { name: /生成草案|写入 ChangeSet|创建下一轮/ })).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ import {
|
|||
type SpaceDraftItem,
|
||||
type SpaceFeedbackAttentionItem,
|
||||
type SpaceIterationDraftAttentionItem,
|
||||
type SpaceNextActionKind,
|
||||
type SpaceNextActionQueueItem,
|
||||
type SpacePendingConfirmation,
|
||||
type SpaceReleaseAttentionItem,
|
||||
type SpaceSavedIdea,
|
||||
|
|
@ -53,6 +55,7 @@ export default function PersonalSpacePage() {
|
|||
recentProjects: data?.recentProjects.length ?? 0,
|
||||
pendingConfirmations: data?.pendingConfirmations.length ?? 0,
|
||||
aiProgress: data?.aiProgress.length ?? 0,
|
||||
nextActions: data?.nextActionQueue.length ?? 0,
|
||||
savedIdeas: data?.savedIdeas.length ?? 0,
|
||||
drafts: data?.drafts.length ?? 0,
|
||||
};
|
||||
|
|
@ -91,7 +94,7 @@ function DesktopSpace({
|
|||
isLoading: boolean;
|
||||
isFetching: boolean;
|
||||
onRetry: () => void;
|
||||
sidebarMetrics: { recentProjects: number; pendingConfirmations: number; aiProgress: number; savedIdeas: number; drafts: number };
|
||||
sidebarMetrics: { recentProjects: number; pendingConfirmations: number; aiProgress: number; nextActions: number; savedIdeas: number; drafts: number };
|
||||
}) {
|
||||
return (
|
||||
<div className="hidden h-full min-h-0 md:flex">
|
||||
|
|
@ -105,9 +108,9 @@ function DesktopSpace({
|
|||
<WorkbenchHeader
|
||||
generatedAt={data?.generatedAt}
|
||||
isFetching={isFetching}
|
||||
projectCount={data?.recentProjects.length ?? 0}
|
||||
pendingCount={data?.pendingConfirmations.length ?? 0}
|
||||
aiCount={data?.aiProgress.length ?? 0}
|
||||
nextActionCount={data?.nextActionQueue.length ?? 0}
|
||||
releaseCount={data?.releaseAttention.length ?? 0}
|
||||
feedbackCount={data?.feedbackAttention.length ?? 0}
|
||||
/>
|
||||
|
|
@ -117,6 +120,8 @@ function DesktopSpace({
|
|||
{!isLoading && !error && data ? (
|
||||
<div className="mt-6 grid grid-cols-[minmax(0,1fr)_360px] gap-6 2xl:grid-cols-[minmax(0,1fr)_392px]">
|
||||
<section className="min-w-0 space-y-6">
|
||||
<NextActionQueuePanel items={data.nextActionQueue} />
|
||||
|
||||
<PanelHeader title="继续推进" description="只聚合项目状态和下一步动作;具体确认、提交、回写都回到主流程。" actionLabel="查看项目列表" actionTo="/projects" />
|
||||
<div className="grid gap-4 xl:grid-cols-2 2xl:grid-cols-4">
|
||||
{data.projects.length ? (
|
||||
|
|
@ -205,10 +210,10 @@ function MobileSpace({
|
|||
{isFetching ? <Loader2 className="mt-1 h-4 w-4 animate-spin text-slate-300" /> : <Clock3 className="mt-1 h-4 w-4 text-slate-300" />}
|
||||
</div>
|
||||
<div className="mt-5 grid grid-cols-4 gap-2 text-center">
|
||||
<MobileMetric label="下一步" value={data?.nextActionQueue.length ?? 0} />
|
||||
<MobileMetric label="待确认" value={data?.pendingConfirmations.length ?? 0} />
|
||||
<MobileMetric label="发布" value={data?.releaseAttention.length ?? 0} />
|
||||
<MobileMetric label="反馈" value={data?.feedbackAttention.length ?? 0} />
|
||||
<MobileMetric label="草案" value={data?.drafts.length ?? 0} />
|
||||
<MobileMetric label="执行中" value={data?.aiProgress.length ?? 0} />
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
|
@ -219,6 +224,16 @@ function MobileSpace({
|
|||
<div className="px-4"><WorkbenchError error={error} onRetry={onRetry} compact /></div>
|
||||
) : data ? (
|
||||
<>
|
||||
<MobileSection title="我的下一步" badge={data.nextActionQueue.length}>
|
||||
<div className="space-y-3">
|
||||
{data.nextActionQueue.length ? (
|
||||
data.nextActionQueue.slice(0, 6).map((item) => <MobileNextActionQueueItem key={item.id} item={item} />)
|
||||
) : (
|
||||
<InlineEmpty title="暂无下一步动作" description="没有真实待确认、执行回写、发布阻塞、发布后反馈或下一轮草案时,这里保持空态。" />
|
||||
)}
|
||||
</div>
|
||||
</MobileSection>
|
||||
|
||||
<MobileSection title="继续推进">
|
||||
{data.projects.length ? (
|
||||
<div className="-mx-4 flex snap-x gap-3 overflow-x-auto px-4 pb-1">
|
||||
|
|
@ -320,17 +335,17 @@ function MobileSpace({
|
|||
function WorkbenchHeader({
|
||||
generatedAt,
|
||||
isFetching,
|
||||
projectCount,
|
||||
pendingCount,
|
||||
aiCount,
|
||||
nextActionCount,
|
||||
releaseCount,
|
||||
feedbackCount,
|
||||
}: {
|
||||
generatedAt?: string;
|
||||
isFetching: boolean;
|
||||
projectCount: number;
|
||||
pendingCount: number;
|
||||
aiCount: number;
|
||||
nextActionCount: number;
|
||||
releaseCount: number;
|
||||
feedbackCount: number;
|
||||
}) {
|
||||
|
|
@ -344,15 +359,15 @@ function WorkbenchHeader({
|
|||
</div>
|
||||
<h1 className="mt-2 text-3xl font-semibold tracking-tight text-slate-950">个人工作面</h1>
|
||||
<p className="mt-2 text-sm leading-6 text-slate-500">
|
||||
聚合继续推进、待确认、执行中、暂存想法、草案箱和最近项目;点击项目后回到主流程处理。
|
||||
聚合我的下一步、继续推进、待确认、执行中、暂存想法、草案箱和最近项目;点击后回到主流程处理。
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid min-w-[640px] grid-cols-5 gap-3">
|
||||
<MetricTile label="最近项目" value={projectCount} />
|
||||
<MetricTile label="我的下一步" value={nextActionCount} tone="blue" />
|
||||
<MetricTile label="待确认" value={pendingCount} tone="orange" />
|
||||
<MetricTile label="发布关注" value={releaseCount} tone="rose" />
|
||||
<MetricTile label="反馈关注" value={feedbackCount} tone="emerald" />
|
||||
<MetricTile label="执行中" value={aiCount} tone="blue" />
|
||||
<MetricTile label="执行中" value={aiCount} tone="emerald" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-4 flex items-center gap-2 text-xs text-slate-500">
|
||||
|
|
@ -363,6 +378,48 @@ function WorkbenchHeader({
|
|||
);
|
||||
}
|
||||
|
||||
function NextActionQueuePanel({ items }: { items: SpaceNextActionQueueItem[] }) {
|
||||
return (
|
||||
<section className="rounded-[20px] border border-[#cbd9ee] bg-white shadow-[0_18px_44px_rgba(15,23,42,0.07)]">
|
||||
<PanelHeader
|
||||
title="我的下一步"
|
||||
description="按风险和时效排序,只读聚合待确认、执行回写、发布阻塞、发布后反馈与下一轮草案;所有动作只跳主流程。"
|
||||
badge={items.length}
|
||||
className="px-5 pt-5"
|
||||
/>
|
||||
<div className="mt-4 divide-y divide-[#eef3f8]">
|
||||
{items.length ? (
|
||||
items.slice(0, 8).map((item, index) => <NextActionQueueRow key={item.id} item={item} rank={index + 1} />)
|
||||
) : (
|
||||
<InlineEmpty title="暂无下一步动作" description="没有真实待确认、执行回写、发布阻塞、发布后反馈或下一轮草案时,这里保持空态;不会用 mock 冒充。" />
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function NextActionQueueRow({ item, rank }: { item: SpaceNextActionQueueItem; rank: number }) {
|
||||
const tone = nextActionTone(item);
|
||||
return (
|
||||
<Link to={item.href} className="grid grid-cols-[40px_44px_minmax(0,1fr)_112px] items-center gap-4 px-5 py-4 transition hover:bg-[#f8fbff]">
|
||||
<span className="text-center text-sm font-semibold text-slate-400">{rank}</span>
|
||||
<span className={cn("flex h-10 w-10 items-center justify-center rounded-xl", tone.iconClass)}>
|
||||
<NextActionIcon kind={item.kind} />
|
||||
</span>
|
||||
<span className="min-w-0">
|
||||
<span className="flex items-center gap-2">
|
||||
<span className="truncate text-sm font-semibold text-slate-950">{item.title}</span>
|
||||
<span className={cn("shrink-0 rounded-md px-2 py-0.5 text-xs font-semibold", tone.badgeClass)}>{item.categoryLabel}</span>
|
||||
<span className="shrink-0 rounded-md bg-slate-100 px-2 py-0.5 text-xs font-medium text-slate-500">{item.priorityLabel}</span>
|
||||
</span>
|
||||
<span className="mt-1 block line-clamp-2 text-xs leading-5 text-slate-500">{item.summary}</span>
|
||||
<span className="mt-1 block truncate text-xs text-slate-500">{item.projectTitle} · {formatRelativeTime(item.updatedAt)}</span>
|
||||
</span>
|
||||
<span className="justify-self-end rounded-lg border border-[#cfe0ff] px-3 py-1.5 text-xs font-semibold text-[#2463EB]">{nextActionCta(item)}</span>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
function ProjectCard({ project }: { project: SpaceWorkbenchProject }) {
|
||||
const riskClass =
|
||||
project.riskLabel === "高"
|
||||
|
|
@ -880,6 +937,27 @@ function MobileIterationDraftAttention({ item }: { item: SpaceIterationDraftAtte
|
|||
);
|
||||
}
|
||||
|
||||
function MobileNextActionQueueItem({ item }: { item: SpaceNextActionQueueItem }) {
|
||||
const tone = nextActionTone(item);
|
||||
return (
|
||||
<Link to={item.href} className="block rounded-[18px] border border-[#dbe5f2] bg-white p-4">
|
||||
<div className="flex items-start gap-3">
|
||||
<span className={cn("flex h-10 w-10 shrink-0 items-center justify-center rounded-xl", tone.iconClass)}>
|
||||
<NextActionIcon kind={item.kind} />
|
||||
</span>
|
||||
<span className="min-w-0 flex-1">
|
||||
<span className="flex items-center gap-2">
|
||||
<span className="truncate text-sm font-semibold text-slate-950">{item.title}</span>
|
||||
</span>
|
||||
<span className="mt-1 block line-clamp-2 text-xs leading-5 text-slate-500">{item.summary}</span>
|
||||
<span className="mt-2 block truncate text-xs text-slate-500">{item.projectTitle} · {formatRelativeTime(item.updatedAt)}</span>
|
||||
</span>
|
||||
<span className={cn("shrink-0 rounded-md px-2 py-1 text-xs font-semibold", tone.badgeClass)}>{item.categoryLabel}</span>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
function MobileRecentProjectRow({ project }: { project: SpaceWorkbenchProject }) {
|
||||
return (
|
||||
<Link to={project.href} className="flex items-center justify-between gap-3 py-3 first:pt-3 last:pb-3">
|
||||
|
|
@ -1051,6 +1129,40 @@ function iterationDraftAttentionTone(item: SpaceIterationDraftAttentionItem): {
|
|||
};
|
||||
}
|
||||
|
||||
function nextActionTone(item: SpaceNextActionQueueItem): { iconClass: string; badgeClass: string } {
|
||||
if (item.severity === "high") {
|
||||
return {
|
||||
iconClass: "bg-rose-50 text-rose-600",
|
||||
badgeClass: "bg-rose-50 text-rose-600",
|
||||
};
|
||||
}
|
||||
if (item.severity === "medium") {
|
||||
return {
|
||||
iconClass: "bg-orange-50 text-orange-600",
|
||||
badgeClass: "bg-orange-50 text-orange-600",
|
||||
};
|
||||
}
|
||||
return {
|
||||
iconClass: "bg-emerald-50 text-emerald-600",
|
||||
badgeClass: "bg-emerald-50 text-emerald-600",
|
||||
};
|
||||
}
|
||||
|
||||
function NextActionIcon({ kind }: { kind: SpaceNextActionKind }) {
|
||||
if (kind === "pending_confirmation") return <ShieldCheck className="h-5 w-5" />;
|
||||
if (kind === "release_blocked") return <AlertCircle className="h-5 w-5" />;
|
||||
if (kind === "execution_attention") return <Sparkles className="h-5 w-5" />;
|
||||
if (kind === "iteration_draft") return <FileText className="h-5 w-5" />;
|
||||
return <MessageCircle className="h-5 w-5" />;
|
||||
}
|
||||
|
||||
function nextActionCta(item: SpaceNextActionQueueItem): string {
|
||||
if (item.href.startsWith("/change-review")) return "去确认";
|
||||
if (item.href.startsWith("/execution-bridge")) return "看执行";
|
||||
if (item.href.startsWith("/release-growth")) return item.kind === "feedback_signal" ? "看反馈" : "看发布";
|
||||
return "进入";
|
||||
}
|
||||
|
||||
function formatRelativeTime(value?: string): string {
|
||||
if (!value) return "未记录时间";
|
||||
const timestamp = new Date(value).getTime();
|
||||
|
|
|
|||
Loading…
Reference in New Issue