CLAUDE.md · git:20261015.dd897ab · 2026-10-15 · sha256 bc87f6ca7c646e59

CLAUDE.md git:20261015.dd897abA

Immutable. This exact content never changes and is served at /api/v1/blob/bc87f6ca7c646e59.

# CLAUDE.md — Contributor Guide

Read by Claude Code and other coding agents at session start, and by people.
It holds the rules that are easy to get wrong, the commands, and links to the
one place each longer procedure lives. Keep it short; the detail belongs in
the files it points at.

| For | Read |
|---|---|
| Workstation setup, toolchain pins | [SETUP.md](SETUP.md) |
| Contribution steps, licensing, checks, flakes, API changes | [CONTRIBUTING.md](CONTRIBUTING.md) |
| Server implementation conventions | [contributing/server.md](contributing/server.md) |
| CI jobs, coverage, partitions, cutting a release | [scripts/ci/README.md](scripts/ci/README.md) |
| Writing and checking manual pages | [contributing/docs.md](contributing/docs.md) |
| The hex-pinned `managoat_*` libraries | [contributing/component-libraries.md](contributing/component-libraries.md) |
| Why things are the way they are | [decisions/index.md](decisions/index.md) |
| Reference templates | [standards/](standards/) |
| Configuration reference | [docs/configuration.md](docs/configuration.md), `.env.example` |

## Product and goal

What Fountain is, in one paragraph, is the top of [README.md](README.md).
Fountain is a self-hosted product. Since 2026-10-12 it is not sold as a
hosted service: there is no billing, no growth target and no product
analytics, and `managoat.com` is the maintainer's own instance. No org/team
(shared people) or billing features are planned.

## Commands

```bash
mise install                        # Erlang/OTP 28 + Elixir 1.19.2, from .tool-versions
mix deps.get && mix setup           # dev DB: create + migrate
MIX_ENV=test mix ecto.create && MIX_ENV=test mix ecto.migrate
mix test                            # the whole suite, from the umbrella root
mix precommit                       # server checks: static analysis, sobelow, release assemble
mix precommit --full                # server checks plus the whole suite
mix precommit --list                # its stages; `mix precommit credo test` runs a subset
gh pr merge <N> --squash --admin    # merge a PR once its CI is green
```

Choose local checks by the changed component in
[CONTRIBUTING.md](CONTRIBUTING.md#before-you-push). Go, SDK and tooling changes
use their own checks; mixed changes combine the relevant checks.

Run every mix command through the pinned toolchain (activate mise, or
`mise exec -- mix ...`). `mix precommit` refuses a different Elixir.

## Repo layout

```
apps/fountain/             the server (Apache-2.0): lib/fountain/ contexts, lib/fountain_web/ Phoenix
  test/support/            DataCase, ConnCase, factory.ex
cli/                       the Go CLI module (Apache-2.0); test it from its own directory
sdk/                       the TypeScript client, against one OpenAPI contract (CONTRIBUTING.md)
site/                      the managoat.com pages: a static Mix build, its own image and CI (site/README.md)
config/                    config.exs, dev.exs, test.exs, prod.exs, runtime.exs
decisions/                 ADRs, an OKF bundle; start at decisions/index.md
docs/                      the public manual, served at /docs and nowhere else
standards/, contributing/  unpublished contributor material
scripts/                   the gates and the CI helpers (scripts/ci/README.md)
```

The two `managoat_*` libraries Fountain uses (broker, docs) are hex
packages pinned in `apps/fountain/mix.exs`, not directories here. The ACP
peer, the sandbox adapters, the runtimes and the OAuth grant state machine
were folded back on 2026-10-15 and live in `apps/fountain/lib/managoat/`,
keeping their `Managoat.*` names. Both are tabled in
[contributing/component-libraries.md](contributing/component-libraries.md).

## The four primitives

| Primitive | Purpose |
|---|---|
| **Environment** | Baseline set of encrypted env vars + runtime config (packages, repos, scripts) attached to an agent. A conversation may name a different one at launch (`environment_id`, scoped by `agent.allowed_environment_ids`) |
| **Vault** | Free-floating bag of env-var overrides. Vault values **win on key collision** when merged with an environment at sandbox spawn |
| **Agent** | A named, re-runnable agent config: model, runtime, skills, MCP servers, optional environment |
| **Conversation** | A single run of an agent inside a sandbox of its own ([ADR 0073](decisions/0073-one-sandbox-per-conversation.md)). Has turns, log events, and a status lifecycle |

## Rules

- Scope every user-facing query by `user_id`; never use `_unsafe_*` as the
  first fetch in a user-facing request.
- Audit mutations in the context, with caller attribution. Never log secret
  values or audit inside a transaction.
- Keep unbuilt behavior explicit in decisions and documentation.
- Check that an issue or PR reference supports its accompanying statement.
- Use the pinned toolchain and focused tests while iterating.

For server changes, read the relevant sections of
[Server conventions](contributing/server.md): tenant ownership, encryption,
audit, UI boundaries, web plumbing and test setup. Read a procedure
when the change touches it; the linked guides are reference material.

## How a change lands

[CONTRIBUTING.md](CONTRIBUTING.md#pull-requests) owns the landing procedure,
release notes and ADR trigger. Every change is a PR, merged with an admin
squash merge once its CI is green; there is no merge queue and no required
review. Never push directly to `main`: the Release bump workflow is the one
thing that does. CI administration lives in [scripts/ci/README.md](scripts/ci/README.md).

## Docs and decisions

- For documentation-only changes, use the focused checks in
  [contributing/docs.md](contributing/docs.md); contributor-only Markdown
  needs no Elixir suite.
- `docs/` is published at `/docs` only. A page not in `docs/nav.yml` fails the
  suite, as does a dead internal link or anchor. The structural rules and
  writing guidance are in [contributing/docs.md](contributing/docs.md).
- Architecturally significant choices are ADRs in `decisions/`, an OKF
  bundle: copy `decisions/0001-template.md`, then run
  `scripts/decisions-index.sh` and `okf validate decisions` in the same PR.
  `okf backlinks decisions <id>` lists what depends on one before amending it.