The quiet milestone

Apr 23, 2026·v0.73.13

v0.73 shipped thirteen features and none of them change anything the player can see. It’s a cleanup milestone before the alpha push. I gave it its own release so the churn could land somewhere without blocking feature work.

The audit came first

Before any code changed I walked the codebase with a checklist across eight categories: comment hygiene, legacy code, magic values, access patterns, signal ownership, teardown symmetry, documentation drift, dead handlers. That produced a report of about 600 findings across roughly 250 files.

Most of it was dead weight. Stale version-archaeology comments from v0.044a that had been copied through three refactors. A zoom-pulse subsystem in CameraManager that wasn’t hooked up to anything. Four consumable handler files that existed because nobody had deleted them after the data migration that made them redundant. A lot of this was boring to find and boring to fix, but it was also real.

The quieter pile was more interesting. Signal connections that never got cleaned up when a handler was removed, and PERCENT modifiers applied to stats that start at zero (the write succeeds, the gameplay effect is dead, and nothing tells you). The PERCENT-on-zero-base case had four confirmed instances in gear and protocol handlers. Nothing was crashing. They just weren’t doing anything at runtime, because the math multiplies by zero. You catch those by reading code, not by observing a symptom.

Player was a bus

The thing from v0.73 I want to write down is signal ownership, because I think it’s a lesson I’ll keep learning.

Signals accumulate in any codebase that runs long enough. Early on, when the game was smaller and the architecture wasn’t settled, some signals got attached to Player because Player was the object everything touched. enemy_killed, item_collected, powerup_collected, weapon_fired, dash_executed, achievement_unlocked. Player was the hub, so the signals lived on the hub.

Which was wrong. Player is a database. It holds state like health, wallet, gear, stats, protocols, and it should emit signals about its own state changing (took damage, gear changed, leveled up), not about things that happened somewhere else. When enemy_killed fires on Player, you’re asking a stat bag to witness an event it didn’t cause, and anything listening to that signal ends up with an implicit dependency on the wrong object.

Ten signals moved. enemy_killed, item_collected, and powerup_collected live on DungeonSpawnHandler now, which is where entities spawn and despawn, so their lifecycle signals belong there. weapon_fired is on WeaponHandler, item_used on ConsumableBelt, dash_executed on PlayerCharacter, achievement_unlocked on AchievementManager. The rule I ended up with was something like: whatever spawns the thing is also what signals about it. It sounds obvious when you type it out. It took eighteen months of code growth for the violation to show up in enough places to be worth a sweep.

How the milestone ran

The work itself was built with parallel developer agents. The architect loop decomposed the audit into about fourteen discrete features and dispatched pairs of dev agents against disjoint file sets, each committing atomically to staging. When both returned, the architect shipped. One session per feature pair. Thirteen features total.

Cleanup work suits this pattern. The tasks are scoped (one file set each, bounded by what the audit said), they often don’t overlap, and they’re methodical rather than creative. Done solo in a long session this kind of thing is draining. Split across paired agents, each feature is maybe thirty minutes of work that runs without the architect babysitting.

The planning surface moved too. The markdown roadmap that lived in docs/roadmap/ from v0.044 through v0.72 is retired. Everything’s in Jira now under project NUL, with a thin CLI layer under pm/ that wraps the Jira REST API so agents and humans use the same commands. The short version of why: Jira represents workflow states natively, cross-ticket linking is first-class, and fixVersion handles release coordination instead of the markdown system imitating it with frontmatter and a regenerated index.

The markdown version was fine for a long time. It stopped scaling around v0.70, when milestones started running eight or ten features in parallel and grepping for status: shipped started feeling like the wrong tool for the job.

What it buys

Not much anyone can point at today. It shows up in the patches that come after: modifier bugs that don’t appear because the signal owner is obvious, a feature that ships as a .tres because the handler already exists, a dev agent that picks up a task and doesn’t have to step around archaeology in the file it’s editing.

Every alpha-polish pass and balance patch from here should run at something like the cadence v0.73 ran at. That’s the point of doing it.

Steamworks paperwork went in

Unrelated to v0.73 but worth noting while I’m writing: I signed up for Steamworks today and submitted the verification documents. A wishlist page should appear once Valve approves the account. First time running the game through that pipeline, so there’s probably more I’ll find out as it moves through review.

What’s next

Alpha. The four protocols are playable end-to-end, the codebase is in decent shape, and the planning surface handles parallel work. The next release should have things in it that players notice.

← All devlog posts