---
title: "API stability"
manual: "TYPO3 LLM Extension"
version: "0.35"
permalink: "https://docs.typo3.org/permalink/netresearch/nr-llm:api-stability@0.35"
source: "Api/Stability.rst"
modified: "2026-09-16T22:09:16+00:00"
---

# API stability

Which classes the semantic-versioning promise covers, and what it promises.
The authority is the marker in each class-level docblock, not this page and
not the DI container visibility ([ADR-127](https://docs.typo3.org/permalink/netresearch/nr-llm:adr-127@0.35)).

## The three markers

### `@api` — call it

Classes and interfaces you *call*: the feature services
(`CompletionServiceInterface` and friends), `LlmServiceManager`,
the option classes, the response and value objects they accept and return,
and the typed exceptions they throw. Within a major version:

-   no class or method is removed,
-   no method signature changes incompatibly,
-   documented behaviour does not break.

Everything a marked method's signature mentions is itself `@api` — you
never receive an object you are not allowed to rely on.

### `@api Extension point` — implement it

Interfaces and attributes third parties *implement*:
`ToolInterface`, `GuardrailInterface`, `ProviderInterface`
and the capability interfaces, `TranslatorInterface`,
`SearchBackendInterface`, the preset/evaluation providers, the
middleware contracts, and the `#[AsLlmProvider]` / `#[AsTranslator]`
attributes. These carry a stricter promise, forced by the direction of
implementation:
**no new abstract member within a major version** — adding one would break
every existing implementation, not just callers.

### `@internal` — hands off

Everything else: backend controllers, dashboard widgets, hooks, upgrade
wizards, console commands, DI compiler passes, TCA form elements, Extbase
repositories and the setup wizard. These may change or disappear in **any**
release, including patch releases. PHPStan and modern IDEs warn when code
outside this package touches them.

## What is out of contract

-   Subclassing internals: protected members of `@api` classes are not part
    of the promise. Extend via the extension points, not inheritance.
-   Constructor signatures of `@api` *services*: obtain them from the DI
    container, never via `new`. Constructing option/value objects directly
    is fine — their constructors are part of the signature promise.
-   Anything reached by reflection or by reading private state.

The snapshot below records the constructor a caller reaches with `new`,
including the service ones that are out of contract here. That is
deliberate: no mechanical rule separates a value object a consumer builds
with `new` from a service it only ever injects, and a new required
argument on the former breaks callers exactly as a deleted method would.
Recording them costs a service-wiring change one snapshot regeneration;
recording none of them let a widened constructor through the gate in
silence.

The constructor is also the one member that is **not** recorded
declared-only. Every other member has to be, because inherited TYPO3 core
members differ between 13.4 and 14.x — but `new Foo(...)` binds to
whatever constructor `Foo` inherits, so a declared-only rule left four
`Specialized` services and two `ProviderResponseException` subclasses
with no constructor line at all, and a required argument added to their
shared base moved nothing. A constructor is therefore taken from the
nearest declaring class whenever that class is inside `Netresearch\NrLlm`.

Two cases still carry no `constructor(...)` line, and both are intended:

-   The constructor is inherited from **outside** this repository — TYPO3
    core's `AbstractEntity`, `\RuntimeException`. That signature is not
    ours and does differ across the version matrix, so recording it would
    make the snapshot depend on which matrix cell rendered it.
-   There is no public constructor. A value object with a
    `private __construct` and static factories is reached through the
    factories, which the snapshot records as methods.

Today that is 70 of the 97 `@api` classes with a constructor line.

## Enforcement

The rendered `@api` surface is frozen in
`Tests/Unit/Api/api-surface.txt`: an unintended signature change fails CI
before review, and the same test asserts the closure rule: every
`Netresearch\NrLlm` type an `@api` **method or property** signature
mentions is itself `@api`. An intended change updates the snapshot in the
same PR — the diff is the review artifact.

Constructor parameter types are outside the closure rule. A DI-built service
is handed its collaborators by the container, so its constructor names
internals by design; the line is still frozen — widening it is a breaking
diff — but the types on it are not a promise that you may call them.

The failure is classified, because "different" makes a new value object read
like a deleted method. An **additive** diff (a new class, method, property,
constant or enum case) is regenerated and noted under `### Added`. A
**breaking** one — anything removed or changed, a widened constructor
included — is a decision, and the failure message says so and points at
[Deprecation and removal policy](https://docs.typo3.org/permalink/netresearch/nr-llm:api-deprecation@0.35).

How something leaves the surface again is [Deprecation and removal policy](https://docs.typo3.org/permalink/netresearch/nr-llm:api-deprecation@0.35). Which
TYPO3 and PHP versions the promise is made on is [Support matrix](https://docs.typo3.org/permalink/netresearch/nr-llm:api-support-matrix@0.35).

## Versioning during 0.x

While the extension is pre-1.0, the promise applies one level down, as is
conventional: **minor releases (0.N → 0.N+1) may break**, and do so only
with a CHANGELOG entry under a BREAKING heading; patch releases do not
break. From 1.0.0 on the promise applies as written above.
