docs: plan Local G2 soft anchor recovery
This commit is contained in:
@@ -0,0 +1,971 @@
|
||||
# Local G2 Soft-Anchor Candidate Recovery Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Prove and implement a bounded soft-anchor Local G2 candidate family that makes the real `SingleTurn` route pass the unchanged safety/quality evaluator, then finish the dedicated Task 8 service pipeline.
|
||||
|
||||
**Architecture:** First preserve the already-proven evaluator seam correction, then make window generation cover representative total lengths before asymmetric variants consume the budget. A disposable-copy feasibility gate evaluates the exact soft-anchor family before the shared production builder changes; only a successful non-zero-offset candidate permits the builder TDD and Task 8 service integration to continue.
|
||||
|
||||
**Tech Stack:** C# 10, .NET Standard 2.0, PowerShell reflection verification, existing `PathGeometryAnalyzer`, `SmoothedPathValidator`, full-body collision checks, Git.
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- `MinimumWindowLengthMeters = 0.20`, `PreferredWindowLengthMeters = 0.50`, and `MaximumWindowLengthMeters = 0.80` are total left-plus-right lengths.
|
||||
- Path start, path end, gear switches, and outer window endpoints remain hard position anchors.
|
||||
- Only an internal primitive-boundary position may move; its vehicle heading and distance-weighted shared curvature remain hard boundary values.
|
||||
- `softOffset = min(0.05 m, 0.5 × MaximumDeviationMeters)`; if it is at most `1e-10 m`, emit no non-zero profile.
|
||||
- Per selected representative window, profile order is exact anchor, `+softOffset`, `-softOffset`, all with derivative multiplier `1.00`.
|
||||
- The configured candidate limit remains authoritative and is capped by the existing hard maximum of 12.
|
||||
- Do not modify vehicle curvature limits, the validator `1e-6` tolerance, raw curvature-range tolerance, collision/clearance gates, `0.10 m` default maximum deviation, 20 percent peak-gradient improvement, or 2 percent variation-cost tolerance.
|
||||
- Do not change Hybrid A*, SQP, legacy smoothing algorithms, or `PathSmoothingComparisonRequest.DefaultMethods`.
|
||||
- Detector report order remains immutable ascending order; processing uses `LocalG2RegionWorkOrder`.
|
||||
- Use TDD. Do not change `LocalG2CandidateBuilder` in the shared tree until the disposable feasibility gate finds an accepted non-zero-offset candidate under the unchanged evaluator.
|
||||
- Preserve unrelated dirty worktree files. Stage only the exact files listed by each task.
|
||||
- The untracked `ClumsyPilot/ParkrobTrajplanner/auto_avoidance` tree currently requires unavailable external assemblies. Never edit or delete it; when it blocks the normal build, verify this work from a disposable copy that excludes only that directory.
|
||||
|
||||
---
|
||||
|
||||
## File Structure
|
||||
|
||||
- `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/LocalG2/LocalG2CandidateEvaluator.cs`
|
||||
- Preserve the analyzer-compatible `1e-10 m` boundary-point de-duplication already proven by RED/GREEN evidence.
|
||||
- `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/LocalG2/LocalG2WindowPlanner.cs`
|
||||
- Expose preferred/minimum/maximum balanced targets before asymmetric variants and keep deterministic target coverage.
|
||||
- `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/LocalG2/LocalG2CandidateBuilder.cs`
|
||||
- Select representative windows, apply the bounded normal soft-anchor profiles, and emit at most 12 deterministic geometries.
|
||||
- `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/LocalG2/LocalG2PreSmoothingPipeline.cs`
|
||||
- Consume work order, evaluate candidates, roll back failed global combinations, and publish raw baseline when no accepted replacement survives.
|
||||
- `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Facade/PathSmoothingService.cs`
|
||||
- Dispatch only `LocalG2Quintic` to the dedicated pipeline while preserving legacy routes.
|
||||
- `ClumsyPilot/tests/verify_path_smoothing_local_g2_detection.ps1`
|
||||
- Prove target coverage, total-window semantics, deterministic order, and configured caps.
|
||||
- `ClumsyPilot/tests/verify_path_smoothing_local_g2_candidates.ps1`
|
||||
- Prove the evaluator seam, feasibility tuple, soft-anchor geometry, candidate budget, G2, direction, and determinism.
|
||||
- `ClumsyPilot/tests/verify_path_smoothing_local_g2_integration.ps1`
|
||||
- Prove public service statuses, safe complete-path publication, two-region order, rollback, and cancellation.
|
||||
- `ClumsyPilot/tests/verify_path_smoothing_service.ps1`
|
||||
- Prove dedicated Local G2 dispatch without disturbing legacy method registration.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Preserve the evaluator window-boundary correction
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/LocalG2/LocalG2CandidateEvaluator.cs`
|
||||
- Modify: `ClumsyPilot/tests/verify_path_smoothing_local_g2_candidates.ps1`
|
||||
- Evidence: `.superpowers/sdd/local-g2-task-8-integration-report.md`
|
||||
|
||||
**Interfaces:**
|
||||
|
||||
- Consumes: interpolated window start/end points and source points from one `PreparedDirectionSegment`.
|
||||
- Produces: `TryExtractWindow(...)` output with exact interpolated endpoints and no consecutive positions within `1e-10 m`.
|
||||
|
||||
- [ ] **Step 1: Separate the completed evaluator regression from the still-RED candidate-family assertion**
|
||||
|
||||
Keep the real SingleTurn seam and this assertion:
|
||||
|
||||
```powershell
|
||||
Assert-Equal 0 $duplicateFailures.Count `
|
||||
'SingleTurn builder candidates must not fail evaluator window analysis due to duplicate or degenerate points.'
|
||||
```
|
||||
|
||||
Remove only the current `$acceptedCandidates` collection and the assertion requiring an accepted candidate. Task 3 reintroduces the accepted non-zero-offset requirement after the feasibility gate.
|
||||
|
||||
- [ ] **Step 2: Confirm the recorded RED/GREEN evidence is complete**
|
||||
|
||||
Read `.superpowers/sdd/local-g2-task-8-integration-report.md` and require both entries:
|
||||
|
||||
```text
|
||||
RED: Expected=0 Actual=6 duplicate/degenerate evaluator failures
|
||||
GREEN: Path smoothing Local G2 candidate checks passed.
|
||||
```
|
||||
|
||||
The production correction must remain exactly:
|
||||
|
||||
```csharp
|
||||
private const double WindowPointToleranceMeters = 1e-10d;
|
||||
|
||||
private static bool SamePosition(SmoothingPoint2D left, SmoothingPoint2D right)
|
||||
{
|
||||
if (left == null || right == null) return false;
|
||||
double x = right.X - left.X;
|
||||
double y = right.Y - left.Y;
|
||||
return x * x + y * y <=
|
||||
WindowPointToleranceMeters * WindowPointToleranceMeters;
|
||||
}
|
||||
```
|
||||
|
||||
and `TryExtractWindow` must preserve the exact interpolated end:
|
||||
|
||||
```csharp
|
||||
if (point.ArcLength > startArcLength &&
|
||||
point.ArcLength < endArcLength &&
|
||||
!SamePosition(points[points.Count - 1], point))
|
||||
{
|
||||
points.Add(point);
|
||||
}
|
||||
|
||||
if (SamePosition(points[points.Count - 1], end))
|
||||
points[points.Count - 1] = end;
|
||||
else
|
||||
points.Add(end);
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Build and run the focused GREEN check**
|
||||
|
||||
Run the normal commands first:
|
||||
|
||||
```powershell
|
||||
dotnet build ClumsyPilot/ClumsyPilot.csproj --no-restore
|
||||
powershell -ExecutionPolicy Bypass -File ClumsyPilot/tests/verify_path_smoothing_local_g2_candidates.ps1
|
||||
```
|
||||
|
||||
If the build fails only because of untracked `auto_avoidance` dependencies, create a disposable verification copy:
|
||||
|
||||
```powershell
|
||||
$verificationRoot = Join-Path $env:TEMP ('parkingrobot-local-g2-' + [Guid]::NewGuid().ToString('N'))
|
||||
New-Item -ItemType Directory -Path $verificationRoot | Out-Null
|
||||
robocopy 'ClumsyPilot' (Join-Path $verificationRoot 'ClumsyPilot') /E /XD 'ClumsyPilot\ParkrobTrajplanner\auto_avoidance'
|
||||
if ($LASTEXITCODE -gt 7) { throw "robocopy failed with $LASTEXITCODE" }
|
||||
dotnet build (Join-Path $verificationRoot 'ClumsyPilot\ClumsyPilot.csproj') --no-restore
|
||||
powershell -ExecutionPolicy Bypass -File (Join-Path $verificationRoot 'ClumsyPilot\tests\verify_path_smoothing_local_g2_candidates.ps1')
|
||||
```
|
||||
|
||||
Expected: build has zero errors; candidate checks pass without an accepted-candidate requirement.
|
||||
|
||||
- [ ] **Step 4: Commit only the evaluator seam**
|
||||
|
||||
```powershell
|
||||
git add -- `
|
||||
ClumsyPilot/ParkrobTrajplanner/PathSmoothing/LocalG2/LocalG2CandidateEvaluator.cs `
|
||||
ClumsyPilot/tests/verify_path_smoothing_local_g2_candidates.ps1
|
||||
git diff --cached --check
|
||||
git diff --cached --name-only
|
||||
git commit -m "fix: deduplicate Local G2 evaluator windows"
|
||||
```
|
||||
|
||||
Expected staged names: exactly the two files above.
|
||||
|
||||
---
|
||||
|
||||
### Task 2: Guarantee representative window-target coverage
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/LocalG2/LocalG2WindowPlanner.cs`
|
||||
- Modify: `ClumsyPilot/tests/verify_path_smoothing_local_g2_detection.ps1`
|
||||
|
||||
**Interfaces:**
|
||||
|
||||
- Consumes: `LocalG2OptionsSnapshot` total-window bounds and `MaximumCandidatesPerRegion`.
|
||||
- Produces: ordered `WindowVariants` whose balanced target pass precedes 40/60 and 60/40 passes.
|
||||
|
||||
- [ ] **Step 1: Add the failing coverage scenario**
|
||||
|
||||
Add this case to `LocalG2WindowPlanner.TestHooks.Execute`:
|
||||
|
||||
```csharp
|
||||
case "InteriorCoverage":
|
||||
transitions = new[] { Transition(1d, 0) };
|
||||
segmentLength = 2d;
|
||||
break;
|
||||
```
|
||||
|
||||
Extend `WindowPlanningTestSnapshot` with:
|
||||
|
||||
```csharp
|
||||
public int FirstRegionVariantCount { get; }
|
||||
public bool RepresentativeTargetsFirst { get; }
|
||||
public bool HasAsymmetricVariant { get; }
|
||||
```
|
||||
|
||||
Append the three values to its internal constructor and assign them exactly:
|
||||
|
||||
```csharp
|
||||
internal WindowPlanningTestSnapshot(
|
||||
int regionCount,
|
||||
string transitionCounts,
|
||||
double maximumWindowLength,
|
||||
bool exactEnvelope,
|
||||
double firstLeftLength,
|
||||
double firstRightLength,
|
||||
string signature,
|
||||
int firstRegionVariantCount,
|
||||
bool representativeTargetsFirst,
|
||||
bool hasAsymmetricVariant)
|
||||
{
|
||||
RegionCount = regionCount;
|
||||
TransitionCounts = transitionCounts;
|
||||
MaximumWindowLength = maximumWindowLength;
|
||||
ExactEnvelope = exactEnvelope;
|
||||
FirstLeftLength = firstLeftLength;
|
||||
FirstRightLength = firstRightLength;
|
||||
Signature = signature;
|
||||
FirstRegionVariantCount = firstRegionVariantCount;
|
||||
RepresentativeTargetsFirst = representativeTargetsFirst;
|
||||
HasAsymmetricVariant = hasAsymmetricVariant;
|
||||
}
|
||||
```
|
||||
|
||||
Compute the values before returning the snapshot:
|
||||
|
||||
```csharp
|
||||
IReadOnlyList<LocalG2WindowVariant> firstVariants = regions[0].WindowVariants;
|
||||
bool representativeTargetsFirst =
|
||||
firstVariants.Count >= 3 &&
|
||||
Math.Abs(WindowLength(firstVariants[0]) - 0.50d) <= 1e-9d &&
|
||||
Math.Abs(WindowLength(firstVariants[1]) - 0.20d) <= 1e-9d &&
|
||||
Math.Abs(WindowLength(firstVariants[2]) - 0.80d) <= 1e-9d;
|
||||
bool hasAsymmetricVariant = false;
|
||||
for (int index = 0; index < firstVariants.Count; index++)
|
||||
{
|
||||
if (Math.Abs(
|
||||
firstVariants[index].LeftWindowLengthMeters -
|
||||
firstVariants[index].RightWindowLengthMeters) > 1e-9d)
|
||||
{
|
||||
hasAsymmetricVariant = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Add the helper:
|
||||
|
||||
```csharp
|
||||
private static double WindowLength(LocalG2WindowVariant variant) =>
|
||||
variant.EndArcLengthMeters - variant.StartArcLengthMeters;
|
||||
```
|
||||
|
||||
Pass the three values through the snapshot constructor. In the PowerShell verifier add:
|
||||
|
||||
```powershell
|
||||
$coverage = $executeMethod.Invoke($null, @('InteriorCoverage'))
|
||||
Assert-True $coverage.RepresentativeTargetsFirst `
|
||||
'Preferred, minimum, and maximum balanced targets must precede asymmetric variants.'
|
||||
Assert-True $coverage.HasAsymmetricVariant `
|
||||
'Default window planning must retain a legal asymmetric variant after balanced coverage.'
|
||||
Assert-True ($coverage.FirstRegionVariantCount -le 12) `
|
||||
'Window planning must obey the configured default cap.'
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run the detection verifier to prove RED**
|
||||
|
||||
Run:
|
||||
|
||||
```powershell
|
||||
dotnet build ClumsyPilot/ClumsyPilot.csproj --no-restore
|
||||
powershell -ExecutionPolicy Bypass -File ClumsyPilot/tests/verify_path_smoothing_local_g2_detection.ps1
|
||||
```
|
||||
|
||||
Use the disposable-copy command from Task 1 only if the normal build is blocked by `auto_avoidance`.
|
||||
|
||||
Expected RED: `RepresentativeTargetsFirst` is false because the current order begins `0.50, 0.50, 0.50` for balanced/asymmetric splits of one target.
|
||||
|
||||
- [ ] **Step 3: Implement coverage-pass enumeration**
|
||||
|
||||
Replace `BuildVariants` with:
|
||||
|
||||
```csharp
|
||||
private static IReadOnlyList<LocalG2WindowVariant> BuildVariants(
|
||||
IReadOnlyList<CurvatureTransition> transitions,
|
||||
double segmentLength,
|
||||
LocalG2OptionsSnapshot options)
|
||||
{
|
||||
var variants = new List<LocalG2WindowVariant>();
|
||||
double firstEvent = transitions[0].LocalArcLengthMeters;
|
||||
double lastEvent = transitions[transitions.Count - 1].LocalArcLengthMeters;
|
||||
double anchor = (firstEvent + lastEvent) / 2d;
|
||||
IReadOnlyList<double> targets = BuildTargets(options, segmentLength);
|
||||
double[] ratios = { 0.5d, 0.4d, 0.6d };
|
||||
for (int ratioIndex = 0; ratioIndex < ratios.Length; ratioIndex++)
|
||||
{
|
||||
for (int targetIndex = 0; targetIndex < targets.Count; targetIndex++)
|
||||
{
|
||||
if (variants.Count >= options.MaximumCandidatesPerRegion)
|
||||
return new ReadOnlyCollection<LocalG2WindowVariant>(variants);
|
||||
AddIfLegal(
|
||||
variants,
|
||||
targets[targetIndex],
|
||||
ratios[ratioIndex],
|
||||
anchor,
|
||||
firstEvent,
|
||||
lastEvent,
|
||||
segmentLength,
|
||||
ratioIndex == 0,
|
||||
options);
|
||||
}
|
||||
}
|
||||
return new ReadOnlyCollection<LocalG2WindowVariant>(variants);
|
||||
}
|
||||
```
|
||||
|
||||
Replace the `requested` array in `BuildTargets` with:
|
||||
|
||||
```csharp
|
||||
double[] requested =
|
||||
{
|
||||
options.PreferredWindowLengthMeters,
|
||||
options.MinimumWindowLengthMeters,
|
||||
options.MaximumWindowLengthMeters,
|
||||
0.75d * options.PreferredWindowLengthMeters,
|
||||
1.25d * options.PreferredWindowLengthMeters,
|
||||
};
|
||||
```
|
||||
|
||||
Do not alter `AddIfLegal`, total-length validation, grouping, or region-envelope calculation.
|
||||
|
||||
- [ ] **Step 4: Verify GREEN and determinism**
|
||||
|
||||
Run twice:
|
||||
|
||||
```powershell
|
||||
powershell -ExecutionPolicy Bypass -File ClumsyPilot/tests/verify_path_smoothing_local_g2_detection.ps1
|
||||
powershell -ExecutionPolicy Bypass -File ClumsyPilot/tests/verify_path_smoothing_local_g2_detection.ps1
|
||||
powershell -ExecutionPolicy Bypass -File ClumsyPilot/tests/verify_path_smoothing_local_g2_candidates.ps1
|
||||
```
|
||||
|
||||
Expected: all pass; both detection runs publish identical signatures.
|
||||
|
||||
- [ ] **Step 5: Commit the planner coverage change**
|
||||
|
||||
```powershell
|
||||
git add -- `
|
||||
ClumsyPilot/ParkrobTrajplanner/PathSmoothing/LocalG2/LocalG2WindowPlanner.cs `
|
||||
ClumsyPilot/tests/verify_path_smoothing_local_g2_detection.ps1
|
||||
git diff --cached --check
|
||||
git diff --cached --name-only
|
||||
git commit -m "fix: cover Local G2 window targets before splits"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 3: Prove and implement the soft-anchor candidate family
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify after feasibility GREEN: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/LocalG2/LocalG2CandidateBuilder.cs`
|
||||
- Modify: `ClumsyPilot/tests/verify_path_smoothing_local_g2_candidates.ps1`
|
||||
- Evidence: `.superpowers/sdd/local-g2-soft-anchor-feasibility-report.md`
|
||||
|
||||
**Interfaces:**
|
||||
|
||||
- Consumes: stabilized `WindowVariants`, internal `CurvatureTransition` anchors, travel direction, and `MaximumDeviationMeters`.
|
||||
- Produces: profile-major deterministic candidates with exact outer states and bounded normal movement at internal anchors.
|
||||
|
||||
- [ ] **Step 1: Reintroduce the permanent RED acceptance seam**
|
||||
|
||||
Before the real-candidate loop initialize:
|
||||
|
||||
```powershell
|
||||
$acceptedSoftCandidates = @()
|
||||
$candidateSignatures = @()
|
||||
```
|
||||
|
||||
For each real candidate, find its point at the first transition arc within
|
||||
`1e-9 m`, then compute:
|
||||
|
||||
```powershell
|
||||
$transition = (Get-InternalProperty $region 'Transitions')[0]
|
||||
$travelHeading = [double](Get-InternalProperty $transition 'VehicleHeadingRadians')
|
||||
$normalX = -[Math]::Sin($travelHeading)
|
||||
$normalY = [Math]::Cos($travelHeading)
|
||||
$softOffset = 0.0
|
||||
foreach ($point in (Get-InternalProperty $realCandidate 'RegionPoints')) {
|
||||
if ([Math]::Abs($point.ArcLength - (Get-InternalProperty $transition 'LocalArcLengthMeters')) -le 1e-9) {
|
||||
$softOffset = ($point.X - (Get-InternalProperty $transition 'X')) * $normalX +
|
||||
($point.Y - (Get-InternalProperty $transition 'Y')) * $normalY
|
||||
break
|
||||
}
|
||||
}
|
||||
if ((Get-InternalProperty $evaluation 'Accepted') -and [Math]::Abs($softOffset) -gt 1e-10) {
|
||||
$acceptedSoftCandidates += $realCandidate
|
||||
}
|
||||
```
|
||||
|
||||
Add:
|
||||
|
||||
```powershell
|
||||
Assert-True ($acceptedSoftCandidates.Count -gt 0) `
|
||||
'SingleTurn must produce an accepted non-zero soft-anchor candidate under unchanged gates.'
|
||||
```
|
||||
|
||||
Run the candidate verifier against the current builder. Expected RED: no non-zero soft-anchor candidate exists.
|
||||
|
||||
- [ ] **Step 2: Run the disposable feasibility gate before shared production edits**
|
||||
|
||||
Create a new disposable verification copy using Task 1's command. In that copy only, apply the complete builder changes from Steps 3 and 4 below with `apply_patch`. Add temporary output inside the candidate evaluation loop:
|
||||
|
||||
```powershell
|
||||
$candidateIndex = Get-InternalProperty $realCandidate 'CandidateIndex'
|
||||
$startArc = Get-InternalProperty $realCandidate 'StartArcLengthMeters'
|
||||
$endArc = Get-InternalProperty $realCandidate 'EndArcLengthMeters'
|
||||
$accepted = Get-InternalProperty $evaluation 'Accepted'
|
||||
$failure = Get-InternalProperty $evaluation 'FailureReason'
|
||||
$rawPeak = Get-InternalProperty $evaluation 'RawPeakCurvatureDerivativePerSquareMeter'
|
||||
$resultPeak = Get-InternalProperty $evaluation 'ResultPeakCurvatureDerivativePerSquareMeter'
|
||||
$maximumCurvature = Get-InternalProperty $evaluation 'MaximumAbsoluteVehicleCurvaturePerMeter'
|
||||
$maximumDeviation = Get-InternalProperty $evaluation 'MaximumDeviationMeters'
|
||||
$minimumClearance = Get-InternalProperty $evaluation 'MinimumBodyClearanceMeters'
|
||||
Write-Output ("soft-probe candidate=$candidateIndex window=$startArc..$endArc offset=$softOffset accepted=$accepted failure=$failure rawPeak=$rawPeak resultPeak=$resultPeak maxCurvature=$maximumCurvature deviation=$maximumDeviation clearance=$minimumClearance")
|
||||
```
|
||||
|
||||
In the disposable verifier, also calculate the raw and candidate curvature
|
||||
ranges with the evaluator's unchanged private analysis path:
|
||||
|
||||
```powershell
|
||||
function Get-AnalysisCurvatureRange($Analysis) {
|
||||
$minimum = [double]::PositiveInfinity
|
||||
$maximum = [double]::NegativeInfinity
|
||||
foreach ($point in (Get-InternalProperty $Analysis 'Path')) {
|
||||
$minimum = [Math]::Min($minimum, [double]$point.VehicleCurvature)
|
||||
$maximum = [Math]::Max($maximum, [double]$point.VehicleCurvature)
|
||||
}
|
||||
return [PSCustomObject]@{ Minimum = $minimum; Maximum = $maximum }
|
||||
}
|
||||
|
||||
$extractMethod = Get-InternalMethod $evaluatorType 'TryExtractWindow'
|
||||
$analyzeMethod = Get-InternalMethod $evaluatorType 'TryAnalyzeWindow'
|
||||
$extractArgs = [object[]]@($preparedSegment, [double]$startArc, [double]$endArc, $null, $null)
|
||||
Assert-True ($extractMethod.Invoke($realEvaluator, $extractArgs)) `
|
||||
'The feasibility probe must extract the unchanged raw window.'
|
||||
$rawWindow = $extractArgs[3]
|
||||
$rawAnalyzeArgs = [object[]]@(
|
||||
$preparedSegment, $rawWindow, [double]$configuration.OutputSpacingMeters, $null, $null)
|
||||
Assert-True ($analyzeMethod.Invoke($realEvaluator, $rawAnalyzeArgs)) `
|
||||
'The feasibility probe must analyze the unchanged raw window.'
|
||||
$candidateAnalyzeArgs = [object[]]@(
|
||||
$preparedSegment,
|
||||
(Get-InternalProperty $realCandidate 'RegionPoints'),
|
||||
[double]$configuration.OutputSpacingMeters,
|
||||
$null,
|
||||
$null)
|
||||
Assert-True ($analyzeMethod.Invoke($realEvaluator, $candidateAnalyzeArgs)) `
|
||||
'The feasibility probe must analyze the soft-anchor candidate window.'
|
||||
$rawRange = Get-AnalysisCurvatureRange $rawAnalyzeArgs[3]
|
||||
$candidateRange = Get-AnalysisCurvatureRange $candidateAnalyzeArgs[3]
|
||||
Write-Output ("soft-probe ranges candidate=$candidateIndex raw=$($rawRange.Minimum)..$($rawRange.Maximum) candidate=$($candidateRange.Minimum)..$($candidateRange.Maximum)")
|
||||
```
|
||||
|
||||
Run:
|
||||
|
||||
```powershell
|
||||
dotnet build (Join-Path $verificationRoot 'ClumsyPilot\ClumsyPilot.csproj') --no-restore
|
||||
powershell -ExecutionPolicy Bypass -File (Join-Path $verificationRoot 'ClumsyPilot\tests\verify_path_smoothing_local_g2_candidates.ps1')
|
||||
```
|
||||
|
||||
Write the exact command, every tuple line, and the final accepted tuple to `.superpowers/sdd/local-g2-soft-anchor-feasibility-report.md` using `apply_patch`.
|
||||
|
||||
Gate result:
|
||||
|
||||
- GREEN: at least one line has `accepted=True`, `abs(offset) > 1e-10`, result peak at most 80 percent of raw peak, and all unchanged evaluator gates pass. Continue.
|
||||
- RED: no such line exists. Stop this plan, leave the shared builder unchanged, and report the design as blocked. Do not change expected fixture statuses or any threshold.
|
||||
|
||||
- [ ] **Step 3: Implement representative-window selection after feasibility GREEN**
|
||||
|
||||
Replace the current window-first/scale-second loop in `Build` with:
|
||||
|
||||
```csharp
|
||||
IReadOnlyList<LocalG2WindowVariant> windows =
|
||||
SelectRepresentativeWindows(region.WindowVariants);
|
||||
double softOffset = Math.Min(0.05d, 0.5d * options.MaximumDeviationMeters);
|
||||
double[] offsets = softOffset <= 1e-10d
|
||||
? new[] { 0d }
|
||||
: new[] { 0d, softOffset, -softOffset };
|
||||
var candidates = new List<LocalG2CandidateGeometry>();
|
||||
int limit = Math.Min(options.MaximumCandidatesPerRegion, 12);
|
||||
for (int profileIndex = 0; profileIndex < offsets.Length; profileIndex++)
|
||||
{
|
||||
for (int windowIndex = 0; windowIndex < windows.Count; windowIndex++)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
if (candidates.Count >= limit) return ReadOnly(candidates);
|
||||
if (TryBuildCandidate(
|
||||
candidates.Count,
|
||||
originalSegment,
|
||||
region,
|
||||
windows[windowIndex],
|
||||
1d,
|
||||
offsets[profileIndex],
|
||||
outputSpacingMeters,
|
||||
cancellationToken,
|
||||
out LocalG2CandidateGeometry candidate))
|
||||
{
|
||||
candidates.Add(candidate);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ReadOnly(candidates);
|
||||
```
|
||||
|
||||
Add these helpers next to `SameArc`:
|
||||
|
||||
```csharp
|
||||
private static IReadOnlyList<LocalG2WindowVariant> SelectRepresentativeWindows(
|
||||
IReadOnlyList<LocalG2WindowVariant> variants)
|
||||
{
|
||||
var selected = new List<LocalG2WindowVariant>(4);
|
||||
if (variants == null || variants.Count == 0)
|
||||
return new ReadOnlyCollection<LocalG2WindowVariant>(selected);
|
||||
|
||||
AddDistinctWindow(selected, variants[0]);
|
||||
LocalG2WindowVariant minimum = variants[0];
|
||||
LocalG2WindowVariant maximum = variants[0];
|
||||
LocalG2WindowVariant asymmetric = variants[0];
|
||||
for (int index = 1; index < variants.Count; index++)
|
||||
{
|
||||
LocalG2WindowVariant candidate = variants[index];
|
||||
if (IsShorter(candidate, minimum)) minimum = candidate;
|
||||
if (IsLonger(candidate, maximum)) maximum = candidate;
|
||||
if (IsMoreAsymmetric(candidate, asymmetric)) asymmetric = candidate;
|
||||
}
|
||||
AddDistinctWindow(selected, minimum);
|
||||
AddDistinctWindow(selected, maximum);
|
||||
AddDistinctWindow(selected, asymmetric);
|
||||
return new ReadOnlyCollection<LocalG2WindowVariant>(selected);
|
||||
}
|
||||
|
||||
private static bool IsShorter(
|
||||
LocalG2WindowVariant candidate,
|
||||
LocalG2WindowVariant current)
|
||||
{
|
||||
double candidateLength =
|
||||
candidate.EndArcLengthMeters - candidate.StartArcLengthMeters;
|
||||
double currentLength =
|
||||
current.EndArcLengthMeters - current.StartArcLengthMeters;
|
||||
return candidateLength < currentLength - 1e-10d ||
|
||||
(Math.Abs(candidateLength - currentLength) <= 1e-10d &&
|
||||
candidate.CandidateIndex < current.CandidateIndex);
|
||||
}
|
||||
|
||||
private static bool IsLonger(
|
||||
LocalG2WindowVariant candidate,
|
||||
LocalG2WindowVariant current)
|
||||
{
|
||||
double candidateLength =
|
||||
candidate.EndArcLengthMeters - candidate.StartArcLengthMeters;
|
||||
double currentLength =
|
||||
current.EndArcLengthMeters - current.StartArcLengthMeters;
|
||||
return candidateLength > currentLength + 1e-10d ||
|
||||
(Math.Abs(candidateLength - currentLength) <= 1e-10d &&
|
||||
candidate.CandidateIndex < current.CandidateIndex);
|
||||
}
|
||||
|
||||
private static bool IsMoreAsymmetric(
|
||||
LocalG2WindowVariant candidate,
|
||||
LocalG2WindowVariant current)
|
||||
{
|
||||
double candidateValue = Math.Abs(
|
||||
candidate.LeftWindowLengthMeters - candidate.RightWindowLengthMeters);
|
||||
double currentValue = Math.Abs(
|
||||
current.LeftWindowLengthMeters - current.RightWindowLengthMeters);
|
||||
return candidateValue > currentValue + 1e-10d ||
|
||||
(Math.Abs(candidateValue - currentValue) <= 1e-10d &&
|
||||
candidate.CandidateIndex < current.CandidateIndex);
|
||||
}
|
||||
|
||||
private static void AddDistinctWindow(
|
||||
List<LocalG2WindowVariant> selected,
|
||||
LocalG2WindowVariant candidate)
|
||||
{
|
||||
for (int index = 0; index < selected.Count; index++)
|
||||
{
|
||||
if (SameArc(selected[index].StartArcLengthMeters, candidate.StartArcLengthMeters) &&
|
||||
SameArc(selected[index].EndArcLengthMeters, candidate.EndArcLengthMeters))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
selected.Add(candidate);
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Implement bounded internal-anchor movement**
|
||||
|
||||
Add `double normalOffsetMeters` to `TryBuildCandidate` immediately after `double multiplier`.
|
||||
|
||||
Replace internal node construction with:
|
||||
|
||||
```csharp
|
||||
double travelHeading = segment.Direction == TravelDirection.Forward
|
||||
? transition.VehicleHeadingRadians
|
||||
: transition.VehicleHeadingRadians - Math.PI;
|
||||
double normalX = -Math.Sin(travelHeading);
|
||||
double normalY = Math.Cos(travelHeading);
|
||||
double nodeX = transition.X + normalOffsetMeters * normalX;
|
||||
double nodeY = transition.Y + normalOffsetMeters * normalY;
|
||||
if (!NumericGuard.IsFinite(nodeX) || !NumericGuard.IsFinite(nodeY))
|
||||
return false;
|
||||
nodes.Add(new BoundaryNode(
|
||||
transition.LocalArcLengthMeters,
|
||||
nodeX,
|
||||
nodeY,
|
||||
transition.VehicleHeadingRadians,
|
||||
sharedCurvature));
|
||||
```
|
||||
|
||||
Declare this immediately before the transition loop:
|
||||
|
||||
```csharp
|
||||
int internalNodeCount = 0;
|
||||
```
|
||||
|
||||
Increment it immediately after adding each moved internal node. Before
|
||||
`TryAssignDerivativeScales` add:
|
||||
|
||||
```csharp
|
||||
if (internalNodeCount == 0 && Math.Abs(normalOffsetMeters) > 1e-10d)
|
||||
return false;
|
||||
```
|
||||
|
||||
Keep the original interpolated outer nodes, distance-weighted `sharedCurvature`, derivative-scale calculation, derivative certification, sampling, direction sign, and candidate endpoint fields unchanged.
|
||||
Delete the now-unused `DerivativeScaleMultipliers` field.
|
||||
|
||||
- [ ] **Step 5: Complete permanent soft-anchor and budget assertions**
|
||||
|
||||
Add assertions that:
|
||||
|
||||
```powershell
|
||||
Assert-True ($acceptedSoftCandidates.Count -gt 0) `
|
||||
'SingleTurn must publish an accepted non-zero soft-anchor candidate.'
|
||||
Assert-True ($realCandidates.Count -le $configuration.LocalG2Quintic.MaximumCandidatesPerRegion) `
|
||||
'Builder output must obey the configured candidate cap.'
|
||||
```
|
||||
|
||||
Run the real builder twice and compare, for every candidate, candidate index, start/end arc, point count, every X/Y/Heading/Source value, and the inferred signed internal offset. Loop candidate limits from `1` through `12`; rebuild options and assert output count never exceeds the configured limit. Set `MaximumDeviationMeters = 0.02` in one request and assert every inferred anchor offset is at most `0.010000001 m`.
|
||||
|
||||
Extend the existing cluster and reverse TestHook cases so that one non-zero profile proves:
|
||||
|
||||
```text
|
||||
outer endpoint position error <= 1e-9 m
|
||||
outer endpoint heading error <= 1e-9 rad
|
||||
outer endpoint curvature error <= 1e-8 1/m
|
||||
internal left/right tangent error <= 1e-8
|
||||
internal left/right curvature error <= 1e-8 1/m
|
||||
```
|
||||
|
||||
- [ ] **Step 6: Verify candidate GREEN and integration progress**
|
||||
|
||||
Run:
|
||||
|
||||
```powershell
|
||||
dotnet build ClumsyPilot/ClumsyPilot.csproj --no-restore
|
||||
powershell -ExecutionPolicy Bypass -File ClumsyPilot/tests/verify_path_smoothing_local_g2_detection.ps1
|
||||
powershell -ExecutionPolicy Bypass -File ClumsyPilot/tests/verify_path_smoothing_local_g2_candidates.ps1
|
||||
powershell -ExecutionPolicy Bypass -File ClumsyPilot/tests/verify_path_smoothing_local_g2_integration.ps1
|
||||
```
|
||||
|
||||
Use the disposable copy when the normal build is blocked only by `auto_avoidance`.
|
||||
|
||||
Expected: detection and candidate scripts pass; service integration advances past `single-turn must publish its required Local G2 status`. Any later Task 8 failure is handled in Task 4 without modifying candidate gates.
|
||||
|
||||
- [ ] **Step 7: Commit the soft-anchor candidate family**
|
||||
|
||||
```powershell
|
||||
git add -- `
|
||||
ClumsyPilot/ParkrobTrajplanner/PathSmoothing/LocalG2/LocalG2CandidateBuilder.cs `
|
||||
ClumsyPilot/tests/verify_path_smoothing_local_g2_candidates.ps1
|
||||
git diff --cached --check
|
||||
git diff --cached --name-only
|
||||
git commit -m "fix: generate bounded Local G2 soft anchors"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 4: Finish the dedicated Task 8 pipeline and service publication
|
||||
|
||||
**Files:**
|
||||
|
||||
- Create from the existing untracked prototype: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/LocalG2/LocalG2PreSmoothingPipeline.cs`
|
||||
- Modify: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Facade/PathSmoothingService.cs`
|
||||
- Create from the existing untracked prototype: `ClumsyPilot/tests/verify_path_smoothing_local_g2_integration.ps1`
|
||||
- Modify: `ClumsyPilot/tests/verify_path_smoothing_service.ps1`
|
||||
|
||||
**Interfaces:**
|
||||
|
||||
- Consumes: verified `RawPathBaseline`, immutable prepared path, detector `reportOrder`, `LocalG2RegionWorkOrder`, candidate builder/evaluator.
|
||||
- Produces:
|
||||
|
||||
```csharp
|
||||
internal PathSmoothingResult Smooth(
|
||||
PathSmoothingRequest request,
|
||||
PreparedPath preparedPath,
|
||||
RawPathBaseline rawBaseline,
|
||||
CancellationToken cancellationToken);
|
||||
```
|
||||
|
||||
- Guarantees: every valid non-cancelled request publishes a complete verified path; invalid raw input and cancellation publish no partial path.
|
||||
|
||||
- [ ] **Step 1: Run the public integration and service scripts as RED**
|
||||
|
||||
Run:
|
||||
|
||||
```powershell
|
||||
dotnet build ClumsyPilot/ClumsyPilot.csproj --no-restore
|
||||
powershell -ExecutionPolicy Bypass -File ClumsyPilot/tests/verify_path_smoothing_local_g2_integration.ps1
|
||||
powershell -ExecutionPolicy Bypass -File ClumsyPilot/tests/verify_path_smoothing_service.ps1
|
||||
```
|
||||
|
||||
Expected before the final pipeline correction: at least one mandatory fixture/status, raw-baseline publication, rollback, or report assertion fails. Record the first expected failure in `.superpowers/sdd/local-g2-task-8-integration-report.md`.
|
||||
|
||||
- [ ] **Step 2: Preserve immutable report order and work-order processing**
|
||||
|
||||
Keep this exact flow in `Smooth`:
|
||||
|
||||
```csharp
|
||||
IReadOnlyList<LocalG2SmoothingRegion> reportOrder =
|
||||
new ReadOnlyCollection<LocalG2SmoothingRegion>(
|
||||
new List<LocalG2SmoothingRegion>(regions));
|
||||
if (!_workOrder.TryCreate(
|
||||
reportOrder,
|
||||
out IReadOnlyList<LocalG2SmoothingRegion> workRegions,
|
||||
out string orderReason))
|
||||
{
|
||||
return Failure(PathSmoothingStatus.Failed, stopwatch, orderReason);
|
||||
}
|
||||
|
||||
foreach (LocalG2SmoothingRegion region in workRegions)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
// Build from preparedPath; evaluate against current.
|
||||
}
|
||||
|
||||
var reports = new List<PathSmoothingRegionReport>(reportOrder.Count);
|
||||
for (int reportIndex = 0; reportIndex < reportOrder.Count; reportIndex++)
|
||||
reports.Add(reportsByRegion[reportOrder[reportIndex]]);
|
||||
```
|
||||
|
||||
Candidate construction must use `preparedPath.Segments[region.SegmentIndex]`; evaluation must receive both `preparedPath` and the evolving `current` path.
|
||||
|
||||
- [ ] **Step 3: Publish the verified raw baseline without reconstructing it**
|
||||
|
||||
Before regional processing, if `transitions.Count == 0`, publish:
|
||||
|
||||
```csharp
|
||||
return Publish(
|
||||
PathSmoothingStatus.NotNeeded,
|
||||
rawBaseline,
|
||||
stopwatch,
|
||||
new List<PathSmoothingRegionReport>());
|
||||
```
|
||||
|
||||
Add:
|
||||
|
||||
```csharp
|
||||
private static PathSmoothingResult Publish(
|
||||
PathSmoothingStatus status,
|
||||
RawPathBaseline baseline,
|
||||
Stopwatch stopwatch,
|
||||
IReadOnlyList<PathSmoothingRegionReport> reports) =>
|
||||
PathSmoothingResult.PublishLocalG2(
|
||||
status,
|
||||
baseline.Path,
|
||||
baseline.Segments,
|
||||
new PathSmoothingDiagnostics(
|
||||
baseline.Metrics,
|
||||
stopwatch.Elapsed,
|
||||
0,
|
||||
0d,
|
||||
string.Empty),
|
||||
reports);
|
||||
```
|
||||
|
||||
After regional evaluation and global rollback, if `improvedCount == 0`, publish the same `rawBaseline` as `Unchanged` with detector-order reports. Do not call `PathGeometryAnalyzer` again for this raw fallback; the pre-Task-8 trusted-curvature gate already produced and fully validated it.
|
||||
|
||||
- [ ] **Step 4: Make accepted and retained reports describe the actual candidate**
|
||||
|
||||
For every region retain:
|
||||
|
||||
```csharp
|
||||
IReadOnlyList<LocalG2CandidateGeometry> candidates
|
||||
LocalG2CandidateEvaluation best
|
||||
LocalG2CandidateGeometry selectedCandidate
|
||||
```
|
||||
|
||||
Find the selected geometry deterministically:
|
||||
|
||||
```csharp
|
||||
LocalG2CandidateGeometry selectedCandidate = null;
|
||||
for (int index = 0; index < candidates.Count; index++)
|
||||
{
|
||||
if (candidates[index].CandidateIndex == best.CandidateIndex)
|
||||
{
|
||||
selectedCandidate = candidates[index];
|
||||
break;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When `best.Accepted`, require `selectedCandidate != null`, store it in `AcceptedRegion`, and report its start/end/left/right lengths. When no candidate is accepted, use candidate index `-1`, the first window only as planned-window diagnostics, and the stable failure reason from `best`.
|
||||
|
||||
Extend `AcceptedRegion` with:
|
||||
|
||||
```csharp
|
||||
internal int CandidateCount { get; }
|
||||
internal LocalG2CandidateGeometry Candidate { get; }
|
||||
```
|
||||
|
||||
Rollback reports use the stored candidate count and selected candidate rather than `region.WindowVariants.Count`.
|
||||
|
||||
- [ ] **Step 5: Preserve global rollback and exact status derivation**
|
||||
|
||||
After candidate processing, validate `current`. If it fails, roll accepted regions back in reverse acceptance order. After each rollback, re-run full validation. Mark every removed region `GlobalValidationRollback`.
|
||||
|
||||
Derive status only after rollback:
|
||||
|
||||
```csharp
|
||||
if (improvedCount == 0)
|
||||
status = PathSmoothingStatus.Unchanged;
|
||||
else if (improvedCount == regions.Count)
|
||||
status = PathSmoothingStatus.Complete;
|
||||
else
|
||||
status = PathSmoothingStatus.PartialImprovement;
|
||||
```
|
||||
|
||||
`Complete` and `PartialImprovement` must have at least one `Improved` report. If every accepted region is rolled back, publish the verified `rawBaseline` as `Unchanged`.
|
||||
|
||||
- [ ] **Step 6: Keep service dispatch isolated**
|
||||
|
||||
In `PathSmoothingService` retain the dedicated branch after common validation, preparation, and raw-baseline creation:
|
||||
|
||||
```csharp
|
||||
if (configuration.Method == SmoothingMethod.LocalG2Quintic)
|
||||
return _localG2Pipeline.Smooth(
|
||||
request,
|
||||
preparedPath,
|
||||
rawBaseline,
|
||||
cancellationToken);
|
||||
```
|
||||
|
||||
Validate `LocalG2QuinticOptions` only for that method; legacy methods continue through `Resolve(...)` and `_runner`. Do not add `LocalG2Quintic` to comparison defaults.
|
||||
|
||||
- [ ] **Step 7: Complete integration assertions**
|
||||
|
||||
The integration script must run every fixture twice and require:
|
||||
|
||||
```text
|
||||
Straight -> NotNeeded
|
||||
SingleTurn -> Complete
|
||||
LargeHeadingChange -> Complete
|
||||
SBend -> Complete or PartialImprovement
|
||||
RectangleDetour -> Complete or PartialImprovement
|
||||
MultiObstacleDetour -> Complete or PartialImprovement
|
||||
ReverseGearSwitch -> Complete, PartialImprovement, or NotNeeded
|
||||
```
|
||||
|
||||
For every published path require complete segment coverage, feasible diagnostics, configured clearance, deterministic status/coordinates/reports, and at least one `Improved` report for `Complete`/`PartialImprovement`. Keep the strict `0.99` improvement request as `Unchanged`, cancellation as empty-path `Cancelled`, and the same-direction two-region case as back-to-front processing with detector-order reports.
|
||||
|
||||
- [ ] **Step 8: Run focused and legacy GREEN checks**
|
||||
|
||||
Run:
|
||||
|
||||
```powershell
|
||||
dotnet build ClumsyPilot/ClumsyPilot.csproj --no-restore
|
||||
powershell -ExecutionPolicy Bypass -File ClumsyPilot/tests/verify_path_smoothing_local_g2_integration.ps1
|
||||
powershell -ExecutionPolicy Bypass -File ClumsyPilot/tests/verify_path_smoothing_service.ps1
|
||||
powershell -ExecutionPolicy Bypass -File ClumsyPilot/tests/verify_path_smoothing_runner.ps1
|
||||
powershell -ExecutionPolicy Bypass -File ClumsyPilot/tests/verify_path_smoothing_integration.ps1
|
||||
powershell -ExecutionPolicy Bypass -File ClumsyPilot/tests/verify_path_smoothing_local_g2_candidates.ps1
|
||||
powershell -ExecutionPolicy Bypass -File ClumsyPilot/tests/verify_path_smoothing_local_g2_detection.ps1
|
||||
```
|
||||
|
||||
Expected: all pass. Use the disposable copy only for the unrelated `auto_avoidance` build blocker.
|
||||
|
||||
- [ ] **Step 9: Commit only Task 8 publication files**
|
||||
|
||||
```powershell
|
||||
git add -- `
|
||||
ClumsyPilot/ParkrobTrajplanner/PathSmoothing/LocalG2/LocalG2PreSmoothingPipeline.cs `
|
||||
ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Facade/PathSmoothingService.cs `
|
||||
ClumsyPilot/tests/verify_path_smoothing_local_g2_integration.ps1 `
|
||||
ClumsyPilot/tests/verify_path_smoothing_service.ps1
|
||||
git diff --cached --check
|
||||
git diff --cached --name-only
|
||||
git commit -m "feat: publish Local G2 presmoothing results"
|
||||
```
|
||||
|
||||
Expected staged names: exactly the four files above. If the service file contains unrelated user hunks, stage the Task 8 patch into the index without staging those hunks and verify with `git diff --cached` before committing.
|
||||
|
||||
---
|
||||
|
||||
### Task 5: Run the complete recovery and Task 8 verification gate
|
||||
|
||||
**Files:**
|
||||
|
||||
- Verify: all files committed by Tasks 1–4
|
||||
- Verify: all `ClumsyPilot/tests/verify_path_smoothing_*.ps1`
|
||||
- Record: `.superpowers/sdd/local-g2-soft-anchor-task-8-final-report.md`
|
||||
|
||||
**Interfaces:**
|
||||
|
||||
- Consumes: the evaluator seam, window scheduling, proven soft-anchor family, and dedicated pipeline.
|
||||
- Produces: evidence that Task 8 is complete without modifying Task 9 documentation.
|
||||
|
||||
- [ ] **Step 1: Build the final source**
|
||||
|
||||
```powershell
|
||||
dotnet build ClumsyPilot/ClumsyPilot.csproj --no-restore
|
||||
```
|
||||
|
||||
Expected: zero errors. The two pre-existing obsolete API warnings may remain. If unrelated `auto_avoidance` dependencies still block the shared build, also record that exact failure and run the same build in the disposable copy.
|
||||
|
||||
- [ ] **Step 2: Run every PathSmoothing verification script**
|
||||
|
||||
```powershell
|
||||
$tests = @(
|
||||
'verify_path_smoothing_algorithm_input.ps1',
|
||||
'verify_path_smoothing_bezier.ps1',
|
||||
'verify_path_smoothing_bspline.ps1',
|
||||
'verify_path_smoothing_comparison.ps1',
|
||||
'verify_path_smoothing_contracts.ps1',
|
||||
'verify_path_smoothing_documentation.ps1',
|
||||
'verify_path_smoothing_fixtures.ps1',
|
||||
'verify_path_smoothing_geometry.ps1',
|
||||
'verify_path_smoothing_integration.ps1',
|
||||
'verify_path_smoothing_local_g2_candidates.ps1',
|
||||
'verify_path_smoothing_local_g2_curve.ps1',
|
||||
'verify_path_smoothing_local_g2_detection.ps1',
|
||||
'verify_path_smoothing_local_g2_integration.ps1',
|
||||
'verify_path_smoothing_png.ps1',
|
||||
'verify_path_smoothing_quintic.ps1',
|
||||
'verify_path_smoothing_runner.ps1',
|
||||
'verify_path_smoothing_service.ps1',
|
||||
'verify_path_smoothing_svg_csv.ps1',
|
||||
'verify_path_smoothing_validation.ps1'
|
||||
)
|
||||
foreach ($test in $tests) {
|
||||
& powershell -ExecutionPolicy Bypass -File (Join-Path 'ClumsyPilot/tests' $test)
|
||||
if ($LASTEXITCODE -ne 0) { throw "$test failed with exit code $LASTEXITCODE" }
|
||||
}
|
||||
```
|
||||
|
||||
Expected: all 19 scripts pass.
|
||||
|
||||
- [ ] **Step 3: Audit scope, safety thresholds, and Task 9 boundary**
|
||||
|
||||
```powershell
|
||||
git diff --check
|
||||
git diff --cached --check
|
||||
git status --short
|
||||
git log -6 --oneline
|
||||
rg -n "MaximumCurvaturePerMeter|CurvatureRangeTolerance|MinimumClearanceReserveMeters|MinimumPeakGradientImprovementRatio|MaximumVariationCostRegressionRatio" `
|
||||
ClumsyPilot/ParkrobTrajplanner/PathSmoothing/LocalG2 `
|
||||
ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Facade/PathSmoothingService.cs
|
||||
```
|
||||
|
||||
Confirm no safety value changed, no staged files remain, unrelated dirty files are untouched, comparison defaults exclude Local G2, and Task 9 README/documentation work has not started.
|
||||
|
||||
- [ ] **Step 4: Record final evidence without an empty commit**
|
||||
|
||||
Write `.superpowers/sdd/local-g2-soft-anchor-task-8-final-report.md` with:
|
||||
|
||||
```text
|
||||
Soft-anchor feasibility gate: PASS with exact accepted tuple and unchanged evaluator metrics.
|
||||
Window target coverage: PASS for preferred/minimum/maximum before asymmetric variants.
|
||||
SingleTurn: Complete with at least one accepted non-zero soft anchor.
|
||||
Task 8 service publication: PASS including raw fallback, rollback, work/report order, and cancellation.
|
||||
Full PathSmoothing verification: 19/19 scripts PASS (or shared-build limitation plus isolated-copy evidence explicitly recorded).
|
||||
Task 9 documentation remains pending.
|
||||
```
|
||||
|
||||
Do not create a verification-only commit.
|
||||
Reference in New Issue
Block a user