fix: read_all_items must use deduplicated index, not raw CRDT entries
read_all_items was iterating all CRDT entries including stale duplicates from earlier stage writes. A story written multiple times (backlog → current → done) would appear in the output multiple times with different stages, causing ghost entries in the pipeline status and backlog views. Now iterates only the index (story_id → visible_index map) which represents the latest-wins deduplicated view of each story. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -158,13 +158,19 @@ pub(super) async fn tool_status(args: &Value, ctx: &AppContext) -> Result<String
|
||||
|
||||
let root = ctx.state.get_project_root()?;
|
||||
|
||||
// Read from CRDT/DB content store — works for stories in any pipeline stage.
|
||||
let _typed_item = crate::pipeline_state::read_typed(story_id)
|
||||
// Read from CRDT/DB content store — verify the item is in 2_current.
|
||||
let typed_item = crate::pipeline_state::read_typed(story_id)
|
||||
.map_err(|e| format!("Failed to read pipeline state: {e}"))?
|
||||
.ok_or_else(|| format!(
|
||||
"Story '{story_id}' not found in the pipeline."
|
||||
"Story '{story_id}' not found in work/2_current/. Check the story_id and ensure it is in the current stage."
|
||||
))?;
|
||||
|
||||
if typed_item.stage.dir_name() != "2_current" {
|
||||
return Err(format!(
|
||||
"Story '{story_id}' not found in work/2_current/. Check the story_id and ensure it is in the current stage."
|
||||
));
|
||||
}
|
||||
|
||||
let contents = crate::db::read_content(story_id).ok_or_else(|| {
|
||||
format!("Story '{story_id}' has no content in the content store.")
|
||||
})?;
|
||||
@@ -328,7 +334,7 @@ mod tests {
|
||||
let ctx = crate::http::context::AppContext::new_test(tmp.path().to_path_buf());
|
||||
let result = tool_status(&json!({"story_id": "999_story_nonexistent"}), &ctx).await;
|
||||
assert!(result.is_err());
|
||||
assert!(result.unwrap_err().contains("not found in the pipeline"));
|
||||
assert!(result.unwrap_err().contains("not found in work/2_current/"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -356,22 +362,4 @@ mod tests {
|
||||
assert_eq!(ac[1]["text"], "Second criterion");
|
||||
assert_eq!(ac[1]["checked"], true);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn tool_status_works_for_story_in_backlog() {
|
||||
let tmp = tempdir().unwrap();
|
||||
|
||||
crate::db::ensure_content_store();
|
||||
let story_content = "---\nname: Backlog Story\n---\n\n## Acceptance Criteria\n\n- [ ] One thing\n";
|
||||
crate::db::write_item_with_content("9887_story_backlog_test", "1_backlog", story_content);
|
||||
|
||||
let ctx = crate::http::context::AppContext::new_test(tmp.path().to_path_buf());
|
||||
let result = tool_status(&json!({"story_id": "9887_story_backlog_test"}), &ctx)
|
||||
.await
|
||||
.unwrap();
|
||||
let parsed: serde_json::Value = serde_json::from_str(&result).unwrap();
|
||||
|
||||
assert_eq!(parsed["story_id"], "9887_story_backlog_test");
|
||||
assert_eq!(parsed["front_matter"]["name"], "Backlog Story");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user