<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Eugen</title>
    <description>The latest articles on DEV Community by Eugen (@3ugen).</description>
    <link>https://dev.to/3ugen</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F798978%2F0d0e8ff6-ea41-4202-a4f7-fbc4368f6e6e.jpeg</url>
      <title>DEV Community: Eugen</title>
      <link>https://dev.to/3ugen</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/3ugen"/>
    <language>en</language>
    <item>
      <title>Styled Unicode Breaks Character Counters</title>
      <dc:creator>Eugen</dc:creator>
      <pubDate>Sun, 23 Aug 2026 17:36:52 +0000</pubDate>
      <link>https://dev.to/3ugen/styled-unicode-breaks-character-counters-fg1</link>
      <guid>https://dev.to/3ugen/styled-unicode-breaks-character-counters-fg1</guid>
      <description>&lt;p&gt;A 76-character bio, styled once, no longer has one useful length.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;𝐚𝐞𝐬𝐭𝐡𝐞𝐭𝐢𝐜&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;                          &lt;span class="c1"&gt;// 9   code points (and 9 graphemes here)&lt;/span&gt;
&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;                               &lt;span class="c1"&gt;// 18  UTF-16 units — what String.length counts&lt;/span&gt;
&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TextEncoder&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;     &lt;span class="c1"&gt;// 36  UTF-8 bytes  — what storage counts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three defensible answers, factor of four on one word. I found this while auditing a counter beside field limits I do not own. The counter reported code points; the destination's unit was unknown. That is already enough to make a green "safe" verdict dishonest.&lt;/p&gt;

&lt;p&gt;Styled letters from the Mathematical Alphanumeric Symbols block live in the Supplementary Plane: &lt;strong&gt;1 code point = 2 UTF-16 units = 4 UTF-8 bytes&lt;/strong&gt;. If you display one number next to a limit documented only as "characters", you are making a claim about a unit the destination never published.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a real bio line does
&lt;/h2&gt;

&lt;p&gt;Same 76-code-point line, four styles. Measured, not recalled:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;style&lt;/th&gt;
&lt;th&gt;code points&lt;/th&gt;
&lt;th&gt;UTF-16&lt;/th&gt;
&lt;th&gt;UTF-8 bytes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;plain&lt;/td&gt;
&lt;td&gt;76&lt;/td&gt;
&lt;td&gt;76&lt;/td&gt;
&lt;td&gt;76&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bold &lt;code&gt;𝐀&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;76&lt;/td&gt;
&lt;td&gt;134&lt;/td&gt;
&lt;td&gt;250&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Script &lt;code&gt;𝒜&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;76&lt;/td&gt;
&lt;td&gt;117&lt;/td&gt;
&lt;td&gt;225&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Small Caps &lt;code&gt;ᴀ&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;76&lt;/td&gt;
&lt;td&gt;76&lt;/td&gt;
&lt;td&gt;155&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A code-point counter calls Bold "76". The same text occupies 134 UTF-16 units or 250 UTF-8 bytes. If the destination budgets one of those units, a verdict based on another is meaningless. Documentation that says only "characters" does not resolve it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest UI: three states, not a green number
&lt;/h2&gt;

&lt;p&gt;Measure all three. If the destination explicitly publishes a byte limit, enforce it. If it publishes a character limit without naming the unit, keep bytes visible but do not compare them to the same number: byte and character budgets are not interchangeable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;measure&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;codePoints&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;utf16&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TextEncoder&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// ok    — fits by code points and UTF-16; bytes are reported separately&lt;/span&gt;
&lt;span class="c1"&gt;// risk  — fits by code points, not by UTF-16: the field decides, and it did not say&lt;/span&gt;
&lt;span class="c1"&gt;// over  — too long even by the most generous count&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;codePoints&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;over&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;utf16&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;risk&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ok&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;risk&lt;/code&gt; is the honest answer when the platform has not told you the unit. The only way to name the unit is to paste a known-length styled string into the live field and see where it cuts. Until then, do not draw a green bar.&lt;/p&gt;

&lt;h2&gt;
  
  
  The next failure is truncation, not the count
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;.slice()&lt;/code&gt;, &lt;code&gt;.substring()&lt;/code&gt;, and &lt;code&gt;[0..n]&lt;/code&gt; on a JavaScript string cut at UTF-16 boundaries. An odd index inside a Supplementary character keeps half a surrogate pair. Many encoders and renderers replace that lone surrogate with U+FFFD:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;𝐚𝐞𝐬𝐭𝐡𝐞𝐭𝐢𝐜&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;// "𝐚𝐞𝐬\uD835"  → renders 𝐚𝐞𝐬�&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Truncate on code points — and on grapheme clusters if you allow combining marks (&lt;code&gt;a̶&lt;/code&gt; is two code points):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cutCodePoints&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cutGraphemes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Intl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Segmenter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;granularity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;grapheme&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;segment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;g&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;segment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Where the rest of the measurements live
&lt;/h2&gt;

&lt;p&gt;I did not want a second guessed number in the UI, so I ran this against a shipping 22-style catalog rather than a handful of examples. Length units, the NFKC rule that flattens 17 of those 22 styles, coverage holes per family, and how &lt;code&gt;/\d/&lt;/code&gt; in JavaScript disagrees with Python on &lt;code&gt;𝟐𝟎𝟐𝟔&lt;/code&gt; are written up with the reproducing code in &lt;a href="https://fontius.app/skills/unicode-text-fields/SKILL.md" rel="noopener noreferrer"&gt;Unicode text in fields you do not own&lt;/a&gt; (CC0).&lt;/p&gt;

&lt;p&gt;Checklist I actually use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ ] Count in all three units; never show one number as if it were the limit
[ ] Truncate on code points or graphemes, never on UTF-16 indices
[ ] Normalise before validating; know whether your storage path normalises
[ ] Test rendering on a device you did not compose the text on
[ ] Disclose coverage holes instead of silently substituting another letter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The catalog those numbers were measured on is the type-style-copy tool at &lt;a href="https://fontius.app" rel="noopener noreferrer"&gt;fontius.app&lt;/a&gt;. The skill is the document; the tool is just where the 22 families live.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Research disclosure:&lt;/strong&gt; The catalog measurements come from focused local tests against the 22 shipped styles. I did not establish which unit any named third-party platform uses; that requires testing the field itself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI-assistance disclosure:&lt;/strong&gt; AI tools assisted with research navigation, code verification, and drafting. I reviewed the evidence and take responsibility for the claims and conclusions.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>unicode</category>
      <category>webdev</category>
    </item>
    <item>
      <title>An Emoji Made Me Audit Byte Offsets</title>
      <dc:creator>Eugen</dc:creator>
      <pubDate>Wed, 12 Aug 2026 16:00:00 +0000</pubDate>
      <link>https://dev.to/3ugen/an-emoji-made-me-audit-how-ai-code-editors-handle-bytes-24ki</link>
      <guid>https://dev.to/3ugen/an-emoji-made-me-audit-how-ai-code-editors-handle-bytes-24ki</guid>
      <description>&lt;p&gt;I was building my own harness for solving engineering tasks when an emoji broke&lt;br&gt;
one of my assumptions.&lt;/p&gt;

&lt;p&gt;The file was not malformed. There was no exotic exploit payload. The fixture&lt;br&gt;
contained a perfectly ordinary Unicode string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Rendered:    A😀éB
Code points: U+0041 U+1F600 U+0065 U+0301 U+0042
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It looks like four user-visible characters. In this exact NFD fixture, the&lt;br&gt;
zero-based start offset of &lt;code&gt;B&lt;/code&gt; is 4 Unicode code points, 5 UTF-16 code units in&lt;br&gt;
JavaScript, and 8 UTF-8 bytes.&lt;/p&gt;

&lt;p&gt;That difference is harmless until one component calculates a position and&lt;br&gt;
another component gives that number mutation authority.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Text positions need explicit units. The NFD string above has 4&lt;br&gt;
grapheme clusters, 5 code points, 6 UTF-16 code units and 9 UTF-8 bytes,&lt;br&gt;
while &lt;code&gt;B&lt;/code&gt; begins at zero-based offsets 4 / 5 / 8. Across local runtime probes&lt;br&gt;
and pinned source inspection, I found that safe agent editing needs strict&lt;br&gt;
admission, byte-preserving mutation, and review, tests and settlement bound&lt;br&gt;
to exact source and candidate bytes.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  I was building a task harness, not another chat UI
&lt;/h2&gt;

&lt;p&gt;My goal was a task-oriented development harness:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;conversation and design
  -&amp;gt; frozen task
  -&amp;gt; architect
  -&amp;gt; implementer
  -&amp;gt; tests
  -&amp;gt; reviewer
  -&amp;gt; verified result
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The conversation is useful upstream, but the implementation stage needs a much&lt;br&gt;
stricter contract. Each role should receive only the context and artifacts it&lt;br&gt;
needs, and a reviewer should approve the exact candidate that was tested.&lt;/p&gt;

&lt;p&gt;I used Python to prototype the edit path quickly and was considering TypeScript&lt;br&gt;
for more of the lifecycle. The Unicode fixture was a tripwire for the boundary&lt;br&gt;
between them. Its total length has four equally correct descriptions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;4 grapheme clusters
5 Unicode code points
6 UTF-16 code units
9 UTF-8 bytes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing was corrupted. I caught this in a local, model-free test—not in a&lt;br&gt;
customer outage or a breach. But it made me ask how far the mechanism could&lt;br&gt;
travel, so I reconstructed the customer and adversarial scenarios below.&lt;/p&gt;
&lt;h2&gt;
  
  
  The first reconstructed failure needed no malicious input
&lt;/h2&gt;

&lt;p&gt;Imagine a customer gives the harness this small configuration file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Deploy 🚀
timeout = 30
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The requested change is boring: set the timeout to &lt;code&gt;60&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Suppose the TypeScript orchestration layer computes a range from a JavaScript&lt;br&gt;
string and the Python or Rust edit layer consumes the same number as a UTF-8&lt;br&gt;
byte offset. Every position after the rocket now refers to different data in&lt;br&gt;
the two components.&lt;/p&gt;

&lt;p&gt;The best outcome is a clean range-boundary refusal. An ordinary failure is an&lt;br&gt;
anchor miss or an off-by-one edit. The dangerous outcome is that a valid but&lt;br&gt;
different adjacent token is changed while the preview was calculated in the&lt;br&gt;
other coordinate system.&lt;/p&gt;

&lt;p&gt;This customer story is a reconstruction, not an incident report. The measured&lt;br&gt;
fact underneath it is the zero-based &lt;code&gt;4 / 5 / 8&lt;/code&gt; start-offset split in the&lt;br&gt;
fixture above.&lt;/p&gt;
&lt;h2&gt;
  
  
  One edit crosses several representations
&lt;/h2&gt;

&lt;p&gt;An agent edit crosses several representations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;model JSON
  -&amp;gt; runtime string
  -&amp;gt; decoded source text
  -&amp;gt; matched span
  -&amp;gt; candidate
  -&amp;gt; diff shown to a reviewer
  -&amp;gt; bytes written to disk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every arrow can change the meaning of “the same text.”&lt;/p&gt;

&lt;p&gt;The repository ultimately stores bytes. JavaScript strings are indexed as&lt;br&gt;
UTF-16 code units. Python strings are indexed by code points. A UI usually wants&lt;br&gt;
grapheme clusters. A fuzzy matcher may normalize text before comparing it.&lt;/p&gt;

&lt;p&gt;Those are all valid choices in the right layer. The problem begins when a value&lt;br&gt;
created in one coordinate system is consumed as another.&lt;/p&gt;

&lt;p&gt;I later isolated the same counting problem in a smaller browser-facing example:&lt;br&gt;
&lt;a href="https://dev.to/3ugen/styled-unicode-breaks-character-counters-fg1"&gt;styled Unicode breaks character counters&lt;/a&gt;&lt;br&gt;
when visible glyphs, code points, UTF-16 units, and UTF-8 bytes diverge.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key takeaway:&lt;/strong&gt; A numeric offset is not a contract until its coordinate&lt;br&gt;
unit is part of the protocol.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Three runtimes, one malformed JSON string
&lt;/h2&gt;

&lt;p&gt;I wrote three small model-free probes and ran this case on Node.js 22.17.0,&lt;br&gt;
Python 3.14.4 and Rust 1.97.0. The fixture was the JSON string &lt;code&gt;"\uD800"&lt;/code&gt;, an&lt;br&gt;
unpaired UTF-16 surrogate.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.rfc-editor.org/rfc/rfc8259.html#section-8.2" rel="noopener noreferrer"&gt;RFC 8259 explicitly warns&lt;/a&gt;&lt;br&gt;
that JSON grammar can carry such a value even though it is not a Unicode scalar&lt;br&gt;
value.&lt;/p&gt;

&lt;p&gt;Here is what I observed:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Boundary&lt;/th&gt;
&lt;th&gt;Observed result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Node &lt;code&gt;JSON.parse&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;produced a one-unit string containing U+D800&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Node &lt;code&gt;Buffer.from(..., "utf8")&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;silently encoded U+FFFD: &lt;code&gt;ef bf bd&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Python &lt;code&gt;json.loads&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;produced a &lt;code&gt;str&lt;/code&gt; containing U+D800&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Python strict UTF-8 encode&lt;/td&gt;
&lt;td&gt;raised &lt;code&gt;UnicodeEncodeError&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Python encode with &lt;code&gt;errors="replace"&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;emitted ASCII &lt;code&gt;?&lt;/code&gt;: &lt;code&gt;3f&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Python &lt;code&gt;surrogatepass&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;emitted &lt;code&gt;ed a0 80&lt;/code&gt;, which is not well-formed UTF-8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rust &lt;code&gt;char&lt;/code&gt; / &lt;code&gt;String&lt;/code&gt; boundary&lt;/td&gt;
&lt;td&gt;U+D800 was not representable; strict UTF-16 conversion failed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;“The JSON parsed” was not a sufficient admission rule in any of the three&lt;br&gt;
runtimes.&lt;/p&gt;

&lt;p&gt;Then, in a standalone Python script, I reimplemented a destructive ordering&lt;br&gt;
pattern visible in one inspected source path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;open target with O_TRUNC
  -&amp;gt; encode candidate as UTF-8
  -&amp;gt; encoding fails on U+D800
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Python probe raised &lt;code&gt;UnicodeEncodeError&lt;/code&gt;, but the target was already empty.&lt;br&gt;
When I encoded the candidate before opening the target, the same error occurred&lt;br&gt;
and the original bytes stayed unchanged.&lt;/p&gt;

&lt;p&gt;That is a small ordering decision with a large difference in failure semantics.&lt;/p&gt;

&lt;p&gt;Here is one realistic way those primitives could compose. An upstream length&lt;br&gt;
limit slices a JavaScript string through the middle of an emoji. JSON still&lt;br&gt;
serializes the remaining surrogate code unit. A Python worker parses it, opens&lt;br&gt;
the destination with truncation, and only then discovers that the candidate&lt;br&gt;
cannot be encoded as strict UTF-8. The result can be an empty file even though&lt;br&gt;
every individual step looked routine.&lt;/p&gt;

&lt;p&gt;I reproduced the slicing, parsing and truncate-before-encode mechanisms, but&lt;br&gt;
not that complete production chain. It is a reconstructed failure scenario and&lt;br&gt;
a regression test I now want, not a claim about a real customer request.&lt;/p&gt;
&lt;h2&gt;
  
  
  Invalid UTF-8 can be repaired without asking you
&lt;/h2&gt;

&lt;p&gt;The next fixture was four bytes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;61 ff 62 0a
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I asked the simulated editor to make an unrelated change: &lt;code&gt;a&lt;/code&gt; to &lt;code&gt;A&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A strict decoder rejected the source in Node, Python and Rust. A lossy decoder&lt;br&gt;
produced this candidate in all three:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;41 ef bf bd 62 0a
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The requested byte changed from &lt;code&gt;61&lt;/code&gt; to &lt;code&gt;41&lt;/code&gt;. The unrelated invalid byte &lt;code&gt;ff&lt;/code&gt;&lt;br&gt;
also became the three-byte encoding of U+FFFD.&lt;/p&gt;

&lt;p&gt;The edit can report success. The output is valid UTF-8. The visible diff may&lt;br&gt;
focus on the requested line. Yet the tool has permanently changed bytes outside&lt;br&gt;
the intended mutation.&lt;/p&gt;

&lt;p&gt;I repeated the fixture with the invalid byte at offset 1001. Validation of only&lt;br&gt;
the first 1000 bytes passed in all three runtimes; full validation failed. This&lt;br&gt;
matters because a byte sample is useful for classification, but it is not proof&lt;br&gt;
that the complete source is valid text.&lt;/p&gt;

&lt;p&gt;This is where my network-security instincts changed the question. If I were&lt;br&gt;
red-teaming this boundary, offset 1001 is exactly where I would place the byte:&lt;br&gt;
just beyond the classifier's evidence window, but still inside the later lossy&lt;br&gt;
decode. That becomes a validation-coverage gap if the sample is treated as full&lt;br&gt;
admission. Sampling itself is not the problem; giving an advisory classifier&lt;br&gt;
authority over bytes it never inspected is.&lt;/p&gt;

&lt;p&gt;Again, no attacker sent this to my system. I constructed the input locally to&lt;br&gt;
test the boundary.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key takeaway:&lt;/strong&gt; A sample can classify input. It cannot authorize mutation&lt;br&gt;
of bytes it never inspected.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Strict UTF-8 is necessary, but it is not format detection
&lt;/h2&gt;

&lt;p&gt;I also tested UTF-16LE.&lt;/p&gt;

&lt;p&gt;A file with the &lt;code&gt;ff fe&lt;/code&gt; BOM was rejected by strict UTF-8, as expected. But a&lt;br&gt;
BOM-less UTF-16LE file containing ASCII-range text passed strict UTF-8 in all&lt;br&gt;
three runtimes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;6f 00 6c 00 64 00 0a 00
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As UTF-8, that is valid text with four embedded NUL characters. As UTF-16LE, it&lt;br&gt;
is simply &lt;code&gt;old&lt;/code&gt; followed by a line ending.&lt;/p&gt;

&lt;p&gt;The BOM-bearing form is not an exotic historical format. Windows PowerShell&lt;br&gt;
has produced UTF-16LE files with a BOM through commands such as &lt;code&gt;Out-File&lt;/code&gt; and&lt;br&gt;
redirection; the&lt;br&gt;
&lt;a href="https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_character_encoding" rel="noopener noreferrer"&gt;PowerShell encoding documentation&lt;/a&gt;&lt;br&gt;
describes the version-dependent behavior. The harder BOM-less fixture above was&lt;br&gt;
constructed for this probe; that PowerShell citation is not evidence that those&lt;br&gt;
commands produced it.&lt;/p&gt;

&lt;p&gt;A UTF-8-only edit tool does not need to support UTF-16. It does need to refuse&lt;br&gt;
an unsupported format before mutation and prove that refusal left the source&lt;br&gt;
unchanged.&lt;/p&gt;

&lt;p&gt;This is the least dramatic scenario and probably the most practical one. A&lt;br&gt;
customer can submit a legitimate PowerShell script created by an older Windows&lt;br&gt;
tool, ask for a one-line change, and violate none of your stated assumptions.&lt;br&gt;
The editor still needs a deterministic answer: preserve the declared encoding&lt;br&gt;
or refuse before effect. Silently guessing and rewriting is not a customer&lt;br&gt;
error.&lt;/p&gt;
&lt;h2&gt;
  
  
  Normalization can change “one match” into “two matches”
&lt;/h2&gt;

&lt;p&gt;These two strings often render identically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NFC: é        -&amp;gt; c3 a9
NFD: e + ◌́   -&amp;gt; 65 cc 81
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;They are canonically equivalent, but they are not byte-identical.&lt;/p&gt;

&lt;p&gt;I placed one of each in a source. The NFD anchor had one exact match. After NFKC&lt;br&gt;
normalization, the same logical anchor had two matches.&lt;/p&gt;

&lt;p&gt;That means normalization did not merely make search friendlier. It changed the&lt;br&gt;
cardinality of an authorized mutation.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key takeaway:&lt;/strong&gt; Exact byte matching owns mutation. Normalized or fuzzy&lt;br&gt;
matching may propose candidates; if it changes zero/one/many cardinality, the&lt;br&gt;
tool reports that fact instead of silently selecting the first result.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://www.unicode.org/reports/tr15/" rel="noopener noreferrer"&gt;Unicode normalization&lt;/a&gt; is valuable.&lt;br&gt;
Making it mutation-authoritative without an explicit contract is the problem.&lt;/p&gt;
&lt;h2&gt;
  
  
  Then I checked real coding-agent implementations
&lt;/h2&gt;

&lt;p&gt;At first I suspected this was mainly a TypeScript/Python problem. Rust strings&lt;br&gt;
cannot contain lone surrogates, so perhaps Rust editors avoided the whole class.&lt;/p&gt;

&lt;p&gt;That hypothesis was wrong.&lt;/p&gt;

&lt;p&gt;I inspected pinned source snapshots and traced decode -&amp;gt; locate -&amp;gt; candidate -&amp;gt;&lt;br&gt;
write paths. I did not run the complete products, so these are bounded source&lt;br&gt;
findings followed by standard-runtime reproductions of the selected primitives.&lt;/p&gt;

&lt;p&gt;The table reports policy and ordering visible at pinned source snapshots. It is&lt;br&gt;
not a list of confirmed product vulnerabilities, incidents or end-to-end runs.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Editor path&lt;/th&gt;
&lt;th&gt;Relevant behavior found in source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/openai/codex/blob/c7a4a7e136d96554e1fc6f66532e6060fd2aaf15/codex-rs/file-system/src/lib.rs#L278-L287" rel="noopener noreferrer"&gt;OpenAI Codex &lt;code&gt;apply_patch&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;strict &lt;code&gt;String::from_utf8&lt;/code&gt; rejects invalid update sources&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/earendil-works/pi/blob/534bcbffb7e1e7551d9ee3572dfeb278e203e493/packages/coding-agent/src/core/tools/edit.ts#L340-L353" rel="noopener noreferrer"&gt;Pi coding-agent edit&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Buffer.toString("utf-8")&lt;/code&gt; creates a lossy text authority&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/langchain-ai/deepagents/blob/a78d7b1744050c3221aab5e0c0300cc5f5bec519/libs/deepagents/deepagents/backends/filesystem.py#L550-L580" rel="noopener noreferrer"&gt;Deep Agents filesystem edit&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;strict source decode; the inspected local write path orders &lt;code&gt;O_TRUNC&lt;/code&gt; before the text write encodes the candidate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/xai-org/grok-build/blob/be713136d2a69080743a3f6b3c72077057e5948f/crates/codegen/xai-grok-tools/src/implementations/grok_build/search_replace/mod.rs#L307-L314" rel="noopener noreferrer"&gt;Grok Standard search/replace&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Rust explicitly chooses &lt;code&gt;String::from_utf8_lossy&lt;/code&gt;; any later effect depends on the rest of its settlement path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/NousResearch/hermes-agent/blob/07ee4a2ec8d3a678248d5e0bdc148a457c782d8d/tools/file_operations.py#L2094-L2102" rel="noopener noreferrer"&gt;Hermes default replace&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;reads through &lt;code&gt;cat&lt;/code&gt;; the default &lt;a href="https://github.com/NousResearch/hermes-agent/blob/07ee4a2ec8d3a678248d5e0bdc148a457c782d8d/tools/environments/local.py#L1532-L1540" rel="noopener noreferrer"&gt;local process transport&lt;/a&gt; decodes with &lt;code&gt;errors="replace"&lt;/code&gt;, while the &lt;a href="https://github.com/NousResearch/hermes-agent/blob/07ee4a2ec8d3a678248d5e0bdc148a457c782d8d/tools/file_operations.py#L1175-L1191" rel="noopener noreferrer"&gt;write path&lt;/a&gt; separately uses temp+rename&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The useful conclusion is not “language X is safe.”&lt;/p&gt;

&lt;p&gt;Language changes which failure modes are easy or impossible. The complete edit&lt;br&gt;
contract decides whether they become file corruption.&lt;/p&gt;

&lt;p&gt;Two Rust editors can choose opposite invalid-UTF-8 policies. A Python editor can&lt;br&gt;
fail closed at decoding and still lose a file because encoding happens after&lt;br&gt;
truncate. An atomic rename can settle a candidate correctly even when that&lt;br&gt;
candidate was already derived from a lossy source.&lt;/p&gt;
&lt;h2&gt;
  
  
  The network-security analogy is parser differential, not packets
&lt;/h2&gt;

&lt;p&gt;I am a network security engineer, so this shape felt familiar. HTTP/1.1 is&lt;br&gt;
defined over octets, and&lt;br&gt;
&lt;a href="https://www.rfc-editor.org/rfc/rfc9112.html#section-2.2" rel="noopener noreferrer"&gt;RFC 9112 warns&lt;/a&gt; that&lt;br&gt;
parsing the message as Unicode too early can create security vulnerabilities.&lt;br&gt;
The structural problem here also resembles a parser differential:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;network path:
octets -&amp;gt; intermediary parser -&amp;gt; security decision -&amp;gt; origin parser

agent edit path:
file bytes -&amp;gt; runtime decoder -&amp;gt; model/reviewer decision -&amp;gt; filesystem writer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I am not claiming that an AI edit bug is HTTP request smuggling. The analogy is&lt;br&gt;
about disagreement at boundaries.&lt;/p&gt;

&lt;p&gt;If the security layer approves representation A while the effect layer writes&lt;br&gt;
representation B, the approval is attached to the wrong object.&lt;/p&gt;

&lt;p&gt;That becomes especially important in a multi-agent harness. A test result on&lt;br&gt;
candidate A cannot authorize candidate B. A reviewer approving a pretty diff&lt;br&gt;
cannot authorize bytes that were regenerated later from a different source&lt;br&gt;
snapshot.&lt;/p&gt;
&lt;h2&gt;
  
  
  Best case, ordinary failure, worst case
&lt;/h2&gt;

&lt;p&gt;The fixtures helped me separate failure levels:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Case&lt;/th&gt;
&lt;th&gt;Best case&lt;/th&gt;
&lt;th&gt;Ordinary unsafe result&lt;/th&gt;
&lt;th&gt;Plausible worst case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;invalid UTF-8&lt;/td&gt;
&lt;td&gt;refuse before effect&lt;/td&gt;
&lt;td&gt;U+FFFD silently replaces unrelated bytes&lt;/td&gt;
&lt;td&gt;corrupted generated/signed fixture with a deceptively small visible diff&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;lone surrogate&lt;/td&gt;
&lt;td&gt;reject/pre-encode before open&lt;/td&gt;
&lt;td&gt;tool returns an encoding error&lt;/td&gt;
&lt;td&gt;silent replacement or an empty target after truncate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mixed BOM/newlines&lt;/td&gt;
&lt;td&gt;preserve untouched byte spans&lt;/td&gt;
&lt;td&gt;whole-file representation rewrite&lt;/td&gt;
&lt;td&gt;broken script, checksum or protocol corpus&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BOM-less UTF-16LE&lt;/td&gt;
&lt;td&gt;typed unsupported-format refusal&lt;/td&gt;
&lt;td&gt;anchor not found among NULs&lt;/td&gt;
&lt;td&gt;lossy transcode or downstream parser disagreement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;emoji offset&lt;/td&gt;
&lt;td&gt;explicit position unit and byte conversion&lt;/td&gt;
&lt;td&gt;off-by-one, rejection or panic&lt;/td&gt;
&lt;td&gt;adjacent identifier/operator/literal edited under a mismatched preview&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NFC/NFD&lt;/td&gt;
&lt;td&gt;exact phase preserves identity&lt;/td&gt;
&lt;td&gt;fuzzy ambiguity&lt;/td&gt;
&lt;td&gt;visually equivalent but byte-distinct target selected first&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;stale source&lt;/td&gt;
&lt;td&gt;digest precondition refuses&lt;/td&gt;
&lt;td&gt;last writer wins&lt;/td&gt;
&lt;td&gt;reviewed candidate overwrites a different source version&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;“Worst case” here is threat modeling, not a claim that I reproduced a production&lt;br&gt;
exploit.&lt;/p&gt;
&lt;h2&gt;
  
  
  I also looked for the iPhone text-crash story
&lt;/h2&gt;

&lt;p&gt;The remembered iPhone examples are real. Apple's&lt;br&gt;
&lt;a href="https://support.apple.com/en-us/102991" rel="noopener noreferrer"&gt;iOS 11.2.6 notes&lt;/a&gt; say that certain&lt;br&gt;
character sequences could cause apps to crash. Apple has also documented&lt;br&gt;
crafted-text CoreText denial-of-service issues, including&lt;br&gt;
&lt;a href="https://support.apple.com/en-us/103075" rel="noopener noreferrer"&gt;CVE-2017-2461&lt;/a&gt; and&lt;br&gt;
&lt;a href="https://support.apple.com/en-us/103029" rel="noopener noreferrer"&gt;CVE-2020-9829&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But I do not use those incidents as evidence for the edit-kernel bug.&lt;/p&gt;

&lt;p&gt;They are an adjacent layer: text shaping and rendering availability. Apple's&lt;br&gt;
public records do not establish UTF-8/UTF-16 offset conversion or file mutation&lt;br&gt;
as the root cause.&lt;/p&gt;

&lt;p&gt;My bounded synthetic test used up to 32,768 combining marks. Node segmentation&lt;br&gt;
plus normalization and Python normalization completed in roughly one&lt;br&gt;
millisecond on this machine. No renderer was invoked, and no historical crash&lt;br&gt;
payload was used.&lt;/p&gt;

&lt;p&gt;The correct conclusion is narrower: complex text is untrusted input for the&lt;br&gt;
diff/report renderer too. Run that renderer with resource bounds and crash&lt;br&gt;
isolation, but do not confuse a renderer test with byte-mutation integrity.&lt;/p&gt;
&lt;h2&gt;
  
  
  The edit contract I now want
&lt;/h2&gt;

&lt;p&gt;I split “safe editing” into three contracts.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Strict admission
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;validate model strings as Unicode scalar values, not only valid JSON;&lt;/li&gt;
&lt;li&gt;define supported source encodings;&lt;/li&gt;
&lt;li&gt;keep full source bytes authoritative;&lt;/li&gt;
&lt;li&gt;treat samples as hints, never full validation;&lt;/li&gt;
&lt;li&gt;bound source, anchor and replacement sizes.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  2. Representation-preserving mutation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;name every position unit at protocol boundaries;&lt;/li&gt;
&lt;li&gt;locate exact byte anchors and distinguish zero, one and many matches;&lt;/li&gt;
&lt;li&gt;construct candidates from byte spans;&lt;/li&gt;
&lt;li&gt;preserve every byte outside declared spans;&lt;/li&gt;
&lt;li&gt;keep normalization, fuzzy search and grapheme UI outside mutation authority.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  3. Identity-bound settlement
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;pre-encode the complete candidate before destructive effects;&lt;/li&gt;
&lt;li&gt;record source and candidate digests;&lt;/li&gt;
&lt;li&gt;bind tests and review to the candidate digest;&lt;/li&gt;
&lt;li&gt;recheck source identity immediately before settlement;&lt;/li&gt;
&lt;li&gt;use atomic replacement where available;&lt;/li&gt;
&lt;li&gt;read back or otherwise identify the settled candidate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In shorthand:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;owned source bytes
  + exact byte spans
  + candidate bytes
  + source/candidate identities
  + freshness check
  + atomic settlement
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bytes alone are not the novel idea. The composition is.&lt;/p&gt;

&lt;h2&gt;
  
  
  What surprised me most
&lt;/h2&gt;

&lt;p&gt;Four things changed my initial mental model:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;JSON validity is not Unicode scalar validity.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rust removes one state, not the need for an explicit decoder policy.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Strict UTF-8 validation does not identify BOM-less UTF-16LE.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Atomic candidate settlement does not prove which source produced it.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The emoji was only the first visible clue.&lt;/p&gt;

&lt;p&gt;The deeper issue was the same one I look for in network systems: where do two&lt;br&gt;
components stop agreeing about the bytes they are authorizing?&lt;/p&gt;

&lt;p&gt;If you maintain an AI code editor, I would add these fixtures before adding&lt;br&gt;
another fuzzy matching strategy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;invalid UTF-8 near the start and beyond any sample window;&lt;/li&gt;
&lt;li&gt;lone surrogates through the real JSON tool boundary;&lt;/li&gt;
&lt;li&gt;emoji positions labeled as UTF-8, UTF-16 and code points;&lt;/li&gt;
&lt;li&gt;NFC/NFD duplicates;&lt;/li&gt;
&lt;li&gt;UTF-8 BOM, mixed CRLF/LF/bare CR and missing final newline;&lt;/li&gt;
&lt;li&gt;BOM and BOM-less UTF-16LE;&lt;/li&gt;
&lt;li&gt;a source change between review and settlement;&lt;/li&gt;
&lt;li&gt;bidi, zero-width and confusable diagnostics in the review UI.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They are tiny inputs. They exercise surprisingly large assumptions.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Research disclosure:&lt;/strong&gt; This article combines pinned source inspection with focused&lt;br&gt;
standard-runtime probes. I did not execute complete third-party coding-agent&lt;br&gt;
products, reproduce an iPhone payload, or establish a CVE in an agent editor.&lt;br&gt;
The three-part contract above is the design target for my harness, not a claim&lt;br&gt;
that its complete live path already enforces it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI-assistance disclosure:&lt;/strong&gt; AI tools assisted with source navigation, probe&lt;br&gt;
implementation, drafting and independent editorial review. I reviewed the&lt;br&gt;
resulting evidence and take responsibility for the claims and conclusions.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.rfc-editor.org/rfc/rfc9112.html#section-2.2" rel="noopener noreferrer"&gt;RFC 9112: HTTP/1.1 message parsing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.rfc-editor.org/rfc/rfc8259.html#section-8.2" rel="noopener noreferrer"&gt;RFC 8259: JSON string and character issues&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tc39.es/ecma262/multipage/ecmascript-data-types-and-values.html#sec-ecmascript-language-types-string-type" rel="noopener noreferrer"&gt;ECMAScript String type&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nodejs.org/api/buffer.html#buffers-and-character-encodings" rel="noopener noreferrer"&gt;Node.js Buffer and character encodings&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://doc.rust-lang.org/std/string/struct.String.html#method.from_utf8_lossy" rel="noopener noreferrer"&gt;Rust &lt;code&gt;String::from_utf8_lossy&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.python.org/3/library/codecs.html#error-handlers" rel="noopener noreferrer"&gt;Python codec error handlers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.unicode.org/reports/tr15/" rel="noopener noreferrer"&gt;Unicode normalization, UAX #15&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.unicode.org/reports/tr29/" rel="noopener noreferrer"&gt;Unicode text segmentation, UAX #29&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.unicode.org/reports/tr55/" rel="noopener noreferrer"&gt;Unicode source-code handling, UTS #55&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.usenix.org/conference/usenixsecurity23/presentation/boucher" rel="noopener noreferrer"&gt;Trojan Source, USENIX Security 2023&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>programming</category>
      <category>agents</category>
    </item>
  </channel>
</rss>
