CLAUDE WATERMARK REMOVER · PRACTICAL TEST
Why does a whole-word regex miss cleaned café but match caféX?
Our /café/u matches cleaned café, but adding word boundaries makes it fail. The bounded expression unexpectedly matches caféX because the transition from é to X is a JavaScript word boundary. Define the intended language boundary separately from literal matching.
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 accented word direct string / keep / invisible true | "café"U+0063 U+0061 U+0066 U+200B U+00E9 | "café"Removed 1 invisible character | {"literal":false,"bounded":false,"positions":[0,3]}{"literal":true,"bounded":false,"positions":[0,3]} |
| Plain accented word direct string / keep / invisible true | "café"U+0063 U+0061 U+0066 U+00E9 | "café"No selected invisible characters found | {"literal":true,"bounded":false,"positions":[0,3]}{"literal":true,"bounded":false,"positions":[0,3]} |
| ASCII final letter direct string / keep / invisible true | "cafe"U+0063 U+0061 U+0066 U+0065 | "cafe"No selected invisible characters found | {"literal":false,"bounded":false,"positions":[0,4]}{"literal":false,"bounded":false,"positions":[0,4]} |
| Accented word plus ASCII direct string / keep / invisible true | "caféX"U+0063 U+0061 U+0066 U+00E9 U+0058 | "caféX"No selected invisible characters found | {"literal":true,"bounded":true,"positions":[0,3,4,5]}{"literal":true,"bounded":true,"positions":[0,3,4,5]} |
A literal success is not a boundary success
The marked fixture inserts U+200B between f and é. Deletion restores the contiguous literal café, so the unbounded expression changes from false to true. The bounded expression remains false. We also record every boundary position: the cleaned string reports positions zero and three, with no boundary after the final é. This evidence pinpoints why appending a boundary assertion changes the answer even though the visible target word is present. Both expressions use Unicode mode, and the input is already precomposed, so missing normalization is not the explanation in this particular test.
A trailing ASCII letter reverses the bounded result
The caféX control contains the same café literal followed immediately by X. Both the literal and bounded expressions return true. Its position list includes the transition at index four between é and X. Thus the expression labeled whole word by a caller accepts this longer visible spelling while rejecting café standing alone. Plain café reproduces the failure without any marker; plain cafe does not match the accented literal at all. We retain all booleans and positions so the demonstration does not depend on calling an accented character a hidden symbol or silently replacing it with ASCII.
Choose boundaries under the destination language contract
A search feature should define what counts as an adjacent letter, mark, digit or punctuation for the languages it supports. Test complete examples under that policy rather than treating a familiar shorthand assertion as a universal linguistic boundary. A segmentation API or a deliberately designed surrounding-character rule may be appropriate, but neither is implemented or validated by this matrix. Preserve the meaningful accent. The earlier Letter-property guide asks which characters a validator consumes; here the consumed literal is fixed and the zero-width assertions decide whether that literal is accepted at its surrounding positions.
The flags and scope stay fixed
We use one JavaScript runtime, no ignore-case flag, four short authored strings and the exact expressions visible in the harness. Position numbers refer to JavaScript string indices, and none of the tested characters needs a surrogate pair. We do not evaluate a document editor, database full-text engine or every natural-language spelling. The homepage removes only the chosen marker and never rewrites the regular expression. This distinction prevents a correct cleanup from being blamed for a destination assertion rule. The results do not attribute the strings to Claude or test any statistical watermark or AI classifier.
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
ECMAScript word-boundary assertion 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.