1
0
Fork 0
forked from wry/wry

Assert multiphase rejection diagnostics

This commit is contained in:
atagen 2026-05-21 21:16:54 +10:00
parent 332a7468f6
commit 01d1545c40
2 changed files with 92 additions and 8 deletions

View file

@ -245,6 +245,9 @@ Current pure planner status:
- Multiphase planning also has an explained-plan entry point. Accepted plans
report the deterministic strategy, phase reasons, participating nodes, and
validation result; rejected plans report every attempted strategy and failure.
- Rejection diagnostics are treated as contractual test output for unsupported
patterns and analytically invalid candidate plans, including attempted strategy
order and exact validation failure.
- Planner tests now include a deterministic split-tree generator. It builds
valid weighted tiling layouts, derives leaf hierarchy metadata, mutates them
through supported transitions, and runs the real planner plus exact validator.
@ -262,6 +265,7 @@ Tests:
- reversing direction produces equivalent motion in reverse
- accepted and rejected plans expose deterministic strategy explanations
- bounded generated split-tree corpus produces identical plans on repeated runs
- unsupported and invalid candidate plans produce exact expected diagnostics
- child waits for parent/container-space phases when moving upward into a
toplevel peer position
- mono-mode tab switches do not animate, while entering/exiting mono can animate

View file

@ -1538,6 +1538,49 @@ mod tests {
.to
}
fn no_pattern_attempts(direction: PlanDirection) -> Vec<RejectedStrategy> {
vec![
RejectedStrategy {
direction,
strategy: PlanStrategy::SingleAction,
reason: MultiphasePlanFailure::NoPattern,
},
RejectedStrategy {
direction,
strategy: PlanStrategy::HierarchyOrderedScales,
reason: MultiphasePlanFailure::NoPattern,
},
RejectedStrategy {
direction,
strategy: PlanStrategy::SwapLanes {
axis: PhaseAxis::Horizontal,
},
reason: MultiphasePlanFailure::NoPattern,
},
RejectedStrategy {
direction,
strategy: PlanStrategy::SwapLanes {
axis: PhaseAxis::Vertical,
},
reason: MultiphasePlanFailure::NoPattern,
},
RejectedStrategy {
direction,
strategy: PlanStrategy::SpaceThenOrthogonalGrowth {
axis: PhaseAxis::Horizontal,
},
reason: MultiphasePlanFailure::NoPattern,
},
RejectedStrategy {
direction,
strategy: PlanStrategy::SpaceThenOrthogonalGrowth {
axis: PhaseAxis::Vertical,
},
reason: MultiphasePlanFailure::NoPattern,
},
]
}
#[test]
fn horizontal_swap_shrinks_moves_then_grows_without_overlap() {
let req = request(vec![
@ -2136,14 +2179,9 @@ mod tests {
let diagnostic = plan_no_overlap_with_diagnostics(&req).unwrap_err();
assert_eq!(diagnostic.forward, MultiphasePlanFailure::NoPattern);
assert_eq!(diagnostic.reverse, Some(MultiphasePlanFailure::NoPattern));
assert!(
diagnostic
.attempted
.iter()
.any(|attempt| attempt.direction == PlanDirection::Forward
&& attempt.strategy == PlanStrategy::SingleAction
&& attempt.reason == MultiphasePlanFailure::NoPattern)
);
let mut expected = no_pattern_attempts(PlanDirection::Forward);
expected.extend(no_pattern_attempts(PlanDirection::Reverse));
assert_eq!(diagnostic.attempted, expected);
}
#[test]
@ -2176,6 +2214,48 @@ mod tests {
));
}
#[test]
fn diagnostics_report_candidate_validation_rejections() {
let req = request(vec![
MultiphaseWindow {
node_id: id(1),
from: rect(0, 0, 60, 60),
to: rect(180, 0, 240, 60),
hierarchy: Default::default(),
},
MultiphaseWindow {
node_id: id(2),
from: rect(90, 0, 150, 60),
to: rect(90, 0, 150, 60),
hierarchy: Default::default(),
},
]);
let rejection =
MultiphasePlanFailure::Validation(MultiphaseValidationError::PhaseOverlap {
phase: 0,
a: id(1),
b: id(2),
});
let diagnostic = plan_no_overlap_with_diagnostics(&req).unwrap_err();
assert_eq!(diagnostic.forward, rejection);
assert_eq!(diagnostic.reverse, Some(rejection));
assert_eq!(
diagnostic.attempted[0],
RejectedStrategy {
direction: PlanDirection::Forward,
strategy: PlanStrategy::SingleAction,
reason: rejection,
}
);
assert!(diagnostic.attempted.iter().any(|attempt| *attempt
== RejectedStrategy {
direction: PlanDirection::Reverse,
strategy: PlanStrategy::SingleAction,
reason: rejection,
}));
}
#[test]
fn hierarchy_metadata_classifies_depth_and_mono_transitions() {
let source = MultiphaseHierarchyPosition {