Files
2026-11-05 09:57:11 -05:00

290 lines
36 KiB
Markdown
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Botany: 100 temperate taxa and calendar integration
## Scope and evidence
The catalogue in [mods/hearth/botany.lua](../mods/hearth/botany.lua) has **100 distinct real taxa** in eight disjoint groups: **13 trees, 14 shrubs, 10 grasses, 22 herbs, 11 ferns/mosses, 10 fungi, 10 aquatic taxa and 10 lichens**. It represents a north-west/central European temperate landscape mosaic, not one plant community or a claim that every species is native everywhere. Hazel is counted among trees, heather among herbs, and lichens separately from other fungi. Existing generic gathering patches are not part of this count.
Taxonomic/reference authorities for maintaining these names and broad habitats:
- **P**: [BSBI/UKCEH Plant Atlas 2020](https://plantatlas2020.org/) for vascular-plant regional distributions, habitat and nomenclature; [Kew Plants of the World Online](https://powo.science.kew.org/) for name/synonym checks. Kew blocked automated access during this implementation; no claim of an automated 80-record authority audit is made.
- **T**: [Woodland Trust British tree accounts](https://www.woodlandtrust.org.uk/trees-woods-and-wildlife/british-trees/a-z-of-british-trees/) and [plant accounts](https://www.woodlandtrust.org.uk/trees-woods-and-wildlife/plants/), complementary common names and woodland niches.
- **W**: [The Wildlife Trusts wildflower accounts](https://www.wildlifetrusts.org/wildlife-explorer/wildflowers), including freshwater plants.
- **M**: British Bryological Society, *Mosses and Liverworts of Britain and Ireland: A Field Guide* (2010), and *Atlas of British & Irish Bryophytes* (2014), for the five moss names/niches. Online BBS access also blocked automated retrieval; these are bibliographic authorities, not downloaded records.
- **F**: [British Mycological Society English names](https://www.britmycolsoc.org.uk/resources/english-names), [Index Fungorum](https://www.indexfungorum.org/), and [First Nature fungal accounts](https://www.first-nature.com/fungi/). Particularly [fly agaric](https://www.first-nature.com/fungi/amanita-muscaria.php) for root-host associations, [birch polypore](https://www.first-nature.com/fungi/piptoporus-betulinus.php) for birch/deadwood associations and its modern name, and [wood blewit](https://www.first-nature.com/fungi/lepista-nuda.php) for litter decomposition and late fruiting.
- **L**: [British Lichen Society species accounts and maps](https://britishlichensociety.org.uk/resources/species-accounts) and its [Lichens of Great Britain and Ireland](https://britishlichensociety.org.uk/identification/lgbi3). Names and species boundaries evolve; common lichen names are not universally standardized.
The numeric light/moisture/pH/depth windows below are **explicit game approximations**, not measured ecological tolerances, identification keys, conservation advice or a scientific simulation. The catalogue selects named species rather than inventing generic “magic herbs”. *Rubus plicatus* is one real blackberry species, not the *Rubus fruticosus* aggregate counted as a species. Meadow fescue *Schedonorus pratensis* is also encountered as *Festuca pratensis* / *Lolium pratense*. Wood blewit *Collybia nuda* is widely recorded as *Lepista nuda* / *Clitocybe nuda*. Birch polypore *Fomitopsis betulina* replaces the familiar *Piptoporus betulinus*. These synonyms are not extra entries.
**No real-world foraging, medicinal or consumption claims are provided.** Only three fictional bush outputs use the existing game item `hearth:berries`. This is a gameplay simplification, not a statement that a wild plant or lookalike is safe. Every fungus, herb, tree and lichen has `food=false`; toxic/stinging examples are flagged but `toxic=false` is **not** a safety certification. `browse` describes wildlife relationships, not player edibility. No new food items, recipes, poisoning system or damage to player plants are registered.
## Exact ID manifest (art, trees and ecology handoff)
`id` is the canonical catalogue key. Every non-tree node is exactly `hearth:<id>`. Every tree's initial node is `hearth:<id>_seedling`; diversity/resources own tree stages and materials. Generic `berry_bush`, `fern`, `wildflowers` and `meadow_grass` remain outside the 100.
Owner flags: **E** = existing node, never registered here; **D** = tree diversity owner, never registered here; **B** = new botany-owned node. The 13 existing mappings are retained verbatim: five trees plus clover, nettle, heather, bluebell, sedge, bulrush, moss and lichen. “Sedge”, “moss” and “lichen” receive a specific catalogue interpretation; their old IDs do not change.
Art contract for **all 100**: `hearth_botany_<id>.png`. Botany registers **79** non-tree nodes (87 non-trees minus eight legacy understory IDs). Diversity/terrain own all thirteen tree stages, bark/end-grain cubes, leaves and planks. No random tint or seasonal mesh substitutes for species materials. Seasonal copying preserves bark faces, `facedir`, callbacks and drops. Assets and native-engine visual validation remain the art/main integration owners' responsibility.
### Exact twenty additions (counts above include these)
| ID | Latin identity from lifecycle contract | Group / ecological emphasis |
| --- | --- | --- |
| dandelion | Taraxacum officinale | herbs; open meadow, spring–autumn flowers |
| daisy | Bellis perennis | herbs; short open grassland flowers |
| red_clover | Trifolium pratense | herbs; summer/autumn flowers, bounded nitrogen fixation |
| birdsfoot_trefoil | Lotus corniculatus | herbs; drier meadow, flowers, bounded nitrogen fixation |
| ribwort_plantain | Plantago lanceolata | herbs; meadow browse, wind-pollinated, no nectar aura |
| selfheal | Prunella vulgaris | herbs; meadow summer/autumn flowers |
| knapweed | Centaurea nigra | herbs; meadow late flowers and cover |
| oxeye_daisy | Leucanthemum vulgare | herbs; summer meadow flowers |
| field_scabious | Knautia arvensis | herbs; drier base-rich meadow flowers |
| teasel | Dipsacus fullonum | herbs; summer flowers and seedhead cover; biennial fallback |
| cow_parsley | Anthriscus sylvestris | herbs; hedge margins, spring/summer flowers |
| garlic_mustard | Alliaria petiolata | herbs; shaded edges, second-season spring flowering policy |
| honeysuckle | Lonicera periclymenum | shrubs; hedge/woodland edge flowers; no food |
| ivy | Hedera helix | shrubs; evergreen cover, autumn flowers, spring seed window; no food |
| gorse | Ulex europaeus | shrubs; open acid scrub, flowers including winter, bounded nitrogen fixation |
| broom | Cytisus scoparius | shrubs; open acid scrub, spring/summer flowers, bounded nitrogen fixation |
| crab_apple | Malus sylvestris | trees; spring flowers, broad round crown |
| wild_cherry | Prunus avium | trees; spring flowers, taller horizontal branch tiers |
| field_maple | Acer campestre | trees; spring flowers, broad lobed crown |
| holly_fern | Polystichum setiferum | ferns_mosses; evergreen damp shade, commonly soft shield-fern |
The historical 80-entry manifest below lists the retained baseline; append these twenty, not twenty per group. No addition grants human food or a new berry output. Nitrogen-fixing *Lotus corniculatus* is birdsfoot trefoil, not an aquatic lotus. Climbing ivy/honeysuckle use stand sprites rather than simulated climbing geometry.
### trees (10; P/T)
| id | Common name | Scientific name | Owner | Habitat emphasis |
|---|---|---|---|---|
| oak | Pedunculate oak | Quercus robur | E/D | Mesic woodland; ectomycorrhizal roots |
| birch | Silver birch | Betula pendula | E/D | Light acid pioneer woods |
| pine | Scots pine | Pinus sylvestris | E/D | Open acid woods; resin |
| willow | White willow | Salix alba | E/D | Wet alluvial woods; spring flowers |
| alder | Common alder | Alnus glutinosa | E/D | Wet woods; Frankia-associated nitrogen fixation |
| beech | European beech | Fagus sylvatica | D | Well-drained woodland, avoiding saturation |
| aspen | European aspen | Populus tremula | D | Light woodland gaps |
| rowan | Rowan | Sorbus aucuparia | D | Cool acid woodland/edges; flowers |
| hazel | Common hazel | Corylus avellana | D | Woodland understorey; ectomycorrhiza |
| spruce | Norway spruce | Picea abies | D | Moist acid conifer woods; resin |
### shrubs (10; P/T)
| id | Common name | Scientific name | Owner | Habitat emphasis |
|---|---|---|---|---|
| bilberry | Bilberry | Vaccinium myrtillus | B | Acid heath/woodland; ericoid, not ectomycorrhizal |
| blackberry | Pleated blackberry | Rubus plicatus | B | Hedgerows/wood edges |
| raspberry | Red raspberry | Rubus idaeus | B | Woodland clearings |
| elder | Black elder | Sambucus nigra | B | Hedgerows; no game food |
| blackthorn | Blackthorn | Prunus spinosa | B | Thorny hedges; spring flowers |
| hawthorn | Common hawthorn | Crataegus monogyna | B | Hedgerows; spring flowers |
| dog_rose | Dog rose | Rosa canina | B | Open scrub; summer pollen |
| holly | European holly | Ilex aquifolium | B | Evergreen woodland cover; no game food |
| juniper | Common juniper | Juniperus communis | B | Open dry heath/scrub; resin |
| guelder_rose | Guelder rose | Viburnum opulus | B | Damp hedges/wet edges; no game food |
### grasses (10; includes sedges, rushes and reedmace; P)
| id | Common name | Scientific name | Owner | Habitat emphasis |
|---|---|---|---|---|
| sedge | Greater tussock-sedge | Carex paniculata | E | Wet bank tussocks |
| bulrush | Bulrush (great reedmace) | Typha latifolia | E | Reedbed margin; bank-cell approximation |
| meadow_fescue | Meadow fescue | Schedonorus pratensis | B | Moist neutral grassland |
| cocksfoot | Cock's-foot | Dactylis glomerata | B | Mesic meadow/edge |
| timothy | Timothy | Phleum pratense | B | Moist meadow |
| sweet_vernal_grass | Sweet vernal-grass | Anthoxanthum odoratum | B | Acid grassland |
| yorkshire_fog | Yorkshire-fog | Holcus lanatus | B | Mesic grassland |
| tufted_hairgrass | Tufted hair-grass | Deschampsia cespitosa | B | Wet meadow tussocks |
| common_reed | Common reed | Phragmites australis | B | Wet banks/reedbed edges |
| soft_rush | Soft rush | Juncus effusus | B | Wet pasture/bog margin |
### herbs (10; P/T/W)
| id | Common name | Scientific name | Owner | Habitat emphasis |
|---|---|---|---|---|
| clover | White clover | Trifolium repens | E | Open grassland; rhizobial nitrogen fixation |
| nettle | Common nettle | Urtica dioica | E | Nutrient-rich damp edges; larval host, not nectar |
| heather | Heather | Calluna vulgaris | E | Acid heath; late-season flowers |
| bluebell | English bluebell | Hyacinthoides non-scripta | E | Spring woodland floor |
| yarrow | Yarrow | Achillea millefolium | B | Meadow; summer/autumn flowers |
| foxglove | Common foxglove | Digitalis purpurea | B | Acid clearings; no game food |
| meadowsweet | Meadowsweet | Filipendula ulmaria | B | Wet meadow; pollen resource |
| wild_garlic | Wild garlic | Allium ursinum | B | Spring moist woodland; no game food |
| cowslip | Cowslip | Primula veris | B | Base-rich open grassland |
| wood_anemone | Wood anemone | Anemone nemorosa | B | Spring woodland floor; no game food |
### ferns_mosses (10; five ferns P/T, five mosses M)
| id | Common name | Scientific name | Owner | Habitat emphasis |
|---|---|---|---|---|
| bracken | Bracken | Pteridium aquilinum | B | Acid slopes/clearings; competition descriptor |
| lady_fern | Lady-fern | Athyrium filix-femina | B | Damp shaded woods |
| male_fern | Male-fern | Dryopteris filix-mas | B | Shaded woods |
| harts_tongue | Hart's-tongue fern | Asplenium scolopendrium | B | Damp base-rich shade |
| polypody | Common polypody | Polypodium vulgare | B | Acid rock/bark/woodland |
| sphagnum | Blunt-leaved bog-moss | Sphagnum palustre | B | Saturated acid bog/wet woodland |
| haircap_moss | Common haircap | Polytrichum commune | B | Damp acid soils |
| moss | Cypress-leaved plait-moss | Hypnum cupressiforme | E | Shady soil, wood, bark and rock |
| fork_moss | Broom fork-moss | Dicranum scoparium | B | Acid woodland/heath |
| star_moss | Swan's-neck thyme-moss | Mnium hornum | B | Damp woodland banks |
### fungi (10; F)
| id | Common name | Scientific name | Owner | Habitat emphasis |
|---|---|---|---|---|
| fly_agaric | Fly agaric | Amanita muscaria | B | Ectomycorrhizal: birch/pine/spruce |
| birch_bolete | Brown birch bolete | Leccinum scabrum | B | Ectomycorrhizal: birch only in this model |
| bay_bolete | Bay bolete | Imleria badia | B | Ectomycorrhizal: conifers/beech/oak |
| chanterelle | Chanterelle | Cantharellus cibarius | B | Ectomycorrhizal woodland roots |
| wood_blewit | Wood blewit | Collybia nuda | B | Litter saprotroph; autumn/winter |
| shaggy_inkcap | Shaggy inkcap | Coprinus comatus | B | Grassland-soil saprotroph |
| turkey_tail | Turkey tail | Trametes versicolor | B | Broadleaf deadwood saprotroph |
| oyster_fungus | Oyster mushroom | Pleurotus ostreatus | B | Broadleaf deadwood; late-season fruiting |
| honey_fungus | Honey fungus | Armillaria mellea | B | Deadwood here; parasitism descriptive only |
| birch_polypore | Birch polypore | Fomitopsis betulina | B | Birch deadwood here; parasitism descriptive only |
### aquatic (10; P/W)
| id | Common name | Scientific name | Owner | Placement niche |
|---|---|---|---|---|
| duckweed | Common duckweed | Lemna minor | B | Surface; 1–6 depth units |
| yellow_waterlily | Yellow water-lily | Nuphar lutea | B | Surface; 1–3, mud anchor |
| white_waterlily | White water-lily | Nymphaea alba | B | Surface; 1–3, mud anchor |
| water_crowfoot | Common water-crowfoot | Ranunculus aquatilis | B | Surface abstraction; 1–3, mud anchor |
| water_mint | Water mint | Mentha aquatica | B | Wet bank, dry destination |
| water_forgetmenot | Water forget-me-not | Myosotis scorpioides | B | Wet bank, dry destination |
| brooklime | Brooklime | Veronica beccabunga | B | Wet bank, dry destination |
| water_starwort | Common water-starwort | Callitriche stagnalis | B | Submerged abstraction; 1–4, mud anchor |
| hornwort | Rigid hornwort | Ceratophyllum demersum | B | Submerged/rootless; 1–4, water substrate |
| pondweed | Broad-leaved pondweed | Potamogeton natans | B | Surface; 1–3, mud anchor |
### lichens (10; L)
| id | Common name | Scientific name | Owner | Habitat emphasis |
|---|---|---|---|---|
| reindeer_lichen | Reindeer lichen | Cladonia rangiferina | B | Open acid heath; wildlife browse |
| oakmoss | Oakmoss | Evernia prunastri | B | Bark in light woodland |
| old_mans_beard | Old man's beard | Usnea subfloridana | B | Humid bark/branches |
| lichen | Map lichen | Rhizocarpon geographicum | E | Acid siliceous rock, not generic soil |
| dog_lichen | Dog lichen | Peltigera canina | B | Mossy soil; cyanobacterial fixation |
| lungwort_lichen | Lungwort lichen | Lobaria pulmonaria | B | Humid woodland bark; cyanobacterial cephalodia |
| sunburst_lichen | Common orange lichen | Xanthoria parietina | B | Well-lit nutrient-enriched bark/base-rich stone |
| hammered_shield | Hammered shield lichen | Parmelia sulcata | B | Woodland bark |
| cup_lichen | Mealy pixie-cup | Cladonia chlorophaea | B | Acid soil/humus, distinct species below |
| pixie_cup | Pebbled pixie-cup | Cladonia pyxidata | B | Soil/humus; not a duplicate of chlorophaea |
## Exact public interface
Loading the file returns **`factory(hearth, api) -> B`**. The factory has no registration side effects and publishes no globals. Main assigns `hearth.botany` before loading succession. Botany installs no scheduler, globalstep, ABM, LBM, mapgen callback, timer or liquid transformation. [mods/hearth/succession.lua](../mods/hearth/succession.lua) consumes this policy in its existing eight-probe loop.
### Catalogue/query APIs
- `B.groups`, `B.group_order`: ordered arrays `trees, shrubs, grasses, herbs, ferns_mosses, fungi, aquatic, lichens`.
- `B.catalogue`: ordered 100-record snapshot, group order then retained and added row order.
- `B.get(value) -> record|nil`; `B.list(group_or_nil) -> ordered records`; `B.group_of(value) -> group|nil`; `B.kind(value) -> id|nil`.
- `value` may be a bare ID, full `hearth:` node name or `{name=...}`. Trees also resolve canonical `_seedling`, `_young`, `_mature`, `_leaves`, `_log`, `_planks`. Seasonal names resolve through `hearth.seasons.kind` when available. Generic patches and foreign namespaces do not resolve.
- Every returned record is a detached copy. Editing a snapshot cannot change internal policy.
- Record fields: `id`, `common`, `latin`, `group`, `node`, `texture`, `existing`, `food`, `toxic`, `seasons` (boolean set), `tags` (boolean set), `niche`; flowering taxa also have `flowering`, the three game-fruit taxa have `fruiting` (both boolean sets).
- `niche` fields: `substrates` set, inclusive `light={min,max}` on 0–15, `moisture={min,max}` on 0–1, `ph={min,max}` in pH units, `water={min,max}` in game node-depth units, `aquatic` string. Some also require `hosts` set or `nutrients` minimum.
### Read-only habitat selection
`B.suitable(value, h, season) -> boolean` and `B.choose(p, h, season) -> bare_node_kind, record | nil` use only supplied observations. `p` must contain finite integer `x,y,z` within ±30990; `h` must have `loaded=true` and finite `light`, `moisture`, `ph`. Missing/invalid facts do not receive optimistic defaults. `season` is exactly `Spring`, `Summer`, `Autumn` or `Winter`.
`h.ground` is a canonical bare/full support node or node table. `h.substrate`, when supplied, overrides the direct substrate for land/bark/deadwood niches. Bark and deadwood are **semantic observations**, not assertions that arbitrary planks/logs are safe recruitment sites. The ecological owner must derive those observations from unmodified natural substrate and enforce owner/protection checks. Do not treat crafted timber or planted trees as free fungal substrate.
`h.host` is a nearby identified tree ID/stage/root association, mandatory for host-specific fungi. Its search radius and finite sampling budget belong to the existing ecological scheduler. Presence of an unrelated tree does not satisfy a birch specialist. `h.nutrients` must be at least one for nettle. pH is a game support-class observation; callers lacking it should explicitly derive a documented approximation from the substrate, not silently infer that all plants fit.
`h.aquatic` is `land`, `bank`, `surface` or `submerged`; land may omit it. `h.water` is **numeric column depth**, not a boolean or permission to replace a water node; land/bank may omit it (zero). The selection represents a candidate only:
| Mode | Required observations | Safe external placement rule |
|---|---|---|
| land | Depth zero; accepted substrate | Loaded air destination above accepted natural substrate |
| bank | Explicit `bank`, depth zero, wet support | Loaded air destination beside real freshwater, never inside/over it |
| surface | Explicit `surface`, `freshwater=true`, allowed positive column depth; accepted mud bottom | Flat pad in a distinct air cell above the actual surface; source node below stays unchanged |
| submerged | Explicit `submerged`, `freshwater=true`, positive column depth; rooted starwort needs mud bottom, rootless hornwort needs `substrate=water` | Succession places a **qualitative shallow-water stand sprite in the separate air cell above conserved source water**. It is not literally rendered inside the liquid. No source replacement, fake liquid, water clearing/refilling or flood-producing liquid flora |
Surface depth ranges/anchors are intentionally coarse: even floating duckweed uses a valid freshwater bottom observation rather than an unbounded “water somewhere” fallback. Succession checks up to six consecutive loaded `hearth:water_source` cells and an accepted bottom, with authority/markers checked on every cell. Flowing water, unknown bottoms, over-depth or unloaded columns fail closed. Surface/submerged mode for a new stand is position-deterministic; established stands retain their species mode. Thus all ten aquatic taxa have establishment/spread/age pathways, but **hornwort/starwort use above-water stand markers, not scientifically exact submerged geometry**. `register_nodes` keeps direct source-water item placement disabled. Bank flora occupies dry cells beside actual water.
`choose` considers all 100 records in a deterministic position-hashed order, skipping trees unless `h.include_trees=true`. It returns the actual bare node kind plus a detached descriptor. The caller independently verifies loaded empty unmodified destination/support and authorization immediately before any write. No plant replaces another, spreads water or creates nutrients through this query.
### Interactions and finite effects
- `B.has_tag(value, tag) -> boolean`.
- `B.interactions(value) -> tag-keyed descriptor copies` with a `description`, and where applicable `cap` or `enabled=false`.
- `B.active(value, season) -> boolean`: establishment/visible-season gate, **not** an instruction to destroy winter-dormant perennial plants. Four spring woodland herbs gate to Spring; evergreen trees, mosses/lichens and persistent bracket fungi have broader windows.
- `B.pollinator(value, season) -> boolean`: actual flowering tag **and** flowering season. Oak, wind-pollinated grasses, nettle, mosses and fungi do not become nectar sources. Meadowsweet and dog rose represent pollen as well as nectar relations.
- `B.seeding(value, season) -> boolean`: qualitative seed-season window; succession additionally requires calendar maturity. This is not a physiological reproduction authorization or a replay queue.
- `B.edible(value, season) -> boolean`: **game berry permission only**, independent of wildlife browse; only bilberry/raspberry in Summer and blackberry in Summer/Autumn return true.
- `B.browse(value, season) -> boolean`: active wildlife relationship; not a universal diet rule. Wildlife owner must still choose an appropriate animal diet.
- `B.resin(value) -> boolean`: only the catalogue's pine/spruce/juniper resin tag. It grants no item or extraction callback.
- `B.nitrogen(value, current) -> suggested_delta`: only nitrogen-fixer-tagged taxa; `min(1,max(0,2-current))`, zero for invalid/negative current. Alder/Frankia, clover/rhizobia and cyanobacterial lichen partners are distinct biological associations. **Ecology owner must apply a per-site contribution/cooldown and metadata cap of two; repeatedly calling this pure query is not permission for unlimited fertilizer.**
- `B.root_fungi(value, tree, budget) -> transfer_ceiling`: only ectomycorrhizal **fungi** matching the supplied host, at most one and at most the finite positive existing budget. A nearby host can receive a bounded establishment advantage through that owner's already-budgeted resource transfer. Caller debits the same budget; the function creates nothing and never independently raises soil nitrogen. Tree ectomycorrhizal tags alone are not free fungal boosts. Saprotrophs receive no root-fungus boost.
`saprotroph` describes recycling existing litter/wood: any later benefit must consume existing substrate, not mint nutrients. `engineer` describes physical substrate/water retention or aquatic cover; it does not grant terraforming. `nursery` means conditional cover/microhabitat. `parasite` and `allelopathy` are **disabled descriptive interactions**: honey fungus/birch polypore do not infect player trees and bracken never kills neighboring plants. The module mutates no ecological state.
### Registration and harvest
`B.register_nodes(register_node)` accepts the content registrar taking **bare names**. It is idempotent and skips existing IDs, all thirteen trees and already registered definitions. It registers **79** nodes in an empty registry, no recipes/aliases/items/tree resources. Main loads the factory after lifecycle/seasons and calls registration after legacy diversity registration.
New terrestrial plants/fungi/shrubs are conventional crossed `plantlike` sprites with no meshes/sculptures. Surface aquatics are thin nodeboxes wholly inside ±0.5 of the node. They are not water; submerged sprites are not given liquid behavior or water groups. All are non-walkable and `buildable_to=false`. Ordinary herbs, mosses, fungi and lichens dig to their own node item. **All new shrubs dig to `hearth:sticks`**, so digging/replanting cannot erase a berry cooldown and mint a fresh fruit-bearing bush. Placement marks `player_placed=1`; normal construction/protection handling is supplied by the existing registrar. There is no new growth timer.
`B.harvest(pos, player) -> success_boolean, count_or_reason` is the only resource mutation API. Berry callbacks return the original wielded stack. It:
1. Validates finite integer bounded coordinates and a real player; looks up **current** `hearth.construction.allowed(pos, player, "use")` at callback time. Missing policy denies access; no permissive fallback. Existing policy owns reach, claim and protection checks.
2. Re-reads the loaded node and requires its exact canonical three-bush ID, then current `hearth.climate_at().season` and game fruiting season.
3. Checks persisted string metadata `botany_ready_at`, rejecting malformed/negative/nonfinite values. Missing metadata means initial fruit stock; future timestamps deny until ready.
4. Requires inventory room for `hearth:berries 2`; full inventory neither changes stock nor starts cooldown.
5. Locks the bush by writing a finite timestamp **one real day** ahead before inserting at most two berries. This blocks callback reentrancy. A completely rejected insertion restores the old timestamp; partial delivery consumes that cycle, never exceeds two or drops overflow into the world. Unexpected insertion results/errors leave the lock in place rather than duplicate stock.
The factory may precede construction publication, but harvest cannot. Time uses optional injected `api.now()` (numeric Unix seconds, for tests), otherwise `os.time()`. No offline catch-up or accumulating fruit: one ready bush gives at most two per cooldown even after months away, and cooldown metadata survives module reload. New callbacks are installed only on `bilberry`, `blackberry`, `raspberry`; generic existing berry-bush behavior is untouched.
Reasons: `invalid`, `denied`, `species`, `season`, `clock`, `metadata`, `cooldown`, `inventory`; success returns delivered count 1 or 2. No player health changes, medicinal effects, wild-consumption guidance or automatic fertilizer is added.
## Calendar integration and bounds (version 1)
Main must assign the pure namespace `hearth.lifecycle` **before** botany/content. This change does not edit bootstrap files owned by main. See [the exact lifecycle API and evidence](lifecycles.md). Succession consumes `plant(id)` and `phase(profile, 0, age_seconds)` with conservative upper-endpoint thresholds, retaining field-level evidence/fallback labels. Its root anchors, not the loaded activity cache, determine age. One real day is 86,400 seconds; one mean year is 365.2425 days. No ten-minute years, accelerated stage counters or nutrient age multipliers remain for catalogue plants/trees.
The existing scheduler still performs eight column probes/second, radius 12, twelve sampled heights, at most 96 seasonal swaps and one globally reserved full tree-plan operation per wall-clock second. The 8,192-entry continuity cache and 1,200-second gap still bound **loaded work**, stress and material decomposition; they no longer truncate biological age. Empty-gap colonization remains a coarse 90/180-active-second recruitment opportunity (woody gaps after 7,200), not a claim that a real seed germinates in minutes. New recruits start calendar age zero and must wait their full profile thresholds before young/mature transitions and propagation. Reconciliation never emerges terrain, scans the whole world or replays missed seasons.
### Root persistence and migration
- `succ_birth_at`: validated UTC epoch anchor; for a natural recruit this is actual recruitment time. `succ_lifecycle_version=1` and `succ_lifecycle_policy=calendar_v1_conservative_max` label this format and threshold policy.
- `succ_initial_age_seconds`: separately recorded **inferred establishment age**, zero for `natural_seed`. It avoids inventing negative/pre-epoch birthdays for ancient-looking generated vegetation. Biological age is initial age plus the entire difference between current epoch and birth anchor.
- `succ_lifecycle_origin`: `natural_seed`, `legacy_established` or `worldgen_established`. Legacy `succ_age` is retained unchanged, never multiplied or subtracted into a birthday. Migration anchors once at NOW; stage inference is a documented assumption, not historical observation. Seedlings infer their profile emergence threshold, young roots the juvenile threshold, mature roots and un-staged botanical stands their known maturity threshold. Unknown maturity infers zero, not a fabricated age.
- `succ_lifecycle_profile`, `succ_lifecycle_seed`: exact identity and persistent deterministic position seed. Version 1 intentionally uses profile maxima, not sampled thresholds; the stored seed reserves reproducibility without claiming a measured age distribution.
- `succ_last_observed_at`: durable high-water timestamp. Clock rollback pauses reconciliation until the clock catches up; it never resets birth or subtracts age. Malformed timestamps, seeds, profile identity or unknown versions fail closed without migration/reroll. Stage swaps preserve all root anchors. Components reference the root and do not age separately.
- `succ_seed_epoch`: next allowed present-time seeding opportunity, advanced **before** one attempt (trees one mean year, understory 30 days). Missed opportunities are not queued. Current seed season, maturity, habitat, loaded air and protection still apply. Unknown maturity does not authorize propagation. `succ_litter_epoch` similarly bounds mature litter attempts to 30 days.
Known unmodified botanical stands and soil-supported worldgen log roots adopt `worldgen_established`; they are not converted to seed sprites, assigned fake historical birthdays, expanded or killed on load. Existing mapgen canopies remain unmanaged timber. Species/generic log roots retain their node and mature-stage provenance. Every adoption checks source/support ownership, protection and markers. Load callbacks only mark/invalidate continuity; first bounded root observations initialize calendar anchors.
Winter/cold pauses visible growth transitions, seeding and loaded stress, **not calendar age**. On a later favorable visit an overdue plant may advance one stage; no historical season integration is claimed. Biennial root/rosette policy persists across winter, not instant flowering or death. All lifespan endpoints are informational; there is **no automatic age-based plant death**, including unknown-lived fungi/lichens. Historical growing seasons, seed banks, annual stem death and fungal fruitbody turnover need future species-specific models. Unfavorable habitat records stress but does not kill a calendar-managed organism using the former ten-minute threshold.
`S.habitat(pos)` is read-only. Its 5×5×5 stencil now exposes exact catalogue dimensions: finite `light`, `moisture`, `ph`, `nutrients`, depth-valued `water`, separate `near_water`, `ground`, semantic `substrate`, `freshwater`, `aquatic`, `host`, active browse count (`edible`, retained for wildlife compatibility), seasonal `pollination`, `nursery` and host-fungus IDs. pH is an explicit **game support-class approximation**: peat 4.5; podzol/granite 5; alpine turf/slate/sandstone/scree/stone/forest floor/bark/deadwood/litter 5.5; grass/soil/alluvium/sand/basalt 6.5; limestone 7.5. Missing support observations fail closed. Nursery-tagged natural cover slows accumulated habitat stress by 25%; it creates neither water nor nutrients. Fungal host observations use only nearby identified natural trees or recorded natural wood species, never player timber.
Material decomposition (generic dung/litter/deadwood/fungi) retains bounded loaded accounting; these are resource-cycle approximations, not living-organism years. Botanical fungi do not independently mint litter nutrients. Saprotrophs advance only an existing natural authorized substrate clock by at most 60 seconds per attempt; consumed material pays once through the capped nutrient path.
All nitrogen-fixer tags use a persisted two-unit **soil support** budget (`succ_fixed_nitrogen`) shared across restart/recolonization; additions require at least 90 calendar days and stop at local fertility four. New gorse, broom, red clover and birdsfoot trefoil use the same cap. Root fungi may transfer/debit at most one existing soil unit when an independently age-eligible seedling grows; they **never reduce calendar thresholds**. Failed growth debits nothing. Parasite/allelopathy remain disabled descriptors. Pollination is habitat, not a berry multiplier; harvest remains at most two berries per real-day cooldown.
### Owned full-block tree lifecycle
`D.tree_plan(species, stage)` supplies fresh full-block plans for all thirteen species. Succession validates integer offsets within ±4 horizontally and 0–9 vertically, at most 810 entries, no duplicate/foreign names, and a root. **One global tree-plan reservation per wall-clock second**, including denied attempts, covers plan construction/preflight/application regardless of player count. Old/new plan union work is bounded (at most two plans); there are no dynamic per-frame world tree registries.
Every destination and its immediate support must be loaded, unclaimed, externally unprotected and unmarked. New components require air; growth never silently replaces even natural groundcover. Existing components must match the root-key `succ_component`, species/stage expected node (season-canonicalized) and rotation. `succ_tree_stage` and `succ_species` persist on the root; components carry its plain coordinate key. Components never independently age or pay nutrients. A tree's own owned leaves do not shade its own root into automatic starvation; neighbouring crowns still shade recruits. Pine **and spruce** retain winter shade.
Automatic senescence/collapse from age is disabled. The owned-component cleanup mechanism remains for explicit root removal; it cannot clear unowned mapgen/player timber. Any protected/marked/foreign component blocks cleanup. Natural epiphytes losing an owned substrate participate in the same bounded preflight; foreign attachments block it.
`S.tree_removed(pos, oldnode) -> boolean` is the optional after-dig handoff. A standard `register_on_dignode` hook calls it when available. It validates an empty loaded root, old stage identity and every remaining matching owned component; it cannot clear unrelated mapgen/player wood. External removals that do not invoke this hook must explicitly call the handoff; a denied/unloaded cleanup is not queued/replayed. Unmanaged pre-existing mapgen tree canopies are not retroactively adopted as managed trees. The independently registered generic deadwood adoption remains intact.
## Focused validation and integration boundary
[tests/botany_spec.lua](../tests/botany_spec.lua) checks all 100 positive niche vectors and dimension rejections, exact uneven group counts, lifecycle Latin identities, legacy mappings, seasonal/host/water distinctions, registration ownership, finite interactions and denied/full/partial/reentrant harvest. Calendar-managed seedlings/juveniles do not supply mature flower habitat: biennial second-season policy needs both maturity and the current flower season.
Focused suites cover all 100 real selector outputs with controlled niche vectors, actual water/wood/pH sampling, calendar migration and rollback, no guessed mortality, bounded material effects, atomic block plans and cube-safe seasons. Require all named successes, final suite markers and empty stderr; Fengari exit zero alone can hide failures. Source is read from disk once into an in-memory loader to avoid SMB repeated-read overhead. Native-engine/asset/bootstrap integration and empirical gameplay remain main-owner verification work; controlled habitat vectors are not real-world distribution evidence.
Calendar expansion validation: **178 named passes**, empty stderr, all six final
markers: botany 38, diversity 15, biomes 26, seasons 16, ecology 9, succession 74.
Only the requested owned files were edited. The separately owned lifecycle suite
and schema/content/art/bootstrap integration are not included in this result.
Security review followed CodeGuard: centralized runtime authorization, deny-by-default missing policy, finite validated positions/time, persistent bounded harvest, inventory preflight and reentrancy lock. No credentials, certificates, cryptography, network operations or dynamic executable inputs are introduced.