> **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:** Build a validated coarse-path smoothing module that compares cubic B-spline, local cubic Bézier, and piecewise quintic paths, then exports reproducible IEEE-style Chinese reports for existing and fast fixture scenarios.
**Architecture:** Add `PathSmoothing` beside `CoarsePath`; keep contracts, shared geometry processing, algorithms, validation, comparison, and rendering isolated. Every algorithm consumes the same preprocessed direction segments, every candidate passes the same analyzer and full-footprint validator, and only accepted results reach the formal facade. Reporting consumes immutable comparison data and cannot affect planning status.
**Tech Stack:** C# 10, .NET Standard 2.0, existing `PlanningGridMap`/`FootprintCollisionChecker`, PowerShell reflection tests, Newtonsoft.Json 13.0.4, StbImageWriteSharp 1.16.7, System.Drawing.Common 10.0.10 on Windows.
## Global Constraints
- Core units are m, rad, and 1/m; no mm or degree conversion inside smoothing algorithms.
- Movement-bound rejection is retryable; invalid input, singular coefficients, and non-finite geometry are terminal failures.
- Every algorithm uses a read-only snapshot of its strong-typed options and maps evaluated points to the original direction segment by local arc length, never by raw point index.
- Chinese text uses `SimSun`; English, numbers, Greek, and mathematics use `Times New Roman`.
- Fixed method colors are raw `#4D4D4D`, B-spline `#0072B2`, Bézier `#D55E00`, quintic `#009E73`, and curvature limits `#CC79A7`.
-`System.Drawing.Common` is a Windows-only report-rendering dependency; smoothing, validation, comparison, SVG, and CSV remain independent of its runtime availability.
- Text SVG is the editable master, not a directly submittable IEEE artifact; submission conversion to font-embedded or outlined PDF/EPS is an explicit external publishing step.
- Offline timing uses one warm-up and five measured deterministic runs per scenario and method; ranking uses the measured median only.
- Produces: `PathSmoothingRequest`; `PathSmoothingResult.Success(...)`; `PathSmoothingResult.Fallback(...)`; `PathSmoothingResult.Failure(...)`; all public enums and value objects used below.
- [ ]**Step 1: Write the failing contract verification**
Create a reflection script that loads `ClumsyPilot.dll`, resolves every type listed above, and verifies these exact defaults and invariants:
Assert-Near0.05$configuration.OutputSpacingMeters'Default output spacing must be 0.05 m.'
Assert-Near0.025$configuration.MaximumCollisionCheckStepMeters'Default collision step must be 0.025 m.'
Assert-Equal4$configuration.RetryStrengthScales.Count'Retry schedule must contain four entries.'
Assert-Near1.0$configuration.RetryStrengthScales[0]'First retry scale must be 1.0.'
Assert-Near0.25$configuration.RetryStrengthScales[3]'Last retry scale must be 0.25.'
$failed=$resultType.GetMethod('Failure').Invoke(
$null,
@([Enum]::Parse($statusType,'InvalidInput'),
[Activator]::CreateInstance($diagnosticsType)))
Assert-Equal0$failed.Path.Count'Failure must publish no path.'
Assert-Equal0$failed.Segments.Count'Failure must publish no segments.'
```
Also construct a `SmoothedPathPoint` and assert all units/properties, construct two `SmoothedPathSegment` instances, and prove returned collections cannot be mutated.
- [ ]**Step 2: Run the verification and confirm RED**
Define parameterless immutable-empty defaults for `PathQualityMetrics` and `PathSmoothingDiagnostics`, plus overloads that accept all measured values. The three result factories are:
`PathSmoothingResult` must copy all input lists into `ReadOnlyCollection<T>`. Reject `Success`/`Fallback` factories with empty paths or segments; reject `Failure` with a success-like status.
- [ ]**Step 4: Run contract verification and existing integration verification**
- Produces: `PathSmoothingPreprocessor.TryPrepare(request, out PreparedPath, out string)` and `PathGeometryAnalyzer.TryAnalyze(candidateSegments, spacing, out PathGeometryAnalysis analysis, out string reason)`.
For each direction segment, recompute arc length from Euclidean position increments, unwrap heading from travel tangents, and use stable one-sided/central differences:
- Produces: `TryValidate(..., out IReadOnlyList<SmoothedPathPoint> pathWithClearance, out double minimumClearance, out string reason)`.
- [ ]**Step 1: Write failing safety cases**
Create an empty map, an occupied rectangle map, and paths that are: valid; point-colliding; swept-motion-colliding; outside bounds; over maximum curvature; changed at a gear switch; and overclaiming clearance.
```powershell
Assert-False$collisionAccepted'A smoothing candidate that cuts through an obstacle must be rejected.'
Assert-False$curvatureAccepted'A smoothing candidate above vehicle maximum curvature must be rejected.'
Assert-False$switchAccepted'A moved gear-switch pose must be rejected.'
Assert-True$validAccepted'A valid straight candidate must pass.'
- [ ]**Step 3: Implement validator using existing collision semantics**
For every point call `FootprintCollisionChecker.IsPoseCollisionFree`; for every non-duplicate adjacent pair call `IsSweptMotionCollisionFree`. Enforce:
Verify a straight segment remains collinear, a five-point corner becomes tangent-continuous, endpoints and travel tangents are exact, and every displacement stays within its supplied movement radius.
- [ ]**Step 3: Implement clamped basis evaluation and constrained control points**
Use degree 3, endpoint knot multiplicity 4, and Cox–de Boor basis evaluation. Fix the first/last control points to endpoints; place adjacent control points on endpoint travel tangents. Blend remaining control points toward local three-point averages:
**Context:** Tasks 1–5 are already committed. This corrective task resolves the review-discovered contract gaps before adding the remaining algorithms.
- Produces: `SmoothingCandidateStatus.Success`, `RetryableInfeasible`, or `Failed`.
- Produces: immutable method-specific option snapshots carried by `SmoothingAlgorithmInput`.
- Produces: `PathReferenceInterpolator.TryInterpolateByArcLength(IReadOnlyList<SmoothingPoint2D> points, double targetArcLength, out SmoothingPoint2D reference, out string reason)` for all three algorithms.
RetryableInfeasible attempts exactly 1.00, 0.75, 0.50, 0.25 and ends Infeasible
Failed attempts exactly once and ends Failed
request/config mutation after construction cannot change the internal option snapshot
custom EndpointTangentScale changes the B-spline endpoint handle
non-uniform source samples interpolate by local ArcLength, not point-index ratio
non-finite/non-positive option scalars and a threshold outside (0, π] are rejected before retry
```
Use a non-uniform source with local arc lengths `0.00, 0.05, 0.10, 0.125`; at target arc `0.1125`, the reference must lie halfway through the final interval regardless of point count.
Expected: missing candidate status/snapshot/interpolator assertions fail, and B-spline still ignores the configured endpoint scale.
- [ ]**Step 3: Implement the retryable candidate state**
Use the exact internal states:
```csharp
internalenumSmoothingCandidateStatus
{
Success,
RetryableInfeasible,
Failed,
}
```
`SmoothingCandidate.Success(...)` requires complete segments. `RetryableInfeasible(reason)` and `Failed(reason)` carry no executable geometry. The runner continues only for `RetryableInfeasible`; it stops immediately for `Failed`. Exhausting retryable outcomes returns `AlgorithmRunResult.Infeasible(...)` even when no rejected comparison geometry exists.
- [ ]**Step 4: Implement immutable options and arc-length interpolation**
`SmoothingOptionsSnapshot` copies these six scalars from the request configuration into get-only values:
```text
CubicBSplineEndpointTangentScale
BezierCornerHeadingThresholdRadians
BezierMaximumWindowLengthMeters
BezierHandleLengthRatio
QuinticKnotSpacingMeters
QuinticMinimumKnotSpacingMeters
```
Snapshot construction rejects non-finite values; all scales, ratios, windows, and knot lengths must be positive, the Bézier threshold must lie in `(0, π]`, and quintic knot spacing must be at least its configured minimum. These are terminal input failures, not retryable geometry outcomes.
`SmoothingAlgorithmInput.Options` is get-only and never exposes the mutable public configuration objects.
`PathReferenceInterpolator.TryInterpolateByArcLength` locates the bracketing source samples by `SmoothingPoint2D.ArcLength` and linearly interpolates position, heading, unwrapped heading, and clearance. A normalized full-segment parameter maps to `targetArc = u * segment.Points[last].ArcLength`; local algorithms map their window or knot interval directly to its endpoint arc lengths.
- [ ]**Step 5: Correct B-spline option and rejection semantics**
Replace the hard-coded endpoint scale with `input.Options.CubicBSplineEndpointTangentScale`. Replace point-index reference interpolation with `PathReferenceInterpolator`. Evaluated-point movement excess returns `RetryableInfeasible`; invalid values, impossible endpoint-tangent construction, and non-finite controls remain `Failed`. Never clamp evaluated curve samples.
- [ ]**Step 6: Run corrective and shared regression checks**
Assert no window is created for a straight line; one corner creates one replacement; overlapping windows merge; outside-window samples remain bitwise equal; endpoints and switch points remain fixed.
For each evaluated point, map `t` to `s_ref = s_entry + t * (s_exit - s_entry)` and obtain the source reference through `PathReferenceInterpolator`. Compare displacement with `max(0, reference.BodyClearance - MinimumClearanceReserveMeters)`. If any point exceeds that radius, return `RetryableInfeasible` with no executable geometry; do not pointwise clamp or project curve samples. Retain original samples outside merged windows.
Tests must set non-default threshold, window length, and handle ratio values and prove each option changes only its intended behavior.
- Produces: C2-connected local polynomial segments without crossing direction boundaries.
- [ ]**Step 1: Write failing continuity and degeneracy tests**
At every shared knot evaluate left/right position, first derivative, and second derivative; assert equality within `1e-6`. Verify exact endpoint pose, bounded movement, and stable failure for a segment shorter than the configured minimum knot spacing.
For each knot interval use normalized `t ∈ [0,1]` and solve the six coefficients independently for X and Y from:
```text
p(0)=p0, p(1)=p1
p'(0)=v0, p'(1)=v1
p''(0)=a0, p''(1)=a1
```
Derive endpoint velocities from travel tangents times interval length. Blend shared accelerations once per knot and reuse the same value on both adjacent intervals. Reject singular or non-finite coefficients before sampling.
Cover valid straight success, invalid input, cancellation, infeasible-without-fallback, and infeasible-with-verified-fallback. Invalid input must include NaN, non-positive scales/windows/spacing, Bézier threshold outside `(0, π]`, and `KnotSpacingMeters < MinimumKnotSpacingMeters`. Assert fallback points use `CoarsePathFallback` and status never equals `Success`.
Validate the full configuration and coarse path before constructing `SmoothingOptionsSnapshot` or starting finite retries; map every configuration-contract violation to `InvalidInput`. On allowed fallback, convert the revalidated coarse points to `SmoothedPathPoint` with `CoarsePathFallback`, re-run shared geometry analysis, and return `Fallback`, preserving the failed method diagnostics.
- [ ]**Step 1: Write failing comparison and ranking tests**
Create synthetic entries whose order changes at each tie-break level. Verify one method failure does not stop the remaining methods, cancellation does, and no feasible method produces a null recommendation.
Also assert the result always contains one separately analyzed raw-path baseline plus exactly one entry for each requested method; the raw baseline is never treated as a candidate method in ranking.
For timing, assert the warm-up is excluded, exactly five samples remain, and a mismatch in status, point count, segment count, or stable geometry digest produces a non-deterministic diagnostic that excludes the method from recommendation.
Use absolute deltas when a raw denominator has magnitude below `1e-12`. The comparison service must force `AllowFallbackToCoarsePath = false` so fallback cannot masquerade as method success.
Run each method once for warm-up and five times for measurement against the same immutable prepared input. Use the first measured result as the canonical comparison geometry only after all five measured outputs match its stable status and geometry digest. `StableGeometryDigest` writes status, segment metadata, enum values, and every double through `BitConverter.DoubleToInt64Bits` in fixed little-endian order, then computes SHA-256; do not use `GetHashCode()`. Store all five elapsed values plus their median; only the median participates in the final lexicographic timing tie-break.
Verify unique IDs, version > 0, matching fingerprint, valid segment coverage, and that fixture-only loading has no reference to `HybridAStarPlanner`.
- [ ]**Step 2: Write failing end-to-end tests**
For `ExplicitEmpty`, `RectangleDetour`, `ManualAndTwoLeg`, and `ReverseGearSwitch`, call the existing planner then comparison service. Assert the raw path is unchanged and every successful smoothing entry preserves segment count and gear-switch count.
`SmoothingFixtureGenerator.Generate(string outputPath, bool overwrite)` must refuse an existing target when `overwrite` is false. The script maps `-Overwrite` to that argument; normal tests call only `LoadAndVerify` and never regenerate data. Each obstacle is serialized as a typed circle or axis-aligned rectangle DTO so the loader can rebuild the exact `PlanningGridMap` without Hybrid A*.
- [ ]**Step 5: Run fixture and integration checks**
- Portability boundary: text SVG is the editable master and requires exact fonts on the viewing machine; it is not claimed as a directly submittable IEEE vector file.
- [ ]**Step 1: Write failing style and serialization tests**
Assert exact colors, line styles, panel labels, view box, equal XY scale, legend order, XML escaping, UTF-8 Chinese, BOM bytes `EF BB BF`, and invariant decimal points.
Include one rejected candidate in the model and assert its normal method-colored curve plus violation cross markers are present while its metrics row says `Infeasible`. Include one numerical failure and assert its legend/row remains but no fake path element is emitted.
- [ ]**Step 3: Implement one physical layout model**
Define all coordinates in typographic points (`72 pt/in`):
```csharp
publicconstdoubleFigureWidthPoints=7.16d*72d;
publicconstdoubleFigureHeightPoints=5.20d*72d;
publicconststringRawColor="#4D4D4D";
publicconststringBSplineColor="#0072B2";
publicconststringBezierColor="#D55E00";
publicconststringQuinticColor="#009E73";
publicconststringLimitColor="#CC79A7";
```
Allocate 60% width to panel `(a)`, split the right side between `(b)` curvature and `(c)` metrics, and compute one world-to-panel transform with equal X/Y scale.
Use `9 pt` for coordinate ticks, axis labels, legend, and table body; use `10 pt` for panel labels. Do not create any text smaller than `9 pt` at final physical size.
- Submission boundary: conversion of the verified SVG master to font-embedded or outlined PDF/EPS is explicit external publishing work, not a hidden exporter side effect.
Expected: restore exits 0. Do not add cross-platform fallback switches; Microsoft documents `System.Drawing.Common` as Windows-only.
- [ ]**Step 4: Implement exact font resolution and mixed-run baseline layout**
Use `InstalledFontCollection` to require `SimSun` and `Times New Roman`. Split text by Unicode category/CJK ranges, measure each run with typographic `StringFormat`, and align runs by font-family ascent:
Render at `4296 × 3120`, opaque white background, anti-aliased geometry, and no gradients/shadows. Convert bitmap pixels to RGBA and use the existing validated PNG path, extending it to insert a `pHYs` chunk with `23622` pixels/meter and correct CRC before `IDAT`.
- Produces: documented formal call example and a developer-only batch command that writes reports under `ClumsyPilot/obj/path_smoothing_reports`.
- [ ]**Step 1: Write failing documentation and batch-entry verification**
Assert README contains exact sections for units, facade usage, status handling, fallback, fixture freshness, IEEE colors/fonts, Windows PNG limitation, SQP boundary, and output files. Assert the batch runner offers `-FixtureOnly` and never writes under source directories.
The batch script builds once, runs eight fixtures by default, optionally runs the four end-to-end cases, prints one-line metrics per method, and writes only beneath `ClumsyPilot/obj/path_smoothing_reports`.
- [ ]**Step 4: Run the complete verification suite**
Inspect `straight`, `rectangle-detour`, and `forward-reverse-switch` PNG/SVG files. Confirm four-method legend order, equal path axes, readable 9–10 pt text, no Chinese mojibake, no clipping/overlap, curvature limits, and infeasible markers where applicable. Confirm the text SVG portability limitation and external PDF/EPS publishing step are documented.