@mrciphersmith/keryx 0.3.3 → 0.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +996 -366
- package/docs/README.md +2 -0
- package/package.json +1 -1
- package/src/gdskills/bundled/install-manifest.json +520 -48
- package/src/gdskills/bundled/stacks/csharp-dotnet/agent-refs.json +4 -0
- package/src/gdskills/bundled/stacks/csharp-dotnet/governance/eval.json +1881 -0
- package/src/gdskills/bundled/stacks/csharp-dotnet/governance/scout.json +33 -0
- package/src/gdskills/bundled/stacks/csharp-dotnet/pack.json +38 -0
- package/src/gdskills/bundled/stacks/csharp-dotnet/rules/coding-style.mdc +100 -0
- package/src/gdskills/bundled/stacks/csharp-dotnet/rules/patterns.mdc +107 -0
- package/src/gdskills/bundled/stacks/csharp-dotnet/rules/security.mdc +86 -0
- package/src/gdskills/bundled/stacks/csharp-dotnet/rules/testing.mdc +89 -0
- package/src/gdskills/bundled/stacks/csharp-dotnet/skills/dotnet-build-fix/SKILL.md +143 -0
- package/src/gdskills/bundled/stacks/csharp-dotnet/skills/dotnet-build-fix/evals.json +77 -0
- package/src/gdskills/bundled/stacks/csharp-dotnet/skills/dotnet-code-review/SKILL.md +121 -0
- package/src/gdskills/bundled/stacks/csharp-dotnet/skills/dotnet-code-review/evals.json +77 -0
- package/src/gdskills/bundled/stacks/csharp-dotnet/skills/dotnet-implementation/SKILL.md +134 -0
- package/src/gdskills/bundled/stacks/csharp-dotnet/skills/dotnet-implementation/evals.json +76 -0
- package/src/gdskills/bundled/stacks/csharp-dotnet/skills/dotnet-testing/SKILL.md +130 -0
- package/src/gdskills/bundled/stacks/csharp-dotnet/skills/dotnet-testing/evals.json +77 -0
- package/src/gdskills/bundled/stacks/django/agent-refs.json +3 -0
- package/src/gdskills/bundled/stacks/django/governance/eval.json +1763 -0
- package/src/gdskills/bundled/stacks/django/governance/scout.json +40 -0
- package/src/gdskills/bundled/stacks/django/pack.json +43 -0
- package/src/gdskills/bundled/stacks/django/rules/coding-style.mdc +80 -0
- package/src/gdskills/bundled/stacks/django/rules/patterns.mdc +92 -0
- package/src/gdskills/bundled/stacks/django/rules/security.mdc +92 -0
- package/src/gdskills/bundled/stacks/django/rules/testing.mdc +89 -0
- package/src/gdskills/bundled/stacks/django/skills/django-build-fix/SKILL.md +149 -0
- package/src/gdskills/bundled/stacks/django/skills/django-build-fix/evals.json +49 -0
- package/src/gdskills/bundled/stacks/django/skills/django-code-review/SKILL.md +137 -0
- package/src/gdskills/bundled/stacks/django/skills/django-code-review/evals.json +48 -0
- package/src/gdskills/bundled/stacks/django/skills/django-implementation/SKILL.md +147 -0
- package/src/gdskills/bundled/stacks/django/skills/django-implementation/evals.json +75 -0
- package/src/gdskills/bundled/stacks/django/skills/django-migrate/SKILL.md +166 -0
- package/src/gdskills/bundled/stacks/django/skills/django-migrate/evals.json +49 -0
- package/src/gdskills/bundled/stacks/django/skills/django-testing/SKILL.md +130 -0
- package/src/gdskills/bundled/stacks/django/skills/django-testing/evals.json +48 -0
- package/src/gdskills/bundled/stacks/fastapi/agent-refs.json +3 -0
- package/src/gdskills/bundled/stacks/fastapi/governance/eval.json +1777 -0
- package/src/gdskills/bundled/stacks/fastapi/governance/scout.json +34 -0
- package/src/gdskills/bundled/stacks/fastapi/pack.json +43 -0
- package/src/gdskills/bundled/stacks/fastapi/rules/coding-style.mdc +68 -0
- package/src/gdskills/bundled/stacks/fastapi/rules/patterns.mdc +108 -0
- package/src/gdskills/bundled/stacks/fastapi/rules/security.mdc +99 -0
- package/src/gdskills/bundled/stacks/fastapi/rules/testing.mdc +85 -0
- package/src/gdskills/bundled/stacks/fastapi/skills/fastapi-build-fix/SKILL.md +157 -0
- package/src/gdskills/bundled/stacks/fastapi/skills/fastapi-build-fix/evals.json +76 -0
- package/src/gdskills/bundled/stacks/fastapi/skills/fastapi-code-review/SKILL.md +150 -0
- package/src/gdskills/bundled/stacks/fastapi/skills/fastapi-code-review/evals.json +74 -0
- package/src/gdskills/bundled/stacks/fastapi/skills/fastapi-implementation/SKILL.md +158 -0
- package/src/gdskills/bundled/stacks/fastapi/skills/fastapi-implementation/evals.json +75 -0
- package/src/gdskills/bundled/stacks/fastapi/skills/fastapi-testing/SKILL.md +146 -0
- package/src/gdskills/bundled/stacks/fastapi/skills/fastapi-testing/evals.json +74 -0
- package/src/gdskills/bundled/stacks/flutter-dart/agent-refs.json +4 -0
- package/src/gdskills/bundled/stacks/flutter-dart/governance/eval.json +1849 -0
- package/src/gdskills/bundled/stacks/flutter-dart/governance/scout.json +33 -0
- package/src/gdskills/bundled/stacks/flutter-dart/pack.json +41 -0
- package/src/gdskills/bundled/stacks/flutter-dart/rules/coding-style.mdc +98 -0
- package/src/gdskills/bundled/stacks/flutter-dart/rules/patterns.mdc +88 -0
- package/src/gdskills/bundled/stacks/flutter-dart/rules/security.mdc +91 -0
- package/src/gdskills/bundled/stacks/flutter-dart/rules/testing.mdc +101 -0
- package/src/gdskills/bundled/stacks/flutter-dart/skills/flutter-build-fix/SKILL.md +134 -0
- package/src/gdskills/bundled/stacks/flutter-dart/skills/flutter-build-fix/evals.json +79 -0
- package/src/gdskills/bundled/stacks/flutter-dart/skills/flutter-code-review/SKILL.md +124 -0
- package/src/gdskills/bundled/stacks/flutter-dart/skills/flutter-code-review/evals.json +74 -0
- package/src/gdskills/bundled/stacks/flutter-dart/skills/flutter-implementation/SKILL.md +139 -0
- package/src/gdskills/bundled/stacks/flutter-dart/skills/flutter-implementation/evals.json +77 -0
- package/src/gdskills/bundled/stacks/flutter-dart/skills/flutter-testing/SKILL.md +134 -0
- package/src/gdskills/bundled/stacks/flutter-dart/skills/flutter-testing/evals.json +74 -0
- package/src/gdskills/bundled/stacks/java-kotlin-spring/agent-refs.json +3 -0
- package/src/gdskills/bundled/stacks/java-kotlin-spring/governance/eval.json +2194 -0
- package/src/gdskills/bundled/stacks/java-kotlin-spring/governance/scout.json +39 -0
- package/src/gdskills/bundled/stacks/java-kotlin-spring/pack.json +40 -0
- package/src/gdskills/bundled/stacks/java-kotlin-spring/rules/coding-style.mdc +67 -0
- package/src/gdskills/bundled/stacks/java-kotlin-spring/rules/patterns.mdc +65 -0
- package/src/gdskills/bundled/stacks/java-kotlin-spring/rules/security.mdc +69 -0
- package/src/gdskills/bundled/stacks/java-kotlin-spring/rules/testing.mdc +80 -0
- package/src/gdskills/bundled/stacks/java-kotlin-spring/skills/java-kotlin-spring-build-fix/SKILL.md +144 -0
- package/src/gdskills/bundled/stacks/java-kotlin-spring/skills/java-kotlin-spring-build-fix/evals.json +74 -0
- package/src/gdskills/bundled/stacks/java-kotlin-spring/skills/java-kotlin-spring-code-review/SKILL.md +129 -0
- package/src/gdskills/bundled/stacks/java-kotlin-spring/skills/java-kotlin-spring-code-review/evals.json +74 -0
- package/src/gdskills/bundled/stacks/java-kotlin-spring/skills/java-kotlin-spring-implementation/SKILL.md +147 -0
- package/src/gdskills/bundled/stacks/java-kotlin-spring/skills/java-kotlin-spring-implementation/evals.json +75 -0
- package/src/gdskills/bundled/stacks/java-kotlin-spring/skills/java-kotlin-spring-migrate/SKILL.md +139 -0
- package/src/gdskills/bundled/stacks/java-kotlin-spring/skills/java-kotlin-spring-migrate/evals.json +74 -0
- package/src/gdskills/bundled/stacks/java-kotlin-spring/skills/java-kotlin-spring-testing/SKILL.md +128 -0
- package/src/gdskills/bundled/stacks/java-kotlin-spring/skills/java-kotlin-spring-testing/evals.json +73 -0
- package/src/gdskills/bundled/stacks/kotlin-android/agent-refs.json +4 -0
- package/src/gdskills/bundled/stacks/kotlin-android/governance/eval.json +1889 -0
- package/src/gdskills/bundled/stacks/kotlin-android/governance/scout.json +34 -0
- package/src/gdskills/bundled/stacks/kotlin-android/pack.json +38 -0
- package/src/gdskills/bundled/stacks/kotlin-android/rules/coding-style.mdc +89 -0
- package/src/gdskills/bundled/stacks/kotlin-android/rules/patterns.mdc +96 -0
- package/src/gdskills/bundled/stacks/kotlin-android/rules/security.mdc +90 -0
- package/src/gdskills/bundled/stacks/kotlin-android/rules/testing.mdc +89 -0
- package/src/gdskills/bundled/stacks/kotlin-android/skills/compose-implementation/SKILL.md +150 -0
- package/src/gdskills/bundled/stacks/kotlin-android/skills/compose-implementation/evals.json +77 -0
- package/src/gdskills/bundled/stacks/kotlin-android/skills/kotlin-android-build-fix/SKILL.md +151 -0
- package/src/gdskills/bundled/stacks/kotlin-android/skills/kotlin-android-build-fix/evals.json +76 -0
- package/src/gdskills/bundled/stacks/kotlin-android/skills/kotlin-android-code-review/SKILL.md +139 -0
- package/src/gdskills/bundled/stacks/kotlin-android/skills/kotlin-android-code-review/evals.json +78 -0
- package/src/gdskills/bundled/stacks/kotlin-android/skills/kotlin-android-testing/SKILL.md +131 -0
- package/src/gdskills/bundled/stacks/kotlin-android/skills/kotlin-android-testing/evals.json +77 -0
- package/src/gdskills/bundled/stacks/python/agent-refs.json +2 -1
- package/src/gdskills/bundled/stacks/python/pack.json +1 -1
- package/src/gdskills/bundled/stacks/rust/agent-refs.json +3 -0
- package/src/gdskills/bundled/stacks/rust/governance/eval.json +1823 -0
- package/src/gdskills/bundled/stacks/rust/governance/scout.json +32 -0
- package/src/gdskills/bundled/stacks/rust/pack.json +42 -0
- package/src/gdskills/bundled/stacks/rust/rules/coding-style.mdc +93 -0
- package/src/gdskills/bundled/stacks/rust/rules/patterns.mdc +85 -0
- package/src/gdskills/bundled/stacks/rust/rules/security.mdc +85 -0
- package/src/gdskills/bundled/stacks/rust/rules/testing.mdc +82 -0
- package/src/gdskills/bundled/stacks/rust/skills/rust-build-fix/SKILL.md +141 -0
- package/src/gdskills/bundled/stacks/rust/skills/rust-build-fix/evals.json +78 -0
- package/src/gdskills/bundled/stacks/rust/skills/rust-code-review/SKILL.md +127 -0
- package/src/gdskills/bundled/stacks/rust/skills/rust-code-review/evals.json +72 -0
- package/src/gdskills/bundled/stacks/rust/skills/rust-implementation/SKILL.md +133 -0
- package/src/gdskills/bundled/stacks/rust/skills/rust-implementation/evals.json +79 -0
- package/src/gdskills/bundled/stacks/rust/skills/rust-testing/SKILL.md +130 -0
- package/src/gdskills/bundled/stacks/rust/skills/rust-testing/evals.json +75 -0
- package/src/gdskills/bundled/stacks/swift-ios/agent-refs.json +4 -0
- package/src/gdskills/bundled/stacks/swift-ios/governance/eval.json +1803 -0
- package/src/gdskills/bundled/stacks/swift-ios/governance/scout.json +32 -0
- package/src/gdskills/bundled/stacks/swift-ios/pack.json +38 -0
- package/src/gdskills/bundled/stacks/swift-ios/rules/coding-style.mdc +92 -0
- package/src/gdskills/bundled/stacks/swift-ios/rules/patterns.mdc +112 -0
- package/src/gdskills/bundled/stacks/swift-ios/rules/security.mdc +78 -0
- package/src/gdskills/bundled/stacks/swift-ios/rules/testing.mdc +90 -0
- package/src/gdskills/bundled/stacks/swift-ios/skills/swift-build-fix/SKILL.md +144 -0
- package/src/gdskills/bundled/stacks/swift-ios/skills/swift-build-fix/evals.json +75 -0
- package/src/gdskills/bundled/stacks/swift-ios/skills/swift-code-review/SKILL.md +122 -0
- package/src/gdskills/bundled/stacks/swift-ios/skills/swift-code-review/evals.json +75 -0
- package/src/gdskills/bundled/stacks/swift-ios/skills/swift-testing/SKILL.md +131 -0
- package/src/gdskills/bundled/stacks/swift-ios/skills/swift-testing/evals.json +75 -0
- package/src/gdskills/bundled/stacks/swift-ios/skills/swiftui-implementation/SKILL.md +149 -0
- package/src/gdskills/bundled/stacks/swift-ios/skills/swiftui-implementation/evals.json +76 -0
- package/src/gdskills/bundled/agents/python-build-fixer.md +0 -52
- package/src/gdskills/bundled/agents/python-code-auditor.md +0 -49
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"query": "Use when a Django project fails manage.py check, makemigrations/migrate, the test suite, or lint/type-check -- resolves migration conflicts and inconsistent migration history, ImproperlyConfigured settings errors, app-loading/circular-import errors, mypy/django-stubs type errors, and ruff failures with the smallest root-cause fix. Not for adding a feature (see django-implementation) or fixing test content (see django-testing).",
|
|
4
|
+
"decision": "fork",
|
|
5
|
+
"topMatch": "python/python-build-fix",
|
|
6
|
+
"recordedAt": "2026-09-25T18:06:19.335Z",
|
|
7
|
+
"skillName": "django-build-fix",
|
|
8
|
+
"justification": "top match python/python-build-fix (0.3-0.55 range) is the same build-fix category but for generic Python tooling, not Django's own manage.py/migration/ImproperlyConfigured failure modes; a fork scoped to Django's own build/check/migration errors is warranted since the shared category is deliberate but the specific failure surface (makemigrations conflicts, app-loading, django-stubs) is not covered by python-build-fix."
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"query": "Use when reviewing Django changes for correctness and injection-class risks -- checks N+1 queries missing select_related/prefetch_related, mark_safe/|safe applied to user-traceable data, raw()/extra() built by string interpolation, @csrf_exempt on a session-authenticated view, missing/hand-edited migrations, and settings.py misconfiguration (DEBUG, SECRET_KEY, ALLOWED_HOSTS). Read-only: reports findings, does not edit code.",
|
|
12
|
+
"decision": "create",
|
|
13
|
+
"topMatch": "fastapi/fastapi-code-review",
|
|
14
|
+
"recordedAt": "2026-09-25T18:06:19.640Z",
|
|
15
|
+
"skillName": "django-code-review",
|
|
16
|
+
"justification": "top match python/python-code-review is the same review category but for generic Python risks (mutable defaults, bare except, resource leaks), not Django's own injection-class sinks (N+1 via select_related, mark_safe/|safe XSS, raw()/extra() SQL, csrf_exempt, migration hygiene); a Django-scoped fork covers concerns python-code-review has no visibility into."
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"query": "Use when implementing or extending a feature in a Django (5.x) project -- covers models and migrations, class-based/function-based views, forms and serializers, querysets with select_related/prefetch_related to avoid N+1, and the framework's CSRF/escaping defaults. Not for plain Python with no Django import (see python-implementation) or a Django version-major upgrade (see django-migrate).",
|
|
20
|
+
"decision": "fork",
|
|
21
|
+
"topMatch": "django/django-code-review",
|
|
22
|
+
"recordedAt": "2026-09-25T18:06:19.952Z",
|
|
23
|
+
"skillName": "django-implementation",
|
|
24
|
+
"justification": "top match django/django-testing is this same pack's test-authoring skill, sharing only generic Django-project vocabulary (models, views, pytest-django) from its own test-authoring scope, not implementation/typing/queryset-design guidance; decision is fork since both are legitimately Django-scoped but cover disjoint workflows (writing production code vs. writing tests)."
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"query": "Use when generating, reviewing, or safely applying Django schema/data migrations -- makemigrations for a model change, RunPython data migrations with forward/reverse functions, merge migrations for divergent branches, and staged rollout for a migration that must ship alongside a multi-phase deploy (e.g. a column removal). Not for a Django-major-version framework upgrade (there is no separate django-upgrade skill in this pack; treat a version jump as django-implementation plus this skill's migration discipline) or for fixing a broken/conflicting migration that's blocking the build (see django-build-fix).",
|
|
28
|
+
"decision": "create",
|
|
29
|
+
"topMatch": "java-kotlin-spring/java-kotlin-spring-migrate",
|
|
30
|
+
"recordedAt": "2026-09-25T18:06:20.259Z",
|
|
31
|
+
"skillName": "django-migrate"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"query": "Use when writing Django TestCase/SimpleTestCase classes, simulating browser requests against a view with Django's own request-simulation helper, wiring pytest-django fixtures (db, django_user_model), building factory_boy model factories, or asserting on permission/form/queryset behavior in a Django app's own test suite. Django-specific: ORM assertions, migration-aware test databases, and DRF serializer/view tests, scoped to code that actually imports Django.",
|
|
35
|
+
"decision": "create",
|
|
36
|
+
"topMatch": "fastapi/fastapi-testing",
|
|
37
|
+
"recordedAt": "2026-09-25T18:06:20.569Z",
|
|
38
|
+
"skillName": "django-testing"
|
|
39
|
+
}
|
|
40
|
+
]
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "django",
|
|
3
|
+
"family": "framework",
|
|
4
|
+
"extends": "python",
|
|
5
|
+
"modules": ["django-rules", "django-skills"],
|
|
6
|
+
"detectionMarkers": ["django"],
|
|
7
|
+
"provenance": {
|
|
8
|
+
"origin": "authored",
|
|
9
|
+
"sourceRef": "flow 335, Wave 4 batch 3"
|
|
10
|
+
},
|
|
11
|
+
"stability": "experimental",
|
|
12
|
+
"skills": {
|
|
13
|
+
"implement": ["django-implementation"],
|
|
14
|
+
"test": ["django-testing"],
|
|
15
|
+
"review": ["django-code-review"],
|
|
16
|
+
"build-fix": ["django-build-fix"],
|
|
17
|
+
"migrate": ["django-migrate"]
|
|
18
|
+
},
|
|
19
|
+
"agentProfile": {
|
|
20
|
+
"displayName": "Django",
|
|
21
|
+
"auditFocus": [
|
|
22
|
+
"a loop that issues one query per row where `select_related`/`prefetch_related` would collapse it (N+1)",
|
|
23
|
+
"`mark_safe`/the `|safe` template filter applied to anything that traces back to user input",
|
|
24
|
+
"`.raw()`/`.extra()` built with f-strings/`%`/`.format()` instead of the `params` argument",
|
|
25
|
+
"`@csrf_exempt` or `CsrfViewMiddleware` disabled on a view that actually needs CSRF protection",
|
|
26
|
+
"a model field or migration change with no corresponding migration file, or a hand-edited migration that drifts from `makemigrations` output",
|
|
27
|
+
"`SECRET_KEY`/`DEBUG=True`/permissive `ALLOWED_HOSTS` reaching a production settings module"
|
|
28
|
+
],
|
|
29
|
+
"buildCommands": [
|
|
30
|
+
"python manage.py check",
|
|
31
|
+
"python manage.py makemigrations --check --dry-run",
|
|
32
|
+
"ruff check . / ruff format --check .",
|
|
33
|
+
"mypy . (with django-stubs) or the project's configured type checker",
|
|
34
|
+
"python manage.py test # or: pytest (pytest-django) if the project uses it"
|
|
35
|
+
],
|
|
36
|
+
"fixGuardrails": [
|
|
37
|
+
"never add `mark_safe`/`|safe` to route around an escaping error instead of fixing the underlying markup",
|
|
38
|
+
"never disable CSRF (`@csrf_exempt`, removing `CsrfViewMiddleware`) to make a failing request succeed",
|
|
39
|
+
"never hand-edit a generated migration file to dodge a `makemigrations` conflict without resolving the actual model drift",
|
|
40
|
+
"never widen a narrowed queryset/model field type or drop a validator just to make a check pass"
|
|
41
|
+
]
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
---
|
|
2
|
+
extends: common
|
|
3
|
+
paths: ["**/models.py", "**/models/**/*.py", "**/views.py", "**/views/**/*.py", "**/forms.py", "**/serializers.py", "**/urls.py", "**/admin.py", "**/*.py"]
|
|
4
|
+
metadata:
|
|
5
|
+
origin: authored
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Django coding style
|
|
9
|
+
|
|
10
|
+
Narrows `python`'s coding style to Django-project conventions. Applies to
|
|
11
|
+
`*.py` files inside a Django project; non-Django Python in the same
|
|
12
|
+
repository (a standalone script, a shared library package with no Django
|
|
13
|
+
import) still comes from `python`'s own coding-style rule, not this file.
|
|
14
|
+
|
|
15
|
+
## App and project layout
|
|
16
|
+
|
|
17
|
+
- One Django "app" per cohesive domain concern (`billing/`, `accounts/`),
|
|
18
|
+
each with its own `models.py`/`views.py`/`urls.py`/`migrations/` — do not
|
|
19
|
+
grow a single app into an unrelated dumping ground once it outgrows one
|
|
20
|
+
clear responsibility.
|
|
21
|
+
- Name an app's `urls.py` route names with the app as a namespace prefix
|
|
22
|
+
(`billing:invoice-detail`) and reverse them with `reverse()`/`{% url %}`,
|
|
23
|
+
never a hand-built path string, so a route can move without breaking every
|
|
24
|
+
caller.
|
|
25
|
+
- Keep `settings.py` split by environment (`settings/base.py` +
|
|
26
|
+
`dev.py`/`prod.py`, or `django-environ`/`django-configurations`) when the
|
|
27
|
+
project already uses that layout; match the existing convention rather
|
|
28
|
+
than introducing a second settings-splitting scheme.
|
|
29
|
+
|
|
30
|
+
## Models
|
|
31
|
+
|
|
32
|
+
- Give every model field an explicit `verbose_name`/`help_text` when the
|
|
33
|
+
field name alone would not be self-explanatory to someone using the admin
|
|
34
|
+
or a generated form.
|
|
35
|
+
- Use `related_name` on a `ForeignKey`/`ManyToManyField`/`OneToOneField`
|
|
36
|
+
whenever the default (`<model>_set`) would collide or read unclearly at
|
|
37
|
+
the call site; pick a name that reads naturally from the related side
|
|
38
|
+
(`author.books`, not `author.book_set`).
|
|
39
|
+
- Put field-level defaults and choices near the field (`choices=Status.choices`
|
|
40
|
+
with a `TextChoices`/`IntegerChoices` enum), not a bare tuple of literal
|
|
41
|
+
strings repeated at each call site.
|
|
42
|
+
- Override `save()`/add a `clean()` only for invariants that must hold for
|
|
43
|
+
every write path (including the admin and shell); a validation rule that
|
|
44
|
+
only applies to one form belongs on that `Form`, not the model.
|
|
45
|
+
|
|
46
|
+
## Views and URLs
|
|
47
|
+
|
|
48
|
+
- Prefer a class-based view (`ListView`, `DetailView`, `CreateView`, or a
|
|
49
|
+
DRF `APIView`/`ViewSet` when the project uses Django REST Framework) for
|
|
50
|
+
the framework's standard CRUD shapes; reach for a function-based view when
|
|
51
|
+
the logic does not map cleanly onto a generic view's hooks — current
|
|
52
|
+
Django guidance treats CBVs and FBVs as equally supported, not one as
|
|
53
|
+
deprecated.
|
|
54
|
+
- Keep business logic out of the view body: a view coordinates
|
|
55
|
+
request/response and delegates the actual work to a model method, a form's
|
|
56
|
+
`clean`/`save`, or a service function — a view that inlines several
|
|
57
|
+
unrelated queries and branches is a sign the logic belongs elsewhere.
|
|
58
|
+
- Name URL patterns and match the project's existing `path()`/`re_path()`
|
|
59
|
+
convention; prefer `path()` with converters (`<int:pk>`) over `re_path()`
|
|
60
|
+
unless the match genuinely needs a regex.
|
|
61
|
+
|
|
62
|
+
## Forms and serializers
|
|
63
|
+
|
|
64
|
+
- Validate with a `Form`/`ModelForm` (or DRF `Serializer`/`ModelSerializer`)
|
|
65
|
+
`clean_<field>`/`clean()`/`validate_<field>`/`validate()` method, not
|
|
66
|
+
ad-hoc validation scattered in the view after the fact.
|
|
67
|
+
- Use `ModelForm`/`ModelSerializer` with an explicit `fields = [...]` list
|
|
68
|
+
(never `fields = "__all__"` on a form or serializer that accepts
|
|
69
|
+
user-submitted data) so a new model field is not silently exposed for
|
|
70
|
+
mass-assignment until it is deliberately added.
|
|
71
|
+
|
|
72
|
+
## Imports and structure
|
|
73
|
+
|
|
74
|
+
- Follow `python`'s stdlib/third-party/local import grouping; within
|
|
75
|
+
"third-party", Django's own imports (`django.db`, `django.http`, ...)
|
|
76
|
+
commonly sort before other third-party packages alphabetically — match
|
|
77
|
+
what the project's `ruff`/`isort` config already produces rather than
|
|
78
|
+
hand-ordering.
|
|
79
|
+
- Import from `django.conf.settings`, never re-read environment variables
|
|
80
|
+
directly in application code that already has a settings value for it.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
---
|
|
2
|
+
extends: common
|
|
3
|
+
paths: ["**/models.py", "**/models/**/*.py", "**/views.py", "**/views/**/*.py", "**/managers.py", "**/migrations/**/*.py", "**/*.py"]
|
|
4
|
+
metadata:
|
|
5
|
+
origin: authored
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Django patterns
|
|
9
|
+
|
|
10
|
+
Narrows `python`'s design guidance to idiomatic Django (5.x) ORM, view, and
|
|
11
|
+
migration patterns and the anti-patterns they replace. Applies to Django
|
|
12
|
+
project files; a non-Django module in the same repo still follows `python`'s
|
|
13
|
+
own `rules/patterns.mdc`.
|
|
14
|
+
|
|
15
|
+
## Query optimization (the N+1 problem)
|
|
16
|
+
|
|
17
|
+
- When a view or serializer will access a related object for every row in a
|
|
18
|
+
queryset (`obj.author.name` in a loop, or a DRF nested serializer field),
|
|
19
|
+
add `select_related("author")` for a `ForeignKey`/`OneToOneField` and
|
|
20
|
+
`prefetch_related("tags")` for a `ManyToManyField`/reverse `ForeignKey` —
|
|
21
|
+
`select_related` joins in one query; `prefetch_related` issues one extra
|
|
22
|
+
query per relation and joins in Python. Combine both when a
|
|
23
|
+
`prefetch_related` path itself needs a `select_related` (`Prefetch("best_pizza__toppings")`
|
|
24
|
+
after `select_related("best_pizza")`).
|
|
25
|
+
- Use `only()`/`defer()` to trim columns from a wide model only when a
|
|
26
|
+
profiled query shows the extra columns matter; reaching for it by default
|
|
27
|
+
makes the queryset fragile to a later field access that silently triggers
|
|
28
|
+
a new query.
|
|
29
|
+
- Prefer `QuerySet.exists()` over `bool(qs)`/`len(qs) > 0` for an
|
|
30
|
+
existence check, and `.count()` over `len(list(qs))`, so the database does
|
|
31
|
+
the work instead of materializing every row into Python.
|
|
32
|
+
- Use `bulk_create`/`bulk_update`/`update()` for a batch write instead of a
|
|
33
|
+
Python loop calling `.save()` per instance — a per-instance loop is an N+1
|
|
34
|
+
on the write side, and skips `bulk_create`'s single round trip (note it
|
|
35
|
+
bypasses `save()`/signals unless explicitly configured to call them).
|
|
36
|
+
|
|
37
|
+
## Migrations
|
|
38
|
+
|
|
39
|
+
- Run `python manage.py makemigrations` to generate a migration from a model
|
|
40
|
+
change; never hand-write a migration's `operations` list to match a model
|
|
41
|
+
edit by guesswork — let Django diff the models, then review the generated
|
|
42
|
+
file.
|
|
43
|
+
- Give a migration that needs to transform existing row data a `RunPython`
|
|
44
|
+
operation with both a forward and a reverse function (or
|
|
45
|
+
`migrations.RunPython.noop` when reversal is genuinely not meaningful),
|
|
46
|
+
not a schema-only migration that silently leaves old data inconsistent
|
|
47
|
+
with a new constraint.
|
|
48
|
+
- Keep a destructive migration (dropping a column/table another release
|
|
49
|
+
still reads) behind a deploy sequence that ships the code change first,
|
|
50
|
+
then the schema change, when the project deploys code and migrations
|
|
51
|
+
separately — a same-release drop-and-read is a two-phase problem, not a
|
|
52
|
+
one-step migration.
|
|
53
|
+
- Never edit a migration file that has already been applied in another
|
|
54
|
+
environment (shared branch, staging, production) to "fix" it in place;
|
|
55
|
+
add a new migration instead — editing history desyncs any environment
|
|
56
|
+
that already recorded the old migration as applied.
|
|
57
|
+
|
|
58
|
+
## Models as the single source of truth
|
|
59
|
+
|
|
60
|
+
- Put a field-level invariant (a `CheckConstraint`, a `unique_together`/
|
|
61
|
+
`UniqueConstraint`, a `validators=[...]` list) on the model field or
|
|
62
|
+
`Meta.constraints`, not only in a form's `clean()` — validation the
|
|
63
|
+
database itself does not enforce can still be violated by a direct
|
|
64
|
+
`.save()`, the admin, a shell, or another service writing to the same
|
|
65
|
+
table.
|
|
66
|
+
- Use a model `Manager`/`QuerySet` subclass to name a repeated filter
|
|
67
|
+
(`Order.objects.pending()`) instead of repeating the same `.filter(...)`
|
|
68
|
+
chain at every call site.
|
|
69
|
+
|
|
70
|
+
## Views
|
|
71
|
+
|
|
72
|
+
- A view that only reads and renders/serializes belongs on a generic
|
|
73
|
+
class-based view (`ListView`/`DetailView`/DRF `ReadOnlyModelViewSet`); a
|
|
74
|
+
view with custom branching logic per HTTP method is a legitimate reason to
|
|
75
|
+
drop to a function-based view or override a specific CBV method
|
|
76
|
+
(`get_queryset`, `form_valid`) rather than reimplementing the whole
|
|
77
|
+
request/response cycle.
|
|
78
|
+
- Return the framework's own response helpers (`HttpResponseRedirect`/
|
|
79
|
+
`redirect()`, `JsonResponse`, DRF's `Response`) instead of building a raw
|
|
80
|
+
`HttpResponse` with a hand-set `Content-Type` and manually serialized body.
|
|
81
|
+
|
|
82
|
+
## Anti-patterns to flag, not introduce
|
|
83
|
+
|
|
84
|
+
- A `for` loop over a queryset that touches a related object per iteration
|
|
85
|
+
with no `select_related`/`prefetch_related` upstream — this is Django's
|
|
86
|
+
most common performance bug and is invisible in a small dev dataset.
|
|
87
|
+
- `fields = "__all__"` on a `ModelForm`/`ModelSerializer` that accepts
|
|
88
|
+
user input, silently exposing every future field for mass-assignment.
|
|
89
|
+
- A signal handler (`post_save`, etc.) that performs a slow or
|
|
90
|
+
externally-visible side effect (sending an email, calling a third-party
|
|
91
|
+
API) synchronously inside the request/response cycle instead of queuing it
|
|
92
|
+
through the project's async task runner (Celery or equivalent).
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
---
|
|
2
|
+
extends: common
|
|
3
|
+
paths: ["**/views.py", "**/views/**/*.py", "**/forms.py", "**/serializers.py", "**/templatetags/**/*.py", "**/settings*.py", "**/settings/**/*.py"]
|
|
4
|
+
metadata:
|
|
5
|
+
origin: authored
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Django security
|
|
9
|
+
|
|
10
|
+
Narrows `python`'s security guidance to Django's own injection-class sinks:
|
|
11
|
+
views, forms/serializers, template-tag modules, and settings — the Python
|
|
12
|
+
files where Django's built-in CSRF/XSS/SQL-injection protections are either
|
|
13
|
+
relied on or bypassed (template-rendered `.html` files are not `.py`, so
|
|
14
|
+
they are out of this rule's `paths:` scope by construction, even though the
|
|
15
|
+
XSS guidance below still applies to what those views/templatetags render
|
|
16
|
+
into them). Applies only to the listed `.py` files; a non-request-facing
|
|
17
|
+
module (a standalone script, a batch job with no HTTP surface) still
|
|
18
|
+
follows `python`'s own `rules/security.mdc`.
|
|
19
|
+
|
|
20
|
+
## CSRF
|
|
21
|
+
|
|
22
|
+
- Leave `django.middleware.csrf.CsrfViewMiddleware` enabled and let
|
|
23
|
+
`{% csrf_token %}`/the framework's CSRF cookie handling do its job for
|
|
24
|
+
every state-changing view (`POST`/`PUT`/`PATCH`/`DELETE`).
|
|
25
|
+
- Never add `@csrf_exempt` to make a failing request succeed without first
|
|
26
|
+
establishing that the endpoint has another, equivalent protection (a
|
|
27
|
+
signed webhook signature, a non-browser API authenticated by token, never
|
|
28
|
+
session/cookie auth) — CSRF exemption on a session-authenticated endpoint
|
|
29
|
+
reopens the exact attack the middleware exists to block.
|
|
30
|
+
- A DRF `APIView`/`ViewSet` using `SessionAuthentication` still needs CSRF
|
|
31
|
+
protection on unsafe methods (DRF enforces this by default for session
|
|
32
|
+
auth); do not disable it to work around a client that isn't sending the
|
|
33
|
+
token — fix the client to send it.
|
|
34
|
+
|
|
35
|
+
## Cross-site scripting (XSS)
|
|
36
|
+
|
|
37
|
+
- Rely on Django's automatic template auto-escaping for any value that
|
|
38
|
+
reaches an HTML template — do not disable it project-wide.
|
|
39
|
+
- Never apply `mark_safe()`/the `|safe` template filter/`format_html()`'s
|
|
40
|
+
raw-string argument to a value that traces back to user input (a form
|
|
41
|
+
field, a query parameter, an uploaded file's name, anything stored from a
|
|
42
|
+
prior user submission) — `mark_safe`/`|safe` tell Django to skip escaping
|
|
43
|
+
entirely, and reintroduce stored or reflected XSS on exactly the value the
|
|
44
|
+
auto-escaper was protecting. `mark_safe`/`|safe` are for markup the
|
|
45
|
+
application itself constructs and fully controls (a trusted CMS field
|
|
46
|
+
rendered through a sanitizer, static admin-authored HTML) — never for
|
|
47
|
+
data supplied by a request.
|
|
48
|
+
- Build dynamically-assembled HTML with `format_html()`/`format_html_join()`
|
|
49
|
+
(which escape each interpolated argument) instead of string
|
|
50
|
+
concatenation or an f-string passed to `mark_safe()`.
|
|
51
|
+
|
|
52
|
+
## SQL injection
|
|
53
|
+
|
|
54
|
+
- Django's ORM parameterizes query values automatically
|
|
55
|
+
(`.filter(name=user_input)`); this protection does not extend to
|
|
56
|
+
`.raw()` or `.extra()` — pass user-derived values through the `params`
|
|
57
|
+
argument (`Model.objects.raw("... WHERE id = %s", [id])`), never by
|
|
58
|
+
interpolating them into the SQL string with an f-string/`%`/`.format()`.
|
|
59
|
+
- Treat `.extra()` as legacy; prefer expressing the same query with the ORM
|
|
60
|
+
(`annotate`, `F()`, `Q()`, `Func`) or a parameterized `.raw()` call — the
|
|
61
|
+
Django security team explicitly does not treat unsanitized input reaching
|
|
62
|
+
`.extra()`'s `select`/`where` clauses as a framework bug, because
|
|
63
|
+
`.extra()` was never given the ORM's own escaping.
|
|
64
|
+
|
|
65
|
+
## Settings and secrets
|
|
66
|
+
|
|
67
|
+
- `SECRET_KEY` must be at least 50 characters, unique per environment, never
|
|
68
|
+
the `django-insecure-` placeholder `startproject` generates, and never
|
|
69
|
+
committed to source — read it from the environment/secret store.
|
|
70
|
+
- `DEBUG` must be `False` in any settings module used in production;
|
|
71
|
+
`DEBUG = True` leaks stack traces, local variable values, and settings
|
|
72
|
+
contents to any visitor who triggers an unhandled exception.
|
|
73
|
+
- `ALLOWED_HOSTS` must be a real, non-empty list of the application's actual
|
|
74
|
+
hostnames in production, never `["*"]` — an empty or wildcard value
|
|
75
|
+
defeats the Host-header validation Django's deploy checks exist to
|
|
76
|
+
enforce.
|
|
77
|
+
- Run `python manage.py check --deploy` before a production release; it
|
|
78
|
+
flags exactly these three (and related HSTS/secure-cookie) settings
|
|
79
|
+
automatically — don't hand-audit `settings.py` for them instead.
|
|
80
|
+
|
|
81
|
+
## Red flags
|
|
82
|
+
|
|
83
|
+
- "I'll add `|safe` here, this content only ever comes from an admin form" —
|
|
84
|
+
if that admin form field can ever hold end-user-influenced text (a
|
|
85
|
+
support agent pasting a customer's message, a bio field editable by the
|
|
86
|
+
account owner), the trust boundary already includes untrusted input.
|
|
87
|
+
- "`.extra()` with an f-string is fine, this value is just an internal ID"
|
|
88
|
+
— an "internal" ID that ever originates from a URL param, form field, or
|
|
89
|
+
header is user-controlled regardless of how it's described; use `params`.
|
|
90
|
+
- "`@csrf_exempt` unblocks this POST and the tests pass now" — passing
|
|
91
|
+
tests after exempting CSRF proves the exemption works, not that it's
|
|
92
|
+
safe; verify the endpoint has an equivalent protection first.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
---
|
|
2
|
+
extends: common
|
|
3
|
+
paths: ["**/tests.py", "**/tests/**/*.py", "**/test_*.py", "**/*_test.py", "**/conftest.py"]
|
|
4
|
+
metadata:
|
|
5
|
+
origin: authored
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Django testing
|
|
9
|
+
|
|
10
|
+
Narrows `python`'s `pytest` testing guidance (`python/rules/testing.mdc`) to
|
|
11
|
+
Django's own test tooling: the Django `TestCase`/test `Client`, fixtures and
|
|
12
|
+
factories for model data, and `pytest-django` where a project has adopted
|
|
13
|
+
it. Applies to Django test files; a non-Django test module in the same repo
|
|
14
|
+
still follows `python`'s own testing rule.
|
|
15
|
+
|
|
16
|
+
## Choosing the test base
|
|
17
|
+
|
|
18
|
+
- Use `django.test.TestCase` for anything that touches the ORM — it wraps
|
|
19
|
+
each test in a transaction that rolls back afterward, so tests stay
|
|
20
|
+
isolated without manually cleaning up rows.
|
|
21
|
+
- Use `django.test.SimpleTestCase` (or mark it `databases = []`
|
|
22
|
+
under `pytest-django`) for a test that touches no database at all — it
|
|
23
|
+
skips the per-test transaction wrapper and is faster for pure logic.
|
|
24
|
+
- Where the project has adopted `pytest-django`, use its `@pytest.mark.django_db`
|
|
25
|
+
fixture-based style (plain `def test_x(db, client): ...` functions,
|
|
26
|
+
`django_user_model`, `rf` for `RequestFactory`) rather than mixing it with
|
|
27
|
+
`unittest`-style `TestCase` subclasses in the same suite — match whichever
|
|
28
|
+
the project already uses; do not introduce the other style alongside it.
|
|
29
|
+
|
|
30
|
+
## The test client
|
|
31
|
+
|
|
32
|
+
- Drive request/response behavior through `self.client`
|
|
33
|
+
(`TestCase`)/the `client` fixture (`pytest-django`), not by calling a view
|
|
34
|
+
function directly with a hand-built request object — the client exercises
|
|
35
|
+
URL resolution, middleware, and template rendering the same way a real
|
|
36
|
+
request does.
|
|
37
|
+
- Assert on `response.status_code`, `response.context`, and
|
|
38
|
+
`response.templates` (or the parsed JSON body for an API) rather than
|
|
39
|
+
inspecting internal view state that the client's response object doesn't
|
|
40
|
+
expose.
|
|
41
|
+
- Log a test user in through `self.client.force_login(user)` (fast, skips
|
|
42
|
+
the auth backend) or `self.client.login(...)` (exercises the real auth
|
|
43
|
+
backend) — pick `force_login` for tests where the auth mechanism itself
|
|
44
|
+
isn't what's under test.
|
|
45
|
+
|
|
46
|
+
## Model data: fixtures and factories
|
|
47
|
+
|
|
48
|
+
- Prefer a factory (`factory_boy`'s `DjangoModelFactory`, if the project
|
|
49
|
+
already depends on it) over a JSON/YAML fixture file for test data —
|
|
50
|
+
factories build valid model instances from code, so a new required field
|
|
51
|
+
doesn't silently break every fixture file that predates it.
|
|
52
|
+
- Keep a `conftest.py`/factory module for related test data in the
|
|
53
|
+
narrowest app-level location that covers the tests needing it, matching
|
|
54
|
+
`python`'s own "narrowest `conftest.py`" guidance.
|
|
55
|
+
- Build only the fields a test actually asserts on or that a model
|
|
56
|
+
constraint requires; a factory that sets every field to a fixed literal
|
|
57
|
+
makes tests brittle to unrelated schema changes.
|
|
58
|
+
|
|
59
|
+
## Forms, views, and permissions
|
|
60
|
+
|
|
61
|
+
- Test a `Form`/DRF `Serializer` by constructing it directly with test data
|
|
62
|
+
and asserting `is_valid()`/`.errors`, separately from a full
|
|
63
|
+
client-driven view test — this isolates a validation bug from a
|
|
64
|
+
view-wiring bug.
|
|
65
|
+
- For a permission-gated view, assert both the authorized case (200/302 to
|
|
66
|
+
the expected page) and the unauthorized case (302 to login, or 403) —
|
|
67
|
+
a view test that only exercises the happy path never proves the
|
|
68
|
+
permission check actually runs.
|
|
69
|
+
|
|
70
|
+
## Migrations in tests
|
|
71
|
+
|
|
72
|
+
- Do not disable migrations for the whole suite (`MIGRATION_MODULES` set to
|
|
73
|
+
a no-op) purely for speed unless the project has already made that
|
|
74
|
+
tradeoff deliberately — a suite that skips migrations can pass while a
|
|
75
|
+
real deploy's migration path is broken.
|
|
76
|
+
- A test asserting data-migration behavior (a `RunPython` step) belongs in
|
|
77
|
+
its own test using `django.test.migrations`' testing utilities or the
|
|
78
|
+
project's configured migration-test helper, not folded into an unrelated
|
|
79
|
+
model test.
|
|
80
|
+
|
|
81
|
+
## Determinism
|
|
82
|
+
|
|
83
|
+
- Freeze time (`freezegun`, or `pytest-django`'s equivalent) instead of
|
|
84
|
+
asserting against `timezone.now()` computed twice in the test and the
|
|
85
|
+
code under test — a real few-millisecond drift between the two calls is a
|
|
86
|
+
known source of flaky Django tests.
|
|
87
|
+
- Never depend on a specific queryset row order unless the code under test
|
|
88
|
+
applies an explicit `order_by()` — Django does not guarantee row order
|
|
89
|
+
without one, even for a small SQLite test database.
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: django-build-fix
|
|
3
|
+
description: "Use when a Django project fails manage.py check, makemigrations/migrate, the test suite, or lint/type-check -- resolves migration conflicts and inconsistent migration history, ImproperlyConfigured settings errors, app-loading/circular-import errors, mypy/django-stubs type errors, and ruff failures with the smallest root-cause fix. Not for adding a feature (see django-implementation) or fixing test content (see django-testing)."
|
|
4
|
+
triggers:
|
|
5
|
+
- "manage.py check is failing"
|
|
6
|
+
- "fix this django migration conflict"
|
|
7
|
+
- "django ImproperlyConfigured error"
|
|
8
|
+
- "django app isn't loading, circular import"
|
|
9
|
+
- "mypy is failing on this django model"
|
|
10
|
+
- "makemigrations wants to make a conflicting migration"
|
|
11
|
+
- "django test suite won't even start"
|
|
12
|
+
metadata:
|
|
13
|
+
origin: authored
|
|
14
|
+
category: build-fix
|
|
15
|
+
version: "1.0.0"
|
|
16
|
+
compatible_harnesses: "claude,codex,cursor,zed,opencode"
|
|
17
|
+
license: "MIT"
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
# Django build-fix
|
|
21
|
+
|
|
22
|
+
Resolve a broken Django `check`/migration/settings/import/type-check/lint
|
|
23
|
+
failure with the smallest change that fixes the actual cause. Scoped to
|
|
24
|
+
making the Django toolchain green again — for adding a feature use
|
|
25
|
+
`django-implementation`, for writing/fixing test *content* (not a collection
|
|
26
|
+
or startup error) use `django-testing`, for reviewing without fixing use
|
|
27
|
+
`django-code-review`.
|
|
28
|
+
|
|
29
|
+
## Workflow
|
|
30
|
+
|
|
31
|
+
### Step 1: Reproduce and classify the failure
|
|
32
|
+
|
|
33
|
+
Run the project's own configured commands (discover the run prefix from
|
|
34
|
+
`pyproject.toml`/lockfile presence):
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
python manage.py check
|
|
38
|
+
python manage.py makemigrations --check --dry-run
|
|
39
|
+
ruff check .
|
|
40
|
+
ruff format --check .
|
|
41
|
+
mypy . # with django-stubs, if configured
|
|
42
|
+
python manage.py test # or: pytest, if pytest-django is configured
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Read the *first* error in each tool's output. Classify:
|
|
46
|
+
|
|
47
|
+
- **`ImproperlyConfigured`** — a required setting missing/misconfigured
|
|
48
|
+
(`SECRET_KEY`, `DATABASES`, `AUTH_USER_MODEL` pointing at a model that
|
|
49
|
+
doesn't exist), or an app used before `AppConfig.ready()`/app registry is
|
|
50
|
+
populated.
|
|
51
|
+
- **Migration conflict** — two migrations in the same app both claim the
|
|
52
|
+
same `dependencies` leaf (usually from two branches adding migrations in
|
|
53
|
+
parallel), or `makemigrations --check` reports an unmigrated model change.
|
|
54
|
+
- **App-loading / circular import** — a model or app imports another app's
|
|
55
|
+
model at module import time before Django's app registry is ready, or two
|
|
56
|
+
apps import each other's models directly instead of via a string reference
|
|
57
|
+
(`"otherapp.Model"`) in a `ForeignKey`.
|
|
58
|
+
- **Type errors** — `mypy`/`django-stubs` reports a real mismatch, often
|
|
59
|
+
around manager/queryset generics or a model field's inferred type.
|
|
60
|
+
- **Lint failures** — `ruff check` reports a rule violation.
|
|
61
|
+
- **Test suite won't start** — an import error in a test file, `conftest.py`,
|
|
62
|
+
or a fixture referencing a model/setting that doesn't exist.
|
|
63
|
+
|
|
64
|
+
### Step 2: Find the root cause
|
|
65
|
+
|
|
66
|
+
- **`ImproperlyConfigured`**: read the exact message — it names the missing/
|
|
67
|
+
bad setting. Check the correct settings module is active
|
|
68
|
+
(`DJANGO_SETTINGS_MODULE`) before assuming the setting itself is wrong.
|
|
69
|
+
- **Migration conflict**: run `python manage.py makemigrations --merge` to
|
|
70
|
+
generate a merge migration when two branches added divergent migrations
|
|
71
|
+
from the same parent — review the generated merge, don't hand-edit
|
|
72
|
+
`dependencies` to force a resolution. For an unmigrated model change,
|
|
73
|
+
run `makemigrations` for the actual diff instead of skipping the check.
|
|
74
|
+
- **App-loading/circular import**: use a string reference
|
|
75
|
+
(`models.ForeignKey("otherapp.Model", ...)`) for a cross-app relation
|
|
76
|
+
instead of importing the model class directly; move an import that only
|
|
77
|
+
needs to run inside a function body (e.g. inside a signal handler) out of
|
|
78
|
+
module level if it's the actual cycle.
|
|
79
|
+
- **Type errors**: read the exact mismatch; check `django-stubs` is
|
|
80
|
+
installed and configured (`mypy_django_plugin` in `mypy.ini`/
|
|
81
|
+
`pyproject.toml`) before assuming the type itself is wrong — a missing
|
|
82
|
+
plugin config produces spurious errors on manager/queryset types.
|
|
83
|
+
- **Lint failures**: apply `ruff check --fix .` for mechanical fixes; fix
|
|
84
|
+
the code for a substantive rule, don't suppress it.
|
|
85
|
+
- **Test suite won't start**: apply the same import-error diagnosis above to
|
|
86
|
+
the failing test/`conftest.py`'s own imports.
|
|
87
|
+
|
|
88
|
+
### Step 3: Apply the smallest fix
|
|
89
|
+
|
|
90
|
+
- Fix the actual cause identified in Step 2 — the missing setting, the
|
|
91
|
+
merge migration, the string-reference relation, the real type mismatch.
|
|
92
|
+
- Touch only what the failure requires; do not refactor unrelated code
|
|
93
|
+
while fixing a build failure.
|
|
94
|
+
- When a migration conflict needs a merge, generate it with
|
|
95
|
+
`makemigrations --merge` and review the result; never hand-edit
|
|
96
|
+
`dependencies`/`operations` to force a resolution.
|
|
97
|
+
|
|
98
|
+
### Step 4: Verify
|
|
99
|
+
|
|
100
|
+
Re-run every command from Step 1 in order; all must exit 0. Also run
|
|
101
|
+
`python manage.py test`/`pytest` even when the original failure was only a
|
|
102
|
+
lint/type error — a fix can introduce a runtime regression static tooling
|
|
103
|
+
won't see.
|
|
104
|
+
|
|
105
|
+
### Step 5: Report
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
Fixed: ImproperlyConfigured: AUTH_USER_MODEL refers to model 'accounts.Member'
|
|
109
|
+
that has not been installed
|
|
110
|
+
Root cause: settings.py set AUTH_USER_MODEL = "accounts.Member" but
|
|
111
|
+
"accounts" was missing from INSTALLED_APPS.
|
|
112
|
+
Fix: added "accounts" to INSTALLED_APPS
|
|
113
|
+
Verified: manage.py check, makemigrations --check, ruff, mypy, test all green
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Rules
|
|
117
|
+
|
|
118
|
+
- NEVER hand-edit a migration's `dependencies`/`operations` to force past a
|
|
119
|
+
conflict — generate a merge migration with `makemigrations --merge` and
|
|
120
|
+
review it.
|
|
121
|
+
- NEVER add `# type: ignore`/`# noqa` as a blanket suppression to make a
|
|
122
|
+
real error disappear without fixing or explicitly justifying it inline.
|
|
123
|
+
- NEVER wrap a real settings/import error in a broad `try/except` to hide
|
|
124
|
+
it — fix the actual misconfiguration or import cycle.
|
|
125
|
+
- Fix the root cause with the smallest change; do not refactor beyond what
|
|
126
|
+
the failure requires.
|
|
127
|
+
|
|
128
|
+
## Red Flags
|
|
129
|
+
|
|
130
|
+
| Rationalization | Why it is wrong |
|
|
131
|
+
|---|---|
|
|
132
|
+
| "I'll hand-edit the migration's `dependencies` to skip the conflict" | Desyncs the migration graph from what actually ran in other environments; generate a proper merge migration instead |
|
|
133
|
+
| "I'll import the model directly instead of using a string reference to dodge the circular import" | Papers over the app-loading order problem instead of fixing it; use `\"otherapp.Model\"` string references for cross-app relations |
|
|
134
|
+
| "mypy is noisy on Django models, I'll just add `# type: ignore` everywhere" | Usually means `django-stubs`'s mypy plugin isn't configured; fix the plugin config instead of suppressing every resulting error |
|
|
135
|
+
| "`makemigrations --check` fails, I'll just skip the check in CI" | Hides real model/migration drift instead of fixing it; generate the missing migration |
|
|
136
|
+
|
|
137
|
+
## Verification
|
|
138
|
+
|
|
139
|
+
Do not report the fix done until all of the following hold:
|
|
140
|
+
|
|
141
|
+
- The originally failing command now exits 0.
|
|
142
|
+
- `python manage.py check`, `makemigrations --check --dry-run`, `ruff
|
|
143
|
+
check .`, `ruff format --check .`, `mypy .`, and the test suite (the
|
|
144
|
+
project's own configured equivalents) all exit 0.
|
|
145
|
+
- No new `# type: ignore`/`# noqa` was added without an inline reason.
|
|
146
|
+
- No migration file's `dependencies`/`operations` was hand-edited; a
|
|
147
|
+
conflict was resolved with a generated merge migration.
|
|
148
|
+
- `git status` shows only the files whose actual cause was diagnosed in
|
|
149
|
+
Step 2 — no unrelated refactor.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"triggers": {
|
|
3
|
+
"positive": [
|
|
4
|
+
"manage.py check is failing with ImproperlyConfigured, can you fix it",
|
|
5
|
+
"After merging release/2.1 into main, `makemigrations` reports two leaf nodes in the payments app's migration history for our Django project -- what's the right way to reconcile that before I merge?",
|
|
6
|
+
"Django app won't load, there's a circular import between two apps",
|
|
7
|
+
"Type-checking chokes on `Invoice.objects` with an incompatible-type error even though django-stubs is installed -- any idea why?",
|
|
8
|
+
"makemigrations wants to create a migration that conflicts with an existing one",
|
|
9
|
+
"Pytest blows up before collecting a single test in our Django app -- traceback points to a bad import inside conftest.py. What's going on?",
|
|
10
|
+
"ruff check is failing on this Django project"
|
|
11
|
+
],
|
|
12
|
+
"negative": [
|
|
13
|
+
"Add a new feature to this Django view, the build is currently green",
|
|
14
|
+
"Write more pytest-django tests for this already-passing view",
|
|
15
|
+
"Review this Django migration for correctness, don't fix anything",
|
|
16
|
+
"Fix this ModuleNotFoundError in our plain Python script with no Django",
|
|
17
|
+
"Fix this FastAPI app that fails to start with a dependency resolution error",
|
|
18
|
+
"Fix this failing Jest test in our React frontend",
|
|
19
|
+
"npm run build is failing in our Node.js service"
|
|
20
|
+
]
|
|
21
|
+
},
|
|
22
|
+
"scenarios": [
|
|
23
|
+
{
|
|
24
|
+
"id": "migration-conflict-merge-not-hand-edit",
|
|
25
|
+
"prompt": "Running `python manage.py makemigrations` fails with: 'Conflicting migrations detected; multiple leaf nodes in the migration graph: (0004_add_phone, 0004_add_address in accounts).' Fix it.",
|
|
26
|
+
"strictness": "high",
|
|
27
|
+
"expected_behavior": [
|
|
28
|
+
{
|
|
29
|
+
"grader": "judge",
|
|
30
|
+
"rubric": "A correct answer resolves a migration conflict with two leaf migrations by generating a merge migration (`python manage.py makemigrations --merge`) and reviewing the result, rather than hand-editing either migration's `dependencies` to force one order.",
|
|
31
|
+
"pass_criteria": [
|
|
32
|
+
"runs (or explicitly instructs running) `python manage.py makemigrations --merge` to generate a merge migration resolving the two leaf nodes",
|
|
33
|
+
"states that the generated merge migration should be reviewed before applying/committing it"
|
|
34
|
+
],
|
|
35
|
+
"fail_criteria": [
|
|
36
|
+
"resolves the conflict by hand-editing one migration's `dependencies` list to point at the other, instead of generating a merge migration. Mentioning that hand-editing dependencies is unsafe only as a warning, while still recommending --merge, is not this failure."
|
|
37
|
+
]
|
|
38
|
+
}
|
|
39
|
+
],
|
|
40
|
+
"calibration": {
|
|
41
|
+
"known_right": "Run:\n```bash\npython manage.py makemigrations --merge\n```\nThis detects the two leaf migrations (`0004_add_phone` and `0004_add_address`, both children of the same parent) and generates a new migration, e.g. `0005_merge_20260925_1200.py`, whose `dependencies` list both leaves so the graph has a single path again. Review the generated file before committing -- confirm it doesn't silently drop any operation -- since `--merge` only needs a real conflict to resolve when both branches' changes are independent (as here, phone vs. address fields); if they touched the same field, the review step is where that would surface.",
|
|
42
|
+
"known_wrong": "Open `accounts/migrations/0004_add_address.py` and change its `dependencies` from `[(\"accounts\", \"0003_initial\")]` to `[(\"accounts\", \"0004_add_phone\")]` so it comes after `0004_add_phone` instead of being a sibling leaf. That resolves the 'multiple leaf nodes' error directly by hand-editing the dependency graph.",
|
|
43
|
+
"vague": "You need to merge the two migration branches together so there's only one path through the migration graph again.",
|
|
44
|
+
"subtle_wrong": "The cleanest fix here is to delete `0004_add_address.py` and regenerate it after `0004_add_phone` has been applied, so its `dependencies` naturally point at `0004_add_phone` instead of the shared parent -- that avoids needing a separate merge migration file at all and keeps the migration history linear."
|
|
45
|
+
},
|
|
46
|
+
"anti_patterns": ["hand-edit"]
|
|
47
|
+
}
|
|
48
|
+
]
|
|
49
|
+
}
|