agentleFS
lbednerlbedner/aegis-stack/CLAUDE.md

aegis-stack

CLAUDE.md144 starsChanged 15 months agoMITView on GitHub

Guidance for Claude Code working in this repository. This file carries only always-true rules and a skills index. Task procedures live in .claude/skills/ and load on demand; see the index at the bottom. - Types are mandatory: every function fully annotated, including return types (-> None if none) and parameters (ctx: dict[str, Any]). Use str | None, dict[str, Any], list[str] (modern generics), never Optional/Dict/List. - Functions: small and focused (~20 lines), descriptive names, guard clauses, prefer pure functions. - Errors:…

We split this CLAUDE.md into the rules it gives an agent

7 rules. About 0 are common, 3 are distinctive, and 4 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈3

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 4

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Never run git add, commit, push, or create PRs, and do not run git status/diff/log as preparation, unless the user explicitly asks.

    line 10
  2. Distinctive:

    Write the failing test before the implementation, always.

    line 15
  3. Distinctive:

    Before writing new code, look for existing logic used elsewhere.

    line 52
  4. Distinctive:

    If a mistake (lint slip, wrong approach, even a real bug) is found and fully fixed within the same turn, before the user ever saw or acted …

    line 56
  5. Very specific:

    No N+1 queries: never query inside a loop.

    line 74
  6. Very specific:

    Prefer the language server (Pyright) over grep for symbols: findReferences, goToDefinition, hover, documentSymbol.

    line 86
  7. Very specific:

    NEVER asyncio.run inside a command.

    line 102

≈ “Common” is an estimate: at least 1 in 5 of the 25,680 CLAUDE.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
CLAUDE.md

CLAUDE.md

Guidance for Claude Code working in this repository. This file carries only always-true rules and a skills index. Task procedures live in .claude/skills/ and load on demand; see the index at the bottom.

Hard rules

No git operations without explicit request

Never run git add, commit, push, or create PRs, and do not run git status/diff/log as preparation, unless the user explicitly asks. Wait for explicit approval before any git operation.

TDD: failing test first

Write the failing test before the implementation, always. Confirm it fails for the right reason, then implement.

Templates are the source of truth; backport immediately

aegis/templates/copier-aegis-project/{{ project_slug }}/ is what ships; generated projects (often a scratch my-app/) are prototypes. Any change made in a generated project MUST be backported to its template in the same session, or it is lost on the next aegis init. Full workflow: the template-dev skill.

Dual-write: templates AND the live my-app

There is always a live my-app instance running that the user drives (docker, hot-reloading a bind-mounted source tree). Every code change must land in BOTH the template AND that live my-app in the same session, so the user sees it on reload. The live tree is whatever is bind-mounted into the running my-app-webserver-1 container, NOT necessarily the top-level my-app/: confirm the path with docker inspect my-app-webserver-1 --format '{{ "{{" }}range .Mounts{{ "}}" }}{{ "{{" }}.Source{{ "}}" }} -> {{ "{{" }}.Destination{{ "}}" }}{{ "{{" }}end{{ "}}" }}' before editing (accidental aegis init nesting has produced my-app/my-app/my-app). .py files render identically across finance stacks so copy them; .jinja files are stack-specific so edit surgically. See memory project-my-app-disposable.

Module size budget

Logic modules cap at 500 lines; declaration modules (models, schemas, locales, seeds, fixtures) at 900. No module may be both >40% of its package and >400 lines - that shape is one file wearing a folder. Enforced by tests/core/test_module_size_budget.py, whose BUDGET dict is a ratchet: it records existing debt at current size, so nothing gets worse and anything new must meet the cap outright. Shrink a file under its limit and the test tells you to delete its entry. The reader being protected is an agent: 500 lines is ~7k tokens, and past ~2000 the Read tool pages instead of loading.

Scope control

Fix exactly what is asked. No scope creep, no unrequested test suites, minimal viable change, and verify it works.

DRY

Before writing new code, look for existing logic used elsewhere; prefer a single shared function over duplicating it.

Don't narrate self-caught-and-fixed issues

If a mistake (lint slip, wrong approach, even a real bug) is found and fully fixed within the same turn, before the user ever saw or acted on the bad outcome, fix it silently and say nothing about it - not in the summary, not as a closing "worth being straight about" aside. Severity does not matter; exposure does. Only report: issues that already reached a prior turn's output (the user saw/relied on the wrong thing), and anything still unverified that the user needs to check themselves. This is deliberate - the user wants minimal context, not a transparency log.

Coding standards

  • Types are mandatory: every function fully annotated, including return types (-> None if none) and parameters (ctx: dict[str, Any]). Use str | None, dict[str, Any], list[str] (modern generics), never Optional/Dict/List.
  • Functions: small and focused (~20 lines), descriptive names, guard clauses, prefer pure functions.
  • Errors: specific exception types (never bare except:), meaningful messages, from None where apt, log at the right level. Never silently swallow an error.
  • No N+1 queries: never query inside a loop; batch with WHERE id IN (...) or eager-load with selectinload()/joinedload().

Components vs services

Components (app/components/) are infrastructure (backend, frontend, worker, scheduler, database, redis) and define WHEN/WHERE. Services (app/services/) are business logic (auth, AI, payment, blog, finance) and define WHAT. Both ride the shared plugin-spec machinery.

Code navigation

Prefer the language server (Pyright) over grep for symbols: findReferences, goToDefinition, hover, documentSymbol. Use grep/glob for text patterns, non-Python files, or when the LSP is unavailable.

Daily commands

  • make check - lint + typecheck + test; run before considering work done
  • make test-template - generate and validate a project after template changes
  • make test-stacks-quick - fast multi-stack smoke (base, everything, insights)

The Makefile documents the rest (per-component template tests, the full stack matrix, release targets).

Generated-app CLI commands (template + my-app)

New @app.command handlers in app/cli/ are async def with plain await - NEVER asyncio.run inside a command. The harness in app/cli/main.py (standalone_mode=False + iscoroutine + one asyncio.run) owns the loop. Exemplar: cli/llm.py:reset.

Nearly every existing command still uses the legacy shape: a sync def that calls asyncio.run(_impl(...)) on a private twin. That is the majority, not the standard - do not copy it, and do not treat its prevalence as a reason to keep writing it. The one converted module in the wild is the crawl4ai plugin's app/cli/crawl.py, if you want six commands' worth of before/after.

Skills index

Procedures load on demand from .claude/skills/<name>/SKILL.md when the task matches the skill's description. Reach for:

  • add-service - add a new business-capability service to the framework
  • add-component - add a new component, or a variant axis on an existing one
  • create-plugin - build an Aegis Stack plugin (a separate aegis-stack-<name> package)
  • i18n - add or translate locale strings (en.py first, parity across locales)
  • add-cli-command - add a command to the aegis tool CLI
  • release - cut a version or rc release (bump, gates, TestPyPI, tagging)
  • template-dev - modify templates, or backport a change from a generated project

See .claude/skills/README.md for the skill format; example-skill is the copy-from template.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.