Files
FT546-WCS/docs/superpowers/plans/2026-07-31-wcs-real-history-driven-dispatch-simulation.md

335 lines
17 KiB
Markdown

# WCS Real History-Driven Dispatch Simulation 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:** Drive all 240 frozen FT546 history rows through deterministic task arrival, priority scheduling, vehicle selection, route planning, traffic arbitration, simulated PLC/vehicle execution, resource release, archive, and idempotent WMS completion.
**Architecture:** Add a focused event-driven simulation package under the existing history module. It adapts verified history rows into business tasks, reuses the existing assignment/planning/SHADOW resource components, and isolates time, faults, PLC, WMS, archive, metrics, and reporting behind small deterministic classes.
**Tech Stack:** Java 8, Maven, Jackson 2.17.2, JUnit 5.10.2, existing FT546 demo/SHADOW classes, bundled spreadsheet runtime for the final `.xlsx`.
## Global Constraints
- Use the committed 240-row snapshot; never connect to SQL Server, a real PLC, WMS, or `ruoyi-*` production modules.
- Default acceleration is `100x`; support `10x`; preserve source, simulated, and scale-adjusted time values.
- Historical `agv_id`, `path_info`, `finish_date`, and completion status are comparisons only.
- `task_no`, type, business start/end, and priority remain business inputs.
- Unknown map and unreachable routes fail closed. `WAITING` is allowed only with recorded deadlock/tick-limit evidence.
- Priority aging raises effective priority every 30 simulated seconds.
- Node and undirected edge capacity are 1; opposite-direction simultaneous traversal is forbidden.
- Fault schedules are deterministic. PLC retries are bounded to 3 attempts.
- Every completed task has exactly one archive row and one WMS business side effect; duplicate WMS deliveries are intentionally exercised.
- Same input and configuration must produce identical canonical results.
---
### Task 1: Preserve Verified Map Details and Convert Historical Inputs
**Files:**
- Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/HistoricalMapSite.java`
- Modify: `javaTest/src/main/java/com/ft546/wcs/shadow/history/HistoricalQueueDataset.java`
- Modify: `javaTest/src/main/java/com/ft546/wcs/shadow/history/HistoricalQueueLoader.java`
- Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/HistoricalSimulationTask.java`
- Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/HistoricalSimulationInput.java`
- Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/HistoricalSimulationInputFactory.java`
- Test: `javaTest/src/test/java/com/ft546/wcs/shadow/history/simulation/HistoricalSimulationInputFactoryTest.java`
**Interfaces:**
- `HistoricalSimulationInput HistoricalSimulationInputFactory.create(HistoricalQueueDataset dataset)`
- Input exposes immutable schedulable tasks, failed inputs, grid/traffic maps, and initial vehicles.
- [ ] **Step 1: Write failing loader and conversion tests**
```java
assertEquals(1129, dataset.getMapSiteDetails().size());
assertEquals(240, input.getTasks().size() + input.getFailedInputs().size());
assertEquals(240, input.allSimulationIds().stream().distinct().count());
assertEquals(Arrays.asList("1", "2", "3"), input.vehicleIds());
```
- [ ] **Step 2: Run RED**
Run: `mvn -f javaTest/pom.xml -Dtest=HistoricalSimulationInputFactoryTest test`
Expected: compilation fails because the new input types do not exist.
- [ ] **Step 3: Implement immutable map loading and task conversion**
Parse SHA-verified map details into an insertion-ordered immutable map. Map task types `1/2/3`, preserve raw business fields, assign stable per-row ID `task_no@guid#stableSequence` because the snapshot contains repeated task/GUID pairs, parse `create_date`, and retain `finish_date/agv_id/path_info` only in comparison fields.
- [ ] **Step 4: Build replannable topology and explicit endpoint mapping**
Build topology from unique valid historical adjacent edges. Route directly from `start_site/end_site` when physical; otherwise store an explicit business-to-physical endpoint mapping to the reference path endpoints. Reject blank, unknown, or malformed mappings as `FAILED/UNKNOWN_MAP`.
- [ ] **Step 5: Run GREEN and commit**
Run: `mvn -f javaTest/pom.xml -Dtest=HistoricalSimulationInputFactoryTest test`
Commit: `git commit -m "feat: prepare verified history simulation inputs"`
---
### Task 2: Implement Accelerated Event Time and Starvation-Safe Queueing
**Files:**
- Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/SimulationConfig.java`
- Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/SimulationClock.java`
- Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/SimulationTaskQueue.java`
- Test: `javaTest/src/test/java/com/ft546/wcs/shadow/history/simulation/SimulationClockTest.java`
- Test: `javaTest/src/test/java/com/ft546/wcs/shadow/history/simulation/SimulationTaskQueueTest.java`
**Interfaces:**
- `SimulationConfig.defaults()` returns acceleration 100, aging interval 30 seconds, retry limit 3, no-progress 500, tick limit 20000.
- `long SimulationClock.toSimulationMillis(LocalDateTime createdAt)` preserves relative arrivals and caps source gaps above 30 minutes.
- `List<HistoricalSimulationTask> SimulationTaskQueue.readyAt(long nowMillis)` orders effective priority, arrival, task number, GUID.
- [ ] **Step 1: Write failing time-scaling tests**
```java
assertEquals(100L, clock.toSimulationMillis(base.plusSeconds(10)));
assertEquals(18_000L, clock.toSimulationMillis(base.plusHours(2)));
assertEquals(clock10.sourceDurationMillis(1_000L), 10_000L);
```
- [ ] **Step 2: Run clock RED, implement, and run GREEN**
Run: `mvn -f javaTest/pom.xml -Dtest=SimulationClockTest test`
- [ ] **Step 3: Write failing priority/aging tests**
```java
queue.add(lowPriorityAtZero);
queue.add(highPriorityAtTwentySeconds);
assertEquals(highPriorityAtTwentySeconds, queue.readyAt(20_000L).get(0));
assertEquals(lowPriorityAtZero, queue.readyAt(90_000L).get(0));
```
- [ ] **Step 4: Implement stable queue ordering and requeue**
Keep original priority immutable. Derive `effectivePriority = max(HIGHEST, priority - floor(wait/30s))`; requeue after HOLD without changing original arrival or losing accumulated age.
- [ ] **Step 5: Run GREEN and commit**
Run: `mvn -f javaTest/pom.xml -Dtest=SimulationClockTest,SimulationTaskQueueTest test`
Commit: `git commit -m "feat: add accelerated starvation-safe simulation queue"`
---
### Task 3: Implement PLC, Vehicle Fault, Archive, and WMS Simulators
**Files:**
- Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/DeterministicFaultPlan.java`
- Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/SimulatedPlcGateway.java`
- Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/SimulatedVehicleRuntime.java`
- Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/SimulationArchive.java`
- Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/IdempotentWmsCompletionReceiver.java`
- Test: `javaTest/src/test/java/com/ft546/wcs/shadow/history/simulation/SimulationAdaptersTest.java`
**Interfaces:**
- PLC result reasons: `ACK`, `SEND_FAILURE`, `ACK_TIMEOUT`, `ACK_LOST`, `STALE_BOOT_ID`.
- Vehicle states: connected, boot ID, battery, faulted, current node; health decides assignment eligibility.
- `SimulationArchive.complete(taskId, result)` is idempotent and stores one terminal row.
- `IdempotentWmsCompletionReceiver.receive(taskId)` counts delivery attempts separately from business effects.
- [ ] **Step 1: Write failing PLC retry and stale-boot tests**
```java
assertEquals(SEND_FAILURE, plc.send(command).getReason());
assertEquals(ACK, plc.send(command).getReason());
runtime.restart();
assertEquals(STALE_BOOT_ID, plc.accept(oldAck).getReason());
```
- [ ] **Step 2: Run RED, implement deterministic PLC behavior, run GREEN**
Run: `mvn -f javaTest/pom.xml -Dtest=SimulationAdaptersTest test`
- [ ] **Step 3: Add failing disconnect, low-battery, fault, archive, and WMS tests**
```java
runtime.disconnect(); assertFalse(runtime.isAssignmentEligible());
runtime.setBattery(29); assertFalse(runtime.isAssignmentEligible());
runtime.fail(); assertFalse(runtime.isAssignmentEligible());
archive.complete("T1", result); archive.complete("T1", result);
wms.receive("T1"); wms.receive("T1");
assertEquals(1, archive.size());
assertEquals(2, wms.getDeliveryAttempts());
assertEquals(1, wms.getBusinessEffects());
```
- [ ] **Step 4: Implement adapters and run GREEN**
Run: `mvn -f javaTest/pom.xml -Dtest=SimulationAdaptersTest test`
- [ ] **Step 5: Commit**
Commit: `git commit -m "feat: simulate PLC vehicle archive and WMS behavior"`
---
### Task 4: Build the Full Event-Driven Dispatch Simulator
**Files:**
- Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/HistoricalDispatchSimulator.java`
- Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/HistoricalDispatchSimulationResult.java`
- Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/SimulationTaskResult.java`
- Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/SimulationTickEvent.java`
- Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/SimulationInvariantSummary.java`
- Test: `javaTest/src/test/java/com/ft546/wcs/shadow/history/simulation/HistoricalDispatchSimulatorTest.java`
**Interfaces:**
- `HistoricalDispatchSimulationResult simulate(HistoricalSimulationInput input, SimulationConfig config, DeterministicFaultPlan faults)`
- Result exposes task/vehicle results, events, HOLD/retry reasons, archive/WMS counts, assignments/routes, and invariant counters.
- [ ] **Step 1: Write failing multi-task completion test**
Use a hand-checked `A-B-C` plus `B-D` graph with two vehicles and three arrivals. Assert all complete, priority order is correct, at least one HOLD occurs, and old nodes/edges are released after each move.
- [ ] **Step 2: Run RED**
Run: `mvn -f javaTest/pom.xml -Dtest=HistoricalDispatchSimulatorTest test`
- [ ] **Step 3: Implement arrival, selection, replanning, and SHADOW arbitration**
At each event time inject arrivals, assign every idle healthy vehicle, plan `vehicle -> source -> target`, build fresh snapshots/candidates, and apply GRANT segments to the authoritative ledger. HOLD an admission whose full route has an opposite shared edge with an active remaining route. Treat historical vehicle/path only as report comparisons.
- [ ] **Step 4: Implement ACK movement and rolling resource release**
Reserve current node, undirected edge, and next node. After ACK move one node, release the prior node and traversed edge, retain the new node. Assert capacity, node uniqueness, opposite-edge exclusion, and one active task per vehicle after every event.
- [ ] **Step 5: Implement HOLD re-dispatch and fault recovery**
On resource HOLD, PLC retry exhaustion, disconnect, low battery, or vehicle fault, release unexecuted reservations and requeue with preserved wait age. Reject stale ACK after boot changes. Resume/reselect/replan after recovery.
- [ ] **Step 6: Implement completion, archive, WMS idempotency, and termination**
Complete only when simulated movement reaches target. Atomically archive, deliver WMS twice in the acceptance path, and assert one business effect. Deadlock/tick limit snapshots remaining tasks as `WAITING` and increments deadlock evidence. Progress is based on terminal task count, so vehicle oscillation cannot mask a livelock.
- [ ] **Step 7: Run GREEN and commit**
Run: `mvn -f javaTest/pom.xml -Dtest=HistoricalDispatchSimulatorTest test`
Commit: `git commit -m "feat: run deterministic full-chain dispatch simulation"`
---
### Task 5: Verify Failure Modes and All 240 Real Tasks
**Files:**
- Create: `javaTest/src/test/java/com/ft546/wcs/shadow/history/simulation/HistoricalDispatchFaultMatrixTest.java`
- Create: `javaTest/src/test/java/com/ft546/wcs/shadow/history/simulation/HistoricalDispatchSimulationRealDataTest.java`
**Interfaces:**
- Fault matrix uses the same simulator with controlled plans for disconnect, restart, low battery, vehicle fault, send failure, ACK timeout/loss, unknown map, and no route.
- Real-data test runs the frozen 240-row workload twice and compares canonical results.
- [ ] **Step 1: Write and run the fault-matrix tests**
Assert each recoverable fault releases resources and completes after redispatch; unknown map/no route end as `FAILED`; stale feedback cannot move a vehicle; all invariant counters remain zero.
Run: `mvn -f javaTest/pom.xml -Dtest=HistoricalDispatchFaultMatrixTest test`
- [ ] **Step 2: Write the 240-row parameterized and aggregate tests**
```java
assertEquals(240, result.getTaskResults().size());
assertEquals(240, result.getCompletedCount() + result.getWaitingCount() + result.getFailedCount());
assertTrue(result.getCompletedCount() > 0);
assertTrue(result.getReassignmentCount() > 0);
assertTrue(result.getRetryCount() > 0);
assertEquals(0, result.getInvariants().totalSafetyViolations());
assertEquals(result.canonicalView(), secondRun.canonicalView());
```
- [ ] **Step 3: Run real data and existing safety suites**
Run: `mvn -f javaTest/pom.xml -Dtest=HistoricalDispatchSimulationRealDataTest,ShadowSimulationDataTest test`
- [ ] **Step 4: Commit**
Commit: `git commit -m "test: verify history simulation and fault recovery"`
---
### Task 6: Produce Metrics and Auditable Machine-Readable Reports
**Files:**
- Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/SimulationMetrics.java`
- Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/HistoricalDispatchSimulationReportWriter.java`
- Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/HistoricalDispatchSimulationApplication.java`
- Modify: `javaTest/README.md`
- Test: `javaTest/src/test/java/com/ft546/wcs/shadow/history/simulation/SimulationMetricsTest.java`
- Test: `javaTest/src/test/java/com/ft546/wcs/shadow/history/simulation/HistoricalDispatchSimulationReportWriterTest.java`
**Interfaces:**
- Metrics use nearest-rank `ceil(p*n)-1`; empty series returns JSON null.
- CLI: `HistoricalDispatchSimulationApplication <outputDir> [acceleration=100]`.
- Outputs: `summary.json`, `task-results.csv`, `tick-events.csv`, `invariant-results.json`, `manifest.sha256`.
- [ ] **Step 1: Write failing hand-calculated metrics tests**
```java
assertEquals(3.0, metrics.average(Arrays.asList(1L, 3L, 5L)));
assertEquals(5L, metrics.percentile(Arrays.asList(1L, 3L, 5L), 0.95));
assertNull(metrics.percentile(Collections.emptyList(), 0.99));
```
- [ ] **Step 2: Implement metrics and run GREEN**
Run: `mvn -f javaTest/pom.xml -Dtest=SimulationMetricsTest test`
- [ ] **Step 3: Write failing report contract tests**
Parse generated JSON/CSV and assert the 240 status counts, per-vehicle totals, reassignments/routes, safety counters, waiting/completion average/P95/P99, HOLD/retry reasons, historical-vs-simulated duration, archive totals, WMS attempts/effects, source boundary, and sorted hashes.
- [ ] **Step 4: Implement reports, CLI, README, and run GREEN**
Run: `mvn -f javaTest/pom.xml -Dtest=HistoricalDispatchSimulationReportWriterTest test`
Commit: `git commit -m "feat: report auditable dispatch simulation metrics"`
---
### Task 7: Generate the Readable Workbook and Perform Final Verification
**Files:**
- Generate: `outputs/wcs-history-driven-dispatch-simulation-<runId>/readable-report.xlsx`
- Modify: `docs/superpowers/plans/2026-07-31-wcs-real-history-driven-dispatch-simulation.md`
- [ ] **Step 1: Run fresh full verification**
Run: `mvn -f javaTest/pom.xml clean test`
Require failures/errors/skips 0 and separately report the 240-row and fault-matrix counts.
- [ ] **Step 2: Generate a fresh unique evidence directory**
Run the CLI at acceleration 100. Do not overwrite prior replay/simulation evidence.
- [ ] **Step 3: Build and verify the workbook**
Create sheets `测试总览`, `任务结果`, `车辆统计`, `重新选车与路径`, `等待与重试`, `安全与归档`, `数据问题`, `来源与边界`. Reconcile formulas and values against JSON/CSV, verify 241 task rows including header, ensure no formula error tokens, and include one status chart.
- [ ] **Step 4: Audit evidence and repository scope**
Recalculate manifests, scan output for secrets, verify the report answers every required question, and prove `git diff --name-only -- 'ruoyi-*'` is empty.
- [ ] **Step 5: Update this checklist and commit final evidence-related changes**
Commit: `git commit -m "test: verify real history dispatch simulation"`
---
## Final Verification Checklist
- [ ] All 240 history rows enter once and have completed/waiting/failed totals.
- [ ] Priority ordering and 30-second aging protection are proven.
- [ ] Automatic vehicle selection and route replanning differ from history at least once.
- [ ] Node, edge, opposite-direction, double-occupancy, and deadlock evidence is reported; safety violations are zero.
- [ ] HOLD redispatch, PLC retry, disconnect, restart, low battery, and vehicle fault recovery are proven.
- [ ] Unknown map and no route fail closed.
- [ ] Waiting/completion average, P95, P99 and history-vs-simulation duration are reported.
- [ ] Every completed task has one archive row and one WMS business effect despite duplicate delivery.
- [ ] Two runs are canonically identical, all Maven tests pass, and no production module changed.