# ELYTH HIVE — Agent World Creation Specification

Package version: **1**. World document version: **5**. Updated: **2026-10-02**.

## Instructions to the world-building agent

Create an original ELYTH HIVE world that satisfies the user's creative brief. Deliver a real, downloadable **`<world-name>.hive.zip`** file. The user will import it using HIVE's **Import world ZIP** button. Do not deliver only a plan, source code, an HTML page, a Three.js application, or a Godot project.

This document is self-contained. You do not need the ELYTH repository, an ELYTH account, API keys, or a running ELYTH server to create the package. Use your available file-generation tools. You may generate GLB models with tools such as Tripo when available and permitted, create them yourself, or build entirely from the supported procedural shapes. External generation tools are optional.

HIVE supplies rendering, storage, character loading, photography, and world sharing. Your deliverable supplies the world data and optional custom GLSL programs. Shader effects have no category whitelist or required artistic style: invent the visual behavior that fits the user's brief within the technical contract in section 5. Choose meaningful editable parameters for that behavior; HIVE generates their controls from the declared types.

Use the user's requested visual theme, scale, and mood. The small example below demonstrates the format; do not treat its appearance as the requested finished world. Keep walkable areas flat, create a clear arrival point, and leave room for a character and photography. Avoid unnecessary requests for credentials or installation of a game engine.

If revising the author’s original source ZIP, preserve stable object IDs and shader parameter names whenever they still identify the same things. Return an updated ZIP and briefly describe what changed. HIVE does not export world ZIPs. Shared or copied HIVE assets remain the creator’s content and must not be downloaded or included in your package. Never include credentials or character models in the world ZIP.

## 1. ZIP layout and delivery

```text
my-world.hive.zip
  world.json
  assets/
    pavilion.glb
    tree.glb
```

- `world.json` must be UTF-8 JSON directly at the archive root, without a wrapping directory or byte-order mark. No comments, trailing commas, NaN, or Infinity.
- The only allowed files are `world.json` and declared `.glb` files. A procedural-only world needs just `world.json`.
- Model paths must match `^assets/[A-Za-z0-9_-]+\.glb$`. Paths and names are case-sensitive. Use forward slashes.
- Do not include `.js`, `.html`, `.ts`, `.glsl`, textures as separate files, README files, screenshots, `.DS_Store`, `__MACOSX`, or source folders. Embed GLSL strings in JSON and textures inside GLB.
- Do not use external URLs, file-system paths, nested ZIPs, duplicate file names, encryption, or path traversal. Use a normal ZIP with stored or DEFLATE compression.
- Hand the ZIP to the user. A source folder or a pasted JSON block alone is not the final deliverable. Keep editable sources outside the ZIP if useful.

## 2. Complete valid starter manifest

The following JSON is a complete, importable format example. Its shader demonstrates the interface, not a required effect, parameter set, or artistic direction. Replace the example geometry, programs, and parameters to suit the user's brief. Save it as `world.json` and ZIP that file at the archive root. All objects below are procedural, so no GLB files are needed.

```json
{
  "format": "elyth-hive",
  "version": 1,
  "title": "Moonlight Courtyard",
  "description": "A quiet courtyard with an adjustable breathing light.",
  "assets": [],
  "document": {
    "version": 5,
    "savedAt": "2026-09-29T00:00:00.000Z",
    "objects": [
      {
        "id": "courtyard-floor",
        "name": "Courtyard floor",
        "assetId": null,
        "geometry": { "kind": "box", "size": [18, 0.2, 18] },
        "position": [0, -0.1, 0],
        "rotation": [0, 0, 0],
        "scale": [1, 1, 1],
        "surface": {
          "color": "#435b65",
          "roughness": 0.85,
          "metalness": 0,
          "emissive": "#000000",
          "emissiveIntensity": 0,
          "opacity": 1,
          "doubleSided": false
        },
        "castShadow": true
      },
      {
        "id": "arrival-lantern",
        "name": "Lantern — adjust glow and color",
        "assetId": null,
        "geometry": { "kind": "sphere", "radius": 0.3, "segments": 20 },
        "position": [0, 1.2, -1],
        "rotation": [0, 0, 0],
        "scale": [1, 1, 1],
        "shader": { "id": "breathing-glow", "values": {} },
        "castShadow": false
      }
    ],
    "environment": {
      "skyboxId": null,
      "skyboxRotationDeg": 0,
      "skyboxIntensity": 1,
      "sunAzimuthDeg": 40,
      "sunElevationDeg": 50,
      "sunIntensity": 1.8,
      "sunColor": "#c2d8ff",
      "shadowAreaHalf": 12,
      "toonEnabled": false,
      "colorGrade": { "saturate": 1, "brightness": 1, "contrast": 1, "hueRotateDeg": 0 }
    },
    "atmosphere": {
      "background": "#111d37",
      "ambientIntensity": 0.8,
      "fogColor": "#111d37",
      "fogNear": 25,
      "fogFar": 80,
      "fogEnabled": false,
      "bloom": 0.6,
      "walkRadius": 8
    },
    "spawn": { "position": [0, 0, 6], "rotationYDeg": 0 },
    "shaders": [
      {
        "id": "breathing-glow",
        "name": "Breathing light",
        "vertex": "void main() { gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }",
        "fragment": "uniform float uTime; uniform float uPower; uniform float uSpeed; uniform vec3 uColor; uniform bool uPulse; void main() { float pulse = uPulse ? 0.85 + 0.15 * sin(uTime * uSpeed) : 1.0; gl_FragColor = vec4(uColor * uPower * pulse, 1.0); }",
        "transparent": false,
        "doubleSided": false,
        "parameters": [
          { "name": "uPower", "label": "Light intensity", "type": "number", "min": 0, "max": 5, "step": 0.05, "default": 2.5 },
          { "name": "uSpeed", "label": "Pulse speed", "type": "number", "min": 0, "max": 3, "step": 0.05, "default": 1 },
          { "name": "uColor", "label": "Light color", "type": "color", "default": "#ffd08a" },
          { "name": "uPulse", "label": "Enable pulsing", "type": "boolean", "default": true }
        ]
      }
    ]
  }
}
```

Use the actual creation/update time for `savedAt`, in UTC ISO 8601 with a trailing `Z`. Both version fields are required and different: package `version: 1`, document `version: 5`.

## 3. Manifest and references

Objects are strict: **do not add undocumented fields** at any level. For example, `scripts`, `lights`, `camera`, `textures`, `children`, `colliders`, and `resident` are not accepted document fields. If you need a feature the format does not support, explain that limitation outside the ZIP rather than inventing a field.

| Manifest field | Contract |
|---|---|
| `format` | Exactly `"elyth-hive"` |
| `version` | Exactly `1` |
| `title` | Nonempty string, at most 60 characters |
| `description` | String, at most 400 characters; use `""` if empty |
| `assets` | Array of GLB declarations; use `[]` for procedural-only worlds |
| `document` | World document with `version`, `savedAt`, `objects`, `environment`, `atmosphere`, `spawn`, and `shaders`, as above |

Each asset declaration has exactly these fields:

```json
{ "id": "40e4bd39-ec17-4c6c-8290-af64a98155b1", "path": "assets/pavilion.glb", "name": "Pavilion" }
```

Generate a valid UUID v4 for each distinct GLB. Asset names must be nonempty after trimming and at most 80 characters. Each declaration must correspond to one bundled file and be referenced by at least one object. Every non-null object `assetId` must resolve to a declaration. Asset IDs and paths must be unique. Reuse a declaration when placing the same GLB multiple times. HIVE remaps these asset UUIDs when importing; no prior asset registration is needed.

## 4. Objects, units, and materials

Every object requires `id`, `name`, `assetId`, `position`, `rotation`, and `scale`.

| Field | Contract |
|---|---|
| `id` | Stable unique string, 1–100 characters. `__resident__` is reserved. Prefer `garden-bench-01` style IDs |
| `name` | Human-readable nonempty label, at most 80 characters |
| `assetId` | GLB declaration UUID, or `null` for a procedural object |
| `position` | Three finite numbers `[x,y,z]`, in meter-like units |
| `rotation` | Three finite numbers `[x,y,z]`, XYZ Euler **radians** |
| `scale` | Three finite numbers; prefer positive values and `[1,1,1]` when possible |
| `geometry` | Required for procedural objects; absent for GLB objects |
| `surface` | Optional standard material settings |
| `shader` | Optional `{ "id": "shader-id", "values": { ... } }` |
| `castShadow` | Optional boolean, default `true` |

Use exactly one representation:

- **Procedural:** `assetId: null` plus `geometry`.
- **GLB:** `assetId: "<UUID>"` and no `geometry`.

Coordinates are right-handed, Y-up. Put the ground surface near Y=0. A plane is created in XY; rotate it by `[-1.57079632679,0,0]` to face upward. A cylinder's height follows Y. GLB transforms are relative to its own origin; export useful pivots and real-world scales.

| `geometry.kind` | Required dimensions | Optional segments |
|---|---|---|
| `box` | `size: [width,height,depth]` | None |
| `plane` | `size: [width,height]` | Integer 1–128, default 1; applied to both axes |
| `sphere` | `radius` | Integer 3–128, default 24; applied to width and height |
| `cylinder` | `radiusTop`, `radiusBottom`, `height` | Integer 3–128, default 16 |
| `torus` | `radius`, `tube` | Integer 3–128, default 32 along the ring; tube cross-section fixed at 12 |

Dimensions are 0.01–200, except `radiusTop` permits 0–200 for cones. Do not subdivide shapes unnecessarily. Animated vertex displacement needs enough vertices; a wave plane usually needs 32–72 segments, not 1.

`surface` accepts only the following fields. Each has a default if omitted:

| Field | Range/type | Default |
|---|---|---|
| `color` | `#rrggbb` | `#ffffff` |
| `roughness` | 0–1 | 0.8 |
| `metalness` | 0–1 | 0 |
| `emissive` | `#rrggbb` | `#000000` |
| `emissiveIntensity` | 0–5 | 0 |
| `opacity` | 0–1 | 1 |
| `doubleSided` | Boolean | false |

GLBs retain their authored materials when `surface` is omitted. A supplied surface overrides materials across the entire GLB object. A custom shader overrides both. Use `toonEnabled: false` to preserve standard PBR appearance. An emissive material or glowing shader looks bright but is not a local light source that illuminates nearby objects.

The current toon conversion does not preserve authored `emissive`, `emissiveMap`, or `emissiveIntensity`. If ceiling lamps, posters, or a distant backdrop depend on emission, use `toonEnabled: false` and verify their appearance in HIVE. Increasing Bloom does not restore lost emission or illuminate a room.

HIVE also draws a 20×20 shadow-receiving plane at world Y=0. A package floor exactly at Y=0 overlaps it and can produce depth competition. Keep authored floor and overlay surfaces separated; for example, put the walking surface at Y=0.01 and set spawn Y=0.01, with floor decorations slightly higher. Check the actual top surface, including GLB child transforms, rather than only the object's origin. This spacing is a workaround for the current renderer's shared floor plane.

The creator can move, rotate, scale, duplicate, and delete whole HIVE objects. GLB child meshes are not individually editable in HIVE. Separate anything the creator should adjust independently; combine static details into a GLB to reduce object count.

## 5. Custom GLSL and editable parameters

This is a programmable material interface, not a list of effect presets. Any visual behavior expressible using the supported geometry, shader stages, inputs, and budgets is allowed. The `environment` and `atmosphere` fields are separate scene settings; they do not define or limit the kinds of custom shaders you may create. Editable parameter types describe data, not effect categories.

HIVE uses Three.js `ShaderMaterial` on a WebGL renderer. Supply a `vertex` and `fragment` source string in each shader definition. Use GLSL1-style `varying` and `gl_FragColor`. Three.js supplies standard attributes/matrices such as `position`, `normal`, `uv`, `modelMatrix`, `modelViewMatrix`, and `projectionMatrix`; do not redeclare these built-ins. No `#version` directive.

| Interface | Contract |
|---|---|
| Geometry | A procedural shape or the triangle meshes in a GLB; bind the shader with the object's `shader.id` |
| Vertex stage | Compute `gl_Position`; vertex displacement and time-dependent motion are permitted |
| Fragment stage | Compute `gl_FragColor` in linear RGBA; procedural patterns, opacity masks, and `discard` are permitted |
| Between stages | Declare matching GLSL `varying` values in both stages as needed |
| Runtime input | `uTime`, standard Three.js geometry/transformation inputs, and your declared editable uniforms |
| Editable input | Author-defined parameter names and meanings, using `number`, `color`, or `boolean` |
| Composition | Use multiple shader-bearing objects, or multiple triangles within a mesh, within the world budgets |

Geometry and shader math may express moving surfaces, trails, or particle-like appearances without adding a new effect type to the manifest. Such appearances do not require a separate particle-system field. HIVE does not provide persistent simulation state, particle emitters, or automatic camera-facing billboards; implement the visual behavior from the available inputs where possible. Choose adequate geometry bounds for displaced vertices because culling and editing bounds do not follow shader motion.

A definition has `id`, `name`, `vertex`, `fragment`, `transparent`, `doubleSided`, and `parameters`, as in the starter. Shader IDs match `^[a-zA-Z0-9_-]{1,80}$` and are unique. Names are 1–80 characters. `transparent` and `doubleSided` default to false. Transparent custom materials disable depth writing. Omitted `parameters` defaults to an empty array.

Materials use normal alpha blending and depth testing. Custom additive blending, scene color/depth textures, reflection/refraction render passes, and screen-space or volumetric rendering pipelines are not exposed. If an effect requires unavailable inputs or passes, explain the limitation or implement an appropriate mesh-based approximation; do not claim that the missing rendering feature exists.

For soft masks, mist, and particle quads, explicitly set the shader's `transparent: true`; an alpha below 1 in `gl_FragColor` alone does not enable blending. An opaque shader writes depth: output alpha 1 for its visible surface, and use `discard` for holes. Transparent custom shaders have `depthWrite: false` and `depthTest: true`. Standard `surface.opacity < 1` materials currently retain depth writing, so they are not interchangeable with transparent custom shaders for overlapping effects. Avoid intersecting or coplanar transparent layers; triangles within one particle mesh are not individually sorted back to front. Emit ordinary, non-premultiplied RGB for this blending contract.

HIVE supplies `uTime` in seconds since the local scene clock began. To use it, declare `uniform float uTime;` in the relevant source. Time is not synchronized between visitors. Do not define `uTime` as an editable parameter.

### Stable shader math and motion

Keep intermediate computations defined and output finite, nonnegative linear RGB with alpha in [0,1]. A shader can compile successfully yet produce dark patches or unstable frames in the actual antialiased HDR pipeline. In a world investigation, an opaque halo using `pow(1.0 - abs(dot(vN, vV)), 1.5)` produced black pixels; guarding that base removed them. Vertex-normalized vectors can leave their intended domain after interpolation, especially around small triangles and antialiased edges. Re-normalize with a nonzero length guard and clamp before a fractional power:

```glsl
// Inside fragment main(); vN and vV are matching varyings from the vertex stage.
vec3 n = vN / max(length(vN), 0.0001);
vec3 v = vV / max(length(vV), 0.0001);
float rimBase = clamp(1.0 - abs(dot(n, v)), 0.0, 1.0);
float rim = pow(rimBase, 1.5);
```

Guard divisions, square roots, logarithms, and normalization at their inputs too. A clamp on the final color cannot reliably repair an undefined intermediate value. Do not depend on a particular GPU's handling of NaN or Infinity; postprocessing is not a sanitizer. Negative bases are outside the defined domain of [GLSL `pow`](https://github.com/KhronosGroup/OpenGL-Refpages/blob/main/es3.1/pow.xml).

`smoothstep` requires `edge0 < edge1`. Use `1.0 - smoothstep(low, high, value)` for a falling transition, rather than reversed or equal edges. For example, replace `smoothstep(0.16, 0.0, distance)` with `1.0 - smoothstep(0.0, 0.16, distance)`. Keep editable thresholds strictly ordered across their entire slider range. The domain rules are defined in the [Khronos GLSL reference](https://registry.khronos.org/OpenGL-Refpages/es3/html/smoothstep.xhtml).

Default animated light to steady or gentle, continuous motion. Avoid high-contrast random drops driven by `floor(uTime * rate)`, rapidly changing hashes, or hard on/off steps across large bright surfaces. An existing neon shader used random brightness drops and a `sin(time * 47.0)` term; disabling its flicker control substantially reduced frame changes. Offer an animation switch with a steady default and small, bounded variation when enabled, for example:

```glsl
uniform float uTime;
uniform bool uAnimate; // Declare a boolean parameter with default false.
void main() {
  float pulse = uAnimate ? 0.95 + 0.05 * sin(uTime * 0.7) : 1.0;
  gl_FragColor = vec4(vec3(1.2) * pulse, 1.0);
}
```

For looping particle motion, fade out before a `mod`/`fract` reset and fade in afterward. Keep each quad's seed and center consistent across all its vertices and compatible with its GLB and object transforms; a shader that decodes grid cells from positions may break when the object is moved, rotated, scaled, or its vertices are quantized. HIVE's CPU culling bounds remain based on undeformed geometry. Keep the authored bounds large enough for the complete displacement, and check camera changes as well as time changes.

Each parameter requires a unique `name`, `label`, `type`, and `default`:

| Type | GLSL uniform | Additional fields | Creator control |
|---|---|---|---|
| `number` | `float` | Required `min`, `max`; optional `step` | Slider |
| `color` | `vec3` | None | Color picker |
| `boolean` | `bool` | None | Switch |

- Names match `^u[A-Z][A-Za-z0-9_]{0,38}$`; `uTime` is reserved. Labels are 1–60 characters. Use labels in the user's language.
- Number min/max must be within -1000–1000, with min < max. Default and overrides must be within the range. Step is 0.001–100, default 0.01. Declare number parameters as `float`, not `int`.
- Colors are six-digit hex `#rrggbb`. HIVE converts them to linear RGB through Three.Color before assigning uniforms. Boolean defaults/overrides must be JSON `true` or `false`.
- Declare the corresponding uniforms yourself, in every shader stage that uses them. Merely listing a parameter does not make it affect the image; use it in the shader computation.
- Each object's `shader.values` contains overrides keyed by declared parameter names. Omitted values use the definition's defaults. Undefined parameter names and wrong types/ranges are rejected.
- No preprocessor directives (`#...`) or `for`, `while`, or `do` loops. Each source must contain `void main(...)`. Functions, arithmetic, conditionals, and other GLSL constructs are permitted when they compile under this contract. Comments are allowed. These syntax limits apply independently of the effect's subject or style.
- Custom texture/sampler bindings, custom vertex attributes, JavaScript, network access, render targets, and arbitrary postprocessing chains are not part of this format. Normal/UV attributes depend on the geometry supplied; prefer `position` for portable procedural effects.
- Output linear color. HIVE applies exposure, Bloom, tone mapping and color grading afterward. Colors greater than 1 can produce glow when Bloom is enabled. Do not add a second output color conversion or tone-mapping pass.
- Custom materials do not automatically receive HIVE's PBR lighting, fog, or shadows and do not cast shadows in this version. Vertex displacement does not update editing bounds or collision geometry.

Compile and visually check shaders when a compatible WebGL environment is available. HIVE compiles them during import before uploading models. Do not claim GPU verification from JSON validation alone. These format restrictions do not guarantee a shader's performance across all GPUs.

## 6. Environment, atmosphere, and arrival

Include the complete `environment` object from the starter and modify its values. It has exactly these fields:

| Field | Accepted values / authoring guidance |
|---|---|
| `skyboxId` | Use `null` for a fully portable world. Non-null IDs reference HIVE's built-in skybox catalog, not a URL or a bundled image |
| `skyboxRotationDeg` | Finite degrees, normally 0–360 |
| `skyboxIntensity` | 0–2 |
| `sunAzimuthDeg` | Finite degrees, normally -180–180 |
| `sunElevationDeg` | Finite degrees; usually 10–80 |
| `sunIntensity` | Nonnegative finite number; usually 0–3 |
| `sunColor` | `#rrggbb` |
| `shadowAreaHalf` | Positive finite number; choose a practical shadow coverage size in world units |
| `toonEnabled` | Boolean; `false` is recommended for authored standard materials |
| `colorGrade.saturate` | 0–2 |
| `colorGrade.brightness` | 0.5–1.5 |
| `colorGrade.contrast` | 0.5–1.5 |
| `colorGrade.hueRotateDeg` | -180–180 |

If supplying `atmosphere`, include all its fields:

| Field | Accepted values |
|---|---|
| `background`, `fogColor` | `#rrggbb` |
| `ambientIntensity` | 0–3 |
| `fogNear` | 0–150 |
| `fogFar` | 1–200, strictly greater than fogNear even if fog is disabled |
| `fogEnabled` | Boolean |
| `bloom` | 0–2 |
| `walkRadius` | 5–90, measured in the XZ plane from world origin |

`spawn` is `{ "position": [x,y,z], "rotationYDeg": degrees }`. Its rotation uses **degrees**, unlike object rotations. Rotation 0 looks toward -Z. Set the spawn inside `walkRadius`, on the intended ground surface and outside objects.

Walking maintains eye height at spawn Y + 1.6. There is currently no collision, gravity, jumping, stair climbing, or terrain-following. Use a flat play area. The radial boundary does not create walls. Design scenic objects around that constraint.

## 7. GLB requirements and budgets

Use self-contained glTF 2.0 binary `.glb` files with triangle meshes, embedded buffers and embedded textures. Do not reference external resources or embed scripts. Prefer standard PBR materials and ordinary uncompressed GLB input; do not rely on arbitrary glTF extensions, Draco/KTX2 loaders, or an external runtime being available. HIVE's supported optimization path uses Meshopt and WebP.

HIVE optimizes imported GLBs, downsizes textures to a maximum edge of 1024px, converts them to WebP and checks the stored result. Package import preserves authored normals/tangents. Review the result because texture conversion and resizing can affect appearance. GLB animation playback is not supported by the world object renderer.

| Budget | Maximum |
|---|---|
| ZIP file size | 50 MiB = 52,428,800 bytes |
| Sum of extracted files | 50 MiB |
| Delivered world data (settings + models) | 50 MiB, excluding HIVE runtime and separately summoned characters |
| Normalized serialized `document` JSON | 768 KiB = 786,432 bytes |
| Raw `world.json` entry | 819,200 bytes; keep it compact and comfortably below this |
| ZIP entries, including directory entries | 128 |
| Distinct GLB declarations | 100 |
| World objects | 200 |
| World triangles, counting every placed instance | 1,500,000 |
| Triangles per GLB, including mesh reuse by nodes and GPU instances | 100,000 |
| Each GLB after HIVE optimization | 8 MiB |
| Input texture edge | 8,192px; stored edge 1,024px |
| Aggregate input texture pixels per GLB | 512,000,000; stored limit 64,000,000 |
| Shader definitions | 8 |
| Parameters per shader | 24 |
| Vertex source / fragment source | 16,000 characters each |

Keep input GLBs below 8MiB where practical; do not assume optimization can rescue a huge model. Texture budgets do not guarantee safe GPU memory usage. Prefer a few reused materials, modest textures and low subdivision.

Ordinary accounts can save 2 worlds, own 100 assets, upload 50 MiB of distinct source asset bytes, and share 300 photos. Operator-managed exceptions belong only to the configured account. Photos and temporary uploads are not included in the asset storage meter; adding them is an undecided policy. A package import can leave completed asset uploads in the library after a later failure.

For procedural triangle budgeting, HIVE counts box=12, plane=2*s², sphere=2*s*(s-1), cylinder=4*s, torus=24*s, where s is `segments` including its default. A reused model's triangle count is multiplied by its number of object placements. Reusing an asset saves file size, not the placed-triangle budget.

## 8. Build the ZIP without the ELYTH repository

Python's standard library is sufficient. First write your completed `world.json` and any declared GLBs into a local source directory. Run this script from a directory containing that source folder; adjust the first two paths. It creates an archive with the correct root layout and only the declared files.

```python
import json
import re
import zipfile
from pathlib import Path

source = Path("my-world-source")
output = Path("my-world.hive.zip")
manifest = json.loads((source / "world.json").read_text(encoding="utf-8"))
assert manifest["format"] == "elyth-hive"
assert manifest["version"] == 1
assert manifest["document"]["version"] == 5

# Compact JSON keeps archive and document sizes predictable.
entries = {"world.json": json.dumps(
    manifest, ensure_ascii=False, allow_nan=False, separators=(",", ":")
).encode("utf-8")}
for asset in manifest["assets"]:
    relative = asset["path"]
    assert re.fullmatch(r"assets/[A-Za-z0-9_-]+\.glb", relative), relative
    assert relative not in entries, relative
    entries[relative] = (source / relative).read_bytes()

assert len(entries) <= 128
assert len(entries["world.json"]) <= 819_200
assert sum(map(len, entries.values())) <= 50 * 1024 * 1024
with zipfile.ZipFile(output, "x", compression=zipfile.ZIP_DEFLATED) as archive:
    for relative, data in entries.items():
        archive.writestr(relative, data)
assert output.stat().st_size <= 50 * 1024 * 1024
print(output.resolve())
```

This packaging script is **not a complete format, GLB, or shader validator**. Check the contracts and budgets above. File mode `"x"` avoids overwriting an existing ZIP; choose a new output name when needed. Keep scripts and sources outside the ZIP.

If you happen to have the ELYTH repository, the optional commands below provide its actual schema and GLB checks. Run them from `apps/web`; they are not required for independent creators:

```text
npm run hive:world -- pack <source-directory> <new-output.hive.zip>
npm run hive:world -- check <world.hive.zip>
npm run hive:world -- unpack <world.hive.zip> <new-source-directory>
```

## 9. Handoff and later revisions

Before delivering, verify:

1. The final file is a ZIP with `world.json` at its root and no unlisted files.
2. JSON is valid, versions are correct, and no undocumented keys were invented.
3. Every GLB declaration is both bundled and used; every non-null asset reference resolves.
4. Object IDs and shader IDs are unique; all shader bindings and overrides are valid.
5. Geometry, texture, file-size, triangle, and shader budgets are respected.
6. Ground orientation, model scale, spawn height/direction, and walking radius are sensible.
7. Exposed shader controls have understandable labels and visibly affect the intended effect. Check their minimum, default, and maximum values for defined math and stable output.
8. Separate format/compile checks from visual checks. HIVE's import compiler renders a 1×1 box at initial time; it does not validate the authored geometry, temporal stability, or the full postprocessing pipeline. Prefer an isolated headless render first, sample fixed times and camera positions, and compare opaque and transparent effects with Bloom enabled and disabled. Do not expose the user's active screen to unverified flashing content. Test the optimized GLBs in HIVE when a safe environment is available.
9. State exactly what you verified, including the renderer/GPU and any limits. If you could not render or import the world, say so briefly; do not claim that it was tested inside HIVE. Report renderer defects separately from package workarounds.

Return the ZIP plus a short description of the world and its adjustable effects. Tell the user to open a new HIVE world, select **Import world ZIP**, confirm the included content, and then **Save**. Import replaces the current editing world and clears its undo history. It does not save the world automatically, although bundled models are uploaded during import.

Creators can export their edited world as another ZIP and send it back to you. The optional reimport setting preserves matching objects' position/rotation/scale and explicit compatible shader overrides with the same shader ID. It does not preserve the previous atmosphere, remove/add decisions, or summoned characters. Stable IDs make this workflow useful.

World ZIPs contain no VRM characters, AI credentials, character IDs, or poses. HIVE handles each user's characters separately. Multiplayer, in-world AI conversation, physics, arbitrary game scripts, custom audio, standalone particle-simulation runtimes, and custom postprocessing pipelines are outside this format. This does not exclude procedural motion or particle-like visuals implemented using section 5's material interface. Do not add unsupported ZIP files or JSON fields.

## User's creative brief

The user supplies the creative brief after this document. Do not infer a required theme or effect from the format example above.

> Create a HIVE world according to this specification. My creative brief is: [describe the desired world]. Design the geometry and shader behavior to fit this brief, expose useful creator controls, and deliver the finished HIVE ZIP.
