Rendered from docs/FUNCTIONAL-SPECIFICATION.md and docs/work-packages/briefs/

OpenZT2 Functional Specification

OpenZT2 is a clean-room, native 64-bit reconstruction of Zoo Tycoon 2 built with Bevy, a data-oriented Rust game engine. Its completion target is a faithful, runnable game with the architecture and performance expected from a new implementation. A readable recovered C++ implementation serves as a behavioral oracle: together with shipped assets, live observation, and binary investigation, it establishes what the original game did. That evidence does not prescribe Rust types, ownership, update loops, or runtime structure.

The finished game must support the complete source-driven shell, map and game mode selection, terrain and camera semantics, construction, living animals, guests, staff, facilities, economy, persistence, progression, scenarios, and expansion systems. It also adds controller support, removes the original 32-bit mod ceiling, and supplies redo alongside undo. A package is complete only when its behavior is integrated into the playable game. Green status is reserved for packages that meet their functional, allocation, mapping, and GPU acceptance contracts in the assembled application.

Native content boundary

Original Z2F game archives, Extensible Markup Language (XML) files, media, downloadable content (DLC), and mods are inputs to a separate preflight application. Preflight resolves enabled-pack precedence, inheritance, aliases, dependencies, original formats, derived data, and target-machine choices before launching the game. It writes one resolved binary profile in the exact layouts consumed by the engine: flat processor tables, graphics-upload-ready payloads, bounded streaming blocks, stable identifiers, and direct lookup indexes. Later-pack override semantics therefore leave no work for gameplay.

Native records use versioned rkyv archives and small fixed representations such as AssetId, TableRange, StringRange, and AssetRange. The game maps the resolved profile once, validates each typed family once, and retains the mapping through ordinary Bevy asset handles. Archived records are read in place. Meshes, textures, shaders, materials, audio, animation, UI documents, world definitions, terrain, navigation, and scenarios arrive in the form their CPU systems or GPU upload paths require. Runtime source parsing, decompression into an intermediate graph, mod merging, asset-instance hardcoding, and a second decoded asset cache are outside the engine boundary.

PreloadMode::Off leaves paging demand-driven. Core faults the bootstrap, menu, zoo, and resident groups within measured free-memory headroom. AllEnabled extends that policy to every enabled on-demand and streaming group. It never reaches an unselected pack because preflight has already produced the single final profile.

Bevy ownership model

Bevy's entity-component-system (ECS) world owns the live game. Each animal, guest, member of staff, facility, habitat, job, objective, vehicle, photo, effect, and other world object is an entity with small components for its current facts. Resources are limited to genuinely global policy, clocks, immutable indexes, reusable scratch, and bounded coordination. Systems query the narrow component sets required for one job and communicate through typed messages only when an operation is genuinely asynchronous.

There is one ECS world and one lifecycle relation for loaded-zoo entities. There are no compatibility hosts, domain managers, shadow runtimes, generic property bags, copied session worlds, string-command dispatchers, or Rust objects that preserve the C++ assembly shape. Package boundaries allocate parallel authorship. Integration may merge, move, inline, rename, or delete package code until the result reads like one coherent Bevy game written by one team.

Simulation uses a fixed authoritative zoo clock and deterministic domain-local random streams. Saved ages, deadlines, durations, cooldowns, calendars, and random-number-generator state use integer ticks or explicit fixed-point units compiled by preflight. Floating-point time remains appropriate for input and presentation that is deliberately independent of simulation time.

Performance contract

Performance is a product requirement. Mapped asset lookup, archived traversal, and cloning a Bevy handle perform zero heap allocations and copy no underlying asset data. Settled gameplay systems target zero allocations. Collections, route buffers, spatial indexes, render extraction, dependency-owned particle capacity, decoder surfaces, and GPU staging reserve bounded capacity before the settled measurement window. Game-content filesystem reads stop after the mapping is installed; save/profile I/O remains an explicit separate boundary.

The regression suite measures three independent categories:

A package cannot become green because it compiles in isolation. Green requires its observable behavior, persistence, cleanup, native-data traversal, steady allocation limit, mapped-page behavior, and GPU-resource accounting to pass in the integrated game. When all package cards are green, the expected result is a runnable complete game whose ordinary scene transitions and native save/load paths avoid repeated conversion, copying, and data reprocessing.

Evidence and implementation method

Behavior is recovered just in time from the shipped data, readable C++ oracle, analysis records, and live original. Commit d5657e5a may supply behavioral leads and previously discovered source vocabulary. Its architecture was deleted by 19dd222f and must not return. Unknown source semantics block the relevant preflight schema or gameplay acceptance until the evidence identifies a typed owner and representation.

Implementation proceeds through the package contracts below. Asset packages own one consumer-shaped native family, its preflight producer, its mapped Bevy asset, and focused contract tests. Gameplay packages own cohesive components, resources, messages, and systems under one Bevy plugin directory. Root integration owns manifests, module declarations, plugin groups, schedules, shared vocabulary, dependency changes, and destructive cleanup across package boundaries.

Library ownership

OpenZT2 buys mature commodity infrastructure and writes recovered zoo-game behavior. docs/work-packages/LIBRARY-DECISIONS.md is normative for every package and assigns physics, particles, codecs, file dialogs, mesh/shader processing, and Bevy-native rendering/UI/input/audio facilities to their off-the-shelf owners. Package workers implement only native conversion, integration, policy, and original-game semantics around those owners. They must not build a parallel solver, renderer, mixer, codec, layout engine, particle engine, optimizer, device backend, or copied library world.

Library adoption does not relax the mapping or allocation contract. The game still loads only the resolved native profile; one-time body/GPU/decoder setup is bounded and measured; capacity-stable operation remains subject to Tracy and hard allocation regressions. A failing mandatory dependency is configured, patched, or narrowly forked before bespoke replacement is considered. The audited pre-library design is preserved at commit 68a4c481 for that explicit fallback only.

Package status

The published grid is generated from the 56 normative package briefs in docs/work-packages/briefs/. A brief may declare one of these states with a Status: <state> line after its title. Absence means brief.

The package briefs are the detailed normative sections of this specification. They retain exact Rust contracts, ECS queries, binary layouts, evidence gates, behavior, and acceptance criteria so each package can be implemented in parallel without inventing an architectural boundary.

Evidence coverage gate

Package-boundary exhaustiveness is checked by tools/audit-work-package-coverage.py. Its generated docs/work-packages/EVIDENCE-CROSSWALK.tsv assigns every current binary function identity, recovered type/vtable identity, C++ oracle file, and official core/DLC archive entry to one implementation package or an explicit external, preflight-only, Bevy-replaced, non-shipping, or binary-data disposition. EVIDENCE-COVERAGE.md summarizes the result.

The audit must be regenerated whenever accepted evidence or package boundaries change, and publication rejects a stale or incomplete crosswalk. Passing means there is no known unowned domain. It is not a claim that unresolved formulas, edge cases, presentation details, or behaviors absent from current evidence have already been recovered; each owning brief retains those acceptance gates.

0 / 56 verifiedEvery card must be green for complete-game acceptance.
A01LauncherBrief readyA02Compiled UI DocumentBrief readyA03Species CatalogueBrief readyA04GPU-ready modelBrief readyA05GPU-Native TextureBrief readyA06Skeletal AnimationBrief readyA07MaterialBrief readyA08Shader modules and variantsBrief readyA09LocalizationBrief readyA10Compiled Behavior ProgramBrief readyA11Unified audioBrief readyA12System Font IntegrationBrief readyA13Video streamBrief readyA14World DefinitionsBrief readyA15Scene PrefabBrief readyA16Streamable TerrainBrief readyA17NavigationBrief readyA18GPU-ready effectsBrief readyA19Worlds, Scenarios and Imported StartsBrief readyG01Source-driven game shellBrief readyG02Native game UIBrief readyG03Device-independent inputBrief readyG04World instantiationBrief readyG05Editable terrain and waterBrief readyG06Authentic game camerasBrief readyG07Construction interaction and historyBrief readyG08Fences, paths and portalsBrief readyG09Habitat containment and membershipBrief readyG10Scenery and facility placementBrief readyG11Animal lifecycle and familyBrief readyG12Animal needs, welfare and suitabilityBrief readyG13Data-authored animal task selectionBrief readyG14Navigation, steering and dockingBrief readyG15Food, drink and refill transactionsBrief readyG16Animal HealthBrief readyG17GuestsBrief readyG18EconomyBrief readyG19DonationsBrief readyG20StaffBrief readyG21Information and CataloguesBrief readyG22PersistenceBrief readyG23ProgressionBrief readyG24Scenarios, Campaigns, Tutorials, and ChallengesBrief readyG25Aquatic SystemsBrief readyG26Animal ShowsBrief readyG27Transport and ToursBrief readyG28PhotosBrief readyG29Extinct Animal RecoveryBrief readyG30Gestures and PuzzlesBrief readyG31Immersive and Special ModesBrief readyG32Simulation time and determinismBrief readyG33World environment and ambient lifeBrief readyG34Display and graphics settingsBrief readyG35Native content mapping and residencyBrief readyG36Maintenance and SanitationBrief readyG37Interactive physics and attachmentsBrief ready

Detailed package contracts

A01 — Launcher

1. Identity and outcome

The separate openzt2-launcher executable lets a player choose a Zoo Tycoon 2 installation, scan its archives, select any discovered core/DLC/mod layers, convert the selected set into one resolved native profile, remove an installed profile, choose Off, Core, or All enabled fast preload, and launch OpenZT2. Core/DLC/mod presentation and progress are ordinary launcher UI; no part enters the game process. Integration wave 6; it consumes the pack assembler and the statically linked A02–A19 source frontends.

2. Exclusive ownership

The launcher package owns launcher/src/ in full. Everything else is read-only. Cargo members, dependencies, icons, converter module wiring and game binary wiring are root handoffs. In particular, do not edit converter/src/pack.rs or any family.

rfd 0.17 is mandatory for the native installation-directory picker and owns all platform dialog integration. The worker must not implement a filesystem browser, portal/GTK/Win32/Cocoa backend, or retain dialog state after selection. Root adds the minimal platform features.

3. Frozen public contract

| Symbol | Kind | Exact shape or signature | Owner and invariant | |---|---|---|---| | PreflightAppPlugin | plugin | unit struct implementing Plugin | Registers only launcher state/UI/jobs. | | PreflightPhase | state enum | ChooseLocation, ChoosingLocation, Scanning, Verifying, Ready, Converting, Launching, Failed | One launcher phase; work disables conflicting browse/remove/play actions. | | PackRole | enum | Core, Dlc, Mod | Classification established by scan evidence, never filename colour. | | PreloadMode | shared native enum | openzt2_native::contract::PreloadMode::{Off, Core, AllEnabled} | Sole launcher→engine page-fault policy; AllEnabled never includes unselected packs. | | ArchiveKey | value | pub struct ArchiveKey(pub AssetId) | Stable ID of normalized source-relative archive path. | | SourceArchive | record | { key:ArchiveKey, source_path:PathBuf, display_name:String, role:PackRole, original_bytes:u64, content_hash:[u8;32], verification:ArchiveVerification, required_for_launch:bool } | Scan result; checksumming is an explicit verification phase and only the three evidenced core archives set required_for_launch. | | InstalledProfile | record | { id:AssetId, path:PathBuf, source_fingerprint:[u8;32], optimized_bytes:u64, target:TargetProfile } | id is the verified native pack/profile identity, never a slot integer. | | ConvertedArchive | record | { key:ArchiveKey, source_hash:[u8;32], cache_path:PathBuf, output_hash:[u8;32], optimized_bytes:u64, target:TargetProfile, converter_schema:u32 } | One verified per-source conversion cache; never loaded by engine directly. | | ConversionCacheManifest | record | { version:u32, converted:BTreeMap<ArchiveKey,ConvertedArchive> } | Atomically persisted launcher cache index; a row is complete only after cache header/hash/source/schema/target validation. | | ArchiveRow | component | { key: ArchiveKey } | UI row identity; visual state is derived. | | ArchiveSelection | resource | { selected: BTreeSet<ArchiveKey> } | One selection drives conversion, deletion and launch inclusion; default empty. | | DiscoveredArchives | resource | { rows:Vec<SourceArchive>, cache:ConversionCacheManifest, installed:Option<InstalledProfile> } | Bounded scan plus per-row conversion truth and final resolved output. | | PreflightSettings | resource | { source_root:Option<PathBuf>, output_profile:PathBuf, preload:PreloadMode } | Persisted launcher preferences, not live game state. | | ConversionProgress | resource | { completed_bytes: u64, total_bytes: u64, current: Option<ArchiveKey>, failure: Option<String> } | Progress is byte based and monotonically increases for one job. | | scan_installation | function | fn scan_installation(root: &Path, target: TargetProfile) -> io::Result<Vec<SourceArchive>> | Pure scan; deterministic normalized ordering. | | convert_selection | function | fn convert_selection(source:&[SourceArchive], selected:&BTreeSet<ArchiveKey>, target:TargetProfile, cache_root:&Path, progress:impl FnMut(ArchiveKey,u64,u64))->io::Result<Vec<ConvertedArchive>> | Converts only missing selected rows to atomic cache entries; caller persists manifest then assembles profile. | | remove_converted_archive | function | fn remove_converted_archive(key:ArchiveKey, cache:&mut ConversionCacheManifest, resolved:Option<&InstalledProfile>)->io::Result<()> | Deletes that row cache, atomically persists manifest and invalidates/deletes resolved output. Core is not special. | | assemble_resolved_profile | function | fn assemble_resolved_profile(source:&[SourceArchive], selected:&BTreeSet<ArchiveKey>, cache:&ConversionCacheManifest, destination:&Path)->io::Result<InstalledProfile> | Flattens selected valid conversions in resolved precedence order into sole engine profile. | | launch_game | function | fn launch_game(executable:&Path, profile:&InstalledProfile, preload:PreloadMode)->io::Result<Child> | Passes exact --profile <absolute path> --profile-id <32 lowercase hex> --target-profile <u32> --preload <off|core|all-enabled> arguments; starts only with selected core. |

4. Data layout or ECS ownership

Launcher entities are presentation only. DiscoveredArchives is the bounded scan catalogue; ArchiveSelection is the single selection truth; one task entity owns any active conversion. No source archive bytes or converted asset graphs are retained after conversion. UI systems derive row colour, enabled buttons, original / optimized sizes and per-row progress bars. Convert is enabled only when the unified selection contains at least one missing/stale row and no job is active; a completed valid row cannot be reconverted. Remove is enabled only when the selection contains converted rows and disabled while converting. Play is disabled until all three core rows are selected and every selected row has a valid conversion represented by the current resolved profile. Once true, Play is green regardless of unselected optional rows. When anything is unselected, the informational text is exactly Unselected packs will not be loaded. Only the three core packs are required to launch.; it does not turn a valid core selection into an error.

View contents opens a modal archive viewer over an immutable snapshot of the same unified selection. It reads each chosen row from its exact source archive, uses the selected set only to resolve referenced dependencies, and cannot change selection or operate during scan, verification, conversion, or launch. The viewer shares the tooling-only source-format crate with conversion; it does not install an archive as a Bevy asset source or enter the game dependency graph.

5. Preflight producer contract

Accept installation roots chosen by a native directory picker. Scan Z2F/source archives read-only, recognize core identities by evidenced canonical paths and hashes, classify DLC/mods, and compute hashes lazily during explicit Scan—not app startup. Later selected layers override earlier ones in preflight; only the winning consumer assets enter assemble_pack. Conversion is atomic, cancelable between files, deterministic, and rejects missing core inputs, corrupt archives, unsupported producer output, source/output hash mismatch, target/schema mismatch, duplicate paths, cache-manifest inconsistency and destination verification failure. A completed valid row cannot be reconverted until removed. assemble_resolved_profile alone applies selected layer precedence; engine sees only that final output.

6. Behavioral evidence and disposition

Start at ignored vendor/original-software/ archive names/manifests, old reconstruction/game/src/bin/launcher.rs and converter sources in commit d5657e5a, and current converter/src/pack.rs. Evidence questions: exact three core archive identities; official expansion versus downloaded-content classification; original launcher naming only where player familiarity matters.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

7. Required behavior

Nothing is selected on first scan. Core rows are deselectable and deletable but launch requires all three selected, converted, and present in the verified resolved profile. A valid checksum/profile is shown complete across restart; changed source/schema/target or invalid cache becomes stale. Each row shows original_bytes / optimized_bytes; no single estimate is reused. Selecting a converted row permits deletion, never reconversion. Removing it also invalidates the resolved profile. Collapsible, scrollable Core/DLC/Mods sections expose all rows. Closing/canceling never leaves a profile that passes validation. The deliberate OpenZT2 option Fast preload is passed as PreloadMode; this application does not fault pages itself.

8. Performance contract

No automatic full scan or conversion on open. Directory enumeration is O(n), hashing and conversion O(total selected bytes), bounded to one conversion job. UI frames do no filesystem access and allocate nothing after row hydration unless scan progress changes. Stream source files with reusable buffers; never hold all source archives in RAM. Report converter heap peak and bytes/sec; game heap/mapping/GPU counters are out of process.

9. Acceptance criteria

All source under the owned subtree is complete formatted Rust. The demonstrated flow is: pick location → scan → select three core plus one DLC → convert with bars → play → quit → restart → scan and observe four individually valid converted rows and the same valid resolved profile. Button states and warnings obey the rules above. Non-goals: original UI styling and any runtime source parsing. No stubs, hidden auto-conversion or fake completion.

10. Required handoff

Report files/API, UI/file-dialog dependencies, static source-frontend wiring and root workspace/bin registration, game executable/environment wiring, remaining archive identity evidence, and conversion throughput/peak heap. Root should erase any temporary compatibility adapter. Archive inspection is launcher functionality and must remain source-tooling-only rather than becoming a game runtime loader.

A02 — Compiled UI Document

1. Identity and outcome

This package produces the one native representation of mod-authored UI: flat layout/style/ binding/command tables that G02 projects directly into Bevy UI. XML templates, skins and command names are completely compiled before launch. Integration wave 1; depends on A05 textures, A09 localization and A12 system-font policy.

2. Exclusive ownership

This package owns native/src/families/ui_document.rs, converter/src/families/ui_document.rs, game/src/assets/ui_document.rs, and preflight/tests/ui_document_contract.rs. All other paths are read-only; module, loader and dependency registration are root handoffs.

Bevy 0.19 UI is the mandatory runtime target and owns layout, clipping, scrolling, picking, focus and widget interaction. A02 compiles only the flat data needed to spawn ordinary Bevy Node, image, text and interaction components. It must not define a parallel layout tree, widget engine, focus engine, retained XML document, or generic UI runtime.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
UI_ARCHIVE_VERSION constant u32 = 1 Bumped on incompatible layout/command changes.
UiArchive archived root { version:u32, target:TargetProfile, id:AssetId, role:UiDocumentRole, logical_size:[f32;2], strings:Vec<u8>, nodes:Vec<UiNodeRecord>, children:Vec<u32>, styles:Vec<UiStyleRecord>, bindings:Vec<UiBindingRecord>, commands:Vec<UiCommandRecord>, dependencies:Vec<AssetId> } Tables immutable; nodes preorder/root index 0; ID is role-derived for semantic screens.
UiDocumentRole enum Splash, MainMenu, ProfileSelect, Globe, MapSelect, Loading, Options, SavedGames, InGameHud, Modal, EntityInfo, PurchaseCatalogue, Zoopedia, Scenario, PhotoAlbum, ShowEditor; pub const fn native_asset_path(self)->&'static str Fixed engine discovery vocabulary and semantic native paths; complete corpus is evidence-gated before acceptance and unknown roles reject.
UiNodeFlags bitflags u16 VISIBLE, ENABLED, CLIP_CHILDREN, MODAL, FOCUSABLE, POINTER_BLOCKING Exact live projection defaults.
UiNodeRecord record { id:AssetId, kind:UiNodeKind, parent:u32, children:TableRange, rect:[f32;4], anchors:[f32;4], style:u32, bindings:TableRange, commands:TableRange, flags:UiNodeFlags, z:i16, tab_order:i16 } TableRange indexes typed tables; u32::MAX parent means root.
UiNodeKind enum Root, Container, Image, Text, Button, Toggle, Slider, Scroll, List, Edit Exhaustive fixed projector vocabulary; any other source widget blocks conversion until this versioned enum is evidence-expanded.
UiStyleFlags bitflags u16 NINE_SLICE, PRESERVE_ASPECT, WRAP_TEXT, ELLIPSIS, PRESSED_OFFSET Exact presentation behavior.
UiStyleRecord record { background:AssetId, font:AssetId, text_key:AssetId, color:[f32;4], font_px:f32, padding:[f32;4], border:[f32;4], flags:UiStyleFlags } Missing references are AssetId::default().
UiBindingRecord record { binding:UiBinding } Property and value type are one closed enum; no source key/property dispatcher.
UiBinding enum Visible(BoolBindingSource), Enabled(BoolBindingSource), Text(TextBindingSource), Image(ImageBindingSource), Value(ScalarBindingSource), Minimum(i64), Maximum(i64), Selected(BoolBindingSource) G02 matches exhaustively and writes only the encoded Bevy property type.
BoolBindingSource enum Constant(bool), HasSelectedEntity, SelectedEntitySellable, ProfilePresent, AnimalDiseased, AnimalUnderTreatment, AnimalTranquilized, AnimalEscaped, AnimalRampaging, AnimalDead, StaffAssigned, StaffWorking, FacilityOpen, HabitatBreached, IsTank, ShowScheduled, ShowRunning, TransportCircuitClosed, TransportWaiting, TransportRiding, ScenarioRunning, ScenarioObjectiveSatisfied{objective:AssetId}, DefinitionUnlocked{definition:AssetId}, ResearchActive, AwardEarned{award:AssetId}, CatalogueRowVisible{row:u16}, CatalogueEntryUnlocked{row:u16}, CatalogueEntryAffordable{row:u16} Every variant names one precise changed live fact.
TextBindingSource enum Localized{key:AssetId,format:AssetId}, SelectedEntityName, SelectedEntityDefinitionName, AnimalWelfareBand, AnimalHealthStatus, AnimalDiseaseName, AnimalTreatmentName, GuestPhase, StaffRoleName, StaffAssignmentName, StaffCurrentJobName, FacilityServiceName, HabitatPrimaryBiomeName, HabitatBiomeName{row:u16}, ShowStateName, ShowCurrentTrickName, ShowSlotTrickName{slot:u16}, TransportCircuitName, TransportStationName, TransportVehicleName, TransportTripState, ZooCash{format:AssetId}, ZooFame{format:AssetId}, ZooRating{format:AssetId}, ZooGuestCount{format:AssetId}, ProfileName, ScenarioObjectiveText{objective:AssetId}, ScenarioObjectiveStatus{objective:AssetId}, ScenarioObjectiveProgress{objective:AssetId,format:AssetId}, ScenarioTimeRemaining{format:AssetId}, ResearchName, ResearchProgress{format:AssetId}, LatestAwardName, CatalogueEntryName{row:u16}, CatalogueEntryPrice{row:u16,format:AssetId}, ZoopediaTitle, ZoopediaBody AssetId fields identify localization/format/objective data only; row/slot fields index bounded preallocated UI rows. G21/domain presenters supply the named fact; no selected-field lookup bag.
ImageBindingSource enum Constant{texture:AssetId}, SelectedEntityIcon, ProfilePortrait, ScenarioObjectiveIcon{objective:AssetId}, CatalogueEntryIcon{row:u16}, ZoopediaImage Resolves through A05's ordinary Bevy image bridge.
ScalarBindingSource enum Constant(i64), AnimalHungerQ16Permille, AnimalThirstQ16Permille, AnimalRestQ16Permille, AnimalPrivacyQ16Permille, AnimalSocialQ16Permille, AnimalExerciseQ16Permille, AnimalStimulationQ16Permille, AnimalEnvironmentQ16Permille, AnimalKeeperCareQ16Permille, AnimalHealthNeedQ16Permille, AnimalWelfarePermille, AnimalVitalityPermille, GuestHungerPermille, GuestThirstPermille, GuestEnergyPermille, GuestRestroomPermille, GuestComfortPermille, GuestSatisfactionPermille, GuestEducationPermille, GuestVisitTicks, StaffWageCentsPerDay, StaffJobUrgency, StaffJobRemainingTicks, FacilityPriceCents, FacilityCapacityUsed, FacilityCapacityTotal, FacilityConditionPermille, FacilityWasteUnits, FacilityWasteCapacity, FacilityInventoryCount, FacilityInventoryCapacity, LitterAmountUnits, HabitatWasteAmountUnits, ZooCleanlinessPermille, HabitatAreaMilliSquareMetres, HabitatLandMilliSquareMetres, HabitatWaterMilliSquareMetres, HabitatBiomeAreaMilliSquareMetres{row:u16}, TankWaterQualityPermille, TankUsedLitres, TankRequiredLitres, ShowScheduleCount, ShowScheduleCapacity, ShowRemainingTicks, ShowSlotDurationTicks{slot:u16}, TransportStationQueued, TransportStationOccupied, TransportStationCapacity, TransportVehicleOccupied, TransportVehicleSeats, TransportTripScoreMilli, ZooCashCents, ZooAdmissionCents, ZooFameHalfStars, ZooRatingPermille, ZooGuestCount, ScenarioObjectiveCurrent{objective:AssetId}, ScenarioObjectiveTarget{objective:AssetId}, ScenarioTimeRemainingTicks, ResearchElapsedTicks, ResearchRequiredTicks, AwardEarnedTick{award:AssetId}, CatalogueEntryPriceCents{row:u16}, CataloguePage Exact integer unit is named by variant; row/slot fields index bounded reusable views. G21 performs the one explicit live-fact quantization where ECS storage is float, then G02 writes the value without reflection or unit inference.
UiCommandRecord record { trigger:UiTrigger, command:UiCommand } Commands are validated preflight; payload belongs to its exact enum variant.
UiTrigger enum Press, Change, Submit, Cancel, Focus, Blur Fixed event vocabulary.
UiCommand enum StartFreeform, OpenChallenge, OpenCampaign, OpenOptions, OpenDownloads, OpenSavedGames, Quit, SetVisible{node:AssetId,visible:bool}, SetValue{node:AssetId,value:i32}, Focus{node:AssetId}, Navigate{role:UiDocumentRole}, SelectEntity, SetTool{tool:AssetId}, Buy{definition:AssetId}, SellSelected, Adopt{species:AssetId}, Save, Load Native fixed vocabulary; every AssetId is a data target. G02 translates shell variants to Bevy GameAction; unsupported semantics reject conversion.
MappedUiArchive owning view tuple wrapper over MappedAsset; new(MappedAsset)->io::Result<Self>, archived(&self)->&ArchivedUiArchive Validates once and keeps mapping alive.
access_ui / access_ui_validated functions fn access_ui(&[u8])->io::Result<&ArchivedUiArchive>; unsafe fn access_ui_validated(&[u8])->&ArchivedUiArchive Validate version, target and every range/reference.
AuthoredUiDocument producer input { virtual_path:String, xml:Vec<u8> } Preflight-only source bytes.
compile_ui_document function fn compile_ui_document(input:&AuthoredUiDocument, target:TargetProfile)->io::Result<Vec<u8>> Pure deterministic producer.
UiDocumentAsset Bevy asset { archive:MappedUiArchive } deriving Asset, TypePath, Clone Never materializes node copies.
UiDocumentLoader loader { profile:Arc<MappedPack> }; new(Arc<MappedPack>)->Self; implements AssetLoader<Asset=UiDocumentAsset> for ozui Uses LoadContext::path() to acquire mapped range, not reader copying.
UiDocumentAssetPlugin plugin unit struct Registers asset and loader only.

4. Data layout or ECS ownership

Little-endian rkyv root at alignment required by rkyv; frozen TableRange indexes typed records while AssetRange is reserved for payload bytes. Strings are UTF-8 in one blob and addressed by StringRange in producer-private extensions; all tables use u32 indexes and stay below u32::MAX. IDs and dependency tables are sorted. Rendering traverses archived nodes directly; textures/fonts are Bevy handles by AssetId. Only texture/font payloads upload to GPU.

5. Preflight producer contract

Accept UI XML/BFXML documents, referenced templates, replacements/skins, localization keys and image/font paths from selected resolved layers. Normalize virtual paths to lowercase /-separated UTF-8 before AssetId::from_key. Resolve includes, template expansion, skin replacements and later-layer overrides; flatten hierarchy and intern strings; validate unique IDs, acyclic templates, finite geometry, valid ranges, known tags/bindings/commands and dependency closure. Semantic documents use AssetId::from_key("ui/role/<role>") so G01/G02 discover handles without paths; later layers replace a role before output. Reject duplicate winners or a missing required splash/main/profile/ globe/map/loading/HUD role, and unknown behavior-bearing commands with source path/node. Deterministic order is document preorder then stable source order. The worker must inventory the full shipped binding corpus before freezing code: every animal, guest, staff, facility, habitat/tank, show, transport, scenario and progression binding must map to one named variant above (or a reviewed addition with an exact unit). Unknown fields fail conversion; there is no selected-field ID, generic property key or fallback reflection path.

6. Behavioral evidence and disposition

Search shipped ui/**/*.xml, *.layout, skin/replacement documents and command attributes; cpp-reconstruction UI/BFXML types via RTTI TSV; the specification; and commit d5657e5a domain/.../ui_contract.rs, presentation/ui/{commands,layout,projection,widgets}.rs. Live-check focus, modal and coordinate scaling. Questions: complete document-role and tag/widget set, command argument semantics, anchor/aspect behavior, animation representation and list templates. Unknown commands must remain named evidence failures.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

7. Required behavior

Templates and overrides yield one final tree. Stable node IDs survive mod replacement; duplicate IDs in a resolved document fail. Layout preserves authored pixel coordinates against logical_size, anchors and z/tab order. Bindings exhaustively match typed live facts; commands emit fixed Bevy actions/requests. No runtime XPath, XML DOM, string dispatch or asset-instance hardcoding. Mod UI may use only the frozen node/binding/command vocabulary.

8. Performance contract

Load validates O(nodes+edges+records) once; lookup/traversal/handle clone are zero-allocation. Projection may allocate entities only on document activation; steady binding uses changed facts and indexed node entities. No per-frame strings/hash growth. Contract test counts zero allocations for archived preorder and command iteration; page-fault and mapped-byte tests distinguish mapping; texture/font GPU bytes belong to their families.

9. Acceptance criteria

The four owned files export every symbol above, convert a nested fixture with template/skin/binding/command references across every G21 presenter family, reject malformed cycles/ranges and unknown bindings/commands, and prove archived traversal returns source order without copying. Acceptance includes a documented full-corpus binding inventory with no unclassified source field. No G02 projection, XML dependency in native/Bevy, stubs or generic property bags.

10. Required handoff

Report XML/parser dependencies, module/asset/loader/plugin registration, the exact G02 projector/command mapping, references to A05/A09/A12, unresolved UI vocabulary, mapped allocation checks and any temporary field that integration should eliminate.

A03 — Species Catalogue

1. Identity and outcome

This package produces the complete immutable species, sex/age variant, compatibility and authored-biological catalogue consumed by animal gameplay without source XML or per-animal copies. It replaces the bootstrap species schema. Integration wave 3; depends on A09 localization and A14 world definitions.

2. Exclusive ownership

This package owns native/src/families/species.rs, converter/src/families/species.rs, game/src/assets/species.rs, preflight/tests/species_contract.rs. Existing native/src/species.rs and converter/src/species.rs are read-only migration inputs; root moves/deletes them and registers modules/loaders.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
SPECIES_ARCHIVE_VERSION constant u32 = 1 Native layout version.
SpeciesArchive root { version:u32, target:TargetProfile, strings:Vec<u8>, species:Vec<SpeciesRecord>, variants:Vec<SpeciesVariantRecord>, needs:Vec<SpeciesNeedRecord>, preferences:Vec<NeedPreferenceRecord>, compatibilities:Vec<SpeciesCompatibilityRecord> } species sorted by id; ranges index typed tables.
SpeciesRecord record { id:AssetId, name_key:AssetId, taxonomy_key:AssetId, world_definition:AssetId, variants:TableRange, needs:TableRange, adult_mass_kg:[f32;2], stage_start_ticks:[u64;4], lifespan_ticks:[u64;2], gestation_ticks:u64, litter:[u16;2], move_speed_mps:f32, swim_speed_mps:f32, appetite_per_tick_q16:i32, waste_definition:AssetId, waste_interval_ticks:u32, waste_units:u16, flags:SpeciesFlags } G32 ticks; stages monotonic/inside lifespan; appetite signed 16.16 permille/tick. Waste fields are all zero/absent together or an evidenced positive G36 production rule.
SpeciesFlags bit flags u32: MALE, FEMALE, SWIMS, FLIES, PREDATOR, PREY, SOCIAL, SOLITARY, ADOPTABLE, EXTINCT Data facts, not live state.
SpeciesVariantFlags bitflags u8 DEFAULT, RARE, ALBINO, STERILE Exact variant facts; evidence expands enum before acceptance if corpus adds one.
SpeciesVariantRecord record { id:AssetId, species:AssetId, sex:Sex, life_stage:LifeStage, model:AssetId, material_variant:AssetId, scale:[f32;2], weight_kg:[f32;2], probability:u16, flags:SpeciesVariantFlags } Probability is 0..=65535 fixed weight.
Sex enum Female, Male, Any Any only on authored compatibility/variant fallback.
LifeStage enum Juvenile, Young, Adult, Elder Ordered lifecycle vocabulary.
NeedKind enum Hunger, Thirst, Rest, Privacy, Social, Exercise, Stimulation, Environment, KeeperCare, Health Evidence-derived animal vocabulary. Scan the complete selected corpus; an additional behavior-bearing need is a schema gate, never Custom or a string.
SpeciesNeedRecord record { kind:NeedKind, initial_permille:u16, decay_per_tick_q16:i32, happiness_weight:i16, good_threshold:u16, critical_threshold:u16, preferences:TableRange } Values/thresholds 0..=1000; decay signed 16.16 permille/tick.
NeedPreferenceRecord record { kind:NeedKind, target:AssetId, weight:i16, minimum_quality:u16 } Target is an A14 definition/affordance owner; weight -1000..=1000.
SpeciesCompatibilityRecord record { species:AssetId, other:AssetId, relation:CompatibilityKind, score:i16 } Sorted (species,other,relation); score -1000..=1000.
CompatibilityKind enum Habitat, Social, Predator, Prey, Breeding Static authored relation.
MappedSpeciesArchive / access_species / access_species_validated owning view/functions tuple wrapper; new(MappedAsset)->io::Result<Self>; archived(&self)->&ArchivedSpeciesArchive; find(&self,AssetId)->Option<&ArchivedSpeciesRecord>; fn access_species(&[u8])->io::Result<&ArchivedSpeciesArchive>; unsafe fn access_species_validated(&[u8])->&ArchivedSpeciesArchive Validate once; mapping owner keeps references alive.
SpeciesSource producer input { virtual_path:String, bytes:Vec<u8> } Resolved XML/data source.
compile_species function fn compile_species(inputs:&[SpeciesSource], timing:&ArchivedSimulationTimingDefinition, target:TargetProfile)->io::Result<Vec<u8>> Pure deterministic catalogue producer; all authored time/rates become G32 ticks/per-tick Q16 here.
SpeciesCatalogAsset Bevy asset { archive:MappedSpeciesArchive } deriving Asset, TypePath, Clone Mapped owner only.
SpeciesAssetLoader loader { profile:Arc<MappedPack> }; new(Arc<MappedPack>)->Self; AssetLoader<Asset=SpeciesCatalogAsset> for ozspecies using LoadContext::path() + get_owned No decoded graph or reader copy.
SpeciesAssetPlugin plugin unit struct Registers asset/loader only.

4. Data layout or ECS ownership

rkyv, little-endian target, 16-byte stable IDs and frozen TableRange typed-table ranges. Archived vectors are traversed directly. Repeated display/source strings are interned; game-facing text uses A09 key IDs. Model/material IDs are dependencies, never embedded handles. Per-animal age/sex/needs remain ECS components in G11/G12. G11 uses this exact Sex/LifeStage; G12 uses this exact NeedKind and need ranges rather than a duplicate enum or per-animal map.

5. Preflight producer contract

Accept resolved animal entity XML, variant/model bindings, BFAI state vars/need adjusts/preferences, taxonomy, biome/compatibility and adoption metadata from selected archives. Normalize canonical entity keys before AssetId::from_key. Apply source inheritance/templates and pack precedence before records. Convert authored days/rates against A14 SimulationTimingDefinition into integer ticks and signed Q16 per-tick rates, resolve all references, deduplicate inherited lists and sort. Reject missing IDs, duplicate variants, nonmonotonic stage/lifespan ticks, impossible ranges, NaN/Inf, unknown sex/life stage/need and dangling dependencies with source diagnostics. Any need outside the evidenced fixed vocabulary blocks conversion and is reported for contract revision.

6. Behavioral evidence and disposition

Search shipped entities/units/animals, lang, biome and taxonomy XML; oracle animal/animal-manager/entity-type classes and RTTI; analysis domain rows; commit d5657e5a simulation/animal.rs, startup/animals.rs, catalog sources and old species code. Questions: exact game-day age thresholds, variant probability, sex availability, breeding compatibility, exact complete need vocabulary, tick conversion, decay/update residual rules and thresholds, appetite/speed unit conversions and which fields are inherited. Resolve before encoding; do not preserve old bags.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

7. Required behavior

Every selectable species has localization, a world definition and at least one valid adult variant. Variant choice filters by sex/stage then uses deterministic weighted selection supplied by gameplay. Integer tick ranges drive lifecycle but do not mutate. Each fixed tick, G12 adds the signed Q16 need rate to its per-need signed Q16 residual, applies the arithmetic whole permille toward zero, retains the fractional remainder and clamps 0..=1000; no runtime day conversion or drift-prone float accumulation occurs. Missing optional model fallbacks are explicit zero IDs; required ones fail. Mod-defined species work through identical records. Authentic values must be recovered for units and lifecycle fields before completion.

8. Performance contract

Validation O(s+v+c), find O(log s), variant/compatibility traversal O(range), all zero-allocation. No runtime strings or per-entity catalogue clones. Test allocator counts find and full traversal; mapping counters cover archive pages; no GPU ownership beyond referenced A04/A07 assets.

9. Acceptance criteria

Four owned files, full API above, fixtures covering two species/sex/stages/ needs/preferences/compatibility and rejection tests for corrupt ranges/NaN/ dangling IDs/unknown needs. Archived lookup returns mapped records without allocation. Non-goals: animal live state, behavior scoring and adoption UI. No bootstrap-only five-field schema remains in this package.

10. Required handoff

Report migration of old species modules, parser dependencies, root registrations, A04/A07/A09/A14 dependency IDs, G11/G12 field consumers, exact unresolved data semantics and allocator/mapping validation.

A04 — GPU-ready model

1. Identity and outcome

Preflight converts original NIF/BFB meshes into target-specific, meshopt- optimized vertex/index/meshlet payloads. Bevy/wgpu upload, extract, batch, cull, skin and draw them through the normal renderer. The game never reconstructs a scene graph or runs mesh optimization.

2. Exclusive ownership and mandatory dependencies

The package worker owns native/src/families/model.rs, converter/src/families/model.rs, game/src/assets/model.rs, and preflight/tests/model_contract.rs only. Root adds meshopt = 0.6 to preflight with only required features.

Meshopt exclusively owns vertex remapping, cache/fetch/overdraw optimization, quantization, LOD simplification and meshlet construction. Bevy/wgpu exclusively own render assets, GPU buffers, extraction, culling, batching and draw commands. No OpenZT2 mesh optimizer, renderer, scene graph, render world, or generic model runtime is permitted.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
MODEL_ARCHIVE_VERSION constant u32 = 1 Native layout version.
ModelArchive root { version:u32, target:TargetProfile, id:AssetId, bounds:ModelBounds, meshes:Vec<MeshRecord>, lods:Vec<LodRecord>, submeshes:Vec<SubmeshRecord>, skeleton:AssetId, dependencies:Vec<AssetId>, payload:Vec<u8> } Metadata sorted; payload ranges aligned for direct upload.
MeshRecord record { vertex_layout:VertexLayout, vertex_count:u32, index_count:u32, vertices:AssetRange, indices:AssetRange, submeshes:TableRange, lods:TableRange, meshlets:AssetRange } Final target layout and index width.
VertexLayout enum fixed supported packed static/skinned layouts Closed pipeline-key vocabulary.
SubmeshRecord record { first_index:u32, index_count:u32, material:AssetId, bounds:ModelBounds } Contiguous validated draw range.
LodRecord record { first_index:u32, index_count:u32, screen_error:f32 } Deterministic ordered LOD threshold.
MappedModelArchive owning view new(MappedAsset)->io::Result<Self> plus zero-copy record/payload access Validated once, mapping-owned.
compile_model producer fn compile_model(input:&ModelSource,target:TargetProfile)->io::Result<Vec<u8>> Pure source conversion using meshopt.
ModelAsset Bevy asset { archive:MappedModelArchive } CPU mapping owner.
ModelAssetPlugin plugin unit struct Registers loader and normal Bevy render-asset preparation only.

The Bevy preparation path reads mapped ranges and uploads directly to wgpu buffers. Private prepared-render values may retain GPU handles/accounting, not copied CPU vertices or an OpenZT2 renderer abstraction.

4. Data layout and ownership

All records are rkyv-compatible fixed data. Vertex/index/meshlet blocks are target-profile aligned, little-endian and directly uploadable. CPU collision, navigation and placement shapes belong to A15–A17 rather than being derived from render meshes in the game.

5. Preflight producer contract

Resolve NIF/BFB hierarchy, geometry, materials, skeletons, transforms and LODs. Bake transforms where semantics permit; normalize coordinates/units; triangulate and weld; invoke meshopt for remap, cache/fetch/overdraw optimization, quantization, optional simplification and meshlets; then emit deterministic payloads. Reject unsupported visible topology, invalid weights/ranges, missing materials/skeletons, nonfinite values, or target limit overflow.

6. Behavioral evidence and disposition

Inspect shipped models, Gamebryo/BF geometry classes, material links, earlier converter evidence and live original for coordinate conventions, LOD thresholds, billboards, alpha ordering, skinning and exceptional geometry.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

7. Required behavior

Models render with correct transforms, bounds, submesh materials, LODs and skinning references. Mod meshes follow the same conversion contract. Unknown visible source semantics block conversion rather than producing placeholder geometry.

8. Performance contract

Archive lookup/traversal allocates zero. GPU preparation performs one direct upload per merged range where practical, reuses staging capacity and retains no second CPU geometry graph. Draws use Bevy batching/culling and stable pipeline keys. Track mapped pages, upload bytes/calls, retained CPU heap, GPU vertex/index bytes, draw counts and LOD behavior.

9. Acceptance criteria

Four files/API; static/skinned/multi-material/LOD fixtures, corrupt range/layout rejection, deterministic meshopt output, zero-copy CPU traversal and exact GPU accounting. Source review must find no custom optimization algorithm, renderer, scene graph, or CPU geometry cache.

10. Required handoff

Report meshopt version/options, Bevy render-asset integration, target layouts, A06/A07/A15 consumers, root registrations, unresolved model evidence and mapping/upload/GPU/render measurements.

A05 — GPU-Native Texture

1. Identity and outcome

This package produces the only runtime texture representation: target-profile GPU-compressed mip/layer payloads with exact dimensions, colour semantics and upload ranges. No DDS/TGA/BMP/JPEG decode occurs in the game. Integration wave 1; only the shared native contract is prerequisite.

2. Exclusive ownership

This package owns native/src/families/texture.rs, converter/src/families/texture.rs, game/src/assets/texture.rs, preflight/tests/texture_contract.rs. Renderer, GPU ledger and module/plugin registration are root-owned/read-only.

Existing image, ddsfile, and image_dds dependencies exclusively own source image decoding/transcoding in preflight; Bevy/wgpu own runtime images, GPU textures and upload. This package must not write an image codec, runtime source decoder, texture renderer, or second CPU pixel cache.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
TEXTURE_ARCHIVE_VERSION constant u32 = 1 Layout version.
TextureArchive root { version:u32, target:TargetProfile, id:AssetId, dimension:TextureDimension, width:u32, height:u32, depth_or_layers:u32, mip_count:u16, format:TextureFormat, color_space:ColorSpace, sampler:SamplerRecord, cursor_hotspot:Option<[u16;2]>, subresources:Vec<TextureSubresource>, payload:Vec<u8> } Payload is final GPU copy source; hotspot is present only for converted CUR.
TextureDimension enum D1, D2, D2Array, Cube, D3 Must match target GPU capabilities.
TextureFormat enum R8, Rg8, Rgba8, Bgra8, Bc1, Bc3, Bc5, Bc7 Exact native format; profile may restrict variants.
ColorSpace enum Linear, Srgb Explicit, never filename guessed at runtime.
SamplerRecord record { address:[AddressMode;3], mag:FilterMode, min:FilterMode, mip:FilterMode, anisotropy:u8 } Anisotropy 1..=16.
AddressMode enum Clamp, Repeat, Mirror Maps directly to wgpu.
FilterMode enum Nearest, Linear Maps directly to wgpu.
TextureSubresource record { mip:u16, layer:u16, range:AssetRange, bytes_per_row:u32, rows_per_image:u32 } Range is payload bytes; sorted layer then mip; block alignment valid.
MappedTextureArchive / access_texture / access_texture_validated owning view/functions tuple wrapper; new(MappedAsset)->io::Result<Self>; archived(&self)->&ArchivedTextureArchive; fn access_texture(&[u8])->io::Result<&ArchivedTextureArchive>; unsafe fn access_texture_validated(&[u8])->&ArchivedTextureArchive Validates dimensions/ranges once and owns mapping.
TextureSource producer input { virtual_path:Cow<str>, bytes:Cow<[u8]>, usage:TextureUsage } Borrowed unless legacy normalization is required.
TextureUsage enum Color, Normal, Mask, Height, Ui, Lookup Preflight source/transcode meaning; distinct from A07 shader TextureSemantic.
compile_texture producer fn compile_texture(input:&TextureSource, target:TargetProfile)->io::Result<Vec<u8>> Deterministic decode/mipmap/transcode.
TextureAsset Bevy asset { archive:MappedTextureArchive } deriving Asset, TypePath, Clone; bevy_image(&self)->Handle<Image> Sole mapped CPU payload owner; the image handle is a stable UUID view of the same native asset ID.
TextureGpuAsset render asset custom RenderAsset<SourceAsset=TextureAsset> Borrows archived &[u8] and prepares the sole GpuImage directly; never constructs Image.data or another pixel buffer.
TextureAssetLoader loader AssetLoader<Asset=TextureAsset> for oztexture using path + get_owned Validates the mapped native archive and performs no pixel conversion or copy.
TextureAssetPlugin plugin unit struct Registers asset/loader plus complete render extraction, preparation and eviction accounting.

4. Data layout or ECS ownership

Metadata is rkyv; payload is contiguous, tightly packed block-compressed data in the same archive. Ranges are bytes relative to payload. Little-endian metadata; subresources have no storage padding. Mapping is kept until asset eviction. A narrow GPU-image registration bridge lets ordinary Bevy materials and ImageNode resolve the prepared texture by the native asset UUID without a CPU Image or custom renderer. Upload creates exactly one GPU texture and view set. Preparation updates GpuMemoryLedger; no external renderer wrapper or parallel texture cache exists.

5. Preflight producer contract

Accept only DDS/TGA/BMP/JPEG and loose-installation CUR, the formats evidenced by shipped archives or the original C++ loaders. PNG, KTX2, and unevidenced BFT texture inputs are not part of the source contract. Normalize virtual paths before ID. Resolve replacements and later pack layers before decode. Compatible DDS blocks and complete authored mips are copied unchanged. Decode, orient, select colour space, generate missing/invalid mips, renormalize normal maps, transcode only target-incompatible formats and compute tight row layout. CUR is decoded offline to the same D2 GPU texture while preserving its hotspot as native metadata. Reject corrupt dimensions, unsupported semantics, excessive target limits, incomplete compressed blocks and ambiguous colour space. Diagnostics name source and failing mip. Output ordering is layer then mip.

6. Behavioral evidence and disposition

Search shipped texture extensions and material slots; old commit source_format/model.rs, presentation/renderer.rs, UI visuals and terrain; oracle NiSourceTexture/BFRTexture classes and Gamebryo calls. Questions: vertical orientation, alpha modes, normal channel convention, UI filtering, terrain tile samplers and source-format coverage. Validate with UI, animal and terrain screenshots rather than guessed defaults.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

7. Required behavior

Pixel orientation, alpha, colour and sampling match original presentation. Missing optional textures resolve to an explicit preflight-generated fallback asset; missing required textures fail conversion. Runtime streams declared mip subresources without decoding. Mod textures are preflight-converted identically.

8. Performance contract

Metadata traversal/Handle<Image> construction is zero allocation and never clones pixel data. Render preparation passes the mapped payload directly to Bevy/wgpu as &[u8]; no payload-sized Rust heap allocation is permitted. O(payload bytes) GPU transfer, O(subresources) validation, O(1) subresource access. Record mapped/resident bytes separately from exact GPU texture bytes and staging. No per-frame texture preparation after residency. Repeated render extraction of unchanged handles is allocation-free.

9. Acceptance criteria

Four files and API above; fixtures cover sRGB colour, linear normal, preserved DDS blocks, mip/layer ranges and non-tight payload rejection. Tests prove expected dimensions/formats and zero-allocation hydration view. A Bevy fixture obtains TextureAsset::bevy_image(), sets it directly on ordinary ImageNode, observes one GPU preparation, and proves no CPU pixel copy. Non-goals: material selection, renderer batching and source decode in engine. A render fixture must verify exact create/upload/remove ledger deltas and no repeated preparation.

10. Required handoff

Report image/transcoder dependencies and profile feature choices, asset/render registration, A07/A12/A16 consumers, unresolved orientation/semantic evidence, and heap/mapping/GPU measurements.

A06 — Skeletal Animation

1. Identity and outcome

This package produces compact skeleton-indexed clips, markers and graph transition tables that can be sampled directly from mapped memory. Source BF/KF/BFM parsing and name resolution end in preflight. Integration wave 3; depends on A04 model.

2. Exclusive ownership

This package owns native/src/families/animation.rs, converter/src/families/animation.rs, game/src/assets/animation.rs, preflight/tests/animation_contract.rs; all other files and schedule/render integration are read-only/root handoffs.

Bevy owns transforms, skeleton/render integration and GPU skinning. A06 owns the mapped source-authentic track sampler, selection/transition policy and presentation markers only. It must not create another scene graph, skeleton world, transform hierarchy, render extraction path, or generic animation host.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
ANIMATION_ARCHIVE_VERSION constant u32 = 1 Layout version.
AnimationArchive root { version:u32, target:TargetProfile, id:AssetId, skeleton:AssetId, strings:Vec<u8>, clips:Vec<ClipRecord>, tracks:Vec<TrackRecord>, keys:Vec<TransformKey>, presentation_markers:Vec<PresentationMarkerRecord>, graph_nodes:Vec<GraphNode>, graph_edges:Vec<GraphEdge> } All times sorted; ranges index tables. Presentation markers never enter gameplay dispatch.
ClipFlags bitflags u8 LOOP, ROOT_MOTION, ADDITIVE Exact clip behavior.
ClipRecord record { id:AssetId, name:StringRange, duration_ms:u32, tracks:TableRange, markers:TableRange, flags:ClipFlags } Duration > 0.
TrackRecord record { joint:u16, channel:TrackChannel, interpolation:Interpolation, keys:TableRange } One sorted track per joint/channel.
TrackChannel enum Translation, Rotation, Scale Runtime canonical TRS only.
Interpolation enum Step, Linear, Spherical Spherical valid only for rotation.
TransformKey POD record { time_ms:u32, value:[f32;4] } Translation xyz, quaternion xyzw, scale xyz; finite and normalized rotations.
PresentationMarkerRecord private record { time_ms:u32, label:StringRange } Diagnostic/presentation metadata within clip duration; never interpreted as a gameplay command.
GraphNode record { id:AssetId, clips:TableRange, selection:ClipSelection } Clips reference clip indexes.
ClipSelection enum First, RandomWeighted, Sequential Fixed authored selection vocabulary.
GraphEdgeFlags bitflags u8 INTERRUPTIBLE, RETURN, IMMEDIATE Exact transition behavior.
GraphEdge record { from:u32, to:u32, transition_clip:u32, blend_ms:u16, flags:GraphEdgeFlags } Node/clip indexes validated.
MappedAnimationArchive / access_animation / access_animation_validated owning view/functions tuple wrapper; new(MappedAsset)->io::Result<Self>; archived(&self)->&ArchivedAnimationArchive; clip(&self,AssetId)->Option<&ArchivedClipRecord>; fn access_animation(&[u8])->io::Result<&ArchivedAnimationArchive>; unsafe fn access_animation_validated(&[u8])->&ArchivedAnimationArchive Binary lookup, validate once, own mapping.
AnimationSource input { virtual_path:String, clip_bytes:Vec<Vec<u8>>, graph_bytes:Option<Vec<u8>> } BF clips/BFM graph.
compile_animation producer fn compile_animation(input:&AnimationSource, skeleton:&ArchivedModelArchive, target:TargetProfile)->io::Result<Vec<u8>> Pure resolved compilation.
AnimationAsset Bevy asset { archive:MappedAnimationArchive } Mapped view.
Animator component { animation:Handle<AnimationAsset>, clip:AssetId, elapsed_ms:u32, speed_permille:i16, state:AnimationState, looped:bool, clock:AnimationClock } Live playback fact on one animated entity.
AnimationState enum Playing, Paused, Finished Playback state only.
AnimationClock enum Simulation, RealTime World animation follows G32 fixed simulation ticks; shell/UI animation follows real time.
PoseRange value { start:u32, len:u16 } Contiguous joint-matrix span in the package pose buffer.
SkinnedPose component { range:PoseRange } Small allocation-free reference; range changes only with skeleton.
AnimationPoseBuffer resource { joints:Vec<[[f32;4];4]>, free:Vec<PoseRange> } Focused contiguous pool, reserved during spawn/load and reused; no per-entity heap allocation.
advance_simulation_animators system simulation-clock Query<&mut Animator> in FixedUpdate/FixedGameSet::Act using one G32 fixed tick Integer elapsed update, paused with zoo simulation.
advance_realtime_animators system Res<Time<Real>>, Query<&mut Animator> filtered to real-time clock in Update/GameSet::Presentation Shell/UI elapsed update independent of zoo pause.
sample_animation_poses system fn(Res<Assets<AnimationAsset>>, ResMut<AnimationPoseBuffer>, Query<(&Animator,&SkinnedPose), Changed<Animator>>) in Update/GameSet::Presentation after advancement Writes assigned span in place.
advance_presentation_markers private system animation assets plus changed animators in Update/GameSet::Presentation Advances animation-local presentation metadata only. It exposes no gameplay message or generic event dispatcher.
AnimationAssetLoader loader { profile:Arc<MappedPack> }; new(Arc<MappedPack>)->Self; AssetLoader<Asset=AnimationAsset> for ozanim via path + get_owned No keyframe/reader copies.
AnimationAssetPlugin plugin unit struct Registers asset/loader, playback components and narrow systems.

4. Data layout or ECS ownership

rkyv tables with frozen TableRange spans; integer milliseconds prevent float search drift. Joint indexes are A04 skeleton order. Sampling binary-searches mapped keys and writes the SkinnedPose range in AnimationPoseBuffer; the asset owns no live playback state. GPU skin matrices are transient A04 renderer data, not archived duplicates. Each system performs one operation; there is no animation manager loop.

5. Preflight producer contract

Accept BF binary clips, NetImmerse KF controller sequences, BFM XML graph documents, and TXTKEYS presentation markers after layer resolution. Prefer the winning same-stem BF clip because the shipped BFM graphs reference BF; when no BF winner exists, compile the KF sequence as the clip. KF target names bind to the final model skeleton, NiKeyframeController/NiKeyframeData become canonical translation/rotation/scale tracks, and NiTextKeyExtraData becomes presentation markers. Preflight flattens quadratic and tension/bias/continuity curves into the native linear/spherical sampling contract. KFM, KFINFO, and BIP remain authoring/controller evidence with explicit validated disposition. None of these source formats or their block graphs reach the runtime. Normalize IDs, map source node names to model joints, convert Euler tracks to canonical quaternions, quantize time to milliseconds only within evidenced tolerance, compile nonsemantic presentation markers/transitions and close clip dependencies. Any source marker with gameplay semantics must be recovered from the complete corpus and promoted to the precise owning gameplay action/message before acceptance; it cannot remain an ID or string event. Reject unknown value-bearing tracks, missing joints/clips, nonmonotonic time, invalid quaternions, graph indexes/cycles where forbidden and source duration mismatch. Deterministic clip ID then track/joint/channel ordering.

6. Behavioral evidence and disposition

Search shipped .bf/.bfm, model skeleton names; commit d5657e5a source_format/animation.rs, animation startup/AI code; oracle animation graph, sequence and controller types. Questions: time unit/quantization, Euler order, loop/end semantics, weighted clip selection, blend durations, whether each marker is presentation metadata or an exact domain action, and graph transition fallback. Live-check representative locomotion/idle clips.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

7. Required behavior

Sampling at 0/end, looping, interpolation and presentation-marker crossing are deterministic. Graphs select only compiled clips; Animator owns current clip/time. Root motion is an explicit evidenced flag/track, never inferred silently. Mod clips/graphs must use the same fixed channels and selection vocabulary.

8. Performance contract

Clip lookup O(log clips), per-track sample O(log keys), zero allocations into a pre-sized pose slice. Presentation-marker traversal is linear only over the crossed range. Allocator tests cover lookup/advance/sample/presentation-marker scan; mapped pages and animation GPU matrix capacity measured separately. No per-frame strings or graph cloning.

9. Acceptance criteria

Four files; convert fixture with translation/quaternion/scale, presentation markers and an edge; validate sampled values and failure cases. A Bevy fixture advances, samples and advances presentation metadata with zero allocations after pose warm-up. Model render skinning is A04; behavior selection is a gameplay concern.

10. Required handoff

Report model archive consumption, parser dependencies, root/schedule/message registrations, graph evidence and allocation/mapping/GPU-pose validation.

A07 — Material

1. Identity and outcome

This package produces compact immutable material render state with shader variant, typed parameters and texture bindings. Material selection requires no string lookup or Gamebryo-style object graph at runtime. Integration wave 1; depends on A05 and A08.

2. Exclusive ownership

This package owns native/src/families/material.rs, converter/src/families/material.rs, game/src/assets/material.rs, preflight/tests/material_contract.rs only.

Bevy material/render-asset APIs and wgpu exclusively own bind groups, render state, pipelines and drawing. A07 supplies compact parameters, references and typed specialization keys; it must not create a material renderer, pipeline registry, graphics abstraction, or copied texture/material graph.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
MATERIAL_ARCHIVE_VERSION constant u32 = 1 Layout version.
MaterialArchive root { version:u32, target:TargetProfile, id:AssetId, shader:AssetId, variant:u32, render:RenderState, parameters:Vec<MaterialParameter>, textures:Vec<TextureBinding>, dependencies:Vec<AssetId> } Parameters/bindings sorted by semantic.
MaterialFlags bitflags u16 UNLIT, RECEIVE_FOG, VERTEX_COLOR, DEPTH_BIAS, TWO_SIDED_LIGHTING Exact compiled render features.
RenderState record { blend:BlendMode, cull:CullMode, depth_write:bool, depth_test:CompareMode, alpha_cutoff:f32, queue:i16, flags:MaterialFlags } Direct pipeline-selection facts.
BlendMode enum Opaque, Masked, Alpha, Additive, Multiply Fixed variants.
CullMode enum None, Front, Back Direct wgpu mapping.
CompareMode enum Always, Less, LessEqual, Equal, GreaterEqual, Greater Direct wgpu mapping.
MaterialParameter record { semantic:ParameterSemantic, value:[f32;4] } Finite, one row per semantic.
ParameterSemantic enum BaseColor, Emissive, Roughness, Metallic, Alpha, UvTransform0, UvTransform1 Exhaustive fixed shader ABI; unknown source semantics reject conversion.
TextureBindingFlags bitflags u8 OPTIONAL, SRGB_OVERRIDE, CLAMP_OVERRIDE Exact evidenced binding policy; no opaque source bits.
TextureBinding record { semantic:TextureSemantic, texture:AssetId, sampler_override:u16, uv_set:u8, flags:TextureBindingFlags } Texture ID nonzero unless explicitly optional.
TextureSemantic enum BaseColor, Normal, Mask, Emissive, Detail, Environment Exhaustive stable bind vocabulary.
MappedMaterialArchive / access_material / access_material_validated owning view/functions tuple wrapper; new(MappedAsset)->io::Result<Self>; archived(&self)->&ArchivedMaterialArchive; fn access_material(&[u8])->io::Result<&ArchivedMaterialArchive>; unsafe fn access_material_validated(&[u8])->&ArchivedMaterialArchive Validate once, own mapping.
MaterialSource input { virtual_path:String, bytes:Vec<u8> } Material XML/NIF properties.
compile_material producer fn compile_material(input:&MaterialSource, resolve:impl Fn(&str)->Option<AssetId>, target:TargetProfile)->io::Result<Vec<u8>> Pure compilation.
MaterialAsset Bevy asset { archive:MappedMaterialArchive } Mapped view.
ModelMaterial component pub struct ModelMaterial(pub Handle<MaterialAsset>) Optional explicit material handle attached directly by G04.
MaterialAssetLoader loader { profile:Arc<MappedPack> }; new(Arc<MappedPack>)->Self; AssetLoader<Asset=MaterialAsset> for ozmaterial via path + get_owned No reader copy/runtime assembly.
MaterialAssetPlugin plugin unit struct Registers asset/loader and complete material extraction, preparation and eviction accounting.

4. Data layout or ECS ownership

One rkyv root per material; fixed enums/numeric rows map to a cached Bevy/wgpu pipeline and bind group. No maps, strings or boxed values. Texture/shader IDs are pack dependencies and become handles during render preparation; GPU uniform and bind-group ownership is implemented here in Bevy's render world. A04 extracts ModelMaterial directly; no renderer service facade exists.

5. Preflight producer contract

Accept NIF material/texturing/alpha properties, BFB material blocks and material XML after override resolution. Canonical path ID; merge inherited/default properties, map texture slots and shader variant, fold UV transforms, choose blend/depth/cull and resolve dependencies. Reject contradictory states, non-finite values, missing required bindings, unsupported value-bearing shader properties and target layout mismatch. Sort by semantic for deterministic bytes.

6. Behavioral evidence and disposition

Search model material properties and texture slots; commit d5657e5a source_format/model.rs and presentation/renderer.rs BFR material records; oracle NiMaterialProperty/NiTexturingProperty/BFRMaterial classes. Questions: alpha test thresholds, blend functions, selector/variant rules, UV animation, two-sided flags, environment/detail slots and queue ordering. Verify terrain, glass/water, foliage and animal materials live.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

7. Required behavior

Opaque/masked/transparent presentation, colour/emissive values, UV selection, texture dependencies and render ordering match evidence. Animated material parameters are immutable curves only if evidence requires them; live time stays outside asset. Unknown properties that change pixels fail rather than disappear.

8. Performance contract

Validation and preparation O(parameters+bindings), lookup O(log n) or bounded linear tiny table, zero allocation during steady draw extraction. Pipelines and bind groups cache by immutable archive key; no per-frame material clones or uniform Vec growth. Count mapped bytes, uniform/bind GPU bytes and cache misses. Unchanged extraction and prepared lookup allocate zero after cache warm-up.

9. Acceptance criteria

Four files/API; opaque, masked and alpha fixtures with texture/shader closure; reject duplicate semantics and invalid target layouts. Archived binding traversal and repeated prepared lookup are allocation-free. A render fixture verifies exact create/remove ledger deltas. Shader/texture production remain A08/A05; A04 owns draw batching.

10. Required handoff

Report A05/A08 IDs, parser deps, root registrations, renderer pipeline/cache requirements, unresolved original render-state evidence and memory counters.

A08 — Shader modules and variants

1. Identity and outcome

Preflight translates and validates the original FX effects and their shipped H includes, plus material-selected fixed-function variants, variant with Naga, selects a target-profile module representation, and emits exact bind/pipeline metadata. Bevy/wgpu create and cache runtime shader modules and pipelines. OpenZT2 contains no shader compiler or graphics API abstraction.

2. Exclusive ownership and mandatory dependencies

The package worker owns native/src/families/shader.rs, converter/src/families/shader.rs, game/src/assets/shader.rs, and preflight/tests/shader_contract.rs only. Root exposes to preflight the exact Naga version selected by Bevy 0.19/wgpu; a second incompatible Naga version is forbidden.

Naga exclusively owns parsing, IR, translation and shader validation. Bevy/wgpu own shader modules, bind-group layouts, pipeline layouts, specialization/cache, driver compilation and render-world integration. This package must not write a shader parser/compiler, SPIR-V/WGSL translator, pipeline cache, renderer backend, or string-based runtime variant system.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
SHADER_ARCHIVE_VERSION constant u32 = 1 Native layout version.
ShaderArchive root { version:u32, target:TargetProfile, id:AssetId, stages:Vec<ShaderStageRecord>, bindings:Vec<BindingRecord>, variants:Vec<ShaderVariant>, payload:Vec<u8> } Prevalidated modules and exact metadata.
ShaderStageRecord record { stage:ShaderStage, entry:StringRange, module:AssetRange, representation:ShaderRepresentation } One target-compatible module range.
ShaderRepresentation enum target-profile accepted Wgsl or SpirV representation Selected only by preflight/backend contract.
BindingRecord record { group:u8, binding:u8, visibility:u8, kind:BindingKind, count:u16, min_size:u32 } Matches Naga validation output and A07 layout.
ShaderVariant record { key:u64, stages:TableRange, bindings:TableRange, vertex_layout:u16, render_state:u32 } Sorted unique complete pipeline key.
MappedShaderArchive owning view new(MappedAsset)->io::Result<Self> and archived/module accessors Validated once; mapping-owned.
compile_shader producer fn compile_shader(input:&ShaderSource,target:TargetProfile)->io::Result<Vec<u8>> Pure Naga-backed translation/validation and native writing.
ShaderAsset Bevy asset { archive:MappedShaderArchive } Mapped modules/metadata.
ShaderAssetPlugin plugin unit struct Registers loader and ordinary Bevy shader/pipeline preparation only.

4. Data layout and ownership

Modules are stored in the exact representation accepted for the selected backend profile, aligned and referenced by AssetRange. Binding/variant tables are flat and directly traversed. Prepared runtime state is Bevy/wgpu state; OpenZT2 retains no parallel module or pipeline registry.

5. Preflight producer contract

Resolve original FX/H effects and material techniques. Convert supported semantics to Naga input, parse, validate capabilities/uniformity, translate to the target representation, reflect exact bindings, enumerate only reachable variants and sort deterministically. Reject unknown required shader semantics, invalid bindings, unsupported capabilities, missing entry points, variant explosion, or target/backend mismatch. Runtime source translation is forbidden; unavoidable driver module/pipeline creation is prewarmed during loading.

6. Behavioral evidence and disposition

Inspect shipped .fx, materials, shader references, Gamebryo renderer evidence, the C++ oracle and live original for techniques, blend/depth/cull policy, skinning, terrain, water, fog, shadows and expansion effects.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

7. Required behavior

Every material resolves a validated compatible variant. Pipeline keys are typed and deterministic. Missing or unsupported visible semantics fail preflight; there is no runtime text substitution, fallback uber-shader, or guessed render state.

8. Performance contract

Mapped module/variant lookup allocates zero. Loading creates each required Bevy/ wgpu module and pipeline once, prewarms scene/menu pipelines, and never compiles or specializes per frame. Track mapping, module/pipeline creation count/time, pipeline-cache misses, bind-group churn and shader/pipeline GPU/driver bytes where available.

9. Acceptance criteria

Four files/API; Naga translation/validation fixtures, binding/variant rejection, deterministic output, zero-allocation lookup and Bevy pipeline preparation for representative model/terrain/water/UI effects. Source review must find no custom shader compiler, runtime source converter, or duplicate pipeline registry.

10. Required handoff

Report exact Bevy/wgpu/Naga version alignment and features, target module representations, A07 bindings/keys, root registrations/prewarm order, unsupported original semantics and mapping/pipeline measurements.

A09 — Localization

1. Identity and outcome

This package produces collision-safe, allocation-free locale lookup tables with UTF-8 text, formatting metadata and pre-resolved fallback chains. Runtime never parses lang XML, merges locales or builds a hash map. Integration wave 1; shared native contract only.

2. Exclusive ownership

This package owns native/src/families/localization.rs, converter/src/families/localization.rs, game/src/assets/localization.rs, and preflight/tests/localization_contract.rs only.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
LOCALIZATION_ARCHIVE_VERSION constant u32 = 1 Layout version.
LocalizationArchive root { version:u32, target:TargetProfile, locale:LocaleCode, fallbacks:Vec<AssetId>, strings:Vec<u8>, entries:Vec<LocaleEntry>, tokens:Vec<FormatToken> } Entries sorted by full 128-bit key ID.
LocaleCode value { language:[u8;2], region:[u8;2] } Lowercase ISO language, uppercase region or zero region.
LocaleEntry record { key:AssetId, key_text:StringRange, value:StringRange, tokens:TableRange } Key text retained to prove collision identity; unique full canonical key.
FormatToken enum Literal(StringRange), Argument{index:u8,kind:ArgumentKind}, Plural{index:u8,one:StringRange,other:StringRange} Fixed safe formatter, no runtime expression language.
ArgumentKind enum Text, Integer, Decimal, Currency, Percent Locale formatting behavior fixed.
MappedLocalizationArchive / access_localization / access_localization_validated owning view/functions tuple wrapper; new(MappedAsset)->io::Result<Self>; archived(&self)->&ArchivedLocalizationArchive; get(&self,AssetId)->Option<&str>; tokens(&self,AssetId)->Option<&[ArchivedFormatToken]>; fn access_localization(&[u8])->io::Result<&ArchivedLocalizationArchive>; unsafe fn access_localization_validated(&[u8])->&ArchivedLocalizationArchive Validate once, own mapping, borrowed UTF-8.
LocalizationSource input { virtual_path:String, locale:LocaleCode, bytes:Vec<u8> } Lang XML/data.
compile_localization producer fn compile_localization(inputs:&[LocalizationSource], fallback_order:&[LocaleCode], target:TargetProfile)->io::Result<Vec<u8>> Pure resolved locale producer.
LocalizationAsset Bevy asset { archive:MappedLocalizationArchive } Borrowing mapped strings.
LocalizationAssetLoader loader { profile:Arc<MappedPack> }; new(Arc<MappedPack>)->Self; AssetLoader<Asset=LocalizationAsset> for ozloc via path + get_owned No reader copy/String reconstruction.
LocalizationAssetPlugin plugin unit struct Registers asset/loader only.

4. Data layout or ECS ownership

UTF-8 key/value/literal text is interned once in strings; StringRange is byte-based and validated on UTF-8 boundaries. AssetId::from_key hashes the trimmed canonical localization key, while stored key_text detects accidental collision. Fallbacks are resolved by preflight into the emitted selected-locale table; fallbacks records provenance only and runtime never searches files.

5. Preflight producer contract

Accept selected language XML/string tables and DLC/mod overrides. Normalize keys with evidenced case/namespace rules, apply later-layer overrides per key, resolve requested locale → parent/region → base fallback, parse safe format placeholders, validate UTF-8 and intern deterministic key order. Reject duplicate keys within one precedence layer, hash collisions with differing key text, malformed formats, missing required fallback and invalid locale codes.

6. Behavioral evidence and disposition

Search shipped lang/locale XML and UI text keys; oracle locale/string manager classes and functions; commit d5657e5a locale sources, UI text and formatter paths. Questions: key case sensitivity, locale fallback order, formatting syntax, plural/currency rules, escaped markup and whether region codes exist. Compare live representative UI, finance and scenario strings.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

7. Required behavior

Lookup returns borrowed selected-locale text; absent optional key yields None for UI to display a diagnostic, never silently key-as-value in release. Fixed format tokens support original authored placeholders without arbitrary scripts. Mods override keys before output. User locale change selects another completed native locale asset/profile, not source remerge.

8. Performance contract

Compile O(total text log entries); validation O(entries+bytes); lookup O(log n) and zero allocation. Formatting writes into caller-provided reusable fmt::Write or byte buffer and must allocate zero after capacity. Track mapped locale pages; no GPU bytes. Regression tests cover lookup and formatted traversal counters.

9. Acceptance criteria

Four files/API; fixture covers override/fallback, UTF-8, arguments and collision rejection. Borrowed lookup and warmed formatting allocate zero. Non-goals: UI text components, locale picker and font shaping.

10. Required handoff

Report XML/parser/locale-format dependencies, root registrations, A02/A03/A14/ A19 consumers, exact unresolved key/format semantics and allocator/mapping validation.

A10 — Compiled Behavior Program

1. Identity and outcome

This package compiles data-authored animal, guest and staff behavior into fixed typed command, scoring and constant tables. Gameplay systems interpret narrow instructions against ECS components; this asset is not a shadow world or a general Lua VM. Integration wave 3; depends on A14 definitions.

2. Exclusive ownership

This package owns native/src/families/behavior_program.rs, converter/src/families/behavior_program.rs, game/src/assets/behavior_program.rs, and preflight/tests/behavior_program_contract.rs only.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
BEHAVIOR_ARCHIVE_VERSION constant u32 = 1 Opcode/layout version.
BehaviorArchive root { version:u32, target:TargetProfile, id:AssetId, sets:Vec<BehaviorSet>, set_programs:Vec<AssetId>, programs:Vec<BehaviorProgram>, considerations:Vec<Consideration>, actions:Vec<BehaviorAction>, constants:Vec<BehaviorConstant>, gesture_templates:Vec<GestureTemplate>, gesture_points:Vec<GestureTemplatePoint>, puzzles:Vec<PuzzleDefinition>, puzzle_steps:Vec<PuzzleStep>, dependencies:Vec<AssetId> } Sets/programs/templates/puzzles sorted by ID; all record spans use TableRange.
BehaviorSet record { id:AssetId, programs:TableRange, think_interval_ticks:u32, idle_program:AssetId } programs indexes root set_programs; IDs resolve to BehaviorProgram; nonzero cadence; idle belongs to the range. G13's BehaviorHandle::set indexes this table directly.
BehaviorProgramFlags bitflags u8 INTERRUPTIBLE, REPEAT, REQUIRES_TARGET, EXCLUSIVE Exact task policy.
BehaviorProgram record { id:AssetId, considerations:TableRange, actions:TableRange, cooldown_ticks:u32, flags:BehaviorProgramFlags } G32 ticks; ranges bounded and nonempty action range.
Consideration record { input:BehaviorInput, curve:ResponseCurve, weight:i16 } Weight fixed-point -1000..=1000; all input parameters live in the typed BehaviorInput variant.
BehaviorInput enum Need(crate::families::species::NeedKind), DistanceTo(TargetKind), HasTarget(TargetKind), HabitatScore, Health, Age, TimeOfDay, Random(BehaviorRandomStream) Uses A03's exact fixed need vocabulary; no duplicate/string key or behavior-domain AssetId.
BehaviorRandomStream enum TaskScore, TargetChoice, ClipChoice Fixed G32-derived RNG domains; adding a domain requires corpus evidence and an enum revision, never an arbitrary ID.
TargetKind enum Food, Water, Shelter, Enrichment, Mate, Keeper, Guest, Facility, Habitat Query category, not entity bag key.
ResponseCurve record { kind:CurveKind, points:[[i16;2];4], count:u8 } Fixed-point x/y; 1..=4 points sorted x.
CurveKind enum Linear, Inverse, Step, Piecewise Deterministic scoring.
BehaviorAction enum ChooseTarget{kind:TargetKind,radius_cm:u32}, MoveToTarget, Consume{need:crate::families::species::NeedKind}, PlayAnimation{clip:AssetId}, Wait{duration_ticks:u32}, ClearTarget, SetCooldown{duration_ticks:u32} All simulation duration payloads are G32 ticks; gameplay package owns execution systems. There is no generic event action.
BehaviorConstant record { key:AssetId, value:BehaviorValue } Sorted by key.
BehaviorValue enum Bool(bool), Integer(i32), Scalar(i32), Asset(AssetId) Scalar is signed 16.16 fixed point.
GestureTemplateKind enum Stroke, Circle, Line, ZigZag, TapSequence, Drag Fixed authored recognition vocabulary; distinct from G30 live context.
GestureTemplate record { id:AssetId, kind:GestureTemplateKind, points:TableRange, tolerance_permille:u16, minimum_length_px:u16, maximum_duration_ms:u32, command:PuzzleCommand } Points index gesture_points; tolerance 0..=1000.
GestureTemplatePoint POD record { x_snorm:i16, y_snorm:i16, time_unorm:u16 } Normalized authored template point.
PuzzleDefinition record { id:AssetId, steps:TableRange, completion:PuzzleCommand, failure:PuzzleCommand, time_limit_ticks:u32 } Steps index puzzle_steps.
PuzzleStep record { id:AssetId, gesture:AssetId, target:AssetId, required:u16, command:PuzzleCommand } Typed authored step, no string dispatch.
PuzzleCommand enum None, Advance, Reset, Complete, Fail, SetMode(crate::families::world_definitions::ModeKind), PlaceFossil{slot:AssetId,piece:AssetId}, StartClone{species:AssetId}, TrainTrick{animal:AssetId,trick:AssetId} Fixed G30/G31 effects. AssetId fields identify data targets only; unknown semantic commands block conversion.
MappedBehaviorArchive / access_behavior / access_behavior_validated owning view/functions tuple wrapper; new(MappedAsset)->io::Result<Self>; archived(&self)->&ArchivedBehaviorArchive; set(&self,AssetId)->Option<&ArchivedBehaviorSet>; set_programs(&self,&ArchivedBehaviorSet)->&[ArchivedAssetId]; program(&self,AssetId)->Option<&ArchivedBehaviorProgram>; fn access_behavior(&[u8])->io::Result<&ArchivedBehaviorArchive>; unsafe fn access_behavior_validated(&[u8])->&ArchivedBehaviorArchive Binary lookup, validate once, own mapping.
BehaviorSource input { virtual_path:String, bytes:Vec<u8>, language:BehaviorSourceLanguage } Resolved XML/task/Lua source.
BehaviorSourceLanguage enum XmlTask, XmlBehavior, LuaSubset, GestureXml, PuzzleXml Preflight-only.
compile_behavior_programs producer fn compile_behavior_programs(inputs:&[BehaviorSource], resolve:impl Fn(&str)->Option<AssetId>, timing:&ArchivedSimulationTimingDefinition, target:TargetProfile)->io::Result<Vec<u8>> Pure closed-world compilation; converts authored simulation durations/cadence to G32 ticks.
BehaviorProgramAsset Bevy asset { archive:MappedBehaviorArchive } Mapped program.
BehaviorProgramLoader loader { profile:Arc<MappedPack> }; new(Arc<MappedPack>)->Self; AssetLoader<Asset=BehaviorProgramAsset> for ozbehavior via path + get_owned No reader copy/Lua parser.
BehaviorProgramAssetPlugin plugin unit struct Registers only asset/loader.

4. Data layout or ECS ownership

All variable collections, including behavior-set program IDs, gesture templates and puzzle steps, are typed flat tables and frozen TableRanges. Fixed point is used for deterministic score/constant values; simulation durations are integer G32 ticks (gesture input windows alone remain real milliseconds) and distances are centimetres. Programs contain no entity state, closures, strings, bytecode stack or mutable instruction pointer. G13/G20 systems own current task/target/cooldown as small components.

5. Preflight producer contract

Accept resolved AI XML/task graphs, gesture/puzzle documents and the evidenced supported subset of Lua behavior scripts. Normalize program/asset keys, expand inheritance/macros, constant-fold expressions, compile recognized queries/actions, resolve asset references and reject any command with effects outside the fixed vocabulary. Resolve source behavior-set membership and idle fallback, then convert authored wait/cooldown/think durations with A14 timing before serialization; runtime does no milliseconds/day conversion. Before implementation, exhaustively inventory every behavior and puzzle command in the shipped/mod corpus and oracle. Each behavior-bearing command must become an explicitly named enum variant with only its genuine typed payload; an AssetId event name, string opcode, generic dispatcher or catch-all variant is not an admissible representation. Later packs replace definitions before compilation. Reject recursion, unbounded loops, dynamic global access, arbitrary I/O, unknown fields, missing references, invalid curves, malformed gesture geometry, puzzle cycles and action sequences with no terminal/yield. Stable program/template/puzzle ID then authored order.

6. Behavioral evidence and disposition

Search shipped AI/task XML and Lua, command strings and entity definitions; oracle BFAI task/evaluator/controller and script callback functions; analysis function domains; commit d5657e5a simulation/behavior.rs, simulation/ai.rs, infrastructure/lua*, gesture/mode dispatch and scripting startup. Questions: complete command/query vocabulary, score formula/tie breaks, task cooldown/interrupt semantics, random distribution, supported Lua expressions, exact gesture normalization/matching, puzzle reset/completion semantics and emitted events. Add opcodes only after evidence and version bump review.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

7. Required behavior

Given identical component observations and RNG input, scoring and chosen action are deterministic. Unknown source behavior fails conversion rather than becoming no-op success. Mod authors may extend programs using the fixed vocabulary, not native code. Action execution is divided among precise gameplay systems; this package supplies immutable instructions only. G30 traverses compiled templates/steps and translates PuzzleCommand to natural domain messages; it does not parse source text or operate a generic puzzle VM.

8. Performance contract

Program lookup O(log n), score O(considerations), action traversal O(actions), all zero allocation. Constants use sorted lookup, not HashMap. Gameplay may evaluate candidates in bounded spatial sets and fixed timestep; no full-world dispatcher. Allocator tests cover lookup/score traversal; mapped pages tracked; no GPU resources.

9. Acceptance criteria

Four files/API; fixture compiles needs/distance behavior plus gesture template and multi-step puzzle, rejects unsupported Lua/I/O/loop/gesture/puzzle command and bad ranges, and proves mapped iteration zero-allocation. Non-goals: ECS target queries, locomotion and animation state.

10. Required handoff

Report parser/compiler dependencies, module/loader registration, A03 NeedKind use and TargetKind promotion request, G13/G20 executor and G30/G31 gesture/puzzle mapping, unresolved opcode evidence and allocation/mapping checks.

A11 — Unified audio

1. Identity and outcome

Original music, ambience, speech, UI sounds, and positional effects become one native audio family consumed through Bevy's entity-based audio API. Bevy owns the device, sinks, playback and spatialization; OpenZT2 supplies mapped resident or streamed payloads plus recovered channel/priority policy. Integration wave 1.

2. Exclusive ownership and Bevy dependency

The package worker owns native/src/families/audio.rs, converter/src/families/audio.rs, game/src/assets/audio.rs, and preflight/tests/audio_contract.rs only. Root enables Bevy 0.19 bevy_audio and the selected native codec feature.

Bevy Audio exclusively owns device output, voice playback, sinks, decoding integration, looping, volume/speed, and spatial emitters/listeners. The package must not implement mix_audio_voices, an audio device backend, a software mixer, a parallel channel world, or copied playback registry. Kira is not added while Bevy Audio meets the recovered behavior; a missing measured behavior requires a root decision before changing backend.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
AUDIO_ARCHIVE_VERSION constant u32 = 1 Native layout version.
AudioArchive root { version:u32, target:TargetProfile, id:AssetId, format:NativeAudioFormat, channels:u8, sample_rate:u32, frames:u64, loop_range:Option<[u64;2]>, seek:Vec<AudioSeekBlock>, payload:Vec<u8> } Payload is final resident PCM or one selected Bevy/rodio-supported stream; no source WAV graph.
NativeAudioFormat enum PcmS16, StreamedMp3, StreamedVorbis Closed target-profile vocabulary. Authored MP3 is retained when it already satisfies the runtime stream contract.
AudioSeekBlock record { first_frame:u64, frame_count:u32, bytes:AssetRange } Sorted, nonoverlapping, bounded stream blocks.
AudioUsage enum Ui, Effect, Voice, Ambient, Music Recovered policy class, not a mixer channel object.
MappedAudioArchive owning view new(MappedAsset)->io::Result<Self>; archived, payload, and binary block_for_frame accessors Validated once and mapping-owned.
compile_audio producer fn compile_audio(input:&AudioSource,target:TargetProfile)->io::Result<Vec<u8>> Pure deterministic decode/normalize/transcode.
AudioAsset Bevy asset { archive:MappedAudioArchive } Keeps mapped bytes alive.
NativeAudioSource Bevy decodable source owning cursor over Arc<MappedPack> plus exact payload range Adapts mapped native bytes to Bevy Audio without copying the whole clip.
AudioCue component { clip:Handle<AudioAsset>, usage:AudioUsage, volume:Volume, looped:bool, spatial:bool, priority:u8 } Domain request on an entity; converted into Bevy AudioPlayer and PlaybackSettings.
AudioAssetPlugin plugin unit struct Registers asset/source adapter and focused cue lifecycle systems.

Live playback uses Bevy's AudioPlayer, PlaybackSettings, AudioSink, SpatialAudioSink, and SpatialListener directly. OpenZT2 does not wrap or mirror their state. A small global policy resource may hold recovered per-usage volume/voice limits; it does not own voices.

4. Data layout and ECS ownership

Metadata and seek blocks are traversed from the mapping. Resident PCM and stream blocks remain mapped until Bevy's decoder/output path consumes them. Each sound is an ordinary playback entity spatially parented or transformed like any other Bevy entity. Completion uses component removal/despawn policy, not a central audio manager.

5. Preflight producer contract

Accept only WAV and MP3, the audio formats present in the official corpus and original loader evidence. Validate MP3 timing/headers but preserve its authored compressed bytes unchanged as the native streamed representation. Decode WAV; select resident PCM for small latency-sensitive clips and streamed Vorbis for large music/ambience. Normalize channel layout, loop points and usage. Emit deterministic ranges and reject corrupt media, invalid loops, unsupported required content, clipping beyond recovered policy, or target codec mismatch. OGG/WMA and other unevidenced source routes are absent.

6. Behavioral evidence and disposition

Inspect shipped audio, sound/music definitions, oracle audio calls and live mixing behavior. Recover usage classification, attenuation, concurrency, priority/stealing, randomness, delays, looping, fades, ducking, pause behavior, listener choice and music transitions. Bevy supplies mechanics, not these rules.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

7. Required behavior

Cues create/update ordinary Bevy playback entities. Positional effects follow their world transforms; UI/music are nonspatial. Usage volumes, loop points, priority, limits, pause and transitions follow recovered policy. Missing optional clips fail quietly with a typed diagnostic; required music/voice conversion fails preflight.

8. Performance contract

Mapped lookup/block selection/handle cloning allocate zero. Playback start and decoder creation are explicit bounded exceptional operations; settled playback must not allocate per frame after decoder/output buffers stabilize. Stream only bounded upcoming blocks. Track Rust heap, mapped faults, active Bevy sinks, decoder buffers, underruns, voices and start latency. There is no OpenZT2 mix buffer or full decoded-cache copy.

9. Acceptance criteria

Four files/API; resident/streamed conversion, corrupt range/loop rejection, zero-allocation mapped lookup, and Bevy fixtures for UI, positional, loop, pause, priority, fade/transition and cleanup. Source review must find no custom mixer, device backend, voice registry, or second playback state.

10. Required handoff

Report exact Bevy audio/codec features, mapped Decodable lifetime strategy, usage policy, A13 synchronization surface, root registrations, recovered audio rules, and allocation/fault/underrun measurements.

A12 — System Font Integration

Outcome and evidence boundary

The official archives and loose installation contain no TTF, OTF, FNT, bitmap font, or other authored font payload. The recovered executable creates fonts through the operating-system font API (D3DXCreateFontA). A12 therefore does not define a source frontend, native rkyv font family, pack entry, mapped asset, or custom glyph cache. Adding any of those would duplicate bytes that do not exist in the original content and would invent a mod contract without evidence.

OpenZT2 maps the recovered UI font aliases, sizes, weights, and fallback policy onto Bevy's ordinary text components and platform/system font integration. Bevy owns shaping, glyph caching, atlas allocation, layout, and rendering. G02 projects the compiled A02 text/style facts directly into Text, TextFont, and TextColor; it does not attach an OpenZT2 font wrapper component.

Runtime and performance contract

Font selection happens once when a native UI document is projected. Unchanged text must not be reshaped or reallocated by OpenZT2 per frame. Font-face heap, glyph-cache heap, and atlas GPU memory are backend-owned measurements, not mapped-pack residency. There is no source-font parsing or filesystem lookup in the game content path.

Mod boundary

The current content contract supports the fixed font vocabulary evidenced by the original game. A future native editor may deliberately introduce authored font assets, but that is a new OpenZT2 feature and must receive its own measured contract; preflight must not silently accept unevidenced original formats.

Acceptance criteria

A13 — Video stream

1. Identity and outcome

Preflight converts original movies into one seekable AV1 elementary stream with an OpenZT2 timestamp/keyframe index and an A11 audio reference. In the game, dav1d decodes into a bounded reusable picture pool and Bevy/wgpu presents those frames. OpenZT2 does not contain a video codec or generic media framework.

2. Exclusive ownership and mandatory dependencies

The package worker owns native/src/families/video.rs, converter/src/families/video.rs, game/src/assets/video.rs, and preflight/tests/video_contract.rs only. Root adds dav1d = 0.11 and its pinned native packaging, plus the approved FFmpeg/libav or rav1e preflight conversion dependency.

The preflight media dependency owns original container demux, source codec decode, scaling/colorspace conversion and AV1 encode. Dav1d owns runtime AV1 entropy/picture decode. Bevy/wgpu own textures and GPU copies. The worker must not write a codec, runtime container parser, general media graph, or per-frame texture allocator. A mandatory dependency failure is configured or narrowly forked before the bespoke baseline at 68a4c481 is considered.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
VIDEO_ARCHIVE_VERSION constant u32 = 1 Layout version.
VideoArchive root { version:u32, target:TargetProfile, id:AssetId, codec:VideoCodec, width:u32, height:u32, time_base:[u32;2], frame_count:u64, duration_ticks:u64, color:VideoColor, audio:AssetId, seek:Vec<VideoSeekBlock>, payload:Vec<u8> } Fixed dimensions and AV1 payload; no source container.
VideoCodec enum Av1 One runtime decoder contract.
VideoSeekBlock record { first_frame:u64, frame_count:u32, first_tick:u64, duration_ticks:u64, keyframe:bool, bytes:AssetRange } Sorted, bounded, packet-aligned range.
MappedVideoArchive owning view new(MappedAsset)->io::Result<Self> plus archived/block accessors Validated once, mapping-owned.
compile_video producer fn compile_video(input:&VideoSource,target:TargetProfile)->io::Result<Vec<u8>> Deterministic invocation of the approved media stack and native index writer.
VideoAsset Bevy asset { archive:MappedVideoArchive } Mapped packets.
VideoPlayer component { video:Handle<VideoAsset>, elapsed_ticks:u64, state:VideoPlaybackState, looped:bool, audio_entity:Option<Entity> } One playback entity.
VideoPlaybackState enum Playing, Paused, Finished Closed state.
VideoDecodePool private bounded resource fixed dav1d decoder slots, custom picture allocator surfaces, staging and GPU texture handles Capacity established before playback; never authoritative media state.
VideoAssetPlugin plugin unit struct Registers asset, decoder pool and focused start/decode/upload/sync/cleanup systems.

4. Data layout and ECS ownership

Seek metadata and AV1 packets remain mapped. Dav1d receives slices from the mapped block through an input owner that retains Arc<MappedPack>. Its custom picture allocator recycles aligned frame storage. GPU textures/staging buffers are created per pool slot and reused. VideoPlayer and A11's playback entity are the only live state; there is no media session graph.

5. Preflight producer contract

Accept only Bink (.bik), the movie format established by the executable's Bink imports and loader calls. Use the approved media dependency to demux/decode Bink, normalize dimensions, frame rate, pixel/color metadata and orientation, split audio into A11, encode AV1 with deterministic target settings, and emit packet/keyframe/timestamp blocks. Reject unsupported required source media, invalid timing, excessive dimensions, missing keyframes, A/V duration mismatch, or decoder-profile incompatibility. Never invoke media conversion in the game.

6. Behavioral evidence and disposition

Inspect Bink references, oracle Bink playback calls and live original for aspect/stretch, skippability, looping, end transitions, audio routing, pause/focus behavior and required seek accuracy.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

7. Required behavior

Players start only after asset/decoder readiness, follow authored timestamps, stay synchronized to A11, skip/loop/end exactly once, and release slots on despawn or world transition. Back-pressure drops or delays presentation by an explicit policy; it never grows the pool.

8. Performance contract

Mapped seek/block lookup allocates zero. Decoder initialization and pool setup are bounded exceptional work. After warm-up, decoding reuses dav1d picture allocations and GPU staging/textures with zero recurring Rust allocation caused by OpenZT2. Prefetch only bounded upcoming blocks. Track decoder heap, mapped faults, queued packets/pictures, upload bytes, GPU texture bytes, decode/upload latency and dropped frames independently.

9. Acceptance criteria

Four files/API; deterministic conversion fixture, corrupt index/timing/profile rejection, seek lookup, decoder-pool reuse, A/V synchronization, skip/loop/end, and GPU accounting. Source review must find no codec algorithm, runtime source container parser, unbounded frame queue, or per-frame texture creation.

10. Required handoff

Report pinned dav1d/native packaging, preflight media dependency/version, picture allocator and mapped-input lifetime, Bevy texture format/upload path, A11 synchronization, root registration and allocation/fault/GPU/frame results.

A14 — World Definitions

1. Identity and outcome

This package produces the immutable typed catalogues used by every world/gameplay package: placeables, facilities, staff/jobs, guests, topology, biomes/tools, health, information, progression, expansion systems, modes/camera, simulation timing and environment. It is deliberately a set of narrow tables, not a generic entity definition graph. Integration wave 1; depends on A09 and stable IDs from the presentation families.

2. Exclusive ownership

This package owns native/src/families/world_definitions.rs, converter/src/families/world_definitions.rs, game/src/assets/world_definitions.rs, and preflight/tests/world_definitions_contract.rs only. Everything else is read-only. Root owns module/asset/plugin registration and any shared-type moves.

3. Frozen public contract

Root and common construction catalogues

Symbol Kind Exact shape or signature Owner and invariant
WORLD_DEFINITIONS_VERSION constant u32 = 1 Bumped for incompatible table/vocabulary change.
WorldDefinitionsArchive root fields are the exact table list under §4 One immutable resolved catalogue; definition tables sorted by ID.
ObjectDefinition record { id:AssetId, kind:ObjectKind, name_key:AssetId, description_key:AssetId, prefab:AssetId, icon:AssetId, placeable:u32, price_cents:i64, upkeep_cents_per_day:i32, tags:ObjectTags, affordances:AffordanceFlags } placeable == u32::MAX when not placeable.
ObjectKind enum Scenery, Shelter, Food, Water, Enrichment, DonationBox, Bin, Bench, Facility, Fence, Gate, Path, Staff, Guest, Tank, ShowStage, Station, Vehicle, Laboratory Exact static classification.
ObjectTags bitflags u64 ANIMAL_USABLE, GUEST_USABLE, STAFF_ONLY, INDOOR, OUTDOOR, WATER_PLACEABLE, WALL_PLACEABLE, PATH_REQUIRED, DONATION_ACCEPTOR, VIEWABLE, DELETABLE, SAVE_RELEVANT No opaque tag bits. Unknown semantic source tag rejects conversion.
AffordanceFlags bitflags u64 EAT, DRINK, REST, PLAY, SHELTER, VIEW, BUY, DONATE, LEARN, CLEAN, REPAIR, TREAT, TRAIN, BOARD, DISEMBARK, OPERATE Fixed interaction capabilities.
PlaceableDefinition record { id:AssetId, footprint:TableRange, pivot_cm:[i16;2], rotation_increment_degrees:u16, constraints:PlacementConstraints, max_slope_permille:u16, min_clearance_cm:u16, price_cents:i64, unlock:AssetId, entrances:TableRange } Generates ArchivedPlaceableDefinition consumed directly by G10.
FootprintCellFlags bitflags u16 OCCUPIED, WALKABLE, ENTRANCE, WATER, FOUNDATION_REQUIRED Exact construction/topology meaning; no opaque source bits.
FootprintCell record { offset:[i16;2], flags:FootprintCellFlags } Canonical row-major local cell.
EntranceDefinition record { position_cm:[i16;3], forward_snorm:[i16;3], purpose:EntrancePurpose } Local docking point.
EntrancePurpose enum Guest, Staff, Service, Vehicle, Animal Fixed docking class.
PlacementConstraints bitflags u32 REQUIRE_PATH, REQUIRE_HABITAT, REQUIRE_WATER, REQUIRE_LAND, REQUIRE_WALL, REQUIRE_FLAT, ALLOW_OVERLAP_SCENERY, ALLOW_ELEVATED Ordered evaluation policy remains G10.
FacilityDefinition record { id:AssetId, object:AssetId, service:ServiceKind, capacity:u16, service_ticks:u32, price_cents:i32, staffing:StaffKind, inventory_capacity:u16, inventory_units_per_service:u16, inventory_restock_per_zoo_day:u16 } Static service facts; all inventory fields are zero for non-stocked services.
ServiceKind enum Food, Drink, Toilet, Gift, Education, Adoption, Transport, Maintenance, Show, Laboratory Fixed transaction vocabulary.
MaintenanceDefinition record { id:AssetId, object:AssetId, initial_condition_permille:u16, deterioration_per_zoo_day_permille:u16, repair_below_permille:u16, waste_capacity_units:u16, empty_at_units:u16, litter_definition:Option<AssetId>, litter_local_offset_cm:[i16;3], service_effects:TableRange } G36 facts only for evidenced maintainable/dirty objects; no synthesized defaults.
ServiceMaintenanceEffect record { service:AssetId, condition_loss_permille:u16, contained_waste_units:u16, loose_litter_units:u16 } Owner-contiguous range on MaintenanceDefinition; service ID is immutable G18 service data identity.
CleanlinessPolicy record { condition_weight:u16, waste_weight:u16, litter_weight:u16, litter_reference_units:u32 } Exactly one resolved G36/G23 integer aggregation policy; total weight nonzero.

Staff and guests

Symbol Kind Exact shape or signature Owner and invariant
StaffKind enum None, Keeper, Maintenance, Educator, Veterinarian, Entertainer Static role.
StaffJobKind enum Feed, RefillWater, CleanHabitat, EmptyBin, SweepLitter, Repair, Treat, Educate, Entertain, Tranquilize, Capture, MaintainTank, OperateShow Exact job vocabulary; unknown job blocks conversion.
StaffJobFlags bitflags u32 FEED, REFILL_WATER, CLEAN_HABITAT, EMPTY_BIN, SWEEP_LITTER, REPAIR, TREAT, EDUCATE, ENTERTAIN, TRANQUILIZE, CAPTURE, MAINTAIN_TANK, OPERATE_SHOW Capability mask with no opaque bits.
StaffDefinition record { id:AssetId, object:AssetId, role:StaffKind, wage_cents_per_day:i32, move_speed_mps:f32, permitted_jobs:StaffJobFlags, job_overrides:TableRange } Overrides index asset_links containing StaffJobDefinition IDs; finite speed.
StaffJobDefinition record { id:AssetId, kind:StaffJobKind, duration_ticks:u32, interaction_radius_cm:u16, capability:StaffJobFlags, effect:StaffJobEffect, priority:i16 } Immutable job duration/effect/capability.
StaffJobEffect enum Fill{amount:u16}, Clean{amount:u16}, Repair{amount:u16}, Treat{treatment:AssetId}, Educate{amount:u16}, Tranquilize{definition:AssetId}, Capture, Operate Fixed mutation request vocabulary.
GuestNeedKind enum Hunger, Thirst, Energy, Restroom, Comfort Exact G17 live-need vocabulary. Additional source need is an evidence/schema gate.
GuestDefinition record { id:AssetId, object:AssetId, starting_cash_cents:[i32;2], radius_cm:u16, move_speed_mps:f32, acceleration_mps2:f32, patience_ticks:u32, needs:TableRange, destination_weights:TableRange, reactions:TableRange, memory:GuestMemoryPolicy } Complete guest spawn/archetype defaults; navigation scalars are finite positive source values.
GuestArrivalDefinition record { id:AssetId, rules:TableRange, archetypes:TableRange } One preflight-resolved arrival policy selected by A19 MapRecord; rules and weighted archetypes are owner-contiguous.
GuestArrivalRule record { minimum_fame_half_stars:u8, maximum_admission_cents:i64, interval_ticks:u32, group_size:[u16;2], population_cap:u32 } Sorted most-specific-first; interval is nonzero G32 ticks and group bounds are ordered/nonzero.
GuestArchetypeWeight record { guest:AssetId, weight:u32 } guest resolves one GuestDefinition; each policy has positive total weight.
GuestNeedDefinition record { kind:GuestNeedKind, initial_permille:u16, decay_per_tick_q16:i32, reconsider_threshold:u16 } 0..=1000 values; signed 16.16 permille/G32 tick with residual-preserving application.
GuestVisitPurpose enum View, Food, Drink, Restroom, Rest, Education, Shop, Exit, Show, Tour G17 must use/translate this exact native vocabulary without duplicate string keys.
GuestDestinationWeight record { purpose:GuestVisitPurpose, weight:i16, need:GuestNeedKind, minimum_need:u16, radius_cm:u32 } Deterministic selection inputs.
GuestMemoryKind enum AnimalView, Facility, Education, Crowding, Litter, Scenery, Danger, Show, Tour Exact memory/reaction category.
GuestReactionDefinition record { kind:GuestMemoryKind, satisfaction_delta:i16, education_delta:i16, retention_ticks:u64 } Deltas are signed permille.
GUEST_MEMORY_CAPACITY constant usize; implementation defines a numeric literal recovered before schema implementation begins Evidence gate: worker first recovers it from complete shipped definitions/oracle; delivered Rust must contain the justified literal, never a placeholder or guess.
GuestMemoryPolicy record { capacity:u16, retention_ticks:u64, replacement:MemoryReplacement } capacity <= GUEST_MEMORY_CAPACITY; preflight rejects excess.
MemoryReplacement enum Oldest, LowestAbsoluteValue Fixed inline-ring replacement behavior recovered from evidence.

Topology, terrain and health

Symbol Kind Exact shape or signature Owner and invariant
TraversalMask bitflags u16 GUEST, STAFF, ANIMAL, VEHICLE, AIR, WATER Exact topology traversal classes.
FenceDefinition record { id:AssetId, object:AssetId, height_cm:u16, strength:u16, blocks:TraversalMask, gate:AssetId, post_prefab:AssetId } Static topology facts.
PathDefinition record { id:AssetId, object:AssetId, width_cm:u16, capacity:u16, speed_permille:u16, elevated:bool, support_prefab:AssetId } Path facts.
BiomeDefinition record { id:AssetId, name_key:AssetId, terrain_material:AssetId, cliff_material:AssetId, water_material:AssetId, foliage:TableRange, temperature_c:[i16;2], humidity_permille:[u16;2] } Foliage indexes asset_links.
BrushDefinition record { id:AssetId, radius_cm:[u16;2], strength_permille:u16, falloff:BrushFalloff, operation:BrushOperation } Editing policy.
BrushFalloff enum Constant, Linear, Smooth Fixed shape.
BrushOperation enum Raise, Lower, Flatten, Smooth, PaintBiome, AddWater, RemoveWater Fixed tool vocabulary.
DiseaseDefinition record { id:AssetId, eligible_species:TableRange, check_interval_ticks:u32, chance_per_check:u32, severity_per_tick_q16:i32, vitality_per_tick_q16:i32, symptoms:TableRange, treatments:TableRange, fatal_threshold:u16, hint_thresholds:[u16;3] } Probabilities use 0..=u32::MAX; rates are signed 16.16 permille/G32 tick; ranges index asset_links.
TreatmentDefinition record { id:AssetId, disease:AssetId, required_staff:StaffKind, duration_ticks:u32, severity_delta:i16, vitality_delta:i16, research:AssetId } Typed cure facts.
TranquilizerEligibility bitflags u8 ESCAPED, RAMPAGING, CONTAINED Exact target-state eligibility.
TranquilizerDefinition record { id:AssetId, duration_ticks:u32, range_cm:u32, eligible_states:TranquilizerEligibility, recovery_ticks:u32 } Fixed tranquilizer rule.
RampageRule record { id:AssetId, species:AssetId, welfare_below:u16, disease_above:u16, probability:u32, minimum_ticks:u32, behavior:AssetId } Changed-fact rule; no runtime guessed chance.

Information and progression

Symbol Kind Exact shape or signature Owner and invariant
CatalogueFilterFlags bitflags u16 PURCHASABLE, BUILDABLE, ADOPTABLE, HIREABLE, EXPANSION, MODDED, HIDDEN_UNTIL_UNLOCKED Exact native filter facts; current unlocked state is live G23 data.
CatalogueEntry record { id:AssetId, definition:AssetId, category:CatalogueCategory, order:u16, filters:CatalogueFilterFlags, name_key:AssetId, icon:AssetId } Stable sorted buy/information index.
CatalogueCategory enum Animals, Biomes, Scenery, Facilities, Fences, Paths, Staff, Tanks, Shows, Transport Complete fixed UI catalogue grouping; G21 consumes this owner.
ZoopediaEntry record { id:AssetId, subject:AssetId, title_key:AssetId, body_key:AssetId, image:AssetId, related:TableRange, order:u16 } Related indexes asset_links; no runtime source markup parse.
ResearchDefinition record { id:AssetId, name_key:AssetId, cost_cents:i64, duration_ticks:u64, prerequisites:TableRange, unlocks:TableRange } G32 ticks; ranges index asset_links; DAG.
UnlockDefinition record { id:AssetId, target:AssetId, requirement:UnlockRequirement } Static progression rule.
UnlockRequirement enum Always, Fame(u16), Research(AssetId), Scenario(AssetId), Award(AssetId) Fixed rule.
RatingDefinition record { id:AssetId, inputs:TableRange, minimum:u16, maximum:u16, smoothing_ticks:u32 } Inputs index rating_inputs.
RatingInput record { kind:RatingInputKind, weight:i16, minimum:i32, maximum:i32 } Fixed-point normalized contribution.
RatingInputKind enum AnimalWelfare, GuestSatisfaction, Education, Variety, Scenery, Finance, Cleanliness Fixed rating inputs.
FameThreshold record { level:u16, minimum_rating:u16, minimum_guests:u32, unlocks:TableRange } Ordered ascending; links index asset_links.
AwardDefinition record { id:AssetId, name_key:AssetId, description_key:AssetId, conditions:TableRange, reward_cents:i64, unlocks:TableRange } Conditions index award_conditions.
AwardCondition record { kind:RatingInputKind, comparison:Comparison, value:i32, duration_ticks:u64 } Typed threshold in G32 time.
Comparison enum AtLeast, AtMost, Equal Shared native comparison.

Expansion definitions

Symbol Kind Exact shape or signature Owner and invariant
TankDefinition record { id:AssetId, wall:AssetId, floor_material:AssetId, water_material:AssetId, min_depth_cm:u16, max_depth_cm:u16, capacity_litres_per_cell:u32, filtration_per_day:u32 } Static tank construction/care.
AquaticRequirement record { species:AssetId, min_depth_cm:u16, salinity_permille:[u16;2], temperature_c:[i16;2], water_quality_min:u16, land_fraction_permille:[u16;2] } One per aquatic species.
ShowStageDefinition record { id:AssetId, object:AssetId, performer_slots:u16, audience_capacity:u16, schedule_slots:u16, supported_tricks:TableRange, admission_cents:i32 } Trick links index asset_links.
TrickDefinition record { id:AssetId, species:TableRange, animation:AssetId, training_ticks:u32, difficulty:u16, welfare_cost:u16, entertainment:u16 } Species links index asset_links.
ShowRuleDefinition record { id:AssetId, minimum_tricks:u16, duration_ticks:[u32;2], cooldown_ticks:u32, payout_per_guest_cents:i32 } Fixed show execution facts.
StationDefinition record { id:AssetId, object:AssetId, track:AssetId, dock_offset_cm:[i16;3], capacity:u16, dwell_ticks:u32 } Transport docking.
TrackDefinition record { id:AssetId, object:AssetId, vehicle:AssetId, width_cm:u16, min_radius_cm:u16, max_grade_permille:u16, speed_cm_per_tick:u16 } Track construction/kinematics.
VehicleDefinition record { id:AssetId, object:AssetId, seats:u16, speed_cm_per_tick:u16, boarding_ticks:u32, running_cost_cents_per_day:i32 } Vehicle facts.
TourViewDefinition record { id:AssetId, subject:AssetId, radius_cm:u32, base_score:i16, dwell_ticks:u32, occlusion_required:bool } Authored tour scoring.
FossilSetDefinition record { id:AssetId, species:AssetId, pieces:TableRange, completion_unlock:AssetId } Pieces index asset_links.
FossilPieceDefinition record { id:AssetId, set:AssetId, model:AssetId, slot:AssetId, discovery_weight:u16 } Weighted discovery.
FossilSlotDefinition record { id:AssetId, transform:[[f32;4];4], tolerance_cm:u16, tolerance_degrees:u16 } Assembly target.
CloneLabDefinition record { id:AssetId, object:AssetId, process_ticks:u32, cost_cents:i64, success_probability:u32, required_research:AssetId, spawn_offset_cm:[i16;3] } Cloning contract; successful animal placement is relative to the lab and validated inside its G09 habitat.

Modes, time and environment

Symbol Kind Exact shape or signature Owner and invariant
ModeKind enum GuestView, FirstPerson, Training, FossilSearch, FossilAssembly, Cloning, Photo, ShowEdit Exact G31 mode list.
ModeActionFlags bitflags u32 MOVE_FORWARD, MOVE_BACK, STRAFE_LEFT, STRAFE_RIGHT, LOOK, PRIMARY, SECONDARY, CONFIRM, CANCEL, ZOOM, ROTATE Native authored mode actions translated by G31/G03, not Bevy-linked.
ModeToolFlags bitflags u8 PAUSE_SIMULATION, HIDE_HUD, LOCK_SUBJECT, RETURN_CAMERA_ON_EXIT Exact mode lifecycle policy; every source flag is corpus-evidenced.
ModeToolDefinition record { id:AssetId, mode:ModeKind, prefab:AssetId, cursor:AssetId, camera:AssetId, allowed_actions:ModeActionFlags, flags:ModeToolFlags } Fixed native action eligibility/lifecycle.
CameraTuningDefinition record { id:AssetId, projection:CameraProjectionKind, fov_y_radians:f32, near_m:f32, far_m:f32, offset_m:[f32;3], pitch_radians:[f32;2], yaw_radians:[f32;2], zoom_m:[f32;2], collision_radius_cm:u16 } Finite evidenced camera profile.
CameraProjectionKind enum Perspective, Orthographic G06/G31 use exact identity.
SimulationTimingDefinition record { fixed_hz:u16, ticks_per_day:u32, speed_multipliers:TableRange, calendar:CalendarPolicy } Exactly one root value; all numbers evidence-gated.
SpeedMultiplier record { numerator:u16, denominator:u16 } Ordered speed tier; positive reduced rational.
CalendarPolicy record { epoch_year:u16, epoch_month:u8, epoch_day:u8, month_lengths:[u8;12], leap:LeapPolicy } Valid calendar used by G32.
LeapPolicy enum None, Gregorian No guessed policy.
EnvironmentDefinition record { id:AssetId, light:TableRange, fog:TableRange, sky:TableRange, weather:TableRange, transitions:TableRange, ambient:TableRange, initial_weather:AssetId, wind_mps:[f32;2] } Light/fog/sky/transitions index typed owner-grouped tables; weather/ambient index asset_links so ID tables remain searchable.
LightKeyframe record { environment:AssetId, day_fraction:u16, direction_snorm:[i16;3], color_unorm:[u16;3], illuminance_lux:f32 } Sorted fraction 0..=65535.
FogKeyframe record { environment:AssetId, day_fraction:u16, color_unorm:[u16;3], start_cm:u32, end_cm:u32 } Sorted, end >= start.
SkyKeyframe record { environment:AssetId, day_fraction:u16, texture:AssetId, tint_unorm:[u16;4], rotation_snorm:i16 } Sorted, resolved texture.
WeatherDefinition record { id:AssetId, environment:AssetId, duration_ticks:[u32;2], wind_mps:[f32;2], light_multiplier:u16, fog_multiplier:u16, audio:AssetId, effect:AssetId, welfare_delta:i16 } Duration/ranges valid; gameplay delta only if evidenced.
WeatherTransitionRule record { environment:AssetId, from:AssetId, to:AssetId, probability:u32, transition_ticks:u32, day_fraction:[u16;2] } Probability is 0..=u32::MAX.
AmbientClass enum Air, Ground, Water Exact G33 class.
AmbientSpawnDefinition record { id:AssetId, environment:AssetId, prefabs:TableRange, biomes:TableRange, class:AmbientClass, day_fraction:[u16;2], population:[u16;2], spawn_interval_ticks:[u32;2], radius_cm:[u32;2], lifetime_ticks:[u32;2], probability:u32, audio:AssetId, effect:AssetId } Prefab/biome links index asset_links; all bounds ordered.

Mapped/producer/Bevy surface

Symbol Kind Exact shape or signature Owner and invariant
MappedWorldDefinitions / access_world_definitions / access_world_definitions_validated owning view/functions tuple wrapper; new(MappedAsset)->io::Result<Self>; archived(&self)->&ArchivedWorldDefinitionsArchive; one typed find_<table>(&self,AssetId)->Option<&Archived<Record>> per ID table including find_maintenance_by_object; timing()->&ArchivedSimulationTimingDefinition; cleanliness_policy()->&ArchivedCleanlinessPolicy; fn access_world_definitions(&[u8])->io::Result<&ArchivedWorldDefinitionsArchive>; unsafe fn access_world_definitions_validated(&[u8])->&ArchivedWorldDefinitionsArchive Validate once, own mapping.
DefinitionSource input { virtual_path:String, bytes:Vec<u8>, family:DefinitionFamily } Resolved preflight source.
DefinitionFamily enum Object, Placeable, Facility, Maintenance, Staff, StaffJob, Guest, Fence, Path, Biome, Brush, Disease, Treatment, Tranquilizer, Rampage, Catalogue, Zoopedia, Research, Unlock, Rating, Fame, Award, Tank, AquaticRequirement, ShowStage, Trick, ShowRule, Station, Track, Vehicle, TourView, FossilSet, FossilPiece, FossilSlot, CloneLab, ModeTool, Camera, SimulationTiming, Environment, Light, Fog, Sky, Weather, WeatherTransition, AmbientSpawn Preflight routing only; not archived runtime polymorphism.
compile_world_definitions producer fn compile_world_definitions(inputs:&[DefinitionSource], resolve:impl Fn(&str)->Option<AssetId>, target:TargetProfile)->io::Result<Vec<u8>> Pure deterministic producer.
WorldDefinitionsAsset Bevy asset { archive:MappedWorldDefinitions } deriving Asset, TypePath, Clone Sole mapped catalogue owner.
WorldDefinitionsLoader loader { profile:Arc<MappedPack> }; new(Arc<MappedPack>)->Self; AssetLoader<Asset=WorldDefinitionsAsset> for ozdefs via path + get_owned No reader copy/source objects.
WorldDefinitionsAssetPlugin plugin unit struct Registers asset/loader only.

4. Data layout or ECS ownership

WorldDefinitionsArchive has this exact root shape and field order:

pub struct WorldDefinitionsArchive {
    version: u32,
    target: TargetProfile,
    objects: Vec<ObjectDefinition>,
    placeables: Vec<PlaceableDefinition>,
    footprint_cells: Vec<FootprintCell>,
    entrances: Vec<EntranceDefinition>,
    facilities: Vec<FacilityDefinition>,
    maintenance_definitions: Vec<MaintenanceDefinition>,
    service_maintenance_effects: Vec<ServiceMaintenanceEffect>,
    cleanliness_policy: CleanlinessPolicy,
    staff: Vec<StaffDefinition>,
    staff_jobs: Vec<StaffJobDefinition>,
    guests: Vec<GuestDefinition>,
    guest_arrivals: Vec<GuestArrivalDefinition>,
    guest_arrival_rules: Vec<GuestArrivalRule>,
    guest_archetype_weights: Vec<GuestArchetypeWeight>,
    guest_needs: Vec<GuestNeedDefinition>,
    guest_destination_weights: Vec<GuestDestinationWeight>,
    guest_reactions: Vec<GuestReactionDefinition>,
    fences: Vec<FenceDefinition>,
    paths: Vec<PathDefinition>,
    biomes: Vec<BiomeDefinition>,
    brushes: Vec<BrushDefinition>,
    diseases: Vec<DiseaseDefinition>,
    treatments: Vec<TreatmentDefinition>,
    tranquilizers: Vec<TranquilizerDefinition>,
    rampage_rules: Vec<RampageRule>,
    catalogue: Vec<CatalogueEntry>,
    zoopedia: Vec<ZoopediaEntry>,
    research: Vec<ResearchDefinition>,
    unlocks: Vec<UnlockDefinition>,
    rating_definitions: Vec<RatingDefinition>,
    rating_inputs: Vec<RatingInput>,
    fame_thresholds: Vec<FameThreshold>,
    awards: Vec<AwardDefinition>,
    award_conditions: Vec<AwardCondition>,
    tanks: Vec<TankDefinition>,
    aquatic_requirements: Vec<AquaticRequirement>,
    show_stages: Vec<ShowStageDefinition>,
    tricks: Vec<TrickDefinition>,
    show_rules: Vec<ShowRuleDefinition>,
    stations: Vec<StationDefinition>,
    tracks: Vec<TrackDefinition>,
    vehicles: Vec<VehicleDefinition>,
    tour_views: Vec<TourViewDefinition>,
    fossil_sets: Vec<FossilSetDefinition>,
    fossil_pieces: Vec<FossilPieceDefinition>,
    fossil_slots: Vec<FossilSlotDefinition>,
    clone_labs: Vec<CloneLabDefinition>,
    mode_tools: Vec<ModeToolDefinition>,
    cameras: Vec<CameraTuningDefinition>,
    simulation_timing: SimulationTimingDefinition,
    speed_multipliers: Vec<SpeedMultiplier>,
    light_keyframes: Vec<LightKeyframe>,
    fog_keyframes: Vec<FogKeyframe>,
    sky_keyframes: Vec<SkyKeyframe>,
    weather: Vec<WeatherDefinition>,
    weather_transitions: Vec<WeatherTransitionRule>,
    ambient_spawns: Vec<AmbientSpawnDefinition>,
    environments: Vec<EnvironmentDefinition>,
    asset_links: Vec<AssetId>,
}

Every TableRange addresses its declared typed table or asset_links; byte AssetRange is unused. Tables are rkyv, immutable, little-endian target data. ID lookup tables are sorted by ID; child tables addressed only by TableRange are grouped by owner and then authored order. catalogue is instead the stable category/order definition index required by G21/G23. Live cash, jobs, memories, diseases, progress, modes, weather and clocks remain small ECS facts in natural gameplay packages. Static definitions are never copied into components/resources.

5. Preflight producer contract

Accept the resolved entity/unit/object/fence/path/biome/AI/disease/research/ expansion/mode/camera/time/environment XML and data families. Normalize canonical paths and aliases before AssetId; flatten inheritance/templates and selected pack precedence; map every behavior-bearing property into the exact typed table; convert authored simulation durations to G32 ticks and continuous guest/disease rates to signed Q16 per tick before writing records; close dependencies and sort deterministically. Integer *_cents_per_day fields are deliberate calendar charges applied exactly once on G32 ZooDayAdvanced, not continuous rates and require no runtime division. filtration_per_day is likewise an exact integer daily tank-care capacity consumed on that event, not a continuous float rate. Preflight derives the stable catalogue/Zoopedia index, guest inline-memory bound, fixed timing/calendar, environment curves and ambient eligibility. Reject unknown semantic properties/enums, opaque flag bits, missing required prefab/name/assets, duplicate IDs, cycles, nonfinite or unordered ranges, table overflow and any guest definition exceeding GUEST_MEMORY_CAPACITY. Guest-arrival conversion also rejects empty policies, zero or overflowing weight totals, invalid guest IDs, overlapping rules without recovered precedence, zero intervals, unordered group bounds, absence of a terminal catch-all rule, and a policy not referenced by any selected A19 map. The producer worker must resolve the memory-capacity literal before implementing/serializing the schema; it may not deliver a placeholder. Diagnostics include family, source path and property. Conversion never emits a generic fallback record. Maintenance conversion additionally rejects permille overflow, zero capacity with nonzero waste rules, empty_at_units > waste_capacity_units, duplicate service IDs within one owner, loose litter without a resolved litter definition, zero total cleanliness weight, and a zero litter reference with nonzero litter weight. Its exact daily cadence/effects/thresholds are evidence gates from shipped definitions/oracle, never defaulted by the producer.

6. Behavioral evidence and disposition

Search all selected shipped entities, units, objects, AI/task, disease, biome, research, expansion, camera/mode, environment/light/weather/ambient and catalog/Zoopedia sources. Search oracle typed-object/entity/type registry, placement/facility/staff/guest/disease/progression/tank/show/transport/fossil/ camera/time/environment classes and analysis TSV domains. Mine commit d5657e5a only for already-found source keys/formulas in corresponding domain modules. Live observation settles presentation and timing where source/oracle do not.

Evidence gates include the complete definition and flag vocabularies, units, guest need/reaction/memory retention and the numeric GUEST_MEMORY_CAPACITY, staff job effects/durations, disease/rampage formulas, catalogue ordering, rating/fame/award formulas, expansion rules, camera values, fixed frequency, ticks/day, speed tiers/calendar, light/fog/sky interpolation, weather transitions and ambient bounds. Workers must recover these; familiar defaults are forbidden.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

7. Required behavior

Every player-selectable/static gameplay concept resolves to exactly one narrow record and required dependencies. Source inheritance and overrides are absent at runtime. G10 directly receives ArchivedPlaceableDefinition; G16/G17/G20/G21/ G23/G25–G27/G29/G31–G33 use the named mapped records above. Guest memories use inline fixed-capacity G17 storage and definition capacity/retention. G32 is the only live clock; environments and expansion state are ordinary entities. Unknown mod semantics fail conversion until assigned a typed owner and schema revision.

8. Performance contract

Validation is O(total records+links); each typed ID lookup O(log n); range traversal O(len), all zero allocation. Stable catalog indexes avoid runtime sorting/hashing. No per-frame catalogue clone/string/property lookup. Consumers use changed filters, fixed schedules and bounded spatial indexes. Regression tests count zero allocations for every typed lookup and representative range walk, track mapped pages/faults, and attribute referenced GPU bytes to A04–A08.

9. Acceptance criteria

The four files export every symbol and exact root field above. Fixtures cover at least one valid record from every table, inheritance/override flattening, cross-family dependencies, timing/environment links and guest memory bounds. Tests reject cycles, dangling links, unknown semantic flags/need/job/mode, nonfinite/bad ranges and excess guest memory; mapped typed access is allocation-free. No live gameplay, source registry, generic value bag, stubs or unresolved silent defaults are delivered.

10. Required handoff

Report files/API, parser dependencies, module/asset/loader/plugin registration, the recovered numeric memory/timing/calendar constants, exact downstream symbol alignment, evidence still unresolved and allocator/mapping checks. Root may move genuinely shared enums into the frozen native contract and must remove any temporary family router after integration; no engine adapter is retained.

A15 — Scene Prefab

1. Identity and outcome

This package produces flat, directly iterable entity/component spawn records for scenery, facilities, animals, staff, guests and map dressing. Source scene graphs are flattened; G04 creates ordinary ECS entities without a prefab VM or reflected property bag. Integration wave 1; depends on A04, A07 and A14.

2. Exclusive ownership

This package owns native/src/families/scene_prefab.rs, converter/src/families/scene_prefab.rs, game/src/assets/scene_prefab.rs, and preflight/tests/scene_prefab_contract.rs only.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
SCENE_PREFAB_VERSION constant u32 = 1 Layout version.
ScenePrefabArchive root { version:u32, target:TargetProfile, id:AssetId, strings:Vec<u8>, entities:Vec<PrefabEntity>, children:Vec<u32>, renderables:Vec<PrefabRenderable>, material_overrides:Vec<MaterialOverride>, definitions:Vec<PrefabDefinition>, colliders:Vec<PrefabCollider>, attachments:Vec<PrefabAttachment>, dependencies:Vec<AssetId> } Entities parent-before-child; typed tables sorted by entity.
PrefabEntityFlags bitflags u8 VISIBLE, ACTIVE, SAVE_RELEVANT, STATIC Exact spawn defaults.
PrefabEntity record { stable_id:AssetId, parent:u32, transform:PrefabTransform, children:TableRange, flags:PrefabEntityFlags } Parent u32::MAX is root; stable ID unique in prefab.
PrefabTransform POD { translation_m:[f32;3], rotation_xyzw:[f32;4], scale:[f32;3] } Finite; normalized quaternion; scale nonzero.
VisibilityFlags bitflags u8 VISIBLE, CAST_SHADOW, RECEIVE_SHADOW, REFLECTION_VISIBLE Exact authored render participation.
PrefabRenderable record { entity:u32, model:AssetId, material_overrides:TableRange, visibility:VisibilityFlags } Overrides index root material_overrides:Vec<MaterialOverride> added to archive.
MaterialOverride record { slot:u16, material:AssetId } Slots unique per renderable.
PrefabDefinition record { entity:u32, definition:AssetId, role:PrefabRole } Links A14 typed definition; at most one per entity.
PrefabRole enum Object, Facility, Staff, Guest, Animal, FencePart, PathPart, Decoration Spawn classification only.
CollisionLayerFlags bitflags u16 WORLD, ANIMAL, GUEST, STAFF, PROJECTILE, WATER, PLACEMENT Exact collision membership/filter vocabulary.
PrefabCollider record { entity:u32, source:ColliderSource, layer:CollisionLayerFlags, mask:CollisionLayerFlags } Static collider description.
ColliderSource enum Model{model:AssetId,index:u16}, Box{half_extent_m:[f32;3]}, Capsule{radius_m:f32,half_height_m:f32} No runtime source parsing.
PrefabAttachment record { entity:u32, target_name:StringRange, target_entity:u32 } Resolved target entity; name retained for diagnostics/animation attachment.
MappedScenePrefab / access_scene_prefab / access_scene_prefab_validated owning view/functions tuple wrapper; new(MappedAsset)->io::Result<Self>; archived(&self)->&ArchivedScenePrefabArchive; entity_components(u32) returns borrowed typed ranges; fn access_scene_prefab(&[u8])->io::Result<&ArchivedScenePrefabArchive>; unsafe fn access_scene_prefab_validated(&[u8])->&ArchivedScenePrefabArchive Validate once, own mapping.
SceneSource input { virtual_path:String, bytes:Vec<u8>, format:SceneSourceFormat } Resolved NIF/BFB/XML scene/prefab.
SceneSourceFormat enum Nif, Bfb, EntityXml, MapObjects Preflight only.
compile_scene_prefab producer fn compile_scene_prefab(input:&SceneSource, resolve:impl Fn(&str)->Option<AssetId>, target:TargetProfile)->io::Result<Vec<u8>> Pure flattening producer.
ScenePrefabAsset Bevy asset { archive:MappedScenePrefab } Mapped spawn rows.
ScenePrefabLoader loader { profile:Arc<MappedPack> }; new(Arc<MappedPack>)->Self; AssetLoader<Asset=ScenePrefabAsset> for ozscene via path + get_owned No reader copy/object graph.
ScenePrefabAssetPlugin plugin unit struct Registers asset/loader only.

4. Data layout or ECS ownership

All relation spans use frozen TableRange; strings use StringRange; byte AssetRange is not needed. Tables are read directly from rkyv. A prefab is immutable construction data. Spawned entities receive only natural G04/Gxx components and retain asset handles/IDs; there is no persistent PrefabEntityBag or scene-tree runtime.

5. Preflight producer contract

Accept resolved scene/entity/map-object documents and model hierarchies. Normalize prefab and stable child keys. Resolve templates/inheritance, compose transforms, flatten parent hierarchy, produce stable child IDs, map recognized source properties to typed A14 definition links, resolve model/material/collision and attachment dependencies, and discard editor-only nodes only when proven nonsemantic. Reject cycles, duplicate stable IDs, nonfinite transforms, unresolved attachment/required assets, invalid material slots and unknown behavior-bearing components. Deterministic preorder and per-entity table order.

6. Behavioral evidence and disposition

Search shipped object/entity XML, NIF/BFB scene nodes and map object lists; oracle entity/type/component/world placement classes; analysis RTTI/function domains; commit d5657e5a model parser, world/templates/startup and placement sources. Questions: coordinate transforms, node visibility flags, attachment semantics, component inheritance, collision source, editor-only nodes and stable identity across save/load. Compare representative facility/animal/scenery spawns.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

7. Required behavior

One archive traversal gives G04 all entities and exact typed component rows. Parent/child and attachments cleanly resolve; deleting a spawned root lets ECS despawn its hierarchy. Source inheritance is absent at runtime. Unsupported semantic component data fails conversion and is assigned to a natural package before extending the schema. Mods compile to identical rows.

8. Performance contract

Validation/spawn traversal O(entities+components), zero allocation in archived iteration; entity creation necessarily allocates ECS storage only during spawn. Repeated steady frames do no prefab work. Spawn batches reserve entity/component capacity from table lengths. Track mapped pages, spawn heap/entity counts and A04/A07 GPU resources separately.

9. Acceptance criteria

Four files/API; fixture with hierarchy, renderable override, definition, collider/attachment; reject cycle/dangling/invalid transform. Archived traversal allocates zero and exposes exact counts for reservation. Non-goals: ECS spawn systems, terrain and live component state.

10. Required handoff

Report parser dependencies, root registrations, A04/A07/A14 links, exact G04 spawn component mapping, unknown source components/evidence and mapping/spawn/GPU validation. Root should inline any package-only spawn adapter.

A16 — Streamable Terrain

1. Identity and outcome

This package produces streamable terrain chunks with quantized heights, biome blends, water, collision samples and GPU-upload-ready mesh/texture payloads. G05 edits live terrain from this immutable base without reparsing maps or rebuilding the entire world. Integration wave 1; depends on A05, A07 and A14.

2. Exclusive ownership

This package owns native/src/families/terrain.rs, converter/src/families/terrain.rs, game/src/assets/terrain.rs, preflight/tests/terrain_contract.rs only.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
TERRAIN_ARCHIVE_VERSION constant u32 = 1 Layout version.
TerrainArchive root { version:u32, target:TargetProfile, id:AssetId, cell_size_cm:u16, chunk_cells:u16, grid_chunks:[u32;2], origin_cm:[i32;2], height_scale_mm:u32, height_bias_mm:i32, chunks:Vec<TerrainChunkRecord>, biome_layers:Vec<BiomeLayer>, water:Vec<WaterRegion>, water_chunks:Vec<u32>, payload:Vec<u8>, dependencies:Vec<AssetId> } Chunks row-major and complete.
TerrainChunkFlags bitflags u8 HAS_WATER, HAS_CLIFF, EDITABLE, STREAMING_BORDER Exact chunk facts.
TerrainChunkRecord record { coordinate:[u32;2], bounds:TerrainBounds, heights:AssetRange, blend:AssetRange, vertices:AssetRange, indices:AssetRange, collision:AssetRange, biome_layers:TableRange, flags:TerrainChunkFlags } Archived definition; distinct from G05 ECS TerrainChunk.
TerrainBounds record { min_m:[f32;3], max_m:[f32;3] } Finite world bounds.
BiomeLayer record { chunk:u32, biome:AssetId, channel:u8, weight_scale:u8, reserved:u16 } Max four active channels per blend texel unless target profile states eight.
WaterFlags bitflags u8 SWIMMABLE, TANK, OCEAN, REFLECTIVE Exact water behavior.
WaterRegion record { id:AssetId, chunks:TableRange, surface_height_mm:i32, material:AssetId, flags:WaterFlags } chunks indexes root water_chunks.
HeightSample value contract little-endian u16; metres = (sample*height_scale_mm+height_bias_mm)/1000 Direct payload cast after alignment validation.
TerrainVertex POD contract { position:[f32;3], normal_packed:u32, uv:[f32;2], blend_uv:[f32;2] } Exact GPU vertex payload.
MappedTerrainArchive / access_terrain / access_terrain_validated owning view/functions tuple wrapper; new(MappedAsset)->io::Result<Self>; archived(&self)->&ArchivedTerrainArchive; chunk(&self,[u32;2])->Option<&ArchivedTerrainChunkRecord>; payload slice accessors; fn access_terrain(&[u8])->io::Result<&ArchivedTerrainArchive>; unsafe fn access_terrain_validated(&[u8])->&ArchivedTerrainArchive O(1), validate once, own mapping.
TerrainSource input { virtual_path:String, bytes:Vec<u8>, format:TerrainSourceFormat } Original map/terrain data.
TerrainSourceFormat enum OriginalMap, HeightField, EditorTerrain Preflight only.
compile_terrain producer fn compile_terrain(input:&TerrainSource, resolve:impl Fn(&str)->Option<AssetId>, target:TargetProfile)->io::Result<Vec<u8>> Pure chunk/derived-data producer.
TerrainAsset Bevy asset { archive:MappedTerrainArchive } Mapping owner.
TerrainAssetLoader loader { profile:Arc<MappedPack> }; new(Arc<MappedPack>)->Self; AssetLoader<Asset=TerrainAsset> for ozterrain via path + get_owned No reader copy/decode/retiling.
TerrainAssetPlugin plugin unit struct Registers the mapped asset and loader only; G05 owns terrain presentation, preparation, eviction and GPU accounting.

4. Data layout or ECS ownership

rkyv metadata is followed by aligned payload ranges: u16 height grids, R8/RGBA8 biome blend blocks, TerrainVertex, u32 indices and compact collision samples. Byte ranges use AssetRange; record links use TableRange. Chunks map directly. G05 prepares these exact ranges into its persistent TerrainGpuAsset and owns dirty-span extraction and partial GPU writes. Live edits never mutate or copy the archive into a second base terrain graph.

5. Preflight producer contract

Accept original terrain/map records, biome paint, water and referenced material/ texture definitions after override resolution. Normalize ID, recover dimensions, orientation and units, chunk with overlap required for normals, quantize heights within target error, bake mesh normals/indices/blends/collision and water chunk links, resolve biomes/materials. Reject unknown record layout, truncated grids, NaN, invalid dimensions, quantization beyond tolerance, >profile biome channels, dangling dependencies and nonmanifold derived mesh. Deterministic row-major chunks and layer ordering.

6. Behavioral evidence and disposition

Search shipped map/terrain files and biome XML/textures; oracle BFTerrain*, terrain data/mesh/composite/water/brush classes; byte-range map and function domains; commit d5657e5a simulation/terrain.rs, placement and save terrain. Questions: grid record layout, cell/height scale, axis orientation, texture tile scale (avoid tiny repetition), normal/water rules, cliff blending, chunk seams, collision precision and brush effects. Validate representative maps live.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

7. Required behavior

Base surface geometry, texture scale/blends, water height and collision match the original. Adjacent chunks are seam-free. Runtime may stream/fault chunks and G05 may apply transactional edits, but archive bytes stay immutable. Missing biome assets fail. Mods/maps compile through the same record contract. Terrain editing authentic constants are recovered in G05/A14, not guessed here.

8. Performance contract

Chunk lookup O(1), payload slicing zero allocation; validation O(chunks+payload ranges). G05 consumes mapped ranges through persistent resources and reusable staging with no recurring allocation after capacity. Residency is bounded by visible/preload policy. This package tracks mapped/resident pages and faults; G05 tracks edit heap, exact terrain GPU bytes and chunk upload time.

9. Acceptance criteria

Four files/API; multi-chunk sloped/biome/water fixture, seam and height decode checks, corrupt range/alignment/dependency rejection, zero-allocation lookup/ traversal. Non-goals: live brush commands/camera/culling systems.

10. Required handoff

Report format/parser dependencies, TerrainVertex placement/root registration, A05/A07/A14 IDs, G05/G17 residency consumers, unresolved grid/scale evidence and mapping/edit/GPU measurements.

A17 — Navigation

1. Identity and outcome

This package produces precomputed navigation tiles, compact graphs, portals and spatial lookup ranges for walkers/vehicles. Runtime pathfinding traverses mapped records and bounded scratch; it never rebuilds a source nav world. Integration wave 1; depends on A15 and A16.

2. Exclusive ownership

This package owns native/src/families/navigation.rs, converter/src/families/navigation.rs, game/src/assets/navigation.rs, and preflight/tests/navigation_contract.rs only.

This package is deliberately bespoke. bevy_landmass is rejected because its owned validated navmesh/Archipelago model would duplicate the final mapped A17 graph and synchronize agent state with G14. oxidized_navigation is rejected in the game because runtime tile generation is preflight work. A17 emits the final borrowed graph representation; it must not retain a library nav world. A small algorithm crate is acceptable only after root proves it operates directly on borrowed mapped slices without retained graph state.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
NAVIGATION_ARCHIVE_VERSION constant u32 = 1 Layout version.
NavigationArchive root { version:u32, target:TargetProfile, id:AssetId, origin_cm:[i32;2], tile_size_cm:u32, grid:[u32;2], tiles:Vec<NavTile>, nodes:Vec<NavNode>, edges:Vec<NavEdge>, portals:Vec<NavPortal>, cells:Vec<NavCell>, cell_nodes:Vec<u32> } Tiles/cells row-major; nodes stable index.
NavTile record { coordinate:[u32;2], nodes:TableRange, edges:TableRange, portals:TableRange, bounds_cm:[i32;4] } Ranges valid and tile-local rows contiguous.
NavNode record { position_cm:[i32;3], edges:TableRange, clearance_cm:u16, flags:NavFlags } Edge range references outgoing edges.
NavEdgeFlags bitflags u8 ONE_WAY, DISABLED_BY_DEFAULT, PREFERRED Exact edge policy.
NavEdge record { to:u32, cost_mm:u32, width_cm:u16, traversal:TraversalKind, flags:NavEdgeFlags } Destination valid; positive cost.
TraversalKind enum Ground, Path, Gate, Ramp, Stair, Swim, Vehicle Fixed routing capability.
NavFlags bit flags u16: GUEST, STAFF, ANIMAL, VEHICLE, ONE_WAY, DISABLED_BY_DEFAULT Static eligibility.
NavPortalFlags bitflags u8 ONE_WAY, GATE_CONTROLLED, ELEVATED Exact portal policy.
NavPortal record { from_tile:u32, to_tile:u32, from_node:u32, to_node:u32, width_cm:u16, flags:NavPortalFlags } Symmetry/one-way validated.
NavCell record { nodes:TableRange } Range indexes cell_nodes; fixed spatial bucket.
MappedNavigationArchive / access_navigation / access_navigation_validated owning view/functions tuple wrapper; new(MappedAsset)->io::Result<Self>; archived(&self)->&ArchivedNavigationArchive; tile(&self,[u32;2])->Option<&ArchivedNavTile>; nearest_candidates(&self,[i32;3])->&[u32]; fn access_navigation(&[u8])->io::Result<&ArchivedNavigationArchive>; unsafe fn access_navigation_validated(&[u8])->&ArchivedNavigationArchive O(1) range access, validate once, own mapping.
NavigationSource input { virtual_path:String, terrain:AssetId, scenes:Vec<AssetId>, authored:Option<Vec<u8>> } Resolved geometry/authored nav data.
compile_navigation producer fn compile_navigation(input:&NavigationSource, terrain:&ArchivedTerrainArchive, prefabs:impl Fn(AssetId)->io::Result<MappedScenePrefab>, target:TargetProfile)->io::Result<Vec<u8>> Pure derived graph producer.
NavigationAsset Bevy asset { archive:MappedNavigationArchive } Mapped graph.
NavigationAssetLoader loader { profile:Arc<MappedPack> }; new(Arc<MappedPack>)->Self; AssetLoader<Asset=NavigationAsset> for oznav via path + get_owned No reader copy/graph reconstruction.
NavigationAssetPlugin plugin unit struct Registers asset/loader only.

4. Data layout or ECS ownership

rkyv fixed records and frozen TableRange adjacency/spatial spans. Centimetre/mm integer coordinates/costs make graph traversal deterministic. Runtime dynamic closures/reservations are sparse ECS/topology overlays keyed by node/edge, not a copy of the graph. A* scratch is caller-owned reusable arrays/heap capacity.

5. Preflight producer contract

Consume A16 terrain collision/water plus A15 static obstacles, gates/paths and any evidenced authored nav records. Normalize ID, rasterize/walk-test target agent profiles, build nodes/edges/portals/clearance/spatial cells, remove unreachable duplicates and deterministically order by tile/position. Reject missing source assets, invalid geometry, asymmetric accidental links, zero cost, out-of-range indexes and target graphs exceeding u32. Pack precedence is already resolved for inputs.

6. Behavioral evidence and disposition

Search shipped path/fence/gate/terrain definitions and map nav records; oracle navigation/pathfinding/influence/path classes and function domains; commit d5657e5a AI navigation manager contracts, pathing, path/fence/transport sources. Questions: grid/node scale, path preference/cost formula, animal versus guest clearance, diagonal/corner rules, gates/portals, swimming, elevated paths and vehicle networks. Live-check route shape/stuck behavior.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

7. Required behavior

Eligible agents find routes consistent with terrain/path/gate restrictions; unreachable requests fail explicitly. Dynamic topology toggles overlay static flags and invalidates only affected routes. No full graph clone per agent. Mods alter inputs before nav build. Authentic cost/clearance semantics must be recovered before path behavior is called done.

8. Performance contract

Tile/cell lookup O(1); adjacency O(out-degree); A* O((V+E)log V) over bounded candidate region using preallocated scratch and zero steady allocations. Changed topology invalidation is event-driven. Count mapped pages, scratch heap capacity and path query time/expanded nodes; no GPU ownership.

9. Acceptance criteria

Four files/API; two-tile fixture with portal, blocked edge and traversal profiles; reject corrupt references/costs; prove lookup/route after warm scratch allocates zero. Non-goals: live steering/reservations and topology editing systems.

10. Required handoff

Report geometry/build dependencies, A15/A16 inputs, root registration, G08/G14 overlay/path API needs, unresolved routing evidence and mapping/scratch/timing validation.

A18 — GPU-ready effects

1. Identity and outcome

Preflight converts original particle/effect controllers into compact, bounded, Hanabi-facing effect records. In the game, bevy_hanabi performs particle storage, GPU simulation, compute dispatch, extraction, and rendering; OpenZT2 entities only select an authored effect and carry zoo-clock/lifecycle policy. Integration wave 1; depends on A04–A08.

2. Exclusive ownership and mandatory dependency

The package worker owns native/src/families/effect.rs, converter/src/families/effect.rs, game/src/assets/effect.rs, and preflight/tests/effect_contract.rs only. Root adds bevy_hanabi = 0.19 with minimal 3D features and registers its plugin.

Hanabi exclusively owns particle buffers, spawning execution, per-particle simulation, GPU compute pipelines, batching, culling, and particle rendering. This package must not implement CPU particle arrays, a GPU particle pool, emitter simulation kernels, particle extraction, or a second effect renderer. Configuration or a narrow Hanabi fork precedes any bespoke fallback; the deleted custom design remains only at commit 68a4c481.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
EFFECT_ARCHIVE_VERSION constant u32 = 1 Native layout version.
EffectArchive root { version:u32, target:TargetProfile, id:AssetId, emitters:Vec<HanabiEmitterRecord>, modifiers:Vec<HanabiModifierRecord>, curves:Vec<EffectCurvePoint>, dependencies:Vec<AssetId> } Deterministic flat configuration; no live particles.
HanabiEmitterRecord record { id:AssetId, capacity:u32, spawn:SpawnPolicy, simulation:EffectClock, shape:EmitterShape, modifiers:TableRange, material:AssetId, mesh:AssetId } Every value is convertible without source interpretation to one Hanabi effect configuration.
SpawnPolicy enum Rate{per_second:f32}, Burst{count:u32}, Once Closed preflight-validated spawning vocabulary.
EffectClock enum Presentation, ZooSimulation Selects real-time or G32-driven spawning/lifetime semantics.
EmitterShape enum Point, Sphere{radius:f32}, Box{half_extents:[f32;3]}, Circle{radius:f32} Supported Hanabi-facing source shapes.
HanabiModifierRecord enum InitialPosition, InitialVelocity, Acceleration, Drag, ColorOverLifetime, SizeOverLifetime, KillSphere, OrientToVelocity with typed payloads Fixed recovered subset; unknown visible source modules fail preflight.
MappedEffectArchive owning view new(MappedAsset)->io::Result<Self> and archived(&self)->&ArchivedEffectArchive Validated once; keeps mapping alive.
compile_effect producer fn compile_effect(input:&EffectSource,target:TargetProfile)->io::Result<Vec<u8>> Pure source-to-native conversion.
EffectAsset Bevy asset { archive:MappedEffectArchive } Mapped OpenZT2 configuration.
EffectInstance component { effect:Handle<EffectAsset>, emitter:AssetId, clock:EffectClock } One live authored effect on an ordinary entity.
EffectAssetPlugin plugin unit struct Registers mapped asset loading and one-time Hanabi preparation/integration only.

The Bevy bridge creates/caches one Hanabi EffectAsset per native emitter during asset preparation. This one-time small configuration construction is analogous to GPU resource preparation and is not a second content graph. Live instances use Hanabi's normal ParticleEffect/spawner components directly rather than an OpenZT2 wrapper hierarchy.

4. Data layout and ECS ownership

Archived tables remain mapped. Texture, mesh, material, curve, and modifier references use stable IDs/ranges. Hanabi owns every live particle and GPU byte. OpenZT2 owns only mapped authoring facts, the entity lifecycle relation, clock selection, and conversion of gameplay effect requests into ordinary Hanabi components.

5. Preflight producer contract

Resolve NIF particle controllers, BFB/effect XML, materials, meshes, textures, curves, and overrides. Convert supported source operations into the closed Hanabi-facing record vocabulary, normalize units/coordinates, derive safe capacities, and sort deterministically. Reject recursive triggers, missing dependencies, invalid curves/capacities, unsupported visible modules, or a target profile without required compute/render support. The game never parses a Hanabi text/serde format.

6. Behavioral evidence and disposition

Search shipped effects and particle controllers, Gamebryo particle identities, effect/material documents, the C++ oracle and live original. Recover spawn timing, lifetime, shapes, forces, collision/kill behavior, curves, clock source, overflow policy, blending, alignment, and attachment semantics.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

7. Required behavior

Effects start, stop, loop, attach, detach, and clean up according to authored facts. Zoo-clock effects pause and change speed with G32; presentation effects remain real-time. Capacity overflow follows an explicit recovered policy. Missing dependencies or unsupported modules fail preflight rather than silently changing the visible effect.

8. Performance contract

Mapped lookup and archive traversal allocate zero. Hanabi configuration creation occurs once during preparation; settled instances allocate no Rust heap after capacity stabilizes. Particle simulation/rendering stays GPU-side and is batched/culling-aware. Track native mapping, Hanabi asset/config heap, particle GPU buffers, dispatch duration, instance counts and overflow. A settled effects scene must pass the frame allocation ceiling without an OpenZT2 particle pool.

9. Acceptance criteria

Four files/API, archive corruption tests, source conversion fixtures, and live fixtures for rate/burst, curves, attachment, both clocks, pause/speed, cleanup, capacity and GPU accounting. Source inspection must show no custom particle simulation, compute shader, pool, extraction pipeline, or renderer.

10. Required handoff

Report minimal Hanabi features/version, root plugin order, native-record to Hanabi preparation mapping, A04–A08 dependencies, unsupported source modules, clock integration, and allocator/GPU/dispatch measurements.

A19 — Worlds, Scenarios and Imported Starts

1. Identity and outcome

This package produces immutable map catalogues, starting-zoo snapshots, campaign/challenge metadata and fixed scenario objective/action programs. Original map/save import exists only here in preflight; G01/G04/G24 consume native records immediately. Integration wave 4; depends on A14–A17.

2. Exclusive ownership

This package owns native/src/families/world_scenario.rs, converter/src/families/world_scenario.rs, game/src/assets/world_scenario.rs, and preflight/tests/world_scenario_contract.rs only.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
WORLD_SCENARIO_VERSION constant u32 = 1 Layout/objective vocabulary version.
WorldScenarioArchive root { version:u32, target:TargetProfile, strings:Vec<u8>, maps:Vec<MapRecord>, starts:Vec<StartingZooRecord>, spawn_entities:Vec<SpawnEntityRecord>, spawn_values:Vec<SpawnValue>, campaigns:Vec<CampaignRecord>, scenario_links:Vec<AssetId>, scenarios:Vec<ScenarioRecord>, challenge_policies:Vec<ChallengeOfferPolicy>, objectives:Vec<ObjectiveRecord>, scenario_rules:Vec<ScenarioRule>, rule_links:Vec<u32>, actions:Vec<ScenarioActionRecord>, photo_scoring:Vec<PhotoScoringDefinition>, photo_challenges:Vec<PhotoChallengeRule>, photo_subject_links:Vec<AssetId>, dependencies:Vec<AssetId> } ID tables sorted; program ranges bounded; action table index is the stable completion correlation.
MapRecord record { id:AssetId, name_key:AssetId, description_key:AssetId, thumbnail:AssetId, globe_marker:[i16;2], environment:AssetId, camera:AssetId, guest_arrivals:AssetId, terrain:AssetId, navigation:AssetId, scene:AssetId, start:AssetId, modes:GameModeFlags } environment, camera, and guest_arrivals name exactly one A14 definition each; complete closure.
GameModeFlags bit flags u16: FREEFORM, CHALLENGE, CAMPAIGN, TUTORIAL Shell availability.
StartingZooRecord record { id:AssetId, profile:AssetId, map:AssetId, cash_cents:i64, admission_cents:i64, fame_half_stars:u8, start_tick:u64, absolute_day:u32, calendar:[u16;3], imported_seed:Option<u64>, entities:TableRange, values:TableRange } profile is player profile identity (zero only for an unbound template); calendar is [year,month,day].
SpawnFlags bitflags u8 ACTIVE, VISIBLE, SAVE_RELEVANT, PLAYER_OWNED Exact imported/start state.
SpawnEntityRecord record { persistent_id:u64, prefab:AssetId, definition:AssetId, transform:PrefabTransform, parent:u32, flags:SpawnFlags } Immutable initial spawn; nonzero world-local G04 ID is unique, and parent is an earlier record index or u32::MAX. Preflight deterministically assigns IDs from canonical source order after override resolution.
SpawnValue enum Name{entity:u32,text:StringRange}, Condition{entity:u32,permille:u16}, CashRegister{entity:u32,cents:i64}, Research{item:AssetId,progress:u16} entity is an index into this start's SpawnEntityRecord range; content definitions remain AssetId. Root also has strings:Vec<u8>.
CampaignRecord record { id:AssetId, name_key:AssetId, description_key:AssetId, icon:AssetId, scenarios:TableRange, order:u16 } Range indexes scenario_links.
ScenarioFlags bitflags u8 TUTORIAL, CHALLENGE, REPEATABLE, ALLOW_PAUSE, HIDDEN_UNTIL_UNLOCKED Exact scenario policy.
ScenarioRecord record { id:AssetId, map:AssetId, start:AssetId, name_key:AssetId, description_key:AssetId, objectives:TableRange, start_actions:TableRange, success_actions:TableRange, failure_actions:TableRange, time_limit_ticks:u64, flags:ScenarioFlags } Time is G32 ticks; ranges index typed tables.
ChallengeOfferPolicy record { id:AssetId, scenario:AssetId, eligible_maps:TableRange, prerequisites:TableRange, minimum_fame_half_stars:u8, first_offer_ticks:u64, repeat_interval_ticks:u64, response_ticks:u64, weight:u32 } Ranges index scenario_links; all durations/weight are positive. Preflight resolves original challenge generation/eligibility into this fixed policy rather than runtime scripts.
ObjectiveRecord record { id:AssetId, text_key:AssetId, rule_index:u32, prerequisite_objectives:TableRange, success_actions:TableRange, failure_actions:TableRange, hidden:bool } Rule indexes scenario_rules; prerequisite IDs use scenario_links.
ScenarioRuleKind enum All, Any, Not, CashCents, FameHalfStars, ZooRatingPermille, SpeciesCount, AnimalCount, GuestCount, ObjectCount, ResearchComplete, ElapsedTicks, SpeciesWelfarePermille, AnimalBirthCount, AnimalAdoptionCount, AnimalReleaseCount, SuccessfulShowCount, PhotoScoreMilli, PhotoSubjectCount, PhotoChallengeComplete Exact stable G24 evaluator vocabulary; event-count variants consume their natural typed lifecycle/result messages, never a generic authored-event counter. Full corpus must compile to these variants or block schema acceptance.
ScenarioRule record { id:AssetId, kind:ScenarioRuleKind, subject:AssetId, comparison:crate::families::world_definitions::Comparison, target:i64, children:TableRange } Children index rule_links; units are cents, half-stars, 0..=1000 permille, counts, ticks or score thousandths by kind.
ScenarioAction enum GrantCashCents(i64), Unlock(AssetId), ShowMessage(AssetId), SetAdmissionCents(i64), Spawn{prefab:AssetId,definition:AssetId}, SetWeather(AssetId), Complete, Fail Fixed requests executed once by natural owners. AssetId identifies data targets only; there is no generic event action.
ScenarioActionCompletion enum Immediate, EconomyCommit, ProgressionCommit, SpawnCommit, WeatherCommit, PresentationCommit Immediate: SetAdmissionCents/Complete/Fail; EconomyCommit: GrantCashCents; ProgressionCommit: Unlock; SpawnCommit: Spawn; WeatherCommit: SetWeather; PresentationCommit: ShowMessage. Preflight rejects every other pair.
ScenarioActionRecord record { action:ScenarioAction, completion:ScenarioActionCompletion } Its root table index is immutable for the loaded scenario and identifies one apply attempt/result.
ScenarioActionKey native value #[repr(C)] { scenario:AssetId, action_index:u32 } G24 and every natural-owner request/completion carry this exact key; no success may satisfy another action.
PHOTO_SUBJECT_CAPACITY constant usize literal recovered from complete shipped/oracle evidence before schema implementation begins The delivered Rust constant must be a numeric literal justified in handoff; a placeholder or guess is not accepted.
PhotoScoringDefinition record { id:AssetId, subject:AssetId, max_subjects:u16, minimum_screen_permille:u16, base_score_milli:i32, center_weight:i16, size_weight:i16, facing_weight:i16, behavior_weight:i16, occlusion_required:bool } max_subjects <= PHOTO_SUBJECT_CAPACITY; score integer thousandths.
PhotoChallengeRule record { id:AssetId, scoring:AssetId, required_subjects:TableRange, minimum_score_milli:i32 } Subject links index photo_subject_links; G24 evaluates completion through the existing typed photo score/subject-count rules.
MappedWorldScenarioArchive / access_world_scenario / access_world_scenario_validated owning view/functions tuple wrapper; new(MappedAsset)->io::Result<Self>; archived(&self)->&ArchivedWorldScenarioArchive; typed find_map/find_start/find_scenario(AssetId)->Option<&Archived...>; fn access_world_scenario(&[u8])->io::Result<&ArchivedWorldScenarioArchive>; unsafe fn access_world_scenario_validated(&[u8])->&ArchivedWorldScenarioArchive Binary lookup, validate once, own mapping.
WorldScenarioSource input { virtual_path:String, bytes:Vec<u8>, format:WorldScenarioFormat } Map/scenario/campaign/original save source.
WorldScenarioFormat enum Map, ScenarioXml, CampaignXml, OriginalSave Preflight only.
compile_world_scenarios producer fn compile_world_scenarios(inputs:&[WorldScenarioSource], resolve:impl Fn(&str)->Option<AssetId>, timing:&ArchivedSimulationTimingDefinition, target:TargetProfile)->io::Result<Vec<u8>> Pure import/compilation; authored simulation durations become G32 ticks preflight.
WorldScenarioAsset Bevy asset { archive:MappedWorldScenarioArchive } Mapped catalogue.
WorldScenarioLoader loader { profile:Arc<MappedPack> }; new(Arc<MappedPack>)->Self; AssetLoader<Asset=WorldScenarioAsset> for ozworlds via path + get_owned No reader copy/original parser.
WorldScenarioAssetPlugin plugin unit struct Registers asset/loader only.

4. Data layout or ECS ownership

The exact root declaration is the WorldScenarioArchive shape above. All variable record spans use frozen TableRange; text uses StringRange. Maps reference A14–A17 handles. Objective records are immutable typed programs; live counters, rule state and current scenario belong to G24 components/resources, not this asset. Every action range indexes ScenarioActionRecord. G24 queues an action by ScenarioActionKey, sends that same key to the natural owner selected by ScenarioActionCompletion, and marks the index applied only after the matching success acknowledgement (Immediate completes synchronously). Rejection never counts as application. ScenarioResult is emitted only after every required terminal action index reaches its correlated terminal state.

5. Preflight producer contract

Accept resolved map/starting-zoo/scenario/campaign XML and original save/map formats. Normalize canonical IDs, apply source precedence, parse original binary only here, resolve referenced definitions/prefabs/terrain/nav/localization, compile scenario commands/tests into ScenarioRuleKind/typed actions, compile photo rules, and convert mutable initial values plus time/calendar/seed into closed spawn rows and sort deterministically. Reject unsupported original state/commands, zero/duplicate assigned persistent IDs, non-earlier parent indexes, spawn-value indexes outside their owning start range, dangling assets, invalid transforms, rule/objective cycles/ranges, invalid timing/calendar, challenge policies with unknown scenarios/maps/prerequisites, zero timing/weight or impossible eligibility, photo capacity overflow, impossible counters and map/start mismatches. Diagnostics name source rule/command. Later-layer semantics are fully resolved before output. Assign the canonical completion kind to every action, reject invalid action/completion pairs and preserve table indexes across deterministic output. Before implementation, inventory every scenario rule/action in the full corpus and oracle. Any additional semantic operation becomes an explicitly named enum variant with only its genuine typed payload. Event IDs, string opcodes, catch-alls and a generic runtime dispatcher are forbidden.

6. Behavioral evidence and disposition

Search shipped maps, freeform/challenge/campaign/scenario XML and original saves; oracle BF/ZT scenario managers/rules/actions/map entries and save/map readers; analysis function domains/byte ranges; commit d5657e5a simulation/scenario.rs, interaction/shell_content.rs, persistence/save.rs, scenario startup. Questions: complete command/test vocabulary/units, globe coordinate projection, campaign ordering, starting money/time/calendar/seed, rule visibility/transitions, photo scoring/capacity, challenge selection/delay, save record units/relationships and map availability. Live-check shell and representative objective progression.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

7. Required behavior

G01 can enumerate maps/campaigns/markers with no file access and select an exact map/start/scenario. G04 spawns stable parent-before-child initial entities. G24 matches ScenarioRuleKind exhaustively, converts G18 cents and G23 half-stars/ normalized ratings to frozen integer units, and issues actions exactly once. Unknown original rule/action blocks conversion. Imported original saves preserve every supported player-visible fact and explicitly diagnose unsupported facts; native ongoing saves are G22. Mods extend only the fixed vocabulary.

8. Performance contract

Catalogue lookup O(log n), starting spawn/objective/rule/challenge/photo traversal O(range), all zero-allocation. Scenario evaluation is event/changed-fact driven in G24, not a full archive scan per frame. Spawn reserves ECS capacity. Track mapped pages, spawn heap/entity counts and referenced terrain/model GPU bytes separately; allocator tests cover map/scenario lookup and traversal.

9. Acceptance criteria

Four files/API; fixtures cover freeform map/start, ordered campaign, objective typed rule tree/actions, weighted challenge-offer policy, photo scoring/challenge and an original-import subset; each action fixture carries its canonical completion kind and a simulated out-of-order acknowledgement proves only the matching ScenarioActionKey completes. Reject unknown command, dangling parent/dependency and bad ranges. Mapped lookup/traversal zero allocation. Non-goals: live shell UI, ECS spawning, native save writer and objective systems.

10. Required handoff

Report original parser dependencies, module/loader registration, A14–A17/A09 links, G01/G04/G22/G24 API mapping, unsupported save/rule evidence and allocation/ mapping/spawn/GPU validation. Root must remove any import adapter from engine dependency closure.

G01 — Source-driven game shell

1. Identity and outcome

The player reaches the authentic splash and main menu, chooses a profile and freeform, challenge, or campaign play, sees the source-defined globe and world markers, selects a map, and enters GamePhase::Loading. The shell owns only this application flow; UI projection belongs to G02 and world creation to G04. Integration wave 2. Prerequisites: A02, A09, A19, G02, G03 and G22's focused native-profile index/operation contract. G34 owns display/graphics settings; A11 owns audio settings and G03 owns input bindings.

2. Exclusive ownership

The worker owns game/src/plugins/shell/ and no other path. Crate declarations, plugin registration, state initialization and ordering are root handoffs.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
ShellPlugin Plugin pub struct ShellPlugin; Registers shell messages and systems; owns no state outside ECS.
PlayMode enum Freeform \| Challenge \| Campaign Selected product mode, not a source token.
ShellScreen component enum Splash \| MainMenu \| ProfileSelect \| Options \| Globe { mode: PlayMode } \| MapSelect { mode: PlayMode } Exactly one shell root bears the current screen.
ProfileChoice component pub struct ProfileChoice(pub AssetId); Stable native profile identity; never a positional slot.
WorldChoice component pub struct WorldChoice { pub scenario: AssetId, pub mode: PlayMode } Attached to a selectable marker/card; ID resolves in A19.
ShellSelection resource pub struct ShellSelection { pub profile: Option<AssetId>, pub mode: Option<PlayMode>, pub scenario: Option<AssetId> } Sole bounded cross-screen selection.
ShellCommand message enum ShowMainMenu \| ShowProfiles \| ShowOptions \| CreateProfile(String) \| ChooseProfile(AssetId) \| DeleteProfile(AssetId) \| ChooseMode(PlayMode) \| ChooseWorld(AssetId) \| StartSelected \| Back \| Quit Fixed semantic commands; the bounded player-entered profile name is the only owned string.
SplashFinished message pub struct SplashFinished; Source-defined splash sequence finished or authentic skip succeeded.

Root contract requests: A02 exposes UiDocumentRole semantic discovery, A19 must expose WorldScenarioAsset and allocation-free AssetId lookup, G02 must expose ShowUiRole/UiCommandActivated, G22 owns ProfileIndex and every profile request/result named below, and G04 owns BeginWorldLoad/WorldLoadFinished/WorldLoadFailed. G01 consumes or writes those exact G04 messages and must not redeclare them. G34 applies display and graphics settings; G01 owns only navigation into/out of the options document.

4. ECS ownership

ShellSelection is genuinely global bounded navigation state. ShellScreen, ProfileChoice, and WorldChoice are facts on projected UI entities.

System Schedule/set Exact query and parameters Writes Run condition/order
enter_boot_splash OnEnter(GamePhase::Boot) Commands, MessageWriter<ShowUiRole> one splash screen and UiDocumentRole::Splash request mapped bootstrap assets ready
finish_boot_splash Update/GameSet::Intent MessageReader<SplashFinished>, Query<&ShellScreen> NextState<GamePhase::MainMenu> current screen is splash
enter_shell_phase OnEnter(GamePhase::MainMenu) Commands, MessageWriter<ShowUiRole> one main-menu screen and UiDocumentRole::MainMenu after splash
translate_shell_ui_commands Update/GameSet::Intent MessageReader<UiCommandActivated>, Query<Option<&WorldChoice>>, Query<Option<&ProfileChoice>>, MessageWriter<ShellCommand> typed shell commands maps only A02 shell commands/arguments; after G02
route_shell_commands Update/GameSet::Intent MessageReader<ShellCommand>, ResMut<ShellSelection>, Query<&WorldChoice> selection, NextState<GamePhase>, BeginWorldLoad, AppExit in menu phases; ordered after G03/G02 input
request_profile_operations Update/GameSet::Intent MessageReader<ShellCommand>, MessageWriter<LoadProfileIndex>, MessageWriter<CreateProfile>, MessageWriter<SelectProfile>, MessageWriter<DeleteProfile> exact G22 requests profile commands only; storage mutation remains G22
show_profile_choices Update/GameSet::Ui Commands, MessageReader<ProfileIndexReady>, Res<ProfileIndex>, MessageWriter<ShowUiRole> UiDocumentRole::ProfileSelect plus ProfileChoice entities sorted G22 records only
finish_profile_operations Update/GameSet::Intent MessageReader<ProfileCreated>, MessageReader<ProfileSelected>, MessageReader<ProfileDeleted>, MessageReader<ProfileOperationFailed>, ResMut<ShellSelection> selected ID and source-defined success/error UI requests only after durable G22 result; never optimistic
show_options_screen Update/GameSet::Intent Commands, MessageReader<ShellCommand>, Query<Entity,With<ShellScreen>>, MessageWriter<ShowUiRole> despawns the current shell root, creates ShellScreen::Options and requests UiDocumentRole::Options for it ShowOptions only; settings commands remain with G34/A11/G03
show_globe_choices OnEnter(GamePhase::MapSelection) Commands, Res<ShellSelection>, Res<Assets<WorldScenarioAsset>>, MessageWriter<ShowUiRole> globe/map role and source-defined marker entities mode must be selected
finish_world_load Update/GameSet::Intent MessageReader<WorldLoadFinished>, Res<ShellSelection> NextState<GamePhase::InGame> only matching pending scenario
clear_main_menu_entities OnExit(GamePhase::MainMenu) Commands, Query<Entity,With<ShellScreen>> matching screen teardown before next phase
clear_map_selection_entities OnExit(GamePhase::MapSelection) Commands, Query<Entity,With<ShellScreen>> matching screen teardown before loading

5. Preflight producer contract

Not applicable: the engine consumes only native assets produced before launch.

6. Behavioral evidence and disposition

Start with shipped ui/ shell XML/images, globe/map/campaign/challenge records, localization keys and profiles; search the oracle and analysis TSVs for ZTMainMenu, ZTWorldSelect, campaign, challenge, freeform, globe and profile functions. Inspect commit d5657e5a only in app/launch.rs, features/core/app_flow.rs, interaction/menu.rs, ui/native.rs and world selection code. Live observation settles splash duration/skipping, back-stack, marker layout, disabled choices and transition timing.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

Evidence questions: exact startup screen order; profile-slot limits; whether each product mode shares the globe; world-marker selection/default/focus rules; and original loading failure presentation. Resolve from shipped documents and live behavior, not guessed timeouts or hardcoded marker positions.

7. Required behavior

Every visible item/marker comes from A02/A19/G22 data. StartSelected is ignored unless profile, mode and scenario are all present and compatible. Back returns one authentic level without retaining hidden screen trees. A matching load completion enters the game; stale completions cannot. Missing/corrupt native assets produce an explicit disabled/error UI state, never fake success. Controller navigation is through G03/G02. The options screen presents G34/A11/ G03 settings and returns through the authentic shell back flow; G01 never owns their live values or application. OpenZT2 adds no new shell rule here.

8. Performance contract

No filesystem access, source parsing, string construction, asset-payload clone, or per-frame scan of scenarios. Screen entry may allocate Bevy UI entities; settled shell frames and command routing allocate zero. Marker creation is O(visible worlds) once; command routing is O(messages). Allocation tests cover idle shell and a command without transition; mapped-page counters cover A02/A19 faults, and shell adds no GPU bytes beyond referenced UI assets.

9. Acceptance criteria

Deliver ordinary formatted Rust under the owned directory exporting every item above. A deterministic system test must prove invalid start is rejected and a valid selection emits one BeginWorldLoad. No placeholder screens, guessed coordinates, todo!, silent stubs, or source-format dependency. Bulk handoff: integrate with A02/A11/A19/G02/G03/G04/G34 and verify the complete navigable shell.

10. Required handoff

Report files/API; dependencies/features; root module/plugin/message/state/order registrations; consumed A02/A19/G02/G03 and requested G04 symbols; unresolved evidence; G34/A11/G03 options seams; seams to erase; and expected allocation, mapping, GPU and navigation validation.

G02 — Native game UI

1. Identity and outcome

Compiled A02 UI documents become ordinary Bevy UI entities with authored layout, style, focus, bindings and fixed commands. Modded documents work because preflight compiles the same fixed vocabulary; XML never enters the game. Integration wave 2. Prerequisites: A02, A05, A09 and A12.

2. Exclusive ownership

The worker owns game/src/plugins/ui/ only. Root owns module/plugin, asset-loader, reflection and schedule registration.

Bevy 0.19 UI is the exclusive layout/widget/picking/focus implementation. Use its ordinary nodes, flex/grid engine, clipping, scrolling, interactions and automatic directional navigation directly. G02 owns document projection and Zoo Tycoon bindings/commands only; no custom layout pass, widget hierarchy, focus graph, UI scene, or egui/third-party UI world is permitted.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
GameUiPlugin Plugin pub struct GameUiPlugin; Registers projection, interaction, focus and teardown.
UiDocumentRoot component pub struct UiDocumentRoot { pub document: Handle<UiDocumentAsset> } One projected tree owns one A02 handle.
UiNodeId component pub struct UiNodeId { pub index: u32, pub id: AssetId } Exact A02 record index and stable authored ID.
UiDocumentOwner component pub struct UiDocumentOwner(pub Entity); On every projected node; points to its UiDocumentRoot without walking parents.
UiCommandRange component pub struct UiCommandRange(pub TableRange); Complete validated archived command range; multiple triggers, no record copies.
UiVisibleBinding component pub struct UiVisibleBinding(pub BoolBindingSource); Exact A02 UiBinding::Visible source only.
UiEnabledBinding component pub struct UiEnabledBinding(pub BoolBindingSource); Exact A02 UiBinding::Enabled source only.
UiTextBinding component pub struct UiTextBinding(pub TextBindingSource); Exact A02 text source only.
UiImageBinding component pub struct UiImageBinding(pub ImageBindingSource); Exact A02 image source only.
UiValueBinding component pub struct UiValueBinding(pub ScalarBindingSource); Exact A02 scalar-value source only.
UiValue component pub struct UiValue(pub i64); Current slider/progress integer in the exact unit encoded by its source variant.
UiMinimum / UiMaximum components pub struct UiMinimum(pub i64); pub struct UiMaximum(pub i64); Authored integer slider bounds; no generic numeric property.
UiSelectedBinding component pub struct UiSelectedBinding(pub BoolBindingSource); Exact A02 selected-state source only.
UiSelected component pub struct UiSelected(pub bool); Current toggle/list selection fact.
UiFocusScope component pub struct UiFocusScope { pub focused: Option<Entity>, pub default: Option<Entity> } Per-screen focus, never a global mirrored UI tree.
UiFocusable component pub struct UiFocusable { pub order: u32, pub enabled: bool } Authored deterministic focus order within nearest scope.
UiCommandActivated message pub struct UiCommandActivated { pub command: UiCommand, pub source: ActionSource, pub node: Entity } Copies the exact typed enum payload from the mapped record and emits once on a confirmed enabled target.
ShowUiDocument message pub struct ShowUiDocument { pub document: Handle<UiDocumentAsset>, pub owner: Entity } Requests one projection parented to an existing owner.
ShowUiRole message pub struct ShowUiRole { pub role: UiDocumentRole, pub owner: Entity } Resolves A02's closed semantic role to its fixed native virtual path; no source path, winning asset instance or registry is retained in the engine.
HideUiDocument message pub struct HideUiDocument { pub owner: Entity } Despawns only roots projected for that owner.
SetUiNodeEnabled message pub struct SetUiNodeEnabled { pub root: Entity, pub node: u32, pub enabled: bool } Event-driven enable state change; node index is validated.
ShowNotification message pub struct ShowNotification { pub operation: Entity, pub message: AssetId } G24 requests presentation of one localized notification key.
NotificationPresented message pub struct NotificationPresented { pub operation: Entity } Emitted after the notification UI entity is projected successfully.
NotificationRejected message pub struct NotificationRejected { pub operation: Entity, pub reason: NotificationFailure } Typed no-fake-success result.
NotificationFailure enum MissingLocalization(AssetId) \| MissingNotificationRole \| ProjectionFailed Exact presentation failures; no string reason.
PendingNotification component pub struct PendingNotification { pub operation: Entity, pub message: AssetId } Temporary owner fact removed after projection acknowledgement/rejection.
UiDocumentProjectionFailed message pub struct UiDocumentProjectionFailed { pub owner: Entity, pub reason: UiProjectionFailure } Exact role-load/project failure correlated to the requesting owner.
UiProjectionFailure enum MissingRoleAsset \| AssetLoadFailed \| InvalidDocument Closed projection failures used by notifications and shell error presentation.

A02 freezes UiDocumentAsset, UiCommand, UiBinding and its typed source enums, archived node/style records, zero-copy indexed accessors, and UiDocumentRole::native_asset_path(self) -> &'static str. Preflight writes the single precedence-resolved document for every supported role at that semantic native virtual path. Do not invent local command strings, JSON-like values, widget traits, a DOM, a binding property bag, or a role-to-handle store. Projection exhaustively converts each UiBinding variant into the corresponding typed component above once. A05's stable native-ID Handle<Image> is the sole bridge to Bevy ImageNode, while a retained Handle<TextureAsset> owns its mapped source through preparation; A12 resolves the recovered system-font policy into ordinary Bevy TextFont beside Text.

4. ECS ownership

All live UI is Bevy Node, Text, ImageNode, interaction and small marker components. Only per-screen focus is retained.

System Schedule/set Exact query and parameters Writes Run condition/order
project_ui_documents Update/GameSet::Ui Commands, MessageReader<ShowUiDocument>, Res<Assets<UiDocumentAsset>>, Res<AssetServer>, Res<ContentMap> Bevy UI tree, exhaustive typed binding components, ImageNode using A05's native-ID image handle plus its mapped texture source handle, and ordinary TextFont using A12 system-font policy only loaded validated assets; before interaction; no custom image/font renderer
resolve_ui_roles Update/GameSet::Ui MessageReader<ShowUiRole>, Res<AssetServer>, MessageWriter<ShowUiDocument>, MessageWriter<UiDocumentProjectionFailed> asset_server.load(role.native_asset_path()) and ShowUiDocument, or correlated failure before projection; role mapping returns a static native path and allocates no constructed path
apply_ui_enable_changes Update/GameSet::Ui MessageReader<SetUiNodeEnabled>, Query<(&UiNodeId,&mut UiFocusable,&mut Interaction)> enabled/interaction state before focus and activation
pointer_activation Update/GameSet::Ui Res<Assets<UiDocumentAsset>>, Query<(Entity,&Interaction,&UiCommandRange,&UiFocusable,&UiDocumentOwner),Changed<Interaction>>, Query<&UiDocumentRoot>, MessageWriter<UiCommandActivated> one message per matching archived Press record enabled pressed nodes
navigate_focus Update/GameSet::Ui MessageReader<ActionRequest>, Query<(Entity,&UiFocusable,&InheritedVisibility)>, Query<&mut UiFocusScope> scope focus navigation actions; after G03
activate_focus Update/GameSet::Ui MessageReader<ActionRequest>, Res<Assets<UiDocumentAsset>>, Query<(&UiFocusScope,)>, Query<(Entity,&UiCommandRange,&UiFocusable,&UiDocumentOwner)>, Query<&UiDocumentRoot> matching archived command activations Confirm only
present_focus Update/GameSet::Presentation Query<(&UiFocusScope,), Changed<UiFocusScope>>, Query<&mut BackgroundColor,With<UiFocusable>> focus visual state after navigation
hide_ui_documents Update/GameSet::Ui Commands, MessageReader<HideUiDocument>, Query<(Entity,&ChildOf),With<UiDocumentRoot>> recursive despawn before new projection for owner
request_notifications Update/GameSet::Ui Commands, MessageReader<ShowNotification>, Res<Assets<LocalizationAsset>>, MessageWriter<ShowUiRole>, MessageWriter<NotificationRejected> one PendingNotification owner plus modal role request, or rejection validates localization/role before projection; no optimistic success
acknowledge_notifications Update/GameSet::Ui Commands, Query<(&ChildOf,&UiDocumentRoot),Added<UiDocumentRoot>>, Query<&PendingNotification>, MessageWriter<NotificationPresented> exact success and removes pending marker only after the requested document root actually exists under its owner
reject_failed_notifications Update/GameSet::Ui Commands, MessageReader<UiDocumentProjectionFailed>, Query<&PendingNotification>, MessageWriter<NotificationRejected> mapped typed failure and removes pending owner matching owner only; load/projection failure can never emit NotificationPresented

5. Preflight producer contract

Not applicable: the engine consumes only native assets produced before launch.

6. Behavioral evidence and disposition

Inspect shipped UI XML/styles/hotkeys/localization and images; oracle/TSVs for BFUI, UIManager, layout, widget, message and focus classes; and commit d5657e5a presentation/ui/{native,layout,tree,widgets,interactions}.rs only for discovered vocabulary. Live observation settles scaling, layering, clipping, hover/pressed/disabled visuals, focus wrapping and modal input capture.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

Evidence questions: coordinate origin/anchor rules, logical-resolution scaling, z/layer ordering, nine-slice behavior, modal focus escape and command repeat. Resolve them in A02 records or observation; never approximate layouts in code.

7. Required behavior

Projection preserves authored parent order, anchors, clipping, visibility, enabled state, style and localized text/image/font references. Unknown validated commands are rejected by preflight, not ignored here. Bindings are precise property sink components, not a generic range, property/source-key dispatcher or copied UI state table. G02 does not read economy, selection, profile, progression, scenario or other gameplay state. G21 owns selected-entity, information and catalogue presenters; G01/G22/G23/G24 and later natural domain owners query their own narrow facts plus the exact G02 sink component and write ordinary Bevy Visibility, Text, ImageNode, UiValue or UiSelected. Hidden/despawned roots cannot retain focus or activate commands. Pointer, keyboard and controller produce identical typed activation facts.

8. Performance contract

Projection may allocate exactly once per created tree; idle UI, focus movement, activation and binding presentation allocate zero. Archived traversal is direct; handles clone without payload allocation. No per-frame tree rebuild, text key formatting, hash growth or asset lookup by constructed path. Role resolution is startup/screen-transition work and loads a fixed &'static str. Work is O(changed nodes + input messages). Counters cover idle UI and focus/confirm; mapped faults remain attributed to A02/A05; backend font/glyph memory is measured under A12.

9. Acceptance criteria

Deliver all symbols above. Pure/system tests prove deterministic projection order, disabled targets never activate, controller and pointer emit the same command, and teardown removes the entire owned tree. No DOM, command router, runtime XML, placeholder widget or generated source. Root performs visual comparison and bulk compile.

10. Required handoff

Report files/API, dependency and registration requests, A02/A05/A09/A12 symbols, command consumers needing root wiring, evidence gaps, integration seams, and allocation/mapping/GPU/visual validation.

G03 — Device-independent input

1. Identity and outcome

Keyboard, mouse and any connected controller drive the same fixed game actions, including controller UI focus, camera controls, undo and redo, with rebinding. The package translates devices only; it does not route domain commands. Integration wave 2. Prerequisite: frozen Bevy contracts.

2. Exclusive ownership

The worker owns game/src/plugins/input/ only. Root owns plugin/schedule and reflection registration and persistence integration.

Bevy Input and bevy_gilrs exclusively own keyboard, mouse, touch, gamepad discovery and raw device state. G03 only maps those facts to GameAction, rebinding and active-device policy. It must not add an input backend, poll an OS API, or mirror device state through Leafwing or another input framework.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
GameInputPlugin Plugin pub struct GameInputPlugin; Produces shared ActionRequest and control state.
InputChord enum Key(KeyCode) \| Mouse(MouseButton) \| Gamepad(GamepadButton) One digital binding; serializable by G22 without strings.
ActionBinding value pub struct ActionBinding { pub action: GameAction, pub primary: InputChord, pub alternate: Option<InputChord> } One action has deterministic primary/optional alternate.
InputBindings resource pub struct InputBindings { pub entries: Box<[ActionBinding]> } Global user configuration, unique chords per context; replaced only on rebind/load.
ControlAxes resource pub struct ControlAxes { pub pan: Vec2, pub look: Vec2, pub zoom: f32 } Current normalized active-device axes; each axis in [-1,1].
PointerInput resource pub struct PointerInput { pub screen: Vec2, pub pressed: bool } Latest raw primary pointer facts in physical window pixels. G06 exclusively derives the world ray.
ActiveInput resource pub struct ActiveInput { pub source: ActionSource, pub changed_at: Duration } Last device producing non-dead-zone input; controller entity must still exist.
RebindAction message pub struct RebindAction { pub action: GameAction, pub slot: u8, pub chord: InputChord } Slot 0/1 replaces primary/alternate after collision validation.
BindingRejected message enum InvalidSlot \| Duplicate { action: GameAction } Explicit rebinding failure, never silent.

ActionRequest, ActionSource, GameAction are the sole shared semantic actions. Domain-specific input remains owned by its feature, not added here.

4. ECS ownership

Resources are genuinely global device/configuration facts and fixed-size during settled play. No entity input state is mirrored.

System Schedule/set Exact query and parameters Writes Run condition/order
read_keyboard_mouse PreUpdate/GameSet::Input Res<ButtonInput<KeyCode>>, Res<ButtonInput<MouseButton>>, Res<InputBindings>, ResMut<ControlAxes>, ResMut<PointerInput>, ResMut<ActiveInput>, MessageWriter<ActionRequest> axes/pointer/source and edge-triggered actions every frame, before intent/UI
read_gamepads PreUpdate/GameSet::Input Query<(Entity,&Gamepad)>, Res<InputBindings>, ResMut<ControlAxes>, ResMut<ActiveInput>, MessageWriter<ActionRequest> axes/source/actions connected pads; dead-zone before normalization
apply_rebindings Update/GameSet::Intent MessageReader<RebindAction>, ResMut<InputBindings>, MessageWriter<BindingRejected> binding entry only outside active chord iteration
clear_disconnected_source PreUpdate/GameSet::Input ResMut<ActiveInput>, Query<(),With<Gamepad>> fallback to keyboard/mouse active controller absent

5. Preflight producer contract

Not applicable: the engine consumes only native assets produced before launch.

6. Behavioral evidence and disposition

Inspect shipped hotkey/input XML and original options, oracle/TSVs for input, hotkey and camera-command readers, and commit d5657e5a interaction/controller.rs, ui/hotkeys.rs, input_actions.rs solely for known bindings/tokens. Observe original press/repeat/modifier and mouse-camera semantics. Controller support is the deliberate OpenZT2 extension.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

Evidence questions: original default keys, action repeat, wheel sign/scale, mouse buttons and mode-dependent capture. Controller dead zones, glyph choice and default layout are new policy and must be documented, deterministic and data-free, not guessed as original behavior.

7. Required behavior

Digital actions emit once on press unless the original binding explicitly repeats. Opposing axes cancel; analog magnitude survives normalization outside dead zone. Last meaningful device wins without flicker from noise. Rebinding rejects invalid slot and collisions atomically. Disconnect clears axes and falls back safely. Undo and Redo are first-class. No gameplay mutation or UI traversal occurs here.

8. Performance contract

All steady input systems allocate zero and perform O(bindings + connected controllers) bounded work. No action-name strings, maps grown per frame or message buffering outside Bevy. The binding box changes only on load/rebind. Allocator regressions cover idle, keyboard edge, analog frame and controller disconnect. No mapped pages or GPU resources are owned.

9. Acceptance criteria

Deliver all symbols above and tests for edge triggering, opposing axes, dead-zone stability, duplicate rejection, disconnect and equivalent keyboard/ controller actions. No command dispatcher, hidden context world, placeholder bindings or polling allocations. Root bulk-validates live devices and G02/G06/ G07 integration.

10. Required handoff

Report API/files, root registrations, any Bevy feature needs, persistence fields, original binding evidence and controller policy, integration seams and exact allocation/device validation.

G04 — World instantiation

1. Identity and outcome

A selected native scenario/map is instantiated directly as Bevy entities with typed components and asset handles, then loading completes. This owns the one-time immutable-record-to-ECS boundary, not a retained prefab world. Integration wave 2. Prerequisites: A14, A15 and A19.

2. Exclusive ownership

The package worker owns game/src/plugins/world_spawn/ only. Root owns declarations, registrations, schedule wiring and cross-plugin type registration.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
WorldSpawnPlugin Plugin pub struct WorldSpawnPlugin; Registers request, instantiation and cleanup.
WorldRoot component pub struct WorldRoot { pub scenario: AssetId } Root of exactly one loaded zoo.
WorldMember component pub struct WorldMember { pub root: Entity } Lifecycle relation on every in-zoo durable or transient entity, independent of transform parenting.
DefinitionId component pub struct DefinitionId(pub AssetId); Immutable native catalogue definition used to create an entity.
PersistentId component pub struct PersistentId(pub u64); Nonzero ID unique within a world and stable across G22 save/load.
PersistentIdAllocator focused resource pub struct PersistentIdAllocator { pub root: Entity, next: u64 } with pub fn allocate(&mut self, root: Entity) -> Result<PersistentId,PersistentIdError> and pub fn reserve_imported(&mut self, root: Entity, id: PersistentId) -> Result<(),PersistentIdError> Sole world-local ID authority. next starts at 1; fresh allocation returns next then checked-increments, and validated imported IDs advance it to max(next,id.0+1). No heap/set lookup or ID reuse.
PersistentIdError enum WrongWorld \| Zero \| Exhausted Fixed allocation/reservation failure; IDs never fall back to Bevy entity bits or random values.
AssignPersistentId message pub struct AssignPersistentId { pub entity: Entity, pub root: Entity } Requests one newly allocated ID for an already spawned saveable entity before it becomes observable to persistence/gameplay.
PersistentIdAssigned message pub struct PersistentIdAssigned { pub entity: Entity, pub id: PersistentId } Emitted after the component is inserted exactly once.
PersistentIdAssignmentFailed message pub struct PersistentIdAssignmentFailed { pub entity: Entity, pub reason: PersistentIdError } Explicit failure; caller must roll back the incomplete entity.
SpawnRecordId component pub struct SpawnRecordId(pub u32); A15/A19 record index, for diagnostics only; never live mutable state.
WorldBounds component pub struct WorldBounds { pub min: Vec2, pub max: Vec2 } World XZ metres, finite and ordered.
ZooEntrance component pub struct ZooEntrance { pub arrival: Vec3, pub inside: Vec3, pub exit: Vec3 } Authored finite arrival, inside-routing and exit points; never guessed from world position.
SpawnScenarioObject message pub struct SpawnScenarioObject { pub operation: Entity, pub root: Entity, pub prefab: AssetId, pub definition: AssetId } G24 request for one native prefab/definition instance in an existing world.
ScenarioObjectSpawned message pub struct ScenarioObjectSpawned { pub operation: Entity, pub entity: Entity } Exact completion after full typed bundle, WorldMember and allocator-issued PersistentId exist.
ScenarioObjectSpawnRejected message pub struct ScenarioObjectSpawnRejected { pub operation: Entity, pub reason: ScenarioObjectSpawnFailure } Typed failure with no partial entity.
ScenarioObjectSpawnFailure enum WrongWorld \| MissingPrefab(AssetId) \| MissingDefinition(AssetId) \| InvalidPrefab \| PersistentId(PersistentIdError) Complete engine-side G24 spawn rejection vocabulary.
BeginWorldLoad message pub struct BeginWorldLoad { pub scenario: AssetId, pub mode: PlayMode, pub profile: AssetId } G04 owns this request; mode is G01 vocabulary and profile is stable G22 identity.
WorldLoadFinished message pub struct WorldLoadFinished { pub scenario: AssetId, pub root: Entity } Emitted after all records and required handles are instantiated.
WorldLoadFailed message pub struct WorldLoadFailed { pub scenario: AssetId, pub reason: WorldLoadFailure } Explicit failure without runtime path strings.
WorldLoadFailure enum MissingScenario \| InvalidRecord(u32) \| MissingDependency(AssetId) \| DuplicatePersistentId(u64) Exhaustive engine-side failures after archive validation.
UnloadWorld message pub struct UnloadWorld(pub Entity); Despawns every matching WorldMember then the root.

G04 consumes G01's already frozen PlayMode; the load request/result messages above remain G04-owned and must not be redeclared by G01. A15/A19 expose ScenePrefabAsset, WorldScenarioAsset, fixed spawn-kind records and allocation-free ID lookup.

4. ECS ownership

Components are durable identity/bounds facts. PersistentIdAllocator is the one focused coordination resource for the active world and contains only its root and monotonic cursor. A bounded PendingWorldSpawn resource may privately retain one scenario handle and next record index during loading; it must be removed at completion and is not a world representation.

System Schedule/set Exact query and parameters Writes Run condition/order
begin_world_spawn Update/GameSet::Intent Commands, MessageReader<BeginWorldLoad>, Res<Assets<WorldScenarioAsset>> root, PersistentIdAllocator { next: 1 } and private pending cursor or failure GamePhase::Loading, no existing root
reserve_world_record_ids Update/GameSet::Intent Res<Assets<WorldScenarioAsset>>, ResMut<PersistentIdAllocator>, Option<Res<PendingWorldSpawn>> after A19 validation, visits every nonzero matching-root imported ID and positions next above the largest without spawning once before the first record; A19 has already rejected zero/duplicate IDs, and arithmetic exhaustion fails the whole load
spawn_world_records Update/GameSet::Intent Commands, Res<Assets<WorldScenarioAsset>>, Res<Assets<ScenePrefabAsset>>, Res<PersistentIdAllocator>, Option<ResMut<PendingWorldSpawn>> typed ECS entities/handles, their already-reserved PersistentIds and cursor bounded batch after complete reservation
assign_requested_persistent_ids FixedUpdate/FixedGameSet::Cleanup Commands, MessageReader<AssignPersistentId>, ResMut<PersistentIdAllocator>, Query<(),(With<WorldMember>,Without<PersistentId>)>, MessageWriter<PersistentIdAssigned>, MessageWriter<PersistentIdAssignmentFailed> one new nonzero ID component or typed failure live-created saveable entities only; before any domain completion acknowledgement
spawn_scenario_objects FixedUpdate/FixedGameSet::Act Commands, MessageReader<SpawnScenarioObject>, Res<Assets<ScenePrefabAsset>>, Res<Assets<WorldDefinitionsAsset>>, ResMut<PersistentIdAllocator>, Query<(),With<WorldRoot>>, MessageWriter<ScenarioObjectSpawned>, MessageWriter<ScenarioObjectSpawnRejected> one natural prefab bundle with DefinitionId, WorldMember and fresh PersistentId, or no entity validates root/assets first; allocates synchronously immediately before spawn; acknowledges only after complete bundle insertion
finish_world_spawn Update/GameSet::Intent Commands, Option<Res<PendingWorldSpawn>>, Query<Entity,With<WorldRoot>>, MessageWriter<WorldLoadFinished> completion and removes cursor all required records emitted
unload_world Update/GameSet::Intent Commands, MessageReader<UnloadWorld>, Query<(Entity,&WorldMember)>, Query<(),With<WorldRoot>> deterministically despawns matching members then root after persistence capture; hierarchy only for natural transforms

5. Preflight producer contract

Not applicable: the engine consumes only native assets produced before launch.

6. Behavioral evidence and disposition

Start with shipped map/scenario/entity instance data and save maps; oracle/TSVs for world/entity factory, placement, load and ID functions; commit d5657e5a startup/world.rs, simulation/world.rs, presentation/world.rs only for record coverage and discovered transforms. Observe map orientation, entrance, parenting, initial visibility and mode-specific starting conditions.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

Questions: original coordinate/rotation convention, stable-ID derivation, instance parent/attachment order, absent optional dependency handling and exact mode overrides. Resolve in A15/A19/preflight; do not keep generic component bags.

7. Required behavior

One request creates one world or one failure. Record order and IDs are deterministic. A19 (or G22 for a save) validates imported IDs for matching root, nonzero value and duplicates before allocator mutation; reserve_imported only advances the high-water mark. Every fixed spawn-kind becomes its natural component bundle and typed handle; unknown kinds were rejected preflight. Parent/attachment links resolve without linear name lookup. Completion waits for required native asset availability, not GPU residency. Every later package spawn attaches WorldMember; every live-created entity that G22 can save obtains its ID only through PersistentIdAllocator (direct allocate while exclusively borrowed or AssignPersistentId) before its owning domain acknowledges creation. IDs are never reused after despawn during that world lifetime. Unload removes all matching members regardless of transform hierarchy plus pending/allocator state. Save IDs never depend on Bevy Entity bits.

8. Performance contract

Loading may allocate ECS storage and GPU handles but never reconstructs an intermediate graph. Record traversal is direct, O(records + dependencies), with bounded per-frame batching and pre-sized temporary lookup. Fresh ID allocation and validated import reservation are O(1) and zero-allocation. Settled frames have zero G04 work/allocation. Measure total load allocations, mapped faults and time; GPU bytes remain owned by asset families. A post-load allocation test must show zero.

9. Acceptance criteria

Deliver exports above and tests for deterministic allocation, imported-ID high-water advancement, upstream duplicate validation, exhaustion rejection, typed spawn coverage and complete unload. No second ID allocator, domain-local counters, generic authored entity, property map, source parser, retained spawn mirror or fake completion. Root validates a real map from G01 through InGame in bulk.

10. Required handoff

Report files/API, registrations, A14/A15/A19 and G01 types, every spawn-kind and G22/live-creation owner needing allocator integration, unresolved records, temporary cursor seam, and load allocation/page/GPU/gameplay validation.

G05 — Editable terrain and water

1. Identity and outcome

Native terrain chunks render with authentic scale and blending, answer height/ surface queries, support terrain/biome/water brushes, update collision and GPU data, and emit reversible changes. Integration wave 2. Prerequisites: A16, G04, G07's frozen atomic edit protocol and G18's Money.

2. Exclusive ownership

The package worker owns game/src/plugins/terrain/ only. Root owns module/plugin/render feature and schedule registration. Do not edit A16 or G07.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
TerrainPlugin Plugin pub struct TerrainPlugin; Registers chunk lifecycle, edits, collision and presentation.
TerrainChunk component pub struct TerrainChunk { pub coord: IVec2, pub asset: Handle<TerrainAsset>, pub record: u32, pub origin: Vec2, pub spacing_m: f32, pub side: u16 } Static chunk mapping; record indexes the validated A16 chunk table and side/spacing match A16 validation.
TerrainRenderChunk component pub struct TerrainRenderChunk; Small render marker on a terrain chunk entity; render extraction reads the A16 handle and record from TerrainChunk, never a duplicated render-world terrain graph.
EditedTerrainSamples component pub struct EditedTerrainSamples { pub heights_cm: Box<[i16]>, pub surfaces: Box<[u32]>, pub water_cm: Box<[i16]> } Dense per-edited-chunk copy-on-first-write overlay. Unedited chunks never copy A16 arrays.
TerrainDirty component pub struct TerrainDirty { pub min: UVec2, pub max: UVec2, pub flags: TerrainDirtyFlags, pub revision: u64 } Inclusive coalesced changed sample rectangle and monotonic nonzero edit revision. Render/collision consumers remember the last applied revision, so an unchanged retained record is a no-op.
TerrainCollisionRevision component pub struct TerrainCollisionRevision(pub u64); Last terrain edit revision applied to this chunk's collider.
TerrainDirtyFlags bitflags HEIGHT = 1, SURFACE = 2, WATER = 4, COLLISION = 8 Only declared bits valid.
TerrainIndex resource pub struct TerrainIndex { pub chunks: HashMap<IVec2,Entity>, pub chunk_span_m: f32 } Sole bounded coordinate-to-chunk accelerator; capacity changes only at load/unload.
TerrainBrushKind enum Raise \| Lower \| Smooth \| Flatten { height_cm: i16 } \| Paint { biome: AssetId } \| AddWater \| RemoveWater Fixed native tool vocabulary; parameters have explicit units.
TerrainBrushPreview component pub struct TerrainBrushPreview { pub center: Vec2, pub radius_m: f32, pub strength_per_s: f32, pub seconds: f32, pub kind: TerrainBrushKind } Frozen finite brush input on the G07 preview; preparation reads it without world mutation.
TerrainStroke message pub struct TerrainStroke { pub preview: Entity, pub center: Vec2, pub radius_m: f32, pub strength_per_s: f32, pub seconds: f32, pub kind: TerrainBrushKind } Updates the one terrain preview during drag; never carries a transaction or mutates terrain.
TerrainChanged message pub struct TerrainChanged { pub transaction: Entity, pub chunk: Entity, pub min: UVec2, pub max: UVec2 } Emitted once per affected chunk after mutation.
TerrainEdit component pub struct TerrainEdit { pub deltas: Box<[TerrainEditDelta]> } One G05 operation component on the G07 transaction, covering every affected chunk in stable chunk-ID order.
TerrainEditDelta value pub struct TerrainEditDelta { pub chunk: PersistentId, pub min: UVec2, pub max: UVec2, pub before: Box<[TerrainSample]>, pub after: Box<[TerrainSample]> } Inclusive row-major rectangle; array length is (max.x-min.x+1)*(max.y-min.y+1).
TerrainSample POD value pub struct TerrainSample { pub height_cm: i16, pub surface: u32, pub water_cm: i16 } Exact reversible live sample.
TerrainEditPrepared message pub struct TerrainEditPrepared { pub transaction: Entity, pub cost: Money } Emitted after TerrainEdit is attached, with checked total cost and no world mutation.
TerrainEditPreparationRejected message pub struct TerrainEditPreparationRejected { pub transaction: Entity, pub reason: PlacementFailure } Typed pre-debit rejection.
CommitTerrainEdit message pub struct CommitTerrainEdit { pub transaction: Entity, pub application: EditApplication } Exact G07-authorized application; initial/redo exists only after successful G18 debit.
TerrainEditAcknowledged message pub struct TerrainEditAcknowledged { pub transaction: Entity, pub application: EditApplication, pub accepted: bool } Exactly one result after all deltas apply atomically or none do.
sample_terrain function pub fn sample_terrain(chunk: &TerrainChunk, archive: &MappedTerrainArchive, archived: &ArchivedTerrainChunkRecord, edited: Option<&EditedTerrainSamples>, world_xz: Vec2) -> Option<TerrainPoint> Reads overlay when present, otherwise MappedTerrainArchive::heights/blend payload directly; allocation-free.
terrain_chunk_at function pub fn terrain_chunk_at(index: &TerrainIndex, world_xz: Vec2) -> Option<Entity> Allocation-free O(1) chunk entity lookup using canonical floor division.
TerrainPoint value pub struct TerrainPoint { pub height_m: f32, pub water_height_m: Option<f32>, pub surface: u32, pub normal: Vec3 } World metres, normalized finite normal.
TerrainGpuAsset prepared render asset pub struct TerrainGpuAsset { pub heights: Buffer, pub blends: Buffer, pub water: Buffer, pub index: Buffer, pub vertex_bytes: u64, pub blend_bytes: u64, pub water_bytes: u64 } Persistent buffers prepared once from A16's GPU-ready payload through Bevy's RenderAsset path; never reconstructed as a CPU Mesh.
TerrainGpuUpdate render value pub struct TerrainGpuUpdate { pub entity: Entity, pub asset: bevy::asset::AssetId<TerrainAsset>, pub record: u32, pub min: UVec2, pub max: UVec2, pub flags: TerrainDirtyFlags, pub revision: u64, pub byte_start: u32, pub byte_len: u32 } One extracted dirty rectangle referencing bytes in reusable staging.
ExtractedTerrainUpdates render resource pub struct ExtractedTerrainUpdates { pub updates: Vec<TerrainGpuUpdate>, pub bytes: Vec<u8>, pub uploaded_revisions: EntityHashMap<u64> } Render-world-only bounded queue and staging storage; capacities grow during load/edit warm-up, then remain stable.

A16 freezes TerrainAsset archived grids/GPU payload and biome layer IDs. G07 owns PrepareConstruction, EditApplication, payment/rollback coordination and history; G18's Money is the only cost type.

4. ECS ownership

Only a chunk changed by gameplay owns editable samples. Unedited sampling, collision and initial GPU preparation read A16 mapped arrays/payload directly through the handle. Each chunk also has the unit TerrainRenderChunk marker. TerrainDirty is a monotonic change fact: collision and render consumers compare its revision with their last-applied revision, so retaining it cannot cause idle work or repeated upload.

System Schedule/set Exact query and parameters Writes Run condition/order
index_terrain_chunks Update/GameSet::Intent Commands, Res<Assets<TerrainAsset>>, ResMut<TerrainIndex>, Query<(Entity,&TerrainChunk),Added<TerrainChunk>> index and TerrainRenderChunk loading only; validates A16 record; no sample copy
update_terrain_preview Update/GameSet::Intent Commands, MessageReader<TerrainStroke>, Query<&mut TerrainBrushPreview,With<ConstructionPreview>> latest preview brush only matching active preview; no terrain write
prepare_terrain_edits FixedUpdate/FixedGameSet::Act Commands, MessageReader<PrepareConstruction>, Res<TerrainIndex>, Res<Assets<TerrainAsset>>, Query<(&TerrainChunk,Option<&EditedTerrainSamples>,&PersistentId)>, Query<&TerrainBrushPreview>, MessageWriter<TerrainEditPrepared>, MessageWriter<TerrainEditPreparationRejected> exact sorted before/after deltas and checked cost on transaction preparation phase only; reads mapped/base or existing overlay; never inserts overlay/dirty state
apply_terrain_edits FixedUpdate/FixedGameSet::Act Commands, MessageReader<CommitTerrainEdit>, Query<&TerrainEdit>, Query<(&TerrainChunk,Option<&mut EditedTerrainSamples>,Option<&mut TerrainDirty>)>, Res<Assets<TerrainAsset>>, MessageWriter<TerrainChanged>, MessageWriter<TerrainEditAcknowledged> copy-on-first-write overlay, selected before/after rectangles and dirty revision exact G07 application; validates every referenced chunk before changing any, then applies all or rejects all
update_terrain_collision FixedUpdate/FixedGameSet::Cleanup Query<(&TerrainChunk,&EditedTerrainSamples,&TerrainDirty,&mut TerrainCollisionRevision,&mut Collider)> changed collider region and applied revision edited revision only; initial collider uses A16
extract_terrain_updates render ExtractSchedule Extract<Query<(Entity,&TerrainChunk,&EditedTerrainSamples,&TerrainDirty),With<TerrainRenderChunk>>>, ResMut<ExtractedTerrainUpdates> coalesced rectangle bytes and metadata in reused render staging only revisions newer than uploaded_revisions; no full chunk copy
write_terrain_updates render Render/RenderSet::PrepareResources Res<RenderQueue>, Res<RenderAssets<TerrainGpuAsset>>, ResMut<ExtractedTerrainUpdates> RenderQueue::write_buffer into exact existing height/blend/water ranges and uploaded revision after extraction; no buffer recreation or full upload

5. Preflight producer contract

Not applicable: the engine consumes only native assets produced before launch.

6. Behavioral evidence and disposition

Inspect shipped terrain/map/biome/brush data; oracle/TSVs for terrain manager, height interpolation, cursor, biome paint, water and undo; commit d5657e5a simulation/terrain.rs and terrain source records only for equations/tokens. Observe tile scale, brush falloff, smoothing, water plane, texture scale/blend, clamps, cost and collision timing.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

Questions: exact grid dimensions/axis orientation; centimetre quantization; height clamps; each brush kernel; biome channel normalization; water depth and terrain transaction cost. Recover before implementing those branches.

7. Required behavior

Sampling is continuous across chunk seams. Brush influence uses recovered kernel and clamps; preparation records exact before/after values once without touching live terrain. Only CommitTerrainEdit after the G18 debit may materialize an overlay and apply after; undo/failure rollback applies before and redo applies after. Undo/redo restores identical samples. Dirty bounds coalesce, update collision before agents navigate, and upload only affected ranges. Surface texture world scale comes from A16/material data, never guessed tiling. Corrupt dimensions fail at asset validation, not with partial terrain.

8. Performance contract

Settled systems allocate/do work only on changed chunks. Sampling is O(1) and zero-allocation; a stroke is O(samples in radius). An overlay allocates once on first write to that chunk; unedited maps remain zero-copy. Deltas allocate only during per-player-edit preparation and are discarded without world mutation if preparation/debit fails. A16 payload preparation creates persistent GPU buffers once. Dirty extraction copies only the changed rectangle into reused staging and RenderQueue::write_buffer updates only corresponding ranges; it never rebuilds a Mesh, vertex buffer, or full chunk. Tests count idle frame, sample and unchanged-revision upload allocations; smaps/page faults cover A16; the GPU ledger covers exact persistent buffers, textures and update bytes.

9. Acceptance criteria

Deliver all exports, mapped-versus-overlay sampling/seam/kernel/round-trip tests, prepare-without-mutation, debit-gate and atomic multi-chunk acknowledgement, proof unedited load/sampling allocates no sample arrays, and a no-dirty no-op test. A changed 8x8 rectangle must produce only the corresponding height/blend/ water queue writes, while a repeated revision produces none. No source parsing, whole-map rebuild/reupload per stroke, CPU Mesh mutation, per-frame GPU asset creation, renderer wrapper, guess constants or eager second terrain graph. Root bulk-validates original map appearance, editing, collision and history.

10. Required handoff

Report API/files, Bevy/render/physics dependencies, A16/G04/G07 symbols, registrations/order, evidence gaps, copy-on-first-write rationale, and allocator/ mapping/GPU/visual validation.

G06 — Authentic game cameras

1. Identity and outcome

The in-game camera pans, edge-scrolls, rotates, zooms, fits terrain, respects map bounds and performs source-authored transitions with original overhead semantics. It also exposes clean mode transitions for later immersive modes. Integration wave 2. Prerequisites: G03 and G05.

2. Exclusive ownership

The package worker owns game/src/plugins/camera/ only. Root owns registration/schedule/reflection.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
ZooCameraPlugin Plugin pub struct ZooCameraPlugin; Registers camera lifecycle, input, fit and transition systems.
ZooCamera component pub struct ZooCamera; Marks the single active Bevy Camera3d for a zoo.
CameraMode component enum Overhead \| Follow(Entity) \| FirstPerson(Entity) \| Rail(Entity) \| Free Current presentation mode; referenced entity must exist.
OverheadRig component pub struct OverheadRig { pub focus: Vec2, pub yaw: f32, pub pitch: f32, pub distance: f32, pub pan_velocity: Vec2, pub turn_velocity: f32, pub zoom_velocity: f32 } World XZ/metres and radians; finite values clamped by tuning.
CameraBounds component pub struct CameraBounds { pub min: Vec2, pub max: Vec2, pub ground_buffer_m: f32 } Derived from G04 bounds/source tuning.
CameraTuning component pub struct CameraTuning { pub distance: RangeInclusive<f32>, pub pitch: RangeInclusive<f32>, pub pan_speed_mps: f32, pub turn_speed_rps: f32, pub zoom_speed_mps: f32, pub start_accel: f32, pub stop_accel: f32, pub edge_scroll: bool } Native recovered values; never fallback guesses.
CameraTransition component pub struct CameraTransition { pub from: Transform, pub to: Transform, pub elapsed: f32, pub duration: f32, pub easing: CameraEasing } Positive duration, removed at completion.
CameraReturnState component pub struct CameraReturnState { pub mode: CameraMode, pub transform: Transform, pub overhead: OverheadRig } Temporary snapshot on the actual camera, captured once before entering an immersive mode and removed after exact restoration; G31 must not duplicate it.
CameraEasing enum Linear \| SmoothStep Only modes evidenced in native camera records.
SetCameraMode message pub struct SetCameraMode { pub mode: CameraMode, pub transition_seconds: Option<f32> } Requests validated mode/target transition.
RestoreCameraMode message pub struct RestoreCameraMode { pub transition_seconds: Option<f32> } Returns to the stored G06 state; rejected if none exists.
FocusCamera message pub struct FocusCamera { pub world: Vec3, pub transition_seconds: Option<f32> } Changes overhead focus, preserving bounded tuning.
CameraMoved message pub struct CameraMoved { pub eye: Vec3, pub focus: Vec3 } Emitted only when pose materially changes.
WorldPointerRay resource pub struct WorldPointerRay(pub Option<Ray3d>); G06-owned projection of G03 raw PointerInput through the authoritative camera; absent outside viewport/no camera.

Camera tuning comes directly from A14 CameraTuningDefinition selected by the A19 map/scenario record; an absent or invalid reference is a load failure, never a runtime default.

4. ECS ownership

Rig/tuning/bounds/transition are facts on the actual Bevy camera entity; there is no camera resource shadowing its transform.

System Schedule/set Exact query and parameters Writes Run condition/order
spawn_zoo_camera OnEnter(GamePhase::InGame) Commands, Query<(Entity,&WorldBounds),With<WorldRoot>>, Res<Assets<WorldScenarioAsset>> one camera entity/rig/tuning/bounds carrying root WorldMember no existing ZooCamera
apply_camera_actions Update/GameSet::Intent MessageReader<ActionRequest>, Res<ControlAxes>, Query<(&mut OverheadRig,&CameraMode),With<ZooCamera>> desired velocities after G03; overhead only
apply_edge_scroll Update/GameSet::Intent Query<&Window>, Res<PointerInput>, Query<(&mut OverheadRig,&CameraTuning,&CameraMode)> pan velocity enabled, pointer inside window
advance_overhead_camera Update/GameSet::Presentation Res<Time<Real>>, Res<TerrainIndex>, Res<Assets<TerrainAsset>>, Query<(&TerrainChunk,Option<&EditedTerrainSamples>)>, Query<(&mut Transform,&mut OverheadRig,&CameraTuning,&CameraBounds),With<ZooCamera>> rig and actual transform unscaled real presentation delta; indexed Query::get, then mapped-or-overlay sample_terrain
project_pointer_ray PostUpdate/GameSet::Presentation Res<PointerInput>, Query<(&Camera,&GlobalTransform),With<ZooCamera>>, ResMut<WorldPointerRay> current world ray after camera transform propagation; G03 remains sole raw-pointer writer
begin_camera_transition Update/GameSet::Intent Commands, MessageReader<SetCameraMode>, MessageReader<FocusCamera>, Query<(Entity,&Transform,&mut CameraMode),With<ZooCamera>> mode/transition valid target only
capture_camera_return_state Update/GameSet::Intent Commands, MessageReader<SetCameraMode>, Query<(Entity,&CameraMode,&Transform,&OverheadRig,Option<&CameraReturnState>),With<ZooCamera>> inserts one return state transition from ordinary overhead to immersive mode only; before mode mutation
restore_camera_return_state Update/GameSet::Intent Commands, MessageReader<RestoreCameraMode>, Query<(Entity,&mut CameraMode,&mut OverheadRig,&CameraReturnState),With<ZooCamera>> restored mode/rig and transition target; removes state after restoration valid stored state only
advance_camera_transition Update/GameSet::Presentation Commands, Res<Time<Real>>, Query<(Entity,&mut Transform,&mut CameraTransition),With<ZooCamera>> transform/removes transition transition present; unscaled real delta; suppress ordinary advance
report_camera_motion PostUpdate/GameSet::Diagnostics Query<(&Transform,&OverheadRig), (With<ZooCamera>,Changed<Transform>)>, MessageWriter<CameraMoved> message changed pose only

5. Preflight producer contract

Not applicable: the engine consumes only native assets produced before launch.

6. Behavioral evidence and disposition

Inspect camera XML/hotkeys/options and map records; oracle/TSVs for ZTCameraCommandReader, overhead/UDV/ground-fit/bounded-object components; commit d5657e5a domain/interaction/camera.rs and features/presentation/camera.rs for evidence only. Live comparison is required for pan axes, acceleration, zoom, pitch, fit, edge thresholds and transitions.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

Questions: units/signs and zoom-dependent speed curve; wheel accumulation; camera anchor clamp vs frustum; soft-fit equations; initial yaw/focus; easing; sky/frustum switches. Resolve exact values from native assets/oracle.

7. Required behavior

Inputs accelerate/decelerate per tuning, remain frame-rate independent, clamp zoom/pitch/anchor and terrain-fit without penetrating water/ground. Rotation is around focus. Transition interpolation uses shortest angular path and reaches the exact endpoint. Invalid/deleted target returns safely to overhead. The component Transform is the authoritative pose used by Bevy rendering and ray casting. Camera motion and transitions always use Time<Real> and therefore continue consistently while simulation speed or pause changes G32 time. No guessed 24/70-style limits.

8. Performance contract

All steady camera systems allocate zero and are O(1) plus at most the terrain chunk spatial lookup, which must not scan all chunks. Only changed poses emit. No strings, path lookup or transform clone collections. Allocation tests cover idle, pan/rotate/zoom and transition frames. Camera owns no mapped or GPU data.

9. Acceptance criteria

Deliver all exports with pure tests for clamp, acceleration, shortest-angle easing, exact transition completion and immersive capture/restore. No camera god resource, fixed guessed semantics, per-frame terrain scan or renderer wrapper. Root validates against live original at several map edges/zoom tiers and controller input.

10. Required handoff

Report API/files, source tuning contract, G03/G04/G05 and later G31 symbols, root registrations/order, evidence gaps, temporary mode seams and allocation/ camera-comparison validation.

G07 — Construction interaction and history

1. Identity and outcome

The player selects a build/delete tool, sees a validated preview, commits or cancels it, and can undo and redo every accepted world edit. G07 owns the generic edit lifecycle, atomic coordination and history; each domain owns its typed transaction component and application system. Integration wave 2 foundation. Prerequisites: A14, A15, G03, G04 and G18. G05/G08/G10 build on this protocol; they are not prerequisites of G07.

2. Exclusive ownership

The package worker owns game/src/plugins/construction/ only. Root owns registrations, schedules and cross-domain transaction ordering.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
ConstructionPlugin Plugin pub struct ConstructionPlugin; Registers tool, preview, commit, delete and history systems.
ConstructionTool resource enum Inspect \| Place(AssetId) \| Fence(AssetId) \| Path(AssetId) \| Terrain(TerrainBrushKind) \| Delete Sole active player tool; referenced definition must be native.
ConstructionCursor component pub struct ConstructionCursor { pub world: Vec3, pub normal: Vec3 } Actual ray hit; finite world metres.
ConstructionPreview component pub struct ConstructionPreview { pub definition: AssetId, pub transform: Transform, pub validity: PlacementValidity } At most one active preview entity.
PlacementValidity enum Pending \| Valid { cost: Money } \| Invalid(PlacementFailure) Uses the exact fixed-point currency value owned by G18; no raw integer convention or display strings.
PlacementFailure enum Unaffordable \| Locked \| Occupied \| OutsideMap \| TooSteep \| NoHeadroom \| InvalidHabitat \| InvalidTopology \| AuthoredRule(AssetId) Fixed UI-localizable reason vocabulary; the ID is localization/data identity only and is never dispatched as behavior.
SelectConstructionTool message pub struct SelectConstructionTool(pub ConstructionTool); Replaces tool and cancels incompatible preview.
CommitConstruction message pub struct CommitConstruction { pub preview: Entity } Confirm request for current valid preview only.
CancelConstruction message pub struct CancelConstruction; Cancels preview/tool drag without a transaction.
DeleteEntity message pub struct DeleteEntity(pub Entity); Requests validated deletion; dependent cleanup remains domain-owned.
EditTransaction component pub struct EditTransaction { pub sequence: u64, pub state: TransactionState, pub cost: Money } Durable history entity bearing one or more G05/G08/G10 typed operation components; cost is the exact checked total committed through G18.
TransactionState enum Applied \| Undone Matches live world exactly.
EditApplication enum InitialCommit \| Undo \| Redo \| FailureRollback Exact application direction sent to domains; FailureRollback reverses only an acknowledged partial application and never enters history.
DomainAckStatus enum NotExpected \| Pending \| Applied \| Reverted \| Rejected Fixed state for one explicit domain lane.
EditCommitPhase enum Preparing \| AwaitingEconomy \| Applying \| RollingBack \| AwaitingCompensation \| Complete \| Failed Transaction coordinator phase; world mutation is legal only in Applying/RollingBack.
EditCommitProgress component pub struct EditCommitProgress { pub phase: EditCommitPhase, pub application: EditApplication, pub cost: Money, pub terrain: DomainAckStatus, pub topology: DomainAckStatus, pub placement: DomainAckStatus } Focused fixed-size coordination on a pending transaction. Three named fields are the complete domain set; counts are derived from them, never stored in a generic domain map.
EditPayment component pub struct EditPayment { pub transaction: Entity, pub purpose: EditPaymentPurpose } Lives on the separate G18 operation entity so forward/reverse transfer results correlate without reusing the history entity as two economy operations.
EditPaymentPurpose enum InitialTransfer \| RedoTransfer \| FailureCompensation \| UndoCompensation Exact economy leg. Positive cost debits zoo, negative cost credits zoo, and the reverse compensation occurs only after inverse/rollback acknowledgements.
EditHistory resource pub struct EditHistory { pub entries: Vec<Entity>, pub cursor: usize, pub limit: usize, pub next_sequence: u64 } Bounded chronological transaction entities; redo tail is despawned on new commit.
PrepareConstruction message pub struct PrepareConstruction { pub transaction: Entity, pub preview: Entity } Fan-out preparation trigger. G05/G08/G10 respond only when their exact preview marker is present and never mutate world state.
PrepareDeletion message pub struct PrepareDeletion { pub transaction: Entity, pub target: Entity } Fan-out pre-removal trigger; every natural owner may attach one typed inverse before any despawn.
EditApplicationAuthorized message pub struct EditApplicationAuthorized { pub transaction: Entity, pub application: EditApplication } Sole forward/inverse dispatch gate: initial/redo only after exact G18 transfer, undo after history validation.
RecordTransaction message pub struct RecordTransaction(pub Entity); Emitted only by G07 after every expected domain acknowledgement succeeds; domains never emit it.
ApplyTransaction message pub struct ApplyTransaction { pub transaction: Entity, pub direction: EditDirection } Domains apply their typed operation on next Act pass.
EditDirection enum Undo \| Redo Exact requested direction.
TransactionApplied message pub struct TransactionApplied { pub transaction: Entity, pub direction: EditDirection, pub accepted: bool } G07 aggregate result after all exact domain acknowledgements and required economy leg; history moves only on accepted result.
ConstructionCommitFailed message pub struct ConstructionCommitFailed { pub transaction: Entity, pub reason: ConstructionCommitFailure } Typed terminal failure after preparation rejection, economy rejection or completed rollback/compensation.
ConstructionCommitFailure enum NoDomain \| Preparation(PlacementFailure) \| Economy(TransactionRejection) \| DomainRejected \| Compensation(TransactionRejection) Exact player/error surface; no partial mutation is reported as success.

G10 owns placement-rule evaluation; G05 owns TerrainEdit, TerrainEditPrepared/TerrainEditPreparationRejected, CommitTerrainEdit and TerrainEditAcknowledged; G08 owns the corresponding Topology* types; G10 owns the corresponding Placement* types. Every acknowledgement has exact shape { transaction:Entity, application:EditApplication, accepted:bool }. G18 owns Money, TransactionRequest, TransactionCompleted, TransactionRejected and cash mutation. This is the complete atomic policy; there is no unresolved root decision, Box<dyn Command>, closure or blob.

4. ECS ownership

Only current tool and bounded entity-ID history are resources. Preview and transaction facts are entities/components, enabling typed domain systems.

System Schedule/set Exact query and parameters Writes Run condition/order
route_construction_actions Update/GameSet::Intent MessageReader<ActionRequest>, ResMut<ConstructionTool>, Query<Entity,With<ConstructionPreview>>, MessageWriter<CommitConstruction>, MessageWriter<CancelConstruction> typed requests/tool after G03
update_construction_cursor Update/GameSet::Intent Res<WorldPointerRay>, Res<TerrainIndex>, Res<Assets<TerrainAsset>>, Query<(&TerrainChunk,Option<&EditedTerrainSamples>)>, Query<&mut ConstructionCursor> indexed mapped-or-overlay ray hit active tool; consumes G06 ray, never mutates G03 input
begin_construction_preparation FixedUpdate/FixedGameSet::Act Commands, MessageReader<CommitConstruction>, Query<(&ConstructionPreview,&WorldMember)>, ResMut<EditHistory>, MessageWriter<PrepareConstruction> one transaction/progress carrying the preview's WorldMember plus preparation fan-out; no debit/world/history mutation valid preview with no pending operation
begin_deletion_preparation FixedUpdate/FixedGameSet::Act Commands, MessageReader<DeleteEntity>, ResMut<EditHistory>, Query<&WorldMember>, MessageWriter<PrepareDeletion> one transaction/progress carrying the target's WorldMember plus exact pre-removal fan-out target belongs to active world; no despawn
collect_domain_preparations FixedUpdate/FixedGameSet::Act MessageReader<TerrainEditPrepared>, MessageReader<TerrainEditPreparationRejected>, MessageReader<TopologyEditPrepared>, MessageReader<TopologyEditPreparationRejected>, MessageReader<PlacementEditPrepared>, MessageReader<PlacementEditPreparationRejected>, Query<(&mut EditCommitProgress,&mut EditTransaction)> checked total cost in progress/history record and named expected-domain statuses after G05/G08/G10 preparation and apply_deferred; rejection/no-domain fails before debit
request_construction_economy FixedUpdate/FixedGameSet::Economy Commands, Query<(Entity,&WorldMember,&mut EditCommitProgress),Changed<EditCommitProgress>>, MessageWriter<TransactionRequest> separate payment operation carrying the transaction's WorldMember plus EditPayment::InitialTransfer; positive cost requests zoo debit, negative cost requests zoo credit for its checked absolute value complete successful preparation; zero cost authorizes directly; before G18 apply_transactions
complete_construction_economy FixedUpdate/FixedGameSet::Economy Commands, MessageReader<TransactionCompleted>, MessageReader<TransactionRejected>, Query<&EditPayment>, Query<&mut EditCommitProgress>, MessageWriter<EditApplicationAuthorized>, MessageWriter<ConstructionCommitFailed> exact initial/redo authorization or terminal no-mutation failure exact transfer operation/purpose only; after G18 apply_transactions
begin_history_application FixedUpdate/FixedGameSet::Economy Commands, MessageReader<ApplyTransaction>, Query<(&EditTransaction,&WorldMember,&mut EditCommitProgress)>, MessageWriter<TransactionRequest>, MessageWriter<EditApplicationAuthorized> undo authorization immediately after validation; redo creates a world-scoped EditPayment::RedoTransfer and waits no existing application; redo never reaches domains before G18 completion
dispatch_domain_application FixedUpdate/FixedGameSet::Act MessageReader<EditApplicationAuthorized>, Query<(Entity,Option<&TerrainEdit>,Option<&TopologyEdit>,Option<&PlacementEdit>,&mut EditCommitProgress)>, MessageWriter<CommitTerrainEdit>, MessageWriter<CommitTopologyEdit>, MessageWriter<CommitPlacementEdit> one exact message for each present typed component and pending named status application must equal progress; initial/redo already paid, undo validated
collect_domain_acknowledgements FixedUpdate/FixedGameSet::Cleanup MessageReader<TerrainEditAcknowledged>, MessageReader<TopologyEditAcknowledged>, MessageReader<PlacementEditAcknowledged>, Query<&mut EditCommitProgress>, MessageWriter<RecordTransaction>, MessageWriter<TransactionApplied> named statuses; initial records, redo completes, undo advances to AwaitingCompensation only after all expected lanes acknowledge duplicate/stale/wrong-application acknowledgements ignored; first rejection begins rollback; undo cannot move history before compensation
dispatch_failure_rollback FixedUpdate/FixedGameSet::Act Query<(Entity,Option<&TerrainEdit>,Option<&TopologyEdit>,Option<&PlacementEdit>,&mut EditCommitProgress)>, MessageWriter<CommitTerrainEdit>, MessageWriter<CommitTopologyEdit>, MessageWriter<CommitPlacementEdit> FailureRollback only to lanes currently Applied after a domain rejection; never asks an unapplied lane to undo
finish_rollback_and_compensation FixedUpdate/FixedGameSet::Economy Commands, Query<(Entity,&mut EditCommitProgress,&EditTransaction)>, MessageReader<TransactionCompleted>, MessageReader<TransactionRejected>, Query<&EditPayment>, MessageWriter<TransactionRequest>, MessageWriter<TransactionApplied>, MessageWriter<ConstructionCommitFailed> exact reverse G18 transfer after successful undo or failure rollback; undo emits accepted aggregate only after compensation, rollback emits terminal failure accounts/direction exactly reverse the corresponding completed transfer
cancel_preview Update/GameSet::Ui Commands, MessageReader<CancelConstruction>, Query<Entity,With<ConstructionPreview>> despawn preview any phase/tool exit; after intent routing
record_transactions FixedUpdate/FixedGameSet::Cleanup Commands, MessageReader<RecordTransaction>, ResMut<EditHistory>, Query<&EditTransaction> bounded history/redo disposal acknowledged applied transaction
request_undo_redo Update/GameSet::Intent MessageReader<ActionRequest>, Res<EditHistory>, MessageWriter<ApplyTransaction> one direction request no transaction pending
advance_history FixedUpdate/FixedGameSet::Cleanup MessageReader<TransactionApplied>, ResMut<EditHistory>, Query<&mut EditTransaction> cursor/state accepted aggregate result only; redo transfer precedes domain application and undo compensation follows successful inverse application

5. Preflight producer contract

Not applicable: the engine consumes only native assets produced before launch.

6. Behavioral evidence and disposition

Inspect build-mode/UI data, placement cursors, failure localization and entity rules; oracle/TSVs for placement, delete and undo functions; commit d5657e5a domain/simulation/placement.rs, features/simulation/{placement,delete,undo}.rs only for discovered commands/rules. Observe drag, single-placement, right-click, refund, selection, confirmation and undo granularity.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

Questions: which actions are transactional together; history bound; redo-tail behavior; refund ratios; preview LOD/colors; continuous placement; deletability and dependent cleanup. Recover, do not infer generic editor behavior.

7. Required behavior

Only valid current previews commit. Preparation freezes typed before/after records and checked signed net cost but cannot mutate the world. G18 transfer completes before any initial/redo forward application. The three named domain acknowledgement lanes are counted from fixed fields; history records only after all expected lanes accept. Any rejection reverses only lanes that acknowledged application, waits for their rollback acknowledgements, then reverses the completed transfer through G18. Undo applies exact inverses before its compensation; redo completes its transfer before exact forwards. Failure during history application restores the pre-request world/economy state and leaves cursor/state unchanged. A new commit after undo deletes the redo tail. History stores transaction entities, never snapshots of the world. Deletion uses domain cleanup and records sufficient data for exact restoration.

8. Performance contract

Idle construction/history systems allocate zero. Cursor update is O(1) plus indexed terrain hit. History allocation occurs only on edit and retains a configured bound; dropping redo/old entries recursively frees their components. No per-frame world scan, payload cloning or generic serialization. Counters cover idle/cursor/undo request; transaction heap is measured per edit. No mapped or GPU bytes beyond preview handles.

9. Acceptance criteria

Deliver exports and tests for invalid/no-domain preparation, economy-before-world ordering, exact cursor progression, multi-domain acknowledgement counting, partial rollback/compensation, undo/redo, stale/duplicate acknowledgement rejection, redo-tail disposal and history bound. No generic command trait, opaque operation enum collecting every domain, world snapshot or fake success. Root bulk-tests terrain/topology/object/economy edits.

10. Required handoff

Report API/files, registrations/order, G03/G04/G05/G08/G10/G18 symbols, exact three-lane ordering, evidence gaps, seams and allocation/history/gameplay checks.

G08 — Fences, paths and portals

1. Identity and outcome

Players construct authentic fences, gates, portals, ground/elevated paths and supports as connected ECS topology used directly by habitats and navigation. Integration wave 2. Prerequisites: A14, A15, A17, G04, G07 and G18.

2. Exclusive ownership

The package worker owns game/src/plugins/topology/ only. Root owns declarations, registration, schedules and operation integration.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
TopologyPlugin Plugin pub struct TopologyPlugin; Registers typed placement, connectivity and history application.
TopologyNode component pub struct TopologyNode { pub cell: IVec3 } Grid coordinate; Z component is authored elevation level.
FenceEdge component pub struct FenceEdge { pub definition: AssetId, pub a: Entity, pub b: Entity } Connects distinct topology-node entities; one undirected edge per pair/level.
PathTile component pub struct PathTile { pub definition: AssetId, pub cell: IVec3 } One path tile per cell/level.
Gate component pub struct Gate { pub open: bool, pub locked: bool } Traversable fence opening; state is live/save-relevant.
Portal component pub struct Portal { pub destination: Entity, pub bidirectional: bool } Valid destination topology entity.
PathSupport component pub struct PathSupport { pub path: Entity, pub ground_height_m: f32 } Derived support for an elevated path tile.
TopologyIndex resource pub struct TopologyIndex { pub nodes: HashMap<IVec3,Entity>, pub paths: HashMap<IVec3,Entity>, pub edges: HashMap<EdgeKey,Entity> } Sole bounded spatial lookup; capacity changes only on edits/load.
EdgeKey value pub struct EdgeKey { pub lo: IVec3, pub hi: IVec3 } Canonically sorted endpoints.
FencePreview component pub struct FencePreview { pub definition: AssetId, pub from: IVec3, pub to: IVec3 } Exact straight/rubber-band candidate on a G07 preview; no live topology.
PathPreview component pub struct PathPreview { pub definition: AssetId, pub from: IVec3, pub to: IVec3 } Exact ground/elevated candidate on a G07 preview.
PlaceFence message pub struct PlaceFence { pub preview: Entity, pub definition: AssetId, pub from: IVec3, pub to: IVec3 } Updates/creates FencePreview; never mutates topology or cash.
PlacePath message pub struct PlacePath { pub preview: Entity, pub definition: AssetId, pub from: IVec3, pub to: IVec3 } Updates/creates PathPreview; never mutates topology or cash.
SetGateState message pub struct SetGateState { pub gate: Entity, pub open: bool } State request; lock/behavior policy validates separately.
TopologyChanged message pub struct TopologyChanged { pub transaction: Option<Entity>, pub bounds: IRect } Affected XZ grid region for G09/G14 incremental rebuild.
TopologyEdit component pub struct TopologyEdit { pub created: Box<[TopologySnapshot]>, pub removed: Box<[TopologySnapshot]> } Attached to G07 transaction; snapshots use stable IDs so repeated undo/redo never retain stale Bevy entities.
TopologySnapshot value Fixed enum Node { id: PersistentId, cell: IVec3 } \| Fence { id, definition, a: PersistentId, b: PersistentId, gate: Option<Gate> } \| Path { id, definition, cell } \| Portal { id, destination, bidirectional } Typed restoration data; no generic bag.
TopologyEditPrepared message pub struct TopologyEditPrepared { pub transaction: Entity, pub cost: Money } TopologyEdit is complete and immutable; no live topology/cash changed.
TopologyEditPreparationRejected message pub struct TopologyEditPreparationRejected { pub transaction: Entity, pub reason: PlacementFailure } Typed pre-debit rejection.
CommitTopologyEdit message pub struct CommitTopologyEdit { pub transaction: Entity, pub application: EditApplication } Exact authorized forward/inverse; initial/redo is sent only after G18 debit completes.
TopologyEditAcknowledged message pub struct TopologyEditAcknowledged { pub transaction: Entity, pub application: EditApplication, pub accepted: bool } One all-or-none G08 result.

Definition grid scale, fence/path placement constraints and navigation-link records come from A14/A17. Every HashMap above is exactly bevy::platform::collections::HashMap. G07 owns the payment gate and coordination. Therefore every priced G08 edit reaches G18 before CommitTopologyEdit; G08 never debits cash directly and never mutates topology from PlaceFence/PlacePath.

4. ECS ownership

Topology facts live on actual nodes/edges/tiles. The resource is only a spatial index, rebuilt from components on load and never authoritative over them.

System Schedule/set Exact query and parameters Writes Run condition/order
index_loaded_topology OnEnter(GamePhase::InGame) ResMut<TopologyIndex>, Query<(Entity,&TopologyNode)>, Query<(Entity,&PathTile)>, Query<(Entity,&FenceEdge)> pre-sized index once after G04
update_fence_previews Update/GameSet::Intent Commands, MessageReader<PlaceFence>, Query<&mut FencePreview,With<ConstructionPreview>> preview facts only active fence tool
update_path_previews Update/GameSet::Intent Commands, MessageReader<PlacePath>, Query<&mut PathPreview,With<ConstructionPreview>> preview facts only active path tool
prepare_topology_construction FixedUpdate/FixedGameSet::Act Commands, MessageReader<PrepareConstruction>, Res<TopologyIndex>, Res<TerrainIndex>, Res<Assets<TerrainAsset>>, Res<Assets<WorldDefinitionsAsset>>, ResMut<PersistentIdAllocator>, Query<(&TerrainChunk,Option<&EditedTerrainSamples>)>, Query<(Option<&FencePreview>,Option<&PathPreview>)>, MessageWriter<TopologyEditPrepared>, MessageWriter<TopologyEditPreparationRejected> immutable snapshots with allocator-issued IDs, checked cost and one prepared result matching topology preview; validates every segment/headroom/overlap, reserves IDs, but changes no entity/index/cash
prepare_topology_deletion FixedUpdate/FixedGameSet::Act Commands, MessageReader<PrepareDeletion>, Res<Assets<WorldDefinitionsAsset>>, Query<(Option<&TopologyNode>,Option<&FenceEdge>,Option<&PathTile>,Option<&Gate>,&PersistentId)>, Query<&PersistentId>, MessageWriter<TopologyEditPrepared>, MessageWriter<TopologyEditPreparationRejected> exact removed snapshots/refund cost on transaction target has G08-owned topology; reads all dependent portals/supports before any despawn
apply_topology_edits FixedUpdate/FixedGameSet::Act Commands, MessageReader<CommitTopologyEdit>, Query<&TopologyEdit>, Query<Entity,With<WorldRoot>>, ResMut<TopologyIndex>, MessageWriter<TopologyChanged>, MessageWriter<TopologyEditAcknowledged> exact forward/inverse entities with WorldMember/reserved PersistentIds and index validates full applicability first, then all-or-none; initial/redo only after G07 reports G18 debit completion
set_gate_state FixedUpdate/FixedGameSet::Act MessageReader<SetGateState>, Query<&mut Gate> gate state, change message valid unlocked gate
remove_orphan_supports FixedUpdate/FixedGameSet::Cleanup Commands, Query<(Entity,&PathSupport)>, Query<(),With<PathTile>> orphan despawn changed topology only

5. Preflight producer contract

Not applicable: the engine consumes only native assets produced before launch.

6. Behavioral evidence and disposition

Inspect fence/path/gate/elevated-path definitions and placement UI; oracle/TSVs for ZTFence, ZTPath, portals, rubber band and traversal; commit d5657e5a domain/runtime fence/path files only for evidence. Observe grid size, 45-degree rotation, intersections, cost, auto-gates/supports, slopes and deletion.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

Questions: exact world-to-grid formula (old evidence includes source tile quantization and a 0.785398185 rotation step); rubber-band line rule; overlaps; gate orientation; path curb variants; elevated levels, slopes/headroom/supports.

7. Required behavior

Canonical indexing rejects duplicate nodes/edges/tiles. Placement expands by authentic rules and preparation validates every segment without mutation. G07 completes one checked G18 debit before G08 applies the immutable snapshots; acknowledgement occurs only after the whole index/entity change succeeds. Gates alter traversal without destroying the fence. Elevated paths retain levels and supports. Undo/redo restores IDs/connectivity. Deleting topology cleans portals/supports and triggers containment/navigation.

8. Performance contract

Steady systems allocate/do no work without changes. Lookup is expected O(1); placement is O(new segments), affected rebuilds are regional. Index reserves on load/edit outside settled frames. No all-world connectivity rebuild per frame or path-vector cloning. Count idle/gate allocations, index heap, A14/A17 faults and mesh/support GPU bytes.

9. Acceptance criteria

Deliver exports and tests for canonical edges, duplicate rejection, recovered line expansion, preparation-without-mutation, debit-before-apply, atomic acknowledgement, gate traversal, elevated support cleanup and undo/redo identity. No topology manager world, string token state or speculative geometry. Root bulk-validates construction, habitats and navigation.

10. Required handoff

Report API/files, map/definition dependencies, root registrations/order, G04/G07/G09/G14/G18 contracts, evidence gaps, index seam and allocator/mapping/GPU/ topology validation.

G09 — Habitat containment and membership

1. Identity and outcome

Closed fence topology forms habitats; animals/objects acquire correct habitat membership, breaches update containment, and suitability systems receive exact area, biome and traversability facts. Integration wave 2. Prerequisites: A16, G05 and G08.

2. Exclusive ownership

The package worker owns game/src/plugins/habitat/ only. Root owns module/plugin/message ordering.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
HabitatPlugin Plugin pub struct HabitatPlugin; Registers regional rebuild, membership and containment.
Habitat component pub struct Habitat; Marks a habitat entity.
HabitatRegion component pub struct HabitatRegion { pub cells: Box<[IVec2]>, pub area_m2: f32, pub boundary_edges: Box<[Entity]> } Deterministically sorted enclosed cells and fence edges; rebuilt only on topology change.
HabitatSummary component pub struct HabitatSummary { pub land_m2: f32, pub water_m2: f32, pub biome_area_m2: Box<[(AssetId,f32)]>, pub breached: bool } Derived exact regional facts; biome pairs sorted by ID.
HabitatMember component pub struct HabitatMember(pub Entity); Entity's current habitat; absent means none.
HabitatLocatable component pub struct HabitatLocatable; Explicit marker for entities whose habitat membership follows world position; prevents querying every transformed entity.
Containment component pub struct Containment { pub habitat: Entity, pub contained: bool } Animal containment relative to membership/boundary.
HabitatIndex resource pub struct HabitatIndex { pub cells: bevy::platform::collections::HashMap<IVec2,Entity> } Sole bounded cell-to-habitat index; component state remains authoritative.
HabitatChanged message pub struct HabitatChanged { pub habitat: Entity, pub topology_bounds: IRect } Region membership/summary changed.
ContainmentChanged message pub struct ContainmentChanged { pub entity: Entity, pub habitat: Option<Entity>, pub contained: bool } Emitted on material membership/containment transition.
RebuildHabitats message pub struct RebuildHabitats(pub IRect); Regional request derived from G08 TopologyChanged.
locate_habitat function pub fn locate_habitat(index: &HabitatIndex, world_xz: Vec2, cell_size_m: f32) -> Option<Entity> Allocation-free canonical floor conversion and lookup.

Cell conversion uses the active A16 TerrainArchive.cell_size_cm; preflight validates that G08 topology cells use that same grid. Do not add a second grid-scale resource or generic membership trait.

4. ECS ownership

Regions/summaries are live components on habitat entities; index is derived bounded acceleration data. Objects and animals carry one direct relationship.

System Schedule/set Exact query and parameters Writes Run condition/order
request_habitat_rebuilds FixedUpdate/FixedGameSet::Cleanup MessageReader<TopologyChanged>, MessageWriter<RebuildHabitats> regional requests after G08 edits
rebuild_habitat_regions FixedUpdate/FixedGameSet::Cleanup Commands, MessageReader<RebuildHabitats>, ResMut<HabitatIndex>, Res<TerrainIndex>, Res<Assets<TerrainAsset>>, Query<(Entity,&FenceEdge,Option<&Gate>)>, Query<(&TerrainChunk,Option<&EditedTerrainSamples>)>, Query<Entity,With<WorldRoot>> habitats with WorldMember, regions, summaries, index and HabitatChanged exactly one world root; affected region; indexed mapped-or-overlay sampling
update_habitat_membership FixedUpdate/FixedGameSet::Cleanup Commands, Res<HabitatIndex>, Query<(Entity,&GlobalTransform,Option<&HabitatMember>),(With<HabitatLocatable>,Or<(Changed<GlobalTransform>,Without<HabitatMember>)>)>, MessageWriter<ContainmentChanged> direct relationship only explicitly eligible moving entities
update_containment_on_change FixedUpdate/FixedGameSet::Cleanup Query<(Entity,&HabitatMember,&mut Containment)>, MessageReader<HabitatChanged>, MessageWriter<ContainmentChanged> containment changed habitat only

5. Preflight producer contract

Not applicable: the engine consumes only native assets produced before launch.

6. Behavioral evidence and disposition

Inspect habitat/fence/biome/species data; oracle/TSVs for habitat manager, containment, traversable area and suitability; commit d5657e5a habitat regions and animal assessment solely for evidence. Observe enclosure recognition, gates, map boundary, multi-region fences, escape and habitat UI area calculation.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

Questions: flood-fill domain and diagonal rules; map-edge containment; gates/ portals; fence traversability by species; habitat identity persistence through edits; water/biome area integration; what objects count as members.

7. Required behavior

Closed valid boundaries produce deterministic regions; open boundaries mark breach/no enclosure according to recovered rules. Regional edits preserve unaffected habitat entities/IDs where possible. Moved/spawned entities update membership once; deleted habitats remove relationships. Summaries match terrain cells and expose facts, not a guessed suitability score. No rectangular shortcut except where evidence defines one.

8. Performance contract

No per-frame global flood fill or entity scan. Rebuild is O(affected cells + edges) on topology events; membership is changed-transform O(1) lookup. Region arrays allocate only on rebuild and replace old storage. Idle/movement allocation tests are zero; track index/region heap and terrain mapped pages; no GPU ownership.

9. Acceptance criteria

Deliver exports and tests for enclosure/breach, deterministic regions, regional preservation, membership transitions and area summaries using recovered grid rules. No habitat world mirror, bounding-box guess or per-frame rebuild. Root validates with fence edits, animals and suitability.

10. Required handoff

Report API/files, registrations/filter wiring, G05/G08/G11/G12 symbols, evidence gaps, index seam and allocator/region/gameplay validation.

G10 — Scenery and facility placement

1. Identity and outcome

Scenery and facilities use authored footprints, orientation, entrances, terrain/topology/habitat constraints, locks and costs to produce an authentic preview and atomic placed entity. Integration wave 2. Prerequisites: A14, A15, G04, G05, G07–G09 and G18.

2. Exclusive ownership

The package worker owns game/src/plugins/placement/ only. Root owns registrations/order and G18/G23 integration.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
PlacementPlugin Plugin pub struct PlacementPlugin; Registers preview construction, rule evaluation, commit and history.
Placeable component pub struct Placeable { pub definition: AssetId } Immutable A14 object/placeable definition identity.
FootprintOccupancy component pub struct FootprintOccupancy { pub origin: IVec2, pub quarter_turns: u8 } Fixed live placement fact; occupied cells are iterated directly from A14's mapped footprint range and transformed without a per-entity heap copy.
AuthoredEntrance component pub struct AuthoredEntrance { pub local_position: Vec3, pub local_forward: Vec3 } Definition-provided service/path docking point.
PlacementIndex resource pub struct PlacementIndex { pub occupied: HashMap<IVec2,Entity> } Sole bounded footprint acceleration index.
EvaluatePlacement message pub struct EvaluatePlacement { pub preview: Entity, pub definition: AssetId, pub transform: Transform } Latest preview fact; repeated requests replace validity.
PlaceObject message pub struct PlaceObject { pub preview: Entity } Player/domain request routed back through G07 CommitConstruction; never mutates or debits directly.
RemovePlacedObject message pub struct RemovePlacedObject { pub entity: Entity } Player/domain deletion request routed through G07 DeleteEntity; G10 reads footprint before any removal.
ObjectPlaced message pub struct ObjectPlaced { pub transaction: Entity, pub entity: Entity, pub definition: AssetId } Emitted after entity, occupancy and economy effects succeed.
PlacementMutation enum Create \| Remove Meaning of the initial commit; undo applies the opposite and redo repeats it.
PlacementEdit component pub struct PlacementEdit { pub entity: PersistentId, pub definition: AssetId, pub transform: Transform, pub cells: Box<[IVec2]>, pub mutation: PlacementMutation, pub applied_entity: Option<Entity> } Exact reversible object placement/removal on G07 transaction.
PlacementEditPrepared message pub struct PlacementEditPrepared { pub transaction: Entity, pub cost: Money } Immutable edit and checked cost/refund are attached without world/index/cash mutation.
PlacementEditPreparationRejected message pub struct PlacementEditPreparationRejected { pub transaction: Entity, pub reason: PlacementFailure } Typed pre-debit rejection.
CommitPlacementEdit message pub struct CommitPlacementEdit { pub transaction: Entity, pub application: EditApplication } Exact G07-authorized application; forward create/redo follows completed G18 debit.
PlacementEditAcknowledged message pub struct PlacementEditAcknowledged { pub transaction: Entity, pub application: EditApplication, pub accepted: bool } One all-or-none placement-domain result.
validate_placement function pub fn validate_placement(definition: &ArchivedPlaceableDefinition, transform: &Transform, terrain: impl Fn(Vec2)->Option<TerrainPoint>, occupied: impl Fn(IVec2)->bool, habitat: Option<Entity>, unlocked: bool, affordable: bool) -> PlacementValidity Pure, allocation-free rule evaluation in authoritative order.

A14 must freeze ArchivedPlaceableDefinition including footprint cells, pivot, rotation increments, constraints, cost, unlock and entrance. G18/G23 provide affordable/unlocked facts without G10 owning money/progression copies.

4. ECS ownership

Placed entities carry their own definition, transform, footprint and entrance. The resource accelerates occupancy only and is reconstructed from components.

System Schedule/set Exact query and parameters Writes Run condition/order
create_object_preview Update/GameSet::Intent Commands, Res<ConstructionTool>, Query<&ConstructionCursor>, Res<Assets<WorldDefinitionsAsset>>, Query<Entity,With<ConstructionPreview>>, Query<Entity,With<WorldRoot>> one preview entity/handle with WorldMember active Place tool; exactly one world root
evaluate_object_preview Update/GameSet::Intent MessageReader<EvaluatePlacement>, Res<PlacementIndex>, Res<TerrainIndex>, Res<Assets<TerrainAsset>>, Res<Assets<WorldDefinitionsAsset>>, Query<(&TerrainChunk,Option<&EditedTerrainSamples>)>, Query<&HabitatMember>, Query<&mut ConstructionPreview> validity only changed input; indexed mapped-or-overlay sampling
route_object_commit_requests Update/GameSet::Intent MessageReader<PlaceObject>, MessageWriter<CommitConstruction> exact G07 request matching active object preview only
route_object_removal_requests Update/GameSet::Intent MessageReader<RemovePlacedObject>, MessageWriter<DeleteEntity> exact G07 deletion request never despawns directly
prepare_object_placement FixedUpdate/FixedGameSet::Act Commands, MessageReader<PrepareConstruction>, Res<ConstructionTool>, Query<&ConstructionPreview>, Res<Assets<WorldDefinitionsAsset>>, Res<TerrainIndex>, Res<PlacementIndex>, ResMut<PersistentIdAllocator>, MessageWriter<PlacementEditPrepared>, MessageWriter<PlacementEditPreparationRejected> immutable create edit with allocator-issued ID, exact cells and checked cost ConstructionTool::Place; revalidates preview; no entity/index/cash mutation
prepare_object_removal FixedUpdate/FixedGameSet::Act Commands, MessageReader<PrepareDeletion>, Res<Assets<WorldDefinitionsAsset>>, Query<(&PersistentId,&Placeable,&Transform,&FootprintOccupancy)>, MessageWriter<PlacementEditPrepared>, MessageWriter<PlacementEditPreparationRejected> immutable remove edit and evidenced refund reads mapped footprint/dependencies before despawn; no mutation
apply_placement_edits FixedUpdate/FixedGameSet::Act Commands, MessageReader<CommitPlacementEdit>, Query<&mut PlacementEdit>, Query<Entity,With<WorldRoot>>, ResMut<PlacementIndex>, Res<Assets<ScenePrefabAsset>>, MessageWriter<ObjectPlaced>, MessageWriter<PlacementEditAcknowledged> exact create/remove or inverse, WorldMember, reserved PersistentId and occupancy validates every cell/entity first, then applies all or rejects all; initial/redo only after G07's G18 payment gate

5. Preflight producer contract

Not applicable: the engine consumes only native assets produced before launch.

6. Behavioral evidence and disposition

Inspect entity/footprint/placement/UI/localization records; oracle/TSVs for place object, footprint, collision, affordability and research predicates; commit d5657e5a placement/world/facility files for evidence only. Observe preview LOD/colors, grid snap, free rotation, entrances, slopes, tank/habitat rules, repeated placement, cost/refund and errors.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

Known original failure tokens include unaffordable, unresearched, overlap, too-steep, no-headroom and out-of-traversable-area. Questions: predicate order, cell rounding, rotation step, footprint pivot, terrain tolerance, object-object exceptions and special placement types. Recover before coding each rule.

7. Required behavior

Preview uses the same transform/footprint/rules as commit. First failing rule produces the authentic typed/localizable reason. Preparation freezes the complete edit and cost/refund without mutation. G07 completes the required G18 economy leg before CommitPlacementEdit; commit is atomic and creates natural typed prefab components/handles, not a generic entity bag. Occupancy is updated with world mutation. Undo/redo restores exact persistent identity and cells. Removal releases cells and dependent domains clean relationships.

8. Performance contract

Idle systems allocate zero. Preview validation is O(footprint cells) with O(1) indexed checks and changed-input gating. Preview handles are reused; placement arrays allocate only at commit. No whole-world collision scan, path construction or strings. Count idle/unchanged validation; track placement index heap, A14/A15 faults and referenced model/material GPU bytes.

9. Acceptance criteria

Deliver exports and tests for transform/footprint parity, predicate precedence, prepare-without-mutation, payment-before-apply, exact acknowledgement, atomic failure, occupancy, rotation and undo/redo. No guessed fallback rules, generic prefab runtime or duplicate economy/progression state. Root bulk-tests representative scenery/facilities against original behavior.

10. Required handoff

Report API/files, A14/A15/G05/G07–G09/G18/G23 contracts, registrations/order, evidence gaps, atomicity seams and allocator/mapping/GPU/placement validation.

G11 — Animal lifecycle and family

1. Identity and outcome

Animals are adopted from native species/variant data, age through authentic stages, mate, become pregnant, give birth with correct inheritance/family links, and can be released. Integration wave 3. Prerequisites: A03, A14, G04 and G09.

2. Exclusive ownership

The package worker owns game/src/plugins/animal_lifecycle/ only. Root owns declarations, registrations, schedules and later economy/health/persistence wiring.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
AnimalLifecyclePlugin Plugin pub struct AnimalLifecyclePlugin; Registers adoption, ageing, reproduction, birth, release and cleanup.
Animal component pub struct Animal; Marker for a live animal entity.
SpeciesHandle component pub struct SpeciesHandle { pub catalog: Handle<SpeciesCatalogAsset>, pub species: AssetId } Sole owning A03 catalogue handle plus stable record ID; lookup stays mapped and allocation-free.
AnimalVariant component pub struct AnimalVariant(pub AssetId); Valid A03 variant ID for the species.
AnimalSex component pub struct AnimalSex(pub Sex); Thin live component around A03's exact Sex; live animals may only use Female or Male, never authored fallback Any.
AnimalLifeStage component pub struct AnimalLifeStage(pub LifeStage); Wraps A03's exact Juvenile \| Young \| Adult \| Elder vocabulary.
Age component pub struct Age { pub born_tick: u64 } Exact G32 tick of birth/import-derived birth; current age is saturating tick subtraction.
Pregnancy component pub struct Pregnancy { pub father: Entity, pub due_tick: u64 } Female-only absolute G32 due tick; no float accumulation.
BirthDue component pub struct BirthDue; One-shot marker preventing duplicate due requests before delivery removes pregnancy.
ReproductionRng component pub struct ReproductionRng(pub DeterministicRng); Domain-local G32 stream derived from ZooSeed plus PersistentId; sole source for reproduction rolls.
Parents component pub struct Parents { pub mother: Option<Entity>, pub father: Option<Entity> } Direct lineage links; missing/deleted ancestors become None.
AdoptAnimal message pub struct AdoptAnimal { pub catalog: Handle<SpeciesCatalogAsset>, pub species: AssetId, pub variant: Option<AssetId>, pub sex: Option<Sex>, pub habitat: Entity, pub transform: Transform } Uses A03 IDs/vocabulary directly; Sex::Any is rejected for an explicit live choice.
PendingAdoption component pub struct PendingAdoption { pub catalog: Handle<SpeciesCatalogAsset>, pub species: AssetId, pub variant: AssetId, pub sex: Sex, pub habitat: Entity, pub transform: Transform, pub cost: Money } Immutable resolved adoption on its operation entity; no animal exists yet.
AnimalAdopted message pub struct AnimalAdopted { pub operation: Entity, pub animal: Entity, pub cost: Money } Emitted after matching economy completion and spawn.
SpawnAnimalRequest message pub struct SpawnAnimalRequest { pub operation: Entity, pub catalog: Handle<SpeciesCatalogAsset>, pub species: AssetId, pub variant: Option<AssetId>, pub sex: Option<Sex>, pub habitat: Entity, pub transform: Transform, pub parents: Parents } Natural-owner creation boundary used after adoption payment and by G29 cloning; operation is caller-owned correlation, while G11 resolves omitted variant/sex with its deterministic domain rules.
AnimalSpawned message pub struct AnimalSpawned { pub operation: Entity, pub animal: Entity } Emitted once only after the complete ordinary animal bundle, WorldMember, and fresh PersistentId exist.
AnimalSpawnRejected message pub struct AnimalSpawnRejected { pub operation: Entity, pub reason: AnimalSpawnFailure } Typed failure with no partial animal.
AnimalSpawnFailure enum MissingSpecies, InvalidVariant, InvalidSex, InvalidHabitat, MarinePlacement(MarinePlacementFailure), PersistentId(PersistentIdError) Closed engine-side creation rejection vocabulary.
MateAnimals message pub struct MateAnimals { pub female: Entity, pub male: Entity } G13 behavior outcome; conception eligibility is revalidated here.
AnimalBorn message pub struct AnimalBorn { pub child: Entity, pub mother: Entity, pub father: Entity } Emitted after child is fully spawned/linked.
ReleaseAnimal message pub struct ReleaseAnimal(pub Entity); Requests authentic eligibility/economy/progression handling.
AnimalReleased message pub struct AnimalReleased { pub animal: PersistentId, pub species: AssetId } Stable completion fact after cleanup.

A03 owns Sex, LifeStage, SpeciesCatalogAsset, variant probabilities, stage_start_ticks, breeding compatibility, gestation_ticks and litter ranges. Exact adoption inventory/cost requires an A14/A03 evidence-backed extension; G11 must not invent it. G18 owns Money; G09 membership is reused.

4. ECS ownership

Each component is one animal fact. There is no animal collection resource; lineage is queried from Parents, not mirrored child vectors.

System Schedule/set Exact query and parameters Writes Run condition/order
request_adoptions FixedUpdate/FixedGameSet::Act Commands, MessageReader<AdoptAnimal>, Res<ZooSeed>, Res<Assets<SpeciesCatalogAsset>>, Res<Assets<WorldDefinitionsAsset>>, Query<&WorldMember,With<Habitat>>, Query<(&Tank,&TankGeometry,&TankCapacity)>, MessageWriter<MarinePlacementRejected>, MessageWriter<TransactionRequest>, MessageWriter<SpawnAnimalRequest> creates one operation carrying the habitat's WorldMember plus resolved PendingAdoption; positive cost emits kind=Purchase, operation=entity, zero cost immediately requests the same natural spawn path validates ordinary habitat facts and, for marine species, G25 tank/depth/space/water before payment; no G25 interception
authorize_paid_adoptions FixedUpdate/FixedGameSet::Act MessageReader<TransactionCompleted>, Query<&PendingAdoption>, MessageWriter<SpawnAnimalRequest> Requests one correlated natural spawn while retaining pending adoption Matching operation/purchase only.
spawn_requested_animals FixedUpdate/FixedGameSet::Act Commands, MessageReader<SpawnAnimalRequest>, Res<ZooSeed>, Res<Assets<SpeciesCatalogAsset>>, Res<Assets<WorldDefinitionsAsset>>, ResMut<PersistentIdAllocator>, Query<&WorldMember,With<Habitat>>, Query<(&Tank,&TankGeometry,&TankCapacity)>, MessageWriter<AnimalSpawned>, MessageWriter<AnimalSpawnRejected> Validates first, then creates exactly one ordinary animal bundle with allocator-issued ID, habitat root, G09 facts and RNG, or no entity Single authoritative live-animal creation boundary; no payment/adoption semantics.
complete_adoptions FixedUpdate/FixedGameSet::Cleanup Commands, MessageReader<AnimalSpawned>, Query<(Entity,&PendingAdoption)>, MessageWriter<AnimalAdopted> Removes matching operation/pending and acknowledges adoption spawned.operation exact match only.
reject_adoptions FixedUpdate/FixedGameSet::Act Commands, MessageReader<TransactionRejected>, Query<&PendingAdoption> removes operation/pending; no animal/world mutation matching operation only; typed failure to UI
reject_adoption_spawns FixedUpdate/FixedGameSet::Cleanup Commands, MessageReader<AnimalSpawnRejected>, Query<(Entity,&PendingAdoption)>, MessageWriter<TransactionRequest> Removes pending operation and issues an exact refund only when payment had completed Exact operation match; no double refund and no animal.
advance_animal_age FixedUpdate/FixedGameSet::Clock Res<ZooClock>, Res<Assets<SpeciesCatalogAsset>>, Query<(&SpeciesHandle,&Age,&mut AnimalLifeStage),With<Animal>> compares tick-born_tick to A03 stage starts after G32 clock advance
begin_pregnancies FixedUpdate/FixedGameSet::Act Commands, MessageReader<MateAnimals>, Res<Assets<SpeciesCatalogAsset>>, Query<(&SpeciesHandle,&AnimalSex,&AnimalLifeStage,Option<&Pregnancy>),With<Animal>> female pregnancy compatible eligible pair only
mark_due_births FixedUpdate/FixedGameSet::Clock Commands, Res<ZooClock>, Query<(Entity,&Pregnancy),Without<BirthDue>> inserts one due marker clock.tick >= due_tick
deliver_litters FixedUpdate/FixedGameSet::Act Commands, Res<ZooClock>, ResMut<PersistentIdAllocator>, Res<Assets<SpeciesCatalogAsset>>, Query<(&SpeciesHandle,&AnimalVariant,&Pregnancy,&mut ReproductionRng,&Transform,&HabitatMember,&WorldMember),With<BirthDue>>, MessageWriter<AnimalBorn> allocator-issued PersistentId child bundles with mother's world root and born_tick=clock.tick; removes pregnancy/due only after successful complete spawn A03 rules using domain RNG only; no domain-local ID counter
release_animals FixedUpdate/FixedGameSet::Cleanup Commands, MessageReader<ReleaseAnimal>, Query<(&PersistentId,&SpeciesHandle,&AnimalLifeStage,Option<&Pregnancy>),With<Animal>>, MessageWriter<AnimalReleased> cleanup/despawn/completion recovered eligibility only
clear_deleted_parents FixedUpdate/FixedGameSet::Cleanup RemovedComponents<Animal>, Query<&mut Parents> only matching parent fields removal events only

5. Preflight producer contract

Not applicable: the engine consumes only native assets produced before launch.

6. Behavioral evidence and disposition

Inspect species entities, young/adult variants, adoption and AI reproduction data; oracle/TSVs for animal, adoption, lifecycle, family, pregnancy and birth; commit d5657e5a domain/simulation/animal.rs and animal lifecycle systems only for evidence. Observe adoption inventory, sex/variant choice, ageing morph, mating gates, gestation, litter placement, naming, release and family UI.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

G32/A03 own tick conversion. Questions: stage thresholds; conception probability/ cooldown; relatedness; litter distribution; variant inheritance; parent links beyond parents; adoption stock refresh; release restrictions/rewards. All are asset/oracle questions, not constants to invent.

7. Required behavior

Adoption validates availability, habitat and funds atomically. Age crosses each stage once and swaps only variant/presentation handles required by A03. Mating cannot self-pair, violate sex/stage/species/family/cooldown rules or overwrite a pregnancy. Birth uses stored seed, creates all children atomically where required, keeps stable parent links and emits one event each. Release cleans habitat, behavior, navigation and selection relationships through owner events.

8. Performance contract

Clock systems are linear in live/pregnant animals, fixed-step and allocation- free. Spawn/birth/release may allocate ECS entities only on transition. No per-frame species cloning, name strings, family vectors or adoption catalogue copy. Counters cover settled ageing and non-due pregnancy; A03 mapped faults and animal model GPU bytes are separately tracked.

9. Acceptance criteria

Deliver exports and deterministic tests for stage crossing, invalid pairing, gestation/litter/inheritance, parent cleanup and release eligibility using recovered fixtures. No adoption manager, animal bag, guessed rates, placeholder birth or duplicated catalogue. Root bulk-tests economy, UI, save and behavior.

10. Required handoff

Report API/files, registrations/order, A03/A14/G04/G09/G13/G18/G22/G23/G32 symbols, evidence gaps, lineage seams and allocation/mapping/GPU/lifecycle validation.

G12 — Animal needs, welfare and suitability

1. Identity and outcome

Each animal has small explicit need components that decay/recover by species rules; habitat suitability and overall welfare are derived by narrow systems and drive authentic statuses and behavior. Integration wave 3. Prerequisites: G09 and G11 plus A03 species data.

2. Exclusive ownership

The package worker owns game/src/plugins/animal_welfare/ only. Root owns registration, scheduling and information/health integration.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
AnimalWelfarePlugin Plugin pub struct AnimalWelfarePlugin; Registers explicit need and derived-fact systems.
Hunger component pub struct Hunger(pub i32); A03 satisfaction in signed Q16 permille, clamped to 0..=(1000 << 16); preserves fractional per-tick decay.
Thirst component pub struct Thirst(pub i32); Thirst satisfaction in the same Q16 permille representation.
RestNeed component pub struct RestNeed(pub i32); Exact A03 NeedKind::Rest Q16 live value.
PrivacyNeed component pub struct PrivacyNeed(pub i32); Exact A03 privacy Q16 live value.
SocialNeed component pub struct SocialNeed(pub i32); Exact A03 social Q16 live value.
ExerciseNeed component pub struct ExerciseNeed(pub i32); Exact A03 exercise Q16 live value.
StimulationNeed component pub struct StimulationNeed(pub i32); Exact A03 stimulation Q16 live value.
EnvironmentNeed component pub struct EnvironmentNeed(pub i32); Exact A03 environment Q16 live value.
KeeperCareNeed component pub struct KeeperCareNeed(pub i32); Exact A03 keeper-care Q16 live value.
HealthNeed component pub struct HealthNeed(pub i32); A03 welfare-health Q16 value; distinct from G16 disease/physical health state.
HabitatSuitability component pub struct HabitatSuitability { pub space: u16, pub biome: u16, pub foliage: u16, pub shelter: u16, pub privacy: u16, pub overall: u16 } Each derived permille 0..=1000.
AnimalWelfare component pub struct AnimalWelfare(pub u16); Recovered aggregate permille; not a need table.
WelfareBand component enum Critical \| Poor \| Fair \| Good \| Excellent Thresholds come from native definitions/localization semantics.
AdjustHunger message pub struct AdjustHunger { pub animal: Entity, pub delta_q16: i32 } Focused Q16 permille adjustment.
AdjustThirst message pub struct AdjustThirst { pub animal: Entity, pub delta_q16: i32 } Focused thirst adjustment.
AdjustRest message pub struct AdjustRest { pub animal: Entity, pub delta_q16: i32 } Focused rest adjustment.
AdjustPrivacy message pub struct AdjustPrivacy { pub animal: Entity, pub delta_q16: i32 } Focused privacy adjustment.
AdjustSocial message pub struct AdjustSocial { pub animal: Entity, pub delta_q16: i32 } Focused social adjustment.
AdjustExercise message pub struct AdjustExercise { pub animal: Entity, pub delta_q16: i32 } Focused exercise adjustment.
AdjustStimulation message pub struct AdjustStimulation { pub animal: Entity, pub delta_q16: i32 } Focused stimulation adjustment.
AdjustEnvironment message pub struct AdjustEnvironment { pub animal: Entity, pub delta_q16: i32 } Focused environment adjustment.
AdjustKeeperCare message pub struct AdjustKeeperCare { pub animal: Entity, pub delta_q16: i32 } Focused keeper-care adjustment.
AdjustHealthNeed message pub struct AdjustHealthNeed { pub animal: Entity, pub delta_q16: i32 } Focused welfare-health adjustment.
NeedChanged message pub struct NeedChanged { pub animal: Entity, pub need: NeedKind, pub value: u16 } Uses A03's exact vocabulary/value units; threshold crossings only.
WelfareChanged message pub struct WelfareChanged { pub animal: Entity, pub old: WelfareBand, pub new: WelfareBand } Material band transition.

A03 owns NeedKind, SpeciesNeedRecord and exact signed Q16 permille/tick units; A10 references that same type. G09 supplies habitat facts.

4. ECS ownership

Every need is independently queryable. Suitability/welfare are derived components retained only because multiple systems/UI consume them.

System Schedule/set Exact query and parameters Writes Run condition/order
initialize_animal_needs FixedUpdate/FixedGameSet::Cleanup Commands, Res<Assets<SpeciesCatalogAsset>>, Query<(Entity,&SpeciesHandle),(Added<Animal>,Without<AnimalWelfare>)> ten independent Q16 need components plus initial suitability, welfare and band Fresh G11 animals only; G22-restored animals already carry AnimalWelfare and are untouched.
decay_hunger FixedUpdate/FixedGameSet::Clock Res<Assets<SpeciesCatalogAsset>>, Query<(&SpeciesHandle,&mut Hunger),With<Animal>> adds A03 decay_per_tick_q16 with checked saturating clamp once per running G32 simulation tick, after advance_zoo_clock
decay_thirst FixedUpdate/FixedGameSet::Clock Res<Assets<SpeciesCatalogAsset>>, Query<(&SpeciesHandle,&mut Thirst),With<Animal>> thirst Q16 decay once per running G32 simulation tick, after advance_zoo_clock
decay_rest FixedUpdate/FixedGameSet::Clock Res<Assets<SpeciesCatalogAsset>>, Query<(&SpeciesHandle,&mut RestNeed),With<Animal>> rest Q16 decay once per running G32 simulation tick, after advance_zoo_clock
decay_privacy FixedUpdate/FixedGameSet::Clock Res<Assets<SpeciesCatalogAsset>>, Query<(&SpeciesHandle,&mut PrivacyNeed),With<Animal>> privacy Q16 decay once per running G32 simulation tick, after advance_zoo_clock
decay_social FixedUpdate/FixedGameSet::Clock Res<Assets<SpeciesCatalogAsset>>, Query<(&SpeciesHandle,&mut SocialNeed),With<Animal>> social Q16 decay once per running G32 simulation tick, after advance_zoo_clock
decay_exercise FixedUpdate/FixedGameSet::Clock Res<Assets<SpeciesCatalogAsset>>, Query<(&SpeciesHandle,&mut ExerciseNeed),With<Animal>> exercise Q16 decay once per running G32 simulation tick, after advance_zoo_clock
decay_stimulation FixedUpdate/FixedGameSet::Clock Res<Assets<SpeciesCatalogAsset>>, Query<(&SpeciesHandle,&mut StimulationNeed),With<Animal>> stimulation Q16 decay once per running G32 simulation tick, after advance_zoo_clock
decay_environment FixedUpdate/FixedGameSet::Clock Res<Assets<SpeciesCatalogAsset>>, Query<(&SpeciesHandle,&mut EnvironmentNeed),With<Animal>> environment Q16 decay once per running G32 simulation tick, after advance_zoo_clock
decay_keeper_care FixedUpdate/FixedGameSet::Clock Res<Assets<SpeciesCatalogAsset>>, Query<(&SpeciesHandle,&mut KeeperCareNeed),With<Animal>> keeper-care Q16 decay once per running G32 simulation tick, after advance_zoo_clock
decay_health_need FixedUpdate/FixedGameSet::Clock Res<Assets<SpeciesCatalogAsset>>, Query<(&SpeciesHandle,&mut HealthNeed),With<Animal>> welfare-health Q16 decay once per running G32 simulation tick, after advance_zoo_clock
adjust_hunger FixedUpdate/FixedGameSet::Act MessageReader<AdjustHunger>, Query<&mut Hunger,With<Animal>> hunger only matching target
adjust_thirst FixedUpdate/FixedGameSet::Act MessageReader<AdjustThirst>, Query<&mut Thirst,With<Animal>> thirst only matching target
adjust_rest FixedUpdate/FixedGameSet::Act MessageReader<AdjustRest>, Query<&mut RestNeed,With<Animal>> rest only matching target
adjust_privacy FixedUpdate/FixedGameSet::Act MessageReader<AdjustPrivacy>, Query<&mut PrivacyNeed,With<Animal>> privacy only matching target
adjust_social FixedUpdate/FixedGameSet::Act MessageReader<AdjustSocial>, Query<&mut SocialNeed,With<Animal>> social only matching target
adjust_exercise FixedUpdate/FixedGameSet::Act MessageReader<AdjustExercise>, Query<&mut ExerciseNeed,With<Animal>> exercise only matching target
adjust_stimulation FixedUpdate/FixedGameSet::Act MessageReader<AdjustStimulation>, Query<&mut StimulationNeed,With<Animal>> stimulation only matching target
adjust_environment FixedUpdate/FixedGameSet::Act MessageReader<AdjustEnvironment>, Query<&mut EnvironmentNeed,With<Animal>> environment only matching target
adjust_keeper_care FixedUpdate/FixedGameSet::Act MessageReader<AdjustKeeperCare>, Query<&mut KeeperCareNeed,With<Animal>> keeper care only matching target
adjust_health_need FixedUpdate/FixedGameSet::Act MessageReader<AdjustHealthNeed>, Query<&mut HealthNeed,With<Animal>> health need only matching target
assess_habitat_suitability FixedUpdate/FixedGameSet::Think Res<Assets<SpeciesCatalogAsset>>, MessageReader<HabitatChanged>, Query<(&SpeciesHandle,&HabitatMember,&mut HabitatSuitability),With<Animal>>, Query<&HabitatSummary> affected members' suitability habitat/member change only
derive_animal_welfare FixedUpdate/FixedGameSet::Think Res<Assets<SpeciesCatalogAsset>>, Query<(&SpeciesHandle,&Hunger,&Thirst,&RestNeed,&PrivacyNeed,&SocialNeed,&ExerciseNeed,&StimulationNeed,&EnvironmentNeed,&KeeperCareNeed,&HealthNeed,&HabitatSuitability,&mut AnimalWelfare,&mut WelfareBand),With<Animal>> aggregate/band/messages changed facts only; this one formula genuinely consumes every A03 need

5. Preflight producer contract

Not applicable: the engine consumes only native assets produced before launch.

6. Behavioral evidence and disposition

Inspect species need definitions, needAdjusts, biome/habitat preferences and status localization; oracle/TSVs for animal needs/happy factors/habitat score; commit d5657e5a animal need and habitat assessment code only as evidence. Live observation settles displayed scale, arrows, thresholds and update cadence.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

Questions: whether source values are satisfaction or deficit; exact time scale, decay and clamping; happiness aggregation; space/biome/foliage/shelter/privacy formulas; species/group effects and critical statuses. Recover each formula.

7. Required behavior

New animals initialize from A03, not universal 100s. A03 decay_per_tick_q16 is applied once per running G32 simulation tick directly to the live Q16 component, with integer saturating clamp and no private time conversion, float residual or lost fractional decay. Each need changes only by its own rate/effects and clamps exactly. Suitability reacts to membership/ habitat changes and uses authored species preferences and measured G09 facts. Welfare aggregation and bands match evidence. Threshold messages are edge- triggered. Disease/health belongs to G16; food transactions to G15.

8. Performance contract

All steady systems allocate zero. Decay/derivation are O(relevant animals) on fixed ticks with change/event filters; suitability is O(animals in affected habitats) and must not scan every habitat for each animal. No need maps, strings, per-frame asset clones or vectors. Count representative settled need and welfare passes; A03 pages separate; no GPU ownership.

9. Acceptance criteria

Deliver all exports and tests using recovered fixtures for decay, clamp, adjustment, suitability, aggregate and single threshold emission. No generic Needs component, guessed percentages, animal update dispatcher or no-op score. Root validates status UI, feeding/behavior and habitat edits.

10. Required handoff

Report API/files, exact A03 fields/formulas, registrations/order, G09/G11/G13/ G15/G16/G21/G32 consumers, evidence gaps and allocation/mapping/gameplay validation.

G13 — Data-authored animal task selection

1. Identity and outcome

Compiled A10 rules score animal tasks through focused ECS systems, select one deterministically, and expose a changed typed task for the domain that executes it. This is not a behavior VM or animal update dispatcher. Integration wave 3. Prerequisites: A03, A10, G09, G11, G12, G14, G15 and G32.

2. Exclusive ownership

The package worker owns game/src/plugins/animal_behavior/ only. Root owns registrations, ordering and command-executor integration.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
AnimalBehaviorPlugin Plugin pub struct AnimalBehaviorPlugin; Registers hydration, focused scorers, selection and completion.
BehaviorHandle component pub struct BehaviorHandle { pub asset: Handle<BehaviorProgramAsset>, pub set: AssetId } Sole mapped A10 archive/set reference.
TaskCandidate value #[repr(C)] pub struct TaskCandidate { pub program: AssetId, pub first_action: u32, pub score: i32, pub target: Option<Entity> } Inline copyable fixed-point candidate; no heap payload.
HungerCandidate component pub struct HungerCandidate(pub Option<TaskCandidate>); Written only by hunger scorer.
ThirstCandidate component pub struct ThirstCandidate(pub Option<TaskCandidate>); Written only by thirst scorer.
RestCandidate component pub struct RestCandidate(pub Option<TaskCandidate>); Written only by rest scorer.
WelfareCandidate component pub struct WelfareCandidate(pub Option<TaskCandidate>); Written only by welfare scorer.
MatingCandidate component pub struct MatingCandidate(pub Option<TaskCandidate>); Written only by the reproduction-eligibility scorer using G11 lifecycle facts.
ActiveTask component pub struct ActiveTask { pub program: AssetId, pub action: u32, pub target: Option<Entity>, pub phase: TaskPhase } Program/action indexes mapped A10 records.
TaskPhase enum Starting \| Running Executors change Starting to their own focused execution component then report completion.
BehaviorRng component pub struct BehaviorRng(pub DeterministicRng); Domain-local G32 stream derived from zoo seed and persistent animal ID.
BehaviorThinkSchedule component pub struct BehaviorThinkSchedule { pub next_tick: u64 } Per-agent integer cadence derived from A10 BehaviorSet::think_interval_ticks; no render-frame thinking.
TaskCompleted message pub struct TaskCompleted { pub animal: Entity, pub program: AssetId, pub outcome: TaskOutcome } Completion for the current program only.
TaskOutcome enum Succeeded \| Failed \| Interrupted Fixed lifecycle result; scoring/cooldown effects come from A10.
CancelTask message pub struct CancelTask { pub animal: Entity, pub reason: TaskCancelReason } Typed exceptional cancellation.
TaskCancelReason enum TargetGone \| Unreachable \| NeedSatisfied \| HabitatChanged \| LifecycleChanged No strings or opaque codes.

A10 owns BehaviorProgramAsset, exact BehaviorSet, BehaviorAction, BehaviorInput and A03 NeedKind references. BehaviorHandle::set resolves by MappedBehaviorArchive::set, and scorers traverse only MappedBehaviorArchive::set_programs plus direct program lookup. No per-entity program list or unresolved root extension remains. Executors inspect only the current mapped action on Changed<ActiveTask> and attach focused components.

4. ECS ownership

An animal carries an archive/set reference, inline disjoint candidate components, active task and RNG. Candidate writers touch different component types and can run in parallel. No manager/vector/blackboard exists.

System Schedule/set Exact query and parameters Writes Run condition/order
hydrate_behavior_agents FixedUpdate/FixedGameSet::Cleanup Commands, Res<ZooSeed>, Res<ZooClock>, Res<Assets<BehaviorProgramAsset>>, Query<(Entity,&PersistentId,&BehaviorHandle),Without<BehaviorRng>> inline candidates, RNG and BehaviorThinkSchedule once per agent after exact A10 set validation
score_hunger_tasks FixedUpdate/FixedGameSet::Think Res<ZooClock>, Res<Assets<BehaviorProgramAsset>>, Query<(&BehaviorHandle,&BehaviorThinkSchedule,&Hunger,&mut HungerCandidate),With<Animal>> hunger candidate from exact mapped set programs clock.tick >= next_tick
score_thirst_tasks FixedUpdate/FixedGameSet::Think Res<ZooClock>, Res<Assets<BehaviorProgramAsset>>, Query<(&BehaviorHandle,&BehaviorThinkSchedule,&Thirst,&mut ThirstCandidate),With<Animal>> thirst candidate from exact mapped set programs due only; parallel with hunger
score_rest_tasks FixedUpdate/FixedGameSet::Think Res<ZooClock>, Res<Assets<BehaviorProgramAsset>>, Query<(&BehaviorHandle,&BehaviorThinkSchedule,&RestNeed,&mut RestCandidate),With<Animal>> rest candidate from exact mapped set programs due only; parallel with hunger and thirst
score_welfare_tasks FixedUpdate/FixedGameSet::Think Res<ZooClock>, Res<Assets<BehaviorProgramAsset>>, Query<(&BehaviorHandle,&BehaviorThinkSchedule,&AnimalWelfare,&HabitatSuitability,&mut WelfareCandidate),With<Animal>> welfare candidate from exact mapped set programs due only; parallel with the other scorers
score_mating_tasks FixedUpdate/FixedGameSet::Think Res<ZooClock>, Res<Assets<BehaviorProgramAsset>>, Res<SpatialGrid>, Query<(Entity,&BehaviorHandle,&BehaviorThinkSchedule,&SpeciesHandle,&AnimalSex,&AnimalLifeStage,Option<&Pregnancy>,&HabitatMember,&GlobalTransform,&mut MatingCandidate),With<Animal>> mating candidate with an exact eligible nearby mate target Due only; uses A03/A10 eligibility and bounded G14 neighbours, never all-pairs.
select_animal_tasks FixedUpdate/FixedGameSet::Think Commands, Res<ZooClock>, Res<Assets<BehaviorProgramAsset>>, Query<(Entity,&BehaviorHandle,&HungerCandidate,&ThirstCandidate,&RestCandidate,&WelfareCandidate,&MatingCandidate,Option<&FoodCandidate>,Option<&DrinkCandidate>,&mut BehaviorRng,&mut BehaviorThinkSchedule,Option<&ActiveTask>),With<Animal>> active task and next_tick = clock.tick + set.think_interval_ticks due after all scorers; stable reduction across the complete frozen candidate tuple; A10 idle program if none wins
finish_animal_tasks FixedUpdate/FixedGameSet::Cleanup Commands, MessageReader<TaskCompleted>, Query<&ActiveTask,With<Animal>> removes matching active task/applies A10 cooldown state exact task match only
cancel_invalid_tasks FixedUpdate/FixedGameSet::Cleanup Commands, MessageReader<CancelTask>, Query<&ActiveTask,With<Animal>> removes matching task owner-requested typed reason

The selector tuple above is complete for the recovered animal task families. Any newly evidenced family requires a reviewed schema/brief revision before implementation; runtime registration or a shared mutable score bag is forbidden. Pure generic score helpers may be zero-cost monomorphs.

5. Preflight producer contract

Not applicable: the engine consumes only native assets produced before launch.

6. Behavioral evidence and disposition

Inspect AI task/behavior-set/entity files; oracle/TSVs for AI thinker, evaluators, selectors, qualifiers, behavior sets and completion; commit d5657e5a domain AI and simulation/behavior.rs only to mine fixed command and formula evidence. Observe task cadence, priorities, interruption, target choice, cooldowns, weighted ties and idle behavior.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

Questions: exact score composition/clamps; selector order; deterministic/random ties; think cadence; current-task inertia; interrupt and failure cooldowns; each fixed command's owner. Resolve/transcode into command-specific A10 tables.

7. Required behavior

All authored supported tasks participate. Disabled qualifiers cannot win; scores and targets correspond to the same task slot. Selection is deterministic under the G32 seed except evidenced random choices. An active task persists until authentic interrupt/completion; stale completions do nothing. Domain packages attach focused execution components on Changed<ActiveTask> and never call a G13 dispatcher. Unsupported source commands fail preflight.

8. Performance contract

Hydration inserts inline candidates and RNG state with no candidate heap. Think passes allocate zero and are O(agents * relevant rule slots) at G32 cadence, not necessarily every render frame. No maps, strings, task trees, boxes or candidate vectors are built at runtime; A10 traversal is mapped. Count each scorer/selection and settled non-think frame; mapped pages and no GPU bytes are tracked separately.

9. Acceptance criteria

Deliver exports and tests for inline candidate reuse, focused scorer isolation, deterministic tie, interrupt policy, stale completion and zero allocation after hydration. No VM, opcode loop, blackboard bag, UpdateAnimalsRuntime, giant query or placeholder success. Root integrates at least feed, drink, rest, locomotion and mating executors in bulk.

10. Required handoff

Report API/files, exact A10 tables/commands, registrations/order, G11/G12/G14– G16/G32 consumers, evidence gaps, scorer seams and allocation/mapping/behavior validation.

G14 — Navigation, steering and docking

1. Identity and outcome

Animals, guests, staff and vehicles request destinations, plan through native and live topology, steer without collisions, dock at precise affordances, and emit arrival/failure facts. Integration wave 3. Prerequisites: A17, G04, G08, G09 and G32 fixed scheduling.

2. Exclusive ownership

The package worker owns game/src/plugins/locomotion/ only. Root owns registrations, schedule and navigation-asset integration.

This package directly traverses A17's mapped graph with bounded reusable A*, funnel/route, spatial-grid and steering scratch. bevy_landmass is rejected because its Archipelago/Island/Agent state would duplicate the native graph and live ECS movement facts; oxidized_navigation is forbidden at runtime. Do not hide either library behind an adapter or reproduce a navigation manager.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
LocomotionPlugin Plugin pub struct LocomotionPlugin; Registers spatial indexing, route planning, steering, motion and docking.
NavAgent component pub struct NavAgent { pub radius_m: f32, pub max_speed_mps: f32, pub acceleration_mps2: f32, pub capabilities: NavFlags } Finite positive parameters; uses A17's exact eligibility flags.
Velocity component pub struct Velocity(pub Vec3); World metres/second; Y follows mode-specific terrain/water rules.
Destination component pub struct Destination { pub world: Vec3, pub arrival_radius_m: f32 } Current requested point; changed value triggers planning.
Route component pub struct Route { pub points: Vec<Vec3>, pub cursor: usize } Reusable pre-reserved path; never reallocated below validated route capacity.
Steering component pub struct Steering { pub desired_velocity: Vec3 } Derived current-step fact.
SpatialCell component pub struct SpatialCell(pub u32); Current G14 grid bucket index.
SpatialGrid resource pub struct SpatialGrid { pub origin: Vec2, pub cell_size_m: f32, pub width: u32, pub height: u32, pub cell_offsets: Box<[u32]>, pub write_cursors: Box<[u32]>, pub entities: Vec<Entity> } Canonical CSR: offsets length is cells+1, range is offsets[i]..offsets[i+1]; cursors are reusable rebuild scratch and flat storage is pre-reserved.
Docking component pub struct Docking { pub target: Entity, pub point: Vec3, pub forward: Vec3, pub radius_m: f32 } Precise target affordance; removed on dock/failure.
NavigateTo message pub struct NavigateTo { pub entity: Entity, pub destination: Vec3, pub arrival_radius_m: f32 } Typed destination request.
DockAt message pub struct DockAt { pub entity: Entity, pub target: Entity, pub local_point: Vec3, pub local_forward: Vec3, pub radius_m: f32 } Target-relative docking request.
Arrived message pub struct Arrived { pub entity: Entity, pub target: Option<Entity> } Emitted once when arrival/docking completes.
NavigationFailed message pub struct NavigationFailed { pub entity: Entity, pub reason: NavigationFailure } Explicit failure.
NavigationFailure enum OutsideWorld \| NoStartNode \| NoRoute \| TargetGone \| ContainmentBlocked Fixed evidence-backed reasons.

A17 owns NavigationAsset, NavFlags, TraversalKind, direct graph/tile access and path capacity bounds. G08 supplies live links/gate state; G09 containment. Route scratch is per agent, never one locked global manager.

4. ECS ownership

Motion facts live on each actual entity. SpatialGrid is a bounded derived accelerator reconstructed from transforms; route vectors retain capacity.

System Schedule/set Exact query and parameters Writes Run condition/order
initialize_spatial_grid OnEnter(GamePhase::InGame) Commands, Query<&WorldBounds>, Res<Assets<WorldDefinitionsAsset>>, Query<(Entity,&GlobalTransform,&NavAgent)> CSR arrays, flat capacity and SpatialCell once after G04; capacity from native world policy
initialize_nav_agents FixedUpdate/FixedGameSet::Think Commands, Res<Assets<NavigationAsset>>, Query<(Entity,&NavAgent),(Added<NavAgent>,Without<Route>)> Inserts zero Velocity/Steering, pre-reserved Route, and initial SpatialCell Fresh live agents only; G22-restored agents already carry Route and are untouched.
accept_navigation_requests FixedUpdate/FixedGameSet::Think Commands, MessageReader<NavigateTo>, Query<(),With<NavAgent>> destination/route dirty valid finite target
accept_docking_requests FixedUpdate/FixedGameSet::Think Commands, MessageReader<DockAt>, Query<(),With<NavAgent>>, Query<&GlobalTransform> docking plus destination valid target
plan_changed_routes FixedUpdate/FixedGameSet::Navigate Res<Assets<NavigationAsset>>, Res<TopologyIndex>, Query<(&NavAgent,&GlobalTransform,&Destination,&mut Route),Changed<Destination>>, MessageWriter<NavigationFailed> existing route buffer/cursor changed requests only; containment/gates considered
compute_local_steering FixedUpdate/FixedGameSet::Navigate Res<SpatialGrid>, Query<(Entity,&NavAgent,&GlobalTransform,&Route,&mut Steering)> desired velocity active routes; bounded neighboring CSR ranges
integrate_motion FixedUpdate/FixedGameSet::Act Res<Time<Fixed>>, Res<TerrainIndex>, Res<Assets<TerrainAsset>>, Query<(&TerrainChunk,Option<&EditedTerrainSamples>)>, Query<(Entity,&NavAgent,&mut Transform,&mut Velocity,&Steering,&mut Route)> transform/velocity/cursor fixed step; indexed terrain only
rebuild_spatial_grid FixedUpdate/FixedGameSet::Cleanup ResMut<SpatialGrid>, Query<(Entity,&GlobalTransform,&mut SpatialCell),With<NavAgent>> counts into offsets, prefix sum, copies offsets to cursors, counting-sorts flat entities after motion; allocation-free
complete_docking FixedUpdate/FixedGameSet::Cleanup Commands, Query<(Entity,&GlobalTransform,&Docking)>, Query<&GlobalTransform>, MessageWriter<Arrived>, MessageWriter<NavigationFailed> snaps/faces/removes movement facts within recovered dock tolerance

Time<Fixed> supplies only physical seconds for integration; G32 controls whether fixed simulation runs and its deterministic ordering.

5. Preflight producer contract

Not applicable: the engine consumes only native assets produced before launch.

6. Behavioral evidence and disposition

Inspect navigation/path/fence/entity locomotion data; oracle/TSVs for locomotion, steering, path route, docking and traversability; commit d5657e5a pathing, domain AI locomotion and behavior docking code only for evidence. Observe gait speeds, turning, obstacle separation, path preference, gates, habitat escape, swimming/flying and docking.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

Questions: native nav cell/graph convention; route cost/heuristic/tie order; traversal classes; replanning; steering weights; max neighbors; slope/water; dock positions/tolerances and animation handoff. Recover from A17/oracle.

7. Required behavior

Requests mutate only the target agent. Routes honor closed gates, topology, containment and traversal class, with deterministic ties. Movement accelerates, steers and terrain-fits at fixed physical time; arrival emits once. Deleted targets fail and release reservations. Dynamic topology invalidates only affected routes. No straight-line fallback may cross invalid space.

8. Performance contract

Steady motion/steering allocates zero. Route planning occurs only on changed/ invalidated destinations and writes reusable capacity; complexity is bounded by A17 affected graph. Local steering visits fixed neighboring CSR ranges, not all agents. Flat capacity is established from native policy during loading; overflow is explicit outside settled measurement and recurring growth fails regression. Track route/grid heap, A17/terrain faults and no G14 GPU bytes.

9. Acceptance criteria

Deliver exports and tests for deterministic routing, gate/containment rejection, changed-only planning, bounded neighbor visits, exact arrival/docking and zero- allocation steady movement. No nav manager, per-agent world scan, direct-line fake route or runtime navmesh generation. Root validates animals/staff/guests.

10. Required handoff

Report API/files, A17/G04/G05/G08/G09/G32 contracts, registrations/order, physics feature needs, evidence gaps, buffer/index seams and allocator/mapping/ navigation validation.

G15 — Food, drink and refill transactions

1. Identity and outcome

Hungry/thirsty animals select compatible authored food or water, reserve and navigate to its container, consume exact portions, satisfy the appropriate need, and create keeper refill work when required. Integration wave 3. Prerequisites: A03, A14 and G12–G14.

2. Exclusive ownership

The package worker owns game/src/plugins/feeding/ only. Root owns declarations, schedules and staff/economy integration.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
FeedingPlugin Plugin pub struct FeedingPlugin; Registers focused scoring, task materialization, consumption and refill.
FoodContainer component pub struct FoodContainer { pub food: AssetId, pub amount_milli: u32, pub capacity_milli: u32 } Amount/capacity in definition-specific milli-portions; amount never exceeds capacity.
DrinkContainer component pub struct DrinkContainer { pub drink: AssetId, pub amount_milli: u32, pub capacity_milli: u32 } Amount/capacity in definition-specific milli-portions; amount never exceeds capacity.
FoodCandidate component pub struct FoodCandidate(pub Option<TaskCandidate>); Inline disjoint G15 candidate read by G13 selection; no shared score bag.
DrinkCandidate component pub struct DrinkCandidate(pub Option<TaskCandidate>); Inline drink candidate.
FeedingTask component pub struct FeedingTask { pub source: Entity, pub program: AssetId } Focused execution for mapped A10 food target/move/consume actions.
DrinkingTask component pub struct DrinkingTask { pub source: Entity, pub program: AssetId } Focused drink execution.
ConsumptionReservation component pub struct ConsumptionReservation { pub consumer: Entity } On food/drink source while exclusively reserved where definition requires it.
RefillPending component pub struct RefillPending; Prevents duplicate keeper requests.
RefillRequested message pub struct RefillRequested { pub container: Entity, pub kind: RefillKind } Edge-triggered empty/threshold work fact.
RefillKind enum Food(AssetId) \| Drink(AssetId) Fixed typed refill content.
RefillContainer message pub struct RefillContainer { pub staff: Entity, pub container: Entity, pub amount_milli: u32 } G20 completion request; compatibility/capacity revalidated.
ContainerRefilled message pub struct ContainerRefilled { pub staff: Entity, pub container: Entity, pub accepted_milli: u32 } Exact accepted quantity.
EndFeedingTask message pub struct EndFeedingTask { pub consumer: Entity, pub outcome: TaskOutcome } Explicit pre-removal cleanup request; lifecycle/behavior owners must send before despawn/task removal.
is_food_compatible function pub fn is_food_compatible(species: &ArchivedSpeciesRecord, object: &ArchivedObjectDefinition) -> bool Checks A03 hunger preferences plus A14 ObjectKind::Food/EAT.
is_drink_compatible function pub fn is_drink_compatible(species: &ArchivedSpeciesRecord, object: &ArchivedObjectDefinition) -> bool Checks thirst preferences plus A14 water/DRINK.

A03 owns diet/feeding vocabulary and need kinds; A14 owns food/drink/container object definitions/affordances; exact portion, capacity and refill fields are an A14 root contract extension required before implementation. A10 owns the action sequence ChooseTarget, MoveToTarget, Consume { need: NeedKind }.

4. ECS ownership

Containers own only live amount/reservation/refill status. Animal execution uses short-lived focused components, not an internal feeding state machine.

System Schedule/set Exact query and parameters Writes Run condition/order
score_food_tasks FixedUpdate/FixedGameSet::Think Res<Assets<SpeciesCatalogAsset>>, Res<Assets<WorldDefinitionsAsset>>, Res<Assets<BehaviorProgramAsset>>, Res<SpatialGrid>, Query<(&SpeciesHandle,&BehaviorHandle,&Hunger,&HabitatMember,&mut FoodCandidate),With<Animal>>, Query<(Entity,&FoodContainer,&GlobalTransform,Option<&ConsumptionReservation>)> food candidate only bounded CSR neighbors; parallel with other scorers
score_drink_tasks FixedUpdate/FixedGameSet::Think Res<Assets<SpeciesCatalogAsset>>, Res<Assets<WorldDefinitionsAsset>>, Res<Assets<BehaviorProgramAsset>>, Res<SpatialGrid>, Query<(&SpeciesHandle,&BehaviorHandle,&Thirst,&HabitatMember,&mut DrinkCandidate),With<Animal>>, Query<(Entity,&DrinkContainer,&GlobalTransform,Option<&ConsumptionReservation>)> drink candidate only bounded CSR neighbors; parallel
begin_feeding_tasks FixedUpdate/FixedGameSet::Act Commands, Res<Assets<BehaviorProgramAsset>>, Query<(Entity,&ActiveTask),Changed<ActiveTask>>, Query<&mut FoodContainer> task/reservation/NavigateTo mapped current action targets food
begin_drinking_tasks FixedUpdate/FixedGameSet::Act Commands, Res<Assets<BehaviorProgramAsset>>, Query<(Entity,&ActiveTask),Changed<ActiveTask>>, Query<&mut DrinkContainer> task/reservation/navigation mapped current action targets water
consume_food FixedUpdate/FixedGameSet::Act Commands, MessageReader<Arrived>, Res<Assets<SpeciesCatalogAsset>>, Res<Assets<WorldDefinitionsAsset>>, Res<Assets<BehaviorProgramAsset>>, Query<(&SpeciesHandle,&mut Hunger,&FeedingTask),With<Animal>>, Query<(&mut FoodContainer,Option<&ConsumptionReservation>)>, MessageWriter<TaskCompleted>, MessageWriter<RefillRequested> exact portion/need/task/refill mapped Consume { need: NeedKind::Hunger } at matching source
consume_drink FixedUpdate/FixedGameSet::Act Commands, MessageReader<Arrived>, Res<Assets<SpeciesCatalogAsset>>, Res<Assets<WorldDefinitionsAsset>>, Res<Assets<BehaviorProgramAsset>>, Query<(&SpeciesHandle,&mut Thirst,&DrinkingTask),With<Animal>>, Query<(&mut DrinkContainer,Option<&ConsumptionReservation>)>, MessageWriter<TaskCompleted>, MessageWriter<RefillRequested> drink/need/task/refill mapped thirst consume at matching source
refill_food_containers FixedUpdate/FixedGameSet::Act Commands, MessageReader<RefillContainer>, Query<(&mut FoodContainer,Option<&RefillPending>)>, MessageWriter<ContainerRefilled> amount/pending matching compatible food request
refill_drink_containers FixedUpdate/FixedGameSet::Act Commands, MessageReader<RefillContainer>, Query<(&mut DrinkContainer,Option<&RefillPending>)>, MessageWriter<ContainerRefilled> amount/pending matching compatible drink request
end_feeding_tasks FixedUpdate/FixedGameSet::Cleanup Commands, MessageReader<EndFeedingTask>, Query<(Option<&FeedingTask>,Option<&DrinkingTask>)>, Query<&ConsumptionReservation> removes exact source reservation and task component, emits TaskCompleted before G13 completion and before lifecycle despawn; never scans sources

5. Preflight producer contract

Not applicable: the engine consumes only native assets produced before launch.

6. Behavioral evidence and disposition

Inspect animal diet flags, food/water/container entities, need adjusts and keeper task records; oracle/TSVs for food/feeding/consume/refill/keeper requests; commit d5657e5a animal care, AI task and staff feeding code only for evidence. Observe compatibility, portions, duration, depletion, multi-user access, spoilage/dirtiness, refill thresholds and keeper interaction.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

Questions: diet token normalization resolved by A03; need-adjust formula; portion units; exclusive/shared capacity; reservation timeout; source search radius/preference; ground food; water bodies; container dirtiness/spoilage; refill amount/cost. Recover rather than assume a universal feeder.

7. Required behavior

Only compatible nonempty reachable sources score. Reservation and navigation target the same entity. Arrival revalidates task/source/reservation; consumption atomically decrements exact amount and adjusts only hunger or thirst. Empty or threshold crossing emits one refill request. Refill clamps capacity and clears pending only on accepted work. Failure/death/task switch releases reservation. Keeper execution belongs to G20; health contamination effects to G16.

8. Performance contract

Steady execution allocates zero. Scoring uses G14 CSR ranges and bounded nearby candidates, never scans every container per animal. Compatibility is O(log n) or better direct archived lookup with no strings. Containers/tasks do not allocate. Count idle/scoring/consume/refill passes; A03/A14 faults separate; no package-owned GPU bytes.

9. Acceptance criteria

Deliver exports and recovered-fixture tests for compatibility, candidate selection, atomic portion, correct single-need adjustment, duplicate refill suppression, capacity clamp and reservation cleanup. No feeder manager, generic care transaction, all-needs query, guessed food values or fake navigation. Root bulk-tests animals and keeper refills.

10. Required handoff

Report API/files, A03/A10/A14/G12–G14/G20/G32 contracts, registrations/order, evidence gaps, explicit cleanup ordering and allocation/mapping/gameplay validation.

G16 — Animal Health

1. Identity and outcome

This package delivers disease, injury treatment, death, escape, rampage, tranquilization, recapture, and recovery as ordinary facts on animal entities. Players can identify and treat sick animals, respond to escaped or dangerous animals, and observe authentic terminal and recovery outcomes. These behaviors share one natural responsibility because each changes whether an animal is healthy, controllable, alive, and available to normal behavior.

Integration wave: 3. Logical prerequisites: A10 behavior programs, A14 world definitions, G11 animal lifecycle, G12 animal welfare, G13 animal behavior, and G14 locomotion. Prerequisites are read-only and must not be copied or wrapped.

2. Exclusive ownership

The worker exclusively owns reconstruction/game/src/plugins/animal_health/ and may create mod.rs plus private child modules there. Every other path is read-only. Root owns crate/module declarations, plugin registration, schedule configuration, dependencies, reflection registration, and alignment of names requested from earlier packages. Report collisions; do not broaden ownership.

3. Frozen public contract

All scalar health ratios use finite f32 in inclusive 0.0..=1.0; durable durations are integer G32 simulation ticks. An AssetId names an immutable native definition.

Symbol Kind Exact shape or signature Owner and invariant
AnimalHealthPlugin Plugin unit struct; fn build(&self, app: &mut App) Registers only this package's types, messages, and systems.
Vitality Component pub struct Vitality(pub f32) Current physical health; finite and clamped. Death occurs only at zero after authentic rule evaluation.
Disease Component pub struct Disease { pub definition: AssetId, pub elapsed_ticks: u64, pub severity_permille: u16, pub hint_level: u8 } Severity uses A14's exact 0..=1000 fixed unit; at most one active disease unless evidence establishes stacking.
DiseaseRng Component pub struct DiseaseRng(pub DeterministicRng) G32 domain-local stream derived from zoo seed and animal PersistentId; sole disease/exposure roll source and persisted by G22.
Treatment Component pub struct Treatment { pub definition: AssetId, pub provider: Entity, pub remaining_ticks: u32 } Temporary treatment actually being performed; provider must be live staff.
Tranquilized Component pub struct Tranquilized { pub remaining_ticks: u32 } Suppresses locomotion/behavior while positive.
Escaped Component pub struct Escaped { pub since_tick: u64 } Animal is outside valid containment; removed immediately after valid recapture.
Rampaging Component pub struct Rampaging { pub elapsed_ticks: u64 } Dangerous behavior state selected by authored rules, not a random default.
Dead Component pub struct Dead { pub cause: DeathCause, pub since_tick: u64 } Terminal marker; ordinary animal think/act systems must exclude it.
DeathCause enum Disease(AssetId), Injury, Age, Welfare Only a cause proven by native rules may be generated; A19 rejects an imported cause outside the frozen vocabulary.
TreatmentRequest Message pub struct TreatmentRequest { pub animal: Entity, pub treatment: AssetId, pub provider: Entity } Real asynchronous request from player/staff interaction.
TranquilizeRequest Message pub struct TranquilizeRequest { pub animal: Entity, pub source: Entity } Requests an evidence-authorized tranquilizer action.
AnimalDied Message pub struct AnimalDied { pub animal: Entity, pub cause: DeathCause } Emitted exactly once when Dead is inserted.
AnimalEscaped Message pub struct AnimalEscaped { pub animal: Entity } Emitted exactly once per transition into Escaped.
AnimalRecovered Message pub struct AnimalRecovered { pub animal: Entity, pub disease: Option<AssetId> } Emitted after disease/treatment state is actually cleared.

Root contract requests: align direct names for G11 Animal, G12 welfare facts, G13 authored behavior selection, G14 navigation/motion facts, G09 containment, G20 staff role/availability, and the A10/A14 mapped definition handles. Do not substitute local traits or mirror components when their final names differ.

4. ECS ownership

Each component is one live per-animal fact. There is no disease manager, health registry, animal snapshot, or package world.

System Schedule/set Exact query and parameters Writes Run condition/order
detect_containment_breaches FixedUpdate / FixedGameSet::Cleanup Res<ZooClock>; animal Query<(Entity,&HabitatMember,&Containment,Option<&Escaped>), (With<Animal>,Without<Dead>)>; MessageWriter<AnimalEscaped>; Commands Inserts Escaped only on contained: true -> false; removal remains recapture-owned After G09 has updated exact membership/containment facts; never re-derives boundaries.
evaluate_disease_exposure FixedUpdate / FixedGameSet::Think MessageReader<ZooDayAdvanced>, Res<ZooClock>, Res<Assets<WorldDefinitionsAsset>>, Query<(Entity,&SpeciesHandle,&HabitatMember,&mut DiseaseRng),(With<Animal>,Without<Disease>,Without<Dead>)>, Query<(&WorldEnvironment,&Weather)>, Commands Inserts eligible Disease Exact A14 interval/event only.
advance_disease FixedUpdate / FixedGameSet::Act Res<ZooClock>, Res<Assets<WorldDefinitionsAsset>>, Query<(Entity,&mut Disease,&mut DiseaseRng,&mut Vitality,Option<&Treatment>),(With<Animal>,Without<Dead>)>, Commands Disease ticks/severity/vitality and terminal components Every simulation tick.
begin_treatment FixedUpdate / FixedGameSet::Act MessageReader<TreatmentRequest>, Res<Assets<WorldDefinitionsAsset>>, Query<(Option<&Disease>,Option<&Treatment>),(With<Animal>,Without<Dead>)>, Query<(),(With<Staff>,With<AvailableForWork>)>, Commands Inserts valid Treatment Before treatment advance.
advance_treatment FixedUpdate / FixedGameSet::Act Res<ZooClock>, Res<Assets<WorldDefinitionsAsset>>, Query<(Entity,&mut Treatment,&mut Vitality,Option<&mut Disease>),(With<Animal>,Without<Dead>)>, Query<(&Transform,Option<&AvailableForWork>),With<Staff>>, Commands, MessageWriter<AnimalRecovered> Treatment/disease/vitality components After docking.
apply_tranquilizer_requests FixedUpdate / FixedGameSet::Act MessageReader<TranquilizeRequest>, Res<Assets<WorldDefinitionsAsset>>, Query<(Option<&Escaped>,Option<&Rampaging>,Option<&Tranquilized>),(With<Animal>,Without<Dead>)>, Commands Inserts/refreshes Tranquilized Before countdown.
advance_tranquilization FixedUpdate / FixedGameSet::Act Res<ZooClock>; Query<(Entity, &mut Tranquilized), (With<Animal>, Without<Dead>)>; Commands Tick countdown/removal In game. G13/G14 exclude Tranquilized.
evaluate_rampage FixedUpdate / FixedGameSet::Think Res<Assets<WorldDefinitionsAsset>>, Query<(Entity,&AnimalWelfare,Option<&Disease>,Option<&Rampaging>),(With<Animal>,Or<(Changed<AnimalWelfare>,Changed<Disease>)>)>, Commands Inserts/removes Rampaging Changed animals only.
finalize_deaths FixedUpdate / FixedGameSet::Cleanup Query<(Entity, &Vitality, Option<&Disease>), (With<Animal>, Without<Dead>)>; Commands; MessageWriter<AnimalDied> Inserts Dead and emits once After all vitality writers. Does not despawn presentation immediately.
recapture_animals FixedUpdate / FixedGameSet::Cleanup Query<(Entity,&Containment,&Docking),(With<Animal>,With<Escaped>,With<Tranquilized>)>, Commands, MessageWriter<AnimalRecovered> Removes Escaped, Rampaging, Tranquilized Contained and docked after G09/G14.

5. Preflight producer contract

Not applicable: the engine consumes only native assets produced before launch. This package must not add filesystem discovery, Z2F, XML, original codecs, source precedence, or conversion dependencies.

6. Behavioral evidence and disposition

Start with shipped disease/task/entity definitions under ignored vendor/ archives; search cpp-reconstruction/ and docs/analysis/type-coverage-reconciliation.tsv for ZTDiseaseMgr, ZTDisease, ZTCureDiseaseMode, ZTBehCureDisease, tranquilizer, rampage, escape, death, and recapture tokens; consult zt-byte-range-map.tsv; observe the original for warning timing and presentation. Mine only behavior/evidence from commit d5657e5a, especially domain/src/simulation/disease.rs and game/src/features/simulation/disease.rs.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

Evidence questions to settle before completion: disease eligibility and fame gates; check intervals/chance formula; symptom effects; cure research/hints and rewards; exact mortality rules; escape detection grace; rampage triggers; tranquilizer duration, miss/failure rules, and recapture sequence. No guessed defaults are permitted: absent values remain rejected or inactive according to the native definition contract.

7. Required behavior

8. Performance contract

Steady systems allocate zero heap memory. Disease/containment work is changed- or timer-driven; proximity uses G09/G14 bounded spatial queries rather than animal-by-world scans. Definition lookup is O(1) by validated AssetId index; each active case is O(1) per due tick. No strings, growing history, cloned asset payloads, or per-frame vectors are allowed. Allocation tests cover each system after warm-up; mapped traversal/handle clone remain zero-allocation; mapped pages and definition page faults are attributed separately. This package owns no GPU resources.

9. Acceptance criteria

Deliver formatted ordinary Rust under the owned directory exporting every symbol above. Focused tests must establish transition uniqueness, compatible treatment, timer clamping, terminal exclusion, and relationship cleanup using data-derived definitions. No todo!, stub, guessed constant, fake success, include_str!, source concatenation, manager, or shadow world. Observable done: a legitimately eligible animal can become sick, show authentic symptoms, be treated/recover, or die; an escaped/rampaging animal can be tranquilized and recaptured. Rendering, UI layout, staff scheduling, and definition conversion are non-goals.

10. Required handoff

Report files/API, Cargo or feature needs, root module/plugin/message/schedule/ reflection registrations, consumed cross-package symbols, evidence resolved or still open, integration seams to erase, and expected allocator/page-fault and gameplay checks. Root performs bulk compilation and integration.

G17 — Guests

1. Identity and outcome

This package makes each visitor a normal ECS entity with arrival/departure, personal needs, satisfaction, viewing, bounded memories, education, and reactions. Guests walk through the zoo, choose authored destinations, respond to what they experience, and leave with an observable evaluation. These facts are one cohesive lifecycle, not a guest simulation resource.

Integration wave: 3. Logical prerequisites: A14 world definitions, G04 world spawn, G14 locomotion, and G36 sanitation facts. G18–G21 consume its facts.

2. Exclusive ownership

The worker exclusively owns reconstruction/game/src/plugins/guests/. Every other path is read-only. Root owns declarations, registration, schedules, dependencies, reflection, and cross-package renames. No collision broadens ownership.

3. Frozen public contract

Needs, satisfaction, education, reactions, memory values, and departure facts use exact integer permille (u16 absolute, i16 signed delta). Durable simulation time uses G32 ticks. A14 owns the one recovered GUEST_MEMORY_CAPACITY literal and rejects a definition exceeding it; this package imports that constant directly rather than redeclaring it.

Symbol Kind Exact shape or signature Owner and invariant
GuestsPlugin Plugin unit struct Registers this guest lifecycle only.
Guest Component marker Identifies visitor entities; never stored in a guest collection resource.
GuestArchetype Component pub struct GuestArchetype(pub AssetId) ID of one immutable A14 GuestDefinition; distinct name prevents static/live ownership ambiguity.
GuestRng Component pub struct GuestRng(pub DeterministicRng) G32 domain-local destination/reaction stream derived from zoo seed and guest PersistentId; persisted by G22.
GuestPhase Component + enum Arriving, Visiting, Leaving Exactly one lifecycle phase.
GuestHunger Component pub struct GuestHunger { pub value:u16, pub residual_q16:u16 } A14 satiation permille, 1000 full; residual preserves exact per-tick Q16 decay.
GuestThirst Component pub struct GuestThirst { pub value:u16, pub residual_q16:u16 } A14 hydration permille, 1000 full; exact Q16 decay.
GuestEnergy Component pub struct GuestEnergy { pub value:u16, pub residual_q16:u16 } A14 rest permille, 1000 rested; exact Q16 decay.
GuestRestroom Component pub struct GuestRestroom { pub value:u16, pub residual_q16:u16 } A14 relief permille, 1000 no current need; exact Q16 decay.
GuestComfort Component pub struct GuestComfort { pub value:u16, pub residual_q16:u16 } Environmental comfort permille with exact Q16 adjustments.
GuestSatisfaction Component pub struct GuestSatisfaction(pub u16) Current aggregate satisfaction permille; changed only by recovered reactions.
GuestEducation Component pub struct GuestEducation(pub u16) Cumulative bounded education permille.
VisitTime Component pub struct VisitTime { pub elapsed_ticks: u64 } Time since entry, excluding paused simulation.
Viewing Component pub struct Viewing { pub subject: Entity, pub elapsed_ticks: u64 } Current valid view target.
GuestDestination Component pub struct GuestDestination { pub entity: Entity, pub purpose: GuestVisitPurpose } Chosen destination using A14's exact enum; G14 owns route/path state.
GuestMemoryEntry struct pub struct GuestMemoryEntry { pub subject: Entity, pub kind: GuestMemoryKind, pub value_permille: i16, pub age_ticks: u64 } Copyable bounded fixed-unit fact using A14's exact enum; entity validity checked before use.
GuestMemories Component pub struct GuestMemories { pub entries: ArrayVec<GuestMemoryEntry, GUEST_MEMORY_CAPACITY>, pub active_capacity: u16, pub next: u16 } Inline storage; len <= active_capacity <= GUEST_MEMORY_CAPACITY; active capacity comes from A14 definition and zero is valid. No per-guest heap owner.
GuestArrivalState Resource pub struct GuestArrivalState { pub policy: AssetId, pub next_tick: u64, pub rng: DeterministicRng } One active world's deterministic visitor-arrival cursor; policy comes from the selected A19 map and state persists in G22.
GuestReaction Message pub struct GuestReaction { pub guest: Entity, pub subject: Entity, pub kind: GuestMemoryKind, pub satisfaction_delta_permille: i16, pub education_delta_permille: i16 } Exact signed A14 fixed-unit outcome; checked/clamped on application.
GuestArrived Message pub struct GuestArrived { pub guest: Entity } Emitted once after entry is completed.
GuestDeparted Message pub struct GuestDeparted { pub guest: Entity, pub satisfaction_permille: u16, pub education_permille: u16 } Emitted once immediately before despawn with exact live fixed values.
GuestArrivalFailed Message pub struct GuestArrivalFailed { pub reason: GuestArrivalFailure } Explicit policy/entrance/definition/ID failure; never silently substitutes a guest.
GuestArrivalFailure enum MissingPolicy, MissingEntrance, InvalidDefinition(AssetId), PersistentId(PersistentIdError) Closed runtime failure vocabulary after archive validation.

Root contract requests: align G04 entrance/spawn-table facts, G14 navigation target/reached facts, A14 definition handle, G09/G08 traversability, and G18 Wallet. Do not create a local navigation API, facility facade, or economy copy.

4. ECS ownership

System Schedule/set Exact query and parameters Writes Run condition/order
initialize_guest_arrivals Update / GameSet::Intent MessageReader<WorldLoadFinished>, Res<Assets<WorldScenarioAsset>>, Res<Assets<WorldDefinitionsAsset>>, Res<ZooSeed>, Option<Res<GuestArrivalState>>, Commands, MessageWriter<GuestArrivalFailed> Inserts one state resolved through scenario→map→A14 policy New native world only; G22 restores saved state instead.
spawn_arriving_guests FixedUpdate / FixedGameSet::Act Res<ZooClock>, Res<Fame>, Res<AdmissionPrice>, Res<Assets<WorldScenarioAsset>>, Res<Assets<WorldDefinitionsAsset>>, ResMut<GuestArrivalState>, ResMut<PersistentIdAllocator>, Query<(Entity,&WorldRoot)>, Query<(&PersistentId,&WorldMember,&ZooEntrance)>, Query<(),With<Guest>>, Commands, MessageWriter<NavigateTo>, MessageWriter<GuestArrivalFailed> At the due tick, allocator-issued PersistentId, WorldMember, Guest, GuestArchetype, Wallet, Transform, and G14 NavAgent for an authored group; emits NavigateTo to the entrance's inside point and advances policy RNG/cursor Chooses the lowest-PersistentId entrance and the first matching compiled rule; respects population cap and current admission/fame.
initialize_spawned_guests FixedUpdate / FixedGameSet::Think Res<ZooSeed>, Res<Assets<WorldDefinitionsAsset>>, Query<(Entity,&PersistentId,&GuestArchetype),(Added<Guest>,Without<GuestRng>)>, Commands Inserts phase/needs/satisfaction/education/time/memory/RNG Fresh G17 guests only; G22-restored guests already carry GuestRng and are untouched.
decay_guest_needs FixedUpdate / FixedGameSet::Think Res<Assets<WorldDefinitionsAsset>>, Query<(&GuestArchetype,&mut GuestHunger,&mut GuestThirst,&mut GuestEnergy,&mut GuestRestroom,&mut GuestComfort),With<Guest>> Five need values and their residuals Exactly once per G32 simulation tick using A14 signed Q16 rates; no float/day batching.
choose_guest_destination FixedUpdate / FixedGameSet::Think Res<Assets<WorldDefinitionsAsset>>, Res<SpatialGrid>, Query<(Entity,&GuestArchetype,&GuestHunger,&GuestThirst,&GuestEnergy,&GuestRestroom,&GuestSatisfaction,&mut GuestRng),(With<Guest>,Without<GuestDestination>)>, Query<(Entity,&Transform,&Inspectable)>, Commands Inserts GuestDestination Due reconsideration after needs.
begin_viewing FixedUpdate / FixedGameSet::Act Query<(Entity,&GuestDestination,&GlobalTransform),(With<Guest>,Without<Viewing>)>, Query<(&GlobalTransform,&Visibility)>, MessageReader<Arrived>, Commands Inserts Viewing Matching reached View destination.
advance_viewing FixedUpdate / FixedGameSet::Act Res<ZooClock>, Res<Assets<WorldDefinitionsAsset>>, Query<(Entity,&mut Viewing,&mut GuestSatisfaction,&mut GuestEducation,&mut GuestMemories),With<Guest>>, Query<(&SpeciesHandle,&AnimalWelfare)>, MessageWriter<GuestReaction> Viewing ticks and reaction messages Current viewers only.
react_to_visible_litter FixedUpdate / FixedGameSet::Think Res<SpatialGrid>, Res<Assets<WorldDefinitionsAsset>>, Query<(Entity,&GlobalTransform),Or<(Added<Litter>,Added<HabitatWaste>)>>, Query<(&GlobalTransform,&mut GuestRng),With<Guest>>, MessageWriter<GuestReaction> Exact A14 GuestMemoryKind::Litter reactions for bounded nearby eligible guests Newly visible waste only; distance/probability/delta come from native definitions, never all-guests × all-waste.
apply_guest_reactions FixedUpdate / FixedGameSet::Act MessageReader<GuestReaction>, Query<(&mut GuestSatisfaction,&mut GuestEducation,&mut GuestMemories),With<Guest>> Fixed guest facts/memory slot After producers.
advance_guest_lifecycle FixedUpdate / FixedGameSet::Cleanup Res<ZooClock>, Res<Assets<WorldDefinitionsAsset>>, MessageReader<Arrived>, Query<(Entity,&mut GuestPhase,&mut VisitTime,&GuestSatisfaction,&GuestEducation,&GuestHunger,&GuestThirst,&GuestEnergy,&GuestRestroom,Option<&GuestDestination>),With<Guest>>, Commands, MessageWriter<GuestArrived>, MessageWriter<GuestDeparted> Phase/time/despawn and lifecycle messages After economy.
age_guest_memories FixedUpdate / FixedGameSet::Cleanup Res<ZooClock>, Res<Assets<WorldDefinitionsAsset>>, Query<&mut GuestMemories,With<Guest>> Ages/evicts fixed entries in ticks Only when ZooClock.tick reaches the next exact A14 interval boundary.

5. Preflight producer contract

Not applicable: the engine consumes only native assets produced before launch. No original source parsing or filesystem discovery belongs here.

6. Behavioral evidence and disposition

Search shipped guest AI/task/need/entity definitions under ignored vendor/; search analysis TSVs and oracle names for ZTGuest, guest needs, viewing, education, thoughts/emoticons, group arrival, and exit behavior. Observe live original destination priorities, viewing arcs, crowd reactions, and departure. Mine commit d5657e5a only for behavioral leads in domain/src/simulation/guest.rs, features/simulation/guests.rs, AI, ambient, influence, tours, and facilities.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

Questions: numeric GUEST_MEMORY_CAPACITY from maximum valid shipped/mod contract and original storage evidence; authentic need scales/rates; group membership effects; destination scoring; line abandonment; animal visibility and viewing duration; memory retention; education sources; happiness aggregation; admission refusal and departure conditions. Recover from definitions/oracle/live behavior; do not reuse old aggregate guest snapshots or guessed constants.

7. Required behavior

8. Performance contract

All steady systems allocate zero. Due arrival may allocate only the new ECS entities/components after reserving group capacity; rule and weighted-archetype selection traverse mapped ranges without allocation. Guest memory is inline fixed-capacity storage with no per-guest heap allocation; A14 validates its active bound. Needs are O(guests due); destination search is O(candidates in bounded spatial cells), not O(guests × world); viewing is O(active viewers). Memory is fixed capacity. No per-frame strings, vectors, asset clones, or hash growth. Changed filters/timers suppress unnecessary work. Allocator regressions cover a settled guest tick and view/reaction path; mapped definition lookup and handle clones remain allocation-free; this package owns no GPU data.

9. Acceptance criteria

Deliver the owned Rust subtree and all exports above. Focused tests cover needs clamping, one destination/reservation, view invalidation, evidence-bounded memory eviction, reaction application, and unique departure. No stubs, guessed behavior, record bags, manager resources, or runtime parsing. Observable done: guests arrive, navigate, satisfy needs, view/learn/react, and depart authentically while other guests remain independently parallelizable.

10. Required handoff

Report files/API, dependencies, root registrations/wiring, consumed symbols, evidence answers/open questions, temporary seams, and allocator/page/gameplay validation. Root performs bulk checks.

G18 — Economy

1. Identity and outcome

This package delivers authoritative zoo cash, admission, prices, commerce, facility capacity, and completed service transactions. Money moves once through typed transactions when a service actually completes; menus and facilities read the same live facts. This is one natural global accounting responsibility, not a port of the original economy component graph.

Integration wave: 3. Prerequisites: A14 definitions, G10 placement/facilities, and G17 guests. G19, G20, G23, G24, G26, and G27 consume transactions.

2. Exclusive ownership

Exclusive path: reconstruction/game/src/plugins/economy/. Everything else is read-only. Root owns module/plugin/schedule/dependency/reflection registration and aligns earlier symbols. Report collisions.

3. Frozen public contract

Money is signed integer hundredths of the displayed base currency. Arithmetic must be checked; no floating-point money enters live state.

Symbol Kind Exact shape or signature Owner and invariant
EconomyPlugin Plugin unit struct Registers only accounting/service systems.
Money value type #[repr(transparent)] pub struct Money(pub i64) with checked checked_add/checked_sub Currency minor units; overflow rejects a transaction.
ZooCash Resource pub struct ZooCash(pub Money) Sole authoritative player cash balance.
AdmissionPrice Resource pub struct AdmissionPrice(pub Money) Current global gate price; bounds come from native scenario/rules.
Wallet Component pub struct Wallet(pub Money) Authoritative cash carried by one guest.
Price Component pub struct Price(pub Money) Current price on one purchasable/service entity.
ServiceFacility Component pub struct ServiceFacility { pub definition: AssetId, pub capacity: u16, pub occupied: u16 } Capacity counters remain occupied <= capacity; entity is the facility.
Inventory Component pub struct Inventory { pub available:u16, pub reserved:u16, pub capacity:u16 } Optional stocked-facility fact; available + reserved <= capacity. Initial capacity and per-service units come from A14.
ServiceReservation Component pub struct ServiceReservation { pub facility: Entity, pub service: AssetId, pub inventory_units:u16 } Lives on customer; at most one active service reservation. Units move available→reserved atomically at reservation.
ServiceProgress Component pub struct ServiceProgress { pub facility: Entity, pub remaining_ticks: u32 } Lives on customer only while service is underway; duration is preflight-compiled G32 ticks.
TransactionKind enum Admission, Purchase, Service, Construction, Sale, Wage, Upkeep, Donation, Reward, Refund Fixed semantic accounting categories.
Account enum Zoo, Entity(Entity), External Transaction endpoints; External is source/sink, not hidden cash state.
TransactionRequest Message pub struct TransactionRequest { pub operation: Entity, pub debit: Account, pub credit: Account, pub amount: Money, pub kind: TransactionKind, pub subject: Option<Entity> } operation is the caller-owned pending-operation entity; amount positive; one requested atomic transfer.
TransactionCompleted Message pub struct TransactionCompleted { pub operation: Entity, pub debit: Account, pub credit: Account, pub amount: Money, pub kind: TransactionKind, pub subject: Option<Entity> } Echoes the request operation exactly once after both sides commit.
TransactionRejected Message pub struct TransactionRejected { pub operation: Entity, pub debit: Account, pub credit: Account, pub amount: Money, pub kind: TransactionKind, pub subject: Option<Entity>, pub reason: TransactionRejection } Echoes the request operation exactly once; balances are unchanged.
TransactionRejection enum NonPositive, InsufficientFunds, MissingAccount, CapacityUnavailable, Overflow, RuleDenied No string error path.
ServiceRequest Message pub struct ServiceRequest { pub customer: Entity, pub facility: Entity, pub service: AssetId } Guest/staff requests actual authored service.
ServiceCompleted Message pub struct ServiceCompleted { pub customer: Entity, pub facility: Entity, pub service: AssetId } Emitted after time and payment completion.
ServicePaymentPending Component pub struct ServicePaymentPending { pub customer: Entity, pub facility: Entity, pub service: AssetId } Lives only on the G18-owned operation entity between transfer request and matching result.

Root contract requests: align G10 facility/placed definitions and affordances, G17 Guest/destination facts, G14 reached-target facts, and A14 price/duration/ capacity records. Other packages must use Money, Wallet, ZooCash, and TransactionRequest directly—not mirror balances or create economy adapters.

4. ECS ownership

ZooCash and AdmissionPrice are genuinely zoo-global policy/facts. There is no ledger vector, transaction manager, facility world, or per-frame finance snapshot. Historical reports, if required, are bounded aggregates on their natural reporting owner rather than retained transactions.

System Schedule/set Exact query and parameters Writes Run condition/order
initialize_service_facilities FixedUpdate / FixedGameSet::Act Commands, Res<Assets<WorldDefinitionsAsset>>, Query<(Entity,&DefinitionId),(Added<DefinitionId>,Without<ServiceFacility>)> Inserts the definition's Price, ServiceFacility, and optional full Inventory directly on the placed facility Fresh placed facilities only; G22-restored facilities already carry ServiceFacility. Zero inventory capacity inserts no Inventory.
admit_guests FixedUpdate / FixedGameSet::Economy MessageReader<GuestArrived>, Res<AdmissionPrice>, ResMut<ZooCash>, Res<Assets<WorldDefinitionsAsset>>, Query<(&mut Wallet,&mut GuestPhase),With<Guest>> Wallet/cash/phase Matching gate arrival; checked direct admission transfer.
reserve_services FixedUpdate / FixedGameSet::Act MessageReader<ServiceRequest>, Res<Assets<WorldDefinitionsAsset>>, Query<(&Wallet,Option<&ServiceReservation>)>, Query<(&Price,&mut ServiceFacility,Option<&mut Inventory>)>, Commands Occupancy, available→reserved stock, reservation/progress Valid reached customer with capacity, affordability and stock; all counters change atomically.
advance_services FixedUpdate / FixedGameSet::Act Res<ZooClock>, Query<(Entity,&WorldMember,&mut ServiceProgress,&ServiceReservation)>, Query<(),With<ServiceFacility>>, Query<(),With<ServicePaymentPending>>, Commands, MessageWriter<TransactionRequest> Progress plus one payment operation carrying the customer's WorldMember, and transaction request One tick; no duplicate.
apply_transactions FixedUpdate / FixedGameSet::Economy MessageReader<TransactionRequest>, ResMut<ZooCash>, Query<&mut Wallet>, MessageWriter<TransactionCompleted>, MessageWriter<TransactionRejected> Exactly two account balances atomically Single authoritative writer; checked arithmetic; deterministic message order.
finish_services FixedUpdate / FixedGameSet::Cleanup MessageReader<TransactionCompleted>, Query<&ServicePaymentPending>, Query<(Entity,&ServiceReservation),With<ServiceProgress>>, Query<(&mut ServiceFacility,Option<&mut Inventory>)>, Commands, MessageWriter<ServiceCompleted> Clears facts, consumes reserved stock, releases occupancy/operation and emits completion completed.operation match.
reject_service_payments FixedUpdate / FixedGameSet::Cleanup MessageReader<TransactionRejected>, Query<&ServicePaymentPending>, Query<(Entity,&ServiceReservation),With<ServiceProgress>>, Query<(&mut ServiceFacility,Option<&mut Inventory>)>, Commands Returns reserved→available stock and releases reservation/capacity/operation rejected.operation match.
cancel_invalid_services FixedUpdate / FixedGameSet::Cleanup Query<(Entity,&ServiceReservation),With<ServiceProgress>>, Query<(&mut ServiceFacility,Option<&mut Inventory>)>, RemovedComponents<Guest>, Commands Returns reserved stock, clears invalid reservation/progress and decrements live facility Before despawn.
restock_inventories FixedUpdate / FixedGameSet::Economy MessageReader<ZooDayAdvanced>, Res<Assets<WorldDefinitionsAsset>>, Query<(&ServiceFacility,&mut Inventory)> Saturating available toward capacity without touching reserved units Exactly once per zoo-day event using authored inventory_restock_per_zoo_day.

5. Preflight producer contract

Not applicable: the engine consumes only native assets produced before launch. No XML/Z2F/filesystem/source precedence belongs here.

6. Behavioral evidence and disposition

Primary code evidence exists in cpp-reconstruction/include/openzt2/economy/ and src/economy/ (ZTEconomyComponent, transaction/tracker/data), plus shipped facility, product, admission, scenario, and economy definitions. Search analysis TSVs for economy messages and live-observe price UI, service timing/refunds, and insufficient-funds behavior. Mine commit d5657e5a only for facts in domain/src/simulation/economy.rs, facilities, features/simulation/economy.rs, and UI finance/money modules.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

Settle: source currency precision/rounding; whether negative cash is mode- dependent; admission debit semantics; purchase/service charge point; capacity and cancellation; sale/refund percentages; wage/upkeep cadence; finance report categories. Native authored/scenario values replace all guesses.

7. Required behavior

8. Performance contract

Steady accounting/service systems allocate zero. Transfers are O(messages) and account lookup O(1); service progression is O(active services), not all facilities × guests. Bevy message buffers are warmed/pre-sized before settled measurement. No ledger growth, strings, floats, filesystem, or asset payload clones. Hard tests cover zero-allocation successful/rejected transfers and a settled service tick. Mapped definition lookup remains allocation-free; no GPU resources are owned.

9. Acceptance criteria

Deliver the owned Rust subtree and exact exports. Tests cover checked money, atomic debit/credit, insufficient funds, missing entity, capacity, service completion/cancellation, and message uniqueness. Observable done: admission and completed commerce change guest/zoo balances once and UI consumers see the same facts. No stub, float money, manager, ledger world, or guessed price.

10. Required handoff

Report API/files, dependency and root wiring needs, registrations, direct consumers, evidence results/questions, seams, and allocation/page/gameplay checks. Root performs bulk compilation.

G19 — Donations

1. Identity and outcome

This package delivers guest donation decisions and donation-box/acceptor use as small ECS facts. A guest identifies an eligible authored subject, reaches an acceptor, and transfers money through G18 exactly once; the acceptor and zoo receive UI-visible totals. Donation decision and completion form one cohesive feature, while accounting remains owned by G18.

Integration wave: 3. Prerequisites: G17 guests and G18 economy.

2. Exclusive ownership

Exclusive path: reconstruction/game/src/plugins/donations/. Everything else is read-only. Root owns declarations, plugin/schedule/message/reflection wiring and cross-package alignment.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
DonationsPlugin Plugin unit struct Registers focused donation systems.
DonationAcceptor Component pub struct DonationAcceptor { pub category: AssetId, pub beneficiary: Option<Entity> } Marks a real box/facility/show/tour acceptor. Beneficiary must be live or absent.
DonationRng Component pub struct DonationRng(pub DeterministicRng) G32 domain-local stream on each guest, derived from zoo seed and PersistentId; sole donation roll source and persisted by G22.
DonationCandidate Component pub struct DonationCandidate { pub acceptor: Entity, pub subject: Entity, pub amount: Money } Lives on guest between decision and reservation; positive amount.
DonationProgress Component pub struct DonationProgress { pub acceptor: Entity, pub subject: Entity, pub amount: Money, pub remaining_ticks: u32 } Carries the accepted candidate facts through animation/payment without a lookup side table.
DonationTotal Component pub struct DonationTotal { pub amount: Money, pub count: u32 } Cumulative acceptor fact; checked arithmetic, saturating count only if evidence requires.
PendingDonationPayment Component pub struct PendingDonationPayment { pub guest: Entity, pub acceptor: Entity, pub subject: Entity, pub amount: Money } Lives only on the domain-owned transaction operation entity.
DonationRequest Message pub struct DonationRequest { pub guest: Entity, pub acceptor: Entity, pub subject: Entity, pub amount: Money } Real request after guest decision/reach.
DonationCompleted Message pub struct DonationCompleted { pub guest: Entity, pub acceptor: Entity, pub subject: Entity, pub amount: Money } Emitted once after matching G18 transaction succeeds.
DonationRejected Message pub struct DonationRejected { pub guest: Entity, pub acceptor: Entity, pub reason: DonationRejection } Typed domain failure, no mutation.
DonationRejection enum InvalidAcceptor, InvalidSubject, Unreachable, InsufficientFunds, RuleDenied, AccountFailure Fixed reason vocabulary.

Root contract requests: align G17 guest needs/satisfaction/destination and G14 reach/spatial facts; consume G18 Money, Wallet, Account, TransactionRequest, and TransactionCompleted directly. A14/G26/G27 must provide authored donation category/value facts without a local catalogue copy.

4. ECS ownership

No donation manager, candidate list resource, ledger copy, or guest snapshot is allowed. Candidate/progress lives on the donating guest and totals on the acceptor.

System Schedule/set Exact query and parameters Writes Run condition/order
choose_donation_candidates FixedUpdate / FixedGameSet::Think Res<ZooClock>, Res<Assets<WorldDefinitionsAsset>>, Res<SpatialGrid>, Query<(Entity,&GuestSatisfaction,&Wallet,&mut DonationRng),(With<Guest>,Without<DonationCandidate>,Without<DonationProgress>)>, Query<(Entity,&DonationAcceptor,&Transform)> , Commands Inserts DonationCandidate Due eligible guests.
begin_donations FixedUpdate / FixedGameSet::Act MessageReader<Arrived>, Query<(Entity,&DonationCandidate),With<Guest>>, Query<&DonationAcceptor>, Query<(),With<PhotoSubject>>, Commands Replaces candidate with progress Matching acceptor arrival.
advance_donations FixedUpdate / FixedGameSet::Act Res<ZooClock>, Query<(Entity,&mut DonationProgress,&Wallet),With<Guest>>, Query<&DonationAcceptor>, Commands, MessageWriter<DonationRequest> Countdown/request from progress facts Active donors.
request_donation_transfers FixedUpdate / FixedGameSet::Economy MessageReader<DonationRequest>, Query<(&Wallet,&DonationProgress,&WorldMember),With<Guest>>, Query<&DonationAcceptor>, Query<(),With<PendingDonationPayment>>, Commands, MessageWriter<TransactionRequest>, MessageWriter<DonationRejected> Pending operation carrying guest WorldMember plus transaction request Valid request.
complete_donations FixedUpdate / FixedGameSet::Cleanup MessageReader<TransactionCompleted>, Query<&PendingDonationPayment>, Query<Entity,With<DonationProgress>>, Query<&mut DonationTotal>, Commands, MessageWriter<DonationCompleted> Total/progress/operation/completion Operation match.
reject_donation_payments FixedUpdate / FixedGameSet::Cleanup MessageReader<TransactionRejected>, Query<&PendingDonationPayment>, Query<Entity,With<DonationProgress>>, Commands, MessageWriter<DonationRejected> Clears progress/operation; emits rejection Operation match.
cancel_invalid_donations FixedUpdate / FixedGameSet::Cleanup Query<(Entity,Option<&DonationCandidate>,Option<&DonationProgress>),With<Guest>>, Query<(),With<DonationAcceptor>>, RemovedComponents<Guest>, Commands, MessageWriter<DonationRejected> Clears transient components Broken relation.

5. Preflight producer contract

Not applicable: the engine consumes only native assets produced before launch. No original formats or filesystem access.

6. Behavioral evidence and disposition

Search shipped donation-box, animal/viewable, show and tour definitions; search analysis records/oracle for donation components/messages and ZT_SET_CURRENT_SHOW_FOR_DONATIONS. Observe original eligibility, animation, amount feedback, and box attribution. Mine commit d5657e5a only for behavioral leads in domain/src/simulation/donation.rs, features/simulation/donation.rs, guests, shows, and tours.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

Settle candidate scoring/chance, subject-to-acceptor association, donation amount formula, distance/reach/animation timing, cooldown/repeat limits, show and tour multipliers, education/satisfaction effects, and attribution categories from native definitions and live behavior. Never invent fallback amounts.

7. Required behavior

8. Performance contract

Zero allocations in steady systems. Candidate search uses bounded G14 spatial cells and due guests: O(nearby eligible pairs), never all guests × all boxes. Completion is O(transaction messages). No ledgers, strings, dynamic candidate lists, asset clones, or filesystem. Hard allocation tests cover a no-donation tick and full donation path after message buffers warm. No GPU ownership.

9. Acceptance criteria

Deliver exact exports under the owned path. Tests cover eligibility, insufficient wallet, invalid relationships, one transfer/one completion, checked totals, and cleanup. Observable done: an eligible guest reaches a matching box and donates; wallet/zoo/box totals agree exactly. No placeholder values or parallel economy.

10. Required handoff

Report files/API, root wiring/registration, dependencies, consumed symbols, resolved/open evidence, integration seams, and allocation/gameplay checks. Root performs bulk compilation.

G20 — Staff

1. Identity and outcome

This package delivers hiring, firing, wages, assignments, and executable work as staff and job entities. Keepers feed/clean/care; maintenance workers repair and empty waste; educators and other authored roles perform their actual jobs. There is no staff manager or central simulated workforce object.

Integration wave: 3. Prerequisites: A14 definitions, G10 facilities, G14 locomotion, G15 feeding, and G18 economy. G16 health requests treatment work.

2. Exclusive ownership

Exclusive path: reconstruction/game/src/plugins/staff/; all else read-only. Root owns declarations, registration, schedules, reflection, dependencies, and cross-package symbol alignment. Report collisions.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
StaffPlugin Plugin unit struct Registers staff employment/job systems only.
Staff Component marker Identifies one staff entity.
StaffRole Component pub struct StaffRole(pub AssetId) Immutable authored role definition.
Employment Component pub struct Employment { pub wage: Money, pub hired_tick: u64 } Wage uses G18 minor units and G32 calendar/ticks; cadence is definition/scenario-derived.
StaffAssignment Component pub struct StaffAssignment { pub area: Option<Entity>, pub target: Option<Entity> } Optional player-authored restriction; referenced entities must be live.
AvailableForWork Component marker Staff may claim work; removed while off-duty/working/incapacitated.
StaffJob Component pub struct StaffJob { pub kind: StaffJobKind, pub target: Entity, pub urgency: u16 } Lives on a dedicated job entity; target is live until cleanup.
JobClaim Component pub struct JobClaim { pub staff: Entity } Lives on job entity; at most one claimant.
CurrentJob Component pub struct CurrentJob { pub job: Entity } Lives on staff; inverse of one valid JobClaim.
JobProgress Component pub struct JobProgress { pub remaining_ticks: u32 } Lives on job after staff reaches target; duration is A14/G32 ticks.
PendingStaffHire Component pub struct PendingStaffHire { pub role: AssetId, pub position: Vec3 } Domain-owned payment operation; no staff entity exists before matching completion.
PendingWagePayment Component pub struct PendingWagePayment { pub staff: Entity, pub due_tick: u64 } Correlates one evidenced wage charge to one employee/cadence.
StaffJobRequest Message pub struct StaffJobRequest { pub kind: StaffJobKind, pub target: Entity, pub urgency: u16 } Natural asynchronous request from a dirty/broken/hungry/sick target.
HireStaffRequest Message pub struct HireStaffRequest { pub role: AssetId, pub position: Vec3 } Player request; validates price/placement through owning domains.
FireStaffRequest Message pub struct FireStaffRequest { pub staff: Entity } Player request; claimed work is released before despawn.
StaffHired Message pub struct StaffHired { pub staff: Entity, pub role: AssetId } Emitted exactly once after spawn/payment.
StaffFired Message pub struct StaffFired { pub staff: Entity } Emitted once before despawn.
StaffJobCompleted Message pub struct StaffJobCompleted { pub staff: Entity, pub job: Entity, pub kind: StaffJobKind, pub target: Entity } Emitted only after target mutation succeeds.
StaffJobCancelled Message pub struct StaffJobCancelled { pub kind: StaffJobKind, pub target: Entity } Emitted before invalid-job cleanup removes a still-live target's job, allowing its natural owner to re-evaluate unresolved work.

StaffJob, StaffJobRequest, and StaffJobCompleted use A14's exact StaffJobKind::{Feed,RefillWater,CleanHabitat,EmptyBin,SweepLitter,Repair,Treat, Educate,Entertain,Tranquilize,Capture,MaintainTank,OperateShow}; G20 declares no second enum. Root aligns G14 navigation target/reached/spatial facts, G15 feeder/container transactions, G16 treatment/recapture, G36 condition/waste, G17 education reactions, G18 transaction types, and A14 staff/job definitions. Do not introduce provider/facility traits or copies.

4. ECS ownership

Jobs are entities because they have lifecycle, contention, target, claim, and save relevance. There is no global queue; spatial lookup may index unclaimed job entities through G14's bounded index.

System Schedule/set Exact query and parameters Writes Run condition/order
create_staff_jobs FixedUpdate / FixedGameSet::Think MessageReader<StaffJobRequest>, Query<(&StaffJob,Option<&JobClaim>)>, Query<&WorldMember>, ResMut<PersistentIdAllocator>, Commands After target validation/ID allocation, spawns unique job entity with target's WorldMember and fresh PersistentId Deduplicate (kind,target); acknowledge no job before both identity facts exist.
claim_staff_jobs FixedUpdate / FixedGameSet::Think Res<Assets<WorldDefinitionsAsset>>, Res<SpatialGrid>, Query<(Entity,&StaffRole,&Transform,&StaffAssignment),(With<Staff>,With<AvailableForWork>,Without<CurrentJob>)>, Query<(Entity,&StaffJob),Without<JobClaim>>, Commands Claim/current job/availability Priority/distance/entity order.
begin_staff_job FixedUpdate / FixedGameSet::Act MessageReader<Arrived>, Res<Assets<WorldDefinitionsAsset>>, Query<(Entity,&CurrentJob),With<Staff>>, Query<&StaffJob>, Commands Inserts JobProgress Matching job target arrival.
advance_feeding_jobs FixedUpdate / FixedGameSet::Act Res<ZooClock>, Query<(Entity,&StaffJob,&mut JobProgress,&JobClaim)>, Query<(&mut FoodContainer,Option<&RefillPending>)>, Query<&mut DrinkContainer>, Commands, MessageWriter<RefillContainer>, MessageWriter<StaffJobCompleted> Progress/container requests Feed/RefillWater only.
advance_facility_operation_jobs FixedUpdate / FixedGameSet::Act Res<ZooClock>, Query<(Entity,&StaffJob,&mut JobProgress,&JobClaim)>, Query<&mut WaterQuality>, Query<&mut ShowState>, Commands, MessageWriter<StaffJobCompleted> Progress and natural target components MaintainTank/OperateShow only; G36 exclusively executes Repair.
advance_health_jobs FixedUpdate / FixedGameSet::Act Res<ZooClock>, Query<(Entity,&StaffJob,&mut JobProgress,&JobClaim)>, Query<(Option<&Disease>,Option<&Escaped>,Option<&Tranquilized>),With<Animal>>, Commands, MessageWriter<TreatmentRequest>, MessageWriter<TranquilizeRequest>, MessageWriter<StaffJobCompleted> Progress/health requests Treat/Tranquilize/Capture.
advance_guest_service_jobs FixedUpdate / FixedGameSet::Act Res<ZooClock>, Query<(Entity,&StaffJob,&mut JobProgress,&JobClaim)>, Query<(),With<Guest>>, Commands, MessageWriter<GuestReaction>, MessageWriter<StaffJobCompleted> Progress/reaction Educate/Entertain.
release_finished_jobs FixedUpdate / FixedGameSet::Cleanup MessageReader<StaffJobCompleted>, Query<(Entity,&CurrentJob),With<Staff>>, Query<&JobClaim>, Commands Job/current/availability After target mutation.
cancel_invalid_jobs FixedUpdate / FixedGameSet::Cleanup Query<(Entity,&StaffJob,Option<&JobClaim>)>, Query<(Entity,Option<&CurrentJob>),With<Staff>>, Query<(),With<WorldMember>>, Commands, MessageWriter<StaffJobCancelled> Emits cancellation for a still-live target, clears reciprocal facts and despawns job Before target/staff despawn; absent targets cannot be requeued.
request_staff_hiring FixedUpdate / FixedGameSet::Economy MessageReader<HireStaffRequest>, Res<Assets<WorldDefinitionsAsset>>, Query<&PendingStaffHire>, Query<(Entity,&WorldRoot)>, Commands, MessageWriter<TransactionRequest> Spawns PendingStaffHire operation with root WorldMember and emits a transaction carrying it No staff spawn and no duplicate (role,position) request.
complete_staff_hiring FixedUpdate / FixedGameSet::Cleanup MessageReader<TransactionCompleted>, Query<(Entity,&PendingStaffHire,&WorldMember)>, Res<Assets<WorldDefinitionsAsset>>, ResMut<PersistentIdAllocator>, Commands, MessageWriter<StaffHired> Validates/allocates first, then spawns staff bundle with operation's WorldMember and fresh PersistentId, emits hired, despawns operation TransactionCompleted.operation must equal the operation entity.
reject_staff_hiring FixedUpdate / FixedGameSet::Cleanup MessageReader<TransactionRejected>, Query<(Entity,&PendingStaffHire)>, Commands Despawns operation without staff spawn/success TransactionRejected.operation must equal the operation entity.
process_staff_firing FixedUpdate / FixedGameSet::Cleanup MessageReader<FireStaffRequest>, Query<(Entity,&StaffRole,&Employment,Option<&CurrentJob>),With<Staff>>, Query<&mut JobClaim>, Commands, MessageWriter<StaffFired> Releases job and despawns staff After job execution.
charge_staff_wages FixedUpdate / FixedGameSet::Economy MessageReader<ZooDayAdvanced>, Res<ZooClock>, Res<Assets<WorldDefinitionsAsset>>, Query<(Entity,&Employment,&WorldMember),With<Staff>>, Query<&PendingWagePayment>, Commands, MessageWriter<TransactionRequest> Spawns world-scoped PendingWagePayment and emits authored wage debit carrying it Exact original cadence; no second operation for the same (staff,due_tick).
settle_staff_wages FixedUpdate / FixedGameSet::Cleanup MessageReader<TransactionCompleted>, MessageReader<TransactionRejected>, Query<(Entity,&PendingWagePayment)>, Query<&mut Employment>, Commands Applies only evidenced employment consequence and despawns operation; never emits hire/job success Result operation must equal the operation entity.

5. Preflight producer contract

Not applicable: engine consumes only native assets produced before launch. No source parsing, filesystem discovery, or original codecs.

6. Behavioral evidence and disposition

Search shipped staff entity/task/role definitions; analysis/oracle names for keeper, maintenance, educator, veterinarian, assignment and hire/fire commands; observe job priority, assignment areas, animation and wages. Mine commit d5657e5a only for evidence in domain/src/simulation/staff.rs, features/simulation/staff.rs, disease, facilities, feeding and AI/navigation.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

Settle roles, job compatibility/scoring, duplicate suppression, assignment semantics, work durations/effects, operating requirements, wages, firing/refund, and unavailable states. Use native records/oracle/live behavior; no generic job bag or guessed work rate.

7. Required behavior

8. Performance contract

Zero allocations after warm-up. Claim search uses bounded spatial indexes and unclaimed changed jobs, O(nearby candidates), not staff × all jobs. Job entities avoid growing queues. Work systems are split by exact query and process only active progress. Allocation regressions cover idle, claim, work and cleanup; mapped lookups/handles allocate zero; no GPU ownership.

9. Acceptance criteria

Deliver exact API under owned path. Tests establish unique jobs/claims, priority/assignment, invalid cleanup, completed mutation, and single wage/hire/ fire transactions. Observable done: staff can be hired, assigned, autonomously complete authentic care/maintenance work, be paid, and be fired. No manager, whole-world dispatcher, stub, guessed rate, or duplicate target state.

10. Required handoff

Report files/API, dependencies, root registrations/wiring, consumed symbols, evidence, seams, and allocator/page/gameplay validation.

G21 — Information and Catalogues

1. Identity and outcome

This package connects selection to native UI panels, purchase catalogues, filters, buy information, entity status, and Zoopedia content. It projects live facts and immutable mapped records into the G02 UI without owning copies of the selected entity, catalogue, or simulation.

Integration wave: 3. Prerequisites: A02 UI, A09 localization, A14 definitions, G02 UI runtime, and G07 construction.

2. Exclusive ownership

Exclusive path: reconstruction/game/src/plugins/information/; all other paths read-only. Root owns registrations, loader/plugin wiring, schedule ordering, dependencies, and cross-package alignment.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
InformationPlugin Plugin unit struct Registers selection and information projection.
SelectedEntity Resource pub struct SelectedEntity(pub Option<Entity>) Sole current world selection; cleared when entity is invalid.
SelectionRequest Message pub struct SelectionRequest { pub entity: Option<Entity>, pub source: ActionSource } Device-independent selection request.
SelectionChanged Message pub struct SelectionChanged { pub previous: Option<Entity>, pub current: Option<Entity> } Emitted exactly once per actual change.
Inspectable Component pub struct Inspectable { pub definition: AssetId } Entity exposes mapped authored information plus live components.
InfoPanel Component pub struct InfoPanel { pub subject: Entity } Lives on one UI root node; no copied domain fields.
CataloguePanel Component pub struct CataloguePanel { pub category: CatalogueCategory, pub page: u16 } UI-node view state only.
CatalogueFilter Component pub struct CatalogueFilter { pub text_key: Option<AssetId>, pub unlocked_only: bool, pub affordable_only: bool } Lives on catalogue UI node; text_key refers to precompiled normalized search token, never runtime lowercase String.
CatalogueCommand Message pub enum CatalogueCommand { SetCategory(CatalogueCategory), SetPage(u16), SetUnlockedOnly(bool), SetAffordableOnly(bool), Choose(AssetId), OpenZoopedia(AssetId) } Fixed UI command result from A02/G02.
PurchaseChoice Message pub struct PurchaseChoice { pub definition: AssetId } Hands selected definition directly to G07/G11/G20 natural owner.
ZoopediaPage Component pub struct ZoopediaPage { pub subject: AssetId, pub section: u16 } UI node points into mapped localized native content.

CataloguePanel/CatalogueCommand consume A14's exact CatalogueCategory directly; this package declares no second category enum. A14 exposes direct indexed catalogue/Zoopedia records; G02 exposes node binding targets/fixed command results; G07/G11/G20 consume PurchaseChoice; G18/G23 expose direct cash/unlock facts; entity owners provide small live status components. Do not create an EntityInfo map, reflection property bag, UI model tree, or copied catalogue resource.

4. ECS ownership

System Schedule/set Exact query and parameters Writes Run condition/order
apply_selection_requests Update / GameSet::Intent MessageReader<SelectionRequest>, Query<(),With<Inspectable>>, ResMut<SelectedEntity>, MessageWriter<SelectionChanged> Selection resource only In game; deterministic last valid request.
clear_removed_selection Update / GameSet::Ui RemovedComponents<Inspectable>, ResMut<SelectedEntity>, MessageWriter<SelectionChanged> Clears invalid selection Before panel projection.
bind_info_panel_subject Update / GameSet::Ui Res<SelectedEntity>, Query<(&mut InfoPanel,&mut Visibility)>, Commands Panel subject/visibility SelectedEntity.is_changed().
project_static_information Update / GameSet::Ui Res<Assets<WorldDefinitionsAsset>>, Res<Assets<LocalizationAsset>>, Query<&Inspectable>, Query<(&InfoPanel,&InheritedVisibility),Changed<InfoPanel>>, Query<(&UiTextBinding,&mut Text)>, Query<(&UiImageBinding,&mut ImageNode)> UI node text/image handles/visibility Selection/locale change only; no owned copy.
project_identity_name Update / GameSet::Ui Res<Assets<WorldDefinitionsAsset>>, Res<Assets<LocalizationAsset>>, Query<(&InfoPanel,&InheritedVisibility)>, Query<(&Inspectable,Option<&Name>)>, Query<(&UiTextBinding,&mut Text)>, Query<(&UiImageBinding,&mut ImageNode)> Identity/name/icon node values only Subject, locale, or Name changed.
project_animal_needs Update / GameSet::Ui Query<(&InfoPanel,&InheritedVisibility)>, Query<(&Hunger,&Thirst,&RestNeed,&PrivacyNeed,&SocialNeed,&ExerciseNeed,&StimulationNeed,&EnvironmentNeed,&KeeperCareNeed,&HealthNeed,&AnimalWelfare,&WelfareBand),With<Animal>>, Query<(&UiValueBinding,&mut UiValue)>, Query<(&UiTextBinding,&mut Text)> Need bars and welfare label only Relevant G12 component changed.
project_animal_health Update / GameSet::Ui Res<Assets<WorldDefinitionsAsset>>, Res<Assets<LocalizationAsset>>, Query<(&InfoPanel,&InheritedVisibility)>, Query<(&Vitality,Option<&Disease>,Option<&Treatment>,Option<&Tranquilized>,Option<&Escaped>,Option<&Rampaging>,Option<&Dead>),With<Animal>>, Query<(&UiValueBinding,&mut UiValue)>, Query<(&UiTextBinding,&mut Text)>, Query<(&UiVisibleBinding,&mut Visibility)> Health/disease/treatment node values and visibility only Relevant G16 component changed.
project_guest_status Update / GameSet::Ui Query<(&InfoPanel,&InheritedVisibility)>, Query<(&GuestPhase,&GuestHunger,&GuestThirst,&GuestEnergy,&GuestRestroom,&GuestEntertainment,&GuestSatisfaction,&GuestEducation,&VisitTime),With<Guest>>, Query<(&UiValueBinding,&mut UiValue)>, Query<(&UiTextBinding,&mut Text)> Guest need/satisfaction/education/visit node values only Relevant G17 component changed.
project_staff_status Update / GameSet::Ui Query<(&InfoPanel,&InheritedVisibility)>, Query<(&StaffRole,&Employment,Option<&StaffAssignment>,Option<&CurrentJob>),With<Staff>>, Query<(&StaffJob,Option<&JobProgress>)>, Query<(&UiValueBinding,&mut UiValue)>, Query<(&UiTextBinding,&mut Text)> Role/wage/assignment/current-job node values only Relevant G20 component changed.
project_facility_status Update / GameSet::Ui Query<(&InfoPanel,&InheritedVisibility)>, Query<(Option<&Price>,Option<&ServiceFacility>,Option<&Inventory>,Option<&Condition>,Option<&WasteContainer>)>, Query<(&UiValueBinding,&mut UiValue)>, Query<(&UiTextBinding,&mut Text)> G18 price/capacity/inventory and G36 condition/waste values only Relevant facility fact changed; no generic component dispatch.
project_sanitation_status Update / GameSet::Ui Query<(&InfoPanel,&InheritedVisibility)>, Query<(Option<&Litter>,Option<&HabitatWaste>)>, Res<ZooCleanliness>, Query<(&UiValueBinding,&mut UiValue)> Loose-waste amount and zoo cleanliness values only Relevant G36 fact changed.
project_habitat_status Update / GameSet::Ui Query<(&InfoPanel,&InheritedVisibility)>, Query<(&HabitatRegion,&HabitatSummary,Option<&TankGeometry>,Option<&WaterQuality>,Option<&TankCapacity>)>, Query<(&UiValueBinding,&mut UiValue)>, Query<(&UiTextBinding,&mut Text)> Habitat area/biome/breach and tank quality/capacity nodes only Relevant G09/G25 fact changed.
project_show_status Update / GameSet::Ui Query<(&InfoPanel,&InheritedVisibility)>, Query<(&ShowStage,&ShowSchedule,&ShowState)>, Query<(&UiValueBinding,&mut UiValue)>, Query<(&UiTextBinding,&mut Text)> Schedule/capacity/current-state nodes only Relevant G26 fact changed.
project_transport_status Update / GameSet::Ui Query<(&InfoPanel,&InheritedVisibility)>, Query<(Option<&TransportCircuit>,Option<&TransportStation>,Option<&TransportVehicle>,Option<&WaitingForTransport>,Option<&TransportRider>,Option<&TourScore>)>, Query<(&UiValueBinding,&mut UiValue)>, Query<(&UiTextBinding,&mut Text)> Circuit/station/vehicle/trip node values only Relevant G27 fact changed.
project_scenario_status Update / GameSet::Ui Res<ActiveScenario>, Res<ZooClock>, Query<(&InfoPanel,&InheritedVisibility)>, Query<(&Objective,&ObjectiveStatus,&ObjectiveProgress,Option<&ObjectiveDeadline>)>, Query<(&UiValueBinding,&mut UiValue)>, Query<(&UiTextBinding,&mut Text)> Objective/status/progress/deadline nodes only Relevant G24 fact or clock display boundary changed.
project_progression_status Update / GameSet::Ui Res<Fame>, Res<ZooRating>, Res<UnlockSet>, Query<(&InfoPanel,&InheritedVisibility)>, Query<(Option<&ResearchProject>,Option<&AwardEarned>)>, Query<(&UiValueBinding,&mut UiValue)>, Query<(&UiTextBinding,&mut Text)> Fame/rating/research/award node values only Relevant G23 fact changed.
apply_catalogue_commands Update / GameSet::Intent MessageReader<CatalogueCommand>, Query<(&mut CataloguePanel,&mut CatalogueFilter)>, MessageWriter<PurchaseChoice>, MessageWriter<ShowUiRole> UI-node state/messages In menu/in game where panel exists.
project_catalogue_page Update / GameSet::Ui Res<Assets<WorldDefinitionsAsset>>, Res<ZooCash>, Res<UnlockSet>, Query<(&CataloguePanel,&CatalogueFilter),Or<(Changed<CataloguePanel>,Changed<CatalogueFilter>)>>, Query<(&UiNodeId,&mut Text,&mut Visibility)> Row bindings/visibility O(page size), no catalogue cloning/sorting.
project_zoopedia_page Update / GameSet::Ui Res<Assets<WorldDefinitionsAsset>>, Res<Assets<LocalizationAsset>>, Query<&ZoopediaPage,Changed<ZoopediaPage>>, Query<(&UiTextBinding,&mut Text)>, Query<(&UiImageBinding,&mut ImageNode)>, Query<(&UiVisibleBinding,&mut Visibility)> Text/media handles/visibility No runtime markup parsing.

5. Preflight producer contract

Not applicable: engine consumes only native assets produced before launch. Search normalization, indexes, UI commands, and Zoopedia markup are compiled by A02/A09/A14; this package never reads XML/Z2F or files.

6. Behavioral evidence and disposition

Search shipped UI/, entity definition, lang and Zoopedia source families; analysis TSVs for select/info/buy/catalogue/Zoopedia commands; live-observe panel tabs, filters, ordering and missing facts. Mine d5657e5a only for evidence in features/interaction/entity_info.rs, UI binding/lists/zoopedia/finance, and catalog records.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

Settle original category/order/filter rules, page sizes, definition-to-panel selection, visible live fields/formatting, Zoopedia navigation, and purchase command semantics. Recover from A02/A14 source/oracle/live UI. Do not guess UI fields or revive generic property projection.

7. Required behavior

8. Performance contract

Mapped lookup/traversal and handle clone allocate zero. Projection is event/ changed/visibility driven; catalogue work O(page size), info work O(visible fields). Rows and text buffers are reused; no per-frame String, sorting, reflection maps, asset clones, filesystem, or dynamic node rebuild. Allocator tests cover settled hidden/visible panels and page navigation. GPU bytes remain owned by A05/A02 handles, not duplicated here.

9. Acceptance criteria

Deliver owned Rust and exact exports. Tests cover valid/invalid selection, single change event, direct live fact projection, deterministic filtering/ paging, unlock/affordability, and stable purchase choice. Observable done: players inspect entities, browse/buy from source-driven catalogues, and navigate Zoopedia with authentic information. No bag, shadow UI model, guessed panel, or runtime source parser.

10. Required handoff

Report files/API, root registrations/wiring, dependencies, direct symbols, evidence, temporary seams, allocator/page/GPU/UI checks.

G22 — Persistence

1. Identity and outcome

This package delivers native player profiles/options and fast save/load of the actual ECS facts. A save is a versioned consumer-shaped snapshot keyed by stable entity IDs and AssetId; loading replaces the current persistent entities and repairs relationships. It never parses original saves at runtime and never retains a mirror world. Original save/map import belongs to A19 preflight.

Integration wave: 4. Prerequisites: G04–G21 and G23–G36 as direct component consumers. G01–G03 and G21 own shell/input/UI state and have no world-snapshot section.

2. Exclusive ownership

Exclusive path: reconstruction/game/src/plugins/persistence/; all else read-only. Root owns Cargo features/dependencies, module/plugin/schedule and reflection/type registration. The worker delivers the complete concrete section pipeline through G36 now; integration must not add a registration facade or a later catch-all serializer. Worker may not edit source components to make them serializable.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
PersistencePlugin Plugin unit struct Registers request/I/O/apply pipeline only.
SaveSlotId value type #[repr(transparent)] pub struct SaveSlotId(pub u32) Platform storage slot key; not a filesystem path.
SaveRequest Message pub struct SaveRequest { pub slot: SaveSlotId } Player/autosave request.
LoadRequest Message pub struct LoadRequest { pub slot: SaveSlotId } Requests native snapshot load.
DeleteSaveRequest Message pub struct DeleteSaveRequest { pub slot: SaveSlotId } Explicit player request to remove one native slot.
SaveCompleted Message pub struct SaveCompleted { pub slot: SaveSlotId, pub bytes: u64 } Emitted after durable atomic replacement succeeds.
LoadCompleted Message pub struct LoadCompleted { pub slot: SaveSlotId } Emitted after entity spawn, relationship repair, and validation.
SaveDeleted Message pub struct SaveDeleted { pub slot: SaveSlotId } Emitted only after the slot is durably absent.
DeleteGeneratedImage Message pub struct DeleteGeneratedImage { pub photo: Entity, pub image: Handle<Image> } G28 requests deletion of one profile-owned generated image; G22 performs the explicit platform-storage operation without exposing filesystem access to gameplay.
PersistenceFailed Message pub struct PersistenceFailed { pub operation: PersistenceOperation, pub slot: SaveSlotId, pub reason: PersistenceFailure } Typed failure; current world remains valid.
PersistenceOperation enum Save, Load, Delete Fixed operation vocabulary.
PersistenceFailure enum Missing, Io, InvalidHeader, UnsupportedVersion, WrongProfile, CorruptSection, UnknownAsset, DuplicateEntityId, BrokenReference, CapacityExceeded No source-format fallback.
SnapshotHeader POD struct #[repr(C)] pub struct SnapshotHeader { pub magic: [u8; 8], pub version: u32, pub profile_id: AssetId, pub entity_count: u32, pub section_count: u32, pub payload_bytes: u64, pub checksum: [u8; 32] } Little-endian native save header validated before apply.
SnapshotSection enum World, Terrain, Topology, Placement, Maintenance, Animals, Welfare, Behavior, Locomotion, Feeding, Health, Guests, Economy, Donations, Staff, Progression, Scenario, Aquatic, Shows, Transport, Photos, ExtinctAnimals, GesturesPuzzles, SimulationTime, Environment Exact deterministic section order; no dynamic registration or opaque section names.
SnapshotScratch Resource pub struct SnapshotScratch { pub bytes: Vec<u8>, pub saved_ids: Vec<PersistentId>, pub entity_map: Vec<(PersistentId, Entity)>, pub validated: bool } Before mutation it holds only snapshot bytes plus sorted saved IDs; no staged World, entities, components, or asset copies. validated is set only after every semantic validator succeeds.
ProfileRecord value pub struct ProfileRecord { pub id: AssetId, pub display_name: String, pub last_played_unix_ms: u64 } Native OpenZT2 profile metadata; bounded UTF-8 player text is loaded only during shell/profile mutation.
ProfileIndex Resource pub struct ProfileIndex { pub entries: Vec<ProfileRecord>, pub selected: Option<AssetId> } Sole bounded installed-profile catalogue; sorted by stable ID and replaced atomically after explicit profile I/O.
ProfileOptions Resource pub struct ProfileOptions { pub profile_id: AssetId, pub locale: AssetId, pub controller_enabled: bool } Genuine global user/profile policy only. A11 AudioMixSettings is the sole live volume authority and is serialized directly; fast preload is startup/mapping policy owned by A01/G35, not persistence state.
LoadProfileIndex Message pub struct LoadProfileIndex; Boot/profile-screen request for native profile metadata.
CreateProfile Message pub struct CreateProfile { pub display_name: String } Creates one OpenZT2 profile after bounded UTF-8/name uniqueness validation.
SelectProfile Message pub struct SelectProfile { pub profile: AssetId } Selects one existing stable profile identity and loads its options/mix state.
DeleteProfile Message pub struct DeleteProfile { pub profile: AssetId } Explicit removal request; active profile and owned saves require evidenced confirmation policy.
ProfileIndexReady Message pub struct ProfileIndexReady; G01 may enumerate ProfileIndex after this fact.
ProfileCreated Message pub struct ProfileCreated { pub profile: AssetId } Emitted after durable metadata/options creation.
ProfileSelected Message pub struct ProfileSelected { pub profile: AssetId } Emitted after ProfileOptions, A11 audio, G03 bindings, and G34 display/graphics settings are loaded.
ProfileDeleted Message pub struct ProfileDeleted { pub profile: AssetId } Emitted after profile metadata and policy-approved owned data are durably absent.
ProfileOperationFailed Message pub struct ProfileOperationFailed { pub profile: Option<AssetId>, pub reason: ProfileFailure } Typed shell-visible failure with no partial selected state.
ProfileFailure enum Io, CorruptIndex, InvalidName, DuplicateName, Missing, Active, HasSaves, CapacityExceeded Fixed native-profile failure vocabulary.

G22 consumes G04's naturally owned PersistentId: G04 assigns the nonzero, world-unique stable identity at authoritative spawn, and G22 persists it. Every snapshot-eligible entity already carries PersistentId and WorldMember before its owning domain acknowledges creation, exactly as G04 requires. Snapshot sections directly encode concrete G04–G20 and G23–G33 components; there is no public serializer trait, registration facade, generic property bag, DynamicScene, or retained World copy. Platform slot I/O is a narrow root-provided storage path or Bevy task, not a public gameplay service graph.

Profile identity is one stable AssetId everywhere; G01 selection and G04 world-load requests already use that exact type. A UI row index is presentation only and is resolved to the stable ID before any request is emitted. Profile-index/create/select/delete systems are integration wave 2 so G01 has a real profile source; full world snapshot systems remain wave 4.

4. ECS ownership

The snapshot exists only during an explicit save/load operation. Serialization systems query concrete component tuples per domain into deterministic sections; load systems spawn concrete bundles then repair PersistentId references. G18 transaction-operation entities and every domain's Pending*Payment/ Pending*Transaction component are transient within one fixed-step economy to cleanup chain. begin_save runs only after that chain is drained, so these are neither snapshot records nor ambiguous resumable transfers.

The following are separate Bevy systems. The writer chain is serial only during an explicit save so section bytes remain canonical; its queries stay narrow and do not force unrelated gameplay systems or normal frames into one access set.

Save section systems

System Schedule/set Exact query and parameters Writes Run condition/order
begin_save Update / GameSet::Intent MessageReader<SaveRequest>, Res<ProfileOptions>, ResMut<SnapshotScratch> Clears logical scratch and writes header placeholder Explicit request; no operation active.
write_world_section Update / GameSet::Intent Query<(&WorldRoot,&WorldBounds)>, ResMut<SnapshotScratch> The sole world-root record; root identity is structural and is not an entity-reference ID After begin; exactly one root.
write_terrain_section Update / GameSet::Intent Query<(&PersistentId,&TerrainChunk,&EditedTerrainSamples)>, ResMut<SnapshotScratch> Edited terrain overlays only After world.
write_topology_section Update / GameSet::Intent Query<(&PersistentId,&TopologyNode,Option<&Gate>)>, Query<(&PersistentId,&FenceEdge)>, Query<(&PersistentId,&PathTile,Option<&PathSupport>)>, Query<(&PersistentId,&Portal)>, ResMut<SnapshotScratch> Topology records and saved-ID links After terrain.
write_placement_section Update / GameSet::Intent Query<(&PersistentId,&Placeable,&Transform,&FootprintOccupancy,Option<&AuthoredEntrance>)>, ResMut<SnapshotScratch> Durable placed-object facts After topology.
write_maintenance_section Update / GameSet::Intent Query<(&PersistentId,Option<&Condition>,Option<&WasteContainer>,Option<&WasteProduction>),Or<(With<Condition>,With<WasteContainer>,With<WasteProduction>)>>, Query<(&PersistentId,&DefinitionId,&Transform,Option<&Litter>,Option<&HabitatWaste>),Or<(With<Litter>,With<HabitatWaste>)>>, ResMut<SnapshotScratch> G36 object facts, animal production deadlines, and persistent loose-waste entities After placement; records sorted by PersistentId.
write_animal_section Update / GameSet::Intent Query<(&PersistentId,&SpeciesHandle,&AnimalVariant,&AnimalSex,&AnimalLifeStage,&Age,Option<&Pregnancy>,Option<&Parents>,&ReproductionRng)>, ResMut<SnapshotScratch> Animal/lifecycle records and saved-ID links After maintenance.
write_welfare_section Update / GameSet::Intent Query<(&PersistentId,&Hunger,&Thirst,&RestNeed,&PrivacyNeed,&SocialNeed,&ExerciseNeed,&StimulationNeed,&EnvironmentNeed,&KeeperCareNeed,&HealthNeed,&HabitatSuitability,&AnimalWelfare,&WelfareBand),With<Animal>>, ResMut<SnapshotScratch> Welfare records After animals.
write_behavior_section Update / GameSet::Intent Query<(&PersistentId,&BehaviorHandle,Option<&ActiveTask>,&BehaviorRng)>, ResMut<SnapshotScratch> Active behavior facts, optional target saved ID, and RNG After welfare.
write_locomotion_section Update / GameSet::Intent Query<(&PersistentId,&NavAgent,&Velocity,Option<&Destination>,Option<&Route>,Option<&Docking>)>, ResMut<SnapshotScratch> In-progress locomotion and docking links; derived Steering, SpatialCell, and SpatialGrid excluded After behavior.
write_feeding_section Update / GameSet::Intent Query<(&PersistentId,&FoodContainer)>, Query<(&PersistentId,&DrinkContainer)>, Query<(&PersistentId,&FeedingTask)>, Query<(&PersistentId,&DrinkingTask)>, Query<(&PersistentId,&ConsumptionReservation)>, Query<(&PersistentId,&RefillPending)>, ResMut<SnapshotScratch> Inventories and active feeding relations After locomotion.
write_health_section Update / GameSet::Intent Query<(&PersistentId,&Vitality,&DiseaseRng,Option<&Disease>,Option<&Treatment>,Option<&Tranquilized>,Option<&Escaped>,Option<&Rampaging>,Option<&Dead>)>, ResMut<SnapshotScratch> Health facts, RNG, and treatment provider saved IDs After feeding.
write_guest_section Update / GameSet::Intent Res<GuestArrivalState>, Query<(&PersistentId,&GuestArchetype,&GuestRng,&GuestPhase,&GuestHunger,&GuestThirst,&GuestEnergy,&GuestRestroom,&GuestComfort,&GuestSatisfaction,&GuestEducation,&VisitTime,Option<&Viewing>,Option<&GuestDestination>,&GuestMemories),With<Guest>>, ResMut<SnapshotScratch> Arrival policy cursor/RNG plus guest facts and saved-ID links After health.
write_economy_section Update / GameSet::Intent Res<ZooCash>, Res<AdmissionPrice>, Query<(&PersistentId,&Wallet)>, Query<(&PersistentId,&Price)>, Query<(&PersistentId,&ServiceFacility)>, Query<(&PersistentId,&Inventory)>, Query<(&PersistentId,&ServiceReservation)>, Query<(&PersistentId,&ServiceProgress)>, ResMut<SnapshotScratch> Zoo/entity economy, stock and service facts/links After guests.
write_donation_section Update / GameSet::Intent Query<(&PersistentId,&DonationAcceptor)>, Query<(&PersistentId,&DonationTotal)>, Query<(&PersistentId,&DonationRng)>, Query<(&PersistentId,&DonationCandidate)>, Query<(&PersistentId,&DonationProgress)>, ResMut<SnapshotScratch> Donation totals, RNG, and proven in-progress facts After economy.
write_staff_section Update / GameSet::Intent Query<(&PersistentId,&StaffRole,&Employment,Option<&StaffAssignment>,Option<&AvailableForWork>,Option<&CurrentJob>),With<Staff>>, Query<(&PersistentId,&StaffJob,Option<&JobClaim>,Option<&JobProgress>)>, ResMut<SnapshotScratch> Staff/job facts and saved-ID links After donations.
write_progression_section Update / GameSet::Intent Res<Fame>, Res<ZooRating>, Res<UnlockSet>, Query<(&PersistentId,&ResearchProject,Option<&ResearchPaused>)>, Query<(&PersistentId,&AwardEarned)>, Query<(&PersistentId,&AwardConditionProgress)>, ResMut<SnapshotScratch> Progression facts After staff.
write_scenario_section Update / GameSet::Intent Option<Res<ActiveScenario>>, Option<Res<CampaignProgress>>, Res<ChallengeOfferSchedule>, Query<(&PersistentId,&Objective,&ObjectiveStatus,&ObjectiveProgress,Option<&ObjectiveDeadline>)>, Query<(&PersistentId,&ScenarioRuleNode,&RuleSatisfied,Option<&RuleProgress>)>, Query<(&PersistentId,&TutorialStep)>, Query<(&PersistentId,&ChallengeOffer)>, ResMut<SnapshotScratch> Optional primary/campaign state, deterministic challenge schedule, offer state, objectives/rules and links After progression.
write_aquatic_section Update / GameSet::Intent Query<(&PersistentId,&Tank,&TankGeometry,&WaterQuality,&TankCapacity)>, Query<(&PersistentId,&AquaticAnimal,&AquaticHome),With<Animal>>, ResMut<SnapshotScratch> Aquatic facts and home links After scenario.
write_show_section Update / GameSet::Intent Query<(&PersistentId,&ShowStage,&ShowSchedule,&ShowState)>, Query<(&PersistentId,&TrickTraining,Option<&TrainingProgress>)>, Query<(&PersistentId,&Performing),With<Animal>>, Query<(&PersistentId,&WatchingShow),With<Guest>>, ResMut<SnapshotScratch> Show facts and participant links After aquatic.
write_transport_section Update / GameSet::Intent Query<(&PersistentId,&TransportCircuit)>, Query<(&PersistentId,&CircuitMember,Option<&TransportStation>,Option<&TrackSegment>,Option<&TransportVehicle>,Option<&RoutePosition>,Option<&StationDestination>)>, Query<(&PersistentId,Option<&WaitingForTransport>,Option<&TransportRider>,Option<&TourScore>),With<Guest>>, Query<(&PersistentId,&TourViewable)>, ResMut<SnapshotScratch> Transport graph/live-trip facts and links After shows.
write_photo_section Update / GameSet::Intent Query<(&PersistentId,&Photo,&PhotoSubjects,&AlbumMember)>, Query<(&PersistentId,&PhotoAlbum)>, ResMut<SnapshotScratch> Photo metadata and generated-image storage IDs, never pixels After transport.
write_extinct_section Update / GameSet::Intent Res<FossilCollection>, Query<(&PersistentId,&FossilSite,&FossilRng)>, Query<(&PersistentId,&FossilPiece)>, Query<(&PersistentId,&AssemblyTable,&FossilAssembly)>, Query<(&PersistentId,&CloneLab,&CloneRng,Option<&CloneProcess>)>, ResMut<SnapshotScratch> Fossil/cloning facts and RNG After photos.
write_puzzle_section Update / GameSet::Intent Query<(&PersistentId,&Puzzle)>, ResMut<SnapshotScratch> Durable puzzle progress; active pointer GestureStroke is excluded After extinct animals.
write_simulation_time_section Update / GameSet::Intent Res<ZooClock>, Res<ZooCalendar>, Res<SimulationControl>, Res<ZooSeed>, ResMut<SnapshotScratch> Exact clock/calendar/control/seed After puzzles.
write_environment_section Update / GameSet::Intent Query<(&PersistentId,&WorldEnvironment,&Daylight,&Weather,Option<&WeatherTransition>,&Wind,&EnvironmentRandom)>, Query<(&PersistentId,&AmbientSpawner)>, ResMut<SnapshotScratch> Weather and deterministic spawner continuation; presentation markers and ambient animals excluded Last section.
finish_save Update / GameSet::Intent Res<ProfileOptions>, ResMut<SnapshotScratch>, Query<(Entity,&mut SlotIoTask)>, Commands, MessageWriter<SaveCompleted>, MessageWriter<PersistenceFailed> Finalizes header/checksum, starts or polls one atomic-replacement task, then clears logical scratch and despawns task After environment; explicit I/O exception.

Load, spawn, repair, and profile systems

The two private asynchronous operation components named below are fixed rather than left as a storage abstraction: ProfileIoTask(Task<io::Result<ProfileIoCompletion>>) and SlotIoTask(Task<io::Result<SlotIoCompletion>>). Their private completion enums contain only the exact owned request payload and native bytes/records needed by the matching completion system. They live on one operation entity, are polled only by the named completion system, and are despawned after one success or failure. They are not a public storage service or retained runtime.

Every entity-producing row after restore_world_snapshot_records also inserts G04 WorldMember { root } and appends (PersistentId, Entity) to the pre-reserved sorted map in SnapshotScratch. Every repair row performs allocation-free binary lookup in that map. All mandatory and optional reference policy was already proven against saved_ids before despawn; repair cannot discover an ordinary data failure after mutation. Derived G09 habitat/index facts and G21 UI selection are rebuilt by their natural systems.

System Schedule/set Exact query and parameters Writes Run condition/order
load_profile_index Update / GameSet::Intent MessageReader<LoadProfileIndex>, Query<(),With<ProfileIoTask>>, Commands, MessageWriter<ProfileOperationFailed> Spawns one bounded native profile-index read task Integration wave 2; explicit request and no existing task.
complete_profile_index_load Update / GameSet::Intent Query<(Entity,&mut ProfileIoTask)>, ResMut<ProfileIndex>, Commands, MessageWriter<ProfileIndexReady>, MessageWriter<ProfileOperationFailed> Atomically replaces sorted profile catalogue and despawns task Ready task only; no partial index.
create_profiles Update / GameSet::Intent MessageReader<CreateProfile>, Res<ProfileIndex>, Query<(),With<ProfileIoTask>>, Commands, MessageWriter<ProfileCreated>, MessageWriter<ProfileOperationFailed> Spawns one create task; its completion atomically creates stable ID/default options/default A11 mix Explicit request; bounded unique name.
select_profile Update / GameSet::Intent MessageReader<SelectProfile>, Res<ProfileIndex>, Query<(),With<ProfileIoTask>>, ResMut<ProfileOptions>, ResMut<AudioMixSettings>, ResMut<InputBindings>, ResMut<DisplaySettings>, ResMut<GraphicsSettings>, Commands, MessageWriter<ProfileSelected>, MessageWriter<ProfileOperationFailed> On ready task, atomically replaces selected ID and every natural live settings owner Existing profile only; after index ready.
delete_profiles Update / GameSet::Intent MessageReader<DeleteProfile>, Res<ProfileIndex>, Query<(),With<ProfileIoTask>>, Commands, MessageWriter<ProfileDeleted>, MessageWriter<ProfileOperationFailed> Spawns one delete task and atomically removes allowed profile metadata/data on completion Existing inactive profile and evidenced save policy.
begin_load Update / GameSet::Intent MessageReader<LoadRequest>, Res<ProfileOptions>, ResMut<SnapshotScratch>, Query<(),With<SlotIoTask>>, Commands, MessageWriter<PersistenceFailed> Spawns bounded slot read; on ready completion validates header/checksum/profile/section ranges and resets validated=false Explicit request; current world untouched.
prevalidate_snapshot_ids Update / GameSet::Intent ResMut<SnapshotScratch>, MessageWriter<PersistenceFailed> Traverses immutable section ranges in SnapshotScratch.bytes, builds sorted saved_ids, and rejects zero/duplicate IDs, wrong counts, overflow, or out-of-range records After structural validation; no ECS mutation.
prevalidate_world_records Update / GameSet::Intent ResMut<SnapshotScratch>, Res<Assets<WorldScenarioAsset>>, MessageWriter<PersistenceFailed> Validates the world section's single root, scenario/map IDs, bounds, and root invariants No ECS mutation.
prevalidate_terrain_records Update / GameSet::Intent ResMut<SnapshotScratch>, Res<Assets<TerrainAsset>>, MessageWriter<PersistenceFailed> Validates terrain chunk IDs, coordinates, sample ranges, ownership and saved IDs After world validator.
prevalidate_topology_records Update / GameSet::Intent ResMut<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>>, MessageWriter<PersistenceFailed> Validates topology record kinds, IDs, cells, state and saved-ID relations After terrain validator.
prevalidate_placement_records Update / GameSet::Intent ResMut<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>>, Res<Assets<ScenePrefabAsset>>, MessageWriter<PersistenceFailed> Validates placement definitions, transforms, occupancy and saved IDs After topology validator.
prevalidate_maintenance_records Update / GameSet::Intent ResMut<SnapshotScratch>, Res<Assets<SpeciesCatalogAsset>>, Res<Assets<WorldDefinitionsAsset>>, MessageWriter<PersistenceFailed> Validates maintenance bounds, definitions, transforms, deadlines, section coverage and saved IDs After placement validator; no ECS mutation.
prevalidate_animal_records Update / GameSet::Intent ResMut<SnapshotScratch>, Res<Assets<SpeciesCatalogAsset>>, MessageWriter<PersistenceFailed> Validates animal identity/lifecycle/RNG/lineage and saved IDs After maintenance validator.
prevalidate_welfare_records Update / GameSet::Intent ResMut<SnapshotScratch>, MessageWriter<PersistenceFailed> Validates animal coverage, G12 bounds, suitability, welfare and band consistency After animal validator.
prevalidate_behavior_records Update / GameSet::Intent ResMut<SnapshotScratch>, Res<Assets<BehaviorProgramAsset>>, MessageWriter<PersistenceFailed> Validates behavior indexes, task policy, RNG and saved IDs After welfare validator.
prevalidate_locomotion_records Update / GameSet::Intent ResMut<SnapshotScratch>, Res<Assets<NavigationAsset>>, MessageWriter<PersistenceFailed> Validates locomotion values, route capacity/cursor, docking and saved IDs After behavior validator.
prevalidate_feeding_records Update / GameSet::Intent ResMut<SnapshotScratch>, Res<Assets<SpeciesCatalogAsset>>, Res<Assets<WorldDefinitionsAsset>>, MessageWriter<PersistenceFailed> Validates feeding contents, bounds, exclusivity and saved-ID links After locomotion validator.
prevalidate_health_records Update / GameSet::Intent ResMut<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>>, MessageWriter<PersistenceFailed> Validates health bounds/states/causes/RNG and saved-ID provider links After feeding validator.
prevalidate_guest_records Update / GameSet::Intent ResMut<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>>, MessageWriter<PersistenceFailed> Validates arrival policy/next tick/RNG plus guest archetype/phase/fixed fields/memories/RNG and saved IDs After health validator.
prevalidate_economy_records Update / GameSet::Intent ResMut<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>>, MessageWriter<PersistenceFailed> Validates economy money, available + reserved <= capacity, reservation-unit consistency, service capacity/price/progress and saved-ID links After guest validator.
prevalidate_donation_records Update / GameSet::Intent ResMut<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>>, MessageWriter<PersistenceFailed> Validates donation totals/progress/RNG and saved-ID links After economy validator.
prevalidate_staff_records Update / GameSet::Intent ResMut<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>>, MessageWriter<PersistenceFailed> Validates staff roles/wages/jobs/assignments/progress and reciprocal saved IDs After donation validator.
prevalidate_progression_records Update / GameSet::Intent ResMut<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>>, MessageWriter<PersistenceFailed> Validates fame/rating/unlock/research/award bounds and uniqueness After staff validator.
prevalidate_scenario_records Update / GameSet::Intent ResMut<SnapshotScratch>, Res<Assets<WorldScenarioAsset>>, MessageWriter<PersistenceFailed> Validates optional primary/campaign state, challenge schedule/RNG/offer state, scenario-scoped objective/rule/status/deadline records and saved IDs After progression validator.
prevalidate_aquatic_records Update / GameSet::Intent ResMut<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>>, Res<Assets<TerrainAsset>>, MessageWriter<PersistenceFailed> Validates tank geometry/water/capacity/requirements and saved-ID home links After scenario validator.
prevalidate_show_records Update / GameSet::Intent ResMut<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>>, MessageWriter<PersistenceFailed> Validates show slots/state/tricks/proficiency and saved-ID participant links After aquatic validator.
prevalidate_transport_records Update / GameSet::Intent ResMut<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>>, Res<Assets<NavigationAsset>>, MessageWriter<PersistenceFailed> Validates transport graph/capacity/route values and saved-ID trip links After show validator.
prevalidate_photo_records Update / GameSet::Intent ResMut<SnapshotScratch>, Res<Assets<WorldScenarioAsset>>, MessageWriter<PersistenceFailed> Validates photo score/subject/image/album records and saved IDs After transport validator.
prevalidate_extinct_records Update / GameSet::Intent ResMut<SnapshotScratch>, Res<Assets<SpeciesCatalogAsset>>, Res<Assets<WorldDefinitionsAsset>>, MessageWriter<PersistenceFailed> Validates extinct bitsets/IDs/counts/ticks/RNG and saved IDs After photo validator.
prevalidate_puzzle_records Update / GameSet::Intent ResMut<SnapshotScratch>, Res<Assets<BehaviorProgramAsset>>, MessageWriter<PersistenceFailed> Validates puzzle definition/step/state and rejects transient samples After extinct validator.
prevalidate_time_records Update / GameSet::Intent ResMut<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>>, MessageWriter<PersistenceFailed> Validates tick/day/calendar/speed/pause/seed consistency After puzzle validator.
prevalidate_environment_records Update / GameSet::Intent ResMut<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>>, Res<Assets<WorldScenarioAsset>>, MessageWriter<PersistenceFailed> Validates environment IDs/transitions/wind/spawner/RNG and exclusions Last semantic validator.
seal_snapshot_validation Update / GameSet::Intent ResMut<SnapshotScratch> Sets validated=true only if every prior validator left the operation intact No ECS mutation; a failure clears scratch and leaves current world intact.
despawn_loaded_world Update / GameSet::Intent Commands, Res<SnapshotScratch>, Query<(Entity,&WorldMember)>, Query<Entity,With<WorldRoot>> Removes all old world members, then old root Requires scratch.validated; shell/profile entities remain.
restore_world_snapshot_records Update / GameSet::Intent Commands, ResMut<SnapshotScratch>, Res<Assets<WorldScenarioAsset>> The sole WorldRoot and WorldBounds; installs a fresh G04 allocator for that root After despawn; uses only the already-validated world record and creates no persistent-ID map entry for the root.
restore_persistent_id_cursor Update / GameSet::Intent Res<SnapshotScratch>, ResMut<PersistentIdAllocator>, Query<Entity,With<WorldRoot>> Calls G04 reserve_imported over the already sorted, duplicate-validated saved_ids, leaving next above the greatest imported ID After world root and before any entity spawn; no allocation or new validation.
spawn_terrain_records Update / GameSet::Intent Commands, ResMut<SnapshotScratch>, Res<Assets<TerrainAsset>> PersistentId, TerrainChunk, EditedTerrainSamples After world.
spawn_topology_records Update / GameSet::Intent Commands, ResMut<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>> PersistentId, TopologyNode, FenceEdge, PathTile, Gate, Portal, PathSupport with unresolved saved IDs After terrain.
spawn_placement_records Update / GameSet::Intent Commands, ResMut<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>>, Res<Assets<ScenePrefabAsset>> PersistentId, Placeable, Transform, FootprintOccupancy, optional AuthoredEntrance After topology.
spawn_maintenance_object_records Update / GameSet::Intent Commands, ResMut<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>> Inserts Condition/WasteContainer on mapped placed entities and spawns saved Litter/HabitatWaste entities with PersistentId, DefinitionId, Transform, and WorldMember After placement; all records already validated.
spawn_animal_records Update / GameSet::Intent Commands, ResMut<SnapshotScratch>, Res<Assets<SpeciesCatalogAsset>> G11 animal/lifecycle components with unresolved lineage IDs After maintenance object records.
restore_animal_waste_schedules Update / GameSet::Intent Commands, Res<SnapshotScratch>, Res<Assets<SpeciesCatalogAsset>> Inserts saved G36 WasteProduction on mapped animal entities After animals; no loose-waste spawn or first-time validation.
spawn_welfare_records Update / GameSet::Intent Commands, Res<SnapshotScratch> Inserts all saved G12 need and welfare components on mapped animals After animals.
spawn_behavior_records Update / GameSet::Intent Commands, Res<SnapshotScratch>, Res<Assets<BehaviorProgramAsset>> BehaviorHandle, ActiveTask, BehaviorRng with unresolved target IDs After welfare.
spawn_locomotion_records Update / GameSet::Intent Commands, Res<SnapshotScratch>, Res<Assets<NavigationAsset>> NavAgent, Velocity, Destination, Route, Docking with unresolved target IDs After behavior.
spawn_feeding_records Update / GameSet::Intent Commands, Res<SnapshotScratch>, Res<Assets<SpeciesCatalogAsset>>, Res<Assets<WorldDefinitionsAsset>> G15 container/task/reservation/refill components with unresolved entity IDs After locomotion.
spawn_health_records Update / GameSet::Intent Commands, Res<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>> G16 health components and DiseaseRng, with unresolved provider IDs After feeding.
spawn_guest_records Update / GameSet::Intent Commands, ResMut<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>> Restores GuestArrivalState and G17 guest bundle with unresolved view/destination/memory entity IDs After health.
spawn_economy_records Update / GameSet::Intent Commands, Res<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>>, ResMut<ZooCash>, ResMut<AdmissionPrice> G18 wallet/price/facility/inventory/service components with unresolved facility IDs After guests.
spawn_donation_records Update / GameSet::Intent Commands, Res<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>> G19 components and DonationRng with unresolved entity IDs After economy.
spawn_staff_records Update / GameSet::Intent Commands, ResMut<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>> G20 staff/job entities with unresolved assignment/claim IDs After donations.
spawn_progression_records Update / GameSet::Intent Commands, Res<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>>, ResMut<Fame>, ResMut<ZooRating>, ResMut<UnlockSet> G23 resources and research/award entities After staff.
spawn_scenario_records Update / GameSet::Intent Commands, ResMut<SnapshotScratch>, Res<Assets<WorldScenarioAsset>> Restores optional G24 primary/campaign resources, ChallengeOfferSchedule, and scenario/offer entities with unresolved objective IDs After progression.
spawn_aquatic_records Update / GameSet::Intent Commands, ResMut<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>>, Res<Assets<TerrainAsset>> G25 components with unresolved home IDs After scenario.
spawn_show_records Update / GameSet::Intent Commands, ResMut<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>> G26 components with unresolved participant/stage IDs After aquatic.
spawn_transport_records Update / GameSet::Intent Commands, ResMut<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>>, Res<Assets<NavigationAsset>> G27 graph/trip components with unresolved IDs After shows.
spawn_photo_records Update / GameSet::Intent Commands, ResMut<SnapshotScratch>, Res<Assets<WorldScenarioAsset>>, ResMut<Assets<Image>> G28 album/photo components and handles with unresolved subject/member IDs After transport.
spawn_extinct_records Update / GameSet::Intent Commands, ResMut<SnapshotScratch>, Res<Assets<SpeciesCatalogAsset>>, Res<Assets<WorldDefinitionsAsset>>, ResMut<FossilCollection> G29 site/piece/assembly/lab/process components and RNG After photos.
spawn_puzzle_records Update / GameSet::Intent Commands, ResMut<SnapshotScratch>, Res<Assets<BehaviorProgramAsset>> PersistentId, Puzzle After extinct animals.
restore_simulation_time Update / GameSet::Intent Res<SnapshotScratch>, ResMut<ZooClock>, ResMut<ZooCalendar>, ResMut<SimulationControl>, ResMut<ZooSeed> Exact saved G32 resources After puzzles.
spawn_environment_records Update / GameSet::Intent Commands, ResMut<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>>, Res<Assets<WorldScenarioAsset>> Saved G33 environment/spawner components with RNG; no presentation markers or ambient animals After simulation time.
repair_topology_links Update / GameSet::Intent Res<SnapshotScratch>, Query<&mut FenceEdge>, Query<&mut Portal>, Query<&mut PathSupport> Resolves topology links After all spawns.
repair_parent_links Update / GameSet::Intent Res<SnapshotScratch>, Query<&mut Pregnancy>, Query<&mut Parents> Resolves lineage links; deleted optional ancestors become None After topology repair.
repair_behavior_targets Update / GameSet::Intent Res<SnapshotScratch>, Query<&mut ActiveTask> Resolves optional task targets After parent repair.
repair_locomotion_links Update / GameSet::Intent Res<SnapshotScratch>, Query<&mut Docking> Resolves docking targets After behavior repair.
repair_feeding_links Update / GameSet::Intent Res<SnapshotScratch>, Query<&mut FeedingTask>, Query<&mut DrinkingTask>, Query<&mut ConsumptionReservation> Resolves source/consumer links After locomotion repair.
repair_treatment_links Update / GameSet::Intent Res<SnapshotScratch>, Query<&mut Treatment> Resolves provider links After feeding repair.
repair_guest_links Update / GameSet::Intent Res<SnapshotScratch>, Query<&mut Viewing>, Query<&mut GuestDestination>, Query<&mut GuestMemories> Resolves guest subject/destination/memory links After treatment repair.
repair_service_links Update / GameSet::Intent Res<SnapshotScratch>, Query<&mut ServiceReservation>, Query<&mut ServiceProgress> Resolves facility links After guest repair.
repair_donation_links Update / GameSet::Intent Res<SnapshotScratch>, Query<&mut DonationAcceptor>, Query<&mut DonationCandidate>, Query<&mut DonationProgress> Resolves beneficiary/acceptor/subject links After service repair.
repair_staff_links Update / GameSet::Intent Res<SnapshotScratch>, Query<&mut StaffAssignment>, Query<&mut StaffJob>, Query<&mut JobClaim>, Query<&mut CurrentJob> Resolves area/target/staff/job links After donation repair.
repair_scenario_links Update / GameSet::Intent Res<SnapshotScratch>, Query<&mut ScenarioRuleNode> Resolves objective links After staff repair.
repair_aquatic_links Update / GameSet::Intent Res<SnapshotScratch>, Query<&mut AquaticHome> Resolves tank-home links After scenario repair.
repair_show_links Update / GameSet::Intent Res<SnapshotScratch>, Query<&mut ShowSchedule>, Query<&mut TrickTraining>, Query<&mut TrainingProgress>, Query<&mut Performing>, Query<&mut WatchingShow> Resolves show participant/stage links After aquatic repair.
repair_transport_links Update / GameSet::Intent Res<SnapshotScratch>, Query<&mut CircuitMember>, Query<&mut TrackSegment>, Query<&mut RoutePosition>, Query<&mut StationDestination>, Query<&mut WaitingForTransport>, Query<&mut TransportRider> Resolves circuit graph/trip links After show repair.
repair_photo_links Update / GameSet::Intent Res<SnapshotScratch>, Query<&mut PhotoSubjects>, Query<&mut AlbumMember> Resolves photo subject/album links Last repair.
finish_load Update / GameSet::Intent ResMut<SnapshotScratch>, MessageWriter<LoadCompleted> Clears validated scratch and emits completion After deterministic spawn/repair and natural derived-index rebuilds; performs no first-time semantic validation.
delete_save Update / GameSet::Intent MessageReader<DeleteSaveRequest>, Query<(Entity,&mut SlotIoTask)>, Commands, MessageWriter<SaveDeleted>, MessageWriter<PersistenceFailed> Starts or polls one atomic slot-deletion task Explicit request; no world mutation.
persist_profile_options Update / GameSet::Ui Res<ProfileOptions>, Res<AudioMixSettings>, Res<InputBindings>, Res<DisplaySettings>, Res<GraphicsSettings>, Query<(Entity,&mut ProfileIoTask)>, Commands, MessageWriter<ProfileOperationFailed> Starts or polls one atomic write containing values read from natural live owners Any listed resource is_changed() and no active profile task; G22 retains no settings mirror.

5. Preflight producer contract

Not applicable to gameplay. Engine consumes native assets. Original Zoo Tycoon 2 save/map import is exclusively A19 preflight. This package may perform only explicit OpenZT2 profile/save slot I/O; it must not discover packs, read Z2F/XML, merge mods, transcode, or interpret original saves.

6. Behavioral evidence and disposition

Use live original save/load/autosave behavior and shipped save examples only to identify persisted semantics. Search analysis/oracle for saveable components, entity reference and calendar/scenario state. Mine commit d5657e5a only for field/evidence leads in domain/src/persistence/save.rs and features/interaction/save.rs; never reuse its aggregate snapshots, generic records, or source parser. A19 owns original conversion.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

Settle exact save relevance, pause/autosave timing, in-progress action resume, deleted-reference policy, thumbnails/metadata, and profile option semantics. Clock/calendar/seed and every domain-local RNG state are mandatory concrete coverage; G33 weather/spawner continuation is saved while nonsavable ambient animals are recreated from those facts.

7. Required behavior

8. Performance contract

Ordinary settled frames allocate zero and perform zero filesystem operations. Explicit save/load is exceptional: scratch buffers are reused, bounded by validated header/capacity, and allocation count/bytes are measured separately. Serialization is O(saved components + references); ID map is sorted once during the explicit operation and binary searched or built into pre-reserved storage. No retained mirror after completion. Bench thresholds on Jarvis: warm save and load must report exact heap allocations/bytes, wall time, bytes copied, and zero asset payload copies; mapped pack pages remain separately accounted. No GPU resources except optional thumbnail staging owned/reused by G28/presentation.

9. Acceptance criteria

Deliver exact exports and concrete focused section pipeline under owned path. Tests cover deterministic bytes, checksum/version/profile rejection, duplicate IDs, broken references, failed-load atomicity, exact relationship repair, round-trip live facts, and empty scratch afterward. No DynamicScene, generic bag, serializer trait facade, retained world, source parser, stub, or fake success. Observable done: save, mutate zoo, load, and instantly recover the same live world and options.

10. Required handoff

Report exact component coverage by section, files/API, Cargo needs, root registrations, symbols consumed, evidence, temporary integration seams, and allocation/I/O/page/gameplay benchmarks. No later generic section extension or registration mechanism is permitted.

G23 — Progression

1. Identity and outcome

This package delivers fame, zoo rating, awards, research, and unlocks as authoritative global progression facts plus per-project entities. Player actions change ratings, unlock native catalogue records, advance one or more authored research projects, and earn awards with visible feedback.

Integration wave: 4. Prerequisites: A03 species indexes, A14 definitions, G18–G22, G32 time, and G36 cleanliness facts.

2. Exclusive ownership

Exclusive path: reconstruction/game/src/plugins/progression/; all else read-only. Root owns declarations, plugin/schedule/reflection wiring, dependencies, and persistence section integration.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
ProgressionPlugin Plugin unit struct Registers focused progression systems.
Fame Resource pub struct Fame { pub half_stars: u8, pub maximum_reached: u8 } Half-star index; valid upper bound comes from A14, not hardcoded. Maximum never decreases.
ZooRating Resource pub struct ZooRating { pub animal_welfare_permille: u16, pub guest_satisfaction_permille: u16, pub education_permille: u16, pub variety_permille: u16, pub scenery_permille: u16, pub finance_permille: u16, pub cleanliness_permille: u16, pub overall_permille: u16 } One exact field per A14 RatingInputKind; all 0..=1000, no float rating.
RatingScratch Resource pub struct RatingScratch { pub species_words: Vec<u64> } Pre-sized reusable variety bitset only; cleared after changed-population evaluation and never authoritative.
UnlockSet Resource pub struct UnlockSet { pub words: Vec<u64>, pub definition_count: u32 } Bounded bitset sized once to A14 catalogue; index space is A14 stable definition index.
ResearchProject Component pub struct ResearchProject { pub definition: AssetId, pub elapsed_ticks: u64, pub required_ticks: u64 } One active project entity; positive duration is preflight-compiled G32 ticks.
ResearchPaused Component marker Project does not advance.
PendingResearchPayment Component pub struct PendingResearchPayment { pub definition: AssetId } Domain-owned operation; a project does not exist before its matching payment completion.
AwardEarned Component pub struct AwardEarned { pub definition: AssetId, pub earned_tick: u64 } Dedicated award entity; one per non-repeatable award.
AwardConditionProgress Component pub struct AwardConditionProgress { pub award: AssetId, pub condition_index: u32, pub satisfied_ticks: u64 } Dedicated bounded condition entity only when A14 duration is nonzero; exact tick continuation persisted by G22.
ProgressionFactChanged Message pub enum ProgressionFactChanged { Fame, Rating, Unlock(AssetId), Research(AssetId), Award(AssetId) } UI/scenario notification after actual mutation.
StartResearchRequest Message pub struct StartResearchRequest { pub definition: AssetId } Validates prerequisites, availability, and cost.
UnlockRequest Message pub struct UnlockRequest { pub operation: Entity, pub definition: AssetId } Typed scenario/system request with caller-owned correlation.
UnlockApplied Message pub struct UnlockApplied { pub operation: Entity, pub definition: AssetId } Echoes operation once after bit mutation or idempotent already-unlocked acceptance.
UnlockRejected Message pub struct UnlockRejected { pub operation: Entity, pub definition: AssetId } Echoes operation once; unlock set unchanged.
ResearchCompleted Message pub struct ResearchCompleted { pub project: Entity, pub definition: AssetId } Emitted once after unlock effects commit.
AwardGranted Message pub struct AwardGranted { pub award: Entity, pub definition: AssetId } Emitted once per earned award entity.
is_unlocked function pub fn is_unlocked(set: &UnlockSet, index: u32) -> bool O(1), allocation-free, returns false out of range.

Root requests A14 stable definition-index lookup and compiled rating/fame/ research/award rules; direct G18 transactions, G17 guest outcomes, G11/G12 animal facts, and G22 persistence. Never copy catalogues or world summaries.

4. ECS ownership

Fame/rating/unlocks are genuinely zoo-global bounded facts. Projects/awards are entities for lifecycle and save identity. UnlockSet allocates only at world load/profile initialization and never grows afterward. The private AwardCandidate { definition: AssetId } message is emitted by the seven independent evaluators. One materializer owns uniqueness and entity creation, so those evaluators remain parallel and never contend on the PersistentIdAllocator.

System Schedule/set Exact query and parameters Writes Run condition/order
recompute_animal_welfare_rating FixedUpdate / FixedGameSet::Economy MessageReader<WelfareChanged>, MessageReader<AnimalBorn>, MessageReader<AnimalReleased>, MessageReader<AnimalDied>, Res<Assets<WorldDefinitionsAsset>>, Query<&AnimalWelfare,(With<Animal>,Without<Dead>)>, ResMut<ZooRating>, MessageWriter<ProgressionFactChanged> animal_welfare_permille only Relevant welfare/population change.
recompute_guest_satisfaction_rating FixedUpdate / FixedGameSet::Economy MessageReader<GuestArrived>, MessageReader<GuestDeparted>, MessageReader<GuestReaction>, Res<Assets<WorldDefinitionsAsset>>, Query<&GuestSatisfaction,With<Guest>>, ResMut<ZooRating>, MessageWriter<ProgressionFactChanged> guest_satisfaction_permille only Relevant guest fact change.
recompute_education_rating FixedUpdate / FixedGameSet::Economy MessageReader<GuestArrived>, MessageReader<GuestDeparted>, MessageReader<GuestReaction>, Res<Assets<WorldDefinitionsAsset>>, Query<&GuestEducation,With<Guest>>, ResMut<ZooRating>, MessageWriter<ProgressionFactChanged> education_permille only Relevant education/population change.
recompute_variety_rating FixedUpdate / FixedGameSet::Economy MessageReader<AnimalAdopted>, MessageReader<AnimalBorn>, MessageReader<AnimalReleased>, MessageReader<AnimalDied>, Res<Assets<SpeciesCatalogAsset>>, Res<Assets<WorldDefinitionsAsset>>, Query<&SpeciesHandle,(With<Animal>,Without<Dead>)>, ResMut<RatingScratch>, ResMut<ZooRating>, MessageWriter<ProgressionFactChanged> variety_permille only; scratch cleared Population change only; no allocation/sort.
recompute_scenery_rating FixedUpdate / FixedGameSet::Economy MessageReader<ObjectPlaced>, RemovedComponents<Placeable>, Res<Assets<WorldDefinitionsAsset>>, Query<&Placeable>, ResMut<ZooRating>, MessageWriter<ProgressionFactChanged> scenery_permille only Durable placement change.
recompute_finance_rating FixedUpdate / FixedGameSet::Economy Res<ZooCash>, Res<Assets<WorldDefinitionsAsset>>, ResMut<ZooRating>, MessageWriter<ProgressionFactChanged> finance_permille only ZooCash.is_changed().
recompute_cleanliness_rating FixedUpdate / FixedGameSet::Economy Res<ZooCleanliness>, ResMut<ZooRating>, MessageWriter<ProgressionFactChanged> Copies the changed exact G36 permille into cleanliness_permille only resource_changed::<ZooCleanliness>; after G36 projection, no placed-object scan.
recompute_overall_rating FixedUpdate / FixedGameSet::Economy Res<Assets<WorldDefinitionsAsset>>, ResMut<ZooRating>, MessageWriter<ProgressionFactChanged> Reads seven input fields and writes overall_permille with checked integer rounding After seven input systems when ZooRating.is_changed().
update_fame FixedUpdate / FixedGameSet::Economy Res<Assets<WorldDefinitionsAsset>>, Res<ZooRating>, ResMut<Fame>, MessageWriter<ProgressionFactChanged> Current/max half-stars After overall rating when ZooRating.is_changed().
request_research_payment FixedUpdate / FixedGameSet::Economy MessageReader<StartResearchRequest>, Res<Assets<WorldDefinitionsAsset>>, Res<UnlockSet>, Query<&ResearchProject>, Query<&PendingResearchPayment>, Query<(Entity,&WorldRoot)>, Commands, MessageWriter<TransactionRequest> Spawns PendingResearchPayment with root WorldMember and emits a transaction carrying it Valid request only; no project yet.
complete_research_payment FixedUpdate / FixedGameSet::Cleanup MessageReader<TransactionCompleted>, Res<Assets<WorldDefinitionsAsset>>, Query<(Entity,&PendingResearchPayment,&WorldMember)>, ResMut<PersistentIdAllocator>, Commands Validates/allocates first, then spawns ResearchProject with operation WorldMember and fresh PersistentId, and despawns operation TransactionCompleted.operation must equal the operation entity.
reject_research_payment FixedUpdate / FixedGameSet::Cleanup MessageReader<TransactionRejected>, Query<(Entity,&PendingResearchPayment)>, Commands Despawns operation without project/unlock mutation TransactionRejected.operation must equal the operation entity.
advance_research FixedUpdate / FixedGameSet::Economy Res<ZooClock>, Query<(Entity,&mut ResearchProject),Without<ResearchPaused>>, MessageWriter<ResearchCompleted> Increments elapsed_ticks by one and completes at required_ticks Exactly one simulation tick; no float duration.
apply_research_unlocks FixedUpdate / FixedGameSet::Cleanup MessageReader<ResearchCompleted>, Res<Assets<WorldDefinitionsAsset>>, ResMut<UnlockSet>, Query<(Entity,&ResearchProject)>, Commands, MessageWriter<ProgressionFactChanged> Bitset; despawns project Atomic dependency closure; after completion.
apply_unlock_requests FixedUpdate / FixedGameSet::Cleanup MessageReader<UnlockRequest>, Res<Assets<WorldDefinitionsAsset>>, ResMut<UnlockSet>, MessageWriter<UnlockApplied>, MessageWriter<UnlockRejected>, MessageWriter<ProgressionFactChanged> Exact target bit only Deterministic request order; echoes every operation once.
initialize_award_progress Update / GameSet::Intent MessageReader<WorldLoadFinished>, Res<Assets<WorldDefinitionsAsset>>, Query<(Entity,&WorldRoot)>, Query<&AwardConditionProgress>, ResMut<PersistentIdAllocator>, Commands For a new world, creates required duration-condition entities with root WorldMember and fresh PersistentId Missing definition/index pairs only; G22-restored progress is untouched.
evaluate_animal_welfare_awards FixedUpdate / FixedGameSet::Think Res<ZooRating>, Res<ZooClock>, Res<Assets<WorldDefinitionsAsset>>, Query<&AwardEarned>, Query<(Entity,&mut AwardConditionProgress)>, MessageWriter<AwardCandidate>, MessageWriter<ProgressionFactChanged> Animal-welfare condition progress/candidates ZooRating.is_changed(); exact A14 comparison/duration.
evaluate_guest_satisfaction_awards FixedUpdate / FixedGameSet::Think Res<ZooRating>, Res<ZooClock>, Res<Assets<WorldDefinitionsAsset>>, Query<&AwardEarned>, Query<(Entity,&mut AwardConditionProgress)>, MessageWriter<AwardCandidate>, MessageWriter<ProgressionFactChanged> Guest-satisfaction condition progress/candidates ZooRating.is_changed(); exact comparison/duration.
evaluate_education_awards FixedUpdate / FixedGameSet::Think Res<ZooRating>, Res<ZooClock>, Res<Assets<WorldDefinitionsAsset>>, Query<&AwardEarned>, Query<(Entity,&mut AwardConditionProgress)>, MessageWriter<AwardCandidate>, MessageWriter<ProgressionFactChanged> Education condition progress/candidates ZooRating.is_changed(); exact comparison/duration.
evaluate_variety_awards FixedUpdate / FixedGameSet::Think Res<ZooRating>, Res<ZooClock>, Res<Assets<WorldDefinitionsAsset>>, Query<&AwardEarned>, Query<(Entity,&mut AwardConditionProgress)>, MessageWriter<AwardCandidate>, MessageWriter<ProgressionFactChanged> Variety condition progress/candidates ZooRating.is_changed(); exact comparison/duration.
evaluate_scenery_awards FixedUpdate / FixedGameSet::Think Res<ZooRating>, Res<ZooClock>, Res<Assets<WorldDefinitionsAsset>>, Query<&AwardEarned>, Query<(Entity,&mut AwardConditionProgress)>, MessageWriter<AwardCandidate>, MessageWriter<ProgressionFactChanged> Scenery condition progress/candidates ZooRating.is_changed(); exact comparison/duration.
evaluate_finance_awards FixedUpdate / FixedGameSet::Think Res<ZooRating>, Res<ZooClock>, Res<Assets<WorldDefinitionsAsset>>, Query<&AwardEarned>, Query<(Entity,&mut AwardConditionProgress)>, MessageWriter<AwardCandidate>, MessageWriter<ProgressionFactChanged> Finance condition progress/candidates ZooRating.is_changed(); exact comparison/duration.
evaluate_cleanliness_awards FixedUpdate / FixedGameSet::Think Res<ZooRating>, Res<ZooClock>, Res<Assets<WorldDefinitionsAsset>>, Query<&AwardEarned>, Query<(Entity,&mut AwardConditionProgress)>, MessageWriter<AwardCandidate>, MessageWriter<ProgressionFactChanged> Cleanliness condition progress/candidates ZooRating.is_changed(); exact comparison/duration.
grant_awards FixedUpdate / FixedGameSet::Cleanup MessageReader<AwardCandidate>, Res<ZooClock>, Res<Assets<WorldDefinitionsAsset>>, Query<&AwardEarned>, Query<(Entity,&WorldRoot)>, ResMut<PersistentIdAllocator>, Commands, MessageWriter<AwardGranted>, MessageWriter<ProgressionFactChanged> Deduplicates candidates, then creates one award entity with root WorldMember and fresh PersistentId before acknowledgement After all seven evaluators; non-repeatable definition only.

5. Preflight producer contract

Not applicable: only native assets are consumed. Preflight A14 compiles rules, stable indexes and dependency closure; no runtime XML/script/filesystem.

6. Behavioral evidence and disposition

Search shipped fame/rating/research/award/unlock definitions and UI; analysis TSVs for ZT_PROCESS_FAME_UPDATE, ZT_SET_FAME_HALFSTARS, ZT_RESEARCH_IS_UNLOCKED, ZT_RESEARCH_UNLOCK_ENTITY_KIND, and award commands; live-observe thresholds/timing. Mine d5657e5a only for behavioral evidence in progression/research modules and feedback records.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

Settle rating inputs/formula/cadence, fame thresholds/max semantics, research cost/time/parallelism, prerequisite closure, unlock categories, repeatable awards and rewards. Use native records/oracle/live behavior, never guessed thresholds.

7. Required behavior

8. Performance contract

Lookup is O(1) bit access and allocation-free. Rating/awards are changed/event- driven over compiled bounded rule ranges; research O(active projects). Bitset capacity is fixed before steady measurement. No scans of all entities per rule, strings, hash growth, payload clones, filesystem, or recurring allocations. Tests measure idle frame, unlock lookup, project tick, and affected award batch; no GPU ownership.

9. Acceptance criteria

Deliver exact exports. Tests cover bounds, monotonic max fame, threshold crossing, research validation/completion, unlock dependency closure, award uniqueness and persistence facts. Observable done: zoo actions alter rating/fame, research unlocks content, awards appear authentically. No guessed formulas, manager, shadow catalogue, or no-op unlock.

10. Required handoff

Report API/files, dependency/root wiring, A14 index contract, G22 sections, consumers, evidence, seams, and allocator/page/gameplay checks.

G24 — Scenarios, Campaigns, Tutorials, and Challenges

1. Identity and outcome

This package instantiates A19 scenario/objective/rule records into entities, evaluates the fixed ScenarioRuleKind variants against live ECS facts, drives campaign/ challenge/tutorial transitions, and emits authentic win/loss/results. It does not embed a scripting VM or duplicate the world into a scenario snapshot.

Integration wave: 4. Prerequisites: A19 world scenarios, G18 economy, G22 persistence, and G23 progression.

2. Exclusive ownership

Exclusive path: reconstruction/game/src/plugins/scenario/; all else read-only. Root owns declarations, plugin/schedule/state/reflection/persistence wiring, dependencies, and alignment with A19's final archived rule enum.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
ScenarioPlugin Plugin unit struct Registers scenario lifecycle and focused evaluators.
ActiveScenario Resource pub struct ActiveScenario { pub definition: AssetId, pub started_tick: u64, pub state: ScenarioState } One active scenario/campaign mission or none (resource absent); elapsed time is ZooClock.tick - started_tick.
ScenarioState enum Running, Won, Lost Terminal states never resume evaluation.
Objective Component pub struct Objective { pub scenario: AssetId, pub record_index: u32 } Indexes one validated A19 objective within a primary scenario or concurrent challenge; text, ranges and root rule remain mapped.
ScenarioRuleNode Component pub struct ScenarioRuleNode { pub objective: Entity, pub rule_index: u32 } One entity per reachable A19 ScenarioRule; composite children follow validated rule_links.
RuleSatisfied Component pub struct RuleSatisfied(pub bool) Current rule result; leaf evaluator or composite aggregator for that exact A19 kind is sole writer.
RuleProgress Component pub struct RuleProgress { pub current: i64, pub target: i64 } Display integers in A19's exact cents/half-star/permille/count/tick/score-milli unit.
ObjectiveStatus Component + enum Inactive, Active, Satisfied, Failed Exactly one status; terminal according to definition.
ObjectiveProgress Component pub struct ObjectiveProgress { pub current: i64, pub target: i64 } Display units defined by rule; target immutable during one objective unless authored.
ObjectiveDeadline Component pub struct ObjectiveDeadline { pub end_tick: u64 } Absolute G32 zoo tick; absent means no deadline and integer comparison avoids drift.
CampaignProgress Resource pub struct CampaignProgress { pub campaign: AssetId, pub completed_words: Vec<u64> } Bounded bitset sized once from A19 mission count.
TutorialStep Component pub struct TutorialStep { pub definition: AssetId, pub step_index: u32 } Dedicated active tutorial objective entity; no UI script object.
ChallengeState enum Offered, Active Offer expires only in Offered; accepted challenge remains until its own terminal result.
ChallengeOffer Component pub struct ChallengeOffer { pub definition: AssetId, pub expires_tick: u64, pub state: ChallengeState } Durable world entity for one offered/accepted A19 challenge policy.
ChallengeOfferSchedule Resource pub struct ChallengeOfferSchedule { pub next_tick: u64, pub rng: DeterministicRng } One deterministic world-level offer cursor; policy selection is weighted over eligible A19 records and G22 persists it.
PendingScenarioCashAction Component pub struct PendingScenarioCashAction { pub scenario: AssetId, pub action_index: u32 } Domain-owned G18 operation for one typed cash action; prevents duplicate application.
PendingScenarioUnlockAction Component pub struct PendingScenarioUnlockAction { pub scenario: AssetId, pub action_index: u32, pub definition: AssetId } Correlates one A19 Unlock request to exact G23 acknowledgement.
PendingScenarioSpawnAction Component pub struct PendingScenarioSpawnAction { pub scenario: AssetId, pub action_index: u32, pub prefab: AssetId, pub definition: AssetId } Correlates one A19 Spawn request to exact G04 spawn acknowledgement.
PendingScenarioWeatherAction Component pub struct PendingScenarioWeatherAction { pub scenario: AssetId, pub action_index: u32, pub weather: AssetId } Correlates one A19 SetWeather request to exact G33 completion.
PendingScenarioMessageAction Component pub struct PendingScenarioMessageAction { pub scenario: AssetId, pub action_index: u32, pub message: AssetId } Correlates one ShowMessage request to G02 acceptance/presentation acknowledgement.
ScenarioTerminalPending Component pub struct ScenarioTerminalPending { pub scenario: AssetId, pub state: ScenarioState } Terminal intent entity; result is withheld until no pending typed action for this scenario remains.
AcceptChallenge Message pub struct AcceptChallenge { pub offer: Entity } Player accepts a live eligible offer.
ChallengeAccepted Message pub struct ChallengeAccepted { pub offer: Entity, pub definition: AssetId } Emitted after all challenge objective/rule entities have identity and the offer becomes active.
ChallengeRejected Message pub struct ChallengeRejected { pub offer: Entity, pub reason: ChallengeRejection } Offer remains unchanged.
ChallengeRejection enum Missing, Expired, AlreadyActive, Ineligible, PersistentId(PersistentIdError) Closed acceptance failure vocabulary.
ChallengeOfferGenerationFailed Message pub struct ChallengeOfferGenerationFailed { pub reason: PersistentIdError } Explicit failure to materialize an otherwise due offer.
ScenarioResult Message pub struct ScenarioResult { pub scenario: AssetId, pub state: ScenarioState } Emitted exactly once after all terminal effects commit.
ObjectiveChanged Message pub struct ObjectiveChanged { pub objective: Entity, pub status: ObjectiveStatus, pub current: i64, pub target: i64 } UI notification after actual changed facts.
TutorialActionObserved Message pub struct TutorialActionObserved { pub action: GameAction, pub subject: Option<Entity> } Fixed action observation; never arbitrary command string.

This package consumes A19 exactly as frozen: ObjectiveRecord.rule_index, ScenarioRule { kind, subject, comparison, target, children }, the exact ScenarioRuleKind enum, and every exact ScenarioAction variant. It compares cents, half-star indexes, 0..=1000 permille, counts, ticks and score thousandths without guessed conversion. There is no second rule enum/operand record, expression language, callback registry, Lua VM, string dispatcher, or world snapshot.

4. ECS ownership

The active scenario and bounded campaign bitset are genuinely global. Each objective/offer/tutorial step is an entity. Evaluators are split by fixed rule kind and query only relevant live components.

System Schedule/set Exact query and parameters Writes Run condition/order
instantiate_scenario Update / GameSet::Intent MessageReader<WorldLoadFinished>, Res<Assets<WorldScenarioAsset>>, Res<ZooClock>, ResMut<PersistentIdAllocator>, Query<(Entity,&WorldRoot)>, Query<(),With<Objective>>, Commands For a new scenario only, creates active resource plus objective/reachable rule-node/tutorial entities, each with root WorldMember and fresh PersistentId No existing objective entities; G22-restored scenario facts are untouched.
initialize_challenge_schedule Update / GameSet::Intent MessageReader<WorldLoadFinished>, Res<Assets<WorldScenarioAsset>>, Res<ZooClock>, Res<ZooSeed>, Option<Res<ChallengeOfferSchedule>>, Commands Inserts schedule at the earliest compiled first-offer tick with a G32 domain RNG New world only; G22-restored schedule is untouched.
generate_challenge_offers FixedUpdate / FixedGameSet::Think Res<ZooClock>, Res<Fame>, Res<UnlockSet>, Res<Assets<WorldScenarioAsset>>, ResMut<ChallengeOfferSchedule>, ResMut<PersistentIdAllocator>, Query<(Entity,&WorldRoot)>, Query<(),With<ChallengeOffer>>, Commands, MessageWriter<ChallengeOfferGenerationFailed> At due tick selects one eligible weighted policy, then creates an Offered entity with root WorldMember and fresh PersistentId; advances schedule At most one offered/active challenge; map/prerequisite/fame eligibility and intervals are compiled A19 facts.
accept_challenge_offers Update / GameSet::Intent MessageReader<AcceptChallenge>, Res<ZooClock>, Res<Assets<WorldScenarioAsset>>, ResMut<PersistentIdAllocator>, Query<(Entity,&mut ChallengeOffer,&WorldMember)>, Commands, MessageWriter<ChallengeAccepted>, MessageWriter<ChallengeRejected> Prevalidates and allocates all required IDs, creates objective/reachable rule-node entities with the offer's WorldMember, then changes offer to Active and acknowledges Exact offered entity, before expiry; no partial challenge on failure.
evaluate_cash_rules FixedUpdate / FixedGameSet::Think Res<Assets<WorldScenarioAsset>>, Res<ZooCash>, Query<(&ScenarioRuleNode,&mut RuleSatisfied,&mut RuleProgress)> Matching CashCents rule facts only ZooCash.is_changed(); A19 cents comparison.
evaluate_fame_rules FixedUpdate / FixedGameSet::Think Res<Assets<WorldScenarioAsset>>, Res<Fame>, Query<(&ScenarioRuleNode,&mut RuleSatisfied,&mut RuleProgress)> Matching FameHalfStars rule facts only Fame.is_changed().
evaluate_rating_rules FixedUpdate / FixedGameSet::Think Res<Assets<WorldScenarioAsset>>, Res<ZooRating>, Query<(&ScenarioRuleNode,&mut RuleSatisfied,&mut RuleProgress)> Matching ZooRatingPermille rule facts only ZooRating.is_changed(); exact permille.
evaluate_species_count_rules FixedUpdate / FixedGameSet::Think MessageReader<AnimalAdopted>, MessageReader<AnimalBorn>, MessageReader<AnimalReleased>, MessageReader<AnimalDied>, Res<Assets<WorldScenarioAsset>>, Query<&SpeciesHandle,(With<Animal>,Without<Dead>)>, Query<(&ScenarioRuleNode,&mut RuleSatisfied,&mut RuleProgress)> Matching SpeciesCount rule facts only Animal lifecycle change only.
evaluate_animal_count_rules FixedUpdate / FixedGameSet::Think MessageReader<AnimalAdopted>, MessageReader<AnimalBorn>, MessageReader<AnimalReleased>, MessageReader<AnimalDied>, Res<Assets<WorldScenarioAsset>>, Query<(),(With<Animal>,Without<Dead>)>, Query<(&ScenarioRuleNode,&mut RuleSatisfied,&mut RuleProgress)> Matching AnimalCount rule facts only Animal lifecycle change only.
evaluate_guest_count_rules FixedUpdate / FixedGameSet::Think MessageReader<GuestArrived>, MessageReader<GuestDeparted>, Res<Assets<WorldScenarioAsset>>, Query<(),With<Guest>>, Query<(&ScenarioRuleNode,&mut RuleSatisfied,&mut RuleProgress)> Matching GuestCount rule facts only Guest lifecycle change only.
evaluate_object_count_rules FixedUpdate / FixedGameSet::Think MessageReader<ObjectPlaced>, RemovedComponents<Placeable>, Res<Assets<WorldScenarioAsset>>, Query<&Placeable>, Query<(&ScenarioRuleNode,&mut RuleSatisfied,&mut RuleProgress)> Matching ObjectCount rule facts only Durable placed-object change only.
evaluate_research_rules FixedUpdate / FixedGameSet::Think MessageReader<ProgressionFactChanged>, Res<Assets<WorldScenarioAsset>>, Res<Assets<WorldDefinitionsAsset>>, Res<UnlockSet>, Query<(&ScenarioRuleNode,&mut RuleSatisfied,&mut RuleProgress)> Matching ResearchComplete rule facts only Unlock/research change only.
evaluate_elapsed_tick_rules FixedUpdate / FixedGameSet::Think Res<Assets<WorldScenarioAsset>>, Res<ZooClock>, Query<(&ScenarioRuleNode,&mut RuleSatisfied,&mut RuleProgress)> Matching ElapsedTicks rule facts only Due target tick only.
evaluate_welfare_rules FixedUpdate / FixedGameSet::Think MessageReader<WelfareChanged>, MessageReader<AnimalAdopted>, MessageReader<AnimalBorn>, MessageReader<AnimalReleased>, MessageReader<AnimalDied>, Res<Assets<WorldScenarioAsset>>, Query<(&SpeciesHandle,&AnimalWelfare),(With<Animal>,Without<Dead>)>, Query<(&ScenarioRuleNode,&mut RuleSatisfied,&mut RuleProgress)> Matching SpeciesWelfarePermille rule facts only Relevant welfare or population change only.
evaluate_birth_count_rules FixedUpdate / FixedGameSet::Think MessageReader<AnimalBorn>, Res<Assets<WorldScenarioAsset>>, Query<&SpeciesHandle,With<Animal>>, Query<(&ScenarioRuleNode,&mut RuleSatisfied,&mut RuleProgress)> Matching AnimalBirthCount rule facts only Matching birth only.
evaluate_adoption_count_rules FixedUpdate / FixedGameSet::Think MessageReader<AnimalAdopted>, Res<Assets<WorldScenarioAsset>>, Query<&SpeciesHandle,With<Animal>>, Query<(&ScenarioRuleNode,&mut RuleSatisfied,&mut RuleProgress)> Matching AnimalAdoptionCount rule facts only Matching adoption only.
evaluate_release_count_rules FixedUpdate / FixedGameSet::Think MessageReader<AnimalReleased>, Res<Assets<WorldScenarioAsset>>, Query<(&ScenarioRuleNode,&mut RuleSatisfied,&mut RuleProgress)> Matching AnimalReleaseCount rule facts only Matching release only.
evaluate_successful_show_rules FixedUpdate / FixedGameSet::Think MessageReader<ShowCompleted>, Res<Assets<WorldScenarioAsset>>, Query<&ShowStage>, Query<(&ScenarioRuleNode,&mut RuleSatisfied,&mut RuleProgress)> Matching SuccessfulShowCount rule facts only Matching successful show only.
evaluate_photo_score_rules FixedUpdate / FixedGameSet::Think MessageReader<PhotoCaptured>, Res<Assets<WorldScenarioAsset>>, Query<&Photo>, Query<(&ScenarioRuleNode,&mut RuleSatisfied,&mut RuleProgress)> Matching PhotoScoreMilli rule facts only Photo capture only.
evaluate_photo_subject_rules FixedUpdate / FixedGameSet::Think MessageReader<PhotoCaptured>, Res<Assets<WorldScenarioAsset>>, Query<&PhotoSubjects>, Query<(&ScenarioRuleNode,&mut RuleSatisfied,&mut RuleProgress)> Matching PhotoSubjectCount rule facts only Photo capture only.
evaluate_photo_challenge_rules FixedUpdate / FixedGameSet::Think MessageReader<PhotoChallengeCompleted>, Res<Assets<WorldScenarioAsset>>, Query<(&ScenarioRuleNode,&mut RuleSatisfied,&mut RuleProgress)> Matching PhotoChallengeComplete rule facts only Matching completed challenge only.
aggregate_rule_nodes FixedUpdate / FixedGameSet::Think Res<Assets<WorldScenarioAsset>>, Query<(Entity,&ScenarioRuleNode,&mut RuleSatisfied,&mut RuleProgress)> Composite All/Any/Not rule facts Leaves first, then reverse topological rule order.
aggregate_objectives FixedUpdate / FixedGameSet::Think Res<Assets<WorldScenarioAsset>>, Query<(&ScenarioRuleNode,&RuleSatisfied,Option<&RuleProgress>)>, Query<(Entity,&Objective,&mut ObjectiveStatus,&mut ObjectiveProgress)>, MessageWriter<ObjectiveChanged> Objective root status/progress After leaf/composite evaluation; prerequisites honored.
expire_deadlines_and_challenges FixedUpdate / FixedGameSet::Think Res<ZooClock>, Option<Res<ActiveScenario>>, Query<(Entity,&ObjectiveDeadline,&mut ObjectiveStatus,&mut ObjectiveProgress)>, Query<(Entity,&ChallengeOffer)>, Commands, MessageWriter<ObjectiveChanged> Failed objectives and only ChallengeState::Offered offers whose response tick expired Running primary objectives when present, plus offers in freeform/challenge worlds; integer tick comparison.
advance_tutorial_steps Update / GameSet::Intent MessageReader<TutorialActionObserved>, Res<Assets<WorldScenarioAsset>>, Query<(Entity,&mut TutorialStep,&mut ObjectiveStatus,&mut ObjectiveProgress)>, Commands, MessageWriter<ObjectiveChanged> Step index/objective status Action-driven.
begin_scenario_resolution FixedUpdate / FixedGameSet::Cleanup Option<Res<ActiveScenario>>, Res<Assets<WorldScenarioAsset>>, Query<(&Objective,&ObjectiveStatus)>, Query<&ChallengeOffer>, Query<&ScenarioTerminalPending>, Query<(Entity,&WorldRoot)>, Commands For each primary/active-challenge scenario whose own objectives became terminal, spawns one transient ScenarioTerminalPending carrying root WorldMember Once per scenario after aggregate/deadlines; statuses from another scenario never participate.
request_scenario_cash_actions FixedUpdate / FixedGameSet::Cleanup Res<Assets<WorldScenarioAsset>>, Query<(Entity,&ScenarioTerminalPending,&WorldMember)>, Query<&PendingScenarioCashAction>, Commands, MessageWriter<TransactionRequest> Spawns a world-scoped PendingScenarioCashAction and emits transaction carrying it Each A19 GrantCashCents action index at most once.
complete_scenario_cash_actions FixedUpdate / FixedGameSet::Cleanup MessageReader<TransactionCompleted>, Query<(Entity,&PendingScenarioCashAction)>, Commands Despawns the exact applied action operation TransactionCompleted.operation must equal the operation entity.
reject_scenario_cash_actions FixedUpdate / FixedGameSet::Cleanup MessageReader<TransactionRejected>, Res<Assets<WorldScenarioAsset>>, Query<(Entity,&PendingScenarioCashAction)>, Commands Fails/retries only by authored policy and despawns operation; never marks success TransactionRejected.operation must equal the operation entity.
apply_scenario_admission_actions FixedUpdate / FixedGameSet::Cleanup Res<Assets<WorldScenarioAsset>>, Query<&ScenarioTerminalPending>, ResMut<AdmissionPrice> Admission price only Each A19 SetAdmissionCents action index once; preflight rejects duplicates whose order would be ambiguous.
request_scenario_unlock_actions FixedUpdate / FixedGameSet::Cleanup Res<Assets<WorldScenarioAsset>>, Query<(&ScenarioTerminalPending,&WorldMember)>, Query<&PendingScenarioUnlockAction>, Commands, MessageWriter<UnlockRequest> Spawns world-scoped PendingScenarioUnlockAction and sends its entity as UnlockRequest.operation Each Unlock action index once.
acknowledge_scenario_unlocks FixedUpdate / FixedGameSet::Cleanup MessageReader<UnlockApplied>, MessageReader<UnlockRejected>, Query<(Entity,&PendingScenarioUnlockAction)>, Commands Despawns the exact acknowledged action operation Acknowledgement operation must equal the operation entity.
request_scenario_spawn_actions FixedUpdate / FixedGameSet::Cleanup Res<Assets<WorldScenarioAsset>>, Query<(&ScenarioTerminalPending,&WorldMember)>, Query<(Entity,&WorldRoot)>, Query<&PendingScenarioSpawnAction>, Commands, MessageWriter<SpawnScenarioObject> Spawns world-scoped PendingScenarioSpawnAction and sends its entity as SpawnScenarioObject.operation Each Spawn action index once.
acknowledge_scenario_spawns FixedUpdate / FixedGameSet::Cleanup MessageReader<ScenarioObjectSpawned>, MessageReader<ScenarioObjectSpawnRejected>, Query<(Entity,&PendingScenarioSpawnAction)>, Commands Despawns the exact acknowledged action operation Acknowledgement operation must equal the operation entity.
request_scenario_weather_actions FixedUpdate / FixedGameSet::Cleanup Res<Assets<WorldScenarioAsset>>, Query<(&ScenarioTerminalPending,&WorldMember)>, Query<&PendingScenarioWeatherAction>, Commands, MessageWriter<SetWeather> Spawns world-scoped PendingScenarioWeatherAction and sends its entity as SetWeather.operation Each SetWeather action index once.
acknowledge_scenario_weather FixedUpdate / FixedGameSet::Cleanup MessageReader<WeatherRequestApplied>, MessageReader<WeatherRequestRejected>, Query<(Entity,&PendingScenarioWeatherAction)>, Commands Despawns the exact acknowledged action operation Acknowledgement operation must equal the operation entity.
request_scenario_message_actions Update / GameSet::Ui Res<Assets<WorldScenarioAsset>>, Query<(&ScenarioTerminalPending,&WorldMember)>, Query<&PendingScenarioMessageAction>, Commands, MessageWriter<ShowNotification> Spawns world-scoped PendingScenarioMessageAction and sends its entity as ShowNotification.operation Each ShowMessage action index once.
acknowledge_scenario_messages Update / GameSet::Ui MessageReader<NotificationPresented>, MessageReader<NotificationRejected>, Query<(Entity,&PendingScenarioMessageAction)>, Commands Despawns the exact acknowledged action operation Acknowledgement operation must equal the operation entity.
queue_scenario_terminal_actions FixedUpdate / FixedGameSet::Cleanup Res<Assets<WorldScenarioAsset>>, Query<&mut ScenarioTerminalPending> Updates terminal intent from mapped A19 Complete/Fail actions only Each action index once; contradictory actions rejected preflight.
finalize_scenario_result FixedUpdate / FixedGameSet::Cleanup Option<ResMut<ActiveScenario>>, Option<ResMut<CampaignProgress>>, ResMut<ChallengeOfferSchedule>, Query<(Entity,&ScenarioTerminalPending)>, Query<(Entity,&ChallengeOffer)>, Query<&PendingScenarioCashAction>, Query<&PendingScenarioUnlockAction>, Query<&PendingScenarioSpawnAction>, Query<&PendingScenarioWeatherAction>, Query<&PendingScenarioMessageAction>, Commands, MessageWriter<ScenarioResult> Commits primary state/campaign bit or removes the matching active challenge and schedules its next offer; emits result and despawns terminal operation Only when no pending action component carries the same scenario.

5. Preflight producer contract

Not applicable: engine consumes only native A19 records. Preflight compiles original rule trees/scripts into the fixed exhaustive rule vocabulary, resolves source precedence and dependencies, and rejects unsupported commands. No XML, Lua, Z2F, filesystem, or original save parsing here.

6. Behavioral evidence and disposition

Search shipped campaign/scenario/challenge/tutorial XML/script/data families; analysis TSVs for ZT_POPULATE_SCENARIO_UI, scenario selection, starting cash, objective/challenge/tutorial types; live-observe mission setup, timers, results, rewards, failure and tutorial gating. Mine d5657e5a only for behavior/evidence in scenario, triggers, script-manager and UI modules—not architecture.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

Settle every shipped rule/operand/aggregation, activation dependency, timer, repeatability, challenge generation/expiry, campaign unlock, reward and result presentation. Unsupported source commands block producer acceptance rather than becoming runtime strings or no-op success.

7. Required behavior

8. Performance contract

Settled systems allocate zero. Evaluators run only affected objective ranges using precompiled kind indexes: O(affected objectives), not rules × world. Campaign bitset capacity fixed at load. No strings, dynamic AST/VM, reflection, hash growth, filesystem, or asset clones. Allocation tests cover idle scenario, one affected condition/objective and terminal resolution; mapped traversal zero; no GPU ownership.

9. Acceptance criteria

Deliver exact API and exhaustive typed evaluators under owned path. Tests cover activation, each A19 rule/action variant, All/Any/Not aggregation, deadlines/pause, terminal uniqueness, reward uniqueness, challenge expiry, tutorial action, campaign persistence, unknown rule rejection at A19 boundary. Observable done: shipped scenarios/campaigns/ challenges/tutorials run to authentic results. No VM, script manager, snapshot, stub, or guessed objective.

10. Required handoff

Report covered A19 rules/actions, files/API, dependencies/root wiring, G22 sections, direct domain events, evidence gaps, seams, and allocator/page/gameplay checks.

G25 — Aquatic Systems

1. Identity and outcome

This package delivers tanks and marine animal care as real terrain/topology and animal facts: bounded water volumes, floor/wall manipulation, fill/drain, marine placement, capacity/depth suitability, containment, and aquatic care. The same ECS world drives rendering, navigation, welfare, and persistence.

Integration wave: 5. Prerequisites: A14–A18, G05 terrain, G09 habitat, G10 placement, G11–G16 animal systems.

2. Exclusive ownership

Exclusive path: reconstruction/game/src/plugins/aquatic/; all else read-only. Root owns declarations, plugin/schedule/reflection/persistence registration, dependencies, and cross-package symbol alignment.

3. Frozen public contract

World distances/heights are finite meters; volumes are cubic meters; normalized quality is 0.0..=1.0. All authored rates/limits come from native definitions.

Symbol Kind Exact shape or signature Owner and invariant
AquaticPlugin Plugin unit struct Registers tank/marine systems.
Tank Component pub struct Tank { pub definition: AssetId } Marker plus immutable tank definition on habitat entity.
TankGeometry Component pub struct TankGeometry { pub floor_height: f32, pub wall_height: f32, pub water_height: f32, pub area: f32, pub volume: f32 } Finite geometry; floor <= water <= wall; area/volume nonnegative and derived from G09 boundary.
WaterQuality Component pub struct WaterQuality(pub u16) Current A14 tank-water quality permille, 0..=1000.
TankCapacity Component pub struct TankCapacity { pub used: f32, pub required: f32 } Derived cubic-meter requirement for current marine population.
AquaticAnimal Component pub struct AquaticAnimal { pub minimum_depth: f32, pub initial_space: f32, pub additional_space: f32 } Native species requirements; finite nonnegative values.
AquaticHome Component pub struct AquaticHome(pub Entity) References one live tank habitat.
TankOperation enum RaiseFloor, LowerFloor, RaiseWall, LowerWall, Fill, Drain Fixed player tool vocabulary.
TankEditRequest Message pub struct TankEditRequest { pub tank: Entity, pub operation: TankOperation, pub elapsed_ticks: u32 } Coalesced continuous-tool simulation ticks; authored rate is preflight-compiled and application is integer-tick deterministic.
TankChanged Message pub struct TankChanged { pub tank: Entity } Emitted once after valid geometry/water mutation.
MarinePlacementRejected Message pub struct MarinePlacementRejected { pub animal_definition: AssetId, pub tank: Entity, pub reason: MarinePlacementFailure } Typed rejection without spawn/payment completion.
MarinePlacementFailure enum NotTank, InsufficientDepth, InsufficientSpace, IncompatibleWater, InvalidGeometry Fixed validated reasons.

Root requests direct G09 habitat boundary/membership and containment; G05 terrain/water/collision dirty-region facts; G07 transaction grouping; G11 animal spawn/species; G12 welfare; G14 aquatic navigation; A14/A16 definitions; and A18 water effects. Do not duplicate meshes, terrain, habitat membership, or animal state.

4. ECS ownership

Tank geometry lives on the habitat/tank entity; marine requirements/home live on the animal. There is no tank manager or separate water world.

System Schedule/set Exact query and parameters Writes Run condition/order
apply_tank_edits FixedUpdate / FixedGameSet::Act MessageReader<TankEditRequest>, Res<Assets<WorldDefinitionsAsset>>, Query<(&Tank,&mut TankGeometry)>, Query<&EditTransaction>, MessageWriter<TankChanged> Geometry/water values Valid continuous request; clamps/rejects by authored limits.
recompute_tank_geometry FixedUpdate / FixedGameSet::Cleanup MessageReader<HabitatChanged>, MessageReader<TerrainChanged>, Res<Assets<WorldDefinitionsAsset>>, Query<(&Tank,&HabitatRegion,&mut TankGeometry)> Area/volume and dependent heights Dirty tanks only; after terrain/topology.
update_tank_capacity FixedUpdate / FixedGameSet::Think MessageReader<ContainmentChanged>, Query<(&AquaticAnimal,&AquaticHome),With<Animal>>, Query<&mut TankCapacity,With<Tank>> Used/required capacity Changed membership only; residents are grouped by AquaticHome, never found by a nested world scan.
update_aquatic_suitability FixedUpdate / FixedGameSet::Think Query<(&TankGeometry,&WaterQuality,&TankCapacity),Or<(Changed<TankGeometry>,Changed<WaterQuality>,Changed<TankCapacity>)>>, Query<(&AquaticAnimal,&AquaticHome,&mut HabitatSuitability),With<Animal>> G12 HabitatSuitability only Dirty tank and residents grouped by AquaticHome.
decay_water_quality FixedUpdate / FixedGameSet::Think MessageReader<ZooDayAdvanced>, Res<Assets<WorldDefinitionsAsset>>, Query<(&TankCapacity,&mut WaterQuality),With<Tank>> Quality permille Exactly once per emitted zoo-day event; no float elapsed accumulation.
invalidate_broken_tanks FixedUpdate / FixedGameSet::Cleanup RemovedComponents<Tank>, RemovedComponents<HabitatRegion>, Query<(Entity,&AquaticHome),With<Animal>>, Commands, MessageWriter<ContainmentChanged> Clears AquaticHome and emits exact containment invalidation; G16 consumes that fact Before tank despawn.

5. Preflight producer contract

Not applicable: engine consumes native definitions/chunks. Source tank XML, materials, entity records and commands are compiled by A14–A18; no runtime filesystem/source formats.

6. Behavioral evidence and disposition

Search shipped Marine Mania tank manager/manipulation UI, tank wall/gate/ support, marine species and water definitions. Search analysis TSVs for ZTTankMgr, ZTTankCommandReader, ZT_SETTANKMODE, floor/wall/fill/drain commands, and tank material tokens. Observe live manipulation speeds, limits, pricing and animal response. Mine d5657e5a only for evidence in domain/src/simulation/tank.rs, features/simulation/tank.rs, expansion and terrain modules.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

Settle water/floor offsets, minimum/show depth, dynamic manipulation speeds, space formula (initial plus additional population), wall material states, fill/drain cost, water-quality decay/care and invalid-tank consequences. Values come from native records/oracle/live behavior only.

7. Required behavior

8. Performance contract

Steady systems allocate zero. Dirty-tank work is O(changed boundary cells + indexed residents), not tanks × animals. Fill/edit O(1) per request. GPU water/ mesh staging uses G05/A16/A18 reusable buffers with zero recurring allocations; this plugin owns no copied payload. Tests separately count heap, terrain mapping page faults, and changed GPU bytes.

9. Acceptance criteria

Deliver exact exports. Tests cover geometry invariants, native rates/limits, capacity formula, placement failures, dirty-only suitability, undo/redo facts, and relationship cleanup. Observable done: build/edit/fill/drain a tank, place marine animals, observe correct suitability/care. No tank manager, guessed offset, duplicate mesh/water world, or runtime parser.

10. Required handoff

Report files/API, root/dependency/schedule/reflection/G22 wiring, consumed symbols, evidence, seams, and allocator/page/GPU/gameplay checks.

G26 — Animal Shows

1. Identity and outcome

This package delivers show stages, editable schedules, performer/trick compatibility, training, execution, audience participation, and results as ordinary entities/components. Animals train and perform authored animations at real stages; guests watch and react; revenue/donations use G18/G19.

Integration wave: 5. Prerequisites: A06 animation, A10 behavior, A14 definitions, G11–G18. G25 supplies aquatic show-stage suitability where applicable.

2. Exclusive ownership

Exclusive path: reconstruction/game/src/plugins/shows/; all else read-only. Root owns registrations, schedule/reflection/persistence wiring, dependencies, and cross-package alignment.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
ShowsPlugin Plugin unit struct Registers schedule/training/performance systems.
ShowStage Component pub struct ShowStage { pub definition: AssetId } Real placed stage entity.
ShowSchedule Component pub struct ShowSchedule { pub slots: Vec<ShowSlot>, pub capacity: u16 } Allocated once to native stage capacity; len <= capacity, no steady growth beyond reserved capacity.
ShowSlot value struct pub struct ShowSlot { pub performer: Entity, pub trick: AssetId, pub duration_ticks: u32 } Live performer and compatible trick; positive duration is preflight-compiled G32 ticks.
ShowState Component + enum Idle, Preparing { remaining_ticks: u32 }, Running { slot: u16, elapsed_ticks: u32 }, Cooldown { remaining_ticks: u32 } Exactly one deterministic G32-tick stage lifecycle state.
TrickTraining Component pub struct TrickTraining { pub performer: Entity, pub trick: AssetId, pub proficiency: f32 } Dedicated relation entity; proficiency finite 0..=1.
TrainingProgress Component pub struct TrainingProgress { pub trainer: Entity, pub remaining_ticks: u32 } Temporary active training on relation entity; preflight-compiled G32 ticks.
Performing Component pub struct Performing { pub stage: Entity, pub trick: AssetId } Lives on animal during current slot; excludes ordinary behavior.
WatchingShow Component pub struct WatchingShow { pub stage: Entity } Lives on guest after seating/view validation.
PendingShowTransaction Component pub struct PendingShowTransaction { pub stage: Entity, pub guest: Option<Entity>, pub outcome: AssetId } Domain-owned G18 operation for one evidenced show charge/reward.
ShowEdit Message pub enum ShowEdit { Insert { stage: Entity, index: u16, performer: Entity, trick: AssetId }, Remove { stage: Entity, index: u16 }, Move { stage: Entity, from: u16, to: u16 }, Clear { stage: Entity } } Typed mixer edit; validates then mutates preallocated schedule.
StartShowRequest Message pub struct StartShowRequest { pub stage: Entity } Starts eligible nonempty schedule.
TrainingRequest Message pub struct TrainingRequest { pub performer: Entity, pub trick: AssetId, pub trainer: Entity } Staff/player request after compatibility validation.
ShowCompleted Message pub struct ShowCompleted { pub stage: Entity, pub audience: u32, pub score_milli: i32 } Emitted once after final effects in exact A14 fixed score units.

Root requests direct G11 animal/species, G13 behavior exclusion, G14 docking, G17 reactions, G18 transactions, G19 donation category, G20 training job, G25 tank/stage compatibility, A06 animation handles, and A14 show/trick/stage records. No show manager, animation copy, audience list, or UI mixer model.

4. ECS ownership

Schedules/states live on stage; training is a relation entity; performance and watching live on participants. Variable schedule capacity is native-derived and reserved once.

System Schedule/set Exact query and parameters Writes Run condition/order
apply_show_edits Update / GameSet::Intent MessageReader<ShowEdit>, Res<Assets<WorldDefinitionsAsset>>, Query<(&mut ShowSchedule,&ShowState),With<ShowStage>>, Query<(&SpeciesHandle,&AnimalLifeStage),With<Animal>>, Query<&TrickTraining> Preallocated schedule slots Idle only unless evidence allows; rejects invalid edit.
begin_show FixedUpdate / FixedGameSet::Act MessageReader<StartShowRequest>, Res<Assets<WorldDefinitionsAsset>>, Query<(&ShowStage,&ShowSchedule,&mut ShowState)>, Query<(&SpeciesHandle,Option<&Performing>),With<Animal>>, Query<&TrickTraining>, MessageWriter<NavigateTo> Preparing state and participant navigation targets Eligible stage only.
advance_show_state FixedUpdate / FixedGameSet::Act Res<ZooClock>, Query<(Entity,&ShowSchedule,&mut ShowState),With<ShowStage>>, Query<(Entity,Option<&Performing>),With<Animal>>, MessageReader<Arrived>, MessageReader<AnimationMarker>, Commands Increments/decrements exact show-state tick fields and Performing One simulation tick after navigation/animation sampling.
acquire_show_audience FixedUpdate / FixedGameSet::Think Res<SpatialGrid>, Res<Assets<WorldDefinitionsAsset>>, Query<(Entity,&GlobalTransform,&ShowSchedule,&ShowState),With<ShowStage>>, Query<(Entity,&GlobalTransform,Option<&WatchingShow>),With<Guest>>, Commands Inserts/removes WatchingShow No retained audience vector.
apply_show_reactions FixedUpdate / FixedGameSet::Act Res<Assets<WorldDefinitionsAsset>>, Query<(Entity,&WatchingShow),With<Guest>>, Query<(&ShowSchedule,&ShowState),With<ShowStage>>, Query<&TrickTraining>, MessageWriter<GuestReaction> Emits reaction facts At authored markers/end, not every frame.
finish_show FixedUpdate / FixedGameSet::Cleanup Res<Assets<WorldDefinitionsAsset>>, Query<(Entity,&ShowSchedule,&mut ShowState),With<ShowStage>>, Query<(Entity,&WatchingShow),With<Guest>>, Query<(Entity,&Performing),With<Animal>>, Commands, MessageWriter<DonationRequest>, MessageWriter<ShowCompleted> Clears participant facts; idle/cooldown; emits nonfinancial outcomes Exactly once when the final slot reaches its authored end marker.
request_show_transactions FixedUpdate / FixedGameSet::Economy MessageReader<ShowCompleted>, Res<Assets<WorldDefinitionsAsset>>, Query<&WorldMember,With<ShowStage>>, Query<&PendingShowTransaction>, Commands, MessageWriter<TransactionRequest> Spawns one PendingShowTransaction carrying the stage's WorldMember and emits request carrying it One operation per authored financial outcome.
settle_show_transactions FixedUpdate / FixedGameSet::Cleanup MessageReader<TransactionCompleted>, MessageReader<TransactionRejected>, Query<(Entity,&PendingShowTransaction)>, Commands, MessageWriter<GuestReaction> Applies only matching authored success/failure feedback and despawns operation Result operation must equal the operation entity; rejection is never success.
begin_training FixedUpdate / FixedGameSet::Act MessageReader<TrainingRequest>, Res<Assets<WorldDefinitionsAsset>>, Query<(&SpeciesHandle,&AnimalLifeStage,&WorldMember),With<Animal>>, Query<&StaffRole,With<Staff>>, Query<(Entity,&TrickTraining,Option<&TrainingProgress>)>, ResMut<PersistentIdAllocator>, Commands Updates an existing relation or, after ID allocation, spawns TrickTraining with the performer's WorldMember and fresh PersistentId; inserts TrainingProgress Valid request only; no acknowledgement before identity exists.
advance_training FixedUpdate / FixedGameSet::Act Res<ZooClock>, Res<Assets<WorldDefinitionsAsset>>, MessageReader<Arrived>, Query<(&mut TrickTraining,&mut TrainingProgress)>, Query<(),With<Animal>>, Query<(),With<Staff>>, Commands Decrements remaining_ticks; updates fixed authored proficiency at completion; clears progress One simulation tick; split from show execution.
clean_invalid_show_links FixedUpdate / FixedGameSet::Cleanup RemovedComponents<ShowStage>, RemovedComponents<Animal>, RemovedComponents<Guest>, RemovedComponents<Staff>, Query<(Entity,&mut ShowSchedule)>, Query<(Entity,&TrickTraining,Option<&TrainingProgress>)>, Query<(Entity,&Performing)>, Query<(Entity,&WatchingShow)>, Commands Removes invalid links/slots Before referenced despawn.

5. Preflight producer contract

Not applicable: A06/A10/A14 compile show/trick/stage/animation contracts. No runtime XML, scripts, paths, filesystem or source command dispatch.

6. Behavioral evidence and disposition

Search shipped show stage, trick, training, AI/task, animation and mixer UI families; analysis TSVs for ZT_SHOWMIXER_*, ZT_SET_SHOW_NAME, show donation and gesture tokens; live-observe scheduling, eligibility, training, crowd and rewards. Mine d5657e5a only for behavioral leads in show modules, interaction dispatch/training, donations and animation evidence.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

Settle stage capacity, compatible species/tricks, training formula, mixer edit semantics, preparation/cooldown, animation marker effects, audience capacity/ score, donation/revenue and cancellation. Native records supply all values.

7. Required behavior

8. Performance contract

Steady systems allocate zero. Schedule edits are explicit exceptional UI events but must remain within reserved capacity without allocation. Runtime is O(active stages + active participants + nearby indexed audience), not stages × guests. Animation/assets remain mapped/handled. GPU staging is A06/render-owned and reused. Tests count idle/running/training allocations and GPU bytes separately.

9. Acceptance criteria

Deliver exact exports. Tests cover schedule operations/capacity, compatibility, state transitions, participant cleanup, proficiency, audience/result uniqueness, and save facts. Observable done: create/edit/train/run an authentic show and see guest/economy effects. No manager, list copy, guessed capacity, script VM, or fake animations.

10. Required handoff

Report API/files, root/dependency/schedule/reflection/G22 wiring, native evidence, consumers, seams, and allocator/page/GPU/gameplay checks.

G27 — Transport and Tours

1. Identity and outcome

This package delivers ground/sky transport circuits and tours as ECS graph entities: stations, track segments, vehicles, riders, routes, queues, viewable subjects, tour scoring, and transport economics. Guests board real vehicles, travel a validated circuit, view authored subjects, pay, and disembark.

Integration wave: 5. Prerequisites: A14 definitions, A15 prefabs, A17 navigation, G14 locomotion, G17 guests, and G18 economy.

2. Exclusive ownership

Exclusive path: reconstruction/game/src/plugins/transport_tours/; all other paths read-only. Root owns registrations, schedules, reflection/persistence, dependencies, and cross-package alignment.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
TransportToursPlugin Plugin unit struct Registers circuits, vehicles, riders and tour scoring.
TransportCircuit Component pub struct TransportCircuit { pub definition: AssetId, pub closed: bool } Circuit root entity; topology is relationships, not embedded vectors.
CircuitMember Component pub struct CircuitMember(pub Entity) Station/track/vehicle belongs to one live circuit.
TransportStation Component pub struct TransportStation { pub definition: AssetId, pub capacity: u16, pub queued: u16, pub occupied: u16 } Counters remain within native capacities.
TrackSegment Component pub struct TrackSegment { pub definition: AssetId, pub from: Entity, pub to: Entity, pub length: f32 } Directed validated circuit edge; finite positive length.
TransportVehicle Component pub struct TransportVehicle { pub definition: AssetId, pub seats: u16, pub occupied: u16, pub speed: f32 } Counters valid; speed finite nonnegative.
RoutePosition Component pub struct RoutePosition { pub segment: Entity, pub distance: f32 } Distance clamped to segment length.
StationDestination Component pub struct StationDestination(pub Entity) Vehicle's next live station.
WaitingForTransport Component pub struct WaitingForTransport { pub station: Entity, pub circuit: Entity } Lives on guest; one queue relation.
TransportRider Component pub struct TransportRider { pub vehicle: Entity, pub boarded_station: Entity } Lives on guest; excludes ordinary locomotion.
TourScore Component pub struct TourScore { pub value: f32, pub observations: u32 } Lives on rider/guest during tour; finite native-derived sum.
TourViewable Component pub struct TourViewable { pub definition: AssetId } Marks subject with compiled category/event score record.
PendingTransportFare Component pub struct PendingTransportFare { pub guest: Entity, pub circuit: Entity, pub score: f32 } Domain-owned operation correlating one completed ride with one fare.
OpenCircuitRequest Message pub struct OpenCircuitRequest { pub circuit: Entity, pub open: bool } Validates closed graph, stations and vehicles before opening.
BoardTransportRequest Message pub struct BoardTransportRequest { pub guest: Entity, pub station: Entity } Actual guest boarding intent.
TransportTripCompleted Message pub struct TransportTripCompleted { pub guest: Entity, pub circuit: Entity, pub score: f32 } Emitted once after disembark/payment/tour effects.

Root requests G08 topology/build transaction facts, G14 spatial/navigation and docking, G17 destination/reaction, G18 price/service/transaction, A14/A17 station/track/vehicle/view records, and G19 donation category where proven. Do not create a circuit manager, passenger vector, route world, or copied graph.

4. ECS ownership

Graph nodes/edges/vehicles/riders are entities/components. Counts are derived and maintained transactionally; rider membership is on guests, never a vehicle Vec<Entity>.

System Schedule/set Exact query and parameters Writes Run condition/order
validate_changed_circuits FixedUpdate / FixedGameSet::Cleanup MessageReader<OpenCircuitRequest>, Res<Assets<WorldDefinitionsAsset>>, Query<(Entity,&mut TransportCircuit)>, Query<(Entity,&CircuitMember,Option<&TrackSegment>,Option<&TransportStation>,Option<&TransportVehicle>)> TransportCircuit.closed only after complete graph validation; invalid requests leave it closed On request or Changed<CircuitMember>/Changed<TrackSegment>/Changed<TransportStation>.
queue_transport_riders FixedUpdate / FixedGameSet::Act MessageReader<BoardTransportRequest>, Query<(&Guest,&Wallet,Option<&WaitingForTransport>,Option<&TransportRider>)>, Query<(&CircuitMember,&mut TransportStation,&GlobalTransform)>, Query<&TransportCircuit>, Query<&GlobalTransform,With<Guest>>, Commands WaitingForTransport and TransportStation.queued Capacity/reach/price valid.
board_transport_riders FixedUpdate / FixedGameSet::Act MessageReader<Arrived>, Query<(Entity,&mut TransportVehicle,&CircuitMember)>, Query<(Entity,&mut TransportStation)>, Query<(Entity,&WaitingForTransport),With<Guest>>, Commands TransportRider, TourScore, station/vehicle counters; removes waiting and ordinary navigation facts Deterministic queue order recovered from evidence.
advance_transport_vehicles FixedUpdate / FixedGameSet::Navigate Res<ZooClock>, Res<Assets<WorldDefinitionsAsset>>, Query<(&mut TransportVehicle,&mut RoutePosition,&StationDestination,&mut Transform,&CircuitMember)>, Query<(&TrackSegment,&GlobalTransform)>, Query<&TransportCircuit> Route distance, transform and speed Open valid circuits only; authored acceleration/spacing.
score_tour_views FixedUpdate / FixedGameSet::Act Res<SpatialGrid>, Res<Assets<WorldDefinitionsAsset>>, Query<(Entity,&TransportRider,&mut TourScore),With<Guest>>, Query<&GlobalTransform,With<TransportVehicle>>, Query<(Entity,&TourViewable,&GlobalTransform)> , MessageWriter<GuestReaction> TourScore; emits exact guest reactions At authored observation cadence, using G14 cell ranges rather than a world scan.
disembark_transport_riders FixedUpdate / FixedGameSet::Act MessageReader<Arrived>, Res<Assets<WorldDefinitionsAsset>>, Query<(Entity,&mut TransportVehicle)>, Query<&mut TransportStation>, Query<(Entity,&TransportRider,&TourScore,&WorldMember),With<Guest>>, Query<&PendingTransportFare>, Commands, MessageWriter<TransactionRequest> Clears rider/counts, restores navigation, spawns PendingTransportFare carrying the guest's WorldMember, and emits fare request carrying it One operation per ride.
finish_transport_trips FixedUpdate / FixedGameSet::Cleanup MessageReader<TransactionCompleted>, Query<(Entity,&PendingTransportFare)>, Commands, MessageWriter<TransportTripCompleted>, MessageWriter<GuestReaction>, MessageWriter<DonationRequest> Emits result once and despawns operation TransactionCompleted.operation must equal the operation entity.
reject_transport_fares FixedUpdate / FixedGameSet::Cleanup MessageReader<TransactionRejected>, Query<(Entity,&PendingTransportFare)>, Commands, MessageWriter<GuestReaction> Applies only evidenced failed-fare consequence and despawns operation; no trip success TransactionRejected.operation must equal the operation entity.
clean_broken_transport_links FixedUpdate / FixedGameSet::Cleanup RemovedComponents<TransportCircuit>, RemovedComponents<TransportStation>, RemovedComponents<TrackSegment>, RemovedComponents<TransportVehicle>, Query<(Entity,&CircuitMember)>, Query<(Entity,&WaitingForTransport),With<Guest>>, Query<(Entity,&TransportRider),With<Guest>>, Query<&mut TransportStation>, Query<&mut TransportVehicle>, Commands Clears relationships and repairs counts Before despawn; authentic evacuation/refund is an evidence gate.

5. Preflight producer contract

Not applicable: A14/A15/A17 compile station endpoints, graph/track geometry, vehicle motion and tour scoring. No runtime XML/Z2F/filesystem/source merging.

6. Behavioral evidence and disposition

Search shipped ground/sky station, track, vehicle, gate, tour AI/viewable and economy definitions; analysis/oracle for transportation/circuit/station/vehicle classes and tour tasks. Observe construction validation, queues, vehicle spacing, boarding, fares and tour reactions. Mine d5657e5a only for evidence in transport/tour domain and feature modules.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

Settle graph closure/direction, endpoint tolerances, slopes/heights, vehicle acceleration/follow distance, capacities, queue order, station dwell, fare timing, rider destination, view radius/score/categories/events and donation effects. All are native/evidence-derived.

7. Required behavior

8. Performance contract

Zero settled allocations. Vehicle movement O(vehicles); boarding/disembark O( affected station queues/riders); view scoring O(nearby spatial cells). No graph clone, passenger vector, all-pairs search, strings, hash growth, filesystem, or payload clone. Render/GPU handles remain A04/A15-owned. Tests profile heap, mapped navigation pages and GPU instances separately.

9. Acceptance criteria

Deliver exact exports. Tests cover graph validity, capacity/counters, queue ordering, movement/spacing, view score, payment/result uniqueness, and broken link cleanup. Observable done: build/open a circuit, guests queue/ride/view/pay/ leave authentically. No manager, route mirror, guessed tolerance or stub.

10. Required handoff

Report API/files, root/dependency/schedule/reflection/G22 wiring, consumed symbols, evidence, seams, and allocator/page/GPU/gameplay checks.

G28 — Photos

1. Identity and outcome

This package delivers player camera capture, authentic subject/framing facts, photo entities/albums, and photo challenges. A capture reuses GPU readback capacity, creates one Bevy image asset and one metadata entity, and reports typed subject evidence to G24. It does not retain a second rendered world.

Integration wave: 5. Prerequisites: A02 UI, A05 textures, A19 challenges, G06 camera, and G24 scenario.

2. Exclusive ownership

Exclusive path: reconstruction/game/src/plugins/photos/; all else read-only. Root owns module/plugin/render-graph/schedule/reflection/persistence registration, dependencies, and cross-package alignment.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
PhotosPlugin Plugin unit struct Registers photo intent, metadata and completion systems.
PhotoMode Component marker on the active camera Capture input is active; G31 owns overall mode transition.
PhotoSubject Component pub struct PhotoSubject { pub definition: AssetId } Marks a photographable live entity with native scoring definition.
Photo Component pub struct Photo { pub image: Handle<Image>, pub captured_tick: u64, pub camera_position: Vec3, pub camera_rotation: Quat, pub score_milli: i32 } Lives on one album-entry entity, owns one generated image handle, and stores A19's exact integer-thousandths score.
PhotoSubjects Component pub struct PhotoSubjects(pub ArrayVec<Entity, PHOTO_SUBJECT_CAPACITY>) Inline sorted/deduplicated subject IDs; no heap owner or runtime resize.
PhotoAlbum Component pub struct PhotoAlbum { pub profile: AssetId } Album root entity; entries are child/relationship entities, not an embedded vector.
AlbumMember Component pub struct AlbumMember(pub Entity) Photo entry references one live album.
CapturePhotoRequest Message pub struct CapturePhotoRequest { pub camera: Entity } Player request from fixed input command.
PhotoCaptured Message pub struct PhotoCaptured { pub photo: Entity, pub score_milli: i32 } Emitted once after GPU result and metadata commit.
PhotoChallengeCompleted Message pub struct PhotoChallengeCompleted { pub photo: Entity, pub challenge: AssetId } Emitted once per newly satisfied A19 challenge after exact score/subject evaluation.
PhotoCaptureFailed Message pub struct PhotoCaptureFailed { pub reason: PhotoFailure } Typed failure; no empty photo entity.
PhotoFailure enum NotInPhotoMode, ReadbackBusy, InvalidCamera, CapacityExceeded, PersistentId(PersistentIdError), Gpu, Storage Fixed failure vocabulary.
PendingPhotoCapture Component pub struct PendingPhotoCapture { pub captured_tick: u64, pub camera_position: Vec3, pub camera_rotation: Quat, pub subjects: PhotoSubjects } Lives on the camera from accepted intent until the mapped GPU buffer is consumed; it is also the render-extraction request and owns no image bytes.
PhotoReadback Resource pub struct PhotoReadback { pub buffer: Buffer, pub capacity_bytes: u64, pub busy: bool, pub ready: bool, pub width: u32, pub height: u32, pub row_bytes: u32 } One reusable GPU readback buffer and its fixed-size completion facts; may grow only outside settled frame and never per capture once target-profile capacity is established.
DeletePhotoRequest Message pub struct DeletePhotoRequest { pub photo: Entity } Removes album entity/image persistence through explicit action.

PhotoSubjects imports A19's one recovered PHOTO_SUBJECT_CAPACITY constant; G28 declares no duplicate. Root aligns G06 active camera and frustum, render graph extraction/readback, G31 mode, G24 typed photo objective observation, A19 native scoring records, and G22 generated-image persistence. Do not copy scene entities, camera world, or texture payload into a Rust resource.

4. ECS ownership

Albums/photos are entities; generated pixels live in Bevy Assets<Image> and GPU resources. The only resource is reusable staging, not album state. The private zero-sized PendingPhotoScore component is inserted only on a freshly captured photo and removed by score_photo; restored Photo entities never carry it and therefore never replay scoring/challenge messages.

System Schedule/set Exact query and parameters Writes Run condition/order
initialize_photo_album Update / GameSet::Intent MessageReader<WorldLoadFinished>, Res<ProfileOptions>, ResMut<PersistentIdAllocator>, Query<(Entity,&WorldRoot)>, Query<(),With<PhotoAlbum>>, Commands, MessageWriter<PhotoCaptureFailed> For a new world only, creates one PhotoAlbum entity with selected profile, root WorldMember, and allocator-issued PersistentId No existing album; G22 restores saved albums instead.
request_photo_readback Update / GameSet::Intent MessageReader<CapturePhotoRequest>, Res<ZooClock>, Query<(Entity,&Camera,&GlobalTransform,&PhotoMode),Without<PendingPhotoCapture>>, ResMut<PhotoReadback>, Commands, MessageWriter<PhotoCaptureFailed> Inserts PendingPhotoCapture, sets PhotoReadback.busy=true, ready=false; render extraction reads that component directly Valid mode/camera and idle staging only.
collect_visible_photo_subjects Update / GameSet::Presentation Res<SpatialGrid>, Res<Assets<WorldScenarioAsset>>, Query<(&Camera,&GlobalTransform,&mut PendingPhotoCapture)>, Query<(Entity,&PhotoSubject,&GlobalTransform,&InheritedVisibility)> PendingPhotoCapture.subjects only Once for a new pending capture; tests exact camera frustum, render visibility and authored occlusion rule over G14 candidate cells.
complete_photo_capture Update / GameSet::Presentation ResMut<PhotoReadback>, Query<(Entity,&PendingPhotoCapture)>, Query<(Entity,&WorldMember),With<PhotoAlbum>>, ResMut<PersistentIdAllocator>, ResMut<Assets<Image>>, Commands, MessageWriter<PhotoCaptureFailed> Reads the already-mapped buffer; after ID allocation creates one Image and photo entity carrying PersistentId, the album's WorldMember, Photo, PhotoSubjects, AlbumMember, and private PendingPhotoScore; removes capture marker and clears busy/ready PhotoReadback.ready; explicit capture only; exceptional image/entity allocation measured.
score_photo Update / GameSet::Intent Res<Assets<WorldScenarioAsset>>, Query<(Entity,&mut Photo,&PhotoSubjects,&AlbumMember),With<PendingPhotoScore>>, Query<&PhotoSubject>, Commands, MessageWriter<PhotoCaptured>, MessageWriter<PhotoChallengeCompleted> Photo.score_milli, removes pending score, and emits exact challenge/completion messages Once per fresh photo/challenge; restored photos never match.
delete_photos Update / GameSet::Intent MessageReader<DeletePhotoRequest>, Query<(Entity,&Photo,&AlbumMember)>, ResMut<Assets<Image>>, Commands, MessageWriter<DeleteGeneratedImage> Removes image/entry and requests persistent generated-image deletion Explicit valid request; DeleteGeneratedImage is the root-frozen G22 storage message, not a filesystem call.

5. Preflight producer contract

Not applicable: A19 compiles photo scoring/challenge records and A02 commands. No source XML, filesystem discovery, Z2F, or image transcoding occurs in ordinary runtime; generated photo persistence is explicit gameplay output.

6. Behavioral evidence and disposition

Search shipped photo UI, camera mode, photographable entity and photo challenge definitions; analysis TSVs/oracle for photo/camera/album/challenge command names; live-observe framing, subject recognition, naming, score and album behavior. Mine d5657e5a only for evidence in domain photo and Bevy presentation/photos, camera, screenshot, save and scenario modules.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

Settle output resolution/format, capture effects, visibility/occlusion, numeric PHOTO_SUBJECT_CAPACITY, maximum subjects, subject/framing/behavior scoring, challenge event, album limit, thumbnail/full image and save/delete policy. All bounds/formulas derive from native target profile/evidence.

7. Required behavior

8. Performance contract

Settled non-capture frames allocate zero and do no readback. Subject IDs use inline evidence-bounded storage validated by A19 preflight. Staging reaches target-profile capacity before measurement; captures are explicit exceptional operations with allocation/readback latency and bytes recorded. Subject search uses existing visibility/spatial results O(visible candidates). No scene clone, per-frame pixels, strings, filesystem read, or recurring GPU allocation. Track Rust heap, mapped definitions, readback bytes and generated texture VRAM separately.

9. Acceptance criteria

Deliver exact exports. Tests cover mode/busy failures, subject dedupe/capacity, single completion, scoring fact, album relationship and deletion ownership; render validation confirms actual pixels/occlusion. Observable done: enter photo mode, capture/view/delete a correctly scored photo and satisfy a challenge. No shadow renderer/world, guessed bound, blank success or recurring staging.

10. Required handoff

Report API/files, render/Cargo/root/G22/G24 wiring, native evidence, seams, and heap/page/readback/VRAM/gameplay measurements.

G29 — Extinct Animal Recovery

1. Identity and outcome

This package delivers fossil discovery, collection, puzzle assembly, cloning, and creation of extinct animals as ordinary progression and world entities. Recovered pieces unlock an authored skeleton assembly; completed specimens feed a cloning lab; successful cloning requests the normal G11 animal spawn path.

Integration wave: 5. Prerequisites: A03 species, A10 behavior, A14 definitions, G11 animal lifecycle, G23 progression, and G24 scenario.

2. Exclusive ownership

Exclusive path: reconstruction/game/src/plugins/extinct_animals/; all else read-only. Root owns registrations, schedules, reflection/persistence, dependencies, and cross-package alignment.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
ExtinctAnimalsPlugin Plugin unit struct Registers fossil/assembly/cloning systems.
FossilSite Component pub struct FossilSite { pub definition: AssetId, pub exhausted: bool } Placed/authored discovery location.
FossilRng Component pub struct FossilRng(pub DeterministicRng) G32 stream on each site, derived from zoo seed and site PersistentId; sole discovery roll source.
FossilPiece Component pub struct FossilPiece { pub set: AssetId, pub piece_index: u16 } Discovered world piece; index validated against native set.
FossilCollection Resource pub struct FossilCollection { pub words: Vec<u64>, pub piece_count: u32 } Profile-wide bounded bitset sized once to native stable piece index.
AssemblyTable Component pub struct AssemblyTable { pub definition: AssetId } Real facility entity.
FossilAssembly Component pub struct FossilAssembly { pub set: AssetId, pub placed_words: Vec<u64>, pub piece_count: u16 } Lives on assembly entity; bitset allocated once to set size.
CloneLab Component pub struct CloneLab { pub definition: AssetId } Real facility entity.
CloneRng Component pub struct CloneRng(pub DeterministicRng) G32 stream on each lab, derived from zoo seed and lab PersistentId; sole cloning roll source.
CloneProcess Component pub struct CloneProcess { pub species: AssetId, pub elapsed_ticks: u64, pub required_ticks: u64, pub attempt: u32 } Lives on lab while one authored clone attempt runs; A14 process duration is already ticks.
PendingClonePayment Component pub struct PendingClonePayment { pub lab: Entity, pub species: AssetId } Domain-owned transaction operation; no CloneProcess exists before matching completion.
PendingCloneSpawn Component pub struct PendingCloneSpawn { pub lab: Entity, pub species: AssetId } Lives on the caller-owned spawn-operation entity and correlates G11's natural spawn result.
DiscoverFossilRequest Message pub struct DiscoverFossilRequest { pub site: Entity, pub tool: Entity } Valid special-mode discovery action.
PlaceFossilPiece Message pub struct PlaceFossilPiece { pub assembly: Entity, pub piece_index: u16, pub slot_index: u16 } Typed assembly puzzle action; native layout validates slot.
StartCloneRequest Message pub struct StartCloneRequest { pub lab: Entity, pub species: AssetId } Requires completed assembly/unlock/cost.
FossilSetCompleted Message pub struct FossilSetCompleted { pub assembly: Entity, pub set: AssetId } Emitted exactly once on valid full assembly.
CloneCompleted Message pub struct CloneCompleted { pub lab: Entity, pub species: AssetId, pub animal: Entity } Emitted after normal animal spawn succeeds.
CloneFailed Message pub struct CloneFailed { pub lab: Entity, pub species: AssetId, pub attempt: u32 } Emitted only when authored probability/rules produce failure.

Root requests A14 stable fossil piece/set/slot/lab definitions; G30 puzzle/gesture observations; G31 fossil/cloning modes; G18 costs; G23 unlocks; G24 typed events; and G11 adoption/spawn request. Do not copy species definitions or create an extinct-animal entity architecture separate from ordinary animals.

4. ECS ownership

Collection is genuinely profile-global bounded inventory. Site/table/lab/piece/ assembly/process are entities/components. Bitsets allocate once from native counts and do not grow in steady play.

The implementation-private CloneSpawnPending { operation: Entity } component lives on a lab only between SpawnAnimalRequest and the matching G11 result. It is a narrow correlation marker, not durable clone state, and G22 never saves it.

System Schedule/set Exact query and parameters Writes Run condition/order
discover_fossils FixedUpdate / FixedGameSet::Act MessageReader<DiscoverFossilRequest>, Res<ActiveImmersiveMode>, Res<Assets<WorldDefinitionsAsset>>, ResMut<FossilCollection>, ResMut<PersistentIdAllocator>, Query<(&GlobalTransform,&ModeTool)>, Query<(Entity,&mut FossilSite,&mut FossilRng,&GlobalTransform,&WorldMember)>, Commands Collection bit/site exhaustion and, after ID allocation, optional FossilPiece with site WorldMember and fresh PersistentId Valid authored action only; no visible piece before identity exists.
place_fossil_pieces Update / GameSet::Intent MessageReader<PlaceFossilPiece>, Res<Assets<WorldDefinitionsAsset>>, Res<FossilCollection>, Query<(Entity,&AssemblyTable,&mut FossilAssembly)>, MessageWriter<FossilSetCompleted> Assembly placed bitset/count Reject unavailable/wrong/occupied piece.
request_clone_payments FixedUpdate / FixedGameSet::Economy MessageReader<StartCloneRequest>, Res<Assets<WorldDefinitionsAsset>>, Res<UnlockSet>, Res<FossilCollection>, Query<(Entity,&CloneLab,&WorldMember,Option<&CloneProcess>)>, Query<&PendingClonePayment>, Commands, MessageWriter<TransactionRequest> Spawns PendingClonePayment carrying lab WorldMember and emits transaction carrying it Valid request only; no process yet.
complete_clone_payments FixedUpdate / FixedGameSet::Cleanup MessageReader<TransactionCompleted>, Res<Assets<WorldDefinitionsAsset>>, Query<(Entity,&PendingClonePayment)>, Query<&CloneLab>, Commands Inserts CloneProcess on the lab and despawns operation TransactionCompleted.operation must equal the operation entity.
reject_clone_payments FixedUpdate / FixedGameSet::Cleanup MessageReader<TransactionRejected>, Query<(Entity,&PendingClonePayment)>, Commands Despawns operation without process/success TransactionRejected.operation must equal the operation entity.
advance_clone_processes FixedUpdate / FixedGameSet::Act Res<ZooClock>, Res<Assets<WorldDefinitionsAsset>>, Res<Assets<SpeciesCatalogAsset>>, Query<(Entity,&CloneLab,&mut CloneProcess,&mut CloneRng,&Transform,&HabitatMember,&WorldMember),Without<CloneSpawnPending>>, Commands, MessageWriter<SpawnAnimalRequest>, MessageWriter<CloneFailed> Elapsed ticks/attempt/failure; on authored success spawns a world-scoped PendingCloneSpawn operation, marks the lab with private CloneSpawnPending { operation }, and requests G11 creation in the lab's habitat at A14's authored offset Active labs only; no AdoptAnimal and no direct assembly of G11 components.
finish_successful_clones FixedUpdate / FixedGameSet::Cleanup MessageReader<AnimalSpawned>, Query<(Entity,&PendingCloneSpawn)>, Query<(&CloneLab,&CloneProcess,&CloneSpawnPending)>, Commands, MessageWriter<CloneCompleted> Clears matching lab process/pending marker, despawns operation and emits result Exact spawned.operation match only.
reject_clone_spawns FixedUpdate / FixedGameSet::Cleanup MessageReader<AnimalSpawnRejected>, Query<(Entity,&PendingCloneSpawn)>, Query<(&mut CloneProcess,&CloneSpawnPending)>, Commands, MessageWriter<CloneFailed> Clears matching pending marker/operation and applies the recovered failure/refund policy Exact operation match; never reports a successful clone.
clean_invalid_extinct_links FixedUpdate / FixedGameSet::Cleanup RemovedComponents<FossilSite>, RemovedComponents<AssemblyTable>, RemovedComponents<CloneLab>, Query<(Entity,&FossilPiece)>, Query<(Entity,&FossilAssembly)>, Query<(Entity,&CloneProcess)>, Commands Clears transient relations/process according to authored refund Before despawn; refund behavior is an explicit evidence gate.

5. Preflight producer contract

Not applicable: A03/A10/A14 compile species, fossil layouts, discovery/cloning rules and fixed commands. No runtime XML, scripts, source assets or filesystem.

6. Behavioral evidence and disposition

Search shipped Extinct Animals fossil site/piece/set, fossil finder, assembly UI, cloning lab and extinct species definitions; analysis TSVs/oracle for fossil, assemble, cloning and minigame modes; live-observe discovery, puzzle, probability, timing and spawn. Mine d5657e5a only for behavior/evidence in expansion, scenario, gesture/modes and animal modules.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

Settle discovery distribution/repeats/exhaustion, piece index/layout, assembly validation/reward, cloning prerequisites/cost/time/failure/progression, name/ variant/sex and the exact lab-relative A14 spawn offset. No guessed odds or generic puzzle bag.

7. Required behavior

8. Performance contract

Settled systems allocate zero. Bit lookup O(1); discovery/placement O(1); cloning O(active labs). Bitset capacities fixed at profile/world load. Explicit spawn may use normal Bevy entity allocation but no asset copy. No strings, map growth, filesystem or VM. Allocation tests cover idle, discovery, placement and clone tick; mapped/handle operations zero; no owned GPU resources.

9. Acceptance criteria

Deliver exact exports. Tests cover inventory bounds/uniqueness, slot validity, single completion, clone prerequisites/payment/timing/outcome, ordinary animal spawn and cleanup/persistence. Observable done: find/assemble fossils and clone an extinct animal authentically. No separate extinct world, guessed probability, stub, or runtime parser.

10. Required handoff

Report API/files, root/dependency/G22/G24/G30/G31 wiring, evidence, seams, and allocator/page/gameplay checks.

G30 — Gestures and Puzzles

1. Identity and outcome

This package delivers controller/mouse gesture capture, recognition against compiled A10 templates, and authored minigame/puzzle step progression. It turns input samples into fixed typed actions for training, fossils, cloning, tutorials, and other proven modes without adding a script VM or source-document runtime.

Integration wave: 5. Prerequisites: A02 UI, A10 behavior programs, G02 UI, G03 input, and G24 scenario.

2. Exclusive ownership

Exclusive path: reconstruction/game/src/plugins/gestures_puzzles/; all else read-only. Root owns registrations, schedules, reflection/persistence, dependencies, and alignment with A10 command/template representation.

3. Frozen public contract

Gesture points are normalized viewport coordinates in [-1.0, 1.0] and carry monotonic simulation-independent input seconds. Sample capacity comes from the active compiled gesture template/profile and is reserved before capture.

Symbol Kind Exact shape or signature Owner and invariant
GesturesPuzzlesPlugin Plugin unit struct Registers input capture, recognition and puzzle progression.
GestureSurface Component pub struct GestureSurface { pub definition: AssetId } UI/world interaction surface with compiled allowed templates.
GestureStroke Component pub struct GestureStroke { pub surface: Entity, pub context: GestureContext, pub points: Vec<InputGesturePoint>, pub capacity: u16, pub active: bool } Lives on input/controller cursor entity; allocated once to native bound, never grows during stroke.
InputGesturePoint POD struct #[repr(C)] pub struct InputGesturePoint { pub position: Vec2, pub time_seconds: f32 } Finite normalized live input sample; distinct from A10's quantized GestureTemplatePoint.
GestureContext enum Training, Fossil, Cloning, Tutorial, Minigame Live interaction context, distinct from A10 GestureTemplateKind.
BeginGesture Message pub struct BeginGesture { pub surface: Entity, pub source: ActionSource } Starts valid idle stroke.
GestureSample Message pub struct GestureSample { pub position: Vec2, pub time_seconds: f32, pub source: ActionSource } Device-independent normalized sample.
EndGesture Message pub struct EndGesture { pub source: ActionSource } Ends current source stroke.
GestureRecognized Message pub struct GestureRecognized { pub surface: Entity, pub template: AssetId, pub command: PuzzleCommand, pub score: f32, pub source: ActionSource } Carries A10's exact copyable typed command; template ID is diagnostic identity only.
GestureRejected Message pub struct GestureRejected { pub surface: Entity, pub reason: GestureFailure, pub source: ActionSource } Typed failure; no guessed nearest success.
GestureFailure enum Busy, InvalidSurface, TooFewSamples, CapacityExceeded, NoMatch, Cancelled Fixed reasons.
Puzzle Component pub struct Puzzle { pub definition: AssetId, pub step: u16, pub state: PuzzleState } Dedicated puzzle entity points into compiled step table.
PuzzleState enum Inactive, Running, Completed, Failed Terminal state follows native rule.
PuzzleAction Message pub struct PuzzleAction { pub puzzle: Entity, pub command: PuzzleCommand, pub subject: Option<Entity> } Carries A10's exact copyable command directly.
PuzzleCompleted Message pub struct PuzzleCompleted { pub puzzle: Entity, pub definition: AssetId } Emitted once after final step effects.

G30 consumes A10 GestureTemplateKind, GestureTemplatePoint, GestureTemplate, PuzzleDefinition, PuzzleStep, and PuzzleCommand directly; it declares no archived-template variants. Root aligns G03 pointer/controller normalization, G02 surface/focus ownership, G24 tutorial observation, G26 training, G29 fossil/cloning, and G31 active mode. Do not add arbitrary string commands, callback traits, or copied gesture documents.

4. ECS ownership

Stroke belongs to the active input cursor; puzzle state belongs to puzzle entity. No gesture manager, minigame world, action registry, or unbounded sample queue.

System Schedule/set Exact query and parameters Writes Run condition/order
begin_gesture_strokes Update / GameSet::Input MessageReader<BeginGesture>, Res<Assets<BehaviorProgramAsset>>, Query<&GestureSurface>, Query<(Entity,&mut GestureStroke)>, Commands, MessageWriter<GestureRejected> Clears preallocated points and activates the source stroke Valid focused surface/source only.
append_gesture_samples Update / GameSet::Input MessageReader<GestureSample>, Query<&mut GestureStroke> Pushes into the matching active stroke within existing capacity Rejects nonfinite/out-of-order/overflow; no reserve.
finish_gesture_strokes Update / GameSet::Intent MessageReader<EndGesture>, Res<Assets<BehaviorProgramAsset>>, Query<&GestureSurface>, Query<&mut GestureStroke>, MessageWriter<GestureRecognized>, MessageWriter<GestureRejected> Deactivates stroke; emits one result Deterministic normalization/scoring from the mapped A10 contract.
route_gesture_commands Update / GameSet::Intent MessageReader<GestureRecognized>, Res<ActiveImmersiveMode>, Query<(Entity,&Puzzle,&GestureSurface)>, MessageWriter<PuzzleAction>, MessageWriter<EnterImmersiveMode>, MessageWriter<PlaceFossilPiece>, MessageWriter<StartCloneRequest>, MessageWriter<TrainingRequest> Exhaustively matches PuzzleCommand and emits one natural-owner typed request No AssetId/string command dispatch or callback registry.
advance_puzzles FixedUpdate / FixedGameSet::Act MessageReader<PuzzleAction>, Res<Assets<BehaviorProgramAsset>>, Query<(Entity,&mut Puzzle)>, Query<(),With<WorldMember>>, Commands, MessageWriter<PuzzleCompleted> Puzzle step/state and mapped typed completion/failure effects Valid expected action only.
cancel_invalid_gestures FixedUpdate / FixedGameSet::Cleanup RemovedComponents<GestureSurface>, RemovedComponents<ControlledEntity>, Query<(Entity,&mut GestureStroke)>, Query<(Entity,&mut Puzzle)>, Commands, MessageWriter<GestureRejected> Clears active stroke/puzzle according to rule Before relationship despawn.

5. Preflight producer contract

Not applicable: A10 compiles source gesture paths/templates, normalization parameters, thresholds, commands and puzzle steps into fixed native records. Unsupported script commands are preflight errors. No XML/Lua/filesystem here.

6. Behavioral evidence and disposition

Search shipped gesture/test/training/fossil/cloning/minigame UI and behavior families; analysis TSVs for ZT_SHOWGESTURE, ZT_QUICKGESTURETEST and related commands; live-observe stroke tolerance/feedback and puzzle sequencing. Mine d5657e5a only for behavioral leads in domain/interaction gesture, Bevy gesture dispatch, training, modes and scenario.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

Settle sampling/filtering/resampling, direction/rotation/scale invariance, scoring/threshold/tie-break, capacity, timeout/cancel, command mapping, puzzle step success/failure/retry/reward. Values and algorithms require native/oracle/ live evidence; no generic recognizer guess.

7. Required behavior

8. Performance contract

Stroke storage allocates/reserves once from native capacity; capture, recognition and puzzle ticks allocate zero. Recognition complexity is O(samples × allowed template samples) with precomputed normalized templates and bounded candidates; SIMD may be used after correctness. No strings, source path parsing, map growth, filesystem or cloned templates. Bench heap, mapped pages and recognition latency; no GPU resources.

9. Acceptance criteria

Deliver exact exports. Tests use recovered fixtures to cover normalization, direction/threshold/tie-break, overflow/rejection, command routing, puzzle step/ terminal uniqueness, controller parity and save facts. Observable done: authentic gestures drive training/fossil/cloning/tutorial/minigame actions. No VM, registry, manager, guessed recognizer, or no-op command.

10. Required handoff

Report API/files, dependencies/root schedule wiring, command coverage, G22/G24/G26/G29/G31 wiring, evidence, seams, and allocator/page/latency validation.

G31 — Immersive and Special Modes

1. Identity and outcome

This package delivers coherent transitions into guest view, first-person, training, fossil, cloning, photo, show and other proven special tool/camera modes. It requests the existing G06 camera, attaches focused control markers to the controlled entity and mode-specific components; it never creates another renderer, scene, input world, or overarching runtime.

Integration wave: 5. Prerequisites: G03 input, G06 camera, G24 scenario, G26 shows, G28 photos, G29 extinct animals, and G30 gestures/puzzles.

2. Exclusive ownership

Exclusive path: reconstruction/game/src/plugins/immersive_modes/; all else read-only. Root owns module/plugin/state/schedule/reflection/persistence wiring, dependencies, and cross-package symbol alignment.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
ImmersiveModesPlugin Plugin unit struct Registers mode transition/control projection systems.
ImmersiveMode enum GuestView, FirstPerson, Training, FossilSearch, FossilAssembly, Cloning, Photo, ShowEdit Fixed proven high-level modes; ordinary construction remains G07.
ActiveImmersiveMode Resource pub struct ActiveImmersiveMode { pub mode: ImmersiveMode, pub controller: Entity, pub subject: Option<Entity>, pub camera: Entity } Resource exists only while special mode is active; it identifies participants but copies no camera/input/domain state.
PendingImmersiveEntry Component pub struct PendingImmersiveEntry { pub mode: ImmersiveMode, pub subject: Option<Entity>, pub failure: Option<ModeEntryFailure> } Lives on requesting controller for at most one intent pass; each validator consumes only its mode.
ControlledEntity Component pub struct ControlledEntity { pub controller: Entity } Lives on guest/animal/vehicle/avatar being directly controlled.
ModeTool Component pub struct ModeTool { pub definition: AssetId } Mode-specific cursor/tool entity using native definition.
GuestViewControl / FirstPersonControl / TrainingControl / FossilSearchControl / FossilAssemblyControl / CloningControl / PhotoControl / ShowEditControl marker components unit structs, exactly one attached to the active controller Focused mode-local input filters; no copied G03 action-context resource or enum dispatcher.
EnterImmersiveMode Message pub struct EnterImmersiveMode { pub mode: ImmersiveMode, pub controller: Entity, pub subject: Option<Entity> } Typed request; validates mode prerequisites before transition.
ExitImmersiveMode Message pub struct ExitImmersiveMode { pub controller: Entity, pub reason: ModeExitReason } Requests removal of mode controls and G06 camera restoration.
ModeExitReason enum Completed, Cancelled, SubjectRemoved, Invalidated, GamePhaseChanged Fixed reason vocabulary.
ModeEntryFailure enum Busy, InvalidController, MissingSubject, WrongSubject, MissingTool, RuleDenied Fixed atomic rejection reasons.
ImmersiveModeRejected Message pub struct ImmersiveModeRejected { pub mode: ImmersiveMode, pub reason: ModeEntryFailure } Emitted once by the exact mode validator; failed entry mutates nothing else.
ImmersiveModeEntered Message pub struct ImmersiveModeEntered { pub mode: ImmersiveMode, pub subject: Option<Entity> } Emitted once after camera/input/tool state commits.
ImmersiveModeExited Message pub struct ImmersiveModeExited { pub mode: ImmersiveMode, pub reason: ModeExitReason } Emitted once after full cleanup/return.

G06 exclusively creates, reads, restores, and removes CameraReturnState on the actual camera. G31 only emits G06 SetCameraMode and RestoreCameraMode messages; it never queries or mutates that component. Root also aligns G06 camera requests, G02 focus surface, G14 controlled locomotion, and direct G26/ G28/G29/G30 components. Do not define camera/input/render facades or duplicate their state.

4. ECS ownership

Only one small active-mode resource coordinates a global mutually exclusive mode. Actual camera/input/subject/tool facts remain on their natural entities.

System Schedule/set Exact query and parameters Writes Run condition/order
reserve_immersive_entry Update / GameSet::Intent MessageReader<EnterImmersiveMode>, Option<Res<ActiveImmersiveMode>>, Query<(),With<PendingImmersiveEntry>>, Commands, MessageWriter<ImmersiveModeRejected> Inserts one PendingImmersiveEntry on controller or rejects Busy In game; first valid request in message order.
enter_guest_view Update / GameSet::Intent Query<(Entity,&PendingImmersiveEntry)> selecting mode==GuestView, Query<(),With<Guest>>, Query<Entity,With<ZooCamera>>, Commands, MessageWriter<SetCameraMode>, MessageWriter<ImmersiveModeEntered> Inserts resource/ControlledEntity/GuestViewControl; sends SetCameraMode { mode: CameraMode::FirstPerson(subject), transition_seconds: evidenced value } Transition value is an evidence gate.
enter_first_person Update / GameSet::Intent Query<(Entity,&PendingImmersiveEntry)>, selecting mode==ImmersiveMode::FirstPerson; Query<(),(With<NavAgent>,Without<Dead>)>, Query<Entity,With<ZooCamera>>, Commands, MessageWriter<SetCameraMode>, MessageWriter<ImmersiveModeEntered> Inserts FirstPersonControl/controlled facts; sends CameraMode::FirstPerson(subject) Exact transition value is an evidence gate.
enter_training Update / GameSet::Intent Query<(Entity,&PendingImmersiveEntry)>, selecting mode==ImmersiveMode::Training; Query<(),With<Animal>>, Query<(),With<Staff>>, Query<&TrickTraining>, Res<Assets<WorldDefinitionsAsset>>, Query<Entity,With<ZooCamera>>, Commands, MessageWriter<SetCameraMode>, MessageWriter<ImmersiveModeEntered> Inserts TrainingControl and ModeTool; emits one SetCameraMode Exact CameraMode variant and transition_seconds are mandatory oracle/live-observation evidence gates; this row must not compile with a guessed value.
enter_fossil_search Update / GameSet::Intent Query<(Entity,&PendingImmersiveEntry)>, selecting mode==ImmersiveMode::FossilSearch; Res<Assets<WorldDefinitionsAsset>>, Query<&FossilSite>, Query<Entity,With<ZooCamera>>, Commands, MessageWriter<SetCameraMode>, MessageWriter<ImmersiveModeEntered> Inserts FossilSearchControl/tool and sends evidenced camera request Exact camera variant and transition value are evidence gates.
enter_fossil_assembly Update / GameSet::Intent Query<(Entity,&PendingImmersiveEntry)>, selecting mode==ImmersiveMode::FossilAssembly; Query<&FossilAssembly>, Query<&GestureSurface>, Query<Entity,With<ZooCamera>>, Commands, MessageWriter<SetCameraMode>, MessageWriter<ImmersiveModeEntered> Inserts FossilAssemblyControl/tool and sends evidenced camera request Exact relations, camera variant and transition value are evidence gates.
enter_cloning Update / GameSet::Intent Query<(Entity,&PendingImmersiveEntry)>, selecting mode==ImmersiveMode::Cloning; Query<(&CloneLab,Option<&CloneProcess>)>, Query<&GestureSurface>, Query<Entity,With<ZooCamera>>, Commands, MessageWriter<SetCameraMode>, MessageWriter<ImmersiveModeEntered> Inserts CloningControl/tool and sends evidenced camera request Exact lab prerequisite, camera variant and transition value are evidence gates.
enter_photo Update / GameSet::Intent Query<(Entity,&PendingImmersiveEntry)>, selecting mode==ImmersiveMode::Photo; Res<PhotoReadback>, Query<Entity,With<ZooCamera>>, Commands, MessageWriter<SetCameraMode>, MessageWriter<ImmersiveModeEntered> Inserts PhotoControl/PhotoMode; sends evidenced camera request Readback idle; exact camera variant and transition value are evidence gates.
enter_show_edit Update / GameSet::Intent Query<(Entity,&PendingImmersiveEntry)>, selecting mode==ImmersiveMode::ShowEdit; Query<(&ShowStage,&ShowSchedule)>, Query<&InfoPanel>, Query<Entity,With<ZooCamera>>, Commands, MessageWriter<SetCameraMode>, MessageWriter<ImmersiveModeEntered> Inserts ShowEditControl; sends evidenced camera request Valid stage/surface; exact camera variant and transition value are evidence gates.
reject_pending_entry Update / GameSet::Intent Query<(Entity,&PendingImmersiveEntry)>, Commands, MessageWriter<ImmersiveModeRejected> Emits stored exact failure and removes pending component After all eight validators.
apply_guest_view_controls Update / GameSet::Input MessageReader<ActionRequest>, Res<ControlAxes>, Res<ActiveImmersiveMode>, Query<(&GlobalTransform,&NavAgent),With<GuestViewControl>>, MessageWriter<NavigateTo> Emits guest NavigateTo only; G06 consumes look axes itself Active GuestView.
apply_first_person_controls Update / GameSet::Input MessageReader<ActionRequest>, Res<ControlAxes>, Res<ActiveImmersiveMode>, Query<(&GlobalTransform,&NavAgent),With<FirstPersonControl>>, MessageWriter<NavigateTo> Emits subject NavigateTo only Active FirstPerson.
apply_training_controls Update / GameSet::Input MessageReader<ActionRequest>, Res<ActiveImmersiveMode>, Query<&ModeTool,With<TrainingControl>>, MessageWriter<TrainingRequest>, MessageWriter<BeginGesture>, MessageWriter<GestureSample>, MessageWriter<EndGesture> Exact training/gesture messages only Active Training.
apply_fossil_search_controls Update / GameSet::Input MessageReader<ActionRequest>, Res<ActiveImmersiveMode>, Query<(&Transform,&ModeTool),With<FossilSearchControl>>, Query<(Entity,&FossilSite,&Transform)>, MessageWriter<DiscoverFossilRequest> Fossil discovery requests only Active FossilSearch; bounded G14 candidates supplied before query.
apply_fossil_assembly_controls Update / GameSet::Input MessageReader<ActionRequest>, Res<ActiveImmersiveMode>, Query<&ModeTool,With<FossilAssemblyControl>>, Query<(&FossilAssembly,&GestureSurface)>, MessageWriter<PlaceFossilPiece>, MessageWriter<PuzzleAction> Assembly/puzzle messages only Active FossilAssembly.
apply_cloning_controls Update / GameSet::Input MessageReader<ActionRequest>, Res<ActiveImmersiveMode>, Query<&ModeTool,With<CloningControl>>, Query<(&CloneLab,Option<&CloneProcess>,&GestureSurface)>, MessageWriter<StartCloneRequest>, MessageWriter<PuzzleAction> Clone/puzzle messages only Active Cloning.
apply_photo_controls Update / GameSet::Input MessageReader<ActionRequest>, Res<ActiveImmersiveMode>, Query<Entity,(With<ZooCamera>,With<PhotoMode>)>, MessageWriter<CapturePhotoRequest> Capture requests only Active Photo.
apply_show_edit_controls Update / GameSet::Input MessageReader<ActionRequest>, Res<ActiveImmersiveMode>, Query<(&ShowStage,&ShowSchedule)>, MessageWriter<ShowEdit>, MessageWriter<StartShowRequest> Show edit/start messages only Active ShowEdit.
update_guest_first_person_view Update / GameSet::Presentation Res<ActiveImmersiveMode>, Query<&GlobalTransform,With<ControlledEntity>>, Query<(&mut Transform,&CameraMode),With<ZooCamera>>, Res<Assets<WorldDefinitionsAsset>> Actual G06 camera Transform only GuestView/FirstPerson after subject movement; exact node offset is an evidence gate.
invalidate_removed_mode_subject Update / GameSet::Intent Option<Res<ActiveImmersiveMode>>, RemovedComponents<ControlledEntity>, RemovedComponents<ModeTool>, MessageWriter<ExitImmersiveMode> Exit request only Relevant participant removal.
exit_immersive_modes Update / GameSet::Intent MessageReader<ExitImmersiveMode>, Option<Res<ActiveImmersiveMode>>, Query<Entity,With<ControlledEntity>>, Query<Entity,Or<(With<ModeTool>,With<GuestViewControl>,With<FirstPersonControl>,With<TrainingControl>,With<FossilSearchControl>,With<FossilAssemblyControl>,With<CloningControl>,With<PhotoControl>,With<ShowEditControl>)>>, Commands, MessageWriter<RestoreCameraMode>, MessageWriter<ImmersiveModeExited> Sends restore; removes G31 components/resource Atomic exactly once; never accesses CameraReturnState.
exit_modes_on_phase_change OnExit(GamePhase::InGame) Option<Res<ActiveImmersiveMode>>, Query<Entity,With<ControlledEntity>>, Query<Entity,Or<(With<ModeTool>,With<GuestViewControl>,With<FirstPersonControl>,With<TrainingControl>,With<FossilSearchControl>,With<FossilAssemblyControl>,With<CloningControl>,With<PhotoControl>,With<ShowEditControl>)>>, Commands, MessageWriter<RestoreCameraMode>, MessageWriter<ImmersiveModeExited> Sends restore and removes G31 state Before world teardown.

The eight entry validators and eight mode-control systems above are the frozen registration lists. They may share private pure math helpers, but must not be recombined into a match-based dispatcher or borrow components from inactive modes.

5. Preflight producer contract

Not applicable: mode/tool/camera node/action definitions are compiled by asset packages. No runtime XML, scripts, filesystem or original command strings.

6. Behavioral evidence and disposition

Search shipped guest view/first-person/camera modes, training, fossil, cloning, photo and show mode UI/config families; analysis TSVs/oracle for mode classes and set/cancel commands; live-observe entry restrictions, camera node/controls, cursor/focus and return semantics. Mine d5657e5a only for evidence in domain interaction modes/camera, guest_view, dispatch/modes/training, and presentation camera.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

Settle exact proven mode list, prerequisite/subject eligibility, time pause, camera nodes/FOV/collision, input mapping, subject control, UI visibility, completion/cancel/invalid return behavior and nested-mode prohibition. No mode or camera value may be guessed.

7. Required behavior

8. Performance contract

Mode-inactive settled cost is zero queries via run conditions and zero allocations. Active systems allocate zero and are O(1) plus their natural feature query. No render target/scene clone, strings, dynamic dispatch, source parsing, filesystem, or per-frame handle clone. Camera/GPU resources remain G06/render- owned. Tests count inactive/each active mode allocations and GPU bytes/page faults separately.

9. Acceptance criteria

Deliver exact exports plus separate narrow control systems. Tests cover atomic entry failure, mutual exclusion, each proven mode prerequisite/control route, subject removal, exact camera/focus restoration and unique exit. Observable done: enter/navigate/complete/cancel every original special mode with authentic camera/input semantics. No host runtime, renderer wrapper, mode manager, god dispatcher, guessed camera, or leaked marker.

10. Required handoff

Report API/files, exact private control system list, root/dependency/schedule/ reflection/G22 wiring, consumed symbols, evidence, seams, and allocator/page/ GPU/gameplay checks.

G32 — Simulation time and determinism

1. Identity and outcome

The zoo advances on one authoritative fixed clock with authentic pause and speed tiers, stable calendar boundaries, and deterministic random streams that remain reproducible across save/load and parallel system execution. This is a natural simulation primitive consumed by many domains; it is not progression state or a general runtime service. Integration wave 2. Prerequisites: A14 timing policy and A19 starting-world facts. G04 initializes it and G22 persists it.

2. Exclusive ownership

The worker exclusively owns game/src/plugins/simulation_time/. Every other path is read-only. Root owns module/plugin registration, fixed-schedule cadence, state wiring, reflection/persistence registration, and Cargo dependencies.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
SimulationTimePlugin Plugin pub struct SimulationTimePlugin; Registers only simulation control, clock, calendar-boundary, and deterministic-seed behavior.
ZooClock Resource pub struct ZooClock { pub tick: u64, pub absolute_day: u32, pub tick_in_day: u32 } Sole authoritative zoo time; tick_in_day < timing.ticks_per_day; integers avoid accumulated float drift.
ZooCalendar Resource pub struct ZooCalendar { pub year: u16, pub month: u8, pub day: u8 } Derived from absolute_day using the validated A14 calendar policy; never advanced independently.
SimulationControl Resource pub struct SimulationControl { pub speed_tier: u8, pub paused: bool } One global player policy; tier indexes the mapped A14 speed table.
ZooSeed Resource #[repr(transparent)] pub struct ZooSeed(pub u64); Save-stable world seed chosen once at world creation/import.
RngDomain #[repr(u8)] enum Reproduction, Behavior, Disease, Guest, Donation, Fossil, Clone, Weather, AmbientSpawner Closed code-owned stream-separation vocabulary; adding a stochastic domain is an explicit schema/version change, never an asset ID.
DeterministicRng value #[repr(C)] pub struct DeterministicRng { state: u64, stream: u64 } with from_entity(seed: ZooSeed, persistent_id: PersistentId, domain: RngDomain) -> Self, from_raw([u64; 2]) -> Self, to_raw(self) -> [u64; 2], next_u32(&mut self) -> u32, unit_f32(&mut self) -> f32, and range_u32(&mut self, upper_exclusive: u32) -> Option<u32> Small copyable PCG-family state; explicit raw round-trip is the sole G22 persistence surface. Domain components own instances; exact algorithm/domain discriminants are frozen in native save schema.
SetSimulationSpeed Message pub struct SetSimulationSpeed { pub tier: u8 } Player/UI request; invalid native tier is rejected without mutation.
SetSimulationPaused Message pub struct SetSimulationPaused(pub bool); Explicit pause request; mode/scenario policy may reject it before this package.
SimulationControlChanged Message pub struct SimulationControlChanged { pub speed_tier: u8, pub paused: bool } Emitted once after an actual accepted control change.
ZooDayAdvanced Message pub struct ZooDayAdvanced { pub previous_day: u32, pub current_day: u32, pub calendar: ZooCalendar } Boundary fact emitted once per crossed zoo day, including multiple fixed ticks processed during catch-up.
simulation_running run condition pub fn simulation_running(control: Res<SimulationControl>) -> bool True exactly when in-game simulation is not paused.

A14 must expose a mapped SimulationTimingDefinition containing evidenced fixed frequency, ticks per zoo day, ordered speed multipliers, and calendar policy. A19 must expose starting tick/day/calendar and optional imported seed. Each stochastic domain owns a narrowly named component wrapping DeterministicRng (for example behavior, disease, donation, reproduction); this package does not collect those streams in a resource or component bag.

4. ECS ownership

Clock, calendar, control, and seed are genuinely world-global facts. RNG state belongs to the entity and stochastic domain that consumes it. There is no timer manager, global mutable random queue, callback scheduler, or list of domain timers.

System Schedule/set Exact query and parameters Writes Run condition/order
initialize_simulation_time Update / GameSet::Intent Commands, MessageReader<BeginWorldLoad>, Res<Assets<WorldScenarioAsset>>, Res<Assets<WorldDefinitionsAsset>> Inserts ZooClock, derived ZooCalendar, SimulationControl, and ZooSeed from the requested A19 scenario/start In GamePhase::Loading; before G04 entity streams are seeded.
apply_simulation_control Update / GameSet::Intent MessageReader<SetSimulationSpeed>, MessageReader<SetSimulationPaused>, Res<Assets<WorldDefinitionsAsset>>, ResMut<SimulationControl>, ResMut<Time<Virtual>>, MessageWriter<SimulationControlChanged> Validated policy and virtual-time pause/relative speed that controls fixed-step accumulation only Presentation/input systems must read Time<Real> and remain independent of zoo speed/pause.
advance_zoo_clock FixedUpdate / FixedGameSet::Clock Res<Assets<WorldDefinitionsAsset>>, ResMut<ZooClock>, ResMut<ZooCalendar>, MessageWriter<ZooDayAdvanced> One integer tick and any crossed day/calendar boundary simulation_running; first fixed simulation system.
remove_simulation_time OnExit(GamePhase::InGame) Commands Removes world clock/calendar/control/seed After persistence/world-exit consumers have finished.

5. Preflight producer contract

Not applicable: the engine consumes only native assets produced before launch. A14 preflight recovers and validates time scale, speed tiers, and calendar rules; A19 preflight converts original starting time and seed. This package performs no filesystem discovery, source parsing, conversion, or original-save handling.

6. Behavioral evidence and disposition

Search shipped options/UI/scenario records for speed tiers, pause restrictions, time-of-day and calendar values; search the oracle and analysis maps for game clock, calendar, pause, speed, random and seed functions. Mine commit d5657e5a only for evidence in features/core/time.rs, features/core/random.rs, scenario, economy, animal, disease and save paths. Live observation settles fixed cadence, displayed date boundaries, speed multipliers, pause exceptions, and whether catch-up crosses multiple day events.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

Evidence gates: exact original fixed frequency and zoo-day conversion; number, order and multipliers of speed tiers; calendar epoch/month rules; pause behavior in menus and immersive modes; original random distributions where observable; and imported-save seed availability. Do not substitute familiar simulation-game defaults. The PCG-family implementation is an explicit OpenZT2 determinism choice, while recovered probability formulas remain domain behavior.

7. Required behavior

8. Performance contract

All systems are O(1), allocate zero after warm-up, and perform no filesystem or asset-payload copies. DeterministicRng methods inline without allocation, locking, atomics, dynamic dispatch, modulo bias, or global contention. No per-tick message is emitted; only material control/day boundaries use warmed Bevy message buffers. Regression checks cover an idle paused frame, one normal tick, a day boundary, speed change, RNG sequence/rejection sampling, and exact save/resume continuation. The package owns no mapped payload copy or GPU bytes.

9. Acceptance criteria

Deliver ordinary formatted Rust under the owned directory with every symbol above. Pure tests use evidence-backed timing fixtures to prove tick/day/calendar boundaries, pause/speed behavior, deterministic independent streams, unbiased bounded range generation, and exact resume state. No guessed time constants, global RNG resource mutation, timer collection, callback queue, todo!, stub, or fake success. Root validates domain consumers in bulk rather than repeatedly building the workspace.

10. Required handoff

Report files/API, required dependencies and root state/schedule/reflection/G22 registrations, A14/A19 fields consumed, every domain RNG wrapper expected, resolved/open timing evidence, any integration seam, and allocator/timing/save validation. Root must ensure no domain retains a competing clock, pause flag, speed multiplier, calendar, or global random generator.

G33 — World environment and ambient life

1. Identity and outcome

Each loaded map presents its authored climate, time-of-day lighting, sky, fog, weather and bounded ambient fauna through ordinary Bevy entities and rendering components. Ambient birds, ground animals and water life are normal lightweight entities selected from native biome/time rules; there is no environment manager, source token state, or second rendered world. Integration wave 2. Prerequisites: A04–A08, A11, A14, A15, A19, G05, G12, G14 and G32.

2. Exclusive ownership

The worker exclusively owns game/src/plugins/environment/. Every other path is read-only. Root owns module/plugin/render-feature registration, schedule and state wiring, persistence policy, and dependency changes.

Bevy/wgpu exclusively own lights, cameras, fog resources, materials, sky/effect rendering and GPU state. G33 owns recovered environmental selection and timing policy only; it must not build a renderer, scene world, weather host, or copied presentation state.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
EnvironmentPlugin Plugin pub struct EnvironmentPlugin; Registers environment instantiation, presentation, weather and ambient-life systems only.
WorldEnvironment Component pub struct WorldEnvironment { pub definition: AssetId } Exactly one entity for the loaded map; points to A14 climate/environment records.
Daylight Component pub struct Daylight { pub fraction: f32 } Derived normalized day fraction in [0,1) from G32 clock and A14 timing policy.
Weather Component pub struct Weather { pub definition: AssetId, pub elapsed_ticks: u64, pub duration_ticks: u64 } Current validated weather state on the environment entity; duration comes from native rules.
WeatherTransition Component pub struct WeatherTransition { pub from: AssetId, pub to: AssetId, pub start_tick: u64, pub duration_ticks: u32 } Temporary authored interpolation; removed exactly at completion.
EnvironmentLight Component pub struct EnvironmentLight { pub keyframe: u16 } Marks the actual Bevy directional-light entity projected from the active native light curve.
EnvironmentSky Component pub struct EnvironmentSky { pub definition: AssetId } Marks the actual sky presentation entity/handle; no copied sky state resource.
EnvironmentFog Component pub struct EnvironmentFog; Marks the authoritative G06 camera carrying Bevy DistanceFog; fog state is not copied to a child world.
Wind Component pub struct Wind { pub direction: Vec2, pub speed_mps: f32 } Current finite world-XZ wind fact on the environment entity.
EnvironmentRandom Component pub struct EnvironmentRandom(pub DeterministicRng); G32 domain-local weather stream on the environment entity; save-relevant when weather is stochastic.
AmbientSpawner Component pub struct AmbientSpawner { pub definition: AssetId, pub next_tick: u64, pub random: DeterministicRng } One authored biome/class spawner entity; RNG is G32 domain-local state.
AmbientAnimal Component pub struct AmbientAnimal { pub definition: AssetId, pub despawn_tick: u64 } Lightweight nonsavable ambient entity using normal A15 prefab/model/animation handles.
SetWeather Message pub struct SetWeather { pub operation: Entity, pub definition: AssetId, pub transition_ticks: Option<u32> } Caller-owned operation gives exact asynchronous correlation; only native weather IDs accepted.
WeatherRequestApplied Message pub struct WeatherRequestApplied { pub operation: Entity, pub definition: AssetId } Echoed once when the requested weather becomes authoritative.
WeatherRequestRejected Message pub struct WeatherRequestRejected { pub operation: Entity, pub definition: AssetId } Echoed once for invalid/busy/rule-denied request; weather unchanged.
WeatherChanged Message pub struct WeatherChanged { pub previous: AssetId, pub current: AssetId } Emitted once after a weather transition actually commits.

A14 must expose typed EnvironmentDefinition, LightKeyframe, FogKeyframe, SkyKeyframe, WeatherDefinition, WeatherTransitionRule, AmbientSpawnDefinition, its exact AmbientClass, and their TableRange link tables, including evidence-derived limits, probabilities, biome/daylight eligibility, lifetimes, prefabs, audio and effects. A19 maps reference one environment definition. A11 owns audio playback, A18 owns effect playback, and this package attaches their natural emitter/instance components rather than forwarding through facades.

4. ECS ownership

Global map environment is one real entity because its lighting/weather facts share lifecycle. Lights, sky and fog are actual Bevy presentation entities. Each ambient rule is a spawner entity and each ambient animal a normal entity. There is no environment resource, ambient population collection, copied render state, source-document object, or global random stream.

System Schedule/set Exact query and parameters Writes Run condition/order
initialize_world_environment Update / GameSet::Intent MessageReader<WorldLoadFinished>, Commands, Res<Assets<WorldScenarioAsset>>, Res<Assets<WorldDefinitionsAsset>>, Res<ZooSeed>, ResMut<PersistentIdAllocator>, Query<(Entity,&WorldRoot)>, Query<(),With<WorldEnvironment>> For a new world only, resolves WorldRoot.scenario and creates the authoritative environment plus bounded ambient-spawner entities, each with the root WorldMember and allocator-issued PersistentId No existing environment; G22-restored worlds already contain environment/spawner facts.
hydrate_environment_presentation OnEnter(GamePhase::InGame) Commands, Query<(Entity,&WorldEnvironment,&WorldMember),Added<WorldEnvironment>>, Query<Entity,With<ZooCamera>>, Query<(),With<EnvironmentLight>>, Res<Assets<WorldDefinitionsAsset>> Creates only actual light/sky presentation entities carrying the environment's WorldMember, and attaches EnvironmentFog/Bevy fog to the authoritative camera Runs for both fresh and G22-restored environment facts; never creates simulation state.
project_daylight Update / GameSet::Presentation Res<ZooClock>, Res<Assets<WorldDefinitionsAsset>>, Query<(&mut Daylight, &WorldEnvironment, &Children)>, Query<(&mut DirectionalLight, &mut Transform), With<EnvironmentLight>>, Query<&mut DistanceFog, (With<ZooCamera>, With<EnvironmentFog>)> Interpolated Bevy light transform/color/illuminance and camera fog parameters Only when source clock crosses a native presentation sample/keyframe interval.
advance_weather FixedUpdate / FixedGameSet::Think Res<ZooClock>, Res<Assets<WorldDefinitionsAsset>>, Query<(Entity, &WorldEnvironment, &mut Weather, Option<&mut WeatherTransition>, &mut Wind, &mut EnvironmentRandom)>, Commands, MessageWriter<WeatherChanged> Current weather/transition/wind/RNG and attached A11/A18 emitters Due environment only; G32 deterministic domain stream.
apply_weather_requests FixedUpdate / FixedGameSet::Act MessageReader<SetWeather>, Res<Assets<WorldDefinitionsAsset>>, Query<(Entity,&WorldEnvironment,&Weather,Option<&WeatherTransition>)>, Commands, MessageWriter<WeatherRequestApplied>, MessageWriter<WeatherRequestRejected> Inserts validated transition or changes immediately; retains operation until transition completion and echoes exact operation once After scenario intent; invalid/busy request emits rejection without mutation.
spawn_ambient_animals FixedUpdate / FixedGameSet::Think Commands, Res<ZooClock>, Res<Assets<WorldDefinitionsAsset>>, Res<TerrainIndex>, Res<SpatialGrid>, Query<(&TerrainChunk,Option<&EditedTerrainSamples>)>, Query<(&WorldMember,&mut AmbientSpawner)>, Query<&SpatialCell> Spawns due A15 prefab with AmbientAnimal, the same WorldMember, and natural presentation/locomotion facts Due spawners only; uses G14 CSR cell ranges and respects compiled population bounds/surface rules.
despawn_expired_ambient_animals FixedUpdate / FixedGameSet::Cleanup Commands, Res<ZooClock>, Query<(Entity, &AmbientAnimal)> Despawns expired ambient entities Query filtered/indexed to ambient entities; no generic world removal path.
apply_environmental_welfare FixedUpdate / FixedGameSet::Think Res<Assets<SpeciesCatalogAsset>>, Res<Assets<WorldDefinitionsAsset>>, Query<(&WorldEnvironment,&Weather,&Daylight),Or<(Changed<Weather>,Changed<Daylight>)>>, Query<(Entity,&SpeciesHandle,&HabitatMember),With<Animal>>, MessageWriter<AdjustEnvironment> Emits exact G12 AdjustEnvironment { animal, delta_q16 } messages only Only if evidence proves a gameplay effect; residents are grouped by habitat/environment and G12 is never mutated directly.
remove_world_environment OnExit(GamePhase::InGame) Commands, Query<Entity,Or<(With<WorldEnvironment>,With<EnvironmentLight>,With<EnvironmentSky>,With<AmbientSpawner>,With<AmbientAnimal>)>> Despawns environment/presentation/spawner/ambient entities Before world teardown completes; G04 world unload remains an idempotent backstop through WorldMember.

5. Preflight producer contract

Not applicable: the engine consumes only native assets produced before launch. A14 preflight resolves environment maps, light rigs, fog, sky, weather, ambient manager documents and source precedence into fixed typed records; A19 selects the final environment. This plugin never parses XML, Z2F, original render records or source weather tokens.

6. Behavioral evidence and disposition

Search shipped environment maps, light rigs, sky/fog/weather records, biome ambient documents, ambient species/prefabs and sound/effect references. Search the oracle and analysis maps for environment, lighting, time-of-day, weather and ambient-AI classes/functions. Mine commit d5657e5a only for evidence in features/presentation/environment.rs, features/simulation/ambient.rs, terrain, audio and world rendering; its strings, managers, defaults and copied runtime state are not reusable. Live comparison settles keyframe interpolation, light direction/color, fog/sky layering, weather cadence, and ambient placement.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

Evidence gates: complete environment/light/fog/sky source vocabulary; day-fraction mapping and interpolation; weather types/transitions/gameplay effects; wind; ambient classes, eligible biomes/day periods, population bounds, spawn attempts, lifetimes, movement and save exclusion. The old code's fallback constants are not evidence and must not be copied.

7. Required behavior

8. Performance contract

Settled systems allocate zero. Daylight work is O(environment presentation entities) only at relevant clock changes. Weather is O(active environments), and ambient spawning O(due spawners × bounded attempts) with O(1) terrain lookup and bounded spatial candidates. No per-frame strings, keyframe vectors, whole-world queries, source parsing, asset-payload clone, or environment graph. Spawn ECS allocation is an explicit bounded transition; A04–A08/A11/A18 own mapped/GPU/ audio storage. Regression checks cover unchanged presentation, weather tick, failed ambient attempt, expiry and population cap while tracking heap, mapped pages, GPU bytes and audio buffers separately.

9. Acceptance criteria

Deliver ordinary formatted Rust under the owned directory exporting every symbol and focused system above. Evidence-backed fixtures prove keyframe interpolation, weather transition uniqueness, terrain-class placement, day/biome eligibility, deterministic bounded spawning, population cap, expiry and cleanup. Observable completion is a representative map matching original lighting/sky/fog/weather while ambient life appears and disappears authentically. No guessed defaults, manager, copied environment resource, global RNG, source token dispatcher, placeholder weather, todo!, or fake effect.

10. Required handoff

Report files/API, root Bevy render/schedule/reflection/G22 registrations, A03/ A04–A08/A11/A14/A15/A18/A19 and G04/G05/G12/G14/G32 symbols, resolved/open environment evidence, temporary seams, and allocator/page/GPU/audio/visual validation. Root must remove any overlap with terrain presentation while preserving G05 ownership of terrain data/editing and G33 ownership of map-wide environment presentation.

G34 — Display and graphics settings

1. Identity and outcome

The source-driven options screen changes native window, presentation quality and UI scale through ordinary Bevy state. Audio settings remain A11's AudioMixSettings; input bindings remain G03's InputBindings; G22 persists all three natural owners. This package does not create a settings host or copy engine state. Integration wave 2. Prerequisites: G01, G02 and G06 plus the prepared presentation contracts from A04–A08, A16 and A18.

2. Exclusive ownership

The worker exclusively owns reconstruction/game/src/plugins/settings/. Every other path is read-only. Root owns module/plugin registration, schedule ordering and G01/G02/G22 wiring.

Bevy window/UI/audio/input and Bevy/wgpu render resources are the only settings targets. G34 validates policy and applies changed values directly; it must not introduce a platform window backend, graphics abstraction, settings host, or mirrored renderer/device state.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
SettingsPlugin Plugin pub struct SettingsPlugin; Registers validation and changed-only application systems.
DisplayMode enum Windowed, BorderlessFullscreen Closed portable mode vocabulary.
FramePacing enum VSync, Immediate, Mailbox Maps to a supported Bevy/wgpu present mode; unsupported choices reject.
DisplaySettings Resource pub struct DisplaySettings { pub width:u32, pub height:u32, pub mode:DisplayMode, pub pacing:FramePacing, pub ui_scale_permille:u16 } Sole live display policy; validated resolution and UI scale 500..=2000.
ShadowQuality enum Off, Low, High Selects pre-existing pipeline/resource policy; never compiles shaders.
EffectQuality enum Low, High Selects A18 compiled capacity/LOD policy.
GraphicsSettings Resource pub struct GraphicsSettings { pub msaa_samples:u8, pub shadows:ShadowQuality, pub water_reflections:bool, pub effects:EffectQuality } Sole live quality policy; MSAA is exactly 1, 2, 4, or 8 when supported.
GraphicsCapabilities Resource pub struct GraphicsCapabilities { pub msaa_mask:u16, pub present_mode_mask:u8, pub shadows:bool, pub water_reflections:bool } Immutable hardware/surface facts populated once from Bevy's render adapter and primary surface; not settings or copied render state.
SetDisplaySettings Message pub struct SetDisplaySettings(pub DisplaySettings); One complete proposed display policy from G01/G02.
SetGraphicsSettings Message pub struct SetGraphicsSettings(pub GraphicsSettings); One complete proposed quality policy.
SettingsApplied Message pub struct SettingsApplied { pub display:bool, pub graphics:bool } Emitted once after actual accepted changes.
SettingsRejected Message pub struct SettingsRejected { pub reason:SettingsFailure } Rejection leaves both live resources unchanged.
SettingsFailure enum InvalidResolution, InvalidScale, UnsupportedPresentMode, UnsupportedMsaa, UnsupportedFeature Fixed localizable failure vocabulary.

G01 owns options-screen navigation, G02 owns UI projection, G22 owns durable profile I/O, A11 owns mix volumes, and G03 owns bindings. This package consumes those owners directly and must not aggregate them into another options object.

4. ECS ownership

The two resources are genuinely process-global presentation policy. Actual window, camera and renderer objects remain Bevy-owned.

System Schedule/set Exact query and parameters Writes Run condition/order
validate_display_settings Update / GameSet::Intent MessageReader<SetDisplaySettings>, ResMut<DisplaySettings>, Query<&Window,With<PrimaryWindow>>, MessageWriter<SettingsApplied>, MessageWriter<SettingsRejected> Replaces display policy only after full capability/range validation Requests only; before apply.
validate_graphics_settings Update / GameSet::Intent MessageReader<SetGraphicsSettings>, ResMut<GraphicsSettings>, Res<GraphicsCapabilities>, MessageWriter<SettingsApplied>, MessageWriter<SettingsRejected> Replaces quality policy only after complete device validation Requests only; before apply.
apply_window_settings Update / GameSet::Presentation Res<DisplaySettings>, Query<&mut Window,With<PrimaryWindow>> Bevy WindowResolution, WindowMode and PresentMode resource_changed::<DisplaySettings> only.
apply_camera_render_settings Update / GameSet::Presentation Commands, Res<GraphicsSettings>, Query<Entity,With<ZooCamera>> Actual Bevy camera Msaa component Graphics settings changed; no camera copy.
apply_ui_scale Update / GameSet::Ui Res<DisplaySettings>, Query<&mut Transform,(With<UiDocumentRoot>,Or<(Added<UiDocumentRoot>,Changed<Transform>)>)> Root UI transform scale derived from permille Display settings changed or a UI root was added.
apply_shadow_quality Update / GameSet::Presentation Res<GraphicsSettings>, Query<&mut DirectionalLight,With<EnvironmentLight>> Existing Bevy directional-light shadow enablement/quality fields only Graphics settings changed only; no source/pipeline compilation.

5. Preflight producer contract

Not applicable: the engine consumes only native assets produced before launch. Target-profile texture/shader/codec decisions remain preflight-owned. Runtime settings only choose among capabilities and variants already compiled into the pack. A18's pooled effect systems and A16/G25 water presentation read changed GraphicsSettings directly in their natural owner; G34 does not proxy or copy those facts. active profile.

6. Behavioral evidence and disposition

Search shipped options UI/XML, default option records and localization; search the C++ oracle/analysis for display, resolution, fullscreen, antialiasing, shadow, reflection and graphics-option functions. Mine commit d5657e5a only for already-discovered option vocabulary and UI behavior. Live original behavior settles apply/cancel confirmation, defaults and which changes are immediate.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

The original option set and ranges are evidence gates. OpenZT2 may expose modern resolution, frame-pacing and controller choices, but they must be labeled as extensions and capability-validated rather than presented as recovered rules.

7. Required behavior

8. Performance contract

Idle settings cost is zero systems through change run conditions, zero allocations and zero filesystem work. Applying a change is O(one window + active cameras + affected presentation entities), occurs only on request, and reuses prepared pipelines/GPU resources. Record any unavoidable swapchain or render- target recreation separately from Rust heap, mapped pages and steady GPU bytes.

9. Acceptance criteria

Deliver ordinary formatted Rust under the owned directory with every symbol and system above. Narrow tests cover range/capability rejection, atomic unchanged state, accepted window mapping, changed-only application and persistence round- trip handoff. No settings manager, aggregate options host, renderer wrapper, runtime source parsing, shader compilation, todo!, stub or fake success.

10. Required handoff

Report files/API, exact Bevy 0.19 window/UI/MSAA APIs used, root registrations, G01/G02/G06/G22 and A04–A08/A11/A16/A18 symbols, evidence questions, temporary API-name seams, and allocator/swapchain/GPU/visual validation. Root must erase any upstream-name shim and preserve A11/G03 as the sole audio/input authorities.

G35 — Native content mapping and residency

1. Identity and outcome

The game opens and validates exactly one A01-resolved native profile, maps it read-only once, exposes its byte ranges through Bevy's asset source/loaders, and applies the selected PreloadMode without creating a heap copy. Off performs no eager page touches, Core greedily faults the core gameplay closure, and AllEnabled greedily faults the entire selected/resolved profile, all within a safe available-memory budget. Integration wave 1. Prerequisites: frozen native pack transport and the launcher-supplied profile path/mode.

2. Exclusive ownership

The implementation lives in reconstruction/game/src/plugins/content/. The superseded game/src/content.rs is available only through Git history; it is not a second content implementation. Crate/plugin registration and the small native-pack API extension named below are central integration concerns. No family loader or gameplay plugin is owned by this package.

Existing memmap2 and rkyv are the mandatory mapping/archive implementation. G35 may add page-fault policy and Bevy asset-source integration only. It must not copy the mapping into Arc<[u8]>, decode a second content graph, introduce an asset store, or add another filesystem/cache layer.

3. Frozen public contract

Symbol Kind Exact shape or signature Owner and invariant
ContentPlugin Plugin pub struct ContentPlugin { profile:Arc<MappedPack>, preload:PreloadMode }; pub fn open(path:impl AsRef<Path>, expected_id:AssetId, expected_target:TargetProfile, preload:PreloadMode)->io::Result<Self> Sole game startup owner of opening/validating the launcher-selected resolved mapping; expected values come from A01's frozen arguments.
ContentMap Resource pub struct ContentMap(pub Arc<MappedPack>); One immutable mapped profile retained only so Bevy family loaders/asset source share its lifetime; never decoded content state.
ContentReady Message pub struct ContentReady { pub assets:u32, pub mapped_bytes:u64, pub prefaulted_bytes:u64, pub mode:PreloadMode } Emitted once after asset-source registration and requested eager touches complete.
ContentLoadFailed Message pub struct ContentLoadFailed { pub reason:ContentFailure } Boot remains blocked; no fallback source path.
ContentFailure enum Open, InvalidPack, WrongProfile, WrongTarget, InsufficientBootstrapMemory, AssetSourceRegistration Fixed startup failures.
ResidencyBudget value pub struct ResidencyBudget { pub available_bytes:u64, pub safety_headroom_bytes:u64, pub touch_budget_bytes:u64 } Snapshot of startup OS memory facts; not retained live state after preload.
PreloadReport value pub struct PreloadReport { pub requested:PreloadMode, pub touched_bytes:u64, pub skipped_bytes:u64, pub major_faults:u64, pub minor_faults:u64 } Startup diagnostics only, then emitted/recorded by performance tooling.
memory_budget function pub fn memory_budget(total_bytes:u64, available_bytes:u64)->ResidencyBudget headroom=max(total_bytes/8, 2_GiB) and touch budget is available-headroom, saturating; explicit OpenZT2 policy.
preload_groups function pub fn preload_groups(mode:PreloadMode)->&'static [LoadGroup] Off=[]; Core=[Bootstrap,Menu,Zoo,Resident]; AllEnabled=[Bootstrap,Menu,Zoo,Resident,OnDemand,Stream].

Root extends MappedPack once with pub fn pack_id(&self) -> AssetId, pub fn target_profile(&self) -> TargetProfile and pub fn prefault_budgeted(&self, groups:&[LoadGroup], budget_bytes:u64)->u64. The first two expose already-validated header values so startup can reject the wrong resolved profile or target without re-reading its header. The prefault function walks final catalogue order and touches at most the requested number of 4-KiB payload bytes, including a partial final asset when needed. It allocates nothing, retains nothing, and cannot see original pack/DLC/mod layers.

4. ECS ownership

ContentMap is genuine immutable global ownership of the mapping used by every typed loader. No asset catalogue, decoded record graph, residency mirror, byte cache or background content world is retained.

System Schedule/set Exact query and parameters Writes Run condition/order
install_content_source plugin build owned Arc<MappedPack>, App::register_asset_source, Commands/resource insertion equivalent Bevy asset source backed by mapped slices and one ContentMap Before any family loader or GamePhase::Boot system.
prefault_content Startup / GameSet::Diagnostics Res<ContentMap>, plugin PreloadMode, OS sysinfo(2) memory snapshot, MessageWriter<ContentReady>, MessageWriter<ContentLoadFailed> Page touches only plus one report/message Once; after source installation and before shell bootstrap requests.

The asset reader returns borrowed SliceReader views into MappedPack; family loaders acquire MappedAsset owners from the same Arc. Bevy Handle<T> and Assets<T> remain the only asset lifecycle/cache authority.

5. Preflight producer contract

Not applicable: the engine consumes only the final native profile. A01 and the family producers have already resolved overrides, dependencies, target formats and LoadGroup. AllEnabled therefore means every asset in the selected final profile, never unselected source archives.

6. Behavioral evidence and disposition

Use the current native/src/pack.rs, converter/src/pack.rs, allocation/mapping regressions and Linux page-fault measurements. Commit d5657e5a may identify previous startup asset demand but contributes no architecture. The original game is not authority for the OpenZT2 preload improvement.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

Evidence questions are limited to measuring the headroom default on Jarvis and representative lower-memory machines. A policy change alters one pure formula; it cannot add prediction caches, source-pack semantics or a content manager.

7. Required behavior

8. Performance contract

Map/open validation is O(catalogue); prefault is O(pages touched); lookup remains the native pack's allocation-free binary lookup. Heap growth is one Arc/Bevy registration only, never proportional to payload bytes. Regression checks measure wall time, minor/major faults and /proc/<pid>/smaps_rollup mapped/RSS separately from Rust heap. After ContentReady, game-content open/read/pread syscalls are forbidden and settled asset-handle cloning allocates zero.

9. Acceptance criteria

Deliver ordinary formatted Rust under the owned directory with every symbol and system above. Narrow tests cover pure budget/group selection, Off zero touches, bounded partial Core/AllEnabled touches and one shared mapping owner. Root bulk checks syscall/page-fault/allocation contracts. No copied pack bytes, asset store, runtime converter, layer merge, host, manager, prediction graph, todo!, stub or fake ready signal.

10. Required handoff

Report files/API, root deletion of old content.rs, native pack_id, target_profile and prefault_budgeted additions, asset-source/plugin ordering, profile/target checks, OS memory API, A01 launch arguments, family-loader use, and exact heap/mapping/ page-fault/syscall results. Any temporary source-name shim is removed during destructive integration.

G36 — Maintenance and Sanitation

1. Identity and outcome

This package makes deterioration, full waste containers, and loose litter real zoo facts. Placed objects wear only according to converted authored rules, completed services add only their authored wear and waste, maintenance staff receive deduplicated repair/empty/sweep work, and successful work mutates the small target component directly. Guests can observe litter, the information UI can present the selected fact, progression receives one changed-only cleanliness value, and native saves restore the same condition and waste.

These responsibilities form one natural greenfield-Bevy domain: the facts that make an object broken or dirty, the events that change those facts, and the three staff effects that resolve them. There is no maintenance manager, dirty entity bag, copied facility model, or package-local world.

Integration wave: 3. Logical prerequisites: A03 species, A14 world definitions, G04 world spawn and persistent identity, G09 habitats, G10 placement, G11 animals, G18 services, G20 staff jobs, and G32 simulation time. G21, G22, and G23 consume the resulting facts after root integration.

2. Exclusive ownership

The worker exclusively owns reconstruction/game/src/plugins/maintenance/. Every other repository path is read-only. Root owns crate declarations, plugin registration, schedule ordering, reflection, dependencies, asset registration, and the adjacent contract corrections listed below. A collision is reported; it never expands the worker's ownership.

3. Frozen public contract

All amounts and thresholds are exact native integer units. Permille values are always 0..=1000. Only this package mutates Condition, WasteContainer, or Litter during normal gameplay; G22 may insert their validated saved values.

Symbol Kind Exact shape or signature Owner and invariant
MaintenancePlugin Plugin pub struct MaintenancePlugin; Registers maintenance facts, their component hooks, focused mutation/job systems, and the derived cleanliness projection only.
Condition Component #[repr(transparent)] pub struct Condition(pub u16); Current structural condition permille: 1000 is fully sound and 0 is exhausted. Present only when A14 has an authored maintenance definition.
WasteContainer Component pub struct WasteContainer { pub units: u16, pub capacity: u16 } Bounded contained waste; units <= capacity, and capacity is the nonzero immutable A14 value for this entity definition.
Litter Component pub struct Litter { pub amount_units: u16 } Positive guest/facility loose-waste amount on a dedicated world entity. Every live litter entity also has DefinitionId, Transform, WorldMember, and PersistentId.
HabitatWaste Component pub struct HabitatWaste { pub amount_units: u16 } Positive animal waste on a dedicated world entity cleaned by CleanHabitat; it carries the same identity/world/transform facts as litter.
WasteProduction Component pub struct WasteProduction { pub next_tick: u64 } Per-animal G32 deadline derived from its A03 species rule; saved so load cannot duplicate or skip production.
ZooCleanliness Resource #[repr(transparent)] pub struct ZooCleanliness(pub u16); Derived zoo cleanliness permille. It is the sole G23 cleanliness input, changes only when a contributing live fact changes, and is rebuilt rather than saved.

Root must freeze these exact additions in A14 rather than allowing the worker to define a second asset schema:

The A14 producer rejects permille overflow, zero capacity paired with nonzero waste rules, empty_at_units > waste_capacity_units, duplicate service IDs within one definition, nonzero loose-litter output without a resolved litter definition, zero total cleanliness weight, or zero litter reference when its weight is nonzero. A source object without evidenced maintenance behavior has no MaintenanceDefinition; the converter must not synthesize defaults.

Root must also add G20 pub struct StaffJobCancelled { pub kind: StaffJobKind, pub target: Entity } as a message emitted before cancellation removes a still-live target's job. This is a natural job-lifecycle fact used to re-request unresolved work, not a maintenance facade. G20 remains the sole owner of StaffJobRequest, StaffJob, JobClaim, JobProgress, and StaffJobCompleted.

4. ECS ownership

Condition, WasteContainer, Litter, HabitatWaste, and WasteProduction are the authoritative per-entity facts. The plugin privately owns exactly CleanlinessTotals { condition_sum:u64, condition_count:u32, waste_units:u64, waste_capacity:u64, litter_units:u64, dirty:bool } as a resource. Component add/remove hooks add or subtract their current contribution, including direct G10 deletion and G04 world unload. Each mutation system replaces its old and new contribution in the same call that changes the component. The totals are a bounded derived reduction, not an entity registry or parallel world, and never retain entity IDs. Loose-waste units are the checked sum of Litter and HabitatWaste amounts.

MaintenancePlugin::build registers on_add/on_remove hooks for Condition, WasteContainer, Litter, and HabitatWaste. Each hook reads only the affected component through Bevy's DeferredWorld, mutates CleanlinessTotals, and marks dirty; it performs no query, allocation, entity lookup table, or deferred event. Systems that mutate an existing component borrow ResMut<CleanlinessTotals> and apply the old→new delta in the same call.

Four private zero-sized components—RepairWork, EmptyBinWork, SweepLitterWork, and CleanHabitatWork—classify newly added G20 job entities once. They contain no state and exist solely so the three execution systems have disjoint archetypal queries instead of rescanning or write-locking every StaffJob.

For condition, an empty population scores 1000; otherwise the score is the nearest-integer condition_sum / condition_count. Waste scores 1000 when total capacity is zero and otherwise 1000 - min(1000, round(waste_units * 1000 / waste_capacity)). Litter scores 1000 - min(1000, round(litter_units * 1000 / litter_reference_units)). ZooCleanliness is the nearest-integer weighted mean of those three scores using the A14 policy. Intermediate arithmetic uses checked u64; invalid archive bounds never reach the engine.

System Schedule/set Exact query and parameters Writes Run condition/order
initialize_maintenance_facts FixedUpdate / FixedGameSet::Act Commands; Res<Assets<WorldDefinitionsAsset>>; Query<(Entity,&DefinitionId),(Added<DefinitionId>,Without<Condition>)> Inserts authored Condition and, when capacity is nonzero, WasteContainer { units:0, capacity } In game, after G04/G10 entity creation; missing maintenance definition is a valid no-op.
deteriorate_maintainable_objects FixedUpdate / FixedGameSet::Think MessageReader<ZooDayAdvanced>, Res<Assets<WorldDefinitionsAsset>>, Query<(&DefinitionId,&mut Condition)>, ResMut<CleanlinessTotals> Saturating condition loss and exact aggregate delta Once per crossed zoo day, multiplying the authored per-day loss by current_day - previous_day with checked arithmetic; never per frame.
apply_service_maintenance_effects FixedUpdate / FixedGameSet::Act Commands, MessageReader<ServiceCompleted>, Res<Assets<WorldDefinitionsAsset>>, ResMut<PersistentIdAllocator>, Query<(&DefinitionId,&Transform,&WorldMember,Option<&mut Condition>,Option<&mut WasteContainer>)>, ResMut<CleanlinessTotals> Authored condition/waste deltas; zero or one litter entity per completion with DefinitionId, transformed authored offset, WorldMember, and synchronously allocated PersistentId After G18 service completion and before maintenance-request systems; an effect is applied only when both facility definition and exact service ID match.
initialize_waste_production FixedUpdate / FixedGameSet::Act Commands, Res<ZooClock>, Res<Assets<SpeciesCatalogAsset>>, Query<&SpeciesHandle,(Added<SpeciesHandle>,Without<WasteProduction>)> Inserts the first exact WasteProduction deadline only for a positive A03 rule After G11 animal creation; no random/default cadence.
produce_animal_waste FixedUpdate / FixedGameSet::Act Commands, Res<ZooClock>, Res<Assets<SpeciesCatalogAsset>>, ResMut<PersistentIdAllocator>, Query<(&SpeciesHandle,&GlobalTransform,&WorldMember,&mut WasteProduction),With<Animal>> At a due tick spawns one HabitatWaste entity with the species waste DefinitionId, transform, same WorldMember, fresh PersistentId, and advances the checked deadline Due animals only; validates/allocates before spawn and never loops once per missed tick after a large time jump.
request_repairs FixedUpdate / FixedGameSet::Think Res<Assets<WorldDefinitionsAsset>>; Query<(Entity,&DefinitionId,&Condition),Changed<Condition>>; MessageWriter<StaffJobRequest> One Repair request candidate Emits only while condition is strictly below the authored threshold; G20 deduplicates (kind,target).
request_bin_emptying FixedUpdate / FixedGameSet::Think Res<Assets<WorldDefinitionsAsset>>; Query<(Entity,&DefinitionId,&WasteContainer),Changed<WasteContainer>>; MessageWriter<StaffJobRequest> One EmptyBin request candidate Emits only at or above the authored nonzero empty threshold; G20 deduplicates (kind,target).
request_litter_sweeping FixedUpdate / FixedGameSet::Think Query<(Entity,&Litter),Added<Litter>>; MessageWriter<StaffJobRequest> One SweepLitter request candidate Positive validated amount only; G20 deduplicates (kind,target).
request_habitat_cleaning FixedUpdate / FixedGameSet::Think Query<(Entity,&HabitatWaste),Added<HabitatWaste>>, MessageWriter<StaffJobRequest> One CleanHabitat request candidate Positive validated amount only; G20 deduplicates (kind,target).
requeue_cancelled_maintenance_work FixedUpdate / FixedGameSet::Think MessageReader<StaffJobCancelled>, Query<&Condition>, Query<&WasteContainer>, Query<&Litter>, Query<&HabitatWaste>, Res<Assets<WorldDefinitionsAsset>>, MessageWriter<StaffJobRequest> Replacement request only if the direct target fact still meets its rule After G20 cancellation and before job creation; dead or already-resolved targets produce nothing.
classify_maintenance_jobs FixedUpdate / FixedGameSet::Think Commands, Query<(Entity,&StaffJob),Added<StaffJob>> Inserts exactly one private RepairWork, EmptyBinWork, SweepLitterWork, or CleanHabitatWork marker on the matching G20 job entity After G20 job creation and before claim/execution; every other job is untouched.
advance_repair_jobs FixedUpdate / FixedGameSet::Act Query<(Entity,&StaffJob,&JobClaim,&mut JobProgress),(With<RepairWork>,Without<EmptyBinWork>,Without<SweepLitterWork>,Without<CleanHabitatWork>)>, Query<&StaffRole>, Query<(&DefinitionId,&mut Condition)>, Res<Assets<WorldDefinitionsAsset>>, ResMut<CleanlinessTotals>, MessageWriter<StaffJobCompleted> Decrements remaining ticks; at zero applies the resolved A14 StaffJobEffect::Repair directly and emits one completion After G20 begin_staff_job; no G20 repair execution system runs in parallel. Preflight and job start reject a missing/incompatible effect rather than admitting an unexecutable job.
advance_empty_bin_jobs FixedUpdate / FixedGameSet::Act Query<(Entity,&StaffJob,&JobClaim,&mut JobProgress),(With<EmptyBinWork>,Without<RepairWork>,Without<SweepLitterWork>,Without<CleanHabitatWork>)>, Query<&StaffRole>, Query<&mut WasteContainer>, Res<Assets<WorldDefinitionsAsset>>, ResMut<CleanlinessTotals>, MessageWriter<StaffJobCompleted> Decrements remaining ticks; at zero applies resolved StaffJobEffect::Clean { amount }, clamped at zero, and emits one completion After job start and before G20 release. Zero amount is rejected by A14 preflight.
advance_sweep_litter_jobs FixedUpdate / FixedGameSet::Act Commands, Query<(Entity,&StaffJob,&JobClaim,&mut JobProgress),(With<SweepLitterWork>,Without<RepairWork>,Without<EmptyBinWork>,Without<CleanHabitatWork>)>, Query<&StaffRole>, Query<&mut Litter>, Res<Assets<WorldDefinitionsAsset>>, ResMut<CleanlinessTotals>, MessageWriter<StaffJobCompleted> Decrements remaining ticks; at zero applies resolved Clean { amount }, despawns litter at zero, then emits one completion After job start and before G20 release; target despawn is through deferred commands after aggregate subtraction.
advance_clean_habitat_jobs FixedUpdate / FixedGameSet::Act Commands, Query<(Entity,&StaffJob,&JobClaim,&mut JobProgress),(With<CleanHabitatWork>,Without<RepairWork>,Without<EmptyBinWork>,Without<SweepLitterWork>)>, Query<&mut HabitatWaste>, Res<Assets<WorldDefinitionsAsset>>, ResMut<CleanlinessTotals>, MessageWriter<StaffJobCompleted> Decrements remaining ticks; at zero applies resolved Clean { amount }, despawns exhausted waste, then emits one completion After job start and before G20 release; no G20 sanitation executor runs in parallel.
project_zoo_cleanliness FixedUpdate / FixedGameSet::Economy Res<Assets<WorldDefinitionsAsset>>, ResMut<CleanlinessTotals>, ResMut<ZooCleanliness> New permille only when CleanlinessTotals.dirty and the computed value differs; clears dirty After all maintenance mutation systems and component hooks; G23 reads it later in the same set/order.

Job urgency is the direct severity in the target's own unit: repair uses 1000 - condition, emptying uses rounded container fullness permille, and sweeping uses min(amount_units, 1000). There is no priority tier table or package-local queue. G20's deterministic job selection combines this request urgency with the authored StaffJobDefinition.priority it already owns.

5. Preflight producer contract

Not applicable: the engine consumes only native assets produced before launch. This gameplay package adds no filesystem discovery, Z2F/XML parsing, source precedence, decompression, or conversion dependency. The A14-owned producer changes requested in section 3 are performed under A14 ownership.

6. Behavioral evidence and disposition

Search shipped entity/component/task definitions under ignored vendor/ for trash containers/cans, recycling bins, loose trash, repair tasks, facility-use mutations, maintenance-worker requests, and condition/status attributes. Search the C++ oracle and analysis tables for ZTAIStaffMgr, maintenance-worker task selection, SweepTrash, EmptyTrash, EmptyRecyclingBin, trash quantity, facility condition, and cleanup events. Concrete starting points include docs/analysis/function-perimeter.tsv entries for populate_trash_level_display, maintenance-worker panels, and maintenance filter handlers; docs/analysis/global-perimeter.tsv trash-quantity and maintenance-worker bindings; and docs/analysis/rtti-classes.tsv staff cleanup types. Observe the original for threshold crossing, job cancellation/retry, litter placement/stacking, repair amount, and rating response.

Mine commit d5657e5a only for already-discovered behavior/evidence in the historical reconstruction/domain/src/simulation/facility.rs and reconstruction/domain/src/simulation/staff.rs paths and their former feature callers. Those files do not exist in the current workspace. Their dynamic maps, parsed strings, managers, and runtime contracts are not implementation material.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

Evidence gates before implementation: confirm which object families possess condition; exact deterioration cadence and scale; service-specific condition, contained-waste, and loose-litter effects; repair/empty thresholds and amounts; whether loose litter coalesces or remains individual; its authored transform and visual definition; guest reaction distance; and the exact original cleanliness weighting/reference. If original deterioration is not zoo-day based, report that contract conflict to root and freeze the recovered G32-tick cadence before coding. Never substitute guessed constants, generic defaults, or asset-instance hardcoding.

7. Required behavior

8. Performance contract

All settled systems and component hooks allocate zero. Service work is O(completed services), request work is O(changed facts), one-time job classification is O(new jobs), and active staff execution is O(active jobs of the queried class) through disjoint marker archetypes. Scheduled deterioration is O(maintainable objects) only on a zoo-day boundary, never a per-frame world scan. Cleanliness update and component add/remove are O(1) per fact and retain no entity map. G20's existing job entities are the sole job queue.

There is no per-frame asset clone, string construction, hash growth, source access, generic dirty collection, or copied definition data. Litter growth is explicit ECS gameplay state, never a hidden queue or retained duplicate; integer and persistent-ID exhaustion reject the originating mutation rather than wrapping. Allocator regression coverage measures a quiet maintenance tick, one service effect without litter, request deduplication, each active job step after warm-up, and cleanliness projection at zero heap allocations. Litter spawn/despawn allocation is measured separately as an intentional ECS structural operation. This package maps no pages itself and owns no GPU resource; mapped A14 lookup and handle cloning remain allocation free.

9. Acceptance criteria

Deliver ordinary formatted Rust under the owned maintenance subtree exporting exactly MaintenancePlugin, Condition, WasteContainer, Litter, HabitatWaste, WasteProduction, and ZooCleanliness. Implement the listed component hooks and focused systems without a manager, registry, runtime source document, generic property bag, duplicate staff queue, or retained entity summary. No todo!, unimplemented!, silent stub, fake completion, guessed rule, include_str! source assembly, or generated Rust concatenation is accepted.

Focused tests cover component bounds, service applied once, saturation, threshold request generation, cancelled-job requeue, stale-target rejection, repair/empty/sweep/clean-habitat mutation, litter and animal-waste lifecycle identity/deadline, aggregate add/remove, and exact integer cleanliness rounding. Observable done: facilities deteriorate and accumulate authored waste, litter appears as ordinary world entities, maintenance staff repair/empty/sweep it, guests/UI/rating see the live result, and save/load restores it. Facility commerce, staff assignment, guest reaction scoring, persistence I/O, and rendering the referenced litter prefab remain owned by their natural adjacent packages.

10. Required handoff

Report files/API, evidence values and unresolved gates, no new Cargo needs (or the exact unavoidable dependency), plugin/message/component/resource registrations, schedule ordering, component-hook registration, and consumed A03/A14/G04/G10/G11/G17/G18/G20/G32 symbols. Report allocation counts for quiet, service, and active-job paths; litter structural allocation separately; no mapped/GPU ownership.

Root integration must make these exact cross-file changes:

  1. Add A03's evidenced animal-waste fields and the A14 records, archive tables/accessors, DefinitionFamily route, conversion/validation, and contract tests specified in section 3.
  2. Add/register/emit G20 StaffJobCancelled; remove EmptyBin and SweepLitter/CleanHabitat execution from G20, remove Repair from its maintenance executor, and order G36's four direct-fact executors before G20 release_finished_jobs. MaintainTank and OperateShow remain with their natural owners.
  3. Replace G10 ownership references to condition/waste in G20, G21, and G23 with G36. G10 remains placement/footprint ownership only.
  4. Extend A02 ScalarBindingSource with FacilityWasteUnits, FacilityWasteCapacity, LitterAmountUnits, HabitatWasteAmountUnits, and ZooCleanlinessPermille; make G21 project G36 facts directly and add the evidenced G17 litter reaction system without a copied view model.
  5. Make G23 recompute_cleanliness_rating consume changed Res<ZooCleanliness> directly and remove its placed-object scan.
  6. Insert Maintenance after Placement in G22 SnapshotSection; add deterministic write/prevalidate/apply coverage for placed-object Condition/WasteContainer, litter/habitat-waste PersistentId, DefinitionId, transform, amount/root relation, and animal WasteProduction. Validation occurs before world mutation. Do not serialize private totals or ZooCleanliness.
  7. Add the module/plugin to the root plugin group and align the manifest, integration wave, public-symbol audit, and final destructive cleanup. No temporary adapter or interface introduced solely for this package survives.

G37 — Interactive physics and attachments

1. Identity and outcome

Physical enrichment props, carried and thrown objects, ropes, authored attachments, collision-driven interactions, and fitting surfaces use Avian's native Bevy ECS physics. OpenZT2 supplies recovered game semantics around that solver; it does not contain a physics engine. Integration wave 3. Prerequisites: A14, A15, A17, G04, G05, G10, G13–G15, and G32.

2. Exclusive ownership and mandatory dependency

The package worker owns game/src/plugins/physics/ only. Root adds avian3d = 0.7 with the minimum 3D/f32/Parry features, registers PhysicsPlugins on the fixed schedule, sets world scale/substeps, and orders OpenZT2 systems against PhysicsSystems.

Avian exclusively owns rigid-body integration, collider representation, broad/narrow phase, the contact graph, solver rows, restitution/friction, sleeping, continuous collision detection, joints, constraints, and spatial queries. The worker must not create PhysicsBody, ColliderShape, PhysicsVelocity, PhysicsLayer, PhysicsSleeping, PhysicsScratch, a custom solver, a body registry, or a mirrored physics world. If Avian misses a measured requirement, report it for configuration or a narrow fork; do not restore the bespoke design archived at commit 68a4c481.

3. Frozen OpenZT2 contract

Avian components such as RigidBody, Collider, LinearVelocity, AngularVelocity, CollisionLayers, Friction, Restitution, Sleeping, FixedJoint, DistanceJoint, CollisionEventsEnabled, and external force/impulse components are used directly and are not wrapped.

Symbol Kind Exact shape or signature Owner and invariant
ZooPhysicsPlugin plugin unit struct implementing Plugin Registers only OpenZT2 conversion, interaction, contact-fact, fitting and cleanup systems.
PhysicsMaterialId component pub struct PhysicsMaterialId(pub AssetId); Authored A14 material identity; Avian friction/restitution components hold live solver values.
CarriedBy component { carrier:Entity, socket:AssetId } Domain relationship; carried bodies use evidenced Avian body/sensor state.
AttachedTo component { parent:Entity, socket:AssetId, local:Transform, policy:AttachmentPolicy } Domain attachment intent; rigid physical attachment uses an Avian joint rather than duplicate transform integration.
AttachmentPolicy enum Rigid, FollowPosition, FollowPositionAndYaw Closed recovered vocabulary.
FittingSurface component { kind:FittingSurfaceKind, normal_tolerance:f32 } OpenZT2 placement/fitting policy, not collision geometry.
FittingSurfaceKind enum Terrain, Tile, Entity Closed authored surface class.
ApplyPhysicsImpulse message { entity:Entity, impulse_ns:Vec3, point_world:Vec3 } Converted once into Avian's impulse component.
AttachPhysicsObject message { object:Entity, parent:Entity, socket:AssetId, policy:AttachmentPolicy } Validated domain request; creates the natural Avian joint or carried relation.
DetachPhysicsObject message { object:Entity, inherited_velocity:Vec3 } Removes relation/joint once and restores evidenced body state.
PhysicsContactFact message { a:Entity, b:Entity, point:Vec3, normal:Vec3, impulse_ns:f32, kind:ContactFactKind } Emitted only for authored gameplay consumers; not a copy of every Avian contact.
ContactFactKind enum Impact, Enter, Exit Fixed gameplay vocabulary.
PhysicsInteractionFailed message { entity:Entity, reason:PhysicsFailure } Explicit invalid request.
PhysicsFailure enum MissingBody, InvalidTarget, InvalidSocket, LayerRejected, CapacityExceeded Fixed failure vocabulary.

A14 owns physical material/layer/socket policy. A15 emits prefab records that G04 translates directly into Avian components and G37 domain components. G14 owns intentional locomotion; Avian owns collision response.

4. ECS ownership and scheduling

There is one entity for each physical object. Avian components on that entity are authoritative mechanical state. OpenZT2 components contain only zoo-game facts Avian cannot know.

System Schedule/order Exact work
apply_attachment_requests fixed, before PhysicsSystems::Prepare Validate sockets/endpoints; insert/remove direct Avian joints and the one domain relationship.
apply_physics_impulses fixed, before PhysicsSystems::Prepare Convert typed requests into Avian impulse components; reject non-dynamic targets.
apply_authored_contact_policy Avian collision hook Apply only evidenced one-way/material/layer policy that cannot be represented by ordinary Avian components.
emit_gameplay_contact_facts fixed, after PhysicsSystems::Writeback Read enabled collision events/contact data and emit only subscribed game facts.
fit_eligible_objects changed transforms, before physics prepare Apply recovered terrain/tile/entity fitting policy to eligible fixed/kinematic bodies.
cleanup_physics_relationships fixed cleanup Remove dead-endpoint joints/relations and restore evidenced detach state exactly once.

Avian's ContactGraph and solver resources are legitimate bounded algorithm state. OpenZT2 must not copy them into a resource or event log.

5. Preflight producer contract

Not applicable in this package. A14/A15 preflight compile source shapes into the small Avian-supported primitive vocabulary, material/layer values, mass/density, CCD/sleep policy, sockets, and joint parameters. Unsupported or ambiguous source shapes fail conversion; runtime mesh decomposition is forbidden.

6. Behavioral evidence and disposition

Recover BFPhysObj, BFPhysComponent, BFPhysicsComponent, BFRealPhysicsComponent, BFFakePhysicsComponent, BFRopeComponent, collision/fitting-surface identities, BFBehAttachObject, BFBehDetachObject, BFBehWhap, and kick data from shipped definitions, TSVs, oracle, and live behavior. Establish units, layers, mass/material values, sleep/CCD policy, socket semantics, detach velocity, rope parameters, fitting and gameplay-visible contacts. Solver internals do not need original parity; observable response does.

Commit 19dd222f intentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.

7. Required behavior

Dynamic props collide, settle, wake, and respond at the fixed zoo timestep. Carried/attached objects follow one typed relationship and detach once. Ropes use Avian distance/fixed constraints and cannot retain dead endpoints. Terrain and fitting surfaces affect only eligible bodies. Gameplay systems consume focused facts, never Avian's entire contact graph.

8. Performance contract

Settled Avian stepping plus G37 integration targets zero heap allocations after body/contact/joint capacities stabilize. Enable collision events only for entities with gameplay consumers. Primitive colliders are preflight-produced; no runtime decomposition. Tracy records Avian broad phase, narrow phase, solver, substeps and OpenZT2 hooks separately. Allocation regressions cover a settled populated zoo, sleeping bodies, repeated contacts, rope constraints, and attachment churn after reserved capacity. A failing mandatory-dependency test must name the Avian allocation/copy before any fork is proposed.

9. Acceptance criteria

Deliver only the OpenZT2 contract above, using Avian components directly. Fixtures cover impulses, sleep/wake, layers, attachments, ropes, fitting, endpoint deletion, fixed-step replay, and bounded collision-fact emission. No custom broad phase, contact solver, collider type, body registry, copied contact collection, guessed material policy, or generic physics wrapper is accepted.

10. Required handoff

Report minimal Avian features/version, root plugin/schedule configuration, A14/A15 schema needs, Avian components inserted by G04, collision hooks, saved domain/Avian facts, recovered constants, remaining evidence, and Tracy/ allocation/contact-count results.