This is the operating manual for the species of the nine AGNT ECO agents (Älva, Ægir, Scaldis, Maas, Scîrwudu, Norppa, Ondine, Haingeist, Eldvatn). It is the same text on the server, on skills.agnt.eco (skill "species-start-here"), on species.agnt.eco/how and in the knowledge graph (:SpeciesPolicy). If you are a Claude session, an agent or a person about to add, hide, show, verify or "fix" a species: read all of this first. Owner of these rules: Amir. Established 2026-09-29 to 2026-10-01.
The rules (Amir's words, not negotiable)
- Literature is proof. A species is shown on an agent's page only when a passage in that agent's own library names it in the agent's own place. The passage travels with it (DOCUMENTED_IN edge with cchips).
- One top source is enough. One tier 1-2 source (peer-reviewed, or a national agency) about the place counts on its own. Otherwise two documents.
- Citizen science is an indication, never proof. GBIF, iNaturalist, observation.org, eBird, Artportalen: these say where to LOOK. The observed list, most-observed first, IS the research query list. No count, however large (not 100, not 10,000), puts a species on the page.
- No scientific evidence = hide. Then search official sources for it ("the other way").
- Never delete. Hide with
<agent>_hidden = true+_hidden_at+_hidden_by+_hidden_reason. Back up every node before any write (/root/<agent>_<step>_backup_<date>.json). - RUNA is hypothesis only.
runa_added = trueis never evidence; a RUNA guess becomes visible only when the passage scan proves it (thenruna_added=false,runa_lit_confirmed_at,runa_added_originalkept). - Facts are attached, verified later. Every shown species gets one candidate fact, "<Common> (<Scientific>) occurs in <the agent's water>.", category "Species presence", dated and tagged, linked ABOUT the species. Only through the verification MCP. Do not verify in the same pass.
- Report literature-backed species first, in their own section. Short and plain.
- Three names for every species: Latin, English, local.
scientific_name(Latin),common_name(English) and the agent's own language inname_<lang>(name_swe, name_nor, name_nld, name_fin, name_fra, name_deu, name_isl; a shared node keeps one field per language). Filled from the GBIF backbone vernacular names (species_engine/fill_taxa.py, fill_local_names.py); a species with no name in a language keeps the field empty, never an invented one. Wherever a species is shown, the common name stands beside the Latin name.
How to READ the current state (no writes)
- Page list:
curl -s https://species.agnt.eco/api/agent/<agent>(what the public sees). - Why a species is shown: its
DOCUMENTED_INedges (origin,cchips) and thegrounding_basis_<agent>property. Why one is hidden:<agent>_hidden_reason. - A species is ON the page when it has label
Species_<Agent>AND notruna_addedANDpresent_<agent>(falling back to the sharedpresent) is not false ANDgrounding_<agent>is notspeculativeAND not<agent>_hidden(see_species_rows_forin /srv/projects/species/app.py). That is the whole page logic. - One species node is often shared by several agents (it carries several
Species_<Agent>labels). Every decision is per agent: use the_<agent>properties (<agent>_hidden,grounding_<agent>,present_<agent>), never a shared property, or you change another agent's page. - The dream "species-trace" runs every night at 04:50 (/etc/cron.d/dream-species-integrity) and reports on dreaming.agnt.eco per agent: on page, with passage proof, untraced, and DRIFT since the last snapshot (species added without proof, removed without a reason, nodes deleted, page filters edited). The "species-observation" dream only lists citizen observations as research leads; it never means "add". The old "species-kg-standard" dream is retired (it demanded verified facts now; the rule is attach now, verify later).
How to ADD or CHANGE species (the only way)
Scripts: /srv/projects/dreaming/species_engine/ (WORKFLOW.md there has every detail and every trap). Run on the server (root@77.42.6.111), conceptum venv unless noted:
- Area: /srv/ops/area_drafts/<agent>.geojson + .wkt (check it against the agent's gazetteer first).
citizen_area.py <agent>(verification venv) -> the search plan.- Library review:
library_placecount.py <agent>; set off-domain documents aside (excluded, reversible). species_rescan.py <agent>-> /tmp/<agent>_rescan/report.json (SHOW / REVIEW / RESEARCH / GONE / NONE).- Red-flag pass: read the SHOW list with snippets;
vernonly.py <agent>for name-only hits. Fix and re-run. apply_fill.py <agent>(plan) then--apply.hide_untraced.py <agent>(plan) then--apply.mint_presence_facts.py <agent>.engine_queue.py <agent>(sources venv) +rebuild_queue.py <agent>-> research queue; scope files for library top-ups in /srv/ops/ingest_queue/.- Ingest the queue: push the queued documents to the chunker with a stated purpose (
push_corpora.py, see the library-ingest and library-pipeline-health skills), collect the passages, then re-run from step 4. A queue that is built and never ingested changes nothing; the nightly dream flags it (ingest_queue_stale). presence_verify.py --agent <agent>(verification venv): the separate verification pass over the candidate presence facts, local model reading the linked passages, one pass per fact through the verification MCP. Until it has run, "proof" on the page means "a passage was found", not "a verifier confirmed it"; the dream flags the backlog (presence_facts_unverified).
A new document only changes the page after step 4 onwards is re-run. Adding a species by hand, by count, by "I know it lives there", or by RUNA is not allowed.
Never do this
- Never show a species because citizens saw it, because GBIF has records, or because a model says so.
- Never delete a species node, a label, a fact or a DOCUMENTED_IN edge. Never "clean up" hidden species.
- Never change the page filters in /srv/projects/species/app.py to make a list longer or shorter.
- Never re-scope who "owns" a species or which agent's evidence counts without Amir's OK.
- Never unhide in bulk. A hidden species returns only through a passage (step 4).
- Never bypass the verification MCP for facts; never edit a claim (claims are immutable; propose a rewrite).
- Never trust a bare place word: "maas" = MAAs, "leman" = Weisfeiler-Leman, "laxa" = a Latin epithet.
Relations between species (what eats, hosts, parasitises or displaces what)
A relation is recorded only when a passage in the agent's OWN library states it. Never from general knowledge, never from a model's belief, never from co-occurrence on a list.
- Where it runs: the home RTX 5090 (Ollama behind the tunnel at localhost:11500, model qwen3.8:27b), about ten times faster than the server CPU. The server's own model is the fallback only. A run is a job on Amir's hardware: it starts only with his yes, agent by agent.
- How:
species_engine/relations_trial.py <agent>(envRELATIONS_URL=http://localhost:11500) picks the passages that name two or more of the agent's page species, and the model lists only relations the passage states, each with the exact quote. Types: EATS, PARASITISES_HOST, HOSTS, MYCORRHIZAL_WITH, DECAYS, COMPETES_WITH. Output is a file. - Check:
relations_write.py <agent>keeps a relation only if every part of the quote (split on "...") is in the passage, the quote names both species (Latin, binomial, "G. species", English or local name), and BOTH species are on the agent's page. A diet stated as a group ("zooplankton", "Daphnia", "fish eggs") has nothing to attach to and is dropped. - Read every relation before saving. The automatic check confirms that the quote exists, not who does what: on the first Älva run 5 of 12 passed the check and were wrong ("signal crayfish eats perch" where the passage says perch eat the crayfish; "pike eats perch" where it says both eat smelt). Each relation is read against its passage by a person or a strong model; a misread one is either dropped or replaced by what the passage states, and the edge records that it was corrected.
- RUNA as a second opinion, never as evidence. Score each predation link and its reverse (
runa.agnt.eco/api/score). A reversed direction scoring higher, or a very low score, sends the relation back for a second read. RUNA judges what is plausible in nature, not what the passage says, and it has gaps (it doubted the well documented Lofoten orcas feeding on herring); it scores no parasite or competition links for these species. - Amir sees the final list;
--writestores it. - Edge: (a)-[:TYPE {agent}]->(b) with quote, passage chip, source title and tier, method, created_by
relations-extract-<date>, confidence 0.7 (one passage), inferred = false. - What to expect: few relations per agent (Ondine 3, Aegir 7 on the first runs). Most libraries state diets by group, not species against species. That is a finding about the literature, not a fault of the run.
Yearly rhythm (when a species is active)
Raw citizen-science month counts measure when PEOPLE go out (summer, weekends, survey days), not when the species is active. Tested on 2026-10-03: raw counts put the great tit's peak on bulk-survey dates and end the swift's season too early. Never write a raw curve. Use, per group, in this order:
- Birds: eBird Status & Trends (Cornell; weekly relative abundance, observer effort modelled out; free access key for non-commercial research, tied to an eBird account, requested by Amir).
- Butterflies: European & Maghreb Butterfly Trait database (Middleton-Welling et al. 2020, flight months per species, open; /srv/ops/phenology_sources/butterfly_flight_months.json). Gives a window, Europe-wide.
- Fungi: ClimFun fruiting phenology (Zenodo 4971099, CC0; fruiting day mean and 2.5-97.5 percentiles on a 10 km grid, autumn and spring sets; /srv/ops/phenology_sources/). Checked: fly agaric Sep, porcini Sep, chanterelle Aug, morel May.
- Flowering plants, ferns, amphibians and reptiles: GBIF records at country level, NOT corrected. They are recorded when they flower or are active, so the observer season and their own season coincide. Effort correction breaks them: one winter photo of a rosette divided by an almost empty winter baseline became the "peak" (dark mullein in November, sow-thistle in December, the common frog in September). Raw: mullein July, sow-thistle July, frog March.
- Other animals (insects, molluscs, fish, birds without eBird, bats): effort-corrected GBIF. Records of the species divided by all records of its class in the same area and month, with a floor (no month counts as more than four times quieter than average) and light circular smoothing, so single records cannot make a peak. Drawn areas when they hold at least 100 records, otherwise the agents' countries (at least 20). GBIF has no class for ray-finned fish, so fish are compared with all records of the common fish orders together (checked: Vänern salmon Apr-May and Aug-Oct, Lofoten herring Dec-Mar). A two-season species shows both windows ("Mar, Nov").
- Visible or active all year: no curve,
activity_pattern = year-round. Lichens, mosses, bracket fungi, trees and shrubs (willow, hazel, hawthorn, elder, ivy, heather and the other woody genera), mammals except bats. Their counts show when people walk outside (the tinder fungus "peaks" in April, when the leaves are off). - No usable source: no curve. The old raw curve is kept only as
activity_curve_raw, never shown.
One curve per species: for a species shown by several agents it is computed over all their areas together. Script: species_engine/fill_rhythm_v2.py (--dry --agent <a> --limit <n> first). The raw values are kept once as *_raw; backups in /root/rhythm_v2_backup_*.json. Every curve records its source in traits_source. Plants: BiolFlor (UFZ) holds German flowering months but has no public API; PEP725 covers about 265 species, mostly trees; until one of those is obtained, plants use rule 4. Applied to all page species on 2026-10-03 (fill_rhythm_v2.py). Checked by hand: nightjar May-Jul, swift May-Jul, whooper swan at Vänern March and Oct-Nov (staging), crane over the Hainich Oct-Nov, puffin at Lofoten May-Jul, magpie and great tit year-round, fly agaric Sep, morel May.
Where to read the state (never copy numbers into this manual)
Numbers go stale the moment anything runs; this manual carries none. Read the live state from:
- the nightly dream "species-trace" on dreaming.agnt.eco (per agent: on page, with passage proof, untraced, unverified presence facts, stale queues, drift since yesterday, missing groups);
- the public API,
curl -s https://species.agnt.eco/api/agent/<agent>(what the page shows now); - the dashboard overview (species count and the top dream item per agent).
History that explains the state
- 2026-09-29/30: engine established on Älva and Ægir; 30 Sep Scaldis (estuary rule); 1 Oct Ondine, Haingeist, Eldvatn (STRICT mode for French, German, Icelandic texts); Maas and Norppa in between.
- The hide step (
hide_untraced.py) was written on 1 Oct, after Älva and Ægir were done, and was not run back on Ægir or on Scîrwudu: both pages kept unproven and borrowed species until 3 Oct. - 2026-10-03: one standard for all nine (STRICT rescan), red-flag pass on cultivated, domestic and escapee taxa (REVIEW, never SHOW), fill + hide + mint applied to all nine, and the first verification pass started.