|
| 1 | +# How the modern-python repos are set up for coding agents |
| 2 | + |
| 3 | +modern-python is about thirty repositories, maintained by one person, with coding agents working |
| 4 | +in every one of them. This page describes the setup that makes that workable, using the same four |
| 5 | +parts as the [agent-ready backend](services.md#agent-ready) offer. Everything here is public and |
| 6 | +linked, so you can check it. |
| 7 | + |
| 8 | +## Context the agent can use { #context } |
| 9 | + |
| 10 | +Every repo has an `AGENTS.md` at the root. It holds what an agent can't learn by reading the code: |
| 11 | +the commands, where each concern lives, and the rules that leave no trace in the code that follows |
| 12 | +them. [lite-bootstrap's](https://github.com/modern-python/lite-bootstrap/blob/main/AGENTS.md) |
| 13 | +is a typical one. It explains why every link in `README.md` must be absolute (the README is also |
| 14 | +the PyPI description, and PyPI doesn't rewrite relative links), and it lists four code style rules, |
| 15 | +each with the reason behind it. |
| 16 | + |
| 17 | +Most repos also carry a `CONTEXT.md`, a glossary limited to terms with a synonym to reject or a |
| 18 | +meaning subtle enough that code and docs have to agree on it. Each entry names the words to avoid. |
| 19 | +In [lite-bootstrap's](https://github.com/modern-python/lite-bootstrap/blob/main/CONTEXT.md), an |
| 20 | +instrument being *configured* and a bootstrapper being *ready* are separate terms, and the agent is |
| 21 | +told to use them exactly. |
| 22 | + |
| 23 | +Decisions go into architecture decision records under `docs/adr/`, 88 of them across the repos. A |
| 24 | +short [domain doc](https://github.com/modern-python/lite-bootstrap/blob/main/docs/agents/domain.md) |
| 25 | +tells the agent to read the ADRs for the area it is changing, and to say so when its change |
| 26 | +contradicts one instead of quietly overriding it. Two more docs in the same folder describe the |
| 27 | +issue tracker and the triage labels, so an agent can pick up and file GitHub issues through `gh`. |
| 28 | + |
| 29 | +Above the repos sits the [standard](standard.md): numbered requirements, each with a stable ID and |
| 30 | +usually a *Why* line. An agent can cite [CI9](standard.md#CI9) in a pull request the same way a |
| 31 | +person would. |
| 32 | + |
| 33 | +## Checks for agent-written code { #checks } |
| 34 | + |
| 35 | +An agent's change goes through the same checks as anyone's, and the commands are identical |
| 36 | +locally and in CI: |
| 37 | + |
| 38 | +- The `justfile` has a fixed set of recipes ([JF1](standard.md#JF1)). `just lint` rewrites files, |
| 39 | + and `just lint-ci` runs the same checks read-only. |
| 40 | +- ruff, ty and eof-fixer, required by the standard ([TL2](standard.md#TL2)), with a 100% line |
| 41 | + coverage gate ([TS2](standard.md#TS2)). A `pragma: no cover` should carry its reason on the same |
| 42 | + line ([TS6](standard.md#TS6)). |
| 43 | +- The `floors` job installs every dependency at its lowest declared version and runs the suite |
| 44 | + ([CI10](standard.md#CI10)), which catches code that only works on the latest release. |
| 45 | +- A links job fails on any relative link or file path the diff broke ([CI7](standard.md#CI7)), and |
| 46 | + docs sites build with `mkdocs build --strict` ([CI8](standard.md#CI8)). |
| 47 | +- A shared check fails when a file cites an ADR that doesn't exist ([CI9](standard.md#CI9)). |
| 48 | + |
| 49 | +Some rules are tests. The org repo's |
| 50 | +[`tests/test_site.py`](https://github.com/modern-python/.github/blob/main/tests/test_site.py) |
| 51 | +asserts that the site's project lists match the brand manifest, and each test's docstring opens |
| 52 | +with `INVARIANT:` and explains what breaks without it. An agent that removes the check has to argue |
| 53 | +with the reason in front of it. |
| 54 | + |
| 55 | +## Signals from outside the diff { #signals } |
| 56 | + |
| 57 | +Libraries don't have a production environment, so this part is thin here. The closest thing is a |
| 58 | +daily scheduled run of each published repo's checks ([CI2](standard.md#CI2)). When a dependency release or a |
| 59 | +new Python breaks a repo, a |
| 60 | +[shared workflow](https://github.com/modern-python/.github/blob/main/.github/workflows/report-scheduled-failure.yml) |
| 61 | +opens or updates an issue linking the failing run, and an agent can pick that issue up like any |
| 62 | +other. |
| 63 | + |
| 64 | +## Telemetry { #telemetry } |
| 65 | + |
| 66 | +The same applies to telemetry: a library has none of its own. For services, the org's answer is |
| 67 | +[`lite-bootstrap`](https://github.com/modern-python/lite-bootstrap), which wires OpenTelemetry, |
| 68 | +Prometheus, Sentry and structlog into FastAPI, Litestar or FastStream. In a client's service, this |
| 69 | +part and the one above carry more weight than they do here. |
| 70 | + |
| 71 | +## What we learned { #lessons } |
| 72 | + |
| 73 | +Copies drift, even byte-identical ones. Twenty-five copies of the ADR citation test drifted within |
| 74 | +a month of being written, so the repos now fetch one shared file from the org repo, unpinned |
| 75 | +([CI9](standard.md#CI9)). Per-repo scripts that reported scheduled failures drifted the same way, |
| 76 | +and several named a dependency their repo didn't have. They were replaced by one shared workflow |
| 77 | +([CI2](standard.md#CI2)). |
| 78 | + |
| 79 | +Not everything should be shared. A single CI workflow for all repos was built and rejected |
| 80 | +([#95](https://github.com/modern-python/.github/issues/95)): reusable workflow inputs are plain |
| 81 | +strings, so service containers and environment maps had to be flattened into them. Each repo keeps |
| 82 | +its own `_checks.yml` ([CI3](standard.md#CI3)), and a change to the jobs is a sweep across repos. |
| 83 | + |
| 84 | +Write down only what the code doesn't show. Agents read code well. What they miss is the reason a |
| 85 | +link must be absolute, why a type checker's warning is noise, or which word the project has already |
| 86 | +rejected, and that is what the `AGENTS.md` and `CONTEXT.md` files hold. |
| 87 | + |
| 88 | +If you'd like the same setup in your own codebase, see [services](services.md#agent-ready). |
0 commit comments