Skip to content

Contributing to this project

First off, thanks for taking the time to contribute! โค๏ธ

1. Code of Conduct

This project and everyone participating in it is governed by the Code of Conduct. By participating, you are expected to uphold this code. Please report unacceptable behavior.

2. Team members:

  • Amine DJEGHRI

3. Best practices ๐Ÿ’ก

  • Docstring your functions and classes, it is even more important as it is used to generate the documentation with Mkdocs
  • If you use an IDE (like PyCharm), define src the "source" folder and test the "test" folder so your IDE can help you auto import files
  • Use the make commands to run your code, it is easier and faster than writing the full command (and check the Makefile for all available commands ๐Ÿ˜‰)

    • Run Use the pre-commit hooks to ensure your code is formatted correctly and is of good quality
    • UV is powerful (multi-thread, package graph solving, rust backend, etc.) use it as much as you can.
    • If you have a lot of data, use Polars for faster and more efficient dataframe processing.
    • If you have CPU intensive tasks, use multiprocessing with python's pool map.
  • Secret scanning (detect-secrets is configured as a pre-commit hook):

    • Use .pre-commit-config.yaml for structural/file-level exclusions โ€” e.g. excluding whole files, known autogenerated patterns, or recurring false-positive lines across the repo (--exclude-files, --exclude-lines, --exclude-secrets args).
    • Use # pragma: allowlist secret as an inline comment on any line that intentionally references a secret (e.g. ${{ secrets.GITHUB_TOKEN }}). This suppresses the false positive for that specific line only, without loosening repo-wide rules.
    • Do not add entire workflow or config files to --exclude-files just to silence one line โ€” use the inline pragma instead, so real leaks in those files are still caught.
  • Exceptions:

    • Always log the exceptions and errors (use loguru) and then raise them
          except Exception as e:
            logger.error(e)  # Log the original error  with a personalized message or with e (only the message will be logged)
            raise e # All the stack trace will be logged
      
    • Sometimes, you don't need to raise the exception (in a loop for example) to not interrupt the execution.
    • Use if else instead of catching and raising the exception when possible (log and raise also)
          if not os.path.exists(file_path):
              logger.error(f"File not found: {file_path}. The current directory is: {os.getcwd()}")
              raise FileNotFoundError(f"The file {file_path} does not exist.")
      

3.1 Security

This project implements a multi-layer security approach:

Local Security (Pre-commit): - actionlint โ€” validates GitHub Actions workflow syntax - zizmor โ€” detects CI/CD security vulnerabilities in workflows - detect-secrets โ€” prevents accidental secret commits - pip-audit โ€” scans dependencies for known vulnerabilities

CI Security (GitHub Actions): - Renovate โ€” manages dependencies with 7-day cooldown to prevent malicious releases

4. How to contribute

4.1 File structure (๐ŸŒณ Tree)

Check the readme file.

4.2 Steps for Installation (Contributors and maintainers)

a. Local development

  • Requires Linux, Windows or MacOS.
  • Python and UV will be installed if they are not already installed.
  • git clone the repository
  • Install the package in editable mode with one of the following commands :
    • make install-dev the recommended way. it uses uv and will automatically create a .venv folder inside the project and install the dependencies
    • or uv pip install -e . will install the package in your selected environment (venv, or conda or ...)
  • Run the scripts locally with ./install_unix.sh for Linux and macOS or ./install_windows.ps1 for Windows.
  • run make pre-commit install to install pre-commit hooks
  • To install the GitHub actions locally, run make install-act

b. or Develop in a container

  • You can run a docker image containing the project with make docker-prod (or make docker-dev if you want the project to be mounted in the container).
  • Inside the container, run make test-installation and make test
  • A venv is created inside the container and the dependencies are installed.
  • (Optional) UV will handle this automatically. However, if you face a problem with the .venv folder, and you have a .venv folder locally and also mounted in the container, you need the local one with (rm -rf .venv); otherwise it will create a conflict since the project is mounted in the container.

4.3. Run the test to see if everything is working

  • Create a .env file (take a look at the .env.example file):
  • Test the package with :
    • make test-installation Will print a hello message
    • make test will run all the tests (requires .env file)

4.4. Pushing your work

  • Before you start working on an issue, please comment on (or create) the issue and wait for it to be assigned to you. If someone has already been assigned but didn't have the time to work on it lately, please communicate with them and ask if they're still working on it. This is to avoid multiple people working on the same issue. Once you have been assigned an issue, you can start working on it. When you are ready to submit your changes, open a pull request. For a detailed pull request tutorial, see this guide.

  • Create a branch from the dev branch and respect the naming convention: feature/your-feature-name or bugfix/your-bug-name.

  • Before committing your code:

    • Run make test to run the tests.
    • Run make pre-commit to check the code style & linting.
    • Run make deploy-doc-local to update the documentation locally and test the website.
    • Commit messages follow Conventional Commits โ€” see the Commit Convention section below.
    • Useful references for git emojis (optional):
    • To stay up to date with dev, sync it into your feature branch:
      • If branch already pushed: git merge dev then git push โ€” safe, no force-push needed
      • If branch not yet pushed: git rebase dev โ€” cleaner history, no merge commit
    • After syncing, run make test and make pre-commit again to ensure tests still pass.
    • Do NOT manually update the version in pyproject.toml โ€” semantic-release handles this automatically on merge.
    • Run CI/CD Locally (optional): run make install-act then make act for GitHub Actions (Docker must be running).
    • Open a pull request targeting dev. Make sure the PR title follows the commit convention (e.g. feat: add new plugin) โ€” it becomes the squash commit message used by semantic-release to determine the version bump.

4.5. Renovate: automatic dependency updates

  • Renovate configuration can be found here
  • How it works:
  • Renovate runs on a schedule
  • Creates PRs for dependency updates with the renovate label
  • 7-day cooldown for all dependencies โ€” prevents immediate adoption of potentially malicious releases
  • PyPI dependencies are not pinned โ€” we rely on uv.lock for reproducible builds. Renovate updates pyproject.toml version constraints, and uv.lock is regenerated to lock specific versions
  • Security PRs are not grouped โ€” vulnerability alerts bypass grouping rules and open immediately as standalone PRs to ensure fast remediation
  • What to do:
  • Review all Renovate PRs manually โ€” they require human approval before merging
  • A dependency dashboard issue is created automatically to track pending updates
  • Check the PR details, then merge when confident

4.6. (For repository maintainers) Merging strategies & GitHub Actions guidelines

  • All PRs use Squash merge โ€” enforced at repository level (no merge commits, no rebase merge via PR).
  • The PR title becomes the squash commit message โ€” it must follow the commit convention so semantic-release can determine the version bump.

GitHub Actions workflows

Workflow Trigger Purpose
๐Ÿ” PR โ€” Quality Checks (ci.yml) pull_request on any branch Pre-commit (actionlint, ruff, detect-secrets, pip-audit) + tests on every PR
๐Ÿ”ถ Release โ€” Dev (dev-release.yml) push to dev (non [skip ci]) RC versioning (v1.1.0-rc.X), builds & publishes prerelease to GitHub
๐Ÿš€ Release โ€” Main (main-release.yml) push/PR merged to main Stable versioning (v1.1.0), builds, publishes to GitHub Releases + deploys docs
๐Ÿค– Renovate (renovate.json) Schedule (Renovate app) Updates GitHub Actions and Python dependencies with 7-day cooldown + selective automerge

Release & Deployment Flow

Branch Commit Type Release? Version Pattern Result
dev feat:, fix:, perf: โœ… RC v1.1.0-rc.1 โ†’ v1.1.0-rc.2 Prerelease on GitHub
dev feat!: / BREAKING โœ… RC v2.0.0-rc.1 Major prerelease on GitHub
dev docs:, chore:, ci:, style:, refactor:, build:, test: โŒ No โ€” No release
main feat:, fix:, perf: โœ… Stable v1.1.0 Stable release + GitHub + docs deploy
main feat!: / BREAKING โœ… Stable v2.0.0 Major stable release + GitHub + docs deploy
main docs:, chore:, ci:, style:, refactor:, build:, test: โŒ No โ€” No release, no docs deploy

Key points: - RC (release candidate) versions allow testing before stable release - Each releasable commit on dev bumps the rc counter: 1.1.0-rc.1 โ†’ 1.1.0-rc.2, etc. - When dev is squash-merged into main, semantic-release on main evaluates the squashed commit and releases the stable version (e.g. 1.1.0) - Release bump commits always include [skip ci] to prevent infinite loops - Syncing main โ†’ dev after a release is a regular merge commit โ€” [skip ci] guard in dev-release.yml prevents a spurious RC

Merging Strategy

Merge Strategy Why
feature/* โ†’ dev Squash Clean history, one conventional commit per feature
dev โ†’ main Squash One commit for semantic-release to evaluate on main
main โ†’ dev (sync) Merge commit (direct push, no PR) Direct local push โ€” squash enforcement only applies to PRs; [skip ci] in release bump commits skips dev pipeline

Step-by-step: shipping a feature to production

1. Sync local dev with remote dev (always fast-forward, no conflicts):
   git checkout dev
   git pull          โ† always do this before branching

2. Branch off dev:
   git checkout -b feature/my-feature

3. Commit with conventional commits:
   feat: add new plugin
   fix: handle auth error

4. (Optional) Sync feature branch with dev if dev moved forward:
   git merge dev     โ† if branch already pushed to remote (safe)
   (not recommended) git rebase dev    โ† if branch is only local (cleaner history)
   Note: either way it gets squashed on PR merge, so both are fine

5. Open PR: feature/my-feature โ†’ dev
   โ†“ PR title must follow commit convention (e.g. "feat: add new plugin")
   โ†“ ๐Ÿ” PR โ€” Quality Checks runs (pre-commit + tests)
   โ†“ Squash merge into dev
   โ†“ ๐Ÿ”ถ Release โ€” Dev runs โ†’ creates v1.1.0-rc.1 on GitHub (prerelease)

6. (Repeat steps 2-5 for more features)
   Each releasable squash commit โ†’ v1.1.0-rc.2, v1.1.0-rc.3, etc.

7. When ready for production, open PR: dev โ†’ main
   โ†“ ๐Ÿ” PR โ€” Quality Checks runs
   โ†“ Squash merge into main
   โ†“ ๐Ÿš€ Release โ€” Main runs โ†’ creates v1.1.0 (stable) + GitHub Release + Docs

8. Sync main back into dev:
   git checkout dev && git pull
   git merge main   โ† regular merge commit (direct push, no PR)
   (The [skip ci] in the release bump commit prevents a spurious RC)

   โš ๏ธ  Expect conflicts on CHANGELOG.md and pyproject.toml โ€” this is normal.
   Both were modified by semantic-release on both branches (RC on dev, stable on main).
   Always keep MAIN's version (stable wins over RC):
   git merge --continue   # git pre-fills "Merge branch 'main' into dev" โ€” keep as-is
   git push

   After this, dev has v1.0.0 as base โ†’ next RC will correctly be v1.1.0-rc.1

GitHub pages automatic deployment :

  • You need to give the right permissions to GithubAction: Check that "Read and write permissions" are enabled in your project: Settings -> Actions -> General -> Workflow permissions : Read and write permissions
  • You need to have the branch 'gh-pages' in your repository after following this tutorial. You can create it with make deploy-doc-gh, it will prompt you for your creds, go to https://github.com/settings/tokens and generate a classic key (your password), then push the documentation to the gh-pages branch.
  • In GitHub Pages settings, select 'Deploy from a branch' and select 'gh-pages' branch.
  • From the next time, GitHub Actions will run automatically and deploy it from the main branch.

Issues: - GitHub Actions will run automatically and deploy it from the main branch. If you face an error saying that your branch is not allowed to deploy to github-pages, check this issue" - (Optional) If you want to manually deploy the documentation, run make deploy-doc-gh it will push the documentation to the gh-pages branch

Creating tags for releases

Tags are created automatically by semantic-release on merge to main or dev. Do NOT create tags manually โ€” it will confuse semantic-release and cause incorrect version bumps.

Commit Convention

Format: [emoji] <type>[(<scope>)][!]: <description>

The emoji prefix is optional โ€” both forms are valid:

feat: add token refresh
โœจ feat: add token refresh


Types & Release Impact

Emoji Type Aliases Release Example
๐Ÿ› fix bugfix, hotfix ๐ŸŸข patch ๐Ÿ› fix(llm): handle null response from API
โšก๏ธ perf โ€” ๐ŸŸข patch โšก๏ธ perf(cache): reduce install time
โœจ feat feature ๐ŸŸก minor โœจ feat(auth): add GitHub App token rotation
๐Ÿ’ฅ feat! BREAKING CHANGE footer ๐Ÿ”ด major ๐Ÿ’ฅ feat!: redesign agent card schema
โ™ป๏ธ refactor โ€” โ€” โ™ป๏ธ refactor: clean up plugin factory
๐Ÿ“ docs โ€” โ€” ๐Ÿ“ docs: update README setup section
๐ŸŽจ style โ€” โ€” ๐ŸŽจ style: format with ruff
๐Ÿงช test โ€” โ€” ๐Ÿงช test: add unit tests for auth module
๐Ÿ”ง chore โ€” โ€” ๐Ÿ”ง chore: bump dependencies
๐Ÿ‘ท ci โ€” โ€” ๐Ÿ‘ท ci: optimize uv cache strategy
๐Ÿ“ฆ build โ€” โ€” ๐Ÿ“ฆ build: update hatchling config

Scopes (optional)

Use a scope to narrow the context:

feat(github): ...
fix(llm): ...
perf(cache): ...
ci(runner): ...

Common scopes: github, llm, auth, agent, cache, api, ci, deps


Breaking Changes

Two ways to declare a breaking change (triggers major bump):

1. ! after the type:

feat!(auth): remove legacy token endpoint

2. BREAKING CHANGE: footer:

feat(auth): redesign token flow

BREAKING CHANGE: /v1/token removed, use /v2/auth instead


Examples

โœจ feat(github): add support for GitHub App token rotation
๐Ÿ› fix(llm): handle empty response from LLM API
โšก๏ธ perf(cache): persist .venv between CI runs
๐Ÿ’ฅ feat!(agent): redesign A2A message schema

โ™ป๏ธ refactor: clean up plugin factory initialization
๐Ÿ“ docs: add deployment guide for Kubernetes
๐Ÿงช test: add integration tests for GitHub auth flow
๐Ÿ”ง chore: update pre-commit hooks
๐Ÿ‘ท ci: add pull_request trigger to pipeline

5. GitHub Actions Philosophy: Local-First Testing

We follow a local-first approach for testing and quality checks. GitHub Actions runs the exact same tools and commands you run locally (uv, make test, make pre-commit), not custom GitHub Actions equivalents. This philosophy ensures:

  • Environment parity: Your local machine matches CI exactly โ€” no surprises when pushing
  • Faster feedback: Run tests locally in seconds before pushing, catch issues early
  • Single source of truth: One set of tools and scripts (Makefile, pyproject.toml) used everywhere
  • Reproducibility: Anyone can reproduce CI behavior locally without GitHub access

Example: Instead of using GitHub Actions-specific linters, we install uv locally (same as in CI), then run make pre-commit and make test โ€” the exact same commands GitHub Actions runs. This means your local environment is 100% reproducible in CI, and you catch issues before pushing.

Always run make test and make pre-commit locally before opening a PR โ€” it's faster and guarantees your code will pass CI since you're running identical tooling.