.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-gaiamerges 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-gaianever 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.
At a glance
Section titled “At a glance”| Path | What it is | In git | Written by | /update-gaia |
|---|---|---|---|---|
VERSION | The GAIA version your project is on | Committed | The release, then /update-gaia | Rewritten at the end of an update |
manifest.json | The list of files GAIA ships, each with its ownership class | Committed | The release, then /update-gaia | Replaced with the new release’s copy at the end of an update |
cli/ | The gaia CLI and the templates the scaffolders use | Committed | The release | Merged |
scripts/ | Scripts that hooks, commands, and skills run | Committed | The release | Merged |
statusline/ | The statusline scripts | Committed | The release | Merged |
templates/ | Starting files for your README and for UAT specs | Committed | The release | Merged |
migrations/ | Upgrade notes for steps a file merge cannot do | Committed | The release | Merged |
audit-ci.yml | The Code Audit Team roster | Committed | The release, then you | Merged key by key |
labels.json | Your repository’s GitHub labels | Committed | The release | Merged |
conventional-commits.json | The allowed commit and pull request title types | Committed | The release | Merged |
setup-features.json | The list of setup steps /setup-gaia owns | Committed | The release | Merged |
state-registry.json | What each entry in .gaia/local/ is and when it is cleaned up | Committed | The release | Merged |
packages.json | The packages in your workspace that GAIA covers | Committed | The release, then you | Never touched |
finding-classes.json | Your project’s own audit finding classes | Committed | The release (empty), then you | Never touched |
project.json | Team settings and project setup decisions | Committed | /gaia-init, /setup-gaia, /update-gaia | Never merged |
update-decisions.json | Your answers to update conflicts | Committed | /update-gaia | Written by the update itself |
audit-residual-dismissals.jsonl | Audit residuals you dismissed or kept | Committed | /gaia-residue | Never touched |
init-state.json | /gaia-init’s progress | Exists only while /gaia-init runs | /gaia-init | Not applicable |
local/ | Machine-local state | Gitignored | Hooks and commands | Never 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.
Files GAIA ships
Section titled “Files GAIA ships”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.
VERSION and manifest.json
Section titled “VERSION and manifest.json”.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.
scripts/
Section titled “scripts/”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.
statusline/
Section titled “statusline/”The scripts that draw GAIA’s statusline: the left side, the right side, and the context reading. See Statusline.
templates/
Section titled “templates/”templates/README.mdis the starting README for your project./gaia-initreplaces GAIA’s own README with this template and fills in your project’s title.templates/spec/uat-spec.ts.tmplis the starting file for a UAT spec, the test that checks one of a SPEC’s acceptance criteria. See/gaia-plan.
migrations/
Section titled “migrations/”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.
audit-ci.yml
Section titled “audit-ci.yml”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.
labels.json
Section titled “labels.json”The GitHub labels GAIA creates on your repository. gaia labels sync reads it. See Labels.
conventional-commits.json
Section titled “conventional-commits.json”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.
setup-features.json
Section titled “setup-features.json”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.
state-registry.json
Section titled “state-registry.json”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/.
Files your project writes
Section titled “Files your project writes”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.
packages.json
Section titled “packages.json”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.
project.json
Section titled “project.json”Team settings and project setup decisions, shared by everyone on the repository:
sandbox_recommendedandisolation_policy, the two team settings. See Team settings.fitness_commit_threshold, optional: how many of your team’s harness commits since the last/gaia-fitnessrun show its statusline nudge. Without it the count is 15. No command sets it, so ask Claude, for exampleSet 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 insetup-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.
update-decisions.json
Section titled “update-decisions.json”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.
audit-residual-dismissals.jsonl
Section titled “audit-residual-dismissals.jsonl”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.
init-state.json, while /gaia-init runs
Section titled “init-state.json, while /gaia-init runs”/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/
Section titled “.gaia/local/”.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.json | This machine’s setup progress and its decisions for machine-level setup steps. Deleting it makes /setup-gaia ask those questions again. |
settings.json | Per-machine choices, such as whether the statusline’s left side is GAIA’s or your own. /setup-gaia writes it. |
telemetry/usage.jsonl | The usage ledger behind every cost readout. See Cost and usage ledger. |
telemetry/token-rates.override.json | Your 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.json | Updates 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.
Cleaning up .gaia/local/
Section titled “Cleaning up .gaia/local/”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.
Next to .gaia/
Section titled “Next to .gaia/”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-gaiaoverwrote, 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.