Files
cozystack/docs/agents/contributing.md
Myasnikov DaniilandClaude Opus 4.8 680e3cc47b docs(agents): clarify PR description template wording
Address review feedback from gemini-code-assist on docs/agents/contributing.md:145:
reword the awkward "on the template" phrasing to "aligned with the template".

Assisted-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-06-19 11:22:02 +05:00

203 lines
8.2 KiB
Markdown

# Contributing Conventions for AI Agents
Project-side conventions for commits, branches, and pull requests in Cozystack.
## Checklist for Creating a Pull Request
- [ ] Commit message follows Conventional Commits format
- [ ] Commit is signed off with `--signoff`
- [ ] Branch is rebased on `upstream/main` (no extra commits)
- [ ] PR body includes description and release note
- [ ] Ran `make generate` in every package whose `values.yaml`, `values.schema.json`, `Chart.yaml`, or `README.md` was touched, and committed the regenerated files
## Regenerate Artifacts Before Committing
Several files in each package are produced by `make generate` from `values.yaml` + `values.schema.json` and must stay in sync with the hand-edited sources:
- `packages/(apps|extra)/<name>/README.md` — regenerated by `cozyvalues-gen` (parameter table, formatting).
- `packages/(apps|extra)/<name>/values.schema.json` — `cozyvalues-gen` rewrites ordering and derived fields.
- `packages/system/<name>-rd/cozyrds/<name>.yaml` — produced by `hack/update-crd.sh`, which `make generate` invokes.
**Before committing edits to any of those sources**, run `make generate` inside the package and stage the full diff:
```bash
make -C packages/<apps-or-extra>/<name> generate
git add packages/<apps-or-extra>/<name>/ packages/system/<name>-rd/
```
The repo's pre-commit CI job runs `make generate` in every package and then `git diff --exit-code`. Any unstaged generator output fails the job with exit code 123 and blocks the PR. Also rerun `make generate` after a `git commit --amend` if the amended change touched any of the sources above.
To locate packages a WIP branch likely needs to be regenerated:
```bash
git diff --name-only | xargs -n1 dirname | sort -u | grep ^packages/
```
## Verify Before Considering Done
After every change, verify the relevant artifacts before opening the PR:
- **Helm templates changed:** `make show` in the package directory renders cleanly.
- **Go code changed:** `make generate && make manifests` at the repo root, then commit the regenerated files.
- **`values.yaml` annotations changed:** `make generate` in the package; review the `values.schema.json` diff.
- **Migration scripts:** confirm what data actually exists first with `kubectl get <resource> -A -o jsonpath='{...}'` on a dev cluster before writing the logic.
## Commit Format
Follow [Conventional Commits](https://www.conventionalcommits.org/) with `--signoff`:
```bash
git commit --signoff -m "type(scope): brief description"
```
**Types:** `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`
**Scopes** (examples — not an exhaustive list; pick the most specific scope that describes the change, and introduce a new one if a genuinely new area needs its own):
- System, e.g.: `dashboard`, `platform`, `operator`, `cilium`, `kube-ovn`, `linstor`, `fluxcd`, `cluster-api`
- Apps, e.g.: `postgres`, `mariadb`, `redis`, `kafka`, `clickhouse`, `virtual-machine`, `kubernetes`
- Other, e.g.: `api`, `hack`, `tests`, `ci`, `docs`, `agents`, `maintenance`
Breaking changes: append `!` after type/scope (`feat(api)!: ...`) or add a `BREAKING CHANGE:` footer.
**Examples:**
```bash
git commit --signoff -m "feat(dashboard): add config hash annotations to restart pods on config changes"
git commit --signoff -m "fix(postgres): update operator to version 1.2.3"
git commit --signoff -m "docs(contributing): add installation guide"
```
## PR Title Auto-Labeling
`.github/workflows/pr-labeler.yaml` parses the PR title on `opened`, `edited`, `reopened`, and `synchronize` events and applies labels additively (never removes). The title is expected to follow Conventional Commits — same format as commit messages above.
**Type → `kind/*`:**
| type | label |
| --------- | ------------------ |
| feat | kind/feature |
| fix | kind/bug |
| docs | kind/documentation |
| chore | kind/cleanup |
| refactor | kind/cleanup |
| style, perf, test, build, ci, revert | (no kind label) |
**Scope → `area/*`** (full mapping in `.github/workflows/pr-labeler.yaml`):
| scope (examples) | label |
| --- | --- |
| agents, ai | area/ai |
| api, cozystack-api | area/api |
| build | area/build |
| ci | area/ci |
| dashboard | area/dashboard |
| postgres, mariadb, redis, etcd, kafka, clickhouse, postgres-operator, mariadb-operator | area/database |
| extra | area/extra |
| kubernetes | area/kubernetes |
| monitoring, vlogs, vmstack, grafana, workloadmonitor | area/monitoring |
| ingress, gateway, vpn, metallb, cilium, kube-ovn, cozy-proxy, ... | area/networking |
| platform, bundle, flux, fluxcd, cluster-api, talos, installer, cozyctl, cozystack-engine, cozy-lib | area/platform |
| backport, release | area/release |
| seaweedfs, bucket, linstor, velero, harbor, backups | area/storage |
| tests, e2e | area/testing |
| kubevirt, cdi, vmi, vm-import, virtual-machine, hami, gpu-operator | area/virtualization |
**Special handling:**
- `[Backport release-1.x]` prefix is stripped before parsing; `area/release` and `backport` labels are added.
- Composite scope (`feat(platform, system, apps): ...`) — each comma-separated part is mapped independently.
- `!` after type or `BREAKING CHANGE:` footer in the body → `kind/breaking-change`.
- Unmapped scope or non-conventional title → `area/uncategorized` (signals the PR needs manual area selection).
- Bracket-style fallback (`[scope] description`) maps `scope` → `area/*` but cannot infer `kind/*`.
### AI Agent Attribution
When an AI agent authors or materially assists with a commit, add an `Assisted-By:` trailer naming the model:
```text
Assisted-By: Claude <noreply@anthropic.com>
Assisted-By: GPT-5 <noreply@openai.com>
Assisted-By: Gemini <noreply@google.com>
```
This sits alongside the `Signed-off-by:` trailer produced by `--signoff`. Use one trailer per model if multiple contributed.
## Rebasing on upstream/main
If the branch has extra commits, clean it up:
```bash
git fetch upstream
git checkout -b my-feature upstream/main
git cherry-pick <your-commit-hash>
git push -f origin my-feature
```
## Pull Request Body
Fill in the template at [`.github/PULL_REQUEST_TEMPLATE.md`](../../.github/PULL_REQUEST_TEMPLATE.md). It includes the required `release-note` block.
Create the PR with `gh pr create --title "type(scope): brief description" --body-file <file>`.
## Review Expectations
- A bug-fix PR should include a behavioural regression test, not just a single field assertion — prove the bug can no longer recur.
- Keep the PR description aligned with the template: `## What this PR does` plus the `release-note` block.
## Fetching Unresolved Review Comments
Cozystack uses GitHub review threads with resolution status. Only unresolved threads are actionable — resolved threads are already handled.
The REST endpoint `/pulls/{pr}/reviews` returns review summaries, not individual review comments. Use the GraphQL API to access `reviewThreads` with `isResolved` status:
```bash
gh api graphql -F owner=cozystack -F repo=cozystack -F pr=<PR_NUMBER> -f query='
query($owner: String!, $repo: String!, $pr: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $pr) {
reviewThreads(first: 100) {
nodes {
isResolved
comments(first: 100) {
nodes {
id
path
line
author { login }
bodyText
url
createdAt
}
}
}
}
}
}
}' --jq '.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false) | .comments.nodes[]'
```
Compact one-line variant:
```bash
gh api graphql -F owner=cozystack -F repo=cozystack -F pr=<PR_NUMBER> -f query='
query($owner: String!, $repo: String!, $pr: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $pr) {
reviewThreads(first: 100) {
nodes {
isResolved
comments(first: 100) {
nodes {
path
line
author { login }
bodyText
}
}
}
}
}
}
}' --jq '.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false) | .comments.nodes[] | "\(.path):\(.line // "N/A") - \(.author.login): \(.bodyText[:150])"'
```