This file is adapted from the shared AGENTS.md in the Read the Docs common repo,
with the project-specific sections rewritten for this site.
We want all interaction in public to be between real people.
- Never comment on pull requests, commits, or other things as the user.
- Make all public-facing copy like commit messages and pull request descriptions for the reader, and not the author.
- Ensure everything is optimized for clarity, conciseness, and effective communication.
This is the source for www.writethedocs.org, the Write the Docs community website. It's a Sphinx-based static site that uses reStructuredText and Markdown content, Jinja2 templating, and YAML data files for conference configuration. Hosted on Read the Docs.
Technology Stack:
- Python 3.12
- Sphinx with custom extensions (
docs/_ext/) - Jinja2 templating over RST/Markdown content
- YAML data files for conference configuration
- UIKit CSS framework with per-year SCSS
- Use short descriptive commit messages
- Only provide simple context, and don't be overly verbose
- Always run
pre-commit run --files <changed files>before committing to ensure code passes all checks
- Make the PR description as useful as possible for humans; lead with the most important context.
- Always open pull requests as drafts
- Note in a footer that the PR was generated by an AI agent, but only if no such footer is already appended automatically — never add a second one
- Use feature branches for all changes
- Write the description about why: the problem it solves, the approach taken, and anything a reviewer should double-check. Don't restate what changed — no "Changes" section, changelog, file lists, or per-change bullets — since the diff already shows that. Prefer "Sharpens the PR rules, which kept producing diff-restating descriptions" over "Reworded one bullet, fixed a typo, removed two sections."
- Don't include Test Plan unless absolutely necessary.
- Link related issues in the PR description, if there are any in the chat context
- Prefix pull request titles with the part of the site being worked on, for example
conf:,blog:,sponsors:, ordocs:.
- Always use
uvfor Python package management and virtual environment operations - Use
uv pip installinstead ofpip install - Use
uv runto execute Python scripts in the virtual environment
- Follow the style guide at https://writethedocs.org/style-guide/
- Use the public names for conferences and events (for example "Write the Docs Portland 2026"), not internal slugs or config keys.
- Direct, second-person voice. No jargon. Less is more.
- Focus on reader benefit, not abstract positioning.
- Don't repeat facts within a section; use standard terminology (e.g. "call for proposals", "unconference").
- Follow PEP 8 style guidelines
- Write clear, concise docstrings for public functions and classes
- Run linters and formatters using before committing code
All build commands run from the docs/ directory using make (not raw sphinx-build):
- Build site:
cd docs && uv run make html - Live preview:
cd docs && uv run make livehtml(serves at http://127.0.0.1:8888) - Clean build:
cd docs && make clean && uv run make html - Compile SCSS:
sass --style=compressed --no-source-map docs/_static/conf/scss/main-YYYY.scss docs/_static/conf/css/main-YYYY.min.css
Python 3.12 is required. Dependencies: uv sync
All run on push/PR to main:
- Ubuntu Build (
ubuntu.yml): Sphinx build + htmlproofer link checking (internal links only, with--swap-urlsto treatwritethedocs.orgURLs as local) - Validate YAML (
validate_yaml.yml): Runsdocs/_scripts/validate-yaml.shwith yamale schema validation - Spellcheck (
spellcheck.yml): codespell with ignore list atcodespell/ignore.txt - Vale (
vale.yml): Prose linting with configs atvale/vale.ini,vale/guide.ini,vale/news.ini
Pre-commit hooks validate YAML files in docs/_data/ (yamllint, check-yaml, trailing whitespace, schema validation with yamale).
All RST/MD files are processed through Jinja2 before Sphinx renders them (_ext/core.py:render_rst_with_jinja). This means Jinja tags ({{ }}, {% %}) work in any content file. Conference pages get their YAML config injected as Jinja context automatically based on the URL path pattern conf/<city>/<year>/.
Each conference (2020+) has three YAML data files in docs/_data/:
<city>-<year>-config.yaml— configuration, feature flags (flag*), dates, buttons, sponsors<city>-<year>-sessions.yaml— speaker/talk data<city>-<year>-schedule.yaml— schedule with slugs referencing sessions
YAML schemas for validation: docs/_data/schema-config.yaml, schema-schedule.yaml, schema-sessions.yaml
Pre-2020 conferences use a different naming convention (config-<city>-<year>.yaml).
Year-specific HTML templates in docs/_templates/<year>/ (base.html, index.html, generic.html, menu-*.html). Pages specify their template via :template: <year>/generic.html metadata in RST.
core.py— Jinja rendering of RST, conference YAML context loading, template override systemfilters.py— Custom Jinja filters (e.g., speaker photo paths)meetups.py—meetup-listingdirectiveatom_absolute.py— Rewrites atom feed URLs to absolute
Per-year SCSS files at docs/_static/conf/scss/main-<year>.scss compile to docs/_static/conf/css/main-<year>.min.css. Uses UIKit CSS framework (via CDN). Both SCSS source and compiled CSS must be committed.
Uses the ablog extension. News posts go in docs/conf/<city>/<year>/news/ with .. post:: directive and :tags: <city>-<year> for filtering via .. postlist:: on conference index pages.
- Content files support both
.rstand.md(via myst-parser) - Conference pages use
:orphan:directive (not in a toctree) - Feature flags in config YAML control what sections display (e.g.,
flagtickets,flagcfp,flaghasschedule) - Conference talk videos are hosted on the Write the Docs YouTube channel