DEV Community

骆驼驼子
骆驼驼子

Posted on

Your AI Rewrite Changed the Date. Here’s How KeepFacts Catches It Locally

AI-assisted rewriting has an awkward failure mode: the prose gets better while a small, important fact gets worse.

A date moves by three days. A pilot cohort shrinks. A link disappears. None of these changes necessarily makes the paragraph sound suspicious.

I built KeepFacts as a deliberately narrow preflight check for that problem. It compares a trusted source with a rewrite, summary, or translation and asks one question:

Were the exact facts that KeepFacts can recognize preserved?

It does not ask an LLM to judge another LLM. It does not search the web or decide whether a claim is true. The comparison is deterministic and runs locally in the browser.

This is an implementation case study, not a customer success story. The text below is KeepFacts’ built-in fictional example, and I am not claiming a user count or an accuracy rate.

The three changes hiding in a fluent rewrite

The trusted source says:

Northstar Studio will release v1.2.0 on September 15, 2026. The initial budget is $30,000, with 100 test users and a 12.5% conversion target. Contact hello@example.com or visit https://example.com/launch.

The sample AI rewrite says:

Northstar Studio plans to release v1.2.0 on September 18, 2026. The initial budget is $30,000, with 80 test users and a 12.5% conversion target. Questions can be sent to hello@example.com.

The rewrite reads naturally, but it contains three review items:

  • September 15 became September 18.
  • 100 test users became 80.
  • https://example.com/launch disappeared.

Other exact items survived: v1.2.0, $30,000, 12.5%, and hello@example.com. The project name can also be entered as must-preserve content and reviewed separately.

That separation matters. KeepFacts does not label the whole rewrite “good” or “bad.” It shows what it extracted, how it paired the two sides, and which items need a human decision.

Why not ask another model to proofread it?

A model can give a useful editorial opinion, but it adds several variables:

  • the same input may not always yield the same output;
  • a reviewer must still discover which source value supports each warning;
  • private text may have to leave the browser;
  • an apparently confident answer can blur extraction, interpretation, and truth checking.

KeepFacts takes a smaller job and makes it inspectable. Given the same source, rewrite, and code version, it produces the same machine comparison. Every result includes the original value and nearby text. There is no prompt to tune and no AI API key to configure.

Deterministic does not mean infallible. It means the behavior is reproducible enough to test, debug, and challenge.

The comparison pipeline

At a high level, KeepFacts performs four steps:

  1. Extract supported fact types from each text.
  2. Normalize only deliberately supported equivalent forms.
  3. Pair source and rewrite facts one to one, using nearby context where needed.
  4. Mark pairs as preserved or needing review, then surface unmatched rewrite facts as additions.

The implementation runs this work in a local Web Worker so a larger comparison does not block editing in the page.

1. Extract typed facts before comparing strings

KeepFacts recognizes a bounded set of forms, including dates, times, money, percentages, measurements, ranges, versions, URLs, email addresses, quoted text, and standalone numbers.

Extraction order matters. A date such as September 15, 2026 contains numbers, but it should be one date fact rather than several unrelated numeric facts. Higher-priority recognizers claim their spans first; lower-priority matches that overlap an existing fact are skipped.

Each extracted fact retains:

  • its type;
  • the raw text;
  • a normalized comparison key;
  • its start and end offsets;
  • nearby source context;
  • a validity flag where applicable.

The offsets and context are what make the final warning reviewable instead of merely statistical.

2. Normalize equivalence without floating-point surprises

Exact comparison still needs normalization. These may be equivalent for the supported rules:

  • 2026-09-15 and September 15, 2026;
  • ¥30,000 and 3万元;
  • 0.001 kg and 1 g;
  • 99.9% and 99.9 percent.

The dangerous shortcut is to parse every numeric value into a JavaScript number. IEEE-754 floating point cannot exactly represent every large integer or small decimal. A fact-preservation checker should not silently round the very values it is supposed to protect.

KeepFacts instead represents a decimal as a BigInt coefficient plus a scale. Conceptually:

9007199254740993 -> coefficient: 9007199254740993, scale: 0
0.0000000000004 -> coefficient: 4, scale: 13
Enter fullscreen mode Exit fullscreen mode

Trailing zeros are removed canonically, and unit or currency factors are applied with integer multiplication while the scales are combined. That makes comparisons such as the following precision-safe:

$9,007,199,254,740,992 != $9,007,199,254,740,993
0.0000000000004 g       != 0 g
0.001 kg                 == 1 g
Enter fullscreen mode Exit fullscreen mode

Normalization is intentionally conservative. For example, currency identity remains part of the key, and URL paths, queries, and fragments are not casually folded together.

3. Match repeated facts by context, one to one

Simple set comparison breaks as soon as a value appears twice.

Consider:

Source:  Alpha has 100 users. Beta has 100 users.
Rewrite: Alpha has 80 users.  Beta has 100 users.
Enter fullscreen mode Exit fullscreen mode

A value-only algorithm sees one 100 on each side and may preserve the wrong occurrence. A greedy nearest-match rule can also fail after paragraphs are reordered.

KeepFacts builds context features for facts of the same type. Those features include the local clause or segment, nearby left/right tokens weighted by distance, and a broader character-bigram fingerprint. It then scores candidate pairs with a strong preference for:

  • a mutually confident context match;
  • an exact normalized value;
  • similar relative position as a stable tie-breaker.

The candidates are resolved with a global maximum-weight one-to-one assignment. In practical terms, one rewrite fact cannot “preserve” two source facts, and grabbing an early local match cannot steal the best partner from a later fact.

For the Alpha/Beta example, the remaining 100 stays attached to Beta, while Alpha’s 100 → 80 becomes the review item. If context is too ambiguous, KeepFacts avoids inventing a confident changed-value pair and leaves the source fact as missing instead.

4. Keep machine findings separate from human decisions

After pairing, an exact valid match is preserved. A confident contextual pair with different normalized values is shown for review as a possible change. A source fact with no usable partner is missing. An unmatched rewrite fact is newly added.

Those automatic results feed a human-review queue, but review decisions do not rewrite machine counts or the extracted-fact retention calculation. This is a useful design boundary: a person can confirm an issue, dismiss it, add a note, or record an expected fix without altering what the comparison actually found.

KeepFacts can export a traceable Markdown report or an explicit session file. These exports are plaintext and may include document text and context, so “local-first” should not be mistaken for “encrypted after download.”

What “local and deterministic” does—and does not—promise

The useful promises are modest:

  • comparison runs in the browser without an AI API call;
  • the app does not automatically upload or persist the working text;
  • the same inputs and code produce the same result;
  • normalized values, source spans, and contexts can be covered by regression tests;
  • warnings can be traced back to the two texts.

The non-promises are just as important:

  • KeepFacts does not verify that the source is true.
  • It does not understand the full meaning of a document.
  • It cannot protect a fact it does not recognize.
  • Its extracted-fact retention percentage is not full-document accuracy or extraction recall.
  • Candidate association uses local wording and relative position, not document-level semantics.
  • Recognition is currently aimed at common Chinese and English forms.
  • Phone numbers, localized decimal separators, written Chinese numerals, and many domain-specific identifiers are not first-class types.
  • High-stakes legal, medical, or financial text still requires qualified human review.

The repository contains regression cases and a separately annotated evaluation corpus, but neither is evidence that every real document is understood. I would rather publish a narrow, falsifiable scope than turn a passing test suite into an accuracy claim it cannot support.

The broader design lesson

The tempting way to build an AI quality tool is to start with a general score. KeepFacts starts with evidence instead:

  • What exact item was in the trusted source?
  • What, if anything, was paired with it in the rewrite?
  • Why was that pairing considered plausible?
  • What context should a person inspect?

That makes the tool less magical and more useful as a release check. It is not a replacement for editorial judgment. It is a small deterministic guardrail before someone publishes fluent text with a quieter, harder-to-notice factual change.

Try the live demo, inspect the source code, or read the project’s validation boundaries.

Change the wording, not the facts.

Top comments (0)