midi-harbor/specs/007-routing/tasks.md
2026-09-28 13:59:10 -05:00

39 lines
4.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Tasks: Routing
Tasks from the task list, under the phase each was done in and by their original numbers. Phases are
the order the project was built in, across every spec; [001's
tasks](../001-service-and-clients/tasks.md) give that order in full.
## Phase 6: User Story 4 — Routing and repeating between endpoints (Priority: P3)
**Goal**: Physical MIDI hardware as a first-class endpoint, and routes carrying any endpoint to any
other — including a physical device repeated over the network or Bluetooth.
**Independent Test**: Plug in a physical device, route it to another endpoint, play it, verify
arrival; unplug and replug and verify the route resumes on its own (quickstart scenario 3).
### Tests for User Story 4
- [x] T105 [P] [US4] Tests in `crates/core/tests/router.rs` covering one-to-many and many-to-one delivery, cycle detection, broken-route reporting, and delivery continuing to reachable destinations when others are unavailable (FR-030a–b, FR-033, FR-035)
- [x] T106 [P] [US4] Test in `tests/integration/repeater.rs` asserting a physical device routed to a network session survives unplug and replug with no stuck notes on the remote side (SC-010c) — *in `crates/daemon/tests/repeater.rs`, two daemons joined by a real session over loopback; it found that silencing never reached a network session, and running the same thing across two machines found R-046*
### Implementation for User Story 4
- [x] T111a [US4] Implement route suspension and resumption for every endpoint kind in `crates/core/src/router.rs` and `Daemon::router`, keeping a route's configuration intact while either endpoint is unavailable and resuming delivery when it returns (FR-032)
- [x] T112 [US4] Implement the route graph with validation — no self-route, source has an input side, destination has an output side, no duplicate pairs — in `crates/core/src/router.rs` (FR-015b, FR-030, FR-030b, data-model §5)
- [x] T113 [US4] Implement cycle detection on every route mutation in `crates/core/src/router.rs`, marking affected routes `LoopDetected` and warning without blocking creation (FR-033)
- [X] T114 [US4] Implement `OriginTag` per-message loop suppression and the network session identifier in `crates/core/src/router.rs` and `crates/rtpmidi/src/session.rs`, so cross-machine repeater loops are caught (research R-013, RISK-9) — caught without a wire marker, at the session-to-session route where every such loop closes, by recognising MIDI a session sent moments ago coming back; the route is switched off and the history says why; `crates/core/src/loops.rs`, R-062
- [x] T115 [US4] Implement the routing hot path in `crates/daemon/src/dataplane.rs` fanning messages from source ring buffers to destination ring buffers with no allocation, locks or logging (Principle III)
- [x] T117 [US4] Implement route request handlers (`CreateRoute`, `DeleteRoute`, `SetRouteEnabled`) and physical device handlers (`ListPhysicalDevices`, `ForgetPhysicalDevice`, `ResolveAmbiguousDevice`) in `crates/daemon/src/handlers/routes.rs` (FR-031, FR-034)
- [x] T118 [P] [US4] Implement `route list|create|delete|enable|disable` commands in `crates/cli/src/route_cmd.rs` (contracts/cli-interface.md §4)
**Checkpoint**: The repeater works — hardware in one room drives hardware in another.
## Phase 11: Convergence
- [x] T160 Make stored routes identify their endpoints unambiguously across kinds that share a name, and use that in `Router::build`, duplicate checks and rename rewriting, per FR-030, FR-004 (partial) — done: routes carry an optional kind per end, written when a name is shared or becomes shared; an unqualified shared name reads as broken
- [x] T177 Reword the GUI's loop note so it no longer says messages circulate, since delivery is direct-only, per FR-033 (contradicts) — done: worded as the command-line guide words it
## Phase 12: The redesigned window
- [x] T183 Let one route carry MIDI both ways: a `both_ways` route setting in the configuration and contract, the router delivering in both directions with loop detection covering both, an RPC to change a route's ends and `both_ways`, `route create --both-ways`, and the window's route dialog, per FR-034a, FR-033 — done: `both_ways` on a route in the configuration and contract (`Route.both_ways` 14, `CreateRouteRequest.both_ways` 5, new `UpdateRoute` RPC); the router delivers back from the destination's MIDI In to the source's MIDI Out of the same numbers under the same route, refuses a route that would carry what a two-way route already does, marks a two-way route broken when a connector of the way back is missing, and treats one as an edge each way for loops without calling A and back a loop; editing a route silences it first and keeps whether it is switched on; silencing a two-way route stops notes at both ends, including when its destination goes away; `route create --both-ways`, `route edit`, and one shared route dialog in the window with a Both ways switch and ↔ on the row. Every new check was mutation-tested.