{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/goodeye-dev/json-schema/goodeye-dev-save-workflow-input-schema.json",
  "title": "SaveWorkflowInput",
  "description": "Publish-skill payload aligned with the Claude Code skills convention.\n\nRequired top-level fields mirror a Claude Code ``SKILL.md`` frontmatter:\n``name`` (kebab-case handle) and ``description`` (one-sentence summary).\n``outcome`` and ``tags`` are top-level discovery facets surfaced by\n``list_skills``; everything else lives in the ``body`` and is opaque\nto the registry.\n\nSkills are always private to the caller. Public sharing is the\n``templates`` surface (``publish_template_version`` / ``fork_template``);\nthere is no ``visibility`` knob on a skill row.\n\nUnknown fields are silently dropped (``extra=\"ignore\"``). Fields such as\n``manifest``, ``visibility``, and ``kpi`` do not cause validation errors\nand are simply ignored.\n\nThe server accepts both ``slug`` and ``name`` as input for the skill\nhandle. Responses return both ``name`` and ``slug`` fields with identical\nvalues.",
  "x-generated": "2026-11-10",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/goodeye-dev-openapi.json#/components/schemas/SaveWorkflowInput",
  "properties": {
    "name": {
      "type": "string",
      "maxLength": 128,
      "minLength": 1,
      "pattern": "^[a-z0-9][a-z0-9-]*$",
      "title": "Name",
      "description": "Skill name in kebab-case (lowercase letters, digits, and hyphens). Doubles as the slug and is unique within your namespace."
    },
    "skill_id": {
      "anyOf": [
        {
          "type": "string",
          "format": "uuid"
        },
        {
          "type": "null"
        }
      ],
      "title": "Skill Id",
      "description": "Target a specific skill to edit. Omit it to save in your own namespace: the call updates your own skill of this name, or creates a new one. Supply it to update a specific skill you own or have edit access to; ``name`` must match that skill's name."
    },
    "description": {
      "type": "string",
      "maxLength": 1024,
      "minLength": 1,
      "title": "Description",
      "description": "One-sentence summary of what the skill does and when to use it. This is the text that decides when the skill fires."
    },
    "body": {
      "type": "string",
      "minLength": 1,
      "title": "Body",
      "description": "The skill runbook as markdown: the full set of instructions an agent follows on the user's behalf."
    },
    "tags": {
      "items": {
        "type": "string"
      },
      "type": "array",
      "title": "Tags",
      "description": "Discovery tags surfaced when listing and searching skills. When omitted on an update, the existing tags are preserved; send an empty list to clear them."
    },
    "outcome": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Outcome",
      "description": "The business outcome this skill is meant to drive, surfaced as a discovery facet. When omitted on an update, the existing outcome is preserved; send null to clear it."
    },
    "verifiers": {
      "anyOf": [
        {
          "items": {
            "additionalProperties": true,
            "type": "object"
          },
          "type": "array"
        },
        {
          "type": "null"
        }
      ],
      "title": "Verifiers",
      "description": "Semantic verifier references this skill invokes on agent output. Each entry names a deployed verifier by id or id@version. When omitted on an update, the existing list is preserved unchanged."
    },
    "image_generators": {
      "anyOf": [
        {
          "items": {
            "additionalProperties": true,
            "type": "object"
          },
          "type": "array"
        },
        {
          "type": "null"
        }
      ],
      "title": "Image Generators",
      "description": "Image generator references for this skill. Each entry binds a local name (used in generation steps) to a generator reference. The reference is either a platform-managed quality tier (``system:<tier>``) or a specific deployed generator (``uuid`` or ``uuid@version``). When omitted on an update, the existing list is preserved unchanged."
    },
    "files": {
      "anyOf": [
        {
          "items": {
            "$ref": "#/$defs/FileEntry"
          },
          "type": "array"
        },
        {
          "type": "null"
        }
      ],
      "title": "Files",
      "description": "Sibling files bundled with the skill. Include a 'demo/README.md' writeup with images referenced by relative path inside 'demo/' to render a visual demo on the public template page once the skill is published as a template. A supplied list is a full snapshot: any path you do not include is deleted. Omitting the field entirely preserves the existing tree unchanged; to change specific files without resending the rest, use `update_skill_files` instead."
    },
    "expected_version_token": {
      "anyOf": [
        {
          "type": "string",
          "format": "uuid"
        },
        {
          "type": "null"
        }
      ],
      "title": "Expected Version Token",
      "description": "Concurrency token from the latest known version, required when editing an existing skill so a stale overwrite is rejected. Omit when creating a new skill."
    },
    "source": {
      "anyOf": [
        {
          "type": "string",
          "enum": [
            "manual",
            "teach",
            "optimization",
            "description_optimization",
            "audit"
          ]
        },
        {
          "type": "null"
        }
      ],
      "title": "Source",
      "description": "Attributes the saved version to how it was produced ('manual', 'teach', 'optimization', 'description_optimization', or 'audit'); omit to leave it unset."
    }
  },
  "type": "object",
  "required": [
    "name",
    "description",
    "body",
    "expected_version_token"
  ],
  "$defs": {
    "FileEntry": {
      "properties": {
        "path": {
          "type": "string",
          "maxLength": 1024,
          "title": "Path",
          "description": "Normalized relative POSIX path for this file within the bundle (e.g. 'references/rubric.md'). Must not be empty, absolute, contain '..' or '.' segments, empty segments (a trailing slash or 'foo//bar'), backslashes, or ASCII control characters."
        },
        "executable": {
          "type": "boolean",
          "title": "Executable",
          "description": "Whether the file should be marked executable when extracted.",
          "default": false
        },
        "purpose": {
          "anyOf": [
            {
              "type": "string",
              "maxLength": 1024
            },
            {
              "type": "null"
            }
          ],
          "title": "Purpose",
          "description": "Short label describing the file's role in the skill (optional)."
        },
        "content": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Content",
          "description": "Inline file content as a UTF-8 text string, stored verbatim. Use 'content_base64' for binary files. Mutually exclusive with 'content_base64' and 'sha256'."
        },
        "content_base64": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Content Base64",
          "description": "Inline binary file content as base64-encoded bytes, decoded on receipt. Mutually exclusive with 'content' and 'sha256'."
        },
        "sha256": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Sha256",
          "description": "Hex SHA-256 digest referencing a blob already known to the registry. Mutually exclusive with 'content' and 'content_base64'."
        }
      },
      "type": "object",
      "required": [
        "path"
      ],
      "title": "FileEntry",
      "description": "A single file in a skill bundle.\n\nExactly one of ``content``, ``content_base64``, or ``sha256`` must be\nprovided. Provide ``content`` to inline a UTF-8 text file (it is stored\nverbatim as the given text; it is never reinterpreted as base64). Provide\n``content_base64`` to inline a binary file (base64-encoded bytes, decoded\non receipt). Provide ``sha256`` to reference a blob the registry can\nalready read."
    }
  }
}