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

Sharing and promotion

On this page

A type, failure or helper used by one file lives in that file. When a second file needs it, it moves to a file of its own, a step called promotion:

  • When a second file in the same domain uses it, it moves to the domain's types/, failures/ or libraries/.
  • When a second domain uses it, it moves to shared/. Only pure types, failures, libraries and messages go there; a message moves to shared/translations/.

Moving to the right folder isn't enough: the file must be named after the declaration. DiscountCode in types/basket.jadpo, used by another file, must move to types/discount_code.jadpo.

Tests never count as users. A route counts only when nothing else uses the type, and a failure counts where it is raised.

A constant (Constants) is placed as a helper function is. Used by one file, it sits at the top of that file, never as a primary declaration. Used by a second, it moves into a library and is read through it, as in pricing.vat_rate, so a library holds functions and constants.

What shared/ holds besides

  • shared/emails/ holds only the app's optional email layout, layout.html and layout.txt, and the email partials every domain's emails may include, each a type X = EmailPartial with its templates beside it (Email).
  • shared/documents/ holds only the app's optional document layout, layout.html.
  • shared/providers/ holds the app's own integrations, each in shared/providers/<name>/<version>/, in exactly the package format (The package format). They aren't promoted there: they are written there from the start.

How a promotion happens

Promotion is required. The compiler reports it as layout.promotion_required with a one-step fix, applied through the editor or jadpo fmt --layout, never silently on save.

Demotion is never forced. A promoted declaration stays where it is until nothing uses it, when it becomes a dead-code error. So code doesn't move back and forth, git blame keeps working and moves don't cause merge conflicts.

Promotion is a move, not a rewrite. The library keeps its name, so calls such as rounding.half_even(x) and the function's text stay identical, and behaviour can't change.

Promoting a private function or type to shared/ makes its name unique across the project, so a private one of the same name in another domain then clashes, as semantic.duplicate_declaration.

The move is also recorded in the identity registry, database/identities.jadpo_record: the committed file that gives every table, column and declaration a lasting identity, so a rename reads as a rename (Stable identity). The identity key of a function or type declared in a domain includes the domain, so the move writes one line there, and it reads as a move, not as a removal and an addition. layout.promotion_required's edits for a type moving to shared/types/ add that line, so jadpo fmt --layout writes it. A failure raised by a second domain gets no edit, so moving it by hand writes no line. A library keeps its name in shared/, so its functions' keys need none.

Helpers that depend on a domain

A helper that depends on one domain's types (order_math.total(order)) stays in that domain, and becomes public when another domain calls it. A helper that depends on two domains' types is an error that needs a design decision. Making a helper generic, for example extracting pricing.sum(amounts) for ordering and payments to share, is a deliberate refactor.

Finding the helper that already exists

Two constants of the same type holding the same value are the declinable constant.similar_exists, so parallel agents each inventing vat_rate find each other's (Constants).

Duplicate helpers are caught as they are written. When a new helper looks like an existing one, jadpo check says so and names it ("a similar library exists: rounding.half_even in shared/libraries/rounding.jadpo"). The compiler also generates a catalogue of every library function, with its signature, documentation comment and callers, and the Jadpo agent skill tells agents to consult it before writing a helper. Agents look things up in the compiler's project map, the language server, rather than browsing folders.

Placement alone can't prevent two copies: two domains may each hold a private helper with the same name.

A function's description is written once, as a /// comment above it. The catalogue, the list of callers and the domains that use it are computed and shown in the editor, never written into source files.

Seeing a change's reach

Before shared or public code changes, its reach shows in three ways: find-references in the editor; a jadpo impact <name> command listing the calling domains, the entry points and the tests that cover it; and jadpo check itself, which reports the domains a change affects and runs their tests.

Not built yet: jadpo impact.

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