# 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)//README.md` — regenerated by `cozyvalues-gen` (parameter table, formatting). - `packages/(apps|extra)//values.schema.json` — `cozyvalues-gen` rewrites ordering and derived fields. - `packages/system/-rd/cozyrds/.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// generate git add packages/// packages/system/-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/ ``` ## 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 Assisted-By: GPT-5 Assisted-By: Gemini ``` 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 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 `. ## 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= -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= -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])"' ```