Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,94 @@
# Capability Spec: network-plugin-devtools
**Capability**: `network-plugin-devtools`
**Status**: ACTIVE
> Every requirement below carries a `**Verified by:**` line at authoring time. No exceptions. (See `openspec/AGENTS.md` §13.)
---
## ADDED Requirements
### Requirement: NET_DEV-01 — NetworkVisualizer produces a stable IR and supports three modes
The package SHALL export a `NetworkVisualizer` class whose `start({ mode, refreshRate })` Zod-validates input (rejecting unknown modes with `VISUALIZER_INVALID_MODE`), whose `render()` produces a `VisualizerScene` IR carrying `nodes`, `edges`, `heat`, `width`, `height`, and an optional `focus` field, and whose three modes (`hierarchy`, `flow`, `heat`) drive distinct edge / heat collection paths from the injected `TopologyAdapter` and `MessageStreamAdapter`. `render()` SHALL surface `VISUALIZER_EMPTY_TOPOLOGY` when the active filter eliminates every node, and `render()` and `mode()` SHALL throw `VISUALIZER_NOT_STARTED` before `start()`.
**Verified by:** `src/visualizer/visualizer.spec-conformance.test.ts`
#### Scenario: Mode switches the edge source
- **WHEN** `start({ mode: "flow", refreshRate: 30 })` is called and the stream returns three flow edges
- **THEN** `render().edges.length === 3`
---
### Requirement: NET_DEV-02 — Visualizer export delegates to the injected Renderer with format checking
`NetworkVisualizer.export(format)` SHALL accept only `"svg"` or `"png"`, reject any other format with `VISUALIZER_INVALID_MODE`, surface renderer exceptions as `VISUALIZER_RENDERER_FAILED` (with the original error preserved as `cause`), and reject any renderer that returns an output whose `format` field does not match the requested format. The bundled `SvgRenderer` SHALL satisfy the `Renderer` contract and produce deterministic UTF-8 SVG bytes with `mimeType === "image/svg+xml"`. PNG support SHALL be delivered via injected `Renderer` (no built-in encoder dependency).
**Verified by:** `src/visualizer/visualizer.spec-conformance.test.ts`
#### Scenario: Renderer format mismatch rejected
- **WHEN** a renderer is injected that returns `{ format: "png" }` while the consumer asked for `"svg"`
- **THEN** `export("svg")` throws `VISUALIZER_RENDERER_FAILED`
---
### Requirement: NET_DEV-03 — StateInspector records, inspects, scrubs, and diffs per path
The package SHALL export a `StateInspector` class with a configurable per-path ring buffer (default 256), `record(StateSnapshot)` Zod boundary surfacing `INSPECTOR_DIFF_FAILED` on invalid input, `inspect(path)` returning `StateView` (throws `INSPECTOR_PATH_NOT_FOUND` when absent), `diff(path, fromVersion, toVersion)` producing `DiffView` with `added` / `removed` / `changed` JSON Pointers and a numeric `summary` (throws `INSPECTOR_INVALID_RANGE` when versions match or are missing), `scrub(path, atVersion)` returning the latest snapshot ≤ `atVersion` (throws `INSPECTOR_HISTORY_EMPTY` when none satisfies the cap), and `search(filter)` AND-combining populated `pathContains` / `peerId` / `sessionId` / `minVersion` / `maxVersion` filters.
**Verified by:** `src/inspector/inspector.spec-conformance.test.ts`
#### Scenario: Diff classifies added / removed / changed paths
- **WHEN** two snapshots are recorded with `{ a: 1 }` and `{ b: 2 }` at versions 1 and 2
- **THEN** `diff(path, 1, 2)` reports `removed: ["/a"]` AND `added: ["/b"]`
---
### Requirement: NET_DEV-04 — SimulationConfigTool builds, records, plays, and round-trips scenarios
The package SHALL export a `SimulationConfigTool` class with peer / link / condition / event editors, `startRecording` / `stopRecording` (throws `SIMULATOR_RECORDING_INACTIVE` on stop without start), `loadPlayback` enforcing scenario-id match (throws `SIMULATOR_SCENARIO_INVALID` on mismatch), `playback()` grouping events by `atMs` (throws `SIMULATOR_PLAYBACK_INACTIVE` before load), `exportJson()` / `importJson(json)` round-trip with full Zod validation (parse errors and schema errors both surface as `SIMULATOR_SCENARIO_INVALID`), `addPeer` rejecting duplicates with `SIMULATOR_PEER_DUPLICATE`, and `addLink` rejecting unknown endpoints with `SIMULATOR_PEER_NOT_FOUND`.
**Verified by:** `src/simulator/simulator.spec-conformance.test.ts`
#### Scenario: Playback groups concurrent events
- **WHEN** three events at `atMs: 100` and one at `atMs: 200` are recorded and replayed
- **THEN** `playback()` returns two ticks of length 3 and 1 in `atMs` order
---
### Requirement: NET_DEV-05 — PerformanceProfiler distributes, baselines, regresses, and suggests
The package SHALL export a `PerformanceProfiler` class that records latency / throughput / plugin-overhead / memory samples into bounded rings, exposes p50 / p95 / p99 / min / max / mean / count distributions per path, supports named baselines (`captureBaseline` rejects duplicates with `PROFILER_BASELINE_DUPLICATE`), `compare(baselineId)` produces a `Regression[]` ranked by Z-score across p50 / p95 / p99 latency and message-throughput drops (throws `PROFILER_NO_BASELINE` when missing), and `suggestions()` emits category-tagged `OptimizationSuggestion`s. Constructor SHALL throw `PROFILER_INVALID_THRESHOLD` on non-positive `windowSize` / `regressionZScore` / `regressionWarnPct`.
**Verified by:** `src/profiler/profiler.spec-conformance.test.ts`
#### Scenario: Tail-latency suggestion fires
- **WHEN** latency samples produce `p50 = 50` and `p99 = 200`
- **THEN** `suggestions()` includes a `category: "latency"` entry whose subject is the path
---
### Requirement: NET_DEV-06 — DevToolsPlugin facade exposes subsystems with a tier gate
`DevToolsPlugin` SHALL expose `visualizer`, `inspector`, `simulator`, `profiler` as public fields, evaluate the injected `tierGate` at `install()` time, record `PLUGIN_TIER_GATE_REJECTED` as `tierRejection()` when the gate returns false, expose `requireActive()` raising `PLUGIN_DEV_DISABLED` when the gate rejected activation, and expose idempotent `stop()` / `dispose()` lifecycles.
**Verified by:** `src/plugin/devtools-plugin.spec-conformance.test.ts`
#### Scenario: Production gate inert
- **WHEN** `tierGate: () => false` is supplied and `install` then `start` are called
- **THEN** `isStarted()` is true, `isActive()` is false, `requireActive()` throws `PLUGIN_DEV_DISABLED`
---
### Requirement: NET_DEV-07 — Frozen NETWORK_DEV_CODES registry
`NETWORK_DEV_CODES` SHALL be frozen (`Object.isFrozen() === true`), every value SHALL be prefixed `NET_DEV.`, and the registry SHALL include every code consumed by visualizer, inspector, simulator, profiler, and plugin facade modules. Removing a code is a major-version change.
**Verified by:** `src/error-codes.spec-conformance.test.ts`