Forge framework contract crate: ModuleIdentity, EntitySpec, Engine traits + core types. Light deps so plugin authors can implement modules without pulling runtime baggage.
| Tier | application |
| Role | unclassified (baselined) |
| Path | crates/application/core |
| Edition | 2021 |
| Targets | application_core |
| Public items | 19 across 8 modules |
| Tests | 4 |
What it is for
application-core — the Forge framework contract crate.
Holds the contract only — no runtime machinery:
ModuleIdentity— object-safe core trait every forge module
implements. Migrations + permission codes + name/version. The generic routing extension ModuleRouter<S> lives in application-engine (kept out of this crate so plugin authors aren't forced to pull the runtime stack).
- Legacy
Enginetrait (kept for backward compat through sprint
55 only — deleted with forge-control at sprint end).
Vertical,ThemeBundle,I18nBundle,SeedEntity
types — vertical recipe metadata.
MigrationSet,MigrationError— types shared between
ModuleIdentity::migrations() and application-engine's migration runner.
Why no EntitySpec
Earlier sprint plans included an EntitySpec + HasFields + HasPermissions + HasAuditHooks + HasSearch trait surface intended as the entry point for auto-generated admin UI (the Frappe DocType pattern). That direction was killed: runtime metadata interpretation is the wrong model for a compile-time Rust framework, and Frappe's specific failure modes — slow, hard to understand, hard to install, plugin compatibility drift — are exactly what this framework is designed to avoid.
Each forge module hand-writes its own UI components (Askama partials or Leptos components) when it needs them. Permission slugs are declared explicitly in ModuleIdentity::permission_codes(). Audit hooks and search indexing live in module service code, not in trait metadata.
Dependency policy
"Pragmatic zero-dep" per Sprint 55 Gate 1.5 Round 3:
sqlxaccepted because all current/planned engines target Postgres.axumaccepted because modules ship routes;HandlerRegistration
abstraction was rejected as premature.
- NOT included: tokio runtime, tower middleware, tower-sessions,
platform-customer-ui, platform-staff-ui, platform-leptos-components.
Capabilities
api_error (other)
Shared error-response helper — emits the canonical
| Item |
|---|
fn api_error(code : ErrorCode, message : impl Into <String>) -> Response |
fn api_error_with_details(code : ErrorCode, message : impl Into <String>, details : impl Into <String>,) -> Response |
fn api_error_with_status(status : StatusCode, code : ErrorCode, message : impl Into <String>,) -> Response |
Engine
Legacy Engine trait — kept for backward compat during Sprint
| Item |
|---|
pub trait Engine |
migrate (other)
Shared migration types: MigrationSet descriptor + MigrationError.
| Item |
|---|
fn created_tables(sql : & str) -> Vec <String> |
fn migration_digest(sql : & str) -> String |
MigrationError
Shared migration types: MigrationSet descriptor + MigrationError.
| Item |
|---|
pub enum MigrationError |
async fn apply_module_migrations(pool : & PgPool, module : & str, files : & (& str, & str),) -> Result <u32, MigrationError> |
MigrationSet
Shared migration types: MigrationSet descriptor + MigrationError.
| Item |
|---|
pub struct MigrationSet |
ModuleIdentity
ModuleIdentity — the object-safe core of the Module trait.
| Item |
|---|
pub trait ModuleIdentity |
ModuleRouter
ModuleRouter — generic routing extension on ModuleIdentity.
| Item |
|---|
pub trait ModuleRouter<S : Clone + Send + Sync + 'static> |
SiteConfig
Shared site-configuration view — Sprint 65 (Gate 1.5 Q2).
| Item |
|---|
pub struct SiteConfig |
SiteConfig :: fn default() -> Self |
sql_invariants (other)
Shared SQL invariant helpers — reusable Postgres trigger functions + a
| Item |
|---|
async fn ensure_sql_invariants(pool : & PgPool) -> Result <(), MigrationError> |
I18nBundle
Vertical — per-vertical recipe: engines + module selection +
| Item |
|---|
pub struct I18nBundle |
SeedEntity
Vertical — per-vertical recipe: engines + module selection +
| Item |
|---|
pub enum SeedEntity |
ThemeBundle
Vertical — per-vertical recipe: engines + module selection +
| Item |
|---|
pub struct ThemeBundle |
Vertical
Vertical — per-vertical recipe: engines + module selection +
| Item |
|---|
pub struct Vertical |
VerticalError
Vertical — per-vertical recipe: engines + module selection +
| Item |
|---|
pub enum VerticalError |
How to use it
No examples/ target and no doctest in this crate's rustdoc. The tests listed under Verification are the closest executable usage.
Module structure
application_core
api_errorenginemigratemodulemodule_routersite_configsql_invariantsvertical
flowchart TD n_application_core["application_core"] n_application_core --> n_api_error["api_error"] n_application_core --> n_engine["engine"] n_application_core --> n_migrate["migrate"] n_application_core --> n_module["module"] n_application_core --> n_module_router["module_router"] n_application_core --> n_site_config["site_config"] n_application_core --> n_sql_invariants["sql_invariants"] n_application_core --> n_vertical["vertical"]
Public surface
`api_error`
| Item | What it is |
|---|---|
fn api_error(code : ErrorCode, message : impl Into <String>) -> Response | Build an error response from an ErrorCode + human message |
fn api_error_with_details(code : ErrorCode, message : impl Into <String>, details : impl Into <String>,) -> Response | Same as api_error but with a details payload attached. |
fn api_error_with_status(status : StatusCode, code : ErrorCode, message : impl Into <String>,) -> Response | Build an error response with an explicit status override |
`engine`
| Item | What it is |
|---|---|
pub trait Engine | A domain engine that forge-control can wire into an app |
`migrate`
| Item | What it is |
|---|---|
pub enum MigrationError | Errors that can occur during migration execution. |
fn created_tables(sql : & str) -> Vec <String> | Table names a migration file creates (CREATE TABLE IF NOT EXISTS), in file order |
fn migration_digest(sql : & str) -> String | The SHA-256 of a migration's exact bytes, hex-encoded |
async fn apply_module_migrations(pool : & PgPool, module : & str, files : & (& str, & str),) -> Result <u32, MigrationError> | Idempotently apply a module's OWN SQL migration files, tracked in a per-module ledger (forge_module_migrations, keyed by (module, filename)) |
pub struct MigrationSet | Descriptive migration set — one row per primitive/module crate that ships migrations |
`module`
| Item | What it is |
|---|---|
pub trait ModuleIdentity | Object-safe contract every forge module implements |
`module_router`
| Item | What it is |
|---|---|
pub trait ModuleRouter<S : Clone + Send + Sync + 'static> | Route registration capability layered on top of ModuleIdentity |
`site_config`
| Item | What it is |
|---|---|
pub struct SiteConfig | Site-wide configuration a consumer exposes to module routers |
SiteConfig :: fn default() -> Self | — |
`sql_invariants`
| Item | What it is |
|---|---|
async fn ensure_sql_invariants(pool : & PgPool) -> Result <(), MigrationError> | Install (idempotently) the shared invariant trigger functions + the forge_counters allocator into pool's database |
`vertical`
| Item | What it is |
|---|---|
pub enum VerticalError | Errors raised when resolving or applying a vertical recipe. |
pub struct Vertical | — |
pub struct ThemeBundle | Theme overrides supplied by a vertical recipe. |
pub struct I18nBundle | i18n configuration supplied by a vertical recipe. |
pub enum SeedEntity | Typed seed entity per D-35C.1.Q3 (strongly-typed enum over schema-aware variants beats raw SQL strings or boxed closures) |
Re-exports. Exported here, defined elsewhere.
| Export | Defined in |
|---|---|
Engine | engine::Engine |
ModuleIdentity | module::ModuleIdentity |
ModuleRouter | module_router::ModuleRouter |
SiteConfig | site_config::SiteConfig |
ensure_sql_invariants | sql_invariants::ensure_sql_invariants |
{I18nBundle,SeedEntity,ThemeBundle,Vertical,VerticalError} | vertical::{I18nBundle,SeedEntity,ThemeBundle,Vertical,VerticalError} |
{api_error,api_error_with_details,api_error_with_status} | api_error::{api_error,api_error_with_details,api_error_with_status} |
{apply_module_migrations,MigrationError,MigrationSet} | migrate::{apply_module_migrations,MigrationError,MigrationSet} |
Boundary
Reaches into foundation, platform.
Shares tier application with 120 other crates: application-agreements, application-ai, application-analytics, application-approvals, application-assessments, application-audit-log, application-auth, application-billing, … (120 total).
_What this crate deliberately does NOT own is a judgment. No committed registry records one for it, so none is stated here._
Where it sits
| Tier (ontology) | application |
| Architectural role (taxonomy) | unclassified (baselined) |
| Location | crates/application/core |
| Vocabulary in force (lexicon) | current |
Tier flow. Which tiers this crate's own edges cross.
flowchart LR n_application["application"] --> n_foundation["foundation"] n_application["application"] --> n_platform["platform"]
Dependencies
Runtime, in this workspace.
| Crate | Tier | Optional | Only on |
|---|---|---|---|
| `foundation-audit-log` | foundation | no | always |
| `platform-dto` | platform | no | always |
Runtime, from outside the workspace.
| Crate | Requirement | Features | Optional | Only on |
|---|---|---|---|---|
async-trait | ^0.1 | — | no | always |
axum | ^0.7 | multipart | no | always |
chrono | ^0.4 | serde | no | always |
serde | ^1 | derive | no | always |
serde_json | ^1 | — | no | always |
sqlx | ^0.8 | runtime-tokio, postgres, chrono, uuid, json | no | always |
thiserror | ^2 | — | no | always |
uuid | ^1 | v4, v7, serde, js | no | always |
Development. None.
Build. None.
Depended on by. 118 workspace crates.
Signal flow — what reaches this crate, and what it reaches.
flowchart LR consumers["118 consumer crates"] -->|uses| SELF SELF["application-core"] SELF -->|runtime| runtime_deps["2 workspace crates"] classDef self fill:#1f883d,stroke:#1f883d,color:#fff; class SELF self;
Feature flags
No Cargo features are defined: every capability is unconditional, so no consumer can receive a half-wired crate.
Targets
| Kind | Name | Source |
|---|---|---|
| lib | application_core | `src/lib.rs` |
Error model
| Error type | Named by |
|---|---|
MigrationError | apply_module_migrations, ensure_sql_invariants |
VerticalError | declared, no public signature returns it |
Operational characteristics
| Property | Evidence |
|---|---|
| async public surface | yes |
| async runtime | none detected |
| database access | yes |
| network I/O | yes |
| unsafe code | none detected |
| environment variables | none detected |
No unsafe block, unsafe fn, unsafe impl or unsafe trait was found by the parser anywhere in this crate's source.
Configuration
No environment variable is read with a literal name anywhere in this crate. A variable whose key is computed at run time cannot be listed here, and is not claimed to be absent.
Related capabilities
118 workspace crates depend on this one: application-agreements, application-ai, application-analytics, application-approvals, application-assessments, application-audit-log, application-auth, application-billing, application-browser-automation, application-browser-automation-wasm, application-browser-cdp, application-cache, … (118 total).
Verification
| Kind | Count |
|---|---|
| Unit tests | 4 |
| Integration tests | 0 |
| Examples | 0 |
| Doctests | 0 |
Evidence by module. How often each public module is named by something executable.
| Module | Tests | Examples | Consumers |
|---|---|---|---|
api_error | 3 | 0 | 22 |
engine | 1 | 0 | 16 |
migrate | 5 | 0 | 284 |
module | 1 | 0 | 113 |
module_router | 1 | 0 | 4 |
site_config | 1 | 0 | 1 |
sql_invariants | 1 | 0 | 0 |
vertical | 5 | 0 | 7 |
What the tests establish, by name:
a_file_creating_nothing_names_no_tables—src/migrate.rsa_migration_batch_carries_only_the_file_and_its_ledger_row—src/migrate.rscreated_tables_finds_bare_and_if_not_exists_forms—src/migrate.rscreated_tables_ignores_mentions_inside_comments_and_strings—src/migrate.rs
Documentation coverage
| Measure | Documented | Total |
|---|---|---|
| Public items with rustdoc | 17 | 19 |
Public modules with a //! block | 8 | 8 |
pie showData
title Public items with rustdoc
"Documented" : 17
"No rustdoc detected" : 2
Metrics
| Metric | Value |
|---|---|
| Rust source files | 9 |
| Source lines | 897 |
| Code lines | 464 |
| Public API items | 19 |
| Public modules | 8 |
| Tests | 4 |
| Examples | 0 |
| Cargo features | 0 |
| Direct runtime dependencies | 10 |
| Workspace reverse dependencies | 118 |
pie showData
title Public API by kind
"enum" : 3
"function" : 7
"method" : 1
"struct" : 5
"trait" : 3
pie showData
title Rust source composition
"Code" : 464
"Blank or comment" : 433
Generation
Rendered by tools-corpus corpus readme from repository evidence alone, renderer schema 2, lexicon current. No model, network service or database was consulted. Regenerate with tools-corpus corpus readme --write; verify with --check.