django/django
Linters clean on a bare checkout, one word runs every check, 780 tests with no setup at all; the only file at an agent-instruction path is a Copilot review muzzle, and there is no lockfile anywhere. Below is the run the agent did and the AINE-REPORT.md it wrote.
- 29 passed
- 16 failed
- 1 n/a
- Python · setuptools + tox · flake8 + black + isort · runtests.py · biome + grunt · GitHub Actions
Run 14 August 2026 · main at c6be0bf
The agent run
I cloned django/django into a temp folder, audited it against all 46 checks and wrote the full report to AINE-REPORT.md in the repo.
AI-Native Readiness — django/django
29 passed · 16 failed · 1 not applicable
High priority — fix these first
- Add three lines to
.gitignore—tests/local_settings*.py,*.env,.env*— and nametests/local_settings.pyas the conventional filename indocs/internals/contributing/writing-code/unit-tests.txt. Why: the docs tell contributors to write a settings module holding a real database password, and nothing in the repo stopsgit add -Afrom staging it. - Add a
gitleakshook at a pinned rev to.pre-commit-config.yaml, and mirror it as a job in.github/workflows/linters.yml. Why: nothing scans for secrets anywhere today —zizmoraudits workflow security, not committed keys — so the mistake the missing ignore rules allow would go unnoticed. - Commit a
package-lock.jsonand switch thenpm installsteps inlinters.ymlandtests.ymltonpm ci; add apip-compile-generatedtests/requirements/py3.lockfor CI and agents, keeping thepy3.txtfloors for contributors. Why: with no lockfile, two agent worktrees a month apart resolve different versions of 20 test dependencies, so a failure cannot be told apart from one the change caused. - Add an
AGENTS.mdat the repo root as a routing table into the docs that already exist: test commands intests/README.rst, the tox and test instructions inunit-tests.txt, style rules incoding-style.txt, commit-message and Trac rules incommitting-code.txt. Why: the only file at an agent-instruction path is.github/copilot-instructions.md, a Copilot review muzzle that names no command, path or rule — this one fix unblocks five high-priority failures and two low ones. - Add a "requires a human" list to that same
AGENTS.md— cutting or tagging a release, pushing to Transifex, backporting tostable/A.B.x, touchingdjango/conf/locale/— and add an "addressed to release managers" line at the top ofdocs/internals/howto-release-django.txt. Depends on the fix above. Why: the release runbook hands out the full publish procedure with no fence, so an agent asked to cut a release finds a complete, followable recipe and no signal to stop.
Low priority — worth doing
- Add
.github/dependabot.ymlcovering three ecosystems:pipscoped totests/requirements/,npmat the root,github-actions. - Mirror the DEP template from
github.com/django/depsinto this repo, so proposal sections are readable without leaving the checkout. - Add
.claude/commands/with two commands: one that writes a commit message in the required shape by wrappingscripts/prepare_commit_msg.py, one that runs the local gauntlet. - In that same directory, add a local opt-in review command that produces no PR comments — this fits the existing no-bot-reviews policy rather than fighting it.
- Add an advisory
tox -e mypyenvironment over a narrow subtree, outside the required CI gate.
Two more low-priority failures — generic instructions and instruction staleness — clear themselves once the AGENTS.md fix lands.
One note on the shape of the result: Django's verification and safety machinery is close to exemplary. The linters pass clean on an untouched checkout, tox runs every check in one word, 780 tests ran in a bare clone with no setup beyond the written steps, every route to a release meets a human with a signing key, and all 28 CI checkouts set persist-credentials: false. Almost the entire deficit is one missing file: the project has excellent instructions for humans and none an agent is pointed at.
The report it wrote
AI-Native Readiness Report
Repo: django/django, a fresh clone of main at commit c6be0bf (2026-08-13). Audited from a bare checkout with no local state.
Date: 2026-08-14
Stack: Python 3.12+ web framework, published as a library. Build backend setuptools>=83 via pyproject.toml. Task runner is tox (tox.ini, 9 environments). Tests run through a bespoke runner, tests/runtests.py, 220 test packages. Lint and format: flake8 (.flake8), black + isort (pyproject.toml), blacken-docs, sphinx-lint, zizmor (zizmor.yml), all wired through .pre-commit-config.yaml. Bundled JavaScript for contrib.admin uses biome (biome.json) and grunt + qunit (package.json, Gruntfile.js). CI is GitHub Actions, 17 workflows. Issue tracking is Trac (external), not GitHub Issues. Docs are Sphinx (docs/, 740 files).
Result: 29 passed, 16 failed, 1 not applicable
How to read this. Every item is a numbered question about this repo, answered from evidence in this repo. Proof is what was actually found — a path, a file, a command and its output. An item with no proof is not an answer. The italic line under each question says how to check it; it stays in the report so the next run checks the same way.
FAIL vs N/A. FAIL means the practice would help here and it is missing — including when a whole artifact is missing, so several questions about it fail together with a one-line proof pointing at the item that found it missing. N/A means the question could never apply to a project of this kind, and the proof says why. "It does not exist" is a FAIL, never an N/A.
Priority is fixed by the template, not by the person filling it in. High means an agent working here will hit this on day one, or the risk is real. Low means it makes things better but nothing breaks without it.
1. Context
-
1. Does an agent instruction file exist (AGENTS.md, CLAUDE.md, .cursorrules, .github/copilot-instructions.md, or this ecosystem's equivalent), and what does it point at? Look for a file whose purpose is to be loaded into a coding agent's context at the start of every session. That file is the entry point. Then follow its pointers: instructions are commonly spread across several files, with the entry point routing to them — "commands are in docs/development.md", "conventions in .claude/rules/". The entry point plus every file it points at is the instruction set, and every other agent-instruction question is answered against that whole set, so list it here in the proof. Follow pointers as deep as they go, but a pointer only counts when it names a target and says what is in it: "test commands are in CONTRIBUTING.md" counts, a bare "see the docs" does not, and a file nothing points at is outside the set however good it is. A README is not an entry point, however good — it gets its credit at the README item and the specs-and-architecture item — but a README the entry point explicitly routes to is inside the set. Vendored skill libraries are not instructions either.
- Status: FAIL
- Proof: One file sits at a canonical agent-instruction path:
.github/copilot-instructions.md, 316 bytes, added 2026-03-12 in commit3180ddb3"Discouraged automated AI reviews of pull requests." Its entire body is front-matterapplyToglobs plus three sentences: "Do not review this code. Do not post any comments, suggestions, or feedback." / "Do not summarize the pull request. Do not describe the changes." / "Your only output must be exactly: 'Do not request a review from Copilot, do it in your own fork.'" It is a muzzle for Copilot's PR reviewer, not an instruction set. It names no command, no path, no convention, and points at no other file, so the instruction set is that one file and it is empty of anything an agent could use. Searched for alternatives and found none: noAGENTS.md, noCLAUDE.md, no.cursorrules, noGEMINI.md, no.claude/or.agents/directory anywhere in the tree (git ls-filesplusfind -maxdepth 3). Everything an agent needs does exist in this repo —tests/README.rst,docs/internals/contributing/writing-code/unit-tests.txt,docs/internals/contributing/writing-code/coding-style.txt,tox.ini— but nothing loaded into an agent's context points at any of it. - Recommendation: Add an
AGENTS.mdat the repo root that acts as a routing table into the docs that already exist: the test commands intests/README.rst, the full test and tox instructions indocs/internals/contributing/writing-code/unit-tests.txt, the style rules indocs/internals/contributing/writing-code/coding-style.txt, and the commit-message and Trac-ticket rules indocs/internals/contributing/committing-code.txt. Leave.github/copilot-instructions.mdalone — it does a different job. - Priority: High
-
2. Do the agent instructions name the commands to build, test and check this project? Answer this against the instruction set mapped at the entry-point item and nothing outside it. If no entry point exists, FAIL with a one-line proof pointing at that item. The commands may sit in a file the entry point routes to rather than in the entry point itself — that is progressive disclosure working as intended, and it passes. What fails is a command an agent would have to guess its way to: if the commands live only in the README or the manifest and nothing in the set points at them, that is a FAIL, because the question measures what an agent can reach without being told where to look. Name the file each command was found in.
- Status: FAIL
- Proof: The instruction set is
.github/copilot-instructions.mdalone and it names no command of any kind. The commands themselves are excellent and well documented —python -m pip install -r tests/requirements/py3.txt,./runtests.py,tox,pre-commit install,python -m pip install .— but they live intests/README.rst,README.rst,INSTALL,tox.inianddocs/internals/contributing/writing-code/unit-tests.txt, and nothing in the instruction set points at any of those. An agent would have to find them by guessing where to look. - Recommendation: Blocked by the missing agent instruction file.
- Priority: High
-
3. Does everything the agent instructions name still exist — the commands, the paths, the libraries, and the files they point at? If no entry point exists, FAIL with a one-line proof pointing at the entry-point item. Verify, do not trust: check every named command against the manifest or build file, spot-check the paths, run the cheap read-only ones. Then resolve every pointer in every file of the set — a link to a moved or deleted file is the most common rot in a multi-file instruction set, and it fails silently: the agent reads the entry point, follows nothing, and carries on without the rules.
- Status: FAIL
- Proof: There is nothing to verify.
.github/copilot-instructions.mdnames no command, no path, no library and no file, so it contains no claim that could be true or false, and it has no pointers to resolve. See the entry-point item. - Recommendation: Blocked by the missing agent instruction file.
- Priority: High
-
4. Are the agent instructions specific to this repo, rather than advice that would read the same in any codebase? If no entry point exists, FAIL with a one-line proof pointing at the entry-point item. The test: could this be pasted into another project unchanged? "Write clean code" and "add tests for new features" would fit anywhere and count for nothing. Judge the whole set, but weigh the files differently: an entry point that is mostly a routing table is fine, even good, when what it routes to is specific — while generic filler in the entry point costs more than generic filler three hops down, because it is loaded into every session whether it is needed or not. Say which files carried the specifics.
- Status: FAIL
- Proof:
.github/copilot-instructions.mdis repo-specific in one narrow sense — it mentions Copilot and forks — but it carries no information about Django. Pasted into any other project it would work exactly as well, because it says nothing about the project it sits in. See the entry-point item. - Recommendation: Blocked by the missing agent instruction file.
- Priority: High
-
5. Do the agent instructions explain where things live and how this project is laid out? If no entry point exists, FAIL with a one-line proof pointing at the entry-point item. The map may live in a routed-to file. Judge coverage against the real tree, not against what the files mention: if the set maps one package well but is silent about sibling packages or directories an agent would land in, that is a FAIL with the omission named. In a multi-file set, check the routing too — a layout document nothing points at is a document the agent never opens.
- Status: FAIL
- Proof: No layout information anywhere in the instruction set. The real tree an agent lands in has 3,688 files under
django/, 2,578 undertests/across 220 test packages, 740 underdocs/, plusscripts/,js_tests/andextras/, and the mapping between them is not obvious — a change todjango/db/models/fields/related.pyneeds a test intests/model_fields/ortests/many_to_many/and a release note indocs/releases/.README.rstdescribes thedocs/tree well, but nothing in the instruction set routes there. See the entry-point item. - Recommendation: Blocked by the missing agent instruction file.
- Priority: High
-
6. Do the agent instructions state the rules that are not obvious from the code — the things never to do here? If no entry point exists, FAIL with a one-line proof pointing at the entry-point item. These are the tribal-knowledge traps: the flag that must be exactly this string, the import that breaks the build, the directory that is generated and must not be edited. Rules in a routed-to file count. Two extra checks in a multi-file set: that the entry point signposts the rules clearly enough for an agent to open them before it needs them, since a trap found afterwards has already been sprung; and that the files do not contradict each other, because nothing tells the agent which one wins.
- Status: FAIL
- Proof: No rules in the instruction set. This one stings, because Django has an unusually rich set of exactly these traps and they are all written down somewhere an agent is never told to look: a non-trivial PR without a Trac ticket will be closed (
CONTRIBUTING.rst), commit messages must be past tense, mention the ticket number and end with a period (docs/internals/contributing/committing-code.txt),tests/test_runner_apps/tagged/tests_syntax_error.pyis force-excluded from black and*.py-tplfiles must not be formatted (pyproject.toml,.pre-commit-config.yaml), the locale.pofiles underdjango/conf/locale/are pulled from Transifex and not hand-edited (.tx/config,scripts/manage_translations.py), code is capped at 88 characters but docs and docstrings at 79 (.flake8,.editorconfig), anddjango/contrib/admin/static/admin/js/vendor/plus**.min.jsare vendored and must not be reformatted (.editorconfig). None of this reaches an agent. See the entry-point item. - Recommendation: Blocked by the missing agent instruction file.
- Priority: Low
-
7. Has the agent instruction file been updated recently enough to still be true, given how active the repo is? If no agent instruction file exists, FAIL with a one-line proof pointing at the entry-point item. Compare the last commit touching the file against the repo's tempo, then spot-check two or three of its claims against the code — a recently touched file can still lie.
- Status: FAIL
- Proof:
git log -- .github/copilot-instructions.mdreturns a single commit,3180ddb3on 2026-03-12, and the file has not been touched in the five months since. The repo's tempo is 89 commits in the last 30 days. But freshness cannot rescue this file: it makes no claim about the codebase, so there is nothing in it that could still be true. See the entry-point item. - Recommendation: Blocked by the missing agent instruction file.
- Priority: Low
-
8. Is the agent instruction file small enough to load into every session without crowding out the actual task? If no agent instruction file exists, FAIL with a one-line proof pointing at the entry-point item. Measure it:
wc -c, and bytes divided by four is a fair token estimate. Under ~5k tokens is comfortable; past ~10k it is eating the context window.- Status: PASS
- Proof:
wc -c .github/copilot-instructions.mdgives 316 bytes, roughly 79 tokens. Nothing to trim, and no risk of crowding out a task. This item measures bytes only; the content problem belongs to the entry-point item and is not double-counted here. Worth noting for when the file recommended at the entry-point item gets written: there is a lot of budget to spend. - Recommendation: —
- Priority: Low
-
9. Can a fresh session pick up a half-finished task — is there a file or convention where progress, decisions and what is left get written down? This is not about the agent instruction file. Any durable place in-flight state gets written counts: a TODO.md, a plans or notes directory, task files, linked issues, a specs folder whose entries carry progress and open questions, an agent memory file or directory, a scratchpad or working-notes convention. The test is whether a fresh session could read it and know what was decided and what is left — not what the place is called. Git history alone does not count: it records what happened, not what was decided or what remains.
- Status: PASS
- Proof: Trac is the durable place, and the convention is committed and enforced.
CONTRIBUTING.rststates it in bold: "non-trivial pull requests (anything more than fixing a typo) without Trac tickets will be closed!".github/pull_request_template.mdrequires aticket-XXXXXline and a checklist item "I have checked the 'Has patch' ticket flag in the Trac system" — those Trac flags (Has patch, Needs tests, Needs documentation, Patch needs improvement) are precisely a record of what is left. Commit messages carry the back-reference: 10 of the last 15 commits startFixed #NNNNN --orRefs #NNNNN --, for examplec6be0bf Fixed #37254 -- Added fields.E323 for models referenced by ManyToManyFields.The triage workflow is documented indocs/internals/contributing/triaging-tickets.txtanddocs/internals/contributing/bugs-and-features.txt. A fresh session handed a ticket number can read what was decided and what remains. One caveat, named rather than held against it: the record lives outside the repo atcode.djangoproject.com, so an agent needs network access to reach it. - Recommendation: —
- Priority: Low
-
10. Is there a README that says what this project is and how to run it? This is where a good README earns its credit. It needs three things: what the project is, how to run it, and how to check a change. Judge what is on the page, not the file's existence.
- Status: PASS
- Proof:
README.rstcarries all three things. What it is: "Django is a high-level Python web framework that encourages rapid development and clean, pragmatic design." How to run it: it routes todocs/intro/install.txtand the tutorials in order, andINSTALLgives the one-linerpython -m pip install .with the Python 3.12 floor. How to check a change: a dedicated "To run Django's test suite" section pointing atdocs/internals/contributing/writing-code/unit-tests.txt. It also routes toCONTRIBUTING.rst, the Discord and the forum.tests/README.rstthen gives the four commands verbatim. Judged on what is on the page, this does its job. - Recommendation: —
- Priority: High
2. Specs
-
11. Is the thinking behind this system written down somewhere durable — a specs, RFC, proposals, design-doc or ADR directory, or architecture notes that record not just what but why? Look for the place and for the convention:
specs/,docs/adr/,rfcs/,proposals/,.specify/, a docs or design folder, architecture notes anywhere in the tree, or this ecosystem's equivalent. Judge substance, not location: "we use X because Y" is a decision, a list of technologies is not, an essay about specs is not a spec, and a docs folder of usage guides with no reasoning is a FAIL whose proof says what was in there instead. Other items are answered against whatever this item finds, so name it precisely — and where forward-looking specs and after-the-fact architecture records live in different places, name both, since a decision log cannot answer a question about acceptance criteria.- Status: PASS
- Proof: Two distinct places, both real, and they answer different questions — so both are named here, as the steer asks. After-the-fact architecture records, in this repo:
docs/misc/design-philosophies.txtopens "This document explains some of the fundamental philosophies Django's developers have used in creating the framework. Its goal is to explain the past and guide the future" and then gives reasoning, not a technology list — loose coupling ("the various layers of the framework shouldn't 'know' about each other unless absolutely necessary"), less code, explicit is better than implicit, each with the rationale attached.docs/misc/api-stability.txtrecords the stability contract and its "Exceptions" (line 50).docs/internals/deprecation.txtis a live decision log of every behaviour change with its target version, anddocs/internals/release-process.txtrecords why the release train is shaped the way it is.docs/releases/*.txtcarry the per-change reasoning. Forward-looking specs, not in this repo: the Django Enhancement Proposal process, documented atdocs/internals/contributing/writing-code/submitting-patches.txt:259-284("Similar to Python's PEPs, Django has Django Enhancement Proposals or DEPs"), with the DEP corpus itself atgithub.com/django/deps— for example DEP 181 on ORM expressions and DEP 182 on multiple template engines, both linked from that file. The reasoning is written down and durable. The one cost for an agent: the forward-looking half is in another repository, so an agent working here cannot read a DEP without leaving the checkout. - Recommendation: —
- Priority: High
-
12. Does the specs directory hold recent entries, or is it an archive nobody has touched? If no specs directory exists, FAIL with a one-line proof pointing at the specs-and-architecture item. Otherwise compare the newest entry's date against the repo's recent activity.
- Status: PASS
- Proof:
git log -1per file against a repo doing 89 commits in 30 days:docs/releases/6.1.txtlast touched 2026-08-11, three days before this audit ("Refs #28586 -- Updated Model.from_db() signature in docs");docs/internals/deprecation.txton 2026-07-13 ("Fixed #37178 -- Moved MiddlewareMixin out of utils.deprecation"), and it already carries entries for 7.1 and 7.0 while the current release is 6.1, so it is written ahead of the code rather than behind it;docs/misc/design-philosophies.txton 2026-05-11 ("Refs #36593 -- Replaced reference to select_related() in design philosophies").docs/misc/api-stability.txtis the oldest at 2025-09-17, which is appropriate for a stability contract that is meant not to move. This is a maintained set, not an archive. - Recommendation: —
- Priority: Low
-
13. Is there a spec template, or an SDD framework, so every spec comes out the same shape? Scaffolding can exist even where no specs directory does — look for a TEMPLATE.md, a
.specify/directory, or framework config. If neither a directory nor any scaffolding exists, FAIL.- Status: FAIL
- Proof: No spec scaffolding in this repo.
git ls-filesfinds noTEMPLATE.md, no.specify/directory and no framework config of any kind. The DEP process does have a shape, but the template lives in the externalgithub.com/django/depsrepo, not here, so nothing in this checkout tells a contributor or an agent what a proposal should contain. Two strong templates do exist for adjacent jobs —.github/pull_request_template.mdand the per-version structure ofdocs/releases/*.txt— but neither is a spec template. - Recommendation: Add a short
docs/internals/contributing/writing-code/dep-template.txt, or a stub that mirrors the DEP template fromgithub.com/django/depsinto this repo, so the required sections are readable without leaving the checkout. - Priority: Low
-
14. Do the specs state acceptance criteria a machine could check? If no specs exist, FAIL with a one-line proof pointing at the specs-and-architecture item. Otherwise open the two newest specs and quote a criterion: "the endpoint returns 403 for expired tokens" is checkable; "the feature works well" is not.
- Status: PASS
- Proof: Opened the two newest records named at the specs-and-architecture item. From
docs/internals/deprecation.txt:23-24: "CallingQuerySet.aiterator()afterprefetch_related()without providing achunk_sizewill raise :exc:ValueError." That is a criterion a test can assert directly, and the version it lands in is the section heading. Same file, line 45: "TheURLIZE_ASSUME_HTTPStransitional setting will be removed." Line 43: "The default protocol inurlizeandurlizetrunctemplate filters will change from HTTP to HTTPS." Each names a symbol, an input condition and an observable outcome.docs/releases/6.1.txtdoes the same for shipped work under "Backwards incompatible changes in 6.1" (line 398). Nothing here reads like "the feature works well". - Recommendation: —
- Priority: High
-
15. Open the newest spec: do its criteria go past the happy path — what happens when a step fails, and how the change gets undone? If no specs exist, FAIL with a one-line proof pointing at the specs-and-architecture item. Look for error cases, edge inputs, and a rollback or undo story, not just the success flow.
- Status: PASS
- Proof: The newest record is
docs/releases/6.1.txt, last touched 2026-08-11. Its section headings go well past the happy path: "Backwards incompatible changes in 6.1" (line 398), "Features deprecated in 6.1" (line 597), "Features removed in 6.1" (line 726) — that is the failure and breakage story stated per change. The undo story is structural rather than per-spec, and it is real: behaviour changes ship behind transitional settings that default to the old behaviour, so a user can turn the change off at runtime with no rebuild.docs/internals/deprecation.txtnames seven of them across recent versions —URLIZE_ASSUME_HTTPS,USE_BLANK_CHOICE_DASH,SIGNED_COOKIE_LEGACY_SALT_FALLBACK,FORMS_URLFIELD_ASSUME_HTTPS,USE_DEPRECATED_PYTZ,CSRF_COOKIE_MASKED,DEFAULT_HASHING_ALGORITHM. See also the staged-rollout item. - Recommendation: —
- Priority: Low
-
16. Do the specs state non-goals, so an agent knows where to stop? If no specs exist, FAIL with a one-line proof pointing at the specs-and-architecture item. Non-goals written elsewhere (a README's "what this is not" list) are worth naming in the proof, but they do not turn this into a PASS — the question is whether specs carry them.
- Status: PASS
- Proof:
docs/misc/api-stability.txthas a dedicated "Exceptions" section at line 50 — "There are a few exceptions to this stability and backwards-compatibility promise" — which is exactly a where-to-stop boundary, and it is explicit about the ordering when goals conflict: "security trumps the compatibility guarantee."docs/misc/design-philosophies.txtstates anti-goals in the same voice as its goals, for instance that the layers of the stack must not know about each other unless absolutely necessary. These are non-goals carried by the architecture records themselves, not borrowed from a README. - Recommendation: —
- Priority: Low
-
17. Can recent shipped work be traced back to a spec? If no specs exist, FAIL with a one-line proof pointing at the specs-and-architecture item. Otherwise take the last few substantial commits or PRs and look for a reference to a spec, an issue, or a design doc in the message or description.
- Status: PASS
- Proof: This is the strongest single practice in the repo. Of the last 15 commits, 10 open with a ticket reference and the rest are release mechanics:
c6be0bf Fixed #37254 --,082b3df Fixed #37270 --,6df8fe3 Fixed #24580 --,616e8c5 Fixed #37264 --,2b4c88b Refs #28586 --,b5388a3 Fixed #37257, Refs #35738 --,c9eb16a Refs #36905 --,ae25a40 Fixed #37196 --,89e8286 Fixed #29969 --,8f52e91 Fixed #37224 --. The convention is enforced rather than hoped for:.github/pull_request_template.mdrequires aticket-XXXXXline,.github/workflows/check_commit_messages.ymlruns on every pull request, and.github/workflows/check_pr_quality.ymlrunsscripts/pr_quality/check_pr.pyagainst the PR description. Larger features additionally trace to a DEP, perdocs/internals/contributing/writing-code/submitting-patches.txt. - Recommendation: —
- Priority: Low
3. Verification
-
18. Does this project have an automated test suite, in whatever form this ecosystem uses? Work out this ecosystem's convention before concluding anything is missing — check the manifest, the build file, the CI config, the README. A shell script that diffs output files is a test suite. If you find one, run it and record the result.
- Status: PASS
- Proof: Django ships a bespoke runner,
tests/runtests.py, over 220 test packages and 2,578 tracked files undertests/. Ran it from a fresh clone in a clean venv:python -m pip install -e .thencd tests && python runtests.py basic model_fields lookupgaveRan 780 tests in 0.640s/OK (skipped=64). The 64 skips are the optional dependencies fromtests/requirements/py3.txtthat were not installed, which the docs say will skip automatically, and they did. The policy is stated indocs/internals/contributing/writing-code/unit-tests.txt: "It's our policy to make sure all tests pass at all times." - Recommendation: —
- Priority: High
-
19. Can the test command be discovered without guessing — is it written down where an agent reads? If no test suite exists, FAIL with a one-line proof pointing at the test-suite item. Otherwise check the places an agent looks: the agent instruction file, the README, the manifest's scripts or targets.
- Status: PASS
- Proof: Written down in four places an agent would plausibly open.
tests/README.rstgives the whole sequence in five lines:cd tests,python -m pip install -e ..,python -m pip install -r requirements/py3.txt,./runtests.py.README.rsthas a "To run Django's test suite" section routing to the long version.tox.inideclarescommands = {envpython} runtests.py {posargs}withchangedir = tests.docs/internals/contributing/writing-code/unit-tests.txthas a "Quickstart" block with the same commands plus the tox path. No guessing required. The one place it is missing is the agent instruction file — see the commands item. - Recommendation: —
- Priority: High
-
20. Do the tests assert real behaviour, rather than asserting that a mock was called? If no test suite exists, FAIL with a one-line proof pointing at the test-suite item. Otherwise open the largest test files and read the assertions: calling real code on real inputs passes;
expect(mock).toHaveBeenCalled()as the main dish fails.- Status: PASS
- Proof: Counted assertion styles across
tests/**/*.py: 18,846assertEqualagainst 97assert_called*. Mocks are a 0.5% garnish, not the dish. There are also 764assertNumQueriesassertions, which check the actual SQL a query set emits — behaviour that only a real database round-trip can prove. Spot-read the three largest test files (tests/admin_views/tests.py,tests/schema/tests.py,tests/queries/tests.py) and the pattern holds: real models, a real database (SQLite by default, Postgres/MySQL/Oracle in CI), real HTTP requests through the test client. - Recommendation: —
- Priority: High
-
21. Is there a linter or static analysis configured for this language, and does it pass on a clean checkout? Configured is not enough — run it. A linter that exits non-zero on an untouched checkout is a FAIL with the error count in the proof, because an agent cannot tell its own damage from the baseline noise.
- Status: PASS
- Proof: Configured and run, not just configured.
.flake8setsmax-line-length = 88,max-doc-length = 79,extend-ignore = E203and four per-file ignores. Ranflake8 .from the untouched clone: no output, exit code 0. A clean baseline, so an agent can tell its own damage from pre-existing noise. Also present:zizmorfor GitHub Actions security (zizmor.yml, with an explicitunpinned-usesref-pin policy),biomefor the bundled admin JavaScript (biome.json), andsphinx-lintfor the docs. - Recommendation: —
- Priority: High
-
22. Is there a formatter, so an agent's diffs do not churn on style? Look for the config file and the dependency in this ecosystem's form — .prettierrc, rustfmt, gofmt, black, an .editorconfig doing real work. If the language ships one formatting standard with the toolchain, that is a PASS and the proof says so.
- Status: PASS
- Proof: Four formatters, all pinned and all enforced.
blackwithtarget-version = ["py312"](pyproject.toml),isortwithprofile = "black"andknown_first_party = "django",blacken-docsfor code blocks indocs/*.txt, andbiomefor JavaScript. All four are hooks in.pre-commit-config.yamlat exact revisions (black26.5.1,isort8.0.1,blacken-docs1.20.0,biome2.4.15). Ranblack --check .on the untouched clone: "2927 files would be left unchanged", exit 0. Ranisort --check-only --diff django tests scripts: no output, exit 0..editorconfigdoes real work on top, includingindent_style = unsetfor**/admin/js/vendor/**and**.min.jsso vendored files are left alone. An agent's diffs will not churn on style here. - Recommendation: —
- Priority: Low
-
23. Is there a compile-time or type-level gate, if this language offers one? N/A only when the language genuinely has no such gate. If the language offers one and the repo does not use it — no strict mode, no typecheck script, no compiler step — that is a FAIL. Run the gate if it exists and record the result.
- Status: FAIL
- Proof: Python offers a type-level gate through
mypyorpyright, and this repo uses neither. Greppedpyproject.toml,tox.iniandsetup.cfgformypyandpyright: no matches.git ls-filesfinds nomypy.ini, nopyrightconfig.jsonand nopy.typedmarker, so the shipped package is not even declared as typed — type hints for Django live in the third-partydjango-stubsproject. This is a deliberate, long-standing project position rather than an oversight, and the checklist records it as a gap anyway: an agent editingdjango/db/models/gets no signal from a type checker when it passes the wrong shape into a function, and has to wait for the test suite instead. - Recommendation: If the project stance on inline annotations is not up for revision, the cheap partial win is a
tox -e mypyenvironment run in advisory mode over a narrow, already-annotated subtree so agents get a fast structural signal; leave it out of the required CI gate. - Priority: Low
-
24. Can an agent prove its own work before it pushes — one command, task-runner target or commit hook that runs every check this project has? One command, not a list to remember: a
checkorverifytarget, aprecommitscript, a Makefile target that chains them. Separate commands documented side by side are close but FAIL — the question is whether the agent can run the whole gauntlet without knowing its parts.- Status: PASS
- Proof: One word:
tox.tox.inideclaresenvlist = py3, black, blacken-docs, flake8, docs, isort, lint-docs, zizmor, so a baretoxruns the test suite plus every lint, format and docs check the project has, in one invocation, without the agent knowing the parts. It is documented as exactly that indocs/internals/contributing/writing-code/unit-tests.txt: "Django includes a basictox.inithat automates some checks that our build server performs on pull requests." A second one-command path exists for the pre-push moment:pre-commit installthenpre-commit run --all-files, covering the six hooks in.pre-commit-config.yaml, documented atdocs/internals/contributing/writing-code/coding-style.txt. - Recommendation: —
- Priority: High
-
25. Is there continuous integration, and does it run before a change lands rather than after? A deploy pipeline that runs on push to the default branch runs after the change has landed — that is a FAIL with the trigger quoted, not a PASS with a caveat. Look for a pre-merge trigger: pull_request events, merge trains, or this forge's equivalent.
- Status: PASS
- Proof: 17 workflows in
.github/workflows/, and the gating ones fire pre-merge onpull_request.tests.yml:on: pull_requestwithpaths-ignore: ['docs/**'].linters.yml: same trigger.docs.yml:on: pull_requestscoped topaths: ['docs/**', '.github/workflows/docs.yml'].check_commit_messages.yml:on: pull_request, types: [edited, opened, synchronize, reopened, ready_for_review].check-migrations.yml,coverage_tests.yml,postgis.yml,python_matrix.yml,selenium.ymlandbenchmark.ymlall trigger onpull_requesttoo, several gated behind a label so the expensive matrices run on demand. Thepush: branches: [main]triggers are additive, not the primary gate.schedules.ymladds a nightlycron: '42 2 * * *'full build on top. - Recommendation: —
- Priority: High
-
26. Does CI actually run the tests and the checks that exist in this repo? If no pipeline of any kind exists, FAIL with a one-line proof pointing at the CI item. Otherwise list what the pipeline runs and diff it against every check found in the Verification section — name each check that exists in the repo but is missing from the pipeline.
- Status: PASS
- Proof: Diffed the pipeline against every check found in this section.
flake8→linters.ymljobflake8.isort→linters.ymljobisort.black→linters.ymljobblackviapsf/black@stable.zizmor→linters.ymljobzizmor.biome→linters.ymljobbiome, runningnpx biome ci. Test suite →tests.yml, on Windows/SQLite, Ubuntu free-threaded 3.14t, plus JavaScript tests, andpython_matrix.yml,postgis.yml,selenium.yml,schedule_tests.ymlfor the wider matrix (3.12, 3.13, 3.14, Postgres, MySQL, Oracle, PostGIS).blacken-docsandsphinx-lint→docs.yml, which runsmake lint,make blackandSPHINXOPTS="-q -W" make spelling. Migration consistency →check-migrations.ymlrunningscripts/check_migrations.py. Coverage →coverage_tests.ymlpluscoverage_comment.yml. Every check that exists in the repo is in the pipeline; nothing found locally is missing from CI. The single gap is the type gate, which does not exist in the repo either — see the type-gate item. - Recommendation: —
- Priority: High
-
27. Does anything here get an LLM to read a change and go looking for problems — a review skill, a saved review prompt, or an AI reviewer on the pull requests? Look for a committed review skill or slash command, a CI step calling an AI reviewer, or bot config. Human review rules are a Safety item; this one is specifically about machine review.
- Status: FAIL
- Proof: Not absent by accident — absent by policy, enforced in two places.
.github/copilot-instructions.mdinstructs Copilot's reviewer to produce nothing but "Do not request a review from Copilot, do it in your own fork.".github/pull_request_template.mdmakes every contributor tick "I have not requested, and will not request, an automated AI review for this PR" and, separately, an "AI Assistance Disclosure (REQUIRED)" block choosing between "No AI tools were used" and a disclosure of which were. The commit that added the Copilot file is titled "Discouraged automated AI reviews of pull requests." Grepped.github/,.pre-commit-config.yamlandtox.inifor any AI reviewer step or bot config: none. There is no review skill, no saved review prompt, no.claude/commandsdirectory. So machine review of a change does not happen here, and the maintainers have decided it should not — recorded as a FAIL because the practice is missing, not because the decision is wrong. - Recommendation: If the objection is unreviewed bot noise on public PRs rather than machine review as such, the version that fits this policy is a local, opt-in review command committed under
.claude/commands/or.github/that a contributor runs on their own branch before pushing, producing no PR comments at all. - Priority: Low
4. Tooling
-
28. Can an agent reach the outside systems this project depends on, with that access committed to the repo rather than set up per laptop? MCP servers are one form: look for a committed
.mcp.jsonor this ecosystem's equivalent. A CLI is another and often the better one —aws,gh,psql,kubectl,stripe, a vendor's own tool — and it counts when the repo names which tools the work needs and how to authenticate, so an agent is not guessing at a tool it cannot see. Judge the axis, not the mechanism: access that exists only in someone's shell history or laptop config is a FAIL, and so is a committed config that turns out to be gitignored, with the distinction in the proof. Project task scripts have their own item; this one is about reaching past the repo's edge. If this project genuinely talks to nothing outside itself, N/A with that as the reason.- Status: PASS
- Proof: No
.mcp.json, but the CLI form is here and committed. Django reaches past its own edge to three outside systems, and each has committed configuration or a committed wrapper. Transifex for translations:.tx/configis tracked and maps every resource ([o:django:p:django:r:core]→django/conf/locale/<lang>/LC_MESSAGES/django.po, and the same for each contrib app), withscripts/manage_translations.pywrapping thetxCLI and documenting its subcommands in the file header —update_catalogs,lang_stats,fetch— including a worked example,python scripts/manage_translations.py lang_stats -l es -r admin. Trac for tickets: linked fromCONTRIBUTING.rst,README.rstand the PR template, public to read. PyPI and the release archive:scripts/do_django_release.pyandscripts/verify_release.sh, with the full procedure indocs/internals/howto-release-django.txt. None of this depends on someone's shell history. One gap worth naming:scripts/manage_translations.pydocuments the commands but not how to authenticate to Transifex — the~/.transifexrctoken setup is nowhere in the repo, so an agent asked to refresh translations would get as far as the first API call and stop. - Recommendation: —
- Priority: Low
-
29. Is there a skills, commands or reusable-prompt library in the repo? Look for .claude/skills, .agents/skills, .claude/commands, or a prompts directory. Committed and pinned beats committed; note whether anything ties the copies to a source.
- Status: FAIL
- Proof: Nothing of the kind.
git ls-filesandfind -maxdepth 3find no.claude/skills, no.claude/commands, no.agents/, and no prompts directory anywhere in the tree. The only agent-facing file in the repo is.github/copilot-instructions.md, and that is a review muzzle — see the entry-point item. - Recommendation: Add
.claude/commands/with two commands covering the two things every Django contribution needs and every agent gets wrong: one that writes a commit message in the required shape (past tense,Fixed #NNNNN --, trailing period) by wrapping the logic already inscripts/prepare_commit_msg.py, and one that runs the local gauntlet (tox, orpre-commit run --all-filesplusruntests.pyon the touched test packages). - Priority: Low
-
30. Do the skills, servers and tools that exist cover the work this team plainly repeats? First identify the repeated work from the README and the commit history. If skills, servers or documented tooling exist but miss it, FAIL naming the gap. If none exist at all and the repo plainly repeats work, FAIL pointing at the outside-systems item and the skills-library item. If the project is too small to repeat anything, N/A.
- Status: PASS
- Proof: Read the repeated work off the commit history and the release docs, then checked each against
scripts/. Fixing a ticket and writing the commit message in Django's exact format →scripts/prepare_commit_msg.py, documented atdocs/internals/contributing/committing-code.txt:255as an installableprepare-commit-msggit hook, and itself covered by tests inscripts/tests.py. Backporting a fix to astable/A.B.xbranch →scripts/backport.sh. Cutting a release →scripts/do_django_release.pyandscripts/verify_release.sh. Pulling translations →scripts/manage_translations.py. Checking that models and migrations have not drifted →scripts/check_migrations.py, wired into.github/workflows/check-migrations.yml. Retiring an end-of-life branch →scripts/archive_eol_stable_branches.py. Checking PR description quality →scripts/pr_quality/check_pr.py, with its own test suite atscripts/pr_quality/tests/test_check_pr.py. The repeated work is covered by committed, tested CLI scripts rather than by skills, which is a legitimate answer to the same question. - Recommendation: —
- Priority: Low
-
31. Are there project CLI scripts or task-runner targets for the common jobs? Check the manifest's scripts, the Makefile, the justfile, or this ecosystem's equivalent. The test: does routine work need a raw multi-flag command that someone has to remember?
- Status: PASS
- Proof: Four task-runner surfaces, no raw multi-flag commands to memorise.
tox.inigives nine named environments (py3,py314t,black,blacken-docs,flake8,docs,isort,javascript,lint-docs,zizmor), each wrapping its own flags.tests/runtests.pytakes bare package names —./runtests.py basic model_fields— instead of a settings-module incantation.docs/Makefilegives 20 targets includinghtml,htmlview,lint,black,spelling,linkcheck.package.jsondeclares"test": "grunt test --verbose"and"biome": "biome check". Plus the nine scripts listed at the repeated-work item. - Recommendation: —
- Priority: High
-
32. Are those scripts named somewhere the agent will actually read them? If no scripts exist, FAIL with a one-line proof pointing at the task-scripts item. Otherwise check the agent instruction file, the README, and whether the manifest itself is self-explanatory.
- Status: PASS
- Proof:
tests/README.rstnames./runtests.py.README.rstroutes to the test-suite docs.tox.iniis self-documenting: its header explains what tox is and says "python -m pip install tox" then run "tox" from this directory, andtox -llists the environments.docs/internals/contributing/writing-code/unit-tests.txtnames the tox environments explicitly andcoding-style.txtnamespre-commit install.docs/Makefilehas ahelp:target. The gap is the same one as everywhere in section 1: the agent instruction file names none of them — see the commands item. - Recommendation: —
- Priority: High
-
33. Can an agent get this project running — is there a reproducible environment or a documented setup path? Look for a pinned runtime (.nvmrc, .tool-versions, rust-toolchain), a lockfile, a container or nix file, and written setup steps. Try the first step if it is cheap and safe. This one bites hardest the moment work happens in a fresh git worktree or a new clone — the normal way to run agents in parallel. A new worktree has no installed dependencies, no
.env, no build cache, so anything that works today only because of untracked state sitting on someone's machine simply does not run there. The test: would a bare checkout plus the written steps get this project up? Name any prerequisite nothing creates — an env file someone hand-made, a seeded database, a manual login — because each one is a wall a worktree hits on its first command.- Status: PASS
- Proof: Tested this the way the steer asks — from a bare clone, nothing but the written steps.
python3 -m venv,pip install -e .,cd tests && python runtests.py basic model_fields lookup→Ran 780 tests in 0.640s,OK (skipped=64). No.envfile, no seeded database, no manual login, no build cache, no hand-made config. The default test settings ship in the repo and use SQLite, so the database creates and destroys itself; the 64 skips were optional dependencies degrading gracefully exactly asunit-tests.txtpromises. A fresh git worktree would come up the same way, which is what makes parallel agents workable here. Written setup exists in three places:INSTALL,tests/README.rst,docs/internals/contributing/writing-code/unit-tests.txt. What is missing is the reproducibility half rather than the runnability half: no lockfile, no.python-versionor.tool-versions, no container or nix file, andrequires-python = ">= 3.12"is a floor not a pin — so two worktrees created a month apart can resolve different dependency versions. That is recorded as a FAIL at the dependency-pinning item and not double-counted here. - Recommendation: —
- Priority: High
-
34. Can an agent see the results of a failed run — do the tools here produce output it can read and act on? Judge from the runs you already did in Verification: does a failure print a path, a line, a name — something actionable — or a wall of noise? If nothing could be run, FAIL saying why.
- Status: PASS
- Proof: Tested it rather than assumed it. Appended a deliberate style error to
tests/basic/tests.py, ranflake8on that file, and gottests/basic/tests.py:1126:6: E225 missing whitespace around operator— path, line, column, rule code, one line, exit 1. Then restored the file and confirmedgit diff --statwas empty. The test runner is standardunittestoutput: named test, file-and-line traceback, expected-versus-actual. CI improves on it further —linters.ymlpipesflake8andisortthroughliskin/gh-problem-matcher-wrapso failures land as inline annotations, andzizmor-actionruns withannotations: true. An agent can act on any of this without parsing a wall of noise. - Recommendation: —
- Priority: Low
5. Safety
-
35. Are credentials kept out of the repo — nothing secret committed, ignore rules in place, an example env file for the shape? Three checks: grep tracked files for key-shaped strings, read the ignore rules for env and key patterns, and look for an example env file. Report each of the three separately.
- Status: FAIL
- Proof: Three checks, reported separately. (1) Nothing secret committed — clean.
git grep -nIE "(AKIA[0-9A-Z]{16}|sk-[A-Za-z0-9]{20,}|ghp_[A-Za-z0-9]{30,}|-----BEGIN (RSA|OPENSSH|PRIVATE) )"overdjango,scripts,.githubandtests: zero matches. (2) Ignore rules — absent..gitignoreis 17 lines of build artefacts (*.egg-info,*.py[co],.tox/,__pycache__,dist/,node_modules/,tests/coverage_html/,tests/.coverage*,build/,tests/report/,tests/screenshots/) and carries no pattern for an env file, a key, a certificate or a local settings module. Grepped it case-insensitively forenv|secret|key|\.pem|credential: no matches. (3) Example env file — none, and none needed, because Django is configured by a Python settings module rather than a dotenv. But that is what turns (2) from theoretical into live:docs/internals/contributing/writing-code/unit-tests.txt:174tells contributors to write a test settings module whoseDATABASESsetting defines adefaultand anotherdatabase, and the CI templates for exactly that file are.github/workflows/data/test_postgres.py.tplandtest_postgis.py.tpl. Anyone running the Postgres, MySQL or Oracle suites locally has a file on disk holding a database password, in a repo where nothing ignores it andgit add -Awill stage it. - Recommendation: Add three lines to
.gitignore—tests/local_settings*.py,*.envand.env*— and nametests/local_settings.pyindocs/internals/contributing/writing-code/unit-tests.txtas the conventional filename for a local database settings module, so the ignore rule and the documented path match. - Priority: High
-
36. Does anything scan for secrets automatically? Look for gitleaks, trufflehog, detect-secrets or this ecosystem's equivalent, wherever it is wired in — a CI step, a pre-commit hook, or forge-level push protection visible from the repo. CI is where this normally lives, and that is a PASS; a local hook on top is better, because it catches the key before it is pushed rather than after, but its absence is a line in the proof, not a FAIL. Say where the scan runs. "Nothing secret exists today" does not make this N/A — the scan is for the day that changes.
- Status: FAIL
- Proof: Nothing scans. Grepped
.github/,.pre-commit-config.yaml,tox.iniandscripts/case-insensitively forgitleaks|trufflehog|detect-secrets|pip-audit|safety check|npm audit|osv-scanner: no matches anywhere.zizmoris present and well configured, but it audits GitHub Actions workflows for security anti-patterns — dangerous triggers, unpinned uses, credential persistence — not for committed secrets, so it does not answer this question. GitHub's own push protection may be enabled at the repository or organisation level, which is not visible from a clone; that possibility is recorded here rather than assumed. Given the ignore-rules gap at the credentials item, this is the check that would catch the mistake it enables. - Recommendation: Add a
gitleakshook to.pre-commit-config.yamlat a pinned rev, alongside the six hooks already there, so a database password in a local settings module is caught before it is pushed rather than after; mirror it as a job in.github/workflows/linters.ymlfor contributors who skip the hook. - Priority: High
-
37. Are dependencies pinned, so a build is reproducible? Look for lockfiles in every package of the repo, exact versions for load-bearing dependencies, a pinned runtime, and an install command that respects the lock (npm ci, not npm install).
- Status: FAIL
- Proof: Split verdict, and the failing half is real. Correctly unpinned:
[project].dependenciesinpyproject.tomluses floors (asgiref>=3.12.1,sqlparse>=0.5.0) — right for a library that must coexist with whatever its users already have, and not counted against it. Genuinely pinned:.pre-commit-config.yamlpins every hook to an exact rev (black26.5.1,blacken-docs1.20.0,isort8.0.1,flake87.3.0,biome2.4.15,zizmor1.25.2), and GitHub Actions are ref-pinned withzizmor.ymlenforcing aref-pinpolicy foractions/*andpsf/*, with two third-party actions pinned to full commit SHAs (liskin/gh-problem-matcher-wrap@d31bb388,zizmorcore/zizmor-action@e639db99). The gap: no lockfile anywhere in the repo —git ls-filesfinds nopackage-lock.json,yarn.lock,uv.lockorpoetry.lock. Every line oftests/requirements/py3.txtis a floor (selenium >= 4.23.0,Pillow >= 10.1.0,redis >= 5.1.0, and so on for 20 packages).package.jsonuses caret ranges (grunt: ^1.6.2,grunt-contrib-qunit: ^10.2.0,qunit: ^2.25.0) and CI runsnpm install, notnpm ci— see.github/workflows/linters.ymlandtests.yml. The runtime is a floor too:requires-python = ">= 3.12", with no.python-versionor.tool-versions. So a test run today and the same test run next month can resolve different versions of 20 test dependencies and three JavaScript ones, and a failure that appears in a fresh agent worktree cannot be told apart from a failure the change caused. - Recommendation: Commit a
package-lock.jsonand switch thenpm installsteps in.github/workflows/linters.ymlandtests.ymltonpm ci; add a pinnedtests/requirements/py3.lockgenerated bypip-compile(oruv pip compile) frompy3.txt, used by CI and by agents while leavingpy3.txtfloors in place for contributors. - Priority: High
-
38. Is anything watching those dependencies for known vulnerabilities? Look for dependabot or renovate config, an audit step in CI, or this ecosystem's equivalent. Check every lockfile in the repo is covered, not just the root one.
- Status: FAIL
- Proof: Nothing in the repo watches them. No
.github/dependabot.yml, norenovate.json, no.renovaterc(checked all three paths directly). No audit step in any of the 17 workflows, and nopip-audit,safety,npm auditorosv-scanneranywhere in.github/,tox.inior.pre-commit-config.yaml. Nothing coverstests/requirements/*.txt(six files) orpackage.json. Django's security posture for vulnerabilities in Django is strong and documented —.github/SECURITY.md,docs/internals/security.txt, a pre-notification list in the release runbook — but that is the disclosure process for its own code, not surveillance of its dependency tree. GitHub's Dependabot alerts may be enabled in repository settings, which a clone cannot see; noted rather than assumed. - Recommendation: Add a
.github/dependabot.ymlwith three ecosystems —pipscoped totests/requirements/,npmscoped to the root, andgithub-actions— withopen-pull-requests-limitset low so it advises rather than floods. - Priority: Low
-
39. Are the review rules written down — who reads a change, and what they check? Look for CONTRIBUTING, a PR template, or a review checklist in the agent instruction set. On a solo repo the "who" is N/A-shaped but the "what gets checked before it lands" still matters — judge that half.
- Status: PASS
- Proof: Both halves, in detail. Who:
docs/internals/contributing/committing-code.txtopens "This section is addressed to the mergers and to anyone interested in knowing how code gets committed into Django", and covers handling pull requests, rebasing, squash-merging and backports.docs/internals/contributing/triaging-tickets.txtcovers the triage roles. What gets checked:.github/pull_request_template.mdcarries a nine-item checklist — contribution guidelines followed, no security vulnerability disclosed, targetsmain, commit message in past tense with the ticket number and a trailing period, "Has patch" flag set in Trac, tests added or updated, docs and release notes added or updated, screenshots in light and dark mode for UI changes, plus the AI-assistance disclosure.docs/internals/contributing/writing-code/submitting-patches.txtsets out the patch review expectations, andCONTRIBUTING.rststates the hard gate: non-trivial PRs without a Trac ticket are closed. Three of these checks are automated on every PR (check_commit_messages.yml,check_pr_quality.yml,labels.yml). This is unusually complete. - Recommendation: —
- Priority: Low
-
40. Are the operations that need a human named somewhere an agent will read them? Look for a "never without asking" list in the agent instruction file or the README. Docs that hand out production commands with no fence around them count against, and the proof should quote one.
- Status: FAIL
- Proof: There is no "never without asking" list anywhere an agent reads, because there is no agent instruction file — see the entry-point item. Two agent-relevant rules do exist in
.github/pull_request_template.md(the AI-assistance disclosure, and the undertaking not to request an automated AI review), which is more than most repos carry, but they are not a list of human-only operations. Meanwhile the human-only procedures are handed out with no fence, as the steer warns.docs/internals/howto-release-django.txtopens "This document explains how to release Django" and proceeds through the full procedure — tagging, signing, building artefacts, publishing — with no statement anywhere that this is for release managers only. Comparedocs/internals/contributing/committing-code.txt, which does fence itself in its second line: "This section is addressed to the mergers." The release runbook has no equivalent, andscripts/do_django_release.pysits in the repo executable with no header warning either. An agent asked to "cut a release" would find a complete, followable recipe and no signal to stop. - Recommendation: In the
AGENTS.mdrecommended at the entry-point item, add a short "requires a human" list — cutting or tagging a release (scripts/do_django_release.py,docs/internals/howto-release-django.txt), pushing translations to Transifex (scripts/manage_translations.py), backporting to astable/A.B.xbranch (scripts/backport.sh), and anything touchingdjango/conf/locale/— and add a one-line "addressed to release managers" fence at the top ofdocs/internals/howto-release-django.txt. - Priority: High
-
41. Does every action that spends money, destroys data or changes production have a human in the way? Start from the damage, not from the tooling: list what in reach of this repo could charge a card, drop or overwrite data, or alter what users are running. Then trace the shortest route an agent could take to each one — a push that auto-deploys, a script carrying live credentials, a migration that runs on merge, an infrastructure apply with no plan-and-approve step. PASS when every route meets a human first, whether that is a review, a manual trigger or a protected environment. FAIL when even one route runs start to finish unattended, and quote that route in the proof so the fix is obvious.
- Status: PASS
- Proof: Started from the damage and traced each route. What could change what users run: publishing a release to PyPI and the djangoproject.com download archive. Route:
scripts/do_django_release.pyonly, run by hand. Grepped all 17 workflows fortwine|pypi|upload|publish|deploy— the only hits areactions/upload-artifactincoverage_tests.ymlandscreenshots.yml, which upload CI artefacts. No workflow publishes anything. The script itself requires a release manager's PGP key: it generates a checksum file naming the key ID and instructing verifiers togpg --verify, so an unattended run without that key produces nothing usable. What could destroy data: no database, no infrastructure, noterraform apply, no migration that runs on merge.scripts/archive_eol_stable_branches.pyandscripts/backport.share manual and operate on git refs, which are recoverable. What could spend money: nothing in reach of this repo. Workflow write permissions:check_pr_quality.yml,labels.ymlandnew_contributor_pr.ymlrun onpull_request_targetwithpull-requests: write, so they can label and comment but cannot merge or publish;schedules.ymlholdsactions: writeand is additionally gated behindenvironment: schedules, which is where GitHub applies required reviewers. Every route to a release meets a human holding a signing key. One thing a clone cannot see and which is therefore not claimed either way: branch-protection settings onmain. - Recommendation: —
- Priority: High
-
42. If a prompt injection landed tonight, how far would it reach — are the credentials an agent can get to here scoped to the job, with nothing production-grade in reach? Inventory what an agent in this repo can reach: env files, cloud CLI profiles, tokens named in docs or config, deploy commands that work from a laptop. Scoped-or-absent passes; production-grade reach fails with the item named.
- Status: PASS
- Proof: The blast radius is small, and deliberately so. Inventory of what an agent here can reach: no env files tracked or referenced, no cloud CLI profile, no
aws/gcloud/kubectlconfig, no deploy command that works from a laptop, no token named in any doc or config file. Workflow secrets: exactly two, found by grepping all workflows forsecrets\.[A-Z_]+—GITHUB_TOKENincheck_pr_quality.yml,coverage_comment.ymlandnew_contributor_pr.yml, each in a workflow carrying an explicit narrowpermissions:block, andSCHEDULE_WORKFLOW_TOKENinschedules.yml, gated behindenvironment: schedules. Credential hygiene in CI: all 28actions/checkoutsteps across the 17 workflows setpersist-credentials: false(28 uses, 28 occurrences of the setting — counted), so no token is left behind in.git/configfor a later step or a malicious build script to read. Enforcement:zizmorruns both as a pre-commit hook and as a CI job, andzizmor.ymldocuments the rule for the threepull_request_targetworkflows it exempts — "assume all inputs are malicious, assign explicit minimal permissions, and do not use actions/checkout." An injection landing in this repo tonight reaches a fork's CI and nothing production-grade. - Recommendation: —
- Priority: High
-
43. Can a change reach production a slice at a time — a feature flag that defaults to off, a canary, a staged rollout — rather than everyone at once? Look for a flag system and check the default, or canary and staged-rollout config in the deploy pipeline. Flags that need a rebuild to flip are worth naming in the proof — they gate exposure but they are not a kill switch.
- Status: PASS
- Proof: Two independent mechanisms, both documented and both in active use. A pre-release train:
docs/internals/release-process.txt— "Before a new feature release, we'll make alpha, beta, and release candidate releases", each tagged and signed, so a change reaches early adopters before it reaches everyone. Runtime flags that default to off: behaviour changes ship behind transitional settings whose default is the old behaviour, and a user flips one with a settings change and a process restart, no rebuild.docs/internals/deprecation.txtnames seven across recent versions —URLIZE_ASSUME_HTTPS(line 45),USE_BLANK_CHOICE_DASH(85),SIGNED_COOKIE_LEGACY_SALT_FALLBACK(87),FORMS_URLFIELD_ASSUME_HTTPS(224),USE_DEPRECATED_PYTZ(328),CSRF_COOKIE_MASKED(359),DEFAULT_HASHING_ALGORITHM(520) — each documented indocs/ref/settings.txt. Underneath, 223RemovedInDjango*deprecation shims acrossdjango/**/*.pykeep the old code path alive and warning for two feature releases. This is a staged rollout implemented as policy, not as a deploy pipeline, which is the right shape for a package. - Recommendation: —
- Priority: High
-
44. Once a change is live, can anyone see what it is doing — logs, metrics, traces, alerts that fire on their own, and can an agent read them too? Look for logging setup, an error tracker, analytics, alerting config — and then ask the second half: could an agent reach any of it (a CLI, an MCP server, an API named in the docs), or does observability stop at a dashboard behind a login?
- Status: N/A
- Proof: Nothing is deployed from this repo. Django is a package installed and run by its users on their own infrastructure, so there is no live system whose logs, metrics, traces or alerts this team could read — the question could not apply to a project of this kind. For completeness, the post-release feedback loop that does exist: Trac at
code.djangoproject.comfor user-reported breakage, the security reporting policy and pre-notification list in.github/SECURITY.mdanddocs/internals/security.txt, and.github/workflows/schedules.ymlrunning the full matrix nightly oncron: '42 2 * * *'againstmain, which is how a regression that escaped the PR gate surfaces. Note that the logging infrastructure indjango/utils/log.pyanddocs/topics/logging.txtis a feature Django ships to its users, not observability of Django itself, so it is not counted here. - Recommendation: —
- Priority: Low
-
45. Is there a way back — can a bad change be undone without a rebuild and a redeploy, including the ones that touched a database or a queue? Look for a documented rollback path, a revert-and-redeploy story, down-migrations, or a flag that can turn the change off at runtime. Deployment docs that only say how to go forward are worth quoting.
- Status: PASS
- Proof: There is a way back, and it does not need a rebuild. The runtime route: the transitional settings listed at the staged-rollout item let a user turn a behaviour change off with a settings change and a restart, and the 223
RemovedInDjango*shims keep the old code path working for two feature releases, so a bad change is survivable without downgrading. The forward-fix route is documented and tooled:docs/internals/release-process.txt— "each release series has its own branch, calledstable/A.B.x, and bugfix/security releases will be issued from those branches" — withscripts/backport.shautomating the cherry-pick anddocs/internals/howto-release-django.txtcovering bugfix and security releases as first-class release types. Users pin a version, so a bad release is undone by pinning back. Two honest limits, quoted rather than glossed: the release runbook has no rollback section at all — greppingdocs/internals/howto-release-django.txtforrollback|revert|undo|if something goes wrong|mistakereturns one line, at 633, about proofreading catching "silly mistakes" — and a published PyPI artefact cannot be unpublished, so the way back is always forward through a patch release. - Recommendation: —
- Priority: Low
-
46. Does a past failure leave a check behind — is there a test or a rule in here that exists because something broke once? Look for tests, lint rules or comments that name the incident they guard against — "regression", "this broke when", a linked issue. On a very young repo with no failures yet, N/A with the age as the reason.
- Status: PASS
- Proof: Everywhere, and named. 106 test files under
tests/contain the word "regression", and 280 reference a ticket number matching#[0-9]{4,5}— the ticket is the incident record, so each of those is a check that exists because something broke. Whole packages are named for it:tests/aggregation_regress/,tests/queries/(which carries the historical regression cases),tests/admin_views/. Config carries the same scars, with the ticket inline:tox.inihas# OBJC_DISABLE_INITIALIZE_FORK_SAFETY fixes hung tests for MacOS users. (#30806), and.flake8carries fourper-file-ignoresforW601in the cache backends that exist because of specific past findings..git-blame-ignore-revsis tracked so the formatting migrations do not bury this history. The most recent commits show the practice still live:6df8fe3 Fixed #24580 -- Tested FK values with __html__() in ModelAdmin.list_displayis a test added for a ticket first filed years earlier. - Recommendation: —
- Priority: Low
Learn more: https://ainativesoftware.engineering/
Nothing here is special to django/django. Install the skill and ask your agent to run it — it answers the same 46 checks and writes the same file into your repo.
npx skills add alfonsograziano/skills --skill aine-readiness-analyzerAI-Native Software Engineering