5.3 KiB
Capability Spec Delta: audio-ir-room-plugin-club (add-audio-ir-room-plugin-club)
Package: @suaveplan/audio-ir-room-plugin-club
Status: PROPOSED
Every requirement carries a
**Verified by:**line.
ADDED Requirements
Requirement: Manifest declares both club room IRs with bundle URIs
The package SHALL export manifest: IrManifest with packageSlug: "club", family: "room", AND entries: readonly IrManifestEntry[] containing exactly two entries:
{ id: "club-club1", family: "room", uri: "bundle://audio-ir-room-plugin-club/club1.wav", channels: <decoded>, sampleRate: <decoded>, gainDb: 0, license: <see README>, source: "Mountain Duck media library: /media/_/audio/ir/room/club1.wav" }.{ id: "club-club2", family: "room", uri: "bundle://audio-ir-room-plugin-club/club2.wav", channels: <decoded>, sampleRate: <decoded>, gainDb: 0, license: <see README>, source: "Mountain Duck media library: /media/_/audio/ir/room/club2.wav" }.
channels AND sampleRate SHALL be the actual values measured by decoding the bundled files at manifest-authoring time (so the manifest never disagrees with the file). Manifest SHALL parse successfully through IrManifestSchema (id-prefix, family coherence, uri non-empty).
Scenario: manifest parses through IrManifestSchema
- WHEN
IrManifestSchema.parse(manifest)is called - THEN the result deep-equals the export AND every
entry.idstarts with"club-"AND everyentry.family === "room"
Scenario: both ids are unique and well-formed
- WHEN the test enumerates
manifest.entries.map(e => e.id) - THEN the result is
["club-club1", "club-club2"](order matches declaration; no duplicates)
Scenario: every URI parses as a bundle:// URI
- WHEN the test parses every
entry.urithroughparseUri(from@suaveplan/storage-uri) AND the bundle plugin'sparse - THEN each parse succeeds AND the parsed
meta.packageSlug === "audio-ir-room-plugin-club"
Verified by: src/manifest/manifest.test.ts::"manifest parses through IrManifestSchema" AND src/manifest/manifest.test.ts::"declares exactly club-club1 and club-club2" AND src/manifest/manifest.test.ts::"every URI parses as a well-formed bundle URI"
Requirement: register(registry, resolver) wires the bundle plugin and registers entries
The package SHALL export a register(registry: IrRegistry, resolver: StorageUriResolver): void function that:
- Calls
resolver.use(bundleUriPlugin({ packageSlug: "audio-ir-room-plugin-club", rootUrl: new URL("../files/", import.meta.url) }))exactly once, idempotently rejected by the resolver if a plugin forbundleis already registered with the samepackageSlugvalue. - Calls
registerManifest(registry, manifest).
The function SHALL NOT swallow errors — duplicate registrations against the same resolver/registry SHALL surface the underlying STORAGE_URI.SCHEME_CONFLICT or AUDIO_IR.DUPLICATE_ID codes verbatim. The package SHALL NOT auto-register on import — registration is explicit per the side-effect-free contract.
Scenario: register adds the bundle plugin and the two entries
- WHEN
register(registry, resolver)is called against fresh fixtures - THEN the resolver dispatches
bundle://audio-ir-room-plugin-club/club1.wavto the package's adapter ANDregistry.has("club-club1") === trueANDregistry.has("club-club2") === true
Scenario: double register surfaces the underlying conflict
- WHEN
register(registry, resolver)is called twice against the same fixtures - THEN the second call rejects synchronously with
AUDIO_IR.DUPLICATE_ID(the registry rejects before the resolver is touched a second time, depending on implementation order; either underlying conflict is acceptable as long as it surfaces verbatim)
Verified by: src/register/register.test.ts::"adds bundle plugin and registers both entries" AND src/register/register.test.ts::"double register surfaces DUPLICATE_ID"
Requirement: Bundled WAV files exist on disk at build time
The package SHALL ship files/club1.wav AND files/club2.wav in its tarball (via the package.json files array). Both files SHALL be non-empty AND SHALL decode successfully through decodeWav from @suaveplan/audio-decode-wav. The integration test SHALL load both files end-to-end through audio-ir-loader and assert non-empty Float32Array channels.
Scenario: both files exist and are non-empty
- WHEN the test reads
<package-root>/files/club1.wavAND<package-root>/files/club2.wavvianode:fs - THEN both reads succeed AND each file's byte length is
> 0
Scenario: end-to-end load returns valid IRs
- WHEN the test constructs
resolver,registry, callsregister(registry, resolver), constructsloader = createIrLoader({ registry, resolver, decoders: { wav: decodeWav } }), AND callsawait loader.load("club-club1")ANDawait loader.load("club-club2") - THEN both calls resolve to frozen
ImpulseResponseobjects whosechannelData[0].length > 0ANDsampleRate > 0ANDchannels >= 1
Verified by: tests/integration/file-presence.test.ts::"club1.wav and club2.wav exist on disk and are non-empty" AND tests/integration/end-to-end.test.ts::"loads both club IRs end-to-end through the loader"