Skip to content
Readiness report

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

64OUT OF 100

The agent run

You
/aine-readiness-analyzer on the https://github.com/django/django repo, downloaded in a new temp folder
Agent

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

  1. Add three lines to .gitignoretests/local_settings*.py, *.env, .env* — and name tests/local_settings.py as the conventional filename in docs/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 stops git add -A from staging it.
  2. Add a gitleaks hook 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 — zizmor audits workflow security, not committed keys — so the mistake the missing ignore rules allow would go unnoticed.
  3. Commit a package-lock.json and switch the npm install steps in linters.yml and tests.yml to npm ci; add a pip-compile-generated tests/requirements/py3.lock for CI and agents, keeping the py3.txt floors 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.
  4. Add an AGENTS.md at the repo root as a routing table into the docs that already exist: test commands in tests/README.rst, the tox and test instructions in unit-tests.txt, style rules in coding-style.txt, commit-message and Trac rules in committing-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.
  5. Add a "requires a human" list to that same AGENTS.md — cutting or tagging a release, pushing to Transifex, backporting to stable/A.B.x, touching django/conf/locale/ — and add an "addressed to release managers" line at the top of docs/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

  1. Add .github/dependabot.yml covering three ecosystems: pip scoped to tests/requirements/, npm at the root, github-actions.
  2. Mirror the DEP template from github.com/django/deps into this repo, so proposal sections are readable without leaving the checkout.
  3. Add .claude/commands/ with two commands: one that writes a commit message in the required shape by wrapping scripts/prepare_commit_msg.py, one that runs the local gauntlet.
  4. 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.
  5. Add an advisory tox -e mypy environment 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

AINE-REPORT.md

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 commit 3180ddb3 "Discouraged automated AI reviews of pull requests." Its entire body is front-matter applyTo globs 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: no AGENTS.md, no CLAUDE.md, no .cursorrules, no GEMINI.md, no .claude/ or .agents/ directory anywhere in the tree (git ls-files plus find -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.md at the repo root that acts as a routing table into the docs that already exist: the test commands in tests/README.rst, the full test and tox instructions in docs/internals/contributing/writing-code/unit-tests.txt, the style rules in docs/internals/contributing/writing-code/coding-style.txt, and the commit-message and Trac-ticket rules in docs/internals/contributing/committing-code.txt. Leave .github/copilot-instructions.md alone — 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.md alone 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 in tests/README.rst, README.rst, INSTALL, tox.ini and docs/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.md names 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.md is 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 under tests/ across 220 test packages, 740 under docs/, plus scripts/, js_tests/ and extras/, and the mapping between them is not obvious — a change to django/db/models/fields/related.py needs a test in tests/model_fields/ or tests/many_to_many/ and a release note in docs/releases/. README.rst describes the docs/ 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.py is force-excluded from black and *.py-tpl files must not be formatted (pyproject.toml, .pre-commit-config.yaml), the locale .po files under django/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), and django/contrib/admin/static/admin/js/vendor/ plus **.min.js are 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.md returns a single commit, 3180ddb3 on 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.md gives 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.rst states it in bold: "non-trivial pull requests (anything more than fixing a typo) without Trac tickets will be closed!" .github/pull_request_template.md requires a ticket-XXXXX line 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 start Fixed #NNNNN -- or Refs #NNNNN --, for example c6be0bf Fixed #37254 -- Added fields.E323 for models referenced by ManyToManyFields. The triage workflow is documented in docs/internals/contributing/triaging-tickets.txt and docs/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 at code.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.rst carries 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 to docs/intro/install.txt and the tutorials in order, and INSTALL gives the one-liner python -m pip install . with the Python 3.12 floor. How to check a change: a dedicated "To run Django's test suite" section pointing at docs/internals/contributing/writing-code/unit-tests.txt. It also routes to CONTRIBUTING.rst, the Discord and the forum. tests/README.rst then 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.txt opens "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.txt records the stability contract and its "Exceptions" (line 50). docs/internals/deprecation.txt is a live decision log of every behaviour change with its target version, and docs/internals/release-process.txt records why the release train is shaped the way it is. docs/releases/*.txt carry the per-change reasoning. Forward-looking specs, not in this repo: the Django Enhancement Proposal process, documented at docs/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 at github.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 -1 per file against a repo doing 89 commits in 30 days: docs/releases/6.1.txt last touched 2026-08-11, three days before this audit ("Refs #28586 -- Updated Model.from_db() signature in docs"); docs/internals/deprecation.txt on 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.txt on 2026-05-11 ("Refs #36593 -- Replaced reference to select_related() in design philosophies"). docs/misc/api-stability.txt is 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-files finds no TEMPLATE.md, no .specify/ directory and no framework config of any kind. The DEP process does have a shape, but the template lives in the external github.com/django/deps repo, 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.md and the per-version structure of docs/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 from github.com/django/deps into 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: "Calling QuerySet.aiterator() after prefetch_related() without providing a chunk_size will 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: "The URLIZE_ASSUME_HTTPS transitional setting will be removed." Line 43: "The default protocol in urlize and urlizetrunc template filters will change from HTTP to HTTPS." Each names a symbol, an input condition and an observable outcome. docs/releases/6.1.txt does 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.txt names 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.txt has 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.txt states 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.md requires a ticket-XXXXX line, .github/workflows/check_commit_messages.yml runs on every pull request, and .github/workflows/check_pr_quality.yml runs scripts/pr_quality/check_pr.py against the PR description. Larger features additionally trace to a DEP, per docs/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 under tests/. Ran it from a fresh clone in a clean venv: python -m pip install -e . then cd tests && python runtests.py basic model_fields lookup gave Ran 780 tests in 0.640s / OK (skipped=64). The 64 skips are the optional dependencies from tests/requirements/py3.txt that were not installed, which the docs say will skip automatically, and they did. The policy is stated in docs/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.rst gives the whole sequence in five lines: cd tests, python -m pip install -e .., python -m pip install -r requirements/py3.txt, ./runtests.py. README.rst has a "To run Django's test suite" section routing to the long version. tox.ini declares commands = {envpython} runtests.py {posargs} with changedir = tests. docs/internals/contributing/writing-code/unit-tests.txt has 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,846 assertEqual against 97 assert_called*. Mocks are a 0.5% garnish, not the dish. There are also 764 assertNumQueries assertions, 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. .flake8 sets max-line-length = 88, max-doc-length = 79, extend-ignore = E203 and four per-file ignores. Ran flake8 . 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: zizmor for GitHub Actions security (zizmor.yml, with an explicit unpinned-uses ref-pin policy), biome for the bundled admin JavaScript (biome.json), and sphinx-lint for 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. black with target-version = ["py312"] (pyproject.toml), isort with profile = "black" and known_first_party = "django", blacken-docs for code blocks in docs/*.txt, and biome for JavaScript. All four are hooks in .pre-commit-config.yaml at exact revisions (black 26.5.1, isort 8.0.1, blacken-docs 1.20.0, biome 2.4.15). Ran black --check . on the untouched clone: "2927 files would be left unchanged", exit 0. Ran isort --check-only --diff django tests scripts: no output, exit 0. .editorconfig does real work on top, including indent_style = unset for **/admin/js/vendor/** and **.min.js so 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 mypy or pyright, and this repo uses neither. Grepped pyproject.toml, tox.ini and setup.cfg for mypy and pyright: no matches. git ls-files finds no mypy.ini, no pyrightconfig.json and no py.typed marker, so the shipped package is not even declared as typed — type hints for Django live in the third-party django-stubs project. This is a deliberate, long-standing project position rather than an oversight, and the checklist records it as a gap anyway: an agent editing django/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 mypy environment 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 check or verify target, a precommit script, 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.ini declares envlist = py3, black, blacken-docs, flake8, docs, isort, lint-docs, zizmor, so a bare tox runs 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 in docs/internals/contributing/writing-code/unit-tests.txt: "Django includes a basic tox.ini that automates some checks that our build server performs on pull requests." A second one-command path exists for the pre-push moment: pre-commit install then pre-commit run --all-files, covering the six hooks in .pre-commit-config.yaml, documented at docs/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 on pull_request. tests.yml: on: pull_request with paths-ignore: ['docs/**']. linters.yml: same trigger. docs.yml: on: pull_request scoped to paths: ['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.yml and benchmark.yml all trigger on pull_request too, several gated behind a label so the expensive matrices run on demand. The push: branches: [main] triggers are additive, not the primary gate. schedules.yml adds a nightly cron: '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. flake8linters.yml job flake8. isortlinters.yml job isort. blacklinters.yml job black via psf/black@stable. zizmorlinters.yml job zizmor. biomelinters.yml job biome, running npx biome ci. Test suite → tests.yml, on Windows/SQLite, Ubuntu free-threaded 3.14t, plus JavaScript tests, and python_matrix.yml, postgis.yml, selenium.yml, schedule_tests.yml for the wider matrix (3.12, 3.13, 3.14, Postgres, MySQL, Oracle, PostGIS). blacken-docs and sphinx-lintdocs.yml, which runs make lint, make black and SPHINXOPTS="-q -W" make spelling. Migration consistency → check-migrations.yml running scripts/check_migrations.py. Coverage → coverage_tests.yml plus coverage_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.md instructs Copilot's reviewer to produce nothing but "Do not request a review from Copilot, do it in your own fork." .github/pull_request_template.md makes 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.yaml and tox.ini for any AI reviewer step or bot config: none. There is no review skill, no saved review prompt, no .claude/commands directory. 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.json or 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/config is 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), with scripts/manage_translations.py wrapping the tx CLI 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 from CONTRIBUTING.rst, README.rst and the PR template, public to read. PyPI and the release archive: scripts/do_django_release.py and scripts/verify_release.sh, with the full procedure in docs/internals/howto-release-django.txt. None of this depends on someone's shell history. One gap worth naming: scripts/manage_translations.py documents the commands but not how to authenticate to Transifex — the ~/.transifexrc token 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-files and find -maxdepth 3 find 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 in scripts/prepare_commit_msg.py, and one that runs the local gauntlet (tox, or pre-commit run --all-files plus runtests.py on 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 at docs/internals/contributing/committing-code.txt:255 as an installable prepare-commit-msg git hook, and itself covered by tests in scripts/tests.py. Backporting a fix to a stable/A.B.x branch → scripts/backport.sh. Cutting a release → scripts/do_django_release.py and scripts/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 at scripts/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.ini gives nine named environments (py3, py314t, black, blacken-docs, flake8, docs, isort, javascript, lint-docs, zizmor), each wrapping its own flags. tests/runtests.py takes bare package names — ./runtests.py basic model_fields — instead of a settings-module incantation. docs/Makefile gives 20 targets including html, htmlview, lint, black, spelling, linkcheck. package.json declares "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.rst names ./runtests.py. README.rst routes to the test-suite docs. tox.ini is self-documenting: its header explains what tox is and says "python -m pip install tox" then run "tox" from this directory, and tox -l lists the environments. docs/internals/contributing/writing-code/unit-tests.txt names the tox environments explicitly and coding-style.txt names pre-commit install. docs/Makefile has a help: 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 lookupRan 780 tests in 0.640s, OK (skipped=64). No .env file, 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 as unit-tests.txt promises. 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-version or .tool-versions, no container or nix file, and requires-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, ran flake8 on that file, and got tests/basic/tests.py:1126:6: E225 missing whitespace around operator — path, line, column, rule code, one line, exit 1. Then restored the file and confirmed git diff --stat was empty. The test runner is standard unittest output: named test, file-and-line traceback, expected-versus-actual. CI improves on it further — linters.yml pipes flake8 and isort through liskin/gh-problem-matcher-wrap so failures land as inline annotations, and zizmor-action runs with annotations: 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) )" over django, scripts, .github and tests: zero matches. (2) Ignore rules — absent. .gitignore is 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 for env|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:174 tells contributors to write a test settings module whose DATABASES setting defines a default and an other database, and the CI templates for exactly that file are .github/workflows/data/test_postgres.py.tpl and test_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 and git add -A will stage it.
    • Recommendation: Add three lines to .gitignoretests/local_settings*.py, *.env and .env* — and name tests/local_settings.py in docs/internals/contributing/writing-code/unit-tests.txt as 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.ini and scripts/ case-insensitively for gitleaks|trufflehog|detect-secrets|pip-audit|safety check|npm audit|osv-scanner: no matches anywhere. zizmor is 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 gitleaks hook to .pre-commit-config.yaml at 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.yml for 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].dependencies in pyproject.toml uses 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.yaml pins every hook to an exact rev (black 26.5.1, blacken-docs 1.20.0, isort 8.0.1, flake8 7.3.0, biome 2.4.15, zizmor 1.25.2), and GitHub Actions are ref-pinned with zizmor.yml enforcing a ref-pin policy for actions/* and psf/*, 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-files finds no package-lock.json, yarn.lock, uv.lock or poetry.lock. Every line of tests/requirements/py3.txt is a floor (selenium >= 4.23.0, Pillow >= 10.1.0, redis >= 5.1.0, and so on for 20 packages). package.json uses caret ranges (grunt: ^1.6.2, grunt-contrib-qunit: ^10.2.0, qunit: ^2.25.0) and CI runs npm install, not npm ci — see .github/workflows/linters.yml and tests.yml. The runtime is a floor too: requires-python = ">= 3.12", with no .python-version or .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.json and switch the npm install steps in .github/workflows/linters.yml and tests.yml to npm ci; add a pinned tests/requirements/py3.lock generated by pip-compile (or uv pip compile) from py3.txt, used by CI and by agents while leaving py3.txt floors 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, no renovate.json, no .renovaterc (checked all three paths directly). No audit step in any of the 17 workflows, and no pip-audit, safety, npm audit or osv-scanner anywhere in .github/, tox.ini or .pre-commit-config.yaml. Nothing covers tests/requirements/*.txt (six files) or package.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.yml with three ecosystems — pip scoped to tests/requirements/, npm scoped to the root, and github-actions — with open-pull-requests-limit set 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.txt opens "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.txt covers the triage roles. What gets checked: .github/pull_request_template.md carries a nine-item checklist — contribution guidelines followed, no security vulnerability disclosed, targets main, 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.txt sets out the patch review expectations, and CONTRIBUTING.rst states 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.txt opens "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. Compare docs/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, and scripts/do_django_release.py sits 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.md recommended 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 a stable/A.B.x branch (scripts/backport.sh), and anything touching django/conf/locale/ — and add a one-line "addressed to release managers" fence at the top of docs/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.py only, run by hand. Grepped all 17 workflows for twine|pypi|upload|publish|deploy — the only hits are actions/upload-artifact in coverage_tests.yml and screenshots.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 to gpg --verify, so an unattended run without that key produces nothing usable. What could destroy data: no database, no infrastructure, no terraform apply, no migration that runs on merge. scripts/archive_eol_stable_branches.py and scripts/backport.sh are 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.yml and new_contributor_pr.yml run on pull_request_target with pull-requests: write, so they can label and comment but cannot merge or publish; schedules.yml holds actions: write and is additionally gated behind environment: 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 on main.
    • 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/kubectl config, 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 for secrets\.[A-Z_]+GITHUB_TOKEN in check_pr_quality.yml, coverage_comment.yml and new_contributor_pr.yml, each in a workflow carrying an explicit narrow permissions: block, and SCHEDULE_WORKFLOW_TOKEN in schedules.yml, gated behind environment: schedules. Credential hygiene in CI: all 28 actions/checkout steps across the 17 workflows set persist-credentials: false (28 uses, 28 occurrences of the setting — counted), so no token is left behind in .git/config for a later step or a malicious build script to read. Enforcement: zizmor runs both as a pre-commit hook and as a CI job, and zizmor.yml documents the rule for the three pull_request_target workflows 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.txt names 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 in docs/ref/settings.txt. Underneath, 223 RemovedInDjango* deprecation shims across django/**/*.py keep 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.com for user-reported breakage, the security reporting policy and pre-notification list in .github/SECURITY.md and docs/internals/security.txt, and .github/workflows/schedules.yml running the full matrix nightly on cron: '42 2 * * *' against main, which is how a regression that escaped the PR gate surfaces. Note that the logging infrastructure in django/utils/log.py and docs/topics/logging.txt is 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, called stable/A.B.x, and bugfix/security releases will be issued from those branches" — with scripts/backport.sh automating the cherry-pick and docs/internals/howto-release-django.txt covering 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 — grepping docs/internals/howto-release-django.txt for rollback|revert|undo|if something goes wrong|mistake returns 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.ini has # OBJC_DISABLE_INITIALIZE_FORK_SAFETY fixes hung tests for MacOS users. (#30806), and .flake8 carries four per-file-ignores for W601 in the cache backends that exist because of specific past findings. .git-blame-ignore-revs is 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_display is a test added for a ticket first filed years earlier.
    • Recommendation: —
    • Priority: Low

Learn more: https://ainativesoftware.engineering/

Get one for your own repo

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-analyzer

AI-Native Software Engineering