#![cfg_attr(not(feature = "extension-backlog"), allow(dead_code))]
use std::collections::BTreeSet;
use serde::{Deserialize, Serialize};
use crate::extensions::{WorkQueueSnapshot, WorkQueueSnapshotItem};
use crate::state::issue_refs::{self, IssueReference};
const PRIMARY_SOURCE_ORDER: &[&str] = &["locality_focus_assignment"];
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub(crate) struct ResolvedBacklogItem {
pub backlog_provider: String,
pub ccd_id: u64,
pub github_issue_number: u64,
pub title: String,
pub url: String,
pub closed: bool,
pub out_of_queue: bool,
}
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "snake_case")]
pub(crate) enum ContinuityResolutionStatus {
ResolvedOpen,
ResolvedClosed,
Unresolved,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)]
#[serde(rename_all = "snake_case")]
pub(crate) enum ContinuityMatchedVia {
Cache,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct ResolvedIssueReference {
pub reference: IssueReference,
pub resolution_status: ContinuityResolutionStatus,
pub matched_via: ContinuityMatchedVia,
pub title: Option<String>,
pub url: Option<String>,
pub cache_item: Option<ResolvedBacklogItem>,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub(crate) struct ContinuityReferenceQuery {
pub source: String,
pub kind: issue_refs::IssueReferenceKind,
pub number: u64,
pub raw_reference: String,
pub context: String,
pub primary: bool,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub(crate) struct TransientContinuityReferenceResolution {
pub source: String,
pub kind: issue_refs::IssueReferenceKind,
pub number: u64,
pub raw_reference: String,
pub context: String,
pub resolution_status: ContinuityResolutionStatus,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub url: Option<String>,
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub(crate) struct IssueReferenceReport {
pub references: Vec<IssueReference>,
pub matches: Vec<ResolvedIssueReference>,
}
impl IssueReferenceReport {
#[cfg_attr(not(test), allow(dead_code))]
pub(crate) fn has_primary_reference(&self) -> bool {
self.primary_source().is_some()
}
#[cfg_attr(not(test), allow(dead_code))]
pub(crate) fn primary_references_all_matched(&self) -> bool {
let Some(primary_source) = self.primary_source() else {
return false;
};
if self.primary_target_is_ambiguous() {
return false;
}
let primary_references = self
.references
.iter()
.filter(|reference| reference.source == primary_source)
.count();
let primary_matches = self
.matches
.iter()
.filter(|resolved| resolved.reference.source == primary_source)
.count();
primary_references == primary_matches
}
#[cfg_attr(not(test), allow(dead_code))]
pub(crate) fn reference_evidence_lines(&self) -> Vec<String> {
self.references
.iter()
.map(|reference| {
format!(
"{} references {} via `{}`.",
source_label(reference),
reference.display(),
reference.raw_reference
)
})
.collect()
}
#[cfg_attr(not(test), allow(dead_code))]
pub(crate) fn matched_reference_count(&self) -> usize {
self.matches.len()
}
pub(crate) fn has_closed_matches(&self) -> bool {
self.matches.iter().any(|resolved| {
resolved.resolution_status == ContinuityResolutionStatus::ResolvedClosed
})
}
pub(crate) fn has_out_of_queue_matches(&self) -> bool {
self.matches.iter().any(|resolved| {
resolved
.cache_item
.as_ref()
.map(|item| !item.closed && item.out_of_queue)
.unwrap_or(false)
})
}
pub(crate) fn unique_closed_items(&self) -> Vec<ResolvedBacklogItem> {
let mut seen = BTreeSet::new();
let mut items = Vec::new();
for resolved in &self.matches {
let Some(item) = resolved.cache_item.as_ref() else {
continue;
};
if !item.closed {
continue;
}
let key = (item.ccd_id, item.github_issue_number);
if seen.insert(key) {
items.push(item.clone());
}
}
items
}
pub(crate) fn start_alert_message(&self, cache_status: &str) -> String {
let cache_label = if cache_status == "stale" {
"stale cached work queue"
} else {
"cached work queue"
};
let closed_items = self.unique_closed_items();
if closed_items.len() == 1 {
let item = &closed_items[0];
return format!(
"workspace-local handoff, work stream, or project-focus assignment references closed work item {}; the {cache_label} already marks it closed, so retarget the next-session handoff or work stream before relying on it",
item.display_label()
);
}
format!(
"workspace-local handoff, work stream, or project-focus assignments reference {} closed work items; the {cache_label} already marks them closed, so retarget the next-session handoff or work stream before relying on them",
closed_items.len()
)
}
#[cfg_attr(not(test), allow(dead_code))]
pub(crate) fn closed_evidence_lines(&self) -> Vec<String> {
let mut evidence = Vec::new();
for resolved in &self.matches {
if resolved.resolution_status != ContinuityResolutionStatus::ResolvedClosed {
continue;
}
if let Some(item) = resolved.cache_item.as_ref() {
evidence.push(format!(
"{} references {} via `{}`; {} marks {} as closed.",
source_label(&resolved.reference),
resolved.reference.display(),
resolved.reference.raw_reference,
matched_via_label(resolved.matched_via),
item.display_label()
));
} else {
let title = resolved.title.as_deref().unwrap_or("unlabeled work item");
evidence.push(format!(
"{} references {} via `{}`; {} marks `{}` as closed.",
source_label(&resolved.reference),
resolved.reference.display(),
resolved.reference.raw_reference,
matched_via_label(resolved.matched_via),
title
));
}
}
evidence
}
#[cfg_attr(not(test), allow(dead_code))]
pub(crate) fn active_evidence_lines(&self) -> Vec<String> {
let mut evidence = Vec::new();
for resolved in &self.matches {
if resolved.resolution_status != ContinuityResolutionStatus::ResolvedOpen {
continue;
}
let item_label = if let Some(item) = resolved.cache_item.as_ref() {
item.display_label()
} else {
format!(
"`{}`",
resolved.title.as_deref().unwrap_or("open work item")
)
};
if resolved
.cache_item
.as_ref()
.map(|item| item.out_of_queue)
.unwrap_or(false)
{
evidence.push(format!(
"{} still maps to open but out-of-queue work item {} via {}.",
source_label(&resolved.reference),
item_label,
matched_via_label(resolved.matched_via)
));
} else {
evidence.push(format!(
"{} still maps to active work item {} via {}.",
source_label(&resolved.reference),
item_label,
matched_via_label(resolved.matched_via)
));
}
}
evidence
}
#[cfg_attr(not(test), allow(dead_code))]
pub(crate) fn out_of_queue_evidence_lines(&self) -> Vec<String> {
let mut evidence = Vec::new();
for resolved in &self.matches {
let Some(item) = resolved.cache_item.as_ref() else {
continue;
};
if item.closed || !item.out_of_queue {
continue;
}
evidence.push(format!(
"{} references {} via `{}`; {} resolves it to {} that is no longer queue-scoped.",
source_label(&resolved.reference),
resolved.reference.display(),
resolved.reference.raw_reference,
matched_via_label(resolved.matched_via),
item.out_of_queue_label()
));
}
evidence
}
pub(crate) fn out_of_queue_start_alert_message(&self) -> String {
let count = self
.matches
.iter()
.filter(|resolved| {
resolved
.cache_item
.as_ref()
.map(|item| !item.closed && item.out_of_queue)
.unwrap_or(false)
})
.count();
if count == 1 {
if let Some(item) = self.matches.iter().find_map(|resolved| {
resolved
.cache_item
.as_ref()
.filter(|item| !item.closed && item.out_of_queue)
}) {
return format!(
"workspace-local handoff, work stream, or project-focus assignment still references {} that is no longer queue-scoped by CCD priority labels; keep continuity if intentional or relabel/retarget it before relying on backlog selection",
item.out_of_queue_label()
);
}
return "workspace-local handoff, work stream, or project-focus assignment still references an open work item that is no longer queue-scoped by CCD priority labels; keep continuity if intentional or relabel/retarget it before relying on backlog selection".to_owned();
}
format!(
"workspace-local handoff, work stream, or project-focus assignments still reference {count} open work items that are no longer queue-scoped by CCD priority labels; keep continuity if intentional or relabel/retarget them before relying on backlog selection"
)
}
fn primary_source(&self) -> Option<&'static str> {
PRIMARY_SOURCE_ORDER.iter().copied().find(|source| {
self.references
.iter()
.any(|reference| reference.source == *source)
})
}
fn primary_target_is_ambiguous(&self) -> bool {
let Some(primary_source) = self.primary_source() else {
return false;
};
let references = self
.references
.iter()
.filter(|reference| reference.source == primary_source)
.collect::<Vec<_>>();
select_primary_reference(&references).is_none() && !references.is_empty()
}
}
impl ResolvedBacklogItem {
fn provider_is_github(&self) -> bool {
self.backlog_provider == "github-issues" && self.github_issue_number != 0
}
fn display_reference(&self) -> String {
let ccd_ref = (self.ccd_id != 0).then(|| format!("ccd#{}", self.ccd_id));
let github_ref = self
.provider_is_github()
.then(|| format!("GH#{}", self.github_issue_number));
match (ccd_ref, github_ref) {
(Some(ccd_ref), Some(github_ref)) if ccd_ref != github_ref => {
format!("{ccd_ref} / {github_ref}")
}
(Some(ccd_ref), _) => ccd_ref,
(None, Some(github_ref)) => github_ref,
(None, None) => "work item".to_owned(),
}
}
fn display_label(&self) -> String {
format!("`{}` `{}`", self.display_reference(), self.title)
}
fn out_of_queue_label(&self) -> String {
if self.provider_is_github() {
format!(
"open GitHub issue `GH#{}` `{}`",
self.github_issue_number, self.title
)
} else {
format!("open work item {}", self.display_label())
}
}
}
pub(crate) fn resolve_issue_references(
branch: &str,
handoff_title: &str,
immediate_actions: &[String],
backlog_cache: Option<&WorkQueueSnapshot>,
) -> IssueReferenceReport {
let references = issue_refs::collect_issue_references(branch, handoff_title, immediate_actions);
let Some(cache) = backlog_cache else {
return IssueReferenceReport {
references,
matches: Vec::new(),
};
};
let matches = references
.iter()
.filter_map(|reference| resolve_reference(reference, &cache.items))
.collect();
IssueReferenceReport {
references,
matches,
}
}
fn resolve_reference(
reference: &IssueReference,
items: &[WorkQueueSnapshotItem],
) -> Option<ResolvedIssueReference> {
let matched = match reference.kind {
issue_refs::IssueReferenceKind::GithubIssue => items
.iter()
.filter(|item| item.github_issue_number == reference.number)
.collect::<Vec<_>>(),
issue_refs::IssueReferenceKind::CcdId => items
.iter()
.filter(|item| item.ccd_id == reference.number)
.collect::<Vec<_>>(),
issue_refs::IssueReferenceKind::Ambiguous => {
let github_matches = items
.iter()
.filter(|item| item.github_issue_number == reference.number)
.collect::<Vec<_>>();
let ccd_matches = items
.iter()
.filter(|item| item.ccd_id == reference.number)
.collect::<Vec<_>>();
match (github_matches.is_empty(), ccd_matches.is_empty()) {
(true, true) => Vec::new(),
(false, true) => github_matches,
(true, false) => ccd_matches,
(false, false) => {
let mut merged = github_matches;
for item in ccd_matches {
if !merged.iter().any(|existing| {
existing.ccd_id == item.ccd_id
&& existing.github_issue_number == item.github_issue_number
}) {
merged.push(item);
}
}
let has_closed = merged.iter().any(|item| item.closed);
let has_open = merged.iter().any(|item| !item.closed);
if has_closed && has_open {
Vec::new()
} else {
merged
}
}
}
}
};
if matched.is_empty() || matched.len() != 1 {
return None;
}
let item = matched[0];
let cache_item = ResolvedBacklogItem {
backlog_provider: item.backlog_ref.provider.clone(),
ccd_id: item.ccd_id,
github_issue_number: item.github_issue_number,
title: item.title.clone(),
url: item.url.clone(),
closed: item.closed,
out_of_queue: !item.closed && item.queue_state == "out_of_queue",
};
Some(ResolvedIssueReference {
reference: reference.clone(),
resolution_status: if cache_item.closed {
ContinuityResolutionStatus::ResolvedClosed
} else {
ContinuityResolutionStatus::ResolvedOpen
},
matched_via: ContinuityMatchedVia::Cache,
title: Some(cache_item.title.clone()),
url: Some(cache_item.url.clone()),
cache_item: Some(cache_item),
})
}
#[cfg_attr(not(test), allow(dead_code))]
fn source_label(reference: &IssueReference) -> String {
match reference.source {
"locality_focus_assignment" => {
format!(
"Project-focus assignment for work stream `{}`",
reference.context
)
}
"branch" => format!("Work stream `{}`", reference.context),
"handoff_title" => format!("Handoff title `{}`", reference.context),
"immediate_action" => format!("Immediate action `{}`", reference.context),
_ => format!("Reference `{}`", reference.context),
}
}
fn select_primary_reference<'a>(
references: &'a [&'a IssueReference],
) -> Option<&'a IssueReference> {
let first = *references.first()?;
if references
.iter()
.all(|reference| same_reference_target(reference, first))
{
Some(first)
} else {
None
}
}
fn same_reference_target(left: &IssueReference, right: &IssueReference) -> bool {
left.kind == right.kind && left.number == right.number
}
#[cfg_attr(not(test), allow(dead_code))]
fn matched_via_label(matched_via: ContinuityMatchedVia) -> &'static str {
match matched_via {
ContinuityMatchedVia::Cache => "the workspace-local work queue cache",
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::content_trust::ContentTrust;
fn test_item(
ccd_id: u64,
github_issue_number: u64,
title: &str,
github_state: &str,
) -> WorkQueueSnapshotItem {
WorkQueueSnapshotItem {
backlog_ref: crate::extensions::BacklogRef {
provider: "github-issues".to_owned(),
kind: "issue".to_owned(),
id: github_issue_number.to_string(),
url: format!("https://example.com/issues/{github_issue_number}"),
},
ccd_id,
github_issue_number,
content_trust: ContentTrust::ExternalAdapterOutput,
title: title.to_owned(),
url: format!("https://example.com/issues/{github_issue_number}"),
section: "tests".to_owned(),
status: if github_state.eq_ignore_ascii_case("closed") {
"done".to_owned()
} else {
"ready".to_owned()
},
queue_state: "queue_candidate",
dispatch_state: "ready",
metadata_status: "enriched",
upstream_claim: "unclaimed",
priority_label: None,
claimed_by: None,
priority_rank: None,
closed: github_state.eq_ignore_ascii_case("closed"),
}
}
fn local_markdown_item(
ccd_id: u64,
github_issue_number: u64,
title: &str,
github_state: &str,
out_of_queue: bool,
) -> WorkQueueSnapshotItem {
WorkQueueSnapshotItem {
backlog_ref: crate::extensions::BacklogRef {
provider: "local-markdown".to_owned(),
kind: "item".to_owned(),
id: ccd_id.to_string(),
url: format!("file:///tmp/local-markdown-{ccd_id}"),
},
ccd_id,
github_issue_number,
content_trust: ContentTrust::ExternalAdapterOutput,
title: title.to_owned(),
url: format!("file:///tmp/local-markdown-{ccd_id}"),
section: "tests".to_owned(),
status: if github_state == "closed" {
"done".to_owned()
} else {
"ready".to_owned()
},
queue_state: if out_of_queue {
"out_of_queue"
} else {
"queue_candidate"
},
dispatch_state: if out_of_queue {
"out_of_queue"
} else {
"ready"
},
metadata_status: "absent",
upstream_claim: "unclaimed",
priority_label: None,
claimed_by: None,
priority_rank: Some(ccd_id),
closed: github_state.eq_ignore_ascii_case("closed"),
}
}
fn test_snapshot(items: Vec<WorkQueueSnapshotItem>) -> WorkQueueSnapshot {
WorkQueueSnapshot {
content_trust: ContentTrust::ExternalAdapterOutput,
provider: "github-issues".to_owned(),
repo: "example/ccd-guide".to_owned(),
fetched_at_epoch_s: 1,
stale_after_s: crate::extensions::work_queue::DEFAULT_STALE_AFTER_SECS,
revalidate_on_refresh: false,
queue_summary: crate::extensions::work_queue::WorkQueueSummary::default(),
dispatch: None,
active_items: Vec::new(),
items,
}
}
#[test]
fn ambiguous_branch_reference_is_ignored_when_cache_conflicts() {
let cache = test_snapshot(vec![
test_item(46, 400, "[ccd#46] local id", "open"),
test_item(99, 46, "github issue 46", "closed"),
]);
let report = resolve_issue_references(
"codex/ccd-46-retry-backoff",
"Next Session: follow-up",
&[],
Some(&cache),
);
assert!(report.matches.is_empty());
}
#[test]
fn closed_evidence_uses_cache_matches() {
let cache = test_snapshot(vec![test_item(
71,
46,
"[ccd#71] retry backoff cleanup",
"closed",
)]);
let report = resolve_issue_references(
"main",
"Next Session: follow-up",
&["Review GH#46 before branching.".to_owned()],
Some(&cache),
);
assert!(report.has_closed_matches());
assert!(report.closed_evidence_lines()[0].contains("GH#46"));
}
#[test]
fn handoff_and_branch_references_are_observed_without_assignment() {
let cache = test_snapshot(vec![test_item(
46,
122,
"[ccd#46] retry backoff cleanup",
"open",
)]);
let report = resolve_issue_references(
"codex/122-follow-up",
"Next Session: Review GH#122 follow-up",
&["Inspect ccd#46 before merge.".to_owned()],
Some(&cache),
);
assert!(!report.has_primary_reference());
assert!(report
.references
.iter()
.any(|reference| reference.source == "handoff_title"));
assert!(report
.references
.iter()
.any(|reference| reference.source == "branch"));
}
#[test]
fn local_markdown_closed_messages_do_not_emit_phantom_github_refs() {
let cache = test_snapshot(vec![local_markdown_item(
71,
171,
"[ccd#71] local markdown item",
"closed",
false,
)]);
let report =
resolve_issue_references("main", "Next Session: inspect ccd#71", &[], Some(&cache));
assert!(report.has_closed_matches());
assert!(report.closed_evidence_lines()[0].contains("ccd#71"));
assert!(!report.closed_evidence_lines()[0].contains("GH#171"));
}
#[test]
fn local_markdown_out_of_queue_messages_do_not_emit_phantom_github_refs() {
let cache = test_snapshot(vec![local_markdown_item(
71,
171,
"[ccd#71] local markdown item",
"open",
true,
)]);
let report =
resolve_issue_references("main", "Next Session: inspect ccd#71", &[], Some(&cache));
assert!(report.has_out_of_queue_matches());
assert!(report.out_of_queue_evidence_lines()[0].contains("ccd#71"));
assert!(!report.out_of_queue_evidence_lines()[0].contains("GH#171"));
assert!(report.out_of_queue_start_alert_message().contains("ccd#71"));
assert!(!report.out_of_queue_start_alert_message().contains("GH#171"));
assert!(!report
.out_of_queue_start_alert_message()
.contains("GitHub issue"));
}
}