# ADR 003: Versioned suggestion contract

- HTML version: https://robbiepalmer.me/projects/agent-first-writing/adrs/003-versioned-suggestion-contract
- Project: Agent-first Writing Editor (https://robbiepalmer.me/projects/agent-first-writing.md)
- Status: Proposed
- Date: 2026-09-04
- Initiatives: Semi-autonomous Software Development (https://robbiepalmer.me/initiatives/semi-autonomous-software-development.md)

# Context

Vale, future classifiers, and contextual models need to report findings through
one review workflow. Free-form model comments cannot reliably anchor a change,
produce a diff, record a decision, or replay an evaluation against a later
editor version.

Local corrections and dependent passage rewrites have different review shapes,
but both need enough provenance to explain where a proposed edit came from.

# Decision

Define a versioned suggestion format before adding another editing layer. Each
local suggestion records:

* the schema version and a stable suggestion ID;
* the producer ID, producer version, and model or rule provenance;
* the source document ID, revision, and SHA-256 hash of its exact UTF-8 bytes;
* a half-open UTF-8 byte span, the source text at that span, and the proposed
  replacement; and
* its category, reason, and optional confidence.

Calculate the suggestion ID from a canonical encoding of the schema version,
producer identity and version, source identity and revision, source hash, span,
category, and replacement. Sort records by start byte, end byte, producer ID,
and suggestion ID so independent runs produce the same order.

Deterministic checks, including Vale, return the same structure. Passage-level
work keeps the same provenance and groups dependent suggestions under one
proposal so the interface can present their combined replacement as a readable
diff. Producers may report overlapping independent suggestions, but the editor
must not apply them together without a new grouped proposal.

Every proposal records a stable proposal ID and an ordered list of its member
suggestion IDs. Use the suggestion sort order above for that list, and calculate
the proposal ID from a canonical encoding of the schema version and ordered
member IDs. A suggestion shown on its own forms a one-member proposal. This
mapping must remain attached to the proposal through review and evaluation.

Record a decision for each proposal with an `accepted`, `rejected`, or `changed`
outcome and its proposal ID. A changed decision stores the replacement the user
chose. Preserve the ordered member IDs, original suggestions, schema version,
producer versions, and source revision so later evaluations compare like with
like.

Before display or application, verify the source hash and that the bytes in the
recorded span equal the recorded source text. Treat a mismatch as a stale-span
conflict: do not apply or silently remap it. Re-run the producer against the new
revision, or create an explicit migrated proposal that retains the old ID as
provenance.

# Consequences

The review interface can combine rule and model producers without discarding their
identity. Decisions become usable evaluation evidence, and source spans make
local changes inspectable.

Schema versioning adds migration work. Byte spans also require adapters at UI
boundaries that use another offset convention, but they keep the persisted
contract unambiguous across producer languages.

---

Markdown index of this site: https://robbiepalmer.me/llms.txt
