Skip to content

feat(cli): watermark-naturalization program — sanitize, naturalize, metadata forensics - #293

Merged
cukas merged 4 commits into
mainfrom
feat/watermark-naturalization
Aug 18, 2026
Merged

feat(cli): watermark-naturalization program — sanitize, naturalize, metadata forensics#293
cukas merged 4 commits into
mainfrom
feat/watermark-naturalization

Conversation

@cukas

@cukas cukas commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Summary

Ships the watermark-naturalization program in three phases (two commits):

Phase 1 — agon sanitize (deterministic forensics)

  • Scans text for hidden Unicode (zero-width chars, bidi controls, soft hyphens, BOMs, tag chars), homoglyph sequences, and variation selectors
  • Strips/sanitizes on request; clean = no actionable findings is the honesty contract
  • Reported-only channels (variation selectors, homoglyph words) are never silently removed
  • Keyed statistical watermarks are always listed as not assessable — no false "watermark-free" claims

Phase 2 — agon naturalize (sanitize → rewrite → re-scan)

  • Deterministic sanitize pass, then rewrite by a non-author engine (writer ≠ rewriter enforced)
  • Mandatory re-scan with refusal-to-emit if the output is still dirty
  • Word-diff report (reordering reads as unchanged)
  • --min-change lexical-overlap threshold with retry (up to --max-attempts) as a heuristic against Anthropic's announced statistical/lexical watermark

Phase 3 — binary provenance metadata

  • agon sanitize --file scans PNG (tEXt/zTXt/iTXt), JPEG (EXIF/COM/XMP), and SVG for metadata channels
  • --strip removes them; report notes that metadata stripping doesn't address pixel-space watermarks

Review

Six-engine review (agy, claude, codex, minimax, kimi, zai) on the uncommitted diff — blocking findings (duplicate test block, CLI flag parsing for --min-change/--max-attempts, append-only bypass of the change threshold) were fixed in commit 2 before landing.

Verification

  • npm run build
  • npm run kern:coverage
  • npm run kern:self-coverage ✓ (baselines bumped to new actuals)
  • npm run test:ts ✓ (incl. new tests/unit/text-forensics.test.ts + tests/unit/naturalize.test.ts)
  • Live E2E: planted zero-width/homoglyph fixture detected; naturalize run with minimax rewriting claude-authored text, re-scan clean

Notes

cukas and others added 4 commits August 18, 2026 15:01
… + agon naturalize (Phase 2)

Phase 1 (sanitize): deterministic invisible-watermark forensics, no AI.
Detects/cleans zero-width chars, bidi overrides, tag chars, homoglyph
substitution, whitespace stego. --detect is report-only (exit 1 on
actionable findings). Honesty contract: clean = no actionable findings;
variation selectors/homoglyph words report-only; keyed statistical
watermarks always listed as not assessable.

Phase 2 (naturalize): sanitize → ONE non-author engine rewrites
(writer ≠ rewriter, enforced) under a meaning-preservation brief →
mandatory re-scan with refusal-to-emit on dirty output → word-level
diff report. Live-verified: AI tells rewritten, facts/structure intact,
51% lexical change, re-scan exit 0.

Co-Authored-By: agon (KERN) <292465531+KERN-Agon@users.noreply.github.com>
Co-Authored-By: agon (KERN) <292465531+KERN-Agon@users.noreply.github.com>
…rk-naturalization program

Wire the program into the interactive surface (previously CLI + `agon call` only):

- intent.kern: /sanitize and /naturalize join SLASH_COMMANDS with usage
  hints; parseSlashCommand maps them to { type, input } intents, leaving
  flag parsing (--detect/--metadata/--strip/--out/--in-place and
  --engine/--author/--min-change/--max-attempts) to the handlers.
- intent-orchestration.kern: /sanitize runs inline (deterministic, no
  engine dispatch); /naturalize runs as a background job like /forge.
- handlers/naturalize.kern (new): handleSanitize renders scan/strip
  results as native TUI tables + headers; handleNaturalize runs the full
  sanitize → non-author rewrite → re-scan pipeline via runNaturalize with
  spinner, word-diff summary, and the honesty contract intact (residual
  statistical watermarks always reported not-assessable).
- handlers/index.ts: barrel export.
- self-coverage baseline bumped to actuals (+1 blocked handler from the
  new kern file).

Gates: build (202/202 kern compile + tsc -b --force), kern:coverage
206/206, kern:self-coverage green, test:ts green.

Co-Authored-By: agon (KERN) <292465531+KERN-Agon@users.noreply.github.com>
…watermark modes

- Fix kern-guard kern-duplicate-symbol: readStdin was defined in both
  sanitize.kern and naturalize.kern; moved to a single source of truth
  in packages/cli/src/kern/blocks/stdin.kern, imported by both commands.
- README: add sanitize/naturalize rows to the mode-picker table and a
  "Sanitize & Naturalize (watermark removal)" section under Core Modes.

Co-Authored-By: agon (KERN) <292465531+KERN-Agon@users.noreply.github.com>
@cukas
cukas merged commit c3d97ce into main Aug 18, 2026
2 checks passed
@cukas
cukas deleted the feat/watermark-naturalization branch August 18, 2026 15:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant