Initial Commit
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
<!-- markdownlint-disable MD024 -->
|
||||
|
||||
# Capability Spec Delta: network-plugin-intra-relay (complete-network-plugins)
|
||||
|
||||
**Package**: `@suaveplan/network-plugin-intra-relay`
|
||||
**Status**: PROPOSED
|
||||
|
||||
> Every requirement below carries a `**Verified by:**` line at authoring time.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: IntraRelayPlugin conforms to NetworkPlugin lifecycle
|
||||
|
||||
The package SHALL export `IntraRelayPlugin implements NetworkPlugin`. The plugin SHALL manage relay-path discovery, connection lifecycle, envelope wrap/unwrap, and security on install.
|
||||
|
||||
#### Scenario: install / uninstall lifecycle
|
||||
|
||||
- **WHEN** a host installs then uninstalls the plugin
|
||||
- **THEN** the plugin reaches `INSTALLED` then `UNINSTALLED` cleanly
|
||||
|
||||
**Verified by:** `src/plugin/intra-relay-plugin.spec-conformance.test.ts::"install / uninstall lifecycle is clean"`
|
||||
|
||||
---
|
||||
|
||||
### Requirement: RelayEnvelope wraps and unwraps inner messages with hop tracking
|
||||
|
||||
The package SHALL export `wrapEnvelope`, `unwrapEnvelope`, `advanceEnvelope`, `nextHopFor`, `WrapInput`, `InnerMessage`, and `DEFAULT_MAX_HOPS`. Each hop SHALL increment a counter; the envelope SHALL throw `NetworkIntraRelayError(MAX_HOPS_EXCEEDED)` when the counter would exceed the configured limit.
|
||||
|
||||
#### Scenario: hop counter increments per advance
|
||||
|
||||
- **WHEN** `advanceEnvelope` is called twice on a fresh envelope
|
||||
- **THEN** the envelope's hop count is 2
|
||||
|
||||
**Verified by:** `src/envelope/relay-envelope.spec-conformance.test.ts::"advance increments hop counter"`
|
||||
|
||||
---
|
||||
|
||||
### Requirement: RelayPerformanceMonitor validates hysteresis config with typed error
|
||||
|
||||
The package SHALL export `RelayPerformanceMonitor`. The class SHALL throw `NetworkIntraRelayError(HYSTERESIS_CONFIG_INVALID)` whenever hysteresis configuration is missing, negative, or non-finite. The class SHALL NOT throw bare `Error`.
|
||||
|
||||
#### Scenario: invalid hysteresis config raises typed error
|
||||
|
||||
- **WHEN** `new RelayPerformanceMonitor({ hysteresisMs: -1 })` is called
|
||||
- **THEN** the call throws `NetworkIntraRelayError` with `code === "NET_INTRA_RELAY.HYSTERESIS_CONFIG_INVALID"`
|
||||
|
||||
**Verified by:** `src/monitor/relay-performance-monitor.spec-conformance.test.ts::"throws HYSTERESIS_CONFIG_INVALID on negative hysteresis"`
|
||||
|
||||
---
|
||||
|
||||
### Requirement: RelaySecurityManager validates rate-limit config with typed error
|
||||
|
||||
`RelaySecurityManager` SHALL throw `NetworkIntraRelayError(RATE_LIMIT_CONFIG_INVALID)` whenever `refillRatePerSec` or `burstSize` is zero, negative, or non-finite. The class SHALL NOT throw bare `RangeError`.
|
||||
|
||||
#### Scenario: zero burstSize raises typed error
|
||||
|
||||
- **WHEN** `new RelaySecurityManager({ rateLimit: { burstSize: 0, … } })` is called
|
||||
- **THEN** the call throws `NetworkIntraRelayError` with `code === "NET_INTRA_RELAY.RATE_LIMIT_CONFIG_INVALID"` and `context.field === "burstSize"`
|
||||
|
||||
#### Scenario: negative refillRatePerSec raises typed error
|
||||
|
||||
- **WHEN** `new RelaySecurityManager({ rateLimit: { refillRatePerSec: -1, … } })` is called
|
||||
- **THEN** the call throws `NetworkIntraRelayError` with `code === "NET_INTRA_RELAY.RATE_LIMIT_CONFIG_INVALID"` and `context.field === "refillRatePerSec"`
|
||||
|
||||
**Verified by:** `src/security/relay-security-manager.spec-conformance.test.ts::"throws RATE_LIMIT_CONFIG_INVALID on zero burstSize"` AND `src/security/relay-security-manager.spec-conformance.test.ts::"throws RATE_LIMIT_CONFIG_INVALID on negative refillRatePerSec"`
|
||||
|
||||
---
|
||||
|
||||
### Requirement: RelayDiscovery surfaces candidates and tracks peer snapshots
|
||||
|
||||
The package SHALL export `RelayDiscovery`, `RelayDiscoveryConfig`, and `PeerSnapshot`. The discovery SHALL maintain a per-peer snapshot and SHALL throw `NetworkIntraRelayError(NO_RELAY_CANDIDATE)` when asked for a candidate and none qualify.
|
||||
|
||||
#### Scenario: no candidate available
|
||||
|
||||
- **WHEN** `discovery.pickCandidate()` is called with no qualifying peers
|
||||
- **THEN** the call throws `NetworkIntraRelayError(NO_RELAY_CANDIDATE)`
|
||||
|
||||
**Verified by:** `src/discovery/relay-discovery.spec-conformance.test.ts::"throws NO_RELAY_CANDIDATE when no qualifying peers"`
|
||||
|
||||
---
|
||||
|
||||
### Requirement: RelayConnectionManager enforces a finite state machine
|
||||
|
||||
The package SHALL export `RelayConnectionManager`, `RelayConnectionManagerConfig`, and the `RELAY_TRANSITIONS` table. Illegal transitions SHALL throw `NetworkIntraRelayError(INVALID_TRANSITION)`.
|
||||
|
||||
#### Scenario: illegal transition raises typed error
|
||||
|
||||
- **WHEN** the manager is asked to transition from `CLOSED` directly to `STREAMING`
|
||||
- **THEN** the call throws `NetworkIntraRelayError` with `code === "NET_INTRA_RELAY.INVALID_TRANSITION"`
|
||||
|
||||
**Verified by:** `src/connection/relay-connection-manager.spec-conformance.test.ts::"throws INVALID_TRANSITION on illegal state move"`
|
||||
|
||||
---
|
||||
|
||||
### Requirement: Error-code registry is frozen and namespaced NET_INTRA_RELAY
|
||||
|
||||
`NETWORK_INTRA_RELAY_CODES` SHALL be frozen under the `NET_INTRA_RELAY` namespace and SHALL include at minimum: `NO_RELAY_CANDIDATE`, `RELAY_LOOP_DETECTED`, `MAX_HOPS_EXCEEDED`, `INVALID_TRANSITION`, `PATH_NOT_FOUND`, `PATH_ALREADY_EXISTS`, `STRATEGY_UNKNOWN`, `ENVELOPE_INVALID`, `RELAY_SIGNATURE_INVALID`, `RELAY_PEER_UNTRUSTED`, `RELAY_RATE_LIMIT_EXCEEDED`, and the new `HYSTERESIS_CONFIG_INVALID`, `RATE_LIMIT_CONFIG_INVALID`.
|
||||
|
||||
#### Scenario: registry contains the two new codes
|
||||
|
||||
- **WHEN** `NETWORK_INTRA_RELAY_CODES` is inspected
|
||||
- **THEN** `HYSTERESIS_CONFIG_INVALID` and `RATE_LIMIT_CONFIG_INVALID` map to their `"NET_INTRA_RELAY.<KEY>"` values
|
||||
|
||||
**Verified by:** `src/error-codes.spec-conformance.test.ts::"NETWORK_INTRA_RELAY_CODES includes HYSTERESIS_CONFIG_INVALID and RATE_LIMIT_CONFIG_INVALID"`
|
||||
|
||||
---
|
||||
|
||||
### Requirement: No bare-Error throws in production code
|
||||
|
||||
`src/` SHALL contain zero bare `throw new (Error|TypeError|RangeError|SyntaxError)\(` sites outside test files.
|
||||
|
||||
#### Scenario: source grep returns zero hits
|
||||
|
||||
- **WHEN** the grep is run
|
||||
- **THEN** the output is empty
|
||||
|
||||
**Verified by:** `tests/no-bare-throws.test.ts::"network-plugin-intra-relay 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-intra-relay/README.md`
|
||||
- **THEN** output is `1`
|
||||
|
||||
**Verified by:** `scripts/generate-package-module-tree.test.ts::"README regen is idempotent for network-plugin-intra-relay"`
|
||||
Reference in New Issue
Block a user