Guides

AI proposals and the export check

Show unchecked AI results as „KI-Vorschlag“ in the Freigabestatus, list what to look at before a Typed block is exported, and register your own check source.

An AI result is a Typed block that the model wrote. Two things tell that a block still waits for a person: the block has a Derivation made by the model, and nobody has changed or checked it since. The editor derives both facts on every read. Nothing new is stored: the decision of a person is the Prüfung (a record pinned to a content hash), and the status is the derived Freigabestatus.

vorgeschlagen

A block is an AI proposal when all of these hold (isAiProposal(editor, node), @skaile/forge-editor/composites):

  • its Derivation names the model as author: method starts with ai: (as derive and composite-fill write it), or activity.agent is set;
  • outputHash is not empty and equals the SHA-256 of the block's body Markdown now, so nobody has edited the body since the model wrote it.

The Freigabestatus of a block, per kind, gains one value:

Per kindMeaning
gepruefta record of this kind exists at the current hash
veraltetrecords of this kind exist, none at the current hash
vorgeschlagenno record of this kind, and the block is an AI proposal
offenno record of this kind, and the block is not an AI proposal

The order of the cases is fixed: a record decides first. An edit of the body ends the proposal, so the kind reads offen, not vorgeschlagen: the person who edits has taken the text over, and the Prüfung that follows pins their version. A block that is vorgeschlagen counts as open in freigabeOf: the document is freigegeben only after every such block has a Prüfung.

Where it shows:

WhereWhat
Chip after the title„KI-Vorschlag“ (warning) while a kind is vorgeschlagen and none is veraltet; „Prüfung veraltet“ and „Geprüft“ keep their rules
Section „Prüfstand“the kind line reads „KI-Vorschlag“

Only blocks whose Block type lists the pruefung capability have a Freigabestatus. PruefungKindStatus is 'geprueft' | 'veraltet' | 'offen' | 'vorgeschlagen'; a host that switches over it adds the case.

The export check

Before a Typed block is exported as Markdown the editor looks for what a reader of the file could trip over. exportIssuesOf(editor, node) (@skaile/forge-editor/composites) returns one ExportIssue per finding, for node and every Typed block inside it, in document order:

interface ExportIssue {
  /** The Typed block the finding concerns (for a reference chip, the block that holds the chip). */
  blockId: string
  code: 'ai-unchecked' | 'stale-derived' | 'source-missing' | 'unresolved-reference'
  /** Title of that block. */
  title: string
}
CodeWhenLabel
ai-uncheckedthe block is an AI proposal and still waits for a Prüfung (see the check probe below)„KI-Vorschlag ungeprüft“
stale-derivedthe block is Derived and its original changed since it was made„Quelle geändert“
source-missingthe block is Derived and its original is gone„Quelle fehlt“
unresolved-referencea reference chip points at a Block that does not exist„Verweis ohne Ziel“

The check hashes and serialises bodies. It runs when someone exports or opens the inspector section, not for every row.

The export never waits

The block action „Als Markdown exportieren“ (document-export) runs the check first. With one or more findings it shows a warning notice („1 Hinweis“, „n Hinweise“) that points at the inspector, and it exports anyway. The check informs, it never blocks.

The inspector section

The composites bindings add the section „Export-Hinweise“ (id export-check) to the Typed block inspector and the details card, for every Block type that lists the export capability. It lists the findings with their label and the block title, or „Keine Hinweise“. It follows document changes and Prüfung records. ExportCheckSection is exported from @skaile/forge-editor/composites/vue and @skaile/forge-editor/composites/react. A host that builds compositeFeatureCore itself passes the component as CompositesUi.exportCheck; without it the Feature adds no section (the notice stays).

Who tells that a block is checked

ai-unchecked needs to know whether a Prüfung exists, and the composites Feature does not know the pruefung Feature. A Feature that records checks provides a check probe for its editor:

import { provideCheckProbe } from '@skaile/forge-editor/composites'

const unprovide = provideCheckProbe(editor, {
  // true while the block still waits for a check
  awaitsCheck: (_editor, node) => !myChecks.has(node.attrs.id),
  // optional: the answer changed without a document change (the inspector section draws again)
  subscribe: listener => myChecks.subscribe(listener),
})

The pruefung Feature does this for its model: a block awaits a check while any configured kind is not geprueft. A block whose Block type has no kind awaits nothing, so it is never listed as ai-unchecked. Without any probe every AI proposal is ai-unchecked. provideCheckProbe returns the function that removes the probe; call it when the editor goes away.

Copyright © 2026