6.5 KiB
Capability Spec Delta: audio-ir-room-plugin-pool (add-audio-ir-room-plugin-pool)
Package: @suaveplan/audio-ir-room-plugin-pool
Status: PROPOSED
Every requirement carries a
**Verified by:**line.
ADDED Requirements
Requirement: Manifest declares one pool IR with a fallback:// URI
The package SHALL export manifest: IrManifest with packageSlug: "pool", family: "room", AND entries: readonly IrManifestEntry[] containing exactly one entry:
{
id: "pool-outdoor-swimming-pool",
family: "room",
uri: buildFallbackUri([
"bundle://audio-ir-room-plugin-pool/outdoor-swimming-pool.wav",
"https://swch.b-cdn.net/_/audio/ir/room/roompool.wav",
]),
channels: <decoded>,
sampleRate: <decoded>,
gainDb: 0,
license: <see README>,
source: "Mountain Duck media library + https://swch.b-cdn.net CDN",
}
channels AND sampleRate SHALL be the values measured by decoding the bundled file at manifest-authoring time. The entry's uri SHALL parse through parseFallbackUri (from @suaveplan/storage-uri-plugin-fallback) AND yield two candidates whose first element starts with bundle:// AND whose second element starts with https://.
Scenario: manifest parses through IrManifestSchema
- WHEN
IrManifestSchema.parse(manifest)is called - THEN the result deep-equals the export AND the lone entry's
id === "pool-outdoor-swimming-pool"AND itsfamily === "room"
Scenario: URI parses through parseFallbackUri with two candidates in order
- WHEN
parseFallbackUri(manifest.entries[0].uri)is called - THEN the result's
candidateshas length 2,candidates[0]starts with"bundle://audio-ir-room-plugin-pool/",candidates[1] === "https://swch.b-cdn.net/_/audio/ir/room/roompool.wav"
Verified by: src/manifest/manifest.test.ts::"manifest parses through IrManifestSchema" AND src/manifest/manifest.test.ts::"URI parses through parseFallbackUri with two ordered candidates"
Requirement: register(registry, resolver) adds the bundle plugin and registers the entry
The package SHALL export register(registry: IrRegistry, resolver: StorageUriResolver): void that:
- Calls
resolver.use(bundleUriPlugin({ packageSlug: "audio-ir-room-plugin-pool", rootUrl: new URL("../files/", import.meta.url) }))exactly once. - Calls
registerManifest(registry, manifest).
The function SHALL NOT register the fallback plugin — that is the consumer's responsibility (one fallbackUriPlugin per resolver, shared across every fallback-using package). The function SHALL NOT register an HTTPS plugin either.
Scenario: register adds the bundle plugin and registers the entry
- WHEN
register(registry, resolver)is called - THEN
resolver.dispatch("bundle://audio-ir-room-plugin-pool/outdoor-swimming-pool.wav")succeeds ANDregistry.has("pool-outdoor-swimming-pool") === true
Scenario: register does NOT register the fallback plugin
- WHEN
register(registry, resolver)is called against a resolver that has nofallbackplugin - THEN attempting to dispatch
"fallback://?u=..."through the same resolver throwsSTORAGE_URI.UNKNOWN_SCHEME(proving the package did not silently add it)
Verified by: src/register/register.test.ts::"adds bundle plugin and registers the entry" AND src/register/register.test.ts::"does not register the fallback plugin"
Requirement: Bundle-first end-to-end resolution
The package SHALL include an integration test asserting that when the bundle file is present on disk AND the consumer has wired bundleUriPlugin, fallbackUriPlugin, AND an HTTPS plugin into the resolver, the loader's load("pool-outdoor-swimming-pool") resolves through the bundle candidate WITHOUT touching the HTTPS candidate.
Scenario: bundle candidate wins; HTTPS candidate not fetched
- WHEN the bundle file exists at
<package-root>/files/outdoor-swimming-pool.wavAND a spy is attached to the HTTPS plugin'sadapter.getANDloader.load("pool-outdoor-swimming-pool")is called - THEN the call resolves to a frozen
ImpulseResponseAND the HTTPS spy's call count is exactly 0
Verified by: tests/integration/bundle-only.test.ts::"bundle candidate wins and HTTPS candidate is not fetched"
Requirement: Fallback walk to HTTPS on bundle miss
The package SHALL include an integration test asserting that when the bundle file is ABSENT at fixture-setup time, the loader's load("pool-outdoor-swimming-pool") falls through to the HTTPS candidate (served by a node:http fixture serving valid WAV bytes), AND that the fallback_uri.fallback_total{candidate_index:0, outcome:"transient"} counter (from the shipped fallback:// plugin) is incremented exactly once. The test SHALL stand up the HTTP fixture in beforeAll AND tear it down in afterAll.
Scenario: bundle missing → HTTPS succeeds via fallback walk
- WHEN the
<package-root>/files/outdoor-swimming-pool.wavsymlink is replaced with a temp dir containing NOoutdoor-swimming-pool.wav(so the bundle adapter returnsBUNDLE_URI.NOT_FOUND) AND the HTTPS fixture athttp://localhost:<port>/roompool.wavserves valid WAV bytes that decode to a known channel/sample-rate pair ANDloader.load("pool-outdoor-swimming-pool")is called - THEN the call resolves to a frozen
ImpulseResponsematching the HTTPS payload ANDfallback_uri.fallback_total{candidate_index:0, outcome:"transient"}is incremented exactly once ANDaudio_ir_loader.bytes_read_total{family:"room"}is incremented exactly once
NOTE: The integration test uses a substituted
rootUrlpointing at an empty temp directory rather than literally deleting the on-disk WAV file from the package. This keeps the on-disk artifact stable across test runs while still exercising the bundle-miss branch of the fallback walk.
Verified by: tests/integration/fallback-walk.test.ts::"bundle miss falls through to HTTPS and increments fallback counter"
Requirement: Bundled WAV exists on disk at build time
The package SHALL ship files/outdoor-swimming-pool.wav in its tarball. The file SHALL be non-empty AND SHALL decode successfully through decodeWav from @suaveplan/audio-decode-wav.
Scenario: bundle file exists and is non-empty
- WHEN the test reads
<package-root>/files/outdoor-swimming-pool.wavvianode:fs - THEN the read succeeds AND the file's byte length is
> 0ANDdecodeWav(bytes)resolves successfully
Verified by: tests/integration/file-presence.test.ts::"outdoor-swimming-pool.wav exists on disk and decodes"