Zerowidth CleanerAll measured guides

CLAUDE WATERMARK REMOVER · PRACTICAL TEST

Why can a cleaned 1.5 still fail a number input?

Our number input retains cleaned 1.5 but reports stepMismatch with its default step. Setting step to any makes that value valid. The marked source is instead sanitized to empty before cleanup, so its optional valid flag is not proof of numeric acceptance.

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

Open the Claude text cleaner · Full measured data

Code-point comparison for Marked decimal

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 were measured separately in Chromium 156 using detached native controls and programmatic value assignment. Node reruns the actual homepage callback and reads the saved browser observations; it does not independently rerun the browser. No form was submitted. 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 decimal
direct string / keep / invisible true
"1.​5"
U+0031 U+002E U+200B U+0035
"1.5"
Removed 1 invisible character
{"anyStep":{"stepMismatch":false,"valid":true,"value":""},"defaultStep":{"stepMismatch":false,"valid":true,"value":""},"number":"NaN"}
{"anyStep":{"stepMismatch":false,"valid":true,"value":"1.5"},"defaultStep":{"stepMismatch":true,"valid":false,"value":"1.5"},"number":"1.5"}
Fraction
direct string / keep / invisible true
"1.5"
U+0031 U+002E U+0035
"1.5"
No selected invisible characters found
{"anyStep":{"stepMismatch":false,"valid":true,"value":"1.5"},"defaultStep":{"stepMismatch":true,"valid":false,"value":"1.5"},"number":"1.5"}
{"anyStep":{"stepMismatch":false,"valid":true,"value":"1.5"},"defaultStep":{"stepMismatch":true,"valid":false,"value":"1.5"},"number":"1.5"}
Integer
direct string / keep / invisible true
"2"
U+0032
"2"
No selected invisible characters found
{"anyStep":{"stepMismatch":false,"valid":true,"value":"2"},"defaultStep":{"stepMismatch":false,"valid":true,"value":"2"},"number":"2"}
{"anyStep":{"stepMismatch":false,"valid":true,"value":"2"},"defaultStep":{"stepMismatch":false,"valid":true,"value":"2"},"number":"2"}
Empty optional
direct string / keep / invisible true
""
""
No selected invisible characters found
{"anyStep":{"stepMismatch":false,"valid":true,"value":""},"defaultStep":{"stepMismatch":false,"valid":true,"value":""},"number":"NaN"}
{"anyStep":{"stepMismatch":false,"valid":true,"value":""},"defaultStep":{"stepMismatch":false,"valid":true,"value":""},"number":"NaN"}

A repaired number can reveal the next condition

The marked decimal contains U+200B between the decimal point and final digit. Assigning it to a number input yields an empty value in this browser. After the actual cleaner removes the marker, assignment retains 1.5, but the default-step record reports stepMismatch true and valid false. This is a meaningful distinction: cleanup makes a decimal available to the control, then a separate step constraint rejects that decimal. More character deletion cannot choose the destination step policy.

Changing the step isolates the cause

The observer captures the default validity first and then sets step to any on the same detached element. The retained 1.5 becomes valid and its mismatch flag becomes false. The plain fraction control behaves identically without cleanup. The integer 2 passes both configurations. These paired results isolate the selected step behavior from decimal conversion or rounding; no code rounds 1.5 to an integer, and the saved valueAsNumber diagnostic remains the decimal string 1.5.

An optional empty result can look successful

The marked source and explicit empty control both have empty element values, valid true and valueAsNumber recorded as NaN. The harness encodes that diagnostic as text so JSON does not silently replace it with null. A valid optional number input therefore does not guarantee that a number was supplied. This experiment has no min, max, required attribute or custom step base. Different attribute combinations require new fixtures rather than treating these four controls as a complete numeric schema.

Test the browser contract at the receiving stage

The browser harness uses programmatic assignment, not keyboard editing or localized number entry. Its validity snapshots and user agent are downloadable. The Node reproducer independently reruns the saved homepage script but consumes recorded browser observations; it is not a replacement browser engine. An earlier numeric-field article tests Number conversion, whereas this page tests a native field constraint after conversion eligibility changes. These are authored examples rather than Claude samples, and a passing number constraint provides no watermark or provenance evidence.

Reproduce this test

For a fresh native-control measurement, save and open reproduce-browser.html in your browser. It embeds the exact fixture observer and saved homepage script, runs locally, and shows its user agent. For the Node cleanup rerun, save reproduce.cjs, tested-app.js and browser-observations.json 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 HTML number-state-(type=number) 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.