1538 lines
62 KiB
Markdown
1538 lines
62 KiB
Markdown
# HarborForge 需求草案:Milestone 状态机与 Propose 流程
|
||
|
||
> 状态:草案
|
||
> 更新时间:2026-03-16
|
||
> 目的:先把业务想法固定下来,后续再拆数据库、API、前端和测试实现。
|
||
|
||
---
|
||
|
||
## 0. 开发状态
|
||
|
||
### 2026-03-16 23:49 UTC(第 1 轮:基线盘点 + 自动开发调度)
|
||
|
||
当前进度:已启动按小时推进的开发节奏,先完成一轮代码基线盘点,方便后续按模块逐步落地。
|
||
|
||
本轮已确认:
|
||
- 后端 milestone 旧状态枚举仍在使用:`open / pending / deferred / progressing / closed`
|
||
- 位置:`HarborForge.Backend/app/models/milestone.py`
|
||
- 后端 task 旧状态枚举仍在使用:`open / pending / progressing / closed`
|
||
- 位置:`HarborForge.Backend/app/models/task.py`、`HarborForge.Backend/app/models/models.py`、`HarborForge.Backend/app/schemas/schemas.py`
|
||
- 后端与前端都还有多处写死 `progressing` / `pending` / `deferred`
|
||
- 典型位置:
|
||
- 后端:`app/api/routers/milestones.py`、`app/api/routers/tasks.py`、`cli.py`
|
||
- 前端:`src/types/index.ts`、`src/components/MilestoneFormModal.tsx`、`src/pages/MilestoneDetailPage.tsx`、`src/pages/TaskDetailPage.tsx`
|
||
- `task_type = task` 仍真实存在,且当前主要表现为 `subtype = defect`
|
||
- 后端:`HarborForge.Backend/app/api/routers/tasks.py` 的 `TASK_SUBTYPE_MAP`
|
||
- 前端:`HarborForge.Frontend/src/components/CreateTaskModal.tsx`、`HarborForge.Frontend/src/pages/CreateTaskPage.tsx`
|
||
|
||
当前判断:
|
||
- 下一步最合适的是先做 **P0/P1 之间的“状态枚举与迁移骨架”**,否则后续 milestone / task / propose 的接口实现会反复返工。
|
||
- 具体优先级建议:
|
||
1. 先统一后端 milestone/task 新旧状态枚举与 schema
|
||
2. 再补 migration / 兼容映射
|
||
3. 然后推进动作接口、权限、前端按钮与页面
|
||
|
||
阻塞项:
|
||
- 暂无硬阻塞;但 `closed` / `completed` 的终态与 task reopen 已写入需求,后续实现时要一次性考虑兼容,避免先做死状态流转。
|
||
|
||
### 2026-03-17 00:00 UTC(第 2 轮:后端状态枚举全量替换)
|
||
|
||
本轮做了什么:
|
||
- 将 milestone 和 task 的后端状态枚举全部替换为新值
|
||
- Milestone: `open/pending/deferred/progressing/closed` → `open/freeze/undergoing/completed/closed`
|
||
- Task: `open/pending/progressing/closed` → `open/pending/undergoing/completed/closed`
|
||
- 新增 `MilestoneStatusEnum` 到 schemas.py,milestone 的 create/update/response 现在使用类型化枚举而非裸字符串
|
||
- 新增 `started_at` 字段到 Milestone model 和 MilestoneResponse
|
||
- 更新所有 router(milestones.py, tasks.py, misc.py)中 `progressing` → `undergoing` 引用
|
||
- 更新 task transition 逻辑支持 `completed` 状态自动记录 `finished_on`
|
||
- 更新 CLI status icon 映射和 choices 参数
|
||
|
||
改了哪些关键文件:
|
||
- `HarborForge.Backend/app/models/milestone.py` — enum + started_at
|
||
- `HarborForge.Backend/app/models/task.py` — enum
|
||
- `HarborForge.Backend/app/models/models.py` — enum
|
||
- `HarborForge.Backend/app/schemas/schemas.py` — MilestoneStatusEnum + TaskStatusEnum + started_at
|
||
- `HarborForge.Backend/app/api/routers/milestones.py` — progressing→undergoing
|
||
- `HarborForge.Backend/app/api/routers/tasks.py` — progressing→undergoing, completed handling
|
||
- `HarborForge.Backend/app/api/routers/misc.py` — progressing→undergoing (3处)
|
||
- `HarborForge.Backend/cli.py` — status icons + choices
|
||
|
||
验证结果:
|
||
- 全部 8 个文件 Python AST 语法检查通过
|
||
- 后端无 sqlalchemy 环境无法做运行时验证,但枚举定义和引用已全部对齐
|
||
- grep 确认后端代码中不再有 `progressing`/`deferred` 残留
|
||
|
||
当前阻塞/风险:
|
||
- 数据库迁移脚本尚未编写(MySQL enum 列需要 ALTER TABLE 才能接受新值)
|
||
- 前端仍使用旧状态值,需要同步更新
|
||
- 没有本地 DB/Docker 环境做集成验证
|
||
|
||
下一轮最建议继续做什么:
|
||
- P1.5 数据库迁移骨架:编写 Alembic 或手写 SQL 迁移脚本,处理 MySQL enum 列的 ALTER 和旧数据映射
|
||
- 或者先做前端 TypeScript 类型定义更新(影响面小、独立性强)
|
||
|
||
### 2026-03-17 01:00 UTC(第 3 轮:前端状态枚举全量替换)
|
||
|
||
本轮做了什么:
|
||
- 将前端所有 TypeScript 类型定义和组件中的旧状态枚举替换为新值
|
||
- Task status: `progressing` → `undergoing` + 新增 `completed`
|
||
- Milestone status: `pending/deferred/progressing` → `freeze/undergoing/completed` + 新增 `started_at` 字段
|
||
- 更新 TaskDetailPage 状态流转映射表,匹配新状态机
|
||
- 更新 MilestoneFormModal 下拉选项
|
||
- 清理 MilestoneDetailPage / MilestonesPage 中的 badge class 映射
|
||
|
||
改了哪些关键文件:
|
||
- `HarborForge.Frontend/src/types/index.ts` — Task/Milestone 接口类型
|
||
- `HarborForge.Frontend/src/components/MilestoneFormModal.tsx` — status 下拉选项
|
||
- `HarborForge.Frontend/src/pages/MilestoneDetailPage.tsx` — isProgressing→isUndergoing, badge class
|
||
- `HarborForge.Frontend/src/pages/MilestonesPage.tsx` — badge class
|
||
- `HarborForge.Frontend/src/pages/TaskDetailPage.tsx` — statusActions 流转表
|
||
|
||
验证结果:
|
||
- `npx tsc --noEmit` 零错误通过
|
||
- `grep -rn "progressing|deferred"` 确认前端无旧枚举残留
|
||
- 已 commit: `e60763b`
|
||
|
||
当前阻塞/风险:
|
||
- 数据库迁移脚本仍未编写(MySQL enum 列 ALTER)
|
||
- 前端 CSS badge class 可能需要新增 `status-freeze` / `status-undergoing` / `status-completed` 样式
|
||
- 没有本地运行环境做视觉验证
|
||
|
||
下一轮最建议继续做什么:
|
||
- P1.5 数据库迁移骨架:编写 SQL 迁移脚本处理 MySQL enum 列 ALTER + 旧数据映射(milestone: pending→open, deferred→closed, progressing→undergoing; task: progressing→undergoing)
|
||
- 或 P1.3 Propose 模型新增(后端新建 propose model + schema)
|
||
|
||
### 2026-03-17 02:00 UTC(第 4 轮:Propose 模型 + DB 枚举迁移脚本)
|
||
|
||
本轮做了什么:
|
||
- 新建 Propose 后端模型(P1.3):`app/models/propose.py`,包含 `ProposeStatus` 枚举(open/accepted/rejected)和完整字段(propose_code, title, description, status, project_id, created_by_id, feat_task_id, timestamps)
|
||
- 新增 Propose Pydantic schemas(P1.3):`ProposeStatusEnum`、`ProposeCreate`、`ProposeUpdate`、`ProposeResponse`,`feat_task_id` 仅在 Response 中输出(只读)
|
||
- 编写 MySQL 枚举迁移脚本(P1.5):在 `main.py` 的 `_migrate_schema()` 中新增:
|
||
- milestone status 列:先扩宽 ENUM 接受新旧值 → UPDATE 旧值映射 → 收缩为新枚举
|
||
- task status 列:同样三步迁移(progressing→undergoing)
|
||
- milestones 表 `started_at` 列自动添加
|
||
- 在 startup 的 `create_all` 引入 propose model,新部署会自动建表
|
||
|
||
改了哪些关键文件:
|
||
- `HarborForge.Backend/app/models/propose.py` — 新增
|
||
- `HarborForge.Backend/app/schemas/schemas.py` — 新增 Propose schemas
|
||
- `HarborForge.Backend/app/main.py` — import propose + 枚举迁移 SQL
|
||
|
||
验证结果:
|
||
- 3 个文件 Python AST 语法检查全部通过
|
||
- 前端 `tsc --noEmit` 零错误(无回归)
|
||
- 后端无旧枚举残留(grep 确认)
|
||
- 已 commit: backend `2bea75e`, parent `7afa8cd`
|
||
|
||
当前阻塞/风险:
|
||
- 无本地 MySQL 环境,枚举迁移 SQL 未做实际 DB 验证
|
||
- Propose CRUD router 尚未创建(P6.1)
|
||
- Propose 编码生成逻辑(P1.4 `{proj_code}:P{i:05x}`)尚未实现
|
||
- 前端 Propose 类型定义和页面尚未开始
|
||
|
||
下一轮最建议继续做什么:
|
||
- P6.1 Propose 基础 CRUD router(create/list/get/update),含 propose_code 自动生成
|
||
- 这是让 Propose 功能可用的最小后端闭环
|
||
|
||
### 2026-03-17 03:00 UTC(第 5 轮:Propose CRUD Router + Accept/Reject/Reopen 动作)
|
||
|
||
本轮做了什么:
|
||
- 新建 `app/api/routers/proposes.py`,完整实现 Propose 后端 CRUD + 状态动作(P6.1-P6.4)
|
||
- **CRUD**:list / create / get / update,遵循 project-scoped 路由 `/projects/{project_id}/proposes`
|
||
- **propose_code 自动生成**(P1.4):`{proj_code}:P{i:05x}` 格式,按 project 独立递增
|
||
- **accept 动作**(P6.2):
|
||
- 接收 `milestone_id` 参数,校验 milestone 属同 project 且 status=open
|
||
- 自动创建 `story/feature` task(继承 title/description/created_by,默认 pending)
|
||
- 自动回填 `feat_task_id`
|
||
- **reject 动作**(P6.3):校验 propose 为 open,支持 reason 参数,记录 activity log
|
||
- **reopen 动作**(P6.4):仅 rejected 可 reopen 回 open,复用原 propose 不新建
|
||
- **feat_task_id 只读保护**(P6.5):update 接口中 pop 掉客户端传入的 feat_task_id
|
||
- 在 `main.py` 注册 proposes_router
|
||
- 所有动作均写 activity log
|
||
|
||
改了哪些关键文件:
|
||
- `HarborForge.Backend/app/api/routers/proposes.py` — 新增(280 行)
|
||
- `HarborForge.Backend/app/main.py` — 注册 proposes_router
|
||
|
||
验证结果:
|
||
- Python AST 语法检查 proposes.py / main.py 均通过
|
||
- 已 commit:backend `75ccbcb`,parent `5602b1b`
|
||
|
||
当前阻塞/风险:
|
||
- 权限检查暂用 `check_project_role(min_role="mgr")` 占位,待 P2 权限骨架落地后替换为细粒度 permission
|
||
- propose_code 生成使用 `max(id)+1`,并发场景理论上有微小重复风险(但有 unique 约束保底)
|
||
- 无本地 MySQL 环境做运行时验证
|
||
- 前端 Propose 页面尚未开始(P10)
|
||
|
||
下一轮最建议继续做什么:
|
||
- P10.1-P10.3 前端 Propose 类型定义 + 列表页 + 详情页骨架(让 propose 功能在 UI 上可见)
|
||
- 或 P3.1 Milestone 动作接口(freeze/start/close),使 milestone 状态机真正可用
|
||
|
||
### 2026-03-17 04:00 UTC(第 6 轮:Milestone 动作接口 freeze/start/close + auto-complete)
|
||
|
||
本轮做了什么:
|
||
- 新建 `app/api/routers/milestone_actions.py`(313 行),实现 P3.1-P3.5 的核心后端逻辑
|
||
- **freeze 接口**(P3.2):
|
||
- 校验 milestone 为 open
|
||
- 校验有且仅有 1 个 `maintenance/release` task,否则拒绝并给出明确错误
|
||
- 调用 `check_permission("freeze milestone")`
|
||
- **start 接口**(P3.3):
|
||
- 校验 milestone 为 freeze
|
||
- 解析 `depend_on_milestones` / `depend_on_tasks` JSON,校验所有依赖已 completed
|
||
- 写入 `started_at`,状态改为 undergoing
|
||
- 调用 `check_permission("start milestone")`
|
||
- **close 接口**(P3.4):
|
||
- 允许从 open/freeze/undergoing → closed
|
||
- 支持 reason 参数
|
||
- 调用 `check_permission("close milestone")`
|
||
- **auto-complete hook**(P3.5):
|
||
- `try_auto_complete_milestone()` 在 task 完成时检测是否为唯一 release task,若是则自动将 milestone 置为 completed
|
||
- 已在 tasks.py 的 transition 和 update 两个入口调用
|
||
- **freeze 后限制**(P3.6 partial):
|
||
- milestones.py 的 `create_milestone_task` 现在禁止在 freeze 状态下添加 feature story task
|
||
- 同时禁止在 undergoing/completed/closed 下添加任何 task
|
||
- milestone 序列化新增 `started_at` 字段
|
||
- 所有动作均写 activity log
|
||
|
||
改了哪些关键文件:
|
||
- `HarborForge.Backend/app/api/routers/milestone_actions.py` — 新增
|
||
- `HarborForge.Backend/app/api/routers/milestones.py` — freeze 限制 + started_at 序列化
|
||
- `HarborForge.Backend/app/api/routers/tasks.py` — auto-complete hook(2 处)
|
||
- `HarborForge.Backend/app/main.py` — 注册 milestone_actions_router
|
||
|
||
验证结果:
|
||
- 4 个文件 Python AST 语法检查全部通过
|
||
- 已 commit:backend `7d8c448`
|
||
|
||
当前阻塞/风险:
|
||
- P2 权限骨架尚未落地(`freeze/start/close milestone` permission 需要注册到 permission 表 + 默认角色种子)
|
||
- milestone 编辑限制(P3.6)只做了部分(freeze 后禁新 feature task),`completed/closed` 禁编辑还需在 update 接口加守卫
|
||
- 前端 milestone 按钮(P8.2)尚未开始
|
||
- 无本地 MySQL 做运行时验证
|
||
|
||
下一轮最建议继续做什么:
|
||
- P3.6 补全 milestone 编辑限制(update 接口在 freeze/completed/closed 时拒绝核心字段修改)
|
||
- 或 P10.1-P10.3 前端 Propose 类型 + 列表/详情页骨架
|
||
- 或 P8.1-P8.2 前端 milestone 状态 badge + 动作按钮
|
||
|
||
### 2026-03-17 05:00 UTC(第 7 轮:前端 Propose 完整页面骨架 P10.1-P10.5)
|
||
|
||
本轮做了什么:
|
||
- 新增 `Propose` 类型定义到 `types/index.ts`(P10.1)
|
||
- 新建 `ProposesPage.tsx` — 按 project 筛选的 propose 列表页 + 创建弹窗(P10.1-P10.2)
|
||
- 项目下拉选择
|
||
- propose 卡片展示 status badge / propose_code / title / description / feat_task_id / created_at
|
||
- 内联创建弹窗(title + description)
|
||
- 新建 `ProposeDetailPage.tsx` — 完整详情页 + 状态动作按钮(P10.3-P10.6)
|
||
- 展示所有必要字段:propose_code / title / description / status / created_by / feat_task_id / timestamps
|
||
- `open` 状态显示 Accept + Reject 按钮
|
||
- Accept 弹窗:列出当前 project 下 open 的 milestone 供选择,确认后调用 accept API
|
||
- Reject:prompt 填写 reason,调用 reject API
|
||
- `rejected` 状态显示 Reopen 按钮
|
||
- `accepted` 状态显示 View Generated Task 跳转按钮
|
||
- 所有动作带 loading 状态和错误提示
|
||
- 在 `App.tsx` 注册 `/proposes` 和 `/proposes/:id` 路由
|
||
- 在 `Sidebar.tsx` 新增 💡 Proposes 导航链接
|
||
|
||
改了哪些关键文件:
|
||
- `src/types/index.ts` — 新增 Propose 接口
|
||
- `src/pages/ProposesPage.tsx` — 新增
|
||
- `src/pages/ProposeDetailPage.tsx` — 新增
|
||
- `src/App.tsx` — 路由注册
|
||
- `src/components/Sidebar.tsx` — 侧边栏链接
|
||
|
||
验证结果:
|
||
- `npx tsc --noEmit` 零错误通过
|
||
- 已 commit: `35e7d3a`
|
||
|
||
当前阻塞/风险:
|
||
- ProposeDetailPage 中 "View Generated Task" 跳转使用 `feat_task_id` 作为路径,需确认后端返回的是 task id 数字还是 task code 字符串
|
||
- Propose 编辑功能(P10.7 `open` 时编辑 title/description)尚未实现
|
||
- CSS badge class `status-completed` / `status-closed` 可能需要新增样式
|
||
- 权限可见性控制(按钮仅对有权限的用户显示)尚未实现,当前所有按钮对所有用户可见
|
||
|
||
下一轮最建议继续做什么:
|
||
- P8.1-P8.2 前端 milestone 状态 badge + 动作按钮(freeze/start/close),让 milestone 状态机在 UI 上可操作
|
||
- 或 P3.6 补全 milestone 编辑限制(后端 update 接口守卫)
|
||
|
||
### 2026-03-17 06:00 UTC(第 8 轮:前端 Milestone 状态动作按钮 P8.1-P8.2)
|
||
|
||
本轮做了什么:
|
||
- 在 MilestoneDetailPage 实现 freeze/start/close 三个状态动作按钮(P8.2)
|
||
- **Freeze 按钮**:仅 `open` 状态显示,调用 `POST /projects/{pid}/milestones/{mid}/actions/freeze`
|
||
- **Start 按钮**:仅 `freeze` 状态显示,调用 `POST .../actions/start`
|
||
- **Close 按钮**:`open/freeze/undergoing` 状态显示,带确认步骤 + reason 输入框
|
||
- 所有按钮有 loading 状态和错误提示(后端返回的 detail 直接展示)
|
||
- 新增 `started_at` 显示在 milestone 元数据区域(P8.1)
|
||
- 在 `completed`/`closed` 终态下隐藏编辑按钮和创建 item 按钮
|
||
- 新增 CSS badge 样式:`status-freeze`(紫色)、`status-undergoing`(琥珀)、`status-completed`(绿色)
|
||
|
||
改了哪些关键文件:
|
||
- `HarborForge.Frontend/src/pages/MilestoneDetailPage.tsx` — 动作按钮 + started_at + 终态守卫
|
||
- `HarborForge.Frontend/src/index.css` — 新状态 badge 样式
|
||
|
||
验证结果:
|
||
- `npx tsc --noEmit` 零错误通过
|
||
- 已 commit: `18703d9`
|
||
|
||
当前阻塞/风险:
|
||
- 按钮权限可见性未实现(当前所有用户都能看到按钮,前端无细粒度权限检查机制)
|
||
- freeze/start 按钮的"禁用+提示"未实现(P8.3,如缺少 release task 时应禁用 freeze 并提示)
|
||
- 后端 milestone 编辑限制(P3.6)仍未补全
|
||
|
||
下一轮最建议继续做什么:
|
||
- P9.2 前端 Task 状态动作按钮(open/start/finish/close/reopen),让 task 状态机在 UI 可操作
|
||
- 或 P8.3 freeze/start 按钮的前端前置条件检查与禁用提示
|
||
- 或 P3.6 后端 milestone 编辑限制守卫
|
||
|
||
### 2026-03-17 07:00 UTC(第 9 轮:前端 Task 动作按钮 P9.2 + P9.4)
|
||
|
||
本轮做了什么:
|
||
- 在 TaskDetailPage 实现完整的 task 状态动作按钮(P9.2),替代旧的裸状态名按钮
|
||
- **pending** 状态:▶ Open + ✕ Close
|
||
- **open** 状态:⏵ Start + ✕ Close
|
||
- **undergoing** 状态:✓ Finish + ✕ Close
|
||
- **completed / closed** 状态:↺ Reopen
|
||
- 实现 Finish 弹窗(P9.4):点击 Finish 时弹出模态框,**必须填写完成 comment** 才能确认,comment 先写入再 transition
|
||
- 实现 Close 弹窗:可选填写关闭原因,原因以 `[Close reason]` 前缀写入 comment
|
||
- 所有按钮有 loading 状态和错误提示(后端 detail 直接展示)
|
||
- 终态(completed/closed)和 undergoing 下隐藏 Edit Task 按钮,匹配 P9.3 编辑限制
|
||
|
||
改了哪些关键文件:
|
||
- `HarborForge.Frontend/src/pages/TaskDetailPage.tsx` — 全量重写 actions 区块
|
||
|
||
验证结果:
|
||
- `npx tsc --noEmit` 零错误通过
|
||
- 已 commit: frontend `e6b91e9`, parent `cc2086e`
|
||
|
||
当前阻塞/风险:
|
||
- 后端 transition 接口暂无状态流转合法性校验(如 pending→completed 也能通过),需要 P5.1-P5.6 补强
|
||
- 按钮权限可见性未实现(close/reopen 应按权限控制,当前所有用户可见)
|
||
- Start 按钮未校验 assignee 和 milestone 状态(P5.3 后端校验 + P9.3 前端守卫)
|
||
|
||
下一轮最建议继续做什么:
|
||
- P5.1-P5.2 后端 task transition 合法性校验(加状态流转守卫到 transition 接口,拒绝非法转换)
|
||
- 或 P3.6 后端 milestone 编辑限制(freeze/completed/closed 时拒绝核心字段修改)
|
||
- 或 P8.3 前端 milestone freeze/start 按钮前置条件检查与禁用提示
|
||
|
||
### 2026-03-17 08:00 UTC(第 10 轮:Task 状态机后端校验 P5.1-P5.6)
|
||
|
||
本轮做了什么:
|
||
- 在后端 tasks.py 实现完整的 task 状态流转校验(P5.1-P5.6)
|
||
- 新增 `VALID_TRANSITIONS` 映射表,定义合法流转:
|
||
- pending → open / closed
|
||
- open → undergoing / closed
|
||
- undergoing → completed / closed
|
||
- completed → open (reopen)
|
||
- closed → open (reopen)
|
||
- 新增 `_check_transition()` 校验函数,非法流转返回 400 + 明确错误信息
|
||
- **transition 接口**:加入状态机校验 + pending→open 校验 milestone 必须为 undergoing + open→undergoing 校验 assignee 非空 + reopen 时清除 finished_on
|
||
- **batch transition 接口**:逐条校验,非法转换跳过并返回 skipped 列表(不破坏已有批量行为)
|
||
- **PATCH update 接口**:通过 PATCH 修改 status 时同样强制状态机校验
|
||
|
||
改了哪些关键文件:
|
||
- `HarborForge.Backend/app/api/routers/tasks.py` — +65 行
|
||
|
||
验证结果:
|
||
- Python AST 语法检查通过
|
||
- 已 commit: `589b1cc`
|
||
|
||
当前阻塞/风险:
|
||
- P5.3 assignee 操作者校验("只有当前 assignee 可以 start")未实现——当前只校验 assignee 非空,未校验请求者身份(transition 接口无 current_user 参数)
|
||
- P5.4 undergoing→completed 需要 comment 的校验尚未加入后端(前端已有弹窗,但后端不拦截)
|
||
- 权限检查(close task / reopen closed task / reopen completed task)尚未注册和调用
|
||
- 无本地 DB 做运行时集成验证
|
||
|
||
下一轮最建议继续做什么:
|
||
- P3.6 后端 milestone 编辑限制(freeze/completed/closed 时拒绝核心字段 PATCH)
|
||
- 或 P8.3 前端 milestone freeze/start 按钮禁用+前置条件提示
|
||
- 或 P5.3 补全 transition 接口的 current_user 依赖注入 + assignee 身份校验
|
||
|
||
### 2026-03-17 09:00 UTC(第 11 轮:P3.6 Milestone 编辑限制 — 前后端完整落地)
|
||
|
||
本轮做了什么:
|
||
- 实现 P3.6 milestone 编辑限制的后端 + 前端完整守卫
|
||
- **后端 PATCH 接口**(milestones.py):
|
||
- `completed` / `closed` 终态:直接拒绝所有 PATCH 修改
|
||
- `freeze` / `undergoing`:禁止修改范围相关字段(title, description, due_date, planned_release_date, depend_on_milestones, depend_on_tasks),返回具体被阻止的字段名
|
||
- 禁止通过 PATCH 修改 status(强制使用 action endpoints)
|
||
- **后端 DELETE 接口**(milestones.py):
|
||
- `undergoing` / `completed` 状态禁止删除 milestone
|
||
- **前端 MilestoneDetailPage**:
|
||
- `open` 状态:正常显示 Edit Milestone 按钮
|
||
- `freeze` / `undergoing`:隐藏编辑按钮,显示 "⚠ scope fields are locked" 提示
|
||
- `completed` / `closed`:不显示编辑入口(已有逻辑保持)
|
||
|
||
改了哪些关键文件:
|
||
- `HarborForge.Backend/app/api/routers/milestones.py` — PATCH 守卫 + DELETE 守卫(+32 行)
|
||
- `HarborForge.Frontend/src/pages/MilestoneDetailPage.tsx` — 编辑按钮可见性调整
|
||
|
||
验证结果:
|
||
- Python AST 语法检查通过
|
||
- `npx tsc --noEmit` 零错误通过
|
||
- 已 commit:backend `314040c`,frontend `a4b4ffc`,parent `6b265ea`
|
||
|
||
当前阻塞/风险:
|
||
- feature story task 在 freeze 后的编辑限制只做了"不能新增",但已有 feature task 的核心字段编辑限制尚未加入 task PATCH 接口
|
||
- P2 权限骨架仍未落地,所有动作接口使用通用 role check 占位
|
||
- 无本地 MySQL/Docker 做运行时集成验证
|
||
|
||
下一轮最建议继续做什么:
|
||
- P8.3 前端 milestone freeze/start 按钮前置条件检查与禁用提示(freeze 时检查 release task 存在性,start 时检查依赖完成情况)
|
||
- 或 P5.3 后端 task transition 补全 current_user 依赖注入 + assignee 身份校验
|
||
- 或 P5.4 后端 undergoing→completed 强制要求 comment
|
||
|
||
### 2026-03-17 10:00 UTC(第 12 轮:P8.3 Milestone freeze/start 按钮前置条件检查与禁用提示)
|
||
|
||
本轮做了什么:
|
||
- 新增后端 `GET /projects/{pid}/milestones/{mid}/actions/preflight` 端点(milestone_actions.py)
|
||
- 当 milestone 为 `open` 时,返回 freeze 是否允许 + 原因(检查 maintenance/release task 数量)
|
||
- 当 milestone 为 `freeze` 时,返回 start 是否允许 + 原因(检查依赖 milestone/task 是否已 completed)
|
||
- 只读接口,不做任何数据变更
|
||
- 前端 MilestoneDetailPage 集成 preflight:
|
||
- milestone 加载后自动调用 preflight API
|
||
- freeze 按钮:条件不满足时禁用 + 显示 ⚠ 原因提示(如"No maintenance/release task found")
|
||
- start 按钮:依赖未完成时禁用 + 显示 ⚠ 原因提示
|
||
- 动作执行成功后自动刷新 preflight 状态
|
||
- 鼠标 hover 按钮时 title 也显示原因
|
||
|
||
改了哪些关键文件:
|
||
- `HarborForge.Backend/app/api/routers/milestone_actions.py` — 新增 preflight endpoint(+84 行)
|
||
- `HarborForge.Frontend/src/pages/MilestoneDetailPage.tsx` — preflight 状态 + 按钮禁用逻辑
|
||
|
||
验证结果:
|
||
- Python AST 语法检查通过
|
||
- `npx tsc --noEmit` 零错误通过
|
||
- 已 commit:backend `7a16639`,frontend `faf7842`
|
||
|
||
当前阻塞/风险:
|
||
- P2 权限骨架仍未落地,preflight 不检查用户是否有 freeze/start 权限(只检查业务前置条件)
|
||
- 无本地 MySQL/运行环境做集成验证
|
||
- P5.3 task transition 的 assignee 身份校验仍缺失
|
||
- P5.4 后端 undergoing→completed 强制要求 comment 仍缺失
|
||
|
||
下一轮最建议继续做什么:
|
||
- P5.3 后端 task transition 补全 assignee 身份校验(open→undergoing 时校验操作者 == assignee)
|
||
- 或 P5.4 后端 undergoing→completed 强制要求 completion comment
|
||
- 或 P10.7 前端 propose 编辑限制(open 时允许编辑 title/description)
|
||
|
||
### 2026-03-17 11:00 UTC(第 13 轮:P5.3 Assignee 身份校验 + P5.4 完成 Comment 强制要求)
|
||
|
||
本轮做了什么:
|
||
- 在后端 transition 接口补全 P5.3 和 P5.4 两项关键校验
|
||
- **P5.3 Assignee 身份校验**:
|
||
- transition 接口新增 `current_user` 依赖注入
|
||
- `open → undergoing`:除校验 assignee 非空外,新增校验 `current_user.id == task.assignee_id`,非 assignee 返回 403
|
||
- `undergoing → completed`:同样校验操作者必须为 assignee
|
||
- **P5.4 完成 Comment 强制要求**:
|
||
- transition 接口新增 `TransitionBody` 请求体(含可选 `comment` 字段)
|
||
- `undergoing → completed` 时强制要求 `comment` 非空,否则返回 400
|
||
- 后端自动创建 Comment 记录(复用 `models.Comment`),不再依赖前端单独调用 comment API
|
||
- transition 所有状态变更现在自动写 `log_activity`,记录 old/new status
|
||
- auto-complete milestone 时传入 `current_user.id` 替代原先的 `None`
|
||
- **前端 TaskDetailPage 同步更新**:
|
||
- `doAction` 改为接收 `body` 参数,直接在 transition POST 请求体中传递 `{ comment }`
|
||
- Finish 流程不再单独调用 `POST /comments`,改为后端统一处理
|
||
- Close 流程的 reason comment 同样通过请求体传递
|
||
|
||
改了哪些关键文件:
|
||
- `HarborForge.Backend/app/api/routers/tasks.py` — transition 接口重构(+40 行)
|
||
- `HarborForge.Frontend/src/pages/TaskDetailPage.tsx` — doAction/finish/close 逻辑简化
|
||
|
||
验证结果:
|
||
- Python AST 语法检查通过
|
||
- `npx tsc --noEmit` 零错误通过
|
||
- 已 commit:backend `ffb0fa6`,frontend `d6a45c3`
|
||
|
||
当前阻塞/风险:
|
||
- P2 权限骨架仍未落地(close task / reopen 权限未注册和调用)
|
||
- batch transition 接口尚未同步 P5.3/P5.4 校验(无 current_user,无 comment body)
|
||
- 无本地 MySQL/运行环境做集成验证
|
||
|
||
下一轮最建议继续做什么:
|
||
- P10.7 前端 propose 编辑限制(open 时允许编辑 title/description,其他状态禁止)
|
||
- 或 P9.3 前端 task 编辑权限守卫(按 assignee/status 显示/隐藏编辑入口)
|
||
- 或 P5.7 后端 task PATCH 接口编辑权限服务端防守
|
||
|
||
### 2026-03-17 12:00 UTC(第 14 轮:P5.7 Task 编辑权限服务端防守 + P9.3 前端守卫)
|
||
|
||
本轮做了什么:
|
||
- 实现 P5.7 后端 task PATCH 接口的 status+assignee 编辑限制
|
||
- 实现 P9.3 前端 task 编辑按钮的 status+assignee 可见性守卫
|
||
- **后端 PATCH 接口**(tasks.py):
|
||
- `undergoing` / `completed` / `closed`:禁止修改 body 字段(status 字段仍可通过 PATCH 传递以走状态机)
|
||
- `open` + `assignee_id` 非空:仅 assignee 本人或 admin 可编辑 body 字段,否则返回 403
|
||
- `open` + `assignee_id` 为空 / `pending`:沿用原有 `ensure_can_edit_task` 通用权限
|
||
- **前端 TaskDetailPage**(P9.3):
|
||
- `canEditTask` 重构为考虑 status + assignee 的完整守卫
|
||
- `undergoing/completed/closed`:Edit Task 按钮不显示
|
||
- `open + assigned`:仅 assignee 和 admin 看到编辑按钮
|
||
- `open + unassigned` / `pending`:项目成员均可编辑
|
||
- 移除冗余的 `!isTerminal && task.status !== 'undergoing'` 外层条件(已内化到 canEditTask)
|
||
|
||
改了哪些关键文件:
|
||
- `HarborForge.Backend/app/api/routers/tasks.py` — PATCH 接口新增 P5.7 守卫(+31 行)
|
||
- `HarborForge.Frontend/src/pages/TaskDetailPage.tsx` — canEditTask 重构
|
||
|
||
验证结果:
|
||
- Python AST 语法检查通过
|
||
- `npx tsc --noEmit` 零错误通过
|
||
- 已 commit:backend `7542f2d`,frontend `638427d`
|
||
|
||
当前阻塞/风险:
|
||
- P2 权限骨架仍未落地(close/reopen 权限未注册)
|
||
- feature story task 在 freeze 后的编辑限制(P3.6 补充)尚未加入 task PATCH 接口
|
||
- batch transition 接口尚未同步 P5.3/P5.4 校验
|
||
- 无本地 MySQL/运行环境做集成验证
|
||
|
||
下一轮最建议继续做什么:
|
||
- P10.7 前端 propose 编辑限制(open 时允许编辑 title/description,其他状态禁止)
|
||
- 或 P9.6 前端创建 task 限制(禁止通用 create 创建 feature story / release maintenance task)
|
||
- 或 P5.2 补全 pending→open 依赖检查(通用依赖检查 helper)
|
||
|
||
### 2026-03-17 13:00 UTC(第 15 轮:P9.6 Task 创建限制 — 前后端完整落地)
|
||
|
||
本轮做了什么:
|
||
- 实现 P9.6:禁止通过通用 create task 创建 `story/feature` 和 `maintenance/release` 类型的 task
|
||
- **后端 tasks.py**:
|
||
- 新增 `RESTRICTED_TYPE_SUBTYPES` 集合,定义受限类型组合
|
||
- `_validate_task_type_subtype()` 增加 `allow_restricted` 参数,默认阻止受限组合
|
||
- 通用 `POST /tasks` 端点自动拦截,返回 400 + 明确错误信息
|
||
- propose accept 内部创建走 ORM 直接写入,不受此限制
|
||
- **后端 milestones.py**:
|
||
- `POST /{milestone_id}/tasks` 端点增加 P9.6 守卫:直接拒绝 `story/feature` 创建
|
||
- `maintenance/release` 不受限(milestone endpoint 是其合法创建入口)
|
||
- **前端 CreateTaskModal + CreateTaskPage**:
|
||
- Story subtypes 移除 `feature`(仅保留 improvement、refactor)
|
||
- Maintenance subtypes 移除 `release`(仅保留 deploy)
|
||
- 两个创建表单同步更新
|
||
|
||
改了哪些关键文件:
|
||
- `HarborForge.Backend/app/api/routers/tasks.py` — RESTRICTED_TYPE_SUBTYPES + 校验增强
|
||
- `HarborForge.Backend/app/api/routers/milestones.py` — story/feature 创建拦截
|
||
- `HarborForge.Frontend/src/components/CreateTaskModal.tsx` — 移除受限 subtypes
|
||
- `HarborForge.Frontend/src/pages/CreateTaskPage.tsx` — 移除受限 subtypes
|
||
|
||
验证结果:
|
||
- Python AST 语法检查 tasks.py / milestones.py 均通过
|
||
- `npx tsc --noEmit` 零错误通过
|
||
- 已 commit:backend `c18b8f3`,frontend `2897172`,parent `6441ea5`
|
||
|
||
当前阻塞/风险:
|
||
- `maintenance/release` task 的受控创建入口尚未明确实现(当前通过 milestone endpoint 可创建,但无专门的 release setup 流程)
|
||
- P2 权限骨架仍未落地
|
||
- batch transition 接口尚未同步 P5.3/P5.4 校验
|
||
- 无本地 MySQL/运行环境做集成验证
|
||
|
||
下一轮最建议继续做什么:
|
||
- P10.7 前端 propose 编辑限制(open 时显示编辑入口,accepted/rejected 禁止编辑主体)
|
||
- 或 P2.1 后端权限枚举注册(freeze/start/close milestone + close/reopen task + accept/reject/reopen propose)
|
||
- 或 P5.2 补全 pending→open 通用依赖检查 helper
|
||
|
||
### 2026-03-17 14:00 UTC(第 16 轮:P10.7 前端 Propose 编辑限制)
|
||
|
||
本轮做了什么:
|
||
- 实现 P10.7:Propose 详情页编辑功能,仅 `open` 状态可编辑 title 和 description
|
||
- **ProposeDetailPage 新增**:
|
||
- ✏️ Edit 按钮:仅 `open` 状态显示(与 Accept/Reject 同行)
|
||
- 编辑弹窗:title 输入框 + description 多行文本框
|
||
- 调用 `PATCH /projects/{pid}/proposes/{id}` 提交修改
|
||
- title 不能为空时 Save 按钮禁用
|
||
- 保存成功后自动刷新详情
|
||
- loading 状态和错误提示完整
|
||
- `accepted` / `rejected` 状态下不显示 Edit 按钮(按钮渲染在 `open` 条件分支内),实现主体不可编辑
|
||
|
||
改了哪些关键文件:
|
||
- `HarborForge.Frontend/src/pages/ProposeDetailPage.tsx` — +75 行(编辑状态 + 弹窗 + handler)
|
||
|
||
验证结果:
|
||
- `npx tsc --noEmit` 零错误通过
|
||
- 已 commit:frontend `208538f`
|
||
|
||
当前阻塞/风险:
|
||
- 编辑权限仅在后端校验(creator / admin / 项目管理员),前端未根据当前用户身份隐藏按钮
|
||
- P2 权限骨架仍未落地(所有动作接口使用通用 role check 占位)
|
||
- batch transition 接口尚未同步 P5.3/P5.4 校验
|
||
- 无本地 MySQL/运行环境做集成验证
|
||
|
||
下一轮最建议继续做什么:
|
||
- P2.1 后端权限枚举注册(freeze/start/close milestone + close/reopen task + accept/reject/reopen propose),这是剩余功能中最基础的依赖项
|
||
- 或 P4.1 通用依赖检查 helper 抽取(milestone start 和 task pending→open 复用)
|
||
- 或 P5.4 补全后端 undergoing→completed 强制 comment 校验(前端已有弹窗,后端尚缺校验)
|
||
|
||
### 2026-03-17 15:00 UTC(第 17 轮:P2.1 权限枚举注册 + 全动作接口权限接入)
|
||
|
||
本轮做了什么:
|
||
- 实现 P2.1:在 `DEFAULT_PERMISSIONS` 注册 9 个新权限,并将所有动作接口从占位 `check_project_role` 切换为真正的 `check_permission`
|
||
- **新增权限**(init_wizard.py):
|
||
- `milestone.freeze` / `milestone.start` / `milestone.close`(category: milestone)
|
||
- `task.close` / `task.reopen_closed` / `task.reopen_completed`(category: task)
|
||
- `propose.accept` / `propose.reject` / `propose.reopen`(category: propose)
|
||
- **milestone_actions.py**:将旧字符串 `"freeze milestone"` 等替换为点分权限名 `"milestone.freeze"` 等
|
||
- **proposes.py**:accept/reject/reopen 三个端点从 `check_project_role(min_role="mgr")` 替换为 `check_permission(..., "propose.accept/reject/reopen")`,移除所有 TODO 占位注释
|
||
- **tasks.py**:transition 接口新增:
|
||
- `new_status == "closed"` 时调用 `check_permission(..., "task.close")`
|
||
- reopen 时根据来源区分 `task.reopen_completed` / `task.reopen_closed`
|
||
- admin 角色通过 `init_admin_role()` 自动获得所有新权限(无需手动配置)
|
||
|
||
改了哪些关键文件:
|
||
- `HarborForge.Backend/app/init_wizard.py` — 新增 9 个权限定义
|
||
- `HarborForge.Backend/app/api/routers/milestone_actions.py` — 权限名标准化
|
||
- `HarborForge.Backend/app/api/routers/proposes.py` — 替换为 check_permission
|
||
- `HarborForge.Backend/app/api/routers/tasks.py` — 新增 close/reopen 权限检查
|
||
|
||
验证结果:
|
||
- 4 个文件 Python AST 语法检查全部通过
|
||
- grep 确认无残留旧权限字符串和 TODO 占位
|
||
- 已 commit:backend `3afbbc2`,parent `054cf43`
|
||
|
||
当前阻塞/风险:
|
||
- P2.2 默认角色权限种子尚未细化(当前只有 admin 全量、guest 只读,dev/mgr 等自定义角色需要手动配置新权限)
|
||
- 前端按钮尚未根据用户权限做可见性控制(当前所有按钮对所有项目成员可见)
|
||
- batch transition 接口尚未同步权限检查
|
||
- 无本地 MySQL/运行环境做集成验证
|
||
|
||
下一轮最建议继续做什么:
|
||
- P4.1 通用依赖检查 helper 抽取(milestone start 和 task pending→open 复用),减少重复逻辑
|
||
- 或 P2.2 默认角色权限种子配置(为 dev/mgr 等常用角色预置合理的新权限组合)
|
||
- 或 P7.1 移除 `task_type = task`(代码清理项,独立性强)
|
||
|
||
### 2026-03-17 16:00 UTC(第 18 轮:P7.1-P7.2 移除 task_type='task' — 前后端 + DB 迁移)
|
||
|
||
本轮做了什么:
|
||
- 完整实现 P7.1(后端)和 P7.2(前端):移除 `task_type = task` 类型
|
||
- **后端 schemas.py**:从 `TaskTypeEnum` 中移除 `TASK = "task"`,默认值改为 `ISSUE`
|
||
- **后端 tasks.py**:从 `TASK_SUBTYPE_MAP` 中移除 `'task': {'defect'}` 条目
|
||
- **后端 task.py(model)**:列默认值从 `"task"` 改为 `"issue"`
|
||
- **后端 misc.py**:import 默认值从 `"task"` 改为 `"issue"`
|
||
- **后端 main.py(DB 迁移)**:
|
||
- 新增迁移步骤:`UPDATE tasks SET task_type='issue' WHERE task_type='task'`
|
||
- 新表创建默认值改为 `'issue'`
|
||
- **前端 types/index.ts**:Task 类型联合移除 `'task'`
|
||
- **前端 CreateTaskModal.tsx**:移除 task 类型条目,默认 task_type 改为 `'issue'`
|
||
- **前端 CreateTaskPage.tsx**:同上
|
||
|
||
改了哪些关键文件:
|
||
- `HarborForge.Backend/app/schemas/schemas.py` — 移除 TASK enum
|
||
- `HarborForge.Backend/app/models/task.py` — 默认值改为 issue
|
||
- `HarborForge.Backend/app/api/routers/tasks.py` — 移除 task subtype map
|
||
- `HarborForge.Backend/app/api/routers/misc.py` — 默认值改为 issue
|
||
- `HarborForge.Backend/app/main.py` — DB 迁移脚本
|
||
- `HarborForge.Frontend/src/types/index.ts` — 类型联合更新
|
||
- `HarborForge.Frontend/src/components/CreateTaskModal.tsx` — 移除 task 类型
|
||
- `HarborForge.Frontend/src/pages/CreateTaskPage.tsx` — 移除 task 类型
|
||
|
||
验证结果:
|
||
- 5 个后端文件 Python AST 语法检查全部通过
|
||
- `npx tsc --noEmit` 零错误通过
|
||
- grep 确认前后端无残留 `task_type='task'` 引用
|
||
- 已 commit:backend `89e3bcd`,frontend `d399668`
|
||
|
||
当前阻塞/风险:
|
||
- P7.3 历史数据处理:DB 迁移脚本已自动将 `task_type='task'` 转为 `'issue'`,但无本地 MySQL 做实际验证
|
||
- `defect` subtype 现在仅属于 `issue` 类型(原先 `task` 和 `issue` 都有 defect),语义上更清晰
|
||
- 无本地运行环境做集成验证
|
||
|
||
下一轮最建议继续做什么:
|
||
- P4.1 通用依赖检查 helper 抽取(milestone start 和 task pending→open 复用)
|
||
- 或 P2.2 默认角色权限种子配置(为 dev/mgr 等常用角色预置合理的新权限组合)
|
||
- 或 P11.1 前端权限列表展示新增权限
|
||
|
||
### 2026-03-17 17:00 UTC(第 19 轮:P4.1 通用依赖检查 Helper 抽取)
|
||
|
||
本轮做了什么:
|
||
- 新建 `app/services/dependency_check.py`,实现 P4.1 通用依赖检查能力
|
||
- `check_milestone_deps()` 函数:接收 `depend_on_milestones` + `depend_on_tasks` JSON 字段,返回结构化 `DepCheckResult`(ok/blockers/reason)
|
||
- 内部 helper `_parse_json_ids()` 安全解析 JSON ID 列表,消除重复的 try/except 模式
|
||
- 重构 `milestone_actions.py`:preflight 和 start 两个端点的依赖检查从 ~50 行内联代码缩减为各 ~5 行调用
|
||
- 移除了 `milestone_actions.py` 中不再需要的 `import json`
|
||
|
||
改了哪些关键文件:
|
||
- `HarborForge.Backend/app/services/dependency_check.py` — 新增(~95 行)
|
||
- `HarborForge.Backend/app/api/routers/milestone_actions.py` — 重构,净减 ~50 行
|
||
|
||
验证结果:
|
||
- 2 个文件 Python AST 语法检查通过
|
||
- 已 commit:backend `c6b14ac`,parent `0bc2379`
|
||
|
||
当前阻塞/风险:
|
||
- task `pending→open` 的依赖检查尚未接入此 helper(当前 tasks.py 只校验 milestone 为 undergoing,不检查 task 自身的 depend_on 字段)
|
||
- P2.2 默认角色权限种子仍未细化(dev/mgr 角色无预置新权限)
|
||
- 无本地 MySQL/运行环境做集成验证
|
||
|
||
下一轮最建议继续做什么:
|
||
- P4.3 在 task transition `pending→open` 中接入 `check_milestone_deps` 检查 task 自身依赖
|
||
- 或 P2.2 默认角色权限种子配置
|
||
- 或 P11.1 前端权限列表展示新增权限
|
||
|
||
### 2026-03-17 18:00 UTC(第 20 轮:P4.3 Task depend_on 依赖检查接入 pending→open)
|
||
|
||
本轮做了什么:
|
||
- 实现 P4.3:在 task `pending→open` transition 中接入 `check_task_deps()` 依赖检查
|
||
- 在 `dependency_check.py` 新增 `check_task_deps()` 函数,专门处理 task 的 `depend_on` 字段(JSON 编码的 task ID 列表)
|
||
- 在 `tasks.py` transition 接口的 `pending→open` 分支中,milestone 状态校验通过后,继续检查 task 自身的 `depend_on` 依赖是否全部 completed
|
||
- 不满足时返回 400 + 具体未完成的 task ID 列表
|
||
|
||
改了哪些关键文件:
|
||
- `HarborForge.Backend/app/services/dependency_check.py` — 新增 `check_task_deps()`(+33 行)
|
||
- `HarborForge.Backend/app/api/routers/tasks.py` — import + pending→open 新增依赖检查(+7 行)
|
||
|
||
验证结果:
|
||
- 2 个文件 Python AST 语法检查通过
|
||
- 已 commit:`0c75045`
|
||
|
||
当前阻塞/风险:
|
||
- batch transition 接口尚未同步 P4.3 依赖检查
|
||
- P2.2 默认角色权限种子仍未细化(dev/mgr 角色无预置新权限)
|
||
- 无本地 MySQL/运行环境做集成验证
|
||
|
||
下一轮最建议继续做什么:
|
||
- P2.2 默认角色权限种子配置(为 dev/mgr 等常用角色预置合理的新权限组合)
|
||
- 或 P11.1 前端权限列表展示新增权限
|
||
- 或 P12.1 CLI 更新 propose 子命令
|
||
|
||
### 2026-03-17 19:00 UTC(第 21 轮:P2.2 默认角色权限种子 — mgr + dev 角色)
|
||
|
||
本轮做了什么:
|
||
- 实现 P2.2:在 `init_wizard.py` 新增 `mgr` 和 `dev` 两个默认角色,各带预置权限
|
||
- **mgr(Manager)权限**:
|
||
- 项目管理:project.read/write/manage_members
|
||
- 全量 task/milestone CRUD
|
||
- 所有新增动作权限:milestone.freeze/start/close、task.close/reopen_closed/reopen_completed、propose.accept/reject/reopen
|
||
- monitor.read
|
||
- **dev(Developer)权限**:
|
||
- project.read(只读项目信息)
|
||
- task.create/read/write(日常 task 操作)
|
||
- milestone.read(只读 milestone)
|
||
- task.close/reopen_closed/reopen_completed(task 状态管理)
|
||
- monitor.read
|
||
- 不含 milestone 动作权限和 propose 审批权限(这些归 mgr/admin)
|
||
- 重构 `init_admin_role()` 为统一的角色种子系统:
|
||
- `_DEFAULT_ROLES` 列表定义所有默认角色 + 权限集
|
||
- `_ensure_role()` 幂等创建角色
|
||
- `_sync_role_permissions()` 增量同步权限(只加不删,不影响手动配置)
|
||
- admin 仍获全量权限,guest 仍仅获 `*.read` 权限
|
||
|
||
改了哪些关键文件:
|
||
- `HarborForge.Backend/app/init_wizard.py` — 重构角色种子系统(+84 行,-52 行)
|
||
|
||
验证结果:
|
||
- Python AST 语法检查通过
|
||
- 前端 `tsc --noEmit` 零错误(无回归)
|
||
- 已 commit:backend `8e38d4c`
|
||
|
||
当前阻塞/风险:
|
||
- 权限同步是增量模式(只加不删),已有部署中手动配置的权限不会被移除,但也意味着降级角色需手动操作
|
||
- 前端按钮尚未根据用户权限做可见性控制(P11)
|
||
- batch transition 接口尚未同步 P5.3/P5.4 校验
|
||
- 无本地 MySQL/运行环境做集成验证
|
||
|
||
下一轮最建议继续做什么:
|
||
- P11.1 前端权限列表展示新增权限(让管理界面能看到和管理 9 个新权限)
|
||
- 或 P12.1 CLI 更新 propose 子命令
|
||
- 或 P5.4 补全后端 batch transition 的 P5.3/P5.4 校验同步
|
||
|
||
### 2026-03-17 22:00 UTC(第 22 轮:收口 milestone 内建 task 的旧默认值残留)
|
||
|
||
本轮做了什么:
|
||
- 先检查了 `HarborForge` 主仓库和相关子模块 git 状态;当前没有新的未提交遗留改动,父仓库是 `ahead 21`,各子模块工作区也干净
|
||
- 在后端 `create_milestone_task` 端点里收口一个残留兼容问题:
|
||
- 默认 `task_type` 从旧的 `task` 改为 `issue`
|
||
- 默认 `status` 从 `open` 改为 `pending`
|
||
- 这样 milestone 详情页/相关入口即使未显式传值,也不会再产出已移除的 `task_type=task`,并且与需求文档“新建 task 默认 pending”保持一致
|
||
|
||
改了哪些关键文件:
|
||
- `HarborForge.Backend/app/api/routers/milestones.py`
|
||
- `HarborForge/docs/milestone-propose-requirements.md`
|
||
|
||
验证结果:
|
||
- `python3 -m compileall HarborForge.Backend/app/api/routers/milestones.py` 通过
|
||
- grep 复查确认 `milestones.py` 内已无 `task_type` 默认回落到 `task` 的代码
|
||
|
||
当前阻塞/风险:
|
||
- 后端仍有一个旧残留:`HarborForge.Backend/app/models/models.py` 里的 `TaskType` 还保留 `TASK = "task"`,虽当前主流程未直接使用,但建议继续清理,避免后续误用
|
||
- 这轮只做了静态验证,未做数据库/接口级运行验证
|
||
|
||
下一轮最建议继续做什么:
|
||
- 继续做一个同样小的后端收口:清理 `app/models/models.py` 中残留的 `TaskType.TASK`,并顺手排查类似旧常量/默认值
|
||
- 或进入 P11.1,把新增 milestone/task/propose 权限在前端角色编辑页中明确分组展示
|
||
|
||
---
|
||
|
||
### 2026-03-17 23:00 UTC(第 23 轮:清理 models.py TaskType.TASK 残留 + P3.6 feature story freeze 编辑锁)
|
||
|
||
本轮做了什么:
|
||
- 从 `app/models/models.py` 移除 `TaskType.TASK = "task"` 枚举成员(第 22 轮标记的残留问题)
|
||
- 清理 docstring 中的旧描述
|
||
- 提交第 22 轮遗留的 `milestones.py` 默认值修复(task_type → issue, status → pending)
|
||
- 实现 P3.6 补充:feature story task 在 milestone freeze/undergoing/completed/closed 后禁止编辑 body 字段
|
||
- tasks.py PATCH 接口中新增 milestone 状态检查,story/feature 类型的 task 在 milestone 非 open 时返回 400
|
||
|
||
改了哪些关键文件:
|
||
- `HarborForge.Backend/app/models/models.py` — 移除 TaskType.TASK
|
||
- `HarborForge.Backend/app/api/routers/milestones.py` — 默认值修复(上轮遗留)
|
||
- `HarborForge.Backend/app/api/routers/tasks.py` — P3.6 feature story freeze 编辑锁
|
||
|
||
验证结果:
|
||
- 3 个文件 Python AST 语法检查全部通过
|
||
- grep 确认后端无残留 `TaskType.TASK` 引用
|
||
- 已 commit:`ec91a15`(models.py + milestones.py)、`586e06f`(tasks.py P3.6)
|
||
|
||
当前阻塞/风险:
|
||
- batch transition 接口尚未同步 P5.3/P5.4 校验(无 current_user, 无 comment body)
|
||
- P11 前端权限展示、P12 CLI/文档、P13 测试均未开始
|
||
- 无本地 MySQL/运行环境做集成验证
|
||
|
||
下一轮最建议继续做什么:
|
||
- P11.1 前端权限列表展示新增的 9 个权限(让管理界面能看到和管理 milestone/task/propose 权限)
|
||
- 或 P12.1 CLI 更新(milestone/task status choices 已在第 2 轮更新,但 propose 子命令未加)
|
||
- 或 P5.4 后端 batch transition 同步 assignee/comment 校验
|
||
|
||
### 2026-03-18 00:00 UTC(第 24 轮:P12.1 CLI 更新 — propose 子命令 + task_type 清理 + milestone status 过滤)
|
||
|
||
本轮做了什么:
|
||
- 完整实现 P12.1 CLI 更新
|
||
- **新增 5 个 propose 子命令**:
|
||
- `proposes --project P` — 列出 project 下的 proposes
|
||
- `propose-create TITLE --project P` — 创建 propose
|
||
- `propose-accept ID --project P --milestone M` — accept propose
|
||
- `propose-reject ID --project P [--reason R]` — reject propose
|
||
- `propose-reopen ID --project P` — reopen propose
|
||
- **task_type 清理**:
|
||
- `tasks --type` 和 `create-task --type` choices 中移除 `task`,默认改为 `issue`
|
||
- `TYPE_ICON` 移除 `task` 条目
|
||
- **milestone status 过滤**:`milestones --status` 新增 choices(open/freeze/undergoing/completed/closed)
|
||
- **transition comment 支持**:`transition` 命令新增 `--comment` 参数,支持 undergoing→completed 的必填 comment
|
||
|
||
改了哪些关键文件:
|
||
- `HarborForge.Backend/cli.py` — +86 行(propose commands, choices cleanup, comment support)
|
||
|
||
验证结果:
|
||
- Python AST 语法检查通过
|
||
- grep 确认 CLI 中无残留 `task_type=task` 引用
|
||
- 已 commit:backend `00a1786`,parent `98c9eb8`
|
||
|
||
当前阻塞/风险:
|
||
- CLI propose 命令依赖后端 API 正常运行,无本地环境做集成验证
|
||
- P12.2-P12.3 文档(README、状态机总览、权限总览、propose 流程图)尚未编写
|
||
- P13 自动化测试未开始
|
||
- P11 前端权限展示:RoleEditorPage 已动态加载后端权限列表,新增的 9 个权限会自动按 category 分组展示,P11.1-P11.2 实质已覆盖
|
||
|
||
下一轮最建议继续做什么:
|
||
- P5.4 后端 batch transition 同步 assignee/comment 校验(当前 batch 接口绕过了 P5.3/P5.4 的校验)
|
||
- 或 P12.2-P12.3 文档编写(状态机总览 + 权限总览 + propose 流程说明)
|
||
- 或 P13.1 开始编写 milestone 后端测试
|
||
|
||
---
|
||
|
||
## 1. 背景
|
||
|
||
当前希望把 milestone 的推进过程做成一个更严格的状态机,并把 feature story 的进入方式从“直接创建 task”改为“先提 propose,再 accept 进入 milestone”。
|
||
|
||
核心目标:
|
||
- milestone 有明确生命周期
|
||
- feature 进入 milestone 要受控
|
||
- release 节点要能约束 freeze / complete
|
||
- 依赖关系在开始前做严格检查
|
||
|
||
---
|
||
|
||
## 2. Milestone 字段调整
|
||
|
||
Milestone 的 `status` 使用新的枚举集合,替代当前代码库中的旧枚举。
|
||
|
||
### 2.1 目标枚举
|
||
|
||
- `open`
|
||
- `freeze`
|
||
- `undergoing`
|
||
- `completed`
|
||
- `closed`
|
||
|
||
### 2.2 替代当前代码中的旧枚举
|
||
|
||
当前代码里 milestone status 旧枚举为:
|
||
- `open`
|
||
- `pending`
|
||
- `deferred`
|
||
- `progressing`
|
||
- `closed`
|
||
|
||
后续应整体替换为新的 milestone 状态集合,不再使用:
|
||
- `pending`
|
||
- `deferred`
|
||
- `progressing`
|
||
|
||
另增字段:
|
||
- `started_at`
|
||
- milestone 进入 `undergoing` 时自动记录开始时间
|
||
|
||
默认:
|
||
- 新建 milestone 时,`status = open`
|
||
|
||
---
|
||
|
||
## 3. Milestone 状态定义
|
||
|
||
### 3.1 open
|
||
|
||
含义:
|
||
- milestone 刚创建,仍可接收 feature 范围内的内容
|
||
|
||
页面按钮:
|
||
- `freeze`
|
||
- `close`
|
||
|
||
约束:
|
||
- milestone 下的 `task / support / meeting` 在此阶段都只能处于锁定状态,不允许进入真正开始执行的状态
|
||
- 当前可认为它们只能停留在 `pending`
|
||
- milestone 基本信息仍可编辑
|
||
|
||
---
|
||
|
||
### 3.2 freeze
|
||
|
||
含义:
|
||
- milestone 进入冻结,需求范围不再继续扩张,准备开始执行
|
||
|
||
进入条件:
|
||
- 该 milestone **有且仅有一个** `maintenance task`
|
||
- 且这个 maintenance task 的 `subtype = release`
|
||
- 如果不满足,不能进入 `freeze`
|
||
|
||
页面按钮:
|
||
- `start`
|
||
- `close`
|
||
|
||
约束:
|
||
- freeze 之后不再允许做范围调整
|
||
- 如果 freeze 之后才发现要调整范围,只能放到未来的 milestone,不提供 `unfreeze`
|
||
- milestone **不能再新增** `subtype = feature` 的 `story task`
|
||
- 已存在的 `feature story task` 不能再编辑核心字段
|
||
- 例如:`title`、`description`、`owner` 等
|
||
- 但允许继续做附属操作
|
||
- 例如:新增 comment
|
||
- milestone 下的 `task / support / meeting` 仍保持锁定,不允许进入执行中状态
|
||
- 当前可认为仍只能停留在 `pending`
|
||
|
||
---
|
||
|
||
### 3.3 undergoing
|
||
|
||
含义:
|
||
- milestone 正式开始执行
|
||
|
||
进入方式:
|
||
- 用户点击 `start`
|
||
|
||
进入前检查:
|
||
- milestone 自身的前置依赖必须全部完成
|
||
- 其前置依赖中涉及的 milestone / task / 其他依赖对象,如果尚未完成,则不能开始
|
||
|
||
进入后效果:
|
||
- milestone `status = undergoing`
|
||
- 自动记录 `started_at`
|
||
- detail 页面只显示:`close`
|
||
- milestone 下的 `task / support / meeting` 才允许进入可执行状态
|
||
- 也就是说,只有在 milestone 为 `undergoing` 之后,这些对象才可以从 `pending` 往后推进
|
||
|
||
---
|
||
|
||
### 3.4 completed
|
||
|
||
含义:
|
||
- milestone 正常完成
|
||
|
||
进入方式:
|
||
- 当该 milestone 下**唯一的** `release maintenance task` 完成后,milestone 自动进入 `completed`
|
||
|
||
约束:
|
||
- `completed` 是终态
|
||
- milestone 一旦进入 `completed`,状态不再允许变更
|
||
|
||
备注:
|
||
- “task 完成”所对应的 task 状态枚举,后续补充
|
||
|
||
---
|
||
|
||
### 3.5 closed
|
||
|
||
含义:
|
||
- 废弃该 milestone,而不是正常完成
|
||
|
||
适用场景:
|
||
- milestone 不再继续推进
|
||
- 即使已经开始,也可以选择废弃
|
||
|
||
页面入口:
|
||
- `open` / `freeze` / `undergoing` 状态下都可以通过 `close` 进入 `closed`
|
||
|
||
进入 `closed` 后的处理原则:
|
||
- 如果 milestone 尚未开始,则直接废弃
|
||
- 如果 milestone 已经开始:
|
||
- 已完成的 `feature task` 可以被废弃
|
||
- 或者移交到其他 `open` 状态的 milestone
|
||
- 所有依赖该 milestone 的对象,都需要修改依赖关系,避免继续依赖一个已废弃 milestone
|
||
|
||
约束:
|
||
- `closed` 视为终态
|
||
- 当前不考虑 reopen 原 milestone
|
||
- milestone 页面不提供状态动作按钮
|
||
|
||
---
|
||
|
||
## 4. Milestone 页面按钮草案
|
||
|
||
所有按钮都按权限可见。
|
||
|
||
对于需要前置校验的按钮,推荐交互:
|
||
- 按钮可见但禁用
|
||
- 并给出提示,说明当前为什么不能点击
|
||
|
||
### 4.1 各状态按钮
|
||
|
||
- `open`
|
||
- `freeze`
|
||
- `close`
|
||
|
||
- `freeze`
|
||
- `start`
|
||
- `close`
|
||
|
||
- `undergoing`
|
||
- `close`
|
||
|
||
- `completed`
|
||
- 不显示状态动作按钮
|
||
|
||
- `closed`
|
||
- 不显示状态动作按钮
|
||
|
||
### 4.2 按钮触发条件
|
||
|
||
#### `freeze`
|
||
- 仅 `open` 状态显示
|
||
- 点击前检查:
|
||
- 当前 milestone 下**有且仅有一个** `maintenance/release task`
|
||
- 不满足时不可点击,并提示原因
|
||
|
||
#### `start`
|
||
- 仅 `freeze` 状态显示
|
||
- 点击前检查:
|
||
- 当前 milestone 的前置依赖全部完成
|
||
- 满足后:
|
||
- milestone 进入 `undergoing`
|
||
- 自动记录 `started_at`
|
||
|
||
#### `close`
|
||
- `open` / `freeze` / `undergoing` 状态显示
|
||
- 需要有对应权限
|
||
- 如果 milestone 已经是 `undergoing`,建议弹确认说明:
|
||
- 已完成的 feature task 需要废弃或移交
|
||
- 依赖该 milestone 的对象需要调整依赖
|
||
|
||
### 4.3 Milestone 动作权限
|
||
|
||
将以下三类动作设计为**独立权限**,由 role 控制:
|
||
- `freeze milestone`
|
||
- `start milestone`
|
||
- `close milestone`
|
||
|
||
规则:
|
||
- 拥有对应权限的 role 才可以执行对应动作
|
||
- 三者互相独立,不合并
|
||
|
||
### 4.4 特别说明
|
||
|
||
- milestone 不提供 `unfreeze`
|
||
- 范围调整只能发生在 `freeze` 之前
|
||
- 如果 freeze 之后才发现需要调整范围,只能放到未来的 milestone
|
||
- milestone 不提供手动 `complete` 按钮
|
||
- `completed` 由唯一的 `release maintenance task` 完成后自动触发
|
||
|
||
---
|
||
|
||
## 5. 依赖规则
|
||
|
||
### 4.1 开始前依赖检查
|
||
|
||
以下对象在进入“开始/执行中”状态前,都要检查依赖是否已完成:
|
||
- milestone
|
||
- task
|
||
- support
|
||
- meeting
|
||
- 其他有依赖关系的对象
|
||
|
||
规则:
|
||
- 如果任一前置依赖未完成,则当前对象不能开始
|
||
- 不需要在依赖完成时批量自动推进下游状态
|
||
- 只在“尝试开始”这一刻做校验即可
|
||
|
||
### 4.2 milestone close 对依赖的影响
|
||
|
||
当 milestone 被 `closed`:
|
||
- 所有依赖它的对象都必须调整依赖关系
|
||
- 调整后,这些对象未来在开始时仍然通过“开始前依赖检查”判断是否可开始
|
||
|
||
也就是说:
|
||
- **不做统一的自动状态更新**
|
||
- **只要求依赖配置保持合法**
|
||
|
||
---
|
||
|
||
## 5. Task 创建限制
|
||
|
||
以下两类 task **不能**通过通用的 `create task` 页面直接创建:
|
||
|
||
- `feature story task`
|
||
- `release maintenance task`
|
||
|
||
设计意图:
|
||
- `feature story task` 应来自 `propose -> accept`
|
||
- `release maintenance task` 应通过更受控的 milestone/release 流程创建
|
||
|
||
---
|
||
|
||
## 6. Propose 新模型
|
||
|
||
新增 `propose` 表。
|
||
|
||
### 6.1 propose 与 project 的关系
|
||
|
||
- `propose` 通过外键连接到 `project`
|
||
|
||
### 6.2 propose code
|
||
|
||
格式:
|
||
|
||
```text
|
||
{proj_code}:P{i:05x}
|
||
```
|
||
|
||
说明:
|
||
- `i` 由每个 `project` 独立维护递增序号
|
||
- 即每个 project 都有自己的 propose 计数器
|
||
|
||
### 6.3 propose 状态枚举
|
||
|
||
当前采用以下状态:
|
||
- `open`
|
||
- `accepted`
|
||
- `rejected`
|
||
|
||
明确:
|
||
- propose **不需要** `closed`
|
||
|
||
### 6.4 propose 基础字段(当前已明确部分)
|
||
|
||
至少需要:
|
||
- `project_id`
|
||
- `propose_code`
|
||
- `title`
|
||
- `description`
|
||
- `status`
|
||
- `created_by_id`
|
||
- `created_at`
|
||
- `updated_at`
|
||
|
||
### 6.5 feat_task_id 字段
|
||
|
||
新增字段:
|
||
- `feat_task_id`
|
||
- 类型:`string`
|
||
- 可选
|
||
|
||
规则:
|
||
- 在 accept 生成 `feature story task` 后自动填写
|
||
- 不允许手动修改
|
||
- 前端页面应显示该字段
|
||
|
||
---
|
||
|
||
## 7. Propose 页面草案
|
||
|
||
### 7.1 页面按钮
|
||
|
||
前端页面按钮规则采用当前草案:
|
||
|
||
- `open`
|
||
- `accept`
|
||
- `reject`
|
||
|
||
- `accepted`
|
||
- 不显示状态动作按钮
|
||
- 可显示跳转入口:`View Generated Task`
|
||
|
||
- `rejected`
|
||
- `reopen`
|
||
|
||
所有按钮都按权限可见。
|
||
|
||
### 7.2 页面显示字段
|
||
|
||
前端 detail 页面至少应显示:
|
||
- `propose_code`
|
||
- `title`
|
||
- `description`
|
||
- `status`
|
||
- `feat_task_id`
|
||
- `created_by_id`
|
||
- `created_at`
|
||
- `updated_at`
|
||
|
||
---
|
||
|
||
## 8. Propose 状态流转与动作规则
|
||
|
||
### 8.1 允许的状态流转
|
||
|
||
- `open -> accepted`
|
||
- `open -> rejected`
|
||
- `rejected -> open`
|
||
|
||
当前不考虑:
|
||
- `accepted -> open`
|
||
- `accepted -> rejected`
|
||
- 任何 `closed` 相关流转(因为 propose 不设 `closed`)
|
||
|
||
### 8.2 accept
|
||
|
||
前置条件:
|
||
- propose 当前必须是 `open`
|
||
- 操作者有 `accept propose` 权限
|
||
- 用户必须从下拉框中选择一个目标 milestone
|
||
- 目标 milestone 必须属于同一个 project
|
||
- 目标 milestone 必须是 `open`
|
||
|
||
accept 后效果:
|
||
- propose.status = `accepted`
|
||
- 自动在所选 milestone 下创建一个 `feature story task`
|
||
- 新 task 的创建者与 propose 创建者保持一致
|
||
- 新 task 默认状态建议为 `pending`
|
||
- 自动填写 `feat_task_id`
|
||
|
||
字段继承:
|
||
- `title`
|
||
- `description`
|
||
|
||
### 8.3 reject
|
||
|
||
前置条件:
|
||
- propose 当前必须是 `open`
|
||
- 操作者有 `reject propose` 权限
|
||
|
||
reject 后效果:
|
||
- propose.status = `rejected`
|
||
- 建议要求填写 reject comment / reason,便于追踪原因
|
||
|
||
### 8.4 reopen
|
||
|
||
前置条件:
|
||
- propose 当前必须是 `rejected`
|
||
- 操作者有 `reopen rejected propose` 权限
|
||
|
||
reopen 后效果:
|
||
- 不创建新 propose
|
||
- 复用当前 propose
|
||
- propose.status 回到 `open`
|
||
- 系统需要保留一条 reopen 记录
|
||
|
||
---
|
||
|
||
## 9. Propose 权限与编辑规则
|
||
|
||
### 9.1 独立权限
|
||
|
||
建议将以下动作设计为独立权限:
|
||
- `accept propose`
|
||
- `reject propose`
|
||
- `reopen rejected propose`
|
||
|
||
### 9.2 编辑规则
|
||
|
||
- `open`
|
||
- creator 可编辑 `title`、`description`
|
||
- admin 可编辑
|
||
- 有特定管理权限的 role 可编辑
|
||
- `accepted`
|
||
- 主体不可编辑
|
||
- `rejected`
|
||
- 主体不可编辑
|
||
|
||
备注:
|
||
- 主体不可编辑不代表不能追加 comment / activity 记录
|
||
|
||
---
|
||
|
||
## 10. 当前推荐的业务流程
|
||
|
||
### 10.1 feature 进入 milestone
|
||
|
||
推荐流程:
|
||
1. 用户先在 project 下创建 `propose`
|
||
2. 负责人在 propose detail 页面审阅
|
||
3. 若接受,则选择目标 milestone 并 `accept`
|
||
4. 系统自动生成 `feature story task`
|
||
5. milestone 处于 `open` 时可继续接纳 feature story
|
||
6. milestone 进入 `freeze` 后,不再接受新的 feature story
|
||
7. milestone 满足条件后 `start`,进入 `undergoing`
|
||
8. milestone 下唯一 `release maintenance task` 完成后,milestone 自动 `completed`
|
||
|
||
### 10.2 废弃 milestone
|
||
|
||
1. 用户点击 `close`
|
||
2. milestone 进入 `closed`
|
||
3. 处理已完成 feature task:
|
||
- 废弃
|
||
- 或移交到其他 `open` milestone
|
||
4. 修正所有依赖该 milestone 的对象依赖配置
|
||
|
||
---
|
||
|
||
## 11. Task 状态枚举调整
|
||
|
||
Task 的 `status` 也采用新的枚举集合。
|
||
|
||
### 11.1 目标枚举
|
||
|
||
- `open`
|
||
- `pending`
|
||
- `undergoing`
|
||
- `completed`
|
||
- `closed`
|
||
|
||
### 11.2 替代当前代码中的旧枚举
|
||
|
||
当前代码里 task status 旧枚举为:
|
||
- `open`
|
||
- `pending`
|
||
- `progressing`
|
||
- `closed`
|
||
|
||
后续应整体替换为新的 task 状态集合,不再使用:
|
||
- `progressing`
|
||
|
||
并新增:
|
||
- `undergoing`
|
||
- `completed`
|
||
|
||
### 11.3 Task 状态语义
|
||
|
||
- `pending`
|
||
- 还不能开始,通常是被 milestone 状态或依赖锁住
|
||
- `open`
|
||
- 可以开始,但尚未真正开工
|
||
- `undergoing`
|
||
- 正在处理
|
||
- `completed`
|
||
- 正常完成
|
||
- `closed`
|
||
- 废弃 / 取消,不是正常完成
|
||
|
||
### 11.4 当前认可的 Task 基础状态流转
|
||
|
||
允许的基础流转:
|
||
- `pending -> open`
|
||
- `open -> undergoing`
|
||
- `undergoing -> completed`
|
||
- `pending -> closed`
|
||
- `open -> closed`
|
||
- `undergoing -> closed`
|
||
|
||
### 11.5 各状态流转条件
|
||
|
||
#### `pending -> open`
|
||
条件:
|
||
- 所有前置依赖都已满足
|
||
|
||
#### `open -> undergoing`
|
||
条件:
|
||
- `assignee` 必须非空
|
||
- 且只有当前 assignee 可以执行这个开始动作
|
||
|
||
#### `undergoing -> completed`
|
||
条件:
|
||
- 不额外要求前置条件
|
||
- 但 assignee 需要留下 comment
|
||
|
||
### 11.6 Reopen 方向
|
||
|
||
补充决定:
|
||
- reopen 时**不创建新 task**
|
||
- 直接复用当前 task
|
||
- 但系统需要保留一条 reopen 记录
|
||
|
||
当前已确认:
|
||
- reopen 允许从 `closed` 与 `completed` 触发
|
||
- reopen 后统一回到 `open`
|
||
- reopen 记录采用 comment、activity log 还是专门字段/表,后续再定
|
||
|
||
当前理解:
|
||
- `completed` 与 `closed` 原本被视为终态
|
||
- 但 task 将引入受控的 reopen 机制,使其可以返回 `open`
|
||
|
||
### 11.7 Task 状态动作权限
|
||
|
||
将以下三类动作设计为**独立权限**,由 role 控制:
|
||
- `close task`
|
||
- `reopen closed task`
|
||
- `reopen completed task`
|
||
|
||
规则:
|
||
- 拥有对应权限的 role 才可以执行对应动作
|
||
- `reopen closed task` 与 `reopen completed task` 分开控制,不合并为一个权限
|
||
|
||
补充:
|
||
- `undergoing -> completed` 不走 role 权限控制
|
||
- `complete` 只有当前 assignee 可以执行
|
||
|
||
### 11.8 Task 页面按钮可见性
|
||
|
||
所有按钮都按权限可见。
|
||
|
||
当前确认的按钮规则:
|
||
|
||
- 除 `closed` 与 `completed` 外,task 页面始终显示 `close` 按钮
|
||
- 也就是:
|
||
- `pending` 有 `close`
|
||
- `open` 有 `close`
|
||
- `undergoing` 有 `close`
|
||
- `closed` 与 `completed` 状态显示 `reopen` 按钮
|
||
- `pending` 状态显示 `open` 按钮
|
||
- `open` 状态显示 `start` 按钮
|
||
- `undergoing` 状态显示 `finish` 按钮
|
||
|
||
可理解为:
|
||
- `pending`:`open` + `close`
|
||
- `open`:`start` + `close`
|
||
- `undergoing`:`finish` + `close`
|
||
- `completed`:`reopen`
|
||
- `closed`:`reopen`
|
||
|
||
其中:
|
||
- `finish` 对应 `undergoing -> completed`
|
||
- `start` 对应 `open -> undergoing`
|
||
- `open` 按钮对应 `pending -> open`
|
||
|
||
### 11.9 与 Milestone 状态的联动约束
|
||
|
||
- 当 milestone 为 `open` / `freeze` 时:
|
||
- task 原则上只能处于 `pending` 或 `closed`
|
||
- 不应进入 `open / undergoing / completed`
|
||
- 当 milestone 为 `undergoing` 时:
|
||
- task 才允许从 `pending` 往执行状态推进
|
||
- 当 milestone 为 `completed` / `closed` 时:
|
||
- milestone 下 task 原则上不再继续推进
|
||
|
||
### 11.10 新建 Task 的默认状态
|
||
|
||
当前倾向:
|
||
- 新建 task 默认 `pending`
|
||
|
||
原因:
|
||
- 与 milestone `open / freeze` 阶段的“锁定”语义一致
|
||
- 避免 task 一创建就被误认为可执行
|
||
|
||
### 11.11 Assignee 字段与编辑权限规则
|
||
|
||
Task 需要一个字段:
|
||
- `assignee`
|
||
- 类型:`string`
|
||
- 可选
|
||
- 含义:用户 id
|
||
|
||
当前业务规则:
|
||
|
||
#### open 状态
|
||
- 当 `assignee = null` 时:
|
||
- 任何人都可以编辑
|
||
- 当 `assignee != null` 时:
|
||
- 仅当前 assignee 和 admin 可以编辑
|
||
|
||
#### undergoing 以及之后的状态
|
||
- `undergoing`
|
||
- `completed`
|
||
- `closed`
|
||
|
||
以上状态一律不可编辑。
|
||
|
||
备注:
|
||
- 这里的“不能编辑”指 task 主体内容不可编辑
|
||
- `undergoing -> completed` 已明确要求 assignee 留下 comment,因此 comment / 记录性动作不等同于主体编辑
|
||
- 其他 comment 等附属动作的权限边界,后续可继续细化
|
||
|
||
---
|
||
|
||
## 12. Task 类型清理
|
||
|
||
当前又发现一个需要清理的点:
|
||
- 现在创建 task 时,存在一个 `task_type = task` 的类型
|
||
- 且它当前只有一个 subtype:`defect`
|
||
|
||
这在语义上比较别扭,也和其它类型存在重叠/混淆。
|
||
|
||
### 12.1 当前决定
|
||
|
||
后续应移除这个类型:
|
||
- 移除 `task_type = task`
|
||
|
||
### 12.2 当前代码确认结果
|
||
|
||
这个问题在当前代码中已经确认存在,而且前后端都写死了:
|
||
|
||
前端:
|
||
- `HarborForge.Frontend/src/components/CreateTaskModal.tsx`
|
||
- `HarborForge.Frontend/src/pages/CreateTaskPage.tsx`
|
||
- 两处都定义了:
|
||
- `task`
|
||
- 其 subtype 只有:`defect`
|
||
- 当前创建表单默认类型也是 `task`
|
||
|
||
后端:
|
||
- `HarborForge.Backend/app/api/routers/tasks.py`
|
||
- `TASK_SUBTYPE_MAP` 中明确存在:
|
||
- `'task': {'defect'}`
|
||
|
||
补充:
|
||
- 前端类型定义里也仍包含 `task`
|
||
- 后端 schema / enum 里也仍包含 `task`
|
||
|
||
### 12.3 本轮只记录,不改代码
|
||
|
||
当前先把它记录为需求与清理项:
|
||
- 暂不实现
|
||
- 暂不修改代码
|
||
- 后续统一与 task type / subtype 体系一起调整
|
||
|
||
---
|
||
|
||
## 13. 待补充事项
|
||
|
||
这些点后续需要进一步定死:
|
||
|
||
### 13.1 feature task 在 freeze 后的可编辑字段白名单/黑名单
|
||
- 当前只明确:核心字段不可编辑,comment 仍可新增
|
||
- 后续要更具体列出:哪些能改,哪些不能改
|
||
|
||
### 13.2 release maintenance task 的创建入口
|
||
- 当前只明确它不能走通用 create task
|
||
- 但具体从哪里创建、谁能创建、何时创建,还需要补
|
||
|
||
### 13.3 close 时的迁移细节
|
||
- 已完成 feature task 移交到其他 `open milestone` 时,是否保留原历史链接
|
||
- task code 是否需要变化
|
||
- 依赖链迁移是否允许批量操作
|
||
|
||
---
|
||
|
||
## 14. 一句话总结
|
||
|
||
这套方案的本质是:
|
||
|
||
- 用 `propose` 控制 feature 进入 milestone 的入口
|
||
- 用 `milestone status` 控制范围冻结与执行节奏
|
||
- 用 `release maintenance task` 作为 milestone 完成的关键闸门
|
||
- 用“开始前依赖检查”替代大规模自动状态联动
|
||
|
||
这样业务会更可控,也更容易避免流程混乱。
|