11 KiB
EM observation MovementTest final-fix report
Date: 2026-08-04
Reviewed base/head: d0e673b
Scope: final-review fixes for the observe-only TrajectoryObservationMovementTest
Outcome
All final-review findings were addressed without adding any chassis, motor, steering, brake, wheel, adapter, or geometric-controller write call. The runtime hardware access remains limited to:
DetourInterface.getCartLocation()BasicPilotBase.Chassis.GetCarSpeed(true)
The dirty worktree contained extensive unrelated changes before this fix wave. They were not edited, staged, or reverted as part of this work.
Modified files
ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/MovementTest.TrajectoryObservationTest.cs- Added the four public editable vehicle fields with Task-1 defaults.
- Copies all vehicle fields into a validated settings snapshot before the worker starts.
- Replaced the per-cycle planner
awaitwith a non-blocking observation tick coordinator. - Uses the freshly captured state timestamp for planning and observation.
- Draws and session-guard logs on every configured observer tick while planning is in flight.
- Clears all three painter layers for a current-session runtime fault under
SessionSync. - Preserves the intentional bootstrap-failure diagnostic view.
- Passes the exact GearSwitch wait state to the world presentation without changing segment
0.
ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/TrajectoryObservationContracts.cs- Added
CreateValidatedSnapshot()to copy and validate all map, cadence, and vehicle settings.
- Added
ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/TrajectoryObservationPipeline.cs- Added
TrajectoryObservationLoop/TrajectoryObservationLoopTickfor one asynchronous in-flight planning cycle plus non-blocking observer ticks. - Added a Painter-free session-end decision model.
- Added a pure GearSwitch runtime-state model containing the exact operator notice.
- Added
ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/TrajectoryObservationPresentation.cs- Draws the exact GearSwitch wait notice in the existing World layer.
ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/README.md- Documents all four vehicle inputs, units, defaults, snapshot behavior, asynchronous cadence, and fault/bootstrap cleanup behavior.
ClumsyPilot/tests/EMPlannerVerificationHost/TrajectoryObservationChecks.cs- Added delayed rolling-planner cadence, lifecycle cleanup, GearSwitch presentation, vehicle defaults/snapshot, full settings validation, fresh state-time, and valid non-empty obstacle-source coverage.
.superpowers/sdd/final-fix-report.md- This evidence and self-review report.
Finding-by-finding resolution
1. Planner latency no longer blocks observation cadence
TrajectoryObservationLoop.Tick(...) polls a completed planning task but never waits for an incomplete one. It permits at most one planner task in flight, observes the controller's atomically published trajectory using the current tick's state and timestamp, and returns a tick record for drawing and logging. The host calls it after every configured delay, draws all layers, and invokes LogIfCurrent(...) for every tick.
The deterministic regression first establishes an existing published trajectory, starts a second planning call that is held by ManualResetEventSlim, and proves that a later observer tick:
- returns before the planner is released;
- retains the existing published trajectory;
- uses the later state sequence and timestamp;
- is eligible for session-guarded logging;
- consumes the planning result after release without reusing the pre-plan state/time.
There is no planner continuation that logs results. A result is consumed only by a tick after its cancellation check, and all host drawing/logging remains guarded by the active session ID.
2. Runtime faults clear stale painter layers
ClearAndLogRuntimeFaultIfCurrent(...) acquires SessionSync, verifies the session ID, clears World/LS/ST together through Presentation.ClearAll(), and then reports the fault. The pure lifecycle model specifies:
- bootstrap failure: preserve the diagnostic view;
- runtime fault: clear all layers;
- cancellation: clear all layers.
Bootstrap failure still follows the normal successful-task completion path after drawing its diagnostic world view, so the fault cleanup path does not erase it.
3. Vehicle inputs are editable, validated, and frozen
The MovementTest now exposes:
| Field | Default |
|---|---|
VehicleLengthMeters |
0.80d |
VehicleWidthMeters |
0.60d |
SafetyMarginMeters |
0.05d |
MaximumCurvaturePerMeter |
1d / 1.20d |
All four are copied into TrajectoryObservationSettings before CreateValidatedSnapshot() validates and copies every setting. Tests mutate the original settings after snapshot creation and prove that the bootstrap vehicle retains the configured values. Validation coverage includes map padding, resolution, replan period, observer period, length, width, safety margin, and maximum curvature.
4. GearSwitch wait state is painted
TrajectoryObservationRuntimeState.Create(...) enters the wait state only when a GearSwitch trajectory reaches its final TimeFromStart. Its WorldNotice is exactly:
等待真实档位/方向确认;观察模式不会推进下一方向段
The host passes that state to DrawWorld(...), which draws it in TrajectoryObserver.World. The controller continues to plan and observe segment 0; no direction-change action or segment advancement was added.
5. Coverage additions
The verification host now proves that a valid non-empty obstacle list creates exactly one required trajectory-observer-manual source, preserves the positive snapshot version, projects as Applied, and retains its geometry. The existing forbidden-call audit remains intact.
RED/GREEN evidence
Command used for each focused cycle:
dotnet run --project ClumsyPilot/tests/EMPlannerVerificationHost/EMPlannerVerificationHost.csproj -- trajectory-observation
| Regression | RED evidence | GREEN evidence |
|---|---|---|
| Delayed planner cadence | Exit 1; CS0246 for missing TrajectoryObservationLoop and TrajectoryObservationLoopTick. |
Exit 0; PASS trajectory-observation. The strengthened version delays the second rolling plan and observes the existing first publication. |
| Every-tick logging signal | Exit 1; CS1061 for missing TrajectoryObservationLoopTick.ShouldLog. |
Exit 0; PASS trajectory-observation. |
| Session cleanup decisions | Exit 1; missing TrajectoryObservationSessionLifecycle and TrajectoryObservationSessionEndReason. |
Exit 0; PASS trajectory-observation. |
| Vehicle input/snapshot contract | Exit 1; missing four MovementTest fields and CreateValidatedSnapshot(); the first draft also exposed an inappropriate direct MovementTest/ClumsyCore test dependency, so the default-field check was corrected to the existing UTF-8 source seam before implementation. |
Exit 0; PASS trajectory-observation. Invalid-setting and valid-obstacle checks run in the same focused suite. |
| GearSwitch world state | Exit 1; CS0246/missing TrajectoryObservationRuntimeState. |
Exit 0; PASS trajectory-observation. |
| Fresh observation timestamp | Exit 1; assertion observer host uses the fresh state snapshot time for each observation tick was false. |
Exit 0; PASS trajectory-observation. |
Final verification evidence
Focused trajectory observation
Command:
dotnet run --project ClumsyPilot/tests/EMPlannerVerificationHost/EMPlannerVerificationHost.csproj -- trajectory-observation
Result: exit 0, PASS trajectory-observation.
EM all-up suite
Command:
dotnet run --project ClumsyPilot/tests/EMPlannerVerificationHost/EMPlannerVerificationHost.csproj -- em-all
Result: exit 0. Passed foundation, segmentation, frenet, corridor, optimization, OSQP solve/loader, lateral model/integration/real OSQP, longitudinal model/integration, trajectory, EM planning service, coordinator, executor, plugin packaging, trajectory observation, rolling end-to-end, and rolling execution tail.
Project build
Command:
dotnet build ClumsyPilot/ClumsyPilot.csproj --no-restore
Result: exit 0, 0 warnings, 0 errors.
Whitespace check
Command:
git diff --check
Result: exit 0. Git emitted only line-ending conversion warnings for existing and scoped working-copy files; it reported no whitespace errors.
Forbidden-call audit
Command pattern:
SendXYThSpeed|SendMotion|SendTh|AccumulateSpeed|ComputeWheelsGeometrically|DriveStop|PredefinedDriveStop
Scope: ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/**/*.cs
Result: exit 0 from the audit wrapper: PASS forbidden-call audit: no enumerated actuator write calls found.
A second scoped method-name audit for Brake|Steer|Wheel|Motor|Adapter invocations also passed with no matches. A live-access inspection found only the two approved read paths listed at the start of this report.
Remaining limitations
- The verification host deliberately does not instantiate
Painteror connect to a live vehicle. GearSwitch presentation and lifecycle cleanup are covered through pure decision/state models plus a source seam proving the World painter consumesruntimeState.WorldNotice. Actual colors/placement and MDCS UI rendering still require an operator environment. - The forbidden-call test exhaustively covers the established explicit token list inside the observation runtime directory. The additional brake/steer/wheel/motor/adapter audit is a scoped method-name heuristic. Proving an exhaustive list of every possible external adapter write API would require inventorying unrelated proprietary hardware assemblies/source outside this feature's authorized scope; this report does not guess at names that are not present in the repository sources.
- Some fresh
dotnet runbuilds emitted two pre-existing obsolete-API warnings inMovementTests.TireFollowing.csandTireFollowing.cs. The required final project build completed with 0 warnings and 0 errors; no unrelated warning source was changed.
Self-review
- Confirmed there is no
await controller.StartCycle(...)in the host loop. - Confirmed each tick reads one fresh vehicle state, uses its capture time, observes, draws, and session-guard logs.
- Confirmed only one planning task can be in flight and stale continuation logging does not exist.
- Confirmed current-session runtime fault cleanup and cancellation cleanup call
ClearAll()while the session lock is held. - Confirmed bootstrap failure returns normally after its diagnostic draw and is not cleared by
Finish(...). - Confirmed GearSwitch state is drawn in World and segment index remains
0. - Confirmed all vehicle fields match Task-1 defaults, are copied before validation, and are included in the settings snapshot.
- Confirmed documentation lists units/defaults and explains snapshot, cadence, and cleanup behavior.
- Confirmed the runtime source introduces no new hardware write reference and retains only the approved live reads.
- Confirmed unrelated dirty-worktree files remain outside the scoped diff and staging list.