Project
Roadmap & backlog
Everything the mod still has to do, ordered by priority — most urgent first, endgame last. Broken things before missing things, foundations before the features that sit on them, content before polish.
Nothing on this page is built. When something ships it is
deleted from here and written up on its own page
(the genetics model, a
gene-*.html page, the horse’s
body, breeding,
horse care, the carrot
family, items,
the horse dimension…).
“Marked shipped” is not the same as “moved”, and only
moving it keeps one source of truth.
To be verified is the near end — built, not yet seen in-game. That is a different list from this one. And this page is work, not reasoning: the why lives in Philosophy, and anything here needing a justification longer than a clause belongs there.
The page was reordered by priority, so the old numbering (“§14 the carrots”, “§21 settled”…) no longer applies. A reference elsewhere to a roadmap §-number predating this date should be re-pointed at the section that now covers it, or at the wiki page the work moved to. Sections are named as well as numbered; link by anchor, not by number.
FRACTAL and PATH shipped on both sides in September, and
the drawing canvas that PATH was waiting for is now
the creator's points control:
the format page documents it. It changed no
format, no engine and no parity fixture, exactly as the entry predicted — it
is a second control over an array that already existed.
So the question this note used to park — whether the textarea was good enough to wait behind the Java gene API — is moot, and the API shipped on its own merits.
Erase-down and the expression preview are in too, and they turned
out to be one feature seen from two ends. A knob may now be marked
"dial": true — this is how much of itself the gene is
showing — which lets the creator put a slider under the
preview instead of making you re-roll horses until one lands near an end, and lets
a PATH carry pointsMin: the same shape at the gene's
weakest, morphed point to point, so a drawn marking gets smaller rather
than fainter. Both sides, with an answer table in the parity fixtures, because the
probe cases do not reach a drawing on a flank. See
the dial and
pointsMin.
Glow intensity is in as well. A layer's
emissive is a level
rather than a bit — any number or knob in [0, 1], scaled by
the mask's coverage, so a soft edge fades its glow out instead of ending it on
a line at coverage 0.5. It reverses the old "there is no half-lit" call
deliberately: the emissive pass blends rather than replaces, so the level is
written as the second texture's alpha and a fraction means something.
What is left of §10 is per-region colour ranges and gradients — and one honest gap beside it: the creator cannot show a glow. It writes one, validates it and round-trips it, but its preview is a flat sheet with no lighting to be brighter than, so a glow is something you have to go and look at in game. Fixing that means shading in the preview, which is a different job from anything in this section.
Where to start
If you have one session, spend it on the top of this list. Each item is picked for blast radius — how much else is wrong, or blocked, until it is done.
- Look at the three dialled genes (§0-CP) — Starburst's emblem now scales per horse, and Lantern and Tron burn at a level their dial sets. They are the first genes whose variation is the point rather than their pattern, and nothing but a person can say whether the range is right.
- Play-test the backlog — the two items that stood at the top of this list both shipped, and neither has been seen by a person. A great deal else is built and unplayed: see To be verified, which is longer than this page's top section.
- What is left of the visual marking designer
(§10) — the canvas, the dial, the minimal shape
and glow intensity have all shipped, so what remains is per-region
colour ranges and gradients: colours are literals in the format today
rather than
Values, so the first question is whether a colour becomes knob-drivable at all. - Deepen the thin science tabs (§science) — every natural gene page now has one, so the work is no longer absence but depth: 23 predate the pass that wrote the rest, and 17 of those read as stubs beside it — starting with the ones the new tabs cite and cannot get an answer from.
The gameplay layer (§§13–20) touches the genetics in only three places — carrots bias a gamete draw, jars carry a genome, and the database and carrot recipes read gene metadata — so it can proceed in parallel with the gene work above.
Part 1 — Things that are wrong now
1. Defects in shipped genes and the coat
All of these are built features behaving incorrectly, so they cost nothing to
schedule and everything to leave. Anything here that moves a coat wants
coat-golden.txt regenerated and
WhitePatternGenesTest’s monotonicity ladder re-checked in the
same commit.
Both painters measure against a topline reference
(WhitePattern.toplineHeight) rather than the whole-horse AABB, so
the fractions in them mean something anatomical: 0.05 is the coronet, 0.26 the
knee, 0.52 the underline, 1.0 the spine. See
the pipeline and
EDNRB.
The two items that stood here after the splash fix are also shipped
and were simply never deleted, which is what this page exists not to do.
EDNRB’s frame band is measured on the barrel’s own
height now, with a separate band on the neck’s (the naive per-part fix
did strip frame’s legitimate neck white, exactly as the entry
warned). And every cover knob is an area fraction
by construction: both EdnrbGene.paintFrame and
TobianoGene score every eligible texel, sort, and cut at the
quantile matching cover, so the knob cannot drift when the field
or the geometry moves under it.
Medicine hat is not on this list and is not a gene (owner’s
call). A near-white sabino keeps its ears and the back of its poll because
WhitePattern.sabino never paints the ears; whether that reads as a
bonnet on any given horse is left to fall out of the draw. It is not being
modelled, retained for, or rolled.
1.2 Genes drawing the wrong thing
| Gene | Problem | Fix |
|---|---|---|
| Leopard complex | “White finds white” is not applied. Leopard composes at priority 73, before the four white loci, so leopard + sabino simply stacks rather than compounding. | Decide whether the appaloosa patterns should read the coat they are handed like the other white painters do. Multi-pattern appaloosas are unverified either way. The spot / blanket / hoof-stripe constants are also first-guess and want a play session. |
1.3 Behaviour that silently does nothing
- A wild horse renders as a default black horse with vanilla attributes
for about one tick before
HerdManagerfounds it. Not perceptible, but it is a frame of wrong, and it is the visible edge of the deferred-join design.
An "over" flag, so a layer can say it paints on top
The root cause behind gap 169, and the only thing that would actually stop it recurring. A magical gene's layers each measure their delta against the colour the gene started from and the deltas are summed. Authors — human and model — write files as though each layer painted over the one above it, because that is what layers mean everywhere else, and the format gives them no way to say it. So they write the thing that looks like stacking and get summing, and the result is a solid black horse (dorsal wing), a white one (contour), or a star with no core (starburst).
The proposal is one optional per-layer flag: "over": true applies the
delta accumulated so far before this layer measures. Everything without the flag
behaves exactly as it does now, so no existing gene moves —
which is the property that makes it worth doing rather than changing the default.
Dorsal wing's original file would have been correct as written — and so would
eight of the twenty-four genes in the 2026-09-10 evening intake, every one of which
arrived nested and had to be rewritten disjoint by hand
(gap 169). That is the rate this is costing.
The cost is why it is here and not done: it is a format change, so it is the
five-file contract — the engine,
SpecSchema, the fixtures, the creator's JS twin and the designer
— plus a decision about whether expected.json and the goldens
move. DeadLayerTest is the cheap half and is already in; this is the
expensive half and wants the owner's call.
2. What horse resizing broke
Attributes.SCALE — written by the size genes and the breed stat
bands — changes the model and the hitbox, and a pile of vanilla assumptions
do not follow it. None of it is game-breaking; all of it is
cosmetic or feel, and all of it needs a pass before 1.0.
- The saddle does not fit. The saddle layer renders at vanilla
scale over a bigger or smaller body, so it floats or clips. It needs to inherit
state.scalethe way the coat does. - Armour, leads and any third-party tack have exactly the same problem.
- The rider sits wrong on a very large or very small horse — the mount offset is a vanilla constant. Related to bareback steering (§14).
- Most animations are unchecked at scale. The walk cycle was
partly addressed (
GeneticHorseRenderer.stretchGaitToSizedivideswalkAnimationPosby the scale, so a bigger horse takes proportionally longer strides), but rear, eat, buck, the tail swish and the seat position have not been looked at. Walk through each on ap/ppony and a Percheron. - A scaled foal has never been seen, and
Skin.BABY’s projection is already approximate. Check ap/pfoal before adding anything else that scales.
Part 2 — Decisions
3. Decisions still open
Each of these changes what gets built, so answering one is worth more than a day of building around it. Settled decisions are at the bottom of the page (§22) so a later session does not relitigate them.
| Question | Where it stands |
|---|---|
Should Condition gain a severity / onset model? | The mod’s health model is binary: a locus is LETHAL_AT_BIRTH / LETHAL_AT_CONCEPTION, or a flat heart reduction. The naturalgeneref.md disorder table is richer — each gene carries a health %, a speed % and a jump %, and distinguishes episodic, chronic and progressive-onset conditions. HERDA, PSSM1, HYPP and CA (§12) all fit that shape and none fits “fewer hearts” cleanly. Two ways: (a) keep the binary model and render each as a flat heart cost plus a flat speed / jump multiplier — cheap, loses the “flares up under exertion” character; (b) give Condition a 0–1 magnitude and an EPISODIC / CHRONIC / PROGRESSIVE kind that the health resolve and a future status tick both read. (b) is the reference’s model and the natural companion to the effect architecture (§7). Still not decided — but all four of those genes have now shipped under (a), so the question has changed from “which model” to “is (b) worth a migration”. Building them also settled one thing the question did not anticipate: the reference’s percentages could not be used as arithmetic at all. Read literally against a 22-health baseline they make an affected but living HYPP horse worse than several of the mod’s outright lethals, so every one of the new loci is calibrated against the disorders that already ship and uses the reference only for the ordering. Whatever (b) turns out to be, it does not get to inherit those numbers. |
| How is a cutie mark chosen — and should the gene be polygenic? | The hook is built (2026-09-06): CutieMarkContribution lets any gene modify the mark, and markFor folds every implementor over the base emblem in code order — the light locus is the first user and makes the mark glow. What is still open is the content: which genes and breeds should bias the item pool (a milk horse leaning toward bucket and bottle icons), and whether mark colour is worth having — a per-instance tint is not renderable through ItemStackRenderState.submit, so it would need a custom render type. Wants more fictional breeds (§19) to be worth doing. |
| The 1.12.2 backport. | Not a question so much as a standing constraint. Everything on this page is common/-side except entity scale, gait animation, the health tick handler and the gene registration hook — those four are the only places the backport goal should shape a design. |
Part 3 — Foundations that unblock the rest
4. Sex-linked inheritance — what is left
The scaffolding and its proof are built.
Gene.inheritance() declares AUTOSOMAL /
X_LINKED / Y_LINKED;
Genotype.breedWith routes the sire's copy by the foal's own sex;
Gene.sexConsistent keeps impossible combinations out of the
catalogue; a hemizygous copy is stored in a reserved slot and displayed with an
X- prefix; founder tables are sex-aware. Brindle
is the gene that proves it. See the brindle
page.
What is still open here:
- Nothing is Y-linked yet. The mirror case is built and tested
through
Inheritance.Y_LINKED— a mare's pair is two reservedXplaceholders — and is waiting for a gene that wants it. A Y-linked locus travels down the male line untouched by any mare, which is a different and equally legible pedigree shape. - A punnett square for a sex-linked gene is a different square,
and neither of the two that now exist draws one. The wiki’s
(
wiki/gene-inheritance/, on every gene page) pairs the two copies of each parent as though both were autosomal, so brindle’s square is drawn wrong rather than refused — it needs the sire’s reservedYslot and a foal’s own sex on one of the axes. The browser’s Breeding preview tab has the same hole. - Hemizygous wording beyond the short form. The genotype short
form prints
X-Brn; the gene-inspect popup and the paper dump still print the pair as-is, so a stallion's brindle locus readsBrn / No copy (Y chromosome)there. - Sexual dimorphism in the coat is a different thing entirely and is unbuilt. Sex paints nothing and deliberately never will, so a stallion's crest would be a separate gene reading this one.
Not planned at all: imprinting, mosaicism, crossing over, linkage.
5. Eye colour — what is left
Everything this section used to list is built, and the channel now has its own
page: Eye colour & heterochromia. Green and
hazel irises (champagne's epigenetic shade and
cream–pearl's blue-green), the blue-eyed
creams, complete heterochromia (one blue eye) and
sectoral heterochromia (a blue wedge) off a per-horse
EyeSpread, and the magical
sectoral heterochromia locus.
What is still open here:
- Champagne foals' blue-green eyes. A real champagne foal is born
blue-green and matures to amber or hazel. Here it is the adult colour from birth:
EyeColorContributionis not told which skin is being baked, and with aging out of scope there is nothing to mature through. Adding aboolean adultto the hook is a five-implementor change and would be cheap; the open question is whether a foal that is permanently blue-green is better or worse than one that never was. - Filmy and blind eyes. Deliberately not started: cloudiness is cornea, lens or inflammation rather than iris pigment, and is not an inherited eye-colour category at all. It would want to be a condition, not a claim on this channel.
- The wedge shapes may be too fine. Twelve masks over four texels
includes single corners and three-corner shapes; whether those read as
heterochromia at riding distance or as a stray pixel is the first thing to look at
in game. Restricting
EyePatch.WEDGESto the four halves is a one-line retreat if they do not. To be verified. - The foal sclera bug is a defect, not this work — §1.2.
7. Group traits, and the rest of the effect architecture
7.1 Traits that come from a group of genes
Wanted, and distinct from the cut polygenic idea: two or more genes, each
with their own alleles, that only together trigger an outcome. This is
epistasis / complementation — still fully Mendelian — and it needs a
home. The leopard complex got what it needed without
one (expressionIn is handed the whole genotype), but that is a
single-gene read, not a rule of its own.
- It is not a gene, so it cannot live on the
Geneinterface. It wants a separately registeredTraitRulethat declares which gene keys it reads and is handed those pairs. - It is still Mendelian. Each gene inherits normally and independently; the rule only reads the result. No new inheritance code.
- It may affect the coat — settled. So a coat-affecting rule needs everything a gene needs: a phase and a priority. Which argues strongly for modelling it as a pseudo-gene: same delta function, same ordering, same determinism rules, differing only in that its inputs are several pairs instead of one. Reusing the gene pipeline wholesale is much cheaper than a parallel one, and the spawner and the coat cache then need no special case.
- The one thing it cannot inherit: it has no alleles, so it contributes nothing to
the genotype code and never appears in an allele lookup. Keep it out of
codeOrder(). - Determinism: rules sort like genes, and a rule reading an unregistered gene must fail loudly rather than default.
- Visibility to the player. A trait with no single gene behind it is invisible in a genotype code and unguessable in a punnett square. It needs a UI surface of its own, or it will read as randomness — exactly what the philosophy says not to do.
Cream + pearl looks like this case and is not: they are one physical locus, and MATP merges them rather than writing a rule.
7.2 The rest of the effect architecture
A first slice is built — eight effects
verbs with boolean when conditions, usable from a gene file and from a
built-in Java gene through the same vocabulary. What is left is
everything else on the trait & effect
architecture: triggers, selectors, auras, resource pools, goals, trait
relations, on_change, and the 0–1 scalar
conditions the built version approximates as booleans. The capability
interfaces should not be considered final until that lands — and the severity
model question in §3 is the same shape of problem.
Two known weak points in the shipped slice: spread converts at most one
block a beat off a narrow, hand-written block list, which is the
piece most likely to be wrong in play (the risk is a horse eating something a player
built), and walk_on_water is surface buoyancy rather than a real
collision plane. Wiring the inert attribute verb is
§1.3.
8. The Java gene-authoring API, and an open registry
A gene that fits the format is already a JSON file — no Java, no rebuild (the format, the creator). That covers the common case, so this is not urgent; it pays for itself on the next hand-written gene, and there are a lot of those still to come (§§10–12).
AbstractNaturalGene, AbstractMagicalGene and the
TwoAlleleGene skeleton under them answer seven of the interface's
eight methods from a declaration;
the short path is the write-up, and
mushroom is the worked example.
AbstractAbilityGene — which is where the shape was first
noticed, seventeen genes ago — was moved onto the same skeleton, which is
the evidence it generalises rather than being a second copy of it.
Genes.register takes any Gene, validates the
namespaced key, and freeze()s;
neoforge.api.RegisterHorseGenesEvent is the way in, and
registering from another mod says
when it fires and why it has to be an event.
What is actually left
AbstractPatternGenewas not built, and the reason is worth keeping. The entry above promised it would absorb “theBodyNoiseboilerplate and the pull-N-knobs-off-the-expressing-copy helper”. Neither exists as boilerplate: reading epigenetics is the single linectx.epigeneticsFor(KEY), andBodyNoiseis a static call whose arguments are the whole design of the pattern using it. A base wrapping those would hide one line and constrain the interesting one. If a pattern base is worth writing it will be because a third pattern gene wants the same shape — write it then, from the duplication, not from this entry.- No third-party gene has ever been registered, in this game or
any other. The event compiles against 26.1.2 and its firing point is read off
the loader's own
constructMods, but the proof is a second mod, and there is not one. §0-CM. - The capability interfaces a third-party gene would implement to
do anything beyond painting (
TraitContribution,AbilityContribution, the rest) are not final — see §7. A modder writing a coat gene is on solid ground today; one writing a behaviour gene is building on something that will move.
The allele rules and the contract a modder must
honour are on Writing a gene. One thing a
third-party gene still stresses: CoatTextureId encodes the whole
genotype into an Identifier path, so more genes means longer paths
— see §21.
10. The visual marking designer
Markings like peacock and laced tobiano cannot be described to an AI in words — they have to be drawn. And a visual editor is simply more fun to use than writing JSON. This is the largest single job on the page, and it is on the critical path for the 1.0 pattern set rather than optional.
- Draw the mask — done. Still unbuilt beside it: per-region colour ranges and gradients.
- Glow intensity — shipped.
CoatOverlayholds a level per texel instead of a bit, the composer and the NeoForge texture factory carry it through as the emissive texture's alpha, and a layer'semissiveis aValue- so a glow can be driven by a knob, drift down a lineage, and be swept by the dial slider. The coverage scaling is the part that changes existing genes:EMISSIVE_THRESHOLDis gone, and fifty-odd shipped layers fade at their edges now instead of ending on a line. Unlooked-at in game — §0-CO. Particle intensity is not part of it (owner's call): particles are theemittereffect, which is a different system with its own rate and count. - Erase-down — shipped, as
pointsMin. APATHmay carry a second array of the same length: the shape at the gene's weakest, which each control point morphs toward as the gene's dial falls. A marking on a weakly marked horse is smaller, with its edge intact, rather than a faded copy of the full one — which is the difference between a morph and the crossfade the format deliberately did not take (a hard-edged mark ghosts through the middle). Both arrays are edited on the one canvas behind a toggle, with the other shown as a dashed ghost, and every structural edit happens to both: the loader refuses a mismatched pair, so the editor cannot write one. - The expression preview — shipped, as the dial slider. The
entry below was right that the channel existed and the creator did not make it
visible. What was missing turned out to be smaller than a preview mode: a
gene had no way to say which of its knobs was the one that means "how much". One
flag (
"dial": true, at most one per gene, per-horse, never a seed) fixed that, and the slider under This horse drew pins it — 0%, 50%, 100% or anywhere between, with as drawn to hand it back. It pins the knob, so every parameter pointing at that knob moves with it and what you see is a horse that could be born. - Fractal pattern definitions — shipped. The
FRACTALmask joins the vocabulary: octaves, lacunarity, gain, a domain warp, and ashapethat reads the summed field asfbm,ridgedorbillow. Peacock and laced tobiano have their primitive. It is documented on the gene file format — including whyoctavesis safe to turn, which is the one thing about it that is not the textbook recipe. - The pipeline already has the scalar channel — this entry
used to say to decide that before building the UI, and the code answered it.
GeneSpec.KnobplusValue.FromKnobmeans any mask parameter can be driven by a per-copy stored scalar over a declared range (SpecValues.get), andValue.PerDosegives a per-dose triple beside it. So “expression scales parts” is expressible today: declare a knob and point every threshold at it. And the creator makes it visible now — see the dial slider above; this entry is kept only because the reasoning under it is still the reason there is no separate “expression” channel in the format. - The
PATHmask — shipped. A drawn shape is vector control points in body space (owner’s call), stroked or filled, extruded through the horse;curvesmooths them with a spline that passes through every point, because the creator is going to draw those points as draggable handles. Documented on the gene file format, including the trap thatbodyspace runs to the ear tips so the topline is 0.62. The format is settled and the engine is done, both sides. - The drawing surface — shipped. A
PATH’s points are handles on a view of the horse in the mask’s own plane: click to add, drag to move, a hollow dot mid-span to insert, right-click or Delete to remove, arrows to nudge. The textarea is still there, folded away, and the two write the same array. It changed no format, no engine and no parity fixture — which is exactly why the mask went first. Written up on the format page. - Parity still applies — and “covered” has to be
demonstrated. Anything the editor previews, it previews through the JS
port of the pipeline, or it will show a horse the game will not breed,
convincingly.
pointsMinis the case that proves the probe cases are not enough on their own: with a minimal shape onprismatic's crescent and nothing else, reversing the blend in the creator left parity green — a drawing on a flank is a few dozen texels and the fixtures sample four per part. It is checked by an answer table now (SpecFixtureTool.pathSection), and the way that was established was to break the port on purpose and watch the check stay quiet. Do that with the next one too.
9.1 The smaller creator work
The three items that used to sit here — the gene-metadata form fields, an
effects editor, and an in-page parity check — all shipped
2026-09-06, along with a fix for the reason they mattered: the creator’s
geometry port had silently missed the top/bottom UV swap and the
committed parity fixtures predated it too, so the terminal check had been reporting
green against a stale baseline while the preview drew every horse with its spine
and belly texture patches exchanged. The in-page check is exactly what makes that
class of rot visible.
What is left of them is one honest limit rather than a task list:
the when form edits a flat all / any of (optionally
negated) condition flags, which is every condition any shipped gene uses. A nested
condition is preserved byte-for-byte and shown read-only, because silently
flattening someone’s tree is worse than declining to edit it. A real
tree editor is only worth building once a gene needs one.
9.2 A usability pass on the creator
“We’ll edit it more to make it a little more user-friendly later.” The tool is correct now — it loads, it previews a real horse, it writes every part of the format, and it proves its own engine against the game’s on boot. What it has never had is a pass for whether it is pleasant to use.
Nothing here is specified yet, deliberately — the point of the pass is to look at it with fresh eyes rather than to implement a list written in advance. Known rough edges to start from:
- The three columns are dense and fixed. Gene, horse and layers-plus-effects-plus-export all compete for width, and the right column now carries three stacked sections. Nothing collapses, so a gene with several layers and several effects pushes the export box a long way down.
- Effects and layers look alike. They share the
.layercard, which is honest about them being peers but makes the right column read as one long undifferentiated stack. - The forms are flat. Every parameter of every mask, op and effect is always visible at the same weight; nothing is folded away behind “advanced”, and the common case is not faster than the rare one.
- Errors are a list at the bottom. The validator names the problem well but does not point at the field that caused it.
- No undo, and no warning before “New gene” discards what you have.
The constraint that does not move: it has to keep working from
file://. That is why every script is a classic one and every asset is
inlined — see the format. A UI framework that
needs a bundler or a dev server is out.
Breeds as packages shipped 2026-09-07 and is no longer on this page:
a breed is a JSON file, there is a loader beside GeneSpecLoader, and
the breed designer is the editor for it. What
that leaves here is only the marking half above. See
the breed file format.
Part 4 — Genes still to build
Finished genes are not listed here — that is all forty-eight of them, rostered on the genetics model with a page each, and any residual tuning lives on the gene’s own page. What follows is only what no gene does yet. Natural genes all exist in real life, so they all belong in the 0–99 priority band and run before any magical gene; the scientific allele names are legal tokens as written.
11. Natural genes
naturalgeneref.md audit — a standing task
naturalgeneref.md in the repo root is a cleaned extract of another
horse game’s natural-genetics guide, and it is the reference the natural
side should be complete against. It already carries a level of game-ready
abstraction, which is why it is worth diffing against rather than primary
sources. Standing task: read it end to end, diff it against the
registered genes, and turn each gap into a row here.
Last run 2026-09-06. Every base colour and dilute in the reference is modelled at least in mechanism. What it turned up is below: six genes that do not exist, several shipped genes whose variation vocabulary is narrower than the reference describes, a phenotype-naming surface the mod has no equivalent of, and a disorder table (§11).
10.1 Genes that do not exist at all
| Gene | What it is | What it needs |
|---|---|---|
Wild bay (and At as a real allele) | An agouti variant pushing the black points down — black only to the fetlock rather than the hock, with a gap between the knee/hock point and any fetlock point, and a more yellow body. | Cleanest as a third agouti allele (A+, dominant to A). And if A+ becomes an allele, At (seal) should too — then agouti is the reference’s four-allele A+ > A > At > a, and seal stops being an epigenetic edge case (next row). |
| Natural (polygenic) face and leg white | The reference is explicit that a horse with no paint gene can still carry a star, stripe, snip, blaze, coronet, sock or stocking. The mod only draws face and leg white as part of a KIT / MITF / PAX3 / EDNRB outcome, so a bay with no white gene is always cleanly solid. | A small always-on marking draw — a handful of epigenetic values, no allele — feeding the shared WhitePattern.faceMarking and leg vocabulary at low strength. The vocabulary already exists; this is a caller, not a painter. |
| DMRT3 — gait | Dominant. Ambling / running-walk / pace. | The gene is trivial; the value is the animation, which is renderer and movement work. That is why it is last in this group. Wanted by both Paso Finos, Tennessee Walking, Standardbred, Icelandic and Hackney. |
10.2 Alleles missing from loci that ship
| Locus | Missing | Notes |
|---|---|---|
| Extension (MC1R) | ea | Purely additive now that multi-allele support exists. ea is phenotypically red like e, so all the interest is in the code string and the carrier display. |
KIT — the rest of the W series | W1–W3, W6–W9, W11, W12, W14, W16–W19, W21, W24–W28, W30, W31, W33 | Twelve of thirty-plus are modelled — W15, W32, W34 and W35 landed with the booster group, which was the important part of the remainder: W32/W34/W35 are common, not rare, and the locus had none of them. What is left is genuinely the long tail — one founder or one family each, frequencies that round to zero. The owner wants the full set mapped eventually, redundant alleles included, as a completeness goal — so this is a “do it” item, not a “not worth it” one. Each is a line in alleles(), a row in expressionOf and a frequency; ones whose published phenotype is indistinguishable from a modelled allele should collapse onto that allele’s outcome (the SW6–SW8 precedent) rather than getting their own row. Watch the combinatorial cost: each allele adds n combinations to a locus already at thirty-six, so do the hash-keyed texture id (§21) first. |
| The splash series | SW9, SW10; possibly splitting SW6–SW8 | SW6–SW8 are folded into SW5 on purpose — the source describes all four in word-for-word identical terms, so four alleles would be four indistinguishable rows. Split them when they become distinguishable. SW9 / SW10 need a locus check first: which of MITF / PAX3 they belong to is not settled in the reference used. |
10.2b Ordinary white markings have no gene of their own
Most stars, snips, socks and blazes in life are polygenic —
many small-effect variants, plus plain developmental variation in how far pigment cells
migrated. There is no single locus for them, and the study that identified
SW1 found no splash allele at all in 112
horses deliberately picked for minimal white.
The mod has no such system, so SW1 stands in for one: it
is the one real allele that genuinely does hide in plain sight, its single copy is
painted over a range from a snip to a full splash, and a majority of founders carry
one. That gives the world its ordinary marked horses at the cost of attributing them
to a locus that mostly does not cause them.
The honest fix is a small markings contribution — a per-horse
score with no allele behind it, or a low-effect additive locus in the
shade / sooty dosage shape
— drawn from the shared face-marking vocabulary and a leg ramp, running before
the white loci so they stack onto it. Then SW1’s frequency drops to
what its own evidence supports and the two stop being the same claim. It is a new
system, which is why it is here and not in the gene.
10.3 Shipped genes whose vocabulary is too narrow
| Gene | What the reference describes that is not drawn |
|---|---|
| Dun | Guard hairs — the pale hairs at the base of a dun’s mane and tail — which want a hair-level treatment the mane box does not have. And d1 should carry faint bars and shoulder shading, not the dorsal stripe alone; the blocker is that d1 does not dilute, so there is nothing on a leg to countershade a bar against. (The dilution, points, leg bars, shoulder bar and face mask / cobwebbing all ship — see the gene page.) |
| Flaxen | Ships as a uniform lightening with strand mixing - a fixed noise field spares some hairs. What it does not draw is the root-to-tip gradient the reference describes: dark at the roots, palest at the ends. That wants a coordinate along the hair rather than across it, which is what HairPattern.axesBySpan already finds for the magical mane colours, so the seam exists. |
| Silver dapple | Ships as the dilution only — the dapples the gene is named for are not drawn. Wants a deterministic fixed-seed BodyNoise modulation, like grey’s but lighter and per-genotype. The flaxen mane also currently reads a little gold rather than pale. |
| Roan | Corn spots — small islands of solid base colour left inside the roan field. The per-texel dither has no notion of them; wants a second, low-frequency mask pinning a few texels fully coloured. |
10.4 Base-aware phenotype naming
A surface, not a gene, and it serves three separate gaps. The reference names gold / amber / sable / classic champagne and chestnut / bay / black / cream pearl as distinct phenotypes by base, and expects cream×champagne and cream×pearl to read differently again. Separately, it treats seal bay as its own base that sable champagne rides on — but seal has no gene in this mod, it is the top of agouti’s epigenetic distribution.
So the mod needs a way to resolve and name “this genotype is an
amber champagne” or “this horse is seal” for the info panel, the
paper dump and the gene dictionary — and champagne needs to read “is this
horse seal” off the agouti epigenetics to paint sable rather than amber. One
base-aware phenotype resolver serves all of it. Making At a real allele
(§10.1) removes the seal half of the problem outright.
“Add a red border to every white area” and “turn all the black to pink”. Neither is a committed gene; both are the acceptance test for whether a magical gene can actually see what is underneath it.
12. Health and disorder genes
The reference table’s disorder list is now covered. Sixteen loci ship along with the health system they were blocked on: a lethal foal is born and then dies, everything sub-lethal is fewer hearts and a slower horse, and no founder is ever born dying. Machinery on the horse’s body; each locus has its own page.
The last batch brought two shapes the mod did not have. HYPP (SCN4A) is the first locus that is both a survivable heart reducer and a homozygous lethal. PSSM1 (GYS1) is the first dominant sub-lethal — and between them they broke the rule that no founder is ever affected, because a dominant with no silent carrier that never appeared in a founder could never appear at all. A wild-caught horse can now be visibly unwell, which is the only disorder shape a player can see before breeding from it.
Two of the nine, CVM and megaesophagus, have no confirmed causal variant in the literature. They are keyed on the condition rather than on an invented gene symbol, and both pages say plainly that one recessive locus is the mod’s simplification rather than a claim about horse genetics.
What else is left on the health layer
- Carrier display is half done. The info panel and the paper dump show a horse’s expressed conditions, which was the easy half. What is still unread is every disorder’s carrier sentence — each declares one, explaining what a single copy means, and nothing shows it.
- A punnett / expected-foal display reading the same table is the natural companion, and it is what turns two carriers from a nasty surprise into a decision. This is the highest-value item in the section. Half-shipped 2026-09-08: every gene page now carries a real punnett square with two parent pickers, so the reference half exists and a player can look up what a cross can throw. What is still missing is the half that matters here — the same square answered about two horses you actually own, in game, with the carrier sentence beside it.
- Per-part scaling for both dwarfisms.
B4GALT7’s Friesian dwarfism should shorten
the limbs and ribs and leave the head, and ACAN’s
is a proportion change too, but
Attributes.SCALEis one number for the whole entity. Doing it properly means owning the horse model rather than borrowing vanilla’s; both render as an overall three-quarter horse today. - The naked-foal coat. ST14’s disorder ships; the coat does not. Phase 1 only removes pigment, and a de-pigmented mane reads as a white mane — a different horse and a worse lie than drawing nothing. Wants a bare-skin template or a real phase-3 pass.
- Founder frequencies are guesses. Carrier rates run 1.2% to 4%, chosen so a wild horse is healthy and an inbred line is not. Whether a player ever actually meets a lethal at those numbers is a play-test question, and the answer may be that they are all too rare to matter. The two dominants are the exception and the one to watch: every copy shows, so PSSM1 at 2.5% is the only disorder a player is likely to meet by accident, and it is the number most likely to want turning.
- Nothing here has been play-tested — not a scaled horse, not a dying foal, not a refused pairing, not the config, and not a wild horse born sick. The checklist is where that lives, and the custom spawn egg is the quickest way in: adding a health gene defaults it to the affected genotype, so “add PLOD1, pick Foal, spawn” is a one-click lethal foal.
13. Magical genes
Invented genes, phase 3, priority 100+. This list has no
upstream source to be complete against — add to it freely. Each needs
a priority, an allele set with n as wild type, a founder distribution
and a delta function. Fixes to the magical genes that do ship are in
§1.2.
12.1 More of what already works
- More LUT palettes. One variant ships
(
Blupnk). The gene is built to take more — a new palette is a new allele here, never a new gene — each being alutResources()entry plus an expression row and a founder weight. Wants a batch of hand-authored gradient PNGs. - More magical marking and “stamp” genes before 1.0. Colourful flank and body emblems in the spirit of cutie marks but simpler — no item atlas. Open-ended; the owner wants a visible batch of these for the 1.0 look.
Two mask parameters the 2026-09-10 evening intake asked for
Both arrived as tool gaps in the authoring
notes, both were read after the approximations had been seen on a horse, and both
describe a shortfall that is really there. Each is one new parameter on an existing
mask, defaulting to today's behaviour, so no shipped gene moves - but
each is still the mask contract: the
painter, SpecSchema, the creator's twin, both tables, a
prismatic case, fixtures and parity.
RAMPangle(degrees, default 0, read only whenaxisis X, Y or Z): rotate the sweep within the plane of the named axis and the next one round (X→Y→Z→X), soangle: 90on X is Y. The coverage is the dot product of the position with the rotated unit vector, normalised throughfrom/toas now. What it buys: an SVG's<linearGradient>runs at an arbitrary angle and today can only be collapsed to one flat tone per band or one sweep along the horse's length - tidewave ships with the latter, and its swells lose the sense of each being separately lit.FANorigin: "cell"(withscale,seed, andvaryas a per-cell rotation of the fan's zero): walk the same latticeSPOTSuses and measure the angle and radius from the texel's own cell centre instead of fromoriginU/originV, so a field of independent sprays is one layer. Multiplying oneFANbySPOTScannot do it - that clips one global fan into cell-shaped pieces that all point at the same pivot. Opal fire ships with two hand-placed fans that land in the same two spots on every horse. Note gap 182 before building it: a per-cell pivot on a 3D lattice will mostly sit off the skin, so this probably wants the same snap-to-surface option as the centre marks.
12.2 Behavioural, diet and shapeshifting genes
A family that changes what a horse does rather than how it looks, riding
the effects path plus whatever new verbs
each needs.
A twenty-nine-gene proposal was specified one gene at a time, two were cut, and
the remaining twenty-seven are built, registered and documented.
Each has its own page carrying the settled inheritance, the rejected
alternatives, and its game-breaking, compatibility and performance hazards on
the coding tab. Those pages are the source of truth; nothing about
them belongs here any more. None of it has been seen in game
— To be verified is the authority on that,
and is where the batch now lives.
The two that were cut. Moon-sick was already shipped as the
Aga/Aga allele on night temper,
and a second locus doing the same thing is not a gene. Electric cannot be
built as described: redstone power comes from blocks and never from entities, so the
honest version is either per-tick block placement or pressure plates, which already
respond to every horse in vanilla. Neither should be reopened without a new argument.
What the batch cost, for the next time somebody estimates one. Seven
new verbs (sound, produce, teleport,
summon, temper, bond, ward), two
new triggers, four new condition flags, five existing verbs widened, and three base
classes that carry twenty-three of the twenty-six between them. The earlier estimate
of “about one verb per behaviour” was roughly three times too high:
behaviours cluster, and the base classes were worth more than any individual verb.
What is left on this family
| Gene / family | What it does | What it needs |
|---|---|---|
| Diet genes | A carnivore horse — eats or is fed meat, refuses or is unaffected by plants. The hook for vampire and werewolf breeds. | Partly built: HorseDietHandler and the diet locus ship. What is missing is the rest of the vocabulary and the breeding / taming side. Food preference now deliberately overrides it, and that ordering is a contract rather than an accident. |
| Vampire horses | Heal only by damaging other living mobs; optionally take sun damage like a skeleton. | Suppress the gated regen for these horses and add a lifesteal-on-attack effect. Sun damage is a burn-in-daylight tick reusing the skeleton condition. The temper verb and the on_hurt trigger both exist now, so this is cheaper than it was. |
| A guardian that answers a player | A guardian currently never retaliates against a player attacker, only against mobs. | A deliberate limitation rather than a gap: 26.1.2 exposes no server-side PvP flag to gate it on, and a horse joining a fight between two people without either agreeing is the wrong default. It wants a mod config option, not a code fix. |
Lycan onto MobRoster | Three loci have an allele per mob; two read one table and lycan still keeps its own copy. | The drift this is designed to prevent. Lycan's allele order is baked into the genotype code and two checked-in goldens, so migrating it moves every saved horse — a real change with a real diff, not a tidy-up. See known gaps. |
12.3 Attached model parts
Blockbench meshes attached to the horse model, gene- or breed-driven, with no independent animation needed. High priority: unicorn horns, spikes / dorsal spines, crystalline growths — small models, head- or spine-parented, big visual payoff. Lower: pegasus wings (body-parented), lion and tufted tails, hoof feathering (already wanted by the draught breeds), tusks, antlers. Do a batch of the head-parented ones first — one render layer, many models.
- This is the mod’s first non-texture visual. The coat
pipeline is texture-only and
common/is Minecraft-free, socommon/can only declare that a gene wants partX, by id; the renderer resolves it — the same shape asGradientLutarriving as anint[]. - The proposal is GeckoLib plus Blockbench, with an attached layer whose bones are children of the horse’s body bone so they inherit every animation. Check whether a plain render layer parented to the existing model part does the job before taking the dependency — animation inheritance is the entire argument for GeckoLib, and a horn does not animate independently of the head.
- Saddle, armour and third-party tack render at the same time. Layer order and z-fighting are the predictable bugs — and see §2, because these all need to scale too.
Part 5 — The gameplay layer
Sections 13–20 come from two design documents handed over whole: a system summary of the gameplay features, and a specification for a trading villager. Together they describe a layer sitting on top of the genetics. The items, carrots and database, bond and herds and seed jar slices that are built are documented on those pages; what follows is the remainder. Settle the custom-entity question (§3) before designing any of it.
14. Items, stalls and the objects nobody specified
The full item roster, every recipe and what each one does is on the items page. What is missing:
13.1 Named in the design documents, never specified
| Thing | What is known | What is missing |
|---|---|---|
| Stalls and holding pens | Definition and its consumer are both built — see stall signs and tickets. | Auto-return — a horse that takes itself home, rather than one sent by a ticket. Cleanup beyond breaking the exact sign block: rebuilding a wall elsewhere, or removing the horse, leaves a stale record. Holding pens as distinct from stalls. A real stall-sign block instead of a repurposed oak_wall_sign. A persistent client wireframe rather than the F7 particle flash, which needs a RenderLevelStageEvent renderer against the changed 26.1.2 pipeline. |
| Whistles | Built — recall your tamed horses within 16 / 32 / 64 blocks. | Bond gating (§14); whether they should path rather than teleport; and what “echo” adds beyond range. All three are answered by callable horses, below. |
| Double-wide gate | Crafted via braided rope. | Presumably a two-block-wide fence gate. A vanilla-shaped block problem, unrelated to the genetics. |
Callable horses — the whistle, bound
The whistles are an area recall: blow one and every tamed horse of yours inside 16 / 32 / 64 blocks comes. The wanted end state is the RPG one — press a thing, and your horse arrives, wherever it is. The reference implementation is the Callable Horses mod: sit on a horse and press P to make it your personal horse, press V to call it (it walks if near, teleports if far, optionally across dimensions), one personal horse per player, with permanent death and a maximum call distance both configurable.
Taking the feature is right. Taking its shape is not, on three counts, and each disagreement points at the version this mod should build instead.
One personal horse is the wrong limit — bind the whistle, not the player
A player of this mod has a stable of horses with pedigrees, and picking a single favourite is exactly the choice the mod exists to make hard. So the binding lives on the item: a whistle is bound to one horse, a player carries as many whistles as they have horses worth calling, and an unbound whistle keeps today’s area recall unchanged.
- The component already exists.
ModDataComponents.BOUND_HORSEcarries a horse UUID plus a display name for tooltips, and its javadoc already anticipates being reused beyond the stall sign. A bound whistle is that component on a whistle. - Bind by right-clicking the horse with the whistle — how the
stall sign binds, and how every other item in this mod claims a horse. Not by a
keybind while mounted. It also means binding is an
EntityInteracthandler of a shape we already have. - Rebinding is free; unbinding is sneak-right-click on air. No ceremony — a whistle is not a contract, the transfer paper is.
- Naming falls out. The component’s
namefield puts the horse’s own name on the whistle’s tooltip, so a hotbar of five whistles is readable at a glance. That is the answer to “how does this work for many horses”.
Keybinds: the item is the interface
This mod has exactly one production keybind — H for the
horse browser, in HorseBrowserKeyBindings —
and everything else a player does is done by holding an item and clicking. That is a
pattern, not an accident, and a feature that arrived with two new default keys would
break it.
- Right-click a bound whistle = call that horse. Right-click an unbound whistle = today’s area recall. Neither needs a key.
- At most one optional keybind, and only as a convenience over the
item: blow the whistle you are holding, else the first whistle in your
hotbar. Registered exactly as
HorseBrowserKeyBindingsdoes it —KeyConflictContext.IN_GAME, a built-inKeyMapping.Categoryrather than a localised custom one, rebindable in Controls, and a default that collides with neither H nor vanilla. - No mounted-bind key at all. Callable Horses needs P because it has nowhere else to put the binding. We have an item in hand already.
The three tiers already are the config
Callable Horses exposes maxCallingDistance,
callableInEveryDimension and a dimension whitelist as options. We have
three crafted tiers that already mean range, and an open question on this page about
what the echo whistle adds beyond reach. This answers it:
| Whistle | Unbound (today) | Bound (proposed) |
|---|---|---|
| Basic | 16 blocks | Calls a horse in a loaded chunk, same dimension |
| Golden | 32 blocks | Any distance, same dimension — including unloaded chunks |
| Echo | 64 blocks | Any distance, across dimensions |
That gives the echo whistle a real identity, gives the crafting ladder a reason to exist, and keeps the numbers in the recipe rather than in a config file, which is the house style. A server that wants Callable Horses’ restrictions still gets them — as a config that caps the usable tier, not as a distance in blocks.
It had to be settled against the tickets, and it was: a whistle brings the horse to you; a ticket sends the horse to its stall. Two directions, two items, no overlap — and the interdimensional ticket and the echo whistle are the same idea pointed opposite ways. The tickets shipped on that reading in 0.3.7, with the tier as reach rather than as a binding.
Walk, then teleport — and gate it on bond
- Path if it can, teleport if it cannot. Callable Horses gets this
right, and it closes the open “should whistles path rather than
teleport” question: inside the bond-follow goal’s range the horse walks
to you, which is legible and charming; beyond it, it teleports, which is the only
thing that works.
BondFollowGoalalready knows how to walk a horse to its owner. - Binding should cost bond. A whistle that binds to a barely-tamed horse makes bond meaningless. Requiring a tier to bind is the gating this page already wants for whistles generally, and this is the natural place for it. Which tier is an open number.
- Re-check ownership at call time, not at bind time. A
transfer paper changes an owner without
rewriting history, and
HorseRosteralready documents that the record’s tamer and the entity’s owner are two different facts. A whistle bound to a horse you have since sold must refuse, and say why.
The one real technical unknown: calling a horse you cannot see
Everything above is design. This is the part that might not work.
HorseRoster states the problem already: whereabouts are entity facts,
so they are only knowable for a horse in a loaded chunk. A whistle that calls a
horse two thousand blocks away has to find an entity that is not ticking.
- Option A — remember where it was. Write last-known position and dimension into the horse’s record on unload; on a call, load that chunk, find the entity by UUID, teleport it. Costs a record field and a chunk load, and goes stale if a horse ever moves by a path that does not unload.
- Option B — ask every level.
ServerLevel.getEntity(UUID)per dimension is a map lookup rather than a scan, andHorseRoster.livealready does exactly this — but it only answers for loaded entities, so it solves the easy half and none of the hard half. - Option C — keep an index. The per-world horse database is
already a
SavedData; extending it to map a horse UUID to its last-seen dimension and chunk is the durable answer, and probably the right one, since the browser, the stall records and the tickets all want the same lookup.
Option C looks right, and it is shared infrastructure rather than whistle infrastructure — so it should be scoped as its own piece of work and the whistle built on top of it, not the other way round.
What we should not take
- Respawning a dead horse (
deathIsPermanent = false). A horse here is a genome and a pedigree, and the whole breeding layer rests on a dead horse being gone. A whistle bound to a dead horse should say so plainly and offer to rebind, and nothing more. Owner’s call — but the default should be no. - The single-instance guarantee. Callable Horses despawns stale copies of your personal horse because its calling can duplicate one. Ours moves an entity found by UUID and never creates one, so there is nothing to de-duplicate — and if we ever need that guarantee it means there is a bug elsewhere.
- A stats viewer. Theirs is a feature; ours is
HorseInfoScreenplus the browser, and both already show more.
Left open
- Which bond tier binding requires.
- Whether a bound whistle keeps the area recall as a secondary use (sneak-right-click?) or gives it up entirely.
- Whether non-horse equines can be bound.
WhistleItemalready filters onAbstractHorse, so donkeys, mules and llamas answer an area whistle today — but onlyminecraft:horsecarries a genome, so a bound llama is a slightly odd object. - Whether the browser should show which of your horses have a whistle out.
- The three whistles still share one texture (below), and a bound whistle needs to look different from an unbound one.
13.2 Art and coverage
- Real textures. The four tickets share one, the three whistles
share one, both stall signs borrow
minecraft:item/oak_sign, andbraided_rope/hair_clothhave recipes but no art. - Audit
item/ModItemsagainst the recipe folder and make sure every craftable item has a shapeless, at-least-one-modded-input recipe or a deliberate “not craftable, obtained by X” note. An item with no recipe and no page is invisible to a player who is not reading the source, and that is a 1.0 blocker. - Keep items.html current. It is hand-written against the recipe JSON; generating it, or checking it in a test, would stop it rotting.
15. Bond, herds and horse care
The core shipped — a per-horse bond number with four behaviour tiers, an emergent herd id, one shared 30-tick scan, and the comfort buff as passive regen. The tier table, gain rates, daily cap and herd rule are on horse care. What is left:
More bond sources
- Sleeping nearby, +1/night — the one remaining source from the original design. Shearing (+5) is wired.
- Being led on a lead counts. Walking a leashed horse is taking
it for a walk — a small per-tick trickle while
isLeashed(), the holder is the owner, and both are moving, capped by the daily limit like everything else. - Flavour interactions — petting, brushing, cleaning. Cheap
if animation-only: right-click with an empty hand (or a
brushitem) plays a short interaction and gives a little bond, and brushing heals a small amount — a second route through the healing gate, or exempt from it the way hand-feeding is. A brush is one more line in the crafting economy (§13).
Herds, and testing any of it
- Nothing consumes the herd alpha. Computed on demand, no consumer — no herd-wide behaviour, no alpha-follows, no display.
- Herd membership is invisible outside the inventory panel’s “• herd” flag. The real surface is the browser (§17).
- Feeding is not dose- or temper-aware: any
isFoodstack from the owner is +2, whatever it is. - Gated healing has no info-panel line naming what is missing — a hurt horse near food but no water shows particles and nothing else.
A research paper names an allele, not a gene
The wanted end state: research papers pull out individual alleles rather than whole genes, the equine research shelf lets you pick two of them and binds them into one book, and that book is what the carrot recipes take. The crafting recipes themselves do not change.
Stage one shipped and is the load-bearing half:
CarrotEffect.KnownGeneSplice carries two allele tokens instead of a
gene key and a het/hom flag, so a carrot already names exactly the pair it grants. That
fixed a limitation nothing had recorded — the pair used to be built from
gene.alleles().get(0), the first-declared allele, so on the forty-allele
particle locus no carrot could ever hand over anything
but dust, and KIT could only ever give one of its thirteen.
What is left, in the order that keeps the game working at each step:
- The paper carries an allele.
RESEARCH_GENEbecomes a gene key plus an allele token, and the discovery system that grants papers has to decide which allele a horse teaches you — presumably the ones it actually carries, which is the same questioncommon/horse/RosterGeneticsalready answers for the browser's pickers. - The shelf binds two into a book. Its Copy tab already
picks one gene and spends blank books; this is that tab taking two allele papers
instead.
ResearchShelfMenuandResearchShelfScreen. - The recipe takes the book.
KnownGeneSpliceRecipecurrently matches a research paper and callsCarrotEffect.defaultSpliceForto decide the pair; it would read the pair off the book instead, at which pointdefaultSpliceForandGene.geneCarrotHomozygous()both stop being needed and should go. - The tooltip. A Known Gene Splice carrot already carries the right data and has never shown it — every one of them looks identical in the inventory. That is worth doing whether or not the rest lands.
Note the interaction: the carrot's homozygous-by-default rule, also settled 2026-09-11, is a stopgap that this design deletes. Once the player picks both alleles there is no default to choose.
16. The carrot content pass
The mechanism is complete. What is missing is content across every registered gene, not machinery.
The parameterised recipe means a gene can have a carrot; it does not
mean the carrot is balanced. Today every built-in falls
back to the minimal recipe and the uniform splice draw. Each gene wants a
hand-tuned carrot block: a rarity tier matching how
strong the gene is, flavour ingredients that read as the gene (a
nether wart for lava milk, a glow ink sac for
light, an amethyst shard for a
LUT palette…), and a splice table
producing a sensible outcome rather than a coin-flip on a forty-allele locus.
Note the recipe currently takes exactly four items with no flavour slot, so shipping flavour lists means re-opening the recipe as well as writing the data.
- The Epigenetic Stabilizer carrot. The other end of the dial from the Unknown Epigenetic Splice: it forces the fed parent’s passed-on copies to be inherited verbatim, suppressing drift for one breeding, so a player who wants a near-clone foal can buy that. Now that drift is the default on every value this is the only way to switch it off, which is what makes it worth having.
- Carrot effects captured on a seed jar and applied at impregnation. None are carried today.
- A database check inside the recipe. The paper ingredient is the whole gate today, which is probably right — but it means a paper traded from another player works without the receiver ever discovering the gene. Decide deliberately.
- New carrots are cheap, and the five
themed splices are the first proof of it: once the
GameteBiashook and thecarrot_effectscomponent exist, a new carrot is aCarrotEffectcase and afoldrule. Those five did not even need a case — they are one field on the existingGeneSplice. The list is meant to grow. - The themed splices need play-balancing, not building. Each rolls a uniform locus out of its slice, so a pool of five (marking) hits each locus four times as often as a pool of twenty would. Whether that reads as “this carrot is about markings” or as “this carrot always gives me sooty” is a play question. A per-theme weighting, or letting a gene declare its own weight, is the lever if it needs one.
17. Assisted reproduction: gestation, and the later tiers
16.1 Finishing the seed jar
The cut-down flow is built and written up on breeding — fill a jar from a breeding-mode stallion, use it on a breeding-mode mare, foal bred through the ordinary path with the draw happening at impregnation. What is left:
- Gestation — the genuinely new part. The foal appears immediately, like vanilla. A gestation state means an attachment plus a tick, and answers for: the mare is killed, the chunk unloads, the world reloads mid-term, the mare is bred normally while carrying.
- The real breeding-carrot gate. Vanilla love
(
isInLove()) currently stands in for “fed a breeding carrot”. The carrot window is the timed window, so there is no separate timer to build. - The donor cooldown — once per Minecraft day, the mod-wide rule — is not applied; only the mare gets the vanilla breeding cooldown.
- The pedigree edge. It is settled that the jar must store enough of the sire’s record — not just his UUID — for the family tree to render his node. It stores name and sex already, but the synthetic sire record still uses the stored UUID as its pedigree edge, so the tree may not find the node. Wire it through.
- A
collectedFrommarker (player-made vs villager-sold) and the “no Known Gene Splice carrot on a jar” recipe restriction are both trivial once the above exists. - This is what the horseman is
missing. His trade table shipped without the two rows that wanted a
vial — selling a random rolled genotype, and buying a player-made one back
— because both need
collectedFromand a vial worth rolling. His tier-5 buy row takes a stallion seed jar instead. Adding the rows later is two JSON files and, for the buy-back, acomponentspredicate on thewantsblock, which the trade format already supports.
16.2 The later tiers
Embryo extraction, gene screening, gene splicing, cloning, cross-species. Each is a block with an inventory and a screen, and each is endgame by intent. Three notes that change how they should be scoped:
- The gene sequencer is not new work — it is the browser (§17) or the family-tree screen pointed at a vial or an embryo. Build it as a view, not a machine with its own UI.
- Gene splicing is the first thing in the mod that writes an arbitrary genotype in survival. It needs the same validity predicate the creative spawner needs — is this allele pair legal, does this locus exist — and that predicate should be written once and shared. Whether a player can splice into a lethal genotype is an open decision (§3).
- Cross-species is much larger than its row. Donkeys and mules are separate vanilla entity types, mules are sterile by design, and neither carries any of this mod’s genetics. Extending the genome to another species is a project, not a tier. Scope it separately or cut it.
18. The horse browser
The H-key screen is a real container menu with My
horses, Gene database, Breeding preview
and Crafting tabs. The full plan is six: the horse list and the
gene database are built, and what is left below is the vial inventory,
the herd view, the bonding view and a generated
crafting tree — plus out-of-band actions (teleport a horse to
a stall, whistle for bonded horses, recall to a holding pen).
The expensive part was never the UI. A proper per-player
owned-horse registry — updated on tame, breed, death and transfer, holding
enough denormalised data (name, sex, age, genotype, bond, herd) that horses in
unloaded chunks still appear — does not exist. The My horses tab
ships on HorseRoster instead, which walks the whole ancestry table
on request, cuts the result at HorseRosterPayload.MAX_ENTRIES and
sends it, and the client sorts and filters what it got.
That is exactly the “do not filter it client-side” this note
used to forbid, and it was chosen with eyes open: the cap is the whole
of the difference, the tab says so in its footer when it bites, and the drawing
code takes a list of HorseListings and does not care who filtered
them. What is still owed is the registry, sort and filter running
server-side over it, and pagination. HorseQuery is
already game-free, so the same predicate can run on the server the day the index
exists.
The tab the owner has still asked for
- The bonding tab. The bond level with each horse, the tier it is at, and concrete guidance on how to raise it — which sources are available, what the daily cap is, how close the next tier is. This is the surface §14 says bond is missing. My horses shows the number and sorts by it, which is not the same thing: it says where you are and not how to move.
The rest
- A row should open the horse. Clicking a My horses row selects it
and fills the footer; it should reach the information screen and the family tree.
Both want a
HorseRecordand the roster deliberately does not carry one (it carries aHorseListing, which is a tenth of the bytes), so this needs a fetch-one-record round trip. FamilyTreeScreenshould become a tab rather than stay a separate screen. Row portraits exist now —client/HorsePortrait, used by the My horses table and both breeding pickers — but they are a second copy of the tree'sdrawHorseModelrather than a shared one (gap 78), and merging them is part of this. It is also the working reference for a from-scratch retained-mode screen in this codebase, scissor clipping and entity draw included — with the standing warning: build the render state from a throwaway client-onlyHorse, and never touchClientCoatCache.- The crafting-tree tab goes stale on contact with a modded gene. Generate it from the recipe manager at runtime; do not hard-code the graph.
- Teleport, recall and whistle need the stall and pen definitions §13 says are missing, plus answers for a destination that no longer exists and for whether any of it crosses dimensions.
- Every tab is an untested GUI. Built against the
26.1.2 retained-mode API with no play session. Risks: the fixed-panel slot and
label geometry at GUI scale 2/3/4, a ghost slot-highlight bleeding through on the
Gene database tab (the slots are
isActive() == falsethere, but the highlight path is only partly reachable),quickMoveStackedge cases, the book → research paper result path, and — new — whether thirteen columns of My horses are legible at GUI scale 3 and 4, wheredrawFittedsqueezes each one rather than dropping it.
A creative-only item that sets extension and agouti plus one heterozygous custom gene is a strict subset of what the custom horse spawn egg already does, with its own validation code to maintain. If a placeable, giftable object is genuinely wanted for events and contest prizes, give the spawner an “export as spawn egg” output instead. One screen, one validator, one code path.
A natural / magical filter on the Breeds tab
The owner's note (2026-09-10): once magical breeds exist, the Breeds tab needs a way to
show the natural ones, the magical ones, or both. Breed.magical() is already
on every breed, so the filter is a toggle beside the list and a clause in
HorseBrowserScreen.drawBreedList. Not built yet because there are no magical
breeds to filter - build it with the first one, so it can be seen doing something.
19. More generated stables
Three ship, and the owner has said more are coming. The machinery is deliberately finished before the content: adding one is an NBT and a JSON, with no Java, so this section is a content backlog rather than a build one.
- The licence question is a blocker on distribution, not on building. Every stable is somebody else’s work redistributed inside the jar, and none of the three has had its terms read (gap 80). That has to be settled before this mod goes anywhere, and it applies to every stable added after.
- Nothing yet decides which stable generates where. All three share one biome tag, so a snowy plains gets the same buildings as a savanna. A stable could name its own biomes — that is a line in its worldgen file and no code — and probably should once there are enough of them to tell apart.
- A stable has no owner, no story and no loot. The buildings have chests, and the bake empties them. A stable that came with a horseman living in it, or with tack in the chests, is the obvious next thing and is not built.
- Nothing stops a player from simply taking the horses. They are untamed and unowned, so the whole reward is "find building, tame seven rare horses". Whether that wants a cost — a price, a fight, a locked gate — is an open design question nobody has asked yet.
20. Breeds
The breed system shipped — 49 breeds, biome-weighted
herd spawning, cross / spliced / mixed labels, per-breed stat bands feeding the four
magical body-stat genes, and (2026-09-07) a
data-driven file format so a breed needs no Java.
No new gene came with it, and the breed definitions
reference genes and cosmetics the mod does not have. Each is noted in the
breed’s notes; the gene-shaped ones are rows in
§§10–11
already. What is left here:
Cosmetics that are not genetics
- Mane shape — Przewalski’s upright with no forelock, the long flowing manes of the Andalusian / Friesian / Gypsy. Render-layer and model work. The Fjord’s colour is off this list: the midtstol is drawn by dun, and the erect clip that shows it off is grooming rather than a mesh the horse is born with.
- Lower-leg feathering — Clydesdale, Shire, Friesian, Gypsy Vanner, Dales, Dartmoor. A render-layer overlay on the lower legs; the sheared look is the precedent, and both want the same layer.
Explicitly not modelling: the Akhal-Teke / Karabakh metallic coat sheen (a shader), the Marwari’s inward-curving ear tips (a mesh change), and dished / convex / Roman head profiles. Noted on the breeds and left there.
Breed-system follow-ups
- More fictional breeds wanted. The 49 are real-world; the mod also wants a batch of invented ones — a werewolf breed built on LYCAN is now possible — as gene suites. That is partly what would make a polygenic cutie mark worth doing. This is now cheap: a breed is a JSON file and the designer writes it, so an invented breed costs no Java at all. Nobody has written one yet, which is the whole of what is left here.
- Per-gene pool rates are estimates, never play-balanced. A herd should read as that breed without being a monoculture, and that is eyeballed across all 49.
- Total horse density is unmeasured. Biome assignments overlap
heavily in the vanilla horse biomes, and
add_horse_lonerstargets#minecraft:is_overworldwholesale. - Traditional-band foals spawn at vanilla’s ~5% instead of
the intended “up to 3”. The
SpawnGroupDataroute is unusable —Horse.finalizeSpawnreplaces it per member — so this needs a directsetAge(-24000)on a herd member, or a mixin. - Herd formation is proximity-only. A clump flood-fill with a deterministic lowest-UUID lead handles the ordinary case, but a pack that streams in over several ticks, or scatters further than the flood-fill hop, can still split.
- Wild aggro is all-or-nothing: 4 flat damage, no
ATTACK_DAMAGEscaling by breed or size, and a fixed 3-second forget-on-line-of-sight timer. Horses are neutral until hit, deliberately, like a wolf.
21. The horse dimension and the creative spawner
Both are built — the dimension and the spawn egg. Two pieces of rework remain.
20.1 Split the corridor
The owner wants the dimension to read as a gallery again, without going back to a catalogue-length corridor:
- Left side: random breeds. Each pen a
BreedFounder.rollof a real breed, signed with the breed name — a walk past the 49 breeds rather than past raw genotypes. - Right side: totally random horses. The current
ShowcaseGenotypesdraw, unchanged — the “what can the genes do” half. - Both sides keep the mare + stallion pair and the per-pen sign. This is a
buildPenchange (pick the draw by which side of the road the pen is on) plus a second sign line for the breed. No new machinery.
If a systematic gallery is ever wanted again, the shape that scales is a wing per locus — each gene’s pairs on a fixed wild-type background, bounded by alleles rather than by their product, extending naturally to a modded gene adding its own wing.
20.2 The per-copy epigenetics editor
The spawner’s whole-genome Rnd epigen. covers the creative use; the
per-copy view is the debugging one — each allele copy’s
priority and seed shown individually, with a type-it-in field for an exact value. It
is the only way to ask “which copy is expressing, and what does that
seed do” without reading a log. Two things to design around: at forty-odd genes
that is eighty fields, so it wants a per-gene expander rather than a flat list, and a
hand-typed seed has to survive Epigenome.parse or be rejected visibly.
Part 6 — Long range and reference
22. What breaks at scale
Forty-six genes ship (forty-eight in-game with the two example gene files), of which twenty-two paint and several carry many alleles. The gene backlog above is not a linear addition to that.
- The genotype space. Now
1,429,196,794,572,529,076,587,315,200,000 raw genotypes, of
which 11,098,128,386 are distinct to look at. It was
311 = 177,147before the seven visual genes shipped — the multiplier in miniature. Two things this has already broken:totalGenotypes()is aBigIntegerbecause the particle locus pushed it past 263 and the figure recorded before that had silently wrapped; andGenotypeCatalog.size()/get()becamelongwhen the leopard complex doubled the reduced catalogue pastInteger.MAX_VALUE. Nothing in production indexes the catalogue, so both were free. A derived number written out by hand is a liability, and one whose computation can silently wrap is worse — re-deriving it reproduces the lie. CoatTextureIdand path length. The encoding expands (A-Z→ 2 chars, anything else → 5), so a 300-character genotype code becomes a >1,000-character path, whichIdentifierwill not accept. The fix is to key the texture on a hash plus a short prefix. Keep theKEY_BY_IDtripwire — it caught the original flat-white-horse bug — but it becomes load-bearing rather than paranoid, so give it a real collision-resolution path (append a counter) instead of throwing. Do this before theW-series expansion (§10.2).- The genotype code string. Already 50 segments; the backlog above
with tokens like
SW10/W36puts a code in the low hundreds of characters. Everything carrying it grows in step — the record, both sync payloads, and the epigenome code, which is already the fatter of the two. Worth a compact binary encoding for the payloads at that point; keep the string form for saves and debugging. - Display. The pen sign caps out; the info panel needs grouping (colour / pattern / performance / health) and probably scrolling. It has already gained two rows and a condition list.
- Wild-spawn frequency needs a pass. With a dozen recessive lethals in the pool and more coming (§11): rare enough that wild horses are healthy, common enough that a breeder eventually meets one.
- Composer cost. Phase 1 is O(painting genes × texels) at
16,384 texels per sheet, and the non-painting genes cost nothing. The pattern
genes’
BodyNoisesampling is the real cost, and coats are cached per texture key, so this only bites on first bake.
api-reference.html hand-transcribes public signatures and each
gene-*.html hand-transcribes that gene’s constants. Neither is
generated, so both drift silently the moment a signature or a tuning number
changes. The gene creator is the one place this is guarded —
check-parity.mjs compares its schema mirror and its whole preview
engine against the real Java, and it has already caught a mis-ported
nextLong() and four drifted schema defaults. The cheap version
elsewhere is a :common:test that greps the wiki for the handful of
numbers the code can compute (Genes.codeOrder().size(),
GenotypeCatalog.size(), totalGenotypes()) and fails when
a page disagrees. Prose about how a gene works survives a change; a number
in prose almost never does.
23. Settled decisions — do not reopen
Kept here so a later session does not relitigate them. These are answers, not work.
Settled 2026-09-10
- Only pure-bred horses fill in a Breeds row. A cross
(
cross:a+b) or a spliced line teaches the breed book nothing. Owner's call - it was already how the code behaved, and is now the rule rather than a gap. - One holding pen per player. A second holding pen sign moves the pen. The owner considered and rejected several pens per player the same day.
- A spawner feeds uncapped. Two creatures per meal the horse eats, no local cap - the food is the cost, and the owner will price it. Only a meal actually eaten counts.
- A hurt mare refuses to be milked, bottle or bucket, and says so.
Settled 2026-09-08
- A gene that changes no pixel of the coat gets no icon, not a picture of
a horse it has nothing to do with. The baker used to fall back to the
plain bay for the thirty-six genes with no coat of their own - the health loci,
the magical stat genes, and the ones like cutie mark and particle that draw in a
client render layer rather than in the sheet. Honest and useless: a jump gene's
card showed a horse that says nothing about it, and a run of identical bays down
a page reads as a bug rather than as a fact. Their cards are words now, and
GeneIconTooldeletes a stale bake rather than skipping it, so a gene that loses its paint loses its picture on the next run. - The icon camera is two angles, not three tuned components. It was a hand-picked view vector plus a basis built with cross products, one of which had an inverted sign - so “up” was 45° off perpendicular and the whole projection was sheared along the view axis. Azimuth and elevation give an orthonormal basis by construction, and they are also the two numbers anybody actually wants to change. Do not go back to a vector: the reason the bug survived a day is that a sheared horse still looks like a horse, and a basis nobody can check by reading it is a basis nobody checks.
- The wiki groups genes the way the gene editors do, and neither list is
written by hand. The sidebar had five hand-written gene sections
(“Natural genes”, “White pattern loci”, “Performance
& size genes”…) and the landing page had its own arrangement
again, and the two gene editors' new family filter would have been a third.
GeneFamily is the one table now, and
:common:bakeGeneWikiPageswrites both lists from the registry: a gene page is registered by existing. Its sidebar label is the page's own<h1>and its views are the tab panels it carries, so neither can promise something the page does not have. - The landing page's four headline numbers are generated. That was
this section, and gap 93. It did not need a
new Gradle task in the end -
bakeGeneWikiPageswas already rewritingindex.htmlfor the gene cards, so the stats block went in the same pass and behind the same markers. The distinct-coat count now prints as a floor: it passedLong.MAX_VALUEsomewhere in the eighty-four magical genes, and a saturated counter should say so rather than quote its own ceiling. - A horse moves between the two editors as a whole horse, through one
format. §9 asked for an Import horse file button beside the
spawn egg's Paste code. It shipped as a replacement for it rather than a
neighbour: there is one format,
common/horse/HorseFile, the designer's Export / Import write it to a file and the spawn egg's Copy horse / Paste write it to the clipboard, and Copy code / Paste code are gone. Keeping both would have meant two ways to copy a horse of which one silently loses its epigenetics, its name and its breed, and nobody reads a button and thinks "this one is the lossy one". - Importing is not creative-gated. §9 left this open. It can only build a horse the editor could already build by hand, and everything above the Spawn button works in every game mode on purpose — the editor is a viewer that can also spawn. Spawning stays creative-only and still re-checked on the server.
Settled 2026-09-07
- The village horse trader is two people, not one. §19 described one villager profession that would sell items. It shipped as a horseman (that profession, exactly) and a cowboy who is not a profession at all: his stock is a live herd rather than a trade table, and a shopkeeper's daily schedule is the wrong machinery for a man who keeps animals. Splitting them lets each use the machinery it actually wants. The original case rested partly on his being permanently mounted, which was abandoned a day later — the split outlived the argument that produced it. See Villagers.
- The cowboy's house is part of the barn's structure piece, not built at runtime. The runtime version looked cheaper and never worked, and loosening its flatness test would not have saved it: the work posts stand on the house, the work posts are what make a cowboy, and a cowboy cannot build the thing that made him. Anything that wants to be near the barn and to exist before the cowboy does belongs in the same baked piece for the same reason. The cost is a deeper footprint and a piece that generates less often, paid for in pool weight. See Villagers and gap 61.
- A horse is sold as a paper, not handed over. The alternative was “pay the trader, receive a horse”, which is one click and leaves nothing behind. A signed transfer paper is a bearer claim on one named animal that is still standing where it was: the buyer has to go and collect it, and until they do the claim is an object they can trade onward, sell at a markup, or lose. That is the entire reason the item exists in this shape, and it is also what makes an in-village horse market possible between players, which no amount of villager trading would have.
- Ownership moves; breeding does not. Every transfer path leaves
bredByalone, so a horse bred by a cowboy still says so after four owners andHorseRecord.attribution()keeps naming him in the family tree.tamedByis only ever written when blank, because it records who first got a rope on the animal - also not a thing a sale changes.
Settled 2026-09-06
- A zebra and a brindle horse do not share a stripe painter. They
did, on the grounds that both are “stripes on a horse”, and it is why
both of them looked wrong: a zebra’s bands are crisp, symmetric and
organised round a body map — vertical on the barrel, arcing over
the hip, ringing the legs, tightening on the neck and face — while
brindle’s are soft, broken, unequal in width and deliberately different
on the two sides of one horse, because they are a record of X-inactivation
mosaicism. A generic stripe field phases on
|z|and is therefore structurally symmetric, so it cannot express brindle at all. They have a field each:ZebraStripesandBlaschkoStripes. See Body space. - A zebra is dark with the colour taken out, not white with black added
on. The dark hair is the default state and a white stripe is locally
suppressed melanin, so
ZebraStripesreturns the dark band coverage and natural zebra whitens1 - cin phase 1. Magic zebra blackenscin phase 3 — the only way to stripe a horse with no pigment left to remove, and the reason the magical one exists at all. - Brindle is white, not a dilution. A brindle streak goes to white hair whatever the horse under it is. The original “a lighter shade of the base colour” reading is defensible from the literature — real BR1 is as much a hair-texture trait as a pigment one — but the mod has one channel and it is colour, so the pigment half is drawn at its most legible and the texture half is a paragraph on the gene page.
- Attachments on the vanilla
Horse, never a subclass.EntityCustomHorse extends HorseEntityis dead and is not coming back. Everything hangs off the vanilla horse and the coat is injected at render time. It keeps horses in existing worlds working, keeps the 1.12.2 backport cheap, and avoids spawn replacement — the fragile part — entirely. The cost is that only one mod can own the horse renderer; that trade and the rest of the compatibility surface are written up on Mod compatibility. - “Unknown” is now Feral Mixed, and it is absorbing. Any cross involving a Feral Mixed horse produces a plain Mixed foal — Feral × Feral included, so there is no back door to a “pure feral” line. As Unknown it combined like an ordinary distinct breed, which forced the model to answer whether a wild loner had a stat band, a gene pool and a claim to purity. Making it absorbing deletes the question instead of answering it, and says the true thing: a horse of unrecorded ancestry contributes unrecorded ancestry.
- A cross's stat band stays the per-axis average. The alternative
— an F1 leaning toward the stronger parent — was rejected because
the Mendelian draw is already doing that work. The band only sets the
range; whether a cross foal carries a variant copy at all is inherited
allele by allele, and which point inside the band it lands on comes from its own
epigenetic seeds. A foal that inherits no
Swiftcopy sits on the baseline whatever its label says. Weighting the band as well would be putting a thumb on a scale that is already loaded. - A player may splice a horse into a lethal genotype — but only on
purpose. The Known Gene Splice carrot names the gene, and what
the player does with it is their business. The Unknown Gene Splice
carrot has a blacklist: it may never roll a locus that kills or
costs hearts. The list is derived, not typed — every
combination the carrot could roll is resolved through
HorseTraitsand the locus is dropped if any of them is lethal, impairing, or short of baseline health — so a health gene added later is excluded by having done its job. Hearts are the line: a pony allele or aSluggishcopy makes a slower horse, not a damaged one, and a surprise you did not want is what the carrot is for. SeeSpliceSafety. - The horse dimension is reachable in survival. That is what the no-break / no-harm rules in it are for — they are what stop it becoming an infinite meat and brick dimension, and they were written with survival access in mind. Hay-bale portals already work in any build; the interdimensional ticket is a convenience on top, not the gate.
- Cutie mark is a channel with a modifier hook, like the LUT.
Other genes reach it through
CutieMarkContributionrather than growing their own copy of it. It can afford the hook precisely because the emblem is the last thing drawn — nothing composes over it, nothing reads it back, and it is not in the baked coat at all, so a modifier cannot corrupt an accumulator or move a texture key. - Eye colour is a channel with ranked claims, and the iris is the dark texels — the sclera stays white. Blue out-ranks any pigment colour, because a depigmented iris has nothing left to recolour. The blue eye belongs to the white loci and is not a gene of its own.
Coat pipeline
- Phase 3 is a signed add/subtract on unclamped
intchannels, capped only at conversion, applied over the natural result — zebra is a large negative contribution, not a special case. - Context-aware phase-3 genes are intended, not a compromise: a magical gene reads the accumulated result and adds relative to it. Order matters, and that is fine — every gene has a fixed priority number, so the outcome is fully deterministic. Magical genes are allowed to be physically unrealistic.
- A magical gene can paint a dominant-white horse (white is natural, magical runs after) and does see both the natural coat and the accumulated magical deltas.
- One gene cannot act in both phases.
100is magical, and all three-digit priorities are magical; the0–99band is convention with a log warning, not enforcement.- Group traits may affect the coat, so they get a phase and a priority like a gene.
Genetics model
- Inheritance is Mendelian with two exceptions: X-linked and Y-linked genes. Crossing over, maternal inheritance, imprinting, mosaicism and polygenic traits are all out.
- Polygenic inheritance is cut. PDK4, CKM and the jumping QTLs are polygenic in reality; each became one atomic gene with two alleles and an additive weight. Atomic genes are legible — a player can breed for one — where a hidden sum of anonymous markers reads as a dice roll. Group traits (§7) stay, because they are Mendelian epistasis rather than polygenic weighting.
- Sex is itself a gene at priority 1. A stallion
carries a real
Xand aY(two occupied slots, no structural change toAllelePair/Epigenome), so both X-linked and Y-linked genes are expressible. A hemizygous allele displays with anX-/Y-prefix; the code keeps the bare token. - A physical locus with many patterns is one gene (KIT, MITF,
MATP) with a large allele set. There is no dominance property at
all — the gene declares an
Expressionper distinct outcome and maps every allele combination to one of them. Grouping alleles onto one locus is a modelling choice, not linkage (linkage: settled no). - Determinism, masking and “changes nothing” are per
combination, not per gene and not per allele. An
Allelecarries no visible / deterministic hint at all — only a token, a label and a slot order. - Every wild type is one look. A gene may declare several, but the gallery collapses them into one pen, because “changes nothing” is one appearance.
- The epigenome lives on
HorseRecord, beside the genotype, not on a separate entity attachment. Both are heritable facts assigned once at birth. - Founder frequency is per genotype, declared by the gene author, normalised proportionally if the percentages do not sum to 100. This replaced the old “1 in N” per-allele frequency. The Unknown Gene Splice carrot has its own per-gene distribution of the same shape, kept separate so the two can differ; omitted → uniform over the gene’s pairs.
- The genotype is a list keyed by gene, not a positional string. Adding or removing a gene is cheap: a missing gene reads as wild type, an unknown one is dropped, and either just triggers a coat regeneration. No segment-padding, no back-compat handling — the mod is pre-release and keeps no saves.
- A foal inherits each allele copy’s epigenetics drifted, not verbatim. This reopened and replaced the original “verbatim, no re-roll” answer, and it did it without the per-gene opt-in flag that was proposed for the job: every scalar takes a tiny two-sided exponential nudge at every breeding, seeds and categories get a small chance of an outright re-roll, and the bound is the hard safety clamp rather than the design range a founder is rolled in — so a line bred long enough is allowed somewhere no wild horse has been. See breeding § drift.
- Allele tokens are alphanumeric, 1–128 chars, wild type
n.
Health and aging
- Aging is out entirely. No coats that change over time, no death of old age. This is a firm no, not a deferral. Two consequences: grey’s fixed progression is the design, not a gap, and flea-bitten grey is cut with it. Grey’s melanoma risk is reported and never simulated — an informational condition off the copy number, with no lesion drawn and no stat docked; see the gene page.
- Health is fewer hearts plus lethal foals. All the health genetics get built; the deaths sit behind a config toggle that is on by default. Three positions: full genetics + deaths (default), genetics + reduced hearts but no deaths, off.
Care, bond and the gameplay layer
- Pen pairs share a genotype only.
- Hand-feeding bypasses the healing gate.
- Every time-gated action is once per 24,000-tick Minecraft day unless there is a specific reason otherwise — bond’s daily cap, milking, shearing, the seed-jar donor cooldown.
- Milking needs a tamed, full-health mare.
- The herd comfort buff is passive health regen. Stamina is not becoming a real resource.
- The Horse Master villager exists. The earlier “removed as unthematic” line was from an older design and is dead.
- The gene database is a plain progressive record, not a fog of war. Nothing is ever hidden in the info panel or the code; discovery gates only the gene-carrot recipe display.
- A fed carrot behaves like a vanilla golden carrot for timing: a temporary breeding-mode-style window, consumed on breed, gone when it lapses — not permanent stored state. Different carrot types show differently-coloured heart particles as feedback.
- The Unknown Epigenetic Splice carrot re-rolls every allele copy the fed parent passes on — expressing and non-expressing.
- The seed jar stores enough of the sire’s record (not just his UUID) to place him in the family tree, and stores the genotype, not a frozen gamete — the draw is at impregnation, exactly like natural breeding.
- A sheared horse gets a render-layer overlay — it only has to read as vaguely different, not look good; no coat-variant texture.
- A gene that declares no rarity defaults to the gold-ingot tier, and a gene can opt out of having a Known Gene Splice carrot at all (sex, the recessive lethals, extension, agouti).
- Cross-locus reads and epigenetic branching are discouraged for
third-party genes. They make a gene hard to reason about, hard to show in
a punnett square and hard to breed toward. The
leopard complex earns the exception because it is
a real, famous pattern that genuinely works that way; the capability exists for
the built-ins and is not a recommended pattern. A gene file can reach it
now (2026-09-08) through
needs, which accretion uses to split “is there a pale field, and what colour” from “which half of the horse”. That does not change the advice: it makes the discouraged thing possible from data, not recommended.
The 23 science tabs that predate the pass
The original backlog is gone. All 25 natural gene pages that had no
science tab have one, and
wiki/tools/check-gene-tabs.mjs now runs with an empty exempt
list, so a new natural gene page without a science tab fails immediately. What follows
is the work that closing it created.
The 25 new tabs run 1,200–2,300 words each and share a shape:
the locus and the variant with coordinates, the mechanism, the real inheritance table,
breed-by-breed frequency where anyone has measured it, an established / inferred /
unsupported split, and an explicit statement of where the mod departs from the
literature. Measured against that, 17 of the 23 tabs that already existed read as
stubs — most are a single <h2> and one or two
paragraphs, and several are shorter than the lede above them.
Word counts below are from
python over the data-tab="science" panel of each page —
recount rather than trust them, they will drift the moment anything here is written.
Magical genes are excluded throughout: they have no real-world counterpart and having
no science to write is correct rather than a gap.
Tier 1 — cited by the new tabs and unable to answer
These are the worst, and not because of their length. Each is named as background by one or more of the new pages, so a reader follows a link expecting the story and finds two sentences.
- EDNRB — 62 words. Frame overo and lethal white foal syndrome. Four new tabs cite it as the cautionary example of a pigment gene with a real health consequence (tobiano, MATP, champagne, mushroom), and it cannot currently support the citation. Highest priority on this page.
- Silver — 129 words. Cited three times for multiple congenital ocular anomalies, and has no MCOA section at all. PMEL, the homozygote severity, and why a champagne-plus-silver horse gets its eye risk from the silver.
- MITF — 90 words. SW1 — the common, centuries-old, minimally-expressed splash allele. Its PAX3 twin now has 2,024 words including the full SW1–SW10 table, and the asymmetry between the two alleles is the whole point of having two loci. This one is close to writable from the PAX3 page alone.
- KIT — 149 words. Where tobiano’s 43 Mb inversion sits, and the home of W20, SB1 and the dominant whites that four pages name in passing.
- Grey — 164 words. STX17, the duplication, and the age-related melanoma risk that the MATP comparison table lists and grey’s own page does not discuss.
- Leopard complex — 224 words. TRPM1 and congenital stationary night blindness in homozygotes — cited twice, absent here. Also the obvious place for Appaloosa, Knabstrupper and POA (see below).
- Roan — 129 words. Rabicano’s new tab contrasts the two at length and roan’s own page does not reciprocate. The honest content is that roan’s causal variant is also unsettled, which is worth a reader knowing.
Tier 2 — foundational loci carrying a paragraph
- Extension / MC1R — 41 words, the shortest science tab in the mod, for the locus every other colour gene is described relative to.
- Agouti — 200 words. Good as far as it goes — it is an argument about why there are four bay outcomes — but it has no ASIP variant, no 11 bp deletion, no breed distribution.
- LCORL — 85 words, for the major height locus. HMGA2 and dun both now cite the ECA3 region against it.
- ACAN — 144 words, and megaesophagus — 152, and CVM — 259. The three health loci that were not in the intake batch, so they are now the only disorder pages without the full treatment their neighbours have.
- RYR2 — 110 words, and PDK4 — 156. PDK4 especially: CKM’s new tab ends by saying the same marker-in-a-cluster caution applies to PDK4 with less force, and PDK4’s own page does not say what that caution is.
- Brindle — 127 words, and manchado — 162. Both are genuinely under-researched patterns, which is itself the content — rabicano’s tab is the template for writing a pattern whose gene nobody has found.
Tier 3 — short, and arguably finished
Shade (382) and natural zebra (394) are short because they are complete arguments rather than surveys, and sex (203) is a locus with no variant story to tell. Flaxen (279), sooty (321) and pangare (175) are the three that already named breeds before this pass. Leave all six alone unless something specific is wrong — padding a finished argument to match a word count is the opposite of the point.
The breed problem, which is separate
20 of the 48 natural science tabs name no breed at all. For some that is correct — extension, agouti, shade and sex are universal loci. For the rest it is a real hole, because “which breed do I look in” is one of the two questions a reader brings to a genetics page, and the mod has a breed system that could act on the answer:
| page | the breed story it is missing |
|---|---|
| leopard complex | Appaloosa, Knabstrupper, Pony of the Americas, Noriker |
| EDNRB | Paint and frame lines — the concentration is the health story |
| ACAN | Miniature Horse dwarfism |
| megaesophagus | Friesian |
| silver | Rocky Mountain Horse, Icelandic, Shetland — and the MCOA link is breed-concentrated |
| manchado | the Argentine breeds it was described in |
| LCORL | the draught-versus-pony spread that makes it a size locus at all |
| MITF | SW1’s cross-breed distribution — the entire contrast with SW2 |
| KIT, roan, grey, brindle, CVM, RYR2, PDK4, natural zebra | varies; check each against a source before asserting one |
How to do one
The 25 written this pass came from owner-supplied source files, not from the model’s memory, and that is the only reason they can state coordinates and frequencies. Do not write one of these from memory. A veterinary claim invented to fill a section is worse than the stub it replaced, and worse than the honest absence the stub replaced before that.
When one is written: re-run node wiki/tools/sync-page-views.mjs,
node wiki/tools/build-search-index.mjs and
node wiki/tools/check-links.mjs. The shape to copy is any of the 25 —
mushroom is the cleanest short one and
dun the fullest. Note that every one of them ends
by saying where the mod and the literature disagree; that section is the reason
the pass was worth doing, and a rewrite that omits it has missed the point. Three
of those disagreements turned out to be substantive enough to become
gap 134.