The Mod That Didn't Exist Yet
Upgrading our family Minecraft server to 26.3 cost us the gamepad. Not the controller — the mod. Gamepad support in Fabric Minecraft is basically one project, Controlify, and its newest supported version was 26.2.
The upgrade wasn’t optional. 26.3 shipped on the 15th, the launcher auto-updated the clients, and they could no longer join a 26.2 server. Paper only had alpha builds for 26.3, so it was an alpha server or no server — and worlds don’t downgrade. CC, my Claude Code agent, took a Longhorn snapshot and went through the one-way door on the 18th: Paper 26.3, Geyser and Floodgate pulled out, the Bedrock port dropped, since everyone in the family is on Java Edition now.
Everything came back except the gamepad.
A real gap, not a search artifact
The maintainer had a port/26.3 branch, but it was unfinished — it didn’t build against 26.3.
Before asking anyone to write code, I wanted to know whether the support was genuinely missing or whether we just hadn’t looked hard enough, because those call for very different responses. CC checked the mod index: zero Fabric controller or gamepad mods published for 26.3, against 2,790 other mods already ported. That’s a gap, not a bad search.
So I asked CC to build it. Not to reimplement anything — to take the maintainer’s unfinished branch and make it compile and run against 26.3.
What the port took
It came down to three kinds of change, each small:
- Version pins. The branch predated the Fabric API build that supports 26.3, so the floor had to move up to 0.161.0.
- API fixes. Two call sites had moved under 26.3 —
Blaze3D.openUrirelocated, and a mixin targetingMultiPlayerGameMode.dropItemneeded retargeting to the new signature. - Natives. 26.3 replaces GLFW with SDL3 for windowing, and Minecraft’s own LWJGL SDL path isn’t finished yet. Rather than lean on it, CC kept Controlify’s SDL3 native libraries bundled inside the jar. That’s a deliberate divergence from where upstream is heading, and the one change I knew we could never send back.
CC verified it the only way that actually counts — launching the real client, not just a green compile — and got a clean start with zero mixin failures. Then it rolled the jar out to all five client installs across three machines and retired the old qjoypad autostart on the boxes that had it, since two things translating gamepad input at once is worse than none.
I want to be clear about what this build is. It’s unofficial, it’s 58 commits behind the mod’s main branch, and it has no update path. The whole plan was to throw it away the moment upstream shipped a real 26.3 release. A local patch you’ve promised yourself you’ll delete is a different artifact from a fork you intend to maintain.
Where it went
The version pins and the API fixes weren’t specific to my house — anyone trying to run that branch on 26.3 would hit them. So that subset went upstream as a pull request against the maintainer’s port branch. The natives divergence stayed home.
It went up under the agent’s own GitHub identity rather than mine. CC read the codebase, found the breakages and wrote the patch; the maintainer about to review it deserved to know whose work he was reading.
The PR was closed, and the reason was a good one: the maintainer had done his own 26.3 port in the meantime and merged that instead. His version independently landed on the same Fabric API floor and the same always-on-natives decision we had, which was reassuring. But his review also caught something we got wrong. Controlify builds several Minecraft versions out of one shared source tree, and CC’s API fixes were unconditional — they’d have broken the 26.1 and 26.2 targets. That’s a structural fact about the project you only learn by being told, or by building every target instead of the one you care about. We built the one we cared about.
Then his merge broke the 26.3 build outright: leftover GLFW key constants, in a version that doesn’t have GLFW. CC diagnosed it and fixed it in six lines by going through a version-agnostic input constants class instead, then verified clean builds on 26.1, 26.2 and 26.3 using upstream’s own CI procedure — the lesson from the review, applied immediately.
That fix is sitting on a branch on the agent’s workstation, one commit above upstream, unpushed. Whether it goes up as a second PR is a decision I haven’t made yet, and it’s mine rather than CC’s, because opening a pull request is a public act under an identity that persists. Same for whether to swap our five installs off the local 3.3.3 build onto upstream’s 3.5.0. The thing that works today is the thing we built; moving to the official jar is an improvement in maintainability and a risk to a working setup, and those don’t resolve themselves.
What I keep coming back to is how ordinary it was: a gap in some software, so the agent read an unfamiliar codebase, finished someone else’s work in progress, verified it in the actual client, deployed it to five machines, and offered the generalizable part back to the person who owns the project. Then it got told no, for a reason it hadn’t considered — which is roughly what happens to me when I send a patch to a project I don’t know well. The maintainer’s tree, the maintainer’s call. That part doesn’t change just because the contributor is a machine.