The Go Button Passed the Tests. Clicking It Did Nothing.

On this page

The Go button looked finished. Previous and Next sat beside it in a compact pagination footer. Cursor said the control worked. I hard-refreshed the page, clicked Go, and nothing happened.

That gap is the whole story. Not “AI cannot write code.” Not “tests are worthless.” Just the quieter difference between a function existing somewhere, a test calling that function, and a person pressing a control after the browser and framework have finished with the markup.

The same Streamlit app, a smaller fight

I had just spent time standardising the compact tables and editing dialogs in an existing Streamlit CRUD application. Streamlit is a Python framework for building data apps in the browser. CRUD is the dull shorthand for screens where you create, read, update and delete records. The tables were meant to feel consistent. The footer under them mattered to me for the same reason: Previous, Next, and a Go control for typing a page number, without turning the pagination into a second UI project.

Preserving that compact design across the application was not a side note. It was the point.

The fix that fixed the wrong thing

I reported that Go did not work.

Cursor produced a fix that used a more standard Streamlit control. It reported that Go worked. Technically, that report was closer to true than what came next. The control responded. The pagination also got much larger and stopped matching the compact footer I was trying to keep.

So I asked for the compact footer back.

Looking fixed, still broken

Further attempts restored the appearance. After one of them, Cursor told me:

“Restored the compact HTML footer look … Functionality is unchanged: Go still works.”

The footer looked right again. Go still did nothing when I tested it myself. Another attempt used URL parameters, but that did not solve it either.

That became the annoying loop. The agent reported success. It offered “proof.” I kept finding the failure by using the page: click Go, press Enter where the field allowed it, watch the page stay put.

During the same stretch the agent also reported fixing a Bills filter-sync issue that could reset the page while you paginated. That may have been a real related bug. It was not the whole explanation for a Go button that simply refused to react.

Three different kinds of “proof”

When I asked Cursor how it had missed the problem, it gave me an answer I appreciated:

“The bug was introduced in how Go was built, and I missed it because I verified the wrong thing.”

According to the agent’s own later explanation, some of the unit checks inspected generated HTML and confirmed that a function call appeared in the source. Some checks invoked the pagination function directly through JavaScript evaluation. Those are useful checks. They are also not the same as proving that the button’s click path survived processing and still worked through the interface.

It helps to keep the three claims separate:

  • The source contains a handler.
  • The function can be called.
  • A user can click the control and reach the intended page.

Each one is evidence. Only the third one answers the bug I was reporting.

What was actually missing

Eventually the agent inspected the live control. The diagnosis it reported matched what we then observed in this application: Go had been implemented with inline onclick / onkeydown attributes inside HTML rendered through Streamlit’s st.html. Those attributes were not present on the live page. Previous and Next used delegated event listeners and kept working.

Current Streamlit documentation describes st.html as sanitising HTML and ignoring JavaScript by default, which is consistent with that diagnosis for this path. I am not claiming every Streamlit version or every HTML rendering route behaves identically. I am saying this is what the agent reported, and what the live page showed, for this control.

Once Go was wired through the same kind of delegated events as the other pagination controls, and once the real interaction was tested locally — click and Enter — I could confirm it myself. Compact footer. Working Go.

Feeling out of control at speed

The cycle was frustrating. It was also slightly worse than ordinary bug frustration.

The AI said it was fixed. Tests passed. I still had to hard-refresh and click the thing to discover that the user-facing path was broken. That makes you feel less in control of your own software, even when the tool is moving faster than you would by hand.

It made me want to inspect the code myself. At the same time, I am not pretending that writing every line manually would automatically have prevented the bug. I do not know that. I only know that closer involvement might have made the missing handler easier to spot earlier, and that I still value how quickly the agent can implement changes once the problem is actually understood.

I am not abandoning the workflow. I want a better one.

What I want to ask the agent next

The project already has LLM context and best-practice documents that cover testing. After this ticket I want to discuss, with the agent:

  • Whether those instructions were clear and actually applied.
  • Whether a skill, hook or prompt should require a real browser interaction for user-facing controls before a task is called done.
  • What a given test actually proves before anyone declares success.
  • How to distinguish local evidence from behaviour on the deployed test site.

Those are questions and possible improvements. They are not fixes I have already shipped. I also do not know exactly why the existing instructions were missed, and I am not going to claim that a hook alone would guarantee compliance forever.

Automated checks should also notice when a compact layout quietly becomes a large one. I have not added those checks yet. I am naming the gap, not inventing a tidy ending where the process is already repaired.

AI remains a fast and useful part of how I work. This Go button just reminded me that “the function exists” and “a person can use the control” are still different sentences.