Files
member-console/CONTRIBUTING.md
T
cgalo5758 4dad0c48c7 Write down where a rule and an act live, output order, and how to prove a test
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.
2026-10-15 20:56:59 -05:00

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

  1. 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.
  2. Substantial or behavioral changes go through OpenSpec. Proposals live under openspec/ and move through proposal → specs → design → tasks before implementation, then are archived once shipped. Docs-only and small mechanical fixes don't need a proposal.
  3. 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 no unsafe-inline. Never add inline <script> blocks or style= attributes — put JavaScript and CSS in internal/embeds/static/ and reference it via <script defer src> / <link>.
  • Template rendering must go through h.Templates.Render(). Handlers must not write to the http.ResponseWriter directly or call .ExecuteTemplate(w, …). make lint-templates enforces this.
  • Every routed URL needs a handler, and every operator mutation route needs a walkthrough claim. go run . lint checks both (dead-route and walkthrough-coverage).
  • Database. Schemas are owned per module under internal/<module>/. Write migrations (goose) and queries, then regenerate with make 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 lint enforces 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.md and 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.sh does.
  • 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.