# Contributing to Maran Maran is source-available and commercial. You can read every line, run it, change it for yourself, and send changes back — the licence (BSL 1.1) is about selling it as a service, not about who may work on it. New here? Start with an issue labelled [`good first issue`](https://github.com/innovayse/maran/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22), and ask anything on the [Innovayse Discord](https://discord.gg/NfqTkyTgD). The rules below are not style preferences. They are the reason a root daemon written by several people is safe to run on somebody else's server. ## Before you write code Read [`rules/README.md`](rules/README.md). Every rule there is binding, and most of them are enforced by a command rather than by a reviewer's memory. The ones that surprise people most: - **Doc comments on all production code**, private members included. `CS1591` is an error. - **One file, one type.** The file name is the type name. - **No shell strings anywhere.** The agent runs as root; every operation is a typed proto RPC against an allow-list of absolute paths. There is no "run this command" call and there will not be one. - **Errors are values.** `Result` and a typed `Error`, not exceptions, for anything a caller can act on. - **The backend owns all user-facing text.** The SPA renders what the panel sends; it never invents an error message. ## Getting set up ```bash git clone https://github.com/innovayse/maran.git cd maran source scripts/dev # sourced, not run: it puts the toolchains and `maran` on your PATH maran check # tells you what your machine is missing maran dev # database, API and SPA together ``` `maran dev` refuses to start on a polygon image that was not built from your tree, and prints the fix: `maran polygon build ubuntu24` (docker/README.md). `source scripts/dev` also creates `.env` from `.env.example` on first use. That file is git-ignored and is the only place local configuration lives — edit it, not the tracked `appsettings.*.json`. Everything else runs through the one CLI. Type `maran` to see the whole toolbox. Do not call the scripts under `scripts/lib/` directly: they are implementations, `maran` is the surface. ## Branches ``` feature branch → dev → main ``` - **`main`** is the release branch. It is protected: no direct pushes, ever, including for maintainers. It only ever receives a reviewed pull request from `dev`. - **`dev`** is where work integrates, and is the default branch — a new pull request targets it without anyone having to remember to. - Branch from `dev`, name it for what it does (`feat/backup-restore`, `fix/quota-rounding`), and open a pull request back into `dev`. ## Before you open a pull request Run the gates. All of them are fast except the integration tests, and CI runs these, so finding out here is cheaper: ```bash maran check # is this machine able to build at all maran structure # file and folder laws no compiler can express maran format --check # formatting and naming maran proto # the API-to-agent contract maran api # SPA request bodies against the commands they bind maran migrate check # a model edited without a migration maran migrate guard # a migration that destroys what the last release reads maran lock selftest # the tree write-lock refuses AND proceeds maran update-selftest # panel self-update: verify, layout, unpack refusals maran licenses --check # third-party notices match the dependencies cd backend && dotnet test cd frontend && npm run lint && npm run typecheck && npm run build && npx playwright test maran agent check # fmt, clippy -D warnings, cargo test, cargo doc maran handshake # agent and API over a real unix socket ``` This list used to say "CI runs the same ones" while being four short of it — `maran api`, `maran migrate guard` and `maran lock selftest` were missing, so a contributor could pass every gate here and still fail CI on a gate they had no way to know about. It is still not identical in both directions: CI also scores the suites against a committed baseline (`maran test rust`, `maran test backend`, `maran test spa`) and runs the container polygons, neither of which is asked of a contributor. What CI actually invokes, which is what settles this list: ```bash grep -rn 'run:.*scripts/maran' .github/workflows/ ``` A toolchain error is a failure to verify, never a pass. "No tests found" is a failure too. ## Tests [`rules/testing.md`](rules/testing.md) is the authority. In short: - Test names are behaviour sentences: `Revoking_another_users_session_answers_404_rather_than_403`. `Test1` and `ItWorks` are review rejects. - Assert behaviour through the public surface. If a test needs a private member, the design is wrong — fix the design. - Every typed error variant of a feature appears in at least one test. - **Never change production code to make a test pass.** If a test is failing because the code is wrong, fix the code and say so. If it is failing because the test asserted the wrong thing, say that too — we have found four tests that were protecting defects, and each one was worth more as a report than as a green tick. - A new gate must be proved able to fail before you trust it. Break the thing on purpose, watch it go red, put it back. ## Changes that need more than a review [`rules/security.md`](rules/security.md) requires a **second reviewer and a threat note in the pull request description** for changes to: - authentication, sessions or token handling - the agent's `privs` module — the only place `unsafe` is allowed - licence verification - the installer's privileged steps The note answers one question: *what could an attacker do with this surface, and why is it safe now.* There is a worked example at [`docs/superpowers/notes/`](docs/superpowers/notes/). A note that lists only what is safe is not a threat note — name what you found and what you left open. ## Commits - English, in the imperative, explaining **why** rather than restating the diff. - One coherent change per commit. A 200-file commit called "updates" is a review reject. - No AI attribution trailers. ## Licensing of what you send Maran is under the Business Source License 1.1, which becomes Apache 2.0 on the Change Date stated in `LICENSE`. By opening a pull request you agree that your contribution is licensed on those same terms — the licence going in is the licence coming out, Change Date included. You keep the copyright in what you wrote. There is no copyright assignment and no separate agreement to sign. If your employer owns your work, make sure you have their permission before you send it. That is between you and them, but it is your responsibility, not ours. ## Reporting a security problem Do not open an issue. See [`SECURITY.md`](SECURITY.md). ## Questions Open an issue. A question that turned out to be a missing line in this file is a useful issue, not a waste of anyone's time.