Skip to content
Draft
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
2 changes: 2 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ contributors. This page routes deeper work without duplicating it.
bridge, SpacetimeDB, rendering, and delivery
- [Lowlands renderer](design/hegemony-lowlands-terrain.md) — terrain,
presentation, and performance principles
- [Living Realm V1](design/living-realm-v1.md) — coherent environmental motion,
bounded surface response, ecology budgets, and fail-closed design
- [Genesis water](design/genesis-water.md) — canonical coast, river, and fog
layout
- [Lowlands audio](design/lowlands-audio.md) — scene transitions and runtime
Expand Down
161 changes: 161 additions & 0 deletions docs/design/living-realm-v1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
# Living Realm V1

## Scope and baseline

Living Realm V1 is a browser-presentation upgrade for Genesis 001. It adds a
coherent wind contract, bounded local surface disturbance, subtle forest
motion, analytic water ripples, and tiny camera-local ambient ecology. None of
those effects is game state: SpacetimeDB remains authoritative for world
membership, Workers, routes, resources, ownership, schedules, and outcomes.

Implementation began from `0999e07b6aa36bb96613731e2837d096ae5a10ed` on the
`agent/living-realm-visual-ecology` branch. The audited toolchain was Node
22.23.1, npm 10.9.8, and Three.js 0.185.1. The unchanged baseline passed
`npm run check`: 266 Vitest files and 2,961 tests, TypeScript, licensing,
runtime-asset provenance, tracked-file size policy, production build,
production exclusions, and the Farcaster Mini App contract.

The canonical rendered-browser command failed before page launch because the
host Chrome bundle did not satisfy its single-link, clean-bundle attestation.
The Chrome installation was not modified. Deterministic loopback-only baseline
captures were instead recorded with the isolated in-app browser; the standard
command remains a required final check and any continued host failure will be
reported rather than hidden.

| Case | Grass instances / triangles / draws | Water triangles / draws | Canonical forest triangles / draws | Ambient cap |
| --- | ---: | ---: | ---: | ---: |
| High 1920×1080 | 2,167 / 58,509 / 3 | 21,198 / 3 | 136,418 / 1 | 30 Hz |
| Balanced 1280×720 | 837 / 17,577 / 2 | 21,198 / 3 | 76,334 / 1 | 22 Hz |
| Balanced tablet 1024×768 | 837 / 17,577 / 2 | 21,198 / 3 | 76,334 / 1 | 22 Hz |
| Balanced portrait 390×844 | 837 / 17,577 / 2 | 21,198 / 3 | 76,334 / 1 | 22 Hz |
| Balanced short landscape 667×375 | 837 / 17,577 / 2 | 21,198 / 3 | 76,334 / 1 | 22 Hz |
| Reduced 1280×720 | 156 / 2,340 / 1 | 21,198 / 3 | 30,139 / 1 | idle |

The baseline images and aggregate datasets are local QA artifacts rather than
runtime or repository assets. They contain only the synthetic 100-castle
fixture and no production identity, account, balance, token, route, or private
state.

## Adaptation decisions

Warpkeep uses the visual lesson of a landscape responding coherently, not the
implementation scale of a close first-person meadow:

- The existing `RealmAmbientScheduler` remains the only ambient clock. Its
frame cap is the maximum needed by active subsystems, never their sum.
- Grass, water, and forest reuse their existing draws. Living Realm may add at
most one instanced bird draw, one points draw, and one compact Rabbit draw in
High or Balanced, and adds none in Reduced or reduced motion.
- Worker wakes sample the owning renderer's sanitized current poses after its
ordinary update. Resource wagons do not yet expose an equivalent clean pose
API, so this version does not duplicate their interpolation or inspect DOM
transforms; wagon wakes are a later owner-layer follow-up.
- Disturbances live in a fixed-capacity renderer-only pool with preallocated
snapshots. They never create database rows, affect picking, alter routes, or
scan the 10,000-cell world.
- Forest wind uses compact normalized byte attributes where geometry ownership
permits and a root-anchored local-height fallback for leased instanced
primitives. Material failure keeps the existing static forest.
- Ambient life is deterministic, camera-local, non-pickable, and visually
subordinate to units, labels, routes, resources, selection, and hover.

This work contains original Warpkeep-native code. The public
[TUMBLE meadow](https://grass-world-meadow.netlify.app/) was used only as a
visual reference for camera-local density, coherent motion, reflective water,
and ambient wildlife. No TUMBLE source, shader, artwork, preset, or binary was
copied into the repository, decompiled, or made a dependency.

## Explicitly rejected techniques

A production 4096×4096 water solver is rejected for this scope. It would add
large floating-point targets, extra render passes, continuous simulation work,
and mobile memory pressure while bypassing Warpkeep's canonical welded water
geometry. Water response instead uses compile-time-bounded analytic ripple
slots in the existing materials.

Gameplay depth of field and a post-processing chain are also rejected. They
would blur labels, routes, resources, selection, and touch-readable strategy
surfaces. The fixed canonical sun, generated environment map, ACES output, and
existing fog remain the lighting and depth contract.

## Fail-closed and lifecycle contract

Reduced quality, reduced motion, strategic overview, hidden documents,
inactive presentation, context loss, shader-contract drift, and disposal all
disable optional moving ambience. The compact Rabbit also fails closed on a
missing, changed, oversized, or structurally incompatible model. No subsystem
owns a second animation frame loop or interval. A failed optional material,
asset, or ecology layer leaves terrain, water topology, forest placement,
Workers, interaction, and the Realm intact.

## Implemented presentation contract

- `realmLivingEnvironment.ts` is the single renderer-neutral wind and gust
definition. Grass and forest inject the same bounded world-space field into
their existing standard materials.
- Grass keeps its established instance pools and draw counts. High/Balanced
patches carry twelve/nine blades, denser meadow and Lowlands candidate fields,
brighter green authored palettes, and a faint chlorophyll fill so distant
blades remain green. Its lighting normal follows its bounded bend, sun-side
transmission remains restrained, and an unrolled quality-specific uniform
array accepts at most eight/four local disturbances.
- Water keeps the canonical welded geometry, analytic picking, fog treatment,
and three active draws. Four/two unrolled ripple slots contribute an
analytic Gaussian-ring height and derivative; deeper blue-grey body colour,
restrained Fresnel reflection, directional currents, and bounded foam make
it read as water without breaking the rivers' physical edge weld.
- Canonical forest batches and the immediate procedural fallback carry two
normalized `Uint8` attributes: root-anchored wind weight and local phase.
They retain one draw. Reduced and reduced-motion install no moving shader.
- Ambient ecology uses one tiny two-triangle-per-instance bird mesh and one
points draw shared by motes and transient material particles. It owns no
timer, animation frame, ray target, identifier, network request, or database
state, and is absent in Reduced/reduced-motion and Realm overview.
- Lowlands Rabbits use the exact 14,808-byte, 146-triangle compact static model
from the reviewed public Warpkeep-Assets release. Ten/six deterministic
camera-local instances share one non-pickable draw and transform-only hop;
the same-origin loader verifies byte length, SHA-256, mesh, vertex, and
triangle counts before presentation. No Rabbit transform is game state.
- Worker wakes read only the owning Worker layer's sanitized current pose
after its normal interpolation update. The per-material pool is fixed,
newest-first, and independently capped. Replacement of an oldest live slot
is reported as an aggregate eviction; a genuine failed insert is reported
separately as a drop. Neither report contains identities or positions.

## Final verification

The completed branch passed `npm run check`: 272 Vitest files and 2,982 tests,
TypeScript, licensing, all runtime-asset and provenance checks, tracked-file
size policy, production build, production exclusions, and the Farcaster Mini
App contract. Focused shader tests also compile against the pinned Three.js
shader chunks and assert static fallback on marker drift.

The same fixed-size in-app WebGL pass used for the baseline reported no grass,
water, forest, or Rabbit fallback. High presented 2,789 grass patches / 100,404
grass triangles / 3 grass draws plus 10 Rabbits; Balanced desktop and portrait
presented 1,243 / 33,561 / 2 plus 6 Rabbits. Existing water and forest topology
and draw counts remain unchanged.

The canonical `npm run qa:rendered-webgl` command was also re-run and failed
before page launch because the reviewed host Google Chrome executable was
unavailable to its fail-closed attestation. The host browser installation was
not modified; this is reported separately from the successful isolated in-app
WebGL evidence above.

| Case | Existing grass / water / forest draws | New draws / triangles | Living slots | Ambient cap |
| --- | ---: | ---: | ---: | ---: |
| High 1920×1080 | 3 / 3 / 1 | 3 / 1,484 | grass 8, water 4 | 30 Hz |
| Balanced 1280×720 | 2 / 3 / 1 | 3 / 888 | grass 4, water 2 | 22 Hz |
| Balanced tablet 1024×768 | 2 / 3 / 1 | 3 / 888 | grass 4, water 2 | 22 Hz |
| Balanced portrait 390×844 | 2 / 3 / 1 | 3 / 888 | grass 4, water 2 | 22 Hz |
| Balanced short landscape 667×375 | 2 / 3 / 1 | 3 / 888 | grass 4, water 2 | 22 Hz |
| Reduced 1280×720 | 1 / 3 / 1 | 0 / 0 | grass 0, water 0 | idle |

High and Balanced canonical forest wind attributes use 763,710 and 454,054
bytes respectively (two normalized bytes per merged vertex). The active
Balanced Worker fixture held exactly four grass disturbances and 48 transient
particles, replacing oldest fixed slots during sustained motion without a
genuine drop. Its Rabbit model passed the runtime loader with one draw, 876
triangles, and no fallback; High used 1,460 Rabbit triangles. Reduced held zero
moving ecology, zero Rabbit fetches, zero ripple/disturbance slots, zero new
draws, and no ambient scheduler demand.
30 changes: 30 additions & 0 deletions docs/reference/assets/2026-08-03-lowlands-rabbit/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Lowlands Rabbit compact runtime record

This record covers the exact visual-only compact Rabbit integrated into draft
PR #181. The source is the public Warpkeep-Assets release
[`rabbit-runtime-ui-bundle-2026-07-30`](https://github.com/ael-dev3/Warpkeep-Assets/releases/tag/rabbit-runtime-ui-bundle-2026-07-30).
The project owner explicitly requested this Rabbit integration on 2026-08-03.
That instruction authorizes the exact compact runtime in this public Warpkeep
PR, but does not approve merging or deployment and does not create a separate
open-content license.

| Runtime file | Bytes | Triangles | Uploaded vertices | SHA-256 |
| --- | ---: | ---: | ---: | --- |
| `public/models/hegemony/environment/wildlife/rabbit/hegemony-lowlands-rabbit-compact-2ecc7b1adf4c1d79.glb` | 14,808 | 146 | 384 | `2ecc7b1adf4c1d79b7ca2d5ea9a6727ed3f6d9072047466082bb912d34ea930c` |

The GLB is glTF 2.0, +Y up, +Z forward, one mesh, one material, no textures,
no external URIs, no skin, and no animation clips. It retains embedded vertex
colors and the supplied `KHR_materials_specular` declaration. The runtime
loader rechecks exact length, SHA-256, primitive count, vertex count, and
triangle count before presentation.

High and Balanced use this compact mesh as one camera-local instanced draw.
Motion is a renderer-owned transform animation on the existing Realm ambient
scheduler; it is not AI, collision, pathing, ownership, population, or
gameplay state. Rabbits are non-pickable, hidden in overview, absent from
Reduced and reduced-motion, and fail closed if the asset cannot be verified.

The complete public release also contains rigged High/Balanced LODs and UI
art, but those files are not copied into this runtime PR. The source release
records public archival/distribution authorization and project-owned
provenance while explicitly declining to assert a separate open-license grant.
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"assetId": "warpkeep.environment.wildlife.rabbit",
"category": "Environment/Wildlife",
"designation": {
"gameplayAuthority": false,
"visualOnly": true
},
"runtime": {
"bytes": 14808,
"embeddedBufferBytes": 13164,
"file": "Warpkeep_Rabbit_LOD2_Compact_Static_Runtime.glb",
"rigged": false,
"sha256": "2ecc7b1adf4c1d79b7ca2d5ea9a6727ed3f6d9072047466082bb912d34ea930c",
"triangles": 146,
"uploadedVertices": 384
},
"provenance": {
"license": "Project-owned; authored for Warpkeep",
"publicArchiveAuthorization": "authorized by Ael",
"release": "https://github.com/ael-dev3/Warpkeep-Assets/releases/tag/rabbit-runtime-ui-bundle-2026-07-30",
"separateOpenLicense": "not asserted",
"source": "Source/Warpkeep_Rabbit_Editable.blend"
},
"runtimeContract": {
"collision": false,
"embeddedTextures": 0,
"externalDependencies": 0,
"buffers": 1,
"frontFacing": "+Z",
"materials": 1,
"meshes": 1,
"metersPerUnit": 1,
"pivot": "ground contact centered between hind feet",
"vertexColorAttribute": "COLOR_0 / WK_Color"
},
"schema": "warpkeep.rabbit-runtime-integration-record.v1",
"version": "1.0.0"
}
Binary file not shown.
88 changes: 88 additions & 0 deletions scripts/install-lowlands-rabbit-runtime.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { createHash } from 'node:crypto';
import { resolve } from 'node:path';

import {
ensureContainedDirectory,
installAtomicFileFamily,
readContainedRegularFile
} from './atomic-install-file-family.mjs';

const root = resolve(import.meta.dirname, '..');
const suppliedRoot = process.env.WARPKEEP_RABBIT_RUNTIME_ROOT
? resolve(process.env.WARPKEEP_RABBIT_RUNTIME_ROOT)
: undefined;
const sourceFilename = 'Warpkeep_Rabbit_LOD2_Compact_Static_Runtime.glb';
const destinationFilename =
'hegemony-lowlands-rabbit-compact-2ecc7b1adf4c1d79.glb';
const expectedBytes = 14_808;
const expectedHash =
'2ecc7b1adf4c1d79b7ca2d5ea9a6727ed3f6d9072047466082bb912d34ea930c';

if (!suppliedRoot) {
throw new Error(
'Set WARPKEEP_RABBIT_RUNTIME_ROOT to the exact Rabbit Runtime/Environment/Wildlife/Rabbit directory.'
);
}

const bytes = readContainedRegularFile({
root: suppliedRoot,
relativePath: sourceFilename,
label: 'Lowlands Rabbit compact supplied runtime',
expectedBytes
});
const hash = createHash('sha256').update(bytes).digest('hex');
if (hash !== expectedHash) {
throw new Error('Lowlands Rabbit compact supplied runtime hash changed: ' + hash + '.');
}
const jsonLength = bytes.readUInt32LE(12);
const json = JSON.parse(bytes.subarray(20, 20 + jsonLength).toString('utf8').trim());
const primitive = json.meshes?.[0]?.primitives?.[0];
const embeddedBuffer = json.buffers?.[0];
if (
bytes.subarray(0, 4).toString('ascii') !== 'glTF'
|| bytes.readUInt32LE(4) !== 2
|| bytes.readUInt32LE(8) !== bytes.byteLength
|| json.asset?.copyright !== 'Copyright Ael / Warpkeep; project-authored rabbit runtime asset'
|| json.scenes?.length !== 1
|| json.scenes[0]?.name !== 'WK_Rabbit_AuthoringScene'
|| json.nodes?.length !== 1
|| json.nodes[0]?.name !== 'WK_Rabbit_LOD2_Compact_Static'
|| json.meshes?.length !== 1
|| json.meshes[0]?.name !== 'WK_Rabbit_LOD2_Compact_Static_Mesh'
|| json.meshes[0]?.primitives?.length !== 1
|| primitive?.indices !== 3
|| primitive?.material !== 0
|| primitive?.attributes?.POSITION !== 0
|| primitive?.attributes?.NORMAL !== 1
|| primitive?.attributes?.COLOR_0 !== 2
|| json.accessors?.[0]?.count !== 384
|| json.accessors?.[3]?.count !== 438
|| json.materials?.length !== 1
|| json.buffers?.length !== 1
|| embeddedBuffer?.byteLength !== 13_164
|| Object.prototype.hasOwnProperty.call(embeddedBuffer ?? {}, 'uri')
|| json.images !== undefined
|| json.textures !== undefined
|| json.samplers !== undefined
|| json.animations !== undefined
|| json.skins !== undefined
) {
throw new Error('Lowlands Rabbit compact supplied runtime structure changed.');
}

const destinationRoot = ensureContainedDirectory({
root,
relativePath: 'public/models/hegemony/environment/wildlife/rabbit',
label: 'Lowlands Rabbit runtime directory'
});
installAtomicFileFamily({
destinationRoot,
entries: [{
bytes,
label: 'Lowlands Rabbit compact runtime',
relativePath: destinationFilename
}]
});
console.log(
'Lowlands Rabbit compact: 14,808 bytes, 146 triangles, sha256 ' + expectedHash
);
Loading