Repository navigation
Description
The gap
docs/architecture/packaging.md lists this under Known gaps:
No CI job generates an image with the staged
sd-server; its gates are--helpand the platform floors, not a picture.
Why it matters
A binary that prints its help text and links only permitted libraries is not a binary that can diffuse. A ggml backend library pruned one file too far, a recipe that stopped building an operation a model needs, or a pin bump that changed a tensor name all survive both gates. test_audiocpp_voicing.py exists because the same was true of audio.cpp: starting and listing devices proved nothing about sound.
Today a broken sd-server reaches the user as Studio failing on its first image, after a three gigabyte download the app told them to make.
Where
.github/workflows/build-sdcpp.yml— the generation gate goes afterGate on the app's Linux floorandGate on the app's macOS floor, beforePack..github/workflows/build-audiocpp.yml—Gate on every curated model voicingis the pattern to copy, including the inline script that reads each pinned file's repo, revision and path out of the manifest and downloads it.surfsense_local/backend/tests/packaging/test_audiocpp_voicing.py— the shape to reuse:pytestmark = pytest.mark.packaging, the staged folder from an environment variable, each file checked against the manifest's sha256, the server started with the sidecar's own flags and stopped in a fixture.surfsense_local/electron/src/main/sidecars/sdcpp.ts—sdcppSpec()is the argv the app really uses,--diffusion-faincluded. Read it rather than inventing a second launch line.surfsense_local/backend/modules/llm/catalog/local/engines/sdcpp/launch.py—file_flags()anddefault_flags()turn a manifest build into those flags.surfsense_local/backend/modules/llm/providers/sdcpp/serving.py—wait_until_serving()already polls until the server names the file it was launched on.
What done looks like
build-sdcpp.ymlfails when the stagedsd-serverstarts but cannot produce a usable image.- The result is checked as a picture: it decodes, it is the size that was asked for, and it is not one flat colour. A byte-length assertion passes on a blank canvas.
- Which curated model, and at how many steps, is yours to choose. Stable Diffusion 1.5 is the cheapest in the manifest — 512 px,
v1-5-pruned_Q4_0.gguf, 3.05 GB — and the runners have no GPU, so wall-clock time is the constraint. Put the run's cost in the PR so the next person can judge it. - The pinned file is checked against the manifest's sha256 before it is used, as the voicing test does.
- Delete the Known gaps line from
docs/architecture/packaging.mdin the same PR, and add the test to the Packaging tests table there.
What not to change
The --help and platform-floor gates stay. They catch a different failure — a binary that will not start on Ubuntu 22.04 or macOS 13.3 — and they cost seconds. Windows is a pinned download staged by the release job rather than this workflow, so leaving it out of this gate is deliberate, not an oversight.
How to verify
Write the failing test first: a packaging-marked test that asserts an image comes back, which fails while nothing generates one. pyproject.toml pins -m 'not packaging' in addopts, so the marker on the command line is what runs it. The workflow's own paths filter includes .github/workflows/build-sdcpp.yml, so the pull request that adds the gate runs the gate.
python scripts/check_docs.py # from the repo root, for the doc edit
cd surfsense_local/backend
uv run pytest tests/packaging/test_sdcpp_image.py -m packaging
uv run pytest -m unit
uv run ruff check .Before you start
Read CONTRIBUTING.md and the packaging doc. Open your PR against dev, not main. Comment here to claim this.
Activity
ybai08 commentedon Oct 2, 2026
I'd like to take this. Plan: a
packagingtest shaped liketest_audiocpp_voicing.pythat starts the stagedsd-serverwith the sidecar's own flags on the manifest's Stable Diffusion 1.5 build (checked against its sha256), asks for one small image at few steps, and checks it as a picture: it decodes, is the size asked for, and is not one flat colour. I will write it failing first, run it on an Apple Silicon Mac against a locally compiled stage, wire it intobuild-sdcpp.ymlafter the floor gates, and put the Linux and macOS runners' wall-clock cost from the PR's own run in the description.MODSetter commentedon Oct 7, 2026
Fixed by #2159, merged into
dev, by @ybai08.build-sdcpp.ymlnow starts the staged sd-server with the app's own launch arguments and has it generate a real image from a pinned, sha256-checked Stable Diffusion 1.5 model on Linux and macOS. The image has to decode, be the requested size and not be one flat colour, and a failure stopsPackand the release that depends on it. The test is in the Packaging tests table.The Known gaps line this issue quoted is deleted in the same PR. Closing by hand: GitHub only auto-closes on a merge to the default branch, and we merge through
dev.