Files
HarborForge/docs/milestone-propose-requirements.md

849 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.pymilestone 的 create/update/response 现在使用类型化枚举而非裸字符串
- 新增 `started_at` 字段到 Milestone model 和 MilestoneResponse
- 更新所有 routermilestones.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 schemasP1.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 routercreate/list/get/update含 propose_code 自动生成
- 这是让 Propose 功能可用的最小后端闭环
---
## 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 完成的关键闸门
- 用“开始前依赖检查”替代大规模自动状态联动
这样业务会更可控,也更容易避免流程混乱。