Meant Lang
A complete example / Invoicing

When is an invoice
ready to send?

This example covers one workflow: an owner sending an invoice.

1. Agree on the business rules

  1. Only the owner can send an invoice.
  2. The invoice must be a draft.
  3. The invoice needs at least one line item.
  4. Sending changes its status to sent.
  5. An empty or already sent invoice is refused with a specific reason.

An engineer chooses the database, API routes, and framework when designing the implementation.

2. Read the complete model

domain invoicing
  "The business rules for sending an invoice."
  mode model-first

dimension actor = owner | client

concept DenyCode
  field copy: string
  values
    NOT_DRAFT "Only a draft invoice can be sent."
    EMPTY_INVOICE "An invoice needs at least one line item."

concept Invoice
  dimension status = draft | sent

concept LineItem
  field invoice: Invoice

shape Draft of Invoice
  where status = draft

shape Sent of Invoice
  where status = sent

shape Empty of Invoice
  where no LineItem

action SendInvoice of Invoice
  "The owner sends an invoice."
  by owner
  requires Draft else NOT_DRAFT
  requires not Empty else EMPTY_INVOICE
  effect
    status = sent

scenario SendingADraft
  with Draft, LineItem
  the owner sends it via SendInvoice -> Sent

scenario SendingAnEmptyDraft
  with Draft, Empty
  the owner sends it via SendInvoice -> denied EMPTY_INVOICE

scenario SendingTwice
  with Sent, LineItem
  the owner sends it again via SendInvoice -> denied NOT_DRAFT

The .meant download contains exactly the model shown above.

What the declarations mean

Draft, Sent, and Empty describe conditions. These conditions can overlap: a draft can also be empty. LineItem.invoice connects line items to their invoice.

SendInvoice restricts the actor, checks the two preconditions, and declares the change to status. The refusal codes are part of the business vocabulary, so they are declared too.

3. Inspect the recorded evidence

These scenario replays were recorded from the CLI. For each one, the simulator creates an example that satisfies the starting conditions, then executes the modeled action.

ScenarioRecorded outcomeExpectation
SendingADraftLands in SentMatched
SendingAnEmptyDraftDenied EMPTY_INVOICEMatched
SendingTwiceDenied NOT_DRAFTMatched

Inspect the full recorded JSON output ↗

Each replay matches its expected outcome. This checks that modeled execution; it does not prove the application implements the rule or cover every possible behavior.

Warnings in this example

The structural check reports zero errors, zero warnings, and two informational notices. Simulation adds two warnings because this focused slice does not model actions that create draft or empty invoices. It also reports Sent as unprobeable for an exit: no supported outgoing transition is declared.

A fuller lifecycle model would also describe how invoices are created, edited, and handled after sending.

4. Make the engineering choices

An engineer can implement these rules in a monolith, a service with a background worker, or an existing system. Separately decide storage, interfaces, transaction boundaries, retries, and delivery guarantees.

The example’s status = sent effect only models a status change. It does not prove that an email was delivered. If delivery is a business requirement, model the observable outcome and its failure cases explicitly.

Translate expectations into application tests

Run these tests against the application. The Meant CLI does not execute them.

Next: work with an agent ↗