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 · 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 · 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 · 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 · Logistic inference. p = σ(bias + Σ weightᵢ · featureᵢ), computed in-process. Contributions sum exactly to the logit, which is what the ledger displays.
- 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 · 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
| feature | direction | weight | what it reads |
|---|---|---|---|
| lightDeficit | higher is worse | −0.7324 | How far the sill falls short of the species' minimum light. |
| lightOvershoot | higher is worse | −0.2032 | Hours past the species' light ceiling, which scorches rather than feeds. |
| lightHeadroom | higher is better | +0.3022 | Comfortable headroom inside the light band. The only positive feature. |
| coldShare | higher is worse | −0.4410 | Share of forecast nights whose modelled sill temperature is below tolerance. |
| heatShare | higher is worse | −0.0342 | Share of days whose modelled sill temperature is above tolerance. |
| waterDry | higher is worse | −0.8511 | Accumulated deficit when the routine is slower than the species wants. |
| waterWet | higher is worse | −1.2444 | The rot risk when the routine is faster than the species wants. |
| climateStrain | higher is worse | −0.4112 | How much of the year sits outside tolerance, from ERA5 monthly normals. |
| careShortfall | higher is worse | −0.5511 | Gap between the stated routine and the species' difficulty. |
| drySpell | higher is worse | −0.6398 | Whether the routine leaves the forecast's dry-heat days uncovered. |
| bias | — | +1.2915 | The 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
- forecast
- Open-Meteo Forecast API
- climate normals
- Open-Meteo archive, ERA5 reanalysis
- taxonomy
- GBIF Backbone Taxonomy
- 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.