Zerowidth CleanerAll measured guides

CLAUDE WATERMARK REMOVER · PRACTICAL TEST

Why can a nested text field change inside an Object.freeze result?

Our frozen parent keeps its original child reference, but that child remains unfrozen and its text changes. Object.freeze on the outer record does not freeze the nested object. Snapshot and immutability policies need explicit depth.

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

Open the Claude text cleaner · Full measured data

Code-point comparison for Marked value

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 value
direct string / keep / invisible true
"A​B"
U+0041 U+200B U+0042
"AB"
Removed 1 invisible character
{"outerFrozen":true,"childFrozen":false,"nestedChanged":true,"outerChanged":false,"originalText":"A​B","storedText":"A​B!","sameChild":true}
{"outerFrozen":true,"childFrozen":false,"nestedChanged":true,"outerChanged":false,"originalText":"AB","storedText":"AB!","sameChild":true}
Plain value
direct string / keep / invisible true
"AB"
U+0041 U+0042
"AB"
No selected invisible characters found
{"outerFrozen":true,"childFrozen":false,"nestedChanged":true,"outerChanged":false,"originalText":"AB","storedText":"AB!","sameChild":true}
{"outerFrozen":true,"childFrozen":false,"nestedChanged":true,"outerChanged":false,"originalText":"AB","storedText":"AB!","sameChild":true}
Empty value
direct string / keep / invisible true
""
""
No selected invisible characters found
{"outerFrozen":true,"childFrozen":false,"nestedChanged":true,"outerChanged":false,"originalText":"","storedText":"!","sameChild":true}
{"outerFrozen":true,"childFrozen":false,"nestedChanged":true,"outerChanged":false,"originalText":"","storedText":"!","sameChild":true}
Retained joiner
direct string / keep / invisible true
"A‍B"
U+0041 U+200D U+0042
"A‍B"
No selected invisible characters found
{"outerFrozen":true,"childFrozen":false,"nestedChanged":true,"outerChanged":false,"originalText":"A‍B","storedText":"A‍B!","sameChild":true}
{"outerFrozen":true,"childFrozen":false,"nestedChanged":true,"outerChanged":false,"originalText":"A‍B","storedText":"A‍B!","sameChild":true}

Outer protection and nested mutation coexist

The observer creates a child containing the supplied text and freezes a new parent that points to that child. Reflect.set on the child appends an exclamation mark and returns true. Reflect.set attempting to replace the parent child property returns false. The saved fields confirm outerFrozen true, childFrozen false and sameChild true. Thus the nested content changes while the protected reference itself stays in place. A developer inspecting only the parent freeze status would miss that the stored text is still reachable through a mutable object.

The mutation is independent of selected marker removal

The marked source becomes AB through cleanup, then the observer stores AB! after its deliberate nested edit. Plain AB behaves identically without cleanup. Empty input becomes an exclamation mark, while the retained-joiner fixture retains its original joiner and gains that same punctuation. Each branch creates fresh objects, so no result depends on a child shared with the previous test. The originalText field remains the supplied string; storedText belongs to the separately constructed object. This is object reassignment of a string field, not mutation inside the immutable primitive string.

Choose whether records are live objects or retained snapshots

When a review record is intended to preserve cleaned content, decide whether later code may modify nested data. A shallow freeze alone does not establish a deep immutable snapshot. This experiment deliberately avoids implementing a recursive freezer, copying graph or serialization policy. Such policies have additional questions about cycles, accessors and supported value types. Reflect.set is used so the success flags are explicit rather than depending on whether a failed assignment happens in strict mode. The outer and nested checks document the exact level of protection actually measured.

Keep the homepage operation separate from record storage

The cleaner returns text and does not create or freeze this parent object. Earlier Buffer coverage studies shared byte storage; these fixtures concern nested property updates on ordinary objects. The common need to preserve evidence does not make those mechanisms interchangeable. All values here are synthetic and the appended punctuation is authored observer behavior, not an unexplained product change. A frozen status provides no evidence about Claude provenance, detector acceptance or a statistical watermark. Retain the source and the storage policy together when reviewing post-cleanup edits.

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 Object.freeze 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.