Skip to content

Latest commit

 

History

History
188 lines (145 loc) · 7.39 KB

File metadata and controls

188 lines (145 loc) · 7.39 KB

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.jsoncozyvalues-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:

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:

git diff --name-only | xargs -n1 dirname | sort -u | grep ^packages/

Commit Format

Follow Conventional Commits with --signoff:

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:

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 scopearea/* 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:

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:

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. It includes the required release-note block.

Create the PR with gh pr create --title "type(scope): brief description" --body-file <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:

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:

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])"'