This commit is contained in:
2026-11-05 09:57:11 -05:00
parent d34388bf2c
commit 89e392cc2e
81 changed files with 12410 additions and 1135 deletions
+269
View File
@@ -0,0 +1,269 @@
-- Standalone Lua 5.1+; policy only, never starts Luanti.
local target=assert(arg[1],"lifecycle.lua path required")
local L=dofile(target)
local passed=0
local function test(label,fn)
local ok,err=pcall(fn); assert(ok,label..": "..tostring(err))
passed=passed+1; print("ok "..passed.." - "..label)
end
local function eq(a,b) assert(a==b,tostring(a).." ~= "..tostring(b)) end
local function url(s) return type(s)=="string" and s:match("^https://[^/]+/.+") end
local DAY,YEAR=86400,365.2425
local function at(p,days) return assert(L.phase(p,0,days*DAY)) end
local new_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",ivy="Hedera helix",gorse="Ulex europaeus",broom="Cytisus scoparius",
crab_apple="Malus sylvestris",wild_cherry="Prunus avium",field_maple="Acer campestre",holly_fern="Polystichum setiferum",
}
local new_animals="rabbit red_squirrel bank_vole wood_mouse hedgehog badger roe_deer stoat weasel otter robin blackbird blue_tit barn_owl mallard common_frog smooth_newt brown_trout bumblebee earthworm"
test("module constants retain unaccelerated real seconds",function()
eq(L.VERSION,1); eq(L.SECONDS_PER_DAY,DAY); eq(L.DAYS_PER_YEAR,YEAR)
eq(at(L.plant("oak"),1).age_seconds,86400)
end)
test("all hundred actual catalogue records receive group baselines",function()
local path=target:gsub("[^/\\]+$","botany.lua")
local B=dofile(path)({},{}); local records=B.list(); eq(#records,100)
for _,r in ipairs(records) do
local p=assert(L.plant(r.id,r.group)); eq(p.group,r.group); eq(p.pending,false)
assert(p.germination_days~=nil and p.juvenile_days~=nil and p.maturity_days~=nil and p.lifespan_days~=nil)
assert(L.phase(p,0,0),r.id); assert(L.resolve(p,731104),r.id)
end
end)
test("twenty registered plant IDs preserve exact botanical contract",function()
local count=0
for id,latin in pairs(new_plants) do
local p=assert(L.plant(id)); eq(p.latin,latin); eq(p.pending,false); count=count+1
end
eq(count,20); eq(#L.ids("plant"),100)
end)
test("twenty registered animals plus three legacy identity slots",function()
local count=0
for id in new_animals:gmatch("%S+") do
local p=assert(L.animal(id)); eq(p.id,id); eq(p.pending,false)
assert(type(p.latin)=="string" and url(p.source_url)); count=count+1
end
eq(count,20); eq(#L.ids("animal"),23)
for _,id in ipairs({"hare","fox","deer"}) do eq(L.animal(id).pending,false) end
end)
test("ten trees expose species evidence without laundering fallback timings",function()
local count=0
for id in ("oak birch pine willow alder beech aspen rowan hazel spruce"):gmatch("%S+") do
local p=L.plant(id,"trees"); assert(p.latin and url(p.source_url))
assert(p.germination_days.confidence=="policy_fallback")
assert(p.juvenile_days.confidence=="policy_fallback")
assert(p.maturity_days.min>=3*YEAR); assert(p.lifespan_days.min>=60*YEAR)
count=count+1
end
eq(count,10); eq(L.plant("birch").maturity_days.confidence,"policy_fallback")
eq(L.plant("alder").maturity_days.confidence,"policy_fallback")
end)
test("verified oak onset is forty real years not forty seconds",function()
local p=L.plant("oak"); eq(p.maturity_days.min,40*YEAR)
assert(url(p.maturity_days.source_url)); eq(p.maturity_days.confidence,"source_reported")
eq(at(p,30).phase,"seed"); eq(at(p,40*YEAR-1).phase,"juvenile")
eq(at(p,40*YEAR).phase,"mature"); eq(at(p,40*YEAR).reproductive_ready,false)
end)
test("verified rowan and beech distinguish onset and heavy crops",function()
eq(L.plant("rowan").maturity_days.max,10*YEAR)
local p=L.plant("beech"); eq(p.maturity_days.min,10*YEAR); eq(p.maturity_days.max,30*YEAR)
eq(p.maturity_days.statistic,"onset_to_heavy_crop")
end)
test("alder composite longevity is not a universal hundred-year constant",function()
local p=L.plant("alder"); eq(p.lifespan_days.min,60*YEAR); eq(p.lifespan_days.max,160*YEAR)
eq(p.lifespan_days.statistic,"composite_envelope"); assert(url(p.evidence.lifespan_context.source_url))
end)
test("all source-reported numerical claims carry field-level references",function()
local fields={"germination_days","juvenile_days","maturity_days","lifespan_days","gestation_days","incubation_days","larval_days","birth_count"}
local claims=0
for _,kind in ipairs({"plant","animal"}) do for _,id in ipairs(L.ids(kind)) do
local p=kind=="plant" and L.plant(id) or L.animal(id)
for _,name in ipairs(fields) do
local r=p[name]
if r then
assert(r.min>=0 and r.max>=r.min and r.max<math.huge)
assert(type(r.note)=="string" and #r.note>0)
if r.confidence=="source_reported" then
assert(url(r.source_url)); assert(url(p.evidence[name].source_url)); claims=claims+1
else eq(r.confidence,"policy_fallback") end
end
end
for _,e in pairs(p.evidence) do assert(url(e.source_url) and #e.note>0); eq(e.reviewed,"2026-11-03") end
end end
assert(claims>=35)
end)
test("verified thirty-day rabbit value is gestation not maturity",function()
local p=L.animal("rabbit"); eq(p.gestation_days.min,30); eq(p.gestation_days.max,37)
eq(p.maturity_days,false); eq(at(p,30).phase,"dependent")
assert(p.evidence.maturity_context.note:find("Conflicting",1,true))
eq(at(p,30).reproductive_ready,false)
end)
test("bank vole maturity weeks differ from weaning days",function()
local p=L.animal("bank_vole"); eq(p.juvenile_days.max,25)
eq(p.maturity_days.min,42); eq(p.maturity_days.max,56)
eq(at(p,24).phase,"dependent"); eq(at(p,30).phase,"juvenile"); eq(at(p,56).phase,"mature")
end)
test("wood mouse and squirrel retain independently verified gestation",function()
eq(L.animal("wood_mouse").gestation_days.min,21); eq(L.animal("wood_mouse").gestation_days.max,26)
eq(L.animal("red_squirrel").gestation_days.min,38); eq(L.animal("red_squirrel").gestation_days.max,39)
eq(at(L.animal("wood_mouse"),30).phase,"juvenile")
end)
test("frogs are not sexually mature at metamorphosis",function()
local p=L.animal("common_frog"); eq(p.larval_days.max,98); eq(p.maturity_days.max,3*YEAR)
eq(p.incubation_days.min,14); eq(at(p,98).phase,"juvenile"); eq(at(p,98).reproductive_ready,false)
end)
test("earthworm natural lifespan stays unknown and maturity takes years",function()
local p=L.animal("earthworm"); eq(p.lifespan_days,false)
eq(p.maturity_days.min,YEAR); eq(p.maturity_days.max,2*YEAR)
eq(at(p,30).phase,"juvenile"); eq(p.reproduction,"paired_hermaphrodite_cocoon")
end)
test("generic deer cannot inherit a roe deer life history",function()
local p=L.animal("deer"); eq(p.latin,false); eq(p.source_url,false); eq(p.maturity_days,false)
eq(p.confidence,"unverified_identity"); assert(L.animal("roe_deer").latin)
end)
test("winter appearance never declares death including annual representation",function()
for _,id in ipairs({"dandelion","daisy","garlic_mustard","foxglove"}) do
local p=L.plant(id); local s=L.seasonal(p,"Winter"); eq(s.dormant,true); eq(s.death_due,false)
end
local p=L.plant("daisy"); p.life_cycle="annual"
eq(L.seasonal(p,"Winter").death_due,false)
eq(L.seasonal(L.plant("pine"),"Winter").dormant,false)
eq(L.seasonal(L.plant("daisy"),"Spring").dormant,false)
end)
test("biennial leaves survive their first winter",function()
local p=L.plant("garlic_mustard"); eq(p.life_cycle,"biennial")
eq(p.life_cycle_confidence,"source_reported"); eq(at(p,200).death_due,false)
eq(L.seasonal(p,"Winter").death_due,false)
eq(L.plant("teasel").life_cycle_confidence,"policy_fallback")
end)
test("averages and record ages are never mandatory death deadlines",function()
for _,p in ipairs({L.animal("rabbit"),L.plant("oak"),L.animal("brown_trout")}) do
local s=at(p,p.lifespan_days.max+1); eq(s.beyond_lifespan_range,true); eq(s.death_due,false)
end
end)
test("age retains unloaded calendar gaps while work is bounded separately",function()
local gap=40*YEAR*DAY; local e=L.elapsed(100,100+gap,8)
eq(e.elapsed_seconds,gap); eq(e.work_seconds,8); eq(e.deferred_seconds,gap-8)
eq(L.phase(L.plant("oak"),100,100+gap).phase,"mature")
eq(L.elapsed(100,100,0).elapsed_seconds,0)
end)
test("clock reversal is explicit and never silently rebaselines age",function()
local p=L.plant("oak"); local value,reason=L.phase(p,100,99)
eq(value,nil); eq(reason,"clock_reversed")
value,reason=L.elapsed(100,99,8); eq(value,nil); eq(reason,"clock_reversed")
eq(L.phase(p,100,100).age_seconds,0)
end)
test("NaN infinities negatives strings and beyond-safe clocks fail closed",function()
local p=L.plant("oak")
for _,v in ipairs({0/0,math.huge,-math.huge,-1,"100",L.MAX_CLOCK+2,1e300}) do
eq(L.phase(p,v,100),nil); eq(L.phase(p,0,v),nil)
eq(L.elapsed(0,v,8),nil); eq(L.elapsed(0,100,v),nil)
end
eq(L.phase(p,nil,100),nil); eq(L.elapsed(0,100,nil),nil)
end)
test("largest valid age is constant-work and is not truncated",function()
local s=assert(L.phase(L.plant("oak"),0,L.MAX_CLOCK))
eq(s.age_seconds,L.MAX_CLOCK); assert(s.age_days>1000000000); eq(s.death_due,false)
local e=assert(L.elapsed(0,L.MAX_CLOCK,1)); eq(e.work_seconds,1); eq(e.elapsed_seconds,L.MAX_CLOCK)
end)
test("duration selection is deterministic bounded and salt dependent",function()
local r={min=10,max=40}
local a=L.duration(r,731104,"oak:maturity_days"); eq(a,L.duration(r,731104,"oak:maturity_days"))
assert(a>=10 and a<=40); assert(a~=L.duration(r,731105,"oak:maturity_days"))
assert(a~=L.duration(r,731104,"birch:maturity_days")); eq(L.duration({min=30,max=30},0,""),30)
end)
test("invalid duration ranges and seeds are rejected",function()
for _,r in ipairs({{min=-1,max=2},{min=3,max=2},{min=0,max=math.huge},{min=0,max=1e300},{min=0/0,max=2},{}}) do
eq(L.duration(r,0,"x"),nil)
end
for _,seed in ipairs({-1,.5,2147483647,math.huge,0/0,"1"}) do
eq(L.duration({min=1,max=2},seed,"x"),nil); eq(L.resolve(L.animal("deer"),seed),nil)
end
eq(L.duration({min=1,max=2},0,string.rep("x",65)),nil)
end)
test("resolved profiles preserve range provenance and ordered stages",function()
for _,kind in ipairs({"plant","animal"}) do for _,id in ipairs(L.ids(kind)) do
local p=kind=="plant" and L.plant(id) or L.animal(id)
for _,seed in ipairs({0,1,731104,2147483646}) do
local r=assert(L.resolve(p,seed)); eq(r.selection_seed,seed)
local previous=0
for _,name in ipairs({"germination_days","juvenile_days","maturity_days","lifespan_days"}) do
local n=r.selected_days[name]
if n then assert(n>=p[name].min and n<=p[name].max and n>=previous); previous=n end
end
assert(L.phase(r,0,100)); eq(p.selected_days,nil)
end
end end
end)
test("profiles nested evidence lists and IDs are fresh defensive copies",function()
local p=L.plant("oak"); p.maturity_days.min=1; p.evidence.maturity_days.note="changed"
eq(L.plant("oak").maturity_days.min,40*YEAR)
assert(L.plant("oak").evidence.maturity_days.note~="changed")
local a=L.animal("roe_deer"); a.breeding_months[1]=1; eq(L.animal("roe_deer").breeding_months[1],7)
local ids=L.ids("plant"); ids[1]="changed"; assert(L.ids("plant")[1]~="changed")
end)
test("identity and group validation never invent profiles",function()
for _,id in ipairs({"hearth:oak","../oak","Oak","oak_mature","unknown",string.rep("a",65),true,1}) do
eq(L.plant(id,"trees"),nil); eq(L.animal(id),nil)
end
eq(L.plant("oak","herbs"),nil); eq(L.ids("other"),nil)
end)
test("malformed profile values and stage ordering fail safely",function()
local p=L.plant("oak"); p.maturity_days={min=1,max=2}; eq(L.phase(p,0,0),nil)
p=L.plant("oak"); p.selected_days=true; eq(L.phase(p,0,0),nil)
p=L.plant("oak"); p.selected_days={maturity_days=0/0}; eq(L.phase(p,0,0),nil)
p=L.plant("oak"); p.maturity_days={min=1,max=math.huge}; eq(L.phase(p,0,0),nil)
eq(L.phase({},0,0),nil); eq(L.seasonal(L.animal("rabbit"),"Winter"),nil)
eq(L.seasonal(L.plant("oak"),"winter"),nil)
end)
test("breeding windows describe mating or nesting not immediate births",function()
local p=L.animal("roe_deer"); eq(p.reproduction,"delayed_implantation"); eq(p.gestation_days,false)
eq(L.breeding_window(p,7).within_window,true); eq(L.breeding_window(p,1).within_window,false)
eq(L.breeding_window(p,7).eligible,false)
eq(L.breeding_window(L.animal("blue_tit"),4).within_window,true)
eq(L.breeding_window(L.animal("blue_tit"),12).within_window,false)
eq(L.breeding_window(L.animal("barn_owl"),4).known,false)
eq(L.breeding_window(p,0),nil); eq(L.breeding_window(p,13),nil); eq(L.breeding_window(p,math.huge),nil)
end)
test("six verified basic profiles enable reproduction without claiming all taxa complete",function()
local enabled={bank_vole=true,wood_mouse=true,blackbird=true,mallard=true,hare=true,fox=true}; local count=0
for _,id in ipairs(L.ids("animal")) do
local p=L.animal(id); eq(p.reproduction_enabled,enabled[id] or false)
if enabled[id] then
count=count+1; assert(p.maturity_days and p.birth_count)
assert(p.gestation_days or p.incubation_days); assert(p.postpartum_days>=21)
end
eq(p.feeding_interval_seconds,nil); eq(p.movement_speed,nil)
end
eq(count,6); eq(L.animal("bank_vole").gestation_days.max,24)
eq(L.animal("blue_tit").incubation_days.max,14); eq(L.animal("blue_tit").maturity_days,false)
eq(L.animal("bumblebee").reproduction,"colony_caste_dependent")
local hare,fox=L.animal("hare"),L.animal("fox")
eq(hare.maturity_days.min,236); eq(hare.gestation_days.max,42); eq(hare.juvenile_days.max,30)
eq(hare.birth_count.max,8); eq(hare.reproduction_enabled,true); eq(hare.postpartum_days,30)
eq(fox.maturity_days.min,304); eq(fox.gestation_days.max,53); eq(fox.juvenile_days.min,56)
eq(fox.birth_count.max,9); eq(fox.reproduction_enabled,true); eq(fox.breeding_seasons.Winter,true)
end)
test("hedgehog and amphibian winter dormancy is explicit and never a death rule",function()
for _,id in ipairs({"hedgehog","common_frog","smooth_newt"}) do
local p=L.animal(id); eq(p.winter_dormant,true)
assert(L.phase(p,0,L.MAX_CLOCK).death_due==false)
end
eq(L.animal("hare").winter_dormant,nil)
end)
test("loading and calling policy does not pollute globals or read the clock",function()
local before={}; for k,v in pairs(_G) do before[k]=v end
local saved_time,saved_random=os.time,math.random
os.time=function() error("unexpected clock read") end; math.random=function() error("unexpected random call") end
local ok,err=pcall(function()
local fresh=dofile(target); fresh.resolve(fresh.plant("oak"),123)
fresh.phase(fresh.animal("wood_mouse"),0,100); fresh.elapsed(0,100,8)
end)
os.time=saved_time; math.random=saved_random; assert(ok,err)
for k,v in pairs(_G) do eq(v,before[k]) end
for k,v in pairs(before) do eq(_G[k],v) end
end)
eq(passed,32)
print("PASS "..passed.." lifecycle checks")