Repository navigation
Upgrade dependencies, add paws CI, and specify the transcription/GPU roadmap - #22
Conversation
Bump every direct crate to its latest compatible release and refresh both lockfiles. Major upgrades needing source changes: - reqwest 0.12 -> 0.13: the `rustls-tls` feature was renamed `rustls`. - songbird 0.5 -> 0.6: `decode_sample_rate`/`decode_channels` are gone; both now live in a `DecodeConfig` carried by `DecodeMode::Decode`. - poise 0.6 -> 0.7: `event_handler` takes `(FrameworkContext, &FullEvent)` instead of four arguments; ctx and data come off the framework context. Held back deliberately: - whisper-rs stays at 0.15.1. 0.16 requires whisper-rs-sys ^0.15, but vendor/whisper-rs-sys is 0.14.1 and is patched in globally, so the bump needs whisper.cpp re-vendored first. - symphonia stays at 0.5.5. songbird 0.6 pins symphonia ^0.5.2, so 0.6 would duplicate the crate and break type sharing. uv.lock refreshed as well (torch 2.9.1 -> 2.14.0, CUDA 12 -> 13 wheels). Verified with cargo check --all-targets, clippy -D warnings, cargo test, cargo fmt --check, and uv lock --check. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The repo had no workflows at all. Scaffolded with `paws workflow generate`, which detected the rust, python, and docker ecosystems, then wired up for real publishing: - builds and tests both the Rust and Python halves via `paws ci` - publishes the image to ghcr.io/mbround18/hammock, authenticating with the workflow's own github.token - tags with --with-latest, --tag-rollup, --tag-branch and --tag-pr, and passes PR labels through so the canary-label gate works Pushes happen on a push to main or a tag; every other build is build-only unless the PR carries the `canary` label. Image name taken from compose.yml, pointed at GHCR. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Ran `specify init --here --integration claude`, which scaffolds .specify/ with the spec/plan/tasks/checklist templates, the helper scripts those commands call, and the workflow registry. The agent skills it installs under .claude/ are machine-local tooling, and the CLI warns that agents may leave credentials in that directory, so .claude/ is gitignored rather than committed. .specify/feature.json is likewise excluded, by the .gitignore the CLI ships, since it is a per-checkout pointer to whichever feature you are currently working on. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Ratify the constitution at v1.0.0. Its five principles are drawn from how this codebase actually fails rather than from generic best practice: the 20ms voice tick path must never block and must shed load explicitly; anything that can move transcript output requires a fixture comparison; CPU stays a first-class target and GPU unavailability falls back rather than failing startup; tunables are env vars with CPU-safe defaults; and an error path that neither counts nor logs does not exist. Specify the four transcription upgrades, in dependency order: - 001-gpu-accelerated-builds: the cuda feature exists but the published image is built without it on a base with no GPU toolchain, so the GPU path is unreachable code. Publish an accelerated variant, keep the CPU variant first-class, make fallback diagnosable. - 002-transcription-worker-throughput: chunks are processed strictly one at a time with a fresh decoder state each, and a full queue blocks submission inside the voice event handler, losing audio for the whole channel. Concurrent workers, reused state, non-blocking submission with counted load shedding. - 003-vad-utterance-segmentation: audio is cut at a fixed sample count regardless of speech, severing words and discarding context. Derive boundaries from speech activity instead. - 004-transcription-decoding-quality: decoding runs at its least accurate settings. Larger model, multi-candidate decoding, confidence fallback, context carryover, vocabulary hinting. Each spec carries a validated requirements checklist recording what failed review and what changed. Two requirements came out of that review rather than the original analysis: 002 FR-003, because reusing decoder state risks one speaker's residue leaking into another's transcript while every throughput criterion still passes; and 004 FR-013, because confidence fallback re-decodes the same audio and needs a per-utterance bound or an accuracy win can push the pipeline permanently behind real time. All four specs depend on an audio fixture set with reference transcripts that does not exist yet. Creating it is assigned to 001 and flagged as an open dependency in every checklist. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The first CI run failed immediately:
Error: provisioning failed for: python: failed to spawn `uv`
— is it installed and on PATH?
`paws ci` provisions a host toolchain for every ecosystem it detects in
the repo, not only the one named by --toolchain. This repo has both a
Cargo.toml and a pyproject.toml, so `paws ci --toolchain rust` provisions
python too, which shells out to `uv`. ubuntu-latest ships rustup but not
uv, so the rust step failed before building anything and both later steps
were skipped.
`paws provision` cannot fix this itself — it uses uv to install a python
version, so uv has to already exist. Confirmed by reproducing both the
failure and the fix locally against a PATH matching the runner's.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Changes recommended
The new CI workflow references PR-only context (github.event.pull_request.labels) on push/tag events and can fail those runs unless guarded.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR refreshes the Rust dependency set, introduces the repo’s first GitHub Actions CI workflow (via paws), and adds Spec Kit scaffolding plus a set of roadmap specs for GPU/transcription improvements.
Changes:
- Bump direct Rust crates (notably reqwest/songbird/poise majors) and update
src/main.rsfor the new APIs. - Add a
paws-generated CI workflow to run Rust/Python checks and build/publish images to GHCR. - Initialize Spec Kit project scaffolding and add feature specs/checklists for the transcription/GPU roadmap.
File summaries
| File | Description |
|---|---|
src/main.rs |
Updates Songbird decode config and Poise event handler signature for upgraded dependencies. |
Cargo.toml |
Refreshes direct crate versions and reqwest feature flags. |
.github/workflows/paws.yml |
Adds CI workflow for Rust/Python + docker build/publish via paws. |
.gitignore |
Ignores .claude/ and normalizes .venv/ entry. |
specs/001-gpu-accelerated-builds/spec.md |
Adds spec describing publishable GPU-capable image variant requirements. |
specs/001-gpu-accelerated-builds/checklists/requirements.md |
Adds requirements-quality checklist for spec 001. |
specs/002-transcription-worker-throughput/spec.md |
Adds spec for throughput/concurrency + load shedding constraints. |
specs/002-transcription-worker-throughput/checklists/requirements.md |
Adds requirements-quality checklist for spec 002. |
specs/003-vad-utterance-segmentation/spec.md |
Adds spec for speech-aware segmentation and associated metrics. |
specs/003-vad-utterance-segmentation/checklists/requirements.md |
Adds requirements-quality checklist for spec 003. |
specs/004-transcription-decoding-quality/spec.md |
Adds spec for decoding-quality improvements (fallbacks, thresholds, context). |
specs/004-transcription-decoding-quality/checklists/requirements.md |
Adds requirements-quality checklist for spec 004. |
.specify/workflows/workflow-registry.json |
Registers bundled Spec Kit workflow metadata. |
.specify/workflows/speckit/workflow.yml |
Adds the “Full SDD Cycle” workflow definition (specify→plan→tasks→implement). |
.specify/templates/tasks-template.md |
Adds Spec Kit tasks template. |
.specify/templates/spec-template.md |
Adds Spec Kit spec template. |
.specify/templates/plan-template.md |
Adds Spec Kit plan template. |
.specify/templates/constitution-template.md |
Adds Spec Kit constitution template. |
.specify/templates/checklist-template.md |
Adds Spec Kit checklist template. |
.specify/scripts/bash/setup-tasks.sh |
Adds Spec Kit helper for tasks setup and template resolution. |
.specify/scripts/bash/setup-plan.sh |
Adds Spec Kit helper for plan setup. |
.specify/scripts/bash/resolve-template.sh |
Adds Spec Kit helper for resolving templates (with JSON mode). |
.specify/scripts/bash/create-new-feature.sh |
Adds Spec Kit helper to create numbered/timestamped feature directories. |
.specify/scripts/bash/common.sh |
Adds shared Spec Kit bash utilities (repo root detection, template stacking, JSON escape, etc.). |
.specify/scripts/bash/check-prerequisites.sh |
Adds consolidated Spec Kit prerequisites checker (paths/docs/templates). |
.specify/memory/constitution.md |
Adds the project constitution (principles + CI/quality gates + governance). |
.specify/memory/.constitution-template.json |
Records constitution template provenance hash/source. |
.specify/integrations/speckit.manifest.json |
Records installed speckit integration file hashes/metadata. |
.specify/integrations/claude.manifest.json |
Records installed claude integration file hashes/metadata. |
.specify/integration.json |
Stores Spec Kit integration selection/settings. |
.specify/init-options.json |
Stores Spec Kit init options (integration/version/etc.). |
.specify/.gitignore |
Ignores machine-local Spec Kit state (feature.json) and local extension overrides. |
Review details
- Files reviewed: 31/34 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
`astral-sh/setup-uv@v10` does not resolve — the action publishes only
exact version tags (v10.0.1, v10.0.0, v9.0.0, ...) with no floating major
alias, so the workflow failed during "Set up job" before running a step:
Unable to resolve action `astral-sh/setup-uv@v10`,
unable to find version `v10`
Pinned to the v10.0.1 commit instead. A SHA is the right pin for a
third-party action that installs a toolchain, and Renovate tracks the
trailing version comment to keep it current.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Keeps the Copilot Autofix's goal — PR label names are attacker-controlled
on a fork PR and must not be interpolated into a run script — while fixing
two problems with how it got there.
The canary gate could never fire. The collect step wrote PAWS_PR_LABELS to
$GITHUB_ENV, but the docker step also declared `PAWS_PR_LABELS: ""` in its
own env block, and a step's env takes precedence over anything a previous
step exported through $GITHUB_ENV. The variable was therefore always empty
at the point it was read, so --labels received nothing and a `canary`
label would have been silently ignored.
The injection was moved rather than removed. The collect step still
expanded ${{ join(...) }} inside a double-quoted `echo`, where a label
named `$(...)` or containing backticks is still command-substituted by the
shell before echo runs.
Setting the value directly in the docker step's env fixes both: the
expansion happens when the environment is built rather than inside a
shell, nothing overrides it, and the run script only references
"$PAWS_PR_LABELS". The separate collect step is no longer needed — on a
non-PR event `join` yields an empty string, which is what the `if` guard
was there to produce.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
🔵 Needs a closer look
The CI workflow references a mutable @main branch for a third-party action and should be pinned to a tag/SHA for reproducibility and supply-chain safety.
Review details
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
.github/workflows/paws.yml:20
- The workflow pins
mbround18/paws/actions/paws-upto themainbranch, which is mutable and can change CI behavior (or be compromised) without any change in this repository. Pinning to a tag or commit SHA makes CI runs reproducible and reduces supply-chain risk.
- Files reviewed: 31/34 changed files
- Comments generated: 0 new
- Review effort level: Lite
There was a problem hiding this comment.
🔵 Needs a closer look
CI uses a floating GitHub Action ref (@main) for a third-party action, which should be pinned to a tag or commit SHA to avoid supply-chain drift.
Review details
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
.github/workflows/paws.yml:20
- This workflow uses a floating ref for a third-party action (
mbround18/paws/actions/paws-up@main). Using a branch ref means the CI behavior can change without any change in this repo, and it also weakens supply-chain guarantees compared to pinning to a tag or (preferably) a commit SHA.
- Files reviewed: 31/34 changed files
- Comments generated: 0 new
- Review effort level: Lite
`paws ci --toolchain rust` cannot build this project. It runs the pipeline
in a bare rust:1-bookworm container with no system packages, and the build
died in libopus_sys' build script:
cargo:info=`pkg_config` could not find `Opus`.
cargo:info=Building Opus via CMake.
failed to execute command: No such file or directory (os error 2)
is `cmake` not installed?
Both native dependencies need a C/C++ toolchain that image lacks:
libopus_sys builds Opus via CMake when pkg-config can't find it, and
whisper-rs-sys needs cmake plus libclang for bindgen. These are exactly
the packages this repo's own Dockerfile installs.
paws offers no hook for a consumer repo to add system packages to the ci
pipeline — the only alternate image it can use is the embedded
builders/rust one behind --coverage, which is the same base plus
cargo-llvm-cov and no closer to what this project needs.
So the Rust gates run natively on the runner: the same four checks paws
would have run (fmt --check, clippy -D warnings, build, test), with
--locked added, which also closes the lockfile-drift gap the constitution
asks for and paws' pipeline does not cover. Python and docker still go
through paws, and the docker step continues to compile the release binary
inside the project's own Dockerfile.
Worth raising upstream: paws' rust pipeline can only build projects with
no native dependencies.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
🔵 Needs a closer look
The CI workflow likely breaks on push/tag events due to join(github.event.pull_request.labels.*.name, ',') being evaluated when pull_request is absent.
Review details
Suppressed comments (3)
Previously missed (1) — in code that hasn't changed since the last review.
.github/workflows/paws.yml:13
- Workflow-level
packages: writeapplies to all events (including pull_request). To follow least-privilege, consider splitting image publishing into a separate job that runs only when publishing is intended (push to main/tags and/or explicitly labeled canary) and grantpackages: writeonly to that job.
This issue also appears in the following locations of the same file:
- line 20
- line 64
.github/workflows/paws.yml:64
${{ join(github.event.pull_request.labels.*.name, ',') }}is evaluated on push/tag events too, wheregithub.event.pull_requestis absent;join()expects an array and can error on null/non-array values, which would break main/tag CI runs.
PAWS_PR_LABELS: ${{ join(github.event.pull_request.labels.*.name, ',') }}
.github/workflows/paws.yml:20
mbround18/paws/actions/paws-up@mainis pinned to a moving ref. If that action changes unexpectedly (or is compromised), CI behavior can change without any change in this repo; prefer pinning to a tag or full commit SHA.
- uses: mbround18/paws/actions/paws-up@main
- Files reviewed: 31/34 changed files
- Comments generated: 0 new
- Review effort level: Lite
That release includes "provision only the toolchain that was actually asked for", which fixes the failure from the first CI run: `paws ci --toolchain rust` was provisioning python as well, purely because a pyproject.toml exists, and dying when uv was not on PATH. The setup-uv step stays, but for its own reason now rather than as a workaround for the rust step: `paws ci --toolchain python` genuinely needs uv on the host, and paws cannot bootstrap it, since it uses uv to install a python version. Comment updated to say that instead. This release does not affect the cmake failure. It changes only paws-cli-core and paws-provision; paws-rust still runs the pipeline in a bare rust:1-bookworm with no C/C++ toolchain, so the native Rust gates added in the previous commit stay as they are. Pinned to the exact prerelease rather than the action's `latest` default, so a future paws release cannot change this repo's CI without a commit. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
🟢 Approval recommended
The dependency-related code updates match the described upstream API changes, CI gates are in place, and the added Spec Kit/spec documents do not introduce runtime behavior changes.
Review details
- Files reviewed: 31/34 changed files
- Comments generated: 0 new
- Review effort level: Lite
`paws ci --toolchain python` runs `uv sync --frozen`, `uv build`, then
`uv run pytest`. The first two passed; the third failed:
error: Failed to spawn: `pytest`
Caused by: No such file or directory (os error 2)
That pipeline is for a Python distribution, and this repo is not one.
pyproject.toml exists only to pin openai-whisper into the runtime venv, so
there is no pytest dependency and no Python test suite — adding pytest
would just move the failure to exit code 5, "no tests collected".
`uv build` was a symptom of the same mismatch: it packaged src/, which is
the Rust source tree, into a hammock-0.1.0-py3-none-any.whl that means
nothing and nothing consumes.
What is worth gating is that uv.lock still agrees with pyproject.toml,
which `uv lock --check` verifies in about a second. Real installability is
already covered by the Docker build, which runs `uv sync --locked` against
the same lockfile.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Changes recommended
The CI workflow references a third-party GitHub Action via a mutable @main ref, which should be pinned (preferably to a commit SHA) to avoid supply-chain and reproducibility risk.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
- Files reviewed: 31/34 changed files
- Comments generated: 1
- Review effort level: Lite
| - uses: mbround18/paws/actions/paws-up@main | ||
| with: | ||
| version: v0.0.1-prerelease.43 |
`paws docker` builds through Dagger, and Dagger could not convert this
Dockerfile at all:
failed to convert Dockerfile LLB to Dagger ID: llbtodagger:
unsupported op "file.mkdir": mkdir without makeParents is unsupported
Bisected to one construct — a cache mount carrying uid=/gid=:
cache mount with uid=/gid= FAIL
same mount without uid=/gid= OK
USER bot + mount without uid=/gid= OK
pre-created dir, then uid=/gid= FAIL
BuildKit emits a file.mkdir without makeParents to set up an
ownership-scoped cache mount, and Dagger's converter cannot represent that
op. The uid=/gid= were there because the step runs as `bot`, who otherwise
cannot write to a root-owned mount. The two apt cache mounts are fine —
they have no uid=/gid=.
Removing it does not change the image. Cache mounts never become part of
the result, so this only forgoes partial uv download reuse on builds where
uv.lock itself changed; when the lockfile is unchanged the whole step
still hits the layer cache.
Verified locally: `dagger core host directory --path=. docker-build
--dockerfile=./Dockerfile sync` reproduced the failure with the identical
op hash beforehand, and exits 0 after.
Worth reporting upstream to Dagger — an ownership-scoped cache mount is
ordinary Dockerfile usage for any image that builds as a non-root user.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Changes recommended
The CI workflow uses a third-party action pinned to @main rather than an immutable ref, which is a supply-chain/reproducibility risk.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
- Files reviewed: 32/35 changed files
- Comments generated: 1
- Review effort level: Lite
| steps: | ||
| - uses: actions/checkout@v7 | ||
|
|
||
| - uses: mbround18/paws/actions/paws-up@main |
Four independent commits: a dependency refresh, the repo's first CI workflow, Spec Kit init, and specs for the transcription/GPU work.
1. Dependency upgrades (
39d6a2e)Every direct crate bumped to its latest compatible release, both lockfiles refreshed.
Three majors needed source changes:
rustls-tlsfeature renamedrustlsdecode_sample_rate/decode_channelsremoved; both now live in aDecodeConfigcarried byDecodeMode::Decodeevent_handlertakes(FrameworkContext, &FullEvent)instead of four argsTwo held back deliberately:
whisper-rs-sys ^0.15, butvendor/whisper-rs-sysis 0.14.1 and patched in globally. Needs whisper.cpp re-vendored first.symphonia ^0.5.2; 0.6 would duplicate the crate and break type sharing.Verified locally:
cargo check --all-targets,clippy -D warnings,cargo test,cargo fmt --check,uv lock --check.2. paws CI workflow (
d37a8e9)The repo had no workflows at all. Scaffolded with
paws workflow generate(detected rust + python + docker), then wired for real publishing toghcr.io/mbround18/hammock.Pushes on a push to
mainor a tag; every other build is build-only unless the PR carries thecanarylabel.3. Spec Kit init (
004510f).specify/scaffold committed..claude/is gitignored — the agent skills there are machine-local and the CLI warns agents may leave credentials in that directory. A fresh clone re-runsspecify init --here --integration claudeto restore the slash commands; the specs and templates travel with the repo regardless.4. Constitution + specs (
d77b03c)Constitution ratified at v1.0.0, with principles drawn from how this codebase actually fails rather than generic best practice: the 20ms voice tick path must never block and must shed load explicitly; anything that can move transcript output requires a fixture comparison; CPU stays first-class and GPU unavailability falls back rather than failing startup; tunables are env vars with CPU-safe defaults; an error path that neither counts nor logs does not exist.
Four specs, in dependency order:
cudafeature exists but the published image is built without it, on a base with no GPU toolchain. The GPU path is currently unreachable code andWHISPER_USE_GPUdefaults off.Two requirements came out of checklist review rather than the original analysis, both cases where a feature could pass all its own criteria while silently regressing something else:
Reviewer notes
main/tag pushes, so the docker step builds only here. The first real publish happens on merge — worth watching that one.🤖 Generated with Claude Code