Skip to content

.gaia folder

The .gaia/ folder at the root of your project holds GAIA’s own machinery: the gaia CLI, the scripts that hooks and commands run, the statusline, and the settings and records your project builds up over time. Its files fall into three groups:

  • Files GAIA ships. They arrive with each release. /update-gaia merges them when you update.
  • Files your project writes. Commands create them as you work. They are committed, so the whole team shares them, and /update-gaia never merges them.
  • Machine-local state under .gaia/local/. It is gitignored and stays on your machine.

You rarely edit anything here by hand. When you want to change a setting, ask Claude, or run the command that owns it.

PathWhat it isIn gitWritten by/update-gaia
VERSIONThe GAIA version your project is onCommittedThe release, then /update-gaiaRewritten at the end of an update
manifest.jsonThe list of files GAIA ships, each with its ownership classCommittedThe release, then /update-gaiaReplaced with the new release’s copy at the end of an update
cli/The gaia CLI and the templates the scaffolders useCommittedThe releaseMerged
scripts/Scripts that hooks, commands, and skills runCommittedThe releaseMerged
statusline/The statusline scriptsCommittedThe releaseMerged
templates/Starting files for your README and for UAT specsCommittedThe releaseMerged
migrations/Upgrade notes for steps a file merge cannot doCommittedThe releaseMerged
audit-ci.ymlThe Code Audit Team rosterCommittedThe release, then youMerged key by key
labels.jsonYour repository’s GitHub labelsCommittedThe releaseMerged
conventional-commits.jsonThe allowed commit and pull request title typesCommittedThe releaseMerged
setup-features.jsonThe list of setup steps /setup-gaia ownsCommittedThe releaseMerged
state-registry.jsonWhat each entry in .gaia/local/ is and when it is cleaned upCommittedThe releaseMerged
packages.jsonThe packages in your workspace that GAIA coversCommittedThe release, then youNever touched
finding-classes.jsonYour project’s own audit finding classesCommittedThe release (empty), then youNever touched
project.jsonTeam settings and project setup decisionsCommitted/gaia-init, /setup-gaia, /update-gaiaNever merged
update-decisions.jsonYour answers to update conflictsCommitted/update-gaiaWritten by the update itself
audit-residual-dismissals.jsonlAudit residuals you dismissed or keptCommitted/gaia-residueNever touched
init-state.json/gaia-init’s progressExists only while /gaia-init runs/gaia-initNot applicable
local/Machine-local stateGitignoredHooks and commandsNever touched

“Merged” means the three-way merge described in The three-way merge. A file you never changed is replaced with GAIA’s new version. A file you changed keeps your changes, and a real conflict becomes a question.

These come with every release. Each one, except VERSION and manifest.json, has a class in .gaia/manifest.json that decides how /update-gaia merges it; the classes are explained in How GAIA classes its files. Nearly every file in .gaia/ is owned (GAIA maintains it). audit-ci.yml is shared: GAIA provides the starting version and you customize it.

.gaia/VERSION holds the GAIA version your project is on, such as 2.0.0. /update-gaia reads it to find your starting point, and it writes the new version last, after everything else in the update is done. If an update stops partway, the version stays where it was, and re-running /update-gaia picks up the same update. See Stopping and resuming.

.gaia/manifest.json lists the files the release ships and the class of each one. /update-gaia uses the classes to decide how to merge each file. Files that are not in the manifest are yours, and the merge never touches them. VERSION and manifest.json are not in it either: instead of merging them, /update-gaia rewrites both at the end of an update.

Don’t edit either file by hand. A wrong version makes the next update compare your files against the wrong release.

.gaia/cli/gaia is the bundled gaia CLI, a command-line tool that GAIA’s hooks and commands call. You don’t need to run it. Ask Claude for what you want instead, for example Sync GAIA's GitHub labels.

.gaia/cli/templates/ holds the templates gaia scaffold fills in for new components, hooks, routes, and services. See Scaffolders.

The shell scripts that GAIA’s hooks, commands, and skills run: the work lifecycle, the audit loop, worktree setup, the usage ledger, and more. You don’t call them directly. When a step needs one, Claude runs it.

The scripts that draw GAIA’s statusline: the left side, the right side, and the context reading. See Statusline.

  • templates/README.md is the starting README for your project. /gaia-init replaces GAIA’s own README with this template and fills in your project’s title.
  • templates/spec/uat-spec.ts.tmpl is the starting file for a UAT spec, the test that checks one of a SPEC’s acceptance criteria. See /gaia-plan.

Each release can include a migrations/<version>.md note for upgrade steps a file merge cannot do, such as moving a folder or rewriting a config. /update-gaia follows these notes from the verified release bundle, never from your copy of the folder, so editing your copy changes nothing. See Migration notes.

The roster of the Code Audit Team: which reviewer covers which files, and which ones can take a light review. You can add your own members and coverage. /update-gaia merges this file key by key rather than as a whole file, so your roster entries are kept.

The GitHub labels GAIA creates on your repository. gaia labels sync reads it. See Labels.

The types allowed at the start of a commit message or pull request title, such as feat and fix. commitlint (opens in a new tab) reads it to check commit messages and pull request titles, and GAIA’s branch names use the same types. See Git conventions.

The list of setup steps /setup-gaia owns. Each step has an id and a decision: applied, declined, or not-applicable. Decisions for project-level steps are committed in project.json, and decisions for machine-level steps stay in .gaia/local/setup-state.json. When a release adds a step, /setup-gaia runs it even on a project whose setup is already complete. See New setup steps from later releases.

A list of everything GAIA keeps under .gaia/local/: what each entry is, whether a linked worktree shares it, and when it gets cleaned up. It also lists files that features removed in earlier releases left behind, which the leftover-file cleanup deletes after an update. See Cleaning up .gaia/local/.

These files belong to your project. Commands create them as you work, and /update-gaia never merges them. All but init-state.json are committed, so every teammate reads the same copy.

The packages in your workspace that GAIA’s harness covers, as a list of {name, path} entries. A new project lists one, frontend. When the file is missing, GAIA assumes frontend anyway. Each package’s own settings live in its gaia.package.json, such as frontend/gaia.package.json.

Team settings and project setup decisions, shared by everyone on the repository:

  • sandbox_recommended and isolation_policy, the two team settings. See Team settings.
  • fitness_commit_threshold, optional: how many of your team’s harness commits since the last /gaia-fitness run show its statusline nudge. Without it the count is 15. No command sets it, so ask Claude, for example Set the /gaia-fitness nudge threshold in .gaia/project.json to <n> commits. See The statusline nudge.
  • setup_decisions, the decision recorded for each project-level step in setup-features.json.

/gaia-init creates the file with GAIA’s defaults. /setup-gaia adds decisions, and only a repo admin running /setup-gaia --reconfigure changes the team settings. /update-gaia adds decisions for setup steps a new release brings, and commits them with the update. Its merge never overwrites the file.

Your answers to the conflict questions /update-gaia asks, one entry per file (or per key, for a file merged key by key). A Decide later answer is recorded too, and asked again at the next update. Because the file is committed, a teammate who runs the next update sees the same answers. See The three-way merge.

The residuals you dismissed or kept during /gaia-residue. A residual is a minor audit finding that was accepted rather than fixed. The file is only ever added to, so every dismissal leaves a record a reviewer can see. /gaia-residue creates it the first time you dismiss or keep one.

/gaia-init records its progress here step by step, so a failed run can pick up where it stopped. It is not a team record: /gaia-init deletes it once every step is complete, so a finished project doesn’t have it. See Resuming after a failure.

.gaia/local/ holds state that belongs to one machine: caches, the usage ledger, audit results, and setup decisions that apply to your machine only. It is gitignored as a whole, so nothing in it is committed, and it is not in the manifest, so /update-gaia never touches it. GAIA creates the folders it needs as it goes.

A linked worktree’s .gaia/local is a link to the main checkout’s copy, so every worktree reads and writes one set of state. See Worktrees provision themselves.

Path under .gaia/local/What it holds
setup-state.jsonThis machine’s setup progress and its decisions for machine-level setup steps. Deleting it makes /setup-gaia ask those questions again.
settings.jsonPer-machine choices, such as whether the statusline’s left side is GAIA’s or your own. /setup-gaia writes it.
telemetry/usage.jsonlThe usage ledger behind every cost readout. See Cost and usage ledger.
telemetry/token-rates.override.jsonYour own token prices, if you set any. See Prices.
audit/The Code Audit Team’s clearances, findings, and security reports.
runs/Per-branch run state, such as the STATE.md a long piece of work saves before you resume it.
red-ledger/, worthiness-ledger/Records of tests seen failing first and of worthiness verdicts, one set per worktree. See Test-driven development.
forensics//gaia-forensics reports.
codify/, cleanse/, debt/State for /gaia-codify, /gaia-cleanse, and /gaia-debt.
fitness/The harness files an /update-gaia run blended. A /gaia-fitness run stops them counting toward its nudge. See The statusline nudge.
declined-updates.jsonUpdates you snoozed in /update-deps.
ports/Which ports each worktree’s dev servers use. See Ports per worktree.
cache/Caches that can be rebuilt, including the verified release bundles /update-gaia downloads (cache/shared/update-gaia/) and the statusline’s update check.
protected/State that only hooks or you may write, such as the audit loop’s state. Claude’s tools cannot write or delete anything here, so Claude cannot fake an approval. You need to touch it only if a state file is corrupt: then you move it aside from a terminal outside Claude Code, and Claude prints the exact command for you. See Audit markers and loop state.

state-registry.json is the complete list.

Because git ignores this folder, leftovers never show up in a diff, so GAIA cleans it up for you in two ways:

  • After a merge. The work lifecycle’s cleanup step removes the merged branch’s audit, run, and pull request state. See Clean up.
  • A daily sweep. At most once a day, at session start, a background job applies each entry’s cleanup rule from state-registry.json. Depending on the entry, that happens once its branch has been merged and deleted, once its worktree is gone, or after a set number of days. Some entries are kept for good: the usage ledger, setup state, and settings. The sweep works only from local information (git branches, the worktree list, the notebook catalog) and makes no network calls. It never leaves .gaia/local/, never follows a link, and never blocks a session.

Files that a removed feature left behind have no owner to clean them up. The leftover-file cleanup, which Claude offers once an update merges, deletes them, and only them.

Two more GAIA folders sit at the root of your project, beside .gaia/:

  • .notebook/ holds your SPECs, plans, and research. It is gitignored and lives in the main checkout. See the Notebook.
  • .gaia-backup/ holds a copy of every file /update-gaia overwrote, in one folder per run. It is gitignored. Once you commit an update, git history is the lasting record, and the next update clears out the old backups.

When GAIA sets up a worktree and finds a real folder where a link should go, it moves that folder aside to <name>.bak.<timestamp>, such as .gaia/local.bak.<timestamp>, instead of deleting it. These backups are gitignored too. Check one before you delete it, since it can hold state written in the worktree before it was set up. Ask Claude:

Compare .gaia/local.bak.<timestamp> with .gaia/local and tell me whether anything in the backup is worth keeping before I delete it.