Zerowidth CleanerAll measured guides

CLAUDE WATERMARK REMOVER · PRACTICAL TEST

Why does deleting every URLSearchParams entry leave one behind?

Our live loop visits a and c but leaves b=2, before and after cleanup. Iterating a snapshot of the keys removes every pair in these fixtures. Cleaning a marked key does not make mutation during traversal safe.

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

Open the Claude text cleaner · Full measured data

Code-point comparison for Marked first 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 first key
direct string / keep / invisible true
"a​=1&b=2&c=3"
U+0061 U+200B U+003D U+0031 U+0026 U+0062 U+003D U+0032 U+0026 U+0063 U+003D U+0033
"a=1&b=2&c=3"
Removed 1 invisible character
{"visited":["a​","c"],"remainingLive":"b=2","snapshot":["a​","b","c"],"remainingSnapshot":""}
{"visited":["a","c"],"remainingLive":"b=2","snapshot":["a","b","c"],"remainingSnapshot":""}
Plain three keys
direct string / keep / invisible true
"a=1&b=2&c=3"
U+0061 U+003D U+0031 U+0026 U+0062 U+003D U+0032 U+0026 U+0063 U+003D U+0033
"a=1&b=2&c=3"
No selected invisible characters found
{"visited":["a","c"],"remainingLive":"b=2","snapshot":["a","b","c"],"remainingSnapshot":""}
{"visited":["a","c"],"remainingLive":"b=2","snapshot":["a","b","c"],"remainingSnapshot":""}
Adjacent duplicates
direct string / keep / invisible true
"a=1&a=2&b=3&c=4"
U+0061 U+003D U+0031 U+0026 U+0061 U+003D U+0032 U+0026 U+0062 U+003D U+0033 U+0026 U+0063 U+003D U+0034
"a=1&a=2&b=3&c=4"
No selected invisible characters found
{"visited":["a","c"],"remainingLive":"b=3","snapshot":["a","a","b","c"],"remainingSnapshot":""}
{"visited":["a","c"],"remainingLive":"b=3","snapshot":["a","a","b","c"],"remainingSnapshot":""}
Single key
direct string / keep / invisible true
"a=1"
U+0061 U+003D U+0031
"a=1"
No selected invisible characters found
{"visited":["a"],"remainingLive":"","snapshot":["a"],"remainingSnapshot":""}
{"visited":["a"],"remainingLive":"","snapshot":["a"],"remainingSnapshot":""}

The remaining pair was never visited

The marked query starts with a plus U+200B, followed by b and c pairs. The cleaner repairs only that first key. In the independent live loop, deleting the current key changes the list being traversed; the recorded visit list contains the first and third keys, with b left in the serialized result. Both the marked and cleaned inputs exhibit this skip. The snapshot branch captures all keys before deleting anything and ends with an empty query. Saving visits and final pairs together locates the issue in traversal rather than in an unsuccessful character deletion.

Duplicates reveal what delete actually removes

The duplicate control contains two adjacent a pairs followed by b and c. Its live loop records a and c and leaves b=3. The snapshot contains a twice, but repeated deletion of that key does not prevent the snapshot branch from removing b and c as well. The single-key control is completely emptied by both approaches. That success is a useful warning about undersized tests: a loop that works on one pair can still fail on a longer list. These observations use key-only delete and do not depend on the optional value argument covered elsewhere.

Capture traversal inputs before destructive edits

When the intent is to remove selected pairs, decide the selection from a stable view and then apply the edits under an explicit duplicate-key policy. A captured key list is enough for our remove-all demonstration. A more selective workflow may need captured key-value pairs or a newly constructed parameter list. Keep the original serialized query so rejected edits can be inspected. The homepage has no query awareness and does not execute either loop. Its output is just the string passed to these separate observers, so a successful cleanup status does not certify the downstream mutation algorithm.

This is local list behavior, not a request result

Four authored query strings are inspected in the recorded Node runtime. No browser navigation or server request is made, and no application-specific meaning is assigned to removing a parameter. The experiment does not claim that every iterator behaves this way or that every query should be emptied. Earlier guides cover repaired duplicate keys and targeted deletion; this matrix covers whether a live traversal reaches every intended pair at all. Reproduce the exact visit arrays before changing a production import. These strings are synthetic controls and supply no evidence about Claude output, watermark detection or authorship.

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 URLSearchParams iteration and mutation 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.