| name | backend-atomic-commit |
| description | Pedantic backend pre-commit and atomic commit Skill for Django/Optimo-style repos. Enforces local AGENTS.md / CLAUDE.md, pre-commit hooks, .security/* helpers, and Monty’s backend engineering taste – with no AI signatures in commit messages. |
| allowed-tools | Bash, Read, Edit, Glob, Grep |
Backend Atomic Commit Skill
When to Use This Skill
Use this Skill in backend/Django repos (especially the Diversio monolith backend) when you want:
/backend-atomic-commit:pre-commit– to actively fix the current code (formatting, imports, type hints, logging, etc.) so that it matches:- Local
AGENTS.md/CLAUDE.mdrules. .pre-commit-config.yamlexpectations..security/diff helpers (ruff and local imports).- Monty’s backend taste.
- Local
/backend-atomic-commit:atomic-commit– to run the same checks plus:- Enforce that the staged changes are atomic (one coherent change).
- Ensure all quality gates are green (no shortcuts).
- Propose a strict, ticket-prefixed commit message without any Claude or AI signatures.
Example Prompts
- “Run
/backend-atomic-commit:pre-commiton this repo and actively fix all files ingit statusso they obey backendAGENTS.md,.pre-commit-config.yaml,.security/*helpers, and Monty’s taste (no local imports, strong typing, structured logging). Then summarize what you changed and what’s still[BLOCKING].” - “Use
/backend-atomic-commit:atomic-committo prepare an atomic commit for the staged changes inbackend/. Enforce all pre-commit hooks and.securityscripts, run Ruff, ty, Django checks, and relevant pytest subsets, then propose a ticket-prefixed commit message with no AI signature and clearly mark any[BLOCKING]issues.” - “Treat my current backend changes as one logical bugfix and run
/backend-atomic-commit:pre-commitin a strict mode: eliminate local imports, fix type hints (noAny, no string-based annotations), clean up debug statements, and ensure Ruff,.security/local_imports_pr_diff.sh, ty, and Django checks are happy." - “Before I commit these
optimo_*changes, run/backend-atomic-commit:atomic-commit --autoto:- enforce structured logging with
TypedDictpayloads, - ensure no PII in logs,
- verify tests and
.security/*scripts, and then tell me whether the commit is ready and what the commit message should be.”
- enforce structured logging with
If you’re not in a backend repo (no manage.py, no backend-style AGENTS.md,
no .pre-commit-config.yaml), this Skill should say so explicitly and fall
back to a lighter “generic Python pre-commit” behavior.
Modes
This Skill behaves differently based on how it is invoked:
pre-commitmode – invoked via/backend-atomic-commit:pre-commit:- Actively applies changes to make the working tree and staged files conform to repo standards and pre-commit requirements.
- Runs all relevant static checks and auto-fixers.
- Does not propose or drive a commit.
atomic-commitmode – invoked via/backend-atomic-commit:atomic-commit:- Runs everything from
pre-commitmode. - Enforces atomicity of staged changes.
- Requires all gates to be green.
- Proposes a commit message, but must never add AI signatures or plugin branding to the message.
- Runs everything from
The command markdown sets the mode. You should detect the mode from the command description/context and adjust behavior accordingly.
Core Priorities
Emulate Monty’s backend engineering and review taste, tuned for pre-commit:
- Correctness & invariants – multi-tenancy, time dimensions, and security constraints come first.
- Safety & reviewability – avoid dangerous schema changes, large risky try/except blocks, hidden PII, or untyped payloads.
- Atomic commits – one commit should represent one coherent change; split unrelated work.
- Local repo rules first – treat
AGENTS.mdandCLAUDE.mdas the source of truth when present; this Skill is a default baseline. - Tooling alignment – use uv wrappers,
.security/*helpers, and.pre-commit-config.yamlhooks as documented, not ad-hoc commands. - Type and structure – prefer precise type hints,
TypedDict/dataclasses, and structured logging over untyped dicts and log soup. - No AI signatures in commits – commit messages must look like a human
wrote them; this Skill should be invisible from
git log.
Always prioritize [BLOCKING] issues over style and nits.
Environment & Context Gathering
When this Skill runs, you should first gather context using Bash, Read,
Glob, and Grep:
- Git context:
git status --porcelaingit branch --show-currentgit diff --cached --statgit diff --cached --name-onlygit log --oneline -10
- Repo configuration:
- Read
AGENTS.mdandCLAUDE.md(if present) for repo-specific rules. - Detect
.pre-commit-config.yaml. - Detect
.security/scripts, especially:./.security/ruff_pr_diff.sh./.security/local_imports_pr_diff.sh
- Detect
manage.py/ Django project layout.
- Read
- Tool availability:
uvand.bin/wrappers:.bin/ruff,.bin/ty,.bin/django,.bin/pytest.
- Fallback to
uv runor plainpython/pytest/ruffwhere necessary.
If the repo clearly isn’t the Diversio backend / Django4Lyfe style, say so and adjust expectations (but you can still run generic Python pre-commit checks).
Checks in Both Modes
In both pre-commit and atomic-commit modes, follow this pipeline:
Scope changed files
- Start from files reported by
git statusandgit diff --cached:- Distinguish staged vs unstaged vs untracked.
- Categorize by type:
- Python (src vs tests;
optimo_*,dashboardapp,survey, etc.). - Templates (Django HTML).
- Config (YAML, JSON,
.pre-commit-config.yaml,pyproject.toml,requirements*.txt). - Docs/markdown.
- Python (src vs tests;
- Start from files reported by
Static formatting/linting
- Ruff:
- Run
./.security/ruff_pr_diff.shif present. - Run
.bin/ruff/ruff checkon changed Python files. - Run
ruff format/.bin/ruff formaton those files.
- Run
- Templates:
- Run
djlint-reformat-djangoanddjlint-djangoon changed templates when configured in.pre-commit-config.yaml.
- Run
- Generic pre-commit hooks:
- Respect hook definitions in
.pre-commit-config.yaml; runpre-commit runon relevant files when possible.
- Respect hook definitions in
- Ruff:
Backend-specific .security gates
- Run
.securityscripts where present:./.security/ruff_pr_diff.sh– Ruff on changed files vs base branch../.security/local_imports_pr_diff.sh– check for local imports.
- Treat failures as at least
[SHOULD_FIX]and usually[BLOCKING]foratomic-commit.
- Run
Type/system checks
- Run
.bin/tyor./ty_checks.pyfor type checking (respecting any documented baseline, but treating new issues seriously). - Run
.bin/django checkor equivalent:uv run python manage.py check --fail-level WARNING.
- For risky changes (models, migrations, core logic), run targeted
pytestsubsets based on changed apps:- Example:
dashboardapp/changes →pytest dashboardapp/tests/.
- Example:
- If tests cannot be run (e.g. env not set up), say so explicitly and treat
“tests not run” as at least
[SHOULD_FIX]and often[BLOCKING]foratomic-commit.
- Run
Interaction with pre-commit hooks
- If
.pre-commit-config.yamlexists:- Expect hooks to run and modify files (ruff, djlint, interrogate, custom scripts).
- After hooks run, re-check
git statusand restage modified files as appropriate.
- If a hook executable is missing (e.g.
check_prepare_commit_msg_hook.pyreferenced but not present), do not crash:- Record a
[SHOULD_FIX]issue stating which hook is missing and why it matters.
- Record a
- If
Monty Backend Taste – Auto-Fix Rules
When running in pre-commit mode, you are allowed and expected to actively
edit code to align with Monty’s backend taste where it is clearly safe. In
atomic-commit mode, you may still fix things, but be more conservative and
always summarize edits.
Imports & local imports
- Enforce no local imports at any cost:
- Avoid
from myapp.models import MyModelinside functions or methods just to dodge cyclic imports. - Use
.security/local_imports_pr_diff.shas the first line of defense. - Prefer:
- Module-level imports.
- Refactoring helpers to avoid cycles.
- Type-only imports (e.g.
from __future__ import annotations) when needed.
- If moving imports risks true cyclic import problems:
- Suggest structural changes (splitting modules, relocating helpers).
- Never silently reintroduce local imports; call out unresolved cycles as
[SHOULD_FIX].
- Avoid
Logging (especially in optimo_* apps)
- Enforce structured logging:
- Prefer structured payloads over single log strings:
- Good:
logger.info("optimo_event", extra={"company_uuid": str(company.uuid)}). - Avoid:
logger.info("Company %s did %s", company.name, something).
- Good:
- In
optimo_*apps, treat unstructured logging as at least[SHOULD_FIX].
- Prefer structured payloads over single log strings:
- PII in logs:
- Never log PII such as
assignment.employee.email; this is enforced by pre-commit hooks already, but you should also conceptually check. - Prefer logging UUIDs/IDs instead of emails or names.
- Never log PII such as
- Log levels:
- Avoid
logger.exceptionfor expected error paths; useerrororwarningwith explicit messages.
- Avoid
Try/except and error handling
- Avoid large, catch-all
try/exceptblocks:- Shrink the
trybody to only the lines that can raise. - Replace bare
except:orexcept Exception:with specific exceptions whenever possible.
- Shrink the
- Never swallow exceptions silently:
- Always log or re-raise; returning silently on failure is
[BLOCKING]for behaviorally important code.
- Always log or re-raise; returning silently on failure is
- Avoid overusing
getattr/hasattras a crutch:- Only use
hasattr()when truly needed (e.g. cross-version adapters) and document why.
- Only use
Types, hints, and data structures
- Be pedantic about type hints:
- Avoid
Anyas much as possible; prefer precise types and generics. - No string-based type hints like
"OptimoRiskQuestionBank"; use real types and proper imports. - Add missing annotations on new/changed functions, especially in
optimo_*,dashboardapp, and other core apps.
- Avoid
- Prefer structured data:
- Replace
Dict[str, Any]or ad-hoc dict payloads withTypedDictor dataclasses when the shape is stable and local. - Avoid shape-changing dicts where keys appear/disappear across branches; suggest a typed structure instead.
- Replace
Tests and fixtures
- Avoid repeating fixtures or introducing fixture collisions:
- Prefer existing rich fixtures described in
AGENTS.md(e.g.responsesin survey tests, company/survey fixtures that respect multi-tenant relationships). - Do not introduce new Django
TestCaseclasses inoptimo_*apps; use pytest + fixtures. - Watch for multi-tenant fixture mismatches (e.g. survey from one company,
user from another); highlight these as
[BLOCKING]when they affect correctness.
- Prefer existing rich fixtures described in
- When touching tests:
- Prefer pytest fixtures and helper factories.
- Avoid local imports inside tests; use module-level imports.
ORM and query patterns
- Use reverse relations where it reduces imports and improves clarity:
- Prefer
company.surveys.all()toSurvey.objects.filter(company=company)when it avoids extra imports and is idiomatic.
- Prefer
- Watch for N+1 queries in obvious loops:
- Flag them as
[SHOULD_FIX]for hot paths or performance-sensitive code.
- Flag them as
Commented / dead code and debug artifacts
- Remove obvious debug leftovers:
print(...),pdb.set_trace(),ipdb.set_trace(),breakpoint()in non-test code.
- Remove clearly obsolete commented-out blocks:
- Old versions of code commented around a new implementation.
- For ambiguous commented sections:
- Flag them as
[SHOULD_FIX]and suggest either deleting them or moving rationale into docs.
- Flag them as
TODO/FIXMEwithout ticket IDs:- Suggest converting to ticket-tagged comments (e.g.
TODO(GH-123): ...orTODO(27pfu0): ...) or moving the note into ClickUp.
- Suggest converting to ticket-tagged comments (e.g.
String / formatting style
- Migrate to f-strings where it improves clarity:
- Replace old
%formatting or.format()with f-strings when not blocked by translation/i18n constraints.
- Replace old
- Avoid giant f-strings with logic:
- Suggest splitting into intermediate variables when readability suffers.
Security & secrets
- Detect obvious secrets checked into code or fixtures:
- Hardcoded tokens, passwords, API keys.
- Flag as
[BLOCKING]and suggest using environment variables and 1Password.
- Avoid staging obvious secret-heavy files:
.env,google_creds.json,google_drive_creds.json, etc.- Recommend un-staging and
.gitignoreupdates where appropriate.
Migrations and schema changes
- Do not change migration behavior automatically; instead:
- Detect destructive schema changes (dropping fields/tables) combined with
code changes that still expect those fields:
- Mark as
[BLOCKING]and recommend a two-step rollout:- PR 1: remove usage in code, keep schema.
- PR 2: drop the field/table once code is clean.
- Mark as
- Detect new non-nullable fields with defaults on large/hot tables:
- Mark as
[SHOULD_FIX]or[BLOCKING]depending on risk. - Suggest the safer pattern:
- Add nullable field with no default.
- Backfill in batches.
- Then add default for new rows only.
- Mark as
- Detect volatile defaults (UUIDs, timestamps) used in migrations:
- Warn against backfilling large tables inside a single atomic migration; recommend batched or non-atomic backfills.
- Detect destructive schema changes (dropping fields/tables) combined with
code changes that still expect those fields:
Critical backend patterns from AGENTS.md
- Watch for new instances of patterns explicitly banned in backend
AGENTS.md:- Django Ninja
Query()module-level constants that break parameter resolution (e.g.Q_INCLUDE_INACTIVE = Query(False, ...)). - Legacy survey models like
OptimoEmployeeSurveyoremployee_survey. - Any other CRITICAL warnings spelled out in that file.
- Django Ninja
- Treat any new usage of these patterns as
[BLOCKING].
Atomic-Commit Mode – Extra Strictness
In atomic-commit mode (invoked via /backend-atomic-commit:atomic-commit),
you must be very strict:
Atomicity of staged changes
- From
git diff --cached --name-only, determine if staged changes belong to one coherent change:- Example of non-atomic:
- Refactor in
survey/plus an unrelated optimo bugfix and docs tweak.
- Refactor in
- Example of non-atomic:
- Emit:
[BLOCKING]if the staged set is clearly multiple logical changes.[SHOULD_FIX]for minor opportunistic cleanups that could be split.
- You may suggest a split (e.g. “extract the optimo fix into a separate commit”) but must not label a non-atomic set as “ready”.
- From
All gates must be green
- The commit is not ready if any of these fail:
./.security/ruff_pr_diff.sh./.security/local_imports_pr_diff.sh.bin/ruff/ruff format.bin/ty/./ty_checks.py.bin/django check/manage.py check- Relevant
pytestsubsets for risky changes - Pre-commit hooks defined in
.pre-commit-config.yaml
- In
--autostyle usage, you may skip conversational confirmation, but you must not relax these gates. - If tests or checks are skipped for any reason, clearly state that and
treat it as at least
[SHOULD_FIX]and usually[BLOCKING].
- The commit is not ready if any of these fail:
Commit message generation (no AI signature)
- Extract ticket ID from branch name using repo conventions:
- For the Diversio backend:
- Branch name:
clickup_<ticket_id>_... - Commit format per
AGENTS.md:<ticket_id>: Description.
- Branch name:
- If commit message hooks like
commit_msg_hook.pyexist:- Avoid double-prefixing ticket IDs.
- If hooks and docs disagree, call that out as
[SHOULD_FIX]and follow the documentedAGENTS.mdconvention for suggestions.
- For the Diversio backend:
- Generate a concise, human-looking subject line:
- Summarize what changed and why in one line.
- Do not mention Claude, AI, this Skill, or plugin names.
- Do not add any footer or signature:
- No “via Claude Code”.
- No “Generated by backend-atomic-commit”.
- Commit messages must look like a human wrote them.
- Final preview and verdict
Your atomic-commit output should include:
- A short summary of what was checked.
Checks run– listing each gate and its status.What’s aligned– strengths and good patterns in the staged changes.Needs changes– bullets with[BLOCKING],[SHOULD_FIX],[NIT].Proposed commit– suggested commit message and list of files.- An explicit verdict:
- “✅ Commit ready” only if there are no
[BLOCKING]items. - Otherwise:
- “❌ Not ready to commit” with concrete next steps.
- “✅ Commit ready” only if there are no
You should never encourage the user to run git commit as-is if any
[BLOCKING] issues remain.
Pre-Commit Mode – Fixing Without Committing
In pre-commit mode (invoked via /backend-atomic-commit:pre-commit):
- You may aggressively auto-fix:
- Formatting, linting, local imports, obvious type hints, logging patterns, removal of debug code, and consistent fixtures.
- You must:
- Run the same gates described above (Ruff,
.security/*, ty, Django checks, tests as appropriate). - Re-run or re-stage files modified by tools or hooks.
- Run the same gates described above (Ruff,
- You do not propose a commit or check atomicity.
- Your output should focus on:
Fixes applied– concrete edits you made.Remaining issues– with severity tags.Checks run– which gates passed/failed.
This mode is the “make my working tree clean and standards-compliant” helper before running an atomic commit.
Severity Tags & Output Shape
Always structure findings using severity tags and sections:
[BLOCKING]– must be fixed before a commit is considered ready:- Failing
.securityscripts or pre-commit hooks. - New banned patterns from
AGENTS.md(e.g., Ninja Query constants, legacy survey models). - Obvious multi-tenant or security regressions.
- Non-atomic staged changes in
atomic-commitmode.
- Failing
[SHOULD_FIX]– important, strongly recommended changes:- Style/structure that harms readability or maintainability.
- Missing type hints where types are clear.
- Ambiguous commented code or TODOs without tickets.
- Missing tests for non-trivial new behavior.
[NIT]– minor cleanups:- Docstring tone/punctuation.
- Minor naming and formatting nits not covered by Ruff.
Output shape for both modes:
- 1–3 sentence summary of what was checked.
- Sections (when appropriate):
What’s alignedNeeds changesChecks runProposed commit(only in atomic-commit mode)
Be direct, specific, and actionable in each bullet, pointing to file/area and suggesting concrete corrections. Never hide behind vague "consider improving" phrases when you can be precise.
Compatibility Notes
This skill is designed to work with both Claude Code and OpenAI Codex.
For Codex users:
- Install via skill-installer with
--repo DiversioTeam/agent-skills-marketplace --path plugins/backend-atomic-commit/skills/backend-atomic-commit. - Use
$skill backend-atomic-committo invoke.
For Claude Code users:
- Install via
/plugin install backend-atomic-commit@diversiotech. - Use
/backend-atomic-commit:atomic-commitor/backend-atomic-commit:pre-committo invoke.