335 lines
13 KiB
Markdown
335 lines
13 KiB
Markdown
# EM Observation Diagnostics 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:** Make every EM planning-cycle failure visible in the host terminal and the World/L-S/S-T observation canvases without changing observation-only safety behavior.
|
|
|
|
**Architecture:** Add a pure diagnostic formatter that keeps `EmPlanningStatus` and the original `FailureReason` from `PlanningCycleResult`. Extend the observation-loop tick with explicit start/completion events so the runner writes one pending line and one completed-cycle line per cycle, while the painters receive the current diagnostic every tick.
|
|
|
|
**Tech Stack:** C# / .NET Standard 2.0 plugin, `Hedingben.ToastText`, `UI.GetPainter`, EM planner contracts, .NET verification host.
|
|
|
|
## Global Constraints
|
|
|
|
- The MovementTest remains observe-only: do not add any chassis, brake, wheel, or actuator call.
|
|
- Terminal output uses `Console.WriteLine` and starts with `[TrajectoryObserver]`.
|
|
- Every completed cycle reports raw `EmPlanningStatus`, `published`, version, elapsed time, and the original nonempty `FailureReason`.
|
|
- A 50 ms observer tick must not emit a duplicate terminal record.
|
|
- World, L-S, and S-T painters must show the diagnostic even when `PublishedTrajectory` is null.
|
|
|
|
---
|
|
|
|
### Task 1: Add a pure planning diagnostic formatter
|
|
|
|
**Files:**
|
|
- Create: `ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/TrajectoryObservationDiagnostics.cs`
|
|
- Modify: `ClumsyPilot/tests/EMPlannerVerificationHost/TrajectoryObservationChecks.cs`
|
|
|
|
**Interfaces:**
|
|
- Consumes: `PlanningCycleResult`, `EmTrajectory`, `TimeSpan`, and the in-flight flag.
|
|
- Produces: `TrajectoryObservationDiagnostic.Text`, a compact multi-line operator string.
|
|
|
|
- [ ] **Step 1: Write the failing test**
|
|
|
|
Add `VerifiesPlanningDiagnosticsKeepRawFailureReason();` to `Run()`, then add:
|
|
|
|
```csharp
|
|
private static void VerifiesPlanningDiagnosticsKeepRawFailureReason()
|
|
{
|
|
var failed = new PlanningCycleResult(
|
|
4L,
|
|
new PlanningCycleIdentity(3L, "diagnostic-reference", 7L, string.Empty, 0),
|
|
new EmPlanningResult(EmPlanningStatus.CorridorInfeasible, null,
|
|
"map=3;reference=diagnostic-reference;state=7;previous=;segment=0;reason=no connected corridor"),
|
|
false,
|
|
"map=3;reference=diagnostic-reference;state=7;previous=;segment=0;reason=no connected corridor");
|
|
|
|
TrajectoryObservationDiagnostic diagnostic = TrajectoryObservationDiagnostics.Create(
|
|
failed, TimeSpan.FromMilliseconds(18d), false, null);
|
|
|
|
Verification.True(diagnostic.Text.Contains("cycle=4"), "diagnostic has cycle version");
|
|
Verification.True(diagnostic.Text.Contains("status=CorridorInfeasible"), "diagnostic preserves raw status");
|
|
Verification.True(diagnostic.Text.Contains("published=False"), "diagnostic preserves publish state");
|
|
Verification.True(diagnostic.Text.Contains("elapsed=18ms"), "diagnostic preserves elapsed time");
|
|
Verification.True(diagnostic.Text.Contains("reason=map=3;reference=diagnostic-reference"),
|
|
"diagnostic preserves planner failure reason");
|
|
|
|
TrajectoryObservationDiagnostic pending = TrajectoryObservationDiagnostics.Create(
|
|
null, TimeSpan.Zero, true, null);
|
|
Verification.Equal("planning status=pending", pending.Text, "diagnostic reports pending before completion");
|
|
}
|
|
```
|
|
|
|
- [ ] **Step 2: Run the test to verify RED**
|
|
|
|
Run:
|
|
|
|
```powershell
|
|
dotnet run --project ClumsyPilot/tests/EMPlannerVerificationHost/EMPlannerVerificationHost.csproj -- trajectory-observation
|
|
```
|
|
|
|
Expected: build failure because `TrajectoryObservationDiagnostic` and `TrajectoryObservationDiagnostics` do not exist.
|
|
|
|
- [ ] **Step 3: Write the minimal formatter**
|
|
|
|
Create `TrajectoryObservationDiagnostics.cs`:
|
|
|
|
```csharp
|
|
using System;
|
|
using System.Globalization;
|
|
using MultiWheelC.TrajectoryPlanning.EMPlanner;
|
|
|
|
namespace MultiWheelC.TrajectoryPlanning.TrajectoryObservation;
|
|
|
|
public sealed class TrajectoryObservationDiagnostic
|
|
{
|
|
public TrajectoryObservationDiagnostic(string text)
|
|
{
|
|
Text = text ?? string.Empty;
|
|
}
|
|
|
|
public string Text { get; }
|
|
}
|
|
|
|
public static class TrajectoryObservationDiagnostics
|
|
{
|
|
public static TrajectoryObservationDiagnostic Create(PlanningCycleResult latestCycle,
|
|
TimeSpan elapsed, bool planningInFlight, EmTrajectory publishedTrajectory)
|
|
{
|
|
if (latestCycle == null)
|
|
return new TrajectoryObservationDiagnostic(planningInFlight
|
|
? "planning status=pending"
|
|
: "planning status=not-started");
|
|
|
|
string text = "planning cycle=" + latestCycle.Version.ToString(CultureInfo.InvariantCulture) +
|
|
" status=" + latestCycle.Result.Status +
|
|
" published=" + latestCycle.Published +
|
|
" elapsed=" + Math.Max(0d, elapsed.TotalMilliseconds).ToString("F0", CultureInfo.InvariantCulture) + "ms";
|
|
if (planningInFlight)
|
|
text += "\nreplan=pending";
|
|
if (publishedTrajectory != null)
|
|
text += "\ntrajectory=" + publishedTrajectory.Metadata.TrajectoryId;
|
|
if (!string.IsNullOrWhiteSpace(latestCycle.Result.FailureReason))
|
|
text += "\nreason=" + latestCycle.Result.FailureReason;
|
|
return new TrajectoryObservationDiagnostic(text);
|
|
}
|
|
}
|
|
```
|
|
|
|
- [ ] **Step 4: Run the test to verify GREEN**
|
|
|
|
Run the Step 2 command.
|
|
|
|
Expected: `PASS trajectory-observation`.
|
|
|
|
- [ ] **Step 5: Commit**
|
|
|
|
```powershell
|
|
git add -- ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/TrajectoryObservationDiagnostics.cs ClumsyPilot/tests/EMPlannerVerificationHost/TrajectoryObservationChecks.cs
|
|
git commit -m "feat: format EM observation diagnostics"
|
|
```
|
|
|
|
### Task 2: Report once at the start and completion of every planning cycle
|
|
|
|
**Files:**
|
|
- Modify: `ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/TrajectoryObservationPipeline.cs`
|
|
- Modify: `ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/MovementTest.TrajectoryObservationTest.cs`
|
|
- Modify: `ClumsyPilot/tests/EMPlannerVerificationHost/TrajectoryObservationChecks.cs`
|
|
|
|
**Interfaces:**
|
|
- Produces: `TrajectoryObservationLoopTick.PlanningStarted` and `.PlanningCompleted`.
|
|
- Consumes: those flags in the MovementTest to issue one terminal/UI status record per lifecycle event.
|
|
|
|
- [ ] **Step 1: Write the failing test**
|
|
|
|
In `VerifiesObserverTicksWhilePlanningIsDelayed`, after the first tick, add:
|
|
|
|
```csharp
|
|
Verification.True(firstTick.PlanningStarted, "observer first tick reports a planning-cycle start");
|
|
Verification.True(!firstTick.PlanningCompleted, "observer first tick has no completed cycle");
|
|
```
|
|
|
|
After `finalTick` is created, add:
|
|
|
|
```csharp
|
|
Verification.True(finalTick.PlanningCompleted, "observer completion tick reports cycle completion");
|
|
```
|
|
|
|
In `VerifiesObservationSourceUsesRequiredOperatorText`, add:
|
|
|
|
```csharp
|
|
Verification.True(source.Contains("Console.WriteLine(\"[TrajectoryObserver] \" + text);"),
|
|
"observer status is mirrored to the host terminal");
|
|
```
|
|
|
|
- [ ] **Step 2: Run the test to verify RED**
|
|
|
|
Run the Task 1 test command.
|
|
|
|
Expected: assertions fail because lifecycle flags and terminal output do not exist.
|
|
|
|
- [ ] **Step 3: Implement lifecycle flags and output**
|
|
|
|
Change `TrajectoryObservationLoopTick` to accept and expose:
|
|
|
|
```csharp
|
|
bool planningStarted, bool planningCompleted
|
|
|
|
public bool PlanningStarted { get; }
|
|
|
|
public bool PlanningCompleted { get; }
|
|
```
|
|
|
|
In `TrajectoryObservationLoop.Tick`, use:
|
|
|
|
```csharp
|
|
bool planningCompleted = ConsumeCompletedPlanning(now);
|
|
bool planningStarted = false;
|
|
if (planningTask == null && controller.ShouldStartCycle(now))
|
|
{
|
|
planningStarted = true;
|
|
planningStartedAtUtc = now;
|
|
planningTask = controller.StartCycle(now, state, cancellationToken);
|
|
planningCompleted |= ConsumeCompletedPlanning(now);
|
|
}
|
|
```
|
|
|
|
Change `ConsumeCompletedPlanning` to return `false` when no completed Task is available and `true` after it assigns `latestCycle`, updates elapsed time, and clears `planningTask`. Pass both flags to the tick constructor.
|
|
|
|
In `RunSessionAsync`, after the tick is created, make one diagnostic and only log event records:
|
|
|
|
```csharp
|
|
TrajectoryObservationDiagnostic diagnostic = TrajectoryObservationDiagnostics.Create(
|
|
tick.LatestCycle, tick.LatestPlanningElapsed, tick.PlanningInFlight,
|
|
observation.PublishedTrajectory);
|
|
if (tick.PlanningStarted)
|
|
LogIfCurrent(sessionId, "planning status=pending");
|
|
if (tick.PlanningCompleted)
|
|
LogIfCurrent(sessionId, diagnostic.Text);
|
|
```
|
|
|
|
Remove the unconditional `if (tick.ShouldLog)` status call. Keep the existing session-start, stop, bootstrap-failure, and runtime-fault calls.
|
|
|
|
Change `PrintStatus` to:
|
|
|
|
```csharp
|
|
private static void PrintStatus(string message)
|
|
{
|
|
string text = ObserveOnlyNotice + "\n" + message;
|
|
Hedingben.ToastText(text, StatusChannel);
|
|
Console.WriteLine("[TrajectoryObserver] " + text);
|
|
}
|
|
```
|
|
|
|
- [ ] **Step 4: Run the test to verify GREEN**
|
|
|
|
Run the Task 1 test command.
|
|
|
|
Expected: `PASS trajectory-observation`; the delayed-planner test still proves the observer does not wait for planning.
|
|
|
|
- [ ] **Step 5: Commit**
|
|
|
|
```powershell
|
|
git add -- ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/TrajectoryObservationPipeline.cs ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/MovementTest.TrajectoryObservationTest.cs ClumsyPilot/tests/EMPlannerVerificationHost/TrajectoryObservationChecks.cs
|
|
git commit -m "feat: report EM observation planning cycles"
|
|
```
|
|
|
|
### Task 3: Persist planning diagnostics in all three painter layers
|
|
|
|
**Files:**
|
|
- Modify: `ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/TrajectoryObservationPresentation.cs`
|
|
- Modify: `ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/MovementTest.TrajectoryObservationTest.cs`
|
|
- Modify: `ClumsyPilot/tests/EMPlannerVerificationHost/TrajectoryObservationChecks.cs`
|
|
|
|
**Interfaces:**
|
|
- Consumes: `TrajectoryObservationDiagnostic.Text`.
|
|
- Produces: World/L-S/S-T empty states that show the precise planning status and reason.
|
|
|
|
- [ ] **Step 1: Write the failing test**
|
|
|
|
Add `VerifiesEmptyChartsReceivePersistentPlanningDiagnostic();` to `Run()` and add:
|
|
|
|
```csharp
|
|
private static void VerifiesEmptyChartsReceivePersistentPlanningDiagnostic()
|
|
{
|
|
string presentationPath = Path.Combine(Directory.GetCurrentDirectory(), "ClumsyPilot",
|
|
"ParkrobTrajplanner", "tarjplanner_movementtest", "TrajectoryObservationPresentation.cs");
|
|
string source = new UTF8Encoding(false, true).GetString(File.ReadAllBytes(presentationPath));
|
|
|
|
Verification.True(source.Contains("DrawLs(TrajectoryObservationCharts charts, string diagnosticText)"),
|
|
"LS painter accepts planning diagnostic input");
|
|
Verification.True(source.Contains("DrawSt(TrajectoryObservationCharts charts, string diagnosticText)"),
|
|
"ST painter accepts planning diagnostic input");
|
|
Verification.True(source.Contains("No published trajectory available for L-S chart.\n"),
|
|
"LS empty state includes diagnostic after chart label");
|
|
Verification.True(source.Contains("No published trajectory available for T-S/T-V charts.\n"),
|
|
"ST empty state includes diagnostic after chart label");
|
|
}
|
|
```
|
|
|
|
- [ ] **Step 2: Run the test to verify RED**
|
|
|
|
Run the Task 1 test command.
|
|
|
|
Expected: source checks fail because painter methods have no diagnostic parameter.
|
|
|
|
- [ ] **Step 3: Add painter parameters and wire the diagnostic**
|
|
|
|
Change signatures to:
|
|
|
|
```csharp
|
|
public void DrawWorld(TrajectoryObservationBootstrapResult bootstrap,
|
|
TrajectoryObservationObservation observation, TrajectoryObservationRuntimeState runtimeState,
|
|
string diagnosticText)
|
|
|
|
public void DrawLs(TrajectoryObservationCharts charts, string diagnosticText)
|
|
|
|
public void DrawSt(TrajectoryObservationCharts charts, string diagnosticText)
|
|
```
|
|
|
|
Add this helper in `TrajectoryObservationPresentation`:
|
|
|
|
```csharp
|
|
private static string EmptyChartMessage(string label, string diagnosticText)
|
|
{
|
|
return string.IsNullOrWhiteSpace(diagnosticText)
|
|
? label
|
|
: label + "\n" + diagnosticText;
|
|
}
|
|
```
|
|
|
|
For World, draw `EmptyChartMessage("No published trajectory available.", diagnosticText)` at `bootstrap.Map.Bounds.XMin + 100f, bootstrap.Map.Bounds.YMin + 300f` before returning from the empty trajectory path. For L-S and S-T, draw `EmptyChartMessage` with their existing label at `0f, 0f`.
|
|
|
|
Change `DrawIfCurrent` to accept `TrajectoryObservationDiagnostic diagnostic` and call:
|
|
|
|
```csharp
|
|
Presentation.DrawWorld(bootstrap, observation, runtimeState, diagnostic?.Text ?? string.Empty);
|
|
Presentation.DrawLs(charts, diagnostic?.Text ?? string.Empty);
|
|
Presentation.DrawSt(charts, diagnostic?.Text ?? string.Empty);
|
|
```
|
|
|
|
Pass the diagnostic created in Task 2 from `RunSessionAsync`. For the bootstrap-failure path, pass:
|
|
|
|
```csharp
|
|
new TrajectoryObservationDiagnostic("bootstrap failed: " + bootstrap.FailureReason)
|
|
```
|
|
|
|
- [ ] **Step 4: Verify focused test, build, and diff**
|
|
|
|
Run:
|
|
|
|
```powershell
|
|
dotnet run --project ClumsyPilot/tests/EMPlannerVerificationHost/EMPlannerVerificationHost.csproj -- trajectory-observation
|
|
dotnet build ClumsyPilot/ClumsyPilot.csproj --no-restore
|
|
git diff --check
|
|
```
|
|
|
|
Expected: `PASS trajectory-observation`, zero build errors, and no diff whitespace errors.
|
|
|
|
- [ ] **Step 5: Commit**
|
|
|
|
```powershell
|
|
git add -- ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/TrajectoryObservationPresentation.cs ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/MovementTest.TrajectoryObservationTest.cs ClumsyPilot/tests/EMPlannerVerificationHost/TrajectoryObservationChecks.cs
|
|
git commit -m "feat: show EM observation failure diagnostics"
|
|
```
|
|
|