1108 lines
60 KiB
Markdown
1108 lines
60 KiB
Markdown
# EM Full-Direction-Segment Planning and Visualization Repair 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:** Add one-shot full-direction-segment LS/ST planning that starts from rest, reaches the configured cruise speed when feasible, stops at the true segment boundary with pose tolerances, and repairs the existing paper-style observation UI without adding actuator output.
|
||
|
||
**Architecture:** Keep rolling planning intact behind an explicit `RollingHorizon` scope and add `FullDirectionSegment` as a separate request scope. Full-segment planning selects the remaining active direction segment, derives a bounded adaptive optimization schedule from the actual Local G2 `s_end` and jerk-limited speed envelope, then publishes a separately resampled trajectory. Observation snapshots remain immutable; Web and Native Painter consume the same corrected semantics, while a test-only jsdom host verifies actual DOM/SVG output.
|
||
|
||
**Tech Stack:** C#/.NET 8, existing EM planner and OSQP abstraction, existing MovementTest observer, embedded HTML/CSS/vanilla JavaScript/SVG/Canvas, Node.js 24 test runtime with `jsdom` 29.1.1 as a development-only DOM dependency.
|
||
|
||
## Global Constraints
|
||
|
||
- MovementTest remains `OBSERVE_ONLY`; do not add chassis, steering, brake, motor, or gear write calls.
|
||
- “Global” means the complete remaining part of one coarse-path direction segment. A trajectory never crosses a forward/reverse boundary.
|
||
- `FullDirectionSegment` plans once when a segment becomes active and reuses the frozen result. Runtime rolling refinement and dynamic-obstacle replanning are outside this plan.
|
||
- If MovementTest starts in the middle of a segment, plan from the valid Local G2 projection of the startup pose to that segment’s true end boundary.
|
||
- Full mode derives `s_end` from actual Local G2 `PathS` and derives `T_end`; `DistanceHorizonMeters` and `TimeHorizonSeconds` do not truncate full mode.
|
||
- Forward desired/hard maximum speed is `1.0 m/s`; reverse desired/hard maximum speed is `0.5 m/s`.
|
||
- Terminal world-position error must be at most `0.03 m`; normalized yaw error must be at most `5°`.
|
||
- Published trajectory sampling is `0.1 s` for MovementTest and is independent of adaptive optimization knots.
|
||
- Jerk contains exactly `N-1` interval values for `N` trajectory points; there is no jerk sample after the terminal point.
|
||
- Resource exhaustion returns `FullSegmentResourceLimitExceeded`; it never silently truncates or falls back to rolling.
|
||
- Preserve the existing four-tab white paper/scientific Web layout, Chinese titles, mathematical symbols, and thin lines.
|
||
- Web zoom changes only the browser viewport. It never mutates snapshot data or sends planner settings.
|
||
- Native Painter receives coordinate, scale, marker, and semantic corrections only; do not redesign or beautify it.
|
||
- Before editing any already-dirty file, record `git diff -- <path>` and preserve all unrelated user hunks. Stage every task with explicit paths only.
|
||
- The approved design is `docs/superpowers/specs/2026-08-06-em-full-direction-segment-visualization-repair-design.md`.
|
||
|
||
---
|
||
|
||
## File and Responsibility Map
|
||
|
||
**New planner files**
|
||
|
||
- `ClumsyPilot/ParkrobTrajplanner/EMPlanner/Contracts/EmPlanningScope.cs`: explicit rolling versus full-direction-segment request scope.
|
||
- `ClumsyPilot/ParkrobTrajplanner/EMPlanner/Longitudinal/LongitudinalKnotSchedule.cs`: immutable optimization-knot schedule, separate from publication sampling.
|
||
- `ClumsyPilot/ParkrobTrajplanner/EMPlanner/Longitudinal/FullDirectionSegmentScheduleBuilder.cs`: derives `T_end`, adaptive knots, and reference progress/speed from Local G2 `PathS` and the speed/stopping envelope.
|
||
|
||
**New Web-test files**
|
||
|
||
- `ClumsyPilot/tests/TrajectoryPlanningVisualizationWebDom/package.json`: test-only Node package with exact jsdom dependency.
|
||
- `ClumsyPilot/tests/TrajectoryPlanningVisualizationWebDom/package-lock.json`: locked dependency graph generated by npm.
|
||
- `ClumsyPilot/tests/TrajectoryPlanningVisualizationWebDom/dashboard.dom.test.mjs`: actual DOM/SVG regression tests.
|
||
|
||
**Existing files with focused changes**
|
||
|
||
- Configuration/contracts: `EmPlannerConfiguration.cs`, `SchedulingConfiguration.cs`, `LongitudinalConfiguration.cs`, `ValidationConfiguration.cs`, `EmPlanningRequest.cs`, `EmPlanningStatus.cs`, `EmTrajectoryMetadata.cs`.
|
||
- Scope and full planning: `PlanningHorizonSelector.cs`, `EmPlanningRequestValidator.cs`, `EmPlanningService.cs`.
|
||
- Longitudinal model: `PathSpeedLimitBuilder.cs`, `LongitudinalPlanningInput.cs`, `SequentialLongitudinalOptimizer.cs`, `LongitudinalObjectiveBuilder.cs`, `LongitudinalConstraintBuilder.cs`, `LongitudinalSolutionValidator.cs`, `LongitudinalPreviousTrajectorySeedBuilder.cs`.
|
||
- Publication: `TrajectorySampleSchedule.cs`, `EmTrajectoryAssembler.cs`, `EmTrajectoryValidator.cs`.
|
||
- MovementTest: `TrajectoryObservationContracts.cs`, `TrajectoryObservationPipeline.cs`, `MovementTest.TrajectoryObservationTest.cs`.
|
||
- Observation semantics: `TrajectoryObservationStaticSnapshotBuilder.cs`, `TrajectoryObservationDynamicSnapshotBuilder.cs`, `TrajectoryObservationKinematicChartBuilder.cs`, `TrajectoryObservationPresentation.cs`.
|
||
- Visualization contracts/assets: `VisualizationCharts.cs`, `VisualizationGeometry.cs`, `PlanningVisualizationSnapshots.cs`, `index.html`, `app.css`, `app.js`, `EmbeddedWebAssets.cs`.
|
||
- Tests: existing EM and visualization verification hosts plus the new test-only jsdom package.
|
||
- Operator docs: MovementTest and visualization READMEs, followed by a new acceptance handoff.
|
||
|
||
---
|
||
|
||
### Task 1: Add explicit planning scope and validated configuration
|
||
|
||
**Files:**
|
||
|
||
- Create: `ClumsyPilot/ParkrobTrajplanner/EMPlanner/Contracts/EmPlanningScope.cs`
|
||
- Modify: `ClumsyPilot/ParkrobTrajplanner/EMPlanner/Contracts/EmPlanningRequest.cs`
|
||
- Modify: `ClumsyPilot/ParkrobTrajplanner/EMPlanner/Contracts/EmPlanningStatus.cs`
|
||
- Modify: `ClumsyPilot/ParkrobTrajplanner/EMPlanner/Contracts/EmTrajectoryMetadata.cs`
|
||
- Modify: `ClumsyPilot/ParkrobTrajplanner/EMPlanner/Configuration/SchedulingConfiguration.cs`
|
||
- Modify: `ClumsyPilot/ParkrobTrajplanner/EMPlanner/Configuration/LongitudinalConfiguration.cs`
|
||
- Modify: `ClumsyPilot/ParkrobTrajplanner/EMPlanner/Configuration/ValidationConfiguration.cs`
|
||
- Modify: `ClumsyPilot/ParkrobTrajplanner/EMPlanner/Configuration/EmPlannerConfiguration.cs`
|
||
- Modify: `ClumsyPilot/ParkrobTrajplanner/EMPlanner/Validation/EmPlanningRequestValidator.cs`
|
||
- Test: `ClumsyPilot/tests/EMPlannerVerificationHost/FoundationChecks.cs`
|
||
- Test: `ClumsyPilot/tests/EMPlannerVerificationHost/EmPlanningServiceChecks.cs`
|
||
|
||
**Interfaces:**
|
||
|
||
- Produces: `EmPlanningScope.RollingHorizon` and `EmPlanningScope.FullDirectionSegment`.
|
||
- Produces: `EmPlanningRequest.PlanningScope` and `EmTrajectoryMetadata.PlanningScope`.
|
||
- Produces configuration properties used by Tasks 2-5:
|
||
`DesiredForwardSpeedMetersPerSecond`, `DesiredReverseSpeedMetersPerSecond`,
|
||
`MaximumOptimizationTimeStepSeconds`, `MaximumOptimizationSpatialStepMeters`,
|
||
`MaximumOptimizationKnotCount`, `MaximumPublishedSampleCount`,
|
||
`TerminalPositionToleranceMeters`, and `TerminalYawToleranceRadians`.
|
||
- Produces status values `NoProgress`, `TerminalPoseMismatch`, and `FullSegmentResourceLimitExceeded`.
|
||
|
||
- [ ] **Step 1: Write failing contract/default/copy tests**
|
||
|
||
Add checks that construct both request scopes, copy the configuration, and assert the exact defaults:
|
||
|
||
```csharp
|
||
Verification.Equal(EmPlanningScope.FullDirectionSegment, fullRequest.PlanningScope,
|
||
"full request freezes its scope");
|
||
Verification.NearlyEqual(1.0d, defaults.Longitudinal.DesiredForwardSpeedMetersPerSecond,
|
||
"forward desired speed");
|
||
Verification.NearlyEqual(0.5d, defaults.Longitudinal.DesiredReverseSpeedMetersPerSecond,
|
||
"reverse desired speed");
|
||
Verification.NearlyEqual(1.0d, defaults.Longitudinal.MaximumForwardSpeedMetersPerSecond,
|
||
"forward hard limit");
|
||
Verification.NearlyEqual(0.5d, defaults.Longitudinal.MaximumReverseSpeedMetersPerSecond,
|
||
"reverse hard limit");
|
||
Verification.NearlyEqual(0.20d, defaults.Scheduling.MaximumOptimizationTimeStepSeconds,
|
||
"adaptive maximum dt");
|
||
Verification.NearlyEqual(0.10d, defaults.Scheduling.MaximumOptimizationSpatialStepMeters,
|
||
"adaptive maximum ds");
|
||
Verification.Equal(401, defaults.Scheduling.MaximumOptimizationKnotCount,
|
||
"optimization knot cap");
|
||
Verification.Equal(5001, defaults.Scheduling.MaximumPublishedSampleCount,
|
||
"publication sample cap");
|
||
Verification.NearlyEqual(0.03d, defaults.Validation.TerminalPositionToleranceMeters,
|
||
"terminal position tolerance");
|
||
Verification.NearlyEqual(5d * Math.PI / 180d, defaults.Validation.TerminalYawToleranceRadians,
|
||
"terminal yaw tolerance");
|
||
```
|
||
|
||
Mutate the source after `Copy()` and verify the copy remains unchanged. Add invalid-value tests for zero/negative knot caps, desired speed above the hard limit, and terminal yaw tolerance above π.
|
||
|
||
- [ ] **Step 2: Run RED**
|
||
|
||
```powershell
|
||
dotnet run --project ClumsyPilot/tests/EMPlannerVerificationHost/EMPlannerVerificationHost.csproj -- foundation
|
||
dotnet run --project ClumsyPilot/tests/EMPlannerVerificationHost/EMPlannerVerificationHost.csproj -- em-planning-service
|
||
```
|
||
|
||
Expected: compilation fails because the scope, statuses, and configuration members do not exist.
|
||
|
||
- [ ] **Step 3: Add the contracts and defaults**
|
||
|
||
Create:
|
||
|
||
```csharp
|
||
namespace MultiWheelC.TrajectoryPlanning.EMPlanner;
|
||
|
||
public enum EmPlanningScope
|
||
{
|
||
RollingHorizon,
|
||
FullDirectionSegment,
|
||
}
|
||
```
|
||
|
||
Add a required `EmPlanningScope planningScope` constructor parameter and immutable property to `EmPlanningRequest`. Add the same field to `EmTrajectoryMetadata`. Update every constructor call explicitly; do not add an implicit default that could silently change legacy rolling behavior.
|
||
|
||
Add the configuration members listed above and copy them in their owning `Copy()` methods. Set the exact defaults from the test. Add the three new failure statuses to `EmPlanningStatus`.
|
||
|
||
- [ ] **Step 4: Validate cross-field invariants**
|
||
|
||
Extend `TryValidateConfiguration` with exact checks:
|
||
|
||
```csharp
|
||
if (!Positive(longitudinal.DesiredForwardSpeedMetersPerSecond) ||
|
||
longitudinal.DesiredForwardSpeedMetersPerSecond > longitudinal.MaximumForwardSpeedMetersPerSecond ||
|
||
!Positive(longitudinal.DesiredReverseSpeedMetersPerSecond) ||
|
||
longitudinal.DesiredReverseSpeedMetersPerSecond > longitudinal.MaximumReverseSpeedMetersPerSecond ||
|
||
!Positive(scheduling.MaximumOptimizationTimeStepSeconds) ||
|
||
!Positive(scheduling.MaximumOptimizationSpatialStepMeters) ||
|
||
scheduling.MaximumOptimizationKnotCount < 3 ||
|
||
scheduling.MaximumPublishedSampleCount < 2 ||
|
||
!Positive(validation.TerminalPositionToleranceMeters) ||
|
||
!Positive(validation.TerminalYawToleranceRadians) ||
|
||
validation.TerminalYawToleranceRadians > Math.PI)
|
||
{
|
||
return false;
|
||
}
|
||
```
|
||
|
||
Validate `request.PlanningScope` with `Enum.IsDefined`. Keep the rolling distance/time sufficiency validation only for `RollingHorizon`; full mode still validates positive legacy values for compatibility but does not use them as truncation inputs.
|
||
|
||
- [ ] **Step 5: Run GREEN**
|
||
|
||
Run the two commands from Step 2. Expected: `PASS foundation` and `PASS em-planning-service`.
|
||
|
||
- [ ] **Step 6: Commit the contract boundary**
|
||
|
||
```powershell
|
||
git add -- ClumsyPilot/ParkrobTrajplanner/EMPlanner/Contracts/EmPlanningScope.cs ClumsyPilot/ParkrobTrajplanner/EMPlanner/Contracts/EmPlanningRequest.cs ClumsyPilot/ParkrobTrajplanner/EMPlanner/Contracts/EmPlanningStatus.cs ClumsyPilot/ParkrobTrajplanner/EMPlanner/Contracts/EmTrajectoryMetadata.cs ClumsyPilot/ParkrobTrajplanner/EMPlanner/Configuration/SchedulingConfiguration.cs ClumsyPilot/ParkrobTrajplanner/EMPlanner/Configuration/LongitudinalConfiguration.cs ClumsyPilot/ParkrobTrajplanner/EMPlanner/Configuration/ValidationConfiguration.cs ClumsyPilot/ParkrobTrajplanner/EMPlanner/Configuration/EmPlannerConfiguration.cs ClumsyPilot/ParkrobTrajplanner/EMPlanner/Validation/EmPlanningRequestValidator.cs ClumsyPilot/tests/EMPlannerVerificationHost/FoundationChecks.cs ClumsyPilot/tests/EMPlannerVerificationHost/EmPlanningServiceChecks.cs
|
||
git commit -m "feat: define full-direction EM planning scope"
|
||
```
|
||
|
||
---
|
||
|
||
### Task 2: Select the complete remaining direction segment
|
||
|
||
**Files:**
|
||
|
||
- Modify: `ClumsyPilot/ParkrobTrajplanner/EMPlanner/Segmentation/PlanningHorizonSelector.cs`
|
||
- Modify: `ClumsyPilot/ParkrobTrajplanner/EMPlanner/Facade/EmPlanningService.cs`
|
||
- Modify: `ClumsyPilot/ParkrobTrajplanner/EMPlanner/Trajectory/EmTrajectoryAssembler.cs`
|
||
- Test: `ClumsyPilot/tests/EMPlannerVerificationHost/LongitudinalModelChecks.cs`
|
||
- Test: `ClumsyPilot/tests/EMPlannerVerificationHost/EmPlanningServiceChecks.cs`
|
||
|
||
**Interfaces:**
|
||
|
||
- Consumes: `EmPlanningScope` from Task 1.
|
||
- Produces: `PlanningHorizonSelector.Select(..., EmPlanningScope planningScope, ...)`.
|
||
- Guarantees: full mode returns the real `Goal` or `GearSwitch` boundary with `ExactStopAtBoundary`; rolling mode preserves existing rolling/approach/exact selection.
|
||
|
||
- [ ] **Step 1: Write failing scope-selection tests**
|
||
|
||
Create a 10 m segment, project the vehicle at segment-local 3 m, set legacy distance horizon to 1 m and time horizon to 2 s, then assert:
|
||
|
||
```csharp
|
||
EmPlanningStatus status = selector.Select(segment, 3d, 0d, 0d,
|
||
EmPlanningScope.FullDirectionSegment, configuration, out PlanningHorizonSelection full, out string failure);
|
||
Verification.Equal(EmPlanningStatus.Success, status, "full selection: " + failure);
|
||
Verification.NearlyEqual(segment.LengthMeters, full.WindowEndReferenceS,
|
||
"full selection reaches the true segment boundary");
|
||
Verification.Equal(EmLongitudinalMode.ExactStopAtBoundary, full.LongitudinalMode,
|
||
"full selection always plans a stopped boundary");
|
||
Verification.True(full.HasStopBoundary, "full selection retains a real stop boundary");
|
||
```
|
||
|
||
Use the same fixture with `RollingHorizon` and assert the window ends at 4 m. Add a gear-pair fixture and assert segment 0 ends at `GearSwitch` without including segment 1 points.
|
||
|
||
- [ ] **Step 2: Run RED**
|
||
|
||
```powershell
|
||
dotnet run --project ClumsyPilot/tests/EMPlannerVerificationHost/EMPlannerVerificationHost.csproj -- longitudinal-model
|
||
```
|
||
|
||
Expected: compile failure because `Select` does not accept planning scope.
|
||
|
||
- [ ] **Step 3: Implement the explicit full branch**
|
||
|
||
Change the selector signature and branch after the initial-state/stopping-distance precheck:
|
||
|
||
```csharp
|
||
if (planningScope == EmPlanningScope.FullDirectionSegment)
|
||
{
|
||
EmBoundaryType boundary = segment.EndBoundary.BoundaryType;
|
||
if (!IsStopBoundary(boundary))
|
||
{
|
||
failureReason = "A full direction segment must end at Goal or GearSwitch.";
|
||
return EmPlanningStatus.InvalidReferencePath;
|
||
}
|
||
selection = new PlanningHorizonSelection(segment.LengthMeters, boundary,
|
||
ToTerminalType(boundary), EmLongitudinalMode.ExactStopAtBoundary,
|
||
segment.LengthMeters, true);
|
||
return EmPlanningStatus.Success;
|
||
}
|
||
```
|
||
|
||
Leave the existing rolling selection below this branch unchanged. Pass `request.PlanningScope` from `EmPlanningService` and include the scope in metadata and diagnostics.
|
||
|
||
- [ ] **Step 4: Verify the service slices from the startup projection to the real boundary**
|
||
|
||
Add an end-to-end service test where the initial pose projects into the middle of a direction segment. Assert the first published point is at the projected startup pose, the final point is the segment endpoint, and no point belongs to the next direction segment.
|
||
|
||
- [ ] **Step 5: Run GREEN**
|
||
|
||
```powershell
|
||
dotnet run --project ClumsyPilot/tests/EMPlannerVerificationHost/EMPlannerVerificationHost.csproj -- longitudinal-model
|
||
dotnet run --project ClumsyPilot/tests/EMPlannerVerificationHost/EMPlannerVerificationHost.csproj -- em-planning-service
|
||
```
|
||
|
||
Expected: both commands print PASS.
|
||
|
||
- [ ] **Step 6: Commit segment selection**
|
||
|
||
```powershell
|
||
git add -- ClumsyPilot/ParkrobTrajplanner/EMPlanner/Segmentation/PlanningHorizonSelector.cs ClumsyPilot/ParkrobTrajplanner/EMPlanner/Facade/EmPlanningService.cs ClumsyPilot/ParkrobTrajplanner/EMPlanner/Trajectory/EmTrajectoryAssembler.cs ClumsyPilot/tests/EMPlannerVerificationHost/LongitudinalModelChecks.cs ClumsyPilot/tests/EMPlannerVerificationHost/EmPlanningServiceChecks.cs
|
||
git commit -m "feat: select complete EM direction segments"
|
||
```
|
||
|
||
---
|
||
|
||
### Task 3: Derive adaptive full-segment optimization knots
|
||
|
||
**Files:**
|
||
|
||
- Create: `ClumsyPilot/ParkrobTrajplanner/EMPlanner/Longitudinal/LongitudinalKnotSchedule.cs`
|
||
- Create: `ClumsyPilot/ParkrobTrajplanner/EMPlanner/Longitudinal/FullDirectionSegmentScheduleBuilder.cs`
|
||
- Modify: `ClumsyPilot/ParkrobTrajplanner/EMPlanner/Longitudinal/PathSpeedLimitBuilder.cs`
|
||
- Modify: `ClumsyPilot/ParkrobTrajplanner/EMPlanner/Longitudinal/LongitudinalPlanningInput.cs`
|
||
- Modify: `ClumsyPilot/ParkrobTrajplanner/EMPlanner/Longitudinal/LongitudinalConstraintBuilder.cs`
|
||
- Modify: `ClumsyPilot/ParkrobTrajplanner/EMPlanner/Longitudinal/LongitudinalSolutionValidator.cs`
|
||
- Modify: `ClumsyPilot/ParkrobTrajplanner/EMPlanner/Longitudinal/SequentialLongitudinalOptimizer.cs`
|
||
- Modify: `ClumsyPilot/ParkrobTrajplanner/EMPlanner/Longitudinal/LongitudinalPreviousTrajectorySeedBuilder.cs`
|
||
- Modify: `ClumsyPilot/ParkrobTrajplanner/EMPlanner/Facade/EmPlanningService.cs`
|
||
- Test: `ClumsyPilot/tests/EMPlannerVerificationHost/LongitudinalModelChecks.cs`
|
||
- Test: `ClumsyPilot/tests/EMPlannerVerificationHost/LongitudinalIntegrationChecks.cs`
|
||
|
||
**Interfaces:**
|
||
|
||
- Produces immutable `LongitudinalKnotSchedule` with `KnotTimes`, `ReferencePathS`, `ReferenceSpeedMetersPerSecond`, `TotalDurationSeconds`, and `IsAdaptive`.
|
||
- Produces `FullDirectionSegmentScheduleBuilder.TryBuild(...)` returning `EmPlanningStatus.Success` or `FullSegmentResourceLimitExceeded`.
|
||
- `LongitudinalPlanningInput.PlanningScope` carries the request scope into the independent validator.
|
||
- `LongitudinalPlanningInput.KnotSchedule` becomes the only optimization-knot source.
|
||
- Publication still uses `Scheduling.OutputTimeStepSeconds`; no caller treats it as the optimization step in full mode.
|
||
|
||
- [ ] **Step 1: Write failing duration, adaptivity, and resource-limit tests**
|
||
|
||
Cover these deterministic fixtures:
|
||
|
||
```csharp
|
||
// A short segment completes before a legacy 10 s horizon.
|
||
Verification.True(shortSchedule.TotalDurationSeconds < 10d, "short segment derives its own T_end");
|
||
// A longer segment takes longer without changing DistanceHorizonMeters/TimeHorizonSeconds.
|
||
Verification.True(longSchedule.TotalDurationSeconds > shortSchedule.TotalDurationSeconds,
|
||
"duration grows from s_end and limits");
|
||
// Curvature transitions and the stop boundary are represented in the adaptive schedule.
|
||
Verification.True(longSchedule.KnotTimes.Count <= configuration.Scheduling.MaximumOptimizationKnotCount,
|
||
"adaptive schedule respects knot cap");
|
||
Verification.NearlyEqual(path.Points[path.Points.Count - 1].PathS,
|
||
longSchedule.ReferencePathS[longSchedule.ReferencePathS.Count - 1], "schedule reaches s_end");
|
||
Verification.NearlyEqual(0d,
|
||
longSchedule.ReferenceSpeedMetersPerSecond[longSchedule.ReferenceSpeedMetersPerSecond.Count - 1],
|
||
"schedule stops at s_end");
|
||
```
|
||
|
||
Set `MaximumOptimizationKnotCount = 4` on a path that needs more intervals and assert exact status `FullSegmentResourceLimitExceeded`, no schedule, and a diagnostic containing required versus configured knots.
|
||
|
||
- [ ] **Step 2: Run RED**
|
||
|
||
```powershell
|
||
dotnet run --project ClumsyPilot/tests/EMPlannerVerificationHost/EMPlannerVerificationHost.csproj -- longitudinal-model
|
||
```
|
||
|
||
Expected: compile failure for the new schedule types.
|
||
|
||
- [ ] **Step 3: Implement the immutable schedule contract**
|
||
|
||
Use this public shape:
|
||
|
||
```csharp
|
||
public sealed class LongitudinalKnotSchedule
|
||
{
|
||
public LongitudinalKnotSchedule(IReadOnlyList<double> knotTimes,
|
||
IReadOnlyList<double> referencePathS,
|
||
IReadOnlyList<double> referenceSpeedMetersPerSecond, bool isAdaptive);
|
||
|
||
public IReadOnlyList<double> KnotTimes { get; }
|
||
public IReadOnlyList<double> ReferencePathS { get; }
|
||
public IReadOnlyList<double> ReferenceSpeedMetersPerSecond { get; }
|
||
public double TotalDurationSeconds { get; }
|
||
public bool IsAdaptive { get; }
|
||
|
||
public static LongitudinalKnotSchedule CreateRolling(
|
||
double timeHorizonSeconds, double timeStepSeconds);
|
||
}
|
||
```
|
||
|
||
Validate equal counts, exact zero first time/path, strictly increasing times, nondecreasing PathS, finite nonnegative speed, and exact zero terminal speed for adaptive schedules.
|
||
|
||
- [ ] **Step 4: Extract a PathSpeedLimitBuilder overload that does not require knot times**
|
||
|
||
Add:
|
||
|
||
```csharp
|
||
public EmPlanningStatus Build(LateralPath path, TravelDirection direction,
|
||
double initialProgressSpeedMetersPerSecond, EmTerminalType terminalType,
|
||
EmPlannerConfiguration configuration, out PathSpeedLimit speedLimit,
|
||
out string failureReason);
|
||
```
|
||
|
||
Keep the existing `Build(LongitudinalPlanningInput, ...)` as a delegating compatibility overload. This allows the service to build the physical PathS envelope before it creates the optimization schedule.
|
||
|
||
- [ ] **Step 5: Implement full-segment schedule derivation**
|
||
|
||
Use one forward/backward envelope pass over the existing `PathSpeedLimit.PathS` stations:
|
||
|
||
1. Start with the hard maximum-speed array.
|
||
2. Forward-propagate a jerk-limited reachable speed from `v0/a0`, never exceeding desired speed or the hard PathS envelope.
|
||
3. Backward-propagate the jerk-limited stopping envelope to exact `(s_end, v=0, a=0)` using `JerkLimitedStoppingMath` for every remaining-distance check.
|
||
4. Intersect forward, backward, curvature, and desired-speed limits.
|
||
5. Integrate `dt = 2*ds/(v0+v1)` on nonzero-speed intervals; use the jerk-limited acceleration/deceleration phase duration when either endpoint speed is zero.
|
||
6. Add exact breakpoints for startup, speed-limit changes, brake onset, terminal stop, and the zero-speed hold.
|
||
7. Refine any interval exceeding either configured maximum `dt` or maximum `ds`.
|
||
8. If refinement needs more than `MaximumOptimizationKnotCount`, return `FullSegmentResourceLimitExceeded`.
|
||
|
||
The builder signature is:
|
||
|
||
```csharp
|
||
public EmPlanningStatus TryBuild(LateralPath path, PathSpeedLimit speedLimit,
|
||
double initialProgressSpeedMetersPerSecond,
|
||
double initialAccelerationMetersPerSecondSquared,
|
||
double desiredSpeedMetersPerSecond, EmPlannerConfiguration configuration,
|
||
out LongitudinalKnotSchedule schedule, out string failureReason);
|
||
```
|
||
|
||
Do not clamp a result to `s_end` after integration. A schedule is accepted only if its final reference state is exactly `s_end/0/0` within configured kinematic tolerance.
|
||
|
||
- [ ] **Step 6: Make all ST components consume `input.KnotSchedule`**
|
||
|
||
Add both `EmPlanningScope planningScope` and the schedule to `LongitudinalPlanningInput`, copy them into immutable `PlanningScope` and `KnotSchedule` properties, and reject an adaptive schedule supplied to rolling scope or a rolling schedule supplied to full scope. Replace every direct call to:
|
||
|
||
```csharp
|
||
LongitudinalCandidate.CreateKnotTimes(
|
||
input.Configuration.Scheduling.TimeHorizonSeconds,
|
||
input.Configuration.Scheduling.OutputTimeStepSeconds)
|
||
```
|
||
|
||
with:
|
||
|
||
```csharp
|
||
input.KnotSchedule.KnotTimes
|
||
```
|
||
|
||
This includes constraints, initial iterate creation, exact-stop stabilization, previous-trajectory resampling, and solution validation. The rolling service path creates `LongitudinalKnotSchedule.CreateRolling(...)`; the full path calls `FullDirectionSegmentScheduleBuilder`.
|
||
|
||
- [ ] **Step 7: Run GREEN and prove publication sampling is independent**
|
||
|
||
```powershell
|
||
dotnet run --project ClumsyPilot/tests/EMPlannerVerificationHost/EMPlannerVerificationHost.csproj -- longitudinal-model
|
||
dotnet run --project ClumsyPilot/tests/EMPlannerVerificationHost/EMPlannerVerificationHost.csproj -- longitudinal-integration
|
||
```
|
||
|
||
Expected: both PASS. Add one assertion that changing `OutputTimeStepSeconds` from 0.1 to 0.05 doubles publication density but leaves `KnotSchedule.KnotTimes.Count` unchanged.
|
||
|
||
- [ ] **Step 8: Commit adaptive scheduling**
|
||
|
||
```powershell
|
||
git add -- ClumsyPilot/ParkrobTrajplanner/EMPlanner/Longitudinal/LongitudinalKnotSchedule.cs ClumsyPilot/ParkrobTrajplanner/EMPlanner/Longitudinal/FullDirectionSegmentScheduleBuilder.cs ClumsyPilot/ParkrobTrajplanner/EMPlanner/Longitudinal/PathSpeedLimitBuilder.cs ClumsyPilot/ParkrobTrajplanner/EMPlanner/Longitudinal/LongitudinalPlanningInput.cs ClumsyPilot/ParkrobTrajplanner/EMPlanner/Longitudinal/LongitudinalConstraintBuilder.cs ClumsyPilot/ParkrobTrajplanner/EMPlanner/Longitudinal/LongitudinalSolutionValidator.cs ClumsyPilot/ParkrobTrajplanner/EMPlanner/Longitudinal/SequentialLongitudinalOptimizer.cs ClumsyPilot/ParkrobTrajplanner/EMPlanner/Longitudinal/LongitudinalPreviousTrajectorySeedBuilder.cs ClumsyPilot/ParkrobTrajplanner/EMPlanner/Facade/EmPlanningService.cs ClumsyPilot/tests/EMPlannerVerificationHost/LongitudinalModelChecks.cs ClumsyPilot/tests/EMPlannerVerificationHost/LongitudinalIntegrationChecks.cs
|
||
git commit -m "feat: derive adaptive full-segment ST schedule"
|
||
```
|
||
|
||
---
|
||
|
||
### Task 4: Fix static-start progress and reject false success
|
||
|
||
**Files:**
|
||
|
||
- Modify: `ClumsyPilot/ParkrobTrajplanner/EMPlanner/Longitudinal/SequentialLongitudinalOptimizer.cs`
|
||
- Modify: `ClumsyPilot/ParkrobTrajplanner/EMPlanner/Longitudinal/LongitudinalObjectiveBuilder.cs`
|
||
- Modify: `ClumsyPilot/ParkrobTrajplanner/EMPlanner/Longitudinal/LongitudinalSolutionValidator.cs`
|
||
- Modify: `ClumsyPilot/ParkrobTrajplanner/EMPlanner/Longitudinal/LongitudinalPlanningResult.cs`
|
||
- Modify: `ClumsyPilot/ParkrobTrajplanner/EMPlanner/Facade/EmPlanningService.cs`
|
||
- Test: `ClumsyPilot/tests/EMPlannerVerificationHost/LongitudinalModelChecks.cs`
|
||
- Test: `ClumsyPilot/tests/EMPlannerVerificationHost/LongitudinalIntegrationChecks.cs`
|
||
- Test: `ClumsyPilot/tests/EMPlannerVerificationHost/EmPlanningServiceChecks.cs`
|
||
|
||
**Interfaces:**
|
||
|
||
- Consumes: desired speeds and adaptive reference profile from Tasks 1 and 3.
|
||
- Produces: a nonzero progress seed from `v0 = 0` when the segment is feasible.
|
||
- Produces: exact `NoProgress` failure with no candidate/trajectory publication.
|
||
|
||
- [ ] **Step 1: Write the failing static-start regression**
|
||
|
||
Use a clear 5 m forward segment, `v0 = 0`, `a0 = 0`, desired/max speed 1.0 m/s, and full scope. Assert:
|
||
|
||
```csharp
|
||
Verification.True(result.Status == EmPlanningStatus.Success ||
|
||
result.Status == EmPlanningStatus.SuccessWithFallback, "static start succeeds");
|
||
Verification.True(result.Trajectory.Points.Any(point => point.PathS > 0.05d),
|
||
"static start makes measurable progress");
|
||
Verification.True(result.Trajectory.Points.Any(point => point.SignedLongitudinalVelocity > 0.05d),
|
||
"static start accelerates");
|
||
Verification.NearlyEqual(0d, result.Trajectory.Points[^1].SignedLongitudinalVelocity,
|
||
"full segment stops at the terminal boundary");
|
||
```
|
||
|
||
Add a direct validator fixture containing all-zero `S/U/A/J` over a nonzero full segment. Expect `NoProgress`. Add allowed zero-progress fixtures for “already within terminal tolerance” and gear-switch stop hold.
|
||
|
||
- [ ] **Step 2: Run RED against the original symptom**
|
||
|
||
```powershell
|
||
dotnet run --project ClumsyPilot/tests/EMPlannerVerificationHost/EMPlannerVerificationHost.csproj -- longitudinal-integration
|
||
```
|
||
|
||
Expected: the static-start assertion fails because the current envelope seed targets the initial zero speed.
|
||
|
||
- [ ] **Step 3: Drive the seed and objective toward the feasible reference speed**
|
||
|
||
In `CreateEnvelopeSeed`, replace the current target:
|
||
|
||
```csharp
|
||
double targetSpeed = Math.Min(input.InitialProgressSpeedMetersPerSecond, speedLimitAtS);
|
||
```
|
||
|
||
with:
|
||
|
||
```csharp
|
||
double desiredSpeed = input.Direction == TravelDirection.Forward
|
||
? configuration.DesiredForwardSpeedMetersPerSecond
|
||
: configuration.DesiredReverseSpeedMetersPerSecond;
|
||
double scheduleSpeed = input.KnotSchedule.ReferenceSpeedMetersPerSecond[index];
|
||
double targetSpeed = Math.Min(desiredSpeed, Math.Min(scheduleSpeed, speedLimitAtS));
|
||
```
|
||
|
||
In `LongitudinalObjectiveBuilder`, track the same reference rather than only the current linearization-point hard limit. Preserve acceleration, jerk, previous-trajectory, and exact-stop terms.
|
||
|
||
- [ ] **Step 4: Add the independent no-progress gate**
|
||
|
||
After physical/dynamics checks and before returning a canonical candidate, reject full-scope output when:
|
||
|
||
```csharp
|
||
bool requiresProgress = input.PathUpperBoundS >
|
||
input.Configuration.Validation.TerminalPositionToleranceMeters;
|
||
double achievedProgress = candidate.S[candidate.S.Count - 1] - candidate.S[0];
|
||
if (input.PlanningScope == EmPlanningScope.FullDirectionSegment &&
|
||
requiresProgress && achievedProgress <= input.Configuration.Validation.SpatialToleranceMeters)
|
||
{
|
||
failureReason = "NoProgress: a nonterminal full direction segment produced zero progress.";
|
||
failureStatus = EmPlanningStatus.NoProgress;
|
||
return false;
|
||
}
|
||
```
|
||
|
||
Extend the validator/result seam to return a status as well as a reason so `NoProgress` is not collapsed into generic `LongitudinalInfeasible`.
|
||
|
||
- [ ] **Step 5: Run GREEN and the service gate**
|
||
|
||
```powershell
|
||
dotnet run --project ClumsyPilot/tests/EMPlannerVerificationHost/EMPlannerVerificationHost.csproj -- longitudinal-model
|
||
dotnet run --project ClumsyPilot/tests/EMPlannerVerificationHost/EMPlannerVerificationHost.csproj -- longitudinal-integration
|
||
dotnet run --project ClumsyPilot/tests/EMPlannerVerificationHost/EMPlannerVerificationHost.csproj -- em-planning-service
|
||
```
|
||
|
||
Expected: all PASS; the explicit all-zero service fixture has `Trajectory == null` and diagnostic `NoProgress`.
|
||
|
||
- [ ] **Step 6: Commit the static-start fix**
|
||
|
||
```powershell
|
||
git add -- ClumsyPilot/ParkrobTrajplanner/EMPlanner/Longitudinal/SequentialLongitudinalOptimizer.cs ClumsyPilot/ParkrobTrajplanner/EMPlanner/Longitudinal/LongitudinalObjectiveBuilder.cs ClumsyPilot/ParkrobTrajplanner/EMPlanner/Longitudinal/LongitudinalSolutionValidator.cs ClumsyPilot/ParkrobTrajplanner/EMPlanner/Longitudinal/LongitudinalPlanningResult.cs ClumsyPilot/ParkrobTrajplanner/EMPlanner/Facade/EmPlanningService.cs ClumsyPilot/tests/EMPlannerVerificationHost/LongitudinalModelChecks.cs ClumsyPilot/tests/EMPlannerVerificationHost/LongitudinalIntegrationChecks.cs ClumsyPilot/tests/EMPlannerVerificationHost/EmPlanningServiceChecks.cs
|
||
git commit -m "fix: accelerate EM trajectories from rest"
|
||
```
|
||
|
||
---
|
||
|
||
### Task 5: Enforce terminal world pose and publication limits
|
||
|
||
**Files:**
|
||
|
||
- Modify: `ClumsyPilot/ParkrobTrajplanner/EMPlanner/Validation/EmTrajectoryValidator.cs`
|
||
- Modify: `ClumsyPilot/ParkrobTrajplanner/EMPlanner/Facade/EmPlanningService.cs`
|
||
- Modify: `ClumsyPilot/ParkrobTrajplanner/EMPlanner/Trajectory/TrajectorySampleSchedule.cs`
|
||
- Modify: `ClumsyPilot/ParkrobTrajplanner/EMPlanner/Trajectory/EmTrajectoryAssembler.cs`
|
||
- Test: `ClumsyPilot/tests/EMPlannerVerificationHost/TrajectoryChecks.cs`
|
||
- Test: `ClumsyPilot/tests/EMPlannerVerificationHost/EmPlanningServiceChecks.cs`
|
||
|
||
**Interfaces:**
|
||
|
||
- Produces: `EmTrajectoryValidator.Validate(..., Pose2D terminalPose, ...)`.
|
||
- Produces normalized yaw helper with result in `[-π, π]`.
|
||
- Produces publication sample-count gate using `MaximumPublishedSampleCount`.
|
||
- Maps pose mismatch to `EmPlanningStatus.TerminalPoseMismatch` and sample overflow to `FullSegmentResourceLimitExceeded`.
|
||
|
||
- [ ] **Step 1: Write failing position, yaw-wrap, and sample-limit tests**
|
||
|
||
Create terminal trajectories at 2.9 cm and 3.1 cm position error; create yaw cases `179°` versus `-179°`, `4.9°`, and `5.1°`. Assert only the outside cases fail. Add a trajectory whose output sampling would need one sample above `MaximumPublishedSampleCount` and assert no partial trajectory is assembled.
|
||
|
||
- [ ] **Step 2: Run RED**
|
||
|
||
```powershell
|
||
dotnet run --project ClumsyPilot/tests/EMPlannerVerificationHost/EMPlannerVerificationHost.csproj -- trajectory
|
||
```
|
||
|
||
Expected: compile failure because terminal pose is not part of validation.
|
||
|
||
- [ ] **Step 3: Add normalized terminal-pose validation**
|
||
|
||
Use the final Local G2 point as the expected world pose and validate the real terminal anchor:
|
||
|
||
```csharp
|
||
private static double NormalizeAngle(double angle)
|
||
{
|
||
while (angle > Math.PI) angle -= 2d * Math.PI;
|
||
while (angle < -Math.PI) angle += 2d * Math.PI;
|
||
return angle;
|
||
}
|
||
|
||
double dx = terminal.X - terminalPose.X;
|
||
double dy = terminal.Y - terminalPose.Y;
|
||
double positionError = Math.Sqrt(dx * dx + dy * dy);
|
||
double yawError = Math.Abs(NormalizeAngle(terminal.Yaw - terminalPose.Heading));
|
||
if (positionError > configuration.Validation.TerminalPositionToleranceMeters ||
|
||
yawError > configuration.Validation.TerminalYawToleranceRadians)
|
||
{
|
||
return Reject(EmTrajectoryValidationFailure.TerminalPoseMismatch, terminalIndex,
|
||
"TerminalPoseMismatch: position=" + Format(positionError) +
|
||
";yaw=" + Format(yawError));
|
||
}
|
||
```
|
||
|
||
Do this only for real `Goal` and `GearSwitchApproach` anchors, not rolling-safety window ends.
|
||
|
||
- [ ] **Step 4: Enforce exact publication cardinality**
|
||
|
||
Before allocating published samples, compute the required count including the exact terminal anchor and hold samples. If it exceeds `MaximumPublishedSampleCount`, return `FullSegmentResourceLimitExceeded`; do not remove the terminal anchor, increase `OutputTimeStepSeconds`, or truncate.
|
||
|
||
Keep jerk interval semantics: the assembler uses interval `i` for point `i` only while `i + 1 < N`; the terminal point stores no successor-interval jerk and the chart builder publishes only `N-1` values.
|
||
|
||
- [ ] **Step 5: Run GREEN**
|
||
|
||
```powershell
|
||
dotnet run --project ClumsyPilot/tests/EMPlannerVerificationHost/EMPlannerVerificationHost.csproj -- trajectory
|
||
dotnet run --project ClumsyPilot/tests/EMPlannerVerificationHost/EMPlannerVerificationHost.csproj -- em-planning-service
|
||
```
|
||
|
||
Expected: both PASS, including yaw wrap and boundary-outside rejection.
|
||
|
||
- [ ] **Step 6: Commit publication gates**
|
||
|
||
```powershell
|
||
git add -- ClumsyPilot/ParkrobTrajplanner/EMPlanner/Validation/EmTrajectoryValidator.cs ClumsyPilot/ParkrobTrajplanner/EMPlanner/Facade/EmPlanningService.cs ClumsyPilot/ParkrobTrajplanner/EMPlanner/Trajectory/TrajectorySampleSchedule.cs ClumsyPilot/ParkrobTrajplanner/EMPlanner/Trajectory/EmTrajectoryAssembler.cs ClumsyPilot/tests/EMPlannerVerificationHost/TrajectoryChecks.cs ClumsyPilot/tests/EMPlannerVerificationHost/EmPlanningServiceChecks.cs
|
||
git commit -m "feat: validate EM terminal world pose"
|
||
```
|
||
|
||
---
|
||
|
||
### Task 6: Make MovementTest one-shot per direction segment
|
||
|
||
**Files:**
|
||
|
||
- Modify: `ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/TrajectoryObservationContracts.cs`
|
||
- Modify: `ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/TrajectoryObservationPipeline.cs`
|
||
- Modify: `ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/MovementTest.TrajectoryObservationTest.cs`
|
||
- Modify: `ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/TrajectoryObservationStaticSnapshotBuilder.cs`
|
||
- Test: `ClumsyPilot/tests/EMPlannerVerificationHost/TrajectoryObservationSettingsChecks.cs`
|
||
- Test: `ClumsyPilot/tests/EMPlannerVerificationHost/TrajectoryObservationChecks.cs`
|
||
- Test: `ClumsyPilot/tests/EMPlannerVerificationHost/TrajectoryObservationSegmentChecks.cs`
|
||
|
||
**Interfaces:**
|
||
|
||
- Produces: `TrajectoryObservationSettings.PlanningScope`, defaulting to `FullDirectionSegment`.
|
||
- Produces: one plan attempt per active segment, reset only after a confirmed segment transition.
|
||
- Preserves: stop hold plus three correctly signed speed samples before `N -> N+1`.
|
||
|
||
- [ ] **Step 1: Write failing settings and cadence tests**
|
||
|
||
Assert the validated snapshot freezes `FullDirectionSegment`, Web defaults on, Native Painter defaults off, and `OutputTimeStepSeconds == 0.1`. Drive 20 observer ticks after one successful full plan and assert planning starts once, while observation and Web publication continue on every configured cadence. Then complete the existing stop-hold/three-sample handshake and assert exactly one new plan starts for segment N+1.
|
||
|
||
- [ ] **Step 2: Run RED**
|
||
|
||
```powershell
|
||
dotnet run --project ClumsyPilot/tests/EMPlannerVerificationHost/EMPlannerVerificationHost.csproj -- trajectory-observation
|
||
```
|
||
|
||
Expected: failures because the current controller delegates to rolling coordinator cadence.
|
||
|
||
- [ ] **Step 3: Add scope to settings and request creation**
|
||
|
||
Add:
|
||
|
||
```csharp
|
||
public EmPlanningScope PlanningScope { get; set; } = EmPlanningScope.FullDirectionSegment;
|
||
```
|
||
|
||
Copy and validate it. In `TrajectoryObservationController.StartCycle`, pass the scope into `EmPlanningRequest`. Apply the approved observer defaults in the MovementTest entry:
|
||
|
||
```csharp
|
||
public bool UseFullDirectionSegmentPlanning = true;
|
||
public double OutputTimeStepSeconds = 0.10d;
|
||
public bool EnableWebVisualization = true;
|
||
public bool EnableNativePainterVisualization = false;
|
||
```
|
||
|
||
Keep `TimeHorizonSeconds` visible only as a rolling compatibility field and label it accordingly. Do not preserve the experimental 20 s/500 m values as the mechanism for full planning.
|
||
|
||
- [ ] **Step 4: Gate one-shot planning in the controller**
|
||
|
||
Track `plannedSegmentIndex` and `planAttemptedForActiveSegment` in the controller:
|
||
|
||
```csharp
|
||
public bool ShouldStartCycle(DateTimeOffset now)
|
||
{
|
||
if (settings.PlanningScope == EmPlanningScope.FullDirectionSegment)
|
||
return !planAttemptedForActiveSegment;
|
||
return coordinator.ShouldStartCycle(now);
|
||
}
|
||
```
|
||
|
||
Set the attempt flag before launching the task so a slow solver cannot trigger duplicates. Reset it only after `TryAdvanceSegment` confirms the real transition or when a new MovementTest session constructs a new controller. A failed full plan remains failed and visible; no automatic rolling retry occurs.
|
||
|
||
- [ ] **Step 5: Keep the existing gear-switch safety state machine unchanged**
|
||
|
||
Run existing stop-hold and signed-speed tests plus the new one-shot assertions. The waiting notice must remain explicit and the active highlight must remain on segment N until all conditions pass.
|
||
|
||
- [ ] **Step 6: Run GREEN and source audit**
|
||
|
||
```powershell
|
||
dotnet run --project ClumsyPilot/tests/EMPlannerVerificationHost/EMPlannerVerificationHost.csproj -- trajectory-observation
|
||
rg -n "SendXYThSpeed|SendMotion|DriveStop|PredefinedDriveStop|AccumulateSpeed|SetGear|SetBrake" ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest
|
||
```
|
||
|
||
Expected: `PASS trajectory-observation`; `rg` returns no actuator call in observer runtime sources.
|
||
|
||
- [ ] **Step 7: Commit one-shot observation**
|
||
|
||
```powershell
|
||
git add -- ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/TrajectoryObservationContracts.cs ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/TrajectoryObservationPipeline.cs ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/MovementTest.TrajectoryObservationTest.cs ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/TrajectoryObservationStaticSnapshotBuilder.cs ClumsyPilot/tests/EMPlannerVerificationHost/TrajectoryObservationSettingsChecks.cs ClumsyPilot/tests/EMPlannerVerificationHost/TrajectoryObservationChecks.cs ClumsyPilot/tests/EMPlannerVerificationHost/TrajectoryObservationSegmentChecks.cs
|
||
git commit -m "feat: observe one full EM direction segment"
|
||
```
|
||
|
||
---
|
||
|
||
### Task 7: Publish unambiguous path, chart, and boundary semantics
|
||
|
||
**Files:**
|
||
|
||
- Modify: `ClumsyPilot/TrajectoryPlanningVisualization/Contracts/VisualizationCharts.cs`
|
||
- Modify: `ClumsyPilot/TrajectoryPlanningVisualization/Contracts/VisualizationGeometry.cs`
|
||
- Modify: `ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/TrajectoryObservationStaticSnapshotBuilder.cs`
|
||
- Modify: `ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/TrajectoryObservationDynamicSnapshotBuilder.cs`
|
||
- Modify: `ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/TrajectoryObservationKinematicChartBuilder.cs`
|
||
- Modify: `ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/MovementTest.TrajectoryObservationTest.cs`
|
||
- Test: `ClumsyPilot/tests/EMPlannerVerificationHost/TrajectoryObservationVisualizationChecks.cs`
|
||
- Test: `ClumsyPilot/tests/TrajectoryPlanningVisualizationVerificationHost/ContractChecks.cs`
|
||
- Test: `ClumsyPilot/tests/TrajectoryPlanningVisualizationVerificationHost/SampleSnapshotFactory.cs`
|
||
|
||
**Interfaces:**
|
||
|
||
- Produces: `VisualizationChartAnnotation` for vertical `s_end` lines and labeled points.
|
||
- Produces marker kinds `plan-start`, `gear-switch-end`, and `final-goal`.
|
||
- Produces distinct polyline kinds `coarse`, `local-g2`, `active-segment`, `previous`, and `current` with no duplicate `current-horizon` in full mode.
|
||
- Chart axes remain `ReferenceS (m)`, `PathS (m)`, `t (s)`, `l (m)`, `v (m/s)`, `a (m/s²)`, `j (m/s³)`, `κ (m⁻¹)`, and `ω (rad/s)`.
|
||
|
||
- [ ] **Step 1: Write failing snapshot semantics tests**
|
||
|
||
Assert that a full-mode snapshot contains exactly one current trajectory polyline, contains both coarse and Local G2 paths, has distinct `s_end` and vehicle/plan-start markers, and uses the correct terminal kind for gear switch versus final goal.
|
||
|
||
For charts assert:
|
||
|
||
```csharp
|
||
Verification.Equal("ReferenceS (m)", ls.XAxisLabel, "LS uses shared reference S");
|
||
Verification.Equal("PathS (m)", st.YAxisLabel, "ST uses actual path arc length");
|
||
Verification.Equal(trajectory.Points.Count - 1, jerk.Series[0].Points.Count,
|
||
"jerk has N-1 interval samples");
|
||
Verification.True(st.Annotations.Any(a => a.Kind == "s-end"), "ST marks s_end");
|
||
Verification.True(curvatureS.Series[0].Points.Any(p => Math.Abs(p.Y) > 1e-6),
|
||
"curvature-distance uses vehicle curvature data");
|
||
```
|
||
|
||
Ensure the curvature hard-limit series uses `VehicleParameters.MaximumCurvaturePerMeter`, not `MaximumCurvatureRatePerMeterPerSecond`.
|
||
|
||
- [ ] **Step 2: Run RED**
|
||
|
||
```powershell
|
||
dotnet run --project ClumsyPilot/tests/EMPlannerVerificationHost/EMPlannerVerificationHost.csproj -- trajectory-observation
|
||
dotnet run --project ClumsyPilot/tests/TrajectoryPlanningVisualizationVerificationHost/TrajectoryPlanningVisualizationVerificationHost.csproj
|
||
```
|
||
|
||
Expected: failures for duplicate current horizon, missing annotations, and incorrect curvature limit.
|
||
|
||
- [ ] **Step 3: Add chart annotations without breaking old consumers**
|
||
|
||
Add:
|
||
|
||
```csharp
|
||
public sealed class VisualizationChartAnnotation
|
||
{
|
||
public VisualizationChartAnnotation(string id, string kind, string labelChinese,
|
||
double x, double? y = null);
|
||
public string Id { get; }
|
||
public string Kind { get; }
|
||
public string LabelChinese { get; }
|
||
public double X { get; }
|
||
public double? Y { get; }
|
||
}
|
||
```
|
||
|
||
Extend `VisualizationChart` with immutable `Annotations`. Keep the old constructor as a delegating overload with an empty annotation list for backward compatibility.
|
||
|
||
- [ ] **Step 4: Correct snapshot builders**
|
||
|
||
In full mode, remove `current-horizon`; in rolling mode publish it only if it is a genuinely different subset. Add current trajectory once above static paths. Add `s_end` annotations to world, LS, and ST. Label a direction boundary as `换向点 N / s_end` only for gear switches; use a distinct final-goal label and marker.
|
||
|
||
Extend `TrajectoryObservationDynamicSnapshotBuilder.Build(...)` with an immutable `VehicleParameters vehicle` argument. Pass `bootstrap.Vehicle` from the MovementTest publication callback into the dynamic builder, then into the kinematic chart builder, so the curvature charts use the real vehicle curvature limit. Change yaw-rate axis text to `ω (rad/s)`.
|
||
|
||
- [ ] **Step 5: Run GREEN**
|
||
|
||
Run both commands from Step 2. Expected: both hosts PASS.
|
||
|
||
- [ ] **Step 6: Commit observation semantics**
|
||
|
||
```powershell
|
||
git add -- ClumsyPilot/TrajectoryPlanningVisualization/Contracts/VisualizationCharts.cs ClumsyPilot/TrajectoryPlanningVisualization/Contracts/VisualizationGeometry.cs ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/TrajectoryObservationStaticSnapshotBuilder.cs ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/TrajectoryObservationDynamicSnapshotBuilder.cs ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/TrajectoryObservationKinematicChartBuilder.cs ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/MovementTest.TrajectoryObservationTest.cs ClumsyPilot/tests/EMPlannerVerificationHost/TrajectoryObservationVisualizationChecks.cs ClumsyPilot/tests/TrajectoryPlanningVisualizationVerificationHost/ContractChecks.cs ClumsyPilot/tests/TrajectoryPlanningVisualizationVerificationHost/SampleSnapshotFactory.cs
|
||
git commit -m "fix: publish distinct EM observation semantics"
|
||
```
|
||
|
||
---
|
||
|
||
### Task 8: Add real DOM/SVG tests and repair the existing Web page
|
||
|
||
**Files:**
|
||
|
||
- Create: `ClumsyPilot/tests/TrajectoryPlanningVisualizationWebDom/package.json`
|
||
- Create: `ClumsyPilot/tests/TrajectoryPlanningVisualizationWebDom/package-lock.json`
|
||
- Create: `ClumsyPilot/tests/TrajectoryPlanningVisualizationWebDom/dashboard.dom.test.mjs`
|
||
- Modify: `ClumsyPilot/TrajectoryPlanningVisualization/Web/index.html`
|
||
- Modify: `ClumsyPilot/TrajectoryPlanningVisualization/Web/app.css`
|
||
- Modify: `ClumsyPilot/TrajectoryPlanningVisualization/Web/app.js`
|
||
- Modify: `ClumsyPilot/tests/TrajectoryPlanningVisualizationVerificationHost/WebAssetChecks.cs`
|
||
|
||
**Interfaces:**
|
||
|
||
- Test-only dependency: exact `jsdom` 29.1.1; no npm package is copied into the production plugin.
|
||
- Test hook: when `window.__TRAJECTORY_VISUALIZATION_TEST__ === true`, `app.js` exposes immutable-input render hooks and does not start fetch/SSE.
|
||
- Produces actual DOM assertions for tab visibility, y ticks, units, paths, markers, empty state, and scientific line classes.
|
||
|
||
- [ ] **Step 1: Add the test package and failing DOM tests**
|
||
|
||
Create `package.json`:
|
||
|
||
```json
|
||
{
|
||
"name": "trajectory-planning-visualization-web-dom-tests",
|
||
"private": true,
|
||
"type": "module",
|
||
"scripts": { "test": "node --test dashboard.dom.test.mjs" },
|
||
"devDependencies": { "jsdom": "29.1.1" },
|
||
"engines": { "node": ">=24" }
|
||
}
|
||
```
|
||
|
||
Run `npm install --package-lock-only` in that directory to create the lock file. In `dashboard.dom.test.mjs`, load the real `index.html` and `app.js`, create `JSDOM` with `runScripts: "outside-only"` and `pretendToBeVisual: true`, stub Canvas `getContext`, set fixed element rectangles, set the test flag, and evaluate the real script.
|
||
|
||
Test these behaviors with actual DOM queries:
|
||
|
||
```javascript
|
||
assert.equal(document.querySelector("#overview").hidden, false);
|
||
click(document.querySelector('button[data-tab="ls-st"]'));
|
||
assert.equal(document.querySelector("#overview").hidden, true);
|
||
assert.equal(document.querySelector("#ls-st").hidden, false);
|
||
assert.equal(document.querySelector("#kinematics").hidden, true);
|
||
assert.ok(document.querySelectorAll("#st .axis-tick-y").length >= 4);
|
||
assert.equal(document.querySelector("#st .axis-label-y").textContent, "PathS (m)");
|
||
assert.ok(document.querySelector("#world-overlay .world-current"));
|
||
assert.ok(document.querySelector("#world-overlay .marker-vehicle"));
|
||
```
|
||
|
||
Add an empty-snapshot test expecting a visible Chinese reason instead of an empty world canvas.
|
||
|
||
- [ ] **Step 2: Run RED**
|
||
|
||
```powershell
|
||
npm test --prefix ClumsyPilot/tests/TrajectoryPlanningVisualizationWebDom
|
||
```
|
||
|
||
Expected: failures because hidden sections are overridden by CSS, y tick labels are absent, and semantic classes/empty messages are missing.
|
||
|
||
- [ ] **Step 3: Fix tab visibility and empty-state behavior**
|
||
|
||
Add the high-specificity CSS rule before panel grids:
|
||
|
||
```css
|
||
main > section[hidden] { display: none !important; }
|
||
#ls-st:not([hidden]), #kinematics:not([hidden]) {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(360px, 1fr));
|
||
gap: 18px;
|
||
}
|
||
```
|
||
|
||
In `renderWorld`, count valid static/dynamic path points. If none exist, add a `.world-empty-state` text element naming the missing source. Do not fabricate fallback points.
|
||
|
||
- [ ] **Step 4: Render y ticks, units, and stable nonzero domains**
|
||
|
||
Split tick generation into `niceDomain`, `formatTick`, and `renderAxes`. Create both x and y text nodes:
|
||
|
||
```javascript
|
||
const yt = svg("text", {
|
||
x: left - 7, y, class: "axis-tick axis-tick-y",
|
||
"text-anchor": "end", "dominant-baseline": "middle"
|
||
});
|
||
yt.textContent = formatTick(domain.y1 - tick * (domain.y1 - domain.y0) / 4);
|
||
```
|
||
|
||
Use `.axis-label-x` and `.axis-label-y` classes. For constant data, pad around the actual constant with `max(abs(value)*0.05, unitFloor)`; never force a nonzero constant onto a zero-centered 1-unit range.
|
||
|
||
- [ ] **Step 5: Apply semantic path order and thin paper style**
|
||
|
||
Dispatch line class by `kind`, not by static/dynamic origin. Draw in this exact order: coarse, Local G2, direction segments, previous, current, annotations/vehicle. Use:
|
||
|
||
```css
|
||
.world-coarse { stroke: #9aa1a8; stroke-opacity: .48; stroke-dasharray: 5 4; stroke-width: .85; }
|
||
.world-local-g2 { stroke: #4f5963; stroke-width: .95; }
|
||
.world-segment-active { stroke: #5d91bd; stroke-opacity: .62; stroke-width: 1.0; }
|
||
.world-previous { stroke: #8d959d; stroke-dasharray: 5 4; stroke-width: .85; }
|
||
.world-current { stroke: #1769aa; stroke-width: 1.15; }
|
||
```
|
||
|
||
Render vehicle pose as a small oriented outline plus thin heading line using `frame.vehiclePose.heading`; render plan start, gear-switch `s_end`, and final goal with different marker classes. Do not use a large filled triangle.
|
||
|
||
- [ ] **Step 6: Expose safe test hooks and keep production boot unchanged**
|
||
|
||
At the end of `app.js`:
|
||
|
||
```javascript
|
||
if (window.__TRAJECTORY_VISUALIZATION_TEST__ === true) {
|
||
window.__trajectoryVisualizationTestHooks = Object.freeze({
|
||
setBootstrap(snapshot) { state.staticSnapshot = snapshot; },
|
||
receiveFrame,
|
||
renderActiveTab,
|
||
installTabs
|
||
});
|
||
installTabs();
|
||
} else {
|
||
boot();
|
||
}
|
||
```
|
||
|
||
Hooks accept snapshots but never expose mutable internal state. Production still performs tokenized fetch and SSE exactly once.
|
||
|
||
- [ ] **Step 7: Run GREEN plus embedded-asset host**
|
||
|
||
```powershell
|
||
npm test --prefix ClumsyPilot/tests/TrajectoryPlanningVisualizationWebDom
|
||
dotnet run --project ClumsyPilot/tests/TrajectoryPlanningVisualizationVerificationHost/TrajectoryPlanningVisualizationVerificationHost.csproj
|
||
```
|
||
|
||
Expected: Node tests report all tests passed; .NET host prints `PASS trajectory-planning-visualization`.
|
||
|
||
- [ ] **Step 8: Commit Web repair and DOM tests**
|
||
|
||
```powershell
|
||
git add -- ClumsyPilot/tests/TrajectoryPlanningVisualizationWebDom/package.json ClumsyPilot/tests/TrajectoryPlanningVisualizationWebDom/package-lock.json ClumsyPilot/tests/TrajectoryPlanningVisualizationWebDom/dashboard.dom.test.mjs ClumsyPilot/TrajectoryPlanningVisualization/Web/index.html ClumsyPilot/TrajectoryPlanningVisualization/Web/app.css ClumsyPilot/TrajectoryPlanningVisualization/Web/app.js ClumsyPilot/tests/TrajectoryPlanningVisualizationVerificationHost/WebAssetChecks.cs
|
||
git commit -m "fix: repair EM observation web charts"
|
||
```
|
||
|
||
---
|
||
|
||
### Task 9: Add per-chart viewport zoom without changing data
|
||
|
||
**Files:**
|
||
|
||
- Modify: `ClumsyPilot/TrajectoryPlanningVisualization/Web/index.html`
|
||
- Modify: `ClumsyPilot/TrajectoryPlanningVisualization/Web/app.css`
|
||
- Modify: `ClumsyPilot/TrajectoryPlanningVisualization/Web/app.js`
|
||
- Modify: `ClumsyPilot/tests/TrajectoryPlanningVisualizationWebDom/dashboard.dom.test.mjs`
|
||
|
||
**Interfaces:**
|
||
|
||
- Produces per-chart `chartViewports[id]` containing only visible x/y domains.
|
||
- Produces actions `box zoom`, `wheel zoom`, `reset`, and `fullscreen`.
|
||
- Consumes immutable chart snapshot arrays and never writes to them.
|
||
|
||
- [ ] **Step 1: Write failing interaction tests**
|
||
|
||
Freeze the input chart and point arrays, dispatch wheel and pointer events, and assert the rendered axis tick labels and path coordinates change while the serialized input remains byte-for-byte identical. Click reset and assert the original tick domain returns. Click fullscreen and assert only the selected `.chart` receives the fullscreen class/API request.
|
||
|
||
- [ ] **Step 2: Run RED**
|
||
|
||
```powershell
|
||
npm test --prefix ClumsyPilot/tests/TrajectoryPlanningVisualizationWebDom
|
||
```
|
||
|
||
Expected: zoom controls and viewport state are missing.
|
||
|
||
- [ ] **Step 3: Implement viewport-only controls**
|
||
|
||
Add a compact `.chart-tools` toolbar in each chart host. Store domains as copied numbers:
|
||
|
||
```javascript
|
||
state.chartViewports[id] = { x0, x1, y0, y1 };
|
||
```
|
||
|
||
Wheel zoom uses the pointer’s data coordinate as its anchor. Box zoom ignores drags shorter than 4 CSS pixels. Reset deletes only `state.chartViewports[id]`. Fullscreen uses `requestFullscreen()` with a CSS fallback for the jsdom/test environment. SSE frame arrival re-renders using the active viewport if it is still finite; it does not alter the snapshot.
|
||
|
||
- [ ] **Step 4: Run GREEN**
|
||
|
||
```powershell
|
||
npm test --prefix ClumsyPilot/tests/TrajectoryPlanningVisualizationWebDom
|
||
dotnet run --project ClumsyPilot/tests/TrajectoryPlanningVisualizationVerificationHost/TrajectoryPlanningVisualizationVerificationHost.csproj
|
||
```
|
||
|
||
Expected: all DOM tests and embedded-asset checks PASS.
|
||
|
||
- [ ] **Step 5: Commit chart navigation**
|
||
|
||
```powershell
|
||
git add -- ClumsyPilot/TrajectoryPlanningVisualization/Web/index.html ClumsyPilot/TrajectoryPlanningVisualization/Web/app.css ClumsyPilot/TrajectoryPlanningVisualization/Web/app.js ClumsyPilot/tests/TrajectoryPlanningVisualizationWebDom/dashboard.dom.test.mjs
|
||
git commit -m "feat: add observation chart viewport zoom"
|
||
```
|
||
|
||
---
|
||
|
||
### Task 10: Correct Native Painter geometry and semantics
|
||
|
||
**Files:**
|
||
|
||
- Modify: `ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/TrajectoryObservationPresentation.cs`
|
||
- Test: `ClumsyPilot/tests/EMPlannerVerificationHost/TrajectoryObservationChecks.cs`
|
||
- Test: `ClumsyPilot/tests/EMPlannerVerificationHost/TrajectoryObservationVisualizationChecks.cs`
|
||
|
||
**Interfaces:**
|
||
|
||
- Consumes the same coarse/Local G2/current/previous/s_end semantics from Task 7.
|
||
- Produces small vehicle/pose outlines and thin heading rays with `endArrow: false`.
|
||
- LS x-axis is `ReferenceS (m)`; ST is `t (s)` versus `PathS (m)`; all world coordinates remain x/y equal-scale millimetres.
|
||
|
||
- [ ] **Step 1: Write failing source/geometry checks**
|
||
|
||
Add checks that `DrawPose` no longer calls `endArrow: true`, that pose outlines contain four rotated vehicle corners, and that world paths use one millimetres-per-metre factor for both x and y. Assert LS/ST labels and `s_end`/gear-switch labels match the Web semantics.
|
||
|
||
- [ ] **Step 2: Run RED**
|
||
|
||
```powershell
|
||
dotnet run --project ClumsyPilot/tests/EMPlannerVerificationHost/EMPlannerVerificationHost.csproj -- trajectory-observation
|
||
```
|
||
|
||
Expected: failure on the current large arrow and old `path-S`/`T-S` labels.
|
||
|
||
- [ ] **Step 3: Replace the black triangle with scaled pose geometry**
|
||
|
||
Draw a four-corner outline based on the observed vehicle length/width and a thin heading ray no longer than half the vehicle length:
|
||
|
||
```csharp
|
||
worldPainter.DrawLine(color, x, y, headingX, headingY, endArrow: false, width: 1);
|
||
```
|
||
|
||
Use thin line widths for coarse, Local G2, current, and previous paths. Mark current vehicle, plan start, gear-switch `s_end`, and final goal distinctly. Keep all existing Painter windows and lifecycle cleanup; do not add new Painter layers.
|
||
|
||
- [ ] **Step 4: Correct Painter LS/ST axes**
|
||
|
||
Use shared world `ReferenceS` for LS horizontal values and actual trajectory `PathS` for ST vertical values. Calculate plot scales from data bounds with the same x/y unit conversion. Show the explicit waiting notice during gear handoff.
|
||
|
||
- [ ] **Step 5: Run GREEN**
|
||
|
||
```powershell
|
||
dotnet run --project ClumsyPilot/tests/EMPlannerVerificationHost/EMPlannerVerificationHost.csproj -- trajectory-observation
|
||
```
|
||
|
||
Expected: `PASS trajectory-observation`.
|
||
|
||
- [ ] **Step 6: Commit Painter correctness**
|
||
|
||
```powershell
|
||
git add -- ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/TrajectoryObservationPresentation.cs ClumsyPilot/tests/EMPlannerVerificationHost/TrajectoryObservationChecks.cs ClumsyPilot/tests/EMPlannerVerificationHost/TrajectoryObservationVisualizationChecks.cs
|
||
git commit -m "fix: correct EM observation painter geometry"
|
||
```
|
||
|
||
---
|
||
|
||
### Task 11: Update operator documentation and run complete acceptance
|
||
|
||
**Files:**
|
||
|
||
- Modify: `ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/README.md`
|
||
- Modify: `ClumsyPilot/ParkrobTrajplanner/EMPlanner/README.md`
|
||
- Modify: `ClumsyPilot/TrajectoryPlanningVisualization/README.md`
|
||
- Create after safe execution: `docs/superpowers/handoffs/em-observation-web/phase-08-vehicle.md`
|
||
|
||
**Interfaces:**
|
||
|
||
- Documents the full-versus-rolling switch, derived `s_end/T_end`, 1.0/0.5 m/s limits, terminal pose tolerances, chart units, zoom, and `OBSERVE_ONLY` safety boundary.
|
||
- Produces fresh automated evidence and either a passed or explicitly pending/blocked vehicle checklist.
|
||
|
||
- [ ] **Step 1: Update the three READMEs**
|
||
|
||
Document these exact operator facts:
|
||
|
||
- `FullDirectionSegment` is the MovementTest default and performs one optimization per active direction segment.
|
||
- `DistanceHorizonMeters` and `TimeHorizonSeconds` apply only to `RollingHorizon` truncation.
|
||
- `s_end` comes from actual Local G2 `PathS`; `T_end` is derived from feasible acceleration/cruise/stopping behavior.
|
||
- Forward desired/max is 1.0 m/s; reverse desired/max is 0.5 m/s.
|
||
- Successful real boundaries require stop plus 3 cm position and 5° normalized yaw tolerances.
|
||
- Web is primary, Native Painter is optional correctness/audit output.
|
||
- Each chart’s axes/units, the `N-1` jerk rule, semantic path line styles, boundary markers, and zoom controls.
|
||
- Browser closure does not stop observation; MovementTest stop reclaims HTTP/SSE/port/Painter.
|
||
- No part of MovementTest writes chassis, steering, brake, motor, or gear commands.
|
||
|
||
- [ ] **Step 2: Run all fresh automated verification**
|
||
|
||
```powershell
|
||
npm test --prefix ClumsyPilot/tests/TrajectoryPlanningVisualizationWebDom
|
||
dotnet run --project ClumsyPilot/tests/TrajectoryPlanningVisualizationVerificationHost/TrajectoryPlanningVisualizationVerificationHost.csproj
|
||
dotnet run --project ClumsyPilot/tests/EMPlannerVerificationHost/EMPlannerVerificationHost.csproj -- trajectory-observation
|
||
dotnet run --project ClumsyPilot/tests/EMPlannerVerificationHost/EMPlannerVerificationHost.csproj -- em-all
|
||
dotnet build ClumsyPilot/ClumsyPilot.csproj -p:ExcludeLegacyAutoAvoidance=true
|
||
rg -n "SendXYThSpeed|SendMotion|DriveStop|PredefinedDriveStop|AccumulateSpeed|SetGear|SetBrake" ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest
|
||
git diff --check
|
||
```
|
||
|
||
Expected: Node tests pass; both visualization and observation hosts print PASS; every `em-all` component prints PASS; build exits 0; actuator audit has no observer-runtime matches; `git diff --check` reports no new whitespace errors. Record pre-existing warnings separately.
|
||
|
||
- [ ] **Step 3: Run deterministic local Web smoke**
|
||
|
||
```powershell
|
||
dotnet run --project ClumsyPilot/tests/TrajectoryPlanningVisualizationVerificationHost/TrajectoryPlanningVisualizationVerificationHost.csproj -- --smoke-seconds 60
|
||
```
|
||
|
||
Open the printed tokenized URL. Verify the four tabs, nonblank world overview, nonhorizontal increasing ST for the moving fixture, y ticks/units, thin path comparison, `s_end`/gear/final markers, vehicle pose, chart zoom/reset/fullscreen, stale state, and port release. This smoke uses synthetic snapshots only.
|
||
|
||
- [ ] **Step 4: Run the supervised vehicle checklist only when safe**
|
||
|
||
In a supervised safe environment, keep MovementTest read-only and verify:
|
||
|
||
1. Web enabled, Native Painter disabled by default, and UI/console/page show `OBSERVE_ONLY`.
|
||
2. The current full direction segment produces complete LS/ST; static start accelerates, reaches feasible cruise speed, begins braking from the envelope, and stops at the real boundary.
|
||
3. ST `PathS` increases; jerk has `N-1` intervals and no terminal successor or false `JerkLimitExceeded`.
|
||
4. World handoff displays `DeltaPosition`, shared-direction `DeltaReferenceS`, `DeltaV`, and `DeltaA`, never a direct comparison of unrelated local PathS.
|
||
5. At a true reversal, N remains highlighted until continuous stop hold and three correctly signed speed samples pass; waiting text is explicit.
|
||
6. Coarse, Local G2, current trajectory, vehicle pose, `s_end`, gear switch, and final goal are visually distinct.
|
||
7. Closing/slow-loading the browser does not stop observation.
|
||
8. Stopping MovementTest releases HTTP/SSE, port, and any Painter; source/log audit shows no hardware writes.
|
||
|
||
If any item cannot be executed safely, record it as not run. Do not claim overall vehicle acceptance.
|
||
|
||
- [ ] **Step 5: Write the vehicle handoff from fresh evidence**
|
||
|
||
If every vehicle item passes, create `phase-08-vehicle.md` with status “完成”, environment prerequisites, execution time, per-item results, logs/screenshots, warnings, and conclusion. If a regression appears, create the same file with status “阻塞”, the first valid failure, exact reproduction, and safety impact; do not expand the fix in the vehicle session.
|
||
|
||
- [ ] **Step 6: Commit docs and, only when available, vehicle evidence separately**
|
||
|
||
```powershell
|
||
git add -- ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/README.md ClumsyPilot/ParkrobTrajplanner/EMPlanner/README.md ClumsyPilot/TrajectoryPlanningVisualization/README.md
|
||
git commit -m "docs: explain full-direction EM observation"
|
||
```
|
||
|
||
After actual supervised acceptance only:
|
||
|
||
```powershell
|
||
git add -- docs/superpowers/handoffs/em-observation-web/phase-08-vehicle.md
|
||
git commit -m "docs: record EM visualization vehicle acceptance"
|
||
```
|
||
|
||
Do not create or commit the vehicle handoff as “完成” from automated or synthetic evidence alone.
|