{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "Manifest",
  "description": "`profile.toml`.",
  "type": "object",
  "properties": {
    "profile": { "description": "Identity.", "$ref": "#/$defs/Meta" },
    "requires": {
      "description": "Profiles this one builds on, by name: each a layer before it.",
      "type": "object",
      "additionalProperties": { "$ref": "#/$defs/RequirementSpec" }
    },
    "features": { "description": "What a target or a requirer may turn on.", "$ref": "#/$defs/Features" },
    "vars": {
      "description": "Variables templates and paths use; the target answers them.",
      "type": "object",
      "additionalProperties": { "$ref": "#/$defs/VarSpec" }
    },
    "merge": { "description": "Merge defaults.", "$ref": "#/$defs/MergeSpec", "default": {} },
    "scaffolds": {
      "description": "Groups of starter files, each written once, when none of its sentinels is there.",
      "type": "object",
      "additionalProperties": { "$ref": "#/$defs/ScaffoldSpec" }
    },
    "files": {
      "description": "Managed files, by path in the target, as found under `files/`; a path may use variables.",
      "type": "object",
      "additionalProperties": { "$ref": "#/$defs/FileSpec" }
    }
  },
  "additionalProperties": false,
  "required": ["profile"],
  "$defs": {
    "Meta": {
      "description": "The `[profile]` table.",
      "type": "object",
      "properties": {
        "name": { "description": "Names the profile: in its source, among the layers, in output, and in `from`.", "type": "string" },
        "version": { "description": "For humans; a git source is pinned by commit.", "type": ["string", "null"], "default": null },
        "description": { "description": "One line for humans.", "type": ["string", "null"], "default": null },
        "devset": { "description": "The devset versions the profile works with.", "type": ["string", "null"], "default": null }
      },
      "additionalProperties": false,
      "required": ["name"]
    },
    "RequirementSpec": {
      "description": "A requirement as written.",
      "type": "object",
      "properties": {
        "features": {
          "description": "Its features to turn on.",
          "type": "array",
          "items": { "description": "A feature of a profile; never `default`, which names the list of features on by default.", "type": "string" },
          "default": []
        },
        "default-features": { "description": "Whether its default features are on; `true` unless set.", "type": "boolean", "default": true },
        "optional": { "description": "Whether it applies only when a feature activates it.", "type": "boolean", "default": false },
        "git": { "description": "The repository of another source.", "type": ["string", "null"], "default": null },
        "tag": { "description": "Its tag; only with `git`.", "type": ["string", "null"], "default": null },
        "branch": { "description": "Its branch; only with `git`.", "type": ["string", "null"], "default": null },
        "rev": { "description": "Its commit; only with `git`.", "type": ["string", "null"], "default": null },
        "path": { "description": "The directory in the repository its profiles are in; only with `git`.", "type": ["string", "null"], "default": null }
      },
      "additionalProperties": false
    },
    "Features": {
      "description": "The `[features]` table: `default`, and every feature with what it turns on.",
      "type": "object",
      "additionalProperties": { "type": "array", "items": { "type": "string" } }
    },
    "VarSpec": {
      "description": "A `[vars.name]` entry.",
      "type": "object",
      "properties": {
        "prompt": { "description": "What to ask; the name if unset.", "type": ["string", "null"], "default": null },
        "default": { "description": "The answer offered.", "type": ["string", "null"], "default": null }
      },
      "additionalProperties": false
    },
    "MergeSpec": {
      "description": "A `[merge]` table: defaults in `profile.toml`, decisions in `.devset/config.toml`.",
      "type": "object",
      "properties": {
        "on-conflict": { "description": "What a run does when a file conflicts.", "anyOf": [{ "$ref": "#/$defs/OnConflict" }, { "type": "null" }] },
        "driver": {
          "description": "A merge program, with `%O %A %B %P` substituted.\n\nFrom a profile it is only a suggestion: devset never runs a program a profile names.",
          "type": ["string", "null"]
        }
      },
      "additionalProperties": false
    },
    "OnConflict": {
      "description": "What a run does when a file conflicts.",
      "oneOf": [
        { "description": "Write every other file and advance the lock; conflicts wait in `.devset/conflicts/`.", "type": "string", "const": "apply-others" },
        { "description": "Write nothing but the conflicts until every one is resolved.", "type": "string", "const": "apply-none" }
      ]
    },
    "ScaffoldSpec": {
      "description": "A `[scaffolds.name]` entry: a group of starter files, written together once.",
      "type": "object",
      "properties": {
        "unless": {
          "description": "Paths or globs of which any, there before the run, means the target has its own: the group\nis not written. A string or a list; none writes the group when the profile is first\napplied.",
          "$ref": "#/$defs/Patterns"
        }
      },
      "additionalProperties": false
    },
    "Patterns": {
      "description": "Paths or globs in the target: a string, or a list of them.",
      "anyOf": [{ "description": "One.", "type": "string" }, { "description": "Several.", "type": "array", "items": { "type": "string" } }]
    },
    "FileSpec": {
      "description": "A profile's `[files.\"path\"]` entry.",
      "type": "object",
      "properties": {
        "policy": {
          "description": "How devset manages the file; `owned` unless set, and `once` in a scaffold.",
          "anyOf": [{ "$ref": "#/$defs/Policy" }, { "type": "null" }],
          "default": null
        },
        "validate": {
          "description": "How a merged result is checked; inferred from the extension unless set.",
          "anyOf": [{ "$ref": "#/$defs/Format" }, { "type": "null" }],
          "default": null
        },
        "template": {
          "description": "Whether the file is a template, rendered with the answers and the `devset` object.",
          "type": "boolean",
          "default": false
        },
        "scope": {
          "description": "How much of the file devset owns: all of it, the keys the payload defines, or a block.",
          "$ref": "#/$defs/Scope",
          "default": "file"
        },
        "comment": {
          "description": "The comment syntax of a block's markers, where devset does not know the file type's.",
          "anyOf": [{ "$ref": "#/$defs/Comment" }, { "type": "null" }],
          "default": null
        },
        "executable": {
          "description": "Whether devset writes the file executable, mode 755, as a script it ships must be.",
          "type": "boolean",
          "default": false
        },
        "scaffold": { "description": "The scaffold group the file belongs to, written with it.", "type": ["string", "null"], "default": null },
        "starter": {
          "description": "For a part, a file under `files/` the target's file starts from when it is absent.",
          "type": ["string", "null"],
          "default": null
        },
        "when": { "description": "When the entry applies; always, unless set.", "$ref": "#/$defs/When" }
      },
      "additionalProperties": false
    },
    "Policy": {
      "description": "How devset manages a file.",
      "oneOf": [
        { "description": "The profile is authoritative; local edits are drift.", "type": "string", "const": "owned" },
        { "description": "Local edits are kept, and merged with the profile's.", "type": "string", "const": "merge" },
        { "description": "Written once if absent; the target's from then on.", "type": "string", "const": "once" }
      ]
    },
    "Format": {
      "description": "How a file is checked after a merge: it must parse, with no duplicate keys.",
      "oneOf": [
        { "description": "TOML.", "type": "string", "const": "toml" },
        { "description": "JSON with comments and trailing commas (JSONC), a superset of JSON.", "type": "string", "const": "json" },
        { "description": "YAML.", "type": "string", "const": "yaml" },
        { "description": "Not checked.", "type": "string", "const": "none" }
      ]
    },
    "Scope": {
      "description": "How much of a file a profile owns.",
      "oneOf": [
        { "description": "The whole file.", "type": "string", "const": "file" },
        { "description": "Every leaf the payload, a partial document, defines; the file's other keys are the target's.", "type": "string", "const": "keys" },
        { "description": "One comment-marked block, named after the profile; the file's other lines are the target's.", "type": "string", "const": "block" }
      ]
    },
    "Comment": {
      "description": "A line comment syntax, which a block's markers are written in.",
      "oneOf": [
        { "description": "`#`: shells, TOML, YAML, Python, `.gitignore`.", "type": "string", "const": "#" },
        { "description": "`//`: C, Rust, JavaScript, JSONC.", "type": "string", "const": "//" },
        { "description": "`--`: SQL, Lua, Haskell.", "type": "string", "const": "--" },
        { "description": "`;`: INI, Lisp, assembly.", "type": "string", "const": ";" },
        { "description": "`%`: TeX, Erlang, MATLAB.", "type": "string", "const": "%" },
        { "description": "`/* */`: CSS.", "type": "string", "const": "/*" },
        { "description": "`<!-- -->`: Markdown, HTML, XML.", "type": "string", "const": "<!--" }
      ]
    },
    "When": {
      "description": "When an entry applies: every condition holds.",
      "type": "object",
      "properties": {
        "features": {
          "description": "Features of this profile that are on.",
          "type": "array",
          "items": { "description": "A feature of a profile; never `default`, which names the list of features on by default.", "type": "string" },
          "default": []
        },
        "profiles": {
          "description": "Profiles active in the target.",
          "type": "array",
          "items": { "description": "A profile's name, unique in its source and among a target's layers.", "type": "string" },
          "default": []
        },
        "vars": {
          "description": "Variables of this profile, each answered with one of its values.",
          "type": "object",
          "additionalProperties": { "type": "array", "items": { "type": "string" } },
          "default": {}
        },
        "exists": {
          "description": "Paths or globs that will exist when the run finishes, whole files or parts; may use\nvariables.",
          "type": "array",
          "items": { "type": "string" },
          "default": []
        }
      },
      "additionalProperties": false
    }
  }
}
