This file provides guidance to Claude Code when working with code in this repository.
DO NOT perform git operations unless explicitly requested. This includes:
- No
git add,git commit, orgit pushcommands - No
git status,git diff, orgit logfor preparation purposes - No creating pull requests
- No staging files
Wait for explicit user approval before ANY git operations.
Aegis Stack is a modular platform for building and evolving production-ready Python applications.
What you get:
- CLI Tool (
aegis): Create, modify, and update projects - Component System: Add/remove building blocks (database, worker, scheduler, redis)
- Service System: Add/remove business capabilities (auth, AI, payment, analytics)
- Living Projects: Generated projects can pull updates, bug fixes, and new features via
aegis update
Each generated project includes:
- Project-specific CLI
- FastAPI backend
- Full test suite
- Docker containerization
Current Version: 0.6.11
pip install aegis-stackLinks:
This project uses uv for dependency management and a Makefile for CLI development tasks.
make test- Run the CLI test suite with pytestmake lint- Run linting with ruffmake typecheck- Run type checking with tymake check- Run all checks (lint + typecheck + test) - run this before committingmake installoruv sync --all-extras- Install/sync dependenciesmake cli-test- Test CLI commands locallymake docs-serve- Serve tool documentation locally on port 8001
make test-template-quick- Quick template test without validationmake test-template- Full template test with validationmake test-template-with-components- Test template with scheduler componentmake test-template-auth- Test auth service templatemake test-template-worker- Test worker component templatemake test-template-database- Test database component templatemake test-template-full- Test template with all componentsmake clean-test-projects- Remove generated test projects
Template testing is critical - always run make test-template after modifying templates to ensure generated projects work correctly.
Before suggesting a version/rc bump, run BOTH locally:
-
make check— the aegis-stack repo's OWN test suite (lint + typecheck + pytest). Catches bugs in the CLI / core code itself, including config tests that gate on constants likeDEFAULT_PYTHON_VERSION. Without this, changing a constant without updating its assertion test passes locally but fails CI. -
make test-stacks-full— the matrix that runs each generated stack through generation → install → lint → typecheck → pytest. Catches drift in templates / cross-stack issues.
History shows multiple rcs in a row were "green locally" by aegis init +
make check (on the GENERATED project) on a single stack, but failed CI
because either:
- The matrix was never run (caught template drift on stacks the local test didn't generate), or
- The aegis-stack repo's own tests were never run (caught constant / config changes without test updates).
Running both targets is the only way to match what CI actually runs.
If the matrix takes too long during iteration, use make test-stacks-quick
for fast feedback (3 representative stacks: base, everything, insights),
then run the full matrix once at the end.
Test upgrade paths using TestPyPI before publishing to PyPI:
# 1. Install old version from TestPyPI and create project
uvx --index-url https://test.pypi.org/simple/ \
--extra-index-url https://pypi.org/simple/ \
--index-strategy unsafe-best-match \
aegis-stack@0.5.3 init test-upgrade-project --no-interactive -y
# 2. Install new RC version and test update
cd test-upgrade-project
uvx --index-url https://test.pypi.org/simple/ \
--extra-index-url https://pypi.org/simple/ \
--index-strategy unsafe-best-match \
aegis-stack@0.5.4rc1 update -yKey uvx flags:
--index-url- Primary source: TestPyPI--extra-index-url- Fallback: PyPI (for dependencies not on TestPyPI)--index-strategy unsafe-best-match- Allows mixing packages from different indexes
To test local committed template changes against an external Aegis project, use the --template-path and --to-version HEAD flags:
# From the aegis-stack root (where .venv lives):
aegis update -y -p /path/to/project -t /path/to/aegis-stack --to-version HEADKey flags:
-p/--project-path- Path to the target Aegis project-t/--template-path- Points Copier at the local aegis-stack repo instead of the installed package--to-version HEAD- Uses the latest commit on the current branch (not the version tag)
Note: Template changes must be committed to git first — Copier reads from the git state, not the working tree.
Use the Language Server (Pyright) for code navigation instead of grep/glob:
Prefer LSP:
LSP(findReferences)- Find all usages of a symbolLSP(goToDefinition)- Jump to where something is definedLSP(hover)- Get type info and docstringsLSP(documentSymbol)- List all symbols in a file
Use grep/glob when:
- Searching for text patterns (not symbol names)
- Searching in non-Python files (yaml, md, json, jinja)
- LSP server not available
Why LSP is better:
- Semantic understanding - knows actual references, not text matches
- Type-aware - hover shows full type signatures
When prototyping, check my-app/ under the aegis-stack root first.
The user often has a test project at /Users/leonardbedner/Workspace/house_bedner/aegis-stack/my-app/ for iterating on changes before backporting to templates. When making fixes:
- Look in
my-app/first for the generated project code - Test changes there
- Backport working changes to templates in
aegis/templates/
When editing code in my-app/ or any generated project, ALWAYS backport changes to templates immediately.
- Edit both
my-app/AND templates in the same session - Copy fixed files:
cp my-app/path/to/file.py "aegis/templates/copier-aegis-project/{{ project_slug }}/path/to/file.py" - Never leave changes only in
my-app/- they will be lost on nextaegis init
1. Template-First (for known changes) Edit templates directly, then generate to verify:
- Edit template files in
aegis/templates/copier-aegis-project/{{ project_slug }}/ - Generate:
make test-templateoraegis init test-project - Verify it works
- Clean up:
make clean-test-projects
2. Prototype-First (for exploratory work) Get it working in a real project, then backport:
- Generate a test project:
aegis init test-project - Make changes in the generated project until it works
- Backport immediately - copy working changes to template files
- Regenerate fresh to verify templates are correct
- Clean up test project
Template files are the source of truth. Any changes you want to persist across future aegis init commands must be backported to templates.
Remember: Generated projects are for validation/prototyping. Templates are what ships.
Copier uses git+file:// in development mode, which means it reads templates from the committed git state, not the working tree. After modifying template files:
- Ask the user to commit the template changes (Claude should NOT commit per the git policy above)
- Then regenerate test projects to verify the changes
Without committing, aegis init will use the old template content even though the files appear modified locally.
- Composable systems - Components combine in powerful, sometimes unexpected ways
- Small, focused components - Each does one thing excellently
- No abstraction for abstraction's sake - Direct access to tools, not wrappers
- Test-first development - Comprehensive testing as a core requirement
Components create emergent capabilities when combined:
- AI + Auth: User-specific conversations, protected endpoints
- AI + Database: Persistent history, analytics
- AI + Worker: Background AI processing, batch operations
- Scheduler + Database: Persistent job scheduling
- Foundation-first - Build capabilities others can build on
- Battle-tested patterns - Everything exists because it solved a real problem
- Agent-ready architecture - Predictable, testable, extendable
Type Hints (MANDATORY - Fortress-Level Type Safety):
- NEVER write untyped functions - all functions MUST have complete type annotations
- Use
str | Noneinstead ofOptional[str](Union syntax preferred) - ALWAYS include return type hints on functions (use
-> Noneif no return value) - ALWAYS type function parameters, including
ctxparameters:ctx: dict[str, Any] - Use
dict[str, Any]instead ofDict[str, Any](modern Python syntax) - Prefer
list[str]overList[str](built-in generics)
Function Design:
- Keep functions small and focused (max ~20 lines)
- Use descriptive names:
get_user_health_status()notget_data() - Return early for error conditions (guard clauses)
- Prefer pure functions when possible
Error Handling:
- Use specific exception types, not bare
except: - Raise meaningful exceptions with context
- Use
from Noneto suppress chaining when appropriate - Log errors at appropriate levels
Database Queries (Avoid N+1):
- NEVER query inside a loop - this causes N+1 query problems
- Batch-fetch related data using
WHERE id IN (...)or JOINs - Use
selectinload()orjoinedload()for eager loading relationships - Example of what NOT to do:
# BAD: N+1 queries (1 query + N queries in loop) for model in models: price = session.exec(select(Price).where(Price.model_id == model.id)).first() # GOOD: Batch fetch (2 queries total) model_ids = [m.id for m in models] prices = session.exec(select(Price).where(Price.model_id.in_(model_ids))).all() price_map = {p.model_id: p for p in prices}
Always look for existing code being used in multiple places. Before writing new code, heavily weigh towards creating a single function imported to other places rather than duplicating logic.
Keep changes minimal and targeted. When fixing a specific issue:
- Identify the core problem - Fix exactly what's broken, nothing more
- Avoid scope creep - Don't add extensive test suites unless specifically requested
- Make minimal viable fixes - Simple, working solutions over complex architectures
- Test the fix works - Verify the original issue is resolved
When editing templates, understand where code belongs:
Components (app/components/): Infrastructure - scheduler, worker, database, backend, frontend
Services (app/services/): Business logic - auth, AI, payment, etc.
Components define WHEN/WHERE (routes, jobs). Services define WHAT (logic).