The code-organization rules the recent refactor worked to were written in no tracked doc. docs/go-conventions.md gains them: - "Where a rule and an act live": one function for a rule a page shows and an endpoint enforces (plans.Offer); one statement for a rule SQL and Go both need (core.superseded_ends); a many-row act in the package that owns its tables (entitlements.RemoveTier, ReorderTiers, PlanActError); a handler that parses, calls, maps and renders; a read page that loads, derives, builds and renders with one name resolver per request; and a failed read is never an answer. The handlers that still open a transaction or run SQL are #221, and the reads that still turn a failure into a value are #207 and #197. - "Output order": a list query ends on a unique key (#225 lists the ones that do not), a loop that writes rows walks a stated order (#218), and Go output from a map ranges over sorted keys. - No call to Stripe while a transaction is open. The transactions section now says new code uses the helpers and points to #186 for the BeginTx calls that remain. docs/operator-ux-conventions.md's failed-collection rule links the general one. docs/testing.md: - Characterize before a refactor, and what a refactor commit may change. - The deliberate-break rule covers new tests and tests whose names or comments state a value, names all six outcomes the break tool reports, and says how to read a MISSED break. - Tests of locks and of order are deterministic; a flaky test is a suspected defect; a database test passes twice without a reset and filters to its own rows. - Golden files: defined order, rows of the test's own fixture, an expected change edited in by hand before the run, `-update` one package at a time. - A helper joins the test kit with its first caller and a test. CONTRIBUTING.md: the Go and test conventions point to their pages; docs move with the code; a written rule names its check; a script that resets or deletes data refuses unknown arguments; "Before you push" runs `make test`, since without the stack the database and Stripe tests skip; a commit message reads from the repository alone; new source files carry the SPDX header; and a small fix rides along only when listed, while a second sighting, a new rule or lock, or two statements of one rule get their own issue. docs/README.md: one page per rule, and the Go conventions entry lists what the page now covers.
6.9 KiB
Contributing to member-console
Thanks for your interest in improving the member console. This project is developed by and for the Wiki Cafe MSC multi-stakeholder co-operative, and contributions from the wider community are welcome.
The canonical repository lives on Gitea at
git.coopcloud.tech/wiki-cafe/member-console. Issues, discussion, and pull
requests happen there. That address is also the Go module path in go.mod, so
moving the canonical host rewrites every import in the tree; such a move has to
land before the first release tag, never after one.
Getting set up
Follow the Quickstart in the README to
bring up the test stack and run the console from source. The full stack contract
(ports, secrets, teardown, running multiple worktrees at once) is documented in
test/AGENTS.md.
Building and testing
Compile the binary to ./member-console:
make build
Run the unit tests — database- and Stripe-backed tests self-skip without a database:
go test ./...
For the full suite, point TEST_DATABASE_URL at a migrated Postgres (the test
stack works). docs/testing.md covers what each kind of test
exercises and how to run them. After a schema change, regenerate the sqlc query
code with make sqlc-generate (see
docs/database-management.md).
How changes are made
- Open an issue first for anything non-trivial, so the approach can be discussed before code is written. Pick the form that matches what closes the issue (a bug, an enhancement, debt, a design question, or docs) and answer its fields; the tracker refuses a blank issue. Write it for someone who has never opened this repository: a title that reads on its own, the console's own terms explained once where they first appear, and no internal shorthand (milestone codes, decision numbers, change names as explanations, dates as narrative) and no deployment's counts or names. Questions go to the forum, not the tracker. A small fix found on the way may ride along with another change when its pull request lists it. A problem seen a second time, one that needs a new rule, lock or invariant, or one where two parts of the code state one rule differently gets its own issue and its own change.
- Substantial or behavioral changes go through OpenSpec.
Proposals live under
openspec/and move throughproposal → specs → design → tasksbefore implementation, then are archived once shipped. Docs-only and small mechanical fixes don't need a proposal. - Branch, implement, and open a pull request against
main. Keep each PR focused on one change.
Conventions
These are enforced by review and, where possible, by tooling. Please run the checks below before opening a PR.
- Strict CSP. The app runs under
script-src 'self'/style-src 'self'with nounsafe-inline. Never add inline<script>blocks orstyle=attributes — put JavaScript and CSS ininternal/embeds/static/and reference it via<script defer src>/<link>. - Template rendering must go through
h.Templates.Render(). Handlers must not write to thehttp.ResponseWriterdirectly or call.ExecuteTemplate(w, …).make lint-templatesenforces this. - Every routed URL needs a handler, and every operator mutation route needs a
walkthrough claim.
go run . lintchecks both (dead-routeandwalkthrough-coverage). - Database. Schemas are owned per module under
internal/<module>/. Write migrations (goose) and queries, then regenerate withmake sqlc-generate. See docs/database-management.md. - Go code follows docs/go-conventions.md: which
errors may go unchecked, where a rule and an act live, transactions and
locks, and output order.
make lintenforces what a tool can check. - Tests follow docs/testing.md: data built through the test kit, HTML selected by role and attributes, and a refactor characterized before it starts.
- Docs move with the code. A commit that moves, renames or changes code
that a page under
docs/names corrects that page in the same commit.docs/database-locks.mdand the model cards name functions and queries, so they are the pages a change most often affects. - A written rule names its check. A sentence in a doc that says something is refused names the check or test that refuses it, or says that nothing checks it yet.
- A script that resets or deletes data refuses arguments it does not take,
printing its usage on stderr and exiting 2 before it touches anything, as
test/reset-app-db.shdoes. - Match the surrounding code — naming, structure, comment density, and idiom.
Before you push
make test # every static check, a reset of the test databases, every package (test stack up)
make lint-templates # template-safety guard
gofmt -l $(git ls-files '*.go')
Without the test stack, go test ./... still runs, but every database and
Stripe test skips itself, and those hold most of the golden and trace tests.
A change to database code, templates or workflows needs make test.
Commit messages
Write clear, imperative-mood subject lines that describe the change ("Add operator plan-topology view", not "changes"). Keep the body focused on why. A message reads from the repository alone: it names no plan, task, note or section kept outside it, states no deployment's counts or names, and every number in it was measured on the commit.
License and the CLA
member-console is dual-licensed: AGPL-3.0-only for everyone, plus commercial
licenses that fund development (see COMMERCIAL.md). Keeping
that possible requires a Contributor License Agreement from every contributor
before their work can be merged.
The CLA leaves the copyright in your contribution with you while granting Christian Galo the right to license it under the AGPLv3 and under separate commercial terms. That grant is what lets the project offer a commercial option while staying open source by default. A Developer Certificate of Origin would not do the same job: it attests provenance and grants no relicensing right.
The agreement is CLA.md. To sign it, add one line to CLA-SIGNATORIES.md in a pull request from your own account: full legal name, email, date, and the CLA version. That commit is your signature, and it can travel in the same pull request as your first change. Nothing is merged from an unsigned contributor.
A new source file carries the SPDX license header: Go, SQL migrations and
seeds, and the templates, JavaScript and CSS under internal/ (the header of
scripts/spdx-headers.sh lists what it leaves out). scripts/spdx-headers.sh <path> stamps it, and make sqlc-generate restores it on generated code.