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.
Open the Claude text cleaner · Full measured data
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.
| Fixture and mode | Input string and code points | Output and cleaner status | Observed before / after |
|---|---|---|---|
| Marked record direct string / keep / invisible true | "AB"U+0041 U+200B U+0042 | "AB"Removed 1 invisible character | {"recordHex":"efbbbf41e2808b42","streaming":["AB","AB",""],"independent":["AB","AB"],"reusedNonStreaming":["AB","AB"]}{"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 | "AB"U+0041 U+200D U+0042 | "AB"No selected invisible characters found | {"recordHex":"efbbbf41e2808d42","streaming":["AB","AB",""],"independent":["AB","AB"],"reusedNonStreaming":["AB","AB"]}{"recordHex":"efbbbf41e2808d42","streaming":["AB","AB",""],"independent":["AB","AB"],"reusedNonStreaming":["AB","AB"]} |
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.cjsReference 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.