# Capability Spec Delta: network-plugin-topology (complete-network-plugins) **Package**: `@suaveplan/network-plugin-topology` **Status**: PROPOSED > Every requirement below carries a `**Verified by:**` line at authoring time. ## ADDED Requirements ### Requirement: TopologyPlugin conforms to NetworkPlugin lifecycle The package SHALL export `TopologyPlugin implements NetworkPlugin` and `TopologyPluginConfig`. The plugin SHALL manage peer-tier assignment and group membership on install, and tear down assigners and buses on uninstall. #### Scenario: install / uninstall lifecycle - **WHEN** a host calls install then uninstall - **THEN** the plugin reaches `INSTALLED` then `UNINSTALLED` and releases all timers **Verified by:** `src/plugin/topology-plugin.spec-conformance.test.ts::"install / uninstall lifecycle is clean"` --- ### Requirement: LatencyAssigner buckets peers by measured latency The package SHALL export `LatencyAssigner`, `LatencyAssignerConfig`, `LatencyBucket`, and `LatencyBus`. The assigner SHALL bucket peers into named latency tiers based on a configurable bucket schedule and SHALL emit bucket-change events on the `LatencyBus`. #### Scenario: peer moves between buckets on RTT change - **WHEN** a peer's measured RTT crosses a bucket boundary - **THEN** the assigner emits a `bucket-change` event on the bus with the old and new bucket **Verified by:** `src/latency/latency-assigner.spec-conformance.test.ts::"emits bucket-change when RTT crosses boundary"` --- ### Requirement: LatencyAssigner validates config with typed errors `LatencyAssigner` SHALL validate its config at construction time and throw `NetworkTopoError` with `code === "NET_TOPO.INVALID_LATENCY_CONFIG"` whenever the bucket schedule is empty, non-monotonic, or contains a non-finite boundary. The class SHALL NOT throw bare `Error`. #### Scenario: empty bucket schedule - **WHEN** `new LatencyAssigner({ buckets: [] })` is called - **THEN** the call throws `NetworkTopoError` with `code === "NET_TOPO.INVALID_LATENCY_CONFIG"` and `context.reason === "empty-buckets"` #### Scenario: non-monotonic bucket boundaries - **WHEN** the bucket schedule is `[100, 50, 200]` - **THEN** the call throws `NetworkTopoError` with `code === "NET_TOPO.INVALID_LATENCY_CONFIG"` and `context.reason === "non-monotonic"` **Verified by:** `src/latency/latency-assigner.spec-conformance.test.ts::"throws INVALID_LATENCY_CONFIG on empty buckets"` AND `src/latency/latency-assigner.spec-conformance.test.ts::"throws INVALID_LATENCY_CONFIG on non-monotonic schedule"` --- ### Requirement: GroupManager exposes a typed group abstraction The package SHALL export `GroupManager`, `Group`, `GroupBus`, and `GroupManagerConfig`. The manager SHALL track named groups of peers with add / remove operations and SHALL emit membership-change events on the `GroupBus`. #### Scenario: add/remove emits membership events - **WHEN** `groupManager.add(groupId, peerId)` is called for a peer not yet in the group - **THEN** a `member-added` event fires on the bus with `{ groupId, peerId }` **Verified by:** `src/group/group-manager.spec-conformance.test.ts::"emits member-added on join"` --- ### Requirement: Error-code registry is frozen and namespaced NET_TOPO `NETWORK_TOPO_CODES` SHALL be frozen under the `NET_TOPO` namespace and SHALL include at minimum: `INVALID_TIER`, `PEER_NOT_FOUND`, `ALREADY_AT_TIER`, and the new `INVALID_LATENCY_CONFIG`. Every code SHALL be reachable from a production throw site. #### Scenario: registry contains INVALID_LATENCY_CONFIG - **WHEN** `NETWORK_TOPO_CODES` is inspected - **THEN** `INVALID_LATENCY_CONFIG` maps to `"NET_TOPO.INVALID_LATENCY_CONFIG"` **Verified by:** `src/error-codes.spec-conformance.test.ts::"NETWORK_TOPO_CODES includes INVALID_LATENCY_CONFIG"` --- ### Requirement: No bare-Error throws in production code The package SHALL NOT contain any `throw new (Error|TypeError|RangeError|SyntaxError)\(` site inside `src/` outside test files. Every production failure throws `NetworkTopoError` or a transitive `SuaveplanError` subclass. #### Scenario: source grep returns zero hits - **WHEN** `grep -rnE "throw new (Error|TypeError|RangeError|SyntaxError)\(" packages/network/network-plugin-topology/src/ --include="*.ts"` excluding tests - **THEN** the output is empty **Verified by:** `tests/no-bare-throws.test.ts::"network-plugin-topology src/ has no bare Error throws"` --- ### Requirement: README is auto-docs idempotent The package `README.md` SHALL contain exactly one balanced auto-docs block; the generator SHALL produce a byte-identical file on a second run. #### Scenario: marker count is exactly one - **WHEN** `grep -c "begin:auto-docs" packages/network/network-plugin-topology/README.md` - **THEN** output is `1` **Verified by:** `scripts/generate-package-module-tree.test.ts::"README regen is idempotent for network-plugin-topology"`