--- name: ak-dev-code-quality description: > Code quality standards, formatting, Python style rules (classes over script-style functions, configuration-field rules), commit conventions, and PR workflow for Agent Kernel development. Use this skill when making contributions, formatting code, writing commit messages, or preparing pull requests. license: Apache-2.0 metadata: author: yaalalabs category: developer --- # Code Quality & Contribution Conventions ## Code Formatting Agent Kernel uses `black` for formatting and `isort` for import sorting. ### Auto-format ```bash # Format ak-py source and tests make lint # Format examples too make lint-all ``` ### Check only (CI mode, no changes) ```bash make lint-check # ak-py only make lint-check-all # ak-py + examples ``` ### Auto-format a remote branch (CI) To apply formatting on a remote branch without running the tools locally, trigger the **Lint and Commit** GitHub Actions workflow (`.github/workflows/lint-fix.yml`) manually from the Actions tab (`workflow_dispatch`). It takes two inputs: - **`lint_target`**: which Makefile target to run — `lint`, `lint-examples`, or `lint-all` (default). - **`branch`**: the branch to format and commit the changes to. The workflow runs the selected target and pushes a `chore:` commit with any formatting changes back to the chosen branch. Protected branches (currently `develop`) are rejected before any changes are made. ### Configuration In `ak-py/pyproject.toml`: ```toml [tool.black] line-length = 150 target-version = ["py312"] [tool.isort] profile = "black" line_length = 150 ``` In example projects, line length is 120: ```toml [tool.black] line-length = 120 target-version = ["py312"] ``` ### Type Checking ```bash cd ak-py uv run mypy src/ ``` Configuration: ```toml [tool.mypy] python_version = "3.12" warn_return_any = true warn_unused_configs = true disallow_untyped_defs = true disallow_incomplete_defs = true ``` ## Commit Convention Use **Conventional Commits** format: ``` : ``` ### Types | Type | When to Use | |------|------------| | `feat:` | New feature or capability | | `fix:` | Bug fix | | `docs:` | Documentation changes only | | `chore:` | Maintenance, dependencies, config | | `refactor:` | Code restructuring without behavior change | | `test:` | Adding or modifying tests | | `style:` | Formatting-only changes | | `ci:` | CI configuration and workflow changes | | `build:` | Build system, packaging, dependency changes | | `perf:` | Performance improvements | An optional scope narrows the type: `type(scope): description`, for example `fix(ws): reconnect gateway after broker restart` or `chore(auto): sync skills/docs`. ### Examples ``` feat: add telegram messaging integration fix: handle empty session in Redis store docs: update deployment guide for Azure containerized chore: bump openai-agents dependency to 0.6.5 refactor: extract common guardrail logic to base class test: add unit tests for CosmosDB session store ``` ### Rules - Use lowercase for commit type and description - Keep the description under 72 characters - Use imperative mood ("add feature" not "added feature") - No period at the end - Reference issue numbers when applicable: `feat: add telegram integration (#123)` - PR titles follow the same format. `.github/workflows/pr-title-check.yaml` fails the PR otherwise, and because `develop` is squash-merge only the title becomes the commit subject ## Pull Request Process ### Base Branch Branch from and target `develop`, not `main` — CI (`.github/workflows/code-quality.yml`) runs on pull requests against `develop`, and `origin/HEAD` points there. ### Before Submitting 1. **Run tests**: `cd ak-py && uv run pytest` 2. **Run linting**: `make lint-check-all` 4. **Ensure no regressions** — all existing tests must pass ### PR Guidelines - **One feature/fix per PR** — keep PRs focused - **Include tests** — new features must have tests - **Update docs** — if the change affects user-facing behavior - **Add examples** — for new features, add or update examples - **Conventional title**: `type: description` or `type(scope): description` using one of the commit types above; the PR Title Check workflow blocks anything else - **Fill in the PR template** — description, type of change, testing done ### Review Workflow - **Copilot review is automatic for collaborators**: `.github/workflows/copilot-review-request.yaml` requests a Copilot code review when a collaborator's PR is opened, reopened, or marked ready for review (bot-authored PRs excluded). Collaborators never need to request it by hand. PRs from outside contributors are not requested automatically; a maintainer runs the workflow from the Actions tab with the PR number after a first read. - **`Reviewed` label**: maintainers add `Reviewed` once they have gone through a PR. `.github/workflows/reviewed-label-reset.yaml` removes it on every new push so the PR reappears in `is:pr is:open -label:Reviewed`. Contributors should not touch the label. ### PR Types - **Core changes**: Modifications to `ak-py/src/agentkernel/core/` - **Integration additions**: New messaging platforms, framework adapters - **Documentation**: Updates to `docs/`, README files - **Testing**: New or improved tests - **Community support**: Bug reports, feature suggestions ## Version Management ### Version Bumping Handled by publish.yaml workflow. This updates: This updates: - `ak-py/pyproject.toml` version field - Terraform module versions - Example dependency versions ### Version Locations The version appears in: - `ak-py/pyproject.toml` → `version = "x.y.z"` - Terraform modules `version` fields in examples - `agentkernel` dependency version constraints in example `pyproject.toml` files ## Development Setup ### Prerequisites - Python 3.12–3.13.x - `uv` package manager - Git - Make ### Setup ```bash git clone https://github.com/yaalalabs/agent-kernel.git cd agent-kernel make dev-setup # Installs pyenv, Python 3.12, uv, then syncs ak-py venv # or directly: ./scripts/dev-setup.sh ``` Alternatively, set things up manually: ```bash cd agent-kernel/ak-py ./build.sh # Creates venv, installs deps ``` ### Running Examples ```bash cd examples/cli/openai ./build.sh uv run demo.py ``` ### Running Tests ```bash cd examples/cli/openai ./build.sh uv run pytest -s ``` ## File Organization Conventions - **Source**: `ak-py/src/agentkernel/` — all package source code - **Tests**: `ak-py/tests/` — unit tests - **Examples**: `examples///` — self-contained demo projects - **Docs**: `docs/docs/` — Docusaurus documentation - **Scripts**: `scripts/` — CI/CD and maintenance scripts - **Terraform**: `ak-deployment/` — Terraform modules ## Python Style Guidelines - Python 3.12+ features are encouraged (type unions with `|`, `match` statements) - Use type hints for all function signatures - Use `logging.getLogger("ak.")` for logger names - Use async/await for all I/O operations - Prefer `BaseModel` (Pydantic) for data models - Use `ABC` and `@abstractmethod` for interfaces - Keep line length under 150 characters (120 for examples) ### Classes, not script-style functions Feature logic is written as classes, not as procedural module-level functions. This is a house rule for maintainability (see the House Patterns section of `ak-dev-architecture`), not a stylistic preference: - A new component is an ABC plus concrete subclasses, a `*Factory` for selection, an orchestrating class (`*Manager`, `*Handler`, `*Runner`, `*Consumer`) for control flow, and Pydantic models for data. State lives on instances, never on module globals. - Do not write a chain of top-level functions that thread state through arguments, or a `main()`-style function that wires a feature together. Wrap it in a class with a `run()`/`create()`/`execute()` method so callers can subclass, compose, and mock it. - Module-level functions are reserved for small, stateless, genuinely shared utilities that belong to no single class (`resolve_dotted`, `require_extra`), and for the plain Python tool functions that framework tool builders bind. A helper that only makes sense next to one class is a method of that class (`@staticmethod`/`@classmethod` when it needs no instance). - When two classes start sharing logic, lift it into a base class or a shared component rather than copying it or extracting a loose function. ### Configuration fields - New knobs go through `AKConfig` (`ak-py/src/agentkernel/core/config.py`); never read `os.environ` or module constants for behavior a user should control. - Reuse an existing config model before defining a new one (`_QueuesConfig`, `_ResponseStoreConfig`, the `_RedisConfig`/`_DynamoDBConfig`/... connection models); subclass to change defaults only. Do not add an `enabled` flag or duplicate `type` selector when the presence of already-configured components can enable the feature. - Every field has a real `description` (they become user docs) and a default that keeps existing YAML and `AK_*` env vars valid. A field nothing reads is a defect, not future-proofing. ## Logging ### Logger Hierarchy - **AK Logger** (`"ak"`): Parent logger for all Agent Kernel components - Child loggers like `"ak.api"`, `"ak.runtime"`, etc. inherit from this - Propagation is disabled at the AK level to prevent logs from bubbling to the root - Use `logging.getLogger("ak.")` for Agent Kernel components ### Log Levels The following log levels are supported (in order of verbosity): - **DEBUG**: Detailed information for diagnosing problems - **INFO**: General information about program execution - **WARNING**: Something unexpected happened - **ERROR**: Due to a more serious problem, the software has not been able to perform some function - **CRITICAL**: A serious error, indicating that the program itself may be unable to continue running