diff --git a/docs/superpowers/plans/2026-08-07-javaTest-source-aligned-two-stage-task.md b/docs/superpowers/plans/2026-08-07-javaTest-source-aligned-two-stage-task.md new file mode 100644 index 0000000..3a71405 --- /dev/null +++ b/docs/superpowers/plans/2026-08-07-javaTest-source-aligned-two-stage-task.md @@ -0,0 +1,1009 @@ +# javaTest Source-Aligned Two-Stage Task 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:** Turn the 240 raw FT546 queue snapshots into an auditable dual-mode replay and execute the 76 reconstructed business tasks through source-aligned pickup, loaded movement, aisle-front return, archive, WMS callback, and vehicle-release phases. + +**Architecture:** Keep the existing raw snapshot simulation as a compatibility baseline, and add a source-aligned business input factory that assembles parent tasks from queue-stage snapshots. Extend the existing deterministic simulator with a small phase coordinator so every movement phase still uses the current vehicle assignment, path planning, floor-transfer gateway, traffic ledger, PLC ACK, reroute, and resource-release mechanisms. + +**Tech Stack:** Java 8, JUnit 5, Jackson 2.17, Maven Surefire, JDK `HttpServer`, plain HTML/CSS/JavaScript, Python Playwright browser QA. + +## Global Constraints + +- Only modify `javaTest`, its tests, resources, scripts, and Superpowers documentation; do not modify production PLC or database code. +- `javaTest` must not connect to SQL Server at runtime; `channel-fronts.json` is an immutable offline snapshot derived from the production `bas_cargo_space` query documented in the design. +- Preserve `RAW_SNAPSHOT` replay and its existing 240-result behavior for comparison. +- Make `SOURCE_ALIGNED_BUSINESS` the default historical replay mode. +- The current fixture must audit exactly 240 raw rows, 98 unique GUIDs, 142 duplicate snapshots, 76 business tasks, and 17 operational queue GUIDs. +- Business type comes from `default02=1/2/7`; historical `task_type=1/2/3` remains queue-stage evidence. +- Historical `agv_id`, `path_info`, completion status, and `finish_date` never force assignment, planning, or completion. +- After pickup, a loaded task remains bound to the same vehicle across HOLD, disconnect, restart, low battery, and recoverable faults. +- Archive and WMS callback happen once at business cargo completion; inbound automatic return may finish later and must not trigger a second business effect. +- Every new behavior follows red-green-refactor; run each named test while red and again while green. +- Keep all user-owned untracked HTML files untouched. + +--- + +## File Structure + +New focused domain files: + +- `javaTest/src/main/java/com/ft546/wcs/shadow/history/HistoricalChannelFrontRecord.java`: Jackson DTO for the offline SQL snapshot. +- `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/ChannelFrontTopology.java`: immutable `cargo site -> authoritative aisle front` lookup. +- `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/SimulationExecutionMode.java`: raw versus source-aligned execution mode. +- `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/HistoricalInputAudit.java`: raw/unique/business/operational counts. +- `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/HistoricalTaskSnapshotEvidence.java`: raw GUID and stage evidence attached to a parent task. +- `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/HistoricalBusinessTaskAssembler.java`: snapshot deduplication and parent-task reconstruction. +- `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/HistoricalBusinessAssembly.java`: assembler output. +- `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/BusinessTaskPhase.java`: source-aligned phase names. +- `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/CargoState.java`: empty/loaded/deposited state. +- `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/VehicleReleaseStatus.java`: not-required/pending/completed/failed cleanup result. +- `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/BusinessTaskLeg.java`: one phase endpoint and completion semantics. +- `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/BusinessPhasePlan.java`: immutable ordered legs for one parent task. +- `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/BusinessPhasePlanner.java`: builds inbound/outbound/relocation legs. +- `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/TaskPhaseEvidence.java`: immutable path, timing, HOLD, and retry evidence for a phase. +- `javaTest/src/main/java/com/ft546/wcs/shadow/history/visualizer/HistoricalSimulationRequest.java`: historical API mode request. + +Existing owners remain stable: + +- `HistoricalSimulationInputFactory` compiles maps and exposes `create(HistoricalQueueDataset)` plus `createRawSnapshots(HistoricalQueueDataset)`. +- `HistoricalDispatchSimulator` owns tick orchestration and delegates phase choice to `BusinessPhasePlanner`. +- `SimulationTaskResult` and `HistoricalDispatchSimulationResult` own immutable audit output. +- `SimulationVisualizationMapper` maps output; browser JavaScript only renders supplied evidence and never infers topology. + +--- + +### Task 1: Load Source Queue Fields And Authoritative Channel Fronts + +**Files:** +- Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/HistoricalChannelFrontRecord.java` +- Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/ChannelFrontTopology.java` +- Modify: `javaTest/src/main/java/com/ft546/wcs/shadow/history/HistoricalQueueRecord.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/test/resources/shadow/history/channel-fronts.json` +- Modify: `javaTest/src/test/resources/shadow/history/manifest.json` +- Test: `javaTest/src/test/java/com/ft546/wcs/shadow/HistoricalQueueLoaderTest.java` +- Test: `javaTest/src/test/java/com/ft546/wcs/shadow/history/simulation/ChannelFrontTopologyTest.java` + +**Interfaces:** +- Produces: `HistoricalQueueRecord.getSnapshotRow/getCargoPickSite/getCargoPlaceSite/getDefault02/getDefault06/getDefault08`. +- Produces: `HistoricalQueueDataset.getChannelFrontRecords()`. +- Produces: `ChannelFrontTopology.from(List)`, `frontFor(String)`, and `requiresFrontMovement(String)`. + +- [ ] **Step 1: Write failing loader and topology tests** + +```java +@Test +void loadsProductionQueueStageFieldsAndChannelFrontSnapshot() { + HistoricalQueueDataset dataset = new HistoricalQueueLoader().load( + getClass().getClassLoader(), "shadow/history"); + HistoricalQueueRecord first = dataset.getRecords().get(0); + + assertEquals("1393", first.getSnapshotRow()); + assertEquals("050101", first.getCargoPickSite()); + assertEquals("030401", first.getCargoPlaceSite()); + assertEquals("8", first.getDefault02()); + assertEquals(17, dataset.getChannelFrontRecords().size()); +} + +@Test +void resolvesTheSameFrontAsTheProductionGroupCodingQuery() { + ChannelFrontTopology topology = ChannelFrontTopology.from(records); + + assertEquals("050301", topology.frontFor("030301").get()); + assertEquals("050401", topology.frontFor("040401").get()); + assertFalse(topology.frontFor("070101").isPresent()); +} +``` + +- [ ] **Step 2: Run tests and verify the missing getters/resource fail** + +Run: + +```bash +cd javaTest +mvn -Dtest=HistoricalQueueLoaderTest,ChannelFrontTopologyTest test +``` + +Expected: test compilation fails because source queue fields, channel-front DTO, and topology do not exist. + +- [ ] **Step 3: Add exact JSON fields and immutable topology** + +```java +@JsonProperty("snapshot_row") private String snapshotRow; +@JsonProperty("cargo_pick_site") private String cargoPickSite; +@JsonProperty("cargo_place_site") private String cargoPlaceSite; +@JsonProperty("default02") private String default02; +@JsonProperty("default06") private String default06; +@JsonProperty("default08") private String default08; +``` + +```java +public final class ChannelFrontTopology { + private final Map frontBySite; + + private ChannelFrontTopology(Map frontBySite) { + this.frontBySite = Collections.unmodifiableMap( + new LinkedHashMap(frontBySite)); + } + + public static ChannelFrontTopology from(List records) { + Map values = new LinkedHashMap(); + for (HistoricalChannelFrontRecord record : records) { + String previous = values.put(record.getSiteId(), record.getFrontSiteId()); + if (previous != null && !previous.equals(record.getFrontSiteId())) { + throw new IllegalArgumentException( + "conflicting channel front for " + record.getSiteId()); + } + } + return new ChannelFrontTopology(values); + } + + public Optional frontFor(String siteId) { + return Optional.ofNullable(frontBySite.get(siteId)); + } + + public boolean requiresFrontMovement(String siteId) { + Optional front = frontFor(siteId); + return front.isPresent() && !front.get().equals(siteId); + } +} +``` + +Add these 17 verified mappings from the production read-only query: `010701 -> 050701`, `030301 -> 050301`, `030401 -> 050401`, `030501 -> 050501`, `030601 -> 050601`, `040301 -> 050301`, `040401 -> 050401`, `050203 -> 050203`, `081301 -> 071301`, `090901 -> 090901`, `091101 -> 091101`, `110301 -> 110301`, `110302 -> 110302`, `110402 -> 110402`, `110501 -> 110501`, `110601 -> 110601`, and `110801 -> 110801`. Add its canonical SHA-256 and row count to `manifest.json`; make `HistoricalQueueLoader` verify both. + +Keep the current `HistoricalQueueDataset` constructor as an overload that delegates with an empty channel-front list, so existing synthetic unit-test fixtures do not need a resource they do not use. + +- [ ] **Step 4: Run focused tests and all existing loader tests** + +Run: + +```bash +cd javaTest +mvn -Dtest=HistoricalQueueLoaderTest,HistoricalQueueDatabaseReplayTest,ChannelFrontTopologyTest test +``` + +Expected: all selected tests pass; changing one byte in the channel-front resource makes the SHA contract test fail. + +- [ ] **Step 5: Commit the snapshot contract** + +```bash +git add javaTest/src/main/java/com/ft546/wcs/shadow/history \ + javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/ChannelFrontTopology.java \ + javaTest/src/test/java/com/ft546/wcs/shadow/history \ + javaTest/src/test/java/com/ft546/wcs/shadow/history/simulation/ChannelFrontTopologyTest.java \ + javaTest/src/test/resources/shadow/history/channel-fronts.json \ + javaTest/src/test/resources/shadow/history/manifest.json +git commit -m "feat(javaTest): load source task and aisle front fields" +``` + +--- + +### Task 2: Reconstruct 76 Business Parent Tasks And Preserve Raw Audit + +**Files:** +- Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/SimulationExecutionMode.java` +- Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/HistoricalInputAudit.java` +- Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/HistoricalTaskSnapshotEvidence.java` +- Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/HistoricalBusinessAssembly.java` +- Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/HistoricalBusinessTaskAssembler.java` +- Modify: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/HistoricalSimulationTask.java` +- Modify: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/HistoricalSimulationInput.java` +- Modify: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/HistoricalSimulationInputFactory.java` +- Modify: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/PointToPointSimulationInputFactory.java` +- Test: `javaTest/src/test/java/com/ft546/wcs/shadow/history/simulation/HistoricalBusinessTaskAssemblerTest.java` +- Modify test: `javaTest/src/test/java/com/ft546/wcs/shadow/history/simulation/HistoricalSimulationInputFactoryTest.java` + +**Interfaces:** +- Produces: `HistoricalBusinessTaskAssembler.assemble(HistoricalQueueDataset, CompiledHistoricalMap, ChannelFrontTopology)`. +- Produces: `HistoricalSimulationInputFactory.create(dataset)` as source-aligned default. +- Produces: `HistoricalSimulationInputFactory.createRawSnapshots(dataset)` as compatibility input. +- Produces: `HistoricalSimulationInput.getExecutionMode/getInputAudit/getChannelFrontTopology`. +- Produces: parent tasks with business type, cargo endpoints, front endpoints, and raw snapshot evidence. +- Produces: point-to-point inputs in `SOURCE_ALIGNED_BUSINESS` mode, using the base input's channel-front lookup for requested A/B endpoints. + +- [ ] **Step 1: Write the exact fixture-count and conflict tests** + +```java +@Test +void assemblesTheCurrentSnapshotIntoSourceAlignedBusinessTasks() { + HistoricalSimulationInput input = new HistoricalSimulationInputFactory().create(dataset); + + assertEquals(SimulationExecutionMode.SOURCE_ALIGNED_BUSINESS, + input.getExecutionMode()); + assertEquals(76, input.getTasks().size()); + assertEquals(38, count(INBOUND, input)); + assertEquals(33, count(OUTBOUND, input)); + assertEquals(5, count(RELOCATION, input)); + assertEquals(240, input.getInputAudit().getRawSnapshotCount()); + assertEquals(98, input.getInputAudit().getUniqueQueueCount()); + assertEquals(142, input.getInputAudit().getDuplicateSnapshotCount()); + assertEquals(17, input.getInputAudit().getOperationalQueueCount()); +} + +@Test +void rejectsConflictingBusinessCodesForOneParentTask() { + HistoricalBusinessAssembly assembly = assembler.assemble( + datasetWithSameTaskNoAndDefault02("1", "2"), compiled, fronts); + + assertEquals(0, assembly.getTasks().size()); + assertEquals("CONFLICTING_HISTORY_SNAPSHOTS:BUSINESS_TYPE", + assembly.getFailures().get(0).getReason()); +} +``` + +- [ ] **Step 2: Run the assembler tests and verify existing row-per-task behavior fails** + +Run: + +```bash +cd javaTest +mvn -Dtest=HistoricalBusinessTaskAssemblerTest,HistoricalSimulationInputFactoryTest test +``` + +Expected: failures show `create(HistoricalQueueDataset)` still returns 240 row tasks and lacks input audit. + +- [ ] **Step 3: Implement stable grouping and source-aligned task creation** + +```java +public final class HistoricalBusinessTaskAssembler { + public HistoricalBusinessAssembly assemble( + HistoricalQueueDataset dataset, + HistoricalGridMapCompiler.CompiledHistoricalMap compiled, + ChannelFrontTopology fronts) { + if (dataset == null || compiled == null || fronts == null) { + throw new IllegalArgumentException( + "dataset, compiled map and channel fronts are required"); + } + List ordered = stableOrder(dataset.getRecords()); + Map> groups = groupBusinessRows(ordered); + return buildAssembly(dataset.getRecords(), groups, compiled, fronts); + } + + private DispatchTask.TaskType businessType(List rows) { + // Exactly one of default02 1, 2, or 7 may appear for a parent task. + } +} +``` + +Use `task_no + cargo_pick_site + cargo_place_site` as the business grouping key, sort groups by earliest `create_date/task_no`, and create IDs as `BUSINESS@#`. Keep every raw GUID/snapshot row as `HistoricalTaskSnapshotEvidence`. Resolve physical endpoints from `cargo_pick_site/cargo_place_site`, not historical queue `start_site/end_site`. + +- [ ] **Step 4: Preserve raw compatibility explicitly** + +Move the current row-per-record factory path behind: + +```java +public HistoricalSimulationInput createRawSnapshots(HistoricalQueueDataset dataset) +``` + +Its input mode is `RAW_SNAPSHOT`, its task count remains 240, and its existing physical endpoint logic remains unchanged. + +Keep current `HistoricalSimulationInput` and `HistoricalSimulationTask` constructors as raw-compatible overloads for existing synthetic tests. Add full constructors that accept execution mode, input audit, channel fronts, source/target fronts, and snapshot evidence. `PointToPointSimulationInputFactory` calls the full constructor with `SOURCE_ALIGNED_BUSINESS`; it must not silently fall back to raw one-leg behavior. + +- [ ] **Step 5: Run focused tests** + +Run: + +```bash +cd javaTest +mvn -Dtest=HistoricalBusinessTaskAssemblerTest,HistoricalSimulationInputFactoryTest test +``` + +Expected: business mode reports the exact 240/98/142/76/17 audit; raw mode still contains 240 unique simulation IDs. + +- [ ] **Step 6: Commit business reconstruction** + +```bash +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): reconstruct historical business parent tasks" +``` + +--- + +### Task 3: Model Source-Aligned Business Phases And Cargo State + +**Files:** +- Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/BusinessTaskPhase.java` +- Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/CargoState.java` +- Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/VehicleReleaseStatus.java` +- Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/BusinessTaskLeg.java` +- Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/BusinessPhasePlan.java` +- Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/BusinessPhasePlanner.java` +- Test: `javaTest/src/test/java/com/ft546/wcs/shadow/history/simulation/BusinessPhasePlannerTest.java` + +**Interfaces:** +- Produces: `BusinessPhasePlanner.plan(HistoricalSimulationTask)`. +- Produces: immutable ordered `BusinessTaskLeg` values with `phase`, `targetSite`, `cargoStateOnArrival`, `completesBusiness`, and `releasesVehicle`. + +- [ ] **Step 1: Write inbound, outbound, relocation, and no-front tests** + +```java +@Test +void inboundDepositsThenReturnsEmptyToTargetFront() { + BusinessPhasePlan plan = planner.plan(task(INBOUND, + "050101", "030401", null, "050401")); + + assertEquals(Arrays.asList( + EMPTY_TO_PICKUP, + LOADED_TO_PLACE, + EMPTY_RETURN_TO_TARGET_FRONT), phases(plan)); + assertTrue(plan.leg(LOADED_TO_PLACE).completesBusiness()); + assertEquals(CargoState.DEPOSITED, + plan.leg(LOADED_TO_PLACE).getCargoStateOnArrival()); + assertTrue(plan.leg(EMPTY_RETURN_TO_TARGET_FRONT).releasesVehicle()); +} + +@Test +void outboundReturnsLoadedCargoToSourceFrontBeforeDelivery() { + BusinessPhasePlan plan = planner.plan(task(OUTBOUND, + "030301", "070101", "050301", null)); + + assertEquals(Arrays.asList( + EMPTY_TO_PICKUP, + LOADED_RETURN_TO_SOURCE_FRONT, + LOADED_TO_PLACE), phases(plan)); +} +``` + +Also assert relocation applies both front movements, while an external site with no mapping produces only `EMPTY_TO_PICKUP -> LOADED_TO_PLACE` and records `NO_CHANNEL_OPERATION` as a skipped-stage reason. + +- [ ] **Step 2: Run the phase tests and verify the domain types are missing** + +Run: + +```bash +cd javaTest +mvn -Dtest=BusinessPhasePlannerTest test +``` + +Expected: test compilation fails for missing phase and cargo types. + +- [ ] **Step 3: Implement immutable phase planning** + +```java +public BusinessPhasePlan plan(HistoricalSimulationTask task) { + List legs = new ArrayList(); + legs.add(BusinessTaskLeg.emptyToPickup(task.getPhysicalSource())); + if ((task.getTaskType() == OUTBOUND || task.getTaskType() == RELOCATION) + && task.requiresSourceFrontMovement()) { + legs.add(BusinessTaskLeg.loadedReturnToSourceFront(task.getSourceFrontSite())); + } + legs.add(BusinessTaskLeg.loadedToPlace(task.getPhysicalTarget())); + if ((task.getTaskType() == INBOUND || task.getTaskType() == RELOCATION) + && task.requiresTargetFrontMovement()) { + legs.add(BusinessTaskLeg.emptyReturnToTargetFront(task.getTargetFrontSite())); + } + return new BusinessPhasePlan(legs); +} +``` + +Reject blank endpoints, identical duplicate consecutive legs, and a front site equal to the opposite business endpoint only when it would create a zero-length semantic phase. + +- [ ] **Step 4: Run phase tests** + +Run: + +```bash +cd javaTest +mvn -Dtest=BusinessPhasePlannerTest test +``` + +Expected: all phase order, cargo transition, completion, release, and skipped-stage assertions pass. + +- [ ] **Step 5: Commit phase domain** + +```bash +git add javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/BusinessTaskPhase.java \ + javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/CargoState.java \ + javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/VehicleReleaseStatus.java \ + javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/BusinessTaskLeg.java \ + javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/BusinessPhasePlan.java \ + javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/BusinessPhasePlanner.java \ + javaTest/src/test/java/com/ft546/wcs/shadow/history/simulation/BusinessPhasePlannerTest.java +git commit -m "feat(javaTest): model source-aligned task phases" +``` + +--- + +### Task 4: Make PLC Commands Phase-Scoped Without Breaking Raw Replay + +**Files:** +- Modify: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/DeterministicFaultPlan.java` +- Modify: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/SimulatedPlcGateway.java` +- Test: `javaTest/src/test/java/com/ft546/wcs/shadow/history/simulation/PhaseAwarePlcGatewayTest.java` +- Modify test: `javaTest/src/test/java/com/ft546/wcs/shadow/history/simulation/SimulationAdaptersTest.java` + +**Interfaces:** +- Produces: `send(parentTaskId, commandId, vehicle, from, to)`. +- Produces: `attemptsForTask(parentTaskId)` and `attemptsForCommand(commandId)`. +- Produces: `DeterministicFaultPlan.Builder.plcCommandFailure(commandId, attempt, reason)` while preserving task-wide `plcFailure(taskId, attempt, reason)` fallback. + +- [ ] **Step 1: Write phase-isolation tests** + +```java +@Test +void failureOnLoadedPhaseDoesNotConsumeEmptyPhaseAttemptNumbers() { + String loadedCommand = "B1:LOADED_TO_PLACE:v1:e0"; + DeterministicFaultPlan plan = DeterministicFaultPlan.builder() + .plcCommandFailure(loadedCommand, 1, SEND_FAILURE) + .build(); + SimulatedPlcGateway gateway = new SimulatedPlcGateway(plan); + + assertEquals(ACK, gateway.send("B1", "B1:EMPTY_TO_PICKUP:v1:e0", + vehicle, "A", "B").getReason()); + assertEquals(SEND_FAILURE, gateway.send("B1", loadedCommand, + vehicle, "B", "C").getReason()); + assertEquals(2, gateway.attemptsForTask("B1")); + assertEquals(1, gateway.attemptsForCommand(loadedCommand)); +} +``` + +- [ ] **Step 2: Run tests and observe the missing command API failure** + +Run: + +```bash +cd javaTest +mvn -Dtest=PhaseAwarePlcGatewayTest,SimulationAdaptersTest test +``` + +Expected: compilation fails for `plcCommandFailure`, the five-argument `send`, and command counters. + +- [ ] **Step 3: Implement task totals plus command-local attempts** + +```java +public PlcResponse send(String parentTaskId, String commandId, + SimulatedVehicleRuntime vehicle, String from, String to) { + int commandAttempt = increment(commandAttempts, commandId); + increment(taskAttempts, parentTaskId); + PlcReason reason = faults.plcReason(parentTaskId, commandId, commandAttempt); + return response(parentTaskId, commandId, vehicle, from, to, commandAttempt, reason); +} +``` + +Keep the existing four-argument `send(taskId, vehicle, from, to)` as a raw-compatible delegation using `commandId=taskId`. `PlcResponse` exposes both parent task ID and command ID. + +- [ ] **Step 4: Run PLC tests** + +Run: + +```bash +cd javaTest +mvn -Dtest=PhaseAwarePlcGatewayTest,SimulationAdaptersTest test +``` + +Expected: command-local failure and task-total assertions pass; all old raw gateway assertions remain green. + +- [ ] **Step 5: Commit phase-scoped protocol behavior** + +```bash +git add javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/DeterministicFaultPlan.java \ + javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/SimulatedPlcGateway.java \ + javaTest/src/test/java/com/ft546/wcs/shadow/history/simulation/PhaseAwarePlcGatewayTest.java \ + javaTest/src/test/java/com/ft546/wcs/shadow/history/simulation/SimulationAdaptersTest.java +git commit -m "feat(javaTest): scope PLC retries by business phase" +``` + +--- + +### Task 5: Execute Multi-Phase Routes And Complete Business Before Vehicle Return + +**Files:** +- Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/TaskPhaseEvidence.java` +- Modify: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/HistoricalDispatchSimulator.java` +- Modify: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/SimulationTaskResult.java` +- Modify: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/SimulationArchive.java` +- Test: `javaTest/src/test/java/com/ft546/wcs/shadow/history/simulation/HistoricalDispatchSimulatorTwoStageTest.java` + +**Interfaces:** +- Produces: `SimulationTaskResult.getPhaseEvidence/getCargoState/getBusinessCompletedMillis/getVehicleReleasedMillis/getVehicleReleaseStatus`. +- Produces: event types `PICKUP_CONFIRMED`, `SOURCE_FRONT_REACHED`, `CARGO_DEPOSITED`, `BUSINESS_ARCHIVED`, `WMS_CALLBACK`, `TARGET_FRONT_REACHED`, and `VEHICLE_RELEASED`. + +- [ ] **Step 1: Write deterministic inbound and outbound integration tests** + +```java +@Test +void inboundCallbacksAtDepositThenReturnsTheSameVehicleEmptyToFront() { + HistoricalDispatchSimulationResult result = simulator.simulate( + inboundInput("PICK", "BIN", "FRONT"), config, noFaults()); + SimulationTaskResult task = result.getTaskResults().get(0); + + assertEquals(Arrays.asList(EMPTY_TO_PICKUP, LOADED_TO_PLACE, + EMPTY_RETURN_TO_TARGET_FRONT), phaseNames(task)); + assertTrue(task.getBusinessCompletedMillis() < task.getVehicleReleasedMillis()); + assertEquals(CargoState.DEPOSITED, task.getCargoState()); + assertEquals(VehicleReleaseStatus.COMPLETED, task.getVehicleReleaseStatus()); + assertEquals(1, result.getArchiveCount()); + assertEquals(1, result.getWmsBusinessEffects()); + assertEquals("FRONT", lastMoveSite(result, task)); +} + +@Test +void outboundCarriesCargoToSourceFrontBeforeBusinessDestination() { + SimulationTaskResult task = runOutbound("BIN", "FRONT", "EXIT"); + + assertEquals(Arrays.asList(EMPTY_TO_PICKUP, + LOADED_RETURN_TO_SOURCE_FRONT, LOADED_TO_PLACE), phaseNames(task)); + assertEquals(Arrays.asList("BIN", "FRONT", "EXIT"), phaseTargets(task)); + assertEquals(task.getBusinessCompletedMillis(), task.getVehicleReleasedMillis()); +} +``` + +- [ ] **Step 2: Run tests and verify the simulator currently terminates after one route** + +Run: + +```bash +cd javaTest +mvn -Dtest=HistoricalDispatchSimulatorTwoStageTest test +``` + +Expected: failures show missing phase evidence and completion at the first route endpoint. + +- [ ] **Step 3: Add phase runtime to the existing `Execution` and `ActiveTask`** + +```java +private static final class Execution { + private BusinessPhasePlan phasePlan; + private int phaseIndex; + private CargoState cargoState = CargoState.EMPTY; + private Long businessCompletedMillis; + private Long vehicleReleasedMillis; + private VehicleReleaseStatus vehicleReleaseStatus = VehicleReleaseStatus.PENDING; + private final List phaseEvidence = new ArrayList(); +} + +private static final class ActiveTask { + private final BusinessTaskPhase phase; + private final int routeVersion; + // Existing route/index/resource behavior remains authoritative. +} +``` + +For `RAW_SNAPSHOT`, keep the current combined approach/service route and current completion method. For `SOURCE_ALIGNED_BUSINESS`, assignment creates the phase plan and activates only `EMPTY_TO_PICKUP`. + +- [ ] **Step 4: Replace terminal-on-last-edge with phase transition** + +When `current.index >= current.route.size() - 1`, branch on `input.getExecutionMode()`: call the existing raw completion path for `RAW_SNAPSHOT`; call the new `completeBusinessPhase` with the same archive, WMS, invariant, reservation, terminal, active-map, and event dependencies for `SOURCE_ALIGNED_BUSINESS`. Keep this branch at the current ACK commit point so no phase can complete before physical movement is committed. + +`completeBusinessPhase` releases the phase reservation, freezes its evidence, applies cargo state, emits its event, and activates the next phase from the vehicle's actual site. At `LOADED_TO_PLACE`, call archive and WMS exactly once. If an inbound/relocation target-return leg exists, keep the result nonterminal until vehicle release but preserve the earlier business completion timestamp. + +- [ ] **Step 5: Replan every phase independently** + +Create `activateNextBusinessPhase` in the simulator with explicit inputs for tick, time, simulation input, execution, vehicle, active map, floor-transfer map, route planner/evaluator, floor-transfer gateway, PLC, ledger, reservations, and events. It calls the existing route candidate planner from `vehicle.currentSite` to `leg.targetSite`, records planning evidence under the phase, obtains corridor reservation, and creates a new `ActiveTask`. It never concatenates historical `path_info` or a future phase route. + +- [ ] **Step 6: Run the new tests and raw simulator regression** + +Run: + +```bash +cd javaTest +mvn -Dtest=HistoricalDispatchSimulatorTwoStageTest,HistoricalDispatchSimulatorTest test +``` + +Expected: inbound/outbound phase assertions pass; existing raw movement, conflict, reroute, archive, and WMS tests pass. + +- [ ] **Step 7: Commit phase execution** + +```bash +git add javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/TaskPhaseEvidence.java \ + javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/HistoricalDispatchSimulator.java \ + javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/SimulationTaskResult.java \ + javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/SimulationArchive.java \ + javaTest/src/test/java/com/ft546/wcs/shadow/history/simulation/HistoricalDispatchSimulatorTwoStageTest.java +git commit -m "feat(javaTest): execute source-aligned business phases" +``` + +--- + +### Task 6: Preserve Loaded-Vehicle Binding Across Faults, Retries, And Floor Transfer + +**Files:** +- Modify: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/HistoricalDispatchSimulator.java` +- Modify: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/FloorTransferJob.java` +- Modify: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/DeterministicFaultPlan.java` +- Modify test: `javaTest/src/test/java/com/ft546/wcs/shadow/history/simulation/HistoricalDispatchSimulatorTwoStageTest.java` +- Modify test: `javaTest/src/test/java/com/ft546/wcs/shadow/history/simulation/HistoricalDispatchFaultMatrixTest.java` + +**Interfaces:** +- Produces: floor-transfer jobs associated with `parentTaskId` and `BusinessTaskPhase`. +- Produces: phase-specific HOLD reasons and same-vehicle recovery evidence. +- Preserves: pre-pickup tasks may requeue; loaded tasks never auto-reassign. + +- [ ] **Step 1: Write loaded-fault, phase-retry, and loaded-floor-transfer tests** + +```java +@Test +void loadedVehicleRemainsBoundAcrossDisconnectRestartAndSynchronize() { + DeterministicFaultPlan faults = DeterministicFaultPlan.builder() + .vehicleEvent(4, "1", DISCONNECT) + .vehicleEvent(5, "1", CONNECT) + .vehicleEvent(6, "1", RESTART) + .vehicleEvent(8, "1", SYNCHRONIZE) + .build(); + SimulationTaskResult task = runOutboundWithFaults(faults); + + assertEquals(Collections.singletonList("1"), task.getAssignedVehicles()); + assertTrue(task.getHoldReasons().containsKey("DISCONNECTED_LOADED")); + assertTrue(task.getHoldReasons().containsKey("RESTART_NOT_SYNCHRONIZED_LOADED")); + assertEquals(COMPLETED, task.getStatus()); +} + +@Test +void loadedCrossFloorLegUsesOneIdempotentTransferJobForThatPhase() { + SimulationTaskResult task = runCrossFloorLoadedLeg(); + + assertEquals(1, task.getFloorTransferCount()); + assertEquals(LOADED_TO_PLACE, + task.getFloorTransferJobs().get(0).getBusinessPhase()); + assertEquals(task.getSimulatedVehicleId(), + task.getFloorTransferJobs().get(0).getVehicleId()); +} +``` + +Add these tests in the same class: + +- a phase-command PLC failure does not move the vehicle, only the current phase retry count increases, and archive/WMS remain one; +- two inbound tasks returning through the same target front produce `PHASE_HOLD` while preserving zero node double-occupancy and both eventually complete; +- a configured channel front that is absent from `GridMap` closes before assignment as `UNKNOWN_MAP_SITE:CHANNEL_FRONT`; +- a permanent disconnection between a valid front and the next phase endpoint closes as `NO_ROUTE_` instead of waiting forever. + +- [ ] **Step 2: Run the fault tests and observe current redispatch behavior fail** + +Run: + +```bash +cd javaTest +mvn -Dtest=HistoricalDispatchSimulatorTwoStageTest,HistoricalDispatchFaultMatrixTest test +``` + +Expected: loaded disconnect or retry exhaustion currently releases the active task or lacks phase-scoped HOLD evidence. + +- [ ] **Step 3: Make requeue policy cargo-aware** + +```java +private boolean mayRedispatch(Execution execution) { + return execution.cargoState == CargoState.EMPTY + && execution.currentPhase() == BusinessTaskPhase.EMPTY_TO_PICKUP; +} +``` + +Use this gate in vehicle-event handling, retry exhaustion, and reroute failure. For a loaded execution, keep `active` and reservations consistent, emit a loaded HOLD, and resume from the same vehicle's actual site after synchronization. A permanent loaded-vehicle failure closes vehicle release as `FAILED/MANUAL_CARGO_RECOVERY_REQUIRED`; it does not assign another vehicle or repeat WMS effects. + +- [ ] **Step 4: Generalize the existing floor-transfer activation to the current phase target** + +`FloorTransferExecution` stores the pending `BusinessTaskPhase`. When a phase endpoint is on another floor, submit a transfer from the vehicle's current floor before that phase route. On completion, move to the actual exit and replan only the pending phase. Do not create a normal cross-floor `GridMap` edge. + +- [ ] **Step 5: Run focused and existing floor-transfer tests** + +Run: + +```bash +cd javaTest +mvn -Dtest=HistoricalDispatchSimulatorTwoStageTest,HistoricalDispatchFaultMatrixTest,SimulatedFloorTransferGatewayTest,FloorTransferVisualizationMapperTest test +``` + +Expected: loaded tasks retain one vehicle, one transfer job per phase intent, and one business completion; all existing transfer idempotency tests pass. + +- [ ] **Step 6: Commit fault-safe phase orchestration** + +```bash +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): keep loaded tasks bound through recovery" +``` + +--- + +### Task 7: Produce Dual-Mode Full-History Results And Reports + +**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/SimulationMetrics.java` +- Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/SimulationDurationSummary.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 test: `javaTest/src/test/java/com/ft546/wcs/shadow/history/simulation/HistoricalDispatchSimulationRealDataTest.java` +- Modify test: `javaTest/src/test/java/com/ft546/wcs/shadow/history/simulation/HistoricalDispatchSimulationReportWriterTest.java` + +**Interfaces:** +- Produces: result-level `executionMode` and `inputAudit`. +- Produces: business completion time, vehicle occupancy time, automatic return time, per-phase HOLD/retry counts, and release-failure count. +- CLI: `HistoricalDispatchSimulationApplication [10|100] [business|raw]`, default `business`. + +- [ ] **Step 1: Split real-data acceptance into raw and business assertions** + +```java +@Test +void rawModeRemainsA240SnapshotCompatibilityBaseline() { + assertEquals(RAW_SNAPSHOT, rawResult.getExecutionMode()); + assertEquals(240, rawResult.getCompletedCount()); + assertEquals(240, rawResult.getArchiveCount()); +} + +@Test +void businessModeRunsThe76ReconstructedParentTasksSafely() { + assertEquals(SOURCE_ALIGNED_BUSINESS, businessResult.getExecutionMode()); + assertEquals(240, businessResult.getInputAudit().getRawSnapshotCount()); + assertEquals(76, businessResult.getTaskResults().size()); + assertEquals(76, businessResult.getCompletedCount()); + assertEquals(0, businessResult.getWaitingCount()); + assertEquals(0, businessResult.getFailedCount()); + assertEquals(76, businessResult.getArchiveCount()); + assertEquals(76, businessResult.getWmsBusinessEffects()); + assertEquals(0, businessResult.getIncompleteVehicleReleaseCount()); + assertEquals(0, businessResult.getInvariants().totalSafetyViolations()); +} +``` + +- [ ] **Step 2: Run real-data tests and capture the first actual phase failure** + +Run: + +```bash +cd javaTest +mvn -Dtest=HistoricalDispatchSimulationRealDataTest test +``` + +Expected during red: the result lacks dual-mode/audit fields or a concrete task closes with a phase-specific topology, reservation, or watchdog reason. Do not relax the expected 76 count; fix the first real cause. + +- [ ] **Step 3: Add result audit and phase-aware metrics** + +```java +public int getIncompleteVehicleReleaseCount() { + int count = 0; + for (SimulationTaskResult task : taskResults) { + if (task.getVehicleReleaseStatus() == VehicleReleaseStatus.PENDING + || task.getVehicleReleaseStatus() == VehicleReleaseStatus.FAILED) { + count++; + } + } + return count; +} + +public SimulationDurationSummary businessCompletionDistribution() { + return new SimulationMetrics().summarize(businessCompletionSamples()); +} + +public SimulationDurationSummary vehicleOccupancyDistribution() { + return new SimulationMetrics().summarize(vehicleOccupancySamples()); +} +``` + +`SimulationDurationSummary` is an immutable value with `Double averageMillis`, `Long p95Millis`, and `Long p99Millis`. `SimulationMetrics.summarize(List)` uses the existing `average` and `percentile` methods. `holdCountByPhase` and `retryCountByPhase` iterate every task's `TaskPhaseEvidence` and sum by its `BusinessTaskPhase` key in insertion order. + +Report JSON/CSV/Markdown includes raw audit counts, business counts, per-vehicle completion, reassignment, phase paths, source/target fronts, P50/P95/P99 business completion, vehicle occupancy, automatic return, phase HOLD/retry reasons, history-versus-business-duration delta, archive counts, WMS effects, and safety invariants. + +- [ ] **Step 4: Make the CLI default to business mode and retain raw mode** + +```java +SimulationExecutionMode mode = args.length == 3 && "raw".equalsIgnoreCase(args[2]) + ? RAW_SNAPSHOT : SOURCE_ALIGNED_BUSINESS; +HistoricalSimulationInput input = mode == RAW_SNAPSHOT + ? factory.createRawSnapshots(dataset) : factory.create(dataset); +``` + +- [ ] **Step 5: Run report and real-data tests** + +Run: + +```bash +cd javaTest +mvn -Dtest=HistoricalDispatchSimulationRealDataTest,HistoricalDispatchSimulationReportWriterTest,SimulationMetricsTest test +``` + +Expected: both modes pass deterministically; business reports identify 76 tasks and expose business completion separately from vehicle release. + +- [ ] **Step 6: Commit dual-mode reporting** + +```bash +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): report source-aligned business replay" +``` + +--- + +### Task 8: Expose Phase Evidence In The Audit API And Full-Map UI + +**Required skill before edits:** `frontend-design` for the audit controls and responsive evidence layout. + +**Files:** +- Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/visualizer/HistoricalSimulationRequest.java` +- Modify: `javaTest/src/main/java/com/ft546/wcs/shadow/history/visualizer/HistoricalWorkloadSimulationService.java` +- Modify: `javaTest/src/main/java/com/ft546/wcs/shadow/history/visualizer/SimulationVisualizationMapper.java` +- Modify: `javaTest/src/main/java/com/ft546/wcs/shadow/history/visualizer/HistoricalDispatchVisualizerServer.java` +- Modify: `javaTest/src/main/java/com/ft546/wcs/shadow/history/visualizer/HistoricalDispatchVisualizerApplication.java` +- Modify: `javaTest/src/main/resources/visualizer/index.html` +- Modify: `javaTest/src/main/resources/visualizer/assets/app.js` +- Modify: `javaTest/src/main/resources/visualizer/assets/app.css` +- Modify test: `javaTest/src/test/java/com/ft546/wcs/shadow/history/visualizer/SimulationVisualizationMapperTest.java` +- Modify test: `javaTest/src/test/java/com/ft546/wcs/shadow/history/visualizer/HistoricalDispatchVisualizerServerTest.java` +- Modify test: `javaTest/src/test/java/com/ft546/wcs/shadow/history/visualizer/PointToPointSimulationServiceTest.java` +- Modify: `javaTest/scripts/visualizer_browser_qa.py` + +**Interfaces:** +- Historical POST body: `{ "mode": "SOURCE_ALIGNED_BUSINESS" }` or `{ "mode": "RAW_SNAPSHOT" }`. +- Summary JSON exposes `rawSnapshotCount`, `uniqueQueueCount`, `duplicateSnapshotCount`, `businessTaskCount`, `operationalQueueCount`, and `incompleteVehicleReleaseCount`. +- Task JSON exposes `businessType`, `cargoState`, `sourceFrontSite`, `targetFrontSite`, `businessCompletedMillis`, `vehicleReleasedMillis`, `vehicleReleaseStatus`, `snapshotEvidence`, and `phaseEvidence`. + +- [ ] **Step 1: Write mapper/server contract tests first** + +```java +@Test +void historicalBusinessEndpointReturnsDualCountsAndPhaseEvidence() throws Exception { + HttpResult response = request("POST", "/api/simulations/historical", + "{\"mode\":\"SOURCE_ALIGNED_BUSINESS\"}"); + Map payload = map(response.body); + Map summary = map(payload.get("summary")); + + assertEquals(240, summary.get("rawSnapshotCount")); + assertEquals(76, summary.get("businessTaskCount")); + assertEquals(76, summary.get("completedCount")); + assertFalse(list(map(list(payload.get("tasks")).get(0)).get("phaseEvidence")).isEmpty()); +} + +@Test +void rawModeStillReturns240CompatibilityResults() throws Exception { + HttpResult response = request("POST", "/api/simulations/historical", + "{\"mode\":\"RAW_SNAPSHOT\"}"); + assertEquals(240, map(map(response.body).get("summary")).get("completedCount")); +} +``` + +- [ ] **Step 2: Run API tests and verify the mode/evidence contract fails** + +Run: + +```bash +cd javaTest +mvn -Dtest=SimulationVisualizationMapperTest,HistoricalDispatchVisualizerServerTest test +``` + +Expected: failures show the historical endpoint ignores mode and mapper lacks phase/audit fields. + +- [ ] **Step 3: Implement dual-input service and JSON mapping** + +`HistoricalDispatchVisualizerApplication` builds both inputs once. The server selects the matching immutable input for each request. The mapper serializes exact backend phase paths and front sites; it does not infer aisle membership or graph edges in JavaScript. + +- [ ] **Step 4: Add a compact segmented replay-mode control and phase evidence panel** + +Add `#simulation-mode-business` and `#simulation-mode-raw` radio controls styled as a segmented control. Default business mode. Add summary cells for raw/unique/duplicate/business/operational counts, and task inspector fields for cargo state, current phase, source/target front, business completion, vehicle release, and original snapshot GUIDs. + +Add a phase list whose selection changes the full-map overlay to that phase's `plannedPath`. Use existing floor selector and projection guards, so floor transfer remains a port event rather than a fake cross-floor line. Give each phase a restrained, distinct route class and keep the existing historical/planned comparison toggle available in raw mode. + +- [ ] **Step 5: Extend browser QA assertions** + +```python +assert page.locator("#simulation-mode-business").is_checked() +assert page.locator("#raw-snapshot-count").inner_text() == "240" +assert page.locator("#business-task-count").inner_text() == "76" +assert page.locator("#phase-list [data-phase]").count() >= 2 +assert page.locator("#business-completed-at").inner_text() != "-" +``` + +Select a task with `EMPTY_RETURN_TO_TARGET_FRONT`, verify the map has nonblank same-floor route pixels, switch to raw mode, and verify completion count changes to 240 without a page reload error. + +- [ ] **Step 6: Run Java UI contracts** + +Run: + +```bash +cd javaTest +mvn -Dtest=SimulationVisualizationMapperTest,HistoricalDispatchVisualizerServerTest,PointToPointSimulationServiceTest test +``` + +Expected: API mode, summary, task evidence, static shell, and point-to-point contracts pass. + +Update `PointToPointSimulationServiceTest` to assert an INBOUND request whose target has a configured front returns phase evidence and a completed vehicle release, while an A/B request with no cargo-group mapping records `NO_CHANNEL_OPERATION` and remains a normal two-leg pickup/delivery task. + +- [ ] **Step 7: Run browser QA at desktop and mobile sizes** + +Start the server on a free local port, then run: + +```bash +cd javaTest +python scripts/visualizer_browser_qa.py --base-url http://127.0.0.1:18089 +``` + +Expected: desktop and mobile screenshots are nonblank; no browser console errors; controls and text do not overlap; both modes and phase path selection work. + +- [ ] **Step 8: Commit audit UI** + +```bash +git add javaTest/src/main/java/com/ft546/wcs/shadow/history/visualizer \ + javaTest/src/main/resources/visualizer \ + javaTest/src/test/java/com/ft546/wcs/shadow/history/visualizer \ + javaTest/scripts/visualizer_browser_qa.py +git commit -m "feat(javaTest): visualize source-aligned task phases" +``` + +--- + +### Task 9: Document, Run Full Verification, And Generate The Result For Review + +**Files:** +- Modify: `javaTest/README.md` +- Generated but not committed: `javaTest/target/source-aligned-history-report/**` +- Generated but not committed: browser QA screenshots under the script's existing output directory. + +**Interfaces:** +- Documents exact business/raw CLI commands, audit URL, mode definitions, phase meanings, and result interpretation. +- Produces a concrete source-aligned report for the current 240-row fixture. + +- [ ] **Step 1: Add README commands and interpretation** + +Document: + +```bash +mvn -q test +mvn -q -DskipTests compile exec:java \ + -Dexec.mainClass=com.ft546.wcs.shadow.history.simulation.HistoricalDispatchSimulationApplication \ + -Dexec.args="target/source-aligned-history-report 100 business" +mvn -q -DskipTests compile exec:java \ + -Dexec.mainClass=com.ft546.wcs.shadow.history.visualizer.HistoricalDispatchVisualizerApplication \ + -Dexec.args="18089" +``` + +State clearly that 240 is the raw snapshot count and 76 is the business parent-task count. + +- [ ] **Step 2: Run the entire Maven suite from a clean test execution** + +Run: + +```bash +cd javaTest +mvn test +``` + +Expected: zero failures and zero errors. Record the exact test count from Surefire output. + +- [ ] **Step 3: Generate and inspect the business report** + +Run: + +```bash +cd javaTest +mvn -q -DskipTests compile exec:java \ + -Dexec.mainClass=com.ft546.wcs.shadow.history.simulation.HistoricalDispatchSimulationApplication \ + -Dexec.args="target/source-aligned-history-report 100 business" +``` + +Check the emitted JSON/Markdown/CSV for 240 raw, 98 unique queues, 142 duplicates, 76 business tasks, 17 operational queues, task status totals, per-vehicle totals, phase HOLD/retry reasons, timing distributions, archive/WMS counts, release completeness, and safety invariants. + +- [ ] **Step 4: Run final browser verification** + +Start the visualizer on `127.0.0.1:18089` unless occupied, run `visualizer_browser_qa.py`, inspect desktop and mobile screenshots, and verify canvas pixels are nonblank for an inbound automatic-return task and an outbound source-front task. + +- [ ] **Step 5: Run repository checks and review the scoped diff** + +Run: + +```bash +git diff --check +git status --short +git log --oneline -10 +``` + +Expected: no whitespace errors; only the four pre-existing untracked user HTML files remain outside committed work; generated `target` files are ignored. + +- [ ] **Step 6: Request code review and resolve all Critical/Important findings** + +Provide the reviewer the design document, this plan, the base SHA `dca8c74`, final HEAD SHA, and the full Maven/browser verification evidence. Re-run the focused test for every accepted fix, then re-run `mvn test`. + +- [ ] **Step 7: Commit documentation** + +```bash +git add javaTest/README.md +git commit -m "docs(javaTest): explain two-stage business replay" +``` + +## Plan Self-Review Checklist + +- Every design requirement maps to a task: snapshot semantics (1-2), phase model (3), PLC idempotency (4), movement/archive/callback (5), loaded recovery and transfer (6), dual reports (7), UI (8), full verification (9). +- All new type names and method signatures are introduced before later tasks consume them. +- Raw 240-row compatibility and source-aligned 76-task acceptance are tested independently. +- No task requires runtime SQL Server, production writes, inferred front sites, or historical-path planning. +- No implementation step contains a placeholder, deferred handler, or unspecified error behavior.