Published:
Some experiments begin with a roadmap. This one began with a model that appeared quietly on OpenRouter and OpenCode, with no benchmark chart and no polished launch page. It was called ox alpha, it was free to use during its alpha window, and it had enough runway to make us curious.
So we gave it a real application.
Aid Registry is a charity portal built around item-based giving. Charities can create campaigns for the actual goods they need, while donors can see what their contribution supports before they give. That makes the path from donation to impact more concrete and accountable than a generic donation page.
It also makes Aid Registry a meaningful migration test. The application has public campaign discovery, charity applications, donor accounts, campaign management, media uploads, and checkout. In other words, it is not a toy repository. It is a production Rails application with real user journeys and real accountability behind it.
Our question was simple: could an agent, working through OpenCode and one unannounced model, take on a serious Rails-to-Go port? Not a landing page. Not a single endpoint. A working application surface that we could read, run, test, and compare with the original.
The brief was deliberately practical:
That is where OpenCode shone. It could hold a long thread across the repository, move between source files and tests, run the code, fix failures, and explain the trade-offs it was making. Instead of treating the migration as a pile of files to translate, it treated it as a system whose behavior had to remain coherent.
Rails is a wonderful way to build an application. We are not telling people to rewrite working products for sport. Aid Registry still runs on Rails in production.
But a migration has always been one of those decisions that feels permanent because the translation work is so expensive. There are the visible parts: routes, views, models, jobs. Then there are the small contracts that live inside a mature framework: validation behavior, session handling, authorization, file uploads, edge cases, and all the details users never see until they break.
Aid Registry also relies on a mature Rails and gem ecosystem to deliver many of those behaviors. That is exactly why it made for a useful experiment. The question was not merely whether a model could translate Ruby into Go. Could it identify what a gem or Rails convention was quietly doing, decide whether to reimplement it or choose a replacement library, and make that decision understandable to a developer reviewing the port?
One or two years ago, treating that as a quick experimental project would have sounded unrealistic. You would expect a long planning phase before anyone wrote code. With an agent that can read the application, work through dependencies, propose a design, write the implementation, and keep returning to tests, the first serious experiment is now much cheaper to run.
We wanted to know whether an agent could make that work tractable. The answer was more encouraging than we expected.
The most useful part of the experiment was not that the agent produced Go files. It was that it kept coming back to verification.
It added backend tests and a Playwright suite that opens Chromium against the full Go API and React application. The browser tests cover the journeys a real user cares about: finding a campaign, opening it, applying as a charity, finding an organization, signing in as a donor, and reaching the donation flow.
For checkout, the test does not stop at an HTTP response. It fills in donor details in the browser and confirms that Stripe’s embedded checkout has mounted:
const stripeFrame = page.frameLocator('iframe[src*="js.stripe.com"]').first()
await expect(stripeFrame.locator('body')).toBeVisible({ timeout: 20_000 })
That is the kind of evidence we wanted. A port is not credible because the build is green. It is credible when someone can click through an important flow and the system behaves as expected.
The agent also surfaced the places where Rails had been doing quiet, valuable work. It turned framework conventions into choices we can see and test in Go: session behavior, password validation, authorization checks, media handling, and clear API boundaries.
One small example is image validation. Rather than trusting the content type supplied by a browser, the new media layer looks at the uploaded bytes first:
contentType := http.DetectContentType(data)
switch contentType {
case "image/jpeg", "image/png", "image/gif", "image/webp":
return contentType, nil
default:
return "", fmt.Errorf("unsupported image type")
}
That is not flashy, but it is the point. Good migration work is a thousand decisions like this one: make implicit behavior explicit, choose a boring reliable primitive, and leave the next person a system they can understand.
The Go port uses a deliberately small set of tools: the standard library’s net/http, pgx for PostgreSQL, golang.org/x/crypto for authentication, the AWS SDK for S3-compatible media storage, and Goldmark for Markdown. No magic required, just understandable pieces assembled with care.
Six days after ox alpha appeared, Z.AI revealed that it was GLM-5.3 Flash.
That made the experiment even more interesting. The model that had spent its free alpha window reading a Rails application, writing Go, building browser tests, and working through failures was now available publicly at a remarkably low cost. Its 1.31M-token context window, long-horizon agent performance, and pricing of $0.075 per million input tokens and $0.25 per million output tokens matched what we had felt in practice: it was fast, capable, and unusually affordable to keep in the loop.
We are not claiming it beats the best frontier model on every difficult task. We are saying that its cost-to-intelligence ratio changed what felt reasonable to try. A migration experiment that once sounded expensive became something we could explore seriously.
We liked the result enough to act on it. GLM-5.3 Flash is now the default model for a new Chat-O conversation, and it is available in Light, Balanced, and Power. The full announcement is here.
Our recommendation is simple:
You do not need to begin with a full rewrite. Pick a bounded but meaningful area of an application you know well. Keep the original code as a reference. Ask an agent to explain what it finds, write the replacement, run the tests, and add an end-to-end check before you call it done.
Start with a workflow you can see: registration, checkout, file upload, a search page, or a report. The goal is not to hand your architecture to an agent and walk away. The goal is to see how much of the slow, repetitive translation work it can carry while you stay focused on the decisions that deserve a human.
If you want to try the same model, sign up for Chat-O and start a conversation with GLM-5.3 Flash. If you want it in your terminal, take OpenCode for a real repository task. Then test the result. That is where the interesting part starts.