Ctrl K
ComunidadGuías…

CONTRIBUTING.md

Rama develop · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Contributing to Agent Fleet

Thanks for your interest. This is a small project with a single maintainer, so a
few conventions keep things smooth.

## License of contributions

By submitting a contribution you agree it is licensed under the project's
**Apache License, Version 2.0** (see `LICENSE`). Apache-2.0 includes an explicit
patent grant; no separate CLA is required.

## Ground rules

- **Never commit secrets.** OAuth secrets, `AF_MASTER_KEY`, cookie secrets, and
  allowlists live in git-ignored files (`deploy/compose/.env`,
  `deploy/local/oauth.env`, `allowed-emails.txt`). Double-check `git diff` before
  committing. Compiled binaries are git-ignored too.
- **Install the pre-commit hook, once per clone** (its worktrees share it):
  ```bash
  git config core.hooksPath .githooks
  ```
  It runs the forbidden-token gate (`deploy/release/scan-forbidden.sh`) over the
  staged content only — a fraction of a second — and refuses the commit if a
  string this project keeps out of its history (real host names, customer names)
  is in it. `ci.yml`'s `release-scan` job runs the same scanner over the whole
  tracked tree on PRs and pushes, when the string is already in a commit. The
  local hook catches it before it enters history. Deliberate exception:
  a path glob in `deploy/release/forbidden.allow` — third-party content only.
- **Keep the core deploy-agnostic.** Don't bake Docker/compose assumptions into
  the Control Plane. Deployment specifics belong behind the ports (Runtime,
  KeyCustodian, MetadataStore, AuthGateway) — see `docs/build/09-deploy.md`.
- **Match the surrounding code.** Go: `gofmt` + `go vet` clean, `go test ./...`
  passing. Console: `npm run build` clean.
- **Run `gofmt` before every commit that touches Go — this is a hard gate.**
  `ci.yml` runs `gofmt -l .` per Go module and fails the build on *any* unformatted
  file, so `go build` / `go vet` / `go test` passing is not enough (editors and
  auto-format often skip `_test.go` files, which is exactly what has slipped through).
  Before committing, run in each touched module and make sure it prints nothing:
  ```bash
  (cd control-plane && gofmt -l .)      # lists unformatted files; empty = OK
  (cd workspace/agent && gofmt -l .)
  ```
  Fix with `gofmt -w <file>` (or `gofmt -w .`). Same for `go vet ./...`.

## Building & running

Local dev (host process): `deploy/local/run-dev.sh`.
On-prem container deploy: `deploy/compose/` (see its `README.md`).

Build paths on the dev host are non-standard: Go is at `$HOME/.local/go/bin`,
Node via nvm. The compose image build is self-contained (multi-stage) and needs
neither on the host — only Docker.

## Commits & PRs

**`main` is the trunk** (and the default branch). Day-to-day changes go through
a work branch → pull request → `main`; do not push them directly to `main`.
Issues are optional; link the related issue in the PR when one exists. The
maintainer checks the changes and verification results, then merges after the
required CI checks pass. Approval from another person is not required.
**"done" means merged into `main`**. Agents must not create branches on their
own — the Console hands each worktree session its own branch. Remote:
`git@github.com:k-k1/agent-fleet.git`.

Hosted CI comes in two tiers. `ci.yml` (gofmt / vet / test /
build) runs on **pull requests targeting `main` and every push to it**. PR checks
verify changes before they reach the trunk; push checks verify the integrated
result. The repository is public, so its runners are free. `e2e.yml` and the
contract workflows do spend an external LLM quota, so those stay concentrated on
pull requests that touch their paths, their nightly / weekly cron over `main`, and
manual dispatch (several contract workflows run on dispatch only).
Either way, the local run above is still the per-commit check — hosted CI is the
safety net, not the first line. See `docs/log/35-packaging.md` for the billing
rationale behind this two-tier split. Hotfixes follow the same path as any other
change: work branch → PR → `main`. Release tags, release builds and public
distribution are all cut from `main`.

Keep commits small and focused, and follow the format below.

### Commit message format

Conventional Commits, written in **English** — both the subject and the body. PR
titles and descriptions and GitHub issues are written in English as well. The commit
history doubles as the design record; history before 2026-09-25 is in Japanese and
stays as it is (do not rewrite it).

```
<type>(<scope>): <summary>      ← first line, no trailing period, imperative, ~50 chars

<body>                          ← after one blank line. What changed and why. For a
                                   behaviour change: root cause → fix → verification.
                                   Wrap at ~72 columns.

Co-Authored-By: <model name> <noreply@<vendor>>   ← trailer, separated by a blank line
```

- **type**: `feat` / `fix` / `docs` / `style` (formatting only, no behaviour change) /
  `refactor` / `perf` / `test` / `build` (baked-in CLI or dependency version bumps) /
  `chore` / `ci`.
- **scope**: the main subject of the change. Real examples: `console`, `chat`,
  `cp` (= control-plane), `agent`, `mirror`, `tts`, `workspace`, `deploy`; docs use
  `docs(NN)` with the chapter number. Add one whenever you can.
- **body**: for bug fixes and behaviour changes, record **root cause → fix →
  verification (how you actually checked)**. This project is verified against a live
  fleet, and the commit history is the only design record of that.
- **migration**: schema changes (`control-plane/internal/store/migrations/`, and
  `migrations-pg/` beside it for Postgres) must be forward
  compatible, and say so in the body — the embedded migrator applies them
  automatically at Control Plane startup and there is no downgrade path.

### Attribution (the Co-Authored-By trailer)

This project is built with Claude Code, Codex and opencode side by side. A commit an
agent wrote carries a co-author trailer after a blank line, naming **the model that
actually generated it** — attribution is by model, not by CLI.

| Environment | Example trailer |
|-------------|-----------------|
| Claude Code | `Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>` (or `Claude Fable 5` etc., matching the running version) |
| Codex | `Co-Authored-By: GPT-5.6 <noreply@openai.com>` |
| opencode | Attributed to the running model (Claude family → `@anthropic.com`, GPT family → `@openai.com`) |

- The address is the vendor's `noreply@` domain (Anthropic = anthropic.com,
  OpenAI = openai.com, otherwise the model vendor's domain). Name the model version
  actually running — don't hardcode one.
- **No session URL line.** The old `Claude-Session:` trailer is retired. Claude Code
  adds one by itself when driven over Remote Control; that is harmless and accepted
  (suppress it in Claude Code's own settings, not here).
- **Always add `Co-Authored-By:`** when an agent was involved — at least one line, and
  several lines when several models or people were. It may be omitted only for a
  commit written purely by a human.