Files
jellytau/CONTRIBUTING.md
dtourolle 6897b290ed docs: add the governance and CI-operations files the project never had
The repo had no SECURITY.md, CONTRIBUTING.md, code of conduct, or issue
and PR templates. For a client that handles Jellyfin credentials and
ships signed binaries, the missing one that actually matters is
SECURITY.md: there was no stated way to report a vulnerability
privately, so the only available channel was the public tracker.

CONTRIBUTING.md documents the gates as they now stand, including the
three ratchets and which direction each is allowed to move, and the two
rules that surprise people: bug fixes start with a failing test, and
Jellyfin's taxonomy stays in Rust.

The bug template asks the three playback questions -- streaming or
downloaded, transcoding or direct, music or video -- because those
answers decide which of several very different code paths a report is
about, and reconstructing them over several round trips is most of the
cost of a playback bug report.

docs/build/ci-operations.md is the missing operations manual: how to
change the builder image and in what order (image pushed before the
workflow that names it, or CI breaks), why tags are dated rather than
:latest or per-SHA, what each secret is for, and what losing the updater
private key would mean -- installed desktop clients only accept payloads
signed by the key matching the public key they shipped with, so losing it
means everyone reinstalls by hand.

Disk exhaustion on the runner is documented as a manual check rather than
a scheduled job. A daily job would occupy the only slot on a single-slot
runner and pull the whole builder image to run `df` -- and `df` inside a
container does not reliably describe the host's disk, so it would spend
real build capacity reporting a number that might be wrong. What the doc
records instead is the part that is actually hard to rediscover: the
symptoms (cargo dying mid-link, docker refusing to pull, actions/cache
quietly not saving) and that `docker volume prune` needs `-a` to touch
named volumes, which is how it filled up unnoticed.

Two things in these docs are stated plainly because they are true and
were not written down anywhere: without branch protection every gate in
the pipeline is advisory, and the Gitea instance -- canonical remote,
signing secrets, registry, runner -- is not backed up by anything in this
repository.
2026-08-21 18:45:40 +02:00

5.2 KiB

Contributing to JellyTau

Thanks for looking. This file is the short version of how the project is built and what has to be true before a change lands. The long version lives in CLAUDE.md and docs/architecture/, which are maintained rather than decorative — read them before a structural change.

Getting set up

Package manager is bun. You will also need a Rust toolchain (the exact version is pinned in src-tauri/rust-toolchain.toml — rustup honours it automatically) and the Tauri Linux dependencies.

bun install
bun run hooks:install    # do this once: it enables the pre-commit gates
bun run tauri dev

hooks:install points core.hooksPath at scripts/hooks/, so hook updates arrive with a git pull instead of needing a re-install.

What has to pass

Everything below runs in CI, and the fast half runs in the pre-commit hook. None of it is advisory:

bun run check          # svelte-check — 0 errors
bun run test           # vitest
bun run format:check   # prettier
bun run lint           # eslint — 0 errors; the warning count is a ratchet
bun run check:boundary # no Jellyfin taxonomy in the frontend
bun run test:rust      # cargo test
cd src-tauri && cargo fmt --all && cargo clippy --all-targets -- -D warnings
cd src-tauri && cargo deny check          # advisories, licences, bans, sources

bun run test:all runs the whole set.

Several of these are ratchets — a number that only ever moves in the improving direction:

Ratchet Where Rule
eslint --max-warnings .gitea/workflows/build-and-test.yml only goes down
Coverage thresholds vitest.config.ts only go up
Traceability coverage .gitea/workflows/traceability-check.yml only goes up

Never relax one to make a build pass. Fix the thing it caught.

The two rules that surprise people

1. Bug fixes start with a failing test. Write a test that reproduces the bug and watch it fail before you touch the fix. A test written against already-fixed code can pass for the wrong reason and guards nothing. If the logic is trapped in a component, extract the pure part into a plain .ts module and test that — see episodeStrip.ts or TrackList.logic.ts for the pattern.

2. Domain vocabulary lives in Rust. The frontend is presentation-only. It must not encode Jellyfin's taxonomy — for example, the set of item types that makes up a category like "Music". Send an opaque scope across the IPC boundary and let the backend expand it. bun run check:boundary is a tripwire, not a proof: it only flags item-type array literals, so a green run does not mean you are clear. docs/specs/scoped-search-boundary.md describes the leak that made this a rule.

Traceability

Code that implements a requirement carries a TRACES: comment naming the requirement IDs, and a tool builds the matrix from those comments:

/// TRACES: UR-005 | DR-001

Every ID must exist as a row in docs/requirements.mdbun run traces:validate fails on a typo or a stale rename. Internal helpers and requirement-less code stay untraced; do not sprinkle IDs to raise the number.

If you add a requirement, add its row. If you implement one, tag the code.

Commits and pull requests

  • Conventional-commit subjects: fix(player): …, feat(updater): …, ci: ….
  • Explain why in the body, not what the diff already shows. The commit log is the main record of why things are the way they are here, and it is used to draft release notes.
  • One concern per commit. A formatting sweep and a behaviour change in the same commit is unreviewable.
  • Rebase rather than merge-commit onto master.

Specs

New features start from docs/specs/SPEC-TEMPLATE.md. Its "Layer assignment" section is the point: each piece of logic gets placed in Rust or the frontend with a reason. Review against docs/specs/SPEC-REVIEW-CHECKLIST.md. Do not frame a spec around "no Rust changes required" — correct placement is the goal, not minimal backend churn.

CI

CI is Gitea Actions (.gitea/workflows/), not GitHub.

🔴 CI installs no system tools. Every build, test and packaging tool must already be in the Docker builder image. If a job needs a tool the image lacks, add it to Dockerfile.builder, rebuild and push the image, and pin the new tag — do not apt-get it at job time. Details in docs/build/ci-operations.md.

Fetching the project's own declared dependencies (bun install, cargo crates, an advisory database) is not a toolchain install and is fine.

Reporting bugs

Use the issue templates. For anything involving playback, include what the platform was, whether the media was streaming or downloaded, and whether it was transcoding — those three answers determine which of several code paths you were actually on.

Security issues go to SECURITY.md, not the tracker.