huskies: merge 1009
This commit is contained in:
@@ -22,8 +22,10 @@ pub struct CrdtItemDump {
|
||||
pub agent: Option<String>,
|
||||
pub retry_count: Option<i64>,
|
||||
pub depends_on: Option<Vec<u32>>,
|
||||
pub claimed_by: Option<String>,
|
||||
pub claimed_at: Option<f64>,
|
||||
/// Agent name holding the claim, or `None` when unclaimed.
|
||||
pub claim_agent: Option<String>,
|
||||
/// Unix timestamp (seconds) when the claim was written.
|
||||
pub claim_ts: Option<f64>,
|
||||
/// Hex-encoded OpId of the list insert op — cross-reference with `crdt_ops`.
|
||||
pub content_index: String,
|
||||
pub is_deleted: bool,
|
||||
@@ -139,11 +141,11 @@ pub fn dump_crdt_state(story_id_filter: Option<&str>) -> CrdtStateDump {
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let claimed_by = match item_crdt.claimed_by.view() {
|
||||
let claim_agent = match item_crdt.claim_agent.view() {
|
||||
JsonValue::String(s) if !s.is_empty() => Some(s),
|
||||
_ => None,
|
||||
};
|
||||
let claimed_at = match item_crdt.claimed_at.view() {
|
||||
let claim_ts = match item_crdt.claim_ts.view() {
|
||||
JsonValue::Number(n) if n > 0.0 => Some(n),
|
||||
_ => None,
|
||||
};
|
||||
@@ -157,8 +159,8 @@ pub fn dump_crdt_state(story_id_filter: Option<&str>) -> CrdtStateDump {
|
||||
agent,
|
||||
retry_count,
|
||||
depends_on,
|
||||
claimed_by,
|
||||
claimed_at,
|
||||
claim_agent,
|
||||
claim_ts,
|
||||
content_index,
|
||||
is_deleted: op.is_deleted,
|
||||
});
|
||||
@@ -326,7 +328,7 @@ pub fn evict_item(story_id: &str) -> Result<(), String> {
|
||||
/// string, or with no name set, are filtered out (`None`) — a nameless item
|
||||
/// is treated as malformed and never surfaces to callers.
|
||||
pub(super) fn extract_item_view(item: &PipelineItemCrdt) -> Option<PipelineItemView> {
|
||||
use super::types::{Claim, EpicId};
|
||||
use super::types::EpicId;
|
||||
use crate::io::story_metadata::{ItemType, QaMode};
|
||||
|
||||
let story_id = match item.story_id.view() {
|
||||
@@ -357,18 +359,17 @@ pub(super) fn extract_item_view(item: &PipelineItemCrdt) -> Option<PipelineItemV
|
||||
_ => Vec::new(),
|
||||
};
|
||||
|
||||
let claimed_by = match item.claimed_by.view() {
|
||||
// `claim_agent`/`claim_ts` are read only to embed in Stage::Coding /
|
||||
// Stage::Merge via `project_stage_for_view`; they are not stored on
|
||||
// `WorkItem` directly (story 1009: readers project from the Stage variant).
|
||||
let claim_agent = match item.claim_agent.view() {
|
||||
JsonValue::String(s) if !s.is_empty() => Some(s),
|
||||
_ => None,
|
||||
};
|
||||
let claimed_at_secs = match item.claimed_at.view() {
|
||||
let claim_ts_secs = match item.claim_ts.view() {
|
||||
JsonValue::Number(n) if n > 0.0 => Some(n as u64),
|
||||
_ => None,
|
||||
};
|
||||
let claim = match (claimed_by, claimed_at_secs) {
|
||||
(Some(node), Some(at)) => Some(Claim { node, at }),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
// `merged_at` is read only to project into `Stage::Done`; it is not
|
||||
// stored on `WorkItem` (callers access it via `Stage::Done { merged_at }`).
|
||||
@@ -397,8 +398,14 @@ pub(super) fn extract_item_view(item: &PipelineItemCrdt) -> Option<PipelineItemV
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let stage =
|
||||
project_stage_for_view(&stage_str, &story_id, merged_at_float, resume_to.as_deref())?;
|
||||
let stage = project_stage_for_view(
|
||||
&stage_str,
|
||||
&story_id,
|
||||
merged_at_float,
|
||||
resume_to.as_deref(),
|
||||
claim_agent.as_deref(),
|
||||
claim_ts_secs,
|
||||
)?;
|
||||
|
||||
Some(PipelineItemView {
|
||||
story_id,
|
||||
@@ -407,7 +414,6 @@ pub(super) fn extract_item_view(item: &PipelineItemCrdt) -> Option<PipelineItemV
|
||||
agent,
|
||||
retry_count,
|
||||
depends_on,
|
||||
claim,
|
||||
qa_mode,
|
||||
item_type,
|
||||
epic,
|
||||
@@ -432,9 +438,11 @@ fn project_stage_for_view(
|
||||
story_id: &str,
|
||||
merged_at: Option<f64>,
|
||||
resume_to: Option<&str>,
|
||||
claim_agent: Option<&str>,
|
||||
claim_ts_secs: Option<u64>,
|
||||
) -> Option<crate::pipeline_state::Stage> {
|
||||
use crate::pipeline_state::{ArchiveReason, BranchName, GitSha, Stage};
|
||||
use chrono::{DateTime, Utc};
|
||||
use crate::pipeline_state::{AgentClaim, AgentName, ArchiveReason, BranchName, GitSha, Stage};
|
||||
use chrono::{DateTime, TimeZone, Utc};
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
// Normalise legacy directory-style strings to their clean wire form so
|
||||
@@ -458,13 +466,30 @@ fn project_stage_for_view(
|
||||
// Story 945: resume target for `Frozen` / `ReviewHold` variants is stored
|
||||
// in the sibling `resume_to` register. Fall back to `Coding` when the
|
||||
// register is empty or holds an unrecognised value.
|
||||
let resume_target =
|
||||
|| -> Box<Stage> { Box::new(resume_to.and_then(Stage::from_dir).unwrap_or(Stage::Coding)) };
|
||||
let resume_target = || -> Box<Stage> {
|
||||
Box::new(
|
||||
resume_to
|
||||
.and_then(Stage::from_dir)
|
||||
.unwrap_or(Stage::Coding { claim: None }),
|
||||
)
|
||||
};
|
||||
|
||||
// Story 1009: reconstruct AgentClaim from `claim_agent`/`claim_ts` registers.
|
||||
let claim = match (claim_agent, claim_ts_secs) {
|
||||
(Some(agent_str), Some(ts)) => Some(AgentClaim {
|
||||
agent: AgentName(agent_str.to_string()),
|
||||
claimed_at: Utc
|
||||
.timestamp_opt(ts as i64, 0)
|
||||
.single()
|
||||
.unwrap_or(DateTime::<Utc>::UNIX_EPOCH),
|
||||
}),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
match clean {
|
||||
"upcoming" => Some(Stage::Upcoming),
|
||||
"backlog" => Some(Stage::Backlog),
|
||||
"coding" => Some(Stage::Coding),
|
||||
"coding" => Some(Stage::Coding { claim }),
|
||||
"qa" => Some(Stage::Qa),
|
||||
"blocked" => Some(Stage::Blocked {
|
||||
reason: String::new(),
|
||||
@@ -472,6 +497,7 @@ fn project_stage_for_view(
|
||||
"merge" => Some(Stage::Merge {
|
||||
feature_branch: BranchName(format!("feature/story-{story_id}")),
|
||||
commits_ahead: NonZeroU32::new(1).expect("1 is non-zero"),
|
||||
claim,
|
||||
}),
|
||||
"merge_failure" => {
|
||||
// Story 986: read the typed kind directly from ContentKey::MergeFailureKind
|
||||
@@ -709,8 +735,6 @@ mod tests {
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
|
||||
// The story is live on this node.
|
||||
@@ -779,8 +803,6 @@ mod tests {
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
assert!(
|
||||
read_item(story_id).is_none(),
|
||||
|
||||
Reference in New Issue
Block a user