Skip to content
tnsaijava agent framework

Migrate 0.11 → 0.12

This is the combined 0.11.0 → 0.12.0 path. Source of truth is the framework CHANGELOG [0.12.0] - 2026-06-04 (published via Changelog). The current lockstep line is 0.14.0 — after this page, read 0.13.0 and 0.14.0.

Historical Linear ids TNS-541TNS-597 belong to the retired linear.app/tnsai tracker. They are provenance, not clickable issues.

TL;DR

  • If you only use @RoleSpec / @ActionSpec / @Tool: you compile after the bump. Nothing in those three declarations was renamed or removed in 0.12.0.
  • If you used the com.tnsai.identity.AgentSpec record or @RoleIdentity: rename them (recipe below). @AgentSpec the annotation is unchanged.
  • If you applied any of the 23 removed annotations: delete the annotation. They were unwired orphans — removing them does not change runtime behaviour.
  • @Norm / @Norms were not newly enforced in 0.12.0. They stayed scaffold (TNS-591). Do not expect deontic checks after this bump.
  • @Idempotent was not removed. It stays a live tool annotation.

Automated path

mvn rewrite:run -Drewrite.activeRecipes=io.github.tansuasici.rewrite.UpgradeTnsAI_0_12_0

The tnsai-rewrite recipe on 0.12.0 applies the two simple-name renames. It does not delete orphan annotations (there is nothing to rewrite them to). Activate the recipe for the version you are moving to.

Four naming collisions

0.11 name0.12 nameUnchanged twin
com.tnsai.identity.AgentSpec recordcom.tnsai.identity.AgentDescriptor@com.tnsai.annotations.AgentSpec
@com.tnsai.roles.annotations.RoleIdentity@RoleDeclarationcom.tnsai.models.role.RoleIdentity class
RoleSpecExtractor.LLMSpec nested recordRoleSpecExtractor.LLMExportSpec@LLMSpec annotation
TYPE-level @LLMremoved — use @RoleSpec(llm=@LLMSpec(...))nested @LLMSpec (the live config)
// 0.11
import com.tnsai.identity.AgentSpec;
import com.tnsai.roles.annotations.RoleIdentity;

AgentSpec descriptor = ...;
@RoleIdentity(name = "analyst")
public class AnalystRole extends Role { }

@LLM(provider = "openai", model = "gpt-4o")
public class LegacyRole extends Role { }

// 0.12
import com.tnsai.identity.AgentDescriptor;
import com.tnsai.roles.annotations.RoleDeclaration;

AgentDescriptor descriptor = ...;
@RoleDeclaration(name = "analyst")
public class AnalystRole extends Role { }

@RoleSpec(llm = @LLMSpec(provider = Provider.OPENAI, model = "gpt-4o"))
public class LegacyRole extends Role { }

RoleSpecExtractor.hasLLMAnnotation(...) is hasLLMConfig(...). Role export now reads @RoleSpec.llm() (@LLMSpec), including endpointbaseUrl.

Removed annotations (23)

Every row is a pure orphan on 0.11: no reflection reader, no behaviour. Migration is “delete the annotation.” Canonical counterparts (if any) were always the wired surface.

TNS-590 (9)

RemovedReplacement
@ContextCompactionnone — delete
@SlashCommandnone — delete
@WorkspaceSpecnone — delete
@Propertynone — delete
@ConfigPropertynone — delete
@Triggernone — delete
@Delegatenone — delete
@Sanitizenone — delete
@ContentFilternone — delete

TNS-566 / 576 / 578 / 589 (9)

RemovedReplacement if you still need the idea
@RateLimited@Resilience retry/timeout (rate-limit itself stayed unwired)
@OnConnect / @OnDisconnect / @OnMessageChannel interface callbacks
@FSMState / @FSMTransition / @FSMStates / @FSMTransitionsStateMachine.builder() in intelligence
@RequiresPairingnone in 0.12 — it never gated dispatch. Do not invent @Security(requiredPermissions=...) as a drop-in

TNS-569 / 573 / 577 (4)

RemovedWired counterpart (never read the annotation)
@SystemPromptSystemPromptBuilder / @RoleSpec.description
@ChannelSpecChannel SPI
@Pipeline / @PipelineStepPipelineBuilder in tnsai-coordination

TNS-642 (1)

RemovedReplacement
@LLM (TYPE-level, export-only)@RoleSpec(llm=@LLMSpec(provider=..., model=...))

@NormType was kept as the value enum for the still-unwired @Norm / @Norms pair.

Newly active in 0.12 (behaviour you may notice)

These compiled on 0.11 and start doing something on 0.12.

SurfaceWhat changed
@AuditLogSecurityEnforcer.audit now emits a named AuditLogger entry (independent of @Security; honours mask flags).
@LLMSpec.topPRole.initializeLLMFromAnnotation() passes topP through. 1.0f still maps to null. Other @LLMSpec fields (frequencyPenalty, presencePenalty, timeoutMs, endpoint, apiKeyEnv) stay inert.
@AgentSpec.maxContextTokens> 0 prunes conversation history. Precedence: template > this shortcut > @MemorySpec.maxContextTokens.
@AgentSpec.toolCallFilterInstantiated via public no-arg ctor (AGENT-V009). AllowAllToolFilter is the “not set” sentinel. Builder / setToolCallFilter still wins.
MCP destructiveHint / idempotentHintIn-process McpToolBridge maps them onto requiresConfirmation / idempotent. WebSocket McpProxyTool is unchanged.
Dynamic tools + AGENT-V006DynamicToolMethod can require confirmation; the builder scan no longer skips MCP/dynamic tools.
@Contract JEXLAgentBuilder.build() warns on malformed clauses (AGENT-V013). Suppress with .relaxValidation("AGENT-V013").

Programmatic Config records

Builder-built roles and agents can now carry the same gates the annotations carry. Records introduced or completed by 0.11.0–0.12.0:

RecordTwin annotationSince
DIDConfig@DIDSpec0.11.0
GroupMemberSpec@GroupMember0.11.0
ActionConfig@ActionSpec (LOCAL)0.11.0
InputGuardrailConfig@InputGuardrail (enforced subset)0.12.0
OutputGuardrailConfig@OutputGuardrail (enforced subset)0.12.0
MemoryConfig@MemorySpec0.12.0
ContractSpec@Contract0.12.0
ResilienceConfig via RoleBuilder.resilience(...)@Resilience (retry + timeout only)0.12.0
AuthorityScope.permanent()long-lived beans (no 3650-day hack)0.12.0
DynamicToolMethod.builder(name)@Tool(requiresConfirmation, keywords)0.12.0

Guardrail resolution: method annotation > per-action config > RoleBuilder config > class annotation. Scaffold fields on the annotations (maxRetries on output, CB/rate-limit/bulkhead on resilience) stay inert.

ContractSpec is a record, not an annotation. That name is a documented outlier of the @*Spec house rule; the rename is TAN-2938, not part of this upgrade.

Sona

No Sona consumer PR is recorded against 0.12.0. If a Sona checkout still imports com.tnsai.identity.AgentSpec or @RoleIdentity, apply the same two renames. Placeholder until a Sona bump lands.

Issue chain (retired tracker)

The annotation re-verification epic that produced this release lived as TNS-541TNS-597 on linear.app/tnsai. The catalog follow-up is TAN-2802. This page is the consumer migration surface of that epic, not the canonical inventory.

See also