Skip to content

Architecture

The library is one published npm package (declarative-hex-worlds) that internally decomposes into 20 domain sub-packages under src/. Cross-domain imports traverse barrel re-exports only — Biome’s noRestrictedImports rule enforces it.

Sub-package Purpose Public surface
traits/ koota trait declarations (37 traits) declarative-hex-worlds/traits
types/ Branded primitives + interface definitions declarative-hex-worlds/types
coordinates/ Hex algebra, projection, grid layout, world↔hex transforms /coordinates, /grid, /layout, /projection
manifest/ KayKit pack manifest schema + the autogenerated FREE manifest /manifest/schema, /manifest/free
ingest/ Node-side asset ingest, source-root validation, manifest generation /ingest
cli/commands/bootstrap/ CLI bootstrap subcommand + programmatic API; downloads + verifies KayKit assets; layout descriptors /bootstrap, /bootstrap/upstream-layout
gameboard/ Plan builders, terrain/connectivity placement construction, occupancy-backed navigation, spawn groups, patrol routes /gameboard, /navigation, /occupancy
pieces/ Custom piece declarations + cross-kit compatibility /pieces
rules/ Plan + scenario validation, layout fill rules /rules, /validation, /rule-types
scenario/ Recipe → blueprint → scenario compilation, catalog queries, guide-page data, public treatment construction /scenario, /recipe, /blueprint, /catalog, /registry
actors/ Actor traits, queries, registration, navigation profiles, targets /actors
movement/ Movement agents + step execution /movement
patrol/ Patrol routes + assignment + scripted patrol simulation /patrol
quests/ Quest entities, objectives, progress, completion /quests
commands/ Interaction handler presets, command planning /commands
selectors/ Internal selector helpers (@internal-tagged) /selectors
koota/ createWorld + per-tile/per-actor spawn helpers /koota
runtime/ Runtime facade for snapshots + asset-root resolution /runtime
systems/ Command dispatch, per-tick movement/patrol/quest orchestration, rules /systems, /world-rules
simulation/ Scripted scenario simulation engine /simulation
interop/ Neutral ECS snapshot, external asset compatibility, release-readiness coverage /interop, /compatibility, /coverage
react/ React bindings: provider, hooks, selectors /react
three/ three.js bindings: loaders, scene helpers, animation /three
cli/ The declarative-hex-worlds Node binary /cli
errors/ 7-class taxonomy: GameboardError + 6 subclasses /errors

interop/ has two deliberately separate jobs:

  • Runtime interop lives in interop.ts and compatibility.ts. These modules normalize live ECS state into neutral snapshots and adapt external assets so games can inspect, serialize, and bridge runtime data.
  • Release-readiness interop lives in coverage.ts. It aggregates guide-page coverage, manifest compatibility, required screenshot artifacts, local reference-pack status, and package/CI gate results into the /coverage maintainer-facing report.

Keeping release coverage beside runtime interop is intentional: the release ledger exists to prove that the public interop promises are backed by docs, assets, visuals, and package checks. Runtime consumers should reach for /interop or /compatibility; maintainers and CI use /coverage to audit release evidence.

The runtime is built on koota. koota’s discipline:

  1. Traits (traits/) declare the data — pure data, no behavior.
  2. Systems (systems/) read traits, decide what to do, mutate traits. Pure functions over world.
  3. Actions (the *Actions exports in each domain) are the bundled mutations consumers call.
  4. Selectors / Queries read traits without mutation.

The library follows that discipline strictly. src/traits/ exports every trait declaration in one barrel so cross-package trait identity stays stable (PRD invariant §6: splitting: true + trait identity test E4 pin this).

tsup builds with splitting: true so each subpath in package.json#exports becomes its own chunk. Trait identity survives across chunks (E4 tests it). sideEffects: false is on so consumers tree-shake aggressively.

The pipeline runs tsc --noEmit for typechecking, then tsup for both JS + DTS bundling. The two-pass approach (instead of tsup --dts only) keeps cycle-detection separate from bundling.

Per PRD §Phase RB, the library is bootstrap-not-bundle:

  • The published tarball ships only assets/free/manifest.json (the metadata).
  • The actual GLTF tree is fetched at install time by pnpm exec declarative-hex-worlds bootstrap.
  • The bootstrap-target lives under <consumer-out>/addons/kaykit_medieval_hexagon_pack/Assets/gltf/.
  • A .bootstrap.json integrity sidecar records per-file SHA256 + library version + source URL.

See the asset bootstrap guide for usage and the bootstrap layout reference for the upstream tree spec.

docs/PRD/1.0.md in the repo holds the rationale for every architectural choice. .agent-state/directive.md holds the work queue. Both are tracked in git; if you see a layout decision that’s confusing, check the PRD for the why.