Zerowidth CleanerAll measured guides

CLAUDE WATERMARK REMOVER · PRACTICAL TEST

Why does a query return A with get but B after Object.fromEntries?

For tag=A&tag=B, get returns A, getAll returns both values, and Object.fromEntries keeps B. Cleanup can turn two differently spelled keys into that duplicate pair. Preserve entries or getAll when repeated parameters matter.

Tested October 24, 2026 · v24.22.1 · 4 measured runs

Open the Claude text cleaner · Full measured data

Code-point comparison for Marked duplicate key

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 duplicate key
direct string / keep / invisible true
"tag=A&ta​g=B"
U+0074 U+0061 U+0067 U+003D U+0041 U+0026 U+0074 U+0061 U+200B U+0067 U+003D U+0042
"tag=A&tag=B"
Removed 1 invisible character
{"entries":[["tag","A"],["ta​g","B"]],"get":"A","getAll":["A"],"object":{"tag":"A","ta​g":"B"}}
{"entries":[["tag","A"],["tag","B"]],"get":"A","getAll":["A","B"],"object":{"tag":"B"}}
Plain duplicate key
direct string / keep / invisible true
"tag=A&tag=B"
U+0074 U+0061 U+0067 U+003D U+0041 U+0026 U+0074 U+0061 U+0067 U+003D U+0042
"tag=A&tag=B"
No selected invisible characters found
{"entries":[["tag","A"],["tag","B"]],"get":"A","getAll":["A","B"],"object":{"tag":"B"}}
{"entries":[["tag","A"],["tag","B"]],"get":"A","getAll":["A","B"],"object":{"tag":"B"}}
Single key
direct string / keep / invisible true
"tag=A"
U+0074 U+0061 U+0067 U+003D U+0041
"tag=A"
No selected invisible characters found
{"entries":[["tag","A"]],"get":"A","getAll":["A"],"object":{"tag":"A"}}
{"entries":[["tag","A"]],"get":"A","getAll":["A"],"object":{"tag":"A"}}
Empty first value
direct string / keep / invisible true
"tag=&tag=B"
U+0074 U+0061 U+0067 U+003D U+0026 U+0074 U+0061 U+0067 U+003D U+0042
"tag=&tag=B"
No selected invisible characters found
{"entries":[["tag",""],["tag","B"]],"get":"","getAll":["","B"],"object":{"tag":"B"}}
{"entries":[["tag",""],["tag","B"]],"get":"","getAll":["","B"],"object":{"tag":"B"}}

The parameter list and exported object disagree

Our first query contains tag=A and a second key interrupted by U+200B with value B. Before deletion, the parsed list and exported object have two distinct names, and getAll for exact tag contains A alone. After deletion, both entries use tag. The list still contains two pairs in the original order, get still reports A, and getAll contains A followed by B. Object.fromEntries represents the repeated name once and stores B. These are four views of one cleaned query, not evidence that the cleaner deleted the first value.

Empty values survive in the list but can disappear in the object

The plain duplicate query reproduces the same first-versus-last distinction without any marker. The single-key reference gives A under every representation. The empty-first-value control is especially revealing: get returns empty text, getAll preserves empty text and B, but the plain object holds B alone. A visually empty first value is still a real list entry. The observer does not call set, delete or sort, so no list-editing operation explains the lost multiplicity. The change occurs when a list of pairs is converted into named object properties.

Choose the transport shape before exporting

If repeated parameters represent multiple selections, retain the entry list or explicitly collect arrays according to the destination schema. Do not assume an ordinary object can store several independent values under an identical property name. Conversely, a destination may intentionally select one value, but it should document whether that means the first or last. Our get and object fields show that those choices already differ here. No network address is requested; URLSearchParams parses a local query string and Object.fromEntries creates only a local object.

This is an export question rather than a universal collision remedy

Earlier query pages inspect exact lookup and replacement; the new failure occurs while exporting an unchanged parameter list. The strings contain ordinary values, not credentials or a real request. Our reproducer does not implement server-side parameter handling, authorization or a multi-value schema. Review those contracts independently before applying cleanup to live request data. Synthetic matching keys provide no evidence about Claude output or a statistical watermark. The exact entry array is the retained evidence for what existed before the object conversion.

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 Object.fromEntries 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.