Skip to content
Packages Examples Agents Blog Get started

For coding agents

This is the operating manual for generating Oridecon code. Humans can skip it and start at Your First App.

  1. /llms.txt — identity, install, rules, every docs URL.
  2. /agents.md — distilled framework AGENTS.md: hierarchy, Result vs exceptions, provider lifecycle, the never-list.
  3. /SKILL.md — drop-in skill for Claude Code, Cursor, OpenCode (same rules, fetchable without another clone).
  4. /llms-full.txt — the same catalog plus architecture notes. Per-package: /llms/index.txt (example /llms/oridecon-sql.txt).
  5. This page — repo map, the one project tree, recipes. Then Common mistakes for fail vs fix.
  6. One example that already boots Application.
  7. The protocol in oridecon-contracts, not a concrete class in an extension.

Installable pack: oridecon-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 in the framework repo (branch dev): AGENTS.md.

oridecon-contracts Zero dependencies. Protocols, types, exceptions only.
oridecon Depends ONLY on oridecon-contracts.
oridecon-* Extension packages. Never import each other.

If two or more packages need the same type, protocol, or exception, it lives in oridecon-contracts. No exceptions.

oridecon-ai-* packages follow the same law. They do not import each other or oridecon-ai. The orchestrator discovers them through entry points. Shared value types (ChatMessage, Document, SearchResult) already live in contracts — do not invent a second copy.

Documented exceptions (deps must be in pyproject.toml):

PackageMay import
oridecon-adminauth, cache, features, resilience, ui
oridecon-ai (orchestrator)oridecon-ai-*, oridecon-vector
oridecon-multimediaoridecon-multimedia-*
oridecon-testingany extension

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

There is one layout. Templates (minimal, api, web-api, graphql, worker, full) add packages and application.yaml sections — not a second shape. There is no --structure flag and no models/ directory.

KindWhere it lives
Composition rootsrc/<app>/app.pycreate_app(), ASGI target <app>.app:app
Unscoped HTTPsrc/<app>/controllers/
Unscoped domain typessrc/<app>/domains/
App providerssrc/<app>/di/*_provider.py
Module providersrc/<app>/modules/<slug>/provider.py
Cross-cuttingsrc/<app>/shared/ (errors, middleware, health, …)
Bounded contextsrc/<app>/modules/<slug>/ after oridecon new module
src/<app>/
├── app.py # create_app() — never list controllers by hand
├── controllers/
├── domains/
├── di/ # app-root providers
├── services/
├── infrastructure/ # db, cache, events
├── shared/
└── modules/
├── __init__.py # empty until oridecon 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 oridecon gen controller does not require editing app.py. Living examples such as support-agent and auth-web may pass controllers=[…] instead — copy that only when matching the example. Adding a module lists it in create_app() next to WebModule.

Full generator map: Project Structure and CLI PROJECT_LAYOUT.

4. Repo map (dbtinoy-/oridecon, branch dev)

Section titled “4. Repo map (dbtinoy-/oridecon, branch dev)”
PathWhat it is
AGENTS.mdCanonical agent rules
examples/<slug>/Gated apps + example-hub — copy these
core/orideconApplication, container, providers, modules, Result, config
core/oridecon-contractsProtocols, types, exceptions — zero deps
packages/oridecon-*Web, SQL, auth, queue, …
experimental/ai/oridecon-ai*AI platform
experimental/multimedia/oridecon-multimedia*Multimedia
experimental/apps/oridecon-{cli,admin,ui}Tooling (experimental except testing)

This docs site (oridecon-docs):

Authored (edit here)Generated (do not hand-edit)
getting-started/, fundamentals/, guides/, ecosystem/, examples/, blog/Package trees under packages/, platform/, experimental/

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 like packages/web/.

Install and scaffold:

Terminal window
uv add "oridecon-cli>=0.1,<0.2"
oridecon new project my-app --template web-api
cd my-app
oridecon run

Add surface area with the CLI, then fill in behavior:

Terminal window
oridecon gen controller users # src/my_app/controllers/…
oridecon gen service greetings # src/my_app/services/…
oridecon gen error not_found # src/my_app/shared/errors/… (always shared)
oridecon new module auth # src/my_app/modules/auth/{protocols,provider,services}
oridecon gen controller users --module auth

A provider binds in register() and resolves in boot(). They take different protocols:

from oridecon.di.provider import Provider
from oridecon.contracts.core import ProviderPriority
from oridecon.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 oridecon.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.

Each example boots the real Application — real DI, real providers, in-memory backends.

NeedCopy
Controller + SQL in the repositoryexamples/sql-repository
Cookie + JWT sessionexamples/auth-web
Agent loop with tools on the containerexamples/support-agent
Module boundary + RBACexamples/auth-rbac
Terminal window
PYTHONPATH=examples/support-agent/src uv run python -m support_agent
make test-examples

Catalog: Examples. Source: github.com/dbtinoy-/oridecon/tree/dev/examples.

If the generated app does not look like an example — 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 Oridecon.

  • Cite packages with the oridecon- prefix (oridecon-sql, not “the SQL helper”).
  • 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.1,<0.2 (public release 0.1.1). Alpha means the surface can move; the four architecture laws will not. Changelog: 0.1.1.

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.