Lesson 1 of 6 · 8 min read · Foundations

Bootstrap your first repo

The fastest way to onboard a repo to ArchRails is to commit a single empty file and let the platform generate the first draft of your architecture for you. You review the result in a normal PR, edit anything that doesn't match reality, and merge. That's it — no out-of-band setup, no separate dashboard wizard.

In this lesson
  1. What bootstrap actually does
  2. Step 1 · Commit an empty config
  3. Step 2 · Open the PR
  4. Step 3 · Review the generated config
  5. Step 4 · Edit the REVIEW REQUIRED placeholders
  6. Step 5 · Merge and you're live
  7. Troubleshooting

What bootstrap actually does

Bootstrap reads the CALM document(s) you already have in your repo and produces a first draft of .archrails/config.yml — the small file that tells ArchRails which CALM files describe your architecture, which branches are governed, and which code paths map to which CALM nodes.

Bootstrap scans your whole repo for *.calm.json files, with a scoring boost for conventional locations (architecture/, arch/, topology/, system/, files named calm.json or architecture.json). Keep yours wherever fits your codebase. For each CALM node, bootstrap infers a likely mapping path from your directory layout; anything it can't pin down is marked REVIEW REQUIRED so it's obvious in the PR review.

Prerequisite — you need CALM already. Bootstrap derives the config from your existing CALM; it does not synthesise CALM from your source code. Your repo must have at least one *.calm.json file checked in before you open the bootstrap PR. If you don't yet have CALM you have two options:
Why a PR, not a wizard? Your architecture lives in your repo, alongside the code it describes. Every change — including the first draft — goes through your normal code review. There is no “ArchRails config” living in our database that drifts from what's in git.

Step 1 · Commit an empty config

On a new branch, create the directory and an empty file:

git checkout -b archrails/bootstrap
mkdir -p .archrails
touch .archrails/config.yml
git add .archrails/config.yml
git commit -m "chore: bootstrap ArchRails"
git push -u origin archrails/bootstrap

The file is genuinely empty — zero bytes. That empty file is the signal: “ArchRails, fill this in for me.”

Step 2 · Open the PR

Open a pull request against your default branch (or whichever branch you want bootstrap to target). Title and body are up to you; a typical first PR looks like:

Title:  chore: bootstrap ArchRails
Body:   Empty .archrails/config.yml so ArchRails generates the
        initial architecture from this repo.

The moment that PR is open, ArchRails runs — you'll see a check appear within seconds.

Step 3 · Review the generated config

A minute or two later, a follow-up commit from github-actions[bot] will appear on your branch:

chore: regenerate .archrails/config.yml via ArchRails bootstrap

That commit adds the populated .archrails/config.yml. Your existing *.calm.json files are untouched — bootstrap reads them to derive the config but never writes to them. A small example of the generated config for a Java service might look like:

# .archrails/config.yml — generated by ArchRails
# Paths were inferred from your repo structure.
# Adjust any path marked REVIEW REQUIRED before merging.

version: 1

governance:
  governed_branches:
    - main

  sources:
    - id: architecture
      provider: calm
      file: "architecture/system.calm.json"

mapping:
  # confidence: high
  - path: "customer-portal"
    node: customer-portal

  # confidence: high
  - path: "orders-server"
    node: orders-server

  # ⚠ REVIEW REQUIRED — no code directory matched for 'orders-db' (type: database).
  # Likely infrastructure-only or external — uncomment and set a path if this
  # node represents code in this repo.
  # - path: ""
  #   node: orders-db

integrations:
  github:
    app: archrails

Three things to notice:

Pointing at a node in another repo? Cross-repo relationships are declared in your CALM JSON, not in .archrails/config.yml. The recommended shape is to put repository: <repo-url> directly on the relationship's destination endpoint (no need to declare the foreign node locally). A legacy metadata.repository on the node spec is also accepted. Both are covered in detail in Declare relationships.

Step 4 · Edit the REVIEW REQUIRED placeholders

Pull the bot commit down and edit the config like any other file:

git pull
$EDITOR .archrails/config.yml
$EDITOR architecture/system.calm.json

Replace each REVIEW REQUIRED with the right value, delete nodes that don't exist, add ones the inference missed. The Declare nodes and Declare relationships lessons cover the field reference in detail.

Push your edits to the same branch. ArchRails re-validates on each push — the PR check stays red until every placeholder is resolved and every relationship references a real node.

The bot's commit doesn't auto-trigger the check. When github-actions[bot] pushes the bootstrap follow-up, GitHub Actions deliberately doesn't fire workflows triggered by another workflow's GITHUB_TOKEN — that's GitHub's loop-prevention behaviour, not an ArchRails quirk. You won't see a new Architecture Governance run on that SHA. The check fires again as soon as you push a commit (the placeholder edits in Step 4 do this). If you only want to verify the bot's output without editing yet, push an empty commit:
git pull
git commit --allow-empty -m "ci: re-trigger ArchRails review"
git push

Step 5 · Merge and you're live

When the check is green, merge. The next PR opened against a governed branch will be reviewed against this architecture — new dependencies that aren't declared, services that move to undeclared hosts, controls that aren't met — all flagged automatically.

You can re-run bootstrap any time. Open another PR that empties .archrails/config.yml and ArchRails will regenerate from scratch. Useful after a major refactor — though typically you'll just edit the existing config by hand.

Troubleshooting

The bot commit never appears on my branch.

Check the ArchRails check on your PR — the message will say where it stopped. The two most common causes:

  • The branch you opened the PR against isn't listed in any existing governed_branches. For the very first bootstrap PR this is fine; for re-bootstraps make sure the target branch matches.
  • Your installation is gated to specific repositories and this one isn't in the allowlist yet. A tenant admin needs to add it via the dashboard's Team Roster page.
Almost every node says REVIEW REQUIRED.

Inference works best when each major service has its own top-level directory whose name matches what the service actually is. If your repo is a single flat src/ tree, bootstrap can't tell which classes belong to which service — you'll need to fill the paths in by hand. The Declare nodes lesson covers owned_paths in detail.

Can I commit my own .archrails/config.yml instead of an empty one?

Yes — bootstrap only runs when the file is empty. If you commit a populated config, ArchRails skips bootstrap and goes straight to validating PRs against it. Hand-authoring is the right call once you've done it once or twice and know the shape.

What happens to existing *.calm.json files in my repo?

Bootstrap reads your *.calm.json files to derive the config but never writes to them. Whatever you've drafted by hand — whether under architecture/, alongside your services, or anywhere else — stays exactly as you wrote it. Bootstrap only adds .archrails/config.yml.

What if my repo has no *.calm.json yet?

Bootstrap won't have anything to derive the config from, so it will skip regenerating the file and leave your empty .archrails/config.yml in place. Two on-ramps:

  • Scaffold via the MCP tool. Start architect mode (archrails mcp start --mode architect) and ask the agent to call generate_calm. The tool walks your repo deterministically and emits an initial CALM under architecture/<repo>.calm.json. You and the agent then walk the REVIEW REQUIRED markers, resolve each one, and commit. Bootstrap picks up the new CALM on the next push.
  • Hand-author it. Follow the Declare nodes lesson. This is the better choice when your services communicate over network protocols (HTTP, AMQP, gRPC) rather than source-code imports — the scaffolder infers edges from import statements, which microservices rarely have across service boundaries.
How does generate_calm decide what's a node and what isn't?

Two passes, both deterministic. First the manifest pass: any directory at depth ≤ 2 from the workspace root containing a package.json, pom.xml, build.gradle, build.gradle.kts, pyproject.toml, setup.py, go.mod, Cargo.toml, composer.json, Gemfile, or any *.csproj file becomes a node. Second the folder-path pass: directories under services/, apps/, packages/, modules/, or components/ also become nodes even without a recognised manifest — for example, a database directory that ships only a Dockerfile. Cross-service connects relationships are inferred from imports across twelve languages (TypeScript, JavaScript, Python, Java, Kotlin, Swift, Dart, Go, C#, Ruby, PHP, Rust, C/C++, and COBOL copybooks). Anything that doesn't pin down structurally is stamped REVIEW REQUIRED and waits for you in the PR.