Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ schemas in `nnnotes.schemas` and the documented Python API. Before 1.0 a minor v

### Features

- Ship schemas for shared product root documents and prefab objects, with numeric font and transform contracts.
- Share catalog versions and labels through depot remotes, with verified catalog files fetched on demand.
- Retain census planning results, emitted task descriptions and standalone task runs as depot collection roots.
- Store content, task inputs, results and catalog versions in a persistent depot with optional remote storage, verified collection and bounded source downloads.
Expand Down
29 changes: 29 additions & 0 deletions docs/contracts.md
Original file line number Diff line number Diff line change
Expand Up @@ -328,3 +328,32 @@ Object reads and writes name individual keys. Catalog discovery and `depot gc --
## Site catalog

[`site.schema.json`](https://github.com/MetaSekaiLab/nnnotes/blob/main/src/nnnotes/schemas/site.schema.json) describes `nnnotes.site/1`. A named site stores its complete region-topic catalog in `site.json`. Each topic names an immutable product manifest and the metadata of its listing entry. Partial selections retain other topics; complete selections replace the requested region and product kind. The successful run points at the complete site layout so depot collection retains every published dependency.

## Product document schemas

The fixed product documents have the following schemas, also available through `nnnotes.schemas.load`.

| Schema | Document |
|---|---|
| [episode](https://github.com/MetaSekaiLab/nnnotes/blob/main/src/nnnotes/schemas/episode.schema.json) | Story episode commands and resource rows |
| [model-json](https://github.com/MetaSekaiLab/nnnotes/blob/main/src/nnnotes/schemas/model-json.schema.json) | Live2D model objects, resources and animation bindings |
| [prefab](https://github.com/MetaSekaiLab/nnnotes/blob/main/src/nnnotes/schemas/prefab.schema.json) | Prefab hierarchy nodes, transforms and components |
| [live](https://github.com/MetaSekaiLab/nnnotes/blob/main/src/nnnotes/schemas/live.schema.json) | Live entry and shared product parts |
| [live-scene](https://github.com/MetaSekaiLab/nnnotes/blob/main/src/nnnotes/schemas/live-scene.schema.json) | Live scene, lane resources and bands |
| [live-notes](https://github.com/MetaSekaiLab/nnnotes/blob/main/src/nnnotes/schemas/live-notes.schema.json) | Note prefabs and shared animation bindings |
| [live-noteskin](https://github.com/MetaSekaiLab/nnnotes/blob/main/src/nnnotes/schemas/live-noteskin.schema.json) | Note skin sprites and animation bindings |
| [live-noteeffect](https://github.com/MetaSekaiLab/nnnotes/blob/main/src/nnnotes/schemas/live-noteeffect.schema.json) | Note effect prefabs and animation bindings |
| [live-music](https://github.com/MetaSekaiLab/nnnotes/blob/main/src/nnnotes/schemas/live-music.schema.json) | Music layers and cue metadata |
| [live-sounds](https://github.com/MetaSekaiLab/nnnotes/blob/main/src/nnnotes/schemas/live-sounds.schema.json) | Sound groups, categories and routing |
| [story](https://github.com/MetaSekaiLab/nnnotes/blob/main/src/nnnotes/schemas/story.schema.json) | Story entry and shared product parts |
| [story-scene](https://github.com/MetaSekaiLab/nnnotes/blob/main/src/nnnotes/schemas/story-scene.schema.json) | Story scene objects, settings and graphics |
| [story-ui](https://github.com/MetaSekaiLab/nnnotes/blob/main/src/nnnotes/schemas/story-ui.schema.json) | Localized story UI objects and text settings |
| [story-asset](https://github.com/MetaSekaiLab/nnnotes/blob/main/src/nnnotes/schemas/story-asset.schema.json) | Story media prefab objects and animation bindings |

### Scene and media document schemas

- [room](https://github.com/MetaSekaiLab/nnnotes/blob/main/src/nnnotes/schemas/room.schema.json)
- [spot](https://github.com/MetaSekaiLab/nnnotes/blob/main/src/nnnotes/schemas/spot.schema.json)
- [story-video](https://github.com/MetaSekaiLab/nnnotes/blob/main/src/nnnotes/schemas/story-video.schema.json)
- [story-sound](https://github.com/MetaSekaiLab/nnnotes/blob/main/src/nnnotes/schemas/story-sound.schema.json)
- [crilips](https://github.com/MetaSekaiLab/nnnotes/blob/main/src/nnnotes/schemas/crilips.schema.json)
33 changes: 33 additions & 0 deletions docs/schemas.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,58 @@ schemas.load("plan") # the schema as a dict
| `common` | | definitions shared by the other schemas |
| `cost` | `nnnotes.cost/1` | the measured cost of a task's last run |
| `coverage` | `nnnotes.coverage/1` | the coverage of a run per class and per reason code |
| `crilips` | product document | CRI Lips tensor blocks and frontend constants |
| `episode` | product document | story commands and resource tables |
| `failures` | `nnnotes.failures/1` | the failed tasks and items of a run |
| `layout` | `nnnotes.layout/1` | the files a layout owns in its output directory |
| `live` | `1` | live chart entry and shared parts |
| `live-music` | `1` | music layers and cue metadata |
| `live-noteeffect` | `1` | note effect resources and animation bindings |
| `live-notes` | `1` | note prefabs, settings and animations |
| `live-noteskin` | `1` | note skin resources and animation bindings |
| `live-scene` | `1` | scene objects, settings and band timelines |
| `live-sounds` | `1` | sound groups and category routing |
| `live-ui` | `nnnotes.live-ui/1` (`format`) | live start strings, text styles, glyph pages and image bindings |
| `model-json` | `3` | Live2D model objects and animation bindings |
| `music-data` | `nnnotes.music-data/2` | every song and chart of one master data version ([music data](music-data.md)) |
| `objects` | `nnnotes.objects/1` | the objects a product reaches: their class, item status and files ([products](products.md)) |
| `plan` | `nnnotes.plan/1` | what a run would do and why (`depot plan`) |
| `prefab` | product document | prefab nodes, transforms and components |
| `product` | `nnnotes.product/1` | a product's manifest: its files and the files of the objects it uses, as content-addressed assets ([products](products.md)) |
| `result` | `nnnotes.result/1` | the result of a task, a function of its key alone |
| `room` | product document | room geometry, materials and textures |
| `run` | `nnnotes.run/1` | a run manifest: context, selection, tasks with keys and statuses, layouts |
| `scripts` | `nnnotes.scripts/1` | every MonoScript of the censuses, by object id |
| `site` | `nnnotes.site/1` | a named site's complete region-topic catalog and immutable product references |
| `spot` | product document | spot settings, characters and Spine data |
| `story` | `1` | story entry and shared parts |
| `story-asset` | `1` | story media prefab objects |
| `story-fonts` | `nnnotes.story-fonts/1` (`format`) | an episode's text bindings, font records, glyph pages and text shaders |
| `story-host` | `nnnotes.story-host/1` (`format`) | an Overlay story's host screen, layout and initial state |
| `story-scene` | `1` | story scene objects and graphics settings |
| `story-simple-ui` | `nnnotes.story-simple-ui/1` (`format`) | localized Overlay talk-window and system-message prefabs |
| `story-sound` | product document | cue metadata and waveform files |
| `story-ui` | `1` | localized story UI and text bindings |
| `story-video` | `1` | video dimensions, timing and media tracks |
| `task` | `nnnotes.task/1` | one stage applied to one subject, with everything needed to run it (`depot run`) |
| `ui-library` | `nnnotes.ui-library/1` | selected UI keys and their product manifests |
| `ui-pack` | `nnnotes.ui-pack/1` | native UI roots, prefab and controller identities, animation bindings |
| `view` | `nnnotes.view/1` | a semantic view of a master table, each row with its names and the objects it names |


The documents of the asset export are described in [contracts](contracts.md), the views in [views](views.md). The
schema files are in
[src/nnnotes/schemas](https://github.com/MetaSekaiLab/nnnotes/tree/main/src/nnnotes/schemas).

## Product documents

The product root and prefab schemas are listed in [Product document schemas](contracts.md#product-document-schemas). Their fields match the shared object interfaces consumed by ournotes-player.

Compare the shared constraints with an ournotes-player checkout or extracted package:

```sh
python scripts/check_product_schemas.py /path/to/ournotes-player
```

The check covers product, live, model, story, prefab, room, spot, audio, video and UI documents. Schema identifiers,
titles and descriptions may differ by package; validation constraints must agree.
76 changes: 76 additions & 0 deletions scripts/check_product_schemas.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
"""Compare the shared product contracts with an ournotes-player checkout or extracted package."""

from __future__ import annotations

import argparse
import json
from pathlib import Path

NAMES = (
"product",
"episode",
"model-json",
"prefab",
"live",
"live-scene",
"live-notes",
"live-noteskin",
"live-noteeffect",
"live-music",
"live-sounds",
"story",
"story-scene",
"story-ui",
"story-asset",
"story-fonts",
"story-host",
"story-simple-ui",
"ui-pack",
"ui-library",
"room",
"spot",
"story-video",
"story-sound",
"crilips",
)


def contract(value):
if isinstance(value, dict):
out = {}
for key, item in value.items():
if key in {"$id", "title", "description"}:
continue
if key in {"properties", "patternProperties", "$defs", "dependentSchemas"}:
out[key] = {name: contract(schema) for name, schema in item.items()}
elif key in {"const", "enum"}:
out[key] = item
else:
out[key] = contract(item)
return out
if isinstance(value, list):
return [contract(item) for item in value]
return value


def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("player", type=Path, help="ournotes-player root containing schema/")
args = parser.parse_args()
source = Path(__file__).resolve().parents[1] / "src" / "nnnotes" / "schemas"
errors = []
for name in NAMES:
paths = [source / f"{name}.schema.json", args.player / "schema" / f"{name}.schema.json"]
try:
left, right = [contract(json.loads(path.read_text(encoding="utf-8"))) for path in paths]
if left != right:
errors.append(f"{name}: schema constraints differ")
except (OSError, ValueError) as error:
errors.append(f"{name}: {error}")
if errors:
parser.exit(1, "\n".join(errors) + "\n")
print(f"{len(NAMES)} shared product schemas match")


if __name__ == "__main__":
main()
57 changes: 57 additions & 0 deletions src/nnnotes/schemas/crilips.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": [
"dtype",
"order",
"total_floats",
"blocks",
"constants",
"frontend"
],
"properties": {
"dtype": {
"const": "float32"
},
"order": {
"const": "LE"
},
"total_floats": {
"type": "integer",
"minimum": 0
},
"blocks": {
"type": "array",
"items": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": [
"name",
"offset",
"count"
],
"properties": {
"name": {
"type": "string"
},
"offset": {
"type": "integer",
"minimum": 0
},
"count": {
"type": "integer",
"minimum": 0
}
}
}
},
"constants": {
"type": "object"
},
"frontend": {
"type": "object"
}
},
"$id": "urn:nnnotes:schema:crilips",
"title": "crilips document"
}
Loading
Loading