---
name: quadkit
description: Build and edit QuadKit Python apps — providers, controllers, AI agents, workflows, 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.50).
Pin `>=0.0.50,<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 (18): `quadkit`, `quadkit-contracts`, `quadkit-web`, `quadkit-http`, `quadkit-ai-llm`, `quadkit-ai-agents`, `quadkit-ai-mcp`, `quadkit-sql`, `quadkit-cache`, `quadkit-storage`, `quadkit-events`, `quadkit-notification`, `quadkit-tasks`, `quadkit-workflow`, `quadkit-monitor`, `quadkit-auth`, `quadkit-cli`, `quadkit-testing`.

## 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.50,<0.1"
quadkit new project my-app --template api
cd my-app && quadkit run
```

Add currently published extensions as needed:

```bash
quadkit add web                                        # write the web: config section (api starter includes quadkit-web)
uv add "quadkit-sql[postgres]" quadkit-cache          # Async SQL repositories & Redis/memory cache
uv add quadkit-events quadkit-tasks quadkit-auth quadkit-monitor
uv add quadkit-ai-llm quadkit-ai-agents quadkit-ai-mcp quadkit-workflow quadkit-notification  # AI, MCP, workflows, notifications
uv add --dev quadkit-testing                          # Harnesses, fakes, compliance suites
```

## Package boundaries

Shared contracts live in `quadkit-contracts` (which depends on `typing-extensions`). Core, foundational packages, regular extensions, and composite packages have distinct boundary rules. Regular extension peers are checked for independence, with tracked exceptions; composite packages and AI/multimedia orchestrators have separate contracts.

Prefer shared protocols in `quadkit-contracts` when packages need the same interface. Do not assume every cross-package import is forbidden. The exact rules live in the repository's root `.importlinter`; run them with `make lint-boundaries`.

## One project tree

The CLI currently ships one `api` starter. Add installed packages with `quadkit add`; the canonical tree stays the same. No `--structure` flag or `models/` directory.

```
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(BootContainerProtocol)` 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).
- Undocumented cross-package imports that violate the configured package-boundary rules.
- `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.DOMAIN

    async def register(self, container: ContainerRegistrarProtocol) -> None:
        container.singleton(PaymentGatewayProtocol, StripeGateway)
        container.singleton(InvoiceRepositoryProtocol, SqlInvoiceRepository)
        container.singleton(PaymentServiceProtocol, PaymentService)

    async def boot(self, container: BootContainerProtocol) -> None:
        service = await container.resolve(PaymentServiceProtocol)
        await service.warmup()
```

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 (`quadkit-skills`)

Install all 10 skills: `npx skills add dbtinoy-/quadkit-skills` (source: https://github.com/dbtinoy-/quadkit-skills).
One-file fetch fallback when you cannot clone: `/SKILL.md`.

### Foundation & CLI

- `quadkit-framework` (quadkit, quadkit-contracts, quadkit-cli) — 18-package catalog (all published at 0.0.50), one-tree app layout (domains/, di/), quadkit new / quadkit gen / quadkit run, skill routing
- `quadkit-contracts` (quadkit-contracts) — @runtime_checkable Protocols, frozen boundary DTOs, StrEnum, QuadkitError, and QK_ERR_* error codes
- `quadkit-di` (quadkit) — ContainerRegistrarProtocol vs BootContainerProtocol, Provider, ProviderPriority, @module(), DynamicModule, scopes, ambient clock/identity/hashing

### Web, AI, Workflows & Data

- `quadkit-web` (quadkit-web, quadkit-http) — Controller routes, body/query/path params, WebModule.configure(discover=[...]), Result -> ProblemDetail, middleware, SSE/WebSocket, HTTPClient
- `quadkit-agents` (quadkit-ai-llm, quadkit-ai-agents, quadkit-ai-mcp) — LLMModule, streaming & structured extraction, AgentsModule tools/strategies/crews, MCPModule server & MCPClient
- `quadkit-workflows` (quadkit-workflow, quadkit-events, quadkit-tasks, quadkit-notification) — DAGs, state machines, sagas, GateNode HITL, CQRS buses & event sourcing, background workers/cron, SMS/push/email/inbox delivery
- `quadkit-data` (quadkit-sql, quadkit-cache, quadkit-storage, quadkit-auth, quadkit-monitor) — Async SQL repositories/UoW, Redis/memory cache, S3/GCS/Azure/local blob stores, JWT/OAuth2/RBAC auth, Prometheus/OTel/health probes

### Style, Testing & Packaging

- `quadkit-style` (quadkit) — Python 3.11+ typing, zero Any on boundaries, Result[T, E] (Ok/Err) vs exceptions, structured get_logger, async task tracking, registry dispatch
- `quadkit-testing` (quadkit-testing) — Async pytest, assert_ok/assert_err_type, QuadkitContainerHarness, AppTestBed, WebTestBed, FixedClock, fakes, contract compliance suites
- `quadkit-package` (quadkit-* extensions) — Authoring quadkit-* distributions: src/quadkit/<name>/ implicit namespace, pkgutil.extend_path, module.py, py.typed, hatchling pyproject.toml

Both the skill pack and this file enforce the same layout: `domains/` (not `models/`), app-root `di/`, cross-cutting `shared/`.

## 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/.
