# Annotation Runtime Status

A reference of the annotations TnsAI exposes in `com.tnsai.annotations`, grouped by area,
with each one's **runtime status** so you know what actually takes effect.

This page is the published runtime-status catalog (wired / nested-wired / partial /
scaffold). For field tables and usage examples, see the
[Semantic Annotation Framework](catalog.md).

TnsAI is annotation-driven: you describe an agent, role, action, or tool declaratively and
the framework wires the behaviour. A handful of annotations are still being wired or are
on their way out — this page tells you which is which, so you don't reach for one that
silently does nothing.

## Status legend

| Status | What it means for you |
|---|---|
| **Wired** | The runtime honours the annotation and all of its advertised fields. Safe to use. |
| **Nested-wired** | Honoured, but only valid *inside* a parent annotation (e.g. `@LLMSpec` inside `@RoleSpec(llm = …)`), not on its own. |
| **Partial** | Honoured, but only a subset of fields takes effect today; the rest are no-ops until a later release. Notes call out which. |
| **Scaffold** | Declared but not yet wired — using it has no runtime effect yet. Planned. |

> Annotations that used to exist but were removed are listed under
> [Recently consolidated](#recently-consolidated) with their replacements. See
> the [Changelog](../../help/changelog.md) for the exact release and migration notes.

## Core

The annotations almost every agent uses.

### `@RoleSpec` — Wired
Declares a role: identity, goals, domains, and (nested) LLM/memory config.

```java
@RoleSpec(
    name = "Researcher",
    description = "Answers questions with citations from a local corpus.",
    llm = @LLMSpec(provider = LLMSpec.Provider.OPENAI, model = "gpt-4o-mini", temperature = 0.2f))
public class ResearchRole extends Role { /* @ActionSpec methods … */ }
```

### `@AgentSpec` — Partial

Type-level agent metadata and partial declarative configuration. The runtime
consumes members including `roles` (as a fallback), `maxContextTokens`, and
`toolCallFilter`, but it does not consume the nested `llm` member. There is no
`name` (derived from the class) and no `role` string.

<!-- java-contract: src/main/java/com/example/tnsai/docs/AnnotatedAgentExample.java -->
```java
package com.example.tnsai.docs;

import com.tnsai.agents.Agent;
import com.tnsai.annotations.AgentSpec;
import com.tnsai.llm.LLMClient;
import com.tnsai.llm.providers.AnthropicClient;
import com.tnsai.roles.Role;

import java.util.List;

@AgentSpec(description = "A helpful research assistant")
public final class AnnotatedAgentExample extends Agent {

    @Override
    protected LLMClient getLLM() {
        return new AnthropicClient("claude-sonnet-4-20250514");
    }

    @Override
    protected List<Role> getRoles() {
        return List.of(Role.create(QuickstartBuilderExample.AssistantRole.class));
    }

    public static void main(String[] args) {
        var agent = new AnnotatedAgentExample();
        agent.start();
        String answer = agent.chat("What is quantum computing?");
        System.out.println(answer);
    }
}
```

Direct subclasses must implement the abstract `getRoles()` method. A non-empty
result takes precedence over `@AgentSpec.roles`; an empty result allows the
annotation's public-no-arg role classes to supply the fallback. Provide the
agent-level LLM through `getLLM()` or `AgentBuilder.llm(...)`.

`toolCallFilter` is `Class<? extends ToolCallFilter>`. The shipped default is
`AllowAllToolFilter` — that class is the "not set" sentinel, not an allow-list.
There is no `AllowlistFilter` / `AllowlistToolFilter` type. A real filter is
your own `ToolCallFilter` with a public no-arg constructor.

### `@ActionSpec` — Wired
Marks a method as an agent action/tool, with type, description, and behavioural constraints.

```java
@ActionSpec(type = ActionType.LOCAL, description = "Look up an order",
            mustAlways = {"validate the order id"})
public Order lookup(String orderId) { … }
```

### `@State` — Wired
Marks a **planner** field on a `Role`. The planner reads those fields as a
`Map` to evaluate `@Goal` conditions and action pre/postconditions. This is
not `com.tnsai.models.agent.Belief` and is not BDI extraction.

### `@Contract` — Wired
Design-by-Contract pre/postconditions and invariants (JEXL), enforced around the action.
See [Contracts](../../contracts.md).

## Tools

### `@Tool` / `@ToolParam` — Wired
Expose a POJO method (and its parameters) to the LLM as a callable tool. `@Tool` carries
`requiresConfirmation` and `keywords`, which the agent's approval gate (AGENT-V006) reads.

```java
public final class PaymentTools {
    @Tool(description = "Send a wire transfer", requiresConfirmation = true)
    public String wire(@ToolParam("recipient") String to, @ToolParam("amount") double amount) { … }
}
```

TnsAI 0.14.0 (`TnsAI@a070d674`, TAN-3411) fails registration when a
parameter has no `@ToolParam(name)` and javac emitted `argN`. The
positional `@ToolParam("recipient")` form above is already safe.
`timeoutMs` ships in Maven Central `0.14.1` as an eighth `@Tool` member
(`@since 0.14.0`; [PR #164](https://github.com/TnsAI-Framework/TnsAI/pull/164)
/ [TAN-2958](https://linear.app/tansuasici-workspace-1/issue/TAN-2958)).
A positive value is one
wall-clock budget for the full POJO dispatch, including idempotency work;
`0` keeps the enclosing agent policy. Expiry is retryable
`ToolTimeoutException`. Interruption is best-effort
([TAN-4387](https://linear.app/tansuasici-workspace-1/issue/TAN-4387)).
Dynamic tools and MCP server deadlines are out of scope. See
[Custom Tools](../../capabilities/tools/custom-tools.md#per-tool-deadlines).

### `@ToolExample` — Nested-wired
Few-shot examples for a tool; read from `@ActionSpec.examples()`.

### `@Idempotent` — Wired
Marks an action safe to retry; consulted by the retry/fallback layer.

## LLM configuration

### `@LLMSpec` — Nested partial

`@LLMSpec` has `@Target({})`, so it is valid only as a member of another
annotation. `@RoleSpec(llm = …)` consumes `provider`, `model`, `temperature`,
`maxTokens`, and `topP` for the role-level client. `frequencyPenalty`,
`presencePenalty`, `apiKeyEnv`, `endpoint`, `timeoutMs`, and `streaming` are
export metadata only; `fallbackModel` and `systemPrompt` are currently unused.
`provider` is `LLMSpec.Provider`, not a `String`; that enum is not the
`LLMClientFactory` provider-name inventory.

`@AgentSpec(llm = …)` is also syntactically valid, but the agent initializer
does not consume it today. Use an agent's `getLLM()` override or
`AgentBuilder.llm(...)`. The older type-level `@LLM` was removed (see below).

Both paragraphs describe the **core agent initializer**. The SCOP bridge resolves
separately and does read the agent tier, as well as `endpoint` and `apiKeyEnv`, when
it builds an `LLMConfiguration` — and an external configuration source can override
any of them per agent. See
[External LLM Configuration](../../capabilities/llm/configuration-sources.md).

### `@LLMParam` / `@LLMParams` — Wired
Bind action parameters to LLM-extracted values.

## Memory & retrieval

### `@Memory` / `@VectorMemory` — Wired / Partial
Configure the agent's memory store. Read from the **agent** class only — on a `Role` these are
introspection/export metadata and do not change memory behaviour. `@VectorMemory.provider` has no
provider SPI lookup yet: `inmemory` uses the base store with TF-IDF search, and every other value
falls back to that same store after logging a notice. `@Memory.vector()` has no runtime consumer at
all.

### `@MemorySpec` — Nested-wired
Memory config nested in `@AgentSpec(memory = …)`; also available programmatically as
`MemoryConfig.builder()` via `AgentBuilder.memoryConfig(...)`.

### `@KnowledgeSource` / `@KnowledgeSources` — Wired
Declares the corpora a Role retrieves from. All six fields are honoured. It
describes ingestion only; how documents are searched is `@Retrieval`'s. `FILE`
is the one bundled loader — `URL`, `DATABASE` and `MEMORY` are `SourceLoader`
SPI extension points an optional module registers against.

A source whose type has no installed loader is **skipped with a warning**. The
Role still binds from whichever sources did load. Binding fails only when
**every** enabled source is unloadable. A missing loader costs part of the
corpus; it does not fail dispatch by itself.

### `@Retrieval` — Wired
Retrieval tuning. All 21 fields are honoured and all 8 strategies have a runtime
path. `queryParam` selects a named action argument, while `navigatorModel`
selects the installed `TreeNavigator` for `Strategy.REASONING`. `GRAPH` resolves
the bundled FILE-corpus, KnowledgeTools, or Neo4j adapters,
while an explicit application provider overrides the framework defaults.
Reranking, query expansion (including `MULTI_QUERY`), and `REASONING` resolve
model SPIs that TnsAI deliberately ships no provider for; the framework side
(resolution, validation, capping, and error classification) is complete.

On Maven Central `0.14.1`, an unservable `rerankerModel` or
`queryExpansionModel` fails at dispatch, inside the span
`@Retrieval.onFailure` guards. `CONTINUE` can then emit an ungrounded
answer. `GRAPH` already treats a missing provider as a configuration
error above that guard.

Since `0.15.0` (`TnsAI@cb14410d`,
[PR #198](https://github.com/TnsAI-Framework/TnsAI/pull/198) /
[TAN-5887](https://linear.app/tansuasici-workspace-1/issue/TAN-5887))
those two model names resolve when `Role.create` establishes the
retrieval configuration. An unservable name is a wiring-time
configuration error and is not reachable through `onFailure`. Failures
a provider raises *while* ranking or expanding still follow
`onFailure`. Roles with no effective `@Retrieval`, and declarations
that do not ask for rerank or expansion, do not load those provider
graphs. To inspect what resolved without throwing, see
[RAG diagnostics](../../capabilities/rag/diagnostics.md)
(`RagDiagnostics`, `@since 0.15.0`).

## Guardrails & safety

### `@InputGuardrail` / `@OutputGuardrail` — Wired
Validate/transform input before, and output after, an action. These annotations
are not the supervisor `Guardrail` interface and not the quality tracing
`Guardrail` SPI — see [Guardrails](../../security/guardrails.md).

### `@Security` — Wired
Permission checks and sandboxing, enforced by `SecurityEnforcer`.

### `@ApprovalRequired` — Wired
Requires human approval before an action runs (raises `ApprovalRequiredException`).

## Resilience & observability

### `@Resilience` — Partial
Umbrella resilience policy. **Retry** and **timeout** are enforced today; circuit-breaker,
rate-limit, and bulkhead fields are scaffolded for a later release. The builder counterpart
is `RoleBuilder.resilience(ResilienceConfig…)`.

### `@Traced` / `@Metered` — Wired
Tracing and metrics decorators around actions.

### `@Fallback` — Wired
Declares a fallback method, resolved by `FallbackResolver`.

## Lifecycle, events & coordination

### `@Lifecycle` — Wired
Lifecycle phase hooks.

### `@EventHandler` / `@EventEmitter` — Wired / Scaffold
`@EventHandler` methods are dispatched by `EventHandlerProcessor`. `@EventEmitter` is not yet wired.

### `@Coordination` — Partial
Multi-agent coordination; only the nested `negotiation()` configuration is honoured today.

### `@Communication` / `@CommunicationStyle` — Wired
Agent communication style, tone, and languages.

## Identity

### `@DIDSpec` — Nested-wired
W3C Decentralised Identifier config, nested in `@AgentSpec(did = …)`.

### `@GroupMember` — Nested-wired
Group membership, nested in `@AgentSpec(groups = …)`.

## Scaffold & being-consolidated

These exist but have **no runtime effect yet** (wiring planned) — don't rely on them:
`@Norm` / `@Norms` (deontic logic), `@EvalMetric` (custom evaluation metrics),
`@QualityGate` (release gates), `@AuditLog` (declarative audit), `@OutputFormatSpec`
(`prettyPrint`/`schema` not yet honoured).

## Recently consolidated

During the 0.11.x annotation cleanup, **23 unwired annotations were removed** in favour of
their working counterparts. **0.14.0** continues the same delete-don't-deprecate
policy for `@Param`. If you were using any of these, switch to the replacement:

| Removed | Use instead |
|---|---|
| `@Param` (removed in 0.14.0) | the Java parameter name (`-parameters`); `@ToolParam` / `@LLMParam` if you need a live annotation |
| `@LLM` (type-level) | `@LLMSpec` nested in `@RoleSpec`/`@AgentSpec` |
| `@RateLimited` | `@Resilience(rateLimit = …)` |
| `@OnConnect` / `@OnDisconnect` / `@OnMessage` | the `Channel` interface methods |
| `@FSMState` / `@FSMTransition` / `@FSMStates` / `@FSMTransitions` | `StateMachine.builder()` |
| `@SystemPrompt` | `@RoleSpec.description` (or `@LLMSpec.systemPrompt`) |
| `@ChannelSpec` | the `Channel` interface |
| `@Pipeline` / `@PipelineStep` | `PipelineBuilder` |
| `@RequiresPairing` | `@Security` |
| `@ContextCompaction`, `@SlashCommand`, `@WorkspaceSpec`, `@Property`, `@ConfigProperty`, `@Trigger`, `@Delegate`, `@Sanitize`, `@ContentFilter` | removed — no runtime consumer existed |

Each removal is a `### Removed` entry in the [Changelog](../../help/changelog.md) with the rationale.

## See also

- [Semantic Annotation Framework](catalog.md) — field tables and usage examples
- [Contracts](../../contracts.md) — `@Contract` Design-by-Contract
- [Validation](../../validation.md) — build-time agent validators
- [Changelog](../../help/changelog.md) — per-release breaking changes & migration notes
