story-kit: merge 166_story_add_done_column_to_pipeline_board

Add Done column to pipeline board. Adds the 'done' stage to
PipelineState, exposes it via the WebSocket and REST API, and
renders a Done column in the frontend pipeline board view.

Squash merge from feature/story-166_story_add_done_column_to_pipeline_board.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dave
2026-02-24 23:42:59 +00:00
parent d442dbeae8
commit 150f654e04
6 changed files with 29 additions and 1 deletions

View File

@@ -73,6 +73,7 @@ enum WsResponse {
current: Vec<crate::http::workflow::UpcomingStory>,
qa: Vec<crate::http::workflow::UpcomingStory>,
merge: Vec<crate::http::workflow::UpcomingStory>,
done: Vec<crate::http::workflow::UpcomingStory>,
},
/// `.story_kit/project.toml` was modified; the frontend should re-fetch the
/// agent roster. Does NOT trigger a pipeline state refresh.
@@ -136,6 +137,7 @@ impl From<PipelineState> for WsResponse {
current: s.current,
qa: s.qa,
merge: s.merge,
done: s.done,
}
}
}
@@ -569,12 +571,14 @@ mod tests {
current: vec![],
qa: vec![],
merge: vec![],
done: vec![],
};
let json = serde_json::to_value(&resp).unwrap();
assert_eq!(json["type"], "pipeline_state");
assert_eq!(json["upcoming"].as_array().unwrap().len(), 1);
assert_eq!(json["upcoming"][0]["story_id"], "10_story_test");
assert!(json["current"].as_array().unwrap().is_empty());
assert!(json["done"].as_array().unwrap().is_empty());
}
#[test]
@@ -696,6 +700,12 @@ mod tests {
}],
qa: vec![],
merge: vec![],
done: vec![UpcomingStory {
story_id: "50_story_done".to_string(),
name: Some("Done Story".to_string()),
error: None,
agent: None,
}],
};
let resp: WsResponse = state.into();
let json = serde_json::to_value(&resp).unwrap();
@@ -706,6 +716,8 @@ mod tests {
assert_eq!(json["current"][0]["story_id"], "2_story_b");
assert!(json["qa"].as_array().unwrap().is_empty());
assert!(json["merge"].as_array().unwrap().is_empty());
assert_eq!(json["done"].as_array().unwrap().len(), 1);
assert_eq!(json["done"][0]["story_id"], "50_story_done");
}
#[test]
@@ -715,6 +727,7 @@ mod tests {
current: vec![],
qa: vec![],
merge: vec![],
done: vec![],
};
let resp: WsResponse = state.into();
let json = serde_json::to_value(&resp).unwrap();
@@ -723,6 +736,7 @@ mod tests {
assert!(json["current"].as_array().unwrap().is_empty());
assert!(json["qa"].as_array().unwrap().is_empty());
assert!(json["merge"].as_array().unwrap().is_empty());
assert!(json["done"].as_array().unwrap().is_empty());
}
// ── WsResponse JSON round-trip (string form) ────────────────────
@@ -849,6 +863,7 @@ mod tests {
}],
qa: vec![],
merge: vec![],
done: vec![],
};
let resp: WsResponse = state.into();
let json = serde_json::to_value(&resp).unwrap();