{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://openwop.dev/spec/v1/artifact-type-pack-manifest.schema.json",
  "title": "ArtifactTypePackManifest",
  "description": "Manifest for a published OpenWOP artifact-type pack — `pack.json` at the pack root with `kind: \"artifact-type\"`. Peer to `node-pack-manifest.schema.json` (RFC 0003), `workflow-chain-pack-manifest.schema.json` (RFC 0013), and `prompt-pack-manifest.schema.json` (RFC 0028); disjoint from all three via the `kind` discriminator. See `spec/v1/artifact-type-packs.md` for the canonical contract and RFC 0071 for the rationale.\n\nArtifact-type packs distribute typed artifact definitions — the JSON Schema, rendering hint, lifecycle, and export-format hints for the rich outputs workflow nodes produce — via the same signed-tarball + Ed25519 + SRI pipeline that already serves node, workflow-chain, and prompt packs. When a host installs an artifact-type pack and advertises `host.artifactTypes: { supported: true }`, it validates produced artifacts of the declared `artifactTypeId`s against their `schemaRef` before emitting `artifact.created`.",
  "type": "object",
  "required": ["name", "version", "kind", "engines", "artifactTypes"],
  "additionalProperties": false,
  "properties": {
    "kind": {
      "type": "string",
      "const": "artifact-type",
      "description": "Pack kind discriminator. MUST be the literal string `\"artifact-type\"`. Manifests carrying any other `kind` value validate against a different pack-manifest schema."
    },
    "name": {
      "type": "string",
      "description": "Reverse-DNS pack name per `node-packs.md` §Naming. Reserved scopes are identical (`core.*` / `vendor.<org>.*` / `community.<author>.*` / `private.<host>.*`). Mirror of `prompt-pack-manifest.schema.json#/properties/name`.",
      "pattern": "^(core|vendor|community|private)\\.[a-z][a-z0-9_-]*(\\.[a-z][a-zA-Z0-9_-]*)+$",
      "minLength": 1,
      "maxLength": 256
    },
    "version": {
      "type": "string",
      "description": "Pack-level SemVer 2.0.0.",
      "pattern": "^\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?(?:\\+[0-9A-Za-z.-]+)?$"
    },
    "description": { "type": "string", "maxLength": 1024 },
    "author": { "type": "string" },
    "license": { "type": "string", "description": "SPDX license identifier (e.g., `Apache-2.0`, `MIT`)." },
    "homepage": { "type": "string", "format": "uri" },
    "repository": { "type": "string", "format": "uri" },
    "keywords": {
      "type": "array",
      "items": { "type": "string", "maxLength": 64 },
      "maxItems": 50
    },
    "engines": {
      "type": "object",
      "required": ["openwop"],
      "properties": {
        "openwop": {
          "type": "string",
          "description": "Semver range — which openwop protocol versions this pack works against. Example: `>=1.1 <2.0.0`."
        }
      },
      "additionalProperties": true
    },
    "dependencies": {
      "type": "object",
      "additionalProperties": { "type": "string" },
      "description": "Other packs this pack depends on. Map of pack name → semver range. Resolved transitively at workflow-register time."
    },
    "peerDependencies": {
      "type": "object",
      "additionalProperties": { "type": "string" },
      "description": "Engine-supplied capabilities the pack consumes (e.g., `{ \"host.artifactTypes\": \"supported\" }`). Resolved against the host's advertised capabilities at register time."
    },
    "artifactTypes": {
      "type": "array",
      "minItems": 1,
      "items": { "$ref": "#/$defs/ArtifactType" },
      "description": "Typed artifact definitions this pack contributes. Each MUST have a unique `artifactTypeId` within the pack."
    },
    "signing": { "$ref": "#/$defs/Signing" }
  },
  "$defs": {
    "ArtifactType": {
      "type": "object",
      "required": ["artifactTypeId", "schemaRef"],
      "additionalProperties": false,
      "properties": {
        "artifactTypeId": {
          "type": "string",
          "description": "Reverse-DNS artifact-type identifier. Same pattern and reserved scopes as a pack `name`. This is the value `WorkflowNode.artifactType`, `nodes[].artifact.typeId`, and `artifact.created.artifactType` reference. Third parties MUST NOT publish under `core.*`.",
          "pattern": "^(core|vendor|community|private)\\.[a-z][a-z0-9_-]*(\\.[a-z][a-zA-Z0-9_-]*)+$",
          "minLength": 1,
          "maxLength": 256
        },
        "schemaRef": {
          "type": "string",
          "minLength": 1,
          "description": "Path inside the pack tarball to the artifact's JSON Schema (Draft 2020-12). The target schema MUST set `additionalProperties: false` at its top level and declare an `$id` under `{HostBase}/schemas/artifacts/{artifactTypeId}.schema.json`, mirroring the envelope convention in `ai-envelope.md` §\"Canonical schema location\"."
        },
        "schemaVersion": {
          "type": "integer",
          "minimum": 0,
          "description": "Non-negative integer artifact-schema version, parallel to the per-kind integer in `capabilities.schemaVersions`. Absent ⇒ treated as 0. Bumped when the artifact schema changes shape. A version *declaration*, not a validation guarantee (RFC 0075 / P1-2)."
        },
        "validation": {
          "type": "string",
          "enum": ["open", "closed"],
          "description": "RFC 0075. Strictness contract of the artifact's `schemaRef` schema. `closed` ⇒ closed-world (`additionalProperties: false`); a consumer MAY rely on the absence of unknown fields. `open` (recommended for AI/LLM-produced artifacts) ⇒ the schema tolerates extra fields to absorb model drift; consumers MUST ignore unknown fields per COMPATIBILITY.md §2.1. Absent ⇒ `open` (the forward-compatible default). The `additionalProperties:false` requirement on `schemaRef` was relaxed MUST → SHOULD by RFC 0075: a closed-world artifact contract contradicts the protocol's own forward-compat mandate and the first AI-native adopter cannot meet it."
        },
        "displayName": {
          "type": "string",
          "description": "Human-readable label for artifact-management UIs. Non-normative."
        },
        "rendering": { "$ref": "#/$defs/RenderingHint" },
        "exportFormats": {
          "type": "array",
          "items": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "pattern": "^([a-z][a-z0-9]*|vendor\\.[a-z][a-z0-9-]*\\.[a-z][a-z0-9-]*|x-[a-z][a-z0-9-]*)$"
          },
          "uniqueItems": true,
          "description": "Export-format identifiers (hints) a renderer MAY offer. Spec-reserved core identifiers carry interoperable meaning (the lowercase file-extension / common name): `pdf`, `pptx`, `docx`, `xlsx`, `md`, `html`, `txt`, `csv`, `json`, `png`, `svg`, `jpeg`, `step`, `stl`, `dxf`. Domain-specific formats outside the core set MUST be `vendor.<org>.<format>`- or `x-<format>`-prefixed (mirrors the `requiredModelCapabilities` reserved-core + extension idiom). Advisory: this spec assigns no byte-level production semantics to any identifier — it standardizes the identifier so two hosts agree what `pptx` names, not how the bytes are produced."
        },
        "syncOn": {
          "type": "string",
          "enum": ["completion", "approval", "manual"],
          "description": "When the host registers the artifact as durable. Mirrors `node-pack-manifest.schema.json#/$defs/PackNode/properties/artifact/properties/syncOn`. Default `completion`."
        },
        "supportsCheckpoint": {
          "type": "boolean",
          "description": "True if the artifact participates in checkpoint/resume. Mirrors the `PackNode.artifact.supportsCheckpoint` field."
        },
        "versionable": {
          "type": "boolean",
          "description": "True if the host SHOULD retain prior versions of the artifact. Non-normative storage hint."
        },
        "diffable": {
          "type": "boolean",
          "description": "True if the artifact's schema supports structural diffing (informs run-diff tooling per rest-endpoints.md §:diff). Non-normative."
        }
      }
    },
    "RenderingHint": {
      "type": "object",
      "additionalProperties": false,
      "description": "Advisory rendering hint. Reuses the closed vocabulary defined in ai-envelope.md §\"Rendering hints\" (RFC 0055). Advisory only — consumers MUST degrade gracefully and MUST NOT treat this as a validation input. The `card` display value is reserved for envelopes and is excluded here (durable artifacts are not transient chat cards).",
      "properties": {
        "display": {
          "type": "string",
          "enum": ["markdown", "code", "image", "audio", "file"],
          "description": "How a consumer SHOULD render the artifact when it recognizes the value."
        },
        "mimeType": { "type": "string", "maxLength": 255 },
        "lang": { "type": "string", "maxLength": 64, "description": "Language hint for `display: code`." },
        "alt": { "type": "string", "maxLength": 1024, "description": "Accessibility text for image/audio/file." },
        "title": { "type": "string", "maxLength": 255 }
      }
    },
    "Signing": {
      "type": "object",
      "description": "Optional signing metadata. See node-packs.md §signing.",
      "additionalProperties": false,
      "properties": {
        "publicKeyRef": { "type": "string", "description": "Path inside the tarball to the Ed25519 public key (PEM-encoded)." },
        "signatureRef": { "type": "string", "description": "Path to the detached signature over `pack.json`." },
        "method": {
          "type": "string",
          "enum": ["manual", "sigstore"],
          "description": "Signing method. `manual` uses publicKeyRef + signatureRef; `sigstore` uses a Sigstore bundle at `pack.json.sigstore`."
        }
      }
    }
  }
}
