Contributing to GuestCraft
Questi contenuti non sono ancora disponibili nella tua lingua.
Thank you for helping. This guide explains how to set up the repository, how changes are made and reviewed, and the rules that keep the code base reliable. Security problems are never reported in public issues: see SECURITY.md.
Before you start
Section titled “Before you start”- Read
docs/architecture/repository.md: it says where each kind of change goes and which dependencies are allowed. - Decisions are recorded in
docs/adr. A change that contradicts an accepted ADR must follow it or propose a new ADR in the same pull request. - For anything larger than a bug fix, open an issue first, so that the design can be agreed before you write the code.
Reporting bugs
Section titled “Reporting bugs”Open an issue with the bug report template: what happened and what you expected, the steps to reproduce, the version or commit, and the runtime. Remove guest data and secrets from logs first. Feature proposals use the feature template. Vulnerabilities are never reported in public: follow SECURITY.md.
You need Node.js 24 (the exact version is in .node-version), pnpm 12 through Corepack, and Docker for the integration tests.
corepack enablepnpm installpnpm exec lefthook install # optional: fast checks before each commit and pushNo test needs a Channex key, a Vercel account or any other external service. Without Docker, the integration tests can use an existing PostgreSQL 17 and PgBouncer: see packages/testing.
Develop in a container or Codespaces
Section titled “Develop in a container or Codespaces”Open the repository in GitHub Codespaces or choose Reopen in Container in VS Code with the Dev Containers extension. The dev container uses Node.js 24, installs the pinned pnpm version through Corepack, and starts a separate Docker daemon for Testcontainers and Compose. It runs pnpm install --frozen-lockfile after creation and forwards the web port 3000 plus development ports 4280–4289. Run pnpm quality inside the container to exercise the same checks as CI; no customer credentials are needed.
Commands
Section titled “Commands”| Command | What it does |
|---|---|
pnpm test |
Fast tests (unit and property) of every package, without Docker |
pnpm test:coverage |
Every test, integration included, with coverage and thresholds; needs Docker |
pnpm typecheck |
TypeScript in every package |
pnpm lint |
Biome: formatting and lint rules |
pnpm actions:lint |
actionlint: the GitHub Actions workflows |
pnpm boundaries |
dependency-cruiser: package boundaries and dependency rules |
pnpm deadcode |
Knip: unused files, exports and dependencies |
pnpm licenses:check |
MIT packages never depend on AGPL packages |
pnpm secrets:scan |
gitleaks over the whole Git history |
pnpm openapi:lint |
Spectral: the API rules of .spectral.yaml on openapi/v1.json |
pnpm --filter @guestcraft/mobile test:coverage |
The mobile app’s Jest tests (jest-expo, React Native Testing Library) with its own coverage thresholds |
pnpm coverage:ratchet |
No package’s coverage below main’s (after pnpm test:coverage) |
pnpm deps:scan |
osv-scanner: known vulnerabilities in pnpm-lock.yaml (needs the network) |
pnpm mutation:core |
StrykerJS mutation testing of packages/core (long; nightly in CI) |
pnpm quality |
Every check above except deps:scan and mutation:core, as CI runs them |
Each file-based Vitest project sets maxWorkers: 1 to leave CPU and memory headroom on CI.
These projects do not inherit the root config’s worker limit. Keep the cap in new project
configs; deadlines, retries and coverage thresholds stay unchanged.
Every change comes with tests at the right level (ADR 0005):
*.unit.test.tsnext to the code, for rules without I/O.*.property.test.tsnext to the code, for invariants checked with fast-check against a simple, independent model.test/integration/*.integration.test.ts, for anything that touches PostgreSQL or another process. They run against a real PostgreSQL 17 started by Testcontainers.
Rules:
- Every bug fix adds a test that fails without the fix.
- No
.onlyand no.skip: Biome rejects both. A flaky test is quarantined through an issue, as ADR 0005 describes, not skipped in place. - The core’s rules against ambient I/O apply to its tests too: they use the injected clock and random source of
@guestcraft/testing, neverDate.now()orMath.random(). - Tests that depend on the current date take it from an injected clock. Never hard-code today’s date or use the runner’s wall clock for an expected value. Run the shifted-clock command in
@guestcraft/testingat calendar boundaries. - Inject time, randomness and identifiers; never read the system clock in the domain.
- Fixtures are synthetic. Never commit real guest data, card data, tokens or API keys.
Coverage thresholds are per package and are checked in CI; they are not lowered to make a pull request pass.
The enforced thresholds in vitest.config.ts, ordered as statements / branches /
functions / lines, are:
| Scope | Minimum |
|---|---|
| Global and web application | 85 / 80 / 85 / 85 |
| Core, auth, API, server, jobs, countries, i18n, tokens and UI components | 95 / 90 / 95 / 95 |
| Channex client and GuestCraft SDK | 95 / 90 / 95 / 95 |
| Database and Channex simulator | 90 / 85 / 90 / 90 |
Mobile app, under Jest (apps/mobile/jest.config.js) |
85 / 80 / 85 / 85 |
| Core money and identifiers, each file | 99 / 95 / 100 / 99 |
All handwritten source is included, even when no test imports it. React Native cannot run under
Vitest, so Vitest’s coverage leaves apps/mobile out and the app’s own Jest run counts every file
of its src/ instead; pnpm quality and CI run both. The reusable helpers in
@guestcraft/testing count too; generated code, fixtures and tests have explicit exclusions.
A new package must receive an appropriate explicit threshold rather than relying only on the
global average.
Functional completeness
Section titled “Functional completeness”Before product work, write observable acceptance criteria: the objects a person manages, the operations they perform, their permissions and what persists after a reload. A feature is complete only when its supported workflow works through the relevant domain, database, API, OpenAPI, SDK and UI layers. A deliberately partial change must name the remaining layers and behavior.
Include denied access, invalid input and recovery in acceptance evidence. A table, type, mock, reusable UI primitive or external API client is not evidence that a PMS workflow exists. Likewise, connecting Channex and listing its properties does not implement local import, mapping, reservation processing or inventory publication.
Coverage measures the exercised implementation, not product scope. Keep planned, partial and implemented capabilities separate in documentation and PR reports. The same rules apply to human contributors and coding agents; see AGENTS.md.
The application currently ships one UI language: English. Put every user-visible message in
packages/i18n/catalogs/en.json and regenerate the runtime catalog with pnpm i18n:catalogs.
Keep locale-aware formatting and guest language data independent of that UI catalog.
- TypeScript, strict, ESM. Only erasable syntax: no
enum,namespaceor constructor parameter properties. - Relative imports include the
.tsextension. - No
process.envoutside the configuration module ofserver, the app entry points and the test harness (packages/testing). - Errors follow ADR 0008: expected outcomes are returned as
Result, infrastructure failures are thrown with a retry flag, bugs throw and stop the operation. No empty or silentcatch. - Money is an integer number of minor units with its currency; dates of stays are
Temporal.PlainDate(ADR 0007).
Licensing of your contribution
Section titled “Licensing of your contribution”Every new source file starts with an SPDX header. In AGPL packages:
// SPDX-FileCopyrightText: 2026 GuestCraft contributors// SPDX-License-Identifier: AGPL-3.0-onlyIn MIT packages (see the license field of the package’s package.json), use MIT instead. Files that cannot carry a comment are covered by REUSE.toml. reuse lint must pass.
Contributions are accepted under the Developer Certificate of Origin: sign off each commit with git commit -s, certifying that you have the right to submit the work under the project’s licenses. The sign-off is required, and checked, from the first outside contribution (ADR 0004); the maintainers’ commits before then carry none.
Commits and pull requests
Section titled “Commits and pull requests”mainrequires a pull request, an up-to-date branch, passing GitHub Actionsqualityand Dockersmokechecks, and resolved review conversations. These protections apply to administrators too; force pushes and branch deletion are disabled. The solo-maintainer configuration does not impose a second GitHub approval; owner review below remains a process rule, not an automated approval.- Branch from
main; keep pull requests small and focused on one change. - Run
pnpm qualitylocally before pushing. CI runs on pull requests, including drafts. - Write commit messages in the Conventional Commits style, in English:
feat(channex-client): parse revision feed pages. The types decide the next version and the changelog:featis a new minor version,fixandperfa patch. Mark a breaking change with!(feat(api)!: …) or aBREAKING CHANGE:footer. Breaking means removing or changing an API operation or field, adding a required setting, renaming an environment variable or changing behaviour operators rely on. See releasing. - Releases are pull requests too: release-please keeps one release pull request on
main, and the owner merges it after the release checklist. Do not editCHANGELOG.mdor the version fields by hand. - Fill in the pull request template. A pull request is merged when CI is green and a maintainer has approved it. Changes to the core, migrations, security code and public contracts need the approval of a code owner, and the independent reviews of the review protocol.
- Update the documentation in the same pull request: READMEs, ADRs, the architecture notes.
- For every new
packages/db/migrations/NNNN_*.sql, add anexpandorcontractentry topackages/db/migrations/kinds.json; when unsure, usecontract. Runpnpm --filter @guestcraft/db migrations:revisionsandpnpm --filter @guestcraft/db migrations:lint. Run the revisions command again after any merge or rebase that touches migrations, even if Git reports no conflict; the generated checksums and kinds must reflect the combined history.expandpromises that the previous runtime still reads and writes after SQL and reapplied grants; inspect the grant floor and review old-runtime behavior. The current tests simulate a previous revision prefix; they do not run a released previous binary. Usecontractfor changes that require new code and document the roll-forward boundary. Do not edit an applied migration or its declaration. See ADR 0038.
Automated checks and planned checks
Section titled “Automated checks and planned checks”The workflows and scripts are the evidence of what is enforced today; the quality program lists every gate, its status, the review protocol and the prioritized gaps. The OpenSSF Best Practices self-assessment answers each passing-level criterion with its evidence, and the security design records the secure-design principles and every use of cryptography.
- Required on every pull request (
quality): the commands above, including gitleaks over the history, the Spectral API rules with their self-test, and the coverage ratchet againsttooling/quality/coverage-baseline.json, and actionlint on the workflows.smokebuilds the checkout’s images and starts them with Compose. - On every pull request, not yet required:
browser(Playwright on the production build) anddependencies(osv-scanner; a pull request fails only on the vulnerabilities it adds).imagebuilds the release images natively on amd64 and arm64 with their SBOMs, without pushing; it runs on every push tomainand on the pull requests that change the build inputs. - On every pull request:
dependency reviewblocks newly introduced high-severity vulnerabilities without commenting. License direction stays inpnpm licenses:check. - On
main: successfulqualityandmutationreports become line-coverage and core-mutation endpoint files on the orphanbadgesbranch. Only source runs from this repository’smainbranch can publish. - On every push to
main:releaseupdates the release pull request; after a release pull request is merged, it publishes the attested images and the GitHub release (releasing). - Nightly: property tests at 10,000 cases in two non-UTC time zones, mutation
testing of the core (overall threshold in
stryker.config.mjs), the full dependency scan and the Channex contract-drift check; weekly, the shifted-clock matrix. - Weekly and on
mainpushes and branch-protection changes: OpenSSF Scorecard publishes results and uploads SARIF to code scanning. - By GitHub, free on a public repository: CodeQL (default setup), secret scanning with push protection, Dependabot alerts and private vulnerability reporting.
A coverage drop below the baseline fails the ratchet. Add tests; lowering a
baseline value is a hand edit whose reason goes in the pull request description.
The tool binaries are pinned by SHA-256 in tooling/requirements/tools.json.
ADR 0005 also targets changed-line coverage, API fuzzing and breaking-change checks; those gates are not yet implemented and must not be reported as passing. The dependency review action is enabled on pull requests. GitHub’s secret scanning and push protection settings were verified through the repository API on 2026-10-28; see quality-program row 23.
Browser acceptance covers front desk, team/custom roles, policies, Channex import and resident-worker ARI publication on the production build with disposable PostgreSQL. The request-pump ARI harness is available separately. Screenshot/axe matrices currently share workflow deadlines and have known timeout limitations; a listed CI capability does not claim a particular run passed. Manual assistive-technology certification and a browser INP budget remain separate work.
Code of conduct
Section titled “Code of conduct”Everyone taking part in the project follows the Code of Conduct.