Allow first review of equal constructor discriminators

This commit is contained in:
2026-09-06 06:01:48 +03:00
parent d130013155
commit 6bb2583b4a
5 changed files with 808 additions and 22 deletions
+38 -10
View File
@@ -562,8 +562,32 @@ fn build_review(
let all = semantic_facets(initial, &trace, &references)?;
let mut acknowledged = vec![];
let mut retired = vec![];
let mut resolutions = request.resolutions.iter().collect::<Vec<_>>();
for (i, resolution) in resolutions.iter().enumerate() {
let mut resolutions = request
.resolutions
.iter()
.map(|resolution| {
let current = capture_facet(initial, oa, sa, &resolution.facet)?;
// A first equal discriminator observation has only its scalar
// scope. Existing history and actual proposals retain the full
// constructor group, including its no-switch rejection.
let first_equal_constructor = is_constructor(&resolution.facet)
&& !baseline
.facets
.iter()
.any(|old| old.selector() == resolution.facet)
&& side(&current, source_layer) == side(&current, target_layer)
&& (resolution.decision != SemanticDecision::Candidate
|| side(
&capture_facet(&candidates, oa, sa, &resolution.facet)?,
target_layer,
) == side(&current, target_layer));
Ok((
resolution,
is_constructor(&resolution.facet) && !first_equal_constructor,
))
})
.collect::<Result<Vec<_>, String>>()?;
for (i, (resolution, constructor_group)) in resolutions.iter().enumerate() {
check_keys(&resolution.facet, &references)?;
if let FacetSelector::Artifact { keys, facet } = &resolution.facet
&& *facet != ArtifactFacet::Presence
@@ -577,11 +601,10 @@ fn build_review(
}
}
}
capture_facet(initial, oa, sa, &resolution.facet)?;
if resolutions[..i].iter().any(|r| {
if resolutions[..i].iter().any(|(r, previous_group)| {
keyed_slots_overlap(&r.facet, &resolution.facet)
|| constructor_contains(&r.facet, &resolution.facet)
|| constructor_contains(&resolution.facet, &r.facet)
|| (*previous_group && constructor_contains(&r.facet, &resolution.facet))
|| (*constructor_group && constructor_contains(&resolution.facet, &r.facet))
}) {
return Err("Duplicate, colliding or overlapping resolution scopes".into());
}
@@ -611,8 +634,8 @@ fn build_review(
}
}
}
resolutions.sort_by_key(|r| {
if is_constructor(&r.facet) {
resolutions.sort_by_key(|(r, constructor_group)| {
if *constructor_group {
0
} else if matches!(r.facet, FacetSelector::Order(_)) {
2
@@ -620,10 +643,15 @@ fn build_review(
1
}
});
for resolution in resolutions {
for (resolution, constructor_group) in resolutions {
let selected = &resolution.facet;
let current = capture_facet(initial, oa, sa, selected)?;
if is_constructor(selected) {
if is_constructor(selected) && !constructor_group {
if let Some(slot) = target_slot(target, selected, target_layer)? {
agree(&mut slots, slot, side(&current, target_layer))?;
}
acknowledged.push(selected.clone());
} else if constructor_group {
let old = baseline.facets.iter().find(|v| v.selector() == *selected);
let current_source = side(&current, source_layer);
let current_target = side(&current, target_layer);