Files
suaveplan/openspec/changes/complete-network-plugins/proposal.md
2026-05-27 23:29:53 +01:00

121 lines
13 KiB
Markdown

# 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/<capability>/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-<name>/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 `<!-- begin:auto-docs -->` / `<!-- end:auto-docs -->` 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-<name>/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-<name>/spec.md`.
- **ADDED**: every requirement carries a `**Verified by:**` line pointing at either an existing `src/<feature>/<feature>.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 `<!-- begin:auto-docs --> … <!-- end:auto-docs -->` 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/<feature>/<feature>.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-<pkg>` 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.