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

4.9 KiB
Raw Blame History

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 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

  • 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)
  • 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

  • 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)
  • 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)
  • 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)
  • 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
  • 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)
  • 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)
  • 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

  • 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
  • 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

  • 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.