Given a rack (a set of module ids the user owns), rank the modules NOT in the rack by how many rack members they pair with. The set-level companion to find_compatible_with: where that answers "what pairs with module X?", this answers "given my whole rack, what single module should I add — the one that pairs with the most of what I already have?".
The ranking signal is `pair_count` — the number of DISTINCT rack members a candidate pairs with. A module that modulates five of your modules ranks above one that modulates one. This aggregate is the point: you can't get it from per-module find_compatible_with calls without tallying distinct members by hand.
Use this for:
- "What should I add to a rack with <modules>?" / "what fills out this system?"
- "Given these modules, what pairs well with the most of them?"
- Inspecting a rack's own internal pairing structure (the `internal` edges).
Combination edges only. Ranking uses the seven patch-time relationships (clock-source-for, cv-source-for, modulator-for, audio-source-for, quantizer-for, trigger-source-for, envelope-target-for) — the "A and B work together in a patch" kinds. The substitution/catalog kinds (alternative-to, replaces, expander-for) are deliberately excluded: a pairing recommender shouldn't suggest replacing your modules with each other. For "what's an alternative to X?" use find_compatible_with.
Args:
- rack (string[], required): module ids, e.g. ["make-noise/maths", "mutable-instruments/plaits"]. Ids that match no module are returned in `unknown_ids` (and in `unresolved` with did-you-mean suggestions) rather than failing the call. Surface those rather than proceeding on a partial rack: the server is stateless about your rack — it keeps no memory of it between calls, so pass the COMPLETE current set every call. Max 64.
- relationship (string, optional): restrict ranking to one combination kind above. Omit to consider all seven.
- limit (number): default 25, max 100.
Returns:
{
"rack": [{ id, name }], // the rack members that resolved
"unknown_ids": [string], // rack ids that matched no module
"internal": [{ from_module_id, to_module_id, relationship, source_id }], // edges within the rack
"candidates": [{
id, name, manufacturer, pair_count,
"pairings": [{ rack_member, relationship, direction, source_id }] // why it pairs, per member
}]
}
`direction` on each pairing is relative to the rack member: 'outbound' = the candidate is the role-bearer (it `relationship`s the member, e.g. the candidate is a modulator-for the member); 'inbound' = the member is the role-bearer.
Coverage caveat: rankings are only as dense as module_relationships. A thin or empty result means the corpus hasn't recorded those edges yet, not that no good pairing exists — call report_gap if you expected matches.