Skip to content

Conventions and tests

The conventions below are not style preferences; each one is load-bearing for a security or operational property PEART commits to. A change that breaks one of them breaks a guarantee, so make check enforces the ones that can be enforced mechanically, and review enforces the rest.

The frontend has no build step, and no innerHTML

The interface is static pages served by go:embed, with Lit vendored under web/vendor/. There is no Node, no bundler, no transpile step. A contribution to the frontend is plain JavaScript and Lit templates that run in the browser as written. Keep it that way: the absence of a build pipeline is what lets the whole interface ship inside a single Go binary and be audited by reading the files that are actually served.

The Content-Security-Policy is strict, with no unsafe-inline and no unsafe-eval. That has a direct consequence for how you write components: no inline event handlers, no inline <style> or style= injection of untrusted values, and no innerHTML or Lit unsafeHTML in our own code. Render through Lit's templating, which escapes by construction. A component that needs to show user-supplied text binds it as text, never as markup. This is the single most important frontend rule, because it is what closes stored XSS at the rendering layer.

Migrations are forward-only

Database migrations are numbered and forward-only; there is no down-migration path. A schema change is a new migration file that moves the schema forward and is safe to apply to a live database. This keeps upgrades predictable and irreversible in the direction that matters, and it means you design a migration to be correct on the first apply rather than counting on a rollback that does not exist.

Errors are explicit, resources are deferred

Handle errors explicitly at the point they occur; do not paper over them to make a function read cleanly. Release every resource with defer at the point of acquisition. Avoid mutable package-level state. These are the ordinary Go disciplines, and the codebase holds to them so that behaviour under failure is the behaviour you can read off the code.

Tests are table-driven and parallel to the source

Every unit of code has tests, and complex logic uses table-driven tests covering the success and the failure paths. Test files sit beside the code they test (foo_test.go for foo.go). The browser tests drive the real interface in a headless Chrome and are part of the suite, not a separate optional tier; a change to a component is not done until its browser test is green. make check runs all of it.

Formatting and documentation are not optional

Code is gofmt-clean and stays within 120 columns. Every package has a package comment above the package declaration, and every exported identifier has a doc comment that is a full sentence beginning with the identifier's name. Comments explain why, in English, not what. make check fails on a gofmt diff, on an over-long line, and on the i18n-check gate that keeps the English and Portuguese message catalogues in lockstep, so run it before you send anything.