This repository is a conformance harness for multiple Sendspin implementations. It does four things:
- builds adapter CLIs for each implementation/toolchain
- runs a scenario matrix across
server -> clientimplementation pairs - records per-case artifacts and summaries
- generates a static HTML report for GitHub Pages
Treat this repository as infrastructure around the implementations, not as the implementations themselves.
Start with these files before making changes:
README.md: current user-facing workflowsrc/conformance/scenarios.py: canonical list and order of scenariossrc/conformance/implementations.py: implementation registry and role capability metadatasrc/conformance/runner.py: matrix execution and artifact layoutsrc/conformance/site.py: static report generationadapters/README.md: stable adapter CLI contract
These are the easiest things to break accidentally.
Every implementation is modeled as exactly two CLIs:
serverclient
Client-side unsupported roles still need a CLI surface that the harness can invoke. Those cases should fail fast, emit a summary, and exit non-zero. Server-side unsupported scenarios are filtered out before case creation, so the runner does not create dead server rows in the matrix.
Adapters do not need to rely on the implementation library owning discovery itself. External mDNS advertisement/browsing or the harness registry handoff are acceptable, as long as the adapter can still attach the implementation to an outbound WebSocket or an accepted inbound WebSocket and report the resulting interaction honestly.
Scenarios are data-driven. Prefer adding scenario metadata in src/conformance/scenarios.py and generic scenario helpers in src/conformance/models.py over adding new if scenario_id == ... branches throughout the codebase.
Important current scenario traits:
initiator_rolepreferred_codec
Adapters should primarily branch on generic traits like initiator-role, not on hard-coded scenario IDs, unless the behavior is truly scenario-specific.
Scenario order is explicit and matters:
- run order
- site index order
- user perception of report health
Today the PCM scenarios are intentionally listed first because they are greener. Preserve that unless there is a strong reason to change it.
The report output layout is fixed:
results/data/results/index.htmlresults/implementations/*.htmlresults/scenarios/*.htmlresults/cases/*.html
Per-case artifacts live under results/data/<environment>__<scenario>__<server>__to__<client>/.
Cross-run metadata such as build logs and repository revisions also live under
results/data/, notably:
results/data/build-report.jsonresults/data/repositories.json
Do not reintroduce SPA-style routing or inline all case details onto scenario pages.
The site is static HTML, not a SPA. The index page shows all scenario matrices. Each scenario gets its own HTML page, and each concrete server/client pairing gets its own dedicated HTML page. Use only minimal vanilla JS; today that is limited to the case-page tabs in src/conformance/site.py.
Keep server/client roles explicit everywhere in the report. Do not regress back to ambiguous from/to terminology.
The overview page also shows repository revision metadata gathered at run time.
If you change how repositories are resolved, merged, or reported, update both
src/conformance/repository_versions.py and the overview rendering in
src/conformance/site.py.
Successful adapters should print a JSON summary and exit 0.
Important fields to preserve where available:
statusimplementationrolescenario_idinitiator_rolepreferred_codecpeer_hello- audio hash fields
peer_hello should contain the full hello message received from the other party whenever capture is possible.
The shared source fixture is almost_silent.flac from sendspin-cli:
- resolved by
src/conformance/fixtures.py - expected at
sendspin-cli/tests/fixtures/almost_silent.flac
Do not hard-code another fixture path in adapters or tests.
GitHub Actions should still publish artifacts and GitHub Pages even when the conformance matrix has failing cases. Harness failures are currently expected. Do not add a workflow step that turns expected red matrix cases into a failed workflow.
Build/setup failures are different: those can still be real workflow failures.
The published workflow currently runs on macos-latest and publishes one matrix without host-specific sections in the UI. Internal environment metadata may still exist in raw artifacts for build-log lookup or local merged runs, but the public report should read as one authoritative matrix.
src/conformance/models.py: shared dataclasses and scenario capability helperssrc/conformance/scenarios.py: scenario registry and ordersrc/conformance/implementations.py: implementation registry and role metadatasrc/conformance/runner.py: matrix execution and process orchestrationsrc/conformance/build.py: adapter build checkssrc/conformance/repository_versions.py: git revision metadata written intoresults/data/repositories.jsonsrc/conformance/merge.py: merges host-specific raw result directories, including build and repository metadatasrc/conformance/site.py: static site generationsrc/conformance/flac.pyandsrc/conformance/pcm.py: canonical decode/hash helpers
src/conformance/adapters/aiosendspin_server.py: real Python server adaptersrc/conformance/adapters/aiosendspin_client.py: real Python client adapteradapters/sendspin-dotnet/client/: real.NETclient adapter sourceadapters/sendspin-go/: real Go client/server adapter sourceadapters/SendspinKit/client/: real Swift client adapter source with an adapter-owned inbound WebSocket listener for server-initiated scenariosadapters/sendspin-rs/client/: real Rust client adapter sourceadapters/sendspin-js/client.mjs: real Node.js client adapter sourcesrc/conformance/adapters/placeholder.py: generic fail-fast adapteradapters/sendspin-js/server.mjs: fail-fast placeholder for the unsupportedsendspin-jsserver role
scripts/setup_repositories.py: clone/check required reposscripts/setup_workspace.py: create local venv and install Python depsscripts/run_all.py: build + run + report orchestrationscripts/merge_results.py: merge multiple host result sets into one report
.github/workflows/publish.yml: macOS build + run + Pages publishing
Bootstrap locally:
python scripts/setup_workspace.py --clone
. .venv/bin/activateBuild adapters:
python -m conformance.cli buildRun full matrix:
python scripts/run_all.py --results-dir results --build-report-path artifacts/build-report.jsonRun with explicit host metadata:
python scripts/run_all.py \
--results-dir results \
--build-report-path artifacts/build-report.json \
--environment-id linux \
--environment-name LinuxRun filtered matrix:
python -m conformance.cli run --results-dir results --from aiosendspin --to aiosendspin,sendspin-dotnetRegenerate report only:
python -m conformance.cli report --results-dir resultsMerge multiple host runs:
python scripts/merge_results.py \
--output-dir artifacts/results \
artifacts/linux-results \
artifacts/macos-resultsFast sanity checks after edits:
python -m compileall src scripts
git diff --checkIf you touched the .NET adapter, also run:
~/.dotnet/dotnet build adapters/sendspin-dotnet/client/Conformance.SendspinDotnet.Client.csprojPrefer this path:
- Add a
ScenarioSpecentry insrc/conformance/scenarios.py. - Reuse generic fields like
initiator_role,preferred_codec, andextra_cli_argsbefore inventing new runner conditionals. - Extend adapter behavior using generic CLI args when possible.
- Keep scenario order intentional.
- Verify the report renders the new scenario on the index page and generates dedicated pages under
results/scenarios/andresults/cases/.
If a scenario requires new behavior that does not fit the existing generic contract, add the minimum new generic scenario field needed instead of scattering per-scenario string checks.
- Add or update the implementation entry in
src/conformance/implementations.py. - Expose both
serverandclientroles. - If a role is unsupported, wire it to a fail-fast adapter and give a precise
reason. - Keep adapter summaries aligned with the common summary contract.
- Update
adapters/README.mdif the adapter contract changes. - Verify both build-time and run-time behavior.
When adding a real adapter, make it consume harness CLI args rather than making the runner special-case that implementation.
- Prefer changing harness behavior in one place over duplicating logic across runner, adapters, and site.
- Preserve explicit naming:
server_impl,client_impl,initiator_role. - Keep result files machine-readable and stable; downstream checks depend on them.
- Treat
results/as generated output, not source. - If you change summary fields or artifact paths, update both the site generator and any runner expectations in the same change.
After meaningful changes, run the smallest relevant subset plus a syntax/build pass.
Typical minimum:
python -m compileall src scriptspython -m conformance.cli run --results-dir results --from aiosendspin --to aiosendspin,sendspin-dotnet --timeout-seconds 25python -m conformance.cli report --results-dir results
If you changed CI, read .github/workflows/publish.yml afterward and confirm:
- Pages still uploads from
artifacts/results - expected harness failures do not fail the workflow
- the macOS job still runs the full published matrix
- the published artifact still preserves
build-report.jsonandrepositories.json
- Make focused commits.
- Push regularly when changes are meaningful.
- Do not rewrite history unless explicitly asked.