Chapter Two · 08 March 2026

Building a Mind

It took two days to build. It has been the thing I think about ever since. Fifty five commits on one file, and six months later it is still learning to walk.

Left is the creature you meet in the snow. Right adds the source, the commits, the two labs and the debugger.
What it is

A mob that does not chase you. Twice a second it scores seven possible things to do and picks whichever wins, and it will flatly refuse two or three of them.

What you do with it

Drag the marker in the simulator below and watch it change its mind in real time. Then read the seven ways it has embarrassed itself.

Why it is odd

It commits to one of you and holds on. The one it holds is whoever is wearing the least armour, so putting a coat on hands its attention to your friend.

7actions it can pick
19things it checks first
decisions per second
16blocks, the closest it will watch from
3seconds of holding still to pick one of you
12pixels of light
6,499lines on day one
41files in the first commit
55commits on one file
10reasons it records for breaking a block
60named scenarios in the lab
37GameTests in the gate
What it actually is

Most Minecraft mobs run on a short list of rules. See the player. Walk at the player. Hit the player.

The Architect doesn't do that. Roughly twice a second it stops and asks itself one question: given everything I know right now, what is the single best thing I could be doing?

It has seven possible answers. It gives every one of them a score, then does whichever scored highest. That is the entire idea. Everything else on this page is detail.

A zombie

  1. Can I see a player?
  2. Walk at the player.
  3. If close enough, hit them.
Three rules, checked in order, every tick. It has no idea you exist the moment you break line of sight.

The Architect

OBSERVE
APPROACH
ATTACK
RETREAT
FORTIFY
TRAP
PEEK
Seven options, scored from nineteen things it knows about the current moment, twice a second. Two of them scored zero here, and it refused them outright.
OBSERVEHang back and study you. Remembers where you were and how you got in.
APPROACHClose the distance, re-routing as the world changes around it.
ATTACKCommit to a hit, and stay committed for a beat.
RETREATBreak contact and heal, if the heal is off cooldown.
FORTIFYPut ice between you and it.
TRAPSeal the way you came in.
PEEKLean around a corner it is already holding.

Those seven names are the real ones, straight out of the enum.

Why it feels alive

The scores themselves are boring arithmetic. The part that gives it a personality is the refusals.

It will not observe you from closer than sixteen blocks. It will not observe you at all once it drops below seventy percent health. It will not retreat while the heal is still cooling down. It will not set a trap if it has run out of ice.

Those flat refusals are what make it read as a thing with its own rules rather than a thing chasing you. You can feel the moments it decides you are not worth watching any more.

What it looks like

Sixty four pixels square. Nine colours, seven of which are barely distinguishable shades of black. Twelve pixels glow, and they are the only reason you ever see it coming.

This is the real texture out of the repo, rebuilt into the model the game actually draws. Drag it.

architect.pngdrag to turn it
head, front face
8 × 8
6 boxesHead, body, two arms, two legs. Thirty six flat faces, one sheet of pixels wrapped around all of them.
9 coloursThe body is almost pure black. Against snow it reads as a hole rather than a shape.
12 pixelsEverything it emits. There is a second texture, architect_blink.png, identicalversion of it, identical except four of those twelve go dark. That is the whole blink.

On the workbench

Merged 16 Sep
both branches landed on 16 September 2026
feat/architect-visual-debugger · +11,239 / −498 across 263 files
fix/2.0.1-hearth-target-commitment · +3,705 / −180 across 54 files

Everything below this line was sitting on an unmerged branch when I wrote it, and might not have survived in that shape. On 16 September both branches went into the mod, so all of it is in the build you can download now. I have left the section standing rather than quietly folding it into the rest of the page, because the honest part of a journal is the stretch between starting something and trusting it, and that stretch is only visible while you are still inside it.

the bench did not stay empty
feat/maeve-belief-store · +1,375 / −3 across 19 files · opened 16 September 2026

One commit old, and it does something the Architect has never been able to do. It writes down what it watched you do, and it keeps that after you log out. A belief, a policy deciding which beliefs are worth keeping, an observation collector, and a class named MaeveDirector that I am not going to explain yet.

Watch it decide

This is the real scoring code, running in your browser. Drag the blue marker anywhere in the box. Twice a second it rebuilds its picture of the situation, scores all seven actions, and does whichever one wins. Nothing here is on rails.

Drag anywhere to move the blue marker
How it learned to think

None of the above existed in one piece. This is the real commit history of the thing you were just playing with.

08 March 2026 · 14ab9eb9
It arrived already thinking

Forty one files and 6,499 lines in a single commit. Utility scoring, D* Lite pathfinding, wall breaching, scaffolding over obstacles, retreat and heal. It was never a walk-at-you mob, not even on day one. The commit title ends with walk oscillation fix, which means it was already pacing back and forth before anyone but me had ever seen it.

09 March 2026 · 202b1f9d
"architect finally done"

That is the entire commit message, verbatim. The next day brought architect polish pass 1 and architect polish pass 2. Fifty more commits followed over the next six months. I have never been more wrong in a commit message and I am leaving it in.

06 April 2026 · fifteen commits in one day
The mind moves out of the body

Everything above had been living inside one enormous entity class. In a single day it came apart: the decision engine, the brain state, the combat state, the approach state, and four separate controllers. One of those commit messages names the reason out loud, to avoid further god-class growth. The engine the simulation on this page runs on is the file that came out of that day.

07 April 2026 · thirteen commits in one day
Teaching it to walk

Deciding turned out to be the easy half. Every fix that day is named after the exact way it had embarrassed itself: slab spin, scaffold-up retry loops, water lockups, chase stalls during corridor replans. A mind that cannot cross a half block is not much of a mind.

08 April 2026 · three commits, same file
How long should it stare at you?

Six seconds, for testing. Then back to thirty to sixty seconds. Then a flat two hundred ticks. Three commits, two changed lines each, all in one afternoon. Nothing about that number was ever going to come out of reasoning. I had to stand in the snow and be watched by it until it felt right.

23 August 2026 · three commits
Still learning, five months later

Traverse frozen atmosphere reliably. Breach enclosed targets. Bound healing retreats. The last one is the telling one: it had been allowed to back off and heal without limit, which is a perfectly rational thing to do and a miserable thing to fight.

What it got wrong

The good parts of its behaviour are mostly accidents of scoring. The bad parts were all deliberate, and all mine.

7 of them
It paced

It would take a step toward you, notice it had just reversed direction, and stop dead. Then do it again. The fix was to let it commit to a walk it had already started, or look past the oscillation zone entirely, rather than re-deciding every single step.

Fixed in the commit that created it · 14ab9eb9 Fixed the same day it was born.
It span on half blocks

Slabs and snow layers read as walkable and not walkable at the same time depending on who was asking. So it would rotate in place on top of one, or decide to scaffold upward and retry forever.

fix(approach): stop slab spin and scaffold-up retry loops · cbb76e1f
It drowned

Submerged, its committed walk kept churning against a plan made for dry land and it never surfaced. It needed a separate fallback chase mode and a bounded shove upward before it could get out of its own way.

fix(approach): escape water lockups without entity bloat · 47694759
It healed forever

Retreating to heal had no ceiling on it. A hurt Architect could simply keep backing away and topping itself up, which is the correct decision and an awful fight. This one survived five months in the wild before I bounded it.

fix(architect): bound healing retreats · 14c9987b
It could not get in

Seal yourself in a room with no opening and it had no answer. It understood breaching a wall it could see a way past, not committing to one it could not.

fix(architect): breach enclosed targets · 36af50fe
It mined the floor out from under itself

The unstick planner offered the block underneath its destination as something it could break. That column is what holds the destination up, so on any walkable path it is always solid, and it won every time the real obstruction turned out to be air. A stall with a perfectly clear path ahead would send the Architect digging into the ground at its own feet, dropping in, and going one block deeper on the next try.

fix(entity): stop Architect mining its own walk surface · 95407a2d
And one I only found building this page

Porting the scoring code into the simulation above meant reading every number again. TRAP, the one that seals your door behind you, tops out at 0.455. APPROACH against a healthy player at range floors out around 0.48. Which means the trap behaviour can essentially never win unless you are already standing inside its base. Six months of play and I had never noticed, because when it does happen it looks completely deliberate.

Open. Not fixed yet.
It stops to think

When a chase stalls, it goes still. Not paused: the planner underneath is running at full speed the entire time, searching for a route it cannot find yet. The stillness is the only part of that you can see, so I gave it something to do with the stillness.

Six ticksA third of a second of not moving and the head tilts. Thirty two ticksA second and a half and it might bring a hand up to its chin. Might, because the roll is one in three, and if it plays it will not play again for twelve seconds. A tell you see every time is an idle animation. A tell you see occasionally is a habit.

6 · tilt 32 · chin roll
still for 0 ticks WALKING no roll yet chin ready
Giving it an elbow

Vanilla Minecraft arms are a single rigid cube. There is no elbow in there to bend. Getting a hand anywhere near a face meant replacing the left arm with a two segment rig, an upper arm and a forearm on their own model layer, and reusing the right arm's existing texture rather than shipping new pixels for a gesture. The pose is suppressed entirely if it is holding something in the off hand or wearing a chestplate, because the armour layer copies shoulder rotation and you end up with a sleeve floating in the air next to the model.

The roll is tied to the individual Architect rather than to the clock, so two of them standing in the same room never bring a hand up on the same beat. And none of it touches the thinking. The pose is decoration laid over a mind that has not paused for a single instant.

Why it never falls into step

The roll is not random in the usual sense. It hashes the entity's own id against the current tick, so it is deterministic per Architect and never touches the shared random stream the rest of the AI is drawing from. And the whole thing is cosmetic: the pose state rides over the network on one byte, and nothing in it is allowed to delay a decision by a single tick.

feat(client): add Architect hand-to-chin pose during pursuit pauses · 2c3c365c
Why it breaks a block

It has always been able to mine. What it never had was a record of why. A trace could tell me it broke the ceiling above its own head, which was useless, because there Watch it break the ceiling above its own head and you learn nothing, because there are four completely different reasons it might do that and they fail in four different ways.

Now every block it considers carries an intent from the moment it is proposed, and keeps that intent until the attempt ends.Every block it thinks about breaking now comes with a reason attached from the moment it is proposed, and it keeps that reason until the attempt is over. There are ten.

The ceiling over its headThe block directly above it. The ordinary one.
Room to climbThe block above its head when the next step is upward. A jump needs two clear cells, not one.
Room to dropHeadroom out in front, on the way down off a ledge.
On the routeA block standing on a path it has already worked out.
Simply in the waySomething directly in front of it, with no longer story than that.
The last wallIt has reached you, and there is still a wall in between.
Its own scaffoldingA block it placed itself to climb, being taken back down.
Straight downThrough the floor.
Only if nothing else worksAllowed, but held back until everything else on the list has failed.
No reason givenIf this ever turns up, something skipped the planner. It is there to catch me out.
HEAD_CLEARANCEThe block directly above its own head. The ordinary one.
STEP_UP_CEILINGThe block above its head when the next step is a climb. A jump needs two clear cells, not one.
STEP_DOWN_CLEARANCEHeadroom out in front, on the way down a drop.
CORRIDOR_NODEA block standing on a route it has already planned through.
IMMEDIATE_CANDIDATESomething directly in the way, with no longer story than that.
CONTACT_BREACHIt has reached you, and there is still a wall in between.
SCAFFOLDA block it placed itself to climb, being taken back down.
DIG_DOWNStraight through the floor.
LAST_RESORTAllowed, but held back until nothing else on the list works.
UNSPECIFIEDNo intent recorded. If this ever appears in a trace, something skipped the planner.

The useful part is not the list, it is that the selection now writes down what happened to every single candidate, including the ones it never got to.

CandidateIntentOutcome
from.above()HEAD_CLEARANCENOT_BREAKABLE
frontIMMEDIATE_CANDIDATEBLACKLISTED
front.above()HEAD_CLEARANCELAST_RESORT_DEFERRED
stepIMMEDIATE_CANDIDATESELECTED
step.above()HEAD_CLEARANCENOT_EVALUATED_AFTER_SELECTION
Blacklisted means it already tried that block and got nowhere. Deferred means it is allowed to break it, but only once nothing better turns up, and if the list runs out that deferred one comes back as the fallback. The rows after the winner were never looked at, and the trace says so rather than leaving them out.
How do you test a mind

You cannot really unit test a decision. You can assert that a function returns 0.71, but the thing I actually want to know is whether it gets stuck, and there is no assertion for stuck. So the only honest option was to build somewhere awkward and put it there over and over.

There is a lab now. Its own dimension, a 21 by 16 by 21 arena that rebuilds itself around a saved origin, and a scenario loader. Setup freezes the entire server. Recording starts before the Architect is allowed to move or mine a single block, so nothing happens off camera.

60named scenarios
500stress cases
30must pass the gate
66structure files
8seeds, four rotations each
78setup scripts
sealed_pocketvanishing_wallgate_reopenstarget_juke closing_passagefooting_honeyslab_checkerboardseeded_maze unreachable_targetroute_opens_miningnarrow_bridgelava_detour scaffold_interruptionmulti_near_enclosedcorridor_soakpit_slab_ramp

Same arena rotated four ways, same scenario reseeded eight ways, because a bug that only exists when the wall runs north to south is still a bug and I would never have found it facing east.

The rule the whole suite runs on is the part I am most pleased with, and it is not technical. A failed case stays a failure with its trace attached. It does not get skipped, quietly relabelled, or handed a larger tick budget until it passes. The last run written down in the notes covers 396 of these cases: 378 passed, 18 failed, nothing missing. Those eighteen are still sitting there failing, which is the entire reason for having built the thing.

test(architect): add Architect lab harness and headless verification gate · 47b09de4
Out of the box

The arena in the last tab is a room. A fair room, rotated four ways and reseeded eight, but still a room I specified: flat floor, square walls, ceiling exactly where I said to put it. After a few hundred passes I stopped believing it. Nothing in Minecraft is that shape, and a mind that only works in a room I built for it is a mind that works in one room.

A room I built for it

So the second lab builds nothing. It takes a 128 by 128 region in a dimension of its own, lets vanilla generate it the way it would generate any other patch of Overworld, and then lets Frozen Dawn do what Frozen Dawn does to a landscape: deep snow over the trails, Acheronite pushing up through the rock, frozen atmosphere settling into the low ground. A cave. A fenced passage. A shelter with doors. A ravine with one crossing over it. Preparation saves the sixty four native chunks before it touches them, puts them back afterwards, and fingerprints the result, so a run that fails can never be blamed on terrain that quietly drifted between attempts.

Chasing something with legs

What it chases out there is not a marker or a moving point. It is a villager with a hundred hit points, walking on its own legs, with normal collision, normal gravity, normal jumping, normal pathfinding and normal damage. The harness only chooses where that villager should go. It never teleports it, never replaces it, never lifts it out of a hole it walked into. If the villager cannot get somewhere, that is the finding, not a bug in the test.

ScenarioRuns forWhat it has to show
surface3 minunbroken pursuit around the frozen circuit
caves5 minin through the cave branch, and back out
shelter_open5 minnothing broken. breaking anything fails it
shelter_breach5 mindoors shut behind the target. break in, land a hit
changing8 mina gate closes, snow rises, the crossing is taken away
construction15 minclimb six, bridge four, breach a wall, duck a ceiling
weather15 minreal phase five snowfall, checked every minute
endurance30 minall of the above at once, with no reset in the middle
roam10 minthe villager runs its own brain. not graded
player30 minit comes after me instead. not graded
The changing scenarios do not fire on a timer. A gate only closes once the villager is through it and the Architect is behind it, a second gate is always left open so there is something to find, and the bridge is only taken away after the villager has crossed. A trap that springs on an empty corridor tests nothing.

Every minute of a graded run has to contain at least twelve blocks of horizontal travel by the target and one melee hit confirmed by health actually coming off it. A good final minute does not rescue a bad third one.

That rule is most of the reason the wilderness lab exists. In a room, a stalled Architect still looks busy. Out here, if it spends a minute mining into a hillside while the villager walks away, that minute fails and stays failed, and the run carries it to the end instead of averaging it away. Seven outcomes are possible and only one of them is a pass.

PASSEDFAILEDTARGET_FAILEDSCENARIO_INCOMPLETE OBSERVEDABORTEDHARNESS_ERROR

The third one is there because of this, which is my favourite thing that has happened on this branch.

14 September 2026 · seed 1337
The villager died of scenery

The surface run stalled at waypoint 43, near (22.5, 65, 40.3). Four decorative snow layers I had put there to make the trail look better had quietly made the step up taller than a villager can physically jump, while vanilla pathfinding went on cheerfully reporting the route as reachable. It bounced in place until it died, at tick 2419. The harness recorded TARGET_FAILED, which is the run saying the Architect never got a fair test, rather than the Architect being told it won.

The fix, in two halves
Move the snow, then stop trusting the clock

The decoration moved to the shoulder of the trail, off the walking line. The more useful half: the stall timer stopped measuring time and started measuring ground. It now wants a quarter of a block of net horizontal displacement, so a target bouncing enthusiastically in one spot no longer reads as progress.

And then the recovery
Sixty ticks, twenty four guesses, three strikes

After sixty ticks without improvement the harness goes looking for a way round: up to twenty four candidate points within six blocks, each needing two separate paths to check out, target to side point and side point back to the original waypoint, both tested with an unspawned probe villager so the real one is never spent on a guess. A hundred and twenty ticks per attempt, and three failed attempts end the run honestly rather than nursing it to the finish.

The notes for this branch end with a line I am leaving exactly where it is, because it is most of what separates a journal from a sales page: this addition has received compilation and resource checks only. No simulations or visual runs were executed during implementation.

feat(debug): add native-terrain Architect wilderness lab · fb56dda3
Watching it think

Everything up to here is me reading numbers after the fact. A trace tells you what it chose. It does not tell you what it was looking at while it chose, and by the time the file exists the moment is long gone and I am guessing at it.

So the newest thing on the bench draws the server's own working state into the world in front of you, while it is happening. Not a replay, and not an artist's impression of intent: the corridor it has actually committed to, the candidates it actually scored, the boxes it actually believes are solid.

Committed routeThe corridor it has settled on. The small box is the one waypoint it is walking to right now.
Next D* stepThe single step the planner wants next, which is not always where the body is going.
Movement and scaffoldWhere the move controller is actually pushing, and any block it placed itself to climb.
MiningA block it means to break, drawn before the first swing lands.
SupportWhat it is standing on, or expects to be standing on next.
ExcludedA candidate it ruled out. Usually the interesting ones.
EvaluatedA candidate it scored and did not take.

Two trails run underneath all of that, one for each actor, which is the cheapest possible way to tell a stall from a circle. A thing stuck against a wall and a thing walking a perfect loop both look like failure in a log. They look nothing alike drawn on the ground.

It is an operator tool rather than a feature. It wants permission level two, it stops drawing past a hundred and twenty eight blocks, and it only ever holds a short window of the recent past.

120frames kept, about 30 seconds
512events
64trail points
64nodes per route
96collision shapes
128blocks, then it stops drawing

Thirty seconds is not an arbitrary budget, it is the useful one. Nearly every Architect bug I have chased announces itself and resolves inside half a minute, and for the rest of the time I would rather the debugger cost the server nothing at all.

freeze / step 20 / resumeStop the world, advance it exactly twenty ticks, let it go again. Decisions happen far too fast to watch at full speed.
history 200 / liveScrub two hundred ticks back through the kept frames, then return to now.
mark contactDrop a labelled pin into the event log the moment something interesting happens.
inspect / dumpPrint what it currently believes, or write the whole bundle out to disk.

The bundle is the real point of it: a snapshot, a frame by frame history, an event table, the camera view it was all drawn from, and a summary carrying a SHA-256 for every attachment, so a recording can be checked against itself later instead of trusted.

What it refuses to pretend

Every honest thing in that overlay was a decision to draw less. Observing never forces a target, never resets the AI, never reseeds anything, because the moment a debugger changes the run it is no longer debugging it. Historical mode says outright that it is painting saved observations over the terrain as it is now, not replaying the terrain as it was.

Where it has no answer it says so rather than filling the gap. When the melee commitment check was not sampled on a frame it draws COMMIT_NOT_SAMPLED, rather than an empty space that would look like a decision. Options it never reached are labelled NOT_EVALUATED_AFTER_SELECTION instead of quietly sharing a colour with the ones it rejected. The range sphere uses the same 2.8 blocks as the real hit check, and the documentation states in plain words that being inside it does not prove the hit landed.

Nothing to subscribe to

Drawing any of it was harder than it had any right to be. The shipping runtime's DebugPackets.sendPathFindingPacket is an empty method, and Path.writeToStream expects optional debug data that an ordinary navigation path does not carry, so there is nothing to subscribe to. The overlay rebuilds the route from the server's own committed corridor and hands it to Minecraft's real PathfindingRenderer through a render hook, which means the lines on your screen are drawn by the same code Mojang draws its own debug paths with, without a line of Mojang source being copied into this mod.

The one part I have not seen

On 13 September the build passed, the unit tests passed, and the required gate passed all thirty seven GameTests, including the thirty two named Architect cases. The live overlay is still unverified, because the client would not start: Failed to locate a primary monitor. The one part of this I built specifically so that I could see it with my own eyes is the one part I have not seen yet.

feat(debug): add Architect visual debugger and snapshot exports · 73e1fc60
How it picks one of you

Stand near a Hearth with a friend and the Architect assessing you never finishes. Not slowly. Never. Both of the controllers that run that scene re-picked the nearest player every single tick and reset the counter whenever the pick changed, and the counter needs sixty consecutive ticks to complete. Three seconds.It works out who is nearest twenty times a second, fresh each time, and its three second count starts over the instant that answer changes. It could not get three uninterrupted seconds with two people in the room, because the two of you only had to keep trading which one was marginally closer.

Three symptoms, one counter

You can watch it happen, which is the part that gave it away. The head snaps back and forth between you. It starts walking toward whoever is momentarily nearer, then turns around. Three symptoms, one cause, and the cause is six lines of code that reset a counterone count being started over when it should have been left alone.

The fix is borrowed from Phasmophobia. The Banshee chooses one person at the start of a contract and keeps them for the whole thing. It lets go on events, when you die or when you leave, and never because somebody else wandered closer.

That is the whole property worth stealing, and it is worth being precise about why it works. Distance changes every tick. Events do not. Releasing on events rather than on proximity is what makes it thrash-proof, instead of merely slower to thrash.

So it commits. One target, held, and the one it holds is the weakest of you. The score is armour value and nothing else, which sounds crude until you look at the ladder it produces, where every piece of gear in the mod already sits in the right place and anything added later slots in without a line of code.

ArmourPoints
Unarmoured0picked first, every time
Leather7
Insulated, tier 110
Chain12
Reinforced, tier 214
Iron15
EVA, tier 316
Diamond, Netherite20
Acheronite24picked last, and it has to be desperate
Ties break on distance, then on player id, so two people dressed identically never coin flip between each other. Taking a commitment away from someone needs a gap of eight points, about two tiers: leather steals you from iron, and leather does not steal you from reinforced. Putting a coat on in front of it genuinely moves its attention to your friend.

Underneath that is a five step resolution that runs once a tick, in this order, and the order is the design.

1 · FORGETThe committed target is no longer anywhere on the server. Died, disconnected, went spectator. Clear it.
2 · RESUMEA bookmarked target has walked back into range. It takes the commitment straight back, without being scored against anyone.
3 · HOLDThe committed target is still in range. Keep it, unless somebody here is eight armour points weaker.
4 · SUSPENDThe committed target is still online but out of range. Bookmark it and let go for now.
5 · PICKNobody is committed. Take the most vulnerable person in range. If the range is empty, take nobody and patrol.
Leaving and being gone

Forgetting somebody and bookmarking them look like the same thing and are not, and keeping them apart is the whole reason the stall closes. Someone who logs out is forgotten. Someone who merely walked off is kept. Get that backwards and the Architect holds on to a person who is never coming back, while you stand directly in front of it being ignored.

Step one and step four are the same situation to a naive implementation and they are the entire reason the stall closes. A player id that is still on the server but out of range gets bookmarked. One that is gone gets forgotten. Without that split, a committed target who logs out locks the Architect onto an id that is never coming back, while somebody stands directly in front of it being ignored.

Being the bookmark

While you are the bookmark, nothing about the Architect looks any different. It assesses whoever else is there, properly, to completion, and patrols when nobody is. The bookmark has no behaviour of its own and there is no way to see it from outside. It matters for exactly one instant: the moment you walk back into range, when you take the commitment back and whoever it had picked up in your absence is dropped mid scan. Resuming restarts the three seconds rather than banking what it already had, and only one bookmark is ever kept, because the alternative is an unbounded queue of people who might come back.

And if you hit it

An Architect being hit should fight, not scan. Both controllers now bail out the moment something has hurt them and hand the tick to the combat brain. That alone was not enough, and the reason is worth writing down: the target lookup underneath filtered on relationship, so a friendly player punching an Architect produced no valid target and therefore no retaliation at all. It did not. The lookup underneath only ever handed back people it had already decided were a threat, and somebody who had not been marked as one did not count, even while they were hitting it. You could stand there hitting it and it would keep politely assessing you. Now whoever actually hit it is returned first, ahead of every other rule, and it does not dogpile. Each Architect answers only the thing that hit it.

Two numbers I left alone

Two of these controllers watch at different distances, twenty eight blocks and thirty two, through two different constants that happen to share a name. I left them different. Unifying them is a change to how far two separate creatures can see, which is not a thing to do quietly while fixing a counter.

A test that was already failing

The last honest part: I did not write the test to match the fix. HearthTargetArbitrationGameTest already existed and already failed, on purpose, and it is the reason the build gate sat red for over a week. One of the commits on this branch is fail the game test gate when tests never run, because the gate had been reporting a pass when zero tests ran, which is the most dangerous kind of green there is.

feat(entity): add Architect target commitment policy and state machine · 2938f9e6 · fourteen commits, 10 September
MAEVE