Contributor Onboarding

On this page

This page is the starting point for new contributors. It does not duplicate the existing developer documentation; it indexes it in the order you should read it.

TL;DR

  1. Get the devstack running: DevStack Guide.

  2. Read CONTRIBUTING.adoc (issue → branch → MR → close).

  3. Pick a ~good-first-issue from the issue tracker.

  4. Open an MR following the developer guide; the pre-push hook gates fmt + clippy + tests.

  5. Get one approval from a CODEOWNERS reviewer; auto-merge.

That’s the whole loop.

Prerequisites

Tool Why

Rust 1.97.0 (pinned by rust-toolchain.toml; rustup picks it up automatically)

Workspace edition is 2024; all 66 workspace members compile against rust:1.97-alpine in CI

Docker + Compose v2

Devstack runs in containers: Postgres, RabbitMQ, Keycloak, Garage S3, the CRAIG services, and the web BFF

glab CLI

Issue/MR/epic operations. Authenticate via glab auth login or set GITLAB_TOKEN

Node.js 18+

Antora docs build; Playwright E2E suite

git

git config core.hooksPath .githooks activates the pre-push gate

DevStack Guide has the full environment setup walkthrough including Keycloak realm bootstrap and seeded test users.

What to read, in order

  1. Why CRAIG? — system context: this is a CCWIS (Comprehensive Child Welfare Information System) for the State of Georgia. Knowing the user (caseworker, supervisor, foster parent, partner agency) is more important than knowing the architecture.

  2. CONTRIBUTING.adoc — non-negotiable workflow: how issues are filed, how MRs flow through the pre-push hook, when to update plan status, how to close issues post-merge. Authoritative for both human and AI agent contributors.

  3. Developer Guide — environment, project structure, how-to guides for adding endpoints, services, and tests.

  4. Architecture — the system shape: the REST API services, the BFF, the message broker topology (ADR-003), the rules-engine integration (ADR-006), the shared per-service reqwest::Client (ADR-014), and the fail-closed encryption posture (ADR-020).

  5. The coding-conventions standard — Rust idioms enforced in CI: #[forbid(unsafe_code)], clippy -D warnings, tokio runtime, sqlx for SQL, axum for HTTP.

  6. Testing Reference (CRAIG) — what the test pyramid looks like, what cargo nextest run --workspace runs, when to use Playwright E2E vs. Rust integration vs. unit.

  7. Glossary — domain vocabulary (CCWIS, AFCARS, NCANDS, ICWA, CICC, IV-E). Read this before your first MR — getting the names right matters in this domain.

That’s enough to make a competent first MR. Everything else is reference material to consult as needed.

Picking your first issue

Issues labeled ~good-first-issue are scoped to:

  • Single-file or single-module changes

  • No cross-service coordination required

  • Acceptance criteria fully spelled out

  • Test coverage path clear (existing test scaffolding can be extended)

Browse the good-first-issue queue. If you don’t see one that fits, file a chore: issue describing what you’d like to work on and ask a maintainer to label it before starting.

When you pick one up:

  1. Assign yourself before starting work (one assignee per issue — coordinate if it’s already taken).

  2. Branch off main: feature/short-description.

  3. Implement; the MR Completion Protocol in CONTRIBUTING.adoc is the ground truth for the rest.

Where to ask questions

  • Architecture questions: file an issue labeled ~question or open a draft MR with Draft: prefix and ask in the description.

  • Design decisions: read the ADR index first; if the decision isn’t recorded, propose one — see Writing ADRs.

  • Workflow questions: CONTRIBUTING.adoc is authoritative; it mirrors the gitlab-workflow standard for the CLI/agent perspective.

  • Domain questions (CCWIS terminology, federal compliance, jurisdiction-specific rules): Glossary + Federal Requirements cover most. Rule Set Patterns and Domain Knowledge explains the JDM-ruleset pattern.

Common gotchas

These have tripped up contributors more than once. Read Known Issues & Lessons Learned for the full list; the ones below are the headline items.

  • Linux: host.docker.internal needs an /etc/hosts entry. Docker Desktop (macOS/Windows) provides the name automatically; the Docker daemon on Linux does not. The devstack’s browser-facing URLs (Keycloak’s issuer, craig-web’s redirect URI) use it so one config serves host browsers and Playwright-in-Docker alike — without 127.0.0.1 host.docker.internal in /etc/hosts, host-side logins and CLI calls fail with could-not-resolve errors. Containers are unaffected (compose maps the name via host-gateway where a container needs it). One-time fix: echo '127.0.0.1 host.docker.internal' | sudo tee -a /etc/hosts. See also the UFW note in Local Development — that is the other Linux networking gotcha (DNS resolves, TCP blocked).

  • Pre-push hook is mandatory. git config core.hooksPath .githooks is the most common skipped step. It runs fmt, clippy, tests, and the 8-question reflection checklist before the push lands. Don’t bypass with --no-verify; if a hook fails, fix the underlying issue.

  • Migrations are additive only. Adding a column or a table is fine; renaming or dropping requires a two-step deprecation. Multiple MRs touching the same service must use non-colliding migration timestamps — check the existing services/<name>/migrations/ dir before stamping yours.

  • Plan status tracking is per-step, not end-of-session. Sessions can end abruptly. Update the == Status table after each step lands; that is what lets the next agent or human pick up without guessing.

  • CLAUDE.md stays lean — no per-plan history rows. When a plan lands, we add its name once to the Completed-Plans list and nothing more. The archived plan bodies under plans/archive/ plus CHANGELOG.adoc are the historical record; duplicating that history (per-MR SHAs, phase-status tables, implementation blow-by-blow) into CLAUDE.md adds no information and crowds out the rules that must actually be read. Never append a detailed status row at archive time.

  • The wildcard audit subscriber is real. Per ADR-003, every .event. topic publishes to a wildcard queue captured by craig-security’s audit subscriber. New events automatically flow through; you don’t need to wire up an audit consumer for every event you publish.

  • claims.sub is a UUID, not a username. API handlers store claims.sub for actor identity; the web UI uses preferred_username for display. Don’t confuse the two.

What we optimise for

Maintainability and code quality come first. When you have a choice between a quick fix that leaves the logic in the wrong place and a proper refactor that puts it in the right module, we take the refactor — even when there is no user-visible behaviour change. Clean module boundaries, clear names, and honest structure are worth an MR on their own.

Two consequences for how you scope work:

  • No fig-leaf seams. Don’t introduce a new function or module that merely looks like a boundary while the real logic stays tangled in the wrong place. Do the actual extraction.

  • Don’t scope-down to hit an estimate. If the proper refactor is larger than an optimistic estimate suggested, that’s a signal the estimate was low — not a reason to ship the smaller, worse version. When a task sits between a scope-limited MR and a larger proper-refactor MR, raise both options and pick the one that leaves the code more maintainable.

Service ownership

The CODEOWNERS file at the repo root maps each path to its owner(s). For the current single-maintainer phase, every path is owned by @bitskrieg. When the team grows, see Service Ownership for the escalation model.

After your first MR

Once your first MR merges, you’ll have learned more than this page can teach. From here:

  • Browse the completed plans archive for examples of multi-step features.

  • Pick up a ~P3-low issue without the ~good-first-issue label.

  • If you see a recurring pattern that isn’t documented, propose an ADR.

Welcome to CRAIG.

Edit this page · latest