Junjie
2026-04-27 1699cafdfb76f354882ee5e6142e1b024de32c64
fix: stop labeling undirected topology as directional
4个文件已修改
40 ■■■■ 已修改文件
docs/superpowers/specs/2026-04-27-auto-tune-dispatch-design.md 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/ai/domain/autotune/AutoTuneFlowTopologyItem.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/ai/service/impl/FlowTopologySnapshotServiceImpl.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/test/java/com/zy/ai/service/FlowTopologySnapshotServiceImplTest.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/superpowers/specs/2026-04-27-auto-tune-dispatch-design.md
@@ -159,6 +159,7 @@
- `targetStationId`
- `direction`
- `adjacentStationIds`
- `upstreamStationIds`
- `downstreamStationIds`
- `flowStationIds`
@@ -171,12 +172,17 @@
Definitions:
- `adjacentStationIds`: sorted station IDs adjacent to the target station when the backend source is an undirected station graph. This is only an adjacency fact and must not be interpreted as upstream or downstream.
- `upstreamStationIds`: directional upstream station IDs only when a stable directional source exists; otherwise this must be an empty list.
- `downstreamStationIds`: directional downstream station IDs only when a stable directional source exists; otherwise this must be an empty list.
- `occupiedCount`: count of stations on the directional flow segment where `loading = true` or `taskNo > 0`
- `freeCount = bufferCapacity - occupiedCount`
- `nonAutoingCount`: count of stations on the directional flow segment where `autoing = false`
- `loadingCount`: count of stations on the directional flow segment where `loading = true`
- `taskHoldingCount`: count of stations on the directional flow segment where `taskNo > 0`
Current implementation note: the repository currently exposes only an undirected station adjacency snapshot for this service. Until a deterministic directional metadata source is added, `adjacentStationIds` and `flowStationIds` carry the graph facts used for conservative occupancy counting, while `upstreamStationIds` and `downstreamStationIds` remain empty. The Agent must treat non-empty `adjacentStationIds` with empty upstream/downstream lists as undirected adjacency only.
## 7. Direction and Buffer Capacity Rules
### 7.1 Direction Source
src/main/java/com/zy/ai/domain/autotune/AutoTuneFlowTopologyItem.java
@@ -13,6 +13,8 @@
    private String direction;
    private List<Integer> adjacentStationIds;
    private List<Integer> upstreamStationIds;
    private List<Integer> downstreamStationIds;
src/main/java/com/zy/ai/service/impl/FlowTopologySnapshotServiceImpl.java
@@ -57,9 +57,10 @@
        AutoTuneFlowTopologyItem item = new AutoTuneFlowTopologyItem();
        item.setTargetStationId(targetStationId);
        item.setDirection(capacity.getDirectionCode());
        // 当前只有后端无向站点图可用,保守地把排序相邻站同时作为上下游候选。
        item.setUpstreamStationIds(adjacentStationIds);
        item.setDownstreamStationIds(adjacentStationIds);
        item.setAdjacentStationIds(adjacentStationIds);
        // 当前只存在无向站点邻接图,不能把邻接事实冒充为上下游方向事实。
        item.setUpstreamStationIds(Collections.emptyList());
        item.setDownstreamStationIds(Collections.emptyList());
        item.setFlowStationIds(flowStationIds);
        item.setBufferCapacity(bufferCapacity);
        item.setOccupiedCount(runtimeCount.getOccupiedCount());
src/test/java/com/zy/ai/service/FlowTopologySnapshotServiceImplTest.java
@@ -31,12 +31,33 @@
        assertEquals(101, item.getTargetStationId());
        assertEquals("OUT", item.getDirection());
        assertEquals(Arrays.asList(102, 103), item.getUpstreamStationIds());
        assertEquals(Arrays.asList(102, 103), item.getDownstreamStationIds());
        assertEquals(Arrays.asList(102, 103), item.getAdjacentStationIds());
        assertEquals(Arrays.asList(), item.getUpstreamStationIds());
        assertEquals(Arrays.asList(), item.getDownstreamStationIds());
        assertEquals(Arrays.asList(101, 102, 103), item.getFlowStationIds());
    }
    @Test
    void undirectedGraphOnlyPopulatesAdjacentAndFlowStations() {
        FlowTopologySnapshotServiceImpl service = new FlowTopologySnapshotServiceImpl();
        StationFlowCapacity capacity = capacity(201, "IN", 4);
        Map<Integer, List<Integer>> adjacency = new LinkedHashMap<>();
        adjacency.put(201, Arrays.asList(203, 202));
        AutoTuneFlowTopologyItem item = service.buildTopologyItem(
                capacity,
                adjacency,
                Arrays.asList(runtime(202, 1, 1, 0), runtime(203, 1, 0, 8001))
        );
        assertEquals(Arrays.asList(202, 203), item.getAdjacentStationIds());
        assertEquals(Arrays.asList(), item.getUpstreamStationIds());
        assertEquals(Arrays.asList(), item.getDownstreamStationIds());
        assertEquals(Arrays.asList(201, 202, 203), item.getFlowStationIds());
        assertEquals(2, item.getOccupiedCount());
    }
    @Test
    void calculateRuntimeCountsUsesOnlyAutoingLoadingAndTaskNo() {
        FlowTopologySnapshotServiceImpl service = new FlowTopologySnapshotServiceImpl();
        List<AutoTuneStationRuntimeItem> runtimeItems = Arrays.asList(