# Living ecosystems The Last Hearth's original succession model takes ecological inspiration from [Dynamic Life](https://www.planetminecraft.com/data-pack/dynamic-life-1-21-5-and-above/). No datapack code, species implementation, textures or other assets were copied. This is a runtime simulation, not extra random map decorations or world regeneration. ## What changes Unclaimed, unmodified ecological ground automatically recruits niche-appropriate groundcover, including the eight understory plants described below. Sunny, moderately grassy sites support wildflowers; dense grass competes with flowers. Moist shaded sites support ferns. Trees reduce local light and retain moisture; prolonged shade kills sun-loving ground cover. Rivers and biome humidity also influence moisture. Established sparse meadow can recruit a tree seedling; seedlings become young trees, then mature seed-bearing trees and finally deadwood. Living plants disperse to one adjacent suitable gap per eligible attempt. Mature trees shed litter and disperse seedlings. Competition and occupation reject seeds; seedlings never replace flowers, crops, another tree, or even existing meadow grass. Wildlife can consume active natural grass, ferns, clover, sedge and nettles and leave dung. Dormant winter plants cannot supply food or breeding habitat. Grazing opens gaps but imposes a five-minute recolonization cooldown. Dung, litter and deadwood decompose; moist, shaded organic patches seed fungi. Decomposition releases finite nutrients into the supporting soil (capped at eight). Fertile soil recruits ground cover sooner and successful establishment consumes one nutrient. Fungi are not edible forage; generic `fungi` decomposes, while botanical fungi have host-specific root associations or saprotrophic substrate effects below. These feedbacks permit shifting meadow/flower/ woodland patches rather than a single guaranteed final biome. Fallen logs (`hearth:deadwood`) are **ordinary full wood cubes laid sideways**, not irregular branch meshes. Their label is **Fallen log (axe; yields timber)**. Each requires an axe and drops **one `hearth:log`**, exactly like standing timber: `tree=1`, `choppy=2`, no hand-diggable `snappy` group. Default full-cube collision and selection apply. Normal reach, claim/external protection and construction support-removal checks remain; there is no right-click bypass. Loose `hearth:sticks` remain hand-gatherable, preserving the first-tool path. Stone still needs a mining tool. New woodland mapgen places sparse three-cube fallen logs, never force-placing them. `paramtype2="facedir"`, `param2=12` points end grain along +X; a random schematic rotation can align the line along Z (`param2=4` is +Z). The name `hearth:deadwood` is retained for decomposition and species metadata. The `hearth:block_deadwood` load callback rotates legacy vertical deadwood once, marks `succ_block_deadwood=1`, and leaves already-horizontal orientation intact. It rechecks exact loaded node identity, respects external protection, and uses `swap_node` to retain owner/player markers, natural flags, age, metadata inventories and timers. It does not convert other timber, rewrite surrounding nodes, or regenerate existing terrain. ### Active-time scales (minimums, not wall-clock promises) | Process | Observed active time | | --- | --- | | Bare soil recruits ground cover | 3 minutes; 90 seconds with nutrients | | Grass/flower/fern dispersal eligibility | 15 minutes; attempt every third evaluation | | Severe niche stress to litter/deadwood | 10 minutes | | Old meadow recruits a seedling | 2 hours; 4 in dense unfertilized grass, only suitable spatial niches | | Seedling to young tree | 6 hours | | Young to mature tree | 24 additional hours | | Mature tree death | 72 additional hours | | Mature seed dispersal | After 30 minutes; every tenth evaluation | | Wildflower / grass and fern senescence | 4 / 6 hours | | Dung / litter / fungi / deadwood decay | 10 / 30 / 40 / 120 minutes | New tree generations therefore take sessions, not seconds. Each stage resets its own age. Cold weather below 3°C suppresses establishment and living reproduction/ stage transitions. Decomposition and stress remain possible. This is a practical niche model, not a botanical or hydrological solver. Young/mature identities are ordinary full-cube log roots; **a mature tree needs an actual multi-node block trunk and leaf canopy**, not one bare root cube. The read-only `diversity.tree_plan` interface below shares the same geometry used by mapgen. Applying it, validating every occupied destination and support, and tracking generated components belong to the ecological growth owner. This content change installs no geometry-writing scheduler or automatic load-time expansion. Do not retain the old 5×5×6 mesh-only clearance assumption: mature profiles reach 9×10×9 and must be preflighted in full. Unknown/player plants, water, unloaded nodes, claims and markers must block growth; the plan's omitted air cells are never permission to clear or replace anything. Existing mapgen log/leaves trees contribute shade but are not aged or destroyed. Player-planted saplings, crops and berry bushes retain their existing time contract. ## Species diversity and understory Shared tree identities are exactly **oak, birch, pine, willow, alder, beech, aspen, rowan, hazel, spruce**, matching the ten trees in [botany.md](botany.md). Each has canonical `hearth:_seedling`, `hearth:_young`, and `hearth:_mature` nodes. The six-hour/24-hour/72-hour stage durations above remain unchanged. Growth and seed dispersal preserve the parent's species, and death records `succ_species` on generic deadwood (retained through its litter transition). Each young/mature root drops **one** of its species logs, never three logs from one cube. Additional generated trunk cubes are separately harvestable. Roots are hidden from creative inventory, use species log top/end and bark side textures, `drawtype="normal"`, `tree=1`, `choppy=2`, and default cube collision. Seedlings are conventional `plantlike` sprites. Mapgen full-block species logs/leaves/planks belong to the biome module; logs/leaves contribute canopy without being adopted as killable succession trees. Recruitment is deterministic, not an all-oak fallback: - Pine favors cold podzol and alpine turf. Warm podzol permits pioneer birch. - Birch pioneers dry/open grass, earth and woodland gaps. - Oak favors stable forest floor with an existing canopy. - Beech has a broad domed crown and avoids saturation; aspen is tall and columnar; rowan has an open crown; hazel is low coppice; spruce has a narrow evergreen spire. All ten have distinct mature block silhouettes and deterministic recruitment. - Willow favors moist alluvium/floodplain; alder favors peat and wet woodland. Riparian recruitment requires moisture ≥0.65. Species soil support is delegated to `hearth.biomes.supports_species(species, surface)` when present; its decision is authoritative. A standalone soil policy works without the biome module. - Ecological soils are grass, soil, forest floor, podzol, alluvium, peat and alpine turf. Biome `surface(node)` moisture hints blend with humidity/water/canopy. This is **not** an expansion of the separate crop-planting soil policy. | Understory | Niche and feedback | | --- | --- | | Clover | Sunny, moderately moist gaps; edible; flowering pollinator habitat; fixes bounded nitrogen | | Nettle | Moist, already fertile ground; edible natural browse, not an inventory food item | | Heather | Dry, open acidic podzol/alpine turf; autumn pollinator habitat | | Bluebell | Moist shade; blooms only in Spring, occupied and dormant otherwise | | Sedge | Saturated, lit riparian soil; edible when active | | Bulrush | Saturated, lit riparian soil; tall wetland cover, not edible | | Moss | Moist shade; persistent through Winter, not edible | | Lichen | Dry exposed rock/open ground; persistent through Winter, not edible | These are active succession species: they establish, compete, spread into **air**, undergo stress, and senesce. They do not merely decorate a fixed map. Suitable understory appears on the first ordinary 90/180-second establishment cycle, rather than waiting for tree maturation. Wet herbs can recruit riparian trees after two hours; heather can recruit pine. Saturated sites cannot fall back to meadow grass, and meadow grass cannot disperse into them. Exposed rock admits lichen only; neither recruitment nor decay replaces rock with soil or fertilizes it. Clover can add at most **two nitrogen units per supporting soil node**, recorded as `succ_fixed_nitrogen` across restart, grazing and recolonization; additions stop at local fertility four. The universal nutrient cap remains eight, and successful establishment consumes one. Clover has no harvest/item drop or timer, and cannot create an endless fertilizer-item loop. Habitat reads clamp corrupt/excessive nutrient metadata to eight without rewriting it. Natural blooming clover, wildflowers, spring bluebells and autumn heather increase the read-only `habitat(pos).pollination` count. This exposes a bounded habitat input for berry-regrowth consumers; this module does **not** change berry timers or grant extra berries. `edible` is a separate active-natural-forage count. Pine and spruce are evergreen in the catalogue and seasonal node policy; deciduous trees retain only 15% canopy influence. Known unmarked worldgen diversity herbs, species stage roots and generic deadwood are naturalized by `diversity.bootstrap(pos, node?)` from a load callback or the existing column probe. Bootstrap checks the plant and support for ownership, ambiguous claims, external protection and placement/unknown metadata. Hand placement marks `player_placed=1`. It never adopts species/generic logs, leaves, planks, crops or berry bushes. There is no general adoption of arbitrary flora, generic litter or fungi, and no new scanning scheduler. Adopted deadwood needs loaded natural ecological soil and decays to litter after **two observed active hours**, releasing three nutrients (soil cap eight); subsequent litter decay releases one more, once. Adoption is idempotent and grants no age or absence catch-up. As with legacy unmarked soil, provenance cannot distinguish third-party placements that deliberately omit every marker; those integrations must mark or claim their flora. ## Seasonal flora and food Appearance and availability use `hearth.climate_at(os.time()).season`: the same actual-epoch/latitude-aware climate as ecology, **not the machine's local month** and not a replay of an optional preview clock. Southern latitude reverses the calendar through the existing solar model. Explicit test climates can preview it. | Flora | Spring | Summer | Autumn | Winter | | --- | --- | --- | --- | --- | | Deciduous block leaves | Spring foliage | Original foliage | Autumn colours | Twig canopy texture | | Young/mature block trunks | Bark/end grain | Bark/end grain | Bark/end grain | Same full cubes; no mesh swap | | Pine/spruce leaves | Evergreen | Evergreen | Evergreen | Evergreen | | Clover / nettle / heather / sedge / bulrush | Active | Active | Active | Dormant | | Bluebell | Bloom | Dormant | Dormant | Dormant | | Moss / lichen | Active | Active | Active | Active | | Meadow grass / fern | Living | Living | Living | Dormant perennial | | Wildflowers | Bloom | Bloom | Dormant | Dormant | | Spring greens patch | Pickable | Dormant | Dormant | Dormant | | Autumn mushroom patch | Dormant | Dormant | Pickable | Dormant | Frost and seasonal dormancy pause living age, stress and reproduction; they do not kill perennials at first frost. Spring resumes the same ecological age. Deadwood, litter, dung and decomposer fungi continue returning nutrients in winter; mature trees can continue shedding litter. First-night berry availability stays year-round. Spring greens recruit and spread into moist (≥0.55), light ≥9 gaps in Spring. Autumn mushrooms recruit and spread into moist (≥0.6), shaded (light ≤10) gaps in Autumn. Deterministic sparse recruitment niches prevent every gap becoming food. Dormant patches remain occupied: neither seeds nor normal placement replace them. The existing decomposer `fungi` is still not edible. Right-click a patch (or dig to pick) within six nodes with normal construction `use` authorization. One picked greens item restores **12 food** when eaten. One picked mushroom can be crafted shapelessly into existing `raw_food`, then cooked at a lit, fuelled hearth. Raw mushrooms have no eat callback. These are original game foods, not advice about identifying or eating real wild plants. Picking never removes a patch. An empty inventory slot is required; one successful item transfer writes `succ_harvest_ready_at = now + 86400` (one real day). Rejected delivery grants nothing and writes no cooldown. The durable deadline survives restart, unload and every seasonal toggle; returning to Spring/Autumn cannot refill it. Harvest writes no age, owner or placement metadata and bypasses no claim. `hearth.seasons.kind(node_or_name)` canonicalizes only registered controlled variants; succession, grazing and habitat see the original kind and natural flag. `name_for(kind, epoch)` selects appearance. `reconcile(pos, node?, epoch?)` swaps only a loaded controlled node, preserves param1/param2 and all metadata, and does not call construction/destruction callbacks or reset timers. Appearance-only swaps may recolour claimed/hand-placed plants without changing permission or provenance; external protection still blocks them. Ecological mutations retain the stricter claim/placement exclusions below. No save schema/version change. ## Reviewed species effects and evidence [species_effects.lua](../mods/hearth/species_effects.lua) is an offline policy factory, `effects(hearth, api)`. It never fetches Wikipedia, interprets article prose, registers a scheduler, or writes nodes/metadata. Succession uses an injected `hearth.species_effects`, or loads this fixed sibling in the engine before its existing globalstep is registered. Minimal standalone fixtures may omit it. Content/bootstrap integration can initialize it before succession. `E.describe(record_or_id)` returns an ordered list of `{title, status, game, source_species, source_url, ...}`. Status is exactly `simulated` or `descriptive`: simulated means the named bounded game mechanism exists, **not** that all real-world consequences or field rates are simulated. `E.source(id)` and `E.sources()` expose identity references for all 80 catalogue IDs. Bundled Wikipedia page URLs/revision IDs are used when available; without the bundle, Latin-title links are only identity pointers, not verified evidence. Unreviewed records explicitly say that no species-effect evidence was reviewed here. `identity_revision` / `identity_revision_url` refer to the bundled introduction snapshot, **not** a claim that its excerpt contains the ecological body section reviewed below. Effect review date: **2026-11-03**. No invented revision IDs, biological rates, or undisclosed effect generation from tags. ### Implemented mechanics, not just catalogue descriptions | Species / relationship | Exact game effect | Evidence and limits | | --- | --- | --- | | Common alder (*Alnus glutinosa*), white clover (*Trifolium repens*) | After 900 warm active seconds, add at most one nutrient per eligible evaluation, never above local fertility four. **Two units total per supporting soil node**, recorded in `succ_fixed_nitrogen`, shared across species, recolonization and restart; soil's universal cap remains eight. Alder seedling, young and mature roots resolve through the same species ID; generated logs/leaves do not earn another contribution. | [Alder, Ecology](https://en.wikipedia.org/wiki/Alnus_glutinosa#Ecology) describes *Frankia* nodules and fertility; [white clover, Forage](https://en.wikipedia.org/wiki/Trifolium_repens#Forage) describes symbiotic fixation. The finite lifetime cap is game balance, not an annual nitrogen flux or physiological exhaustion claim. | | Dog lichen (*Peltigera canina*) | Uses the same existing finite nitrogen budget, never a fertilizer item or independent annual allowance. The pre-existing lungwort-lichen nitrogen tag retains that shared policy; its species-effect evidence is not independently reviewed here. | [Dog lichen, Description](https://en.wikipedia.org/wiki/Peltigera_canina#Description) identifies *Nostoc*; [Peltigera, Description](https://en.wikipedia.org/wiki/Peltigera#Description) explains nitrogen fixation. Explicit species identity plus genus-level mechanism, not a measured species rate. | | Blunt-leaved bog-moss (*Sphagnum palustre*) | A natural, active mat on loaded, permitted peat/alluvium at the sample's ground level adds **0.10 moisture** and subtracts **0.20 pH** from the local **land/bank habitat estimate**, capped at **two mats** (+0.20 / -0.40); moisture clamps to 0..1, pH to 3.5..8.5. This changes actual recruitment/stress niche tests. | [S. palustre](https://en.wikipedia.org/wiki/Sphagnum_palustre) describes water absorption; [Sphagnum](https://en.wikipedia.org/wiki/Sphagnum) explains water retention and acidification by cation exchange. Acidification evidence is genus-level. Coefficients are game approximations, not measured pH changes per plant. No permanent chemistry, peat creation, terrain conversion or global hydrology. | | Common duckweed (*Lemna minor*) | Each natural active mat immediately above conserved source water at the sample's stand level reduces nearby **submerged freshwater** habitat light by **1.5**, capped at four mats / six light units. It never brightens a dark site, and never lowers light below two. Surface plants and terrestrial sites are not shaded by this rule. | [L. minor, Cultivation](https://en.wikipedia.org/wiki/Lemna_minor#Cultivation) describes floating carpets; [Lemnoideae, Duckweed in natural environments](https://en.wikipedia.org/wiki/Lemnoideae#Duckweed_in_natural_environments) describes shade. This is a local group-level shading approximation, not an algae, oxygen, contaminant-removal or nutrient-purification simulation. | | Host-associated fungi, including fly agaric (*Amanita muscaria*) | An active compatible nearby fungus permits a tree seedling's young-stage transition at **16,200 rather than 21,600 active seconds**. Only a successful transition consumes **at most one existing soil nutrient**. Multiple fungi select the maximum allowance, not a sum; zero nutrients means no early growth. | [Fly agaric, Distribution and ecology](https://en.wikipedia.org/wiki/Amanita_muscaria#Distribution_and_ecology) describes ectomycorrhizal associations. Game host lists are narrower than the article. The existing growth debit is not nutrient creation, a full carbon-for-mineral ledger, or simulated water transfer. | | Saprotrophic wood fungi, including birch polypore (*Fomitopsis betulina*) | On suitable natural deadwood/litter only, advance its existing decay age by at most **60 seconds per eligible evaluation**, capped at 7,200 / 1,800. The ordinary substrate transition consumes deadwood to litter to air, paying **three then one** soil nutrients once, subject to cap eight. Removing the support removes its natural fungal occupant. The fungus itself has no extra nutrient payout. Soil-growing saprotrophs receive no special nutrient effect. | [Birch polypore, Distribution, habitat and ecology](https://en.wikipedia.org/wiki/Fomitopsis_betulina) describes birch-specific brown rot. Existing host suitability remains authoritative. Natural parasitism on weakened living birches is **not simulated**. No attacks on living/player trees. | The sampler collects `species_counts` (including canonical alder roots, moss and duckweed) within its existing **5x5x5** stencil. Mat feedback uses only the sample's stand level, with natural/protection/placement checks on the mat and its immediate support; claimed, planted, protected, unsupported or unloaded sources do not contribute. A partially unloaded sample grants no derived mat effects. `E.habitat(base, observations)` returns a copy and exposes `source_moisture`, `source_ph`, and `source_light`. Succession calls it **once after deriving fresh base moisture, support-class pH and aquatic classification**. Repeated habitat reads cannot accumulate acidification or retention and never change metadata. Consumers must not feed an already-derived habitat back as a new base sample. All aquatic stands still occupy air above water: no rule clears, replaces, refills or converts a water-source node. These habitat estimates drive botanical niches, not the renderer's actual node light or a global pH map. Existing season-gated browse, flowering/pollination and nursery counts are also reported, as is canopy shade/moisture (pine/spruce evergreen; deciduous Winter influence 15%). Nursery cover reduces stress accrual to 75%; pollination is an input, not a new berry-yield promise. Engineer tags on other species remain **descriptive / not simulated**: no automatic bank stabilization, erosion or rock weathering. Resin, allelopathy, toxicity attacks and living-tree parasitism are explicitly not simulated by this module. Article food/medicinal statements never grant food permission, poison attacks, healing or recipes. Focused evidence is in [species_effects_spec.lua](../tests/species_effects_spec.lua) and [succession_spec.lua](../tests/succession_spec.lua): all-80 source mapping, descriptor honesty, quantitative mat caps and changed real niche eligibility, read-only/repeated sampling, source/support protection, shared durable nitrogen, no duplicate fungal nutrients, and source-water preservation. These are standalone Lua tests, not a claim of full-engine or multiplayer validation. ## Safety and execution contract - One scheduler runs once per second, doing **at most eight column probes total**, shared round-robin across connected players (not eight per player). Columns scan a 25×25 square and twelve vertical nodes (+6..−5) around a player, seeking existing natural ground. No connected players means no ecological work. - Each probe checks at most twelve heights and one site. Encountered foliage, including old unmarked mapgen leaves, is reconciled on that same probe: **at most twelve appearance swaps per column, 96 per second total**, not a second player radius scan. An LBM reconciles each controlled flora node on load without emergence or a world registry; that load-driven work is one swap per callback, separate from the scheduler's per-second budget. Habitat uses a fixed 5×5×5 stencil; site safety uses a fixed 3×3×3 stencil. Mutations per evaluated site are at most two (mature seed plus litter); other stages write at most one. Wildlife calls scan at most nine candidate sites and mutate at most one. - Only `get_node_or_nil` reads are used. Missing/ignore nodes fail closed. There is no emerge, force-load, mapgen edit, ABM, per-node timer or offline replay. - Ages accrue from consecutive observations, not from absolute planting dates. First observation after restart, load, cache eviction, disconnect, clock rewind, or a gap longer than 1,200 seconds only establishes a baseline. The load callback invalidates observations when vegetation or its natural support reloads. A stalled server step gets one fixed budget, never a backlog of simulation steps. - A bounded 8,192-entry in-memory observation cache is disposable (enough for ten disjoint stationary-player scan squares). Age, stress, next-attempt timestamp, natural-origin flag and attempt serial live in node metadata; soil stores only its nutrient count. There is no global save schema. Durable attempt timestamps/serials prevent repeated evaluation or rerolling a blocked seed by reconnecting/restarting. Restarting pauses growth; it cannot accelerate it. Ages are sampled approximations between visits, not per-tick accounting; very busy/many-player worlds may deliberately progress more slowly. - Source, destination and natural supports must all be unclaimed, externally unprotected and free of placement metadata. All normal content placement marks `player_placed=1`, including hand-placed soil/turf and soil left by crop harvest. Recognized legacy markers are `owner`, `planter`, `placed_by`, `hearth_placed`, `hearth_player_placed` and `planted_at`. Unknown third-party non-succession metadata also opts out conservatively. Any nonempty/nonzero marker opts out. Nearby markers/builds/farms also block succession conservatively. Only explicitly natural simulation plants may be consumed, replaced or decayed. Never replace water or arbitrary `buildable_to` nodes. Old unmarked player earth cannot be distinguished from mapgen earth: claim it to keep nature out. ## Wildlife interface `hearth.succession` is installed by the content registrar: - `forage(pos) -> boolean`: consume exactly one nearby active natural `meadow_grass`, `fern`, `clover`, `sedge` or `nettle` on safe natural support. Does not eat dormant flora, flowers, crops or player plants. - `deposit(pos) -> boolean`: place exactly one dung nutrient patch in nearby **air** over safe natural support. Never replaces existing plants or water. Nutrients arrive through active decomposition, not immediately on deposit. - `habitat(pos) -> table`: read-only counts `grass`, `flowers`, `cover`, `water`, `edible`, `pollination`, plus `ground`, `nutrients` (0..8), biome `heat`, `canopy`, estimated `light`, `moisture` and `loaded`. Counts use the fixed local stencil; wildlife should reject an incomplete (`loaded=false`) sample. - `step(dtime) -> number`: registered scheduler; returns columns processed. Exposed constants document the scheduler's fixed production limits, not settings. Positions must be finite near-world-range vectors; APIs round to node coordinates. Callers supply animal ground-cover height (the air/plant cell above soil), not the supporting soil or the animal's eye height. Calls return false when no safe target exists. Habitat has no writes, timers or protection-violation reports. ## Assets and verification ### Exact registered node / art integration contract All names below have the `hearth:` namespace. Existing canonical nodes retain their names and callbacks/groups/drops. Summer always uses the existing canonical tree and leaves node; no redundant summer variant is registered. - Legacy `tree_seedling`, `young_tree`, `mature_tree`, `leaves` retain `_spring`, `_autumn`, `_winter` IDs for save compatibility (12 variants). The content registrar removes old mesh/atlas references from every legacy stage variant: seedlings use `hearth_sapling.png`; young/mature trees use log top/side textures and remain normal cubes. Seasonal appearance belongs to their separate leaves. - Deadwood uses `hearth_log_top.png` on both ends and `hearth_log_side.png` on the remaining faces, with no mesh, custom collision/selection or seasonal variant. - Mapgen leaf variants use `hearth_leaves_spring.png`, `hearth_leaves_autumn.png`, `hearth_leaves_winter.png`; canonical Summer uses existing `hearth_leaves.png`. - `meadow_grass_winter`, `fern_winter`, `wildflowers_autumn`, `wildflowers_winter` use `hearth_meadow_grass_dormant.png`, `hearth_fern_dormant.png` and `hearth_wildflowers_dormant.png`. Other seasons use existing canonical nodes/art. - `spring_greens_patch` and `autumn_mushrooms_patch` are dormant canonical nodes; `spring_greens_patch_spring`, `autumn_mushrooms_patch_autumn` are active variants. Textures: `hearth_spring_greens.png`, `hearth_spring_greens_dormant.png`, `hearth_autumn_mushrooms.png`, `hearth_autumn_mushrooms_dormant.png`. - Picked craftitems (not nodes): `hearth:spring_greens`, `hearth:autumn_mushrooms`; icons `hearth_item_spring_greens.png`, `hearth_item_autumn_mushrooms.png`. The legacy seasonal set adds **20 nodes total**, including both canonical food patches. All variants are hidden from creative inventory, retain base callbacks and drop definitions, and cannot become duplicate harvest sources by toggling. The registrar honours explicit mesh tiles directly; it does not require `override_item` in engine mocks. ### Main-agent wiring and exact diversity interface The blockshape change owns only diversity, content, biomes, their three focused tests and this document. Succession, ecology, seasons, botany and generated assets remain other owners' responsibility. Exact integrated order: 1. Create/install `hearth.seasons = dofile(modpath .. "/seasons.lua")(hearth, api)`. 2. Create/install `hearth.botany = dofile(modpath .. "/botany.lua")(hearth, api)`. 3. **Before succession setup**, create/install `hearth.diversity = dofile(modpath .. "/diversity.lua")(hearth, api)`. This factory immediately extends `seasons.add_variants` with species policy. 4. Instantiate succession using its factory and assign `hearth.succession`. 5. After normal content, register/install biomes (ten species' logs/leaves/planks). 6. Call `hearth.diversity.register_nodes(register_node)` with the bare-name wrapper: 30 species stage nodes, eight existing understory nodes and the bootstrap LBM. 7. Call `hearth.botany.register_nodes(register_node)`: exactly 62 new non-tree taxa, with all explicit tiles and inventory/wield images preserved. The ten trees and eight existing understory nodes are not registered twice. 8. Call `seasons.register()` after every canonical node, then instantiate wildlife. The returned diversity table exposes: - `species_order`, `plants`: ordered ten/eight canonical short names. - `grounds`, `rocks`: exact short-name membership sets; `is_ground(node_or_name)`, `is_rock(node_or_name)` and `supports_ground(kind, support)` are boolean policies. - `tree_info(kind_or_node) -> species, stage`: two Lua return values, or nil; stages are `seedling`, `young`, `mature`. Seasonal names canonicalize first. - `family(kind_or_node) -> canonical_family`: species stages map to legacy `tree_seedling`, `young_tree`, `mature_tree`; species leaves/logs map to `leaves`/`log`; herbs retain their own canonical kind. - `supports_species(species, surface)`, `suitable(kind, habitat, season?)`: support/niche policy with optional biome integration. - `choose_recruit(pos, habitat, season) -> understory_kind_or_nil`; `choose_tree(pos, habitat) -> species_seedling_kind_or_nil`. Habitats supply `ground` as a short name and normalized `moisture`, `light`, `loaded`, `nutrients`, `canopy` and optionally biome `heat`. - `edible(kind, epoch?)`, `pollinator(kind, season)`; `is_worldgen_flora(kind)`, `bootstrap(pos, node?) -> boolean`; `register_nodes(register_node)`, idempotent node registration. ### Geometry handoff to ecological growth `diversity.tree_plan(species, stage?) -> plan | nil` is read-only. Species is one of the ten IDs or `"generic"` for legacy trees; stage is `"young"` or `"mature"` (default mature). Invalid IDs/stages or missing biome integration return nil. `plan` is a fresh dense array of `{x, y, z, name, param2}` records: integer offsets relative to the stage root at `(0,0,0)`, fully qualified node names, and facedir. `plan.size={x,y,z}` gives the bounding schematic dimensions. There are **no air records**. The root is `hearth:_`; remaining trunk/branch and canopy records use `hearth:_log` and `hearth:_leaves`. Generic plans use oak geometry with `young_tree`/`mature_tree` roots and canonical `log`/`leaves`. Every non-root occupied cell exactly matches `biomes.tree_schematic(species, canonical?, stage?)` (x-fastest, then y, then z). Young plans are at most 3×5×3; mature plans at most 9×10×9. Mutating a returned plan cannot change later plans or mapgen geometry. **Ecology owner:** preflight the complete bounded plan, loaded supports and all destination claims/protection/provenance before writing. Only the exact existing natural root and verifiably owned tree components may be reused on stage growth; never overwrite water, player nodes, farms or unrelated flora. Track component ownership/root association and stale callbacks; reject partial growth, coordinate overflow, unloaded cells and blocked transitions. Preserve species identity and handle component removal/death within the existing scheduler budget. This API neither applies geometry nor migrates/expands old roots on load. Mature roots must not be treated as finished trees without their real block canopies. When ecological death creates fresh `hearth:deadwood`, supply `param2=12` (or `4` for a Z-oriented log) immediately; the load migration is for legacy saves, not a substitute for orienting newly created fallen timber. `seasons.add_variants(kind, definitions, active_seasons?)` is available immediately after factory creation and before registration. Definitions map `Spring`, `Summer`, `Autumn`, `Winter` to `{texture=..., mesh=...}`. An optional active-season boolean set controls availability. Missing base nodes in mocks/optional integrations are skipped without an assertion; a later registration call can register their variants. No original 12-month climate calculation or preview-clock semantics changed. ### Exact species/understory art contract For **all ten** species in `species_order`: - Seedlings use `hearth_botany_.png` on a plantlike sprite. Young/mature roots and species timber use `hearth__log_top.png` on both ends and `hearth__log_side.png` elsewhere. Planks use `hearth__planks.png`. No ecological tree definition references a mesh or atlas. - Biome-owned full-block leaves use `hearth__leaves.png`. Deciduous seasonal variants use `hearth__leaves_spring.png`, `hearth__leaves_autumn.png`, `hearth__leaves_winter.png`. Pine **and spruce** leaves retain the base texture throughout the year. - Deciduous stage/leaf variants append `_spring`, `_autumn`, `_winter`; Summer restores the canonical node. Seedling variants keep the botany tile. Young/mature seasonal IDs are retained for existing saves but have the **same bark cubes**, never foliage atlases. Their availability still pauses in Winter for deciduous species. Diversity registers those multi-face cube variants before the seasonal foliage copier; seasons remains responsible for name selection and swapping. Understory base textures are `hearth_clover.png`, `hearth_nettle.png`, `hearth_heather.png`, `hearth_bluebell.png`, `hearth_sedge.png`, `hearth_bulrush.png`, `hearth_moss.png`, `hearth_lichen.png`. Clover, nettle, heather, bluebell, sedge and bulrush additionally use `hearth__dormant.png` in Winter (nodes append `_winter`). Bluebell also has `_summer` and `_autumn` node variants using its same dormant texture. Moss and lichen have no dormant art. No art is generated or modified here. With all biome leaves installed, diversity adds **142 nodes**: 38 canonical nodes, 72 deciduous stage variants, 24 deciduous full-block leaf variants and eight understory variants. Pine and spruce add no seasonal variants. Botany independently adds 62 nodes. Species provenance on generic deadwood remains metadata. The standalone succession fixture tests growth, niches, shade, competition, seedlings, seeds, nutrient feedback, dung/fungi/decay, grazing, source/destination/ support protection, placement markers, unloaded nodes, work/read budgets, cold, restart cooldowns and no-catchup behavior. Content tests check node registration, named textures, wildlife API wiring and placement marking. Seasonal fixtures add all four seasons, northern/southern calendars, seasonal foliage/restoration, metadata/age conservation, forage cooldowns/restart, niches/occupation, protected and unloaded no-write cases, target-volume clearance and the 96-swap bound. The blockshape-focused standalone suites are diversity, biomes and content; botany has its separate catalogue suite. They check exact ten-species ordering, distinct mature schematics, bounded young canopies, shared rooted geometry, metadata-safe deadwood migration, axe-only harvesting, all 78 unique recipes, 62 additional botany nodes and preserved aquatic icons/placement guards. Engine visuals, multiplayer ecology balance and long-session woodland density still require in-engine playtesting; unit fixtures are not a performance benchmark. Security review: no credentials, cryptography, certificates, network requests, deserialization or unbounded external inputs were introduced. Mutation authority is least-privilege nature (no player identity); loaded/protection/marker checks fail closed before writes.