The README was two documents in one, and neither reader was served by the other's half. It opened on a feature list, then spent its second half on Go versions, WebKitGTK packages and `make` targets — while its install table named a `darwin-universal.app.zip` and a `windows-amd64.exe` that nothing has ever produced, and its header claimed Windows and never mentioned Android, which is the one platform with a published, self-updating channel. So this is a correctness pass as much as a friendliness one. The README now answers a user's questions only: what the app is, three screenshots from the seeded fixture library so anyone can retake them, the four formats, one install section per channel that names what is actually published, first run, where the data lives, and pointers out. The version-restart note is linked to the two documents that own it rather than copied, because a copy is a second thing to keep true. CONTRIBUTING.md takes the technical half: prerequisites, the system libraries, the build and codegen commands, which verification tier a change demands, the tracker workflow, the commit grammar and the style rules. CLAUDE.md is unchanged apart from one paragraph naming the split — it was already the deep reference both of the others point at, and stays the only one of the three that explains why a shape is what it is. Closes #50
7.9 KiB
Contributing to YellowJacket
This is the contributor's half of the README: how to build it, how
to check a change, and how a change gets in. CLAUDE.md is the
deep reference — the architecture, and the reasons behind the shape of it —
and is worth reading before a change of any size, because most of this
codebase's traps are written down there and nowhere else.
Building from source
YellowJacket is Go with a Lit/TypeScript frontend, bridged by Wails v3.
| Tool | Version |
|---|---|
| Go | 1.25+ |
| Node.js | 22+ |
| pnpm | 10+ |
| Wails CLI | v3 — vendored, no install needed (go tool wails3) |
The Wails v3 CLI resolves from the tool block in go.mod, so there is nothing
to install globally; make setup fetches it with the rest of the tooling.
On Linux, install the system libraries Wails needs. v3 builds against GTK4 + WebKitGTK 6.0 by default:
sudo apt-get install libasound2-dev libgtk-4-dev libwebkitgtk-6.0-dev # Debian/Ubuntu
sudo pacman -S alsa-lib gtk4 webkitgtk-6.0 # Arch
A machine without webkitgtk-6.0 can still build with -tags gtk3 against the
older WebKit2GTK 4.1 stack, but that is an escape hatch, not what CI or a
release builds. macOS and Windows need no extra system packages. Run
go tool wails3 doctor to check your environment.
make setup # install tooling, frontend packages and the git hooks
make dev # run with hot-reload
make build-dev # debug build with symbols
make build-prod # production build (stripped and trimmed)
make android # the arm64 APK, into bin/
The Makefile is the front door and carries a one-line description against
each target; Taskfile.yml and build/<platform>/Taskfile.yml are the build
implementation behind it and are not called directly.
Generated code
Two generators run from go generate ./..., which make generate wraps:
sqlc turns backend/database/sql/queries/ into Go in
backend/database/sql/sqlcgen/, and templ turns .templ files into
*_templ.go beside them. Never edit either output by hand — run
make generate after touching a .sql or a .templ file.
The TypeScript bindings in frontend/bindings/ are generated by wails3
rather than by go generate, so they are a separate step: make bindings
regenerates them and make bindings-check fails if they are stale.
frontend/src/events.ts is generated too, from backend/events/events.go.
A pre-commit hook checks that all of this is fresh, so the usual way to meet it is a failing commit rather than a bug.
Checking a change
Run the tier the change actually demands, not the cheapest one.
| Change | Command |
|---|---|
| Go | make lint and make test — both cover all three build configurations |
| A frontend component or store | make ui-test (Vitest in a real Chromium, no backend) |
| A user-visible flow | make e2e, against a running make dev-headless |
| CSS | make css-check — see the Chrome 113 note below |
| Anything cosmetic | look at a screenshot; several bugs here were invisible to every assertion and obvious in an image |
make test needs the fixture library, which is generated rather than
committed — it runs make testdata itself (about a second).
The end-to-end tier drives the real app with no display at all: make dev-headless starts it in the background on :34115 (add SEED=<name> for a
seeded library, built by make sandbox-seed NAME=<name>), make dev-logs tails
it and make dev-stop stops it. Check the port before starting one — if
:34115 is already answering, someone else's app is there, and a green result
about their build is worse than no result.
Two smaller checks exist because the failure they catch is silent:
make bindings-check (stale generated bindings) and make css-check, which is
two passes — one fails on a css literal ended early by a backtick inside a
comment, the other on a nested CSS rule that begins with a bare element
selector. Chrome 113 is what the reference Android device renders with, and it
drops such a rule without a word.
make vulncheck runs govulncheck over the module.
The issue tracker is the source of truth
Work is described by issues before it is described by branches, and the tracker is shared with people who cannot see your terminal.
- Search before starting, closed issues included:
./scripts/issue.sh search <terms>. "That was fixed three weeks ago" is the cheapest possible answer. - Claim before the first edit, not before the commit:
./scripts/issue.sh claim <n>sets the assignee, appliesStatus/In Progressand comments with the branch, so the work is visibly taken while it is being done. It refuses if somebody else holds it — talk to them rather than working around it. - If no issue covers the work, open one first (
./scripts/issue.sh new). - Findings get filed. A bug tripped over on the way to something else is an issue with a reproduction, not a wider diff and not a sentence in a chat log.
- #73 is the roadmap and states the order the backlog should be worked in.
scripts/issue.sh is the whole interface (list, mine, search, show,
new, claim, unclaim, comment, close, label, depends, labels) and
wants a GITEA_TOKEN with write:issue. The labels are a taxonomy rather than
tags: Kind/*, Area/*, Priority/*, Platform/*, plus Reviewed/* and
Status/*, of which the last two are exclusive scopes.
Commits and pull requests
main is protected, so a branch and a PR are the only way in. Branch from
origin/main, and name the branch after the issue (fix/140-…, feat/25-…).
Commit subjects are Conventional Commits
— type(scope): subject, imperative, ≤72 characters — and are enforced by a
commit-msg hook and by CI (make commit-check). This is load-bearing rather
than decorative: semantic-release reads the type to decide the next version,
so a CI-only change is ci: and never fix(ci):, which would ship a patch
release. make release-dry prints what a release would cut right now.
The closing keyword goes in the commit body, one issue per line, because
Gitea parses commit messages that reach main and does not parse the PR body:
docs: rewrite the README as a landing page
<why>
Closes #50
A PR body carries a commit-to-issue table, the verification you actually ran
(with results), and a Closes list for whoever reads it.
Style
- Go — golangci-lint v2, strict:
err113(static errors),nlreturn,wsl_v5,godot,sloglint,perfsprint, and imports grouped stdlib → third-party →yellowjacket/…by gci. - TypeScript — strict mode, no implicit
any, no unused locals or parameters. - Match the surrounding code. Where
CLAUDE.mdexplains why something is shaped the way it is, that shape is load-bearing and there is usually a test pinning it.
Hooks do most of the enforcing (lefthook.yml, installed by make setup):
pre-commit runs vet, lint, the codegen checks, the frontend typecheck and the
two CSS checks in parallel; pre-push runs the Go suite and the UI tier,
deliberately one after the other rather than together.
Where the rest of the documentation is
CLAUDE.md— architecture and constraints, in depth.docs/PROFILING.md— Go pprof and frontend profiling.docs/android-release.md— the APK, its signing key, and what the release workflow checks.docs/index-cache.md— the search-index build cache and why it has a snapshot.packaging/arch/README.md,packaging/homebrew/README.md— the two package channels..planning/— design documents and measured history, not a queue. The queue is the tracker.