Initial Commit
This commit is contained in:
160
openspec/changes/audio-shipped-file-size-cleanup/design.md
Normal file
160
openspec/changes/audio-shipped-file-size-cleanup/design.md
Normal file
@@ -0,0 +1,160 @@
|
||||
# Design — audio-shipped-file-size-cleanup
|
||||
|
||||
This change is a pure mechanical refactor across three packages. The design is the **split strategy** per file: every public symbol stays exported from its current module path; the splits happen *below* the public surface.
|
||||
|
||||
---
|
||||
|
||||
## 1. `@suaveplan/audio-timeline` — `engine/engine.ts` (548 → ≤300)
|
||||
|
||||
`TimelineEngine` is the single class in this file and currently owns:
|
||||
|
||||
- Construction + adapter wiring (`setAdapter`, `dispose`)
|
||||
- Track CRUD (`addTrack`, `removeTrack`, `updateTrack`, `setTracks`)
|
||||
- Clip CRUD (`addClip`, `removeClip`, `updateClip`, `splitClip`, `joinClip`)
|
||||
- Mixer mutations (`setMasterVolume`, `setTrackVolume`, `setTrackMute`, `setTrackSolo`, `setTrackPan`)
|
||||
- Transport (`play`, `pause`, `stop`, `seek`, `setLoop`, `setTempo`)
|
||||
- Snapshot / restore + undo/redo dispatch
|
||||
- Event emission
|
||||
|
||||
### Proposed split
|
||||
|
||||
Keep `TimelineEngine` in `engine.ts` as the public class, but extract helper modules called from its methods:
|
||||
|
||||
```text
|
||||
src/engine/
|
||||
engine.ts ← class shell + delegating methods (≤300)
|
||||
engine-tracks.ts ← addTrack/removeTrack/updateTrack/setTracks helpers
|
||||
engine-tracks.md
|
||||
engine-clips.ts ← addClip/removeClip/updateClip/splitClip/joinClip helpers
|
||||
engine-clips.md
|
||||
engine-mixer.ts ← setMasterVolume/setTrackVolume/Mute/Solo/Pan helpers
|
||||
engine-mixer.md
|
||||
engine-transport.ts ← play/pause/stop/seek/setLoop/setTempo helpers
|
||||
engine-transport.md
|
||||
engine.test.ts ← class-level invariants only (≤500)
|
||||
engine-tracks.test.ts ← extracted track tests
|
||||
engine-clips.test.ts ← extracted clip tests
|
||||
engine-mixer.test.ts ← extracted mixer tests
|
||||
engine-transport.test.ts ← extracted transport tests
|
||||
```
|
||||
|
||||
Each helper module exports pure functions of `(state, args) => newState` (where `state` is `TimelineEngine`'s internal record) plus any side-effect-free derived values. `TimelineEngine` methods become 1–3 liners that call the helper and forward the result. This pattern matches how `audio-core`'s `process/` directory is structured.
|
||||
|
||||
The original `engine.test.ts` splits along the same four axes; the residual `engine.test.ts` keeps construction, adapter wiring, snapshot/restore, undo/redo dispatch, and event emission tests — the cross-cutting concerns no single helper owns.
|
||||
|
||||
---
|
||||
|
||||
## 2. `@suaveplan/audio-transport` — `transport/transport.ts` (419 → ≤300)
|
||||
|
||||
`Transport` is the single class and owns:
|
||||
|
||||
- Construction + clock injection
|
||||
- BPM + time-signature + PPQ state
|
||||
- Tick advance + lookahead scheduling loop
|
||||
- Schedule / cancel API
|
||||
- Loop region + swing
|
||||
- Tempo automation
|
||||
|
||||
### Proposed split
|
||||
|
||||
```text
|
||||
src/transport/
|
||||
transport.ts ← class shell (≤300)
|
||||
transport-schedule.ts ← schedule/cancel/lookahead helpers
|
||||
transport-schedule.md
|
||||
transport-tempo.ts ← BPM + tempo automation + tick conversion helpers
|
||||
transport-tempo.md
|
||||
transport.test.ts ← stays as-is (already under cap)
|
||||
```
|
||||
|
||||
No test splitting needed — `transport.test.ts` is already within the 500-line cap.
|
||||
|
||||
---
|
||||
|
||||
## 3. `@suaveplan/audio-metadata` — `parsers/id3v2/id3v2.ts` (536 → ≤300)
|
||||
|
||||
`id3v2.ts` is the most over-budget src file. It owns:
|
||||
|
||||
- Magic-byte detection
|
||||
- Header + extended-header parsing
|
||||
- Frame iteration
|
||||
- Per-frame-type decoders (TXXX, T..., COMM, USLT, APIC, UFID, WCOM, WCOP, …)
|
||||
- Text-encoding decoders (ISO-8859-1, UTF-16, UTF-16BE, UTF-8)
|
||||
- Unsynchronisation reversal
|
||||
|
||||
### Proposed split
|
||||
|
||||
```text
|
||||
src/parsers/id3v2/
|
||||
id3v2.ts ← public parseId3v2() + frame dispatch (≤300)
|
||||
id3v2-header.ts ← header + extended-header parsing
|
||||
id3v2-header.md
|
||||
id3v2-frames-text.ts ← T... / TXXX text frame decoders
|
||||
id3v2-frames-text.md
|
||||
id3v2-frames-binary.ts ← APIC / UFID / GEOB binary frame decoders
|
||||
id3v2-frames-binary.md
|
||||
id3v2-frames-url.ts ← W... URL frame decoders
|
||||
id3v2-frames-url.md
|
||||
id3v2-text-encoding.ts ← ISO-8859-1 / UTF-16 / UTF-16BE / UTF-8 decoders
|
||||
id3v2-text-encoding.md
|
||||
id3v2-unsync.ts ← unsynchronisation reversal
|
||||
id3v2-unsync.md
|
||||
id3v2.test.ts ← integration parses on real fixture files (≤500)
|
||||
id3v2-header.test.ts ← header-specific
|
||||
id3v2-frames-text.test.ts ← text-frame-specific
|
||||
id3v2-frames-binary.test.ts ← binary-frame-specific
|
||||
id3v2-frames-url.test.ts ← url-frame-specific
|
||||
id3v2-text-encoding.test.ts ← encoding-specific
|
||||
id3v2-unsync.test.ts ← unsync-specific
|
||||
```
|
||||
|
||||
The split mirrors the spec sections of the ID3v2.4 standard, so the file boundaries match how future maintainers will reason about the format.
|
||||
|
||||
### `id3v2.test.ts` (1340 → 7 files of ≤500 each)
|
||||
|
||||
The current `id3v2.test.ts` mixes header tests, every frame type, encoding round-trips, and integration parses. Splitting by frame family (per the source split above) gives ~150–200 lines per file. The residual `id3v2.test.ts` keeps the end-to-end "parse a real fixture file" cases.
|
||||
|
||||
---
|
||||
|
||||
## 4. `@suaveplan/audio-metadata` — `parsers/mp4-atom/mp4-atom.ts` (351 → ≤300)
|
||||
|
||||
Lightest split. The file owns atom-walking + per-atom decoders (`ftyp`, `moov`, `mvhd`, `mdat`, `ilst` and the iTunes metadata sub-atoms). Extract the iTunes-specific decoders:
|
||||
|
||||
```text
|
||||
src/parsers/mp4-atom/
|
||||
mp4-atom.ts ← public parseMp4Atom() + container walker (≤300)
|
||||
mp4-atom-itunes.ts ← iTunes/ilst metadata decoders
|
||||
mp4-atom-itunes.md
|
||||
mp4-atom.test.ts ← container + non-iTunes tests (≤500)
|
||||
mp4-atom-itunes.test.ts ← iTunes-specific
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. `@suaveplan/audio-metadata` — `parsers/ogg-vorbis/ogg-vorbis.test.ts` (803 → ≤500)
|
||||
|
||||
Source file is already within cap. Only the test file needs splitting:
|
||||
|
||||
```text
|
||||
src/parsers/ogg-vorbis/
|
||||
ogg-vorbis.ts ← unchanged
|
||||
ogg-vorbis.test.ts ← page parsing + comment header (≤500)
|
||||
ogg-vorbis-frames.test.ts ← extracted frame-level tests
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Cross-cutting rules for the split
|
||||
|
||||
- **Public surface is invariant.** Every existing `export` from `<pkg>/src/index.ts` must continue to resolve to the same symbol with the same signature. The split is *internal*.
|
||||
- **Subpath exports unchanged.** The `package.json` `exports` map for each package keeps every existing entry. New internal helper modules are NOT added to `exports` — they are implementation details.
|
||||
- **Barrels use explicit named re-exports** (§6a). No `export * from` in new feature barrels.
|
||||
- **Sibling `.md` files** added for every new `.ts` per §4 (200-word minimum, six required sections).
|
||||
- **Coverage stays 100/100/100/100** per `vitest.config.ts` thresholds.
|
||||
- **No new behavior.** A diff of `dist/*.d.ts` must show only path changes — no type-shape changes.
|
||||
|
||||
## 7. Risk
|
||||
|
||||
Low. The packages have no published consumers; the refactor is mechanical; the tests are the contract and they all must continue to pass. The largest file (`id3v2.test.ts` at 1340 lines) is the highest-leverage win: shrinking it makes the format-spec correspondence explicit and gives future maintainers per-frame-family focus.
|
||||
|
||||
The one non-mechanical risk is in `audio-timeline/engine.ts` — extracting helper modules implies extracting a `TimelineEngineState` shape, which is currently inlined as private class fields. The helpers operate on that shape; if the extraction inadvertently widens the visibility of private state, the public surface widens. Mitigation: keep `TimelineEngineState` as a non-exported type in `engine-state.ts` (or inline in `engine.ts`) and pass it explicitly to helpers; do not export it.
|
||||
77
openspec/changes/audio-shipped-file-size-cleanup/proposal.md
Normal file
77
openspec/changes/audio-shipped-file-size-cleanup/proposal.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# OpenSpec Change: audio-shipped-file-size-cleanup
|
||||
|
||||
- **Change ID**: audio-shipped-file-size-cleanup
|
||||
- **Packages**: `@suaveplan/audio-timeline`, `@suaveplan/audio-transport`, `@suaveplan/audio-metadata`
|
||||
- **Category**: maintenance (refactor — no behavior change)
|
||||
- **Status**: PROPOSED
|
||||
- **Date**: 2026-05-15
|
||||
- **Design**: `./design.md`
|
||||
- **Spec deltas**: `./specs/audio-timeline/spec.md`, `./specs/audio-transport/spec.md`, `./specs/audio-metadata/spec.md`
|
||||
|
||||
---
|
||||
|
||||
## Why
|
||||
|
||||
A post-ship audit of the three universal audio packages flagged source and test files exceeding `CLAUDE.md` Rule §3 / `openspec/AGENTS.md` §3 (300-line hard cap on source, 500-line cap on tests):
|
||||
|
||||
| Package | File | Lines | Cap | Over |
|
||||
|---|---|---|---|---|
|
||||
| audio-timeline | `src/engine/engine.ts` | 548 | 300 | +248 |
|
||||
| audio-timeline | `src/engine/engine.test.ts` | 819 | 500 | +319 |
|
||||
| audio-transport | `src/transport/transport.ts` | 419 | 300 | +119 |
|
||||
| audio-metadata | `src/parsers/id3v2/id3v2.ts` | 536 | 300 | +236 |
|
||||
| audio-metadata | `src/parsers/id3v2/id3v2.test.ts` | 1340 | 500 | +840 |
|
||||
| audio-metadata | `src/parsers/mp4-atom/mp4-atom.ts` | 351 | 300 | +51 |
|
||||
| audio-metadata | `src/parsers/mp4-atom/mp4-atom.test.ts` | 670 | 500 | +170 |
|
||||
| audio-metadata | `src/parsers/ogg-vorbis/ogg-vorbis.test.ts` | 803 | 500 | +303 |
|
||||
|
||||
The 1340-line `id3v2.test.ts` (more than 2.6× the cap) is the most egregious. All eight files passed their own gates because the size budget is currently enforced by manual audit rather than a CI check.
|
||||
|
||||
In parallel, the audit found a §7 / Rule 11 violation that was already migrated as a separate change (audio-timeline test files importing directly from `vitest` — the `bench` import was wrong-framework too and is now migrated to mitata). That work is out of scope for this proposal.
|
||||
|
||||
## What changes
|
||||
|
||||
- REFACTOR: Split each oversized source file at natural responsibility boundaries while preserving the **exact public API surface** (every existing `export` keeps its name, signature, and semantics).
|
||||
- REFACTOR: Split each oversized test file into co-located sibling `*.test.ts` modules, one per cohesive behavior cluster (e.g. `id3v2/frames.test.ts`, `id3v2/text-encoding.test.ts`, `id3v2/extended-header.test.ts`).
|
||||
- ADDED: Per-package barrel updates if new sub-modules are created — explicit named re-exports only (§6a).
|
||||
- ADDED: Sibling `.md` docs for any new implementation files; the original file's `.md` stays as the surface-level overview.
|
||||
- NO BEHAVIOR CHANGE. Test counts must not decrease. Coverage must remain 100/100/100/100.
|
||||
|
||||
## Out of scope
|
||||
|
||||
- API redesigns or new features.
|
||||
- The runner-import migration (already done in audio-timeline).
|
||||
- File-size cleanups in other packages (handled separately if needed).
|
||||
- Introducing a new CI gate for the 300/500 caps — recommended as a follow-up.
|
||||
|
||||
## Source mapping
|
||||
|
||||
This is a pure-genesis maintenance change; no upstream reference. The size caps come from `CLAUDE.md` (§Quality) and `openspec/AGENTS.md` §3.
|
||||
|
||||
## Impact
|
||||
|
||||
**Breaking changes**: none. Public API surface, package exports, and `package.json` `exports` entries are preserved verbatim. Internal sub-module paths may shift, but they were never part of the public contract.
|
||||
|
||||
### Affected dependents
|
||||
|
||||
The three packages are universal-tier and currently have **no published consumers** on genesis (`bunx turbo ls --filter=...@suaveplan/audio-timeline` / `audio-transport` / `audio-metadata` returns empty). Future consumers (`audio-engine-browser`, `audio-engine-server`, react-audio*) consume the public surface, which this change does not touch.
|
||||
|
||||
- No dependents (verified by `bunx turbo ls --filter=...@suaveplan/audio-timeline`).
|
||||
- No dependents (verified by `bunx turbo ls --filter=...@suaveplan/audio-transport`).
|
||||
- No dependents (verified by `bunx turbo ls --filter=...@suaveplan/audio-metadata`).
|
||||
|
||||
## Verification approach
|
||||
|
||||
For each touched package:
|
||||
|
||||
1. `bunx turbo lint typecheck test --filter=@suaveplan/<pkg>` — green.
|
||||
2. `bunx turbo test:coverage --filter=@suaveplan/<pkg>` — 100/100/100/100.
|
||||
3. `bunx turbo build --filter=@suaveplan/<pkg>` — green.
|
||||
4. `find packages/audio/<pkg>/src -name "*.ts" ! -name "*.test.ts" | xargs awk 'END { if (NR > 300) print FILENAME, NR " lines" }'` — empty.
|
||||
5. `find packages/audio/<pkg>/src packages/audio/<pkg>/tests -name "*.test.ts" | xargs awk 'END { if (NR > 500) print FILENAME, NR " lines" }'` — empty.
|
||||
6. Diff the public `exports` map in `package.json` before/after — identical.
|
||||
7. Diff `dist/` `.d.ts` files before/after — only path / source-map changes; no type-shape changes.
|
||||
|
||||
## Follow-up
|
||||
|
||||
Once this change archives, add a `scripts/lint/check-file-sizes.ts` CI gate so the caps are enforced automatically. That belongs in its own change — out of scope here.
|
||||
@@ -0,0 +1,70 @@
|
||||
<!-- markdownlint-disable MD024 -->
|
||||
|
||||
# Capability Spec Delta: audio-metadata (audio-shipped-file-size-cleanup)
|
||||
|
||||
**Package**: `@suaveplan/audio-metadata`
|
||||
**Status**: PROPOSED
|
||||
|
||||
> Every requirement below carries a `**Verified by:**` line at authoring time. No exceptions.
|
||||
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: id3v2 parser source modules respect the 300-line cap
|
||||
|
||||
The ID3v2 parser implementation MUST be split such that no single `.ts` file under `src/parsers/id3v2/` exceeds the 300-line hard cap. The public function export `parseId3v2` SHALL remain available from `@suaveplan/audio-metadata` with its existing signature and observable behavior across every supported frame type (T... text frames, TXXX user-defined text, W... URL frames, APIC attached picture, UFID file identifier, COMM comments, USLT lyrics, GEOB general encapsulated object) and every supported text encoding (ISO-8859-1, UTF-16, UTF-16BE, UTF-8) with unsynchronisation reversal applied where the header flag is set.
|
||||
|
||||
#### Scenario: every id3v2 source file is at or under the 300-line cap
|
||||
|
||||
- **WHEN** `find packages/audio/audio-metadata/src/parsers/id3v2 -name "*.ts" ! -name "*.test.ts" | xargs awk 'END { if (NR > 300) print FILENAME, NR }'` is run
|
||||
- **THEN** the output is empty
|
||||
|
||||
#### Scenario: id3v2 fixture parses are unchanged
|
||||
|
||||
- **WHEN** `parseId3v2` is invoked on the existing fixture set used by `id3v2.test.ts`
|
||||
- **THEN** every parse result is deep-equal to the pre-split result
|
||||
|
||||
**Verified by:** `src/parsers/id3v2/id3v2.test.ts::"every supported v2.4 fixture parses to the documented shape"` AND `tests/file-size.test.ts::"every id3v2 src file is under 300 lines"`
|
||||
|
||||
---
|
||||
|
||||
### Requirement: id3v2 test modules respect the 500-line cap
|
||||
|
||||
The ID3v2 test suite MUST be split such that no single `*.test.ts` file under `src/parsers/id3v2/` exceeds the 500-line hard cap. The aggregate test count across the split files SHALL be greater than or equal to the pre-split count. The residual `id3v2.test.ts` SHALL hold end-to-end fixture-parse cases; per-frame-family and per-encoding cases SHALL move to sibling files named after the family they cover.
|
||||
|
||||
#### Scenario: every id3v2 test file is at or under the 500-line cap
|
||||
|
||||
- **WHEN** `find packages/audio/audio-metadata/src/parsers/id3v2 -name "*.test.ts" | xargs awk 'END { if (NR > 500) print FILENAME, NR }'` is run
|
||||
- **THEN** the output is empty
|
||||
|
||||
**Verified by:** `tests/file-size.test.ts::"every id3v2 test file is under 500 lines"` AND `tests/exports.test.ts::"aggregate id3v2 test count is greater than or equal to pre-split"`
|
||||
|
||||
---
|
||||
|
||||
### Requirement: mp4-atom parser modules respect the size caps
|
||||
|
||||
The MP4 atom parser implementation MUST be split such that no `.ts` source file under `src/parsers/mp4-atom/` exceeds the 300-line cap and no `.test.ts` file exceeds the 500-line cap. The public function export `parseMp4Atom` SHALL remain available from `@suaveplan/audio-metadata` with its existing signature and observable behavior across container atoms (`ftyp`, `moov`, `mvhd`, `mdat`, `ilst`) and iTunes metadata sub-atoms.
|
||||
|
||||
#### Scenario: every mp4-atom source file is at or under the 300-line cap
|
||||
|
||||
- **WHEN** `find packages/audio/audio-metadata/src/parsers/mp4-atom -name "*.ts" ! -name "*.test.ts" | xargs awk 'END { if (NR > 300) print FILENAME, NR }'` is run
|
||||
- **THEN** the output is empty
|
||||
|
||||
#### Scenario: every mp4-atom test file is at or under the 500-line cap
|
||||
|
||||
- **WHEN** `find packages/audio/audio-metadata/src/parsers/mp4-atom -name "*.test.ts" | xargs awk 'END { if (NR > 500) print FILENAME, NR }'` is run
|
||||
- **THEN** the output is empty
|
||||
|
||||
**Verified by:** `tests/file-size.test.ts::"every mp4-atom src file is under 300 lines"` AND `tests/file-size.test.ts::"every mp4-atom test file is under 500 lines"`
|
||||
|
||||
---
|
||||
|
||||
### Requirement: ogg-vorbis test modules respect the 500-line cap
|
||||
|
||||
The Ogg-Vorbis test suite MUST be split such that no `*.test.ts` file under `src/parsers/ogg-vorbis/` exceeds the 500-line hard cap. The aggregate test count across the split files SHALL be greater than or equal to the pre-split count. The source file is already within cap and is unchanged.
|
||||
|
||||
#### Scenario: every ogg-vorbis test file is at or under the 500-line cap
|
||||
|
||||
- **WHEN** `find packages/audio/audio-metadata/src/parsers/ogg-vorbis -name "*.test.ts" | xargs awk 'END { if (NR > 500) print FILENAME, NR }'` is run
|
||||
- **THEN** the output is empty
|
||||
|
||||
**Verified by:** `tests/file-size.test.ts::"every ogg-vorbis test file is under 500 lines"`
|
||||
@@ -0,0 +1,44 @@
|
||||
<!-- markdownlint-disable MD024 -->
|
||||
|
||||
# Capability Spec Delta: audio-timeline (audio-shipped-file-size-cleanup)
|
||||
|
||||
**Package**: `@suaveplan/audio-timeline`
|
||||
**Status**: PROPOSED
|
||||
|
||||
> Every requirement below carries a `**Verified by:**` line at authoring time. No exceptions.
|
||||
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: TimelineEngine source modules respect the 300-line cap
|
||||
|
||||
The `TimelineEngine` implementation MUST be split across source modules such that no single `.ts` file under `src/engine/` exceeds the 300-line hard cap defined in `CLAUDE.md` Rule §3 / `openspec/AGENTS.md` §3. The public class export from `@suaveplan/audio-timeline` SHALL remain `TimelineEngine` with its existing constructor signature, public methods, and observable behavior (track CRUD, clip CRUD, mixer mutations, transport controls, snapshot/restore, undo/redo, event emission). Internal helper modules MAY be added under `src/engine/` but SHALL NOT appear in the `package.json` `exports` map; they are implementation detail.
|
||||
|
||||
#### Scenario: every engine source file is at or under the 300-line cap
|
||||
|
||||
- **WHEN** `find packages/audio/audio-timeline/src/engine -name "*.ts" ! -name "*.test.ts" | xargs awk 'END { if (NR > 300) print FILENAME, NR }'` is run
|
||||
- **THEN** the output is empty
|
||||
|
||||
#### Scenario: public TimelineEngine surface is unchanged
|
||||
|
||||
- **WHEN** a consumer imports `TimelineEngine` from `@suaveplan/audio-timeline` and exercises every public method documented in `src/engine/engine.md`
|
||||
- **THEN** every method exists with its existing signature and returns the existing shape
|
||||
|
||||
**Verified by:** `tests/exports.test.ts::"TimelineEngine surface is byte-identical to pre-split"` AND `tests/file-size.test.ts::"every engine src file is under 300 lines"`
|
||||
|
||||
---
|
||||
|
||||
### Requirement: TimelineEngine test modules respect the 500-line cap
|
||||
|
||||
The `TimelineEngine` test suite MUST be split such that no single `*.test.ts` file under `src/engine/` exceeds the 500-line hard cap. The aggregate test count across all split files SHALL be greater than or equal to the pre-split count — no test SHALL be deleted or weakened as part of the split. Test names SHALL describe behavior, not implementation (per `openspec/AGENTS.md` §7 "Test structure").
|
||||
|
||||
#### Scenario: every engine test file is at or under the 500-line cap
|
||||
|
||||
- **WHEN** `find packages/audio/audio-timeline/src/engine packages/audio/audio-timeline/tests -name "*.test.ts" | xargs awk 'END { if (NR > 500) print FILENAME, NR }'` is run
|
||||
- **THEN** the output is empty
|
||||
|
||||
#### Scenario: aggregate test count is preserved
|
||||
|
||||
- **WHEN** `bunx turbo test --filter=@suaveplan/audio-timeline` is run after the split
|
||||
- **THEN** the reported total test count is greater than or equal to the pre-split count captured in task 1.1
|
||||
|
||||
**Verified by:** `tests/file-size.test.ts::"every engine test file is under 500 lines"` AND `tests/exports.test.ts::"aggregate engine test count is greater than or equal to pre-split"`
|
||||
@@ -0,0 +1,26 @@
|
||||
<!-- markdownlint-disable MD024 -->
|
||||
|
||||
# Capability Spec Delta: audio-transport (audio-shipped-file-size-cleanup)
|
||||
|
||||
**Package**: `@suaveplan/audio-transport`
|
||||
**Status**: PROPOSED
|
||||
|
||||
> Every requirement below carries a `**Verified by:**` line at authoring time. No exceptions.
|
||||
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Transport source modules respect the 300-line cap
|
||||
|
||||
The `Transport` implementation MUST be split such that no single `.ts` file under `src/transport/` exceeds the 300-line hard cap. The public class export from `@suaveplan/audio-transport` SHALL remain `Transport` with its existing constructor signature, public methods, and observable behavior (clock injection, BPM + time-signature + PPQ state, tick advance, lookahead scheduling, schedule/cancel, loop region, swing, tempo automation). Internal helper modules MAY be added under `src/transport/` but SHALL NOT appear in the `package.json` `exports` map.
|
||||
|
||||
#### Scenario: every transport source file is at or under the 300-line cap
|
||||
|
||||
- **WHEN** `find packages/audio/audio-transport/src/transport -name "*.ts" ! -name "*.test.ts" | xargs awk 'END { if (NR > 300) print FILENAME, NR }'` is run
|
||||
- **THEN** the output is empty
|
||||
|
||||
#### Scenario: public Transport surface is unchanged
|
||||
|
||||
- **WHEN** a consumer imports `Transport` from `@suaveplan/audio-transport` and exercises every public method documented in `src/transport/transport.md`
|
||||
- **THEN** every method exists with its existing signature and returns the existing shape
|
||||
|
||||
**Verified by:** `tests/exports.test.ts::"Transport surface is byte-identical to pre-split"` AND `tests/file-size.test.ts::"every transport src file is under 300 lines"`
|
||||
82
openspec/changes/audio-shipped-file-size-cleanup/tasks.md
Normal file
82
openspec/changes/audio-shipped-file-size-cleanup/tasks.md
Normal file
@@ -0,0 +1,82 @@
|
||||
# Tasks — audio-shipped-file-size-cleanup
|
||||
|
||||
> Every checkbox MUST reflect work actually done in this change. No behavior change is permitted.
|
||||
|
||||
## 0. Pre-flight (non-negotiable)
|
||||
|
||||
- [ ] 0.1 Confirm clean working tree (`git status` reports nothing) and no in-flight worktrees with uncommitted work (`git worktree list` then `git status` in each)
|
||||
- [ ] 0.2 If any worktrees exist under `.claude/worktrees/`, account for each one: still-active agent, work-pending-merge, or stale-and-prune. Stale worktrees MUST be removed (`git worktree remove`) before opening a new one — code that has been lost twice on main is the failure mode this guards against.
|
||||
- [ ] 0.3 Confirm the package set in `MANIFEST.md` matches `find packages -maxdepth 3 -name package.json` count. Drift here means a prior change archived without regenerating the manifest.
|
||||
|
||||
## 1. Pre-flight
|
||||
|
||||
- [ ] 1.1 Capture baseline: `bunx turbo lint typecheck test:coverage build --filter=@suaveplan/audio-timeline --filter=@suaveplan/audio-transport --filter=@suaveplan/audio-metadata` — record green output.
|
||||
- [ ] 1.2 Snapshot `dist/**/*.d.ts` for the three packages to a temp dir for post-split comparison.
|
||||
- [ ] 1.3 Capture each package's `package.json` `exports` map for post-split comparison.
|
||||
|
||||
## 2. `@suaveplan/audio-timeline` — `engine.ts` split
|
||||
|
||||
- [ ] 2.1 Extract `src/engine/engine-tracks.ts` + `.md` + `.test.ts` from `engine.ts` (track CRUD helpers).
|
||||
- [ ] 2.2 Extract `src/engine/engine-clips.ts` + `.md` + `.test.ts` (clip CRUD helpers).
|
||||
- [ ] 2.3 Extract `src/engine/engine-mixer.ts` + `.md` + `.test.ts` (mixer mutation helpers).
|
||||
- [ ] 2.4 Extract `src/engine/engine-transport.ts` + `.md` + `.test.ts` (transport helpers).
|
||||
- [ ] 2.5 Shrink `engine.ts` to delegating shell (≤300 lines); keep public class export at `src/engine/index.ts` unchanged.
|
||||
- [ ] 2.6 Shrink `engine.test.ts` to construction / adapter wiring / snapshot / undo-redo / event cases only (≤500 lines).
|
||||
- [ ] 2.7 Verify: `bunx turbo lint typecheck test:coverage build --filter=@suaveplan/audio-timeline` green; test count ≥ pre-split count.
|
||||
- [ ] 2.8 Diff `dist/**/*.d.ts` vs baseline — only path / source-map changes, no type-shape changes.
|
||||
|
||||
## 3. `@suaveplan/audio-transport` — `transport.ts` split
|
||||
|
||||
- [ ] 3.1 Extract `src/transport/transport-schedule.ts` + `.md` (schedule/cancel/lookahead helpers).
|
||||
- [ ] 3.2 Extract `src/transport/transport-tempo.ts` + `.md` (BPM + tempo automation helpers).
|
||||
- [ ] 3.3 Shrink `transport.ts` to delegating shell (≤300 lines).
|
||||
- [ ] 3.4 Verify: `bunx turbo lint typecheck test:coverage build --filter=@suaveplan/audio-transport` green; test count unchanged.
|
||||
- [ ] 3.5 Diff `dist/**/*.d.ts` vs baseline — no type-shape changes.
|
||||
|
||||
## 4. `@suaveplan/audio-metadata` — `id3v2.ts` split (largest)
|
||||
|
||||
- [ ] 4.1 Extract `src/parsers/id3v2/id3v2-header.ts` + `.md` + `.test.ts` (header / extended header parsing).
|
||||
- [ ] 4.2 Extract `src/parsers/id3v2/id3v2-text-encoding.ts` + `.md` + `.test.ts` (ISO-8859-1 / UTF-16 / UTF-16BE / UTF-8 decoders).
|
||||
- [ ] 4.3 Extract `src/parsers/id3v2/id3v2-unsync.ts` + `.md` + `.test.ts` (unsynchronisation reversal).
|
||||
- [ ] 4.4 Extract `src/parsers/id3v2/id3v2-frames-text.ts` + `.md` + `.test.ts` (T... / TXXX text frame decoders).
|
||||
- [ ] 4.5 Extract `src/parsers/id3v2/id3v2-frames-binary.ts` + `.md` + `.test.ts` (APIC / UFID / GEOB binary frame decoders).
|
||||
- [ ] 4.6 Extract `src/parsers/id3v2/id3v2-frames-url.ts` + `.md` + `.test.ts` (W... URL frame decoders).
|
||||
- [ ] 4.7 Shrink `id3v2.ts` to public `parseId3v2()` + frame-dispatch only (≤300 lines).
|
||||
- [ ] 4.8 Shrink `id3v2.test.ts` to end-to-end fixture parses only (≤500 lines).
|
||||
- [ ] 4.9 Verify: per-file split tests cover the same code paths; aggregate test count ≥ pre-split (1340-line file's tests redistributed, none deleted).
|
||||
|
||||
## 5. `@suaveplan/audio-metadata` — `mp4-atom.ts` + tests split
|
||||
|
||||
- [ ] 5.1 Extract `src/parsers/mp4-atom/mp4-atom-itunes.ts` + `.md` + `.test.ts` (iTunes/ilst decoders).
|
||||
- [ ] 5.2 Shrink `mp4-atom.ts` to public `parseMp4Atom()` + container walker (≤300 lines).
|
||||
- [ ] 5.3 Shrink `mp4-atom.test.ts` to container + non-iTunes tests (≤500 lines).
|
||||
|
||||
## 6. `@suaveplan/audio-metadata` — `ogg-vorbis.test.ts` split
|
||||
|
||||
- [ ] 6.1 Extract `src/parsers/ogg-vorbis/ogg-vorbis-frames.test.ts` (frame-level tests).
|
||||
- [ ] 6.2 Shrink `ogg-vorbis.test.ts` to page parsing + comment header tests (≤500 lines).
|
||||
|
||||
## 7. Per-package verification (post-split)
|
||||
|
||||
- [ ] 7.1 `bunx turbo lint typecheck test:coverage build --filter=@suaveplan/audio-timeline` — 100/100/100/100.
|
||||
- [ ] 7.2 `bunx turbo lint typecheck test:coverage build --filter=@suaveplan/audio-transport` — 100/100/100/100.
|
||||
- [ ] 7.3 `bunx turbo lint typecheck test:coverage build --filter=@suaveplan/audio-metadata` — 100/100/100/100.
|
||||
- [ ] 7.4 File-size sweep: `find packages/audio/audio-timeline/src packages/audio/audio-transport/src packages/audio/audio-metadata/src -name "*.ts" ! -name "*.test.ts" | xargs awk 'END { if (NR > 300) print FILENAME, NR " lines" }'` — empty.
|
||||
- [ ] 7.5 Test-size sweep: same `find ... -name "*.test.ts" ...` with cap 500 — empty.
|
||||
- [ ] 7.6 Diff `package.json` `exports` maps vs baseline — identical for all three packages.
|
||||
- [ ] 7.7 Diff `dist/**/*.d.ts` vs baseline — only path/source-map changes.
|
||||
|
||||
## 8. Per-package docs refresh
|
||||
|
||||
- [ ] 8.1 Update `MANIFEST.md` via `bun scripts/folder-reorg/generate-manifest.ts` (new sub-modules surfaced).
|
||||
- [ ] 8.2 No `STATUS.md` change — packages remain `✅` Completed.
|
||||
- [ ] 8.3 `bunx markdownlint-cli2 --fix` over touched files; re-stage; verify exit 0 without `--fix`.
|
||||
|
||||
## 9. Archive
|
||||
|
||||
- [ ] 9.1 Fold `specs/audio-timeline/spec.md`, `specs/audio-transport/spec.md`, and `specs/audio-metadata/spec.md` into the canonical `openspec/specs/<capability>/spec.md` files.
|
||||
- [ ] 9.2 Move `openspec/changes/audio-shipped-file-size-cleanup/` → `openspec/archive/YYYY-MM-DD-audio-shipped-file-size-cleanup/`.
|
||||
|
||||
## 10. Follow-up (separate change)
|
||||
|
||||
- [ ] 10.1 Open a `tooling-add-file-size-ci-gate` change that adds `scripts/lint/check-file-sizes.ts` (run from `bunx turbo lint`) so the 300/500 caps are enforced automatically. Out of scope here.
|
||||
Reference in New Issue
Block a user