JSON reports: the commands that answer questions

The JSON Schemas of what ascribe explain, outline, link, refs, rename, resolve, and search write with --format json.

For AI agents: the documentation index is at llms.txt, and this page is available as Markdown.

Part of the JSON report contract, whose rules every document here follows.

ascribe explain

schemas/explain.schema.json:

{
  "$defs": {
    "ExampleFile": {
      "description": "A file an example assumes besides its page.",
      "properties": {
        "path": {
          "description": "Its content path.",
          "type": "string"
        },
        "text": {
          "description": "Its text.",
          "type": "string"
        }
      },
      "required": [
        "path",
        "text"
      ],
      "type": "object"
    },
    "ExampleText": {
      "description": "A diagnostic's example.",
      "properties": {
        "files": {
          "description": "The project's other files the example assumes, in path order.",
          "items": {
            "$ref": "#/$defs/ExampleFile"
          },
          "type": "array"
        },
        "model": {
          "description": "The content model the example assumes, when the problem depends on\none: the example's own fragment, or else the explain model. `null`\nwhen it's a problem under any content model.",
          "type": [
            "string",
            "null"
          ]
        },
        "right": {
          "description": "The same page, fixed.",
          "type": "string"
        },
        "wrong": {
          "description": "The page, `page.md`, with the problem.",
          "type": "string"
        }
      },
      "required": [
        "wrong",
        "right",
        "model",
        "files"
      ],
      "type": "object"
    },
    "MessageVariant": {
      "description": "Another wording of a diagnostic's message.",
      "properties": {
        "message": {
          "description": "The message, with its placeholders as written.",
          "type": "string"
        },
        "name": {
          "description": "The case it's for.",
          "type": "string"
        }
      },
      "required": [
        "name",
        "message"
      ],
      "type": "object"
    }
  },
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "description": "What `ascribe explain <CODE>` answers: one diagnostic.",
  "properties": {
    "ascribe_version": {
      "description": "The version of Ascribe that wrote it.",
      "type": "string"
    },
    "code": {
      "description": "The code, such as `ASC036`.",
      "type": "string"
    },
    "docs": {
      "description": "The address of its entry in the diagnostics reference: the docs'\npermalink for its code, such as `https://ascribed-dev.com/go/ASC036/`,\nwhich forwards to the entry wherever it is.",
      "type": "string"
    },
    "example": {
      "anyOf": [
        {
          "$ref": "#/$defs/ExampleText"
        },
        {
          "type": "null"
        }
      ],
      "description": "A page that has the problem and the same page fixed; `null` when the\ndiagnostic has no example."
    },
    "fix": {
      "description": "How to fix it, in general. Every diagnostic has one.",
      "type": [
        "string",
        "null"
      ]
    },
    "level": {
      "description": "`file` for a problem found in each file on its own; `page` for one\nfound in each page as a build resolves it.",
      "type": "string"
    },
    "message": {
      "description": "The message, with its placeholders (`{path}`) as written.",
      "type": "string"
    },
    "next": {
      "description": "The kind of next step: `fix`, `choose`, `write`, `outside`, or\n`review`. Every diagnostic has one.",
      "type": [
        "string",
        "null"
      ]
    },
    "schema_version": {
      "description": "The version of this schema. It changes only when a field is removed\nor changes meaning.",
      "minimum": 0,
      "type": "integer"
    },
    "severity": {
      "description": "`error`, `warning`, or `advice`.",
      "type": "string"
    },
    "slug": {
      "description": "The diagnostic's name, such as `link-target-missing`.",
      "type": "string"
    },
    "variants": {
      "description": "The other ways the message is worded, by the case they're for, in\nname order.",
      "items": {
        "$ref": "#/$defs/MessageVariant"
      },
      "type": "array"
    }
  },
  "required": [
    "schema_version",
    "ascribe_version",
    "code",
    "slug",
    "severity",
    "next",
    "level",
    "message",
    "variants",
    "fix",
    "docs",
    "example"
  ],
  "title": "ExplainReport",
  "type": "object"
}

With --list:

schemas/explain-list.schema.json:

{
  "$defs": {
    "ListedDiagnostic": {
      "description": "A diagnostic in the list.",
      "properties": {
        "code": {
          "description": "The code.",
          "type": "string"
        },
        "severity": {
          "description": "`error`, `warning`, or `advice`.",
          "type": "string"
        },
        "slug": {
          "description": "The slug.",
          "type": "string"
        }
      },
      "required": [
        "code",
        "slug",
        "severity"
      ],
      "type": "object"
    }
  },
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "description": "What `ascribe explain --list` answers: every diagnostic.",
  "properties": {
    "ascribe_version": {
      "description": "The version of Ascribe that wrote it.",
      "type": "string"
    },
    "diagnostics": {
      "description": "Every diagnostic, in code order.",
      "items": {
        "$ref": "#/$defs/ListedDiagnostic"
      },
      "type": "array"
    },
    "schema_version": {
      "description": "The version of this schema. It changes only when a field is removed\nor changes meaning.",
      "minimum": 0,
      "type": "integer"
    }
  },
  "required": [
    "schema_version",
    "ascribe_version",
    "diagnostics"
  ],
  "title": "ExplainListReport",
  "type": "object"
}

ascribe outline

schemas/outline.schema.json:

{
  "$defs": {
    "OutlineHeading": {
      "description": "A heading a link can name.",
      "properties": {
        "explicit_id": {
          "description": "Whether the id is the heading's `@id`, which stays when its text\nchanges.",
          "type": "boolean"
        },
        "file": {
          "description": "The file it's written in, from the project root.",
          "type": "string"
        },
        "fragment": {
          "description": "The fragment it comes from, as a path from the content root; `null`\nfor the page's own.",
          "type": [
            "string",
            "null"
          ]
        },
        "id": {
          "description": "Its id: what a link writes after `#`.",
          "type": "string"
        },
        "level": {
          "description": "1 to 6.",
          "maximum": 255,
          "minimum": 0,
          "type": "integer"
        },
        "line": {
          "description": "Its line in that file, from 1.",
          "minimum": 0,
          "type": "integer"
        },
        "memos": {
          "description": "The memo files about its section, or a block in it, in path order.",
          "items": {
            "$ref": "#/$defs/OutlineMemo"
          },
          "type": "array"
        },
        "text": {
          "description": "Its text, phrases replaced by their values.",
          "type": "string"
        }
      },
      "required": [
        "level",
        "text",
        "id",
        "explicit_id",
        "file",
        "line",
        "fragment",
        "memos"
      ],
      "type": "object"
    },
    "OutlineMemo": {
      "description": "A memo file about a page or a heading.",
      "properties": {
        "file": {
          "description": "The memo file, from the project root.",
          "type": "string"
        },
        "first_line": {
          "description": "The first line of its text that isn't blank.",
          "type": "string"
        },
        "personal": {
          "description": "Whether it's one of the memos kept on this machine only, rather than\nthe project's `memos/`.",
          "type": "boolean"
        },
        "quote": {
          "description": "Its `quote`; `null` when it has none.",
          "type": [
            "string",
            "null"
          ]
        },
        "state": {
          "$ref": "#/$defs/OutlineMemoState",
          "description": "`placed`, on what its `about` names or the block it quotes, or\n`outdated`, when no block holds its quote."
        }
      },
      "required": [
        "file",
        "state",
        "quote",
        "first_line",
        "personal"
      ],
      "type": "object"
    },
    "OutlineMemoState": {
      "description": "Where a listed memo is.",
      "oneOf": [
        {
          "const": "placed",
          "description": "On its page or section, or the block its quote is in.",
          "type": "string"
        },
        {
          "const": "outdated",
          "description": "Its quote isn't found: it's on its page or section, and no block.",
          "type": "string"
        }
      ]
    }
  },
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "description": "What `ascribe outline <PAGE>` answers.",
  "properties": {
    "ascribe_version": {
      "description": "The version of Ascribe that wrote it.",
      "type": "string"
    },
    "build": {
      "description": "The build the headings are limited to, with `--build`.",
      "type": [
        "string",
        "null"
      ]
    },
    "file": {
      "description": "The page's file from the project root, as `ascribe check` reports it.",
      "type": "string"
    },
    "fragment": {
      "description": "Whether it's a fragment, which pages include rather than link to.",
      "type": "boolean"
    },
    "headings": {
      "description": "The headings a link to the page can name, in document order, each id\nonce.",
      "items": {
        "$ref": "#/$defs/OutlineHeading"
      },
      "type": "array"
    },
    "memos": {
      "description": "The memo files about the page as a whole, in path order. A detached\nmemo isn't listed.",
      "items": {
        "$ref": "#/$defs/OutlineMemo"
      },
      "type": "array"
    },
    "not_published": {
      "description": "Why that build doesn't publish the page, when it doesn't; there are\nno headings then.",
      "type": [
        "string",
        "null"
      ]
    },
    "page": {
      "description": "The page's path from the content root, as links write it.",
      "type": "string"
    },
    "schema_version": {
      "description": "The version of this schema. It changes only when a field is removed\nor changes meaning.",
      "minimum": 0,
      "type": "integer"
    },
    "title": {
      "description": "Its title (frontmatter `title`).",
      "type": [
        "string",
        "null"
      ]
    },
    "type": {
      "description": "Its content type; `null` for a fragment, or when no one type applies.",
      "type": [
        "string",
        "null"
      ]
    }
  },
  "required": [
    "schema_version",
    "ascribe_version",
    "page",
    "file",
    "fragment",
    "title",
    "type",
    "build",
    "not_published",
    "memos",
    "headings"
  ],
  "title": "OutlineReport",
  "type": "object"
}

schemas/link.schema.json:

{
  "$defs": {
    "LinkKind": {
      "description": "What a link target is.",
      "oneOf": [
        {
          "const": "page",
          "description": "A page.",
          "type": "string"
        },
        {
          "const": "heading",
          "description": "A heading on a page.",
          "type": "string"
        },
        {
          "const": "fragment",
          "description": "A fragment, which pages include and links can't name.",
          "type": "string"
        },
        {
          "const": "file",
          "description": "A file that isn't a page, which a build copies: an image or a\ndownload.",
          "type": "string"
        },
        {
          "const": "external",
          "description": "A URL with a scheme.",
          "type": "string"
        },
        {
          "const": "operation",
          "description": "An operation of a declared API's spec: a link to the spec with a\nfragment.",
          "type": "string"
        },
        {
          "const": "missing",
          "description": "Nothing: no file has that path.",
          "type": "string"
        }
      ]
    },
    "LinkLine": {
      "description": "Where a link goes in one line of a versioned build (SPEC §5.2).",
      "properties": {
        "build": {
          "description": "The line's build: the declared build for the current line, and\n`<build>@<line>` for each other.",
          "type": "string"
        },
        "leads_to": {
          "description": "For a link out of the line, the line it leads to.",
          "type": [
            "string",
            "null"
          ]
        },
        "line": {
          "description": "The line, as `[releases.<target>]` writes it.",
          "type": "string"
        },
        "page": {
          "description": "The page it lands on, as a path from the content root: the target,\nor the page that takes its place in the line. `null` when no live\nline publishes the target, which is `link-page-dropped`.",
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "build",
        "line",
        "page",
        "leads_to"
      ],
      "type": "object"
    },
    "LinkOperation": {
      "description": "An operation of a declared API's spec (`[apis.<name>]`), as a link to it\nnames it.",
      "properties": {
        "address": {
          "description": "Its address in the published reference, as the editor's build writes\nthe link; `null` when the reference has none for it: an operation\nwith no id, under an address pattern.",
          "type": [
            "string",
            "null"
          ]
        },
        "api": {
          "description": "The API's name.",
          "type": "string"
        },
        "deprecated": {
          "description": "Whether the spec marks it `deprecated`.",
          "type": "boolean"
        },
        "method": {
          "description": "Its method, in lowercase.",
          "type": "string"
        },
        "operation_id": {
          "description": "Its `operationId`; `null` for an operation with none, which a link\nnames by its method and path.",
          "type": [
            "string",
            "null"
          ]
        },
        "path": {
          "description": "Its path, as the spec writes it.",
          "type": "string"
        },
        "summary": {
          "description": "Its `summary`, when it has one.",
          "type": [
            "string",
            "null"
          ]
        },
        "tag": {
          "description": "Its first tag, when it has one.",
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "api",
        "operation_id",
        "method",
        "path",
        "summary",
        "tag",
        "deprecated",
        "address"
      ],
      "type": "object"
    },
    "LinkSuggestion": {
      "description": "A target a link could name instead.",
      "properties": {
        "href": {
          "description": "The destination to write.",
          "type": "string"
        },
        "id": {
          "description": "The heading id, if it's a heading.",
          "type": [
            "string",
            "null"
          ]
        },
        "path": {
          "description": "The page, as a path from the content root.",
          "type": "string"
        },
        "title": {
          "description": "The page's title, or the heading's text.",
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "href",
        "path",
        "id",
        "title"
      ],
      "type": "object"
    }
  },
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "description": "What `ascribe link <TARGET> --from <PAGE>` answers.",
  "properties": {
    "ascribe_version": {
      "description": "The version of Ascribe that wrote it.",
      "type": "string"
    },
    "closest": {
      "description": "The closest targets that exist, best first, when it doesn't.",
      "items": {
        "$ref": "#/$defs/LinkSuggestion"
      },
      "type": "array"
    },
    "exists": {
      "description": "Whether a link from that page to the target works. An external URL\ncounts as existing; it isn't checked.",
      "type": "boolean"
    },
    "from": {
      "description": "The page the link is written on, as the command was given it: a path\nfrom the folder it was run in, as `ascribe check` names a file.",
      "type": "string"
    },
    "href": {
      "description": "The destination to write on that page, when the target exists.",
      "type": [
        "string",
        "null"
      ]
    },
    "id": {
      "description": "The heading id after `#`, if any.",
      "type": [
        "string",
        "null"
      ]
    },
    "kind": {
      "$ref": "#/$defs/LinkKind",
      "description": "What the target is."
    },
    "lines": {
      "description": "Where a link to a page goes in each line of each versioned build,\nnewest first; absent when the target isn't a page or no build is\nversioned.",
      "items": {
        "$ref": "#/$defs/LinkLine"
      },
      "type": "array"
    },
    "operation": {
      "anyOf": [
        {
          "$ref": "#/$defs/LinkOperation"
        },
        {
          "type": "null"
        }
      ],
      "description": "The operation a link to a declared API's spec names, when the spec\nhas it; absent for any other target."
    },
    "path": {
      "description": "The file it names, as a path from the content root; `null` for an\nexternal URL, or a file that doesn't exist.",
      "type": [
        "string",
        "null"
      ]
    },
    "problem": {
      "description": "Why it doesn't work, when it doesn't.",
      "type": [
        "string",
        "null"
      ]
    },
    "schema_version": {
      "description": "The version of this schema. It changes only when a field is removed\nor changes meaning.",
      "minimum": 0,
      "type": "integer"
    },
    "target": {
      "description": "The target, as given.",
      "type": "string"
    },
    "title": {
      "description": "The page's title, or the heading's text: what a link with no text\nshows.",
      "type": [
        "string",
        "null"
      ]
    }
  },
  "required": [
    "schema_version",
    "ascribe_version",
    "from",
    "target",
    "exists",
    "kind",
    "path",
    "id",
    "title",
    "href",
    "problem",
    "closest"
  ],
  "title": "LinkReport",
  "type": "object"
}

ascribe refs

schemas/refs.schema.json:

{
  "$defs": {
    "Place": {
      "description": "A place a target is used.",
      "properties": {
        "column": {
          "description": "The column, from 1, in Unicode characters.",
          "minimum": 0,
          "type": "integer"
        },
        "file": {
          "description": "The file, from the project root, as `ascribe check` reports it.",
          "type": "string"
        },
        "line": {
          "description": "The line, from 1.",
          "minimum": 0,
          "type": "integer"
        },
        "personal": {
          "description": "Whether it's a memo file kept on this machine only, in\n`.ascribe/memos/`, rather than the project's `memos/`; `false` for\nevery place that isn't a memo.",
          "type": "boolean"
        },
        "spec": {
          "description": "The availability spec, as written, for an `availability` place; null\nfor every other place.",
          "type": [
            "string",
            "null"
          ]
        },
        "use": {
          "description": "How it uses the target: `link`, `include`, `phrase`, `availability`,\n`term`, `variant`, `note`, `widget`, `image`, an image that shows it,\n`html` or `frontmatter`, raw HTML or a frontmatter field that names\nit, or `memo`, a memo file about it.",
          "type": "string"
        }
      },
      "required": [
        "file",
        "line",
        "column",
        "use",
        "personal",
        "spec"
      ],
      "type": "object"
    },
    "TargetKind": {
      "description": "What a target is.",
      "oneOf": [
        {
          "const": "page",
          "description": "A page.",
          "type": "string"
        },
        {
          "const": "fragment",
          "description": "A fragment.",
          "type": "string"
        },
        {
          "const": "heading",
          "description": "A heading, by its page and id.",
          "type": "string"
        },
        {
          "const": "image",
          "description": "An image under the content root.",
          "type": "string"
        },
        {
          "const": "phrase",
          "description": "A phrase, `phrase:<key>`.",
          "type": "string"
        },
        {
          "const": "feature",
          "description": "A feature, `feature:<key>`.",
          "type": "string"
        },
        {
          "const": "term",
          "description": "A glossary term, `term:<id>`.",
          "type": "string"
        },
        {
          "const": "dimension",
          "description": "A dimension, `dimension:<name>`.",
          "type": "string"
        },
        {
          "const": "note",
          "description": "A note type, `note:<type>`.",
          "type": "string"
        },
        {
          "const": "widget",
          "description": "A project widget, `widget:<name>`.",
          "type": "string"
        },
        {
          "const": "release",
          "description": "A line of a release target, `release:<target>@<version>`.",
          "type": "string"
        }
      ]
    }
  },
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "description": "What `ascribe refs <TARGET>` answers.",
  "properties": {
    "ascribe_version": {
      "description": "The version of Ascribe that wrote it.",
      "type": "string"
    },
    "exists": {
      "description": "Whether the target exists. When it doesn't, the only places are the\nmemo files that name it.",
      "type": "boolean"
    },
    "kind": {
      "$ref": "#/$defs/TargetKind",
      "description": "What the target is."
    },
    "next_command": {
      "description": "The command that lists them all, when the list was cut.",
      "type": [
        "string",
        "null"
      ]
    },
    "places": {
      "description": "The places that use it, file by file in path order and in document\norder within a file, then the memo files about it, in path order; at\nmost `shown` of them.",
      "items": {
        "$ref": "#/$defs/Place"
      },
      "type": "array"
    },
    "schema_version": {
      "description": "The version of this schema. It changes only when a field is removed\nor changes meaning.",
      "minimum": 0,
      "type": "integer"
    },
    "shown": {
      "description": "How many are listed.",
      "minimum": 0,
      "type": "integer"
    },
    "target": {
      "description": "The target, as given.",
      "type": "string"
    },
    "total": {
      "description": "How many places use it.",
      "minimum": 0,
      "type": "integer"
    },
    "truncated": {
      "description": "Whether the list was cut: `shown` is less than `total`.",
      "type": "boolean"
    }
  },
  "required": [
    "schema_version",
    "ascribe_version",
    "target",
    "kind",
    "exists",
    "places",
    "total",
    "shown",
    "truncated",
    "next_command"
  ],
  "title": "RefsReport",
  "type": "object"
}

ascribe rename

schemas/rename.schema.json:

{
  "$defs": {
    "FileJson": {
      "description": "A file a rename changes.",
      "properties": {
        "edits": {
          "description": "How many places in it change.",
          "minimum": 0,
          "type": "integer"
        },
        "file": {
          "description": "The file, from the current directory.",
          "type": "string"
        },
        "text": {
          "description": "Its whole text after the rename.",
          "type": "string"
        }
      },
      "required": [
        "file",
        "text",
        "edits"
      ],
      "type": "object"
    },
    "MoveJson": {
      "description": "A move `--from-git` records.",
      "properties": {
        "key": {
          "description": "The entry's key: where the page was.",
          "type": "string"
        },
        "value": {
          "description": "The entry's value: where it is now.",
          "type": "string"
        }
      },
      "required": [
        "key",
        "value"
      ],
      "type": "object"
    },
    "MovedJson": {
      "description": "The file a rename moves.",
      "properties": {
        "from": {
          "description": "Where it is, from the current directory.",
          "type": "string"
        },
        "to": {
          "description": "Where it goes, from the current directory. Its folder is made when\nit isn't there.",
          "type": "string"
        }
      },
      "required": [
        "from",
        "to"
      ],
      "type": "object"
    }
  },
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "description": "What `ascribe rename --format json` writes, and the MCP server's\n`ascribe_rename` gives.",
  "properties": {
    "ascribe_version": {
      "description": "The version of Ascribe that wrote it.",
      "type": "string"
    },
    "files": {
      "description": "Each file the rename changes, in path order, with its whole text\nafter. The file that moves is under where it was, and\n`ascribe.addresses` is one of them when the rename records a move.",
      "items": {
        "$ref": "#/$defs/FileJson"
      },
      "type": "array"
    },
    "moved": {
      "anyOf": [
        {
          "$ref": "#/$defs/MovedJson"
        },
        {
          "type": "null"
        }
      ],
      "description": "The page or fragment that moves, when one does."
    },
    "recorded": {
      "description": "The entries of `ascribe.addresses`'s `[moved]` the rename records, in\nkey order, so the build forwards the old addresses; none when it\nrecords nothing, as for a phrase, a fragment, or a project with no\n`ascribe.addresses`.",
      "items": {
        "$ref": "#/$defs/MoveJson"
      },
      "type": "array"
    },
    "schema_version": {
      "description": "The version of this schema. It changes only when a field is removed\nor changes meaning.",
      "minimum": 0,
      "type": "integer"
    },
    "written": {
      "description": "Whether the files were written and the file moved: `--dry-run`, and\nthe MCP server's `ascribe_rename`, write nothing, and give the\nchanges to make.",
      "type": "boolean"
    }
  },
  "required": [
    "schema_version",
    "ascribe_version",
    "files",
    "recorded",
    "written"
  ],
  "title": "RenameReport",
  "type": "object"
}

ascribe resolve

schemas/resolve.schema.json:

{
  "$defs": {
    "Emit": {
      "description": "Which output a page is written as, by the name `ascribe build --emit`\ngives it.",
      "oneOf": [
        {
          "const": "plain",
          "description": "The plain output: fully resolved CommonMark with no HTML.",
          "type": "string"
        },
        {
          "const": "site",
          "description": "The site output: Markdown with Ascribe's elements, as a site's\nintegration receives it.",
          "type": "string"
        }
      ]
    }
  },
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "description": "What `ascribe resolve <PAGE> --format json` answers.",
  "properties": {
    "ascribe_version": {
      "description": "The version of Ascribe that wrote it.",
      "type": "string"
    },
    "build": {
      "description": "The build.",
      "type": "string"
    },
    "emit": {
      "$ref": "#/$defs/Emit",
      "description": "Which output `text` is written as: `plain`, the build's plain\noutput, or `site`, its site output."
    },
    "not_published": {
      "description": "Why the build doesn't publish the page, when it doesn't; `text` is\nempty then.",
      "type": [
        "string",
        "null"
      ]
    },
    "page": {
      "description": "The page, as a path from the content root.",
      "type": "string"
    },
    "route": {
      "description": "The page's route in the build's site.",
      "type": [
        "string",
        "null"
      ]
    },
    "schema_version": {
      "description": "The version of this schema. It changes only when a field is removed\nor changes meaning.",
      "minimum": 0,
      "type": "integer"
    },
    "text": {
      "description": "The page as the output `emit` names writes it: with its frontmatter\nfirst when asked for.",
      "type": "string"
    }
  },
  "required": [
    "schema_version",
    "ascribe_version",
    "page",
    "build",
    "emit",
    "not_published",
    "route",
    "text"
  ],
  "title": "ResolveReport",
  "type": "object"
}

ascribe resolve go:<name>

schemas/resolve-permalink.schema.json:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "description": "What `ascribe resolve go:<name> --format json` answers: the name's entry\nas the build's `_ascribe/help.json` writes it, with the file's `version`,\n`build`, and `line`.",
  "properties": {
    "address": {
      "description": "The permalink's address; absent without `[consumer] site`.",
      "type": [
        "string",
        "null"
      ]
    },
    "ascribe_version": {
      "description": "The version of Ascribe that wrote it.",
      "type": "string"
    },
    "build": {
      "description": "The build, without its line.",
      "type": "string"
    },
    "choices": {
      "additionalProperties": {
        "additionalProperties": {
          "type": "string"
        },
        "type": "object"
      },
      "description": "For a section with a switcher, its text with each arm chosen: keyed\nby dimension, then by value, both in the content model's order.\nAbsent when the section has no switcher on a dimension.",
      "type": [
        "object",
        "null"
      ]
    },
    "line": {
      "description": "The line, for a line of a versioned build that has more than one.",
      "type": [
        "string",
        "null"
      ]
    },
    "name": {
      "description": "The permalink's name, as asked for.",
      "type": "string"
    },
    "schema_version": {
      "description": "The version of this schema. It changes only when a field is removed\nor changes meaning.",
      "minimum": 0,
      "type": "integer"
    },
    "source": {
      "description": "The page the permalink is recorded on, as a path from the content\nroot.",
      "type": "string"
    },
    "summary": {
      "description": "The section's summary; absent when it has none.",
      "type": [
        "string",
        "null"
      ]
    },
    "text": {
      "description": "The section as the plain output writes it, in Markdown, with each\nimage as its alt text.",
      "type": "string"
    },
    "title": {
      "description": "The heading's text, or the page's title for a page's permalink, as\nplain text.",
      "type": "string"
    },
    "version": {
      "description": "The version of `help.json`'s format, which this shares. A later\nversion adds keys and changes none of these.",
      "minimum": 0,
      "type": "integer"
    }
  },
  "required": [
    "schema_version",
    "ascribe_version",
    "version",
    "build",
    "name",
    "title",
    "text",
    "source"
  ],
  "title": "ResolvePermalinkReport",
  "type": "object"
}

schemas/search.schema.json:

{
  "$defs": {
    "PageCount": {
      "description": "A page and how many places it shows: what `--summary` lists.",
      "properties": {
        "page": {
          "description": "The page, from the project root.",
          "type": "string"
        },
        "places": {
          "description": "How many places it shows.",
          "minimum": 0,
          "type": "integer"
        }
      },
      "required": [
        "page",
        "places"
      ],
      "type": "object"
    },
    "PartialPlace": {
      "description": "A place that has some of the words and phrases a place needs, not all\nof them.",
      "properties": {
        "arm": {
          "description": "The arm of a group the block is written in, as its attributes are\nwritten (`deployment=cloud`), or else its title.",
          "type": [
            "string",
            "null"
          ]
        },
        "column": {
          "description": "The column of the match's first character, from 1, in Unicode\ncharacters.",
          "minimum": 0,
          "type": "integer"
        },
        "end_line": {
          "description": "For a snippet's code that can't be lined up with its file, the last\nline the snippet shows: the place is its lines, from `line`, and\nnot the line the match is on. `null` for every other place.",
          "minimum": 0,
          "type": [
            "integer",
            "null"
          ]
        },
        "file": {
          "description": "The file, from the project root, as `ascribe check` reports it: a\npage, a fragment, a snippet's code file, or `ascribe.toml` for a\nphrase.",
          "type": "string"
        },
        "has": {
          "description": "How many of the words and phrases a place needs it has.",
          "minimum": 0,
          "type": "integer"
        },
        "in": {
          "description": "What it is: `title`, `description`, `heading`, `text`, `table`,\n`code`, or `phrase`, a phrase's value at its line in `ascribe.toml`.",
          "type": "string"
        },
        "lacks": {
          "description": "The words and phrases it lacks, as the query writes them, a phrase\nin its quotes, in the query's order.",
          "items": {
            "type": "string"
          },
          "type": "array"
        },
        "line": {
          "description": "The line, from 1.",
          "minimum": 0,
          "type": "integer"
        },
        "match": {
          "description": "Where the match is in `text`: its first character and the one after\nits last, in Unicode characters; when `text` is cut before the\nmatch's last character, the end ends where it's cut, before the `…`.",
          "items": {
            "minimum": 0,
            "type": "integer"
          },
          "maxItems": 2,
          "minItems": 2,
          "type": "array"
        },
        "pages": {
          "description": "The pages that show it, in path order, at most three.",
          "items": {
            "$ref": "#/$defs/SearchPage"
          },
          "type": "array"
        },
        "pages_total": {
          "description": "How many pages show it; for a phrase, how many places in the source\nuse it.",
          "minimum": 0,
          "type": "integer"
        },
        "refs_command": {
          "description": "The `ascribe refs` command that lists where it's used, for a phrase,\nand for a fragment's place when `pages` was cut.",
          "type": [
            "string",
            "null"
          ]
        },
        "text": {
          "description": "The block's text as a reader reads it, or for a phrase, its value:\nwhole when it's at most 240 characters, and otherwise cut to about\nthat many around the match's first character, at words, with `…`\nwhere it's cut. `line` and `column` say where to read the rest.",
          "type": "string"
        },
        "through": {
          "description": "What the match reads through that the line doesn't spell, in order.",
          "items": {
            "$ref": "#/$defs/SearchThrough"
          },
          "type": "array"
        }
      },
      "required": [
        "file",
        "line",
        "column",
        "end_line",
        "in",
        "text",
        "match",
        "through",
        "arm",
        "pages",
        "pages_total",
        "refs_command",
        "has",
        "lacks"
      ],
      "type": "object"
    },
    "SearchPage": {
      "description": "A page that shows a place.",
      "properties": {
        "builds": {
          "description": "The builds that show it there, in the content model's order.",
          "items": {
            "type": "string"
          },
          "type": "array"
        },
        "heading": {
          "description": "The heading the place is under there, as a reader reads it; for a\nheading, the heading itself.",
          "type": [
            "string",
            "null"
          ]
        },
        "id": {
          "description": "That heading's id.",
          "type": [
            "string",
            "null"
          ]
        },
        "page": {
          "description": "The page's content path.",
          "type": "string"
        },
        "title": {
          "description": "Its title, as a reader reads it in the first build searched that\npublishes the page.",
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "page",
        "title",
        "heading",
        "id",
        "builds"
      ],
      "type": "object"
    },
    "SearchPlace": {
      "description": "A place a query matches: a line of the source to edit.",
      "properties": {
        "arm": {
          "description": "The arm of a group the block is written in, as its attributes are\nwritten (`deployment=cloud`), or else its title.",
          "type": [
            "string",
            "null"
          ]
        },
        "column": {
          "description": "The column of the match's first character, from 1, in Unicode\ncharacters.",
          "minimum": 0,
          "type": "integer"
        },
        "end_line": {
          "description": "For a snippet's code that can't be lined up with its file, the last\nline the snippet shows: the place is its lines, from `line`, and\nnot the line the match is on. `null` for every other place.",
          "minimum": 0,
          "type": [
            "integer",
            "null"
          ]
        },
        "file": {
          "description": "The file, from the project root, as `ascribe check` reports it: a\npage, a fragment, a snippet's code file, or `ascribe.toml` for a\nphrase.",
          "type": "string"
        },
        "in": {
          "description": "What it is: `title`, `description`, `heading`, `text`, `table`,\n`code`, or `phrase`, a phrase's value at its line in `ascribe.toml`.",
          "type": "string"
        },
        "line": {
          "description": "The line, from 1.",
          "minimum": 0,
          "type": "integer"
        },
        "match": {
          "description": "Where the match is in `text`: its first character and the one after\nits last, in Unicode characters; when `text` is cut before the\nmatch's last character, the end ends where it's cut, before the `…`.",
          "items": {
            "minimum": 0,
            "type": "integer"
          },
          "maxItems": 2,
          "minItems": 2,
          "type": "array"
        },
        "pages": {
          "description": "The pages that show it, in path order, at most three.",
          "items": {
            "$ref": "#/$defs/SearchPage"
          },
          "type": "array"
        },
        "pages_total": {
          "description": "How many pages show it; for a phrase, how many places in the source\nuse it.",
          "minimum": 0,
          "type": "integer"
        },
        "refs_command": {
          "description": "The `ascribe refs` command that lists where it's used, for a phrase,\nand for a fragment's place when `pages` was cut.",
          "type": [
            "string",
            "null"
          ]
        },
        "text": {
          "description": "The block's text as a reader reads it, or for a phrase, its value:\nwhole when it's at most 240 characters, and otherwise cut to about\nthat many around the match's first character, at words, with `…`\nwhere it's cut. `line` and `column` say where to read the rest.",
          "type": "string"
        },
        "through": {
          "description": "What the match reads through that the line doesn't spell, in order.",
          "items": {
            "$ref": "#/$defs/SearchThrough"
          },
          "type": "array"
        }
      },
      "required": [
        "file",
        "line",
        "column",
        "end_line",
        "in",
        "text",
        "match",
        "through",
        "arm",
        "pages",
        "pages_total",
        "refs_command"
      ],
      "type": "object"
    },
    "SearchThrough": {
      "description": "What a match reads through that its line doesn't spell.",
      "oneOf": [
        {
          "additionalProperties": false,
          "description": "A phrase's value, by its key.",
          "properties": {
            "phrase": {
              "type": "string"
            }
          },
          "required": [
            "phrase"
          ],
          "type": "object"
        },
        {
          "additionalProperties": false,
          "description": "The title of a page a link with no text takes, or the text of the\nheading it names: `page.md` or `page.md#id`.",
          "properties": {
            "title": {
              "type": "string"
            }
          },
          "required": [
            "title"
          ],
          "type": "object"
        },
        {
          "additionalProperties": false,
          "description": "A snippet's code: its address, as written (`code:app.py#main`).",
          "properties": {
            "code": {
              "type": "string"
            }
          },
          "required": [
            "code"
          ],
          "type": "object"
        }
      ]
    },
    "SourcePlace": {
      "description": "A place the source writes words no build searched publishes.",
      "properties": {
        "file": {
          "description": "The file, from the project root.",
          "type": "string"
        },
        "line": {
          "description": "The line, from 1.",
          "minimum": 0,
          "type": "integer"
        }
      },
      "required": [
        "file",
        "line"
      ],
      "type": "object"
    }
  },
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "description": "What `ascribe search <QUERY>` answers: what `--format json` writes.",
  "properties": {
    "ascribe_version": {
      "description": "The version of Ascribe that wrote it.",
      "type": "string"
    },
    "builds": {
      "description": "The builds searched, in the content model's order.",
      "items": {
        "type": "string"
      },
      "type": "array"
    },
    "next_command": {
      "description": "The command that lists them all, when the list was cut.",
      "type": [
        "string",
        "null"
      ]
    },
    "not_searched_builds": {
      "description": "When nothing matched, the builds that weren't searched that publish\nthe words, in the content model's order; empty otherwise.",
      "items": {
        "type": "string"
      },
      "type": "array"
    },
    "pages": {
      "description": "With `--summary`, in place of the places: each page that shows a\nplace and how many it shows, most first, then in path order; at\nmost `shown` of them. `null` without it.",
      "items": {
        "$ref": "#/$defs/PageCount"
      },
      "type": [
        "array",
        "null"
      ]
    },
    "pages_matched": {
      "description": "How many pages show a place.",
      "minimum": 0,
      "type": "integer"
    },
    "partial_places": {
      "description": "When nothing matched and a place needs two or more words or\nphrases, the places that have some of them, at most 10: those that\nhave the most first, then those with the most stop words, then in\nthe order of `places`. Each is a place as in `places`, its `match`\ncovering the words it has, with how many it `has` and what it\n`lacks`. Empty when something matched.",
      "items": {
        "$ref": "#/$defs/PartialPlace"
      },
      "type": "array"
    },
    "places": {
      "description": "The places, at most `shown` of them: titles, then headings, then the\nrest; in each, the places written in pages, then those in fragments\nand code files, each in path order and in order within a file.\nEmpty with `--summary`.",
      "items": {
        "$ref": "#/$defs/SearchPlace"
      },
      "type": "array"
    },
    "query": {
      "description": "The query, as given.",
      "type": "string"
    },
    "schema_version": {
      "description": "The version of this schema. It changes only when a field is removed\nor changes meaning.",
      "minimum": 0,
      "type": "integer"
    },
    "shown": {
      "description": "How many places are listed, or with `--summary`, pages.",
      "minimum": 0,
      "type": "integer"
    },
    "source_places": {
      "description": "When nothing matched, the first places the source writes the words\nas given, at most three, though no build searched publishes them\nthere; empty when something matched, or when the words aren't\nwritten.",
      "items": {
        "$ref": "#/$defs/SourcePlace"
      },
      "type": "array"
    },
    "stop_words": {
      "description": "The query's stop words, as written: common English words, such as\n`how`, `do`, and `the`, that a place doesn't need to have. Empty\nwhen every word is one, since then a place needs them all.",
      "items": {
        "type": "string"
      },
      "type": "array"
    },
    "total": {
      "description": "How many places match.",
      "minimum": 0,
      "type": "integer"
    },
    "truncated": {
      "description": "Whether the list was cut: `shown` is less than `total`, or with\n`--summary`, than `pages_matched`.",
      "type": "boolean"
    }
  },
  "required": [
    "schema_version",
    "ascribe_version",
    "query",
    "builds",
    "places",
    "pages",
    "pages_matched",
    "total",
    "shown",
    "truncated",
    "next_command",
    "source_places",
    "not_searched_builds",
    "stop_words",
    "partial_places"
  ],
  "title": "SearchReport",
  "type": "object"
}