新增: 推进链阶段3收口(advance_task/run_workflow工具+status状态机收口)

This commit is contained in:
2026-06-16 21:20:41 +08:00
parent dfe0096498
commit b94e74a96d
6 changed files with 126 additions and 15 deletions

View File

@@ -331,11 +331,15 @@ pub fn allowed_columns_for(table: &str) -> Option<&'static [&'static str]> {
"tasks" => &[
// id/created_at 不列入:主键与创建时间不可通过通用 update_field 改写
// (防篡改主键/伪造创建时间/跨项目移动)。
// status 不列入(D-260616-04 status 收口):所有 status 改动走
// advance_status_atomic(CAS SQL `WHERE id AND status=expected`,唯一 status 写入路径),
// 不经通用 update_field。防 update_task IPC / AI 工具旁路改 status 绕过状态机
// (can_transition 校验)与 review_rounds 累加。后人勿把 status 补回白名单。
// review_rounds 不列入:它仅 advance_status_atomic 退回转换
// (in_review→in_progress / testing→in_review)时原子 +1(收口:仅该专用路径可改,
// update_task/update_field 白名单均不含)。后人勿把 review_rounds 补进白名单,
// 否则破坏「review_rounds 唯一写入路径」收口、引入旁路写导致计数错乱。
"project_id", "title", "description", "status", "priority", "branch_name",
"project_id", "title", "description", "priority", "branch_name",
"assignee", "workflow_def_id", "base_branch", "updated_at",
// TODO(B-260616-16): project_id 跨表存在性校验待 commands/task.rs 层补。
// 通用 CRUD 层(db repo)只懂表/列语义,不持有跨表业务约束(查 projects 表存在性)。

View File

@@ -247,15 +247,23 @@ async fn update_field_rejects_cross_table_column_tasks_name() {
}
#[tokio::test]
async fn update_field_allows_tasks_status() {
async fn update_field_rejects_tasks_status() {
// D-260616-04 status 收口:status 已从 tasks 白名单移除,所有 status 改动须走
// advance_status_atomic(CAS SQL 独立路径,不经通用 update_field)。update_field
// 改 status 应在白名单阶段被拒(防 update_task IPC / AI 工具旁路绕过状态机与 review_rounds 累加)。
let (projects, tasks, _r, _b) = setup().await;
projects.insert(project("p1")).await.unwrap();
tasks.insert(task("t1", "p1")).await.unwrap();
let ok = tasks.update_field("t1", "status", "done").await.unwrap();
assert!(ok);
let res = tasks.update_field("t1", "status", "done").await;
assert!(
res.is_err(),
"status 不在 tasks 白名单,应被拒(改走 advance_status_atomic)"
);
// 对照:status 未被改写,仍为初始值(task fixture 的初始 status)
let rec = tasks.get_by_id("t1").await.unwrap().unwrap();
assert_eq!(rec.status, "done");
assert_ne!(rec.status, "done", "白名单拒绝后 status 不应被改写");
}
#[tokio::test]