<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"><channel><title>Shannon Engineering</title><description>The engineering journal behind Shannon — architecture notes, incident writeups, and build-in-public updates from building Cursor for knowledge workers.</description><link>https://blog.shannon.bot/</link><language>en-us</language><item><title>Trust boundaries don&apos;t live in folders</title><link>https://blog.shannon.bot/blog/moving-a-folder-moved-a-trust-boundary/</link><guid isPermaLink="true">https://blog.shannon.bot/blog/moving-a-folder-moved-a-trust-boundary/</guid><description>A ticket that just moved a folder, same files, same format, diff untouched, quietly moved tweet-derived text into a trusted agent&apos;s read path: a prompt-injection regression that shipped clean because it passed its own acceptance criteria. The fix: key trust on provenance the server controls, never on which folder a file sits in.</description><pubDate>Thu, 02 Jul 2026 00:00:00 GMT</pubDate><category>agents</category><author>Tuan Huynh</author></item><item><title>Real web search for every model in Shannon</title><link>https://blog.shannon.bot/blog/web-search-every-model/</link><guid isPermaLink="true">https://blog.shannon.bot/blog/web-search-every-model/</guid><description>Every model in Shannon&apos;s lineup can now get a real, cited answer from the web, even the ones that never had search of their own — so a cheaper model doesn&apos;t have to guess or refuse when a question needs today&apos;s answer.</description><pubDate>Tue, 23 Jun 2026 00:00:00 GMT</pubDate><author>Tuan Huynh</author></item><item><title>Sign in with X, and link it to your Google account</title><link>https://blog.shannon.bot/blog/sign-in-with-x/</link><guid isPermaLink="true">https://blog.shannon.bot/blog/sign-in-with-x/</guid><description>You can now sign in to Shannon with X, connect it to an existing Google-authed account (or vice versa), and merge two separate accounts that were created before you ever linked them.</description><pubDate>Sat, 20 Jun 2026 00:00:00 GMT</pubDate><author>Tuan Huynh</author></item><item><title>The model that scored 7 and 3 on the same test</title><link>https://blog.shannon.bot/blog/the-model-that-scored-7-and-3-on-the-same-test/</link><guid isPermaLink="true">https://blog.shannon.bot/blog/the-model-that-scored-7-and-3-on-the-same-test/</guid><description>The same model scored 7 out of 9, then 3 out of 9, on the same injection-resistance eval. Neither number was real: the first came from a prompt shape production never serves, the second from a token cap that truncated answers before the validator could see them.</description><pubDate>Thu, 18 Jun 2026 00:00:00 GMT</pubDate><category>agents</category><category>testing</category><author>Tuan Huynh</author></item><item><title>Intelligent model routing with Auto</title><link>https://blog.shannon.bot/blog/auto-picks-the-model/</link><guid isPermaLink="true">https://blog.shannon.bot/blog/auto-picks-the-model/</guid><description>Shannon now defaults to Auto: each request is matched to the right model before work starts — Qwen3-Next for straightforward requests, Sonnet 4.6 for the hard ones — and the moment you pick a model yourself, your choice wins.</description><pubDate>Wed, 17 Jun 2026 00:00:00 GMT</pubDate><author>Tuan Huynh</author></item><item><title>False green: when a passing test vouches for a dead feature</title><link>https://blog.shannon.bot/blog/false-green-when-passing-tests-ship-dead-features/</link><guid isPermaLink="true">https://blog.shannon.bot/blog/false-green-when-passing-tests-ship-dead-features/</guid><description>A feature passed six-reviewer diff review, two rounds of execution and bug fixes, and full CI, then shipped broken. The only test covering it mocked a state its own state machine can never produce. Every offline gate proved the change was well-formed, not that it ran. Here&apos;s the anatomy of that false green, and the fix.</description><pubDate>Mon, 15 Jun 2026 00:00:00 GMT</pubDate><category>testing</category><author>Tuan Huynh</author></item><item><title>The one-line model swap that broke every multi-turn run</title><link>https://blog.shannon.bot/blog/the-field-nobody-told-me-to-echo-back/</link><guid isPermaLink="true">https://blog.shannon.bot/blog/the-field-nobody-told-me-to-echo-back/</guid><description>Swapping a Gemini model alias for a concrete id looked like housekeeping. It jumped a major model version. Gemini 3 requires echoing back an opaque thought_signature field on every replayed tool call, and no test caught the gap because it only surfaces on the second turn of a run.</description><pubDate>Tue, 09 Jun 2026 00:00:00 GMT</pubDate><category>agents</category><author>Tuan Huynh</author></item><item><title>Preview agent-built web pages with HTML View</title><link>https://blog.shannon.bot/blog/html-view/</link><guid isPermaLink="true">https://blog.shannon.bot/blog/html-view/</guid><description>Shannon can now write a web page as ordinary workspace files and show it running live in a sandboxed preview, so asking for a tweak is a small edit instead of a full rebuild.</description><pubDate>Wed, 03 Jun 2026 00:00:00 GMT</pubDate><author>Tuan Huynh</author></item><item><title>The security rule I reverted was right all along</title><link>https://blog.shannon.bot/blog/my-security-probe-raced-the-thing-it-was-probing/</link><guid isPermaLink="true">https://blog.shannon.bot/blog/my-security-probe-raced-the-thing-it-was-probing/</guid><description>I reverted a security rule because a probe against the live load balancer said it failed open. Cloud Armor takes about 100 seconds to propagate a new rule to the edge; my probe fired at 10 seconds and measured a rule that did not exist yet. Verifying the right layer isn&apos;t enough, you have to wait until it has decided.</description><pubDate>Tue, 19 May 2026 00:00:00 GMT</pubDate><category>architecture</category><author>Tuan Huynh</author></item><item><title>Edit Word, Excel, and PowerPoint files, and preview the result inline</title><link>https://blog.shannon.bot/blog/office-documents/</link><guid isPermaLink="true">https://blog.shannon.bot/blog/office-documents/</guid><description>Shannon can now edit a Word document, an Excel workbook, or a PowerPoint deck directly — add a row, fix a formula, add a slide — and hand back a new version that renders inline, so you can see the result before deciding whether to keep it.</description><pubDate>Thu, 14 May 2026 00:00:00 GMT</pubDate><author>Tuan Huynh</author></item><item><title>Image generation and editing, versioned in place</title><link>https://blog.shannon.bot/blog/image-generation/</link><guid isPermaLink="true">https://blog.shannon.bot/blog/image-generation/</guid><description>Shannon can now generate a new image from a prompt or edit an existing one by referencing its file. Every result versions in place, and the edit&apos;s origin — a fresh generation or a derivative of another image — shows up in the activity stream.</description><pubDate>Fri, 08 May 2026 00:00:00 GMT</pubDate><author>Tuan Huynh</author></item><item><title>One click, 221 re-renders: React Context can&apos;t subscribe to a field</title><link>https://blog.shannon.bot/blog/one-click-221-rerenders/</link><guid isPermaLink="true">https://blog.shannon.bot/blog/one-click-221-rerenders/</guid><description>I memoized the context value and wrapped the hot components in React.memo, standard advice, and one click still re-rendered a list 221 times. Context notifies every consumer when the value object&apos;s identity changes, regardless of which field a consumer reads, and no memo can stop that. The fix: a selector-based Zustand store.</description><pubDate>Tue, 05 May 2026 00:00:00 GMT</pubDate><category>architecture</category><category>ux</category><author>Tuan Huynh</author></item><item><title>Python and JavaScript count characters differently</title><link>https://blog.shannon.bot/blog/python-and-javascript-count-characters-differently/</link><guid isPermaLink="true">https://blog.shannon.bot/blog/python-and-javascript-count-characters-differently/</guid><description>I asked Claude to bold every occurrence of a word in a markdown file, and every edit landed one character off, because of a single emoji earlier in the document. Python counts that emoji as one character; JavaScript counts it as two. An integer that crosses a language boundary has units, declare them, or each side quietly picks its own.</description><pubDate>Sat, 02 May 2026 00:00:00 GMT</pubDate><category>realtime</category><author>Tuan Huynh</author></item><item><title>The test database that lied</title><link>https://blog.shannon.bot/blog/the-test-database-that-lied/</link><guid isPermaLink="true">https://blog.shannon.bot/blog/the-test-database-that-lied/</guid><description>A workspace delete that passed four code reviews and a green CI run still crashed production with a 500. The test suite ran on SQLite, which silently ignores a lock clause Postgres rejects outright, so the tests were honestly green about the wrong database.</description><pubDate>Tue, 28 Apr 2026 00:00:00 GMT</pubDate><category>storage</category><category>testing</category><author>Tuan Huynh</author></item><item><title>Rate-limited by tokens, not requests</title><link>https://blog.shannon.bot/blog/rate-limited-by-tokens-not-requests/</link><guid isPermaLink="true">https://blog.shannon.bot/blog/rate-limited-by-tokens-not-requests/</guid><description>A 27-second stall traced back to a token quota, not a request quota: Shannon&apos;s agent loop was re-sending its entire context on every turn. The fix is prompt caching with breakpoints placed where each layer of context stops changing, plus a canary token that almost broke it again.</description><pubDate>Thu, 23 Apr 2026 00:00:00 GMT</pubDate><category>agents</category><category>architecture</category><author>Tuan Huynh</author></item><item><title>Translating an SDK loop into one stream</title><link>https://blog.shannon.bot/blog/translating-an-sdk-loop-into-one-stream/</link><guid isPermaLink="true">https://blog.shannon.bot/blog/translating-an-sdk-loop-into-one-stream/</guid><description>A Shannon agent run looks like one continuous stream in the browser. Underneath it&apos;s a loop of separate Anthropic API calls with tool execution wedged between them. The fix is a server-side translation layer that hides every seam behind a small, fixed vocabulary of UI events.</description><pubDate>Wed, 22 Apr 2026 00:00:00 GMT</pubDate><category>architecture</category><category>realtime</category><author>Tuan Huynh</author></item><item><title>Two pipelines, one timeline: rendering agent activity across live and historical views</title><link>https://blog.shannon.bot/blog/two-pipelines-one-timeline/</link><guid isPermaLink="true">https://blog.shannon.bot/blog/two-pipelines-one-timeline/</guid><description>Every AI agent UI is secretly two UIs: one renders events live over SSE, the other rebuilds the same timeline from the database on reload. They have to match exactly, and five separate bugs, from parsing to null handling to ordering, proved they easily don&apos;t.</description><pubDate>Fri, 10 Apr 2026 00:00:00 GMT</pubDate><category>realtime</category><category>ux</category><category>architecture</category><author>Tuan Huynh</author></item><item><title>Hibernating an agent loop</title><link>https://blog.shannon.bot/blog/hibernating-an-agent-loop/</link><guid isPermaLink="true">https://blog.shannon.bot/blog/hibernating-an-agent-loop/</guid><description>What happens when a cloud AI agent needs to ask the user a question mid-task? You can&apos;t just block a coroutine. The fix: treat the agent loop as a function whose entire state is a message list, serialize it to Postgres, exit the process, and reconstruct the loop from scratch when the human answers.</description><pubDate>Thu, 09 Apr 2026 00:00:00 GMT</pubDate><category>agents</category><category>architecture</category><author>Tuan Huynh</author></item><item><title>Import from Google Drive, and accepted edits write back</title><link>https://blog.shannon.bot/blog/google-drive-in-and-out/</link><guid isPermaLink="true">https://blog.shannon.bot/blog/google-drive-in-and-out/</guid><description>Import Google Docs, Sheets, and Slides into Shannon, and when you accept an edit on an imported Doc, Shannon writes it back to the original in Drive. The agent works on the same document your team opens tomorrow.</description><pubDate>Thu, 09 Apr 2026 00:00:00 GMT</pubDate><author>Tuan Huynh</author></item><item><title>Track agent changes through Version History</title><link>https://blog.shannon.bot/blog/every-version-kept/</link><guid isPermaLink="true">https://blog.shannon.bot/blog/every-version-kept/</guid><description>Every document in Shannon keeps its full version history: see exactly what changed between a version and the one before it, flip through versions with the arrow keys, and restore any one with a click. Every agent edit is a version, and restoring never rewrites history.</description><pubDate>Sun, 01 Mar 2026 00:00:00 GMT</pubDate><author>Tuan Huynh</author></item><item><title>Review every agent edit with tracked changes</title><link>https://blog.shannon.bot/blog/review-every-edit/</link><guid isPermaLink="true">https://blog.shannon.bot/blog/review-every-edit/</guid><description>Shannon&apos;s agent edits your document directly, and every change lands as a tracked edit you give one of four answers: accept, reject, rewrite, or branch. You review an agent&apos;s work the way you review a teammate&apos;s marked-up draft.</description><pubDate>Sun, 22 Feb 2026 00:00:00 GMT</pubDate><author>Tuan Huynh</author></item><item><title>A vision for coworking with agents</title><link>https://blog.shannon.bot/blog/why-i-built-this/</link><guid isPermaLink="true">https://blog.shannon.bot/blog/why-i-built-this/</guid><description>AI assistants were supposed to assist, but I did all the grunt work: copy, paste, prompt, iterate. Then Cursor showed me what it means to hand a task to an agent that acts instead of talks. But for folks who don&apos;t work in an IDE, what would it look like to increase productivity exponentially? This is the question I set out to answer, and the five guiding principles that came out of it.</description><pubDate>Sun, 15 Feb 2026 00:00:00 GMT</pubDate><author>Tuan Huynh</author></item></channel></rss>