ADR-214 historical context
This appendix preserves the decision and its amendments. For present use, follow ADR-214: Approved skill versions instruct, load on demand and run processes and its current guide links.
Context
Editors ask for guided processes in the chat: optimise the SEO of one page, improve the content of one page. Such a process picks a page, analyses it, gives an overview, then works through the findings one at a time. Each finding gets a proposal with the current and the proposed text and three answers: apply, another variant, skip. Nothing is saved without an explicit confirmation. The editor sees the progress and the affected content element. A skipped finding stays open for the next conversation.
The product owner wants such processes as editable text, not as one extension per process. Skills (ADR-035, ADR-036) are the existing record for reusable prose, and the agent run already has the suspension states a guided flow needs. Two product requirements are fixed: a skill can be started by a slash command and loaded by the model when its name and description match the request; and skills are enabled per skill, like tools. Seven properties of the current code stand in the way.
1. Every skill is data, never an instruction
Skill wraps every admitted skill in the same frame: a guard
preamble that calls the block "UNTRUSTED task-reference DATA" and fence
markers that say
Classes/.
Skill applies that frame to every skill
Skill admits, whatever its
Skill. The trust level decides admission only: a skill below
skills. is not composed at all
(ADR-061, item 1;
ext_;
Skill). The governance profiles expect
community, verified or first_ there, and development expects
untrusted (Governance). A first_
skill is admitted under every profile and still reaches the model as text it is told
not to follow. ADR-036 item 3 puts the block into the first user message and
never into the system role; ADR-061 item 4 added the fence. Both were right for
third-party text fetched from the internet, and neither leaves room for text
the installation itself vouches for.
2. The checksum covers the body only, and a backend edit invalidates it
The source types are single_, repo and marketplace
(Skill), and every fetch goes through
Git, whose host allow-list names only GitHub hosts
(Classes/).
The sync computes body_ from the body alone
(Classes/),
and compose re-verified it the same way (ADR-036 item 6); for a legacy row
without a version digest it still does
(Classes/).
The sync's change test compared that value only, until item 1 below replaced
it with the version digest.
The same sync also rewrites name, description, allowed_,
support_ and raw_ (Skill).
An upstream change to the frontmatter alone is therefore an updated skill:
it stays enabled, and nothing records that the text the model reads changed.
allowed_ decides which tools a run may call, name heads the
composed section, and support_ decides whether asset references are
stripped from the body (Skill).
The body of tx_ is an editable text field, but
body_ is read-only, so a body edit in the backend produces a skill
that is skipped on every call. read is a FormEngine setting only; no
field of tx_ carries exclude, so a DataHandler write by a
group with tables_ would reach trust_, body_ and
enabled (ADR-169, section 1). A sync overwrites the record
in place, so no earlier version is kept.
3. Every attached body is sent in full, and the tail is dropped
Skills attach to a configuration and to a task through MM tables
(ADR-036 item 8). The admin playground can force further skills onto one run
(Run). A queued run persists those as uids only
(Agent), so the worker composes whatever body the record
holds when it picks the run up. A suspended run replays the transcript stored
in Suspended, which already contains the composed text, and
re-reads the forced skills by uid only for the ceiling check
(ADR-165). Nothing records which version of a skill a run
was composed with. Every admitted body is composed in full. When the block
exceeds skills. (default 24 000), whole skills are dropped from
the end, task skills first (ADR-036 item 5). There is no way to list a skill
without sending its body, to load it when it is needed, or to invoke one
explicitly.
4. Snippets do not reach a caller that brings its own system message
Prompt snippets (ADR-031) are appended to the
system_ option. Message returns
the message list unchanged when it already holds a system message
(Classes/), and ADR-031 records
this as intended: "a caller-supplied system message still suppresses the
configuration's system prompt — and with it the snippet block". The chat of
the nr_ extension builds its own system prompt
(Chat), which always starts with a fixed
identity text, and Chat prepends it as a system
message whenever it is not empty. Read together, the code says snippets never
reach the chat. This is inferred from the code of both extensions; no run was
measured.
Neither tx_ nor tx_
is restricted to administrators, and ADR-169 section 2
recommends that non-administrators manage both tables. Whoever edits text that
reaches a system message holds an instruction-level privilege.
5. Text appended to an existing system message is not charged
Context charges the configuration's system prompt
only when the first message is not a system message
(Context returns 0 otherwise),
plus the fenced skill block passed as $injected. When the budget is
exceeded, Context removes the oldest whole turns
(ADR-107). Text that a later stage appends into an existing
system message is not charged at all, and a larger head is paid for by
evicting earlier turns, not by a refusal.
6. The tool allow-list is resolved from the configuration alone, live
Allowed returns the union of the
allowed- declarations of the effective skills, or null ("no
restriction") when none declares. Tool calls it with the
configuration and no task
(Classes/),
in Tool and in Tool.
Task skills and forced skills therefore restrict nothing at run time, although
ADR-038 item 5 names the task. A resume re-resolves the list
live (Tool): if the only declaring
skill is disabled while the run waits, the resumed run gets null, which
is every tool. The resolver's own docblock names the same widening for the
budget drop and rejects it there.
7. The run suspends for input or for approval, never for both
A tool can suspend a run in WAITING_ and resume it with input
validated against the schema the tool declared (ADR-105).
No production tool implements Requires today; only test
fixtures do. A builtin that declares a write effect requires a human approval
(ADR-134), and the approval binds to the preview lines the
approver saw: a resume recomputes them and bounces when they differ
(ADR-184). ADR-134 bans a write tool that also requires input,
because the approval resume carries no input
(Tool takes a boolean). A denial feeds the model a
fixed text that says who declined and nothing about why
(Classes/,
ADR-200). On a configuration with
require_ the run owner cannot approve their own write
(ADR-172;
Classes/).