- Docs
- Core
- Getting started
- For coding agents
For coding agents
This is the operating manual for generating QuadKit code. Humans can skip it and start at Your First App.
1. Read in this order
Section titled “1. Read in this order”/llms.txt— identity, install, rules, every docs URL./agents.md— distilled frameworkAGENTS.md: hierarchy, Result vs exceptions, provider lifecycle, the never-list./SKILL.md— drop-in skill for Claude Code, Cursor, OpenCode (same rules, fetchable without another clone)./llms-full.txt— the same catalog plus architecture notes. Per-package:/llms/index.txt(example/llms/quadkit-web.txt).- This page — repo map, the one project tree, recipes. Then Common mistakes for fail vs fix.
- The generator output you just scaffolded with
quadkit new project. - The protocol in
quadkit-contracts, not a concrete class in an extension.
Installable pack: quadkit-skills (Claude Code, Cursor, OpenCode). One-file fetch: /SKILL.md. Install commands: Agent skills. Do not re-derive the rules from blog posts. If a packed skill and this site disagree on layout, this site wins (domains/, app-root di/).
Canonical ruleset lives in this repo at AGENTS.md.
2. The hierarchy is the whole design
Section titled “2. The hierarchy is the whole design”quadkit-contracts Zero dependencies. Protocols, types, exceptions only. ↑quadkit Depends ONLY on quadkit-contracts. ↑quadkit-* Extension packages. Never import each other.If two or more packages need the same type, protocol, or exception, it lives in
quadkit-contracts. No exceptions.
Thirteen packages are published: quadkit, quadkit-contracts, quadkit-web, quadkit-http, quadkit-sql, quadkit-cache, quadkit-storage, quadkit-events, quadkit-tasks, quadkit-monitor, quadkit-auth, quadkit-cli, quadkit-testing. Anything else you remember from an older revision of these docs is not installable — do not add it to pyproject.toml, and do not import it.
Documented exceptions (deps must be in pyproject.toml):
| Package | May import |
|---|---|
quadkit-cli | quadkit, quadkit-contracts |
quadkit-testing | any extension (optional extras) |
An import linter enforces this. A cross-extension import that type-checks will still fail CI.
3. One project tree
Section titled “3. One project tree”There is one layout. Templates (minimal, api, web-api, graphql, worker, full, fullstack) add packages and application.yaml sections — not a second shape. There is no --structure flag and no models/ directory.
| Kind | Where it lives |
|---|---|
| Composition root | src/<app>/app.py — create_app(), ASGI target <app>.app:app |
| Unscoped HTTP | src/<app>/controllers/ |
| Unscoped domain types | src/<app>/domains/ |
| App providers | src/<app>/di/*_provider.py |
| Module provider | src/<app>/modules/<slug>/provider.py |
| Cross-cutting | src/<app>/shared/ (errors, middleware, health, …) |
| Bounded context | src/<app>/modules/<slug>/ after quadkit new module |
src/<app>/├── app.py # create_app() — never list controllers by hand├── controllers/├── domains/├── di/ # app-root providers├── services/├── infrastructure/├── shared/└── modules/ ├── __init__.py # empty until quadkit new module └── auth/ ├── protocols.py # the only types other modules import ├── provider.py ├── domains/ └── controllers/Controllers are discovered. Teach WebModule.configure(discover=["<app>.controllers", "<app>.modules"]) so quadkit gen controller does not require editing app.py. WebModule.configure() also accepts controllers=[…] when you want an explicit list. Adding a module lists it in create_app() next to WebModule.
Full generator map: Project Structure and CLI PROJECT_LAYOUT.
4. Repo map (dbtinoy-/quadkit, branch main)
Section titled “4. Repo map (dbtinoy-/quadkit, branch main)”| Path | What it is |
|---|---|
AGENTS.md | Canonical agent rules |
core/quadkit | Application, container, providers, modules, Result, config |
core/quadkit-contracts | Protocols, types, exceptions — zero deps |
packages/quadkit-web | ASGI, controllers, middleware, OpenAPI |
packages/quadkit-testing | Test harnesses, fakes, fixtures |
apps/quadkit-cli | Scaffolding, generators, dev server |
This docs site (oridecon-website):
| Authored (edit here) | Generated (do not hand-edit) |
|---|---|
getting-started/, fundamentals/, guides/, ecosystem/, blog/ | Package trees under packages/ |
Package pages are copies from the framework. Destinations follow src/data/packages.json hrefs (the same URLs as the sidebar). Re-run scripts/sync-readmes.py / scripts/generate-api.py — do not invent hub paths.
5. Recipes that will pass CI
Section titled “5. Recipes that will pass CI”Install and scaffold:
uv add "quadkit-cli>=0.0.42,<0.1"quadkit new project my-app --template web-apicd my-appquadkit runAdd surface area with the CLI, then fill in behavior:
quadkit gen controller users # src/my_app/controllers/…quadkit gen service greetings # src/my_app/services/…quadkit gen error not_found # src/my_app/shared/errors/… (always shared)quadkit new module auth # src/my_app/modules/auth/{protocols,provider,services}quadkit gen controller users --module authA provider binds in register() and resolves in boot(). They take different protocols:
from quadkit.di.provider import Providerfrom quadkit.contracts.core import ProviderPriorityfrom quadkit.contracts.core.di import ( ContainerRegistrarProtocol, ContainerResolverProtocol,)
class BillingProvider(Provider): name = "billing" priority = ProviderPriority.DOMAIN
async def register(self, container: ContainerRegistrarProtocol) -> None: from my_app.services.billing_service import BillingService
container.singleton(BillingService, BillingService)
async def boot(self, container: ContainerResolverProtocol) -> None: billing = await container.resolve(BillingService) await billing.warmup()App-root providers land in src/<app>/di/. Do not put business logic on the provider class.
Domain failures are values. Infrastructure failures raise:
from quadkit.result import Result, Ok, Err
async def find_user(self, user_id: str) -> Result[User, DomainError]: user = await self.repo.get(user_id) if not user: return Err(UserNotFound(user_id)) return Ok(user)Never unwrap() without is_ok(). Never return Result from __init__ or lifecycle hooks. Never wrap a dead database in Err.
6. Copy the generator output, not a vibe
Section titled “6. Copy the generator output, not a vibe”quadkit new project boots the real Application — real DI, real providers. Read what it writes before writing anything else:
quadkit new project my-app --template fullstackfind my-app/src -name '*.py' | sort| Need | Start from |
|---|---|
| A route with request validation | quadkit gen controller <name> |
| A service behind a protocol | quadkit gen service <name> |
| A typed domain error | quadkit gen error <name> |
| A bounded context with its own protocols | quadkit new module <slug> |
| A test that boots the app | quadkit-testing’s TestEnvironment — see Testing |
If the generated app does not look like what you were about to write — application.yaml, module.py / app.py as composition root, a provider that only registers, services that return Result[T, E] — it will not look like QuadKit.
7. When you answer or generate
Section titled “7. When you answer or generate”- Cite packages with the
quadkit-prefix (quadkit-web, not “the web layer”). - Talk to the protocol in contracts, not the class in the extension.
- Link the matching page on oridecon.dev.
- FastAPI users are not starting over: keep Starlette routing and Pydantic; add a composition root. See Coming from FastAPI and the migration guide.
- Pin
>=0.0.42,<0.1(current release 0.0.42). Alpha means the surface can move; the architecture laws will not. Changelog: 0.0.4. - Never reference an unpublished package as if it were installable — that is the fastest way to produce code that cannot run.
The never-list lives in /agents.md: no service locator, no module-level singletons, no Any on injected constructors, no mocks in src/, no if/elif type dispatch.
Next steps
Section titled “Next steps”- Installation —
uv add quadkit-cli - Your First App — scaffold,
quadkit run, add a route - Project Structure — the tree generators write
- Core Concepts — Provider, DI, Result, modules
- Common mistakes — fail vs fix
/SKILL.md— drop-in agent skill