{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"opm-manifest@1","title":"Okonomi parametric model manifest, version 1","description":"The `manifest.json` inside a .opm archive: what the file builds, which inputs each Bauart takes, and which kernel its program is written against.\n\nIt is the *envelope* that had no schema. `field-schema@1` describes the fields inside a generator entry and always did; `format`, `kernel`, `vendor` and the shape of a generator entry had a TypeScript type, four hand-written examples and a structural check in `checkManifest()` whose own comment admits it does not validate the fields. This is the missing half, and it composes with the other rather than restating it: every `$ref` into `field-schema@1` below is a place where the two documents meet at one definition instead of two.\n\nA loader is not required to run this. `openOpm` deliberately does its own small structural pass and no more, because ajv is not a dependency a page should acquire to open a file. What this is for is a vendor writing a .opm, a tool checking one before publishing it, and this repository holding its own four to the same standard it holds everybody else's.","type":"object","required":["format","kernel","vendor","fieldSchema","generators"],"additionalProperties":false,"properties":{"format":{"description":"The container version, and the first thing a loader reads. A file naming a format the loader does not carry is refused outright rather than opened hopefully — the alternative is a half-built model whose parts a reader cannot tell from a correct one.","const":"okonomi-parametric@1"},"kernel":{"description":"The geometry kernel the program is written against, as a contract rather than as a file list. `okonomi-mesh@1` is the only value a loader accepts today; the loader carries four Lua modules behind it (mesh, animation, builder, plan) and a program reaches all of them through `require_kernel`. Two different lists, and this is the one a file may state — a kernel that gained a fifth module would not change it, a kernel that changed the meaning of an existing call would.","type":"string","pattern":"^okonomi-[a-z]+@[0-9]+$"},"vendor":{"description":"Who wrote the file. It travels with the artefact because an artefact outlives the conversation that produced it.","type":"object","required":["name","version"],"additionalProperties":false,"properties":{"name":{"type":"string","minLength":1},"version":{"type":"string","minLength":1}}},"fieldSchema":{"description":"The field format the `inputs` below obey. Named rather than assumed, so a reader who opened this expecting JSON Schema is told immediately which document does describe it.","const":"field-schema@1"},"generators":{"description":"The Bauarten this file builds. **A file may carry several**, and that is the ordinary case rather than the exception: a Lieferprogramm is what a provider sells, and a fixed light and a casement are the same three members one rail apart. Sharing one file is what keeps their glazing build, their frame widths and their arris limits one calculation instead of two that drift.","type":"array","minItems":1,"items":{"$ref":"#/$defs/generator"}}},"$defs":{"generator":{"description":"One Bauart: what it is, what it demands, what it is made of, and what it attaches.","type":"object","required":["key","type","typeContract","label","description","groups","inputs","slots","ports","animations"],"additionalProperties":false,"properties":{"key":{"description":"How a caller asks for it. Unique within the file, and stable — it goes into a cache key.","$ref":"field-schema@1#/$defs/key"},"type":{"description":"The product type, and the half of `typeContract` after the namespace. A host reads this to learn what kind of thing it is holding without knowing the vendor.","$ref":"field-schema@1#/$defs/key"},"typeContract":{"description":"The contract this Bauart claims, as `<namespace>/<type>@<version>`. The namespace is load-bearing: `core/` is the shared layer that describes a kind of product, and everything else is somebody's article. Two vendors may both publish a `window@1`, and without the namespace the bare name would be a question with two answers.","type":"string","pattern":"^[a-z0-9_]+/[a-z0-9_]+@[0-9]+$"},"label":{"$ref":"field-schema@1#/$defs/i18n"},"description":{"$ref":"field-schema@1#/$defs/i18n"},"groups":{"description":"The headings its fields sit under. An id and a translated label, never a string doing both jobs — a heading used as a key works exactly as long as it has one spelling.","type":"array","minItems":1,"items":{"$ref":"field-schema@1#/$defs/group"}},"inputs":{"description":"Every input, in one flat list. There is no parameter/option split here: when a value is bound and who is asked are facts about a host's forms, and each field carries a `scope` saying which of the two demanded it.","type":"array","minItems":1,"items":{"$ref":"field-schema@1#/$defs/field"}},"slots":{"description":"The parts a finish can be applied to. A build names its materials after these keys and a client dresses by that name, so a slot called anything else silently never gets its finish.","type":"array","items":{"$ref":"#/$defs/slot"}},"ports":{"description":"Every mounting point the build attaches, named before it is built. **Both directions**: a host should not have to build a Bauart to learn whether it carries a handle, and a build may not attach a port this list does not name.","type":"array","items":{"$ref":"#/$defs/port"}},"animations":{"description":"Every clip the build ships. Same bargain as `ports`, and an empty list is a real answer — a fixed light opens no more than a skirting board does.","type":"array","items":{"$ref":"#/$defs/animation"}}}},"slot":{"type":"object","required":["key","label"],"additionalProperties":false,"properties":{"key":{"$ref":"field-schema@1#/$defs/key"},"label":{"$ref":"field-schema@1#/$defs/i18n"},"defaultMaterial":{"description":"What it is made of when nothing was asked. Absent is legitimate; an undressed slot is grey rather than wrong.","type":"string","minLength":1}}},"port":{"type":"object","required":["key","type","label"],"additionalProperties":false,"properties":{"key":{"$ref":"field-schema@1#/$defs/key"},"type":{"description":"What may be mounted there — a handle, a hinge. A key says which one it is; this says what kind.","type":"string","minLength":1},"label":{"$ref":"field-schema@1#/$defs/i18n"}}},"animation":{"type":"object","required":["key","label"],"additionalProperties":false,"properties":{"key":{"description":"The clip's name, and **not a plain key** — it may carry dots. A double casement ships `open` for the pair and `open.sash_left` and `open.sash_right` for each leaf, and the dot is what says the last two are refinements of the first rather than three unrelated motions. A host that knows nothing about sashes can still play `open`; one that wants a single leaf can look for a longer name under the same head.","type":"string","pattern":"^[a-z][a-z0-9_]*(\\.[a-z][a-z0-9_]*)*$"},"label":{"$ref":"field-schema@1#/$defs/i18n"}}}}}