huskies: merge 520_story_typed_pipeline_state_machine_in_rust_foundation_replaces_stringly_typed_crdt_views_with_strict_enums_subsumes_436

This commit is contained in:
dave
2026-04-09 21:24:11 +00:00
parent 1d9287389a
commit 84717b04bd
18 changed files with 1569 additions and 122 deletions
+9 -7
View File
@@ -207,14 +207,15 @@ async fn tick_once(
// remove the timer before it fires; this guard covers the case where
// cancellation was not yet called or the story raced forward through
// the pipeline while the timer was pending.
if let Some(item) = crate::crdt_state::read_item(&entry.story_id) {
match item.stage.as_str() {
"3_qa" | "4_merge" | "5_done" | "6_archived" => {
if let Ok(Some(item)) = crate::pipeline_state::read_typed(&entry.story_id) {
use crate::pipeline_state::Stage;
match &item.stage {
Stage::Qa | Stage::Merge { .. } | Stage::Done { .. } | Stage::Archived { .. } => {
crate::slog!(
"[timer] Skipping timer for story {} — currently in '{}', \
not in backlog/current; timer is stale",
entry.story_id,
item.stage
item.stage.dir_name()
);
continue;
}
@@ -425,8 +426,9 @@ pub async fn handle_timer_command(
// The story must be in backlog or current. When the timer fires,
// backlog stories are moved to current automatically.
// Check CRDT state first, then fall back to filesystem.
let in_valid_stage = if let Some(item) = crate::crdt_state::read_item(&story_id) {
matches!(item.stage.as_str(), "1_backlog" | "2_current")
let in_valid_stage = if let Ok(Some(item)) = crate::pipeline_state::read_typed(&story_id) {
use crate::pipeline_state::Stage;
matches!(item.stage, Stage::Backlog | Stage::Coding)
} else {
let work_dir = project_root.join(".huskies").join("work");
work_dir.join("1_backlog").join(format!("{story_id}.md")).exists()
@@ -588,7 +590,7 @@ fn resolve_story_id(number_or_id: &str, project_root: &Path) -> Option<String> {
// --- DB-first lookup ---
for id in crate::db::all_content_ids() {
let file_num = id.split('_').next().unwrap_or("");
if file_num == number_or_id && crate::crdt_state::read_item(&id).is_some() {
if file_num == number_or_id && crate::pipeline_state::read_typed(&id).ok().flatten().is_some() {
return Some(id);
}
}