Jadpo is experimental and not ready for production. These docs follow the compiler's main branch as of 30 Oct 2026. What's built · Install 0.1.0
jadpo
Reference: Project layout and modules

Project layout and modules

Files and names

On this page

One primary declaration per file

Each file holds one primary declaration, named after it: ServiceCredential lives in service_credential.jadpo. Route groups and libraries are named declarations, so the same rule covers them.

Supporting types, failures and helpers that only that file uses live in it too. A declaration any other file uses, apart from the file's own tests, has a file of its own named after it. So every name a use line brings in is in <folder>/<name>.jadpo (see Imports).

Everything in a file besides its primary declaration is used by the primary declaration, directly or through one another. A declaration that only the file's tests use, or that nothing uses, is dead code, reported as layout.unused_declaration, as an unused shared declaration is. So a file never collects helpers for something else. A declaration another file uses is reported by layout.promotion_required, which moves it, not as unused.

The primary declaration is the one of the folder's kind named after the file, or the first of that kind when the file is misnamed. In database/seeds/ it is the seed. Test files, root files and loose files have no primary declaration, and a library's functions keep their own rule: a function nothing calls is unused.

While the primary declaration, or one it uses, writes a name that names nothing, perhaps a misspelling of the one it meant, nothing beside it is called unused, as with imports.

Order within a file

Definitions come before their use. After any opening comment and the use lines, every declaration in a file comes before the first one that uses it. So the primary declaration comes last, and you read it with the constants and types it needs already in view.

When two declarations could go in either order:

  • They go by kind: constants, then types, then failures, then helpers.
  • Within a kind, they go in the order the primary declaration first reaches them. Reaching follows the primary's names in the order it first writes them, into each declaration before the next.
  • Declarations that refer to each other keep their written order among themselves, and constants written together stay together.
  • A declaration nothing reaches (reported as layout.unused_declaration) goes after the reached ones of its kind, in written order.
  • A declaration still waits for what it uses, whatever its kind, so a constant of type Stock comes after Stock.

jadpo fmt writes this order, and jadpo fmt --check fails on a file out of it. A file with no primary declaration (a test file, a root file, a loose file) keeps its written order.

Names are unique

A name must convert to its file name and back unchanged, so a name can't have two capitals in a row: VatRate, not VATRate.

Names are unique across the whole project and across kinds, with one exception. A private function or type needs a name unique only within its domain, whether it has a file of its own or sits beside the declaration that uses it. A private constant follows the same rule (Constants). A failure or an email (type X = Failure or EmailMessage) doesn't get the exception.

Everything else stays unique across the project:

  • anything public, and anything in shared/ or the root files;
  • every other kind of declaration, whatever its visibility, since most have a name outside the code: entities (tables), failures (public error codes), events, actions, queries, routes, schedules, subscribers (stored deliveries and schedule state name them), services, fixtures and emails;
  • a type an API's record names, private or not: a route's input or output, or a type reached through their fields, is an OpenAPI schema name;
  • library names, which also qualify their functions (rounding.half_even).

A name unique across the project is unique against the per-domain ones too. A private Address in billing clashes with a public or shared Address, while two private Address types in billing and shipping don't. Two names folding to one file name (naming.folded_collision) follow the same scope. So a search for a name finds one file, or one file per domain for a private function or type.

Domain, library and standard-library names are unique among themselves, and local variables can't reuse them. Standard-library names are fixed per language version, so a new standard namespace never breaks an existing library.

A private name can be imported only inside its own domain, where it is unique, and anything importable across domains is unique across the project. So import.missing can always name the exact use line.

A domain is not a namespace: Customer means the same thing whichever folder it appears in.

There is one exception to file names being unique. A type declared in the same file as an action, query or function may fold to that callable's name, as an input does: type CreateTodo = Input beside action create_todo in actions/create_todo.jadpo. Only one file carries the name. If the type is promoted to its own file, the rule applies again.

When two names clash

A clash is semantic.duplicate_declaration. It says whether the two are a copy (use the existing one, or move it to shared/) or two things with one name (rename one). It never invents a name, and it lists the names in use that contain the same word. jadpo rename refuses a new name already in use before editing anything.

An agent working in parallel with others merges main before pushing, runs jadpo check (it builds nothing, so it is quick), fixes any clash, then pushes. Generated CI runs jadpo check first, so a clash fails in seconds (Tools for agents).

Tests and other files

Tests sit beside their subject as <name>.test.jadpo. Scenarios that span several domains go in the root tests/. The kind of test (pure, action, route, schedule) is known from what it calls, so it isn't a folder; jadpo test --kind route runs one kind.

Files that aren't Jadpo (provider contracts, email templates, search mappings) sit beside the file that uses them, and that file must refer to them.

Not built yet: jadpo rename.

Markdown for agents: /docs/files-and-names.md.Built from Jadpo main at a707efb.