Skip to content
Plant Pact

reading the 14-day outlook for London, United Kingdom…

Method

How the odds are produced

No hidden service, no proprietary model, no API key. This page states the algorithm, the corpus, the metrics and the limits so the number on a pact can be argued with.

Model card

logistic regression (L2, batch gradient descent)

Features are observable quantities: measured window light, stated watering cadence, stated care level, plus live forecast and climate-normal aggregates. Contribution = weight x feature; the sum of contributions plus the bias is the logit, and sigmoid(logit) is the reported survival probability.

version
pact-survival@2026.10.0
engine
pact-verdict@2026.10.0
trained at
2026-10-03 02:36:08 utc
seed
20261002
features
10 observable quantities
corpus
8,040 placements across 24 cities
train / holdout
6,432 / 1,608
label balance
4,690 survived · 3,350 failed
held-out AUC
0.9556
held-out accuracy
0.8887
held-out log loss
0.4586
held-out Brier
0.1413
base survival rate
58.3%
alert threshold
50% survival

Metrics are computed with the rounded weights that ship in the repository, so they describe the code that actually runs. Regenerate everything with npm run train; refresh the weather corpus with npm run corpus.

The pipeline

  1. 1 · Real weather. A 14-day daily forecast from Open-Meteo plus 12 monthly normals derived from the ERA5 reanalysis archive for the recipient's coordinates. Cached 15 minutes and 30 days respectively.
  2. 2 · Sill thermal model. Outdoor extremes are damped toward 17 °C: cold by ×0.45, heat by ×0.6. A flat is not the street outside, and judging a plant by the raw outdoor forecast makes every temperate city look arctic.
  3. 3 · Ten features. Each is derived from something measurable — window light, watering cadence, stated care level — or from the weather above. Nine are risk-shaped where larger means worse; Light headroom is the only one where more is better.
  4. 4 · Logistic inference. p = σ(bias + Σ weightᵢ · featureᵢ), computed in-process. Contributions sum exactly to the logit, which is what the ledger displays.
  5. 5 · Projection path. The same weight vector is re-evaluated on the forecast prefix available on each day, so the curve drifts as cold nights and dry spells accumulate. The last point equals the headline number.
  6. 6 · The failing week. The first day survival drops below 50%, or a slope projection beyond the window when it never crosses inside it.

Features and shipped weights

featuredirectionweightwhat it reads
lightDeficithigher is worse−0.7324How far the sill falls short of the species' minimum light.
lightOvershoothigher is worse−0.2032Hours past the species' light ceiling, which scorches rather than feeds.
lightHeadroomhigher is better+0.3022Comfortable headroom inside the light band. The only positive feature.
coldSharehigher is worse−0.4410Share of forecast nights whose modelled sill temperature is below tolerance.
heatSharehigher is worse−0.0342Share of days whose modelled sill temperature is above tolerance.
waterDryhigher is worse−0.8511Accumulated deficit when the routine is slower than the species wants.
waterWethigher is worse−1.2444The rot risk when the routine is faster than the species wants.
climateStrainhigher is worse−0.4112How much of the year sits outside tolerance, from ERA5 monthly normals.
careShortfallhigher is worse−0.5511Gap between the stated routine and the species' difficulty.
drySpellhigher is worse−0.6398Whether the routine leaves the forecast's dry-heat days uncovered.
bias—+1.2915The base odds before any risk is applied.

Read this honestly: water dominates. The two watering features carry the largest weights in the model, which matches the two ways gifted plants actually die. Heat is real but almost never the deciding factor once the sill model is applied, and its near-zero weight is an empirical finding rather than an oversight.

The labelling procedure

Training labels come from a separate, hand-written risk procedure: accumulate hazard from every way a placement can fail, and call it a survival when the total stays under 1.6. The application never scores with this function — it scores with the trained model. Keeping them separate is the point: the model can be audited against the procedure it approximates instead of silently standing in for it.

lightDeficit
×1.50
lightOvershoot
×0.50
coldShare
×1.20
heatShare
×0.90
waterDry
×1.10
waterWet
×1.20
climateStrain
×0.80
careShortfall
×0.70
drySpell
×0.80
threshold
survives if hazard < 1.6
version
pact-risk-procedure@2026.10.0

Integrity

Every create, update, outcome and deletion appends an event to a per-pact chain. Seals are computed over recursively key-sorted JSON so the same logical event always hashes the same way.

seal_n = SHA-384(
  UTF-8( prevSeal ) + canonicalJson( event_n )
)

genesis = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Deleting a pact tombstones the row and keeps every event, so the record of a prediction cannot be quietly rewritten. The verify route replays a chain and names the first sequence that fails.

Data provenance

care ranges
Curated in src/lib/species.ts
offline fallback
Sealed London sample, 2026-10-02
third-party keys
none

Every response states its own origin: live, or the sealed sample with its real capture date and a note saying it does not describe the requested place. Fallback data is never written to the database and never replaces user data.

Ownership, limits and safety

  • No accounts. Ownership is an unguessable v4 UUID in an HTTP-only, SameSite=Lax cookie. Every query is scoped by it, so one visitor can never read or mutate another visitor's pacts.
  • Abuse control is best-effort. Anonymous writes are limited to 20 per minute per session and 40 pacts per session. The limiter is an in-memory bucket, so it resets per serverless instance; a hosted limiter is the honest upgrade.
  • Not medical, legal or veterinary advice. Plant Pact gives placement guidance, not horticultural or veterinary advice. Care ranges in the species catalogue are approximate published guidelines compiled for this project, not measurements of a specific cultivar. Toxicity flags are conservative and general. If an animal or a child chews a plant, confirm the species with a veterinarian or your local poison service.
  • The sills are modelled, not measured. An indoor window's light hours are a number a person has to estimate. The model can only be as honest as that estimate, which is why the evidence string on every factor repeats it back.
  • Deleting keeps history. DELETE tombstones; the chain survives.

Source and history: https://github.com/aniruddhaadak80/plant-pact. Health and store verification at /api/health, and the agent manifest at /mcp.json.