This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Based on myk-org/github-metrics CLAUDE.md
- ❌ NEVER add
# noqa,# type: ignore,# pylint: disable - ❌ NEVER disable linter/mypy rules to work around issues
- ✅ FIX THE CODE - If linter complains, the code is wrong
- If you think a rule is wrong: ASK the user for explicit approval
Before writing ANY new code:
- SEARCH codebase for existing implementations
- CHECK
utilities/for shared functions - CHECK
libs/for shared libraries - CHECK
tests/for shared fixtures and helper functions - VERIFY no similar logic exists elsewhere
- NEVER duplicate logic - extract to shared module
- REUSE existing code and patterns — only write new when nothing exists
- Type hints MANDATORY - mypy strict mode in
libs/, all new public functions under utilities MUST be typed - Use
TYPE_CHECKINGfor type-only imports - wrap imports needed solely for type hints inif TYPE_CHECKING:to avoid runtime overhead and circular imports - Google-format docstrings REQUIRED - for all public functions with non-obvious return values OR side effects
- No defensive programming - fail-fast, don't hide bugs with fake defaults (see exceptions below)
- ALWAYS use
uv run- NEVER executepython,pip, orpytestdirectly. Useuv run python,uv run pytest,uv addfor package installation. - ALWAYS use absolute imports - NEVER use relative imports
- ALWAYS import specific functions - use
from module import func, NEVERimport module - ALWAYS use named arguments - for function calls with more than one argument
- NEVER use single-letter variable names - ALWAYS use descriptive, meaningful names
- No dead code - every function, variable, fixture MUST be used or removed. Code marked with
# skip-unused-codeis excluded from dead code analysis (enforced via custom ruff plugin). - Prefer direct attribute access - use
foo.attrdirectly. Save to variables only when: reusing the same attribute multiple times improves readability, or extracting clarifies intent. - Imports always at the top of the module - do not import inside functions
conftest.pyis for fixtures only - helper functions, utility functions, and classes must NOT be defined in conftest.py or test_*.py; place them in dedicated utility modules instead
The "no defensive programming" rule has these five exceptions:
- Destructors/Cleanup - May be called during incomplete initialization
- Optional Parameters - Explicitly typed as
Type | Nonewith defaultNone - Lazy Initialization - Attributes intentionally starting as
Nonebefore first use - Platform/Architecture Constants - Features unavailable on all platforms (amd64, arm64, s390x)
- Unversioned External Libraries - External dependencies with unknown API stability
Still Prohibited (with examples):
- ❌ Checking attributes that are ALWAYS provided - Do NOT check if
vm.nameexists when VirtualMachine always has a name field. If the schema guarantees it, trust it. - ❌ Defensive checks on data guaranteed by architecture - Do NOT validate that
namespace.clientis not None when the Namespace class always sets client in__init__. If the constructor guarantees it, trust it. - ❌ Using
hasattr()for type discrimination - Do NOT useif hasattr(obj, 'some_method')to detect type. Useisinstance(obj, ExpectedType)for explicit type checking. - ❌ Version checking for pinned dependencies - Do NOT check
if kubernetes_version >= Xwhen pyproject.toml pins the exact version. The lock file guarantees the version.
- All new tests MUST have markers - check pytest.ini for available markers, NEVER commit unmarked tests
- Tier marker reviews: tier3 marker - when warranted (complex/hardware/platform-specific/time-consuming tests). Tier2 marker is not needed. Tier1 is not relevant.
- Each test verifies ONE aspect only - single purpose, easy to understand
- Tests MUST be independent - use
pytest-dependencyONLY when test B requires side effects from test A (e.g., cluster-wide configuration). For resource dependencies, use shared fixtures instead. When using@pytest.mark.dependency, a comment explaining WHY the dependency exists is REQUIRED. - ALWAYS use
@pytest.mark.usefixtures- REQUIRED when fixture return value is not used by test - Do not offer to use
pytest.skip()or@pytest.mark.skipor@pytest.mark.skipif- pytest skip and skipif options are forbidden
__test__ = False Usage Rules:
- ✅ ALLOWED for STD placeholder tests - tests that contain ONLY:
- Docstrings describing expected behavior
- No actual implementation code (no assertions, no test logic)
- ❌ FORBIDDEN for implemented tests - if a test has actual implementation code (assertions, test logic, setup/teardown), do NOT use
__test__ = False
Rationale: STD (Standard Test Design) placeholder tests document what will be tested before implementation. These can use __test__ = False to prevent collection errors. Once a test has implementation code, __test__ = False must be removed.
STD Docstring Format (MANDATORY):
When writing or reviewing STD (Software Test Description) test docstrings, follow the format defined in docs/SOFTWARE_TEST_DESCRIPTION.md:
- Required sections:
Preconditions:,Steps:,Expected: - ❌ NEVER use alternative section names.
- Each test verifies ONE thing with ONE
Expected:assertion (rare exceptions allowed when multiple assertions verify a single behavior — see STD doc) - No implementation details in STD docstrings — no fixture names, no code references, no variable names; describe behavior in natural language
- STP link REQUIRED — module docstring must contain the STP (Software Test Plan) link directly, not a reference to a README or other file
- Markers can be at any level — module, class, or test docstring; place them at the level they apply to
- Parametrized markers — parameter values may have inline markers using
[Markers: ...]syntax (e.g.,- ipv4 [Markers: ipv4]) to differentiate common markers from parameter-specific ones - Name resources by function — in Preconditions, name objects by their role (e.g., "client VM", "server VM", "under-test VM"), not generic labels (e.g., "VM-A", "VM-B")
- Shared vs. test-specific preconditions — class/module docstring holds shared
Preconditions:, individual tests add only their own. When a shared resource (e.g., a VM) is directly used by a test, it must appear in both the shared and test-level preconditions. [NEGATIVE]indicator REQUIRED — tests verifying failure scenarios must include[NEGATIVE]in the description
- Single Action REQUIRED: Fixtures MUST do ONE action only (single responsibility)
- Naming REQUIRED: ALWAYS use NOUNS (what they provide), NEVER verbs
- ✅
vm_with_disk - ❌
create_vm_with_disk
- ✅
- Parametrization format: Use
request.paramwith dict structure for complex parameters - Ordering REQUIRED: pytest native fixtures first, then session-scoped, then module/class/function scoped
- Fixture scope rules:
- Use
scope="function"(default) - for setup requiring test isolation - Use
scope="class"- for setup shared across test class - Use
scope="module"- for expensive setup in a test module - Use
scope="session"- for setup that persists the entire test run (e.g., storage class, namespace) - NEVER use broader scope if fixture modifies state or creates per-test resources
- Use
- INFO level REQUIRED for - test phase transitions, resource creation/deletion, configuration changes, API responses, intermediate state
- WARNING level REQUIRED for - skipped operations due to known issues, unusual configurations that may cause problems, missing optional configuration, deprecation notices
- ERROR level REQUIRED for - exceptions with full context: what failed, expected vs actual values, resource state
- NEVER use DEBUG level - if a log is needed, use INFO.
- NEVER log - secrets, tokens, passwords, or PII
- Log format REQUIRED - Use f-string formatting:
LOGGER.info(f"VM {vm} created in {ns} namespace")LOGGER.warning(f"CRD {crd.name} is unreadable due to {jira_id} bug")
Exception Handling:
- ALWAYS re-raise with context - use
raise NewError("message") from original_errorto preserve stack trace - Do not catch bare
Exception- catch specific exception types only - NEVER silently swallow exceptions - at minimum, log the error before continuing
Context Managers:
- ALWAYS use
withfor resources - files, connections, locks MUST use context managers - Fixtures with cleanup MUST use yield - use
yield resourcefollowed by cleanup code, NEVER return + finalizer
Timeouts and Polling:
- ALWAYS use
timeout_sampler- fromtimeout_samplerpackage for any operation that waits for a condition:from timeout_sampler import TimeoutSampler for sample in TimeoutSampler(wait_timeout=60, sleep=5, func=check_condition): if sample: break
- NEVER use
time.sleep()in loops - usetimeout_samplerwith appropriate wait time
Assertions:
- Use pytest assertions -
assert actual == expected, NEVERself.assertEqual() - Include failure messages -
assert condition, "descriptive message explaining failure"
Boolean Checks:
- Use implicit boolean -
if items:NOTif len(items) > 0:orif items != []: - Use identity for None -
if x is None:NOTif x == None: - NEVER compare to True/False -
if flag:NOTif flag == True:
- Feature subdirectories REQUIRED - each feature MUST have its own subdirectory under component (e.g.,
tests/network/ipv6/) - Test file naming REQUIRED - ALWAYS use
test_<functionality>.pyformat - Local helpers location - place helper utils in
<feature_dir>/utils.py - Local fixtures location - place in
<feature_dir>/conftest.py - Move to shared location - move to
utilities/ortests/conftest.pyONLY when used by different team directories
This is a test suite - internal APIs have NO backward compatibility requirements:
- Return types and method signatures can change freely
- Internal utility functions can be refactored without deprecation
- Only external interfaces (pytest markers, CLI options) need stability
Before committing, these checks MUST pass:
# Required before every commit
pre-commit run --all-files # Linting and formatting
# Full CI checks
tox
# Run utilities unit tests
tox -e utilities-unittests
No exceptions. Fix all failures before committing. Do not use --no-verify to bypass hooks.