dancerudiments 0.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,80 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ cpp:
13
+ name: C++ (${{ matrix.os }})
14
+ strategy:
15
+ fail-fast: false
16
+ matrix:
17
+ os: [ubuntu-latest, windows-latest, macos-latest]
18
+ runs-on: ${{ matrix.os }}
19
+ steps:
20
+ - uses: actions/checkout@v6
21
+ with:
22
+ persist-credentials: false
23
+ - name: Configure
24
+ run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
25
+ - name: Build
26
+ run: cmake --build build --config Release
27
+ - name: Test
28
+ run: ctest --test-dir build -C Release --output-on-failure
29
+
30
+ python:
31
+ name: Python package (${{ matrix.os }})
32
+ strategy:
33
+ fail-fast: false
34
+ matrix:
35
+ os: [ubuntu-latest, windows-latest]
36
+ runs-on: ${{ matrix.os }}
37
+ steps:
38
+ - uses: actions/checkout@v6
39
+ with:
40
+ persist-credentials: false
41
+ - uses: actions/setup-python@v6
42
+ with:
43
+ python-version: "3.13"
44
+ - run: python -m pip install --upgrade build
45
+ - run: python -m build
46
+ - run: python -m pip install --no-index --find-links dist dancerudiments
47
+ - run: python -c "import dancerudiments as d; assert len(d.catalogue()) == 15"
48
+
49
+ javascript:
50
+ name: TypeScript and browser runtime
51
+ runs-on: ubuntu-latest
52
+ steps:
53
+ - uses: actions/checkout@v6
54
+ with:
55
+ persist-credentials: false
56
+ - uses: actions/setup-node@v6
57
+ with:
58
+ node-version: "24"
59
+ - run: npm ci
60
+ - run: npm test
61
+ - run: node --check harness/dancerudiments.js
62
+ - run: node --check harness/app.js
63
+
64
+ conan:
65
+ name: Conan recipe
66
+ runs-on: ubuntu-latest
67
+ steps:
68
+ - uses: actions/checkout@v6
69
+ with:
70
+ persist-credentials: false
71
+ - uses: actions/setup-python@v6
72
+ with:
73
+ python-version: "3.13"
74
+ - run: python -m pip install "conan>=2.20,<3"
75
+ - run: conan profile detect --force
76
+ - name: Build Conan package
77
+ shell: bash
78
+ run: |
79
+ VERSION=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
80
+ conan create . --version "$VERSION" --build=missing
@@ -0,0 +1,175 @@
1
+ name: Release packages
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ validate:
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v6
15
+ with:
16
+ persist-credentials: false
17
+ - uses: actions/setup-python@v6
18
+ with:
19
+ python-version: "3.13"
20
+ - run: python tools/check_release_version.py "${{ github.event.release.tag_name }}"
21
+
22
+ python-wheels:
23
+ needs: validate
24
+ strategy:
25
+ fail-fast: false
26
+ matrix:
27
+ os: [ubuntu-latest, windows-latest, macos-latest]
28
+ runs-on: ${{ matrix.os }}
29
+ steps:
30
+ - uses: actions/checkout@v6
31
+ with:
32
+ persist-credentials: false
33
+ - uses: actions/setup-python@v6
34
+ with:
35
+ python-version: "3.13"
36
+ - run: python -m pip install cibuildwheel==4.2.0
37
+ - run: python -m cibuildwheel --output-dir wheelhouse
38
+ - uses: actions/upload-artifact@v6
39
+ with:
40
+ name: wheels-${{ matrix.os }}
41
+ path: wheelhouse/*.whl
42
+ if-no-files-found: error
43
+
44
+ python-sdist:
45
+ needs: validate
46
+ runs-on: ubuntu-latest
47
+ steps:
48
+ - uses: actions/checkout@v6
49
+ with:
50
+ persist-credentials: false
51
+ - uses: actions/setup-python@v6
52
+ with:
53
+ python-version: "3.13"
54
+ - run: python -m pip install --upgrade build
55
+ - run: python -m build --sdist
56
+ - uses: actions/upload-artifact@v6
57
+ with:
58
+ name: python-sdist
59
+ path: dist/*.tar.gz
60
+ if-no-files-found: error
61
+
62
+ publish-pypi:
63
+ needs: [python-wheels, python-sdist]
64
+ runs-on: ubuntu-latest
65
+ environment: pypi
66
+ permissions:
67
+ id-token: write
68
+ steps:
69
+ - uses: actions/download-artifact@v7
70
+ with:
71
+ pattern: "{wheels-*,python-sdist}"
72
+ path: dist
73
+ merge-multiple: true
74
+ - uses: pypa/gh-action-pypi-publish@release/v1
75
+
76
+ publish-npm:
77
+ needs: validate
78
+ runs-on: ubuntu-latest
79
+ environment: npm
80
+ permissions:
81
+ contents: read
82
+ id-token: write
83
+ container: emscripten/emsdk:4.0.15
84
+ steps:
85
+ - uses: actions/checkout@v6
86
+ with:
87
+ persist-credentials: false
88
+ - uses: actions/setup-node@v6
89
+ with:
90
+ node-version: "24"
91
+ registry-url: "https://registry.npmjs.org"
92
+ package-manager-cache: false
93
+ - run: npm ci
94
+ - run: npm test
95
+ - run: emcmake cmake -S . -B build-wasm -DDANCERUDIMENTS_BUILD_WASM=ON -DDANCERUDIMENTS_BUILD_TESTS=OFF
96
+ - run: cmake --build build-wasm --config Release
97
+ - run: mkdir -p dist/wasm && cp build-wasm/dancerudiments.js build-wasm/dancerudiments.wasm dist/wasm/
98
+ - run: npm publish --access public
99
+ env:
100
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
101
+
102
+ cpp-archives:
103
+ needs: validate
104
+ strategy:
105
+ fail-fast: false
106
+ matrix:
107
+ include:
108
+ - os: ubuntu-latest
109
+ label: linux-x64
110
+ - os: windows-latest
111
+ label: windows-x64
112
+ - os: macos-latest
113
+ label: macos-arm64
114
+ runs-on: ${{ matrix.os }}
115
+ steps:
116
+ - uses: actions/checkout@v6
117
+ with:
118
+ persist-credentials: false
119
+ - name: Build and install
120
+ shell: bash
121
+ run: |
122
+ cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DDANCERUDIMENTS_BUILD_TESTS=OFF
123
+ cmake --build build --config Release
124
+ cmake --install build --config Release --prefix stage
125
+ - name: Create archive
126
+ shell: bash
127
+ run: cmake -E tar cf "DanceRudiments-${{ github.event.release.tag_name }}-${{ matrix.label }}.zip" --format=zip stage
128
+ - uses: actions/upload-artifact@v6
129
+ with:
130
+ name: cpp-${{ matrix.label }}
131
+ path: DanceRudiments-*.zip
132
+ if-no-files-found: error
133
+
134
+ attach-github-release:
135
+ needs: [cpp-archives, python-wheels, python-sdist]
136
+ runs-on: ubuntu-latest
137
+ permissions:
138
+ contents: write
139
+ steps:
140
+ - uses: actions/download-artifact@v7
141
+ with:
142
+ pattern: "{cpp-*,wheels-*,python-sdist}"
143
+ path: release-assets
144
+ merge-multiple: true
145
+ - name: Attach verified packages
146
+ env:
147
+ GH_TOKEN: ${{ github.token }}
148
+ run: gh release upload "${{ github.event.release.tag_name }}" release-assets/* --clobber
149
+
150
+ publish-conan:
151
+ needs: validate
152
+ runs-on: ubuntu-latest
153
+ environment: conan
154
+ env:
155
+ CONAN_REMOTE_URL: ${{ vars.CONAN_REMOTE_URL }}
156
+ CONAN_LOGIN_USERNAME: ${{ secrets.CONAN_LOGIN_USERNAME }}
157
+ CONAN_PASSWORD: ${{ secrets.CONAN_PASSWORD }}
158
+ steps:
159
+ - uses: actions/checkout@v6
160
+ with:
161
+ persist-credentials: false
162
+ - uses: actions/setup-python@v6
163
+ with:
164
+ python-version: "3.13"
165
+ - run: python -m pip install "conan>=2.20,<3"
166
+ - run: conan profile detect --force
167
+ - name: Build Conan package
168
+ shell: bash
169
+ run: conan create . --version "${GITHUB_REF_NAME#v}" --build=missing
170
+ - name: Upload when a Conan remote is configured
171
+ if: ${{ env.CONAN_REMOTE_URL != '' && env.CONAN_LOGIN_USERNAME != '' && env.CONAN_PASSWORD != '' }}
172
+ run: |
173
+ conan remote add release "$CONAN_REMOTE_URL" --force
174
+ conan remote login release "$CONAN_LOGIN_USERNAME" --password "$CONAN_PASSWORD"
175
+ conan upload "dancerudiments/*" --remote release --confirm
@@ -0,0 +1,25 @@
1
+ # CMake and direct compiler output
2
+ /build/
3
+ /build-*/
4
+ /cmake-build-*/
5
+ CMakeUserPresets.json
6
+
7
+ # Compiled language bindings
8
+ /dist/
9
+ *.pyd
10
+ *.so
11
+ *.wasm
12
+
13
+ # Node and Python environments
14
+ /node_modules/
15
+ /.npm-cache/
16
+ /venv/
17
+ /.venv/
18
+ __pycache__/
19
+ *.py[cod]
20
+
21
+ # Editors and operating systems
22
+ .idea/
23
+ .vscode/
24
+ .DS_Store
25
+ Thumbs.db
@@ -0,0 +1,64 @@
1
+ cmake_minimum_required(VERSION 3.20)
2
+ project(DanceRudiments VERSION 0.1.2 LANGUAGES CXX)
3
+
4
+ option(DANCERUDIMENTS_BUILD_TESTS "Build C++ tests" ON)
5
+ option(DANCERUDIMENTS_BUILD_PYTHON "Build the pybind11 Python module" OFF)
6
+ option(DANCERUDIMENTS_BUILD_WASM "Build the Emscripten JavaScript module" OFF)
7
+
8
+ add_library(DanceRudiments STATIC src/dance_rudiments.cpp)
9
+ add_library(DanceRudiments::DanceRudiments ALIAS DanceRudiments)
10
+ set_target_properties(DanceRudiments PROPERTIES POSITION_INDEPENDENT_CODE ON)
11
+ if(WIN32)
12
+ # Avoid colliding with the dancerudiments Python module's MSVC import library.
13
+ set_target_properties(DanceRudiments PROPERTIES OUTPUT_NAME DanceRudimentsCore)
14
+ endif()
15
+ target_compile_features(DanceRudiments PUBLIC cxx_std_17)
16
+ target_include_directories(DanceRudiments PUBLIC
17
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
18
+ $<INSTALL_INTERFACE:include>)
19
+
20
+ if(DANCERUDIMENTS_BUILD_PYTHON)
21
+ set(PYBIND11_FINDPYTHON ON)
22
+ find_package(pybind11 CONFIG REQUIRED)
23
+ pybind11_add_module(dancerudiments_python bindings/python/module.cpp)
24
+ set_target_properties(dancerudiments_python PROPERTIES OUTPUT_NAME dancerudiments)
25
+ target_link_libraries(dancerudiments_python PRIVATE DanceRudiments::DanceRudiments)
26
+ if(MINGW)
27
+ target_link_options(dancerudiments_python PRIVATE -static-libgcc -static-libstdc++)
28
+ get_filename_component(DANCERUDIMENTS_MINGW_BIN "${CMAKE_CXX_COMPILER}" DIRECTORY)
29
+ set(DANCERUDIMENTS_WINPTHREAD_DLL "${DANCERUDIMENTS_MINGW_BIN}/libwinpthread-1.dll")
30
+ if(EXISTS "${DANCERUDIMENTS_WINPTHREAD_DLL}")
31
+ install(FILES "${DANCERUDIMENTS_WINPTHREAD_DLL}" DESTINATION .)
32
+ endif()
33
+ endif()
34
+ install(TARGETS dancerudiments_python LIBRARY DESTINATION . RUNTIME DESTINATION .)
35
+ endif()
36
+
37
+ if(DANCERUDIMENTS_BUILD_WASM)
38
+ if(NOT EMSCRIPTEN)
39
+ message(FATAL_ERROR "DANCERUDIMENTS_BUILD_WASM requires Emscripten (use emcmake cmake).")
40
+ endif()
41
+ add_executable(dancerudiments_wasm bindings/typescript/wasm.cpp)
42
+ target_link_libraries(dancerudiments_wasm PRIVATE DanceRudiments::DanceRudiments)
43
+ set_target_properties(dancerudiments_wasm PROPERTIES OUTPUT_NAME dancerudiments)
44
+ target_link_options(dancerudiments_wasm PRIVATE
45
+ "--bind"
46
+ "-sMODULARIZE=1"
47
+ "-sEXPORT_ES6=1"
48
+ "-sENVIRONMENT=web,node"
49
+ "-sALLOW_MEMORY_GROWTH=1")
50
+ endif()
51
+
52
+ if(DANCERUDIMENTS_BUILD_TESTS)
53
+ enable_testing()
54
+ add_executable(dancerudiments_tests tests/cpp/test_main.cpp)
55
+ target_link_libraries(dancerudiments_tests PRIVATE DanceRudiments::DanceRudiments)
56
+ add_test(NAME dancerudiments_cpp COMMAND dancerudiments_tests)
57
+ endif()
58
+
59
+ include(GNUInstallDirs)
60
+ install(TARGETS DanceRudiments EXPORT DanceRudimentsTargets
61
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
62
+ install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
63
+ install(EXPORT DanceRudimentsTargets NAMESPACE DanceRudiments::
64
+ DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/DanceRudiments)
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kieran Simkin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,148 @@
1
+ Metadata-Version: 2.4
2
+ Name: dancerudiments
3
+ Version: 0.1.2
4
+ Summary: Integer-pip rhythmic position functions with a C++ core
5
+ Author: Kieran Simkin
6
+ License-Expression: MIT
7
+ Classifier: Programming Language :: C++
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Topic :: Multimedia :: Sound/Audio
10
+ Classifier: Topic :: Software Development :: Libraries
11
+ Project-URL: Homepage, https://github.com/kieransimkin/DanceRudiments
12
+ Project-URL: Repository, https://github.com/kieransimkin/DanceRudiments
13
+ Project-URL: Issues, https://github.com/kieransimkin/DanceRudiments/issues
14
+ Requires-Python: >=3.9
15
+ Description-Content-Type: text/markdown
16
+
17
+ # DanceRudiments
18
+
19
+ DanceRudiments is a small C++17 library of deterministic rhythmic position functions. One integer pip is `1/64` of a beat. Every rudiment owns a 64-, 128-, or 256-pip loop and wraps any positive or negative input into that loop before sampling.
20
+
21
+ Outputs are dimensionless offsets, normally in `[-1, 1]`. The caller chooses pixels, CSS units, metres, or another scale. There is deliberately no time interpolation in the public API: renderers advance with integer pips and get one exact sample per pip.
22
+
23
+ ## Included rudiments
24
+
25
+ The general motions are `bounce`, `sway`, `circle`, `figure_eight`, `step_touch`, `box_step`, `helix`, and `clay_background`.
26
+
27
+ The first drum-derived set is `single_stroke_roll`, `multiple_bounce_roll`, `double_stroke_roll`, `single_paradiddle`, `flam`, `drag`, and `five_stroke_roll`. The first six mirror the fundamentals in Vic Firth's Tier One learning sequence and collectively cover the roll, diddle, flam, and drag families in the Percussive Arts Society's 40 International Drum Rudiments. A right-hand stroke moves right, a left-hand stroke moves left, and both rise slightly. Every stroke is a raised-cosine (`sin²`) gesture: zero displacement at the start, smooth attack, a rounded peak, and smooth decay back to zero before the next motion. Accents use greater amplitude rather than an instantaneous position jump.
28
+
29
+ Source references: [Percussive Arts Society International Drum Rudiments](https://pas.org/rudiments/) and [Vic Firth 40 Essential Rudiments](https://ae.vicfirth.com/education/40-essential-rudiments/), accessed 28 September 2026.
30
+
31
+ `clay_background` preserves the translation waypoints from the Clay/Stars `ks-particle-dance` background treatment as a normalized 256-pip lookup table. The source effect's opacity, rotation, and scale are not position offsets, so they are intentionally excluded.
32
+
33
+ ## Build and test the C++ core
34
+
35
+ ```powershell
36
+ cmake -S . -B build -G Ninja
37
+ cmake --build build
38
+ ctest --test-dir build --output-on-failure
39
+ ```
40
+
41
+ ## Python binding
42
+
43
+ Install pybind11 in your chosen environment, then configure with `-DDANCERUDIMENTS_BUILD_PYTHON=ON`. The module exposes `sample(name, pip_count)`, the named functions, and `catalogue()`.
44
+
45
+ ## TypeScript/WASM binding
46
+
47
+ Configure under Emscripten with `emcmake cmake -DDANCERUDIMENTS_BUILD_WASM=ON`, build `dancerudiments_wasm`, import its generated ES module, then call `bindNative(await createDanceRudiments())` from `bindings/typescript/index.ts`.
48
+
49
+ The dependency-free harness uses a browser-native mirror of the same discrete definitions so it can run without a toolchain. Its tests validate modulo wrapping and seam behaviour. Production TypeScript consumers should use the WASM binding so C++ remains the single runtime authority.
50
+
51
+ ## Harness
52
+
53
+ Serve the repository root over HTTP and open `harness/index.html`. The harness provides play/pause, exact pip stepping, BPM, amplitude, trails, a numeric readout, and reduced-motion-aware manual operation.
54
+
55
+ ```powershell
56
+ python -m http.server 4173
57
+ ```
58
+
59
+ Then open `http://localhost:4173/harness/`.
60
+
61
+ ## Continuous integration and releases
62
+
63
+ `.github/workflows/ci.yml` builds and tests the C++ core on Linux, Windows, and macOS; builds and imports the Python package; compiles/tests the TypeScript and browser runtime; and creates a Conan package on every push to `main` and every pull request.
64
+
65
+ Publishing is deliberately tied to a GitHub release with a `vMAJOR.MINOR.PATCH` tag. Before creating the release, update the matching version in `CMakeLists.txt`, `pyproject.toml`, and `package.json`. `.github/workflows/release.yml` rejects mismatches before it publishes anything, then:
66
+
67
+ - builds platform Python wheels and a source distribution and publishes them to PyPI using OIDC Trusted Publishing;
68
+ - builds the TypeScript wrapper and C++ WebAssembly module and publishes `@kieransimkin/dance-rudiments` to npm using npm Trusted Publishing and provenance;
69
+ - builds installable C++ archives for Linux, Windows, and macOS and attaches them, along with Python distributions, to the GitHub release; and
70
+ - builds a Conan package and uploads it only when a separate Conan remote has been configured. ConanCenter packages are submitted through `conan-center-index`; they are not directly uploaded by this repository.
71
+
72
+ One-time registry configuration is required before the first release:
73
+
74
+ 1. On PyPI, create a pending Trusted Publisher for owner `kieransimkin`, repository `DanceRudiments`, workflow `release.yml`, environment `pypi`, and project name `dancerudiments`.
75
+ 2. On npm, configure the package's GitHub Actions Trusted Publisher for `kieransimkin/DanceRudiments`, workflow `release.yml`, environment `npm`, with direct publishing allowed. Because npm Trusted Publishers are configured from an existing package's settings, the first scoped-package registration may require a granular `NPM_TOKEN` secret in the `npm` environment. Remove that bootstrap token after Trusted Publishing is configured.
76
+ 3. Create GitHub environments named `pypi`, `npm`, and `conan`; add required reviewers if desired.
77
+ 4. For an optional private or organisational Conan repository, set environment variable `CONAN_REMOTE_URL` and secrets `CONAN_LOGIN_USERNAME` and `CONAN_PASSWORD` in the `conan` environment. If they are absent, the recipe is built and verified but not uploaded.
78
+
79
+ The publishing jobs use short-lived OIDC identity for PyPI and npm and do not require long-lived PyPI or npm tokens. See the official [PyPI Trusted Publisher](https://docs.pypi.org/trusted-publishers/using-a-publisher/) and [npm Trusted Publishing](https://docs.npmjs.com/trusted-publishers/) documentation.
80
+
81
+ ## Motion safety
82
+
83
+ Apply an output to a bounded subject. DanceRudiments changes position only; it must not be used to add a repetitive full-frame tint, brightness, flash, or colour-grade effect. Under `prefers-reduced-motion`, do not autoplay the harness or production motion.
84
+
85
+ ## Potential problems
86
+
87
+ ### MSVC core and Python import libraries have the same filename
88
+
89
+ - **Symptom (28 September 2026):** after excluding 32-bit wheels, the `v0.1.1` Windows wheel still failed at `cp39-win_amd64` with `LINK : fatal error LNK1114: cannot overwrite the original file '.../Release/dancerudiments.lib'; error code 5`.
90
+ - **Cause:** on Windows' case-insensitive filesystem, the static core's default `DanceRudiments.lib` filename collided with the Python extension's `dancerudiments.lib` import library. Both targets were valid, but MSVC placed their archive outputs in the same configuration directory.
91
+ - **Corrective action:** retain the public CMake target name `DanceRudiments::DanceRudiments`, but give its Windows archive the distinct physical filename `DanceRudimentsCore.lib`. Add a Windows Python-package job to ordinary CI so MSVC builds and imports the wheel before any release.
92
+ - **Verification:** CI must build and import the Python wheel on `windows-latest`; the release workflow must then complete every `win_amd64` wheel and import test.
93
+ - **Research:** CMake's [`OUTPUT_NAME`](https://cmake.org/cmake/help/latest/prop_tgt/OUTPUT_NAME.html) and [`add_library`](https://cmake.org/cmake/help/latest/command/add_library.html) documentation, accessed 28 September 2026, confirms that output filenames may be changed independently of logical target names and that Windows shared/module targets have associated import libraries.
94
+
95
+ ### cibuildwheel attempts unsupported 32-bit Windows wheels
96
+
97
+ - **Symptom (28 September 2026):** the `v0.1.0` release workflow failed in `python-wheels (windows-latest)` while building `cp39-win32`; cibuildwheel reported that its isolated `python -m build` command exited with code 1. The Linux and macOS wheel jobs were unaffected, and the npm package published successfully.
98
+ - **Cause:** the broad `cp39-*` through `cp314-*` build selectors also include 32-bit Windows identifiers, while DanceRudiments currently targets 64-bit package architectures.
99
+ - **Corrective action:** add `*-win32` to `[tool.cibuildwheel].skip`, retaining the existing musllinux exclusion. Do not advertise or emit a 32-bit wheel until that architecture is deliberately supported and tested.
100
+ - **Verification:** the patch-release workflow must complete the Windows `win_amd64` matrix and its import tests before PyPI publication.
101
+ - **Research:** [cibuildwheel build/skip options](https://cibuildwheel.pypa.io/en/stable/options/), accessed 28 September 2026; the official examples explicitly use `*-win32` to skip 32-bit Windows builds.
102
+
103
+ ### npm cache access is denied on Windows
104
+
105
+ - **Symptom (28 September 2026):** `npm pack --dry-run` failed with `EPERM: operation not permitted, open 'C:\Users\Kieran\AppData\Local\npm-cache\_cacache\tmp\…'` after the TypeScript tests had passed.
106
+ - **Cause:** the shared user cache could not create its temporary file. npm's Windows issue tracker records this class of `EPERM` failure, including cases involving cache files and real-time scanning; the exact process holding this particular file was not identified.
107
+ - **Corrective action:** keep the shared cache intact and run package validation with a repository-local cache: `npm pack --dry-run --cache .npm-cache`. The cache directory is ignored by Git.
108
+ - **Verification:** the dry-run package completed with the local cache. This workaround changes only npm's disposable cache location and does not change the package contents.
109
+ - **Research:** [npm CLI Windows EPERM report](https://github.com/npm/cli/issues/8072) and [npm CLI cache-isolation guidance](https://github.com/npm/cli/issues/1785), accessed 28 September 2026.
110
+
111
+ ### CMake stalls while detecting the C++ compiler ABI on this host
112
+
113
+ - **Symptom (28 September 2026):** CMake 3.30 with Ninja and the project-available MinGW GCC 16.2 compiler stopped after `Detecting CXX compiler ABI info` for more than 90 seconds. Compiler identification itself succeeded, and no diagnostic failure was emitted.
114
+ - **Cause:** unknown. Similar CMake reports show that this stage is a nested `try_compile`, but the available reports do not establish the cause on this Windows/Z-drive setup.
115
+ - **Working validation route:** compile the exact core and test sources directly with `g++ -std=c++17 -I include src/dance_rudiments.cpp tests/cpp/test_main.cpp -o build/dancerudiments_tests.exe`, then run the executable. Do not weaken or hard-code CMake's portable compiler detection merely to hide the local stall.
116
+ - **Verification:** the direct GCC build completed and all C++ assertions passed. This verifies the library sources with the available compiler; it does not claim that CMake configuration succeeded on this host.
117
+ - **Research:** [CMake discussion of a compiler-ABI detection hang](https://discourse.cmake.org/t/how-to-debug-detecting-c-compiler-abi-info-hanging-cygwin-on-github-actions/4580), accessed 28 September 2026.
118
+
119
+ ### Node test isolation is denied
120
+
121
+ - **Symptom (28 September 2026):** `node --test "tests/typescript/*.test.js"` failed before assertions with `Error: spawn EPERM`.
122
+ - **Cause:** the restricted Windows host denied Node's child-process worker spawn.
123
+ - **Corrective action:** run `node --test --test-isolation=none "tests/typescript/*.test.js"`.
124
+ - **Verification:** both runtime suites passed in the single process. Keep tests free of shared mutable global state when adding more files.
125
+
126
+ ### An isolated Python build selects unavailable NMake on Windows
127
+
128
+ - **Symptom (28 September 2026):** the first scikit-build-core attempt failed during configuration with `Running 'nmake' '-?' failed with: no such file or directory` and `CMAKE_CXX_COMPILER not set`.
129
+ - **Cause supported by current evidence:** CMake selected the `NMake Makefiles` generator although this host provides Ninja and MinGW GCC, not NMake/MSVC. The first `uv run` invocation also attempted an unnecessary editable install of the current project before running the requested build command.
130
+ - **Corrective action:** set `CMAKE_GENERATOR=Ninja` for this local toolchain and use `uv run --no-project --with build python -m build`, leaving CI runners free to select their native supported compiler environment.
131
+ - **Verification:** the isolated build then produced `dancerudiments-0.1.0.tar.gz` and a CPython 3.13 Windows wheel, with the C++ extension compiled and installed successfully.
132
+ - **Research:** CMake documents `CMAKE_GENERATOR` as the supported generator-selection mechanism, while scikit-build-core documents Ninja selection and its `ninja.make-fallback` behaviour. Sources: [CMake generator environment variable](https://cmake.org/cmake/help/latest/envvar/CMAKE_GENERATOR.html) and [scikit-build-core configuration](https://scikit-build-core.readthedocs.io/en/stable/configuration/), accessed 28 September 2026.
133
+
134
+ ### A MinGW-built Python wheel cannot locate its C++ runtime DLLs
135
+
136
+ - **Symptom (28 September 2026):** the first locally built Windows wheel installed successfully but `import dancerudiments` failed with `ImportError: DLL load failed while importing dancerudiments: The specified module could not be found.`
137
+ - **Cause supported by current evidence:** the MinGW-built extension dynamically referenced GCC runtime libraries that were available in the compiler directory but not in the isolated Python environment. This local toolchain differs from cibuildwheel's normal Windows MSVC environment.
138
+ - **Corrective action:** inspect the built `.pyd` with the matching toolchain's `objdump -p`. It identified `libwinpthread-1.dll` as the remaining non-system dependency after the standard C++ runtimes were made static. Only for MinGW, link with `-static-libgcc -static-libstdc++` and install the compiler's matching `libwinpthread-1.dll` beside the extension; do not apply that bundling to MSVC or other platforms.
139
+ - **Verification:** rebuild the wheel and import it in a fresh isolated environment; require the catalogue and modulo-sampling smoke assertions to pass. The release workflow independently runs cibuildwheel's installed-wheel test on every platform.
140
+ - **Research:** GCC documents `-static-libstdc++` as linking the C++ runtime statically without making the whole module static, and the pybind11 issue tracker records the same generic Windows import symptom when a compiler runtime DLL is missing. Sources: [GCC link options](https://gcc.gnu.org/onlinedocs/gcc/Link-Options.html) and [pybind11 missing-runtime discussion](https://github.com/pybind/pybind11/issues/2771), accessed 28 September 2026.
141
+
142
+ ### Conan detects an unavailable future Visual Studio generator
143
+
144
+ - **Symptom (28 September 2026):** local `conan profile detect` selected `msvc` version 195 and generated `Visual Studio 18 2026`, while CMake 3.30 on this host only exposes Visual Studio generators through 2022. The recipe consequently failed before compiling.
145
+ - **Cause:** the host's compiler discovery evidence is inconsistent: no usable `cl.exe` is on the command path, but Conan detected a newer MSVC installation than the installed CMake understands. This is a local toolchain/profile mismatch, not a recipe failure established across supported runners.
146
+ - **Corrective action:** do not commit the guessed local profile or hard-code a generator in the portable recipe. CI detects and builds the recipe on `ubuntu-latest` with its supported native profile; Windows consumers should use a profile naming an installed compiler and generator combination.
147
+ - **Verification:** require the GitHub Actions Conan job to complete `conan create` from the committed recipe. Until that run passes, local recipe syntax/export is verified but the Conan binary build remains pending.
148
+ - **Research:** Conan's profile detector warns that detected profiles are guesses and not guaranteed stable; CMake documents that the selected generator must match an available build environment. See [CMake's user interaction guide](https://cmake.org/cmake/help/latest/guide/user-interaction/index.html), accessed 28 September 2026.