CLAUDE WATERMARK REMOVER · PRACTICAL TEST
Why does assigning pathname encode ? and # instead of creating a query?
Our pathname setter treats ? and # as path data and preserves the old query and fragment. Constructing a URL from the same string separates them. Cleaning removes U+200B but does not choose between those APIs.
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 pathname direct string / keep / invisible true | "/ab?x=1#frag"U+002F U+0061 U+200B U+0062 U+003F U+0078 U+003D U+0031 U+0023 U+0066 U+0072 U+0061 U+0067 | "/ab?x=1#frag"Removed 1 invisible character | {"setter":{"pathname":"/a%E2%80%8Bb%3Fx=1%23frag","search":"?keep=1","hash":"#old","href":"https://example.com/a%E2%80%8Bb%3Fx=1%23frag?keep=1#old"},"constructor":{"pathname":"/a%E2%80%8Bb","search":"?x=1","hash":"#frag","href":"https://example.com/a%E2%80%8Bb?x=1#frag"}}{"setter":{"pathname":"/ab%3Fx=1%23frag","search":"?keep=1","hash":"#old","href":"https://example.com/ab%3Fx=1%23frag?keep=1#old"},"constructor":{"pathname":"/ab","search":"?x=1","hash":"#frag","href":"https://example.com/ab?x=1#frag"}} |
| Plain delimiters direct string / keep / invisible true | "/ab?x=1#frag"U+002F U+0061 U+0062 U+003F U+0078 U+003D U+0031 U+0023 U+0066 U+0072 U+0061 U+0067 | "/ab?x=1#frag"No selected invisible characters found | {"setter":{"pathname":"/ab%3Fx=1%23frag","search":"?keep=1","hash":"#old","href":"https://example.com/ab%3Fx=1%23frag?keep=1#old"},"constructor":{"pathname":"/ab","search":"?x=1","hash":"#frag","href":"https://example.com/ab?x=1#frag"}}{"setter":{"pathname":"/ab%3Fx=1%23frag","search":"?keep=1","hash":"#old","href":"https://example.com/ab%3Fx=1%23frag?keep=1#old"},"constructor":{"pathname":"/ab","search":"?x=1","hash":"#frag","href":"https://example.com/ab?x=1#frag"}} |
| Encoded question mark direct string / keep / invisible true | "/ab%3Fx=1"U+002F U+0061 U+0062 U+0025 U+0033 U+0046 U+0078 U+003D U+0031 | "/ab%3Fx=1"No selected invisible characters found | {"setter":{"pathname":"/ab%3Fx=1","search":"?keep=1","hash":"#old","href":"https://example.com/ab%3Fx=1?keep=1#old"},"constructor":{"pathname":"/ab%3Fx=1","search":"","hash":"","href":"https://example.com/ab%3Fx=1"}}{"setter":{"pathname":"/ab%3Fx=1","search":"?keep=1","hash":"#old","href":"https://example.com/ab%3Fx=1?keep=1#old"},"constructor":{"pathname":"/ab%3Fx=1","search":"","hash":"","href":"https://example.com/ab%3Fx=1"}} |
| Simple pathname direct string / keep / invisible true | "/ab"U+002F U+0061 U+0062 | "/ab"No selected invisible characters found | {"setter":{"pathname":"/ab","search":"?keep=1","hash":"#old","href":"https://example.com/ab?keep=1#old"},"constructor":{"pathname":"/ab","search":"","hash":"","href":"https://example.com/ab"}}{"setter":{"pathname":"/ab","search":"?keep=1","hash":"#old","href":"https://example.com/ab?keep=1#old"},"constructor":{"pathname":"/ab","search":"","hash":"","href":"https://example.com/ab"}} |
Two APIs interpret the same supplied text differently
The setter observation begins with a fixed URL whose query is keep=1 and fragment is old. Assigning the marked string to pathname encodes its marker and question/hash characters as path data, while preserving those existing components. Constructing a new URL from that same relative string instead treats x=1 as its query and frag as its fragment. After cleanup both lose only the marker from the supplied text; their interpretation difference remains. The plain delimiter control repeats it without any marker. The encoded-question-mark control remains encoded path data under both operations. The simple path control highlights that the setter preserves old components even when the assigned text has none.
A component setter is not whole-URL construction
Our earlier URL pages use constructors to study hostname, ports, dot segments and relative bases. Here the gap is selecting a component mutation API when a caller supplies characters that could otherwise delimit URL structure. The homepage only produces a string; no browser navigation, server routing or HTTP request runs in this observer. The host is the reserved example domain, and all values are invented. We do not test every scheme, opaque paths, malformed percent notation or assignment to search and hash. The table saves pathname, query, fragment and complete URL for both paths so the retained old state cannot be mistaken for newly parsed components.
State whether input is a path component or a URL reference
Before applying a repair, define what the destination expects from the supplied string. If it is a whole relative URL, use the appropriate parsing contract; if it is path data, treat delimiters according to the component setter’s contract and update other components separately when intended. Do not decode encoded delimiters blindly to make a display look familiar. Our encoded control shows that similar visible content can remain in a different component. The downloads reproduce exact local URL objects and their serialized forms, not successful destination requests. Keep the original and inspect all components after an edit, especially when an existing URL carries a query or fragment that the later operation may preserve.
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 URL pathname setter 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.