Skip to content
Tracon

Versions and upgrades

Tracon is a pre-1.0 package family. Treat version selection as part of your application architecture, not as a restore detail.

The release process produces 21 NuGet packages and one npm package (@tracon/client), all cut from the same v* tag and sharing one version line: there is no split between a stable subset and a preview subset. The public surface carries no compatibility promise for as long as that line stays below 1.0.0-rc.1 - a narrowing or a reshaped type is not treated as a breaking change until the family reaches that release.

Every published version has a dated entry on the Release notes page, grouped as Added, Changed, Deprecated, Removed, Fixed, and Security. Each package’s PackageReleaseNotes metadata links to the entry for the exact version you installed.

The entry names, by its exact name, every public type that was removed or changed since the previous published version, and every target framework a package no longer ships. The release process checks this against a machine-made list: it validates each package against the previous published package, and it does not publish when the notes leave out a name. A narrowing is still allowed before 1.0.0-rc.1; an unannounced one is not. From 1.0.0-rc.1 even an announced one is not: see What 1.x promises for the .NET API and What 1.x promises for HTTP, telemetry, configuration, error codes and the CLI.

Tracon freezes the public .NET API at 1.0.0-rc.1. From that release on, each change to a public type or member belongs to one SemVer class. The release process reads the public API and rejects a release that puts such a change in a class that does not allow it. Behavior is a different matter: two rows of the table are a maintainer’s review, and the release process only makes them visible (see below). Until rc.1 the earlier rule holds: a break is allowed when the release notes name it.

Change Patch Minor Major
A new public type No Yes Yes
A new public member on an existing type (optional, with a documented default) No Yes Yes
A new interface member with a default body No Yes Yes
A new interface member without a body No No Yes
A new abstract member on an abstract class No No Yes
A new required member on an existing type No No Yes
A required member becomes optional No Yes Yes
A new positional parameter on a record, or a new optional parameter on a method No No (a new overload: Yes) Yes
A new enum value No Yes Yes
An enum value removed, or its number changed No No Yes
A public type or member removed, renamed, or given another signature No No Yes
A target framework dropped No No Yes
A new test in Tracon.Testing.Contracts.Xunit that measures behavior the documentation already states Yes (a defect fix) Yes Yes
A new test in Tracon.Testing.Contracts.Xunit that asks for new behavior No No Yes
A change to behavior that the XML documentation states No No Yes

How the release process holds each row:

  • A frozen release rejects a break. When the last published version is 1.0.0-rc.1 or later and the new version has the same major version, a removed type or member, a changed signature, a dropped target framework, an interface member without a body, an abstract member, an enum value that is gone or renumbered, and a required member that an existing type gains all stop the release. The release notes do not change that. A new major version is the one place where a break is allowed, and the notes must name it.
  • A patch release does not grow the surface. It compares the public API files with those of the last published version, and any added line stops the release. The one exception is a new contract test (below). A required member that becomes optional or goes away stops a patch as well.
  • Between 1.0.0-rc.1 and 1.0.0 the surface only closes. A release with the same major and minor version as the last published one counts as a patch: 1.0.0-rc.2 and 1.0.0 add no public member to 1.0.0-rc.1.
  • A required member is checked apart. The compatibility check of the .NET SDK does not report a required member added to an existing type. The repository keeps a list of every public required member, a test keeps the list true to the code, and the release process compares it with the list at the last published version.
  • Every run prints its rule. The first line of the check says whether the release is judged as a preview, a frozen release, or a new major version.
  • Behavior is reviewed, not read. The release process cannot tell whether a new contract test asks for new behavior, or whether a change alters behavior that the XML documentation states. It lists every new contract test and stops when the release notes do not name one; the maintainer judges the rest.

Three more rules complete the table:

  • Your code handles an enum value it does not know. A switch over a Tracon enum has a default branch. Tracon adds a value in a minor release.
  • One rule covers every public interface. An interface that Tracon calls and you never implement, such as IToolRegistry or IWorkflowRunner, follows the same rule as one you implement. The release check does not tell them apart, so the rule does not.
  • A contract test only measures stated behavior. When you upgrade, a new test in Tracon.Testing.Contracts.Xunit runs against your store, provider, or judge, and it can fail. Tracon adds one only when the XML documentation or this site stated the behavior before the version that adds the test. The release notes name each new test as Class.Method. If a new test fails against your implementation, either the implementation or the documentation is wrong. Report it as an issue.

What 1.x promises for HTTP, telemetry, configuration, error codes and the CLI

Section titled “What 1.x promises for HTTP, telemetry, configuration, error codes and the CLI”

Your application depends on more than the .NET API. A client calls the HTTP endpoints and reads the SSE event names. A dashboard reads metric and attribute names. A deployment file holds configuration keys. Your code matches error codes. A CI script calls the tracon CLI. From 1.0.0-rc.1 each of these surfaces has the same rule as the .NET API: a change belongs to one SemVer class, and the release process rejects a release that puts it in the wrong one.

Surface A minor release may Only a major release may
HTTP Add an endpoint. Add a field to a response. Add an optional field or parameter to a request. Add a value to a response enum. Remove an endpoint or a method. Remove a response field, or change its type. Add a required field or parameter to a request. Make an optional one required. Remove a value from a request or a response enum. Make a field that could be null non-null in a request, or one that could not be null nullable in a response. Change a 2xx status code. Tighten the role an endpoint needs.
SSE Add an event name. Add a field to an event payload. Remove or rename an event name. Remove a payload field.
Telemetry Add a source, an instrument or an attribute. Add an attribute that is off by default to an instrument. Change the name, the unit or the kind of an instrument. Remove an attribute. Turn an attribute on or off by default.
Configuration Add a key with a documented default. Rename a key if Tracon still reads the old name. Remove a key. Rename a key and stop reading the old name. Change a default so that behavior changes.
Error code Add a code. Remove a code, rename it, or change its meaning. This covers the codes of a failed run, a failed job and an OpenAI-compatible error, and the codes of the findings that validating an agent definition returns.
CLI Add a command, an option or an exit code. Remove a command or an option. Change what an exit code means.

When a minor release gives a configuration key a new name, Tracon reads the old name for the rest of the 1.x line and logs one warning. The old name is gone only in a major release.

Three more rules complete the table:

  • Your code handles a value it does not know. A response enum value, an SSE event name and an error code can all be new in a minor release. A switch over them has a default branch. In the TypeScript client, this applies to the switch over an event name too.
  • The release process cannot see a role change. A role that an endpoint needs is not in the OpenAPI document. The rule above is written down, and the maintainer reviews it.
  • Text for people is not a contract. The detail of a problem response, a log message and the human output of a CLI command can change in any release. The fields a program reads are the contract: the errorType code, the event name, the key, the option.

Match an error code with a constant. TraconErrorTypes holds the code of a failed run, of a failed job and of an OpenAI-compatible error as constants, for example TraconErrorTypes.ToolTimeout. The ErrorType of a Tracon exception and the errorType that the HTTP API returns carry the same value.

How the release process holds each row:

  • Every surface has a file in the repository. One file lists the SSE event names, one the telemetry names with the kind, unit and default attributes of each instrument, one the configuration keys, one the error codes of runs, jobs and OpenAI-compatible errors, one the codes of validation findings, and one the CLI commands and options. A test keeps each file true to the code. The HTTP surface is the OpenAPI document of the build.
  • The release process compares each file with the one at the last published version. A line that is gone or changed is a break. A new line is an addition. For HTTP it reads the OpenAPI document with the rules of the table.
  • A change that the rules do not name is a break. If the check cannot say which row a change belongs to, it stops the release. It never lets such a change pass. If it cannot read the file of the last published version, it stops too.
  • A frozen release rejects a break. When the last published version is 1.0.0-rc.1 or later and the new version has the same major version, a break on any of these surfaces stops the release. The release notes do not change that. Before 1.0.0-rc.1, and in a new major version, a break passes when the release notes name it in a code span.
  • A patch release does not add to a surface. A new endpoint, field, event name, key, code or option stops a patch release.
  • The files hold names, not meaning. The release process does not see a field removed from an SSE event payload, a configuration default that changed, an exit code that now means something else, an error code that is used for another failure, or a role that an endpoint now needs. The rules above hold for them as well. The maintainer reviews them.

The telemetry row includes the default of an attribute. The tracon.tenant.id attribute is on spans and on the quota gauges, and it is off by default on the other metrics. Set Tracon:Observability:IncludeTenantTag to true to add it. Turning the default back on in a later release is a change of this row.

For packages produced by the official release process, a <package id, version> pair names exactly one artifact. That guarantee comes from the pipeline, not from NuGet: a locally packed or otherwise unofficial build can reuse a version string that already named different content, and NuGet’s cache will not notice — see the caching caution if you pack previews yourself. For official artifacts the release process enforces this before a package is ever produced:

  • Every packed artifact traces back to a commit. The build refuses to produce a package from an uncommitted working tree — there is no version that can mean “whatever the source happened to be at the time.” Each package’s .nuspec carries the exact commit it was built from.
  • A version is never silently replaced with different content. If a release run would produce a <package id, version> pair that already exists with different bytes, the run stops and the existing, previously published artifact is left untouched — it is never overwritten in place.

This is independent of NuGet’s own package integrity check: every .nupkg carries a content hash (<id>.<version>.nupkg.sha512 next to it in your local package cache — find the cache path with dotnet nuget locals global-packages --list), and nuget.org publishes the same hash for the version it lists. If those two ever disagree, something between the registry and your machine changed the bytes — that check exists independently of anything Tracon does, and applies to any NuGet package, not only this family.

This site is built from the repository’s main branch. The .NET reference is generated from the assemblies built from that same source, and the HTTP reference is generated from the OpenAPI snapshot in that source tree.

That makes the site the best description of the next build. It can also document a public API that is newer than the preview package you installed. When exact reproducibility matters, pin every Tracon package and check the Release notes entry for the version you installed before you rely on anything this site describes.

Use NuGet’s pre-release selection explicitly:

Terminal window
dotnet add package Tracon --prerelease

The project template resolves pre-release packages by default, but pinning it makes a team build reproducible:

Terminal window
TRACON_VERSION=1.0.0-preview.N # replace N with the published preview
dotnet new install "Tracon.Templates@$TRACON_VERSION"
dotnet new tracon-api --TraconVersion "$TRACON_VERSION"

Do not mix Tracon preview versions. The packages share public contracts, DI registrations, database migrations, and generated code, and one package can call a sibling member that another release removed.

Every Tracon package depends on its Tracon siblings at exactly its own version ([1.0.0-preview.N]), so NuGet and the host stop a mixed graph at three points:

flowchart TD
    accTitle: Where a mixed Tracon graph stops
    accDescr: Two Tracon packages on different versions fail restore with NU1107. A direct reference to a shared Tracon package overrides the exact range, and NuGet only warns NU1608, unless the project treats NU1608 as an error. A graph that restores anyway, or that uses a package published before the exact range, stops when the host starts.
    G[Your project] --> Q{How does the shared<br/>Tracon package enter?}
    Q -- through two packages<br/>on different versions --> R1[Restore fails: NU1107]
    Q -- through a direct reference<br/>on another version --> R2[Restore warns: NU1608]
    R2 -- NU1608 in WarningsAsErrors --> R3[Restore fails]
    R2 -- warning only --> S[Host start]
    OLD[A package published<br/>before the exact range] --> S
    S -- more than one version --> X[TraconException:<br/>the host does not start]
    S -- one version --> OK[Normal start]
  • Two packages on different versions that share a sibling (for example Tracon.AspNetCore and Tracon.Voice, which both need Tracon.Core) fail restore with NU1107.
  • A direct reference wins. Referencing Tracon.Core at another version overrides the exact range; NuGet reports only warning NU1608. Add <WarningsAsErrors>$(WarningsAsErrors);NU1608</WarningsAsErrors> to stop restore instead. The dotnet new tracon-api project already carries that line. It also turns the same warning from any other package into an error; delete it if you want NU1608 to stay a warning.
  • A graph that restores anyway does not start. When the host starts, before any hosted service runs, Tracon compares the versions of the Tracon package assemblies the process loaded. If they differ, the host stops with a TraconException that lists each assembly and its version on its own line. Tracon.Client is not compared: it talks to a server over HTTP and may call another release by design. The check has no setting to turn it off, and it runs only in a process that starts a host.
  • 1.0.0-preview.1 and 1.0.0-preview.2 accept any newer sibling. They were published before the exact range, so restore cannot stop them; the host check does when a host starts.
  • A store author who tests with Tracon.Testing.Contracts.Xunit sees NU1608 when Tracon.Abstractions is on a newer version than the contract package. Upgrade the contract package with it: an older contract package can call members the newer abstractions package no longer exposes.

With Central Package Management, keep the versions in one place:

<ItemGroup>
<PackageVersion Include="Tracon" Version="1.0.0-preview.N" />
<PackageVersion Include="Tracon.PostgreSql" Version="1.0.0-preview.N" />
<PackageVersion Include="Tracon.OpenAI" Version="1.0.0-preview.N" />
<PackageVersion Include="Tracon.UI" Version="1.0.0-preview.N" />
</ItemGroup>

If you reference individual packages directly, pin each one to the same version. Avoid floating ranges in production.

Tracon.Client is generated from the exact same OpenAPI document the server build carries — both come from the same repository build, so they cannot drift apart at a given version the way a hand-written client could. A caller on a newer preview than the server it targets sees only the operations the server actually serves; calling one the server does not yet have returns a 404. Tracon.Cli follows the same version family, since it wraps Tracon.Client.

The npm package @tracon/client is cut from the same v* git tag as every NuGet package above — there is no separate npm version scheme. @tracon/client 1.0.0-preview.N and Tracon.Client 1.0.0-preview.N always describe the identical OpenAPI document.

An upgrade can leave sessions and workflow checkpoints in storage from before the upgrade. This is the compatibility promise for that stored payload, split by who owns each layer:

Layer Owner Promise
Record envelope (id, tenant, timestamps, generation, schema version) Tracon A minor version only adds envelope fields; it never removes one. An envelope written by an older Tracon version is still readable
Session state body (SessionRecord.State) Microsoft Agent Framework No promise. A Microsoft Agent Framework minor version bump can make an older body unreadable
Checkpoint state body (WorkflowCheckpointRecord.State) Microsoft Agent Framework Same as the session body — no promise

SessionRecord.StateSchemaVersion (always stamped, never null) and WorkflowCheckpointRecord.StateSchemaVersion (null on a row written before this field existed) record Tracon’s own envelope generation. StateMafVersion on both records (null on an older row) records the Microsoft Agent Framework package version that wrote the body — this is what lets a failure message name the exact recorded and running versions instead of guessing.

What happens when a body cannot be read:

flowchart LR
    accTitle: Persisted payload restore decision
    accDescr: Restoring a session or checkpoint checks the recorded schema generation first, then attempts to deserialize the Microsoft Agent Framework body; either failure produces a defined error naming the recorded and current versions, and the row is never deleted or reset.
    A[Restore a session<br/>or checkpoint] --> B{Recorded schema<br/>generation}
    B -- newer than<br/>this build understands --> C[Defined error:<br/>recorded vs. current generation]
    B -- understood --> D[Attempt to deserialize<br/>the Microsoft Agent<br/>Framework body]
    D -- succeeds --> E[Restored normally]
    D -- fails --> F[Defined error:<br/>recorded vs. current<br/>Microsoft Agent Framework version]
    C --> G[Row is NOT deleted<br/>or silently reset]
    F --> G
  • The error names both compared facts: the recorded generation or Microsoft Agent Framework version, and the one this build runs. It never says a row “may have become unreadable” without saying why.
  • The row is never deleted or silently reset. A silently reset session loses conversation history with nothing telling the caller it happened.
  • You have two ways forward: open a new session or workflow run under a new identity, or clear old sessions and checkpoints before the upgrade if you do not need them to survive it.

Until now this page said what happens when a stored body cannot be read, but not which upgrades are supposed to work in the first place. This is that promise:

Any Tracon version can read the envelope written by any earlier version in the same major version line. You are never required to step through intermediate releases. Going from 1.0.0-preview.3 straight to 1.0.0-preview.19 is supported; so is any 1.x to any later 1.x.

A change that would break this is, by definition, a major version bump — the envelope generation (StateSchemaVersion) advances, and a build that meets a generation newer than it understands refuses with a defined error rather than guessing.

What the promise rests on: the repository keeps session and checkpoint state captured from real runs of earlier versions and reads it back with today’s code on every build. That is a test, not an intention — when it goes red, a release is blocked rather than shipped with a footnote.

tracon state-check asks the question while the old build is still serving traffic. It reads the database directly, so it needs no running application:

Terminal window
tracon state-check --provider postgres --connection "$TRACON_CONNECTION"

Run it with the new version of the tool against a copy of production data. It writes nothing — no row, no migration table entry, no lock — so it is also safe against the live database.

It reports two different things, and reads them out separately:

  • A count of every row, grouped by the envelope generation stamped on it, across every tenant. Each generation is marked readable or not by the build running the command. This part is complete.
  • A decode of a sample, at most --sample rows of each generation (default 5), deserialized through Microsoft Agent Framework. This part is a sample. A clean run says the rows that were read came back readable — it never says all of them would.

Exit code 0 means nothing was found that blocks reading; 3 means it found state this build cannot read. Two kinds of row are reported as checked for structure only and are never counted as failures: workflow checkpoints (whose payload has no decoder outside a running workflow) and sessions encrypted at rest (the CLI holds no content protection key). See the CLI guide for the full command surface, and Production deployment for what to do when it comes back red.

  1. Create a branch and update all Tracon packages together.
  2. Read the Release notes for public API, configuration, and migration changes — and whether the Microsoft Agent Framework version moved, which affects persisted session and checkpoint bodies (above).
  3. Build with warnings as errors and run the full test suite.
  4. Run tracon state-check with the new tool version against a copy of production data (above). A 3 here is a stop sign, not a warning.
  5. Start a disposable environment against a copy of production-shaped data.
  6. Inspect /api/meta, health checks, provider health, and migration diagnostics.
  7. Exercise one synchronous run, one streamed run, every enabled background service, and your approval and guard paths.
  8. Back up the database before the production migration. Deploy API and worker processes from the same artifact.
  9. Watch run failures, provider latency, queue depth, webhook delivery, and cost after the rollout.

Database migrations are forward-only. Do not assume that rolling back the application also rolls back the schema. See Production deployment for the migration and backup contract.

Include these facts when you report a defect:

  • the exact Tracon package versions from dotnet list package;
  • the .NET target framework and deployment runtime;
  • the model provider, model or Azure deployment name, and provider SDK version;
  • the storage engine and migration state;
  • the failing endpoint or .NET API and a minimal reproduction;
  • redacted diagnostics, logs, trace ids, and run ids.

Never attach credentials, connection strings, raw API keys, or unreviewed prompt and tool content.