Contracts change. Entities gain new states, rules are updated, operations are added or removed. Tenor provides tooling to analyze these changes, classify their impact, and generate migration plans.
Structural Diff
Compare two versions of a contract:
tenor diff v1.json v2.jsonThe diff output shows every structural change: added, removed, and modified constructs. Each change is annotated with its impact level.
To see only breaking changes:
tenor diff v1.json v2.json --breakingFull Migration Analysis
For a complete migration plan:
tenor migrate v1.json v2.jsonThis produces a migration report including: all structural changes, their classifications, required actions, and recommendations for in-flight workflows.
Breaking Change Taxonomy
Every change is classified into one of four categories:
BREAKING
Changes that alter the contract's observable behavior in ways that existing consumers cannot handle without modification.
- Removing a state from an entity
- Removing a persona from an operation's
allowed_personas - Changing a fact's type
- Removing a verdict that operations depend on
- Removing a transition that flows depend on
NON_BREAKING
Changes that are backward-compatible. Existing consumers continue to work without modification.
- Adding a new state to an entity (with appropriate transitions)
- Adding a new fact
- Adding a new persona
- Adding a new operation
- Adding a new verdict (from a new rule)
REQUIRES_ANALYSIS
Changes where the impact depends on the current state of the system. These require human review.
- Adding a precondition to an existing operation (may block currently-valid actions)
- Changing a rule's
whenclause (may change which verdicts fire) - Modifying flow step ordering
INFRASTRUCTURE
Changes that affect tooling and adapters but not behavioral semantics.
- Changing a source declaration
- Updating source endpoints or protocols
- Modifying error contract messages
Migration Safety Rules
Never remove a state that entities currently occupy. If any entity instance is in state
X, you cannot removeXfrom the state set. Migrate entities out of the state first.Never remove a transition that in-flight flows depend on. If a flow step has an effect
(Entity, A, B)and that transition is removed, in-flight flows break.Add before remove. When renaming a state, add the new state first, migrate, then remove the old state.
Verdicts are the API. If external systems depend on specific verdicts, changes to rules that produce those verdicts are breaking changes regardless of internal restructuring.
In-Flight Flow Migration Policies
When a contract changes while flows are executing, three policies are available:
Blue-Green
Run both contract versions simultaneously. In-flight flows complete under the old version. New flows start under the new version. Once all old-version flows complete, the old version is retired.
tenor migrate v1.json v2.json --policy blue-greenThis is the safest approach. No in-flight flow is affected by the change.
Force-Migrate
Apply the new contract to all flows, including in-flight ones. The migration tool checks whether each in-flight flow can continue under the new contract.
tenor migrate v1.json v2.json --policy force-migrateIf any in-flight flow cannot continue (e.g., it depends on a removed state or verdict), the migration tool reports the conflict and suggests remediation.
Abort
Abort all in-flight flows and restart under the new contract.
tenor migrate v1.json v2.json --policy abortThis is the simplest approach but may lose in-progress work. Use it only when in-flight flows can be safely discarded.
Best Practices
- Version your contracts. Use semantic versioning. Breaking changes increment the major version.
- Run
tenor diffin CI. Catch breaking changes before they reach production. - Test migrations with
tenor check. After applying a migration, run the full analysis suite against the new contract. - Document REQUIRES_ANALYSIS changes. These need human judgment. Include the analysis context in your change review.