> For the complete documentation index, see [llms.txt](https://ascribed-dev.com/docs/llms.txt). This documentation describes Ascribe 0.4.4. Anything marked in main, not yet released isn't in that release yet.

# JSON reports: `ascribe sources` and `ascribe observe`

> Reference · The JSON Schemas of what ascribe sources and ascribe observe write with --format json.

Part of the [JSON report contract](https://ascribed-dev.com/docs/contracts/json-reports.md), whose rules every document here follows.

## `ascribe sources fetch`

Available: Ascribe (in main, not yet released, 0.4.5)

[`schemas/sources-fetch.schema.json`](https://github.com/ascribed-dev/ascribe/blob/main/schemas/sources-fetch.schema.json):

```json
{
  "$defs": {
    "Failure": {
      "description": "A file a snippet names that couldn't be copied.",
      "properties": {
        "path": {
          "description": "Its path in the source.",
          "type": "string"
        },
        "reason": {
          "description": "Why.",
          "type": "string"
        }
      },
      "required": [
        "path",
        "reason"
      ],
      "type": "object"
    },
    "SourceFetch": {
      "description": "What `fetch` did for one source.",
      "properties": {
        "commit": {
          "description": "Its pin, when it has one.",
          "type": [
            "string",
            "null"
          ]
        },
        "copied": {
          "description": "The copies written.",
          "items": {
            "type": "string"
          },
          "type": "array"
        },
        "failed": {
          "description": "The files snippets name that couldn't be copied.",
          "items": {
            "$ref": "#/$defs/Failure"
          },
          "type": "array"
        },
        "first_copy": {
          "description": "Whether these are the first files copied from the source, which\npublishes them to everyone who can read the project.",
          "type": "boolean"
        },
        "name": {
          "description": "The source.",
          "type": "string"
        },
        "pinned": {
          "description": "Whether this run pinned it, to the head of its branch.",
          "type": "boolean"
        },
        "removed": {
          "description": "The copies removed.",
          "items": {
            "type": "string"
          },
          "type": "array"
        }
      },
      "required": [
        "name",
        "commit",
        "pinned",
        "copied",
        "removed",
        "failed",
        "first_copy"
      ],
      "type": "object"
    }
  },
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "description": "What `ascribe sources fetch --format json` writes: what was copied and\nremoved for each source, and the files that couldn't be copied.",
  "properties": {
    "ascribe_version": {
      "description": "The version of Ascribe that wrote it.",
      "type": "string"
    },
    "lock_written": {
      "description": "Whether `ascribe.lock` was written.",
      "type": "boolean"
    },
    "schema_version": {
      "description": "The version of this schema. It changes only when a field is removed\nor changes meaning.",
      "minimum": 0,
      "type": "integer"
    },
    "sources": {
      "description": "Each source looked at, in declaration order.",
      "items": {
        "$ref": "#/$defs/SourceFetch"
      },
      "type": "array"
    },
    "unpinned": {
      "description": "The pins removed, of sources ascribe.toml no longer declares in\nanother repository.",
      "items": {
        "type": "string"
      },
      "type": "array"
    },
    "written": {
      "description": "Whether the copies and the lock were written: false with\n`--dry-run`, which says what it would copy, remove, and write.",
      "type": "boolean"
    }
  },
  "required": [
    "schema_version",
    "ascribe_version",
    "written",
    "sources",
    "unpinned",
    "lock_written"
  ],
  "title": "SourcesFetchReport",
  "type": "object"
}
```

## `ascribe sources status`

[`schemas/sources-status.schema.json`](https://github.com/ascribed-dev/ascribe/blob/main/schemas/sources-status.schema.json):

```json
{
  "$defs": {
    "CopyState": {
      "description": "The state of a copy.",
      "oneOf": [
        {
          "const": "current",
          "description": "It's the file the lock pins, and a snippet uses it, or it's the\nsource's tested examples' config or one of their observations.",
          "type": "string"
        },
        {
          "const": "changed",
          "description": "It isn't the file the lock pins.",
          "type": "string"
        },
        {
          "const": "missing",
          "description": "The lock lists it, and it isn't there.",
          "type": "string"
        },
        {
          "const": "unlocked",
          "description": "It's there, and the lock doesn't list it.",
          "type": "string"
        },
        {
          "const": "unused",
          "description": "No snippet names it.",
          "type": "string"
        },
        {
          "const": "not_copied",
          "description": "A snippet names it, and it hasn't been copied.",
          "type": "string"
        },
        {
          "const": "not_at_pin",
          "description": "A snippet names it, and it isn't in the repository at the pin.",
          "type": "string"
        }
      ]
    },
    "CopyStatus": {
      "description": "A file of a source's copies.",
      "properties": {
        "path": {
          "description": "Its path in the source.",
          "type": "string"
        },
        "state": {
          "$ref": "#/$defs/CopyState",
          "description": "Its state."
        }
      },
      "required": [
        "path",
        "state"
      ],
      "type": "object"
    },
    "SourceStatus": {
      "description": "One source's pin and copies.",
      "properties": {
        "branch": {
          "description": "The branch an update follows; `None` for the repository's default.",
          "type": [
            "string",
            "null"
          ]
        },
        "commit": {
          "description": "Its pin, when it has one to this repository.",
          "type": [
            "string",
            "null"
          ]
        },
        "files": {
          "description": "Each copy, and each file snippets name with none, by path.",
          "items": {
            "$ref": "#/$defs/CopyStatus"
          },
          "type": "array"
        },
        "git": {
          "description": "Its repository.",
          "type": "string"
        },
        "name": {
          "description": "The source.",
          "type": "string"
        }
      },
      "required": [
        "name",
        "git",
        "branch",
        "commit",
        "files"
      ],
      "type": "object"
    }
  },
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "description": "What `ascribe sources status --format json` writes: each source's pin and\nthe state of its copies, read from the files alone.",
  "properties": {
    "ascribe_version": {
      "description": "The version of Ascribe that wrote 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"
    },
    "sources": {
      "description": "Each source in another repository, in declaration order.",
      "items": {
        "$ref": "#/$defs/SourceStatus"
      },
      "type": "array"
    }
  },
  "required": [
    "schema_version",
    "ascribe_version",
    "sources"
  ],
  "title": "SourcesStatusReport",
  "type": "object"
}
```

## `ascribe sources update`

[`schemas/sources-update.schema.json`](https://github.com/ascribed-dev/ascribe/blob/main/schemas/sources-update.schema.json):

```json
{
  "$defs": {
    "BrokenExample": {
      "description": "A snippet that resolved at the base and doesn't in the working tree: its\nregion, file, or source is gone.",
      "properties": {
        "address": {
          "description": "Its address, as the page writes it.",
          "type": "string"
        },
        "problem": {
          "description": "The slug of the diagnostic `ascribe check` reports for it\n(`snippet-region-missing`).",
          "type": "string"
        },
        "reason": {
          "description": "Why it doesn't resolve, in a few words.",
          "type": "string"
        },
        "source": {
          "description": "The source the address names.",
          "type": "string"
        }
      },
      "required": [
        "address",
        "source",
        "problem",
        "reason"
      ],
      "type": "object"
    },
    "ChangedExample": {
      "description": "A snippet whose code changed.",
      "properties": {
        "added": {
          "description": "Lines only in the code now.",
          "minimum": 0,
          "type": "integer"
        },
        "address": {
          "description": "Its address, as the page writes it.",
          "type": "string"
        },
        "file": {
          "description": "The code file now, from the repository's root.",
          "type": "string"
        },
        "removed": {
          "description": "Lines only in the code at the base.",
          "minimum": 0,
          "type": "integer"
        },
        "source": {
          "description": "The source the address names.",
          "type": "string"
        },
        "was_file": {
          "description": "The code file at the base, when it was renamed since.",
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "address",
        "source",
        "file",
        "was_file",
        "added",
        "removed"
      ],
      "type": "object"
    },
    "CommitLine": {
      "description": "A commit, by its hash and the first line of its message.",
      "properties": {
        "commit": {
          "description": "Its full hash.",
          "type": "string"
        },
        "subject": {
          "description": "The first line of its message.",
          "type": "string"
        }
      },
      "required": [
        "commit",
        "subject"
      ],
      "type": "object"
    },
    "Commits": {
      "description": "The commits between two pins.",
      "properties": {
        "count": {
          "description": "How many.",
          "minimum": 0,
          "type": "integer"
        },
        "newest": {
          "description": "The newest, newest first, at most 20.",
          "items": {
            "$ref": "#/$defs/CommitLine"
          },
          "type": "array"
        }
      },
      "required": [
        "count",
        "newest"
      ],
      "type": "object"
    },
    "DriftPage": {
      "description": "A page with an example that changed or broke.",
      "properties": {
        "broken": {
          "description": "The examples that resolved at the base and don't now, by address.",
          "items": {
            "$ref": "#/$defs/BrokenExample"
          },
          "type": "array"
        },
        "builds": {
          "description": "The builds that show it with a changed example, in the order asked\nfor.",
          "items": {
            "type": "string"
          },
          "type": "array"
        },
        "examples": {
          "description": "The examples that changed, by address. Empty when only `broken`\nisn't.",
          "items": {
            "$ref": "#/$defs/ChangedExample"
          },
          "type": "array"
        },
        "page_changed": {
          "description": "Whether the page changed apart from its examples: its own file, or\nits resolved content through anything else it uses (a fragment, the\ncontent model), in any of those builds. When it didn't, the example\nchanged and the words around it didn't.",
          "type": "boolean"
        },
        "path": {
          "description": "The page's content path.",
          "type": "string"
        },
        "route": {
          "description": "Its route, in the first of the builds it's in.",
          "type": "string"
        }
      },
      "required": [
        "path",
        "route",
        "builds",
        "page_changed",
        "examples",
        "broken"
      ],
      "type": "object"
    },
    "ExampleState": {
      "description": "Where a tested example stands against the observation copied with it.",
      "oneOf": [
        {
          "const": "current",
          "description": "The observation lists the file as it is: the page says what it was\ntested with.",
          "type": "string"
        },
        {
          "const": "stale",
          "description": "The file changed, and the observation didn't follow.",
          "type": "string"
        },
        {
          "const": "unobserved",
          "description": "No observation lists the file.",
          "type": "string"
        }
      ]
    },
    "ExampleUpdate": {
      "description": "A tested example whose status an update changed.",
      "properties": {
        "path": {
          "description": "Its path in the source.",
          "type": "string"
        },
        "status": {
          "$ref": "#/$defs/ExampleState",
          "description": "Its status at the new pin."
        },
        "was": {
          "anyOf": [
            {
              "$ref": "#/$defs/ExampleState"
            },
            {
              "type": "null"
            }
          ],
          "description": "Its status before; `null` when it wasn't an example, or had no\ncopy."
        }
      },
      "required": [
        "path",
        "status",
        "was"
      ],
      "type": "object"
    },
    "Failure": {
      "description": "A file a snippet names that couldn't be copied.",
      "properties": {
        "path": {
          "description": "Its path in the source.",
          "type": "string"
        },
        "reason": {
          "description": "Why.",
          "type": "string"
        }
      },
      "required": [
        "path",
        "reason"
      ],
      "type": "object"
    },
    "FileChange": {
      "description": "What happened to a copy.",
      "oneOf": [
        {
          "const": "added",
          "description": "It's new.",
          "type": "string"
        },
        {
          "const": "changed",
          "description": "Its contents changed.",
          "type": "string"
        },
        {
          "const": "removed",
          "description": "It's gone.",
          "type": "string"
        }
      ]
    },
    "FileUpdate": {
      "description": "How a copy changed.",
      "properties": {
        "change": {
          "$ref": "#/$defs/FileChange",
          "description": "What happened to it."
        },
        "path": {
          "description": "Its path in the source.",
          "type": "string"
        }
      },
      "required": [
        "path",
        "change"
      ],
      "type": "object"
    },
    "SourceUpdate": {
      "description": "What `update` did for one source.",
      "properties": {
        "back": {
          "description": "Whether the new pin isn't after the old one: it moved back, or to\nanother line of history. No commits are counted then.",
          "type": "boolean"
        },
        "commits": {
          "anyOf": [
            {
              "$ref": "#/$defs/Commits"
            },
            {
              "type": "null"
            }
          ],
          "description": "The commits after the old pin up to the new one, when it moved\nforward from one and the repository could say."
        },
        "examples": {
          "description": "The tested examples snippets use whose status the new pin changed:\neach one that went stale, lost its observation, or became current,\nby path.",
          "items": {
            "$ref": "#/$defs/ExampleUpdate"
          },
          "type": "array"
        },
        "failed": {
          "description": "The files snippets name that couldn't be copied.",
          "items": {
            "$ref": "#/$defs/Failure"
          },
          "type": "array"
        },
        "files": {
          "description": "The copies that changed.",
          "items": {
            "$ref": "#/$defs/FileUpdate"
          },
          "type": "array"
        },
        "first_copy": {
          "description": "Whether these are the first files copied from the source.",
          "type": "boolean"
        },
        "followed": {
          "description": "The revision followed: `--to`'s, the branch, or `HEAD`.",
          "type": "string"
        },
        "from": {
          "description": "The pin before, if it had one.",
          "type": [
            "string",
            "null"
          ]
        },
        "git": {
          "description": "Its repository.",
          "type": "string"
        },
        "moved": {
          "description": "Whether the pin moved.",
          "type": "boolean"
        },
        "name": {
          "description": "The source.",
          "type": "string"
        },
        "to": {
          "description": "The pin now.",
          "type": "string"
        }
      },
      "required": [
        "name",
        "git",
        "followed",
        "from",
        "to",
        "moved",
        "back",
        "commits",
        "files",
        "examples",
        "failed",
        "first_copy"
      ],
      "type": "object"
    }
  },
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "description": "What `ascribe sources update --format json` writes: how each source's pin\nmoved, and the pages whose examples the new copies change.",
  "properties": {
    "ascribe_version": {
      "description": "The version of Ascribe that wrote it.",
      "type": "string"
    },
    "changed": {
      "description": "Whether any file changed: a copy, or the lock. With `--dry-run`,\nwhether any would.",
      "type": "boolean"
    },
    "pages": {
      "description": "The pages whose examples the new copies change, as `ascribe drift\n--format json` lists them; `null` when nothing changed, or when that\ncan't be told.",
      "items": {
        "$ref": "#/$defs/DriftPage"
      },
      "type": [
        "array",
        "null"
      ]
    },
    "pages_unavailable": {
      "description": "Why there are no `pages`, when there aren'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"
    },
    "sources": {
      "description": "What the update did for each source, in declaration order.",
      "items": {
        "$ref": "#/$defs/SourceUpdate"
      },
      "type": "array"
    },
    "written": {
      "description": "Whether the copies and the lock were written: false with\n`--dry-run`, which lists no pages, since it wrote no copies.",
      "type": "boolean"
    }
  },
  "required": [
    "schema_version",
    "ascribe_version",
    "changed",
    "written",
    "sources",
    "pages",
    "pages_unavailable"
  ],
  "title": "SourcesUpdateReport",
  "type": "object"
}
```

## `ascribe observe`

[`schemas/observe.schema.json`](https://github.com/ascribed-dev/ascribe/blob/main/schemas/observe.schema.json):

```json
{
  "$defs": {
    "Changes": {
      "description": "What differs between an observation and the one after it, or the folder.",
      "properties": {
        "added": {
          "description": "Examples it lists that the earlier one didn't, by path.",
          "items": {
            "type": "string"
          },
          "type": "array"
        },
        "changed": {
          "description": "Examples both list, with different hashes.",
          "items": {
            "type": "string"
          },
          "type": "array"
        },
        "environment": {
          "description": "Environment values that differ, in the newer one's order, then those\nonly the earlier one had.",
          "items": {
            "$ref": "#/$defs/EnvironmentChange"
          },
          "type": "array"
        },
        "removed": {
          "description": "Examples the earlier one listed that it doesn't.",
          "items": {
            "type": "string"
          },
          "type": "array"
        }
      },
      "required": [
        "added",
        "removed",
        "changed",
        "environment"
      ],
      "type": "object"
    },
    "EnvironmentChange": {
      "description": "An environment value that differs.",
      "properties": {
        "key": {
          "description": "The entry's key: `os`, `arch`, or the config's.",
          "type": "string"
        },
        "now": {
          "description": "What the newer one has; absent when it has none.",
          "type": [
            "string",
            "null"
          ]
        },
        "was": {
          "description": "What the earlier observation had; absent when it had none.",
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "key",
        "was",
        "now"
      ],
      "type": "object"
    },
    "EnvironmentRun": {
      "description": "An `[environment]` command that ran.",
      "properties": {
        "command": {
          "description": "The line of shell.",
          "type": "string"
        },
        "kept": {
          "description": "The first line it printed, as the observation records it.",
          "type": "string"
        },
        "key": {
          "description": "The entry's key.",
          "type": "string"
        }
      },
      "required": [
        "key",
        "command",
        "kept"
      ],
      "type": "object"
    },
    "TestRun": {
      "description": "The test command, and how it ended.",
      "properties": {
        "command": {
          "description": "The line of shell.",
          "type": "string"
        },
        "exit_code": {
          "description": "Its exit code; absent when a signal stopped it.",
          "type": [
            "integer",
            "null"
          ]
        },
        "passed": {
          "description": "Whether it exited with 0.",
          "type": "boolean"
        }
      },
      "required": [
        "command",
        "passed",
        "exit_code"
      ],
      "type": "object"
    }
  },
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "description": "What `ascribe observe --format json` writes: the commands it ran, how the\ntest ended, the observation it wrote, and what changed since the one it\nreplaced. With `--check`, nothing ran: the observation it compared, and\nwhether it's current.",
  "properties": {
    "ascribe_version": {
      "description": "The version of Ascribe that wrote it.",
      "type": "string"
    },
    "changes": {
      "anyOf": [
        {
          "$ref": "#/$defs/Changes"
        },
        {
          "type": "null"
        }
      ],
      "description": "What changed since the observation it replaced: absent when there was\nnone, or the test failed. With `--check`, what differs between the\nobservation and the folder."
    },
    "check": {
      "description": "Whether it was run with `--check`, which runs nothing.",
      "type": "boolean"
    },
    "config": {
      "description": "The config it ran, `ascribe.examples.toml`, in the current folder.",
      "type": "string"
    },
    "current": {
      "description": "With `--check`, whether the observation is current: it lists every\nexample in the folder, with the hash it has now, and nothing else.\nAbsent without `--check`.",
      "type": [
        "boolean",
        "null"
      ]
    },
    "environment": {
      "description": "The `[environment]` commands, in the order the config has them, each\nwith the line it kept. Empty with `--check`.",
      "items": {
        "$ref": "#/$defs/EnvironmentRun"
      },
      "type": "array"
    },
    "files": {
      "additionalProperties": {
        "type": "string"
      },
      "description": "The hash of each example, by path from the current folder. Empty when\nthe test failed.",
      "type": "object"
    },
    "observation": {
      "description": "The observation it wrote, from the current folder: none when the test\nfailed. With `--check`, the one it compared; with `--dry-run`, the\none it would write.",
      "type": [
        "string",
        "null"
      ]
    },
    "recorded": {
      "additionalProperties": {
        "type": "string"
      },
      "description": "The environment the observation records, by name: `os`, `arch`, and\neach entry's kept line. Empty when the test failed.",
      "type": "object"
    },
    "schema_version": {
      "description": "The version of this schema. It changes only when a field is removed\nor changes meaning.",
      "minimum": 0,
      "type": "integer"
    },
    "test": {
      "anyOf": [
        {
          "$ref": "#/$defs/TestRun"
        },
        {
          "type": "null"
        }
      ],
      "description": "The test command, and how it ended. Absent with `--check`."
    },
    "tested": {
      "description": "What a page says it was tested with: `Node 24.9.0 on Linux`. Absent\nwhen the test failed.",
      "type": [
        "string",
        "null"
      ]
    },
    "written": {
      "description": "Whether it wrote the observation: false with `--check` or\n`--dry-run`, or when the test failed.",
      "type": "boolean"
    }
  },
  "required": [
    "schema_version",
    "ascribe_version",
    "config",
    "check",
    "environment",
    "test",
    "observation",
    "written",
    "current",
    "changes",
    "recorded",
    "files",
    "tested"
  ],
  "title": "ObserveReport",
  "type": "object"
}
```
