Concepts

Contracts

The rules every write is checked against, the verdict they return, and the catalog of rules a collection turns on.

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

{
  "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.

The catalog

A collection turns on more from the catalog, each with a severity (an error blocks publishing; a warning has to be acknowledged) and its parameters. Add them in the admin, on the page of the collection, or over MCP with set_collection; get_structure lists the catalog with the JSON Schema of each rule's parameters.

RuleChecksParameters
translation-pairthe entry exists in every locale of its translation group, to publishnone
internal-links-resolvelinks to other entries point at published ones, when publishingnone
images-have-altevery image has alternative textnone
field-lengtha text field has between min and max characters, or a list between min and max itemsfield, min, max, unless
field-patterna text field matches a regular expression, as a wholefield, pattern, description
required-to-publishfields a draft may leave empty and a published entry must fillfields
banned-wordswords that may appear neither in a text field nor in the bodywords, reason
referenceeach slug a field names is an entry of another collection, which must be published to publishfield, collection, locale

A title of at most 60 characters, unless a seoTitle is given, is one rule:

{
  "rule": "field-length",
  "severity": "warning",
  "params": { "field": "title", "max": 60, "unless": "seoTitle" },
  "id": "seo-title-length"
}

An entry can name others by their slug, such as the author of a post, a series or the event of a talk. The rule reference checks the slugs of a field (one, or a list) against a collection, in the entry's locale or the locale you give:

{ "rule": "reference", "severity": "error", "params": { "field": "author", "collection": "authors" } }

A site reads them with the entry: get_entry with references: true (or getEntry(…, { references: true }), or ?references=true over HTTP) answers references, the published entries each field names, in its order.

id names the rule in verdicts; give one when a collection has the same rule twice. Every violation comes with a fix written for the agent that reads it: what to do, not what went wrong.