Actions
What it models
Section titled “What it models”Everything the keeper does to the tank between hours. An action takes the state and returns a new one plus a human-readable message, applied immediately rather than queued for the next tick. A refused action returns the state it was handed, unchanged, with the message explaining why.
Status
Section titled “Status”| Mechanic | Status | Why |
|---|---|---|
| Dispatch | settled | One exhaustive switch over a discriminated union, so a new verb that forgets its case fails to compile. |
| Refusal discipline | settled | Every action taking a number refuses a non-finite one before its own bounds. A comparison against Na is false, so an unguarded bound would write it into a resource nothing downstream ever clears. |
| Water change | settled | Removes proportional dissolved mass, then blends temperature and both gases by volume against the tap and brings in the tap’s KH and GH. The one verb with real physics in it. |
| Feed · dose · root tab · stocking | settled | Bounded writes to a single stock, with the consequences left to the tick. |
| Scrub | scaffolding | Clears the walls’ share of every bloom’s habitat, read off the tank’s geometry and hardscape. Coverage is one figure over the habitat, so what the floor and hardscape held spreads back over the glass at once, and a second scrub takes the walls’ share of it again. |
| Top-off | scaffolding | Fills to capacity and adds the tap’s KH and GH, nothing else. The tap water it adds carries no temperature, no oxygen and no CO₂ — while the auto top-off doing the same job blends all three. |
| Trim | settled | An absolute size inside a unit’s own range — one plant, one family or the whole planting — and the cut leaves the tank. |
| Planting by floor | settled | A unit goes in when its form’s full footprint, whatever its size, fits the floor the planting leaves free. |
| Water-change shock | missing | A 90 % change costs a fish nothing beyond the temperature, hardness and gases the blend leaves behind. |
| Substrate vacuuming | settled | A water change can vacuum the bed, taking a share of its settled mulm out of the tank. The food and waste standing in the water are left behind. |
| Config changes as actions | missing | Equipment settings are plain fields the host writes directly. There is no action type, no validation and no log for them inside the engine. |
How it works
Section titled “How it works”The shape of a verb
Section titled “The shape of a verb”applyAction(state, action, config?) → { state, message }Immediate, pure, and outside the tick loop entirely — an action never advances the clock. The passive readings — light, flow, surface, aeration — are settled off the tank it leaves, so a water change that carries green water out reads brighter at once. Past hour zero the day’s light history keeps the hours the tick recorded; at hour zero, with no day lived yet, the whole day is re-read through the water the verb leaves. The message is for the host to show; a refusal is not an exception.
Two verbs draw on the tank’s random stream: stocking a fish or a plant draws its individual variation and its id from it. The keeper’s hand is therefore part of the replay — two tanks on one seed diverge the moment their keepers act differently.
The verbs
Section titled “The verbs”| Verb | Input | What it moves | Refused when |
|---|---|---|---|
| Feed | grams | food up by the amount, at the food’s composition | non-finite or not positive |
| Top off | — | water set to capacity | already at capacity |
| Water change | a fraction in (0, 1], optionally a vacuum share in [0, 1] | see below | either non-finite or outside its range, or the tank is empty |
| Dose | millilitres | the four nutrient pools, by the fertilizer ratio | non-finite, below the minimum or above the maximum |
| Root tab | a whole number of tabs | the bed’s nutrient store, by root per tab | not a whole number from 1 to MAX_, or no bed |
| Scrub algae | — | the film on the glass walls, into waste: the film’s coverage down by the walls’ share of its habitat, what is left spread back over the glass; green water floats free of it | nothing on the glass |
| Trim plants | a target size, optionally one plant id or one family id | every plant above the target down to it — of that family, or just the named plant | target outside [MIN_, nothing above the target, plant not found |
| Add plant | species, optional size | a unit founding its own family, fed and stocked, at condition 100 | unknown species, size outside [MIN_, not enough floor for its footprint |
| Remove plant | plant id | the plant leaves the array; a rooted one disturbs its footprint’s share of the bed | not found |
| Add fish | species, optional size in % of grown length | a fish the shop raised to that size (see Livestock), with sampled sex, hardiness offset, health jitter and survival threshold | unknown species, size outside its hatchling’s to MAX_, or the physical mass ceiling reached |
| Remove fish | fish id | the fish leaves the array | not found |
| Sell fry | — | every fish that reads as fry: short of puberty and under half its grown length. A juvenile — past half but short of puberty — and an adult stay | no fry in the tank |
Trimmed tissue leaves the system outright, its reserve and stores with it — the keeper carried it out of the room. Scrubbed film, stores and all, rots in the water as waste, a gravel vac taking what has settled into the bed. Green water a water change takes leaves with the water.
A refused planting names the floor it lacks — “Not enough floor: 180 cm² free, Amazon Sword needs 700” — against the floor budget, which also says what a trim does to a plant.
What a water change actually does
Section titled “What a water change actually does”| Step | Effect |
|---|---|
| Dissolved mass | ammonia, nitrite, nitrate, phosphate, potassium, iron, kh and gh each scaled by 1 − amount |
| Green water | its budget scaled by 1 − amount, leaving with the water; its condition is the population’s and stays |
| Temperature | blended against tap by volume |
| Oxygen | blended against tap water at saturation for the tap’s temperature |
| CO₂ | blended against atmospheric |
| KH | the refill brings tap per litre, so KH moves toward the tap by the share changed — and pH with it |
| GH | the same, at tap |
| Water | refilled to capacity, so a change is also a top-off |
| Bed | with a vacuum share, the bed’s mulm scaled by 1 − vacuum; off by default |
What it does not touch: standing food and waste, the two bacterial colonies, the
bed’s nutrient store, the film on the surfaces, and the other organisms.
Without the vac, a change dilutes the products of the nitrogen chain while
leaving every gram of the material still feeding it.
Granularity
Section titled “Granularity”| Stock | Resolution a verb writes at |
|---|---|
| Nutrients | milligrams, from a dose in millilitres or a count of root tabs |
| Water | whole tank — top-off and water change both end at capacity, never in between |
| A bloom’s coverage | a share of the standing coverage — a scrub’s the walls’ share of its habitat, a water change’s the share changed — never an absolute figure |
Plant size | an absolute target, not a delta |
| Organisms | one at a time, except selling fry, which is the whole cohort |
Config changes
Section titled “Config changes”Equipment settings — the heater’s target, the lid, the light’s schedule and
rating, the filter and powerhead, the doser’s amount and schedule — are ordinary
fields on state.equipment that the host writes directly. They are not actions:
no dispatch, no message, no refusal.
The engine ships two helpers for the changes with a consequence behind them. Swapping the bed scales both colonies by the share of colonisable surface that went out with it, and takes the floor’s share of the film. Writing doser settings clamps the amount into the doser’s own range. Every other setting is a plain assignment, and the colony feels it only when the next tick truncates it to whatever surface is left. A light change reaches the substrate on the next tick, and the day’s light catches up as Light describes.
Tunables are a separate surface again. TunableConfig is validated against each
leaf’s declared range — see
state & persistence.
Key tunables
Section titled “Key tunables”The fertilizer formula and the root tab are listed in the reference under Nutrients; the action bounds are module constants.
| Constant | Meaning | Value |
|---|---|---|
| Dose bounds, manual | Floor and ceiling on a hand dose | 0.1–50 ml |
| Dose bounds, automatic | Floor and ceiling on a scheduled dose | 0.5–10 ml |
MAX_ | Most tabs one push takes | 10 |
MAX_ | Share of the water stocked fish may occupy — a plausibility ceiling, not a husbandry one | 0.5 |
STOCKED_ | Size a fish is stocked at when the caller says nothing, in % of grown length | 80 % |
MAX_ | Largest a shop sells: grown length is an asymptote no fish reaches | 90 % |
MIN_ | Least a planting or a trim may name; the most is a full unit | 1 % |
DEFAULT_ | Size a plant goes in at when the caller says nothing | 50 % |
| Water change presets | The steps the dashboard offers; the action itself takes any fraction | 0.1 · 0.25 · 0.5 · 0.9 |
The stocking ceiling blocks the physically impossible rather than the unwise. A tank crammed far past its sane bioload is allowed, and the vitality engine plays out the ammonia and the oxygen crash; only a tank that would hold more solid fish than water is refused. Breeding is exempt, so a bred cohort can push past the ceiling and simply lock further stocking out.
| Neighbour | Read | Written |
|---|---|---|
| Water & gases | oxygen, co2, kh and gh for the blend | All four on a water change; kh and gh alone on a top-off; kh on a scrub |
| Nitrogen cycle | — | ammonia, nitrite and nitrate diluted proportionally; nitrate raised by a dose |
| Environment | tap and tap as the thing being blended toward; water and tank. for every refill | water and temperature |
| Plants | plants for the floor budget, and the trim target and family | The array, and each budget directly |
| Livestock | fish for the mass ceiling and the fry count | The array; food for the fish to eat next tick |
| Algae | Each bloom’s coverage, and the share of its habitat on the glass and in the column | The film’s budget, down by the walls’ share into waste and the water; green water’s, by the share of water changed |
| Equipment | substrate. — whether there is a bed to tab | substrate., raised by a root tab; settings are written by the host |
| Alerts & logging | — | Every applied action appends a user entry; a scrub files itself under scrub. A refusal logs nothing |
| State & persistence | The draw stream, for every new organism’s id | The advanced stream position |
Source
Section titled “Source”src/simulation/actions/ — the union, the dispatcher and one module per verb.
The two equipment-side helpers live in src/simulation/equipment/.