Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions .github/workflows/paws.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Generated by `paws workflow generate` — https://github.com/mbround18/paws
name: paws

on:
push:
branches: [main]
tags: ["v*"]
pull_request:

permissions:
contents: read
packages: write

jobs:
paws:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7

- uses: mbround18/paws/actions/paws-up@main
with:
version: v0.0.1-prerelease.43
Comment on lines +20 to +22

Comment thread
mbround18 marked this conversation as resolved.
# `paws ci --toolchain python` provisions python by shelling out to `uv`,
# and the runner does not ship it. paws cannot bootstrap uv itself — it
# uses uv to install a python version, so uv has to already exist.
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
enable-cache: true

# `paws ci --toolchain rust` runs in a bare rust:1-bookworm container with
# no system packages, so it cannot build this project: libopus_sys and
# whisper-rs-sys need cmake, clang and pkg-config — the same packages this
# repo's own Dockerfile installs. paws exposes no way for a consumer repo
# to add them (its --coverage builder image doesn't carry them either), so
# the Rust gates run natively here. Same four checks paws would have run,
# plus --locked.
- name: Install native build dependencies
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
build-essential pkg-config libssl-dev clang libclang-dev cmake

- run: cargo fmt --all -- --check
- run: cargo clippy --all-targets --locked -- -D warnings
- run: cargo test --locked --verbose

# Not `paws ci --toolchain python`: 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 `uv build` packages the
# Rust src/ tree into a meaningless wheel and `uv run pytest` fails —
# there is no pytest and no Python test suite to run. Lockfile agreement
# is the real gate; the Docker build exercises `uv sync --locked` for
# actual installability.
- run: uv lock --check

# Pushes on a push to main or a tag; other builds are build-only unless
# the PR carries the `canary` label.
#
# PR label names are attacker-controlled on a fork PR, so they reach the
# command through the environment instead of being interpolated into the
# run script. On a non-PR event `join` yields an empty string.
- run: >
paws docker
--image ghcr.io/mbround18/hammock
--with-latest --tag-rollup --tag-branch --tag-pr
--labels "$PAWS_PR_LABELS"
env:
GHCR_USERNAME: ${{ github.actor }}
GHCR_TOKEN: ${{ github.token }}
Comment thread
Copilot marked this conversation as resolved.
PAWS_PR_LABELS: ${{ join(github.event.pull_request.labels.*.name, ',') }}
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
.env
/models
/captions
.venv/
.venv/
.claude/
9 changes: 9 additions & 0 deletions .specify/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Machine-local Spec Kit state — not meant to be shared.
# Managed by the Specify CLI; safe to edit (your changes are preserved on refresh).

# Local pointer to the current feature directory. Rewritten every time you
# switch features, so it is per-checkout state rather than something to share.
feature.json

# Per-machine extension config overrides.
extensions/*/local-config.yml
9 changes: 9 additions & 0 deletions .specify/init-options.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"ai": "claude",
"ai_skills": true,
"feature_numbering": "sequential",
"here": true,
"integration": "claude",
"script": "sh",
"speckit_version": "1.0.4"
}
15 changes: 15 additions & 0 deletions .specify/integration.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"version": "1.0.4",
"integration_state_schema": 1,
"installed_integrations": [
"claude"
],
"integration_settings": {
"claude": {
"script": "sh",
"invoke_separator": "-"
}
},
"integration": "claude",
"default_integration": "claude"
}
17 changes: 17 additions & 0 deletions .specify/integrations/claude.manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"integration": "claude",
"version": "1.0.4",
"installed_at": "2026-09-08T22:17:13.791384+00:00",
"files": {
".claude/skills/speckit-analyze/SKILL.md": "72a6e6ff794e3099debe70e492433b94e6c8da57e49e03e8711b506ecfc3e608",
".claude/skills/speckit-clarify/SKILL.md": "f4b3f2c95087ac2343c0b67faff67f7223d34213ca1816aa25908db5b9aff0ac",
".claude/skills/speckit-constitution/SKILL.md": "a93047917a5fefeefff7aa35991109ce8f9938c4890e52206408b511bf756eb5",
".claude/skills/speckit-implement/SKILL.md": "51bd89322e0258ae377ea66a6af41a159b0e1a05304d5a17ea0f9a9baa6640a9",
".claude/skills/speckit-converge/SKILL.md": "6eca60f035306017d43afefd3a7a23ae448484b584080aa404ff65e3e3fdec0a",
".claude/skills/speckit-plan/SKILL.md": "2fe3f96886e96284965c8586d14df5d226c1e796b3eb39d110c9d8231abe6417",
".claude/skills/speckit-checklist/SKILL.md": "34c8c681f5472f2790d65ac29ca01e23f4f32ab6f06c9dca1b38fc89e2cfba86",
".claude/skills/speckit-specify/SKILL.md": "42fe016b9183bb8fa7ce7c65e04ea8d382f7f2abfc94849aeead999247675886",
".claude/skills/speckit-tasks/SKILL.md": "597853362a0a770fe967c5d56136e2db38de1aa7b97b89552db667b70ac493c1",
".claude/skills/speckit-taskstoissues/SKILL.md": "76a6ec1fcc2d4f2f4ae4d1a70e59eca034fd9a3f97c5454094b023ed3da65151"
}
}
19 changes: 19 additions & 0 deletions .specify/integrations/speckit.manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"integration": "speckit",
"version": "1.0.4",
"installed_at": "2026-09-08T22:17:13.805279+00:00",
"files": {
".specify/scripts/bash/check-prerequisites.sh": "daa377146db4fb69912f42611a7b91c5d55873b3e3e27ed33bd8d67505826344",
".specify/scripts/bash/setup-tasks.sh": "4a33dd1e6c32ddc7d570f4b538572c54069192573eed0ec654d9fb826e31a4fe",
".specify/scripts/bash/setup-plan.sh": "f417e1b8de7a48fa9d5fea3aabea8fe62149fa11c4b41b3103de06efe73dec8d",
".specify/scripts/bash/resolve-template.sh": "829e227096abc8bf0889889ec9f792f503ca5d395b7836a8a7eb739ee75214e7",
".specify/scripts/bash/create-new-feature.sh": "fe99ea8da184380056ce8512ca5d37f67fb499ee4234835c15dcf7c4fb75451c",
".specify/scripts/bash/common.sh": "170e91ece502b88d83c715e427473843e0b358f550e86e3ff524546df405a9ca",
".specify/templates/tasks-template.md": "fc29a233f6f5a27ca31f1aa46b596af6500c627441c6e62b2bc4a1d721525842",
".specify/templates/plan-template.md": "7e637502d41eccf0ca672496636365691fdca62ef37b27ec07fcb412dbfa90d4",
".specify/templates/constitution-template.md": "ce7549540fa45543cca797a150201d868e64495fdff39dc38246fb17bd4024b3",
".specify/templates/spec-template.md": "3945437fc35cd30a5b2bf7beea680337c3516826d3efa5a6b92c4a7eca1ba28e",
".specify/templates/checklist-template.md": "856532b3cb66171c662cc16f16b31a5856e4655a8666aad1e545bbfc7f603ca1",
".specify/.gitignore": "8c908410d177a1ef3d0dee16d7ad55f2ac3333df3104c4d4adee1c9b82f1dbc1"
}
}
4 changes: 4 additions & 0 deletions .specify/memory/.constitution-template.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"sha256": "ce7549540fa45543cca797a150201d868e64495fdff39dc38246fb17bd4024b3",
"source": "core"
}
162 changes: 162 additions & 0 deletions .specify/memory/constitution.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
<!--
Sync Impact Report
==================
Version change: (unversioned template) → 1.0.0
Rationale: Initial ratification. All template placeholders replaced with concrete
project governance; no prior version existed.

Modified principles: none (initial adoption)

Added sections:
- Core Principles I–V (Real-Time Voice Fidelity; Transcript Accuracy Is Measured,
Not Asserted; Hardware-Optional Parity; Configuration Over Recompilation;
Observable by Default)
- Additional Constraints (technology and deployment constraints)
- Development Workflow & Quality Gates
- Governance

Removed sections: none

Follow-up TODOs: none. RATIFICATION_DATE set to the repository's first commit date
(2025-11-30), treated as the project's adoption date.
-->

# Hammock Constitution

Hammock is a Discord voice bot that joins a voice channel, transcribes what each
participant says in near real time, and persists speaker-attributed captions.
These principles govern how it is changed.

## Core Principles

### I. Real-Time Voice Fidelity

The audio receive path is a hard real-time budget and must never be blocked by
downstream work. Songbird delivers a voice tick every 20 ms; any handler on that
path MUST complete without awaiting transcription, disk I/O, network calls, or an
unbounded queue.

When a downstream stage cannot keep up, the system MUST shed load explicitly —
drop the work, count the drop, and log it — rather than apply backpressure to the
receive path. Silently stalling the tick loop loses audio for every speaker in the
channel, which is strictly worse than losing one measured chunk.

Rationale: dropped audio is unrecoverable and invisible. Making overload explicit
is the only way it can be diagnosed or tuned.

### II. Transcript Accuracy Is Measured, Not Asserted

Transcript quality is the product. Any change that can plausibly alter transcript
output — model selection, decoding parameters, segmentation, resampling, VAD — MUST
be evaluated against a fixed, committed audio fixture set before and after, and the
comparison MUST be reported in the change description.

"Sounds better" is not evidence. A change that improves one metric while degrading
another MUST state the tradeoff explicitly.

Rationale: speech recognition parameters interact in non-obvious ways, and
regressions are easy to ship and hard to notice in production, where there is no
ground truth to compare against.

### III. Hardware-Optional Parity

GPU acceleration is an optimization, never a requirement. Every feature MUST work
on a CPU-only build, and the CPU path MUST remain a supported, tested
configuration.

When GPU acceleration is requested but unavailable — the feature was not compiled
in, no device is present, or initialization fails — the system MUST log the reason
at warning level and continue on CPU. It MUST NOT fail to start, and it MUST NOT
silently pretend the GPU is in use.

Rationale: contributors and self-hosters largely do not have CUDA hardware. A build
that only runs on a GPU is a build most users cannot run.

### IV. Configuration Over Recompilation

Operational behavior is configured through environment variables, not code edits or
build flags. Any newly introduced tunable MUST have a documented default that is
safe for a CPU-only self-hoster, MUST appear in `.env.sample` with a comment
explaining its effect, and MUST validate its input at startup with an actionable
error rather than failing later at use.

Build-time features (such as GPU backends) are reserved for things that genuinely
cannot be selected at runtime.

Rationale: the primary deployment is a container image. Anything that requires a
rebuild to change is effectively unavailable to the people running it.

### V. Observable by Default

Every failure, drop, fallback, and degraded mode MUST be both counted in metrics and
logged with enough context to identify the guild, channel, and speaker involved.

An error path that neither increments a counter nor emits a log does not exist as
far as an operator is concerned and MUST NOT be merged. Health and readiness
endpoints MUST reflect the actual ability to serve, not merely that the process is
alive.

Rationale: this system runs unattended in voice channels the maintainer is not
listening to. Undetectable degradation is indistinguishable from working.

## Additional Constraints

**Technology stack**: Rust (2024 edition) using serenity + poise for Discord,
songbird for voice, and whisper-rs for transcription. `whisper-rs-sys` is vendored
under `vendor/` and pinned via `[patch.crates-io]`; the vendored version and the
`whisper-rs` version MUST be kept compatible, and a bump to either requires bumping
both together.

**Audio contract**: audio reaches the transcriber as 16 kHz mono PCM. Resampling
introduced anywhere in the pipeline MUST be anti-aliased; naive decimation is not
acceptable.

**Deployment**: the deliverable is a container image published to GHCR. Both a
CPU-only image and any accelerated variant MUST be buildable from the same
repository, and the runtime image MUST NOT require a toolchain that is only present
in the build stage.

**Dependency hygiene**: `Cargo.lock` and `uv.lock` are committed and authoritative;
builds are `--locked`. Dependency upgrades that cross a major version MUST be
verified to compile, lint, and pass tests before merge.

## Development Workflow & Quality Gates

All changes pass through CI driven by `paws` (`.github/workflows/paws.yml`). The
following gates are mandatory and non-negotiable:

- `cargo fmt --check` — formatting is not a review topic.
- `cargo clippy --all-targets -- -D warnings` — warnings are errors.
- `cargo test` — the suite must pass.
- The container image must build.

Additional workflow rules:

- Feature work follows the Spec Kit flow: specification → plan → tasks →
implementation. Changes that alter transcript output or the real-time audio path
require a written spec; routine maintenance does not.
- All commits MUST be cryptographically signed.
- Changes touching the voice or transcription path MUST state their measured effect
on latency and on transcript accuracy, per Principle II.

## Governance

This constitution supersedes ad-hoc convention. Where a proposed change conflicts
with a principle here, the principle wins unless the constitution is amended first.

**Amendment procedure**: amendments are proposed as a change to this file,
accompanied by the rationale for the change and an assessment of what existing
behavior becomes non-compliant. An amendment is adopted when merged.

**Versioning policy**: this document is versioned semantically.
- MAJOR — a principle is removed or redefined in a backward-incompatible way.
- MINOR — a principle or section is added, or existing guidance is materially
expanded.
- PATCH — clarification, rewording, or typo correction that does not change meaning.

**Compliance review**: every review verifies that the change complies with these
principles. Complexity that violates a principle MUST be justified in writing in
the change description, or the change MUST be simplified. Deviations accepted
without justification are defects.

**Version**: 1.0.0 | **Ratified**: 2025-11-30 | **Last Amended**: 2026-09-08
Loading