feat: milestone state machine + propose flow + task state machine #8

Merged
hzhang merged 23 commits from feat/milestone-propose-state-machine into main 2026-03-19 11:11:09 +00:00
Owner

Milestone 状态机 + Propose + Task 状态机 (后端)\n\n22 commits - 详见 docs/milestone-propose-requirements.md\n\n- Milestone 枚举替换 + freeze/start/close/auto-complete\n- Task 状态机 + assignee + 编辑限制\n- Propose CRUD + accept/reject/reopen\n- 9 权限 + 角色种子 + 依赖检查\n- 70 测试 + CLI propose

## Milestone 状态机 + Propose + Task 状态机 (后端)\n\n22 commits - 详见 docs/milestone-propose-requirements.md\n\n- Milestone 枚举替换 + freeze/start/close/auto-complete\n- Task 状态机 + assignee + 编辑限制\n- Propose CRUD + accept/reject/reopen\n- 9 权限 + 角色种子 + 依赖检查\n- 70 测试 + CLI propose
zhi added 22 commits 2026-03-18 09:59:46 +00:00
Milestone: open/freeze/undergoing/completed/closed (was open/pending/deferred/progressing/closed)
Task: open/pending/undergoing/completed/closed (was open/pending/progressing/closed)

- Add MilestoneStatusEnum to schemas with typed validation
- Add started_at field to Milestone model
- Update all router/CLI references from progressing->undergoing
- Add completed status handling in task transition logic
- New Propose model (app/models/propose.py) with status enum (open/accepted/rejected)
- New Propose schemas (ProposeCreate/Update/Response) in schemas.py
- MySQL enum migration in main.py for milestone/task status columns
  - milestone: pending→open, deferred→closed, progressing→undergoing
  - task: progressing→undergoing
- Import propose model in startup for create_all
- Add started_at column migration for milestones
- New milestone_actions router with POST freeze/start/close endpoints
- freeze: validates exactly 1 release maintenance task exists
- start: validates all milestone/task dependencies completed, records started_at
- close: allows from open/freeze/undergoing with reason
- try_auto_complete_milestone helper: auto-completes milestone when sole release task finishes
- Wired auto-complete into task transition and update endpoints
- Added freeze enforcement: no new feature story tasks after freeze
- Added started_at to milestone serializer
- All actions write activity logs
- Add milestone.freeze/start/close, task.close/reopen_closed/reopen_completed, propose.accept/reject/reopen to DEFAULT_PERMISSIONS
- Replace placeholder check_project_role with check_permission in proposes.py accept/reject/reopen
- Replace freeform permission strings with dotted names in milestone_actions.py
- Add task.close and task.reopen_* permission checks in tasks.py transition endpoint
- Admin role auto-inherits all new permissions via init_wizard
- New app/services/dependency_check.py with check_milestone_deps()
- Replaces 3x duplicated JSON-parse + query + filter logic
- Supports both milestone and task dependency checking
- Returns structured DepCheckResult with ok/blockers/reason
- Refactored preflight and start endpoints to use shared helper
New test infrastructure:
- tests/conftest.py: SQLite in-memory fixtures, TestClient wired to test DB,
  factory fixtures for User/Project/Milestone/Task/Roles/Permissions
- tests/test_milestone_actions.py: 17 tests covering:
  - freeze success/no-release-task/multiple-release-tasks/wrong-status
  - start success+started_at/deps-not-met/wrong-status
  - close from open/freeze/undergoing, rejected from completed/closed
  - auto-complete on release task finish, no auto-complete for non-release/wrong-status
  - preflight allowed/not-allowed
zhi added 1 commit 2026-03-19 10:18:20 +00:00
SQLAlchemy 2.0 defaults to mapping Python enum *names* (OPEN, CLOSED)
to DB values, but MySQL stores lowercase *values* (open, closed).
This mismatch causes LookupError on read.

Adding values_callable=lambda x: [e.value for e in x] tells SQLAlchemy
to use the enum values for DB mapping.

Affected models: milestone, task, meeting, propose, support
hzhang merged commit 0b1e47ef60 into main 2026-03-19 11:11:09 +00:00
hzhang deleted branch feat/milestone-propose-state-machine 2026-03-19 11:11:09 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: zhi/HarborForge.Backend#8