This document contains critical knowledge for AI agents working on the xerox-python repository. Read this ENTIRELY before making any changes.
This is a Copier template repository with a special branch structure:
main ─┬─ fastapi (single commit with FastAPI specializations)
└─ flask (single commit with Flask specializations)
CRITICAL RULES:
- NEVER merge
fastapiorflaskintomain - ALWAYS rebase these branches when updating them
- Each framework branch must be EXACTLY ONE COMMIT on top of
main - Framework branches contain framework-specific specializations only
- Contains the base template shared by all frameworks
- Gets updates to dependencies, tooling, common patterns
- Examples: Python version support, type checker updates, Makefile conventions
- Single commit on top of
main - Contains ONLY FastAPI-specific code:
uvicornapp server- FastAPI routers (not Flask blueprints)
- ASGI middleware
- FastAPI-specific dependencies
- Single commit on top of
main - Contains ONLY Flask-specific code:
flaskapp server- Flask blueprints (not FastAPI routers)
- WSGI middleware
- Flask-specific dependencies
When making changes to main:
- Make your changes on
main - Commit and push to
origin/main - DO NOT merge into framework branches
- See "Rebasing Framework Branches" below
After updating main, you MUST rebase both framework branches:
# 1. Checkout the framework branch
git checkout fastapi
# 2. Rebase onto main (will have conflicts)
git rebase main
# 3. Resolve conflicts by copying from the previous good version
# Use git reflog to find the previous good commit
git reflog
# Example: ac3789a was the last good fastapi commit
git show ac3789a:path/to/conflicted/file > path/to/conflicted/file
# 4. Continue the rebase
git add -A
git rebase --continue
# 5. Apply any additional fixes needed for main changes
# (e.g., API updates, new type ignore comments)
# 6. Squash everything into ONE commit
git reset --soft main
git commit -m "feat: Added a branch for FastAPI"
# 7. Verify single commit
git log --oneline main..fastapi
# Should show EXACTLY one commit
# 8. Repeat for flask branch
git checkout flask
# ... same processALWAYS test both framework branches before pushing:
# Test FastAPI
cd /path/to/test/location
rm -rf test-fastapi-final
cd xerox-python
uv run copier copy --trust --defaults --vcs-ref fastapi \
--data project_name=test-fastapi-final \
--data run_qa_checks=false --data initialize_git=false \
--data push_to_github=false --skip-tasks . ../test-fastapi-final
cd ../test-fastapi-final/test-fastapi-final
echo "3.13" > .python-version
sed -i.bak 's/requires-python = ">=3.9, ~=3.14"/requires-python = ">=3.9, ~=3.13"/' pyproject.toml
UV_PYTHON=3.13 uv sync --quiet
make qa/full
# MUST show: "All quality checks pass!" with 0 diagnostics
# Test Flask (same process with flask branch)git checkout fastapi
git merge main # NEVER DO THIS!git checkout fastapi
git rebase main
# resolve conflicts
git reset --soft main
git commit -m "feat: Added a branch for FastAPI"git log --oneline main..fastapi
abc123 fix: update imports
def456 fix: resolve conflicts
789ghi feat: Added a branch for FastAPI
# THREE commits - WRONG!git log --oneline main..fastapi
abc123 feat: Added a branch for FastAPI
# ONE commit - CORRECT!- main.py: Uses
FastAPI()app, async/await, lifespan context manager - Routers:
from fastapi import APIRouter, async route handlers - Middleware:
CORSMiddlewareneeds# ty: ignore[invalid-argument-type]TrackRequestsMiddlewareneeds# ty: ignore[invalid-argument-type]
- Dependencies:
fastapi,uvicorn,httpx,logot - Makefile:
app/serveusesuvicorn
- main.py: Uses
create_app()factory pattern, WSGI - Routes:
from flask import Blueprint, sync route handlers - Middleware:
MiddlewareManager(app)needs# ty: ignore[invalid-assignment]app.wsgi_app.add_middleware()needs# ty: ignore[unresolved-attribute]
- Dependencies:
flask,flask-buzz,flask-http-middleware,httpx - Makefile:
app/serveusesflask run
- version.py: Version detection logic
- constants.py: Enums like
DeployEnv - pydantix.py: Pydantic custom types (TimeDelta)
- Base Makefile/pyproject.toml: Common build/test/QA setup
The repository recently migrated from mypy and basedpyright to ty as the type checker.
# OLD (don't use):
# pyright: ignore[reportArgumentType]
# type: ignore[arg-type]
# NEW (use this):
# ty: ignore[invalid-argument-type]
# ty: ignore[unresolved-attribute]# OLD:
qa/types: qa/types-basedpyright qa/types-mypy
# NEW:
qa/types: qa/types-tyThe whenever library deprecated parse_common_iso() and format_common_iso():
# OLD (don't use):
TimeDelta.parse_common_iso("PT1S")
instance.format_common_iso()
# NEW (use this):
TimeDelta.parse_iso("PT1S")
instance.format_iso()This affects:
pydantix.pyserialization- Test files using
TimeDeltaorInstant
If you need to reference the last working version:
# View reflog to find previous commits
git reflog
# Show a file from a specific commit
git show <commit-hash>:path/to/file
# Copy a file from a previous commit
git show <commit-hash>:path/to/file > path/to/file# Visualize branch structure
git log --oneline --graph --all -10
# Should look like:
# * 0b201fd feat: Added a branch for Flask
# | * 07b1b86 feat: Added a branch for FastAPI
# |/
# * ff437f6 (main) Latest main commit
# * e42f565 Previous main commit
# Count commits on a branch
git log --oneline main..fastapi | wc -l
# Should output: 1After generating a test project, look for:
All checks passed!
All checks passed!
All quality checks pass!
And specifically check for 0 diagnostics from the type checker.
If you accidentally merge instead of rebase:
# 1. Find the last good commit in reflog
git reflog | grep fastapi
# 2. Hard reset to that commit
git reset --hard <good-commit-hash>
# 3. Force push (use --force-with-lease for safety)
git push origin fastapi --force-with-leaseWhen pushing all branches after updates:
# Push all three branches together
git push origin main fastapi flask --force-with-lease
# The --force-with-lease is necessary because:
# - Framework branches are rebased (history rewritten)
# - But it's safer than --force (won't overwrite others' work)Before pushing changes, verify:
- Did I rebase instead of merge?
- Does each framework branch have EXACTLY one commit?
- Did I test BOTH fastapi and flask branches?
- Did both QA runs show 0 diagnostics?
- Did all tests pass (17 tests each)?
- Are coverage requirements met (>85%)?
- Did I use
--force-with-leasewhen pushing rebased branches?
# 1. Update main branch if it's a shared dependency
# 2. Update fastapi branch pyproject.toml.jinja with FastAPI-specific version
# 3. Update flask branch pyproject.toml.jinja with Flask-specific version
# 4. Test both branches
# 5. Push all three branches# 1. Update main branch .python-version and pyproject.toml
# 2. Rebase both framework branches
# 3. Test with new Python version
# 4. Push all branches# 1. Determine if it's in shared code (main) or framework-specific
# 2. Add appropriate # ty: ignore[...] comment
# 3. If in main: rebase both framework branches after
# 4. If in framework branch: fix that branch only
# 5. Test thoroughly"The framework branches must NEVER be merged to main. They should remain as SINGLE COMMITS branching off from main, containing all framework-specific specializations."
When in doubt:
- Rebase, don't merge
- Test both branches before pushing
- Verify single commit structure
- Ask the human if you're unsure
This repository has a unique structure for a good reason - it allows maintaining multiple framework specializations while sharing common code. Respect the structure, and it will work beautifully.