Files
ParkingRobot/docs/superpowers/specs/2026-07-31-local-g2-soft-anchor-candidate-recovery-design.md
T

213 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Local G2 Soft-Anchor Candidate Recovery Design
> **Superseded on 2026-08-01.** The mandatory feasibility gate found that all
> prescribed non-zero `0.05 m` profiles failed the unchanged vehicle-curvature
> gate, reaching approximately `14.86 1/m` against a `0.8333 1/m` limit.
> Production soft-anchor work stopped with the shared builder unchanged. The
> normative replacement is
> `2026-08-01-local-g2-split-derivative-scale-recovery-design.md`, which restores
> every primitive-boundary position as a hard anchor and uses independent
> incoming/outgoing derivative scales. The evaluator de-duplication and window
> target-coverage work completed before this failure remain valid.
## Goal
Repair the Local G2 candidate family so that a real, same-direction curvature
transition can produce a candidate accepted by the existing evaluator, without
weakening vehicle-curvature, raw-range, peak-gradient, collision, clearance,
deviation, or global-validation gates.
## Evidence and decision
The current two-piece quintic Hermite family holds every internal primitive
boundary at its original position and heading. For the real `SingleTurn`
fixture, a bounded sweep of internal derivative scales (`0.50` through `1.60`)
and shared curvatures within the original `[left, right]` curvature interval
produced no candidate that both remained in the original curvature range and
reduced peak `|dκ/ds|` by the required 20 percent. This is a geometric-family
limitation, not a service-dispatch or evaluator-threshold failure.
Three alternatives were considered:
1. Relax curvature/range/quality gates. Rejected: it would weaken the safety
and quality contract.
2. Mark `SingleTurn` as `Unchanged`. Rejected: it contradicts the already
approved main-route acceptance target and hides the incomplete candidate
family.
3. Keep all outer window boundary states exact while treating each internal
primitive boundary as a bounded, deterministic soft anchor. Chosen: it
expands geometry freedom only inside the configured deviation budget;
every candidate still passes the unchanged evaluator before publication.
This document explicitly overrides the internal-anchor position rule in
sections 9.3 and 9.4 of the main Local G2 design. Path start, path end, gear
switches, and outer window endpoints remain hard position anchors. An internal
primitive boundary becomes a soft **position** anchor only; its vehicle heading
and distance-weighted shared curvature remain hard boundary values for a given
candidate. No other main-design contract is relaxed.
## Feasibility gate before production
The fixed-anchor sweep does not prove that any particular soft-anchor offset is
feasible. Before changing `LocalG2CandidateBuilder`, a temporary test-only
probe must construct the exact soft-anchor profiles defined below for the real
`SingleTurn` fixture and evaluate them with the unchanged
`LocalG2CandidateEvaluator`.
The probe records, for every attempted tuple, the window, signed offset,
derivative multiplier, rejection reason, maximum vehicle curvature, raw and
candidate curvature range, peak `|dκ/ds|`, variation cost, maximum deviation,
and minimum clearance. It is removed after the evidence is recorded.
The feasibility gate passes only if at least one non-zero-offset tuple satisfies
all existing gates, including 20 percent peak-gradient improvement. If it does
not pass, production builder work stops and this design must be revised; fixture
expectations and evaluator thresholds are not changed to force GREEN.
## Candidate construction
For each `LocalG2SmoothingRegion`, the outer window endpoints remain exact
position/tangent/curvature boundary conditions. At each internal curvature
transition, candidate generation keeps the original vehicle heading and
distance-weighted shared curvature, but may offset the internal anchor position
along its original geometric normal:
```text
P_soft = P_original + offset × N_travel
N_travel = (-sin(travelHeading), cos(travelHeading))
```
`travelHeading` continues to use vehicle heading for forward travel and vehicle
heading minus π for reverse travel. The same offset profile is applied to all
internal anchors in a combined region, so adjacent quintic pieces still share
one position, heading, derivative scale, and curvature at each anchor and
therefore remain G2.
The finite profile order for every selected representative window is:
1. `offset = 0`, derivative multiplier `1.00` (exact-anchor baseline);
2. `offset = +softOffset`, derivative multiplier `1.00`;
3. `offset = -softOffset`, derivative multiplier `1.00`.
The offset is configuration-aware:
```text
softOffset = min(0.05 m, 0.5 × MaximumDeviationMeters)
```
If `softOffset <= 1e-10 m`, only the exact-anchor baseline is emitted. Offset
and derivative scale are not coupled in this recovery: the feasibility result
must isolate whether position freedom itself fixes the geometric limitation.
The existing `0.85` and `1.15` derivative profiles are not silently combined
with soft offsets; adding them later requires separate evidence and another
bounded design update.
The signed offsets are intentionally both present; vehicle turn sign and map
orientation must not choose a preferred side. A two-node, one-piece region has
no internal anchor and therefore emits only the exact-anchor baseline. No
configuration value is added in this recovery step.
`softOffset` is only a candidate-construction bound, not an acceptance
relaxation. The unchanged evaluator must still prove the complete candidate's
maximum deviation is at most the request's configured
`MaximumDeviationMeters`, as well as all curvature, collision, clearance,
finite-value, direction, and quality gates.
## Candidate-budget scheduling
`MaximumCandidatesPerRegion` limits emitted geometries, not window variants.
The window planner and builder must not exhaust the budget on asymmetric splits
or three profiles for the first few targets.
The window planner exposes total-length targets in deterministic coverage
passes. Distinct, legal target lengths are ordered as preferred, minimum,
maximum, 0.75 × preferred, and 1.25 × preferred. The first pass attempts the
balanced split for every target, the second pass attempts 40/60, and the third
pass attempts 60/40. Each pass stops at the existing window-variant cap. Thus
the default limit of 12 exposes preferred, minimum, and maximum total-length
windows before optional asymmetric variants can consume the budget. With a
configured limit below three, only the earliest target lengths are guaranteed;
the result remains deterministic and within the user's requested cap.
From the variants actually exposed by the planner, the builder selects at most
four distinct representative windows in this fixed order:
1. the planner's first window (preferred planner order);
2. the legal window with minimum total length;
3. the legal window with maximum total length;
4. the legal window with greatest left/right asymmetry.
Ties use `LocalG2WindowVariant.CandidateIndex`; duplicate window references are
removed. If fewer than four representatives exist, all distinct
representatives are used. If the candidate limit is below the required count,
enumeration is profile-major and window-stable:
1. emit the exact-anchor baseline once for each representative window;
2. emit `+softOffset` once for each representative window;
3. emit `-softOffset` once for each representative window;
4. stop immediately when the configured limit (capped by the existing hard
maximum of 12) is reached.
This guarantees baseline coverage before optional soft profiles and prevents a
single window from consuming the complete budget. Invalid or duplicate
geometries do not alter the deterministic attempt order; candidate indices are
assigned densely to successfully emitted geometries.
## Publication and failure behavior
The Task 8 pipeline continues to build candidates from the immutable original
segment, evaluate them against the current path, process regions in work order,
and publish reports in detector order. Soft-anchor candidates are ordinary
candidates: they are never specially accepted. If none passes, the region is
retained and the final result remains `Unchanged` or `PartialImprovement` as
defined by the existing result contract.
## Test-first acceptance
Before changing the builder, extend the real `SingleTurn` builder/evaluator
regression to assert all of the following against actual preprocessor, detector,
window planner, builder, and evaluator instances:
- the exact-anchor baseline remains present and deterministic;
- the feasibility probe finds at least one accepted non-zero-offset tuple before
production code changes;
- after implementation, a non-zero-offset builder candidate is emitted and at
least one such candidate is accepted by the unchanged evaluator;
- the accepted candidate stays within the configured `MaximumDeviationMeters`,
original curvature range, vehicle curvature limit, collision/clearance gates,
and 20 percent peak-gradient-improvement gate;
- the outer window endpoint positions, headings, and curvatures remain exact;
- every moved internal anchor retains its original heading and shared curvature,
and both adjacent pieces meet it with one shared position/tangent/curvature;
- with the default candidate cap, representative-window selection includes the
preferred, minimum, maximum, and most-asymmetric legal windows when they are
distinct;
- limits from `1` through `12` never emit more candidates than configured and
preserve the documented profile-major order;
- a configuration with `MaximumDeviationMeters < 0.10 m` scales `softOffset`
and never constructs an anchor outside half of that configured budget;
- the same request produces the same candidate sequence and selected result on
two runs;
- forward and reverse source/heading conventions remain unchanged;
- a combined multi-event region remains internally G2 and does not exceed the
candidate budget when the same signed profile is applied to all soft anchors.
The permanent regression must be observed failing before production changes,
then pass after the smallest planner-and-builder implementation. Task 8
service integration must then
restore `SingleTurn -> Complete` and continue to prove cancellation, rollback,
two-region work order, report order, and legacy-method isolation.
## Scope
Production changes are limited to `LocalG2WindowPlanner.cs` and
`LocalG2CandidateBuilder.cs`; the focused detection/candidate verifiers and
existing Task 8 integration/service files may change for TDD and end-to-end
coverage. The previously authorized evaluator boundary de-duplication remains
part of this Task 8 recovery. No safety threshold, validator tolerance, window
length, vehicle model, comparison-default method, or collision/clearance
behavior changes. The main Local G2 design is amended only to reclassify
internal primitive-boundary positions as bounded soft anchors and to guarantee
representative target coverage before asymmetric window variants; all other
hard-anchor and safety contracts remain unchanged.