Shape: Single Repo

A single-repo project has one language, one deployable unit, and one directory of source code. This is the simplest project shape and needs the least configuration.

When this shape applies

Use the single-repo shape when your project is:

  • A backend service in one language (Python, Go, Java, Rust, etc.).
  • A command-line tool.
  • A library or SDK published to a package registry.
  • A web application built with a server-rendered framework (Rails, Django, Laravel, Phoenix).
  • A mobile app in a single framework (Flutter, React Native, SwiftUI, Kotlin).

If your project has a separate frontend and backend, see Shape: Full-Stack instead.

Folder layout

A typical single-repo project looks like this:

my-project/
  .gaia/
    config/
      project-config.yaml
  docs/
    planning-artifacts/
    implementation-artifacts/
  src/           # (or lib/, app/, cmd/ -- depends on the language)
  tests/         # (or test/, spec/, __tests__/)
  package.json   # (or pyproject.toml, go.mod, Cargo.toml, etc.)
  README.md

The key point: there is one source directory and one test directory. GAIA's stacks configuration has a single entry.

Configuration

Here is a complete project-config.yaml for a single-repo project. This example uses a Node.js service, but the structure is the same for any language.

# .gaia/config/project-config.yaml
project:
  name: my-service
  description: A REST API for managing widgets

stacks:
  - name: api
    language: typescript
    framework: express
    path: .
    test_command: npm test
    build_command: npm run build
    lint_command: npm run lint

platforms:
  - web

ci_cd:
  platform: github-actions
  preset: small-team
  promotion_chain:
    - staging
    - production

environments:
  staging:
    url: https://staging.my-service.example.com
    auto_deploy: true
  production:
    url: https://my-service.example.com
    auto_deploy: false
    requires_approval: true

test_execution:
  default_command: npm test
  timeout_seconds: 120
  coverage:
    enabled: true
    threshold: 80

Key points:

  • stacks has exactly one entry.
  • path: . means the stack lives at the repository root. No path-based CI filters needed.
  • platforms has one entry. For a CLI tool, use cli. For a mobile app, use ios, android, or mobile.

Edit the stacks section with /gaia-config-stack and the platforms section with /gaia-config-platform.

CI setup

With a single stack and no path filters, CI is straightforward. Every change triggers the same test suite.

/gaia-ci-setup

For a solo developer, the solo preset generates a single workflow that runs on push to main. For a small team, the small-team preset generates a PR-triggered workflow with concurrency cancellation.

Because there is only one stack, you do not need path filters, parallel jobs, or matrix builds. One job runs lint, tests, and build in sequence.

Language-specific examples

Python service

stacks:
  - name: api
    language: python
    framework: fastapi
    path: .
    test_command: pytest --cov=src --cov-report=term-missing
    build_command: docker build -t my-service .
    lint_command: ruff check src/

Go CLI tool

stacks:
  - name: cli
    language: go
    path: .
    test_command: go test ./...
    build_command: go build -o bin/mytool cmd/mytool/main.go
    lint_command: golangci-lint run

Rails web application

stacks:
  - name: web
    language: ruby
    framework: rails
    path: .
    test_command: bundle exec rspec
    build_command: bundle exec rails assets:precompile
    lint_command: bundle exec rubocop

Flutter mobile app

stacks:
  - name: mobile
    language: dart
    framework: flutter
    path: .
    test_command: flutter test
    build_command: flutter build apk
    lint_command: flutter analyze

platforms:
  - android
  - ios

For mobile apps, list each target platform separately. This tells GAIA to include platform-specific testing (device matrix, responsive testing) in your test strategy. See /gaia-config-device-target for device matrix configuration.

What to read next