{
	"$schema": "https://json-schema.org/draft/2020-12/schema",
	"$id": "https://xript.dev/schema/manifest/v0.8.json",
	"title": "xript Manifest",
	"description": "Declarative manifest for xript-enabled applications. Defines the API surface, capabilities, and documentation for modders.",
	"type": "object",
	"required": [
		"xript",
		"name"
	],
	"properties": {
		"$schema": {
			"description": "Optional JSON Schema reference for editor autocomplete. Should point to the xript manifest schema URL.",
			"type": "string",
			"format": "uri"
		},
		"xript": {
			"description": "The xript specification version this manifest conforms to.",
			"type": "string",
			"pattern": "^\\d+\\.\\d+$",
			"examples": [
				"0.3",
				"0.6",
				"0.8"
			]
		},
		"extends": {
			"description": "One or more base manifests to inherit from. Resolved and deep-merged before validation: maps key-merge, arrays append, scalars are child-wins, and duplicate ids are an error. Paths are filesystem-relative to this manifest. Resolution is transitive with cycle detection.",
			"oneOf": [
				{
					"type": "string"
				},
				{
					"type": "array",
					"items": {
						"type": "string"
					},
					"minItems": 1
				}
			]
		},
		"name": {
			"description": "Machine-readable identifier for this application. Used in generated package names and documentation URLs.",
			"type": "string",
			"pattern": "^[a-z][a-z0-9-]*$",
			"minLength": 1,
			"maxLength": 64
		},
		"version": {
			"description": "The version of this application's scripting API. Follows semver.",
			"type": "string",
			"pattern": "^\\d+\\.\\d+\\.\\d+(-[a-zA-Z0-9.]+)?$",
			"examples": [
				"1.0.0",
				"0.3.0-beta.1"
			]
		},
		"title": {
			"description": "Human-readable display name for the application.",
			"type": "string",
			"maxLength": 128
		},
		"description": {
			"description": "A brief description of what this application does and what modders can extend.",
			"type": "string",
			"maxLength": 1024
		},
		"bindings": {
			"description": "The functions and namespaces exposed to scripts. This is the API surface.",
			"type": "object",
			"propertyNames": {
				"pattern": "^[A-Za-z_$][A-Za-z0-9_$]*$",
				"description": "Binding and member names are JavaScript identifiers: a script calls `world.getHealth(...)`, and a kebab name would parse as subtraction. camelCase is the convention; underscores are legal."
			},
			"additionalProperties": {
				"$ref": "#/$defs/binding"
			}
		},
		"capabilities": {
			"description": "Named capabilities that scripts can request. Each capability gates access to a set of bindings or behaviors. Keys are scope-only: a declared capability names a scope node (e.g. 'run', 'run.command', 'fs.addon') with no mode prefix. The 'read:'/'write:' axis is a property of a grant or require reference, not of the declaration.",
			"type": "object",
			"propertyNames": {
				"pattern": "^[a-z][a-z0-9-]*(\\.[a-z][a-z0-9-]*)*$"
			},
			"additionalProperties": {
				"$ref": "#/$defs/capability"
			}
		},
		"types": {
			"description": "Custom type definitions referenced by bindings. Enables rich type generation and documentation.",
			"type": "object",
			"propertyNames": {
				"pattern": "^[A-Za-z_$][A-Za-z0-9_$]*$",
				"description": "Type names become generated TypeScript interface and type names, so they are JavaScript identifiers. PascalCase is the convention."
			},
			"additionalProperties": {
				"$ref": "#/$defs/typeDefinition"
			}
		},
		"hooks": {
			"description": "DEPRECATED in favor of event-typed slots: declare a slot whose 'accepts' is the event-handler type and fire it by calling its fills. Retained for back-compat; the host still fires registered hook handlers. Hooks are fired by the host and handled by scripts — the reverse direction of bindings.",
			"type": "object",
			"additionalProperties": {
				"$ref": "#/$defs/hook"
			}
		},
		"limits": {
			"$ref": "#/$defs/executionLimits"
		},
		"execution": {
			"$ref": "#/$defs/executionIntent",
			"description": "The manifest-wide default execution intent. Every binding, hook, and slot that declares no `execution` of its own inherits this one; absent both, the schema defaults apply (`brief` / `any`)."
		},
		"slots": {
			"description": "The canonical fill surface: typed plug-points the host declares and mods fill. Each slot declares an ID, accepted formats/kinds, and optional capability gating. A fill's shape is governed by the slot's 'accepts' type — a fragment, a role map, or an event handler are all fills of slots of the appropriate type.",
			"type": "array",
			"items": {
				"$ref": "#/$defs/slot"
			}
		},
		"events": {
			"description": "Discovery catalog of the named events this host emits or broadcasts and their payload types. Distinct from slots and from fragment handlers: an event-typed slot is an extension POINT that addons fill with handlers; a fragment's 'handlers' wire DOM responses on a fill; an entry here declares what the host itself emits, with no consumer presupposed — a sandbox script, the host's own UI, or an external subscriber may all listen. In short: bindings are what you can call, slots and handlers are what handles, events are what the host emits.",
			"type": "array",
			"items": {
				"$ref": "#/$defs/eventDefinition"
			}
		},
		"libraries": {
			"description": "Curated allow-list of libraries mod code may import. Keys are module specifiers (npm-style: '@example/doc', 'lodash'). The blanket import-deny stays in force; an approved specifier is the only thing that lifts it, and only for mods holding the library's capability. An imported library runs IN the sandbox at the importing mod's own privilege — approving one vouches 'this is sandbox-safe shared code I choose to offer', it grants no new power. The host supplies each library's source at runtime construction; xript provides the allow-listed link path, never the library itself.",
			"type": "object",
			"propertyNames": {
				"pattern": "^(@[a-z0-9-~][a-z0-9-._~]*\\/)?[a-z0-9-~][a-z0-9-._~]*$"
			},
			"additionalProperties": {
				"$ref": "#/$defs/library"
			}
		},
		"formats": {
			"description": "Fragment formats this host declares. A format entry names the parser a fragment of that media type is read with and the node vocabularies in scope while it is read. Keys are media types; each must also appear in some slot's 'accepts' to be reachable. The built-in formats ('text/html', 'text/html+jsml', 'application/j5ml+json', and its deprecated spelling 'application/jsml+json') bring the built-in 'html' vocabulary into scope and may not be redeclared here — a host that wants a component vocabulary registers a new media type of its own.",
			"type": "object",
			"propertyNames": {
				"pattern": "^[a-z0-9][a-z0-9!#$&^_.+-]{0,126}\\/[a-z0-9][a-z0-9!#$&^_.+-]{0,126}$",
				"not": {
					"enum": [
						"text/html",
						"text/html+jsml",
						"application/j5ml+json",
						"application/jsml+json",
						"application/x-xript-role",
						"application/x-xript-hook",
						"application/x-xript-command"
					]
				}
			},
			"additionalProperties": {
				"$ref": "#/$defs/fragmentFormat"
			}
		},
		"vocabularies": {
			"description": "Reusable node vocabularies. A vocabulary declares nodes and their props and nothing about how they are authored: any number of fragment formats may bring the same vocabulary into scope under different syntaxes and alongside different neighbouring vocabularies. Ids are lowercase dotted names of two or more segments; single-segment ids name the built-in vocabularies and may not be declared here.",
			"type": "object",
			"propertyNames": {
				"$ref": "#/$defs/vocabularyId"
			},
			"additionalProperties": {
				"$ref": "#/$defs/vocabulary"
			}
		}
	},
	"$defs": {
		"fragmentNodeName": {
			"type": "string",
			"pattern": "^[A-Za-z_][A-Za-z0-9_.-]*(:[A-Za-z_][A-Za-z0-9_.-]*)?$",
			"description": "A node name. CASE-SENSITIVE and matched verbatim: 'Panel' and 'panel' are different nodes. One optional namespace segment is permitted ('svg:rect')."
		},
		"vocabularyId": {
			"type": "string",
			"pattern": "^[a-z][a-z0-9]*(\\.[a-z][a-z0-9]*)+$",
			"maxLength": 128,
			"description": "A host-declared vocabulary id: two or more lowercase dot-separated segments, e.g. 'xtyle.components'. Single-segment ids name the built-in vocabularies and are referenceable but not declarable."
		},
		"vocabularyRef": {
			"anyOf": [
				{
					"type": "string",
					"enum": [
						"html"
					]
				},
				{
					"$ref": "#/$defs/vocabularyId"
				}
			],
			"description": "A reference to a vocabulary in scope: either a built-in id (currently exactly 'html') or the id of a vocabulary declared in this manifest's 'vocabularies' block. The two grammars are disjoint by construction, so a reader can tell which kind of thing a reference names without a lookup, and a host can never shadow a built-in."
		},
		"uriScheme": {
			"type": "string",
			"pattern": "^[a-z][a-z0-9+.-]*$",
			"maxLength": 64,
			"not": {
				"enum": [
					"javascript",
					"vbscript"
				]
			},
			"description": "A URI scheme a fragment's 'uri' and 'image-uri' sinks may name: a lowercase scheme name WITHOUT the trailing colon, e.g. 'https', 'mailto', 'tauri'. 'javascript' and 'vbscript' are a hard floor no host may lift: they are refused in every fragment regardless of what is declared here, and naming either is an error rather than a silent no-op."
		},
		"vocabulary": {
			"description": "A node vocabulary: nodes and their props, and nothing about how they are authored. A vocabulary declares what a node IS, never what it may parent — containment is a separate axis and defaults open.",
			"type": "object",
			"required": [
				"nodes"
			],
			"additionalProperties": false,
			"properties": {
				"description": {
					"type": "string",
					"maxLength": 1024,
					"description": "What this vocabulary describes and which formats are expected to speak it. Shown in generated docs."
				},
				"nodes": {
					"type": "object",
					"minProperties": 1,
					"propertyNames": {
						"$ref": "#/$defs/fragmentNodeName"
					},
					"additionalProperties": {
						"$ref": "#/$defs/fragmentNode"
					},
					"description": "The nodes this vocabulary declares."
				},
				"refines": {
					"type": "boolean",
					"default": false,
					"description": "When true, this vocabulary refines a base manifest's vocabulary of the same id: 'description' is child-wins and 'nodes' key-merges. Without it, redeclaring a vocabulary id inherited through 'extends' is a resolution error."
				}
			}
		},
		"fragmentFormat": {
			"description": "A fragment format. 'syntax' names the parser a runtime must use to read a fill's source; 'vocabularies' names the node vocabularies in scope, in resolution order; 'nodes' is an inline single-use vocabulary resolved ahead of all of them. Default-deny: a node resolvable through none of the vocabularies in scope is an error, not a silent drop.",
			"type": "object",
			"required": [
				"syntax"
			],
			"additionalProperties": false,
			"anyOf": [
				{
					"required": [
						"nodes"
					]
				},
				{
					"properties": {
						"vocabularies": {
							"type": "array",
							"minItems": 1
						}
					},
					"required": [
						"vocabularies"
					]
				}
			],
			"properties": {
				"description": {
					"type": "string",
					"maxLength": 1024,
					"description": "What this format is for and what renders it. Shown in generated docs."
				},
				"syntax": {
					"type": "string",
					"enum": [
						"j5ml",
						"jsml",
						"html"
					],
					"description": "How a fill's source is parsed. 'j5ml' parses JSON in J5ML array form; 'html' tokenizes an HTML string. 'jsml' is a deprecated spelling of 'j5ml' and parses identically. The enum is closed on purpose: the value names a parser the runtime must actually possess."
				},
				"vocabularies": {
					"type": "array",
					"items": {
						"$ref": "#/$defs/vocabularyRef"
					},
					"uniqueItems": true,
					"description": "Node vocabularies in scope, in resolution order: the first vocabulary that claims a node NAME wins. The format's own inline 'nodes' are always resolved first, ahead of every entry here. The built-in 'html' vocabulary, when present, must be the last entry — an HTML element name may only be reached after every declared vocabulary has declined it. An empty array alongside an inline 'nodes' block means deliberate closure: only the inline nodes are legal."
				},
				"nodes": {
					"type": "object",
					"minProperties": 1,
					"propertyNames": {
						"$ref": "#/$defs/fragmentNodeName"
					},
					"additionalProperties": {
						"$ref": "#/$defs/fragmentNode"
					},
					"description": "An inline single-use vocabulary, always first in resolution order. Exactly equivalent to declaring a vocabulary containing these nodes and naming it as the first entry of 'vocabularies', except that the resulting vocabulary has no id and cannot be referenced by any other format."
				},
				"schemes": {
					"type": "array",
					"items": {
						"$ref": "#/$defs/uriScheme"
					},
					"uniqueItems": true,
					"description": "URI schemes a fill of this format may name in a 'uri' or 'image-uri' sink. Absent means the default safe set: 'http', 'https', 'mailto', 'data'. Present REPLACES that set wholesale rather than extending it, so the one key both widens a host's surface (adding 'tauri') and narrows it (allowing only 'https'). An empty array means deliberate closure: no absolute URI passes at all. A value carrying no scheme is relative and is never subject to this list — relative paths, absolute paths, scheme-relative '//host/path', fragment-only '#anchor', and query-only '?q=1' all pass whatever it says. 'data' grants no more than it ever did: only on an image sink, and only the four safe image subtypes."
				},
				"refines": {
					"type": "boolean",
					"default": false,
					"description": "When true, this format refines a base manifest's format of the same media type: scalars are child-wins, 'nodes' key-merges, and 'vocabularies' and 'schemes' — when present — REPLACE the base's array wholesale. Without it, redeclaring a format id inherited through 'extends' is a resolution error."
				}
			}
		},
		"fragmentNode": {
			"description": "A node in a fragment-format vocabulary. The host owns this declaration exactly as it owns its slots and bindings: it is the contract a fill's markup is checked against.",
			"type": "object",
			"properties": {
				"description": {
					"type": "string",
					"maxLength": 1024,
					"description": "What this node renders. Shown in generated docs."
				},
				"props": {
					"type": "object",
					"propertyNames": {
						"pattern": "^[A-Za-z_][A-Za-z0-9_.:-]*$"
					},
					"additionalProperties": {
						"$ref": "#/$defs/fragmentProp"
					},
					"description": "The props this node accepts. Default-deny: an undeclared prop is dropped and reported. Three protocol props are legal on every node of every vocabulary and need not be declared: 'id', 'data-bind', 'data-if'. A prop name beginning with 'on' is always rejected."
				},
				"children": {
					"description": "What may appear as this node's children. 'any' (default), 'none' (a void node), 'text' (text nodes only), or an explicit list of permitted child node names.",
					"default": "any",
					"oneOf": [
						{
							"type": "string",
							"enum": [
								"any",
								"none",
								"text"
							]
						},
						{
							"type": "array",
							"items": {
								"type": "string"
							},
							"minItems": 1
						}
					]
				},
				"bindTo": {
					"type": "string",
					"default": "children",
					"description": "Where a resolved 'data-bind' value is written on this node. 'children' replaces the node's children with a single text node; any other value names one of this node's declared props, and the value is written to it THROUGH THAT PROP'S SINK. Defaults to 'children', or to 'value' when 'children' is 'none'."
				},
				"refines": {
					"type": "boolean",
					"default": false,
					"description": "When true, this node refines a base format's node of the same name: 'props' key-merges, and a same-named prop is REPLACED wholesale rather than deep-merged, so a child that widens a prop's type must restate its 'sink'. Without it, redeclaring a node name inside a refining format is a resolution error."
				}
			},
			"additionalProperties": false
		},
		"fragmentProp": {
			"description": "A node prop. The body is a JSON Schema (draft 2020-12) describing the prop's value; the xript-specific 'sink' keyword declares WHERE the value lands, which is the only thing sanitization needs to know. A prop with no 'sink' is inert data: it never becomes markup, so there is nothing to sanitize. Runtimes enforce the 'type' keyword; the full schema is enforced by 'xript validate'.",
			"type": "object",
			"properties": {
				"sink": {
					"type": "string",
					"enum": [
						"text",
						"uri",
						"image-uri",
						"css",
						"html"
					],
					"default": "text",
					"description": "Where this prop's value lands. 'text' (default): inert data, no sanitization. 'uri': a navigational URL (javascript:, vbscript:, and all data: URIs rejected). 'image-uri': a media source (safe data:image/* permitted). 'css': an inline style value (url(), expression(), -moz-binding, behavior stripped). 'html': a raw markup string the component will inject, run through the HTML sanitizer. Any sink other than 'text' REQUIRES \"type\": \"string\"."
				}
			},
			"additionalProperties": true
		},
		"library": {
			"description": "An approved importable library. The host registers the matching pre-bundled ES module source when constructing the runtime; the manifest entry declares that the specifier exists, what gates it, and what version contract the host vouches for.",
			"type": "object",
			"required": [
				"description"
			],
			"properties": {
				"description": {
					"type": "string",
					"description": "What this library provides to mod code. Shown in generated docs."
				},
				"capability": {
					"$ref": "#/$defs/capabilityRef",
					"description": "The capability a mod must hold to import this library. Omit for an ungated library available to every mod."
				},
				"version": {
					"type": "string",
					"description": "The version (or semver range) of the library the host ships. Informational: documents the contract mod authors compile against."
				},
				"deprecated": {
					"type": "string",
					"description": "If present, the library is deprecated; the value explains what to use instead."
				}
			},
			"additionalProperties": false
		},
		"capabilityRef": {
			"type": "string",
			"pattern": "^(read:|write:)?[a-z][a-z0-9-]*(\\.[a-z][a-z0-9-]*)*$",
			"description": "A capability name: an optional 'read:'/'write:' mode prefix followed by a dot-delimited scope. Each scope segment is lowercase-hyphen (e.g. 'modify-player'). Absent prefix means write (full access). Examples: 'fs.addon', 'read:fs.addon', 'write:run.command'."
		},
		"typeRef": {
			"description": "A reference to a type. Can be a primitive, a custom type name, an array type, or a union.",
			"oneOf": [
				{
					"type": "string",
					"description": "A primitive type name, custom type reference, or shorthand.",
					"examples": [
						"string",
						"number",
						"boolean",
						"void",
						"null",
						"Player",
						"string[]",
						"number[]"
					]
				},
				{
					"type": "object",
					"description": "A complex type expression.",
					"properties": {
						"array": {
							"$ref": "#/$defs/typeRef",
							"description": "An array of this type."
						},
						"union": {
							"type": "array",
							"items": {
								"$ref": "#/$defs/typeRef"
							},
							"minItems": 2,
							"description": "A union of multiple types."
						},
						"map": {
							"$ref": "#/$defs/typeRef",
							"description": "A string-keyed map with values of this type."
						},
						"optional": {
							"$ref": "#/$defs/typeRef",
							"description": "An optional (nullable) version of this type."
						}
					},
					"oneOf": [
						{
							"required": [
								"array"
							]
						},
						{
							"required": [
								"union"
							]
						},
						{
							"required": [
								"map"
							]
						},
						{
							"required": [
								"optional"
							]
						}
					],
					"additionalProperties": false
				}
			]
		},
		"parameter": {
			"description": "A function parameter with a name, type, and optional description.",
			"type": "object",
			"required": [
				"name",
				"type"
			],
			"properties": {
				"name": {
					"type": "string",
					"description": "The parameter name."
				},
				"type": {
					"$ref": "#/$defs/typeRef",
					"description": "The parameter's type."
				},
				"description": {
					"type": "string",
					"description": "What this parameter represents."
				},
				"default": {
					"description": "The default value if the parameter is omitted. Implies the parameter is optional."
				},
				"required": {
					"type": "boolean",
					"description": "Whether this parameter is required. Defaults to true unless a default value is provided.",
					"default": true
				}
			},
			"additionalProperties": false
		},
		"binding": {
			"description": "A function or namespace exposed to scripts.",
			"oneOf": [
				{
					"$ref": "#/$defs/functionBinding"
				},
				{
					"$ref": "#/$defs/namespaceBinding"
				}
			]
		},
		"functionBinding": {
			"description": "A callable function exposed to scripts.",
			"type": "object",
			"required": [
				"description"
			],
			"properties": {
				"description": {
					"type": "string",
					"description": "What this function does. Shown in generated docs and editor tooltips."
				},
				"params": {
					"type": "array",
					"items": {
						"$ref": "#/$defs/parameter"
					},
					"description": "The function's parameters, in order."
				},
				"returns": {
					"$ref": "#/$defs/typeRef",
					"description": "The return type. Omit for void functions."
				},
				"async": {
					"type": "boolean",
					"description": "Whether this function is asynchronous — a declaration about the SHAPE of the value the host implementation returns (a promise rather than a settled value), not about the cost of the call and not a promise that the interpreter yields. Declare cost in `execution`. Defaults to false.",
					"default": false
				},
				"execution": {
					"$ref": "#/$defs/executionIntent",
					"description": "The cost of calling this host function. Advisory: a host routes on it, nothing enforces it."
				},
				"capability": {
					"$ref": "#/$defs/capabilityRef",
					"description": "The capability required to call this function. If omitted, the function is always available. May carry a 'read:'/'write:' mode prefix and a dotted scope; a grant satisfies it by mode-lattice and prefix-subsumption."
				},
				"examples": {
					"type": "array",
					"items": {
						"$ref": "#/$defs/example"
					},
					"description": "Usage examples for documentation."
				},
				"deprecated": {
					"type": "string",
					"description": "If present, this function is deprecated. The value describes the migration path."
				}
			},
			"additionalProperties": false
		},
		"namespaceBinding": {
			"description": "A namespace that groups related functions.",
			"type": "object",
			"required": [
				"description",
				"members"
			],
			"properties": {
				"description": {
					"type": "string",
					"description": "What this namespace represents."
				},
				"members": {
					"type": "object",
					"propertyNames": {
						"pattern": "^[A-Za-z_$][A-Za-z0-9_$]*$",
						"description": "Member names are JavaScript identifiers, like top-level binding names."
					},
					"additionalProperties": {
						"$ref": "#/$defs/binding"
					},
					"description": "The functions and sub-namespaces in this namespace."
				}
			},
			"additionalProperties": false
		},
		"capability": {
			"description": "A named capability that gates access to functionality.",
			"type": "object",
			"required": [
				"description"
			],
			"properties": {
				"description": {
					"type": "string",
					"description": "What this capability grants access to. Shown to the user when a script requests it."
				},
				"risk": {
					"type": "string",
					"enum": [
						"low",
						"medium",
						"high"
					],
					"description": "Advisory risk level. Helps users make informed decisions about granting capabilities.",
					"default": "low"
				},
				"reserved": {
					"type": "boolean",
					"default": false,
					"description": "When true, the capability is intentionally declared without yet gating a slot, binding, or hook — inherited for canon parity, or reserved for a future surface. Tooling treats it as aspirational rather than vestigial, and suppresses the unreferenced-capability warning."
				},
				"instances": {
					"description": "Declares that this scope's children are instantiated by the host at run time rather than written in the manifest — one child per configured filesystem root, connected account, or opened project. The scope tree is static; the *instances* under this node are not, so a host can grant `write:fs.workspace.my-project` for a root the manifest could never have named. Prefix subsumption already matches such a grant; this block is what makes the shape discoverable, so a mod author can tell an intended dynamic child from a scope they guessed at. Declaring it also silences the `dynamic-scope-undeclared` lint on descendants of this node.",
					"type": "object",
					"required": [
						"description"
					],
					"properties": {
						"description": {
							"type": "string",
							"description": "What one child of this scope corresponds to, in the host's terms — 'one per workspace the user has configured'. Shown wherever the capability tree is documented."
						},
						"example": {
							"type": "string",
							"description": "A representative fully-qualified child scope, e.g. `fs.workspace.my-project`. Illustrative only; nothing resolves or validates against it."
						}
					},
					"additionalProperties": false
				}
			},
			"additionalProperties": false
		},
		"typeDefinition": {
			"description": "A custom type definition.",
			"type": "object",
			"required": [
				"description"
			],
			"properties": {
				"description": {
					"type": "string",
					"description": "What this type represents."
				},
				"fields": {
					"type": "object",
					"additionalProperties": {
						"$ref": "#/$defs/fieldDefinition"
					},
					"description": "The fields of this type (for object/struct types)."
				},
				"values": {
					"type": "array",
					"items": {
						"type": "string"
					},
					"description": "The allowed values (for enum types)."
				},
				"open": {
					"type": "boolean",
					"default": false,
					"description": "When true, the `values` are the known set but any other string is also valid — an open enum. Codegen emits `... | (string & {})` so unlisted values (for example, addon-contributed ones) still type-check; xript does not enforce the closed set at runtime regardless."
				},
				"abstract": {
					"type": "boolean",
					"default": false,
					"description": "When true, this type is declared and described but not populated — it supplies neither fields nor values, standing as a contract hole that an extending manifest is expected to fill with concrete fields or values. An abstract type that survives into the resolved manifest is a defect ('abstract-type-unfilled')."
				},
				"refines": {
					"type": "boolean",
					"default": false,
					"description": "When true, this definition refines a concrete base type of the same name, deep-merging onto it field by field. Without it, redeclaring a concrete type name is a resolution error. Does not apply to abstract base types, which are filled rather than refined."
				}
			},
			"additionalProperties": false
		},
		"fieldDefinition": {
			"description": "A field within a custom type.",
			"type": "object",
			"required": [
				"type"
			],
			"properties": {
				"type": {
					"$ref": "#/$defs/typeRef"
				},
				"description": {
					"type": "string"
				},
				"optional": {
					"type": "boolean",
					"description": "Whether this field can be absent. Defaults to false.",
					"default": false
				},
				"default": {
					"description": "Default value if the field is absent. Documentation and codegen hint only; xript does not apply it at runtime."
				},
				"enum": {
					"type": "array",
					"items": {},
					"minItems": 1,
					"description": "Allowed literal values for this field. Codegen emits a literal union; xript does not enforce at runtime."
				},
				"open": {
					"type": "boolean",
					"default": false,
					"description": "When true, the `enum` values are the known set but any other string is also valid — an open enum. Codegen emits `... | (string & {})` so unlisted values still type-check; xript does not enforce the closed set at runtime regardless."
				}
			},
			"additionalProperties": false
		},
		"example": {
			"description": "A usage example for documentation.",
			"type": "object",
			"required": [
				"code"
			],
			"properties": {
				"title": {
					"type": "string",
					"description": "A short title for this example."
				},
				"code": {
					"type": "string",
					"description": "The JavaScript code demonstrating usage."
				},
				"description": {
					"type": "string",
					"description": "Explanation of what this example demonstrates."
				}
			},
			"additionalProperties": false
		},
		"hook": {
			"description": "A hook that scripts can register handlers for. Hooks are the reverse of bindings: the host fires them, and scripts handle them.",
			"type": "object",
			"required": [
				"description"
			],
			"properties": {
				"description": {
					"type": "string",
					"description": "What this hook represents and when it fires. Shown in generated docs."
				},
				"phases": {
					"type": "array",
					"items": {
						"type": "string",
						"enum": [
							"pre",
							"post",
							"done",
							"error"
						]
					},
					"minItems": 1,
					"uniqueItems": true,
					"description": "Lifecycle phases for this hook. If omitted, the hook is a simple notification with no lifecycle."
				},
				"params": {
					"type": "array",
					"items": {
						"$ref": "#/$defs/parameter"
					},
					"description": "The parameters passed to hook handlers when fired."
				},
				"capability": {
					"$ref": "#/$defs/capabilityRef",
					"description": "The capability required to register for this hook. If omitted, any script can register. May carry a 'read:'/'write:' mode prefix and a dotted scope; a grant satisfies it by mode-lattice and prefix-subsumption."
				},
				"async": {
					"type": "boolean",
					"description": "Whether handlers run asynchronously. Host-controlled. Defaults to false.",
					"default": false
				},
				"limits": {
					"$ref": "#/$defs/executionLimits",
					"description": "Per-handler execution limits. Overrides the manifest-level defaults for this hook's handlers."
				},
				"execution": {
					"$ref": "#/$defs/executionIntent",
					"description": "The cost of FIRING this hook, handler fan-out included. Advisory: a host routes on it, nothing enforces it."
				},
				"examples": {
					"type": "array",
					"items": {
						"$ref": "#/$defs/example"
					},
					"description": "Usage examples for documentation."
				},
				"deprecated": {
					"type": "string",
					"description": "If present, this hook is deprecated. The value describes the migration path."
				}
			},
			"additionalProperties": false
		},
		"slot": {
			"description": "A typed plug-point the host declares and mods fill. The 'accepts' type governs what a valid fill looks like and what the host does with it (mount it, call it, resolve it, or fire it).",
			"type": "object",
			"required": [
				"id",
				"accepts"
			],
			"properties": {
				"id": {
					"type": "string",
					"pattern": "^[a-z][a-z0-9.-]*$",
					"description": "Unique slot identifier (e.g. 'sidebar.left', 'statusbar.right')."
				},
				"accepts": {
					"type": "array",
					"items": {
						"type": "string"
					},
					"minItems": 1,
					"description": "The format(s) or kind(s) this slot takes from fills, e.g. 'text/html+jsml', 'application/javascript+esm', 'application/json', 'application/x-xript-role', 'application/x-xript-hook', 'application/x-xript-command'. The three 'application/x-xript-*' kinds are reserved: they carry no markup, are never sanitized, and may not be declared in 'formats'. A slot accepting 'application/x-xript-command' takes command fills — named, invocable actions the host lists and calls, each naming a mod export as its 'handler'. Reserved kinds are matched in the order role, hook, command, so a slot listing more than one resolves deterministically."
				},
				"description": {
					"type": "string",
					"description": "Human-readable description of what this slot is for. Surfaced in generated docs and used by tooling."
				},
				"capability": {
					"$ref": "#/$defs/capabilityRef",
					"description": "Capability required to fill this slot. May carry a 'read:'/'write:' mode prefix and a dotted scope; a grant satisfies it by mode-lattice and prefix-subsumption."
				},
				"multiple": {
					"type": "boolean",
					"default": false,
					"description": "Whether multiple mods can fill this slot simultaneously."
				},
				"payload": {
					"type": "object",
					"additionalProperties": true,
					"description": "A JSON Schema (draft 2020-12) that a fill's payload for this slot must satisfy. The host validates each fill against it; an extending manifest may refine the contract."
				},
				"reserved": {
					"type": "boolean",
					"default": false,
					"description": "When true, the slot is intentionally declared without a current filler — inherited for canon parity, or reserved for future mods. Tooling treats it as aspirational rather than dead, and suppresses the unfilled-slot warning."
				},
				"style": {
					"type": "string",
					"enum": [
						"inherit",
						"isolated",
						"scoped"
					],
					"default": "inherit",
					"description": "Styling mode: 'inherit' applies host styles, 'isolated' prevents style bleed, 'scoped' exposes CSS custom properties."
				},
				"refines": {
					"type": "boolean",
					"default": false,
					"description": "When true, this slot definition refines a base slot of the same id, deep-merging onto it including its payload contract. Without it, redeclaring a slot id is a resolution error."
				},
				"execution": {
					"$ref": "#/$defs/executionIntent",
					"description": "The cost a host should assume when driving fills of this slot. A mod's own `execution` on the export it fills with is widened against this one — the more expensive claim wins. Advisory: a host routes on it, nothing enforces it."
				},
				"limits": {
					"$ref": "#/$defs/slotExecutionLimits",
					"description": "Enforced execution limits for calls into this slot's fills, overriding the manifest's `limits` field by field. The counterpart to `execution`: that one is advisory routing, this one is enforcement."
				}
			},
			"additionalProperties": false
		},
		"slotExecutionLimits": {
			"description": "Per-slot execution limits. A slot whose fills legitimately run long says so here rather than inheriting a budget tuned for something else, and a slot handling untrusted input can tighten below it. Declared by the host in both directions, so neither is an escalation. Fields left out inherit the manifest's `limits`, and the embedder's hard limits still clamp the result.\n\nOnly `timeout_ms` appears here, and the omission is deliberate: `memory_mb` and `max_stack_depth` are properties of a runtime at the moment it is created in all four engines, so a per-slot value for either could not be enforced without standing up a separate runtime. That path exists already — route the slot with `execution.affinity` and give the worker its own limits. Declaring keys no engine can honor would promise enforcement and deliver nothing.",
			"type": "object",
			"additionalProperties": false,
			"properties": {
				"timeout_ms": {
					"type": "integer",
					"minimum": 1,
					"description": "Maximum execution time in milliseconds for a call into a fill of this slot. Replaces the manifest's `timeout_ms` for these calls; may raise it as well as lower it."
				}
			}
		},
		"eventDefinition": {
			"description": "A named event the host emits or broadcasts. A discovery declaration, not an extension point: it states what the host fires and the shape of the payload, leaving consumers (sandbox scripts, the host UI, external subscribers) unpresupposed.",
			"type": "object",
			"required": [
				"id",
				"description"
			],
			"properties": {
				"id": {
					"type": "string",
					"pattern": "^[a-z][a-z0-9.-]*$",
					"description": "Unique event identifier (e.g. 'document.saved', 'selection.changed')."
				},
				"description": {
					"type": "string",
					"description": "What this event represents and when the host emits it. Shown in generated docs."
				},
				"payload": {
					"$ref": "#/$defs/typeRef",
					"description": "The type of the payload delivered with this event. Omit for events that carry no payload."
				},
				"capability": {
					"$ref": "#/$defs/capabilityRef",
					"description": "The capability required to subscribe to this event via 'events.on'. If omitted, any script can subscribe. Gating happens at registration time, reusing the hook gate model: a grant satisfies it by mode-lattice and prefix-subsumption."
				}
			},
			"additionalProperties": false
		},
		"executionIntent": {
			"description": "How a host should expect this surface to behave in time and where it may run. Advisory routing signal — never enforced, never a capability. Enforcement lives in `limits`.",
			"type": "object",
			"additionalProperties": false,
			"properties": {
				"duration": {
					"type": "string",
					"enum": [
						"instant",
						"brief",
						"long",
						"unbounded"
					],
					"default": "brief",
					"description": "How long a host should expect this to take."
				},
				"affinity": {
					"type": "string",
					"enum": [
						"any",
						"pinned",
						"isolated"
					],
					"default": "any",
					"description": "Where this may run. `any` is poolable. `pinned` must run on the runtime that owns the calling mod's state. `isolated` wants a runtime nobody else is using."
				}
			}
		},
		"executionLimits": {
			"description": "Default execution limits for scripts. Runtimes enforce these limits to prevent denial of service.",
			"type": "object",
			"properties": {
				"timeout_ms": {
					"type": "integer",
					"minimum": 1,
					"description": "Maximum execution time in milliseconds.",
					"default": 5000
				},
				"memory_mb": {
					"type": "integer",
					"minimum": 1,
					"description": "Maximum memory usage in megabytes.",
					"default": 64
				},
				"max_stack_depth": {
					"type": "integer",
					"minimum": 1,
					"description": "Maximum call stack depth.",
					"default": 256
				}
			},
			"additionalProperties": false
		}
	}
}
