Contributing to Rotamer
Thank you for making it this far. This file is the path for anyone working on the code: how to start the environment, what must remain true, and how a change becomes a commit.
First: the nonnegotiable rules
Section titled “First: the nonnegotiable rules”They are in CLAUDE.md, and apply to everyone, with or without a coding assistant. The most important ones:
- The deterministic core decides. AI explains. Validity, valence, formula, mass, SMILES, InChIKey, descriptors, aromaticity, stereochemistry, normal-mode frequency, and mission scores always come from RDKit, the force field, or the mission engine. Never from a language model, and never from a homegrown calculation “for a quick fix.” A chemist finding a subtle error loses trust in the entire product.
- Code in English, text in Portuguese. File, variable, function, type, and data-key names are in English; comments, test names, interface strings, and error messages are in pt-BR. Errors explain the chemistry, not the code: “The C atom has 5 bonds, but supports at most 4.”
- CPK colors belong to atoms. No button, border, or semantic state uses a CPK color. Every
color, spacing, radius, and duration comes from
packages/ui/src/tokens.css. Light and dark themes always ship together. - An MIT-compatible license. MIT, BSD, Apache-2.0, and ISC are accepted; GPL, LGPL, and AGPL
are not. Every new dependency goes into
docs/TERCEIROS.mdin the same commit. - Out-of-scope ideas go in
docs/FORA-DE-ESCOPO.md, not in the code. Scope creep is the project’s number-one risk.
Starting the environment
Section titled “Starting the environment”Use Node 24, pnpm 11.24.0, and Docker for Postgres. This is the Node version used in Docker and CI; database dependencies require versions newer than the minimum declared at the monorepo root.
corepack enable # or: npm install --global pnpm@11.24.0pnpm installdocker compose up -d postgres # only the database; the app runs outside the containercp apps/web/.env.example apps/web/.env # DATABASE_URL already points to this PostgresIf you already configured POSTGRES_PASSWORD in the root .env, set the password in
apps/web/.env’s DATABASE_URL to the same value.
With the connection configured, apply migrations and start the app:
pnpm --filter @rotamer/web db:migrate # applies migrationspnpm devhttp://localhost:3000. predev copies RDKit and the MMFF94 tables from node_modules to
apps/web/public/chem/ and generates the Prisma client—do not commit this directory or edit it
by hand.
Without DATABASE_URL, the product starts without accounts, classes, or assignments, and the
editor works in full. Without GEMINI_API_KEY, the tutor disables itself and says so on screen.
To become a teacher on your development instance after creating an account at /entrar:
cd apps/web && node scripts/promote-teacher.mjs voce@exemplo.br --escola "Escola de teste"With --administrador at the end, the account also gains the Professores da escola (School
teachers) section at /turmas, where a school promotes its own teachers (D-29). Administrators
can only be created through this script, and can promote no higher than teacher in the interface.
Where things live
Section titled “Where things live”apps/web Next.js 16 · routes, server actions, accounts, classes, assignments, tutorpackages/core graph · RDKit · geometry, dynamics, and normal modes — runs without a browserpackages/editor2d the 2D canvas: tools, selection, menu, shortcuts, historypackages/viewer3d Three.js · rendering onlypackages/quests declarative missions, objective extraction, scoringpackages/ui tokens and componentsdocs/ architecture, decisions, design system, assignments, installation, guideDependency rule: core depends on no one, and no package depends on editor2d.
The full map is in docs/ARQUITETURA.md; the reasoning behind each choice
is in docs/DECISOES.md.
R-1, §4.5, and similar references. Class code cites rules by number—R-3 in a select,
§6.3 in a component. They all live in docs/ROTEIROS.md: §5.1 contains the
server-rule table, §6 the screen copy, and §7 the test protecting each rule. Read it first if
you work on assignments, teacher missions, or the catalog.
How a change becomes a commit
Section titled “How a change becomes a commit”-
Every change ends with a test that would fail without it. New chemistry needs a case in
packages/core/test/, running without a browser, using the value RDKit calculates—never a value from a third-party table. Where RDKit differs from PubChem, RDKit wins, and the screen identifies whose definition is used. -
Run what CI runs, before opening the pull request:
pnpm lintpnpm typecheckpnpm test # with Postgres running, action tests run; without it, they skip with a warningpnpm test:e2e # Playwright, desktop and mobile, against the production buildtest:e2ebuilds and starts the app on port 3100. Account, class, and assignment tests need Postgres; name-search tests skip when PubChem does not respond; tutor tests cover the behavior your.envallows—with a key, the response; without a key, its disabled state. -
Schema changes require additive migrations.
pnpm --filter @rotamer/web db:migratecreates the migration inapps/web/prisma/migrations/. Columns are never deleted or rewritten: self-hosters update withdocker compose up -d, and the migration runs before the app opens its port. -
Screen copy is content. Every sentence the student reads receives the same care as code: pt-BR, no programming jargon, and no claims about things the product does not do—the “Never claim” section of
CLAUDE.md. -
Documents describe what exists. If a change contradicts a document in
docs/, that document changes in the same commit. New or revised decisions go intodocs/DECISOES.md, without deleting the previous decision. Comments explain why, not what the line does; test names say what they protect. -
Commit messages in Portuguese, in the format
tipo: o que mudou(type: what changed)—feat:,fix:,docs:,refactor:,test:,chore:.
Found a chemistry error?
Section titled “Found a chemistry error?”This is the most important kind of issue. Provide the SMILES (or attach the molblock), what the screen showed, and what you expected, with its source. If the discrepancy is between RDKit and another tool, identify it: many differences come from definitions (TPSA with perceived aromaticity, rotatable bonds under the strict definition), and the right response is for the screen to identify whose definition it uses—not to adjust the calculation to match the other table.
The agent team
Section titled “The agent team”Nine roles live in .claude/agents/ for people working with a coding assistant—pm,
ui-ux, frontend, backend, security, deploy, researcher, marketing, and reviewer.
They are optional: they describe how the project approaches each kind of task and are useful
reading even for people who do not use any agents.
License
Section titled “License”By contributing, you agree that your contribution is published under the repository’s MIT license (LICENSE).