Case study · Observatory
Migrating an AI-built prototype into a production Angular app, with AI
Anthony Turner · September 2026
- Angular 22
- Strict TypeScript
- Angular CDK
- Three.js (WebGL)
- Node.js API
- Vitest
- Vercel
- Upstash Redis
- GitHub OAuth
- Claude Code
In one paragraph
I built a developer dashboard, pr-starmap, fast with AI coding agents. In three days it worked, but it had become three single-file HTML pages of 2,300 to 7,100 lines each: hard to test, hard to change, and impossible to hand to anyone. Rather than keep patching it, I planned a rebuild in Angular and directed AI agents (Claude Code) through it under an engineering process I set up: every change started as a GitHub issue, went through a pull request with tests and a written review, and was merged only once verified. In five days the new app, Observatory, replaced every feature of the old one. It ended with 105 merged pull requests, about 1,500 automated tests, and a clean shutdown of the original.
- lines of single-file HTML replaced
- 16,288
- merged pull requests
- 105
- automated tests
- ~1,500
- to replace every feature of the old app
- 5 days
The problem
pr-starmap ranks a developer's open pull requests "blocked first" and draws them as an animated star map, with a 3D "orrery" of every project and a voice assistant on the home page. Built quickly with AI, it grew into:
- 16,288 lines in three HTML files: 7,091 (star map), 6,860 (home) and 2,337 (orrery), with the logic, styles and 3D code all in one file each.
- No build, no types and no unit tests on the pages. Every change risked breaking something unrelated.
- A delivery model that capped it: a Claude Code plugin that published snapshots as static pages, which couldn't reach GitHub live.
It proved the idea. It was not something I would show an employer or keep extending.
The decision: rebuild alongside, don't rewrite in place
I chose a strangler-style migration:
- Keep the old app running. pr-starmap kept working while Observatory replaced it piece by piece.
- Small pieces. Each step was sized at 30 to 90 minutes of work, one pull request each.
- A fixed order. Theme and shell first, then the home page's panels, the 3D core, live data, the orrery, the star map, and finally the voice assistant.
- A hard independence rule. Observatory could never depend on pr-starmap at runtime, so the old one could be deleted at the end.
How I directed the AI: process over prompts
The work was done by AI agents. The speed and quality came from the rules I set around them.
- Every change was tracked. Each change started as a GitHub issue with acceptance criteria, got its own branch and pull request, and was merged with a merge commit. That's 102 issues and 105 pull requests, all readable in the repo.
- Written standards the agents follow. An AGENTS.md routes each agent to the rules it needs: SOLID and Clean Code, comment style, how to verify a change, and six Architecture Decision Records. One ADR keeps the AI assistant local-only for security; another records which tasks may run on the machine at all.
- An honest review on every pull request. Each PR description has a Verified section and a Not verified section, and each gets a posted review before merge. When the evidence was weak, the PR said so.
- Parallel agents without collisions. Several AI sessions worked on the repo at once, each in its own git worktree, so no two ever shared a checkout.
- Faithful ports. For the visuals I required function-for-function ports of the original drawing code, including matching random seeds, so the new app looks the same rather than "inspired by".
How the work was verified
- Automated tests: 936 front-end tests (Vitest) and 583 API tests (Node's test runner), across 197 and 102 test files. Lint, strict type-checking and formatting ran on every change.
- Real screenshots: for visual work the agent ran the app and captured headless-Chrome screenshots, including WebGL through a software renderer. Two real bugs surfaced this way: an overlay hidden behind the 3D canvas, and a toolbar running off narrow screens.
- A final gap audit: before calling the migration done, a separate AI agent compared both codebases feature by feature. It found about 45 features fully ported and a short list missing. It also showed that several of my own progress notes were wrong: some things I'd noted as missing were already built. Lesson: verify against the code, not the notes.
What got built
| Area | In Observatory |
|---|---|
| Architecture | 104 Angular components, about 34,500 lines of app TypeScript, plus a 13,000-line Node API of its own |
| Live data | GitHub through gh locally, or a server token when hosted. Refresh forces a fresh read instead of a cached one. |
| 3D | A WebGL core and a Three.js orrery with custom shaders (lit planets, ring shadows, a ray-marched corona, bloom). Both load lazily, and a 2D fallback takes over if WebGL fails. |
| Voice assistant | Speech-to-text (ElevenLabs Scribe, with in-browser Whisper as fallback) and replies read aloud. The assistant runs on Claude Code and can look up projects, pull requests and usage. |
| Hosting | Deployed on Vercel with GitHub sign-in, an allowlist, a read-only public preview, and Upstash Redis for stored data |
| Terminal | A command-line review queue sharing triage with the web app |
Retiring the old app
Finishing a migration means turning the old thing off cleanly:
- Data carried over: usage history and 128 recorded agent handoffs were imported into Observatory.
- Unhooked: the old plugin was uninstalled and its status-line hook removed, after checking that Observatory's replacements were already live.
- Archived, not deleted: the old repository was archived on GitHub, read-only with its history kept.
- Nothing lost locally: before the local copies were removed, every folder was checked for uncommitted work. Two unsaved files and two old experiments were saved first.
Mistakes, and what I took from them
- A PR merged with a formatting warning. I fixed it in a follow-up and corrected the original PR's record instead of hiding it.
- Documentation drift. The README still described the assistant's old AI provider after it had changed. The fix was a deliberate "is the README still true?" pass.
- Notes aren't the source of truth. The gap audit showed several progress notes were wrong. Checking against the code is what made the final list trustworthy.
What this shows
- Directing AI as an engineer. The agents wrote the code; I set the architecture, the order of work, the standards and the definition of done, and they held across 105 pull requests.
- Modernising legacy code safely. Old and new ran side by side, the move happened in small verified steps, the data was migrated, and the old system was shut down cleanly.
- Frontend depth: Angular with strict TypeScript, component architecture, WebGL and Three.js, accessibility (keyboard paths, screen-reader labels, reduced-motion support), and responsive layout.
- Honest delivery: every change says what was verified and what wasn't.
Timeline
pr-starmap started 22 Sep 2026. Observatory's first PR merged 25 Sep, feature parity arrived 29 Sep, and pr-starmap was archived 29 Sep.