Writing, Rewriting and Proofreading Are Different Operations
Writer, Rewriter and Proofreader all accept text and return text-related results.
That surface similarity makes it tempting to place them behind one function:
const output = await improveText(input);
The name hides the most important property of the operation.
How much is it allowed to change?
A writer may create content that did not exist. A rewriter may change expression while preserving the underlying message. A proofreader should correct a bounded class of errors while leaving correct content alone.
These are different authority levels.
Writer → create
Rewriter → transform
Proofreader → correct
If we evaluate all three with “Does the output sound good?”, the most fluent system can win by violating the task.
This chapter defines each operation by what it is permitted to alter, then uses Browser AI Observatory to make that authority visible in configuration, traces and fixtures.
1. Begin with the allowed delta
Let $x$ be the input text and $y$ the output.
For writing, $x$ may be an instruction rather than a source passage. Large semantic distance between $x$ and $y$ is expected because the operation generates an artifact.
For rewriting, the output should differ in requested dimensions while preserving protected meaning.
For proofreading, the desired delta is sparse:
This gives us three different failure questions:
| Operation | Primary failure question |
|---|---|
| Writer | Did it satisfy the requested artifact and constraints? |
| Rewriter | Did it change the requested property without changing protected meaning? |
| Proofreader | Did it correct actual errors without introducing unnecessary edits? |
The evaluation target is the delta, not generic output polish.
2. Writer creates an artifact
The Writer API can be configured with properties including tone, format, length, shared context and language expectations.
const writer = await Writer.create({
tone: "neutral",
format: "plain-text",
length: "short",
expectedInputLanguages: ["en"],
expectedContextLanguages: ["en"],
outputLanguage: "en",
sharedContext: "Interface copy for a developer tool."
});
The operation receives a writing request:
const copy = await writer.write(
"Explain why the observatory needs access to the inspected site.",
{
context: "Access is optional and begins only after the developer grants it."
}
);
The request is not prose to preserve. It is a specification to satisfy.
A good evaluation asks:
- Does the output perform the requested communicative job?
- Does it use the selected tone and format?
- Does it stay within a useful length?
- Does it preserve facts supplied by context?
- Does it avoid inventing authority the extension does not have?
- Does it make the next user decision clear?
For interface copy, factual restraint matters more than rhetorical confidence.
3. Shared context and request context have different lifetimes
Writer and Rewriter distinguish context supplied when the object is created from context supplied to an individual operation.
shared context
→ applies across work performed by this configured object
request context
→ applies to one write or rewrite operation
For Browser AI Observatory, shared context might describe the product:
Browser AI Observatory is a DevTools extension for developers. It uses metrics-only capture by default and requires explicit site access for application instrumentation.
Request context might describe the current control:
This message appears beside the button that requests access to
https://example.test.
Combining both into every input string is possible, but it erases provenance and wastes repeated context. Keeping them separate makes the trace easier to interpret and the session configuration easier to compare.
Creation parameters are fixed for the configured object. If the application needs a different tone, length or output language, it should create a new object rather than pretend to mutate the old one.
The observatory enforces that experimentally by clearing session state when the selected API or creation options change.
4. Rewriter transforms an existing artifact
The Rewriter API begins from text that already has meaning.
Its configuration includes transformations such as:
more-formal,as-is, ormore-casualtone;shorter,as-is, orlongerlength;- Markdown, plain text, or preserved format where supported by the contract;
- shared and request-specific context;
- language expectations.
const rewriter = await Rewriter.create({
tone: "as-is",
format: "plain-text",
length: "shorter",
expectedInputLanguages: ["en"],
expectedContextLanguages: ["en"],
outputLanguage: "en"
});
Then:
const result = await rewriter.rewrite(original, {
context: "Preserve that the developer must explicitly grant access."
});
The context identifies a protected proposition. Shortening is successful only if that proposition survives.
This is why output length cannot be the only score. The empty string is maximally short. A rewrite that removes “explicitly” and turns permission into automatic access may be smoother while being wrong.
5. Rewrite evaluation needs invariants
Before running a rewrite, divide the task into variables and invariants.
requested variable
length → shorter
protected invariants
actor → developer
action → grants access
timing → before instrumentation
scope → inspected site
modality → must, not may
The generated output can vary in wording. These relations cannot.
Our canonical fixture starts with:
Before the observatory can receive events from the inspected application, the developer must explicitly grant the extension optional access to that site using the control in the DevTools panel.
Automatic checks require a compact output and reject the word “automatically.” A human rubric asks whether explicit authorization survived.
assertions: [
{ kind: "contains", value: "grant" },
{ kind: "not-contains", value: "automatically" },
{ kind: "max-words", value: 28 },
{
kind: "custom",
rubric: "The rewrite must preserve explicit user authorization."
}
]
The contains check is weak. “No grant is required” would pass it. That is why it remains a supporting structural test rather than the semantic verdict.
6. Proofreader returns evidence about edits
Proofreading is narrower again.
The Proofreader API returns a corrected input and a collection of corrections. The correction records identify ranges in the original text and may include additional information exposed by the current implementation.
const proofreader = await Proofreader.create({
expectedInputLanguages: ["en"]
});
const result = await proofreader.proofread(
"The browser have downloaded the models, but the session are not ready."
);
The important output is not only the final sentence.
It is also the edit set.
original text
↓
correction ranges and replacements
↓
corrected text
A plain-text wrapper that returns only correctedInput discards the information needed to render suggestions, explain changes and detect unnecessary edits.
Browser AI Observatory serializes the complete structured result for its first display. The next refinement will retain the typed result separately from its JSON representation.
7. Correct input is a critical test case
Many evaluation sets contain obvious mistakes because those cases make success easy to see.
A proofreader also needs examples where the correct action is no action.
Input: The session is ready.
Target: The session is ready.
If the system changes session to model session, replaces ready with available, or rewrites the sentence for style, it has exceeded proofreading authority even if the result remains grammatical.
Define:
Measuring only the corrected final string over-rewards aggressive rewriting.
8. Correction ranges create indexing obligations
Proofreader corrections refer to positions in the original input. JavaScript strings use UTF-16 code units for indexing. Human-visible characters and Unicode code points do not always occupy one code unit.
That matters when highlighting a range:
const before = input.slice(0, correction.startIndex);
const error = input.slice(correction.startIndex, correction.endIndex);
const after = input.slice(correction.endIndex);
The application should use the indices according to the platform contract, preserve the original string, and test text containing emoji, combining characters and non-Latin scripts. Converting the input to a code-point array before applying UTF-16 offsets can move the highlight to the wrong place.
The debugger should record the original input hash or captured content mode, correction ranges and corrected result together. Otherwise a later view cannot reconstruct which string the ranges described.
9. One adapter should not mean one evaluator
The observatory uses a shared runtime adapter for all three APIs:
availability
create
run
trace
It uses different fixtures because the success conditions differ.
Writer fixture
Checks that permission copy mentions permission, stays short and does not imply access to all browsing history. Human review verifies that it does not imply permission was already granted.
Rewriter fixture
Checks the requested shortening and preserves explicit authorization as a human-reviewed invariant.
Proofreader fixture
Checks that the structured result contains correctedInput and corrections. Human review verifies both subject–verb agreement errors and detects unrelated edits.
The event sequence is comparable. The scores are not interchangeable.
This is the same rule we used for the API registry:
Normalize mechanics; preserve task semantics.
10. Run the text-authority laboratory
Select Run with Browser AI from Chapter 07 to open:
/tools/ai/browser-ai-from-first-principles/07-chapter/
The laboratory presents one operation selector and three visibly different authority levels:
Writer create a new artifact
Rewriter transform expression; preserve protected meaning
Proofreader correct justified errors only
Changing the selection does more than replace a label. It destroys the previous browser-managed instance, loads the selected API’s creation options, replaces the request or source text, and installs that operation’s evaluation contract. A successful Writer session is not reused as evidence that Proofreader is available.
For every operation, the reader follows the same mechanics:
- inspect the selected global with its exact options;
- explicitly prepare a browser-managed instance;
- run the canonical fixture;
- inspect the generated or structured result;
- compare automatic checks with the unresolved human rubric;
- export the shared Observatory trace.
The common lifecycle makes those runs comparable operationally. The visible authority card and operation-specific event fields prevent the comparison from flattening their meaning.
Writer: permission copy
Writer receives a request rather than prose to preserve. Automatic checks require the output to mention permission, avoid the phrase “all browsing history,” and remain under ninety words. Human review asks whether the copy falsely implies that access has already been granted.
A fluent paragraph can pass all lexical checks and still violate that authority boundary.
Rewriter: preserve authorization
Rewriter receives an existing sentence and a context instruction protecting explicit authorization. Automatic checks require grant, reject automatically, and enforce the requested shorter form. Human review decides whether the rewrite still says that the developer must grant access before instrumentation begins.
Here, improvement without preservation is failure.
Proofreader: justify every correction
Proofreader receives two subject–verb agreement errors. Its result is rendered as formatted JSON so correctedInput and corrections remain inspectable rather than being collapsed into polished text.
Automatic checks establish only that those fields exist. Human review verifies that both errors were corrected and that no unrelated edit was introduced. Correct-input fixtures remain necessary because an eager proofreader can look useful by changing prose that was already valid.
The trace records the requested authority alongside the API and operation:
{
"apiId": "rewriter",
"operation": "rewrite",
"authority": "Transform expression; preserve authority",
"outcome": "completed"
}
That metadata cannot prove semantic preservation, but it makes suspicious deltas diagnosable. A future debugger can flag a large Proofreader delta or a longer shorter rewrite without pretending the heuristic is a final verdict.
The Chapter 07 code is loaded as a separate task module and sends validated events into the shared experience trace. This keeps the growing book interface modular while preserving one export format.
Replay mode contains another deliberate negative result: the reviewed September 2 trace has no Writer, Rewriter or Proofreader events. The laboratory reports the absence instead of borrowing Prompt API evidence.
11. Trace the requested authority
Every run should identify the operation and its requested transformation.
For a rewriter:
{
"apiId": "rewriter",
"options": {
"tone": "as-is",
"length": "shorter",
"format": "plain-text"
}
}
For a proofreader:
{
"apiId": "proofreader",
"options": {
"expectedInputLanguages": ["en"]
}
}
The absence of a length option is meaningful. The proofreader was never authorized to shorten the text.
This gives a debugger a way to flag suspicious deltas even before it understands their full semantics:
- a proofreader changed half the document;
- a
shorterrewrite became longer; - a plain-text request returned Markdown structure;
- a writer exceeded the interface’s display budget.
Those are anomaly signals, not final judgments, but they direct attention to the right runs.
12. Stream without losing the final artifact
Writer and Rewriter can stream output. The panel appends chunks for responsiveness and records chunk timing.
for await (const chunk of writer.writeStreaming(request, options)) {
visibleOutput.append(document.createTextNode(chunk));
traceChunk(chunk);
}
The final assembled output must also be stored as the artifact evaluated by the fixture. Evaluating each chunk independently would mistake incomplete generation for a bad answer.
Cancellation produces a partial artifact. The trace should preserve its size when content policy permits, but the fixture should normally mark the run as not evaluated rather than grading an intentionally interrupted response.
Operational state again precedes behavioral judgment.
13. Product use requires user ownership
Writing assistance affects a user’s voice and intent.
The interface should therefore make the operation visible:
Write a draft
Rewrite selected text
Proofread selected text
One ambiguous “Improve” button conceals how much authority the system will exercise.
Generated text should be proposed rather than silently committed when the change has meaningful consequences. A rewrite view should show what changed. Proofreading should permit correction-by-correction review. Undo should remain available after insertion.
Local execution can reduce the need to transmit draft content to a remote model. It does not remove the user’s need to inspect and control changes.
Conclusion
Writer, Rewriter and Proofreader are separated by authority.
Writer creates an artifact from a request. Rewriter transforms an existing artifact along requested dimensions while preserving invariants. Proofreader corrects a narrow error class and should leave correct text alone.
A shared browser-AI adapter is useful for lifecycle and telemetry. A shared quality score is not. Each operation needs fixtures aligned with its permitted delta, and proofreading needs structured correction evidence rather than only a polished final string.
Chapter 07 now makes those authority levels executable. Readers can move between the three APIs while the interface replaces the options, source/request semantics and checks as one contract—not as three cosmetic modes of an “improve text” button.
The next chapter moves to another pair of APIs that look like a simple pipeline: detect a language, then translate it. The difficult part is not calling them in sequence. It is deciding when the detector knows enough to choose a translator at all.
Sources and further reading
- Chrome for Developers, Writer API.
- Chrome for Developers, Rewriter API.
- Chrome for Developers, Proofreader API.
- Chrome for Developers, Built-in AI do and don’t.