A Human and an AI, On One Repo: Building a GitHub Workflow We Can Actually Trust
A Human and an AI, On One Repo: Building a GitHub Workflow We Can Actually Trust
*September 2026 — a development-workflow story from a small open-source fork project.*
Some software projects start with a grand plan. Ours started with a fork, two days, and no plan at all.
A developer and an AI pair-programming assistant, sharing a real repository — an open-source macOS desktop-pet app that we forked mid-week and started shipping features into by the weekend. It was fast, it was fun, and it was completely unsustainable. By the third day we were making hotfixes *inside* a feature branch, and that's when the whole thing told us to slow down.
This is the story of what happened after that: how we backed up, found our documentation quietly lying to everyone, and built the workflow that now runs the project — a workflow where every change is one git revert away from gone.
The flurry: fork it, ship it (day one and two)
The project began the way most side-projects begin: with momentum and no ceremony. On day one we forked the repo. On day one and two we wrote two real features back to back:
- a handshake channel — the app reports who it is (program, hostname, model) on startup and when the model changes
- a command channel — the AI can make the app speak through its text-to-speech stack
Both landed in their own feature branches. It felt like we were flying. Branches were cheap, commits were clean, and the pair-of-us loop — human sets direction, AI handles the mechanics — was genuinely fast.
The mistake: hotfixes in the feature
Then came the third day. Two small quality-of-life tweaks looked harmless: a chat-widget layout adjustment so bubbles didn't clip at the screen edge, and a new window-drag mode so the desk pet could be placed anywhere. Both were "small fixes." Both got committed *on top of the newest feature branch*, because that's where we happened to be working.
That was the dilution. The feature branch, which was supposed to carry one coherent idea, now carried:
- the feature itself
- an unrelated UX experiment (chat bubbles)
- another unrelated UX experiment (window dragging)
- a growing pile of patch-on-patch commits as each tweak fought the last
Milestones get blurred. Reverting one thing becomes entangled with everything else. The branch stopped being a story and started being a heap.
The realization: back up, gain structure and discipline
It took about a day of patch-on-patch churn to see it clearly: we needed to back up and gain structure and discipline before we shipped one more line.
So we did the cleanest thing possible — we backed both experiments out. Because the git history was still ours, undoing them was a single, clean revert of each change, a few minutes of work. The confusion was cheap to clean up because it hadn't hardened into anything permanent yet. That's when the first principle crystallized:
The experiments went into the roadmap's "backed-out changes" log, each with a one-line lesson: *experimental UX changes go on experiment branches, never milestone branches.*
The discovery: our documentation was lying to us
With the work backed out and the adrenaline gone, we finally did the unglamorous pass that should have come first: we read the documentation. It was bad — not in style, but in facts.
- The README's header table called the fork source "this repo," in all three languages the README is maintained in. The real fork was a different URL entirely.
- User-facing links — bug tracker, releases page — pointed at the upstream's Issues and Releases, not ours. Anyone filing a bug would have filed it on the wrong project.
- The download section instructed users to grab a release archive from a page that had nothing published on it. The artifact name it cited wasn't even something our build tooling produced.
- Licenses and third-party notices were incomplete or mistyped.
None of it was malicious. All of it was just *stale* — documentation that described a hopeful future or a different project instead of the actual one.
Lesson: documentation is part of the codebase. A README that can't accurately state who you are, where your bugs go, and what you've actually published will quietly misdirect every contributor and every user who reads it.
The realignment: define the road forward
With the wreckage surveyed, we defined the road forward — not as a fixed goal, but as structure and discipline the codebase could grow inside:
A roadmap, not a deadline. A vision statement — staying current with a fast-moving OS, keeping pace with advancing AI/voice technology, and following changing use cases — with milestones tracked as their own branches, a backlog for what's proposed but unstarted, and the backed-out changes logged with their lessons. Editing those steering documents is restricted to the two of us.
A versioning strategy built on Git Revert. A small set of branch lanes — feature/, fix/, hotfix/, docs/, experiment/ — with a classification rule for every change, and a backout playbook so any merged fix can be undone with git revert -m 1 <merge>. The cardinal rule, learned the hard way: milestone branches carry only their milestone.
Issue triage as an on-ramp to the lanes. Every bug report lives on our tracker with a label — bug, hotfix, enhancement, localization, macos-version, docs — and each label maps one-to-one onto a branch lane. A confirmed bug becomes a one-commit fix branch. An enhancement enters the roadmap backlog first. Reporters get a template, so a well-classified issue triages in minutes.
Localization as a ship blocker. The app ships in 13 languages, with Japanese and Chinese as priority audiences, and the README is maintained trilingually. Any user-facing change that can't land in all maintained locales doesn't ship. We caught the "wrong this repo" bug in all three languages precisely because parity is a rule, not an aspiration.
Documentation that describes reality. The phantom download section got a banner — *"No release published yet, build it yourself until milestone M7 ships"* — the numbered steps were scrubbed entirely, and M7 (the first real release) became a milestone with a publish checklist. The docs are now true today, and they define how they'll become true later.
How the human and the AI actually split the work
None of this structure exists because we love paperwork. It exists because the two of us have very different strengths, and the workflow is what keeps those strengths from colliding:
- The human sets direction, decides what counts as a milestone versus an experiment, applies taste, and reads every diff. Nothing merges without their word.
- The AI does the high-volume mechanical work — branch mechanics, reversible reverts, keeping three languages of documentation in lockstep, cross-checking claims against reality (up to and including hitting the GitHub API to verify that *no release actually exists* before we scrubbed the download steps), and pointing out the inconsistencies, like the README that called the wrong repo "this repo."
The guardrails are what make the AI safe to run at speed. When a feature idea threatens to wander off-milestone, the checklist catches it. When a fix looks like it might prove harmful, the strategy has the backout waiting.
What's next
The roadmap says it plainly: the milestone that publishes our first release is queued, and the download section will come back to life — accurately this time — when it ships. The steering documents live on the main branch now, merged as a single revertible unit, because of course they were.
*If you're starting a similar project — with or without an AI partner — the cheapest insurance you can buy is three things: a roadmap with a vision instead of a deadline, a versioning strategy where every change is one git revert away from gone, and a README that says exactly what is true today.*
*The AI won't make your project wise. The structure will.*