Zerowidth CleanerAll measured guides

CLAUDE WATERMARK REMOVER · PRACTICAL TEST

Why does a failed text search return the last character after slice?

In our absent XY control, indexOf returns -1 and slice(-1) returns Y. An explicit not-found check returns null instead. Cleanup repairs the marked AB match, but it cannot make the unguarded slicing pattern correct for all inputs.

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

Open the Claude text cleaner · Full measured data

Code-point comparison for Marked separator

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 separator
direct string / keep / invisible true
"A​B"
U+0041 U+200B U+0042
"AB"
Removed 1 invisible character
{"index":-1,"unguarded":"B","guarded":null,"found":false}
{"index":0,"unguarded":"AB","guarded":"AB","found":true}
Present separator
direct string / keep / invisible true
"AB"
U+0041 U+0042
"AB"
No selected invisible characters found
{"index":0,"unguarded":"AB","guarded":"AB","found":true}
{"index":0,"unguarded":"AB","guarded":"AB","found":true}
Absent separator
direct string / keep / invisible true
"XY"
U+0058 U+0059
"XY"
No selected invisible characters found
{"index":-1,"unguarded":"Y","guarded":null,"found":false}
{"index":-1,"unguarded":"Y","guarded":null,"found":false}
Empty input
direct string / keep / invisible true
""
""
No selected invisible characters found
{"index":-1,"unguarded":"","guarded":null,"found":false}
{"index":-1,"unguarded":"","guarded":null,"found":false}

The not-found sentinel is a valid slice argument

The marked AB fixture does not initially contain the exact substring AB because U+200B interrupts it. Its indexOf result is -1. Feeding that number directly into slice returns the last character B; this is a slicing result, not a successful search match. Cleanup removes the interruption, after which indexOf returns zero and both slicing paths return AB. The unmarked AB reference gives the same successful output. Crucially, the XY control remains absent after cleanup: the unguarded path returns Y and the guarded path returns null. That control shows why fixing one marked input does not repair the underlying missing-match handling in application code.

An empty output can hide the same incorrect control flow

The empty-string fixture also has index -1, yet slice(-1) returns an empty string because there are no source characters to select. Looking only at the displayed tail would miss its failed search. We therefore retain index and found alongside both returned values. The guard explicitly checks index below zero rather than testing index for truthiness; a valid match at zero is falsey in JavaScript. Our earlier empty-query page asks whether an empty substring matches every target. Here the search query is the fixed nonempty AB and the issue is interpreting a sentinel as a valid negative offset. We do not test case-insensitive search, normalization or every index-coercion rule.

Branch on the search result before consuming an offset

Retain the exact index returned by the search, handle the absent case, and only then pass the successful nonnegative offset to a slicing operation. Choose the absent-case output according to the receiving application; null here is an explicit demonstration rather than a required universal API design. Do not infer success because a returned fragment looks plausible or because the cleaner removed one character. The harness changes no files or application data and supplies full before-and-after observations. It offers a reproducible way to distinguish failed lookup logic from an unwanted invisible character, while leaving the broader parsing or extraction task to its own clearly stated contract.

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 String.prototype.indexOf 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.