From 5cbc660b928b30333963997dde84d6b40fd2aac0 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 14 Oct 2026 20:24:43 +0000 Subject: [PATCH 1/2] A9: each engine asserts the DspTap it was tested with The roadmap's A9 (row P12, Q4: DspTap tagged independently, the minimum asserted, no coupled cadence). DspTap gains tap/dsp/version.h (tap/DspTap#62): TAP_DSP_VERSION, packed (major << 16) | (minor << 8) | patch like this family's C ABI probes and pinned to DspTap's project() version by test. Its first tag, v0.1.0, marks the commit that carries it. - submodules/dsptap moves to that tree. This commit pins the PR branch's 9cc002c; once #62 merges, the pin moves to the identical tree on DspTap's main, tagged v0.1.0. - Each umbrella header (async.h, ratio.h, rational.h) defines TAP_SR_DSPTAP_MINIMUM, TAP_DSP_VERSION_ENCODE(0, 1, 0), token-identically, the way D13 defines the family version. It also static_asserts the pinned DspTap's TAP_DSP_VERSION against it. A DspTap 0.0.0 fails to compile with "tap::sr::bridge needs DspTap 0.1.0 or newer". - family.VersionMacrosAgree (tests/family/version_macros.cpp) checks the three definitions are equal, pins the floor at 0.1.0, and checks the pinned DspTap is not older. - The root README's dependency-rule section gains "Tested pairs": SampleRateTap 0.6.0, DspTap floor 0.1.0, pinned v0.1.0. The root CLAUDE.md states the rule beside D13, and the roadmap marks A9 landed and P12 green. No codegen change: the headers gain a macro and a static_assert. Locally with clang 18 -Werror, all 327 tests pass and clang-tidy is clean on the four files; GCC 13 -Werror compiles the version test. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_016GKzq1VSdcMYCRMhddiMaf --- CLAUDE.md | 5 ++++ README.md | 13 ++++++++++ async/include/tap/sr/async/async.h | 10 ++++++++ bridge/include/tap/sr/bridge/ratio.h | 9 +++++++ docs/ROADMAP.md | 4 +-- rational/include/tap/sr/rational/rational.h | 9 +++++++ submodules/dsptap | 2 +- tests/family/version_macros.cpp | 27 +++++++++++++++++---- 8 files changed, 71 insertions(+), 8 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index dd91e24..2ce26ee 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -48,6 +48,11 @@ made this tree (sections 5–6). Do not re-derive what it settles. three headers and the root `project()` together (0.5.0 came with `rational`'s M6, 0.6.0 with the audit response's async vocabulary pass: `preset` retired for `profile`, `economy()` to `program()`, the async C ABI in the siblings' shape). +- **The DspTap floor (A9).** Each umbrella header also defines `TAP_SR_DSPTAP_MINIMUM` (0.1.0) + token-identically and `static_assert`s it against DspTap's `TAP_DSP_VERSION` + (`tap/dsp/version.h`); `family.VersionMacrosAgree` checks the three agree. DspTap is tagged on its + own cadence (Q4): a commit that moves `submodules/dsptap` names the tag it pins, and raising the + floor updates the root README's tested pairs in the same PR. - **Clean renames, no aliases (D7).** Retired options fail the configure (`cmake/retired_options.cmake`); retired override macros hit an `#error`; retired words fail the style job (`scripts/retired_words.txt`, `scripts/check_retired_words.py`). Do not add aliases. diff --git a/README.md b/README.md index 3f3918c..39e8ea1 100644 --- a/README.md +++ b/README.md @@ -169,6 +169,19 @@ directory on the path, no public header names `srt/`, a sibling's code (design math, traits, kernels, quantization, instruments) lands in DspTap first; this tree bumps the submodule pin. +### Tested pairs + +DspTap is versioned on its own cadence (`tap/dsp/version.h`, tags `vX.Y.Z`), +never in lockstep with this tree. Each engine's umbrella header states the +oldest DspTap the family was tested with, `TAP_SR_DSPTAP_MINIMUM`, and a +`static_assert` against the substrate's `TAP_DSP_VERSION` rejects an older +one at compile time (`family.VersionMacrosAgree` holds the three definitions +equal). A commit that moves `submodules/dsptap` names the DspTap tag it pins. + +| SampleRateTap | DspTap floor | DspTap pinned | +|---|---|---| +| 0.6.0 | 0.1.0 | v0.1.0 | + ## Provenance and license MIT (`LICENSE`: Timothy Place and the SampleRateTap contributors). The tree diff --git a/async/include/tap/sr/async/async.h b/async/include/tap/sr/async/async.h index 26320ee..3021f30 100644 --- a/async/include/tap/sr/async/async.h +++ b/async/include/tap/sr/async/async.h @@ -31,8 +31,18 @@ // README's table states all three ladders). #pragma once +#include "tap/dsp/version.h" + #define TAP_SR_VERSION_MAJOR 0 #define TAP_SR_VERSION_MINOR 6 #define TAP_SR_VERSION_PATCH 0 +// The oldest DspTap this family was tested with (the roadmap's A9; the root +// README's tested pairs), defined token-identically in each engine's +// umbrella header like the family version (D13) and checked against the +// pinned substrate's tap/dsp/version.h. +#define TAP_SR_DSPTAP_MINIMUM TAP_DSP_VERSION_ENCODE(0, 1, 0) +static_assert(TAP_DSP_VERSION >= TAP_SR_DSPTAP_MINIMUM, + "tap::sr::async needs DspTap 0.1.0 or newer (submodules/dsptap)"); + #include "tap/sr/async/converter.h" diff --git a/bridge/include/tap/sr/bridge/ratio.h b/bridge/include/tap/sr/bridge/ratio.h index 1a7c297..9417187 100644 --- a/bridge/include/tap/sr/bridge/ratio.h +++ b/bridge/include/tap/sr/bridge/ratio.h @@ -35,6 +35,7 @@ // HANDOFF.md is the original design brief. #pragma once +#include "tap/dsp/version.h" #include "tap/sr/bridge/converter.h" // IWYU pragma: export #include "tap/sr/bridge/design.h" // IWYU pragma: export #include "tap/sr/bridge/phase_table.h" // IWYU pragma: export @@ -44,6 +45,14 @@ #define TAP_SR_VERSION_MINOR 6 #define TAP_SR_VERSION_PATCH 0 +// The oldest DspTap this family was tested with (the roadmap's A9; the root +// README's tested pairs), defined token-identically in each engine's +// umbrella header like the family version (D13) and checked against the +// pinned substrate's tap/dsp/version.h. +#define TAP_SR_DSPTAP_MINIMUM TAP_DSP_VERSION_ENCODE(0, 1, 0) +static_assert(TAP_DSP_VERSION >= TAP_SR_DSPTAP_MINIMUM, + "tap::sr::bridge needs DspTap 0.1.0 or newer (submodules/dsptap)"); + namespace tap::sr::bridge { /// The fixed rational ratio pair this library exists for: 44.1 -> 48 kHz diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 65462d5..48a9b81 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -62,7 +62,7 @@ is this checklist, not an opinion. | P9 | Non-finite input behaviour pinned | ✓ (A7) | ✓ (A7) | ✓ (A7) | | P10 | Host hot path gated | ✓ (A8) | ✓ (A8) | ✓ (A8) | | P11 | C ABI: one error model, extension rule, compat test, version probe | **open** (C3) | **open** (C3) | **open** (C3) | -| P12 | Versioned substrate pin asserted at compile time | **open** (A9) | **open** (A9) | **open** (A9) | +| P12 | Versioned substrate pin asserted at compile time | ✓ (A9) | ✓ (A9) | ✓ (A9) | | P13 | Changelog, semver and deprecation statement reconciled with D7 | **open** (C3) | **open** (C3) | **open** (C3) | ## 3. Block A (weeks 1–2): contracts and hygiene @@ -117,7 +117,7 @@ gated on shipping. - **A8. A host ratchet.** *Landed 2026-10-14: `scripts/icount.py --target host` runs each engine's `tap_sr__hostcount_*` workloads (`bench/icount/host_main.cpp`: the float converter in 128-frame blocks at 2 and 12 channels, `async`'s 12 on the channel-parallel path) under callgrind, counting only `tap_sr_host_counted()`, with eager binding and glibc's CPU dispatch and copy thresholds pinned. The counts are gated two-sided at ±3 % against the `host` key of each `bench/baselines.json` in the `host-ratchet` job (ubuntu-24.04, GCC 13). Two builds count identically. `async` at 12 channels runs about 30 M instructions per channel over the two-second stream, against 55 M at 2.* Deterministic `Ir` from callgrind (or `perf stat -e instructions:u` with pinned flags) over the multichannel float pipelines, ±3 %, the same `icount.py` mechanics, a hard gate two-sided like the QEMU ratchet (Q6). -- **A9. DspTap versioning.** Tag DspTap v0.1.0 at the tree this family pins (d7ebe4b or +- **A9. DspTap versioning.** *Landed 2026-10-14: DspTap's `tap/dsp/version.h` (tap/DspTap#62, `TAP_DSP_VERSION`, packed like the family's C ABI probes and pinned to its `project()` version by test) and its first tag, v0.1.0; each umbrella header here defines `TAP_SR_DSPTAP_MINIMUM` (0.1.0) token-identically and `static_assert`s it against the pinned substrate, `family.VersionMacrosAgree` holds the three equal, and the root README states the tested pairs.* Tag DspTap v0.1.0 at the tree this family pins (d7ebe4b or its successor), add one `tap/dsp/version.h` (one tree, one header; the D13 triplication exists only because the engines share no header), a `static_assert` in each engine umbrella header against the minimum DspTap it was tested with, a "tested pairs" table in diff --git a/rational/include/tap/sr/rational/rational.h b/rational/include/tap/sr/rational/rational.h index 27802d0..8edb392 100644 --- a/rational/include/tap/sr/rational/rational.h +++ b/rational/include/tap/sr/rational/rational.h @@ -31,6 +31,7 @@ // instruction-count ratchet (bench/). #pragma once +#include "tap/dsp/version.h" #include "tap/sr/rational/chain.h" // IWYU pragma: export #include "tap/sr/rational/converter.h" // IWYU pragma: export #include "tap/sr/rational/design.h" // IWYU pragma: export @@ -40,3 +41,11 @@ #define TAP_SR_VERSION_MAJOR 0 #define TAP_SR_VERSION_MINOR 6 #define TAP_SR_VERSION_PATCH 0 + +// The oldest DspTap this family was tested with (the roadmap's A9; the root +// README's tested pairs), defined token-identically in each engine's +// umbrella header like the family version (D13) and checked against the +// pinned substrate's tap/dsp/version.h. +#define TAP_SR_DSPTAP_MINIMUM TAP_DSP_VERSION_ENCODE(0, 1, 0) +static_assert(TAP_DSP_VERSION >= TAP_SR_DSPTAP_MINIMUM, + "tap::sr::rational needs DspTap 0.1.0 or newer (submodules/dsptap)"); diff --git a/submodules/dsptap b/submodules/dsptap index 3b729a0..9cc002c 160000 --- a/submodules/dsptap +++ b/submodules/dsptap @@ -1 +1 @@ -Subproject commit 3b729a0c1b7067789ce8e58c373c255c28577116 +Subproject commit 9cc002cc413565cbf97fb9fceaa7c82617cd7951 diff --git a/tests/family/version_macros.cpp b/tests/family/version_macros.cpp index ad56eb8..c279d65 100644 --- a/tests/family/version_macros.cpp +++ b/tests/family/version_macros.cpp @@ -4,21 +4,30 @@ // D13 (PLAN.md): one family version, TAP_SR_VERSION_*, defined // in each engine's umbrella header with no shared header (so the engines // stay independent, 4.2 check 1). The three definitions must agree, and -// this is the family's pinned release. Compile-only: its static_asserts are the -// test (tests/CMakeLists.txt builds this target as family.VersionMacrosAgree). +// this is the family's pinned release. The same holds for the DspTap floor +// (the roadmap's A9): TAP_SR_DSPTAP_MINIMUM, defined in each umbrella header, +// must agree, be the release the root README's tested pairs name, and not +// exceed the pinned substrate's TAP_DSP_VERSION. Compile-only: its +// static_asserts are the test (tests/CMakeLists.txt builds this target as +// family.VersionMacrosAgree). #include "tap/sr/async/async.h" namespace { - constexpr int k_async_major = TAP_SR_VERSION_MAJOR; - constexpr int k_async_minor = TAP_SR_VERSION_MINOR; - constexpr int k_async_patch = TAP_SR_VERSION_PATCH; + constexpr int k_async_dsptap_minimum = TAP_SR_DSPTAP_MINIMUM; + constexpr int k_async_major = TAP_SR_VERSION_MAJOR; + constexpr int k_async_minor = TAP_SR_VERSION_MINOR; + constexpr int k_async_patch = TAP_SR_VERSION_PATCH; } // namespace #undef TAP_SR_VERSION_MAJOR #undef TAP_SR_VERSION_MINOR #undef TAP_SR_VERSION_PATCH +#undef TAP_SR_DSPTAP_MINIMUM #include "tap/sr/bridge/ratio.h" +static_assert(k_async_dsptap_minimum == TAP_SR_DSPTAP_MINIMUM, + "TAP_SR_DSPTAP_MINIMUM must be identical in async.h and ratio.h (A9)"); + static_assert(k_async_major == TAP_SR_VERSION_MAJOR && k_async_minor == TAP_SR_VERSION_MINOR && k_async_patch == TAP_SR_VERSION_PATCH, "TAP_SR_VERSION_* must be identical in async.h and ratio.h (D13)"); @@ -26,10 +35,18 @@ static_assert(k_async_major == TAP_SR_VERSION_MAJOR && k_async_minor == TAP_SR_V #undef TAP_SR_VERSION_MAJOR #undef TAP_SR_VERSION_MINOR #undef TAP_SR_VERSION_PATCH +#undef TAP_SR_DSPTAP_MINIMUM #include "tap/sr/rational/rational.h" +static_assert(k_async_dsptap_minimum == TAP_SR_DSPTAP_MINIMUM, + "TAP_SR_DSPTAP_MINIMUM must be identical in async.h and rational.h (A9)"); + static_assert(k_async_major == TAP_SR_VERSION_MAJOR && k_async_minor == TAP_SR_VERSION_MINOR && k_async_patch == TAP_SR_VERSION_PATCH, "TAP_SR_VERSION_* must be identical in async.h and rational.h (D13)"); static_assert(k_async_major == 0 && k_async_minor == 6 && k_async_patch == 0, "the family version is 0.6.0 (D13); re-pin here when it is bumped"); +static_assert(k_async_dsptap_minimum == TAP_DSP_VERSION_ENCODE(0, 1, 0), + "the family's DspTap floor is 0.1.0 (A9); re-pin here, and in the root README's tested pairs, " + "when it moves"); +static_assert(TAP_DSP_VERSION >= k_async_dsptap_minimum, "the pinned DspTap is older than the family's floor"); From 7a106801e3e72b16dd6858cf634b4e9cf52bf2ce Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 14 Oct 2026 20:43:36 +0000 Subject: [PATCH 2/2] dsptap: pin v0.1.0 (dfd766b), the merged tree of tap/DspTap#62 Moves submodules/dsptap from the PR branch's 9cc002c to dfd766b on DspTap's main, the commit tagged v0.1.0. The two carry the identical tree (523e75a), so nothing this tree builds changes; the pin now stays reachable after DspTap's branch is deleted. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_016GKzq1VSdcMYCRMhddiMaf --- submodules/dsptap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/dsptap b/submodules/dsptap index 9cc002c..dfd766b 160000 --- a/submodules/dsptap +++ b/submodules/dsptap @@ -1 +1 @@ -Subproject commit 9cc002cc413565cbf97fb9fceaa7c82617cd7951 +Subproject commit dfd766b14ca313833468d8ef7c5df19b4a952d73