Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,45 @@
# Add `@suaveplan/audio-ir-room-plugin-club`
**Package**: `@suaveplan/audio-ir-room-plugin-club`
**Status**: PROPOSED
**Type**: new-package
## Why
Genesis-original IR content package. Ships the `club1.wav` and `club2.wav` room impulse responses (from `/mnt/c/Users/Administrator/Mountain Duck/74a17879-9780-426d-8bd9-7ed805f53d41/media/_/audio/ir/room/`) as bundle-local assets resolved through the shipped `@suaveplan/storage-uri-plugin-bundle`. Serves as the canonical example of the **bundle-only** content-package pattern — every future `audio-ir-{family}-plugin-<name>` package that ships only local files follows this shape.
## What changes
- ADDED: `manifest: IrManifest` — `{ packageSlug: "club", family: "room", entries: [<club-club1>, <club-club2>] }`.
- ADDED: `register(registry: IrRegistry, resolver: StorageUriResolver): void` — registers the package-scoped `bundleUriPlugin` on the resolver AND calls `registerManifest(registry, manifest)`.
- ADDED: `files/club1.wav` AND `files/club2.wav` — bundled binary assets, package-included in tarball.
- ADDED: `entries` ids `club-club1` and `club-club2`, each with `uri: "bundle://audio-ir-room-plugin-club/club1.wav"` (and `club2.wav`).
## Out of scope
- Decoding — `audio-ir-loader` owns it. This package only registers manifest entries.
- Any non-room IRs — separate packages per family per source.
- Multi-source fallback — these IRs are bundle-only by design; the `pool` package demonstrates the fallback pattern.
## Source mapping
Files copied from `/mnt/c/Users/Administrator/Mountain Duck/74a17879-9780-426d-8bd9-7ed805f53d41/media/_/audio/ir/room/club{1,2}.wav` (Mountain Duck media library). Cross-cutting model: `openspec/changes/add-audio-ir-core/design.md`.
### Requirements with no source line
| Requirement | Justification |
|---|---|
| every spec requirement | Genesis-original content package |
## Verification approach
- `src/manifest/manifest.test.ts` — manifest parses successfully through `IrManifestSchema`; every entry's `id` matches the slug-prefix rule; both `uri` values parse as well-formed `bundle://` URIs.
- `src/register/register.test.ts` — `register(registry, resolver)` (a) adds a `bundleUriPlugin` for `audio-ir-room-plugin-club` to the resolver and (b) registers both manifest entries; double-call throws `AUDIO_IR.DUPLICATE_ID` (the registry's invariant).
- `tests/integration/end-to-end.test.ts` — wire `bundleUriPlugin` into a real `StorageUriResolver`, register via `register(...)`, pass to `createIrLoader({ ..., decoders: { wav: decodeWav } })`, call `loader.load("club-club1")`, assert non-empty Float32Array channels with the manifest-declared shape. Run identically for `club-club2`.
- `tests/integration/file-presence.test.ts` — assert `files/club1.wav` and `files/club2.wav` exist on disk at build time and are non-empty.
## Impact
### Affected dependents
No dependents (brand new). Verified by `bunx turbo ls --filter=...@suaveplan/audio-ir-room-plugin-club` returning only the package itself.

View File

@@ -0,0 +1,79 @@
<!-- markdownlint-disable MD024 -->
# 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:
1. `{ 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" }`.
2. `{ 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.id` starts with `"club-"` AND every `entry.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.uri` through `parseUri` (from `@suaveplan/storage-uri`) AND the bundle plugin's `parse`
- **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:
1. 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 for `bundle` is already registered with the same `packageSlug` value.
2. 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.wav` to the package's adapter AND `registry.has("club-club1") === true` AND `registry.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.wav` AND `<package-root>/files/club2.wav` via `node: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`, calls `register(registry, resolver)`, constructs `loader = createIrLoader({ registry, resolver, decoders: { wav: decodeWav } })`, AND calls `await loader.load("club-club1")` AND `await loader.load("club-club2")`
- **THEN** both calls resolve to frozen `ImpulseResponse` objects whose `channelData[0].length > 0` AND `sampleRate > 0` AND `channels >= 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"`
<!-- markdownlint-enable MD024 -->

View File

@@ -0,0 +1,70 @@
# Tasks — Add `@suaveplan/audio-ir-room-plugin-club`
> Prereqs: `add-audio-ir-core`, `add-audio-ir-loader`, `add-audio-decode-wav` must reach the SHIPPED state before this change's §4 integration tests can pass.
## 1. Spec authoring
- [ ] 1.1 Read `openspec/AGENTS.md` §§ 13, 14, 14a, 2c, 6a, 193
- [ ] 1.2 Read `openspec/changes/add-audio-ir-core/design.md`
- [ ] 1.3 `proposal.md` (incl. `## Impact` per §14a)
- [ ] 1.4 `specs/audio-ir-room-plugin-club/spec.md` — every requirement carries `**Verified by:**`
- [ ] 1.5 Working-notes checklist of every `### Requirement` and `#### Scenario`
## 2. Package scaffold
- [ ] 2.1 `mkdir -p packages/audio-ir/audio-ir-room-plugin-club/{src/{manifest,register},files,tests/integration}`
- [ ] 2.2 `package.json` — `version: 0.0.0`, `sideEffects: false`, `files: ["dist", "files", "README.md"]`, deps `@suaveplan/{audio-ir-core,storage-uri,storage-uri-plugin-bundle,error,types}`, dev `@suaveplan/{audio-ir-loader,audio-decode-wav}`, `zod`, `tslib`
- [ ] 2.3 `tsconfig.lib.json`, `vite.config.ts` (vite-plugin-static-copy to mirror `files/` into `dist/files/` if needed; OR use the `files` field of `package.json` to ship `files/` directly without copy)
- [ ] 2.4 Subpath exports: `.`, `./manifest`, `./register`
- [ ] 2.5 `README.md` — including a `## License & sources` section recording the Mountain Duck origin path AND the upstream attribution for the IRs
## 3. Asset import
- [ ] 3.1 Copy `/mnt/c/Users/Administrator/Mountain Duck/74a17879-9780-426d-8bd9-7ed805f53d41/media/_/audio/ir/room/club1.wav` → `packages/audio-ir/audio-ir-room-plugin-club/files/club1.wav`
- [ ] 3.2 Copy `/mnt/c/Users/Administrator/Mountain Duck/74a17879-9780-426d-8bd9-7ed805f53d41/media/_/audio/ir/room/club2.wav` → `packages/audio-ir/audio-ir-room-plugin-club/files/club2.wav`
- [ ] 3.3 Verify both files exist AND are non-empty AND decode through a standalone `decodeWav` smoke test
- [ ] 3.4 Record decoded `channels` and `sampleRate` for each file — these are the manifest constants
## 4. Implementation
- [ ] 4.1 `src/manifest/manifest.ts` + `.md` — `manifest: IrManifest` with both entries
- [ ] 4.2 `src/register/register.ts` + `.md` — `register(registry, resolver)` wiring `bundleUriPlugin` + `registerManifest`
- [ ] 4.3 `src/index.ts` — explicit named re-exports (`manifest`, `register`)
## 5. Tests
- [ ] 5.1 `src/manifest/manifest.test.ts` — every scenario from manifest requirement
- [ ] 5.2 `src/register/register.test.ts` — every scenario from register requirement
- [ ] 5.3 `tests/integration/file-presence.test.ts` — files exist on disk
- [ ] 5.4 `tests/integration/end-to-end.test.ts` — full pipeline through real `audio-ir-loader` + `decodeWav`
- [ ] 5.5 Every `it(...)` carries `{ timeout: 5000 }` (or 30000 for async)
## 6. Documentation
- [ ] 6.1 Sibling `.md` for every `.ts` (≥200 words, basic + advanced examples)
- [ ] 6.2 README `## Modules` + `## License & sources`
- [ ] 6.3 Update root `CLAUDE.md` Audio IR subsection
## 7. Pre-archive verification (5-gate triple)
- [ ] 7.1 `bun run check` — 0/0/0/0
- [ ] 7.2 `bunx turbo lint typecheck test --filter=@suaveplan/audio-ir-room-plugin-club` — green
- [ ] 7.3 `bunx turbo test:coverage --filter=@suaveplan/audio-ir-room-plugin-club` — 100/100/100/100
- [ ] 7.4 `bun scripts/openspec-conformance.ts @suaveplan/audio-ir-room-plugin-club` — exit 0
- [ ] 7.5 `bunx turbo build --filter=@suaveplan/audio-ir-room-plugin-club` — green
## 8. Pre-archive 7-row checklist (AGENTS §13)
- [ ] 8.1 Type assertions on the manifest constant (literal types preserved through export)
- [ ] 8.2 Zod schemas wired (manifest validates through `IrManifestSchema`)
- [ ] 8.3 No error codes owned by this package (it reuses `AUDIO_IR.*` from core) — N/A
- [ ] 8.4 Strict-mode behaviors tested
- [ ] 8.5 OTel is inherited from core + loader — N/A here
- [ ] 8.6 Lifecycle dedup tested (double-register surfaces DUPLICATE_ID)
- [ ] 8.7 No retroactive tick
## 9. Archive
- [ ] 9.1 Fold delta into new `openspec/specs/audio-ir-room-plugin-club/spec.md`
- [ ] 9.2 Move folder to `openspec/archive/<yyyy-mm-dd>-add-audio-ir-room-plugin-club/`
- [ ] 9.3 Refresh `MANIFEST.md`