Code Standards#
Complete coding standards for BioPAL, including naming conventions, formatting rules, type hints, and test requirements.
Table of Contents#
General Guidelines
Naming Conventions
Formatting Rules
Type Hints
Test Requirements
Documentation Standards
Error Handling
Logging
General Guidelines#
Quick Reference Table#
The following table provides a quick reference for code conventions:
Element |
Convention |
Example |
|---|---|---|
Variables |
snake_case |
|
Functions |
snake_case, verb-based |
|
Classes |
PascalCase |
|
Constants |
UPPER_SNAKE_CASE |
|
Modules |
snake_case |
|
Type hints |
Required |
|
Docstrings |
NumPy style |
|
Imports |
Grouped and sorted |
|
Line length |
Max 120 characters |
Use black for formatting |
Tests |
Prefix |
|
Core Principles#
Clear Naming:
Use descriptive names for variables, functions, and modules
Avoid abbreviations unless widely understood
Be consistent across the codebase
Pure Functions:
Prefer functions that take explicit inputs and return outputs
Avoid modifying global state
Make functions testable and predictable
Licensing Compliance:
All code must be compatible with Apache License 2.0
External dependencies must be license-compatible
Third-party code must be properly attributed
See the Contributions and REUSE compliance pages for complete requirements
DRY Principle:
Don’t Repeat Yourself
Avoid duplication by refactoring shared logic
Extract common patterns into utilities
Single Responsibility:
Keep functions focused on one task
Keep modules focused on one domain
Separate concerns clearly
Readability:
Code should be self-documenting
Use comments for “why”, not “what”
Prefer clear code over clever code
Naming Conventions#
Variables#
Snake Case:
# Good
biomass_value = 125.5
incidence_angle = 30.0
sar_backscatter = np.array([0.1, 0.2, 0.3])
# Avoid
biomassValue = 125.5 # camelCase
incidenceAngle = 30.0
SARBackscatter = np.array([0.1, 0.2, 0.3]) # PascalCase
Descriptive Names:
# Good
above_ground_biomass = calculate_agb(sar_data)
canopy_height_map = process_height_data(lidar_data)
# Avoid
agb = calc(sd) # Too abbreviated
chm = proc(ld) # Unclear
Constants:
# UPPER_SNAKE_CASE for constants
MAX_ITERATIONS = 100
DEFAULT_THRESHOLD = 0.5
EARTH_RADIUS_KM = 6371.0
Functions#
Snake Case, Verb-Based:
# Good
def calculate_biomass(sar_data, config):
"""Calculate biomass from SAR data."""
pass
def process_l2_product(input_file, output_file):
"""Process L2 product."""
pass
# Avoid
def BiomassCalc(sar_data, config): # PascalCase
pass
def processL2(input_file, output_file): # camelCase
pass
Action-Oriented Names:
# Good
def validate_input_data(data):
"""Validate input data."""
pass
def load_configuration(file_path):
"""Load configuration from file."""
pass
# Avoid
def data_validation(data): # Noun instead of verb
pass
def config(file_path): # Too generic
pass
Classes#
PascalCase:
# Good
class BiomassProcessor:
"""Process biomass data."""
pass
class ConfigurationManager:
"""Manage configuration."""
pass
# Avoid
class biomass_processor: # snake_case
pass
class ConfigMgr: # Abbreviation
pass
Modules and Packages#
Snake Case, Lowercase:
# Good
biomass_l2_core/
biomass_retrieval.py
uncertainty_quantification.py
# Avoid
BiomassL2Core/ # PascalCase
BiomassRetrieval.py # PascalCase
Private Functions and Variables#
Leading Underscore:
# Good
def _internal_helper_function():
"""Internal helper (not part of public API)."""
pass
_internal_variable = 42
# Public API
def public_function():
"""Public function."""
_internal_helper_function()
Formatting Rules#
Python Style Guide#
We follow PEP 8 with modifications enforced by black:
Line Length: 120 characters (configured in pyproject.toml and ruff.toml)
# Good: Break long lines appropriately
def calculate_biomass(
sar_data: np.ndarray,
incidence_angle: float,
config: dict,
aux_data: Optional[np.ndarray] = None
) -> np.ndarray:
"""Calculate biomass."""
pass
# Avoid: Lines exceeding 120 characters without breaking
def calculate_biomass(sar_data: np.ndarray, incidence_angle: float, config: dict, aux_data: Optional[np.ndarray] = None, extra_param: Optional[str] = None) -> np.ndarray:
pass
Indentation: 4 spaces (no tabs)
# Good: 4 spaces
if condition:
do_something()
if nested_condition:
do_nested()
# Avoid: Tabs or inconsistent indentation
if condition:
do_something() # Tab
do_other() # Mixed
Blank Lines:
2 blank lines between top-level functions and classes
1 blank line between methods in a class
Use blank lines to separate logical sections
# Good
import numpy as np
import xarray as xr
def function_one():
"""First function."""
pass
def function_two():
"""Second function."""
pass
class MyClass:
"""My class."""
def method_one(self):
"""First method."""
pass
def method_two(self):
"""Second method."""
pass
Imports:
Group imports: standard library, third-party, local
Sort imports alphabetically within groups
Use
rufffor automatic import sorting
# Good
import logging
from typing import Dict, Optional
import numpy as np
import xarray as xr
from biomass_l2_core.algorithms import calculate_biomass
from biomass_l2_io.readers import read_l1_product
Code Formatting Tools#
black (v24.10.0):
Automatic code formatter: handles indentation, quotes, trailing commas, blank lines, and more
Line length: 120 characters (read from
pyproject.toml, no need to pass--line-length)Consistent style across codebase
# Format code
black src/ tests/
# Check formatting without modifying (CI)
black --check src/ tests/
ruff (v0.9.1):
Fast linting and import sorting
Style checks and auto-fix
Docstring code formatting enabled (
docstring-code-format = trueinruff.toml)
# Lint and auto-fix
ruff check --fix src/ tests/
# Check only
ruff check src/ tests/
mypy (v1.14.1):
Static type checking
Catches type errors before runtime
Configuration (in
pyproject.toml):python_version = "3.12"explicit_package_bases = truenamespace_packages = true
# Type check (reads config from pyproject.toml)
mypy src/
Pre-commit Configuration#
Pre-commit hooks automatically enforce formatting and code quality before commits. This ensures consistent code style and catches common issues early.
Setup#
After cloning the repository and installing dependencies:
pip install pre-commit
pre-commit install
pre-commit install --hook-type commit-msg # Required for the DCO check
This installs git hooks that run automatically on every commit.
Configuration#
The actual pinned versions in .pre-commit-config.yaml:
Hook |
Version |
|---|---|
black |
24.10.0 |
ruff |
v0.9.1 |
mypy |
v1.14.1 |
detect-secrets |
v1.5.0 |
reuse |
v5.0.2 |
What Pre-commit Checks#
General checks (pre-commit standard hooks):
trailing-whitespace: removes trailing whitespaceend-of-file-fixer: ensures files end with a newlinecheck-yaml: YAML validation (excludesrecipe/meta.yaml)check-added-large-files: blocks files larger than 10MBcheck-merge-conflict: detects merge conflict markersdetect-private-key: blocks accidental private key commits
Python code quality:
black (v24.10.0): Automatic code formatting
ruff (v0.9.1): Fast linting and import sorting, with
--fixmypy (v1.14.1): Type checking (excludes tests, docs, noxfile, recipe, aux_pp2_models)
detect-secrets (v1.5.0): Prevents committing secrets/credentials (baseline:
.secrets.baseline)
License compliance:
reuse (v5.0.2): Verifies SPDX headers on all source files
DCO (commit-msg stage):
check-dco-commit-msg.sh: VerifiesSigned-off-by:trailer on every commit message
Running Pre-commit Manually#
To run all hooks on all files:
pre-commit run --all-files
To run on staged files only:
pre-commit run
To run a specific hook:
pre-commit run black --all-files
pre-commit run ruff --all-files
DCO: Developer Certificate of Origin#
Every commit (excluding merge commits) must carry a Signed-off-by: trailer. This is verified locally by the check-dco-commit-msg.sh hook (commit-msg stage) and by the baseline-dco job in the CI pipeline.
Sign a commit:
git commit -s -m "feat: my feature"
Enable automatic signing for all commits:
git config format.signoff true
Remediation if you forgot to sign:
git commit --amend --signoff
git push --force-with-lease
For multiple unsigned commits, use interactive rebase:
git rebase --signoff HEAD~<number-of-commits>
git push --force-with-lease
REUSE / SPDX Compliance#
Every new source file must include an SPDX header. The reuse pre-commit hook and the baseline-reuse CI job enforce this: a PR with non-compliant files is blocked.
Python:
# SPDX-FileCopyrightText: 2026 [Your Organisation]
#
# SPDX-License-Identifier: Apache-2.0
YAML / shell:
# SPDX-FileCopyrightText: 2026 [Your Organisation]
# SPDX-License-Identifier: Apache-2.0
License texts are stored in LICENSES/Apache-2.0.txt and LICENSES/MIT.txt at the repository root.
Check compliance locally:
pre-commit run reuse --all-files
# or directly:
reuse lint
Standard: https://reuse.software/
Skipping Hooks (Not Recommended)#
If you need to skip hooks for a specific commit (not recommended):
git commit --no-verify -m "Emergency fix"
Note: Skipping hooks may cause CI to fail. Always fix issues before pushing.
Updating Hooks#
To update hook versions:
pre-commit autoupdate
Troubleshooting#
If hooks fail:
Review the error messages
Most hooks auto-fix issues (black, ruff-format)
Fix remaining issues manually
Re-run:
pre-commit run --all-files
If you encounter issues with a specific hook, you can temporarily disable it by commenting it out in .pre-commit-config.yaml.
Type Hints#
Requirements#
All public functions must have type hints:
# Good: Complete type hints
from typing import Dict, List, Optional, Tuple
import numpy as np
import xarray as xr
def process_data(
input_file: str,
parameters: Dict[str, float],
output_format: Optional[str] = None
) -> xr.Dataset:
"""Process input data."""
pass
# Avoid: Missing type hints
def process_data(input_file, parameters, output_format=None):
"""Process input data."""
pass
Type Hint Examples#
Basic Types:
def calculate_value(x: float, y: int) -> float:
"""Calculate value."""
return x * y
Collections:
from typing import List, Dict, Tuple, Optional
def process_list(items: List[str]) -> List[int]:
"""Process list of strings."""
pass
def get_config() -> Dict[str, float]:
"""Get configuration."""
pass
def get_coordinates() -> Tuple[float, float]:
"""Get coordinates."""
pass
def find_item(key: str) -> Optional[str]:
"""Find item, may return None."""
pass
NumPy Arrays:
import numpy as np
from numpy.typing import NDArray
def process_array(data: NDArray[np.float64]) -> NDArray[np.float64]:
"""Process numpy array."""
pass
xarray:
import xarray as xr
def process_dataset(data: xr.Dataset) -> xr.Dataset:
"""Process xarray dataset."""
pass
def get_variable(ds: xr.Dataset, name: str) -> xr.DataArray:
"""Get variable from dataset."""
pass
Complex Types:
from typing import Union, Callable, Any
def flexible_input(value: Union[str, int, float]) -> str:
"""Accept multiple types."""
pass
def apply_function(
data: np.ndarray,
func: Callable[[np.ndarray], np.ndarray]
) -> np.ndarray:
"""Apply function to data."""
pass
Generic Types:
from typing import TypeVar, Generic
T = TypeVar('T')
def process_generic(item: T) -> T:
"""Process generic type."""
pass
Type Checking#
Running mypy:
# Type checking (reads config from pyproject.toml)
mypy src/
Common Issues:
Missing type hints: Add type hints to all public functions
Incorrect types: Fix type annotations
Import errors: Install type stubs (e.g.
types-PyYAMLis already included as a dependency)
Test Requirements#
Test Coverage#
Minimum Coverage: ≥ 60% on code touched by the PR.
This is enforced by the CI pipeline (--cov-fail-under=60).
# Run tests with coverage
pytest --cov=src --cov-report=html
# Check coverage threshold (matches CI)
pytest --cov=src --cov-fail-under=60
Test Markers#
All markers are declared in pytest.ini. Use them to classify tests and control which pipeline stage runs them:
Marker |
Pipeline stage |
Purpose |
|---|---|---|
|
Baseline (every PR) |
Fast, isolated tests of individual functions |
|
Baseline (every PR) |
Quick sanity checks: output must match reference |
|
Extended (Tier 1+) |
Fast end-to-end sanity run |
|
Extended (Tier 1+) |
Full workflow tests with realistic data |
|
Extended (Tier 1+) |
Broader coverage for code changes |
|
Heavy (Tier 2) |
Full scientific regression, requires TDS dataset |
|
: |
Public API surface tests (legacy compatibility) |
# Example: tagging a test for the baseline pipeline
@pytest.mark.baseline
def test_output_matches_reference():
...
# Example: tagging a slow test for Tier 2 only
@pytest.mark.heavy
def test_full_scientific_regression():
...
Test Types#
Unit Tests#
Purpose: Test individual functions and classes in isolation
Location: tests/unit/
Example:
import pytest
import numpy as np
from biomass_l2_core.algorithms import calculate_biomass
def test_calculate_biomass_basic():
"""Test basic biomass calculation."""
sar_data = np.array([0.1, 0.2, 0.3])
incidence_angle = 30.0
config = {"threshold": 0.5}
result = calculate_biomass(sar_data, incidence_angle, config)
assert result.shape == sar_data.shape
assert np.all(result >= 0)
assert np.all(result <= 500) # Max biomass constraint
def test_calculate_biomass_edge_cases():
"""Test edge cases."""
# Test with zero values
sar_data = np.array([0.0, 0.1, 0.2])
result = calculate_biomass(sar_data, 30.0, {"threshold": 0.5})
assert np.all(result >= 0)
# Test with invalid input
with pytest.raises(ValueError):
calculate_biomass(np.array([-1.0]), 30.0, {"threshold": 0.5})
Best Practices:
Test one thing per test function
Use descriptive test names
Test edge cases and error conditions
Use fixtures for test data
Keep tests fast and independent
Integration Tests#
Purpose: Test end-to-end workflows
Location: tests/integration/
Example:
import pytest
from pathlib import Path
from biomass_l2_orchestration.workflows import process_l2_chain
def test_end_to_end_processing(tmp_path):
"""Test complete processing workflow."""
input_file = "tests/data/l1_product.nc"
output_file = tmp_path / "output.nc"
config_file = "tests/data/config.yaml"
process_l2_chain(input_file, output_file, config_file)
assert output_file.exists()
# Verify output format and content
# Check metadata
# Validate scientific results
Best Practices:
Use realistic test data
Test complete workflows
Verify output formats
Clean up test artifacts
Scientific Regression Tests#
Purpose: Ensure no scientific regressions
Location: tests/scientific_regression/
Example:
import pytest
import numpy as np
from biomass_l2_core.algorithms import calculate_biomass
def test_biomass_regression():
"""Test against reference results."""
# Load reference data
reference_results = np.load("tests/data/reference_biomass.npy")
# Process with current code
sar_data = np.load("tests/data/test_sar_data.npy")
result = calculate_biomass(sar_data, 30.0, {"threshold": 0.5})
# Compare with reference
np.testing.assert_allclose(
result,
reference_results,
rtol=1e-5,
atol=0.1
)
Best Practices:
Use validated reference datasets
Compare with previous versions
Check for scientific accuracy
Document reference data sources
Writing Tests#
Test Structure:
def test_function_name_description():
"""
Test description.
What is being tested and why.
"""
# Arrange: Set up test data
input_data = create_test_data()
config = {"param": 1.0}
# Act: Execute function
result = function_under_test(input_data, config)
# Assert: Verify results
assert result is not None
assert result.shape == expected_shape
assert np.all(result >= 0)
Test Fixtures:
import pytest
@pytest.fixture
def sample_sar_data():
"""Fixture for sample SAR data."""
return np.array([0.1, 0.2, 0.3, 0.4])
@pytest.fixture
def default_config():
"""Fixture for default configuration."""
return {
"threshold": 0.5,
"max_iterations": 100
}
def test_with_fixtures(sample_sar_data, default_config):
"""Test using fixtures."""
result = process_data(sample_sar_data, default_config)
assert result is not None
Parametrized Tests:
@pytest.mark.parametrize("input_value,expected", [
(0.1, 10.0),
(0.2, 20.0),
(0.3, 30.0),
])
def test_multiple_cases(input_value, expected):
"""Test multiple input/output pairs."""
result = calculate_value(input_value)
assert result == expected
Running Tests#
# Unit tests (run locally before pushing)
pytest -m unit
# Baseline tests (mirrors the CI baseline pipeline)
pytest -m baseline
# Extended tests (Tier 1: code changes)
# ⚠ WARNING: extended tests require a TDS dataset not distributed with the repository.
# A procedure to retrieve the TDS will be made available shortly.
pytest -m extended
# Heavy tests (Tier 2: scientific changes)
# ⚠ WARNING: heavy tests require a TDS dataset not distributed with the repository.
# A procedure to retrieve the TDS will be made available shortly.
pytest -m heavy
# Specific test file
pytest tests/unit/test_algorithms.py
# Specific test function
pytest tests/unit/test_algorithms.py::test_calculate_biomass_basic
# With coverage (matches CI threshold)
pytest -m unit --cov=src --cov-report=html --cov-fail-under=60
# Verbose output
pytest -v
# Stop on first failure
pytest -x
Test Requirements Checklist#
Before Submitting PR:
Unit tests added for new functions
Integration tests for new workflows
Scientific regression tests (if applicable)
Test coverage meets minimum requirements
All tests pass locally
Tests are fast and independent
Test data is included or documented
Documentation Standards#
Docstring Format#
NumPy Style (Required):
def calculate_biomass(
sar_data: np.ndarray,
incidence_angle: float,
config: dict
) -> np.ndarray:
"""
Calculate above-ground biomass from SAR backscatter data.
This function implements the inversion algorithm described in
[Reference Paper, 2023]. The algorithm uses P-band SAR data
to estimate forest biomass with uncertainty quantification.
Parameters
----------
sar_data : np.ndarray
Input SAR backscatter data in linear scale.
Shape: (n_pixels,) or (n_azimuth, n_range)
Expected range: [0.0, 1.0]
incidence_angle : float
Incidence angle in degrees.
Range: [20, 60]
config : dict
Configuration parameters containing:
- threshold: float, detection threshold (default: 0.5)
- max_iterations: int, maximum iterations (default: 100)
- biomass_range: tuple, (min, max) in Mg/ha (default: (0, 500))
Returns
-------
np.ndarray
Calculated biomass values in Mg/ha.
Shape: matches sar_data shape
Range: [0, 500] Mg/ha
Raises
------
ValueError
If sar_data contains negative values or out-of-range data
RuntimeError
If convergence not achieved within max_iterations
References
----------
.. [1] Author et al. (2023). "Biomass Estimation from P-band SAR".
Remote Sensing Journal.
Examples
--------
>>> import numpy as np
>>> sar_data = np.array([0.1, 0.2, 0.3])
>>> angle = 30.0
>>> config = {"threshold": 0.5}
>>> biomass = calculate_biomass(sar_data, angle, config)
>>> print(biomass)
[12.5 25.3 38.1]
Notes
-----
The algorithm assumes forested areas. Non-forested pixels
should be masked before calling this function.
"""
pass
Documentation Sections#
Required Sections:
Summary: One-line description
Parameters: All parameters with types and descriptions
Returns: Return value with type and description
Optional Sections:
Raises: Exceptions that may be raised
See Also: Related functions
References: Scientific papers or documentation
Examples: Usage examples
Notes: Additional information
Inline Comments#
Use comments for “why”, not “what”:
# Good: Explains why
# Use iterative method because analytical solution is unstable
# for low backscatter values
result = iterative_solve(data, threshold)
# Avoid: States the obvious
# Calculate result
result = calculate(data)
Error Handling#
Exception Types#
Use Appropriate Exception Types:
# ValueError: Invalid input values
if value < 0:
raise ValueError(f"Value must be non-negative, got {value}")
# TypeError: Wrong type
if not isinstance(data, np.ndarray):
raise TypeError(f"Expected numpy array, got {type(data)}")
# FileNotFoundError: Missing files
if not Path(file_path).exists():
raise FileNotFoundError(f"File not found: {file_path}")
# RuntimeError: Algorithm failures
if not converged:
raise RuntimeError("Algorithm did not converge")
Error Messages#
Informative Error Messages:
# Good: Clear and helpful
if sar_data.min() < 0:
raise ValueError(
f"SAR data contains negative values: min={sar_data.min()}. "
"Expected range: [0.0, 1.0]"
)
# Avoid: Vague
if sar_data.min() < 0:
raise ValueError("Invalid data")
Error Handling Patterns#
Try-Except Blocks:
try:
result = process_data(input_file)
except FileNotFoundError:
logger.error(f"Input file not found: {input_file}")
raise
except ValueError as e:
logger.error(f"Invalid input data: {e}")
raise
except Exception as e:
logger.error(f"Unexpected error: {e}", exc_info=True)
raise
Custom Exceptions:
class BiomassCalculationError(Exception):
"""Error in biomass calculation."""
pass
class ValidationError(Exception):
"""Error in data validation."""
pass
# Usage
if invalid_condition:
raise BiomassCalculationError("Detailed error message")
Logging#
Logging Setup#
import logging
logger = logging.getLogger(__name__)
def process_data(data):
"""Process data with logging."""
logger.info("Starting data processing")
logger.debug(f"Input data shape: {data.shape}")
try:
result = process(data)
logger.info("Data processing completed successfully")
return result
except Exception as e:
logger.error(f"Data processing failed: {e}", exc_info=True)
raise
Log Levels#
Use Appropriate Log Levels:
# DEBUG: Detailed diagnostic information
logger.debug(f"Processing pixel {i}/{total}")
# INFO: General informational messages
logger.info("Processing started")
logger.info(f"Processed {n_files} files")
# WARNING: Warning messages
logger.warning("Unusual value detected, using default")
logger.warning(f"Low data quality: {quality_score}")
# ERROR: Error messages
logger.error("Processing failed")
logger.error(f"Invalid configuration: {config}")
# CRITICAL: Critical errors
logger.critical("System failure, aborting")
Logging Best Practices#
Include Context:
# Good: Includes context
logger.info(f"Processing file {file_path} with config {config_name}")
# Avoid: Missing context
logger.info("Processing")
Use Structured Logging:
# Good: Structured information
logger.info("Processing completed", extra={
"file": file_path,
"duration": duration,
"pixels": n_pixels
})
# Avoid: String concatenation
logger.info(f"Processing completed: file={file_path}, duration={duration}")
Code Review Checklist#
Before Submitting#
Code follows naming conventions
Code formatted with
blackCode linted with
ruffType hints added to all functions
Docstrings added (NumPy style)
Tests added and passing
Test coverage meets requirements
Documentation updated
No secrets or sensitive data
Pre-commit hooks passing
Code Quality#
Functions are focused and short
No code duplication
Error handling is appropriate
Logging is used appropriately
Performance considerations documented
Code is readable and maintainable
Resources#
Documentation#
Getting Started - Introduction and getting started guide
Licensing - Apache 2.0 license requirements and legal obligations
Code of Conduct - Community standards and expectations
Contributing overview - Contribution process and workflows
CI automation and contribution tiers - Pipeline reference, tier detection, branch protection
Governance - Roles, responsibilities, and decision-making
Architecture - Monorepo layout and
bps-*modulesDocumentation standards - Documentation writing standards and best practices
Communication - Communication channels and meeting schedules
External Resources#
Code Review Process#
The following flowchart illustrates the code review process from writing code to merging:
flowchart TD
Code[Code written] --> Lint[Pre-commit hooks<br/>black, ruff, mypy]
Lint --> LintOK{Pass?}
LintOK -->|No| FixLint[Fix errors]
FixLint --> Lint
LintOK -->|Yes| Tests[Run local tests]
Tests --> TestOK{All pass?}
TestOK -->|No| FixTests[Fix tests]
FixTests --> Tests
TestOK -->|Yes| Docs[Check documentation]
Docs --> DocOK{Complete?}
DocOK -->|No| AddDocs[Add documentation]
AddDocs --> Docs
DocOK -->|Yes| PR[Create Pull Request]
PR --> CI[CI/CD Pipeline]
CI --> CIOK{CI passes?}
CIOK -->|No| FixCI[Fix CI issues]
FixCI --> PR
CIOK -->|Yes| Review[Review by maintainers]
Review --> ReviewOK{Approved?}
ReviewOK -->|No| Address[Address comments]
Address --> PR
ReviewOK -->|Yes| Merge[Merge into develop]
classDef defaultStyle fill:#f5f5f5,stroke:#9e9e9e,stroke-width:2px,color:#333
class Code,Lint,LintOK,FixLint,Tests,TestOK,FixTests,Docs,DocOK,AddDocs,PR,CI,CIOK,FixCI,Review,ReviewOK,Address,Merge defaultStyle
style Code fill:#f5f5f5,stroke:#9e9e9e,stroke-width:2px
style Lint fill:#e1bee7,stroke:#e1bee7,stroke-width:2px
style Tests fill:#a3d8b0,stroke:#a3d8b0,stroke-width:2px
style Docs fill:#e1bee7,stroke:#e1bee7,stroke-width:2px
style PR fill:#f5f5f5,stroke:#9e9e9e,stroke-width:2px
style CI fill:#ef9a9a,stroke:#e57373,stroke-width:2px
style Review fill:#e1bee7,stroke:#e1bee7,stroke-width:2px
style Merge fill:#a3d8b0,stroke:#a3d8b0,stroke-width:3px
Questions? Open an issue with the code-standards label or contact core maintainers.
Last Updated: 2026
Previous: Architecture | Next: Documentation standards