<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Bruce Blog</title><link>https://flydogai.com/bruce/blog?lang=en</link><description>Design, product, interaction, engineering and decisions. Notes on building, for developers and the agents working with them.</description><language>en</language><atom:link href="https://flydogai.com/bruce/blog/feed.en.xml" rel="self" type="application/rss+xml"/><item><title>Leave a trail for the next developer</title><link>https://flydogai.com/bruce/blog/why-we-build-in-public?lang=en</link><guid isPermaLink="true">https://flydogai.com/bruce/blog/why-we-build-in-public</guid><description>The founding issue of the Bruce blog: design, product, interaction, engineering and decisions, written so developers and their agents can build on the work.</description><dc:creator>The Bruce team</dc:creator><category>editorial</category><pubDate>Thu, 05 Nov 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Leave a trail for the next developer&lt;/h1&gt;&lt;p&gt;The founding issue of the Bruce blog: design, product, interaction, engineering and decisions, written so developers and their agents can build on the work.&lt;/p&gt;&lt;p&gt;The Bruce team · Published &lt;time datetime=&quot;2026-11-05&quot;&gt;2026-11-05&lt;/time&gt;&lt;/p&gt;&lt;section id=&quot;everyone-who-builds&quot;&gt;&lt;h2&gt;People who use the tools are becoming developers&lt;/h2&gt;&lt;p&gt;We build Bruce to help more people turn ideas into things they can use. Independent makers, first-time product builders and professional teams arrive with different experience. Once they start asking questions, testing ideas and judging results, they are taking part in development.&lt;/p&gt;&lt;p&gt;AI can handle more of the code. People still decide why a product should work a certain way, what feels awkward and which experiments failed. Those decisions often live in conversations, changes and commits. The project moves forward; its reasoning can disappear behind it.&lt;/p&gt;&lt;/section&gt;&lt;section id=&quot;leave-a-trail&quot;&gt;&lt;h2&gt;Make the work a path someone else can follow&lt;/h2&gt;&lt;p&gt;This journal begins with a commitment to record Bruce&amp;#39;s design, product, interaction, engineering explorations and important decisions. A useful article explains the original problem, what we observed, what we tried, what we chose and what remains unresolved.&lt;/p&gt;&lt;p&gt;That is one form of Build in Public. A finished result is only part of the story. A detour, a disproved hypothesis or a measurement that only makes sense in its real setting may save the next developer a wrong turn. We write for people and for the agents helping them. Clear context, constraints and verification make knowledge easier to use again.&lt;/p&gt;&lt;/section&gt;&lt;section id=&quot;from-work-to-writing&quot;&gt;&lt;h2&gt;Let development grow its own journal&lt;/h2&gt;&lt;p&gt;This is also something Bruce aims to help its users do. Its existing built-in writing prompts can gather relevant memories, milestones and records around a topic, use available Git history, and organize blog posts, development diaries, progress reports and Build-in-Public drafts.&lt;/p&gt;&lt;p&gt;Finding material and drafting text are followed by human review: check facts, add explanations, remove information that should stay private, then decide whether to save and publish. That decision matters. Writing assistance should help you express your work rather than announce achievements you have not verified.&lt;/p&gt;&lt;/section&gt;&lt;section id=&quot;a-place-to-talk&quot;&gt;&lt;h2&gt;Give technical conversations somewhere concrete to begin&lt;/h2&gt;&lt;p&gt;We do not expect every article to offer the only answer. We want discussion grounded in a particular situation. Why did you choose a different approach? How does the same interaction behave on your device? What did your agent notice that we missed?&lt;/p&gt;&lt;p&gt;Articles have stable addresses, linkable sections and a text version you can keep. Share one with a colleague or an agent, or email us about a specific article. Starting from work that actually happened is easier than reconstructing all of its context.&lt;/p&gt;&lt;/section&gt;&lt;section id=&quot;explain-the-product&quot;&gt;&lt;h2&gt;Use one article to explain the work more than once&lt;/h2&gt;&lt;p&gt;A short feature description should have solid reasoning behind it. The blog gives that reasoning room: why an interaction is arranged this way, what a technical decision buys us, and how we balance a light tool with a complete experience. Product pages can link to the relevant article when readers want more depth.&lt;/p&gt;&lt;p&gt;Reviewed articles can also become source material for contributed pieces, product introductions and conversations with the media. We will keep the full explanation, then adapt summaries for their setting. Public communication and actual work should share the same evidence; future readers should be able to understand the choices we made.&lt;/p&gt;&lt;/section&gt;&lt;section id=&quot;start-here&quot;&gt;&lt;h2&gt;Begin here, then recover the path behind us&lt;/h2&gt;&lt;p&gt;Our first collection includes this founding issue, the puppy identity story, two interaction investigations and an engineering explanation of the Agent&amp;#39;s runtime boundary. Older accounts retain their event dates alongside their publication date. Historical measurements keep their test conditions.&lt;/p&gt;&lt;p&gt;We will gradually turn more of the project&amp;#39;s documents and commit history into articles worth reading. Design, product, interaction, engineering and decisions each have stories to tell. We hope an experience you find here helps your next experiment, and that these records introduce us to more people who are making things.&lt;/p&gt;&lt;/section&gt;</content:encoded></item>
<item><title>Good work, Bruce: a name, a companion, a puppy</title><link>https://flydogai.com/bruce/blog/the-puppy-we-kept?lang=en</link><guid isPermaLink="true">https://flydogai.com/bruce/blog/the-puppy-we-kept</guid><description>The names, the driving-dog joke and the trust behind Bruce. From early exploration involving Grok Bot to ice-blue C and native SVG, keeping the puppy’s story together.</description><dc:creator>The Bruce team</dc:creator><category>design</category><pubDate>Thu, 05 Nov 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Good work, Bruce: a name, a companion, a puppy&lt;/h1&gt;&lt;p&gt;The names, the driving-dog joke and the trust behind Bruce. From early exploration involving Grok Bot to ice-blue C and native SVG, keeping the puppy’s story together.&lt;/p&gt;&lt;p&gt;The Bruce team · Published &lt;time datetime=&quot;2026-11-05&quot;&gt;2026-11-05&lt;/time&gt;&lt;/p&gt;&lt;section id=&quot;names&quot;&gt;&lt;h2&gt;Free to fly. Quick to move. A companion beside you.&lt;/h2&gt;&lt;p&gt;In FlyDog, flight means freedom and speed. The dog is a loyal, approachable companion, ready to go with you. We want our tools to have that character too: quick to act, easy to work with, and there when you need them.&lt;/p&gt;&lt;p&gt;Both 飞狗 and FlyDog are short, familiar words. Easy to say and easy to remember, in either language. We want the name to feel natural from the first introduction.&lt;/p&gt;&lt;p&gt;FlyDog is the parent brand. Bruce is the development companion that helps you turn ideas into something real. How the name FlyDog first came to us is a story for another day.&lt;/p&gt;&lt;/section&gt;&lt;section id=&quot;bruce-c1&quot;&gt;&lt;h2&gt;Bruce took the manual-driving test. Or so the joke goes.&lt;/h2&gt;&lt;p&gt;Bruce brings Bruce Lee to mind for many people. Agility and real skill are a happy coincidence, and qualities we hope to earn. The name stayed with us because of a dog viewers call “Bruce” in Chinese dog-driving memes, and a running joke about its C1 manual-driving test. A dog sitting earnestly at the wheel, with a relaxed human beside it: that image stayed with us.&lt;/p&gt;&lt;p&gt;The driving-test claim is a joke. What we liked was the feeling of trust: someone takes care of the complicated controls, and you can finally breathe. Our founder kept a screenshot of the clip as it circulated. We wanted to keep the thought behind it, too.&lt;/p&gt;&lt;ul class=&quot;article-sources&quot;&gt;&lt;li&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1nN411G7V9/&quot; rel=&quot;noopener noreferrer&quot;&gt;A Chinese post using the driving-test joke · 2023&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1wD4y1g7rF/&quot; rel=&quot;noopener noreferrer&quot;&gt;A “You’ve got this, Bruce!” video post · 2023&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.sina.cn/news/detail/5254032233070765.html&quot; rel=&quot;noopener noreferrer&quot;&gt;“Good work, Bruce” used for an adventure companion · 2026&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/section&gt;&lt;section id=&quot;good-boy-bruce&quot;&gt;&lt;h2&gt;Good work, Bruce.&lt;/h2&gt;&lt;p&gt;“Good work, Bruce.” “You’ve got this, Bruce.” Simple words, with both encouragement and the comfort of trusting a companion. Similar expressions appear on clips of dogs at motorcycle handlebars or out on adventures. The footage varies; the feeling is easy to understand.&lt;/p&gt;&lt;p&gt;We imagine a brave puppy that moves forward and welcomes difficult challenges. We hope Bruce can take on more of development’s manual controls: installing agents, configuring environments, coordinating sessions, launching scripts, and the small chores that pull you away from an idea. You choose the direction. The tools should gradually handle more of the fiddly work of getting there, making the experience feel more like driving an automatic.&lt;/p&gt;&lt;p&gt;That is where we want to go. Each small task made easier brings us closer to a companion you can rely on. And another reason to say: Good work, Bruce.&lt;/p&gt;&lt;/section&gt;&lt;section id=&quot;first-drafts&quot;&gt;&lt;h2&gt;The puppy came before today’s SVG.&lt;/h2&gt;&lt;p&gt;Grok Bot contributed to the early brand and icon exploration in September. Tools including Cursor also helped implement the artwork and integrate it into the apps. We went back and forth between V, play, and code-bracket ideas before keeping the puppy in profile, wearing goggles. Choosing an image, redrawing it, and putting it into an app were distinct jobs.&lt;/p&gt;&lt;p&gt;These eight recovered studies bring that early work back into view. Some directions were briefly used; others remained alternatives in the archive. The numbers make them easier to browse, rather than setting out a strict order of creation.&lt;/p&gt;&lt;p&gt;The later ice-blue C addressed a problem with the existing puppy: it needed to stand out at small sizes. Codex then rebuilt the selected image as a native SVG. That vector refinement continues the earlier work, and gives us another reason to keep the first attempts alongside it.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;6 September 2026: V, play, and code brackets were compared and revisited within the same day.&lt;/li&gt;&lt;li&gt;9 September 2026: the goggled profile entered the apps. Its silhouette connections were revised, and we selected a more detailed puppy image.&lt;/li&gt;&lt;li&gt;12 September 2026: the approved character was preserved while the area outside the rounded plate was cleaned up.&lt;/li&gt;&lt;li&gt;5 November 2026: we selected ice-blue C. Codex subsequently completed this round of native SVG reconstruction.&lt;/li&gt;&lt;/ul&gt;&lt;/section&gt;&lt;section id=&quot;explore&quot;&gt;&lt;h2&gt;Start by making the possibilities visible&lt;/h2&gt;&lt;p&gt;This refinement round began with an open question: could Bruce use a new symbol inspired by FlyDog&amp;#39;s geometric language? We explored a folded B, twin wings, a negative-space B and a pilot cue side by side. Each offered a direction, but none became the final mark.&lt;/p&gt;&lt;p&gt;The useful result was seeing the existing identity more clearly. Bruce&amp;#39;s puppy already had an expression: a left-facing profile, two ears, a tilted head, goggles and a violet badge. Together they created familiarity. A complete-looking new symbol did not by itself justify giving up those recognition cues.&lt;/p&gt;&lt;/section&gt;&lt;section id=&quot;keep&quot;&gt;&lt;h2&gt;Return to the familiar puppy&lt;/h2&gt;&lt;p&gt;The feedback clarified the brief: use the existing puppy as the reference and improve it from there. We could keep the character we liked while addressing the difficulty of seeing it clearly in use.&lt;/p&gt;&lt;p&gt;Its dark fur sat close to the original plate, and details merged when reduced. The task narrowed from establishing a new identity to separating foreground and background, adjusting the face&amp;#39;s presence and improving small-size recognition. Ears, goggles, expression and badge became constraints to preserve rather than decorations to replace.&lt;/p&gt;&lt;/section&gt;&lt;section id=&quot;choose&quot;&gt;&lt;h2&gt;Measure A, B and C on the same scale&lt;/h2&gt;&lt;p&gt;We compared large artwork with actual 16, 24, 32 and 48px use cases, on both light and dark interface backgrounds. Detail in a large image cannot stand in for clarity in a small icon. The three studies tested continuity, a different plate color and stronger foreground/background separation.&lt;/p&gt;&lt;p&gt;C became the final choice. A pale ice plate lifted the dark outline and gave the face, goggles and badge clearer layers. This was a decision based on a practical use problem. The other originals remain evidence of exploration rather than alternative official identities.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;A / Teal continuity: keep the original teal character, strengthen the outline and enlarge the face. Familiar, but the dark fur still sits close to its plate.&lt;/li&gt;&lt;li&gt;B / Indigo exploration: a more vivid indigo-and-cyan combination, while structure inside the dark plate still relies on bright edges.&lt;/li&gt;&lt;li&gt;C / Ice separation: distinguish the pale plate from dark structural lines so the familiar puppy remains easier to recognize at small sizes.&lt;/li&gt;&lt;/ul&gt;&lt;/section&gt;&lt;section id=&quot;vector&quot;&gt;&lt;h2&gt;Make every line a real curve&lt;/h2&gt;&lt;p&gt;After choosing C, the next requirement concerned the file itself: do not simply put the picture inside an SVG. Wrapping a PNG does not create editable shapes that remain vector geometry when enlarged.&lt;/p&gt;&lt;p&gt;We fitted the original contours and color regions in layers, preserving the face, ears, goggles, collar and chevrons while removing edge speckles and tiny surface artifacts. The full mark consists of eighteen editable paths and six native gradients, is about 14KB, and embeds no bitmap.&lt;/p&gt;&lt;p&gt;The plate uses a consistent ice gradient and rounded outline. Websites, shared interface components and vector-capable platforms use the native SVG. Required PNG, ICO and ICNS resources are exported from that master with each platform&amp;#39;s safe areas, transparency and mask requirements. Identity changes have one clear source.&lt;/p&gt;&lt;/section&gt;&lt;section id=&quot;sizes&quot;&gt;&lt;h2&gt;Put the full mark and sidebar glyph into their actual sizes&lt;/h2&gt;&lt;p&gt;The full mark is checked at 16, 24, 32, 48 and 64px on light and dark backgrounds. The value of vector art includes preserving structure at its target size. The illustrated article also offers a sliding overlay of the original and vector on the same canvas and at the same scale.&lt;/p&gt;&lt;p&gt;The Agent sidebar needs a different density. We extracted the same puppy&amp;#39;s ears, goggles and friendly profile into a six-path monochrome companion: 20px for the normal stripe and 16px for compact mode. Its color follows the interface state, with strokes proportioned to neighboring tools.&lt;/p&gt;&lt;p&gt;The companion glyph and full-color mark have different presentation jobs but identify the same Bruce. The shared component and website workbench demo use the same geometry, keeping one concept from acquiring different symbols at each entry point.&lt;/p&gt;&lt;/section&gt;&lt;section id=&quot;materials&quot;&gt;&lt;h2&gt;Keep the result together with the reasoning&lt;/h2&gt;&lt;p&gt;The official full mark is bruce-mark.svg; its Agent companion is bruce-agent.svg. The design archive also retains four early symbols, the previous puppy, original A/B/C images, large and small comparisons, design notes and a file manifest. The originals are historical evidence; current identity resources follow the SVG master.&lt;/p&gt;&lt;p&gt;Keeping the process explains why the puppy stayed, why we chose a light plate and why the sidebar needed its own glyph. Developers and their agents can follow the problem, constraints, comparisons and decisions, while media introductions have a complete account to draw on.&lt;/p&gt;&lt;p&gt;FlyDog is the parent brand and Bruce its product. We keep a familiar character while making its identity clearer across the website, applications and toolbars.&lt;/p&gt;&lt;/section&gt;&lt;p&gt;&lt;a href=&quot;https://flydogai.com/bruce/brand?lang=en&quot;&gt;Explore the full brand story and design archive&lt;/a&gt;&lt;/p&gt;</content:encoded></item>
<item><title>Distance, not time: investigating terminal scrolling</title><link>https://flydogai.com/bruce/blog/distance-not-time?lang=en</link><guid isPermaLink="true">https://flydogai.com/bruce/blog/distance-not-time</guid><description>Seven rounds of measurements could not explain a heavy scroll. The turning point was measuring how far the view moved for the same gesture.</description><dc:creator>Yang Yong’an + Claude Code</dc:creator><category>interaction</category><pubDate>Thu, 05 Nov 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Distance, not time: investigating terminal scrolling&lt;/h1&gt;&lt;p&gt;Seven rounds of measurements could not explain a heavy scroll. The turning point was measuring how far the view moved for the same gesture.&lt;/p&gt;&lt;p&gt;Yang Yong’an + Claude Code · Published &lt;time datetime=&quot;2026-11-05&quot;&gt;2026-11-05&lt;/time&gt; · Originally recorded &lt;time datetime=&quot;2026-09-21&quot;&gt;2026-09-21&lt;/time&gt;&lt;/p&gt;&lt;section id=&quot;the-feeling&quot;&gt;&lt;h2&gt;“It does not follow my hand” is not yet a diagnosis&lt;/h2&gt;&lt;p&gt;The original report was specific: a full-screen TUI inside Bruce scrolled smoothly under inertia, yet felt heavy during repeated changes of direction. The reference was a familiar IDE terminal. We interpreted the problem as slow response and started measuring latency.&lt;/p&gt;&lt;p&gt;This article adapts a development record from September 2026. Its numbers belong to the device, versions and fixtures used then; they are not performance promises for every terminal or computer. The useful part is the method for turning an accurate physical description into measurements that distinguish possible causes.&lt;/p&gt;&lt;/section&gt;&lt;section id=&quot;good-numbers&quot;&gt;&lt;h2&gt;Healthy numbers, the wrong explanation&lt;/h2&gt;&lt;p&gt;We checked writing, transport, program responses, parsing, frame timing and the glyph atlas. Some improvements were real: shorter batching windows, better scheduling and preventing a blocked write from holding up other work. None explained the consistent heavy feeling.&lt;/p&gt;&lt;p&gt;The fixture mattered too. A minimal browser page, an empty newly started TUI and the native window someone is using are different scenes. We added a development evaluation channel into the actual window, so a request could inspect live state and return a result. Only then could we identify the path taken by a real gesture.&lt;/p&gt;&lt;p&gt;One trace showed the terminal moving its own scrollback. The gesture never entered the path we had been optimizing, which sends wheel reports to the program. Establishing who owns the gesture should come before optimizing its handling.&lt;/p&gt;&lt;/section&gt;&lt;section id=&quot;travel&quot;&gt;&lt;h2&gt;The turning point was a simpler comparison&lt;/h2&gt;&lt;p&gt;The developer repeated the gesture in another terminal and described the difference more precisely: it was not taking too long; it was not traveling far enough. Time and distance finally became separate questions. A responsive system can still feel sluggish when its displacement gain is wrong.&lt;/p&gt;&lt;p&gt;A recorded three-second gesture contained 165 events totaling roughly 1002px, averaging about 6.1px per event. The relevant path converted pixels into rows and multiplied events below 50px by an additional 0.3. A trackpad split the gesture into many small steps, nearly all of which were reduced. At the measured row height of about 15px, roughly 66 reports were expected, but only about 20 arrived.&lt;/p&gt;&lt;p&gt;This affected wheel reports sent to the full-screen application, not every kind of scrolling. When two paths are mixed together, healthy latency measurements cannot establish whether the distance is correct.&lt;/p&gt;&lt;/section&gt;&lt;section id=&quot;fix-the-path&quot;&gt;&lt;h2&gt;Apply the correction to the path that needs it&lt;/h2&gt;&lt;p&gt;Our first compensation targeted the wrong path. It amplified already healthy scrollback and produced overshoot. The corrected condition was narrower: the event must go to the program, use pixel mode, and fall within the original range affected by the reduction. This snippet preserves only the core decision used at the time.&lt;/p&gt;&lt;p&gt;This is not a global sensitivity increase. Larger mouse steps did not receive the same reduction and must not be amplified with them. We also turned equal total travel with different event partitions into regression cases and submitted the issue and a proposed fix upstream at the time. Local behavior still needed its own verification.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;const sensitivity = toProgram &amp;amp;&amp;amp; pixelMode &amp;amp;&amp;amp; Math.abs(deltaY) &amp;lt; 50
  ? configured / 0.3
  : configured;&lt;/code&gt;&lt;/pre&gt;&lt;/section&gt;&lt;section id=&quot;takeaways&quot;&gt;&lt;h2&gt;Leave the next investigation a shorter route&lt;/h2&gt;&lt;p&gt;The durable result was more than a scroll correction. Access to the actual window, instruments that report their own failures and a habit of comparing the same gesture can help the next developer or Agent avoid applying correct numbers to the wrong scene.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Identify the surface and processing path that own the event.&lt;/li&gt;&lt;li&gt;Check both elapsed time and displacement when something feels unresponsive.&lt;/li&gt;&lt;li&gt;Record the device, version, content and foreground state with the measurement.&lt;/li&gt;&lt;li&gt;Turn the decisive comparison into a regression case, rather than leaving only a feeling of improvement.&lt;/li&gt;&lt;/ul&gt;&lt;/section&gt;</content:encoded></item>
<item><title>Where did the mouse-up go? Investigating a missing click</title><link>https://flydogai.com/bruce/blog/the-missing-release?lang=en</link><guid isPermaLink="true">https://flydogai.com/bruce/blog/the-missing-release</guid><description>A light tap failed, while a firm press restored it. Following the event chain revealed a missing release before the page, and a recovery policy that shaped everything after it.</description><dc:creator>Yang Yong’an + Claude Code</dc:creator><category>interaction</category><pubDate>Thu, 05 Nov 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Where did the mouse-up go? Investigating a missing click&lt;/h1&gt;&lt;p&gt;A light tap failed, while a firm press restored it. Following the event chain revealed a missing release before the page, and a recovery policy that shaped everything after it.&lt;/p&gt;&lt;p&gt;Yang Yong’an + Claude Code · Published &lt;time datetime=&quot;2026-11-05&quot;&gt;2026-11-05&lt;/time&gt; · Originally recorded &lt;time datetime=&quot;2026-10-02&quot;&gt;2026-10-02&lt;/time&gt;&lt;/p&gt;&lt;section id=&quot;an-incomplete-click&quot;&gt;&lt;h2&gt;A click with one half missing&lt;/h2&gt;&lt;p&gt;The report sounded like a frontend issue: some settings tabs ignored a light trackpad tap, yet a firm press made subsequent taps work again. We had previously blamed similar behavior on React click compatibility and moved some actions to the press. That explanation had never been measured all the way through.&lt;/p&gt;&lt;p&gt;This time we recorded events in the actual window. A failure contained pointerdown and mousedown, then no release. The next action lacked a fresh pointerdown. WebKit still believed the previous button press was held. The question changed from an unreliable click to a missing release somewhere in the pipeline.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;pointerdown → mousedown → …
Next action: mousedown → pointerup → mouseup → click&lt;/code&gt;&lt;/pre&gt;&lt;/section&gt;&lt;section id=&quot;follow-the-event&quot;&gt;&lt;h2&gt;Ask each layer whether it received the event&lt;/h2&gt;&lt;p&gt;First we confirmed that hit testing reached the intended button, without an overlay or drag region intercepting it. We then checked the WebView, window, application and events delivered to the process. The missing mouseUp appeared in none of them. Even the system continued reporting the left button as pressed. Changing the page handler could no longer explain this evidence.&lt;/p&gt;&lt;p&gt;The test machine ran Magnet, a window-management utility with an active event interceptor watching mouse presses and releases. Quitting it changed the comparison: two recorded groups of eight clicks lost two to three releases; twelve clicks of valid duration after quitting it lost none. This was a result for the device and utility state tested then, not a finding about every installation.&lt;/p&gt;&lt;p&gt;Synthetic input needed its own control. A 0ms press-and-release could fail in a minimal native window too, so it could not stand in for a real tap. An instrument defect nearly became our product diagnosis.&lt;/p&gt;&lt;/section&gt;&lt;section id=&quot;recovery&quot;&gt;&lt;h2&gt;The same missing event can produce different experiences&lt;/h2&gt;&lt;p&gt;We compared the same class of events in Chromium and WKWebView. Both could receive incomplete sequences; their behavior after the loss differed. In the measurement, Chromium ended a stale press before accepting a new one, while WebKit could continue with the incorrect state.&lt;/p&gt;&lt;p&gt;Title-bar ownership mattered as well. A native title-bar click need not enter the webpage, whereas Bruce&amp;#39;s web-based drag region involves the WebView. A report that Electron feels better may involve recovery and event boundaries; it does not establish that one framework&amp;#39;s click handler is more reliable.&lt;/p&gt;&lt;/section&gt;&lt;section id=&quot;repair-with-boundaries&quot;&gt;&lt;h2&gt;Repair state without inventing intent&lt;/h2&gt;&lt;p&gt;Bruce cannot repair another application&amp;#39;s event interceptor, but it can maintain press/release pairing at its own WebView entry points. The implementation combines system button-change signals, the event queue and a subsequent real press to decide whether a repair is needed.&lt;/p&gt;&lt;p&gt;Location matters as much as timing. An early repair with a trustworthy target can finish the tap at its original press location. A late repair resets state without carrying out an action the person has abandoned. Releasing at the pointer&amp;#39;s new location can instead give a common ancestor a ghost click.&lt;/p&gt;&lt;p&gt;Follow-up work distinguished pointer drags from native drag sessions and tracked late real releases to avoid settling one action twice. Repairing a missing event should not mean synthesizing clicks indiscriminately across targets.&lt;/p&gt;&lt;/section&gt;&lt;section id=&quot;make-the-evidence-useful&quot;&gt;&lt;h2&gt;Leave a route the next developer can follow&lt;/h2&gt;&lt;p&gt;These notes can help developers working with WebViews, native windows or hybrid interfaces, as well as Agents investigating on their behalf. A specific repair may change with platform versions. Observability and controlled comparisons remain useful.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Separate hit testing, press, release, dispatch and state recovery.&lt;/li&gt;&lt;li&gt;Check whether each layer received the event, and stop blaming boundaries already ruled out.&lt;/li&gt;&lt;li&gt;Keep failing and successful controls, with the synthetic-input conditions recorded.&lt;/li&gt;&lt;li&gt;Confirm the target window before an instrument posts input.&lt;/li&gt;&lt;li&gt;Reset stale state without completing an intent the person has already abandoned.&lt;/li&gt;&lt;/ul&gt;&lt;/section&gt;</content:encoded></item>
<item><title>Why one data store has one Agent</title><link>https://flydogai.com/bruce/blog/one-store-one-agent?lang=en</link><guid isPermaLink="true">https://flydogai.com/bruce/blog/one-store-one-agent</guid><description>A session appeared on the desktop but not on the phone. Two parallel services led us back to ownership, discovery and explicit client context.</description><dc:creator>Bruce</dc:creator><category>engineering</category><pubDate>Thu, 05 Nov 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Why one data store has one Agent&lt;/h1&gt;&lt;p&gt;A session appeared on the desktop but not on the phone. Two parallel services led us back to ownership, discovery and explicit client context.&lt;/p&gt;&lt;p&gt;Bruce · Published &lt;time datetime=&quot;2026-11-05&quot;&gt;2026-11-05&lt;/time&gt; · Originally recorded &lt;time datetime=&quot;2026-10-28&quot;&gt;2026-10-28&lt;/time&gt;&lt;/p&gt;&lt;section id=&quot;two-different-answers&quot;&gt;&lt;h2&gt;Two entry points that each looked healthy&lt;/h2&gt;&lt;p&gt;During one investigation, the resident Agent and a development window&amp;#39;s Agent served the same data directory in parallel. The window searched a presumed port, found nothing there and started another service. The phone&amp;#39;s entry point belonged to the service that had arrived first. A new desktop session never appeared on the phone, with no obvious error on either side.&lt;/p&gt;&lt;p&gt;Adding another refresh could not explain the situation. The entry points had found different service instances. The October 2026 fix started with a more basic rule: ownership of shared state must be explicit.&lt;/p&gt;&lt;/section&gt;&lt;section id=&quot;store-identity&quot;&gt;&lt;h2&gt;The boundary is a data store, not the entire device&lt;/h2&gt;&lt;p&gt;Bruce now uses the data directory as the ownership boundary. One Agent holds a store. A second launch for that store discovers the existing holder, says who is serving and steps aside. Clients no longer need to guess a fixed port independently.&lt;/p&gt;&lt;p&gt;Separate stores may have separate Agents. This is useful isolation, particularly for tests, which must not mix their fixtures with a person&amp;#39;s task list. A rule allowing only one process on an entire computer would erase that useful distinction.&lt;/p&gt;&lt;/section&gt;&lt;section id=&quot;lock-and-record&quot;&gt;&lt;h2&gt;A lock establishes ownership; a record enables discovery&lt;/h2&gt;&lt;p&gt;The implementation separates two jobs. An operating-system file lock establishes who may serve the state, and the Agent acquires it before touching the store. The lock is released when its holder exits. A file remaining on disk must not be mistaken for a lock still being held.&lt;/p&gt;&lt;p&gt;After claiming ownership, the Agent records its process, listening address and startup information. It writes the complete record to a temporary file before renaming it, so readers do not see half a record. Other entry points read that record to locate the serving instance.&lt;/p&gt;&lt;p&gt;A record can outlive its process, so finding it does not establish that the service is online. The caller must still check that the recorded address answers. Keeping ownership, discovery and availability distinct makes failures easier to locate.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;One data store
  Lock     → Who may serve this state
  Record   → Where to find the holder
  Response → Whether it is available now&lt;/code&gt;&lt;/pre&gt;&lt;/section&gt;&lt;section id=&quot;context&quot;&gt;&lt;h2&gt;A shared service still needs explicit project context&lt;/h2&gt;&lt;p&gt;Finding the service is only the first step. A window, phone or other client must identify the project, session and requester. The service cannot infer which directory a phone means from the most recently active window.&lt;/p&gt;&lt;p&gt;This separates the interface from the work state. Clients express their current context; the service owns the tasks, terminals and records it holds. Changing entry points should not accidentally switch to a different body of work. Access remains governed by the relevant permissions and connection configuration.&lt;/p&gt;&lt;/section&gt;&lt;section id=&quot;regression-and-lifetime&quot;&gt;&lt;h2&gt;Test the boundary and explain the lifetime&lt;/h2&gt;&lt;p&gt;Regression tests cover a second claim on the same store being rejected, ownership becoming available after release, and independent stores remaining independent. Those conditions are more precise than one successful-looking connection.&lt;/p&gt;&lt;p&gt;Closing a client and restarting the Agent are different operations. A store lock and discovery record do not keep terminal processes alive across an Agent restart. Continued work still depends on an online host, service settings and the actual process lifetime. Lossless handoff requires separate design and verification.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Confirm that both clients mean the same data and service before investigating synchronization.&lt;/li&gt;&lt;li&gt;Use explicit ownership to prevent launch races, and discovery records instead of port guesses.&lt;/li&gt;&lt;li&gt;Declare the project, session and requester rather than inferring them from recent interface activity.&lt;/li&gt;&lt;li&gt;Give tests isolated storage and preserve the person&amp;#39;s working sessions.&lt;/li&gt;&lt;/ul&gt;&lt;/section&gt;</content:encoded></item></channel></rss>
