Files
2026-05-27 23:29:53 +01:00

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 its family === "room"

Scenario: URI parses through parseFallbackUri with two candidates in order

  • WHEN parseFallbackUri(manifest.entries[0].uri) is called
  • THEN the result's candidates has 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 calls register(registry, collector) against the post-change source
  • THEN collector.plugins.length === 1 AND collector.plugins[0].scheme === "bundle" AND registry.has("pool-outdoor-swimming-pool") === true (every assertion from the pre-change register.test.ts continues to hold)

Scenario: type import from @suaveplan/audio-ir-core

  • WHEN the test inspects src/register/register.ts for the MutableUriPluginRegistrar source
  • 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 createUriPluginCollector from @suaveplan/audio-ir-core AND 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 createUriPluginCollector from @suaveplan/audio-ir-core AND uses it to compose the resolver AND the storage_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.wav via node:fs
  • THEN the read succeeds AND the file's byte length is > 0 AND decodeWav(bytes) resolves successfully

Verified by: tests/integration/file-presence.test.ts::"outdoor-swimming-pool.wav exists on disk and decodes"