-- Pure calendar policy, Lua 5.1+. No engine, clock reads, registrations or writes. -- Ranges describe populations or explicit policy fallbacks, never death deadlines. local L = {} local DAY, YEAR, MAX_CLOCK = 86400, 365.2425, 9007199254740991 local function finite(n) return type(n)=="number" and n==n and math.abs(n)=0 and n<=MAX_CLOCK end local function copy(t) if type(t)~="table" then return t end local out={}; for k,v in pairs(t) do out[k]=copy(v) end; return out end local function range(a,b,url,note,statistic) return {min=a,max=b,source_url=url or false,confidence=url and "source_reported" or "policy_fallback", note=note or "Unverified group policy; not a measured species range.",statistic=statistic or "policy_window"} end local function years(a,b,url,note,statistic) return range(a*YEAR,b*YEAR,url,note,statistic) end local WT="https://www.woodlandtrust.org.uk/trees-woods-and-wildlife/" local WILDLIFE="https://www.wildlifetrusts.org/wildlife-explorer/" local WIKI="https://en.wikipedia.org/wiki/" local ADW="https://animaldiversity.org/accounts/" local RSPB="https://www.rspb.org.uk/birds-and-wildlife/" local plants, animals = {}, {} local baseline={ trees={range(14,365),years(1,5),years(10,60),years(60,500)}, shrubs={range(14,365),years(1,2),years(2,10),years(5,80)}, grasses={range(7,90),range(30,90),range(90,365),years(2,10)}, herbs={range(7,90),range(90,365),range(365,730),years(2,10)}, ferns_mosses={range(30,730),years(1,2),years(2,5),false}, fungi={false,false,false,false}, aquatic={range(7,90),range(90,365),range(365,730),false}, lichens={false,years(1,5),false,false}, } local function plant_record(id,group,latin,pending) local b=baseline[group] plants[id]={id=id,kind="plant",group=group,latin=latin or false,pending=pending or false, germination_days=copy(b[1]),juvenile_days=copy(b[2]),maturity_days=copy(b[3]),lifespan_days=copy(b[4]), life_cycle=group=="fungi" and "not_applicable" or "perennial", life_cycle_confidence="policy_fallback",source_url=false,confidence="policy_fallback", evidence={},seasonal_policy="dormant_not_dead",age_origin="seed_or_propagule"} end -- Fixed snapshot of all 100 registered IDs; no catalogue factory is invoked here. local groups={ trees="oak birch pine willow alder beech aspen rowan hazel spruce", shrubs="bilberry blackberry raspberry elder blackthorn hawthorn dog_rose holly juniper guelder_rose", grasses="sedge bulrush meadow_fescue cocksfoot timothy sweet_vernal_grass yorkshire_fog tufted_hairgrass common_reed soft_rush", herbs="clover nettle heather bluebell yarrow foxglove meadowsweet wild_garlic cowslip wood_anemone", ferns_mosses="bracken lady_fern male_fern harts_tongue polypody sphagnum haircap_moss moss fork_moss star_moss", fungi="fly_agaric birch_bolete bay_bolete chanterelle wood_blewit shaggy_inkcap turkey_tail oyster_fungus honey_fungus birch_polypore", aquatic="duckweed yellow_waterlily white_waterlily water_crowfoot water_mint water_forgetmenot brooklime water_starwort hornwort pondweed", lichens="reindeer_lichen oakmoss old_mans_beard lichen dog_lichen lungwort_lichen sunburst_lichen hammered_shield cup_lichen pixie_cup", } for group,text in pairs(groups) do for id in text:gmatch("%S+") do plant_record(id,group) end end -- The added twenty are registered by botany, not by this pure module. local extra_plants={ {"dandelion","Taraxacum officinale"},{"daisy","Bellis perennis"},{"red_clover","Trifolium pratense"}, {"birdsfoot_trefoil","Lotus corniculatus"},{"ribwort_plantain","Plantago lanceolata"}, {"selfheal","Prunella vulgaris"},{"knapweed","Centaurea nigra"},{"oxeye_daisy","Leucanthemum vulgare"}, {"field_scabious","Knautia arvensis"},{"teasel","Dipsacus fullonum"},{"cow_parsley","Anthriscus sylvestris"}, {"garlic_mustard","Alliaria petiolata"},{"honeysuckle","Lonicera periclymenum","shrubs"}, {"ivy","Hedera helix","shrubs"},{"gorse","Ulex europaeus","shrubs"},{"broom","Cytisus scoparius","shrubs"}, {"crab_apple","Malus sylvestris","trees"},{"wild_cherry","Prunus avium","trees"}, {"field_maple","Acer campestre","trees"},{"holly_fern","Polystichum setiferum","ferns_mosses"}, } for _,r in ipairs(extra_plants) do plant_record(r[1],r[3] or "herbs",r[2],false) end local function evidence(p,field,url,note) p.evidence[field]={source_url=url,note=note,reviewed="2026-11-03"} p.source_url=p.source_url or url; p.confidence="partial_species_evidence" end local function field(p,name,value) p[name]=value; evidence(p,name,value.source_url,value.note) end -- All ten trees have distinct profiles. Unsourced timing fields retain their -- own policy_fallback label even when a different field has species evidence. local tree_rows={ {"oak","Quercus robur",40,60,500,1000,"https://www.forestryengland.uk/article/english-oak"}, {"birch","Betula pendula",10,20,60,90,"https://treesforlife.org.uk/into-the-forest/trees-plants-animals/trees/birch/birch-facts/"}, {"pine","Pinus sylvestris",15,30,150,300,WIKI.."Pinus_sylvestris"}, {"willow","Salix alba",5,15,60,120,WIKI.."Salix_alba"}, {"alder","Alnus glutinosa",10,20,60,160,WIKI.."Alnus_glutinosa"}, {"beech","Fagus sylvatica",10,30,300,500,WIKI.."Fagus_sylvatica"}, {"aspen","Populus tremula",10,20,80,200,WIKI.."Populus_tremula"}, {"rowan","Sorbus aucuparia",10,10,80,150,WIKI.."Sorbus_aucuparia"}, {"hazel","Corylus avellana",3,10,60,80,WT.."british-trees/a-z-of-british-trees/hazel/"}, {"spruce","Picea abies",20,40,200,532,WIKI.."Picea_abies"}, } for _,r in ipairs(tree_rows) do local p=plants[r[1]]; p.latin=r[2] p.maturity_days=years(r[3],r[4]) p.lifespan_days=years(r[5],r[6]) evidence(p,"natural_history",r[7],"Species reference; only separately attributed fields are verified numerical claims.") end field(plants.oak,"maturity_days",years(40,40,tree_rows[1][7],"Acorns usually begin around 40 years; approximate report, not a universal minimum.","approximate_onset")) field(plants.oak,"lifespan_days",years(500,1000,WIKI.."Quercus_robur","Long-lived survivors often exceed 500 years; some exceed 1000. Not a mortality distribution.","longevity_envelope")) field(plants.birch,"lifespan_days",years(60,90,tree_rows[2][7],"Typical birch longevity; page covers silver and downy birch and notes exceptional ages above 150.","typical_range")) field(plants.pine,"lifespan_days",years(150,300,tree_rows[3][7],"Normal reported lifespan; exceptional trees exceed this range.","typical_range")) field(plants.alder,"lifespan_days",years(60,160,tree_rows[5][7],"Composite envelope: Woodland Trust approximately 60; Wikipedia up to 160. Not typical 60-160 survival.","composite_envelope")) evidence(plants.alder,"lifespan_context",WT.."british-trees/a-z-of-british-trees/alder/","Approximately 60 years; contrasts with upper longevity reports.") field(plants.beech,"maturity_days",years(10,30,tree_rows[6][7],"Small seed quantities around 10 years, heavy crops at least 30; different reproductive milestones.","onset_to_heavy_crop")) field(plants.beech,"lifespan_days",years(300,500,tree_rows[6][7],"Undisturbed lifespan around 300; exceptional individual above 500.","longevity_envelope")) field(plants.aspen,"lifespan_days",years(80,200,tree_rows[7][7],"Upper endpoint 200 reported; lower endpoint 80 is unverified policy, not a sourced range.","policy_envelope")) plants.aspen.lifespan_days.confidence="policy_fallback" -- source supports only its upper limit field(plants.rowan,"maturity_days",years(10,10,tree_rows[8][7],"Reported maturity at about ten years.","approximate_onset")) field(plants.rowan,"lifespan_days",years(80,150,tree_rows[8][7],"Often not older than 80 in warmer regions; occasional 150.","longevity_envelope")) field(plants.hazel,"lifespan_days",years(60,80,tree_rows[9][7],"Uncoppiced upper limit 80 reported; lower endpoint 60 is policy. Coppice stools can persist centuries.","policy_envelope")) plants.hazel.lifespan_days.confidence="policy_fallback" field(plants.spruce,"lifespan_days",years(200,532,tree_rows[10][7],"532-year ring-dated individual; lower endpoint 200 is policy, not sourced. Never use clonal root ages as stem ages.","policy_envelope")) plants.spruce.lifespan_days.confidence="policy_fallback" for _,id in ipairs({"pine","spruce","holly","ivy","gorse","holly_fern","harts_tongue","polypody"}) do plants[id].seasonal_policy="retain_foliage_policy" end for _,id in ipairs({"fungi","lichens","ferns_mosses"}) do for _,p in pairs(plants) do if p.group==id then p.age_origin="propagule_or_colony" end end end local garlic=WILDLIFE.."wildflowers/garlic-mustard" plants.garlic_mustard.life_cycle="biennial"; plants.garlic_mustard.life_cycle_confidence="source_reported" evidence(plants.garlic_mustard,"life_cycle",garlic,"First-season leaves retained over winter; flowers in second spring.") plants.garlic_mustard.maturity_days=range(365,730,false,"Second-season policy window; not an exact source-measured number of days.") plants.garlic_mustard.lifespan_days=range(365,730,false,"Two-season policy; do not kill at the first frost.") local foxglove=WILDLIFE.."wildflowers/foxglove" plants.foxglove.life_cycle="biennial"; plants.foxglove.life_cycle_confidence="source_reported" evidence(plants.foxglove,"life_cycle",foxglove,"Flower stems in second year; biennial representation, not a guaranteed death date.") plants.foxglove.maturity_days=copy(plants.garlic_mustard.maturity_days) -- Registered teasel remains explicitly unverified rather than inventing a citation. plants.teasel.life_cycle="biennial" local function animal_record(id,latin,path,diet,movement,life_a,life_b) local url=WILDLIFE..path local p={id=id,latin=latin,kind="animal",pending=false, maturity_days=false,gestation_days=false,incubation_days=false,juvenile_days=false,lifespan_days=false, breeding_months=false,birth_count=false,birth_count_unit="young_per_litter",reproduction="unknown", diet=diet,movement=movement,source_url=url,confidence="partial_species_evidence",evidence={}, age_origin="birth_or_hatching",reproduction_enabled=false} animals[id]=p evidence(p,"diet",url,diet); evidence(p,"movement",url,movement) if life_a then field(p,"lifespan_days",years(life_a,life_b,url,"Reported average lifespan, not maximum age or a death deadline.","reported_average")) end return p end animal_record("rabbit","Oryctolagus cuniculus","mammals/rabbit","grass and other vegetation","hopping; underground warrens",3,3) animal_record("red_squirrel","Sciurus vulgaris","mammals/red-squirrel","nuts and conifer seeds","tree climbing and branch leaping; no hibernation",6,6) animal_record("bank_vole","Myodes glareolus","mammals/bank-vole","fruit, nuts and small insects","agile; shallow burrows; no hibernation",.5,1.5) animal_record("wood_mouse","Apodemus sylvaticus","mammals/wood-mouse","berries and seeds","mostly nocturnal; climbing and food caching",1,1) animal_record("hedgehog","Erinaceus europaeus","mammals/european-hedgehog","invertebrates including beetles and earthworms","nocturnal; winter hibernation",2,3) animal_record("badger","Meles meles","mammals/european-badger","worms, fruit, plants, eggs and small animals","digging; family groups in setts",5,8) animal_record("roe_deer","Capreolus capreolus","mammals/roe-deer","tree and shrub buds and leaves; herbs and grasses","solitary in summer; loose winter groups",7,7) animal_record("stoat","Mustela erminea","mammals/stoat","small rodents and rabbits","bounding gait; active day and night",2,5) animal_record("weasel","Mustela nivalis","mammals/weasel","voles, mice and small birds","low running gait; enters rodent burrows",2,2) animal_record("otter","Lutra lutra","mammals/european-otter","fish, amphibians, waterbirds and crustaceans","swimming; sheltered holts",5,10) local function bird(id,latin,diet,movement,life) local p=animal_record(id,latin,"unused",diet,movement) p.source_url=RSPB..id:gsub("_","-"); p.evidence={} evidence(p,"diet",p.source_url,diet); evidence(p,"movement",p.source_url,movement) p.reproduction="egg"; p.birth_count_unit="eggs_per_clutch" if life then field(p,"lifespan_days",years(life,life,p.source_url,"Reported average, not maximum or mandatory death age.","reported_average")) end end bird("robin","Erithacus rubecula","worms, insects, other invertebrates, seeds and fruit","hopping; territorial; mostly UK resident") bird("blackbird","Turdus merula","worms, insects, berries and fruit","ground hopping; some seasonal migration",3) bird("blue_tit","Cyanistes caeruleus","insects, caterpillars, seeds and nuts","clinging and flight; winter feeding flocks",3) bird("barn_owl","Tyto alba","mice, voles, shrews and small birds","low silent hunting flight, often dawn or dusk") bird("mallard","Anas platyrhynchos","seeds, plants, insects and shellfish","swimming and flight; resident or migratory") animal_record("common_frog","Rana temporaria","amphibians/common-frog","invertebrates and smaller amphibians","hopping and swimming; winter shelter",5,10) animal_record("smooth_newt","Lissotriton vulgaris","amphibians/smooth-newt","land invertebrates; aquatic crustaceans, molluscs and tadpoles","aquatic breeding; nocturnal land activity; winter shelter",6,6) animal_record("brown_trout","Salmo trutta","freshwater-fish/brown-trout","insect larvae, small fish and flying insects","river swimming; freshwater resident form",15,20) animal_record("bumblebee","Bombus terrestris","invertebrates/bees-and-wasps/buff-tailed-bumblebee","floral nectar and pollen","flight; underground colony; some winter-active colonies") local worm=animal_record("earthworm","Lumbricus terrestris","unused","dead leaf litter and mineral soil","vertical burrows; moist-surface movement") worm.source_url=WIKI.."Lumbricus_terrestris"; worm.evidence={} evidence(worm,"diet",worm.source_url,worm.diet); evidence(worm,"movement",worm.source_url,worm.movement) evidence(worm,"lifespan_context",worm.source_url,"Natural lifespan unknown; six years in captivity is not a wild lifespan range.") field(worm,"maturity_days",years(1,2,worm.source_url,"At least one year development; reproductive maturity in second year.","development_window")) worm.reproduction="paired_hermaphrodite_cocoon" animal_record("hare","Lepus europaeus","mammals/brown-hare","vegetation and young woody bark","bounding; surface forms, not rabbit warrens",2,4) animal_record("fox","Vulpes vulpes","mammals/red-fox","small mammals, birds, amphibians, worms and fruit","mostly nocturnal; territorial scent marking",2,3) -- Generic legacy deer is deliberately NOT silently reidentified as roe deer. animals.deer={id="deer",kind="animal",latin=false,pending=false,confidence="unverified_identity",source_url=false, evidence={},maturity_days=false,juvenile_days=false,gestation_days=false,incubation_days=false, lifespan_days=false,breeding_months=false,birth_count=false,reproduction="unknown",reproduction_enabled=false} local function count(id,a,b) local p=animals[id]; field(p,"birth_count",range(a,b,p.source_url,"Source-reported litter or clutch count; not guaranteed survivors.","offspring_count")) end count("rabbit",3,7); count("bank_vole",3,5); count("wood_mouse",4,8); count("stoat",6,12) count("weasel",3,6); count("roe_deer",2,3); count("hare",2,4) count("robin",4,5); count("blackbird",3,4) local function months(id,list,url,note) local p=animals[id]; p.breeding_months=list; evidence(p,"breeding_months",url or p.source_url,note) end months("roe_deer",{7,8},nil,"Mating July-August; delayed implantation; births May-June, not immediate gestation completion.") animals.roe_deer.reproduction="delayed_implantation" animals.stoat.reproduction="delayed_implantation" evidence(animals.stoat,"reproduction",animals.stoat.source_url,"Summer mating, implantation delayed until following spring; no unsupported numeric gestation.") months("blackbird",{3,4,5,6,7,8},nil,"UK nesting usually March-August, not a universal physiological limit.") months("blue_tit",{4,5,6},nil,"UK nesting April-June; normally one brood annually.") local hare=animals.hare field(hare,"maturity_days",range(236,365.2425,ADW.."Lepus_europaeus/","Sexual maturity reported from about eight months to one year; conservative runtime uses the upper endpoint.","reported_range")) field(hare,"gestation_days",range(30,42,ADW.."Lepus_europaeus/","Reported gestation range; several litters can occur during the breeding season.","reported_range")) field(hare,"juvenile_days",range(30,30,ADW.."Lepus_europaeus/","About one month to weaning/independence; leverets are precocial at birth.","approximate_duration")) field(hare,"birth_count",range(1,8,ADW.."Lepus_europaeus/","Reported range is 1-8 leverets; runtime uses the conservative lower endpoint.","offspring_count")) months("hare",{1,2,3,4,5,6,7},ADW.."Lepus_europaeus/","Midwinter (January/February) through midsummer in the cited northern-range account.") hare.reproduction="live_birth"; hare.reproduction_enabled=true hare.breeding_seasons={Winter=true,Spring=true,Summer=true}; hare.season_policy="sourced_midwinter_to_midsummer_coarse_seasons" hare.postpartum_days=30; hare.care_confidence="source_reported" local fox=animals.fox field(fox,"maturity_days",range(304,304,ADW.."Vulpes_vulpes/","ADW reports maturity at about ten months (304-day entry); both sexes are reported similarly.","reported_average")) field(fox,"gestation_days",range(51,53,ADW.."Vulpes_vulpes/","Typical gestation is 51-53 days; the account also notes a broader 49-56-day span.","typical_range")) field(fox,"juvenile_days",range(56,70,ADW.."Vulpes_vulpes/","Nursing/weaning lasts about 56-70 days; leaving the den at 4-5 weeks is not independence.","weaning")) field(fox,"birth_count",range(1,9,ADW.."Vulpes_vulpes/","ADW's summary table gives 1-9 pups, while its prose gives 1-13; use the narrower table range and lower endpoint.","source_disagreement_envelope")) months("fox",{12,1,2},ADW.."Vulpes_vulpes/","Central-range mating is reported in January-February; winter is the conservative coarse runtime window.") fox.reproduction="live_birth"; fox.reproduction_enabled=true fox.breeding_seasons={Winter=true}; fox.season_policy="central_range_winter_policy" fox.postpartum_days=300; fox.care_confidence="policy_fallback" -- Winter dormancy is a coarse loaded-world behavior, distinct from death or -- a measured metabolic rate. These taxa retain their saved residents. animals.hedgehog.winter_dormant=true animals.common_frog.winter_dormant=true animals.smooth_newt.winter_dormant=true local rabbit=animals.rabbit field(rabbit,"gestation_days",range(30,37,ADW.."Oryctolagus_cuniculus/","ADW reproduction reports 30-37 days; approximately 30 typical.","reported_range")) field(rabbit,"juvenile_days",range(22,31,ADW.."Oryctolagus_cuniculus/","Weaning age, not sexual maturity.","weaning")) -- ADW rabbit maturity prose (8 months) conflicts with an embedded 730-day -- value. Keep maturity unknown rather than laundering that discrepancy. evidence(rabbit,"maturity_context",ADW.."Oryctolagus_cuniculus/","Conflicting maturity values on this page: not accepted as a verified threshold.") months("rabbit",{1,2,3,4,5,6,7,8,9,10,11,12},ADW.."Oryctolagus_cuniculus/","Can breed year-round, mostly first half of year; habitat/health still required.") local squirrel=animals.red_squirrel field(squirrel,"gestation_days",range(38,39,ADW.."Sciurus_vulgaris/","Gestation 38-39 days.","reported_range")) field(squirrel,"juvenile_days",range(56,70,ADW.."Sciurus_vulgaris/","Independence eight to ten weeks, not sexual maturity.","independence")) field(squirrel,"maturity_days",range(296,365.2425,ADW.."Sciurus_vulgaris/","Composite envelope of female 296-day entry, male 320-day entry and approximately one-year narrative.","composite_envelope")) -- Source gives birth/litter months, not conception months: no month override. local vole=animals.bank_vole field(vole,"maturity_days",range(42,56,WT.."animals/mammals/bank-vole/","Females six weeks, males eight weeks; use sex-specific values if available.","sex_envelope")) field(vole,"juvenile_days",range(25,25,WT.."animals/mammals/bank-vole/","Weaning about 25 days.","weaning")) local mouse=animals.wood_mouse field(mouse,"gestation_days",range(21,26,ADW.."Apodemus_sylvaticus/","Gestation range 21-26 days.","reported_range")) field(mouse,"juvenile_days",range(21,21,ADW.."Apodemus_sylvaticus/","Weaning at three weeks.","weaning")) field(mouse,"maturity_days",range(65,71,ADW.."Apodemus_sylvaticus/","Male 65-day and female 71-day entries; narrative approximately two months.","sex_envelope")) field(animals.common_frog,"larval_days",range(98,98,animals.common_frog.source_url,"About 14 weeks AFTER hatching to froglet; not maturity or egg incubation.","metamorphosis")) animals.common_frog.reproduction="external_eggs"; animals.smooth_newt.reproduction="eggs" animals.brown_trout.reproduction="external_eggs"; animals.bumblebee.reproduction="colony_caste_dependent" -- Additional retrieved reproductive accounts, reviewed 2026-11-03. Registration -- status is independent of evidence. A lifespan is NOT a breeding prerequisite. field(vole,"gestation_days",range(17,24,ADW.."Myodes_glareolus/","17-24 days, mean 21; upper endpoint includes lactation delay.","reported_range")) months("bank_vole",{4,5,6,7,8,9},ADW.."Myodes_glareolus/","Late April-September; runtime uses conservative Spring/Summer season policy.") months("wood_mouse",{2,3,4,5,6,7,8,9,10},WIKI.."Wood_mouse","February-October in this account; ADW instead says March-early winter.") local blackbird=animals.blackbird field(blackbird,"maturity_days",years(1,1,ADW.."Turdus_merula/","Usually starts mating at one year.","approximate_onset")) field(blackbird,"incubation_days",range(13,14,ADW.."Turdus_merula/","13-day prose and 14-day summary; conservative composite.","composite_envelope")) field(blackbird,"juvenile_days",range(13,14,ADW.."Turdus_merula/","Leaving nest at 13-14 days; NOT full independence.","fledging")) local mallard=animals.mallard field(mallard,"maturity_days",range(365,365,ADW.."Anas_platyrhynchos/","Both sexes reported at 365 days; yearling breeding.","reported_average")) field(mallard,"incubation_days",range(26,28,ADW.."Anas_platyrhynchos/","Time to hatching 26-28 days.","reported_range")) field(mallard,"birth_count",range(9,13,ADW.."Anas_platyrhynchos/","Typical hen lays 9-13 eggs; not guaranteed survivors.","clutch_count")) field(mallard,"juvenile_days",range(50,60,WIKI.."Mallard","50-60 days to flight; not reproductive maturity.","fledging")) local blue=animals.blue_tit local diary="https://www.bto.org/understanding-birds/articles/blue-tit-diary" field(blue,"incubation_days",range(14,14,diary,"About two weeks; approximate, not an exact physiological constant.","approximate_duration")) field(blue,"birth_count",range(8,10,diary,"Majority of clutches contain 8-10 eggs; larger clutches exist.","typical_clutch")) field(blue,"juvenile_days",range(18,21,diary,"Fledging 18-21 days; parents feed for further weeks.","fledging")) evidence(blue,"maturity_context",diary,"Usually breeds in spring after hatching; no exact day threshold accepted. Caterpillar provisioning absent, so disabled.") field(animals.common_frog,"maturity_days",years(3,3,WIKI.."Common_frog","Sexual maturity after three years in this account; local variation exists.","approximate_onset")) field(animals.common_frog,"incubation_days",range(14,21,WIKI.."Common_frog","Eggs hatch after 2-3 weeks; temperature-dependent, NOT froglet age.","reported_range")) evidence(animals.brown_trout,"reproduction_context",WIKI.."Brown_trout","About 2000 eggs/kg; requires female mass, gravel redd and temperature-dependent development, not a fixed litter.") evidence(animals.bumblebee,"reproduction_context",WIKI.."Bombus_terrestris","Queen-founded colonies, diploid females/haploid males, larvae and pupae; caste-specific care cannot use a mammalian litter timer.") -- Explicit seasonal/care policies, NOT source-reported month precision or a -- claim to model nests/physiology. Upper dependency endpoints avoid postpartum -- immediate breeding; bird care lock is deliberately longer than fledging. for _,id in ipairs({"bank_vole","wood_mouse","blackbird","mallard"}) do local p=animals[id] p.reproduction_enabled=true p.reproduction=(id=="bank_vole" or id=="wood_mouse") and "live_birth" or "egg" p.breeding_seasons={Spring=true,Summer=true} p.season_policy="conservative_temperate_spring_summer" p.postpartum_days=math.max(21,p.juvenile_days.max)+(p.reproduction=="egg" and 21 or 0) p.care_confidence="policy_fallback" end local function id_ok(id) return type(id)=="string" and #id<=64 and id:match("^[a-z][a-z_]*$")~=nil end function L.plant(id,group) if not id_ok(id) then return nil,"id" end if plants[id] then if group~=nil and group~=plants[id].group then return nil,"group" end return copy(plants[id]) end return nil,"unknown_species" -- never fabricate an identity from a group hint end function L.animal(id) if not id_ok(id) then return nil,"id" end if not animals[id] then return nil,"unknown_species" end return copy(animals[id]) end function L.ids(kind) local out={}; local src=kind=="plant" and plants or kind=="animal" and animals if not src then return nil,"kind" end for id in pairs(src) do out[#out+1]=id end; table.sort(out); return out end local function valid_range(r) return type(r)=="table" and finite(r.min) and finite(r.max) and r.min>=0 and r.max>=r.min and r.max<=1000000 end -- Stable numeric seed, field salt <=64 bytes, bounded floating-point arithmetic. -- Reproducible selection only; NOT cryptography and never math.randomseed(). function L.duration(r,seed,salt) if not valid_range(r) then return nil,"range" end if not finite(seed) or seed<0 or seed>2147483646 or seed%1~=0 then return nil,"seed" end if type(salt)~="string" or #salt>64 then return nil,"salt" end local h=seed+1.0 for i=1,#salt do h=(h*131.0+salt:byte(i))%2147483647.0 end h=(h*48271.0)%2147483647.0 return r.min+(r.max-r.min)*(h/2147483647.0) end local durations={"germination_days","juvenile_days","maturity_days","lifespan_days","gestation_days","incubation_days","larval_days"} function L.resolve(profile,seed) if type(profile)~="table" or not id_ok(profile.id) then return nil,"profile" end if not finite(seed) or seed<0 or seed>2147483646 or seed%1~=0 then return nil,"seed" end local out=copy(profile); out.selected_days={} for _,name in ipairs(durations) do if profile[name] then local n,err=L.duration(profile[name],seed,profile.id..":"..name) if not n then return nil,err end out.selected_days[name]=n end end -- Overlapping stage ranges must not produce maturity before dependency or -- germination. Clamp to the preceding selected endpoint, never outside a range. local previous=0 for _,name in ipairs({"germination_days","juvenile_days","maturity_days","lifespan_days"}) do local n=out.selected_days[name] if n then n=math.max(previous,n) if n>profile[name].max then return nil,"profile_order" end out.selected_days[name]=n; previous=n end end out.selection_seed=seed; return out end function L.elapsed(previous,now,work_cap_seconds) if not clock(previous) or not clock(now) then return nil,"clock" end if nowr.max then return nil end return selected end return r.max -- conservative unsampled policy, not a random draw on each read end function L.phase(profile,born_at,now) if type(profile)~="table" or (profile.kind~="plant" and profile.kind~="animal") then return nil,"profile" end if not clock(born_at) or not clock(now) then return nil,"clock" end if nowj) or (j and m and j>m) or (m and life and m>life) then return nil,"profile_order" end local phase="unknown" if profile.kind=="plant" and g and age=life or false return {phase=phase,age_days=age,age_seconds=now-born_at,beyond_lifespan_range=beyond, -- A population average or record age must NEVER be an automatic death. death_due=false,reproductive_ready=false,confidence=profile.confidence} end function L.seasonal(profile,season) if type(profile)~="table" or profile.kind~="plant" then return nil,"profile" end if not ({Spring=true,Summer=true,Autumn=true,Winter=true})[season] then return nil,"season" end return {dormant=season=="Winter" and profile.seasonal_policy=="dormant_not_dead",death_due=false, confidence="policy_fallback"} -- presentation only; not a frost or survival model end function L.breeding_window(profile,month) if type(profile)~="table" or profile.kind~="animal" then return nil,"profile" end if not finite(month) or month%1~=0 or month<1 or month>12 then return nil,"month" end local list=profile.breeding_months if list==false or list==nil then return {known=false,within_window=false,eligible=false} end if type(list)~="table" or #list>12 then return nil,"profile" end local allowed=false for _,m in ipairs(list) do if not finite(m) or m%1~=0 or m<1 or m>12 then return nil,"profile" end if m==month then allowed=true end end return {known=true,within_window=allowed,eligible=false} -- window alone never authorizes reproduction end L.VERSION=1; L.SECONDS_PER_DAY=DAY; L.DAYS_PER_YEAR=YEAR; L.MAX_CLOCK=MAX_CLOCK return L