cd ../blog
7 min read

Week in Review: Jul 27 - Aug 2, 2026

The week didn’t start with a bang; it started with a cascade. Between July 30th and August 3rd, the automated release pipeline for the AV field app fired off twelve distinct version bumps, taking the app from v0.7.0 all the way to v0.19.0 in less than 72 hours. It’s the kind of velocity that usually signals a crisis, but in this case, it was the result of clearing a massive backlog of feature flags and user-requested tweaks. While I was navigating that sprint, a parallel track of work on our internal gateway and a utility app revealed some deeply frustrating but instructive bugs in how we handle state, identity, and memory in LLM-integrated systems.

The AV Field App Sprint

The sheer volume of AV field app updates can be confusing if you look at them chronologically. The core narrative, however, is about stabilizing the rendering engine and responding to community feedback on accuracy. The most significant technical conversation came from user feedback on a deceptively simple question: why does our acoustic map look 10-20 dB louder than reality?

The answer lay in a fundamental difference in how we define “level” versus how professional tools like EASE Focus handle it. Our map is a max-SPL (Sound Pressure Level) map. Every speaker in the simulation is driven at its maxPowerW. If a speaker has a maximum output of 110 dB, we simulate it at 110 dB, regardless of what the user is actually playing. This creates a “base layer” that is objectively loud but not necessarily representative of typical listening levels.

However, the bigger trap wasn’t the absolute numbers—it was the relative error between mixed speaker models. Different manufacturers measure sensitivity differently. For instance, the Control 30 publishes sensitivity at 2.83V @ 4 ohms, which equals 2 watts. If you mix that with a Control 25 that assumes 1 watt, you get a 3-6 dB discrepancy in the relative level between boxes. This skews the visual balance in the room, even if the absolute max level is correct.

We researched AFMG’s documentation to see how the industry standard handles this. EASE Focus defaults to manufacturer max SPL as well, but it exposes a per-source attenuation column and allows users to see the effective input voltage. Our gap isn’t in the math; it’s in the control. We lack a UI element that lets users dial back the drive level or specify headroom.

So, we shipped v0.19.0 with a new design assumption: we’re keeping the max-SPL map as the default (because it’s safer for system design—never underestimate a system), but we’re building the framework for drive-level control in the next cycle. For now, rooms with a single speaker model or 70V mode are unaffected in their visual shape, so the immediate user experience is stable. The set of features that got compressed into those twelve releases mostly revolved around refining the rack layout engine and improving the PDF export logic for FAT/SAT documents. It was a heavy lift on the backend data model, ensuring that racks, PDU outlets, and speaker placements are persisted correctly, but the result is a much more robust foundation for complex commercial installations.

The “Who Am I?” Identity Crisis

While the AV field app was churning out updates, our internal gateway—a tool I use for drafting and code review—hit a weird hallucination bug. I asked the model which model it was, and it replied, “I am Claude, running in the cloud,” even though the router had served the response from our local Ollama instance.

This is a classic prompt injection failure, but with a twist: the model was lying about its own environment. I initially thought a system prompt fix would suffice, but experience tells me that models can be argued out of system instructions if the context allows it. So, I implemented a three-layer defense. First, _BASE_PROMPT states the model’s identity as a hard fact. Second, and most importantly, I added a Tier 0-ID intercept. This is a deterministic function that answers “which model are you?” directly from the config file in 1ms, without consulting the LLM at all. It’s fast, it’s accurate, and it cannot hallucinate. Third, I added a regex-based cleanup that strips any false “Claude” claims from the rest of the response.

I tested this against 22 different phrasings, including the exact sentences the model used to lie to me. All cases pass in both directions. The key lesson here is that you can’t rely on the model to know what it is. You have to enforce identity at the infrastructure level.

Memory, Context, and the “Bug” Incident

The most time-consuming bug of the week wasn’t a crash or a race condition; it was a memory leak in my assistant’s brain. I had taught the gateway about a person named “Bug,” including specific facts about their work. A few hours later, I asked, “Check what you have on Bug again,” and the assistant denied having any information.

This took four releases to fix because every “fix” I tried was verified under conditions that didn’t match the user’s actual workflow.

  1. v2.16.0: I updated the extractor to capture facts about people, not just the user. This failed because the live prompt path never called the memory context function I was modifying.
  2. v2.16.1: I tried to inject the facts into the system prompt directly. This worked for some queries but failed for others because the history contained the previous denial, and the model just copied its own precedent.
  3. v2.16.2: I added a relevance override block. This helped with direct questions but not with follow-ups that referenced the prior context.
  4. v2.16.3: The actual fix was surgical. In _build_messages, we now drop past turns that deny knowledge of someone we currently hold facts about. It was a simple line of code, but it revealed a profound truth: if you feed an LLM its own past error, it will trust its past error over your current instructions.

The proof was in the test: the same question answered correctly with no context, but wrongly with the real context attached. By scrubbing the denial from the context window, we forced the model to rely on the current facts. It was a reminder that in RAG (Retrieval-Augmented Generation) systems, context management is often more critical than retrieval accuracy.

The Update Banner That Wouldn’t Die

In a lighter but equally frustrating incident, I noticed that an update banner on our pet-log app persisted even after I uninstalled and reinstalled the app. The issue was a version code mismatch baked into the bundle.

The app’s update check logic reads app.json to determine the current version. I had shipped a build where build.gradle was at version code 64, but app.json was still at 63. The manifest advertised 64, so the app thought it was behind the published version. Reinstalling doesn’t help because the stale version code is inside the compiled bundle.

The fix required syncing the manifest to match the published build and updating app.json for the next release. It’s a small detail, but it highlights the importance of keeping configuration files in sync across different build systems. A version mismatch like this can lead to endless user confusion and support tickets.

Webhook Key Rotation and Gateway Security

Finally, I had to rotate a webhook API key after realizing a gate test had inadvertently logged the live key into a public channel transcript. The rotation process was straightforward, but it revealed a fragility in our automation: the old key sweep timed out, leaving us in a gray area until I manually shredded the temporary keys.

To prevent this in the future, I tightened the gateway’s tool-use permissions. I discovered that the canUseTool hook was never being invoked for allowlisted tools, which meant users could append to protected channels without approval. I implemented a PreToolUse hook that enforces gates before any tool execution. I also fixed a bug in my own LIVE_PRODUCTION_APPS list, which was inventing two apps and missing five real ones. A drift test against our verification script now ensures that our internal definitions match reality.

Looking Ahead

Next week, I want to focus on two things: refining the drive-level control in the AV field app to address the relative level errors my tester pointed out, and hardening the gateway’s memory context to prevent the “denial propagation” issue we saw with Bug. There’s also a utility app we’re developing that needs a clean release cycle, and I want to make sure we avoid the version code mismatch that plagued the pet-log update.

The week was a reminder that automation is powerful, but it’s not infallible. The twelve AV field app releases were a victory for velocity, but the gateway bugs were a lesson in precision. In both cases, the solution wasn’t just writing more code—it’s understanding the underlying mechanics of how the system behaves under real-world conditions.

Newsletter

Enjoyed this post?

Subscribe to get notified when I publish new articles about homelabs, automation, and development.

// no spam, unsubscribe anytime. ~2-4 emails / month

Keep reading