Review closed semantic constructor groups with retained typed drafts
This commit is contained in:
@@ -266,7 +266,7 @@ impl State {
|
||||
.iter()
|
||||
.filter(|f| f.drift == Drift::Unreviewed)
|
||||
.count();
|
||||
content = content.push(text(format!("{} selected decisions · {unreviewed} currently unreviewed facts · Other constructor groups and structured collections require further review.", self.decisions.len())).size(10).color(style::MUTED));
|
||||
content = content.push(text(format!("{} selected decisions · {unreviewed} currently unreviewed facts · Independent keyed/ordered decisions and historical reset remain pending.", self.decisions.len())).size(10).color(style::MUTED));
|
||||
if let Some(trace) = trace {
|
||||
content = content.push(text(summary(project, trace)).size(10).color(style::MUTED));
|
||||
}
|
||||
@@ -364,31 +364,42 @@ impl State {
|
||||
.and_then(|t| t.baseline.as_ref())
|
||||
.and_then(|b| b.semantic.as_ref())
|
||||
.and_then(|b| b.facets.iter().find(|f| f.selector() == fact.selector));
|
||||
let is_message_kind = message_kind::handles(&fact.selector);
|
||||
let timer_previous = trace
|
||||
.and_then(|t| t.baseline.as_ref())
|
||||
.and_then(|b| b.semantic.as_ref())
|
||||
.and_then(|b| {
|
||||
b.facets
|
||||
.iter()
|
||||
.find(|f| f.selector() == message_kind::timer_selector())
|
||||
});
|
||||
let timer_current = self
|
||||
.facts
|
||||
.iter()
|
||||
.find(|f| f.selector == message_kind::timer_selector());
|
||||
let is_group = scalar::grouped(&fact.selector);
|
||||
let group_observations = |project: &Project, facts: Vec<&FacetReview>, layer| {
|
||||
facts
|
||||
.into_iter()
|
||||
.filter(|f| constructors::contains(&fact.selector, &f.selector()))
|
||||
.map(|f| {
|
||||
format!(
|
||||
"{}: {}",
|
||||
title(project, &f.selector()),
|
||||
observed(project, Some(f), layer)
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
};
|
||||
let side = |layer, label: &str| {
|
||||
let mut before = observed(project, previous, layer);
|
||||
let mut current = observed(project, Some(&fact.current), layer);
|
||||
if is_message_kind {
|
||||
before.push_str(&format!(
|
||||
"\nTimer expression: {}",
|
||||
observed(project, timer_previous, layer)
|
||||
));
|
||||
current.push_str(&format!(
|
||||
"\nTimer expression: {}",
|
||||
observed(project, timer_current.map(|f| &f.current), layer)
|
||||
));
|
||||
if is_group {
|
||||
before = group_observations(
|
||||
project,
|
||||
trace
|
||||
.and_then(|t| t.baseline.as_ref())
|
||||
.and_then(|b| b.semantic.as_ref())
|
||||
.map(|b| b.facets.iter().collect())
|
||||
.unwrap_or_default(),
|
||||
layer,
|
||||
);
|
||||
if before.is_empty() {
|
||||
before = "No previous group observations".into();
|
||||
}
|
||||
current = group_observations(
|
||||
project,
|
||||
self.facts.iter().map(|f| &f.current).collect(),
|
||||
layer,
|
||||
);
|
||||
}
|
||||
column![
|
||||
text(format!(
|
||||
@@ -426,11 +437,17 @@ impl State {
|
||||
.color(style::TEAL),
|
||||
)
|
||||
.push(
|
||||
text(if is_message_kind {
|
||||
self.target(project)
|
||||
.and_then(|id| message_kind::source_value(proposed, id).ok())
|
||||
.map(|value| formatted(proposed, &value, &BTreeSet::new()))
|
||||
.unwrap_or_else(|| "Message kind is not applicable.".into())
|
||||
text(if is_group {
|
||||
semantic_facets(proposed, &proposed.traces[&trace.id], &self.references)
|
||||
.ok()
|
||||
.map(|facts| {
|
||||
group_observations(
|
||||
proposed,
|
||||
facts.iter().map(|f| &f.current).collect(),
|
||||
self.direction.target_layer(),
|
||||
)
|
||||
})
|
||||
.unwrap_or_else(|| "Group payload is unavailable.".into())
|
||||
} else {
|
||||
observed(proposed, Some(&after), self.direction.target_layer())
|
||||
})
|
||||
@@ -444,10 +461,11 @@ impl State {
|
||||
);
|
||||
}
|
||||
if self.editable(project, &fact.selector) {
|
||||
if is_message_kind {
|
||||
detail = detail.push(text("A replacing message-kind decision reviews its Timer Expression as a group. The exact acknowledgment and retirement appear below.").size(12));
|
||||
if is_group {
|
||||
detail = detail.push(text(constructors::description(&fact.selector)).size(12));
|
||||
detail = detail.push(text("Replacing a kind reviews its complete payload. Review every removed reference, row and prior observation in the exact scope below; changing kind retains your draft for switching back.").size(11));
|
||||
if self.can_acknowledge_constructor(project, &fact.selector) {
|
||||
detail = detail.push(text("These initial kinds are equal. Keep or Take acknowledges only the kind; an unchanged final kind also needs a separate Timer Expression decision for expression edits.").size(11).color(style::MUTED));
|
||||
detail = detail.push(text("These initial kinds are equal. Keep or Take acknowledges only the kind; an unchanged Candidate kind needs independent descendant decisions for payload edits.").size(11).color(style::MUTED));
|
||||
}
|
||||
}
|
||||
let destination = if self.direction.target_layer() == Layer::Sa {
|
||||
@@ -472,8 +490,8 @@ impl State {
|
||||
]
|
||||
.spacing(5);
|
||||
buttons = buttons.push(action(
|
||||
if is_message_kind {
|
||||
"Set final message kind…"
|
||||
if is_group {
|
||||
"Set final kind and payload…"
|
||||
} else {
|
||||
"Set final value…"
|
||||
},
|
||||
@@ -485,52 +503,85 @@ impl State {
|
||||
));
|
||||
if self.decisions.get(&fact.selector) == Some(&SemanticDecision::Candidate)
|
||||
&& let Some(draft) = self.candidate_draft(&fact.selector)
|
||||
&& let Some(path) = self
|
||||
&& let Some(paths) = self
|
||||
.target(project)
|
||||
.and_then(|id| scalar::binding(project, id, &fact.selector))
|
||||
.and_then(|id| constructors::roots(project, id, &fact.selector))
|
||||
{
|
||||
detail = detail.push(text("Final destination value").size(12)).push(
|
||||
crate::forms::scoped(draft, project, &path, &title(project, &fact.selector))
|
||||
detail = detail.push(text("Final destination value").size(12));
|
||||
for path in paths {
|
||||
let label = if path.ends_with("/default") {
|
||||
"Default"
|
||||
} else if path.ends_with("/legacy") {
|
||||
"Generic association"
|
||||
} else {
|
||||
"Kind and payload"
|
||||
};
|
||||
detail = detail.push(
|
||||
crate::forms::scoped(
|
||||
draft,
|
||||
project,
|
||||
&path,
|
||||
if is_group { label } else { "Final value" },
|
||||
)
|
||||
.map(form_event),
|
||||
);
|
||||
);
|
||||
}
|
||||
}
|
||||
} else if self.can_acknowledge_constructor(project, &fact.selector) {
|
||||
detail = detail.push(text("Both constructor kinds are equal and have no prior observation. Acknowledge only the kind; its properties remain independent.").size(12))
|
||||
.push(action("Acknowledge equal kind only", Event::Decide(fact.selector.clone(), Some(SemanticDecision::KeepTarget))))
|
||||
.push(action("Leave unselected", Event::Decide(fact.selector.clone(), None)));
|
||||
} else {
|
||||
detail = detail.push(text(if scalar::grouped(&fact.selector) { "Constructor groups are shown for comparison. Group replacement and its complete descendant review are a later milestone." } else { "This structured or inapplicable fact is read-only here. Its review history is retained." }).size(12).color(style::MUTED));
|
||||
detail = detail.push(text(if scalar::grouped(&fact.selector) { "This constructor is unavailable on one of the current counterparts. Review the outer constructor before its nested kind." } else { "This structured or inapplicable fact is read-only here. Its review history is retained." }).size(12).color(style::MUTED));
|
||||
}
|
||||
if is_message_kind || fact.selector == message_kind::timer_selector() {
|
||||
if self.decisions.contains_key(&message_kind::selector())
|
||||
&& self.decisions.contains_key(&message_kind::timer_selector())
|
||||
{
|
||||
detail = detail.push(text("Both kind and timer decisions are selected. An initial equal-kind acknowledgment can coexist with a timer decision; a replacing group requires one combined decision. Resolve any overlap explicitly; both drafts are retained.").size(11))
|
||||
.push(action("Replace timer decision with message-kind group", Event::Decide(message_kind::timer_selector(), None)))
|
||||
.push(action("Leave message kind unselected; use timer decision", Event::Decide(message_kind::selector(), None)));
|
||||
let overlaps: Vec<_> = self
|
||||
.decisions
|
||||
.keys()
|
||||
.filter(|other| {
|
||||
**other != fact.selector
|
||||
&& (constructors::contains(&fact.selector, other)
|
||||
|| constructors::contains(other, &fact.selector))
|
||||
})
|
||||
.collect();
|
||||
if self.decisions.contains_key(&fact.selector) && !overlaps.is_empty() {
|
||||
detail = detail.push(text("Related decisions are selected. A first equal-kind acknowledgment can coexist with descendants; a replacing group requires one combined decision. Resolve overlaps explicitly; all drafts are retained.").size(11));
|
||||
for other in overlaps {
|
||||
detail = detail.push(action(
|
||||
format!("Leave {} unselected", title(project, other)),
|
||||
Event::Decide(other.clone(), None),
|
||||
));
|
||||
}
|
||||
if let Some(review) = self.plan.as_ref().and_then(|p| p.reviews.first()) {
|
||||
detail = detail.push(text("Exact review scope from preview").size(13));
|
||||
for (label, selectors) in [
|
||||
("Acknowledge", &review.acknowledged),
|
||||
("Retire previous observation", &review.retired),
|
||||
] {
|
||||
detail = detail.push(
|
||||
text(format!(
|
||||
"{label}: {}",
|
||||
if selectors.is_empty() {
|
||||
"None".into()
|
||||
} else {
|
||||
selectors
|
||||
.iter()
|
||||
.map(|s| title(project, s))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
}
|
||||
))
|
||||
.size(11),
|
||||
);
|
||||
}
|
||||
detail = detail.push(action(
|
||||
"Leave this decision unselected; use the related decisions",
|
||||
Event::Decide(fact.selector.clone(), None),
|
||||
));
|
||||
}
|
||||
if (is_group
|
||||
|| constructors::selectors().iter().any(|group| {
|
||||
self.decisions.contains_key(group) && constructors::contains(group, &fact.selector)
|
||||
}))
|
||||
&& let Some(review) = self.plan.as_ref().and_then(|p| p.reviews.first())
|
||||
{
|
||||
detail = detail.push(text("Exact review scope from preview").size(13));
|
||||
for (label, selectors) in [
|
||||
("Acknowledge", &review.acknowledged),
|
||||
("Retire previous observation", &review.retired),
|
||||
] {
|
||||
detail = detail.push(
|
||||
text(format!(
|
||||
"{label}: {}",
|
||||
if selectors.is_empty() {
|
||||
"None".into()
|
||||
} else {
|
||||
selectors
|
||||
.iter()
|
||||
.map(|s| title(project, s))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
}
|
||||
))
|
||||
.size(11),
|
||||
);
|
||||
}
|
||||
}
|
||||
if matches!(
|
||||
|
||||
Reference in New Issue
Block a user