cozystack/docs/agents/contributing.md
Aleksei Sviridkin 90a9d6e905
docs(contributing): sync scope lists and fix lint nits
Address review feedback:
- Sync scope lists between PR template and contributing guide
- Add 'maintenance' to Other scopes in contributing guide
- Add scope to the docs example for format consistency
- Simplify rebase push example to drop unnecessary refspec mapping
- Add 'text' language tag to trailer code fence (MD040)

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-04-13 23:09:27 +03:00

3.8 KiB

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

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:

  • System: dashboard, platform, cilium, kube-ovn, linstor, fluxcd, cluster-api
  • Apps: postgres, mariadb, redis, kafka, clickhouse, virtual-machine, kubernetes
  • Other: api, hack, tests, ci, docs, 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"

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