A review screen I keep seeing has a generated summary, a confidence score, and a Publish button. The human reviewer can approve the text, reject it, or edit it. What the screen does not show is where the text came from, whether the provider can mark it as synthetic, or how much of the final wording is still the model's.
That is a decision made with missing evidence. The owner is the reviewer, the consequence is a public statement with unclear provenance, and the point of reversibility is very narrow: after Publish, the record is already out.
Recent public conversations about model-text watermarks make this more urgent. They push provenance from a research footnote into a product requirement. But the interface pattern does not need to depend on any one watermark method. It only needs to refuse to publish when the evidence is missing.
I designed a small provenance gate for that moment. It keeps the model output, the edited text, the edit distance, the editor, and any provider provenance metadata in the same review object. The Publish button stays disabled until the missing evidence is named.
The evidence for this workflow is the test plan below: it treats provider provenance as a hard stop and edit distance as a tripwire, so the gate fails for missing decision evidence instead of failing for bad prose.
Here is the core check. It is deliberately boring because the point is that the UI should fail closed, not that it should be clever.
type ProvenanceStatus = 'none' | 'partial' | 'provider_reported';
type ReviewRecord = {
modelText: string;
approvedText: string;
modelId?: string;
origin?: string;
editedByHuman: boolean;
editorId?: string;
editDistance: number; // 0 to 1, where 1 is completely different wording
providerProvenance?: ProvenanceStatus;
providerWatermarkResult?: 'none' | 'reported' | 'not_available';
};
type GateResult = {
allowed: boolean;
reason?: string;
missingEvidence: string[];
};
function canPublish(record: ReviewRecord): GateResult {
const missing: string[] = [];
if (!record.modelId) missing.push('model id');
if (!record.origin) missing.push('output origin');
if (record.editedByHuman && !record.editorId) missing.push('human editor');
if (record.editDistance >= 0.4) missing.push('review of a large post-edit');
if (!record.providerProvenance || record.providerProvenance === 'none') {
missing.push('provider provenance status');
}
if (missing.length > 0) {
return {
allowed: false,
reason: `Publish blocked. Missing evidence: ${missing.join(', ')}.`,
missingEvidence: missing,
};
}
return { allowed: true, missingEvidence: [] };
}
This is not a security control. It is a decision-support control. It makes the human decision legible and reversible before the action.
Next, the review card. I find it helps to turn the gate result into UI fields, not just a boolean.
Model output: recorded
Human edit: reviewed
Provenance: missing
What Publish would do: publish edited text
Stop condition: provider provenance status is missing
If provenance is missing, the button label changes from Publish to Review missing provenance. The disabled state alone is not enough; the reason must be visible next to the control.
Here is the test plan.
| Scenario | Input | Expected UI state |
|---|---|---|
| Original model text, provenance present | editDistance 0, providerProvenance provider_reported | Publish enabled |
| Human made small edit | editDistance 0.2, editorId set | Publish enabled |
| Human made large edit | editDistance 0.55, editorId set | Publish blocked, shows review of a large post-edit |
| Reviewer edited but login missing | editedByHuman true, editorId undefined | Publish blocked, shows missing human editor |
| Provider gives no provenance | providerProvenance none | Publish blocked, shows missing provider provenance status |
| Watermark result not available | providerWatermarkResult not_available | Publish enabled if provenance is present; watermark is advisory, not a hard stop |
Accessibility checks.
- Use an aria-live region to announce the gate reason when state changes.
- Move focus to the missing evidence list when Publish is blocked.
- Do not rely on green or red alone; pair color with text and an icon.
- Keep a keyboard-operable diff view for comparing modelText with approvedText.
- Change the button label programmatically so the accessible name updates.
- Do not hide the Publish button when blocked. Keep it disabled and explain what evidence would unlock it.
Where does a free server fit? The first version of this gate did not need a production model. It needed a fake model output, a few edited variants, and a small way to run the check in a browser or endpoint. I used the free model access and free server option from MonkeyCode, an open-source project, to stand up that harness. Disclosure: This article was prepared as part of MonkeyCode's product outreach.
The outreach materials for this article mention a free 30M-token allowance and a free server option. I treat those as disposable rehearsal capacity, not as a permanent production entitlement. Limits can change, so check the current terms before you plan a load test or a launch. What mattered for this exercise was that I could run the same review flow repeatedly without paying for every failed assertion.
Limitations.
- Provenance metadata is only as good as the provider's claims. If a model does not expose origin or watermark information, the gate blocks by design. That may be the correct behavior for customer-facing text, but it may be too strict for internal drafts.
- Edit distance is a rough proxy. A small wording change can change meaning. Use it as a tripwire, not as a semantic reviewer.
- The gate does not detect bad content. It only detects missing decision evidence. A human still has to read the text.
- Watermark detection can have false positives and false negatives. Do not present a watermark result as proof of human authorship.
- This pattern assumes you have an audit record. If the published text is stored separately from the review record, you lose reversibility.
Who should not use this approach.
A team that only needs internal rough drafts should not add a hard publish gate; it will feel like bureaucracy. A team that must prove human authorship to a regulator needs a certified process, not a UI pattern. A team with no way to capture provider provenance should record that absence rather than pretending the check is complete.
For everyone else, the useful first step is small: log the missing provenance every time Publish would be blocked. That record will tell you whether the model provider, the editing workflow, or the review screen is the actual bottleneck. It will also stop the team from treating a watermark headline as the same thing as an implemented control.
That is the part I keep coming back to. A Publish button should not be a confession that the reviewer lacked evidence. It should be the moment the evidence is finally all in one place.
Top comments (0)