Zerowidth CleanerAll measured guides

CLAUDE WATERMARK REMOVER · PRACTICAL TEST

Why did sorting my cleaned list also reorder the saved original?

Our sort call changes its input array and returns that same array. toSorted returns the same ordered values in a new array while retaining the input order. The cleaner does not choose between those ownership contracts.

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

Open the Claude text cleaner · Full measured data

Code-point comparison for Marked first item

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 first item
direct string / keep / invisible true
"b​,a,c"
U+0062 U+200B U+002C U+0061 U+002C U+0063
"b,a,c"
Removed 1 invisible character
{"before":["b​","a","c"],"sortResult":["a","b​","c"],"originalAfterSort":["a","b​","c"],"sortReturnsOriginal":true,"toSortedResult":["a","b​","c"],"originalAfterToSorted":["b​","a","c"],"toSortedReturnsOriginal":false}
{"before":["b","a","c"],"sortResult":["a","b","c"],"originalAfterSort":["a","b","c"],"sortReturnsOriginal":true,"toSortedResult":["a","b","c"],"originalAfterToSorted":["b","a","c"],"toSortedReturnsOriginal":false}
Plain unsorted
direct string / keep / invisible true
"b,a,c"
U+0062 U+002C U+0061 U+002C U+0063
"b,a,c"
No selected invisible characters found
{"before":["b","a","c"],"sortResult":["a","b","c"],"originalAfterSort":["a","b","c"],"sortReturnsOriginal":true,"toSortedResult":["a","b","c"],"originalAfterToSorted":["b","a","c"],"toSortedReturnsOriginal":false}
{"before":["b","a","c"],"sortResult":["a","b","c"],"originalAfterSort":["a","b","c"],"sortReturnsOriginal":true,"toSortedResult":["a","b","c"],"originalAfterToSorted":["b","a","c"],"toSortedReturnsOriginal":false}
Already sorted
direct string / keep / invisible true
"a,b,c"
U+0061 U+002C U+0062 U+002C U+0063
"a,b,c"
No selected invisible characters found
{"before":["a","b","c"],"sortResult":["a","b","c"],"originalAfterSort":["a","b","c"],"sortReturnsOriginal":true,"toSortedResult":["a","b","c"],"originalAfterToSorted":["a","b","c"],"toSortedReturnsOriginal":false}
{"before":["a","b","c"],"sortResult":["a","b","c"],"originalAfterSort":["a","b","c"],"sortReturnsOriginal":true,"toSortedResult":["a","b","c"],"originalAfterToSorted":["a","b","c"],"toSortedReturnsOriginal":false}
Duplicates
direct string / keep / invisible true
"b,a,a"
U+0062 U+002C U+0061 U+002C U+0061
"b,a,a"
No selected invisible characters found
{"before":["b","a","a"],"sortResult":["a","a","b"],"originalAfterSort":["a","a","b"],"sortReturnsOriginal":true,"toSortedResult":["a","a","b"],"originalAfterToSorted":["b","a","a"],"toSortedReturnsOriginal":false}
{"before":["b","a","a"],"sortResult":["a","a","b"],"originalAfterSort":["a","a","b"],"sortReturnsOriginal":true,"toSortedResult":["a","a","b"],"originalAfterToSorted":["b","a","a"],"toSortedReturnsOriginal":false}

The order changes on the array passed to sort

The marked list contains b plus U+200B, a and c. Cleanup removes the marker but retains that serialized order. The observer splits the string into an array, saves a value snapshot and calls sort. Its originalAfterSort field is ordered a, b, c and the returned-object check is true. A separate fresh array goes through toSorted and retains b, a, c in originalAfterToSorted while returning a, b, c in another object. Both operations produce the same sorted values here; the important difference is which array still holds the incoming sequence.

An already ordered control can hide the mutation contract

Plain b,a,c has the same ownership results without any cleanup. The already sorted a,b,c reference looks unchanged after either call, yet sort still returns its input object and toSorted returns another object. The duplicate control retains both a entries through both sorting paths and keeps b,a,a only in the source of the copying path. These controls show why checking a single final printed order is insufficient to establish preservation. We record both content and identity. No comparator is supplied, keeping this question separate from the earlier numeric-order comparison.

Retain a stable source for review and export

If an interface needs both original and sorted order, choose a copying operation or explicitly copy the array before an in-place sort. Preserve the serialized source as well when separators and precise spelling matter. The array split in this test uses simple commas and does not parse quoted CSV. Our values are primitive strings, so the demonstration also avoids claims about copying nested objects. The cleaner returns another string and does not create or reorder this list. All array construction and ordering are independent destination operations whose outputs are recorded on the exact source and cleaned strings.

Copying the container does not define every policy

This matrix uses four short lists in the recorded Node runtime. It establishes array identity, retained input order and duplicate counts for these calls; it does not supply a locale comparator, deep copy, stable database order or CSV importer. The existing comparator guide explains why default sorting can look numerically wrong. Here every tested ordering is lexicographic and the issue is preserving a review baseline. Run the reproducer before relying on a destination that mutates its inputs. These authored controls do not come from a Claude conversation or demonstrate removal of a statistical watermark or any detector signal.

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

ECMAScript Array.prototype.toSorted 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.