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:
dave
2026-04-10 19:32:55 +00:00
parent 2e0ed98d42
commit ea36160667
9 changed files with 88 additions and 526 deletions
+7 -16
View File
@@ -600,19 +600,6 @@ pub fn our_node_id() -> Option<String> {
Some(hex::encode(&state.crdt.id))
}
/// Sign a byte slice with this node's Ed25519 private key.
///
/// Used by the CRDT sync auth handshake: when a remote peer sends a
/// challenge nonce, this node signs it to prove possession of the
/// private key corresponding to its public node ID.
/// Returns `None` before `init()`.
pub fn sign_bytes(message: &[u8]) -> Option<Vec<u8>> {
use bft_json_crdt::keypair::sign;
let state = CRDT_STATE.get()?.lock().ok()?;
let sig = sign(&state.keypair, message);
Some(sig.as_ref().to_vec())
}
/// Write a claim on a pipeline item via CRDT.
///
/// Sets `claimed_by` to this node's ID and `claimed_at` to the current time.
@@ -948,9 +935,13 @@ pub fn read_all_items() -> Option<Vec<PipelineItemView>> {
let state_mutex = CRDT_STATE.get()?;
let state = state_mutex.lock().ok()?;
let mut items = Vec::new();
for item_crdt in state.crdt.doc.items.iter() {
if let Some(view) = extract_item_view(item_crdt) {
// Only return items that appear in the deduplicated index.
// The index maps story_id → visible_index and represents the
// latest-wins view of each story. Iterating raw CRDT entries
// would return stale duplicates from earlier stage writes.
let mut items = Vec::with_capacity(state.index.len());
for &idx in state.index.values() {
if let Some(view) = extract_item_view(&state.crdt.doc.items[idx]) {
items.push(view);
}
}