5.9 KiB
Capability Spec: audio-ir-room-plugin-pool
Package: @suaveplan/audio-ir-room-plugin-pool
Status: SHIPPED
Every requirement carries a
**Verified by:**line.
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, target, options?) imports MutableUriPluginRegistrar from @suaveplan/audio-ir-core
The package SHALL export register(registry: IrRegistry, target: MutableUriPluginRegistrar, options?: RegisterOptions): void where MutableUriPluginRegistrar is imported as a type from @suaveplan/audio-ir-core rather than declared locally. The local interface declaration in src/register/register.ts SHALL be removed. The function body's behavior (construct bundleUriPlugin, call target.use(plugin), call registerManifest(registry, manifest)) is unchanged — the change is type-import only.
This requirement supersedes the prior register requirement shipped in add-audio-ir-room-plugin-pool, which referenced a locally-defined MutableUriPluginRegistrar interface.
Scenario: type-only switch leaves behavior unchanged
- WHEN the test composes
collector = createUriPluginCollector()(from@suaveplan/audio-ir-core) AND callsregister(registry, collector)against the post-change source - THEN
collector.plugins.length === 1ANDcollector.plugins[0].scheme === "bundle"ANDregistry.has("pool-outdoor-swimming-pool") === true(every assertion from the pre-changeregister.test.tscontinues to hold)
Scenario: type import from @suaveplan/audio-ir-core
- WHEN the test inspects
src/register/register.tsfor theMutableUriPluginRegistrarsource - THEN the symbol is imported (not declared locally) from
@suaveplan/audio-ir-core
Verified by: src/register/register.test.ts::"register populates collector and registry via foundation interface" AND src/register/register.test.ts::"MutableUriPluginRegistrar is imported from @suaveplan/audio-ir-core not declared locally"
Requirement: Integration tests use createUriPluginCollector() from the foundation
Both tests/integration/bundle-only.test.ts and tests/integration/fallback-walk.test.ts SHALL construct their plugin collector via createUriPluginCollector() from @suaveplan/audio-ir-core rather than via ad-hoc { use: (p) => arr.push(p) } literals. All other test behavior (bundle wins → HTTPS not fetched; bundle miss → HTTPS succeeds via fallback walk; storage_uri.fallback_total{candidate_index:0, outcome:"transient"} counter increment) is unchanged.
This requirement supersedes the prior integration-test requirements shipped in add-audio-ir-room-plugin-pool, which referenced ad-hoc collector literals.
Scenario: bundle-only integration test uses foundation collector
- WHEN the test reads
tests/integration/bundle-only.test.ts - THEN the file imports
createUriPluginCollectorfrom@suaveplan/audio-ir-coreAND uses it to compose the resolver
Scenario: fallback-walk integration test uses foundation collector
- WHEN the test reads
tests/integration/fallback-walk.test.ts - THEN the file imports
createUriPluginCollectorfrom@suaveplan/audio-ir-coreAND uses it to compose the resolver AND thestorage_uri.fallback_total{candidate_index:0, outcome:"transient"}counter still ticks exactly once on the fallback hop
Verified by: tests/integration/bundle-only.test.ts::"bundle candidate wins without fetching HTTPS via foundation collector" AND tests/integration/fallback-walk.test.ts::"bundle miss falls through to HTTPS via foundation collector"
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"