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:
@@ -67,12 +67,12 @@ pub(super) fn handle_depends(ctx: &CommandContext) -> Option<String> {
|
||||
// --- DB-first lookup ---
|
||||
for id in crate::db::all_content_ids() {
|
||||
let file_num = id.split('_').next().unwrap_or("");
|
||||
if file_num == num_str && let Some(item) = crate::crdt_state::read_item(&id) {
|
||||
if file_num == num_str && let Ok(Some(item)) = crate::pipeline_state::read_typed(&id) {
|
||||
let path = ctx
|
||||
.project_root
|
||||
.join(".huskies")
|
||||
.join("work")
|
||||
.join(&item.stage)
|
||||
.join(item.stage.dir_name())
|
||||
.join(format!("{id}.md"));
|
||||
found = Some((path, id));
|
||||
break;
|
||||
|
||||
@@ -129,8 +129,10 @@ fn unblock_by_story_id(story_id: &str) -> String {
|
||||
updated = set_front_matter_field(&updated, "retry_count", "0");
|
||||
|
||||
crate::db::write_content(story_id, &updated);
|
||||
let stage = crate::crdt_state::read_item(story_id)
|
||||
.map(|i| i.stage)
|
||||
let stage = crate::pipeline_state::read_typed(story_id)
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(|i| i.stage.dir_name().to_string())
|
||||
.unwrap_or_else(|| "2_current".to_string());
|
||||
crate::db::write_item_with_content(story_id, &stage, &updated);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,11 +108,11 @@ pub async fn handle_assign(
|
||||
// --- DB-first lookup ---
|
||||
for id in crate::db::all_content_ids() {
|
||||
let file_num = id.split('_').next().unwrap_or("");
|
||||
if file_num == story_number && let Some(item) = crate::crdt_state::read_item(&id) {
|
||||
if file_num == story_number && let Ok(Some(item)) = crate::pipeline_state::read_typed(&id) {
|
||||
let path = project_root
|
||||
.join(".huskies")
|
||||
.join("work")
|
||||
.join(&item.stage)
|
||||
.join(item.stage.dir_name())
|
||||
.join(format!("{id}.md"));
|
||||
found = Some((path, id));
|
||||
break;
|
||||
|
||||
@@ -76,13 +76,13 @@ pub async fn handle_delete(
|
||||
// --- DB-first lookup ---
|
||||
for id in crate::db::all_content_ids() {
|
||||
let file_num = id.split('_').next().unwrap_or("");
|
||||
if file_num == story_number && let Some(item) = crate::crdt_state::read_item(&id) {
|
||||
if file_num == story_number && let Ok(Some(item)) = crate::pipeline_state::read_typed(&id) {
|
||||
let path = project_root
|
||||
.join(".huskies")
|
||||
.join("work")
|
||||
.join(&item.stage)
|
||||
.join(item.stage.dir_name())
|
||||
.join(format!("{id}.md"));
|
||||
found = Some((path, item.stage, id));
|
||||
found = Some((path, item.stage.dir_name().to_string(), id));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,11 +95,11 @@ pub async fn handle_start(
|
||||
// --- DB-first lookup ---
|
||||
for id in crate::db::all_content_ids() {
|
||||
let file_num = id.split('_').next().unwrap_or("");
|
||||
if file_num == story_number && let Some(item) = crate::crdt_state::read_item(&id) {
|
||||
if file_num == story_number && let Ok(Some(item)) = crate::pipeline_state::read_typed(&id) {
|
||||
let path = project_root
|
||||
.join(".huskies")
|
||||
.join("work")
|
||||
.join(&item.stage)
|
||||
.join(item.stage.dir_name())
|
||||
.join(format!("{id}.md"));
|
||||
found = Some((path, id));
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user