{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://unifold.md/0.3/unifold.schema.json",
  "title": "unifold manifest",
  "description": "A unifold.yaml: the manifest of a unit. See https://unifold.md/spec. Keys marked 'root only' are allowed in the manifest of the project root, which is the one that has the key `unifold`.",
  "type": "object",
  "required": [
    "responsibility"
  ],
  "additionalProperties": false,
  "patternProperties": {
    "^x-": {
      "description": "Project-specific; ignored by tools."
    }
  },
  "properties": {
    "unifold": {
      "description": "Root only, and required there: the version of the standard, as a string.",
      "enum": [
        "0.3"
      ]
    },
    "name": {
      "description": "Root only. The name of the project. Default: the name of the root folder. Every root manifest should set it, so that generated files do not depend on the folder name.",
      "type": "string",
      "minLength": 1,
      "pattern": "^[^\\n]*\\S[^\\n]*$"
    },
    "responsibility": {
      "description": "What the unit is responsible for, in one sentence. `TODO` marks a draft.",
      "type": "string",
      "minLength": 1,
      "pattern": "^[^\\n]*\\S[^\\n]*\\n?$",
      "not": {
        "pattern": "^<[^<>]*>\\s*$"
      }
    },
    "kind": {
      "description": "Free text. logic, stateful and adapter are well known and carry rules. Required on a unit with children below the root.",
      "type": "string",
      "minLength": 1,
      "pattern": "^[^\\n]*\\S[^\\n]*$"
    },
    "status": {
      "description": "planned: code missing or incomplete. active: the default. deprecated: to be removed.",
      "enum": [
        "planned",
        "active",
        "deprecated"
      ],
      "default": "active"
    },
    "owner": {
      "description": "Team or person responsible. Units below inherit it.",
      "type": "string",
      "minLength": 1,
      "pattern": "^[^\\n]*\\S[^\\n]*$"
    },
    "secret": {
      "description": "What the unit hides because it is hard to get right or likely to change.",
      "type": "object",
      "required": [
        "what",
        "decision"
      ],
      "additionalProperties": false,
      "properties": {
        "what": {
          "type": "string",
          "minLength": 1,
          "pattern": "^[^\\n]*\\S[^\\n]*$",
          "description": "One line."
        },
        "decision": {
          "type": "string",
          "minLength": 1,
          "pattern": "^(?![A-Za-z][A-Za-z0-9+.-]*:)(?![/\\\\]).*\\.md$",
          "description": "A path, relative to the manifest, to a decision of this unit or of an ancestor that says why this is the secret."
        }
      }
    },
    "not": {
      "description": "What the unit does not do. An item is one line, or one line mapped to the need of this unit through which the thing is delegated.",
      "type": "array",
      "items": {
        "oneOf": [
          {
            "type": "string",
            "minLength": 1,
            "pattern": "^[^\\n]*\\S[^\\n]*$"
          },
          {
            "type": "object",
            "minProperties": 1,
            "maxProperties": 1,
            "additionalProperties": {
              "$ref": "#/$defs/entryName"
            }
          }
        ]
      }
    },
    "capabilities": {
      "description": "The capabilities, keyed by name.",
      "type": "object",
      "propertyNames": {
        "$ref": "#/$defs/entryName"
      },
      "additionalProperties": {
        "oneOf": [
          {
            "$ref": "#/$defs/summary"
          },
          {
            "$ref": "#/$defs/capability"
          }
        ]
      }
    },
    "needs": {
      "description": "The needs, keyed by name. A need says what is required, never who supplies it. The parent wires it, unless this unit meets it inside, in its own wiring.",
      "type": "object",
      "propertyNames": {
        "$ref": "#/$defs/entryName"
      },
      "additionalProperties": {
        "oneOf": [
          {
            "$ref": "#/$defs/summary"
          },
          {
            "$ref": "#/$defs/need"
          }
        ]
      }
    },
    "wiring": {
      "description": "Connections inside this unit; every connection is written out, there is no wildcard and no connection by equal names. Key: `child.need`, a need of this unit (met by a child), or a capability of this unit (implemented by a child). Value: the provider, or a mapping of named alternatives (the first is the default). A reference is split at its last dot; `child` is a folder name or a path relative to the unit.",
      "type": "object",
      "propertyNames": {
        "type": "string",
        "minLength": 1,
        "pattern": "^[^*]+$"
      },
      "additionalProperties": {
        "oneOf": [
          {
            "$ref": "#/$defs/provider"
          },
          {
            "type": "object",
            "minProperties": 1,
            "additionalProperties": {
              "$ref": "#/$defs/provider"
            }
          }
        ]
      }
    },
    "scenarios": {
      "description": "The scenarios this unit holds, keyed by name: one story as the chain of calls it causes.",
      "type": "object",
      "propertyNames": {
        "$ref": "#/$defs/entryName"
      },
      "additionalProperties": {
        "$ref": "#/$defs/scenario"
      }
    },
    "ignore": {
      "description": "Root only. Folders, relative to the root, that are not searched for units.",
      "type": "array",
      "items": {
        "type": "string",
        "minLength": 1
      }
    },
    "diagrams": {
      "description": "Root only. parents: diagrams for units that have children. all: also a context diagram for every other unit. none: no diagrams.",
      "enum": [
        "parents",
        "all",
        "none"
      ],
      "default": "parents"
    },
    "diagram-links": {
      "description": "Root only. How boxes in generated diagrams link: none, relative, or the base URL of the repository's file view.",
      "type": "string",
      "pattern": "^(none|relative|https?://.+)$",
      "default": "none"
    },
    "max-lines": {
      "description": "Root only. The line limit for hand-written documents.",
      "type": "integer",
      "minimum": 1,
      "default": 200
    }
  },
  "$defs": {
    "entryName": {
      "description": "Letters, digits, '-' and '_'. No dot.",
      "type": "string",
      "pattern": "^[A-Za-z0-9][A-Za-z0-9_-]*$"
    },
    "summary": {
      "description": "One line of text. `TODO` marks a draft and is reported as a warning.",
      "type": "string",
      "minLength": 1,
      "pattern": "^[^\\n]*\\S[^\\n]*\\n?$",
      "not": {
        "pattern": "^<[^<>]*>\\s*$"
      }
    },
    "provider": {
      "description": "`child.capability`, or, for a need of a child, a need of this unit (bare name) that this unit does not meet inside. A connection joins two units; a folder is never a provider.",
      "type": "string",
      "minLength": 1,
      "pattern": "^[^*]"
    },
    "capability": {
      "type": "object",
      "required": [
        "summary"
      ],
      "additionalProperties": false,
      "patternProperties": {
        "^x-": {}
      },
      "properties": {
        "summary": {
          "$ref": "#/$defs/summary"
        },
        "kind": {
          "description": "Free text for the technical nature, for example library, http, events, cli.",
          "type": "string",
          "minLength": 1,
          "pattern": "^[^\\n]*\\S[^\\n]*$"
        },
        "contract": {
          "oneOf": [
            {
              "type": "string",
              "minLength": 1,
              "pattern": "^(?![A-Za-z][A-Za-z0-9+.-]*:)(?![/\\\\])[^#]*\\.md(#.*)?$"
            },
            {
              "type": "object",
              "required": [
                "path"
              ],
              "additionalProperties": false,
              "properties": {
                "path": {
                  "type": "string",
                  "minLength": 1,
                  "pattern": "^(?![A-Za-z][A-Za-z0-9+.-]*:)(?![/\\\\])[^#]*\\.md(#.*)?$"
                },
                "parts": {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "pattern": "^[a-z0-9][a-z0-9-]*$"
                  },
                  "description": "The parts of the contract this side uses: the `###` headings under `## Parts`, or the files of a contract folder."
                }
              }
            }
          ],
          "description": "The contract of the connection: a path, relative to the folder of the manifest, to a Markdown file in docs/references/ of this unit or of an ancestor, or a mapping with `path` and `parts`. Not a URL. Both sides of a connection name the same document."
        }
      }
    },
    "need": {
      "type": "object",
      "required": [
        "summary"
      ],
      "additionalProperties": false,
      "patternProperties": {
        "^x-": {}
      },
      "properties": {
        "summary": {
          "$ref": "#/$defs/summary"
        },
        "kind": {
          "description": "Free text for the technical nature.",
          "type": "string",
          "minLength": 1,
          "pattern": "^[^\\n]*\\S[^\\n]*$"
        },
        "contract": {
          "oneOf": [
            {
              "type": "string",
              "minLength": 1,
              "pattern": "^(?![A-Za-z][A-Za-z0-9+.-]*:)(?![/\\\\])[^#]*\\.md(#.*)?$"
            },
            {
              "type": "object",
              "required": [
                "path"
              ],
              "additionalProperties": false,
              "properties": {
                "path": {
                  "type": "string",
                  "minLength": 1,
                  "pattern": "^(?![A-Za-z][A-Za-z0-9+.-]*:)(?![/\\\\])[^#]*\\.md(#.*)?$"
                },
                "parts": {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "pattern": "^[a-z0-9][a-z0-9-]*$"
                  },
                  "description": "The parts of the contract this side uses: the `###` headings under `## Parts`, or the files of a contract folder."
                }
              }
            }
          ],
          "description": "The contract of the connection: a path, relative to the folder of the manifest, to a Markdown file in docs/references/ of this unit or of an ancestor, or a mapping with `path` and `parts`. Not a URL. Both sides of a connection name the same document."
        },
        "optional": {
          "description": "true if the need may stay unconnected.",
          "type": "boolean",
          "default": false
        },
        "platform": {
          "description": "On a need of the root only: true marks a service of the host such as time or randomness, with a controllable fake. The adapter rule does not apply to it.",
          "type": "boolean",
          "default": false
        }
      }
    },
    "scenario": {
      "type": "object",
      "required": [
        "summary",
        "hops",
        "expect"
      ],
      "additionalProperties": false,
      "patternProperties": {
        "^x-": {}
      },
      "properties": {
        "summary": {
          "$ref": "#/$defs/summary"
        },
        "source": {
          "type": "string",
          "description": "Free text: where the story comes from."
        },
        "hops": {
          "type": "array",
          "minItems": 1,
          "items": {
            "type": "string",
            "pattern": "^\\s*\\S.*\\s->\\s.*\\S\\.[A-Za-z0-9][A-Za-z0-9_-]*\\s*$"
          },
          "description": "`caller -> target.part`: units by their path from this unit, a capability or need of this unit bare."
        },
        "expect": {
          "type": "string",
          "minLength": 1,
          "description": "The observable outcome."
        }
      }
    }
  }
}
