Files
FT546-WCS/docs/superpowers/plans/2026-08-04-standard-wcs-topology-dispatch-traffic.md

22 KiB
Raw Blame History

Standard WCS Topology Dispatch Traffic 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: 在标准订单与步骤模型之上实现版本化仓库拓扑、设备能力、选车、路径规划、资源预约和无环交通裁决。

Architecture: 运行时只读取编译后的不可变 EffectiveTopology;车辆选择和路径规划产生候选,ResourceLedger 与 DeadlockGuard 在同一调度快照中决定 GRANT/HOLD/YIELD。项目只提供定义和评分权重,不直接持有资源或修改步骤状态。

Tech Stack: Java 8, JUnit 5, MyBatis, SQL Server, deterministic graph search.

Global Constraints

  • 依赖 2026-08-04-standard-wcs-core-foundation.md 已交付的 TransportOrder、TaskStep、共享事件和 Repository 边界。
  • 交通区域是版本与协调边界,不默认是容量 1 的资源。
  • 节点和无向边默认容量为 1;容量大于 1 必须显式配置。
  • 地图加载后不可变;运行循环不得临时查询多张地图表或解释 remark。
  • 车辆筛选先执行硬条件,再执行可配置评分。
  • 已占资源只有匹配物理事实或恢复裁定才能释放。
  • 等待图新增授权后必须无环。
  • 本计划只生成设备无关的调度决定,不调用 PLC。

Task 1: Define, validate, publish, and compile versioned warehouse configuration

Files:

  • Create: ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/topology/TopologyDefinition.java
  • Create: ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/topology/TopologyNode.java
  • Create: ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/topology/TopologyEdge.java
  • Create: ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/topology/ResourceDefinition.java
  • Create: ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/topology/EffectiveTopology.java
  • Create: ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/topology/TopologyCompiler.java
  • Create: ruoyi-wcs/src/main/java/com/ruoyi/wcs/port/in/ConfigurationUseCase.java
  • Create: ruoyi-wcs/src/main/java/com/ruoyi/wcs/port/out/TopologyDefinitionProvider.java
  • Create: ruoyi-wcs/src/main/java/com/ruoyi/wcs/port/out/ConfigurationRepository.java
  • Create: ruoyi-wcs/src/main/java/com/ruoyi/wcs/application/configuration/PublishConfigurationCommand.java
  • Create: ruoyi-wcs/src/main/java/com/ruoyi/wcs/application/configuration/ConfigurationPublicationResult.java
  • Create: ruoyi-wcs/src/main/java/com/ruoyi/wcs/application/configuration/PublishedConfigurationView.java
  • Create: ruoyi-wcs/src/main/java/com/ruoyi/wcs/application/configuration/ConfigurationApplicationService.java
  • Create: ruoyi-wcs/src/test/java/com/ruoyi/wcs/domain/topology/TopologyCompilerTest.java
  • Create: ruoyi-wcs/src/test/java/com/ruoyi/wcs/application/configuration/ConfigurationApplicationServiceTest.java

Interfaces:

  • Consumes: TopologyDefinitionProvider.load(String warehouseCode, long version).

  • Produces: TopologyCompiler.compile(TopologyDefinition): EffectiveTopology.

  • Produces: EffectiveTopology.neighbors(String nodeId), edge(String sourceNodeId,String targetNodeId), resource(String resourceKey), containsNode(String nodeId), version().

  • Produces: ConfigurationUseCase.validate(PublishConfigurationCommand), publish(PublishConfigurationCommand): ConfigurationPublicationResult, read(String warehouseCode,long version): PublishedConfigurationView, and readActive(String warehouseCode): PublishedConfigurationView.

  • publish atomically stores the immutable warehouse, floor, topology, traffic-zone, resource, device, capability, port, charger, and workflow version after all cross-reference checks pass.

  • Rejects publication while an active order references the version being replaced; running orders remain pinned to their original topology/configuration version.

  • Step 1: Write failing topology invariant tests

@Test
void canonicalizesUndirectedEdgeResources() {
    EffectiveTopology topology = compiler.compile(definition(
            node("A", 1), node("B", 1), edge("A", "B")));
    assertEquals("EDGE:A:B", topology.edge("A", "B").getResourceKey());
    assertEquals("EDGE:A:B", topology.edge("B", "A").getResourceKey());
}

@Test
void rejectsDisconnectedLiftPortReference() {
    assertThrows(DomainRuleViolation.class, () ->
            compiler.compile(definitionWithLiftPort("MISSING")));
}

@Test
void publishesOneImmutableConfigurationVersionAtomically() {
    ConfigurationPublicationResult result = service.publish(validConfiguration(7L));
    assertEquals(7L, result.getPublishedVersion());
    assertTrue(repository.isPublished("FT546", 7L));
    assertEquals(0, repository.partialWriteCount());
}

@Test
void readsOneInternallyConsistentPublishedVersion() {
    service.publish(validConfiguration(7L));
    PublishedConfigurationView view = service.read("FT546", 7L);
    assertEquals(7L, view.getTopologyVersion());
    assertEquals(7L, view.getDeviceConfigurationVersion());
    assertEquals(7L, view.getWorkflowConfigurationVersion());
}
  • Step 2: Run RED

Run: mvn -pl ruoyi-wcs -am -Dtest=TopologyCompilerTest,ConfigurationApplicationServiceTest test

  • Step 3: Implement immutable compilation
public EffectiveTopology compile(TopologyDefinition definition) {
    requirePublishedVersion(definition);
    Map<String, TopologyNode> nodes = uniqueNodes(definition.getNodes());
    Map<String, TopologyEdge> edges = canonicalEdges(definition.getEdges(), nodes);
    validatePortsAndResources(definition, nodes);
    return new EffectiveTopology(definition.getWarehouseCode(),
            definition.getVersion(), nodes, edges,
            immutableResources(definition, nodes, edges));
}

Reject duplicate IDs, duplicate coordinates on one floor, missing endpoints, nonpositive capacity, unconfirmed traffic zones, and lift/conveyor ports without nodes.

ConfigurationApplicationService.publish first compiles topology, validates every device floor/capability/port/charger reference and every workflow step type, then calls one optimistic ConfigurationRepository.publish(command, effectiveTopology) transaction. It returns stable validation codes and never exposes partially published versions.

read and readActive return immutable DTOs through the inbound use case. Admin configuration pages may call these methods but may not query the core configuration Mapper or tables directly.

  • Step 4: Run GREEN

Run: mvn -pl ruoyi-wcs -am -Dtest=TopologyCompilerTest,ConfigurationApplicationServiceTest test

  • Step 5: Commit
git add ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/topology ruoyi-wcs/src/main/java/com/ruoyi/wcs/application/configuration ruoyi-wcs/src/main/java/com/ruoyi/wcs/port/in/ConfigurationUseCase.java ruoyi-wcs/src/main/java/com/ruoyi/wcs/port/out/TopologyDefinitionProvider.java ruoyi-wcs/src/main/java/com/ruoyi/wcs/port/out/ConfigurationRepository.java ruoyi-wcs/src/test/java/com/ruoyi/wcs/domain/topology ruoyi-wcs/src/test/java/com/ruoyi/wcs/application/configuration
git commit -m "feat(wcs): publish immutable warehouse configuration"

Task 2: Add device registry and capability matching

Files:

  • Create: ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/equipment/DeviceType.java
  • Create: ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/equipment/DeviceCapability.java
  • Create: ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/equipment/DeviceDescriptor.java
  • Create: ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/equipment/VehicleSnapshot.java
  • Create: ruoyi-wcs/src/main/java/com/ruoyi/wcs/port/out/DeviceRegistry.java
  • Create: ruoyi-wcs/src/test/java/com/ruoyi/wcs/domain/equipment/DeviceCapabilityTest.java

Interfaces:

  • Produces: DeviceRegistry.vehicles(): List<DeviceDescriptor>.

  • Produces: DeviceDescriptor.supports(DeviceCapability): boolean.

  • Produces: VehicleSnapshot.isDispatchable(int minimumBattery).

  • Step 1: Write failing dispatchability tests

@ParameterizedTest
@CsvSource({
    "ONLINE,AUTO,NORMAL,IDLE,80,true",
    "OFFLINE,AUTO,NORMAL,IDLE,80,false",
    "ONLINE,MANUAL,NORMAL,IDLE,80,false",
    "ONLINE,AUTO,FAULT,IDLE,80,false",
    "ONLINE,AUTO,NORMAL,BUSY,80,false",
    "ONLINE,AUTO,NORMAL,IDLE,20,false"
})
void dispatchabilityUsesOrthogonalStateDimensions(
        String connection, String mode, String health,
        String occupation, int battery, boolean expected) {
    assertEquals(expected, snapshot(connection, mode, health, occupation, battery)
            .isDispatchable(30));
}
  • Step 2: Run RED

Run: mvn -pl ruoyi-wcs -am -Dtest=DeviceCapabilityTest test

  • Step 3: Implement descriptors and snapshots

Keep connection, mode, health, occupation, location, load, battery, current command, floor, and snapshot time as separate immutable fields. supports checks an immutable EnumSet<DeviceCapability>.

  • Step 4: Run GREEN

Run: mvn -pl ruoyi-wcs -am -Dtest=DeviceCapabilityTest test

  • Step 5: Commit
git add ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/equipment ruoyi-wcs/src/main/java/com/ruoyi/wcs/port/out/DeviceRegistry.java ruoyi-wcs/src/test/java/com/ruoyi/wcs/domain/equipment
git commit -m "feat(wcs): add device capabilities and snapshots"

Task 3: Implement deterministic route planning

Files:

  • Create: ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/traffic/RoutePlan.java
  • Create: ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/traffic/RoutePlanner.java
  • Create: ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/traffic/AStarRoutePlanner.java
  • Create: ruoyi-wcs/src/test/java/com/ruoyi/wcs/domain/traffic/AStarRoutePlannerTest.java

Interfaces:

  • Produces: RoutePlanner.plan(EffectiveTopology,String source,String destination,Set<String> blockedResources): RoutePlan.

  • Produces: ordered node IDs, ordered resource keys, distance cost, turn count, and topology version.

  • Step 1: Write failing shortest-safe-route tests

@Test
void avoidsBlockedEdgeAndUsesStableNodeIdTieBreak() {
    RoutePlan route = planner.plan(grid(), "A", "D",
            Collections.singleton("EDGE:B:D"));
    assertEquals(Arrays.asList("A", "C", "D"), route.getNodeIds());
}

@Test
void rejectsUnknownDestination() {
    assertThrows(DomainRuleViolation.class, () ->
            planner.plan(grid(), "A", "UNKNOWN", Collections.<String>emptySet()));
}
  • Step 2: Run RED

Run: mvn -pl ruoyi-wcs -am -Dtest=AStarRoutePlannerTest test

  • Step 3: Implement A with deterministic ordering*

Use Manhattan distance only when coordinates exist; otherwise use zero heuristic. Sort equal-cost open nodes by total cost, path cost, turn count, then node ID. Never read historical path_info.

  • Step 4: Run GREEN

Run: mvn -pl ruoyi-wcs -am -Dtest=AStarRoutePlannerTest,TopologyCompilerTest test

  • Step 5: Commit
git add ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/traffic ruoyi-wcs/src/test/java/com/ruoyi/wcs/domain/traffic
git commit -m "feat(wcs): add deterministic route planning"

Task 4: Implement vehicle assignment policy

Files:

  • Create: ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/dispatch/DispatchTask.java
  • Create: ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/dispatch/VehicleAssignmentDecision.java
  • Create: ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/dispatch/VehicleAssignmentPolicy.java
  • Create: ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/dispatch/DefaultVehicleAssignmentPolicy.java
  • Create: ruoyi-wcs/src/main/java/com/ruoyi/wcs/port/out/DispatchPolicy.java
  • Create: ruoyi-wcs/src/test/java/com/ruoyi/wcs/domain/dispatch/DefaultVehicleAssignmentPolicyTest.java

Interfaces:

  • Produces: assign(DispatchTask,List<VehicleSnapshot>,EffectiveTopology): VehicleAssignmentDecision.

  • Produces: selected vehicle ID, route, total score, and ordered reason codes.

  • Consumes: optional DispatchPolicy.adjustment(DispatchTask,VehicleSnapshot): int with no side effects.

  • Step 1: Write failing hard-filter and stable-score tests

@Test
void rejectsCloserVehicleWithoutRequiredCapability() {
    VehicleAssignmentDecision decision = policy.assign(taskRequiringLoad(),
            Arrays.asList(vehicle("V1", 1, 90, false), vehicle("V2", 2, 70, true)), topology);
    assertEquals("V2", decision.getVehicleId());
    assertTrue(decision.getReasonCodes().contains("CAPABILITY_MATCH"));
}

@Test
void breaksEqualScoresByVehicleId() {
    assertEquals("V1", policy.assign(task(), Arrays.asList(vehicle("V2"), vehicle("V1")), topology)
            .getVehicleId());
}
  • Step 2: Run RED

Run: mvn -pl ruoyi-wcs -am -Dtest=DefaultVehicleAssignmentPolicyTest test

  • Step 3: Implement filtering and scoring

Apply online/auto/healthy/idle/battery/capability/reachability filters before scoring. Score business priority, waiting age, same floor, path cost, battery margin, current workload, policy adjustment, then stable vehicle ID.

  • Step 4: Run GREEN

Run: mvn -pl ruoyi-wcs -am -Dtest='*VehicleAssignment*Test,*RoutePlannerTest' test

  • Step 5: Commit
git add ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/dispatch ruoyi-wcs/src/main/java/com/ruoyi/wcs/port/out/DispatchPolicy.java ruoyi-wcs/src/test/java/com/ruoyi/wcs/domain/dispatch
git commit -m "feat(wcs): add deterministic vehicle assignment"

Task 5: Implement the resource ledger and reservation lifecycle

Files:

  • Create: ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/traffic/ReservationState.java
  • Create: ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/traffic/ResourceReservation.java
  • Create: ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/traffic/ReservationRequest.java
  • Create: ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/traffic/ReservationDecision.java
  • Create: ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/traffic/ResourceLedger.java
  • Create: ruoyi-wcs/src/test/java/com/ruoyi/wcs/domain/traffic/ResourceLedgerTest.java

Interfaces:

  • Produces: reserve(ReservationRequest): ReservationDecision.

  • Produces: occupy(String reservationId,long fencingToken).

  • Produces: release(String reservationId,String releaseEvidenceId,long fencingToken).

  • Produces: abortUnoccupied(String reservationId,String reason).

  • ReservationState values are REQUESTED, GRANTED, OCCUPIED, RELEASED, REJECTED, EXPIRED, and ABORTED.

  • Step 1: Write failing capacity and UNKNOWN tests

@Test
void grantsOnlyOneReservationForCapacityOneResource() {
    assertTrue(ledger.reserve(request("R1", "NODE:A")).isGranted());
    assertFalse(ledger.reserve(request("R2", "NODE:A")).isGranted());
}

@Test
void occupiedReservationCannotExpireByTime() {
    ResourceReservation reservation = occupiedReservation();
    assertThrows(DomainRuleViolation.class, reservation::expire);
}
  • Step 2: Run RED

Run: mvn -pl ruoyi-wcs -am -Dtest=ResourceLedgerTest test

  • Step 3: Implement atomic in-memory ledger semantics

Sort requested resource keys lexicographically, verify all capacities before changing any count, assign one fencing token to the decision, and roll back the whole request on any conflict. Permit only REQUESTED -> GRANTED/REJECTED/EXPIRED/ABORTED, GRANTED -> OCCUPIED/EXPIRED/ABORTED, and OCCUPIED -> RELEASED. EXPIRED is legal only before physical occupation; OCCUPIED can be released only with matching evidence/fencing token and must remain protected when a linked command is UNKNOWN or a RecoveryCase is unresolved.

  • Step 4: Run GREEN

Run: mvn -pl ruoyi-wcs -am -Dtest=ResourceLedgerTest test

  • Step 5: Commit
git add ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/traffic ruoyi-wcs/src/test/java/com/ruoyi/wcs/domain/traffic/ResourceLedgerTest.java
git commit -m "feat(wcs): add resource reservation ledger"

Task 6: Add deadlock prevention, HOLD, and YIELD decisions

Files:

  • Create: ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/traffic/WaitForGraph.java
  • Create: ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/traffic/TrafficDecisionType.java
  • Create: ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/traffic/TrafficDecision.java
  • Create: ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/traffic/DeadlockGuard.java
  • Create: ruoyi-wcs/src/test/java/com/ruoyi/wcs/domain/traffic/DeadlockGuardTest.java

Interfaces:

  • Produces: wouldCreateCycle(String waiter,Set<String> holders): boolean.

  • Produces: decide(candidate,ledger,topology): TrafficDecision with GRANT/HOLD/YIELD and reason codes.

  • Step 1: Write failing cycle and safe-yield tests

@Test
void rejectsGrantThatClosesAWaitCycle() {
    graph.addWait("V1", "V2");
    graph.addWait("V2", "V3");
    assertTrue(graph.wouldCreateCycle("V3", Collections.singleton("V1")));
}

@Test
void doesNotInventYieldPointWhenTopologyHasNone() {
    TrafficDecision decision = guard.decide(blockedCandidate(), ledger, topologyWithoutSafeWait());
    assertEquals(TrafficDecisionType.HOLD, decision.getType());
    assertEquals("NO_SAFE_YIELD_POINT", decision.getReasonCode());
}
  • Step 2: Run RED

Run: mvn -pl ruoyi-wcs -am -Dtest=DeadlockGuardTest test

  • Step 3: Implement deterministic wait-graph traversal

Use stable vehicle-ID traversal. YIELD may target only topology nodes flagged safeWait=true, must not cross occupied resources, and must never alter the business destination.

  • Step 4: Run GREEN

Run: mvn -pl ruoyi-wcs -am -Dtest=DeadlockGuardTest,ResourceLedgerTest test

  • Step 5: Commit
git add ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/traffic ruoyi-wcs/src/test/java/com/ruoyi/wcs/domain/traffic/DeadlockGuardTest.java
git commit -m "feat(wcs): prevent traffic deadlocks"

Task 7: Orchestrate dispatch decisions and persist traffic state

Files:

  • Create: ruoyi-wcs/src/main/java/com/ruoyi/wcs/application/dispatch/DispatchCoordinator.java
  • Create: ruoyi-wcs/src/main/java/com/ruoyi/wcs/application/dispatch/DispatchCycleResult.java
  • Create: ruoyi-wcs/src/main/java/com/ruoyi/wcs/application/dispatch/DispatchSnapshot.java
  • Create: ruoyi-wcs/src/main/java/com/ruoyi/wcs/application/dispatch/GrantedDispatch.java
  • Create: ruoyi-wcs/src/main/java/com/ruoyi/wcs/port/out/DispatchSnapshotProvider.java
  • Create: ruoyi-wcs/src/main/java/com/ruoyi/wcs/port/out/TrafficStateRepository.java
  • Create: ruoyi-wcs/src/main/java/com/ruoyi/wcs/port/out/DecisionEventRepository.java
  • Create: sql/wcs/V002__wcs_topology_and_traffic.sql
  • Create: sql/wcs/verify_V002__wcs_topology_and_traffic.sql
  • Create: ruoyi-wcs/src/test/java/com/ruoyi/wcs/application/dispatch/DispatchCoordinatorTest.java
  • Create: ruoyi-wcs/src/test/java/com/ruoyi/wcs/infrastructure/persistence/traffic/TrafficConcurrencyIT.java

Interfaces:

  • Produces: DispatchSnapshotProvider.load(): DispatchSnapshot; the immutable snapshot contains ready steps, current vehicle snapshots, effective topology, and one optimistic snapshotVersion.

  • Produces: dispatch(DispatchSnapshot): DispatchCycleResult.

  • DispatchCycleResult.getGranted() returns List<GrantedDispatch>; each item contains order ID, step ID, vehicle ID, protected route, reservation IDs, topology version, and fencing token.

  • Persists: vehicle assignment, route plan, reservations, resource counts, and decision event in one transaction.

  • Produces: no device command; Plan 3 consumes granted assignments.

  • Step 1: Write a failing two-vehicle arbitration test

@Test
void grantsOneVehicleAndHoldsTheOtherForTheSameIntersection() {
    DispatchCycleResult result = coordinator.dispatch(dispatchSnapshot(
            Arrays.asList(step("S1", 1), step("S2", 2)),
            Arrays.asList(vehicle("V1"), vehicle("V2")), topology));
    assertEquals(1, result.getGranted().size());
    assertEquals(1, result.getHeld().size());
    assertEquals(0, result.getInvariantViolations().size());
}
  • Step 2: Run RED

Run: mvn -pl ruoyi-wcs -am -Dtest=DispatchCoordinatorTest test

  • Step 3: Implement the coordinator transaction boundary

Load one immutable DispatchSnapshot, sort ready steps by priority/age/order ID, assign vehicles, plan routes, call DeadlockGuard, then call one repository method:

trafficStateRepository.commit(new DispatchCommit(
        topology.version(), snapshotVersion, assignments,
        routePlans, reservations, decisionEvents));

If optimistic versions changed, return RETRY_SNAPSHOT_CHANGED and persist no partial assignment.

  • Step 4: Add SQL Server tables and locking test

Create wcs_warehouse, wcs_floor, wcs_topology_version, wcs_topology_node, wcs_topology_edge, wcs_traffic_zone, wcs_resource_definition, wcs_device, wcs_device_capability, wcs_device_port, wcs_charger, wcs_vehicle_assignment, wcs_route_plan, wcs_resource_state, wcs_resource_reservation, and wcs_traffic_decision_event. The integration test runs two concurrent capacity-1 reservations and asserts exactly one commit succeeds.

  • Step 5: Run full Plan 2 verification

Run: mvn -pl ruoyi-wcs -am -Dtest='*Topology*,*Capability*,*RoutePlanner*,*VehicleAssignment*,*ResourceLedger*,*DeadlockGuard*,*DispatchCoordinator*' test

Run SQL-tagged tests against the isolated SQL Server: mvn -pl ruoyi-wcs -Dgroups=sqlserver -Dtest=TrafficConcurrencyIT test.

  • Step 6: Commit
git add ruoyi-wcs/src/main/java/com/ruoyi/wcs/application/dispatch ruoyi-wcs/src/main/java/com/ruoyi/wcs/port/out ruoyi-wcs/src/test/java/com/ruoyi/wcs/application/dispatch ruoyi-wcs/src/test/java/com/ruoyi/wcs/infrastructure/persistence/traffic sql/wcs
git commit -m "feat(wcs): coordinate and persist traffic decisions"