---
title: "ADR-163: A use-case pack is data plus a small installer"
manual: "TYPO3 LLM Extension"
version: "0.35"
permalink: "https://docs.typo3.org/permalink/netresearch/nr-llm:adr-163@0.35"
source: "Adr/Adr163UseCasePacks.rst"
modified: "2026-09-16T22:09:16+00:00"
---

# ADR-163: A use-case pack is data plus a small installer

-   *Status:* Accepted
-   *Date:* 2026-08-11
-   *Authors:* Netresearch DTT GmbH

## Context

Setup starts at "Provider". That is the last question an operator can answer
and the first one the wizard asks: endpoint, adapter type, API key, then models,
then a configuration. Someone who wants help writing teasers has to translate
that want into four technical decisions before anything happens, and nothing in
the product makes the translation for them.

The pieces a working setup needs already exist separately — configuration
presets ([ADR-056](https://docs.typo3.org/permalink/netresearch/nr-llm:adr-056@0.35)), tasks, prompt snippets, tool groups,
governance profiles ([ADR-145](https://docs.typo3.org/permalink/netresearch/nr-llm:adr-145@0.35)). What is missing is a name for a
combination of them and a place to ask the earlier question.

## Decision

**A pack is DATA.** `UseCasePack` declares a configuration preset, task
records, snippet records, a recommended governance posture and the tool groups
its tasks benefit from. It has no behaviour. `UseCasePackInstaller` is the
only thing that writes, it writes ordinary records through the existing
services, and it is roughly a hundred lines. A pack that could configure would
be a second configuration system beside the one it is made of.

**The configuration half IS a configuration preset.** Not a copy of one — the
same `ConfigurationPreset`, published to the preset registry through
`UseCasePackPresetProvider`. ADR-056 already expresses model REQUIREMENTS
rather than a chosen model, already preflights them against the installed
models, and already owns import and drift resolution. The visible consequence is
intended: a pack's configuration also appears in the Configuration module's
pending list, and importing it there installs exactly that configuration.

**A pack recommends a governance posture; it cannot apply one.**
[ADR-145](https://docs.typo3.org/permalink/netresearch/nr-llm:adr-145@0.35) decided that a profile describes and never enforces,
and this record does not reopen it. The pack names the posture its content was
written for, the plan screen renders it next to a link to the governance
readout, and installing changes no governance value. The same reasoning covers
tool groups: the pack names them, the Tools module's admin enable stays the only
way to switch one on. A pack that enabled its own tools would hand an install
button the authority of the tool gate.

**Nothing is written before an operator confirms.** `plan()` is read-only
and answers what would be created, what is already there, and whether the
configuration's requirements can be met at all. The confirm is a POST; the
install action refuses a GET, so a bookmark or a prefetch cannot provision.

**"Already installed" means a record with that identifier exists.** Nothing
more. The installer never overwrites and never compares contents: an operator
who renamed a pack task or rewrote its prompt owns that record. The single
exception is the configuration's snippet-tag selection, described below — an
addition, never a replacement.
Identifier lookups run through the repositories' backend query settings, which
ignore enable fields, so a record the operator DISABLED still counts as
installed — otherwise "install again" would quietly resurrect what they switched
off.

**The configuration is the one hard requirement.** When it is neither present
nor importable, the install is refused rather than half-applied. Tasks pointing
at a configuration that does not exist fall back to the default one and would
quietly run under settings the pack never described.

**Snippet tags are derived, not declared.** A configuration composes the active
snippets carrying any of its tags ([ADR-031](https://docs.typo3.org/permalink/netresearch/nr-llm:adr-031@0.35)), so the pack takes
the union of its snippets' own tags. A separately declared tag list could name a
tag no snippet has, and the snippets a pack installs would then be read by
nothing.

**The tag link is written on every install, and it only adds.** It is the one
field the installer sets on a record it did not create, and the reason is the
bridge above: the pack's configuration can equally be created by importing its
preset in the Configuration module, and that import writes no `snippet_tags`
— ADR-056 does not own that field. Writing the link only on the created record
would mean that whoever imported the preset first got a pack whose snippets are
installed, active, and composed into nothing, with no error anywhere. So the
installer adds the pack's tags to whatever the configuration already selects,
never removes, and re-adds nothing that is already there. What it would add is
listed on the plan screen, and a plan whose records all exist but whose tag link
is missing still offers the confirm button — that is the state that repairs it.

**A shared tag reaches in both directions, and the plan says so both times.**
Snippets are selected by tag, not by owner, and the vocabulary is free-form and
shared. Outwards: any existing configuration that already selects
`tone_of_voice` composes the pack's house-style snippet the moment it is
created. Inwards: adding `tone_of_voice` to the pack's own configuration
composes every active snippet that already carries it, including operator
snippets the pack never saw.

The inward direction is the sharper one, because a snippet brings its data class
with it. Classification takes the STRICTEST class over the composed snippets, so
one CONFIDENTIAL operator snippet raises the whole configuration and an enforcing
input-context gate then refuses every send through it — a configuration that
worked before the install stops working, with nothing on the screen having
predicted it.

The installer prevents neither direction — scoping snippets to one configuration
would be a second selection mechanism beside ADR-031 — but the plan screen names
both: every configuration the new snippets would reach, and every existing
snippet the added tags would pull in, with its data class. The operator confirms
this screen and cannot confirm an effect they were not shown.

## What a pack does not contain

**Skills.** A skill record carries provenance and a trust level from the source
it was synced from: a body checksum, a source SHA, an injection scan, a trust
level the trust gate reads. A pack has none of those and could only fabricate
them. A pack-installed skill would therefore be a first-party-looking record
with no source — precisely the shape the skill trust model exists to prevent.
Packs point at the Skills module instead.

**Five of the six use cases.** `UseCase` names all six the plan asks about
— editorial, translation, metadata, media accessibility, agent workflows,
developer integration — because the entry step has to offer the question
whole. Only *Editorial Starter* is built. A use case with no pack says so and
links to the technical wizard rather than hiding itself, which is a more useful
answer than a question with invisible options. The others are not built; nothing
here claims otherwise.

## Consequences

✓ An operator can start from what they want to do, see exactly which records
would be created, and get a working editorial setup in one confirmed step.

✓ Re-running an install is a no-op for everything already there, including
records the operator edited or disabled, and including a snippet tag the
configuration already selects.

◐ An install changes the prompts of existing configurations that already select
one of the pack's snippet tags. That follows from tag-based selection and is not
prevented; it is computed read-only and listed on the plan screen before the
operator confirms.

◐ It changes the pack's own configuration the same way, in reverse: existing
snippets carrying the added tags are composed into it, and the strictest data
class among them becomes the configuration's. Where the input-context gate
enforces, that can refuse sends the configuration made before. Also not
prevented, also computed read-only, and listed on the plan screen with each
snippet's data class.

✓ No second configuration system, no second policy engine, no bypassed gate. The
pack's configuration lives in the preset lifecycle, its governance is a
recommendation, its tools go through the admin enable, and its tasks are
ordinary tasks.

◐ A pack cannot be uninstalled. Nothing marks a record as pack-owned — that is
what makes the installed records ordinary — so removing them is deleting
records, one by one, like any other. A remove flow would need ownership tracking
and would have to decide what to do with an edited record; neither is worth it
for four tasks and two snippets.

◐ A changed pack declaration updates nothing outside the configuration. The
preset drift flow covers the configuration half; a task whose declared prompt
changed after installation stays as installed. Detection is possible the same
way the preset does it (a stored checksum) and is deliberately not built: the
first question is whether operators want their edited tasks touched at all.

✕ A pack says nothing about providers or API keys. It requires capabilities and
lets the preflight report what is missing. Installing a pack on an installation
with no models still needs the technical wizard, and every screen links there.

## Revisit when

A second pack needs something the shape does not carry — an agent-workflow pack
would want an agent definition, a media pack a speech or image configuration.
Both are additive: the pack gains a field and the installer a branch, and none
of the decisions above change.
