# Complete network-plugin-* family hygiene - **Change ID**: complete-network-plugins - **Packages**: `@suaveplan/network-plugin-protocol`, `@suaveplan/network-plugin-topology`, `@suaveplan/network-plugin-state`, `@suaveplan/network-plugin-intra-relay`, `@suaveplan/network-plugin-monitor`, `@suaveplan/network-plugin-auth`, `@suaveplan/network-plugin-circuit-breaker`, `@suaveplan/network-plugin-migration`, `@suaveplan/network-plugin-partition`, `@suaveplan/network-plugin-qos` (cross-cutting fixes touch the rest of `network-plugin-*` and `network-*` README fleet) - **Status**: PROPOSED - **Type**: spec-conformance + bug-fix + new-spec (multi) - **Date**: 2026-05-17 - **Design**: ./design.md - **Spec deltas**: ./specs//spec.md (10 files — one per plugin) ## Why A family-wide audit of `packages/network/network-plugin-*` surfaced three independent hygiene gaps that must be closed before any of the plugins can be declared enterprise-done. Per CLAUDE.md §3 ("Build feature-rich, production-grade packages") and the "no deferrals on migration" rule, all three problems ship in this single change rather than being split into follow-ups that would leave the family in a half-migrated state. 1. **Structured-error non-conformance (HIGH).** Eleven concrete `throw new Error(...)`, `throw new RangeError(...)` sites across five plugins bypass the per-package `SuaveplanError` subclass. This loses programmatic-handling (`error.code`), OTel span-event tagging, redaction, i18n message keys, and downstream `instanceof` assertions. The audit named five sites; verification (`grep -rnE "throw new (Error|TypeError|RangeError|SyntaxError)\("`) found six additional unflagged sites in `network-plugin-intra-relay` and `network-plugin-monitor` that ship in the same fix. 2. **Missing canonical capability specs (MED).** Five plugins (`auth`, `circuit-breaker`, `migration`, `partition`, `qos`) have **no** archived OpenSpec change and **no** `openspec/specs/network-plugin-/spec.md`. Spot-check confirms the other five HIGH plugins ALSO have no canonical spec — every archived `network-plugin-*` change went straight to `proposal.md`/`tasks.md` without a `## ADDED Requirements` delta. This change authors specs for all ten as the baseline future deltas will be applied against. 3. **README auto-docs regeneration bug (LOW).** `scripts/generate-package-module-tree.ts` replaces the section between `` / `` markers, but when a previous run injected nested or unbalanced markers, the regex matches the outermost pair only, leaving every nested duplicate in place. `network-plugin-auth/README.md` has 22 `begin:auto-docs` markers; the worst offender (`network-discovery-trystero/README.md`) has 110. **43 network-package READMEs** are malformed. ## What changes ### Part A — error-handling migration (5 HIGH plugins; 11 cited + 6 newly-found sites) - **MODIFIED**: `packages/network/network-plugin-protocol/src/decoder/decoder.ts` — `throw new Error("protocol.incomplete-message")` becomes `throw new NetworkProtoError(NETWORK_PROTO_CODES.DESERIALIZATION_FAILED, "protocol.incomplete-message", { context: { reason: "incomplete-message", remaining } })`. - **MODIFIED**: `packages/network/network-plugin-topology/src/latency/latency-assigner.ts` — both `throw new Error(...)` (L57, L68) become `NetworkTopoError` with new code `INVALID_LATENCY_CONFIG`. - **MODIFIED**: `packages/network/network-plugin-state/src/bloom/bloom-filter-comparator.ts` — `throw new Error("bloom: byte length mismatch")` becomes `NetworkStateError` with new code `BLOOM_LENGTH_MISMATCH`. - **MODIFIED**: `packages/network/network-plugin-intra-relay/src/monitor/relay-performance-monitor.ts` — `throw new Error(...)` (L51) becomes `NetworkIntraRelayError` with new code `HYSTERESIS_CONFIG_INVALID`. - **MODIFIED**: `packages/network/network-plugin-intra-relay/src/security/relay-security-manager.ts` — both `throw new RangeError(...)` (L57, L62) become `NetworkIntraRelayError` with new code `RATE_LIMIT_CONFIG_INVALID` (audit-cited only `relay-performance-monitor.ts`; this one was missed). - **MODIFIED**: `packages/network/network-plugin-monitor/src/grafana/dashboard-generator.ts` — `throw new Error("addRow: title must be non-empty")` becomes `NetworkMonError` with new code `DASHBOARD_ROW_INVALID`. - **MODIFIED**: `packages/network/network-plugin-monitor/src/quality/bandwidth-monitor.ts` — both `throw new RangeError(...)` (L49, L53) become `NetworkMonError` with new code `BANDWIDTH_MONITOR_CONFIG_INVALID` (newly found). - **MODIFIED**: `packages/network/network-plugin-monitor/src/quality/degradation-manager.ts` — both `throw new RangeError(...)` (L50, L54) become `NetworkMonError` with new code `DEGRADATION_CONFIG_INVALID` (newly found). - **ADDED**: per-plugin error codes — `NETWORK_TOPO_CODES.INVALID_LATENCY_CONFIG`, `NETWORK_STATE_CODES.BLOOM_LENGTH_MISMATCH`, `NETWORK_INTRA_RELAY_CODES.{HYSTERESIS_CONFIG_INVALID, RATE_LIMIT_CONFIG_INVALID}`, `NETWORK_MON_CODES.{DASHBOARD_ROW_INVALID, BANDWIDTH_MONITOR_CONFIG_INVALID, DEGRADATION_CONFIG_INVALID}`. No new code needed for the protocol case — reuses existing `DESERIALIZATION_FAILED`. - **ADDED**: new `*.spec-conformance.test.ts` assertions on each migrated throw — pin `instanceof NetworkXxxError`, `error.code === "NET_XXX.YYY"`, and stable `context` keys. ### Part B — author canonical capability specs (10 plugins) - **ADDED**: `openspec/changes/complete-network-plugins/specs/network-plugin-/spec.md` for all ten plugins. Each delta is a `## ADDED Requirements` block enumerating the full current public surface (classes, factories, lifecycle, error codes, OTel instruments where present, plugin-host contract) so that future changes have a baseline to delta against. On archive these fold into `openspec/specs/network-plugin-/spec.md`. - **ADDED**: every requirement carries a `**Verified by:**` line pointing at either an existing `src//.spec-conformance.test.ts` or a new test authored in this change (Part A creates several). - **ADDED**: parity-vs-intent notes for the five MED plugins are folded into the spec deltas — no separate "missing features" list because the audit found no under-shipped surface (every export in `index.ts` has a matching `.md` and a matching `*.spec-conformance.test.ts`; the gap was purely the missing canonical spec). ### Part C — README regeneration fleet-wide - **MODIFIED**: `packages/network/network-plugin-auth/README.md` and the 42 other malformed `packages/network/*/README.md` files — strip nested ` … ` blocks (regex matching balanced outermost pairs, repeated to a fixpoint) before re-running `bun scripts/generate-package-module-tree.ts`. - **MODIFIED**: `scripts/generate-package-module-tree.ts` — `replaceMarkedSection` is hardened to detect and collapse nested marker pairs before substitution (prevents recurrence). A regression test pins the round-trip: README → generate → generate again → byte-identical. - **ADDED**: pre-archive scan `grep -c "begin:auto-docs" packages/network/*/README.md` MUST return exactly `1` for every file. ## Out of scope - **Protocol-level redesigns** — the binary wire format in `network-plugin-protocol` (164-byte header, version negotiation), the gossip envelope in `network-plugin-intra-relay`, and the bloom-filter parameters in `network-plugin-state` are unchanged. - **New transports** — no new `network-transport-*` packages, no edits to existing transports beyond what `network-plugin-*` consumers strictly require. - **OTel instrumentation expansion** — only the error-handling paths gain OTel span-event tagging (free with `SuaveplanError`); broader meter/histogram coverage is left for a future `network-observability-*` change. - **Cross-mesh trust-store persistence** — `network-plugin-auth` currently holds the trust set in-memory; persistent backing is a separate change. - **Family-wide test-runner-import sweep** — `bun scripts/lint/check-test-runner-imports.ts` already gates this on every commit; if any `network-plugin-*` test still imports from `vitest` directly the gate flags it, and we fix it inline during Part A. ## Source mapping This change is genesis-original (the per-plugin audits and the README bug were discovered on `genesis`, not in upstream `enterprise/*.md`). Where an upstream enterprise audit doc exists for a sibling concept (`../suaveplan/enterprise/network-observability.md`, `../suaveplan/enterprise/network-protocol.md`) the new code names follow upstream conventions, but no clause is lifted verbatim. | Requirement | Source | Notes | |---|---|---| | Bare-error replacement (all sites) | genesis grep evidence | sites enumerated in §What changes | | Per-plugin spec drafts | current `src/` of each plugin | reverse-engineered from existing exports + `*.spec-conformance.test.ts` | | README regeneration | `scripts/generate-package-module-tree.ts` bug | reproducer: run script twice, see marker count double | ### Requirements with no source line | Requirement | Justification | |---|---| | New error codes (7 added across 4 plugins) | The bare-`Error` sites have no existing matching code in the per-plugin registry; introducing codes is the structural fix, not a feature add | | README idempotency regression test | Prevents the same defect from re-landing once auto-docs is re-run | ## Verification approach - **Part A**: each migrated throw site gets a `src//.spec-conformance.test.ts` assertion — `expect(() => fn()).toThrowError(NetworkXxxError)` AND `.toMatchObject({ code: "NET_XXX.YYY", context: { … } })`. The existing `src/error-codes.spec-conformance.test.ts` for each plugin is extended to pin the new code constants in the frozen registry. - **Part B**: `bunx openspec validate complete-network-plugins --strict` confirms every requirement has a `**Verified by:**` line referencing a real file path; `bun scripts/openspec-conformance.ts @suaveplan/network-plugin-` confirms every cited test exists and matches the requirement. - **Part C**: post-fix scan `for f in packages/network/*/README.md; do n=$(grep -c "begin:auto-docs" "$f"); [ "$n" -eq 1 ] || echo "BAD: $f ($n)"; done` MUST return zero lines. A test in `scripts/generate-package-module-tree.test.ts` pins the double-run-idempotent invariant. ## Migration This change introduces 7 new error codes that consumers MAY assert on. None of the migrated throw sites was previously stable API (the bare `Error.message` strings were never documented as a contract), but downstream `network-*` packages that catch generically MAY now receive a `SuaveplanError` subclass where they previously received a plain `Error`. - **Old behavior**: `catch (e) { if (e.message === "bloom: byte length mismatch") { … } }` - **New behavior**: `catch (e) { if (e instanceof NetworkStateError && e.code === "NET_STATE.BLOOM_LENGTH_MISMATCH") { … } }` - **Search command for consumers**: `grep -rln "bloom: byte length mismatch\|protocol.incomplete-message\|addRow: title must be non-empty\|hysteresisMs must be\|burstSize must be\|maxSamples must be\|smoothingFactor must be\|windowSize must be" packages/` - **Recommended replacement**: switch to the typed `code` check shown above; both `e.message` (still the same i18n key) and `e.code` are stable now. ## Impact ### Affected dependents Run `bunx turbo ls --filter=...@suaveplan/network-plugin-{protocol,topology,state,intra-relay,monitor,auth,circuit-breaker,migration,partition,qos}` to enumerate. Per-plugin dependents are the network composite packages and downstream consumers: - `@suaveplan/network` — [x] tests run / [x] docs reviewed / [ ] no action needed (consumes all 10 plugins via plugin registry; behavior-change is additive — new error codes inherit from `SuaveplanError`) - `@suaveplan/network-testing` — [x] tests run / [ ] docs reviewed / [ ] no action needed (fixture helpers may catch the migrated throws; assertions updated to `instanceof NetworkXxxError`) - `@suaveplan/network-bootstrap` — [ ] tests run / [x] docs reviewed / [x] no action needed (consumes the plugin registry but not the migrated throw sites) - `@suaveplan/network-content-delivery`, `@suaveplan/network-intra-relay`, `@suaveplan/network-session`, `@suaveplan/network-webrtc` — [ ] tests run / [x] docs reviewed / [x] no action needed (composite packages re-export plugin types; new error codes are purely additive) - `@suaveplan/drive-network-integration` — [ ] tests run / [x] docs reviewed / [x] no action needed (doesn't import any of the migrated modules directly) - All `@suaveplan/network-transport-*` — [ ] tests run / [x] docs reviewed / [x] no action needed (transports do not import plugin-level error classes) Dependent-sweep proof — after Part A lands, run: ```bash bunx turbo test --filter=...@suaveplan/network-plugin-protocol \ --filter=...@suaveplan/network-plugin-topology \ --filter=...@suaveplan/network-plugin-state \ --filter=...@suaveplan/network-plugin-intra-relay \ --filter=...@suaveplan/network-plugin-monitor ``` A green run is the §13 step 10 acceptance for the five HIGH plugins.