---
title: Contracts
description: The rules every write is checked against, the verdict they return, and how to write your own.
section: concepts
order: 1
---

# Contracts

A contract is a rule about an entry. Hollware runs the contracts of a collection on every write, and again when it publishes, and returns a **verdict**.

## The verdict

```json
{
  "ok": false,
  "phase": "write",
  "errors": [
    {
      "contract": "schema",
      "severity": "error",
      "path": "/frontmatter/description",
      "message": "Too big: expected string to have <=160 characters",
      "fix": "Shorten the description to 160 characters or fewer."
    }
  ],
  "warnings": []
}
```

Every violation has a `path` (a JSON Pointer into the entry), a `message` and a `fix`: an agent can act on it without reading your code.

## Writes never fail on a contract

A draft that breaks a contract is **saved**, with the verdict. Work in progress is never lost, and the agent fixes it with `update_draft`. What a contract stops is publishing:

- an **error** blocks `publish`, which answers `blocked`;
- a **warning** asks for acknowledgement: `publish` answers `needs_acknowledgement` with the warnings, and publishes once they are sent back in `acknowledgeWarnings`.

`validate` runs the contracts on Markdown without saving anything.

## Built in

These always run: the schema of the frontmatter, the components and their props, content safety (no raw HTML that runs script), and the size limit of an entry. Hollware ships more that a collection turns on:

| Contract                        | Checks                                                          |
| ------------------------------- | --------------------------------------------------------------- |
| `contracts.translationPair()`   | the entry exists in every locale of its translation group       |
| `contracts.internalLinksResolve()` | links to other entries point at published ones, when publishing |
| `contracts.imagesHaveAlt()`     | every image has alternative text                                |

Each takes `{ severity: 'warning' }` to warn instead of block.

## Your own

A contract is an object with an id, a severity, a description and a `check`, which returns the violations of an entry:

```ts
{
  id: 'seo-title-length',
  severity: 'warning',
  description: 'A title of at most 60 characters, which search engines show.',
  check: ({ frontmatter }) =>
    frontmatter.title.length <= 60
      ? []
      : [{
          path: '/frontmatter/title',
          message: `The title has ${frontmatter.title.length} characters; search engines show about 60.`,
          fix: 'Shorten the title to 60 characters or fewer.',
        }],
}
```

Write the `fix` for the agent that will read it: say what to do, not what went wrong.
