Guides

Building the index

How Carrick builds the index, rescanning a repository, scanning from your own machine, and the GitHub Actions workflow that keeps the index current.

Carrick builds the index once the Carrick GitHub App is installed on your repositories, as the Quickstart describes. It writes and keeps each repository’s configuration itself, unless the repository has a carrick.json at its root. Every repository goes into the workspace’s one project.

Rescan a repository

To rebuild a repository’s index, press Rescan beside it on the Repos page of the Carrick dashboard. Carrick also rescans a repository when you push to its default branch.

Scan from your own machine

You can also build the index with the CLI. Your coding agent writes a carrick.json and the GitHub Actions workflow from the prompt that carrick init prints, runs the first scan if the repository has no index yet, and then opens a pull request.

Index synchronization

After completing workspace setup, carrick init checks whether an index already exists in Carrick Cloud for your repositories:

  • If an index exists: Carrick downloads the compiled index into .carrick/ by running a local deterministic pass and applying hosted classification rows. No model inference runs locally.
  • If no index exists: Carrick reports that an initial scan is required, which your agent runs from the scaffold prompt below.

Generate configuration with your agent

When initialising repositories that do not yet have a carrick.json or scan workflow, carrick init provides a prompt to pass to your AI coding agent:

Run the carrick scaffold tool for owner/repo, passing its owner/repo as `repo`, and follow what it returns.

The scaffold tool generates the following configuration files:

  • carrick.json: The repository service configuration derived from .carrick/proposal.json and local source files.
  • .github/workflows/carrick.yml: The GitHub Actions workflow for continuous indexing.
  • Agent instructions: Adds a ## Carrick instruction block to AGENTS.md or CLAUDE.md.

In a folder of several repositories, the prompt names each one, and the agent works through them one at a time. carrick index refuses to run while any repository in the folder has no carrick.json, so the scan runs once, after the last one.

Run the initial scan

Cross-service features, such as who calls a route and whether a consumer’s types still match its producer, need at least two services indexed in the same project, for example a backend and the frontend that calls it. The two services can sit in one repository or in several.

If your repositories have not yet been indexed in Carrick Cloud, execute the initial scan from the workspace directory:

carrick index --detach

carrick index parses carrick.json, extracts routes, calls, and types, submits unresolved relationships for cloud classification, and uploads the compiled index to Carrick Cloud.

The --detach flag runs the scan in a background process, printing a scan ID and log location (.carrick/scan-<id>.log). This prevents agent execution timeouts on large codebases.

On macOS the scan keeps the machine from idle-sleeping until it finishes, for at most three hours, and says so when it starts. Closing the lid still puts it to sleep. Pass --no-keep-awake to let it sleep.

Monitor the progress of a background scan using carrick status:

carrick status

carrick status reports the active scan phase, file counts, and final completion status.

Manual configuration alternative

If you are configuring a repository manually without an agent:

  1. Create a carrick.json at the repository root using carrick templates carrick.json or according to the carrick.json schema.
  2. Generate the CI workflow with carrick templates workflow > .github/workflows/carrick.yml.
  3. Run carrick index from your terminal to build and upload the index.

For Deno workspaces, ensure Deno 2.9.4 or newer is on PATH and prepare the module graph before scanning:

deno install --frozen --node-modules-dir=none

Commit the workflow to CI

Commit carrick.json and .github/workflows/carrick.yml to your repository on a branch, open a pull request, and merge it into your default branch.

The following workflow definition belongs in .github/workflows/carrick.yml:

name: Carrick

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
  # Carrick Cloud sends this when a sibling repo in the same project changes
  # on its main branch, so this repo's cross-repo results catch up without
  # anyone pushing. If you add deploy steps to this file, gate them on
  # github.event_name so a sibling change never deploys this repo.
  repository_dispatch:
    types: [carrick-sibling-updated]
  # Rescan on demand (Actions tab, or: gh workflow run carrick.yml). After a
  # Carrick release the index only refreshes on the next scan.
  workflow_dispatch:
    inputs:
      full-scan:
        description: >-
          Re-analyze every file instead of reusing the cached answer for a file
          that has not changed. Slower and costs a full analysis; ask for it
          when Carrick has started extracting something it did not extract
          before, so the cache holds answers from before it could.
        type: boolean
        default: false

permissions:
  # Mint a short-lived OIDC token Carrick exchanges for keyless upload auth
  # (no upload secret to configure).
  id-token: write
  contents: read

jobs:
  carrick:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        # Full git history lets Carrick diff against the last scan and run incrementally.
        with:
          fetch-depth: 0
      - uses: carrick-tools/carrick@v1
        with:
          # Empty on every trigger but workflow_dispatch, which is the point:
          # a full re-analysis is asked for, never carried by a push.
          full-scan: ${{ inputs.full-scan }}

Key workflow configuration requirements:

  • Branch matching: Ensure branches: [main] matches your repository’s default branch (main, master, develop).
  • OIDC token permissions: The id-token: write permission is required for keyless upload authentication to Carrick Cloud.
  • Repository dispatch: The carrick-sibling-updated trigger allows Carrick Cloud to re-run this repository’s scan when sibling repositories in the same project update. It is enabled server-side but not delivering yet.
  • Pull requests: A pull request run reports drift and never alters the index.
  • Fork security: Carrick automatically skips scanning pull requests originating from repository forks, because GitHub withholds Actions OIDC tokens on fork-generated PRs. The scan runs when a maintainer pushes the branch to the repository itself.

Dependencies

Package types need their dependencies on disk. Node projects use installed node_modules, and Deno projects use the Deno dependency cache. The Action prepares those dependencies before analysis:

  • For Node projects, installation runs for every service the scan will visit, at that service’s own nearest lockfile. A monorepo whose packages carry their own lockfiles is installed package by package, and a workspace that hoists to one lockfile at its root is installed once.
  • The lockfile picks the package manager: package-lock.json runs npm ci, pnpm-lock.yaml runs pnpm install --frozen-lockfile, yarn.lock runs yarn install, and bun.lock or bun.lockb runs bun install (add oven-sh/setup-bun first).
  • Lifecycle scripts are disabled in every case.
  • Each install command has a five-minute timeout. A failed or timed-out install prints a warning, and the scan that follows refuses any service whose dependencies are still missing.
  • A service with existing node_modules skips its Node install. A Deno manifest still triggers Deno cache preparation.
  • The package managers’ download caches are restored between runs, keyed on the hash of every lockfile the run installs from.

For Deno roots the Action runs deno install --frozen --node-modules-dir=none, which prepares the Deno dependency cache and prevents npm lifecycle scripts from running even when the project authorises them through allowScripts. A root with both Node and Deno configuration prepares both. Projects that import generated declarations must generate them through their normal build before indexing.

Private registries

Private registries use your own credentials, and Carrick adds no authentication of its own. Put the token your .npmrc reads in the Carrick step’s environment, and the install inherits it:

      - uses: carrick-tools/carrick@v1
        env:
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

Turn off installation

Set install-dependencies: false to skip dependency preparation. It also implies allow-unprepared: true.

      - uses: carrick-tools/carrick@v1
        with:
          install-dependencies: false

Unprepared checkouts are refused

Carrick refuses to scan a checkout it cannot type, rather than building an index whose types are any without saying why. The check runs before the scan starts, per service, on what is reachable from that service’s own directory, so an installed monorepo root says nothing about a nested workspace that is not installed. Carrick refuses two things:

  • Dependencies that an npm, pnpm, Yarn or Deno lockfile states and the tree has not installed. The refusal names the service and the exact install command. A tree with no lockfile above the service is scanned as it is. Deno services are checked only when their config sets nodeModulesDir, since Deno otherwise caches outside the tree.
  • A config mapping that the service imports through, whose target directory is not on the checkout. This covers a TypeScript paths entry, a package.json imports key, or a Deno import-map entry pointing at, for example, a generated client whose generator has not run. The refusal names the mapping and the missing directory. A mapping left behind by a deleted package, which nothing imports, is logged and scanned past.

To scan anyway, pass --allow-unprepared to the CLI, set CARRICK_ALLOW_UNPREPARED=1 in the environment, or set allow-unprepared: true on the Action. A pipeline that scans a bare checkout deliberately keeps working, with a warning.

Re-analysing everything

A scan analyses each changed file and reuses the stored answers for the rest. Occasionally the stored answers themselves need redoing, because Carrick has started extracting something it did not extract before and the cache holds answers from before it could. The full-scan input re-analyses every file for one run.

The workflow above wires full-scan to its workflow_dispatch input, so you ask for it on one run rather than leaving it on. Run it from the Actions tab, or with the GitHub CLI:

gh workflow run carrick.yml -f full-scan=true

On every other trigger the input is empty and the incremental scan runs as usual.

An ordinary scan indexes a commit once per scanner version, so a second run on an unchanged commit stores nothing and says so. A full scan, or any run that re-analyses a file, is the exception, and its answers replace what the index holds for that commit.

  • Quickstart is the short path from install to a first index.
  • carrick.json explains monorepo service splitting and outbound call classification.
  • CLI covers advanced flags, background scan management, and recheck budgets.
  • PR comments describes automated GitHub pull request checks.