---
name: quadkit
description: Build and edit QuadKit Python apps — providers, controllers, Result types, modules, and quadkit-cli scaffolds. Use when the user mentions QuadKit, quadkit-*, oridecon.dev, or an import linter on quadkit packages.
---

# QuadKit

Async-first, contract-driven Python application framework (3.11+, Apache 2.0, alpha 0.0.x, public release 0.0.42).
Pin `>=0.0.42,<0.1`. Prefer protocols over concrete classes.

Canonical human docs: https://oridecon.dev/
Machine indexes: https://oridecon.dev/llms.txt · https://oridecon.dev/llms-full.txt · https://oridecon.dev/llms/index.txt
Agent rules: https://oridecon.dev/agents.md
Playbook: https://oridecon.dev/quadkit/getting-started/for-coding-agents/
Mistakes (fail vs fix): https://oridecon.dev/quadkit/getting-started/common-mistakes/
Changelog: https://oridecon.dev/quadkit/changelog/
Framework (branch `main`): https://github.com/dbtinoy-/quadkit
Installable skill pack: https://github.com/dbtinoy-/quadkit-skills
This file is the one-URL umbrella (https://oridecon.dev/SKILL.md). Per-task skills live in the pack.

Published packages: `quadkit`, `quadkit-contracts`, `quadkit-web`, `quadkit-cli`, `quadkit-testing`. Nothing else is installable.

## Read order

1. https://oridecon.dev/llms.txt
2. https://oridecon.dev/agents.md
3. https://oridecon.dev/quadkit/getting-started/for-coding-agents/
4. https://oridecon.dev/quadkit/getting-started/common-mistakes/
5. The package page for whatever you are touching: https://oridecon.dev/quadkit/packages/
6. Talk to the protocol in `quadkit-contracts`, not a concrete class in an extension.

Per-package machine indexes: https://oridecon.dev/llms/<package>.txt (example: https://oridecon.dev/llms/quadkit-web.txt).
Install the pack: https://oridecon.dev/quadkit/getting-started/agent-skills/

## Install

```bash
uv add "quadkit-cli>=0.0.42,<0.1"
quadkit new project my-app --template web-api
cd my-app && quadkit run
```

Add packages only when needed: `quadkit-web` for HTTP, `quadkit-testing` (dev) for tests.

## Hierarchy (blocking)

```
quadkit-contracts    Zero deps. Protocols, types, exceptions only.
    ↑
quadkit              Depends ONLY on quadkit-contracts.
    ↑
quadkit-*            Never import each other.
```

Golden rule: if two packages need the same type, protocol, or exception, it lives in `quadkit-contracts`. No exceptions.

Documented exception (must be in pyproject.toml): `quadkit-testing` may import any extension — it is a dev dependency.

An import linter enforces this. A cross-extension import that type-checks still fails CI.

## One project tree

No `--structure` flag. No `models/` directory. Templates add packages, not a second shape.

```
src/<app>/
  app.py                 # create_app() — composition root; ASGI <app>.app:app
  controllers/           # unscoped HTTP, auto-discovered
  domains/               # unscoped domain types
  di/                    # app providers (*_provider.py)
  services/
  infrastructure/
  shared/                # errors, middleware, health
  modules/<slug>/        # quadkit new module — protocols.py, provider.py, domains/
```

Teach `WebModule.configure(discover=["<app>.controllers", "<app>.modules"])` so `quadkit gen controller` works without editing `app.py`. Adding a module lists it in `create_app()` next to `WebModule`.

## Always

- Constructor injection. Type-hint the protocol, not `Any`.
- `register(ContainerRegistrarProtocol)` binds. `boot(ContainerResolverProtocol)` resolves. They never mix.
- `Result[T, E]` for expected domain failures. Exceptions for infrastructure failures.
- Registry dispatch. Empty `__init__`, `with_defaults()` classmethod.
- Absolute imports. `from __future__ import annotations`.
- Async I/O. Store `asyncio.create_task()` references.
- `from quadkit.logging import get_logger`. Core config key is `json_format`. Never `print()`.

## Never

- Service locator (passing the container into services).
- Direct cross-extension imports (`quadkit-web` → `quadkit-cli`).
- `result.unwrap()` without `is_ok()`.
- `Result` from constructors or lifecycle hooks.
- Module-level singletons. Mocks in production `src/`.
- A `models/` directory. Use `domains/`.
- Business logic on Provider classes.

## Result

```python
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)

result = await service.find_user("123")
if result.is_ok():
    user = result.unwrap()
else:
    error = result.unwrap_err()
```

## Provider

```python
from quadkit.di.provider import Provider
from quadkit.contracts.core import ProviderPriority
from quadkit.contracts.core.di import ContainerRegistrarProtocol, BootContainerProtocol

class BillingProvider(Provider):
    name = "billing"
    priority = ProviderPriority.APPLICATION

    async def register(self, container: ContainerRegistrarProtocol) -> None:
        container.singleton(PaymentGateway, StripeGateway(cfg.stripe_key))
        container.singleton(InvoiceRepository, InvoiceRepository(cfg.db_url))

    async def boot(self, container: BootContainerProtocol) -> None:
        gateway = await container.resolve(PaymentGateway)
        repo = await container.resolve(InvoiceRepository)
        container.singleton(PaymentService, PaymentService(gateway, repo))
```

App-root providers: `src/<app>/di/*_provider.py`. Module providers: `modules/<slug>/provider.py`.

## Recipes

```bash
quadkit gen controller users
quadkit gen service greetings
quadkit gen error not_found          # always shared/
quadkit new module auth
quadkit gen controller users --module auth
```

Tests boot the real application: https://oridecon.dev/quadkit/guides/testing/

## Agent skill pack\n\nInstallable markdown skills (not a PyPI package): https://github.com/dbtinoy-/quadkit-skills\nOne-file fetch if you cannot clone: this site's /SKILL.md.\n\n### CLI\n\n- `cli-project-scaffolding` — new project, new module, init, add\n- `cli-code-generation` — gen controllers, services, errors\n- `cli-config-and-inspect` — view config, inspect runtime, diagnose\n\n### Core\n\n- `creating-providers-and-modules` — providers, modules, DI container\n- `configuration-management` — YAML config, env vars, profiles\n- `using-result-and-error-codes` — Result[T, E], error codes, exceptions\n\n### Web\n\n- `web-controllers-and-routing` — HTTP controllers, middleware, OpenAPI\n- `real-time-web` — SSE, WebSocket, EventChannel\n\n### Testing\n\n- `testing-with-quadkit` — TestEnvironment, stubs, fakes\n
If a packed skill and this file disagree on layout, this file wins: `domains/` (not `models/`), app-root `di/`.

## When writing code

- Cite packages with the `quadkit-` prefix.
- Link https://oridecon.dev/ pages.
- FastAPI users keep Starlette routing and Pydantic; add a composition root. See https://oridecon.dev/quadkit/guides/migrating-from-fastapi/.
- Fail vs fix: https://oridecon.dev/quadkit/getting-started/common-mistakes/.
