For coding agents
This is the operating manual for generating Oridecon 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/oridecon-sql.txt).- This page — repo map, the one project tree, recipes. Then Common mistakes for fail vs fix.
- One example that already boots
Application. - 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.
2. The hierarchy is the whole design
Section titled “2. The hierarchy is the whole design”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):
| Package | May import |
|---|---|
oridecon-admin | auth, cache, features, resilience, ui |
oridecon-ai (orchestrator) | oridecon-ai-*, oridecon-vector |
oridecon-multimedia | oridecon-multimedia-* |
oridecon-testing | any extension |
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) 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 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)”| Path | What it is |
|---|---|
AGENTS.md | Canonical agent rules |
examples/<slug>/ | Gated apps + example-hub — copy these |
core/oridecon | Application, container, providers, modules, Result, config |
core/oridecon-contracts | Protocols, 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/.
5. Recipes that will pass CI
Section titled “5. Recipes that will pass CI”Install and scaffold:
uv add "oridecon-cli>=0.1,<0.2"oridecon new project my-app --template web-apicd my-apporidecon runAdd surface area with the CLI, then fill in behavior:
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 authA provider binds in register() and resolves in boot(). They take different protocols:
from oridecon.di.provider import Providerfrom oridecon.contracts.core import ProviderPriorityfrom 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.
6. Copy an example, not a vibe
Section titled “6. Copy an example, not a vibe”Each example boots the real Application — real DI, real providers, in-memory backends.
| Need | Copy |
|---|---|
| Controller + SQL in the repository | examples/sql-repository |
| Cookie + JWT session | examples/auth-web |
| Agent loop with tools on the container | examples/support-agent |
| Module boundary + RBAC | examples/auth-rbac |
PYTHONPATH=examples/support-agent/src uv run python -m support_agentmake test-examplesCatalog: 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.
7. When you answer or generate
Section titled “7. When you answer or generate”- 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 docs.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.
Next steps
Section titled “Next steps”- Installation —
uv add oridecon-cli - Your First App — scaffold,
oridecon 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- How coding agents should read Oridecon — the same loop as a narrative