Zerowidth CleanerAll measured guides

CLAUDE WATERMARK REMOVER · PRACTICAL TEST

Why does the second record gain a BOM character in a streaming decoder?

Our continuous decoder suppresses the first record’s UTF-8 BOM but emits the second one as U+FEFF. Independent decoders and repeated completed decode calls suppress each record’s leading BOM. Cleanup of the payload does not define record boundaries.

Tested October 14, 2026 · v24.19.0 · 4 measured runs

Open the Claude text cleaner · Full measured data

Code-point comparison for Marked record

Measured inputs and outputs

These are locally constructed test strings, not evidence that Claude inserts these characters. We executed the saved homepage script snapshot with a minimal DOM harness and compared exact strings. The invisible-checkbox state and dash mode appear in each row. The observations below test this page's specific question. Destination observations run independently on the exact input and cleaned strings in the recorded Node runtime. We make no detector-score or statistical-watermark removal claim.

Before and after observations; full code points and strings are in the JSON download.
Fixture and modeInput string and code pointsOutput and cleaner statusObserved before / after
Marked record
direct string / keep / invisible true
"A​B"
U+0041 U+200B U+0042
"AB"
Removed 1 invisible character
{"recordHex":"efbbbf41e2808b42","streaming":["A​B","A​B",""],"independent":["A​B","A​B"],"reusedNonStreaming":["A​B","A​B"]}
{"recordHex":"efbbbf4142","streaming":["AB","AB",""],"independent":["AB","AB"],"reusedNonStreaming":["AB","AB"]}
Plain record
direct string / keep / invisible true
"AB"
U+0041 U+0042
"AB"
No selected invisible characters found
{"recordHex":"efbbbf4142","streaming":["AB","AB",""],"independent":["AB","AB"],"reusedNonStreaming":["AB","AB"]}
{"recordHex":"efbbbf4142","streaming":["AB","AB",""],"independent":["AB","AB"],"reusedNonStreaming":["AB","AB"]}
Empty record
direct string / keep / invisible true
""
""
No selected invisible characters found
{"recordHex":"efbbbf","streaming":["","",""],"independent":["",""],"reusedNonStreaming":["",""]}
{"recordHex":"efbbbf","streaming":["","",""],"independent":["",""],"reusedNonStreaming":["",""]}
Retained joiner
direct string / keep / invisible true
"A‍B"
U+0041 U+200D U+0042
"A‍B"
No selected invisible characters found
{"recordHex":"efbbbf41e2808d42","streaming":["A‍B","A‍B",""],"independent":["A‍B","A‍B"],"reusedNonStreaming":["A‍B","A‍B"]}
{"recordHex":"efbbbf41e2808d42","streaming":["A‍B","A‍B",""],"independent":["A‍B","A‍B"],"reusedNonStreaming":["A‍B","A‍B"]}

Two complete records are not automatically two decoder streams

For every payload we construct a full record from EF BB BF followed by its UTF-8 bytes. A shared decoder receives that same complete record twice with stream true and is then finalized. Its first result omits the initial BOM. Its second result begins with U+FEFF because those bytes now occur inside the continuous stream. Independent decoders omit both leading BOMs, and a reused decoder with completed nonstreaming calls also omits them both. The marked payload loses only its U+200B before encoding; the second continuous result still gains the authored BOM. Even the empty payload shows an empty first result followed by a single U+FEFF second result. No file loader or unknown source bytes are involved.

State needed within one record can be wrong across separate records

Our existing streaming guide splits a multibyte sequence across chunks of the same input, where retaining state is necessary. Here every supplied buffer is already a complete BOM-prefixed record, and the question is whether the application intends one continuous stream or independent records. The BOM bytes are added by this observer after cleanup, not found in a downloaded Claude file. The earlier BOM and File.text guides concern leading-byte handling at a different loading stage. We do not test alternate encodings, malformed bytes at a boundary or every streaming transport. The observations are limited to default UTF-8 decoding in the saved runtime. A visible editor may hide the emitted U+FEFF, which is why exact strings and hex accompany each row.

Associate decoder lifetime with the intended data lifetime

Define the unit of input before selecting streaming calls. Chunks of one encoded document and complete independent messages require different lifecycle decisions. Finish or recreate decoding as appropriate for that actual contract, and verify the resulting text for every record instead of assuming the payload cleaner determines byte framing. Our reused nonstreaming control is particularly useful: it shows that a new decoder object is not the only way the tested completed calls reset record treatment. The downloadable experiment preserves all three policies side by side without prescribing a protocol. It does not guarantee that removing an emitted U+FEFF is safe in arbitrary content. Retain the original record bytes when diagnosing the stage that introduces it, then test the real transport and decoder behavior before changing meaningful boundaries.

Reproduce this test

Save reproduce.cjs and tested-app.js in the same folder. Run the command below with Node.js. The harness prints its runtime, script SHA-256 and every measured row. Compare those rows with the original record. Using a newer script or runtime creates a new experiment; retain the version information with your rerun.

node reproduce.cjs

Reference and next check

WHATWG Encoding Standard provides the relevant primary definition. The table and fixture analysis are original measurements. For broader inspection, use our Unicode inspector. Read the scope distinction before interpreting cleanup as a watermark result.