- Format and lint:
./scripts/fix(always run this before committing) - Run tests:
./scripts/test [package] - Server tests: Add
--serverwhen changes touchplain/server/ortools/server scripts
Always use ./scripts/ commands from the repo root — never run uv run plain fix, uv run plain pre-commit, etc. directly in the example/ directory.
| Command | Purpose |
|---|---|
./scripts/fix |
Format and lint code |
./scripts/pre-commit |
Full pre-commit validation |
./scripts/test [package] |
Run tests (add --server when changing plain/server/) |
./scripts/server-test |
Server conformance, load, and resilience tests |
./scripts/create-migrations |
Create database migrations (calls plain migrations create) |
./scripts/type-check <dir> |
Type check a directory |
uv run python |
Open Python shell |
Use the scratch directory for temporary files and experimentation. This directory is gitignored.
The example directory contains a demo app. Use cd example && uv run plain to test.
Don't worry about backwards compatibility for API changes like function renames, argument changes, or import path updates. The /plain-upgrade skill integrates an AI agent into the upgrade process that can automatically fix user code during updates.
Deeper breaking changes that users can't directly control or fix in their own code still need careful consideration.
- Plain requires Python 3.13+ — use modern Python APIs and syntax freely (e.g.
X | Yunions,match,ExceptionGroup, etc.) - Prefer unique, greppable names over overloaded terms
- Verify changes with
print()statements, then remove before committing
Plain ships three tiers of AI guidance per package, each with a different purpose:
graph TD
R["<b>Rules</b><br/><i>always loaded, ~50 lines</i><br/>Guardrails: what to do, what not to do<br/>1-line reminders, no full code examples"]
D["<b>Docs</b><br/><i>on demand via CLI</i><br/>Full reference with examples<br/><code>plain docs <pkg> --section X</code>"]
S["<b>Skills</b><br/><i>invoked via /slash-commands</i><br/>Multi-step workflows<br/>e.g. /plain-install, /plain-upgrade"]
R -- "points to" --> D
R -- "points to" --> S
Concise guardrails always loaded into context. Keep them short (~50 lines) — bullet-point reminders, not tutorials. Point to docs for details. Use paths: frontmatter to scope rules to relevant files.
Django-specific corrections (e.g., "use X not Django's Y") belong only in plain.md's "Key Differences from Django" section. Package rules should describe how Plain works, not what Django does differently. It's fine for those corrections to cross package boundaries — they live in one place.
Example pattern (from plain-postgres rule → querying section):
- Use `select_related()` for FK access in loops, `prefetch_related()` for reverse/M2N
- Use `.exists()` not `.count() > 0`, `.count()` not `len(qs)`
Run `uv run plain docs postgres --section querying` for full patterns with code examples.
Package README.md files are the primary documentation — rendered on the website, PyPI, and GitHub. They're also available to AI via the CLI:
uv run plain docs postgres— full docsuv run plain docs postgres --section querying— just the Querying sectionuv run plain docs postgres --api— public API surface from__all__
Write docs for humans first. Sections are ## headings in the README — keep each one self-contained enough to be useful when loaded independently via --section.
Multi-step workflows invoked via /slash-commands. These coordinate tools, run commands, and guide multi-turn processes (e.g. installing a package, running a release).
- Package-level
<package>/plain/<module>/agents/.claude/: Source of truth. Shipped to end users viaplain agent install. - Top-level
.claude/rules/and.claude/skills/: Copies for developing this repo. Generated byplain agent install— do not edit directly. - A few top-level skills and rules (
release,readme) are unique to development and have no package-level counterpart.
When editing a rule or skill, always edit the package-level file in agents/.claude/ first. Then run plain agent install to sync.