24 KiB
WCS Complete Center 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: Make all 240 frozen historical tasks complete deterministically through independent center dispatch, full-grid path planning, traffic arbitration, PLC simulation, node movement, archive, and idempotent WMS completion.
Architecture: Extend the frozen dataset with a separately hashed complete wcs_srgv_map projection and compile routing topology solely from passable grid cells. Resolve business aliases before scheduling, then use explicit corridor direction ownership and real internal clearance moves to guarantee progress without weakening node/edge safety. Historical vehicle, path, finish time, and completed status remain comparison data only.
Tech Stack: Java 8, Maven, JUnit 5, Jackson, Apache POI, existing in-memory dispatcher/path planner/SHADOW traffic coordinator.
Global Constraints
- Work only under
javaTestanddocs; do not modify anyruoyi-*production module. path_infomust never create a node, edge, endpoint, candidate vehicle, or route-planning decision.- Full-map input is a read-only bounded snapshot of
dbo.wcs_srgv_map; include no connection strings, addresses, or credentials. - Passability is exactly
value IN (1,2)and edges are same-floor Manhattan adjacency only. tsjresolves by the production alias rule07050{floor}.- Real-data acceptance is exactly
240 COMPLETED / 0 WAITING / 0 FAILED; watchdog or tick exhaustion fails the test. - Final invariants are archive
240, WMS attempts480, WMS business effects240, deadlocks0, and all safety violations0. - Add every behavior test first, run it, and observe the expected failure before changing implementation.
- Keep
map-sites.jsonunchanged because legacy replay tests rely on its historical-path semantics.
Task 1: Freeze and verify the complete grid snapshot
Files:
- Create:
javaTest/src/main/java/com/ft546/wcs/shadow/history/HistoricalMapCell.java - Create:
javaTest/src/test/resources/shadow/history/full-map-grid.json - Modify:
javaTest/src/test/resources/shadow/history/manifest.json - 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/test/java/com/ft546/wcs/shadow/history/HistoricalQueueLoaderTest.java
Interfaces:
-
Produces:
HistoricalMapCell(String siteId, int x, int y, int z, int value, String axy, String warehouse, String spaceArea)with getters. -
Produces:
HistoricalQueueDataset.getFullMapCells(): List<HistoricalMapCell>,getFullMapSha256(): String, andgetFullMapRows(): int. -
Consumes later: the exact frozen cell list; no later task reads
map-sites.jsonfor routing. -
Step 1: Add a failing loader contract test
@Test
void loadsAndVerifiesTheIndependentCompleteGridSnapshot() {
HistoricalQueueDataset data = new HistoricalQueueLoader().load(
getClass().getClassLoader(), "shadow/history");
assertEquals(2552, data.getFullMapRows());
assertEquals(2552, data.getFullMapCells().size());
assertEquals(1175, data.getFullMapCells().stream()
.filter(cell -> cell.getValue() == 1 || cell.getValue() == 2).count());
assertEquals(64, data.getFullMapSha256().length());
}
- Step 2: Run the focused test and observe RED
Run: cd javaTest && mvn -Dtest=HistoricalQueueLoaderTest#loadsAndVerifiesTheIndependentCompleteGridSnapshot test
Expected: compilation fails because the full-map accessors do not exist.
- Step 3: Export the bounded read-only projection and calculate its canonical hash
Use the configured read-only SQL Server connection to query, ordered deterministically:
SELECT site_id, x, y, z, value, axy, warehouse, space_area
FROM dbo.wcs_srgv_map
ORDER BY z, y, x, site_id;
Write the JSON array to full-map-grid.json; verify 2,552 rows, 1,175 passable rows, nonblank unique passable IDs, and unique passable (x,y,z) coordinates. Add fullMapRows: 2552 and sha256.full-map-grid.json to manifest.json.
- Step 4: Implement strict parsing and verification
Load full-map-grid.json beside the existing three resources, call verifySha256, reject blank/duplicate passable IDs and duplicate passable coordinates, and reject a manifest row-count mismatch. Preserve all existing constructor data and make both new lists immutable.
- Step 5: Run loader and legacy replay tests GREEN
Run: cd javaTest && mvn -Dtest=HistoricalQueueLoaderTest,HistoricalQueueDatabaseReplayTest test
Expected: all tests pass and the old 240-row replay contract is unchanged.
- Step 6: Commit the data contract
git add javaTest/src/main/java/com/ft546/wcs/shadow/history javaTest/src/test/resources/shadow/history javaTest/src/test/java/com/ft546/wcs/shadow/history/HistoricalQueueLoaderTest.java
git commit -m "test(javaTest): freeze complete WCS grid snapshot"
Task 2: Compile routing topology from grid semantics only
Files:
- Create:
javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/HistoricalGridMapCompiler.java - Create:
javaTest/src/test/java/com/ft546/wcs/shadow/history/simulation/HistoricalGridMapCompilerTest.java
Interfaces:
-
Consumes:
List<HistoricalMapCell>from Task 1. -
Produces:
HistoricalGridMapCompiler.compile(List<HistoricalMapCell>): CompiledHistoricalMap. -
Produces:
CompiledHistoricalMap.getGridMap(): GridMap,getTrafficMap(): EffectiveTrafficMap,getPassableSites(): Map<String,HistoricalMapCell>, andgetComponentId(String): int. -
Step 1: Add failing grid-only topology tests
@Test
void connectsOnlyPassableSameFloorManhattanNeighbors() {
List<HistoricalMapCell> cells = Arrays.asList(
cell("A", 1, 1, 1, 1), cell("B", 2, 1, 1, 2),
cell("BLOCK", 1, 2, 1, 0), cell("DIAGONAL", 2, 2, 1, 1),
cell("UP", 1, 1, 2, 1));
CompiledHistoricalMap map = new HistoricalGridMapCompiler().compile(cells);
assertEquals(new LinkedHashSet<String>(Arrays.asList("A", "B", "DIAGONAL")),
map.getGridMap().getSites().keySet());
assertTrue(map.getGridMap().getNeighbors("A").contains("B"));
assertFalse(map.getGridMap().getNeighbors("A").contains("DIAGONAL"));
assertFalse(map.getGridMap().getSites().containsKey("UP"));
}
Add a second fixture whose HistoricalSimulationTask.historicalNodes contains a fake adjacency and prove compilation is unchanged because the compiler accepts cells only.
- Step 2: Run the test and observe RED
Run: cd javaTest && mvn -Dtest=HistoricalGridMapCompilerTest test
Expected: compilation fails because the compiler and result type do not exist.
- Step 3: Implement deterministic map compilation
Index passable cells by coordinate, add a Site for each, connect only the positive X and positive Y neighbors on the same floor, construct a capacity-1 TrafficMapCompiler.MapDefinition, and calculate connected components in site-ID-stable order. Reject duplicate IDs, duplicate coordinates, and edges with missing endpoints.
- Step 4: Run focused tests GREEN
Run: cd javaTest && mvn -Dtest=HistoricalGridMapCompilerTest test
- Step 5: Commit
git add javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/HistoricalGridMapCompiler.java javaTest/src/test/java/com/ft546/wcs/shadow/history/simulation/HistoricalGridMapCompilerTest.java
git commit -m "feat(javaTest): compile routing map from full grid"
Task 3: Resolve physical endpoints independently of historical paths
Files:
- Create:
javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/PhysicalEndpointResolver.java - Create:
javaTest/src/test/java/com/ft546/wcs/shadow/history/simulation/PhysicalEndpointResolverTest.java - Modify:
javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/HistoricalSimulationInputFactory.java - Modify:
javaTest/src/test/java/com/ft546/wcs/shadow/history/simulation/HistoricalSimulationInputFactoryTest.java
Interfaces:
-
Produces:
PhysicalEndpointResolver.resolve(HistoricalQueueRecord, Map<String,HistoricalMapCell>): ResolvedEndpoints. -
Produces:
ResolvedEndpoints.getSource(),getTarget(), andgetFloor(). -
Consumes:
CompiledHistoricalMapfrom Task 2; factory builds tasks with historical nodes only for later comparison. -
Step 1: Add failing alias and path-independence tests
@ParameterizedTest
@CsvSource({"1,070501", "2,070502", "3,070503"})
void resolvesTsjByTheProductionFloorRule(int floor, String expected) {
assertEquals(expected, resolver.resolve(record("START" + floor, "tsj", floor), sites).getTarget());
}
@Test
void changingOrRemovingHistoricalPathDoesNotChangePlannedInput() {
HistoricalSimulationInput original = factory.create(dataset);
HistoricalSimulationInput mutated = factory.create(copyWithMutatedPathInfo(dataset));
assertEquals(planningView(original), planningView(mutated));
}
Also replace the old 238 schedulable / 2 failed expectation with 240 / 0, and assert all 240 resolved pairs belong to the same connected component.
- Step 2: Run both tests and observe RED
Run: cd javaTest && mvn -Dtest=PhysicalEndpointResolverTest,HistoricalSimulationInputFactoryTest test
Expected: alias assertions or the 240 / 0 assertion fail because the factory still normalizes path_info into topology/endpoints.
- Step 3: Implement endpoint resolution and refactor the factory
Compile the full map once. Resolve ordinary IDs directly; for tsj, use the start cell floor and 07050 prefix. Reject unknown or cross-component endpoints as dataset contract errors. Sort records by create_date/task_no/guid, preserve historical path/time/vehicle fields in HistoricalSimulationTask, but never ask HistoricalQueueNormalizer to decide schedulability or topology.
- Step 4: Initialize the fleet without constraining selection to historical AGVs
Keep stable vehicle IDs 1, 2, 3 as the available fleet and place each at a deterministic passable start cell. Historical agv_id remains only in HistoricalQueueRecord for reassignment reporting.
- Step 5: Run factory and planner tests GREEN
Run: cd javaTest && mvn -Dtest=PhysicalEndpointResolverTest,HistoricalGridMapCompilerTest,HistoricalSimulationInputFactoryTest,HistoricalDispatchSimulatorTest test
- Step 6: Commit
git add javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation javaTest/src/test/java/com/ft546/wcs/shadow/history/simulation
git commit -m "feat(javaTest): plan history tasks from physical endpoints"
Task 4: Replace whole-route rejection with deterministic corridor ownership
Files:
- Create:
javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/CorridorReservationManager.java - Create:
javaTest/src/test/java/com/ft546/wcs/shadow/history/simulation/CorridorReservationManagerTest.java - Modify:
javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/HistoricalDispatchSimulator.java - Modify:
javaTest/src/test/java/com/ft546/wcs/shadow/history/simulation/HistoricalDispatchSimulatorTest.java
Interfaces:
-
Produces:
reserve(taskId, directedRoute, effectivePriority, arrivalMillis): ReservationDecision. -
Produces:
releasePassedEdge(taskId, from, to),releaseTask(taskId), andownerFor(edge): String. -
ReservationDecisionexposesisGranted(),getOwnerTaskId(), andgetReason(). -
Step 1: Add failing ownership and starvation tests
@Test
void oppositeDirectionsHaveOneStableOwnerUntilTheConflictExit() {
assertTrue(manager.reserve("HIGH", route("A","B","C"), 1, 20).isGranted());
assertFalse(manager.reserve("LOW", route("C","B","A"), 5, 10).isGranted());
manager.releasePassedEdge("HIGH", "B", "C");
manager.releaseTask("HIGH");
assertTrue(manager.reserve("LOW", route("C","B","A"), 0, 10).isGranted());
}
Add an integration assertion that same-direction routes can enter while next-segment capacity still serializes physical movement.
- Step 2: Run tests and observe RED
Run: cd javaTest && mvn -Dtest=CorridorReservationManagerTest,HistoricalDispatchSimulatorTest test
- Step 3: Implement reservations and integrate them
Canonicalize each undirected edge, compare requested direction, choose ownership by effective priority then original arrival then task ID, and retain ownership until the current owner passes the last shared edge. Remove hasOppositeRouteConflict; keep the existing per-tick opposite-edge and ResourceLedger checks as authoritative safety guards.
- Step 4: Preserve queue age on HOLD and redispatch
Use SimulationTaskQueue.requeue without changing original arrival. Emit HOLD with CORRIDOR_DIRECTION_OWNED and REDISPATCH after the configured threshold, but do not count repeated HOLD as watchdog progress.
- Step 5: Run focused tests GREEN and commit
Run: cd javaTest && mvn -Dtest=CorridorReservationManagerTest,HistoricalDispatchSimulatorTest,SimulationTaskQueueTest test
git add javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation javaTest/src/test/java/com/ft546/wcs/shadow/history/simulation
git commit -m "feat(javaTest): arbitrate corridor direction ownership"
Task 5: Move idle blockers through real clearance tasks
Files:
- Create:
javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/ClearanceMovePlanner.java - Create:
javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/ClearanceMove.java - Create:
javaTest/src/test/java/com/ft546/wcs/shadow/history/simulation/ClearanceMovePlannerTest.java - Modify:
javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/HistoricalDispatchSimulator.java - Modify:
javaTest/src/test/java/com/ft546/wcs/shadow/history/simulation/HistoricalDispatchSimulatorTest.java
Interfaces:
-
Produces:
ClearanceMovePlanner.plan(blockingVehicle, ownerRemainingRoute, occupiedSites, reservedEdges, gridMap): Optional<ClearanceMove>. -
ClearanceMoveexposes vehicle ID, source, target, planned nodes, inherited owner task ID, current node index, protocol attempt count, and completion state. -
Step 1: Add a failing three-vehicle corridor scenario
@Test
void movesAnIdleBlockerNodeByNodeAndCompletesBothBusinessDirections() {
HistoricalDispatchSimulationResult result = simulator.simulate(
threeVehicleBlockedCorridorInput(), config, DeterministicFaultPlan.none());
assertEquals(2, result.getCompletedCount());
assertTrue(result.getEvents().stream().anyMatch(e -> "CLEARANCE_PLANNED".equals(e.getType())));
assertTrue(result.getEvents().stream().anyMatch(e -> "CLEARANCE_MOVE".equals(e.getType())));
assertEquals(0, result.getInvariants().getOppositeEdgeViolations());
assertEquals(0, result.getInvariants().getNodeDoubleOccupancyViolations());
}
Assert every consecutive clearance event pair is adjacent in GridMap, proving no teleport.
- Step 2: Run the scenario and observe RED
Run: cd javaTest && mvn -Dtest=HistoricalDispatchSimulatorTest#movesAnIdleBlockerNodeByNodeAndCompletesBothBusinessDirections test
Expected: no clearance event is produced and the scenario reaches HOLD/tick exhaustion.
- Step 3: Implement deterministic clearance target selection
Exclude the owner route, all vehicle positions, and reserved nodes/edges. Route to every remaining reachable candidate with the center path planner, then sort by path length, non-trunk preference, and site ID. Return empty only when the map has no legal clearance destination.
- Step 4: Execute clearance through the normal movement chain
Create an internal move when an idle vehicle blocks the owner route. Run every step through SHADOW arbitration, ResourceLedger, PLC send/ACK, vehicle move, and segment release. Do not archive or WMS-callback a clearance move. On completion, release it and replan the waiting business task.
- Step 5: Add recovery behavior
On disconnect, restart, low battery, vehicle fault, send failure, or exhausted ACK retry, release the unexecuted segment and replan or reschedule without changing the business task's original arrival time.
- Step 6: Run scenario and fault matrix GREEN, then commit
Run: cd javaTest && mvn -Dtest=ClearanceMovePlannerTest,HistoricalDispatchSimulatorTest,HistoricalDispatchFaultMatrixTest test
git add javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation javaTest/src/test/java/com/ft546/wcs/shadow/history/simulation
git commit -m "feat(javaTest): clear blocked routes with real vehicle moves"
Task 6: Enforce complete deterministic real-data acceptance
Files:
- Modify:
javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/SimulationProgressWatchdog.java - Modify:
javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/HistoricalDispatchSimulator.java - Modify:
javaTest/src/test/java/com/ft546/wcs/shadow/history/simulation/SimulationProgressWatchdogTest.java - Modify:
javaTest/src/test/java/com/ft546/wcs/shadow/history/simulation/HistoricalDispatchSimulationRealDataTest.java
Interfaces:
-
Watchdog progress events are exactly business terminal transition, authorized vehicle node movement, or completed clearance move.
-
Real-data simulation throws
IllegalStateExceptionon watchdog/tick exhaustion rather than returning acceptable WAITING snapshots. -
Step 1: Tighten the real-data test before implementation
assertEquals(240, result.getCompletedCount());
assertEquals(0, result.getWaitingCount());
assertEquals(0, result.getFailedCount());
assertEquals(240, result.getArchiveCount());
assertEquals(480, result.getWmsDeliveryAttempts());
assertEquals(240, result.getWmsBusinessEffects());
assertEquals(0, result.getInvariants().getDeadlocks());
assertEquals(0, result.getInvariants().totalSafetyViolations());
assertEquals(result.canonicalView(), secondRun.canonicalView());
Add assertions that all 240 planned paths contain only grid-adjacent cells and that mutating all historical path_info values leaves the canonical planned-route view unchanged.
- Step 2: Run the real-data test and observe RED
Run: cd javaTest && mvn -Dtest=HistoricalDispatchSimulationRealDataTest test
Expected: at least the 240/0/0 assertion fails until direction ownership and clearance progress are complete.
- Step 3: Distinguish progress from churn and fail fast
Feed the watchdog only on the three approved progress events. Include active task IDs, owners, vehicle positions, and held reasons in the exception message. Make tick-limit exhaustion an exception for this acceptance configuration.
- Step 4: Run twice and reconcile exact results
Run: cd javaTest && mvn -Dtest=HistoricalDispatchSimulationRealDataTest test
Expected: two identical runs, 240/0/0, archive 240, WMS 480/240, deadlocks 0, safety 0.
- Step 5: Commit
git add javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation javaTest/src/test/java/com/ft546/wcs/shadow/history/simulation
git commit -m "test(javaTest): require all history tasks to complete"
Task 7: Publish complete center-dispatch evidence
Files:
- Modify:
javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/HistoricalDispatchSimulationResult.java - Modify:
javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/HistoricalDispatchSimulationReportWriter.java - Modify:
javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/HistoricalDispatchSimulationApplication.java - Modify:
javaTest/src/test/java/com/ft546/wcs/shadow/history/simulation/HistoricalDispatchSimulationReportWriterTest.java - Modify:
javaTest/README.md - Create at runtime:
javaTest/outputs/wcs-history-driven-center-dispatch-<runId>/
Interfaces:
-
Summary adds
fullMapRows,passableMapRows,fullMapSha256,plannerVersion, andpathInfoUsedForPlanning=false. -
Task CSV adds business/physical endpoints, historical/simulated vehicles, historical/new paths, wait/completion durations, HOLD/retry causes, archive count, and WMS attempt/effect counts.
-
Workbook contains overview, task detail, vehicle totals, HOLD/retry detail, safety invariants, and scope statement.
-
Step 1: Add failing report assertions
assertEquals(240, summary.path("completedCount").asInt());
assertFalse(summary.path("pathInfoUsedForPlanning").asBoolean(true));
assertEquals(2552, summary.path("fullMapRows").asInt());
assertEquals(480, summary.path("wmsDeliveryAttempts").asInt());
assertTrue(Files.exists(output.resolve("readable-report.xlsx")));
Open the workbook with POI and assert the overview contains status totals, per-vehicle totals, average/P95/P99 waits and completions, HOLD/retry causes, historical-vs-simulated duration difference, safety results, archive count, and WMS idempotency totals.
- Step 2: Run report test and observe RED
Run: cd javaTest && mvn -Dtest=HistoricalDispatchSimulationReportWriterTest test
- Step 3: Implement report fields and workbook
Calculate percentile indexes deterministically from sorted millisecond durations. Derive HOLD/retry causes from events rather than hard-coded labels. Preserve full historical and planned paths in detail output. Include the statement 真实历史负载驱动纯内存仿真,不是现场 SHADOW/HIL/ACTIVE 验收 on the workbook overview.
- Step 4: Generate a new evidence directory
Run: cd javaTest && mvn compile exec:java -Dexec.mainClass=com.ft546.wcs.shadow.history.simulation.HistoricalDispatchSimulationApplication -Dexec.args="outputs/wcs-history-driven-center-dispatch-20260731 100"
Do not overwrite the old 18/112/110 output. Verify manifest.sha256 covers summary.json, task-results.csv, tick-events.csv, invariant-results.json, and readable-report.xlsx.
- Step 5: Run report test GREEN and commit
Run: cd javaTest && mvn -Dtest=HistoricalDispatchSimulationReportWriterTest test
git add javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation javaTest/src/test/java/com/ft546/wcs/shadow/history/simulation javaTest/README.md javaTest/outputs/wcs-history-driven-center-dispatch-20260731
git commit -m "docs(javaTest): publish complete center dispatch evidence"
Task 8: Full verification and independent review
Files:
- Modify only files required by concrete review findings.
Interfaces:
-
Produces the final reproducible acceptance evidence and a clean worktree.
-
Step 1: Run the entire module suite
Run: cd javaTest && mvn clean test
Expected: all tests pass with zero failures, errors, or skips.
- Step 2: Run the real workload one more time after
clean
Run: cd javaTest && mvn -Dtest=HistoricalDispatchSimulationRealDataTest test
Expected: 240/0/0, deterministic canonical view, archive 240, WMS 480/240, deadlocks and safety violations 0.
- Step 3: Reconcile evidence hashes and counts independently
Recalculate SHA-256 for every manifest entry, count 240 task-result rows, check unique simulation IDs/task archive keys, count exactly 480 WMS delivery events and 240 business effects, and confirm no invariant row is nonzero.
- Step 4: Request code review
Use superpowers:requesting-code-review against the complete diff. Require findings to cite file and line and specifically inspect path_info data flow, full-map hash verification, clearance movement adjacency, corridor release, watchdog semantics, archive uniqueness, and WMS idempotency.
- Step 5: Apply verified findings and rerun affected plus full tests
Use superpowers:receiving-code-review for any finding. Reproduce it, add or adjust a failing test, make the smallest correction, then run cd javaTest && mvn clean test again.
- Step 6: Check repository state
Run: git status --short && git log --oneline -8
Expected: no uncommitted changes and all task commits present on optimize/wcs.