cd /news/ai-safety/four-times-my-system-looked-correct-… · home › topics › ai-safety › article
[ARTICLE · art-157527] src=dev.to ↗ pub= topic=ai-safety verified=true sentiment=· neutral

Four times my system looked correct and wasn't

A developer identified four cases where tests passed while the underlying property was still broken, including a prompt-injection test in the LLM-Council multi-model pipeline that verified string assembly rather than attack resistance. The fix uses a fresh random nonce in delimiters for every prompt so untrusted model output cannot forge the closing marker, with tests confirmed to fail when the nonce is made static. The same pattern appeared in a RAG pipeline's retrieval ranking, an OpenTelemetry monitoring pipeline leaking identity attributes like email addresses, and a hash-based CSP broken by inline syntax-highlighting styles.

by read3 min views6 publishedOct 26, 2026

Most of the bugs that taught me something weren't crashes. The system worked, the checks were green, and the property I cared about was still broken. Here are four of them, from three different projects.

In LLM-Council, three models answer a question, rank each other's answers blind, and a chairman model writes the final synthesis. Output from one stage becomes input to the next, so I had to fence untrusted model output off from the instructions around it.

My first version used static delimiters. I wrote tests that checked where the markers were placed and how the string was built. They passed.

The problem: those tests checked that the Python string was assembled correctly. An attacker doesn't care about that. A hostile model output can simply reproduce the static closing delimiter, and the next model reads the untrusted block as finished.

The fix was a fresh random nonce in the delimiters for every prompt, so untrusted output can't predict the real closing marker. I found a second boundary that needed the same treatment: the rankings going into the chairman stage were untrusted too.

Then I broke the fix on purpose. I replaced the random nonce with a static one and checked that the tests went red. I removed the ranking fences and checked that those tests failed as well.

One limit worth stating: the nonce protects the boundary against delimiter forgery. It doesn't make the content inside trustworthy, and it doesn't solve prompt injection in general.

Full write-up: I wrote a test for prompt injection. It passed while the attack worked. MonferrinoAI is a RAG pipeline (Firecrawl, Voyage AI embeddings, Supabase with pgvector) that prepares weekly content briefings for a local business website. A human still writes and publishes.

I tested retrieval against the real database and found that a keyword-only query could rank the wrong page above the one that was actually relevant. Nothing was technically broken: every result was a valid row. Adding context to the query, the keyword cluster and the target page, fixed the ranking.

The same project has a security side. Web pages are untrusted input and can contain text that looks like an instruction. So the page bodies never reach the final briefing: the handoff only carries URLs, headings and similarity distances.

Agentic-OS collects Claude Code telemetry through an OpenTelemetry Collector, with Prometheus and Grafana for metrics and a private Loki pipeline for logs. The point is to see what an agent actually did, not what it says it did.

Testing against the real client showed that it can send identity attributes, including an email address. My monitoring pipeline was a privacy boundary and I hadn't treated it as one.

Now attributes go through an allow-list, and the tests run against the real telemetry shape. Anything unknown is dropped instead of stored.

My website uses a strict hash-based Content Security Policy without unsafe-inline. Syntax highlighting added inline styles that the policy would block.

The quick fix was to loosen the CSP. I removed the incompatible behavior instead, and added CI tests that run against the built HTML, the files that actually ship, checking inline scripts, hashes and style attributes. Some of those problems only exist after the build, so testing the source wouldn't see them.

In all four cases a check existed and looked reasonable. It just wasn't looking at the property I cared about.

So before I trust a control, I ask: what should turn red if this property stops being true? Then I break the property and see if it does.

This post grew out of my Developer Story on CoderLegion, written by Mehadi Hasan, which covers the projects and the path behind them in more detail.

── more in #ai-safety 4 stories · sorted by recency
── more on @llm-council 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/four-times-my-system…] indexed:0 read:3min 2026-10-26 · —