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:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user