Skip to content
Lenso
English
Esc
navigateopen⌘Jpreview
On this page

Contracts and Checks

Source-controlled contracts and the local gates that keep Lenso safe for agents and teams.

Lenso is agent-ready because important surfaces are explicit and checkable. The agent does not have to infer the system from conventions alone.

If you are consuming the API rather than changing it, start with API Reference.

Committed contracts

The current committed contract surface is intentionally small:

Contract Consumer
contracts/openapi/app-api.v1.yaml API clients, SDK generation, Lenso Console API expectations
contracts/errors/error-response.v1.schema.json RFC 9457 Problem Details consumers (application/problem+json)
contracts/services/lenso-service.v2.schema.json Service and provided-Module topology
contracts/services/support-grpc.v1.proto example Service-owned gRPC API

Rust route annotations and platform schemas are the source. Generated artifacts are committed so downstream users can depend on stable files.

ErrorResponse remains the committed schema name. HTTP failures use its top-level Problem Details fields; clients should not expect a nested error wrapper.

Route source of truth

HTTP routes should keep implementation and OpenAPI metadata together:

use lenso::host::http::{Json, UserActor};
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;

#[derive(Deserialize, ToSchema)]
pub struct CreateTicketRequest {
    pub title: String,
    pub requester_email: String,
}

#[derive(Serialize, ToSchema)]
pub struct TicketResponse {
    pub id: String,
    pub status: String,
}

#[utoipa::path(
    post,
    path = "/v1/support/tickets",
    operation_id = "support_create_ticket",
    tag = "support",
    request_body = CreateTicketRequest,
    responses((status = 200, body = TicketResponse))
)]
pub async fn create_ticket(
    _actor: UserActor,
    Json(request): Json<CreateTicketRequest>,
) -> Json<TicketResponse> {
    Json(TicketResponse {
        id: format!("ticket:{}", request.title),
        status: "open".to_owned(),
    })
}

The route still has to be declared in the module manifest if operators should see route metadata, story titles, and capability expectations.

Local gates

Use the smallest gate that proves the changed surface:

Change Gate
Rust code just check
OpenAPI or generated schema just generated-check
module boundary or contract layout just arch-check
release candidate just release-check
Lenso Console package work pnpm check in lenso-console
docs site work pnpm check in lenso-site

For a public release, local green is not enough. Verify the published crate, npm package, GitHub Release, or workflow artifact that users will actually install.

Architecture guardrails

just arch-check keeps the module architecture honest. It checks for things that are easy to accidentally break:

  • missing committed OpenAPI artifact
  • stale generated contract artifacts
  • missing event payload contracts for current events
  • cross-module imports inside module source code
  • DDD folder drift such as api, application, domain, or infrastructure inside module crates

The goal is not ceremony. The goal is to catch architectural drift before it turns into a framework people have to memorize.

Agent change checklist

When an agent changes Lenso, ask for checks and visible status in this shape:

Changed:
- module manifest
- linked route
- business operation

Checks:
- cargo check --bins
- just generated-check

Lenso Console:
- module appears in Modules
- receipt-bound Surface appears under the Module
- generated client calls the declared business operation through Surface Gateway
- story contains the business operation

If the work is supposed to appear in Lenso Console, a compile-only check is not enough.

Do not hand-edit generated files

Generated contracts are source-controlled artifacts, but they are not authored by hand. Change Rust route annotations, platform schemas, or proto sources, then regenerate:

just generate-contracts
just generated-check

Commit the source change and the generated diff together.

Last updated on August 13, 2026

Was this page helpful?