This file defines the mandatory policies that all AI coding agents (Cursor, Claude Code, Copilot, etc.) must follow when working in this repository. These policies also apply to human contributors.
All code changes must follow the Red-Green-Refactor cycle:
- Red — Write a failing test that describes the desired behaviour before writing any implementation code.
- Green — Write the minimum amount of production code required to make the test pass.
- Refactor — Clean up the code while keeping all tests green.
- No production code may be written without a corresponding test.
- Tests must be committed alongside (or before) the implementation.
- Pull requests without adequate test coverage will be rejected.
- When fixing a bug, first write a test that reproduces the bug, then fix it.
- Prefer small, focused tests over large integration tests (unit → integration → e2e).
Agents must not jump straight into writing code. The required workflow is:
- Analyse — Read and understand the relevant code, context, and requirements.
- Plan — Produce a clear, numbered implementation plan that includes:
- Files to be created or modified.
- Tests to be written (TDD — tests come first).
- Any dependencies or infrastructure changes.
- Potential risks or trade-offs.
- Confirm — Present the plan to the user and wait for explicit approval before making any changes.
- Implement — Only after approval, proceed with the plan step by step.
- Never skip the planning step, even for "small" changes.
- If the scope changes during implementation, stop and re-plan.
- When in doubt, ask — do not assume.
All secrets, API keys, tokens, and credentials must be managed through organisation-level secret stores. Locally stored credentials are strictly prohibited.
- GitHub Actions: Use GitHub Organisation Secrets or Repository Secrets — never hard-code values in workflow files.
- Environment variables: Reference secrets from a secure vault (e.g., GitHub Secrets,
1Password, HashiCorp Vault). Never commit
.envfiles containing real credentials. - Docker / Compose: Inject secrets at runtime via environment variables sourced from the org secret store. Never bake credentials into images.
- Local development: Use
.env.examplewith placeholder values. Developers pull real values from the org secret manager.
.envfiles containing real secrets must be in.gitignoreand never committed.- No API keys, tokens, passwords, or private keys in source code — ever.
- Rotate any credential that has been accidentally committed, immediately.
- Prefer short-lived tokens and scoped permissions over long-lived master keys.
- All CI/CD pipelines must source secrets exclusively from org-level secret stores.
All work must be committed on the dev branch. The main branch is the release branch.
- Develop on
dev— All feature work, bug fixes, and improvements are committed todev. Do not commit directly tomain. - No version bumps on
dev— The version inpyproject.toml(or equivalent) stays unchanged while working ondev. Do not bump the version as part of a feature or fix commit. - Bump version only when merging to
main— Whendevis merged intomainfor a release, the version is bumped as part of that merge:- Patch release (bug fixes, small improvements): bump
+0.0.1(e.g.,1.1.5→1.1.6) - Minor release (new features, non-breaking changes): bump
+0.1.0(e.g.,1.1.6→1.2.0) - Major release (breaking changes, large rewrites): bump
+1.0.0(e.g.,1.2.0→2.0.0)
- Patch release (bug fixes, small improvements): bump
- Tag the release — After merging to
main, tag the commit with the version (e.g.,v1.1.6).
- Never commit directly to
main— always merge fromdev. - Never bump the version on
dev— the bump happens at merge-to-main time. - Use Semantic Versioning (MAJOR.MINOR.PATCH).
- When in doubt about bump level, ask the user.
Android requires versionCode to be strictly increasing across releases. A lower
versionCode than an already-installed APK causes "App not installed — package appears
to be invalid".
- Before modifying
versionCode, always check the latest released value in git history:git log --all -1 -p -- android-app/app/build.gradle.kts | grep versionCode. - The new
versionCodemust be greater than the previously released value. - Never reset
versionCode(e.g., back to1or2). It must only go up. - When bumping
versionName, always bumpversionCodeby at least+1from the last released value. - Include both
versionCodeandversionNamechanges in the same commit.
When creating a new shortcode for the CryptoLabs AI Gateway plugin, all four steps are required. Missing any step (especially step 3) will cause deployment failures.
- Create the shortcode class:
plugin/cryptolabs-ai-gateway/includes/class-<name>-shortcode.php - Register in
cryptolabs-ai-gateway.php— add to the$includesarray and call::init(). - Deploy — add
put includes/class-<name>-shortcode.phpto the SFTP batch in.github/workflows/deploy-synchronized.yml. - Page — create or document the WordPress page with the
[shortcode_tag].
- Step 3 is the most commonly missed. The SFTP batch uses an explicit file list — files not listed are never uploaded to WordPress, even though they exist in Git.
- A missing file causes a PHP fatal error because
require_oncereferences a file that was never deployed. - Before marking a shortcode task as complete, verify the filename appears in the
batch.txtheredoc inside thedeploy-wordpress-pluginjob. - See
.cursor/rules/wordpress-shortcodes.mdcfor the full checklist.
All CryptoLabs projects share self-hosted GitHub Actions runners. To prevent permission failures, container breakage, and cross-project interference, all workflows must follow these standards.
- Runners execute as the
runneruser (uid 1001), which is a member of thedockergroup. - Runner services are managed by systemd (
actions.runner.cryptolabsza.gpu-runner-*.service). - After any NVIDIA driver update or Docker daemon config change, restart all runner services
via
systemctl restart actions.runner.cryptolabsza.gpu-runner-*.service.
The runner user has limited sudoers entries. Using sudo causes intermittent failures
when the sudoers list doesn't cover the exact binary path (e.g., sudo docker compose vs
sudo /usr/bin/docker-compose).
- Docker commands: Use
dockeranddocker composedirectly — the runner is in thedockergroup. - Directory creation: Ensure deployment directories (e.g.,
/opt/<project>) are owned byrunner:runner. Create them once via root SSH, not in workflows. - Writing files: Use
cat >orteewithout sudo. Ensure target files are writable by the runner user.
Every project that deploys to a self-hosted runner must have its deployment directory
pre-created and owned by runner:runner:
# One-time setup (run as root on the target server)
mkdir -p /opt/<project>
chown -R runner:runner /opt/<project>If a directory is accidentally created as root, the next CI/CD run will fail with
"Permission denied". Fix with chown -R runner:runner /opt/<project>.
- The NVIDIA container runtime config at
/etc/nvidia-container-runtime/config.tomlmust usemode = "auto"(not"cdi"). CDI mode is incompatible with thedeploy.resources.reservations.devicessyntax used in docker-compose files. - After changing nvidia-container-runtime config, run
systemctl restart docker— then immediately restart all compose stacks because Docker restart stops all containers. - GPU containers require the
deploy.resources.reservations.devicesblock in docker-compose:deploy: resources: reservations: devices: - driver: nvidia count: 1 capabilities: [gpu]
All self-hosted workflows must use the array format with an explicit OS label:
runs-on: [self-hosted, linux]Do not use bare self-hosted without the OS label.
For projects that only build and push images (dc-overview, cryptolabs-proxy, ipmi-monitor):
- Prefer GitHub Actions (
docker/login-action,docker/build-push-action,docker/setup-buildx-action) over raw Docker CLI commands. - Use
GITHUB_TOKENorCR_PATorg secret for GHCR authentication. - Tag strategy:
devbranch →:devtagmainbranch →:latesttag- Version tags (
v*) →:v1.2.3,:1.2.3,:1.2,:stable - PRs →
:pr-<number>(build only, no push)
- Clean up Docker config after builds:
if: always()step to remove credentials.
For projects that deploy directly on the runner host:
- Use
rsyncto sync files (exclude.git,__pycache__,*.pyc). - Write
.envfiles withcat >(no sudo). Setchmod 600on.envfiles. - Always run
docker compose downbeforedocker compose up -d. - Include a health check after deployment:
sleep 10 if curl -sf http://localhost:<port>/health; then echo "✅ Deployment successful!" else echo "❌ Health check failed" docker compose logs --tail 30 exit 1 fi
- On health check failure, show logs for debugging — do not silently fail.
Running systemctl restart docker stops all containers on the host. Before restarting
Docker:
- Document all running compose stacks:
docker ps --format '{{.Names}}' - After restart, bring all stacks back up in order:
- Infrastructure first (postgres, redis)
- Backend services (litellm, api-proxy)
- Application services (ipmi-monitor-ai, sre-api, etc.)
- Uses
[self-hosted, linux]runner labels - No
sudoin any step - Deployment directory is
runner:runnerowned - Secrets via
${{ secrets.* }}only — never hardcoded -
.envfiles written withchmod 600 - Health check after deployment
- Docker image tag follows the standard scheme
- GPU projects use
deploy.resources.reservations.devicessyntax
| Policy | One-Liner |
|---|---|
| TDD | Write the test first, then make it pass, then clean up. |
| Plan → Confirm → Implement | Always present a plan and wait for approval. |
| Org Secrets | Never store credentials locally — use org secret management. |
| Branching & Versioning | Work on dev, bump version only when merging to main. |
| Android versionCode | Always check the last released versionCode and increment — never reset. |
| WordPress Shortcodes | Four steps: create class, register, add to SFTP batch, create WP page. |
| CI/CD Runners | No sudo, runner-owned dirs, health checks, [self-hosted, linux] labels. |
Last updated: 2026-03-21