CLAUDE WATERMARK REMOVER · PRACTICAL TEST
Why does JSON.stringify omit the note attached to my cleaned-text array?
Our array owns note and 01 properties, but JSON.stringify exports only its indexed element. Spreading the same array into an object includes those properties and changes the export shape. Use an explicit record for metadata.
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 value direct string / keep / invisible true | "AB"U+0041 U+200B U+0042 | "AB"Removed 1 invisible character | {"keys":["0","note","01"],"length":1,"json":"[\"AB\"]","objectJSON":"{\"0\":\"AB\",\"note\":\"retained in memory\",\"01\":\"leading-zero key\"}","hasNote":true}{"keys":["0","note","01"],"length":1,"json":"[\"AB\"]","objectJSON":"{\"0\":\"AB\",\"note\":\"retained in memory\",\"01\":\"leading-zero key\"}","hasNote":true} |
| Plain value direct string / keep / invisible true | "AB"U+0041 U+0042 | "AB"No selected invisible characters found | {"keys":["0","note","01"],"length":1,"json":"[\"AB\"]","objectJSON":"{\"0\":\"AB\",\"note\":\"retained in memory\",\"01\":\"leading-zero key\"}","hasNote":true}{"keys":["0","note","01"],"length":1,"json":"[\"AB\"]","objectJSON":"{\"0\":\"AB\",\"note\":\"retained in memory\",\"01\":\"leading-zero key\"}","hasNote":true} |
| Empty value direct string / keep / invisible true | "" | ""No selected invisible characters found | {"keys":["0","note","01"],"length":1,"json":"[\"\"]","objectJSON":"{\"0\":\"\",\"note\":\"retained in memory\",\"01\":\"leading-zero key\"}","hasNote":true}{"keys":["0","note","01"],"length":1,"json":"[\"\"]","objectJSON":"{\"0\":\"\",\"note\":\"retained in memory\",\"01\":\"leading-zero key\"}","hasNote":true} |
| Numeric text direct string / keep / invisible true | "2"U+0032 | "2"No selected invisible characters found | {"keys":["0","note","01"],"length":1,"json":"[\"2\"]","objectJSON":"{\"0\":\"2\",\"note\":\"retained in memory\",\"01\":\"leading-zero key\"}","hasNote":true}{"keys":["0","note","01"],"length":1,"json":"[\"2\"]","objectJSON":"{\"0\":\"2\",\"note\":\"retained in memory\",\"01\":\"leading-zero key\"}","hasNote":true} |
Ownership does not guarantee appearance in an array export
The observer constructs a one-element array from the supplied text, then adds two enumerable named properties. Object.keys reports 0, note and 01, while length remains one. JSON serialization contains the element alone. The object-spread comparison includes all three keys in a JSON object, and hasNote confirms that the named metadata still exists on the original array. The omission therefore occurs during serialization rather than targeted text deletion. The leading-zero spelling is intentionally different from an ordinary array index, so it does not create a second indexed element.
An empty element still occupies its exported position
The empty-value control exports an array containing an empty string; the array does not disappear and length does not become zero. The numeric-text control retains a quoted 2, because the supplied element remains a string. In the marked fixture, the selected deletion changes interrupted AB to AB in both export views. The fixed note and leading-zero property remain present in memory at both stages. These controls separate element content, property ownership and export shape instead of treating every missing field as an invisible-character problem.
Give metadata a place in the transport schema
When a payload needs both text items and a note, design an object with explicit fields rather than attaching metadata to an array and expecting the JSON array syntax to carry it. Object spread is shown as a diagnostic comparison, not a recommendation to change an existing API from an array to an object. That change needs a destination schema decision. Our experiment uses no replacer, custom toJSON method, prototype modification or sparse element. It isolates the ordinary array serialization path for one indexed text item and two named properties.
Inspect the exact serialized document
The keys list is evidence about the in-memory value; the json field is evidence about the exported document. Keep both if you are diagnosing a lost annotation after cleanup. The cleaner receives only the text element and never creates the array or its metadata. Earlier collection coverage concerns Map and Set shapes; the new case is an ordinary Array with extra own properties. The authored note is not a real user annotation or a Claude artifact. Successful character cleanup cannot establish preservation of an application transport format, and these results do not measure model-level watermarks.
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
ECMAScript SerializeJSONArray 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.