The Real Bugs Live in the UI (A Testing Reality Check)
July 1
After weeks of building sophisticated architecture, implementing AI reasoning, and solving complex integration challenges, I sat down to run four simple UI tests.
How hard could that be?
Test 1: Can it say hello?
First challenge: The web UI wasn’t running.
Failed to fetch: Connection refused on port 8081Oh. Right. The web server needs to be… started. Separately. Because it’s a separate FastAPI app that serves the frontend.
bash
cd web
uvicorn app:app --host 0.0.0.0 --port 8081There we go. UI loads. But wait — where’s the logo? Just a 🤖 emoji staring back at me. Didn’t I just add that? Made a note to fix it later. First, the tests.
“How are you?” I typed.
The response was perfectly professional, task-oriented, and slightly cold. Exactly as designed. We built a PM assistant, not a friend. Working as intended.
Test 1: ✅
The CORS dance
Tried to continue testing but got hit with the classic “Failed to Fetch” error. Not connection refused this time — CORS.
I was accessing the UI via http://0.0.0.0:8081 but the API expected http://localhost:8081. Same machine, same port, different origin according to CORS.
Twenty minutes of my life I’ll never get back, debugging why identical addresses aren’t identical.
Solution: Use localhost. Always use localhost.
I swear, one month or so into this project, that I had by then spent more time chasing environment issues than writing fresh code or fixing actual bugs.
Test 2: The file upload saga
Time to test file uploads. This had four subcases:
- Basic upload
- Give context about a file
- Give instructions about a file
- Use file as reference for GitHub issue
Uploaded a file. Got back a UUID. Success!
Asked “What’s in the file I just uploaded?”
Error: 'Workflow' object has no attribute 'input_data'Wait, what? This was working last week.
The bug hunt begins
Started digging. The intent was recognized correctly. The workflow was created. But when it tried to persist to the database… boom.
Domain model vs database model mismatch. Classic.
Dug deeper. Found another error:
QueryRouter missing conversation_query_service argumentThe QueryRouter initialization was incomplete. Missing dependency injection. Another classic.
But the best was yet to come.
The asterisk incident
Deep in the orchestration engine, on line 483, Claude found this:
async def *analyze*file(self, workflow: Workflow, task: Task) -> TaskResult:Those aren’t underscores. Those are asterisks. In a method name.
This turned out to be a red herring. At this point I was still pasting errors and terminal output into chat windows to get help, and unfortunately the markdown interpreter was reading _analyze_file as analyzefile (treating the underscores as italics, as markdown does) and then de-encoding the italics back to *analyze*file because * is another way of indicating italics in markdown.
There went another half hour of my life till I realized and explained it to Claude and we stopped hunting for nonexistent typos. Shades of the most expensive hyphen in history!
Fixed the logo, at least!
While taking a break between test 2.2 and 2.3, I decided to have Cursor just fix that missing logo, oh and make me a favicon at the same time. (I used to struggle with things like that!)
Found the static file configuration. Cursor added the logo path. Refreshed.
Piper Morgan’s dolphin logo smiled back at me from the UI. Sometimes the small victories matter most.
Testing just begun…
By end of day:
✅ Test 1: Greeting/chitchat — PASSED
⚠️ Test 2: File upload — PARTIAL
- ✅ 2.1: Basic upload works
- ❌ 2.2: Context fails (workflow persistence)
- ❌ 2.3: Instructions fail (asterisk method name)
- ⏳ 2.4: Not tested yet
⏳ Test 3: Error handling — NOT STARTED
⏳ Test 4: GitHub integration — NOT STARTED
Three major bugs found. Two architectural issues exposed. One logo fixed.
This is what testing really looks like.
Some lessons
- The UI is where reality lives — Your beautiful architecture meets actual users here
- Simple tests find real bugs — Forget edge cases; start with “does it turn on?”
- Environment issues waste more time than logic errors — CORS, ports, servers not running
- Typos can hide anywhere — Even in method names, somehow
- Small victories matter — Sometimes fixing a logo is the win you need
The beauty of systematic testing
The session log shows something important: methodical progress. Test 1, then 2.1, then 2.2. Each failure revealed something specific:
- Workflow persistence wasn’t actually implemented (just mocked in unit tests)
- Dependency injection was incomplete in query routing
- Method names can somehow have asterisks
These aren’t sophisticated bugs. They’re the kind you only find by actually using the system.
Next session: more reality to come
Three more tests to go. Error handling and GitHub integration still await. Workflow persistence needs real implementation.
The architecture is sophisticated. The AI is intelligent. The orchestration is elegant.
But the real bugs? They live in the UI, wearing asterisks as disguises.
