---
title: "ADR-214 historical consequences"
manual: "LLM Foundation"
version: "main"
permalink: "https://docs.typo3.org/permalink/netresearch/nr-llm:adr-214-historical-consequences@main"
source: "Adr/History/Skill214Consequences.rst"
rendered: "2026-11-12T15:41:35+00:00"
---

# ADR-214 historical consequences {#adr-214-historical-consequences}

This appendix preserves the decision and its amendments. For present use,
follow [ADR-214: Approved skill versions instruct, load on demand and run processes](https://docs.typo3.org/permalink/netresearch/nr-llm:adr-214@main) and its current guide links.

## Considered alternatives {#adr-214-alternatives}

**Keep the untrusted frame for every skill and add a separate "tour" record.**
A new record type would hold the steps and be composed as instructions.
Rejected: it creates a second kind of reusable prose next to skills, with its
own source, review and audit, and it still needs the approval, loading and
pinning of this decision for that record.

**A trust flag without version binding.** Mark a skill or a source as trusted
once. Rejected: the next sync or the next backend edit would turn new text into
instructions without anyone reading it. ADR-035 already disables an enabled
skill whose body changed, for the same reason; an instruction is a stronger
privilege than being enabled.

**A checksum over the body alone.** Today's value. Rejected: the frontmatter
carries the tool allow-list, the section heading and the catalogue text, and
an upstream change to it would keep a skill approved and enabled.

**A versioned value in the existing checksum column.** Rejected: the column is
`varchar(64)`, and the manifest fingerprint and the legacy check still need the
body-only hash. A separate column keeps both meanings.

**A revision table with every version.** Rejected in favour of the approval
snapshot: only approved versions are ever composed as instructions or pinned,
and the snapshot holds exactly those. Backend edits are in `sys_history`.
Unapproved synced versions are not kept, as today.

**Checking the provenance threshold once, at approval.** Simpler on paper.
Rejected: a source downgraded after approval would keep instructing, and the
check at compose and resume is one comparison per source.

**Limiting revocation to new runs.** Rejected; see
[item 6](https://docs.typo3.org/permalink/netresearch/nr-llm:adr-214-d6@main).

**Pins carried in the request.** The continuation request listing the pins
it holds, as the second version of this record had it. Rejected: an `@api`
caller could hand in any approved version, unattached, disabled, a process
without its invocation, or an older digest. nr_llm derives the pins from the
predecessor run instead.

**Releasing a process run in the Agent Runs inbox.** The earlier versions of
this record let an inbox release reach the chat through a hand-back, and
every review round since found a new defect in that path: the hand-back
missed new messages, a cancel raced with the release, several approvals in
one run were told apart wrongly, and open points under four-eyes never
closed. Rejected: process runs are decided only on the chat card, and
four-eyes tours are left to a later decision.

**Progress and highlight only in an earlier turn.** Keeping ADR-084's
suspend-before-any-call rule and asking the process skill to report progress
in a turn of its own. Rejected: it depends on the model following prose, and
the decision moves process state into tools precisely to avoid that.

**Loads that last one turn.** Defining a load as valid for the run that made
it, so the model loads again on every chat turn. Rejected: the model would
re-load at whatever digest is current, so a tour could change version
mid-conversation, and every turn would pay the load again.

**Admitting a section only with every current turn kept.** The first version
of this record. Rejected: every chat turn is a new run carrying the whole
history, so a long tour would fail permanently once its history passed the
headroom, although `fit()` could still send it.

**A denial reason per call.** Letting one card carry a reason for each of
several write calls. Rejected for process runs: an approval decides the whole
turn (ADR-132), and one proposal per card is also what the editor reads.
One write per turn keeps the existing turn semantics.

**Let the model load any skill, trusted or not.** Simpler, and the usual shape
of skill systems. Rejected: the model reads page content, and a page could ask
it to load a skill the editor never chose. For an untrusted skill that is
fenced text and mostly cost; for a process it would start a flow by content
injection.

**Deferring model loading, the catalogue and load modes.** Suggested to keep
the first version small. Rejected by the product requirement: a skill must be
reachable both by slash command and by the model matching its description.

**Reply buttons as an input pause, followed by the write approval.** Rejected:
two pauses for one decision, and the variant that collects the answer in the
approval pause is what ADR-134 bans. Approve-means-apply with an enumerated
denial reason is one pause.

**Admin-only TCA for snippets, or a mark on the record.** Rejected; see
[item 8](https://docs.typo3.org/permalink/netresearch/nr-llm:adr-214-d8@main).

**A workflow engine.** Steps, transitions and state as data, with the model
filling in the text. Rejected: it is a new runtime next to the agent loop,
and every process would need a schema change rather than a text change, which
is what the product owner asked to avoid. The run's existing suspensions
(ADR-105, ADR-134) cover the interaction points.

**One extension per process.** Rejected by the product owner: every process
would need a release and a deployment, and editors could not adjust one.

**Processes as prompt snippets.** Snippets are always on, unversioned and
unapproved. Rejected for processes; kept for shared context
([item 8](https://docs.typo3.org/permalink/netresearch/nr-llm:adr-214-d8@main)).

## Consequences {#adr-214-consequences}

● Editors and administrators write a process as a skill in the backend, see
its history in `sys_history` and the diff against the last approved
version, and an approved version guides the chat without a release.

● The instruction privilege is bound to a digest over everything the model
reads of a version and to the source that vouches for it. A changed body or
frontmatter, from a sync or an edit, or a move to another source, loses it
until someone with the approval permission approves again, and every
approval and revocation is in the append-only audit.

● A conversation cannot silently continue with an instruction that was
revoked, detached or replaced: every instruction section is pinned, derived by
nr_llm from the predecessor run, and checked at start, pickup, resume and
continuation. No caller can hand a pin in.

● A loaded skill cannot widen the tool allow-list, a backend author cannot
widen it without an approval, and a resumed run cannot gain tools through the
skill allow-list.

● Instruction sections are no longer cut from the tail and are admitted
against the smallest send `fit()` can reach, so a section that does not fit
is refused visibly and a long tour does not strand on the budget. A tour
whose last run was purged by retention cannot be continued; the editor is
told to start a new conversation.

● Marked snippets, such as an editorial profile, reach the chat on
configurations that opt in, and only in the text an administrator marked.

◐ The governance profiles gain an expectation for the new threshold. Proposed:
`verified` for `local_only`, `controlled_cloud` and `development`,
`first_party` for `enterprise_strict`. `skills.minTrustLevel` keeps its
default and its profile values.

◐ **Upgrade note.** What changes on an existing installation, and what does
not:

-   No approval exists after the upgrade, so every skill keeps the fenced frame.
    Existing attachments migrate to `always`, so no catalogue and no load
    tool appear on existing configurations.
-   The snippet path changes nothing until an administrator marks a snippet and
    opts a configuration in.
-   Legacy rows keep verifying against `body_checksum` until the sync or the
    upgrade wizard writes `version_digest`. The first sync compares the
    new-form digest of the stored fields with the incoming one, so an unchanged
    skill is not disabled and a frontmatter change is caught. The wizard
    disables and audits a synced row whose stored `name`, `description` or
    `allowed_tools` no longer match its `raw_frontmatter`.
-   After the migration, an upstream change to a skill's frontmatter alone
    disables an enabled skill, which today it does not. Expect more re-reviews
    for sources that change their frontmatter.
-   Forced skills' `allowed-tools` now count for the run they are forced onto;
    today they count for nothing. The change goes both ways: when the
    configuration's skills declare nothing, a declaring forced skill narrows the
    run from every tool to its list; when they declare a list, the forced
    skill's tools are added to it. Forcing is an administrator's act in the
    playground, so the widening follows the rule that attachment grants tools.
-   A backend edit of a synced skill's `name` or `description` now fails the
    integrity check, like a body edit; FormEngine shows those fields read-only
    for synced sources.
-   Eight fields gain `exclude => true` ([item 3](https://docs.typo3.org/permalink/netresearch/nr-llm:adr-214-d3@main)). A
    non-admin group that was granted `tables_modify` on the skill tables loses
    write access to them until an administrator grants the fields; the
    extension ships those tables in an admin-only module (ADR-035 item 7), so no
    shipped surface changes.
-   A process run that requests two approval-bound calls in one turn gets an
    error for the second; its reads execute before the card appears, and any
    other call gets an error and does not run. Outside process runs nothing
    changes.
-   In a process tour, a new chat message while a card or an input pause
    waits withdraws it: the waiting run is cancelled and the proposal shown as
    discarded, with no open point. Only "Überspringen" on the card records an
    open point. Runs of ordinary chats, without a process pin, are not
    cancelled and stay releasable in the inbox, as today.
-   Process runs appear in the Agent Runs inbox read-only and cannot be decided
    there, by administrators neither.
-   A process skill cannot be started on a configuration with
    `require_second_approver` set.
-   A backend skill that was never approved contributes a declared empty tool
    list. Attached to a configuration whose other skills declare nothing, it
    turns off every tool except the load tool until a version is approved. That
    is the price of never letting unreviewed text grant or lift a restriction.

✕ **An approved skill is an instruction with the acting user's reach.** It can
steer which tools the model calls and with what arguments, within the run's
allow-list and the user's TYPO3 permissions. The controls that bound it are
the approval of the digest, the allow-list fixed at run start and the global
tool state, and the write approval of ADR-134, which no skill can switch off.
Approving a version is therefore a privileged act and gets its own
permission.

✕ **Content can still ask the model to load a skill.** The catalogue lists
only approved instruction skills that are not processes, and the load tool
re-checks each at load time, so the worst outcome is approved text in the
wrong place, not foreign text as instructions, and no change to the tool
allow-list. Message role remains defence in depth, not a trust boundary
(ADR-036): an untrusted, fenced skill can still influence output, as today.

✕ **Manifest fingerprints cover identifier/body pairs only.** They are not
upstream signatures. Frontmatter is covered by the version digest and approval;
the expected manifest digest still needs a trusted delivery channel.

✕ The work spans nr_llm (digest column and migration, compose path, approval,
load tool, run-start allow-list, pins and continuation, budget charge,
snippet path, denial reason, one-approval rule, guarded cancel, the
process-run decider and four-eyes guards, `pendingTarget()`, the write
completeness on every write tool and the loop-level hook-failure flag on the
write step, progress, highlight and choice builtins,
catalogue API, actor-scoped access check) and nr_mcp_agent (slash
commands, the approval card's three answers, the input pause, naming the
predecessor run, the withdraw order in `queueTurn()`, progress, highlight,
open-point table and listing tool). The process use case works only when
both ship.

## Open questions {#adr-214-open}

-   **The approval permission.** [ADR-117](https://docs.typo3.org/permalink/netresearch/nr-llm:adr-117@main) withdrew nr_llm's own
    capability checkboxes, and [ADR-169](https://docs.typo3.org/permalink/netresearch/nr-llm:adr-169@main) routes record
    management through TYPO3's permission model. Which mechanism carries "may
    approve a skill version" is open; until it is decided, administrators only.
-   **Who may author backend skills.** ADR-035 item 7 keeps skills in an
    admin-only module, and ADR-169 keeps `tx_nrllm_skill` out of non-admin
    management because the sync writes it. Backend-authored skills are written
    by people, so that has to be revisited for the new source. The fields that
    must not travel with `tables_modify` — on the skill and on its source —
    are the eight that [item 3](https://docs.typo3.org/permalink/netresearch/nr-llm:adr-214-d3@main) excludes; whether more belong
    there is part of that revisit.
-   **Names.** `skills.instructionTrustLevel`, `version_digest`, the
    `process` frontmatter key, the load modes, the snippet mark, the
    configuration opt-in, `ApprovalDenialReason`, `cancelIfWaiting()`,
    `pendingTarget()`, `WriteCompleteness` and `hookFailedAfterWrite` are
    working names.
-   **Catalogue size.** Whether the catalogue needs its own byte cap, or the
    admission rule of [item 7](https://docs.typo3.org/permalink/netresearch/nr-llm:adr-214-d7@main) covers it.
-   **Headroom.** Whether 10 % of the budget is the right reserve, measured on
    the first processes that run.
-   **Flipping existing attachments to on_demand.** Whether and when existing
    `always` attachments move to `on_demand`, which changes what an existing
    configuration sends.
-   **Four-eyes tours.** Whether and how a process can run on a configuration
    that requires a second approver: the second person would have to decide on
    a surface other than the chat card, which this decision forbids for
    process runs.
-   **Process completion without a report.** A tour the model abandons without
    a completion report keeps its process pin until the conversation ends or
    the configuration changes. This original question was resolved by [ADR-228](https://docs.typo3.org/permalink/netresearch/nr-llm:adr-228@main):
    `AgentRuntimeInterface::endProcess()` explicitly ends the process pin.
