# OpenSpec Change: enterprise-gaming-weather - **Change ID**: enterprise-gaming-weather - **Package**: `@suaveplan/weather` - **Category**: gaming/world - **Target path**: `gaming/world/weather` - **Status**: PROPOSED - **Date**: 2026-04-24 - **Reference spec**: `/mnt/s/web/suaveplan/enterprise/weather.md` --- ## Summary Deliver `@suaveplan/weather` on the genesis branch from scratch at enterprise quality. The package provides dynamic weather simulation with smooth transitions, regional zones, and gameplay effects. A `WeatherSystem` holds the current `WeatherState` (temperature, precipitation, wind, visibility, lightning) and advances it by transitioning toward a `WeatherPreset`; `WeatherZone` maps world regions to preset overrides; a `WeatherTransition` interpolates between two presets over a configurable duration. Genesis adds Zod preset schemas, structured errors, OTel weather-state gauge, injectable clock and RNG, and 100% coverage. --- ## Current-state audit (genesis) Package does not exist on genesis yet — fresh build. --- ## Available workspace deps on genesis | Package | Role | |---|---| | `@suaveplan/error` (`workspace:^`) | `WeatherError`; `WEATHER.*` codes | | `@suaveplan/types` (`workspace:^`) | `Clock`, `Rng` | | `@suaveplan/events` (`workspace:^`) | `weather:changed`, `weather:lightning`, `weather:zone_entered` events | | `@suaveplan/day-night` (`workspace:^`) | `DayNightClock` for time-based weather probability | | `zod` (`catalog:forms`) | `WeatherPreset`, `WeatherZone` schemas | | `@opentelemetry/api` (`catalog:observability`) | Weather-state change counter | --- ## Public API surface ```ts interface WeatherPreset { name: string temperature: number // celsius precipitation: number // 0–1 windSpeed: number // m/s windDirection: number // degrees visibility: number // 0–1 (1=clear, 0=zero visibility) fogDensity: number // 0–1 cloudCover: number // 0–1 lightningProbability?: number // per-minute probability 0–1 } interface WeatherState extends WeatherPreset { readonly isTransitioning: boolean readonly transitionProgress: number // 0–1 } function createWeatherSystem(options: WeatherSystemOptions): WeatherSystem interface WeatherSystemOptions { initialPreset: WeatherPreset transitionDuration?: number // seconds, default 60 clock?: Clock rng?: Rng } interface WeatherSystem { transitionTo(preset: WeatherPreset, duration?: number): void tick(dt: number): void readonly state: WeatherState readonly currentPreset: WeatherPreset } // Weather zones function createWeatherZone(options: WeatherZoneOptions): WeatherZone interface WeatherZoneOptions { id: string center: Vec3 radius: number preset: WeatherPreset blendRadius?: number // blend distance at zone edge } interface WeatherZone { readonly id: string getBlendFactor(position: Vec3): number // 0 = outside, 1 = fully inside } function blendWeather(base: WeatherPreset, override: WeatherPreset, factor: number): WeatherPreset ``` ### Error codes ```ts type WeatherErrorCode = | 'WEATHER_INVALID_PRESET' | 'WEATHER_ZONE_NOT_FOUND' | 'WEATHER_TRANSITION_INVALID' ``` --- ## File layout ```text gaming/world/weather/ package.json tsconfig.json vitest.config.ts src/ index.ts error-codes.ts constants/index.ts types/common.ts types/common.md core/ weather-system/ weather-system.ts weather-system.md weather-system.types.ts weather-system.test.ts index.ts weather-zone/ weather-zone.ts weather-zone.md weather-zone.types.ts weather-zone.test.ts index.ts presets/ index.ts # CLEAR, RAIN, STORM, SNOW, FOG built-in presets utils/ blend.ts blend.md blend.test.ts tests/ integration.test.ts ``` --- ## Impact **Downstream**: `@suaveplan/r3f-weather` renders rain particles, fog, lightning effects driven by `WeatherState`. Prerequisites: `@suaveplan/error`, `@suaveplan/types`, `@suaveplan/events`, `@suaveplan/day-night`.