* Make sequence-policy stability mode lock satisfied constraint endpoints
Stability-mode sequence policies were only influencing WebCola via seed
positions plus fixed=1 on unconstrained nodes. Any node touching a
Left/Top/Alignment constraint was unconditionally unfixed, so the solver
re-placed it on every frame and prior layouts visibly drifted between
sequence steps.
Move the unfix decision out of toColaConstraint into a post-pass that
checks each constraint against the prior positions: satisfied → both
endpoints stay locked, violated → both unfixed so the solver can
repair, and unknown (one side is a new node) → only the unseeded
endpoint is unfixed. Legacy behavior is preserved when
lockUnconstrainedNodes is false.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Make constraint-aware locking O(c) and consolidate helpers
Three structural fixes to the post-pass added in the previous commit:
- Add nodeIndexMap so getNodeIndex is O(1) instead of an O(n)
findIndex. The existing toColaConstraint loop benefits too.
- Fold endpointsOf + evaluateConstraintAtPriorPositions into one
classifyConstraintForLocking switch — each constraint pays one
type-dispatch and two O(1) node lookups instead of four.
- Short-circuit applyConstraintAwareLocking when no node was ever
locked (legacy mode + empty priorPositionMap).
Net: post-pass is O(c), constructor is O(c + n).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Verify post-solver constraint satisfaction with locking enabled
The earlier locking tests only checked the pre-solver `fixed` flag;
they did not prove WebCola's final layout actually satisfies the
LayoutConstraints when locks reduce the solver's degrees of freedom.
Add a `Post-solver constraint satisfaction` block that runs cola.Layout
to convergence (matching the production reduced-iterations path) and
asserts every original LeftConstraint / TopConstraint /
AlignmentConstraint holds at the final node positions. Covers:
- satisfied prior + locked: locks hold AND constraint still holds
- violated prior: solver repairs the violation
- mixed prior + new node: new node placed without violating anything
- legacy mode: regression check on the non-stability path
Catches both classes of regression: lock-induced infeasibility and
iteration-cap exhaustion under reduced iterations.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>