36 KiB
Local G2 Diagnostic Visualization 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: Export a factual Local G2 visualization that shows the ordinary strict Unchanged result and the recorded clearance-rejected diagnostic candidate without publishing or recommending that candidate.
Architecture: The existing comparison service remains the source for normal Local G2 status and output. A test/demo-only evidence loader reads a compact immutable record of single-turn/s0/r0/w5/seed2, reconstructs a visual-only spliced path with the existing preprocessor/splicer/analyzer, and appends it to an immutable figure model. The existing SVG/PNG/CSV exporter then produces the six standard figures plus a seventh diagnostic figure only for that augmented model.
Tech Stack: C# 10 targeting netstandard2.0, Newtonsoft.Json 13.0.4 already referenced by ClumsyPilot.csproj, existing System.Drawing PNG renderer, PowerShell verification scripts, .NET 10 Windows verification host.
Global Constraints
- Do not modify
ClumsyPilot/ParkrobTrajplanner/PathSmoothing/LocalG2/, evaluator, validator, publication, or recommendation logic. - The normal comparison must retain the actual Local G2
PathSmoothingStatus; a diagnostic candidate must never create aPathSmoothingResultor a recommendation. - Evidence is fixed to fixture SHA-256
3d05daee5a211b3e7aa0b77193423b5fa07d3135e241a4413be3518fc7efe563, batch SHA-256ac8166828813d85bf6f8b58f985839e5b2a049e75cb94186ed04f59d540e4eed, and stable keysingle-turn/s0/r0/w5/seed2. - Preserve normal-export file stems
01-coarse-path-overviewthrough06-curvature-comparisonand its six-file contract. - The diagnostic candidate must be labelled
净空拒绝,未发布; do not render a collision cross because the evidence records a clearance rejection, not an occupied-cell collision. - Keep trajectories point-only. Do not add SVG paths or dashed stroke rendering.
- Place generated artifacts only below
ClumsyPilot/obj/path_smoothing_reports. - Use targeted
git add -- <paths>andgit commit --only -- <paths>; do not include unrelated worktree changes.
File Structure
| Path | Responsibility |
|---|---|
ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Comparison/PathSmoothingComparisonRequest.cs |
Adds normal Local G2 to the immutable default offline comparison order. |
ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Visualization/IeeeFigureStyle.cs |
Holds normal Local G2 and diagnostic-candidate colors. |
ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Visualization/SmoothingFigureModelBuilder.cs |
Adds normal Local G2 series and CSV metric row. |
ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Visualization/SmoothingFigureModel.cs |
Creates an immutable model copy with one added diagnostic series. |
ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Visualization/SmoothingFigureDefinition.cs |
Moves a five-entry legend upward enough to remain inside the fixed figure height. |
ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Visualization/SmoothingFigureKind.cs |
Names the optional seventh figure. |
ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Visualization/SmoothingFigureSetBuilder.cs |
Adds Local G2 to normal comparison figures and conditionally creates figure 07. |
ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Test/Fixtures/local-g2-diagnostic-single-turn.json |
Immutable compact evidence extract used by the visual-only route. |
ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Test/LocalG2DiagnosticEvidenceLoader.cs |
Parses and verifies evidence identity, geometry, and rejection state. |
ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Test/LocalG2DiagnosticVisualizationDemo.cs |
Reconstructs renderable candidate geometry and invokes the existing exporter. |
ClumsyPilot/tests/PathSmoothingPngVerificationHost/Program.cs |
Verifies and exports the augmented seven-file report. |
ClumsyPilot/tests/verify_path_smoothing_comparison.ps1 |
Checks Local G2 default order and normal comparison semantics. |
ClumsyPilot/tests/verify_path_smoothing_svg_csv.ps1 |
Checks normal Local G2 figure/CSV content and visual layout contracts. |
ClumsyPilot/tests/verify_path_smoothing_local_g2_diagnostic_evidence.ps1 |
Checks evidence parsing and deterministic rejection validation. |
ClumsyPilot/tests/verify_path_smoothing_local_g2_diagnostic_visualization.ps1 |
Calls the verification host for the seven-file diagnostic report. |
ClumsyPilot/tests/run_local_g2_diagnostic_visualization.ps1 |
Builds and exports the user-facing Local G2 diagnostic image. |
Task 1: Add Normal Local G2 To Existing Comparison Reports
Files:
- Modify:
ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Comparison/PathSmoothingComparisonRequest.cs - Modify:
ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Visualization/IeeeFigureStyle.cs - Modify:
ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Visualization/SmoothingFigureModelBuilder.cs - Modify:
ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Visualization/SmoothingFigureDefinition.cs - Modify:
ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Visualization/SmoothingFigureSetBuilder.cs - Modify:
ClumsyPilot/tests/verify_path_smoothing_comparison.ps1 - Modify:
ClumsyPilot/tests/verify_path_smoothing_svg_csv.ps1 - Modify:
ClumsyPilot/tests/PathSmoothingPngVerificationHost/Program.cs
Interfaces:
-
Consumes:
PathSmoothingComparisonRequest(PathSmoothingRequest, IReadOnlyList<SmoothingMethod> methods = null)andSmoothingFigureModelBuilder.Build(PathSmoothingComparisonResult, PlanningGridMap, Pose2D, Pose2D, string, string). -
Produces: A default method order of
CubicBSpline,LocalCubicBezier,PiecewiseQuintic,LocalG2Quintic; figure keylocal-g2; CSV methodLocalG2Quintic; unchanged standard six-figure export count. -
Step 1: Write the failing default-order and standard-model assertions
In
verify_path_smoothing_comparison.ps1, parse the Local G2 enum and construct a request without the optional methods list. Add these assertions after the explicit three-method request assertions:$localG2 = [Enum]::Parse($methodType, 'LocalG2Quintic') $defaultComparisonRequest = [Activator]::CreateInstance($comparisonRequestType, @((New-SmoothingRequest), $null)) Assert-Equal 4 $defaultComparisonRequest.Methods.Count 'Default comparison must include Local G2.' Assert-Equal 'CubicBSpline,LocalCubicBezier,PiecewiseQuintic,LocalG2Quintic' (($defaultComparisonRequest.Methods | ForEach-Object ToString) -join ',') 'Default comparison order must be stable.'In
verify_path_smoothing_svg_csv.ps1, add#56B4E9to the expected normal SVG colors, assert that$model.Seriesincludes a series whoseKeyislocal-g2, and assert that the generated CSV containsLocalG2Quintic,Unchangedfor the frozensingle-turnrequest. Add assertions that the standard figure set still contains only the six existing stems.In
Program.cs, add aVerifyNormalLocalG2Series(SmoothingFigureModel model)call immediately afterVerifySixFigureDefinitionContract(model). It must require exactly onelocal-g2series and aPathSmoothingStatusvalue defined by the enum. If its strict output path is visible, require that it has at least two samples. Do not hard-codeUnchangedfor this artificial high-curvature host fixture; the frozensingle-turnevidence assertion in the SVG/CSV test owns that requirement. -
Step 2: Run the focused checks and confirm they fail before implementation
Run:
dotnet build .\ClumsyPilot\ClumsyPilot.csproj --no-restore powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_comparison.ps1 powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_svg_csv.ps1Expected: the comparison assertion reports three default methods and the SVG/CSV check cannot find
local-g2or#56B4E9. -
Step 3: Implement the smallest normal-comparison extension
Append the enum in the existing default array, preserving all existing order:
private static readonly SmoothingMethod[] DefaultMethods = { SmoothingMethod.CubicBSpline, SmoothingMethod.LocalCubicBezier, SmoothingMethod.PiecewiseQuintic, SmoothingMethod.LocalG2Quintic, };Add these fixed colors to
IeeeFigureStyle:public const string LocalG2Color = "#56B4E9"; public const string LocalG2DiagnosticColor = "#B1373E";In
SmoothingFigureModelBuilder, append the normal series and metric row after the existing piecewise-quintic entries. Use the exact stable key and label:series.Add(CreateSeries( Find(comparison, SmoothingMethod.LocalG2Quintic), SmoothingMethod.LocalG2Quintic, "local-g2", "局部 G2", IeeeFigureStyle.LocalG2Color, string.Empty, false, map)); CreateRow(Find(comparison, SmoothingMethod.LocalG2Quintic), "LocalG2Quintic", "局部 G2")In
SmoothingFigureSetBuilder.Build, resolvelocal-g2with the other standard series and include it in only the existing all-path and curvature comparisons:SmoothingFigureSeries localG2 = Find(model, "local-g2"); // Add View(localG2, 1d) after View(quintic, 1d) in figures 02 and 06.Keep individual figures
03through05unchanged. InSmoothingFigureDefinition, use a five-entry-safe legend origin:public double LegendYPoints => LegendEntries.Count > 4 ? 332d : 340d;Do not special-case
Unchanged:PathSmoothingComparisonServicealready supplies its strict path and status. Do not modify that service or the ranker. -
Step 4: Run the focused checks and confirm they pass
Run:
dotnet build .\ClumsyPilot\ClumsyPilot.csproj --no-restore powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_comparison.ps1 powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_svg_csv.ps1 powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_png.ps1Expected: all three scripts exit
0; normal exports retain exactly six figures; the Local G2 row is present with its actualUnchangedstatus. -
Step 5: Commit only Task 1 files
git add -- ` ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Comparison/PathSmoothingComparisonRequest.cs ` ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Visualization/IeeeFigureStyle.cs ` ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Visualization/SmoothingFigureModelBuilder.cs ` ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Visualization/SmoothingFigureDefinition.cs ` ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Visualization/SmoothingFigureSetBuilder.cs ` ClumsyPilot/tests/verify_path_smoothing_comparison.ps1 ` ClumsyPilot/tests/verify_path_smoothing_svg_csv.ps1 ` ClumsyPilot/tests/PathSmoothingPngVerificationHost/Program.cs git -c core.autocrlf=false diff --cached --check git commit --only -m "feat: show Local G2 in smoothing comparisons" -- ` ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Comparison/PathSmoothingComparisonRequest.cs ` ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Visualization/IeeeFigureStyle.cs ` ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Visualization/SmoothingFigureModelBuilder.cs ` ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Visualization/SmoothingFigureDefinition.cs ` ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Visualization/SmoothingFigureSetBuilder.cs ` ClumsyPilot/tests/verify_path_smoothing_comparison.ps1 ` ClumsyPilot/tests/verify_path_smoothing_svg_csv.ps1 ` ClumsyPilot/tests/PathSmoothingPngVerificationHost/Program.cs
Task 2: Freeze And Validate The Diagnostic Evidence Extract
Files:
- Create:
ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Test/Fixtures/local-g2-diagnostic-single-turn.json - Create:
ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Test/LocalG2DiagnosticEvidenceLoader.cs - Create:
ClumsyPilot/tests/verify_path_smoothing_local_g2_diagnostic_evidence.ps1
Interfaces:
-
Produces:
public sealed class LocalG2DiagnosticEvidenceLoaderwithpublic LocalG2DiagnosticEvidence LoadAndVerify(string evidencePath). -
Produces:
LocalG2DiagnosticEvidencepropertiesScenarioId,FixtureSha256,CandidateStableKey,CandidateSha256,CandidateIndex,SegmentIndex,WindowStartArcLengthMeters,WindowEndArcLengthMeters, start/end curvatures,CandidatePoints,EvaluatorResult,StopGate, andPublishedStatus. -
Consumed later by:
LocalG2DiagnosticVisualizationDemo.Export(string fixturePath, string evidencePath, string outputDirectory, CancellationToken cancellationToken = default). -
Step 1: Write the failing evidence-loader verification script
Create
verify_path_smoothing_local_g2_diagnostic_evidence.ps1. LoadClumsyPilot.dll, resolveMultiWheelC.TrajectoryPlanning.PathSmoothing.Test.LocalG2DiagnosticEvidenceLoader, and invokeLoadAndVerifywith the new fixture path. Assert all of these exact values:Assert-Equal 'single-turn' $evidence.ScenarioId 'Diagnostic evidence scenario must be stable.' Assert-Equal 'single-turn/s0/r0/w5/seed2' $evidence.CandidateStableKey 'Diagnostic evidence key must be stable.' Assert-Equal 5 $evidence.CandidateIndex 'Diagnostic evidence candidate index must be stable.' Assert-Equal 10 $evidence.CandidatePoints.Count 'Diagnostic evidence must retain all ten recorded samples.' Assert-Equal 'InsufficientClearance' $evidence.EvaluatorResult 'Diagnostic evidence must retain the observed evaluator result.' Assert-Equal 'Clearance' $evidence.StopGate 'Diagnostic evidence must retain the observed stop gate.' Assert-Equal 'Unchanged' $evidence.PublishedStatus 'Diagnostic evidence must retain the strict final status.'Copy the JSON to a uniquely named temp path, replace only
"StopGate": "Clearance"with"StopGate": "Collision", and require thatLoadAndVerifythrows. Delete the temp copy infinally. -
Step 2: Run the evidence check and confirm it fails before implementation
Run:
dotnet build .\ClumsyPilot\ClumsyPilot.csproj --no-restore powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_local_g2_diagnostic_evidence.ps1Expected: failure because the loader type and evidence file do not exist.
-
Step 3: Add the immutable compact evidence extract and loader
Create the JSON file with this exact top-level contract and the ten recorded
CandidatePoints. Preserve the displayed IEEE-754 decimal values; they are the evidence values, not rounded drawing inputs:{ "SourceMeasurementBatchSha256": "ac8166828813d85bf6f8b58f985839e5b2a049e75cb94186ed04f59d540e4eed", "FixtureSha256": "3d05daee5a211b3e7aa0b77193423b5fa07d3135e241a4413be3518fc7efe563", "ScenarioId": "single-turn", "CandidateStableKey": "single-turn/s0/r0/w5/seed2", "CandidateSha256": "7cefb76c77e48a49bf3212e7a2472e23036c3899daa94b5c6b68fa5db29a3392", "CandidateIndex": 5, "SegmentIndex": 0, "WindowStartArcLengthMeters": 1.9199999999999982, "WindowEndArcLengthMeters": 2.1199999999999983, "StartGeometricCurvaturePerMeter": 0.41666666666666663, "EndGeometricCurvaturePerMeter": 0.0, "StartVehicleCurvaturePerMeter": 0.41666666666666663, "EndVehicleCurvaturePerMeter": 0.0, "EvaluatorResult": "InsufficientClearance", "StopGate": "Clearance", "PublishedStatus": "Unchanged", "CandidatePoints": [ { "X": 2.7216397035494579, "Y": 1.7279184433567971, "ReferenceArcLengthMeters": 1.9199999999999982, "HeadingRadians": 0.799999999999999, "UnwrappedHeadingRadians": 0.799999999999999, "Source": 4 }, { "X": 2.7355188205174521, "Y": 1.7423187041196302, "ReferenceArcLengthMeters": 1.9399999999999982, "HeadingRadians": 0.80751185024996908, "UnwrappedHeadingRadians": 0.80751185024996908, "Source": 4 }, { "X": 2.7492871654421882, "Y": 1.7568248978050907, "ReferenceArcLengthMeters": 1.9599999999999982, "HeadingRadians": 0.8157618125921976, "UnwrappedHeadingRadians": 0.8157618125921976, "Source": 4 }, { "X": 2.7629234692741469, "Y": 1.7714552535786874, "ReferenceArcLengthMeters": 1.9799999999999982, "HeadingRadians": 0.82542852165703573, "UnwrappedHeadingRadians": 0.82542852165703573, "Source": 4 }, { "X": 2.7764244476704873, "Y": 1.786210614200662, "ReferenceArcLengthMeters": 1.9999999999999982, "HeadingRadians": 0.83333333333333237, "UnwrappedHeadingRadians": 0.83333333333333237, "Source": 4 }, { "X": 2.7925350453990334, "Y": 1.803999635249544, "ReferenceArcLengthMeters": 2.0239999999999982, "HeadingRadians": 0.835253334801853, "UnwrappedHeadingRadians": 0.835253334801853, "Source": 4 }, { "X": 2.80865418003672, "Y": 1.8217809211927092, "ReferenceArcLengthMeters": 2.0479999999999983, "HeadingRadians": 0.83333333355178374, "UnwrappedHeadingRadians": 0.83333333355178374, "Source": 4 }, { "X": 2.8248074262445737, "Y": 1.8395312269498194, "ReferenceArcLengthMeters": 2.0719999999999983, "HeadingRadians": 0.8318933325232104, "UnwrappedHeadingRadians": 0.8318933325232104, "Source": 4 }, { "X": 2.8409691920174533, "Y": 1.8572737784943611, "ReferenceArcLengthMeters": 2.0959999999999983, "HeadingRadians": 0.83237333274470626, "UnwrappedHeadingRadians": 0.83237333274470626, "Source": 4 }, { "X": 2.8571139169604551, "Y": 1.8750318365841865, "ReferenceArcLengthMeters": 2.1199999999999983, "HeadingRadians": 0.83333333333333237, "UnwrappedHeadingRadians": 0.83333333333333237, "Source": 4 } ] }Implement the public loader in the test namespace using
Newtonsoft.Json.JsonConvert.DeserializeObject<LocalG2DiagnosticEvidence>(File.ReadAllText(evidencePath)). UseInvalidDataExceptionfor every rejected input. The validation must require the three exact SHA/key constants,ScenarioId == "single-turn",CandidateIndex == 5,SegmentIndex == 0,EvaluatorResult == "InsufficientClearance",StopGate == "Clearance", andPublishedStatus == "Unchanged".Validate the two window values and all point coordinates/headings/reference arc lengths with this helper:
private static bool IsFinite(double value) => !double.IsNaN(value) && !double.IsInfinity(value);Require exactly ten points, first/last reference arcs equal the window endpoints within
1e-8d, strictly increasing reference arcs,Source == (int)SmoothedPathPointSource.LocalG2Transition, and non-null start/end curvature values. Return only after all checks pass. -
Step 4: Run the evidence verification and confirm it passes
Run:
dotnet build .\ClumsyPilot\ClumsyPilot.csproj --no-restore powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_local_g2_diagnostic_evidence.ps1Expected: exit
0; the valid extract loads and the altered stop gate is rejected. -
Step 5: Commit only Task 2 files
git add -- ` ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Test/Fixtures/local-g2-diagnostic-single-turn.json ` ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Test/LocalG2DiagnosticEvidenceLoader.cs ` ClumsyPilot/tests/verify_path_smoothing_local_g2_diagnostic_evidence.ps1 git -c core.autocrlf=false diff --cached --check git commit --only -m "test: freeze Local G2 diagnostic evidence" -- ` ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Test/Fixtures/local-g2-diagnostic-single-turn.json ` ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Test/LocalG2DiagnosticEvidenceLoader.cs ` ClumsyPilot/tests/verify_path_smoothing_local_g2_diagnostic_evidence.ps1
Task 3: Build The Visual-Only Candidate And Optional Figure 07
Files:
- Modify:
ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Visualization/SmoothingFigureModel.cs - Modify:
ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Visualization/SmoothingFigureKind.cs - Modify:
ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Visualization/SmoothingFigureSetBuilder.cs - Create:
ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Test/LocalG2DiagnosticVisualizationDemo.cs - Modify:
ClumsyPilot/tests/PathSmoothingPngVerificationHost/Program.cs - Create:
ClumsyPilot/tests/verify_path_smoothing_local_g2_diagnostic_visualization.ps1
Interfaces:
-
Produces:
internal SmoothingFigureModel WithAdditionalSeries(SmoothingFigureSeries series)that preserves all existing model layout, metric rows, endpoints, and scales. -
Produces:
SmoothingFigureKind.LocalG2DiagnosticCandidateand optional stem07-local-g2-diagnostic-candidate. -
Produces:
public SmoothingReportExportResult LocalG2DiagnosticVisualizationDemo.Export(string fixturePath, string evidencePath, string outputDirectory, CancellationToken cancellationToken = default). -
Produces: a host verification command that accepts a fixture path and evidence path, plus an export command that accepts fixture, evidence, and output-directory paths.
-
Step 1: Write the failing seven-file verification host branch and wrapper script
In
Program.Main, add command dispatch before the existing fixture/export cases:if (arguments.Length == 3 && arguments[0] == "--verify-local-g2-diagnostic") { VerifyLocalG2Diagnostic(arguments[1], arguments[2]); Console.WriteLine("Local G2 diagnostic visualization verification completed."); return 0; } if (arguments.Length == 4 && arguments[0] == "--export-local-g2-diagnostic") { ExportLocalG2Diagnostic(arguments[1], arguments[2], arguments[3]); return 0; }Create the wrapper script to resolve the fixture and evidence paths and run this command:
& dotnet run --project $hostProject --no-restore -- --verify-local-g2-diagnostic $resolvedFixturePath $resolvedEvidencePath if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }Run it before the demo/model implementation. Expected: build error because
VerifyLocalG2DiagnosticandExportLocalG2Diagnosticdo not yet exist. -
Step 2: Add immutable model augmentation and the optional figure definition
Add this internal method to
SmoothingFigureModel:internal SmoothingFigureModel WithAdditionalSeries(SmoothingFigureSeries series) { if (series == null) throw new ArgumentNullException(nameof(series)); var combined = new List<SmoothingFigureSeries>(Series.Count + 1); for (int index = 0; index < Series.Count; index++) { if (Series[index].Key == series.Key) throw new ArgumentException("Figure series keys must be unique.", nameof(series)); combined.Add(Series[index]); } combined.Add(series); var copy = new SmoothingFigureModel( ScenarioId, ScenarioLabel, WorldXMinMeters, WorldXMaxMeters, WorldYMinMeters, WorldYMaxMeters, PathPanelX, PathPanelY, PathPanelWidth, PathPanelHeight, CurvaturePanelX, CurvaturePanelY, CurvaturePanelWidth, CurvaturePanelHeight, MetricsPanelX, MetricsPanelY, MetricsPanelWidth, MetricsPanelHeight, Obstacles, combined, MetricRows, Start, Goal) { PathScaleX = PathScaleX, PathScaleY = PathScaleY, }; return copy; }Add
LocalG2DiagnosticCandidatetoSmoothingFigureKind. InSmoothingFigureSetBuilder.Build, retain the six normal definitions first. Use a non-throwingTryFindhelper for keylocal-g2-diagnostic; when it succeeds, append exactly this overhead figure:BuildOverhead( SmoothingFigureKind.LocalG2DiagnosticCandidate, "07-local-g2-diagnostic-candidate", "G2 诊断候选:净空拒绝,未发布;严格输出=原始路径", model, true, View(raw, 0.45d), View(diagnostic, 1d))The diagnostic series must carry key
local-g2-diagnostic, statusPathSmoothingStatus.Infeasible, colorIeeeFigureStyle.LocalG2DiagnosticColor, and an emptyViolationMarkerslist. Do not reuseSmoothingFigureModelBuilder.CreateSeries, because its genericInfeasiblebehavior synthesizes a violation cross when it cannot identify an occupied point. -
Step 3: Implement
LocalG2DiagnosticVisualizationDemowith the existing geometry componentsThe class stays in namespace
MultiWheelC.TrajectoryPlanning.PathSmoothing.Testand creates noPathSmoothingResult. ItsExportmethod must execute this exact sequence:LocalG2DiagnosticEvidence evidence = _evidenceLoader.LoadAndVerify(evidencePath); PathSmoothingComparisonRequest request = FindFixtureRequest(fixturePath, evidence.ScenarioId); PathSmoothingComparisonResult comparison = _comparisonService.Compare(request, cancellationToken); PathSmoothingComparisonEntry localG2 = FindEntry(comparison, SmoothingMethod.LocalG2Quintic); Require(localG2 != null && localG2.Status == PathSmoothingStatus.Unchanged, "Strict Local G2 result must be Unchanged for the diagnostic evidence."); RequireSameGeometry(comparison.RawPathBaseline.Path, localG2.Path); _preprocessor.TryPrepare(request.SmoothingRequest, out PreparedPath prepared, out string reason); LocalG2CandidateGeometry candidate = CreateCandidate(evidence); _splicer.TryReplace(prepared, candidate, out PreparedPath spliced, out reason); _analyzer.TryAnalyze(spliced.Segments, request.SmoothingRequest.Configuration.OutputSpacingMeters, out PathGeometryAnalysis analysis, out reason); CoarsePathPoint first = request.SmoothingRequest.CoarsePath[0]; CoarsePathPoint last = request.SmoothingRequest.CoarsePath[request.SmoothingRequest.CoarsePath.Count - 1]; SmoothingFigureModel normal = _figureBuilder.Build( comparison, request.SmoothingRequest.Map, new Pose2D(first.X, first.Y, first.Heading), new Pose2D(last.X, last.Y, last.Heading), evidence.ScenarioId, evidence.ScenarioId); SmoothingFigureModel augmented = normal.WithAdditionalSeries(CreateDiagnosticSeries(analysis.Path)); return _exporter.Export(new SmoothingReportExportRequest { Model = augmented, OutputDirectory = outputDirectory, FileStem = "comparison" });FindFixtureRequestmust callSmoothingScenarioFactory.CreateFixtureRequests(fixturePath), locate exactly one request by the matching fixture index fromSmoothingScenarioFixtureLoader.LoadAndVerify(fixturePath), and reject missing or duplicatesingle-turnIDs.CreateCandidatemust turn every evidence point into:new SmoothingPoint2D( point.X, point.Y, point.ReferenceArcLengthMeters, point.HeadingRadians, point.UnwrappedHeadingRadians, 0d, false, SmoothedPathPointSource.LocalG2Transition)Construct
LocalG2CandidateGeometrywith evidence index/window/curvatures,0dleft and right lengths, the converted point list, andtrueforinternalConnectionsAreG2. The zero clearance exists only to satisfy geometry-analysis input validity; it must not be fed to a validator or a metric row.CreateDiagnosticSeriesmust convertanalysis.PathtoSmoothingFigurePointvalues, use labelG2 候选(净空拒绝,未发布), and supplyArray.Empty<SmoothingFigurePoint>()as violation markers. Its path can be visualized but is not a published/safe path. -
Step 4: Implement host verification and run it
VerifyLocalG2Diagnosticmust create a fresh temp output directory, invoke the demo, and require all of the following before deleting the directory infinally:Require(report.Status == SmoothingReportExportStatus.Success, "Diagnostic report export failed: " + report.Reason); Require(report.SvgPaths.Count == 7 && report.PngPaths.Count == 7 && File.Exists(report.CsvPath), "Diagnostic export must publish seven SVGs, seven PNGs and one CSV."); Require(Path.GetFileName(report.SvgPaths[6]) == "07-local-g2-diagnostic-candidate.svg", "Diagnostic SVG stem is incorrect."); Require(Path.GetFileName(report.PngPaths[6]) == "07-local-g2-diagnostic-candidate.png", "Diagnostic PNG stem is incorrect."); string diagnosticSvg = File.ReadAllText(report.SvgPaths[6]); Require(diagnosticSvg.Contains("data-series=\"raw\"") && diagnosticSvg.Contains("data-series=\"local-g2-diagnostic\""), "Diagnostic SVG must contain raw and diagnostic samples."); Require(diagnosticSvg.Contains("净空拒绝") && diagnosticSvg.Contains("未发布"), "Diagnostic SVG must disclose rejection and publication state."); Require(!diagnosticSvg.Contains("violation-cross"), "Clearance rejection must not be drawn as an obstacle collision."); Require(File.ReadAllText(report.CsvPath).Contains("LocalG2Quintic,Unchanged"), "CSV must retain the normal strict Local G2 row.");Run
VerifyPng(File.ReadAllBytes(path))for every returned PNG and use the existingContainsTemporaryFileshelper to assert atomic publication. Also copy the evidence file to a temp file, alter the fixture hash, call the demo with a separate empty output path, require an exception, and require that the output directory was never created.Run:
dotnet build .\ClumsyPilot\ClumsyPilot.csproj --no-restore powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_local_g2_diagnostic_visualization.ps1Expected: exit
0, seven valid figures, raw and diagnostic series in07, no fabricated collision cross, and bad evidence rejected atomically. -
Step 5: Commit only Task 3 files
git add -- ` ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Visualization/SmoothingFigureModel.cs ` ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Visualization/SmoothingFigureKind.cs ` ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Visualization/SmoothingFigureSetBuilder.cs ` ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Test/LocalG2DiagnosticVisualizationDemo.cs ` ClumsyPilot/tests/PathSmoothingPngVerificationHost/Program.cs ` ClumsyPilot/tests/verify_path_smoothing_local_g2_diagnostic_visualization.ps1 git -c core.autocrlf=false diff --cached --check git commit --only -m "feat: export Local G2 diagnostic candidate" -- ` ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Visualization/SmoothingFigureModel.cs ` ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Visualization/SmoothingFigureKind.cs ` ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Visualization/SmoothingFigureSetBuilder.cs ` ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Test/LocalG2DiagnosticVisualizationDemo.cs ` ClumsyPilot/tests/PathSmoothingPngVerificationHost/Program.cs ` ClumsyPilot/tests/verify_path_smoothing_local_g2_diagnostic_visualization.ps1
Task 4: Add The User-Facing Export Script And Perform Full Acceptance
Files:
- Create:
ClumsyPilot/tests/run_local_g2_diagnostic_visualization.ps1 - Modify:
ClumsyPilot/tests/verify_path_smoothing_png.ps1
Interfaces:
-
Consumes: the diagnostic host export command, its fixture path, its evidence path, and its bounded output directory.
-
Produces:
ClumsyPilot/obj/path_smoothing_reports/local-g2-single-turn/07-local-g2-diagnostic-candidate.pngand six companion standard figures. -
Step 1: Write the failing runner assertion in the PNG smoke test
In
verify_path_smoothing_png.ps1, add a call to the not-yet-created runner with an explicit output directory belowobj/path_smoothing_reports, followed by an existence assertion for its primary PNG. Its default runner paths must be:[string]$FixturePath = (Join-Path $PSScriptRoot '..\ParkrobTrajplanner\PathSmoothing\Test\Fixtures\path-smoothing-fixtures.json'), [string]$EvidencePath = (Join-Path $PSScriptRoot '..\ParkrobTrajplanner\PathSmoothing\Test\Fixtures\local-g2-diagnostic-single-turn.json'), [string]$OutputDirectory = (Join-Path $PSScriptRoot '..\obj\path_smoothing_reports\local-g2-single-turn')Add this smoke-test block after the existing host invocation, changing no unrelated test behavior:
$runnerPath = Join-Path $PSScriptRoot 'run_local_g2_diagnostic_visualization.ps1' $runnerOutput = Join-Path $PSScriptRoot '..\obj\path_smoothing_reports\local-g2-png-smoke' & powershell -ExecutionPolicy Bypass -File $runnerPath -OutputDirectory $runnerOutput if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } $primaryPng = Join-Path $runnerOutput '07-local-g2-diagnostic-candidate.png' if (-not (Test-Path -LiteralPath $primaryPng)) { throw "Local G2 diagnostic runner did not publish $primaryPng" }Run the PNG smoke test. Expected: PowerShell reports that
run_local_g2_diagnostic_visualization.ps1does not exist, so the runner acceptance assertion fails before implementation. -
Step 2: Implement the bounded export script and extend the PNG smoke test
Follow the existing
run_path_smoothing_comparison.ps1root validation exactly: resolve$clumsyPilotRoot, require$OutputDirectoryto equal or be below$clumsyPilotRoot\obj\path_smoothing_reports, resolve both input files, buildClumsyPilot.csproj, then execute:& dotnet run --project $hostProject --no-restore -- --export-local-g2-diagnostic $resolvedFixturePath $resolvedEvidencePath $resolvedOutputDirectory if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } Write-Output "Local G2 diagnostic visualization written below $resolvedOutputDirectory"In
verify_path_smoothing_png.ps1, call the new diagnostic verification wrapper after the existing host verification so both standard six-file and diagnostic seven-file image contracts run in the normal PNG check. Retain the runner call and primary-PNG existence assertion added in Step 1; use alocal-g2-png-smokeoutput subdirectory below the allowed report root. -
Step 3: Run the full automated acceptance sequence
Run these commands in order and inspect every exit code:
dotnet build .\ClumsyPilot\ClumsyPilot.csproj --no-restore powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_comparison.ps1 powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_svg_csv.ps1 powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_local_g2_diagnostic_evidence.ps1 powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_local_g2_diagnostic_visualization.ps1 powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_png.ps1 powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\run_local_g2_diagnostic_visualization.ps1Expected: every command exits
0; the primary PNG, matching SVG, andcomparison.csvexist underClumsyPilot/obj/path_smoothing_reports/local-g2-single-turn. -
Step 4: Inspect the generated image and report the factual result
Open the primary file with the local image viewer:
ClumsyPilot/obj/path_smoothing_reports/local-g2-single-turn/07-local-g2-diagnostic-candidate.pngConfirm visually that the map is nonblank, the gray raw/final path and red diagnostic candidate are both visible, the legend/title disclose
净空拒绝and未发布, there is no collision cross, and text remains inside the 4296-by-3120 PNG frame. Also open02-all-paths-comparison.pngand06-curvature-comparison.pngfrom the same directory to confirm that normal Local G2 appears withUnchangedalongside the existing methods. -
Step 5: Commit only Task 4 source/test files
Do not commit generated
objartifacts. Commit only the runner and PNG smoke-test changes:git add -- ` ClumsyPilot/tests/run_local_g2_diagnostic_visualization.ps1 ` ClumsyPilot/tests/verify_path_smoothing_png.ps1 git -c core.autocrlf=false diff --cached --check git commit --only -m "test: add Local G2 diagnostic visualization runner" -- ` ClumsyPilot/tests/run_local_g2_diagnostic_visualization.ps1 ` ClumsyPilot/tests/verify_path_smoothing_png.ps1
Final Verification Checklist
- Re-read
2026-08-02-local-g2-diagnostic-visualization-design.mdand map every acceptance criterion to a passing command or visual check above. - Run
git -c core.autocrlf=false diff --checkonly on the files changed by these tasks. - Verify that no
PathSmoothing/LocalG2/implementation, evaluator, validator, publishing, or ranking file changed. - Verify each task commit contains only its listed paths.
- Report the primary image path and state plainly that the red curve is a rejected diagnostic candidate, while the strict Local G2 result remains
Unchanged.