---
title: "Why aontu"
description: "Define system constraints that a coding agent can check: service types, relations, defaults, and validation results."
source: "https://aontu.dev/why/"
---

# Why aontu

A code change can pass a service's tests and still violate a rule about the wider system. aontu lets you state those rules as types, defaults, and relations that a program can check.

## What the agent does not have

A repository can describe each service without stating which services may call each other, what each tier requires, or which fields every record needs. If those rules are only in prose or a diagram, a build cannot check them. An aontu document makes them explicit.

A type checker checks the rules expressed in its types; a JSON Schema validator checks the supplied schema. aontu adds a shared definition for entities and their relations. You can require an owner for every service, constrain its dependencies, and refuse a cycle in the graph.

## Types, defaults, and data use one notation

In aontu a type is a value that admits many values, a default is a value that yields to a more specific one, and a fact is a value that is already specific. All three are the same kind of thing, so one document holds the vocabulary and the data together and the engine checks them in one pass. Say a key twice and the two statements combine:

```aon
deploy: region: "eu-west-1"|"us-east-1"
deploy: replicas: integer & min(2)
deploy: region: "eu-west-1"
deploy: replicas: 6
```

evaluates to

```json
{
  "deploy": { "region": "eu-west-1", "replicas": 6 }
}
```

Set `replicas` to `1` and the document fails to evaluate because `min(2)` still applies. The constraint and the concrete value are checked together.

## Check relations between entities

Services depend on services, jobs feed jobs, and records reference records. In aontu, an entity's identity is its path in the document. A relation can require typed endpoints, an acyclic graph, or a corresponding edge on the target. This example keeps the vocabulary under `hide()`, so it is checked but omitted from output:

```aon
spec: hide({
  job: {
    kind: job
    downstream?: rel($.spec.shape) & acyclic() & inverse(upstream)
    upstream?: rel($.spec.shape)
  }
  shape: kind: job
})

jobs: { &: $.spec.job }
jobs: extract: downstream: [path($.jobs.transform)]
jobs: transform: {
  upstream: [path($.jobs.extract)]
  downstream: [path($.jobs.load)]
}
jobs: load: upstream: [path($.jobs.transform)]
```

evaluates to

```json
{
  "jobs": {
    "extract":   { "downstream": ["$.jobs.transform"], "kind": "job" },
    "load":      { "kind": "job", "upstream": ["$.jobs.transform"] },
    "transform": { "downstream": ["$.jobs.load"], "kind": "job",
                   "upstream": ["$.jobs.extract"] }
  }
}
```

Remove the edge from `load` back to `transform`. The refusal identifies the missing inverse edge and the declaration that requires it:

```aontu-diagnostic
[aontu/relation_inverse_missing]: Cannot relate value at path $.jobs.transform.downstream

This relation declared inverse(name), and an edge has no mirroring
edge under that name: A relates to B, and B does not name A back.

 Cannot relate value: path($.jobs.load)
  --> pipeline.aon:4:18
  2 |   job: {
  3 |     kind: job
  4 |     downstream?: rel($.spec.shape) & acyclic() & inverse(upstream)
                       ^ value was: path($.jobs.load)
  5 |     upstream?: rel($.spec.shape)
  6 |   }
```

The inverse-edge requirement is part of the schema, so validation checks it whenever you evaluate this model.

## One definition, every environment

An environment can reuse a base definition and add its own values. Here, production sets the replica count and inherits the log level:

```aon
spec: hide({
  service: { owner:string replicas: *2|integer log: *"info"|string }
})

base: { &: $.spec.service api:owner:"platform" }
prod: $.base & { api:replicas:6 }
```

evaluates to

```json
{
  "base": { "api": { "log": "info", "owner": "platform", "replicas": 2 } },
  "prod": { "api": { "log": "info", "owner": "platform", "replicas": 6 } }
}
```

Changing a default affects environments that have not specified that value. A conflicting overlay cannot replace a fixed value. Use [why](https://aontu.dev/docs/reference-api#aontu-model-why) to see which statements contributed to a result, with their filenames and line numbers.

## Use validation results in a program

[`vet`](https://aontu.dev/docs/reference-api#aontu-vet) reports valid, invalid, incomplete, or error, with a distinct exit code for each. A repair loop can use the verdict and [error class](https://aontu.dev/errors) to choose its next action. `breaking` checks compatibility with an earlier schema, and `subsume` checks whether one definition admits another. `aontu mcp` provides the same reports locally over stdio.

## What it costs

aontu has no user-defined functions, clock, or randomness. Evaluation uses bounded recursion and configured budgets. The [trust contract](https://aontu.dev/docs/trust) specifies the conditions for termination and deterministic output, including host inputs and file access. Use a host language for behaviour beyond those limits.

[Unification](https://aontu.dev/docs/unification) combines compatible statements into a more specific value and reports contradictions. Statement order does not give one source priority over another.

Start with the [tutorial](https://aontu.dev/docs/tutorial), or run the [use cases](https://github.com/aontu-lang/aontu/tree/main/use-cases) to check models such as a service catalog and an event-contract registry.
