5.1 KiB
Capability Spec: network-content-delivery
Capability: network-content-delivery
Status: PROPOSED
Every requirement below carries a
**Verified by:**line at authoring time. No exceptions. (Seeopenspec/AGENTS.md§13.)
ADDED Requirements
Requirement: PackerIntegration adapts packer output to ChunkManifest
The package SHALL export a PackerIntegration class that consumes a PackerOutputLike (fileId: string, chunkHashes: readonly string[], hashAlgorithm: string, packerVersion: string) and produces a wire-stable ChunkManifest whose totalChunks equals chunkHashes.length and whose chunkHashes is a defensive copy. buildManifest() SHALL be pure; publish() SHALL build AND broadcast cd:manifest on the optional bus. Empty / non-string fields SHALL surface as NETWORK_CD_CODES.PACKER_MANIFEST_INVALID.
Verified by: src/packer/packer-integration.spec-conformance.test.ts
Scenario: Round-trip equality
- WHEN
publish(output)is called with a valid output and a bus - THEN the bus observes the same
ChunkManifestinstance the method returns
Requirement: ChunkStore enforces TTL + LRU + byte budget
The package SHALL export a ChunkStore class persisting chunks via a pluggable ChunkStoreAdapter (put / get / delete, sync OR async). put SHALL evict oldest-touched entries until the new chunk fits and SHALL throw STORE_QUOTA_EXCEEDED when a single chunk exceeds maxBytes. get SHALL evict and return undefined when the entry's age exceeds maxAgeMs. Every accepted put SHALL emit cd:chunk-stored; every removal SHALL emit cd:chunk-evicted with reason: "ttl" | "lru" | "manual". MemoryChunkStoreAdapter SHALL be the default adapter.
Verified by: src/store/chunk-store.spec-conformance.test.ts, src/store/chunk-store.eviction.spec-conformance.test.ts
Scenario: Touch survives next eviction
- WHEN the store is full and
get(h)runs before the nextput - THEN the touched entry survives the next LRU eviction and the head of the LRU list is evicted instead
Requirement: PrefetchEngine fans out only inside the session scope
The package SHALL export a PrefetchEngine class whose dispatch(ctx) invokes a pluggable PredictionModel, filters predictions against (a) a LocalChunkPredicate, (b) the ChunkIndex (in-scope holders), and (c) the SessionScopeProvider (peers in the requester's session, with the requester itself filtered out), then emits cd:prefetch-issued per surviving chunk. A model that throws or returns a non-array SHALL surface as PREFETCH_MODEL_FAILED. The engine SHALL truncate predictions to maxBatchSize (default 16) and report the truncated count.
Verified by: src/prefetch/prefetch-engine.spec-conformance.test.ts
Scenario: Empty scope short-circuits
- WHEN the session scope returns an empty array
- THEN the dispatch returns
peersAvailable: 0andissued: []without emitting any bus event
Requirement: BandwidthThrottler tags BULK and respects two-bucket admit
The package SHALL export a BandwidthThrottler class maintaining one per-peer + one global token bucket (each with configurable rate + burst). admit() SHALL block until both buckets have sufficient tokens AND tag the resulting envelope with priority: "bulk". tryAdmit() SHALL be the non-blocking probe form. A single-chunk request larger than either burst capacity SHALL throw THROTTLE_RATE_INVALID. Every blocking wait SHALL emit cd:throttled with reason: "per-peer" | "global" and delayMs.
Verified by: src/throttle/bandwidth-throttler.spec-conformance.test.ts
Scenario: Per-peer isolation
- WHEN peer-a has emptied its per-peer bucket and peer-b admits a chunk that fits in peer-b's full bucket
- THEN peer-b's admit completes without sleeping and without emitting
cd:throttled
Requirement: ContentDeliveryPlugin facade exposes optional subsystems
ContentDeliveryPlugin SHALL expose packer: PackerIntegration (always constructed), store: ChunkStore | undefined, prefetch: PrefetchEngine | undefined, and throttler: BandwidthThrottler | undefined (constructed iff their respective config field is supplied). The plugin SHALL forward an injected clock and bus into every constructed subsystem.
Verified by: src/plugin/content-delivery-plugin.spec-conformance.test.ts
Scenario: Optional subsystems absent by default
- WHEN the plugin is constructed without
store/prefetch/throttleconfig fields - THEN
plugin.store,plugin.prefetch,plugin.throttlerSHALL all beundefinedwhileplugin.packerSHALL be defined
Requirement: Error registry growth is additive
NETWORK_CD_CODES SHALL include STORE_QUOTA_EXCEEDED, STORE_CHUNK_NOT_FOUND, PACKER_MANIFEST_INVALID, PREFETCH_MODEL_FAILED, THROTTLE_RATE_INVALID IN ADDITION to the seven core codes shipped with the parent package. Every code SHALL be prefixed NET_CD..
Verified by: src/error-codes.spec-conformance.test.ts
Scenario: Codes are frozen
- WHEN the registry is loaded
- THEN
Object.isFrozen(NETWORK_CD_CODES) === trueand every value matches/^NET_CD\./