Versioned contract-surface isolation contracts¶
Versioned contract-surface isolation contracts keep one statically selected
contract surface from exposing types belonging to another version or to an
implementation surface. They build on the existing contract-surface exposure
analysis, but let each rule name its source and forbidden surfaces locally.
This is an additive policy family; existing contract_surface_exposure
contracts are unchanged.
Groups:
strict_versioned_contract_surface_isolationaudit_versioned_contract_surface_isolation
Example¶
contracts:
strict_versioned_contract_surface_isolation:
- id: orders-v1-isolation
name: orders-v1-isolation
surfaces:
- id: v1-contracts
types_matching:
namespace: Acme.Orders.V1.Contracts
- id: v2-contracts
types_matching:
namespace: Acme.Orders.V2.Contracts
- id: transport-implementation
types_matching:
role: TransportImplementation
source_surface: v1-contracts
forbidden_surfaces:
- v2-contracts
- transport-implementation
reason: V1 contracts must not disclose newer-version or transport types.
Strict and audit migration choices¶
Version isolation is a local grouping of the same recursive contract-surface
evidence. It does not create API membership or a second snapshot: use
public_api_surface.surface_selector and the existing capture/diff/update
snapshot lifecycle for that. Then use this family to state which locally
selected version or implementation surfaces are forbidden in the source
surface. Pair it with attribute usage when the marker
used for membership must itself be confined to a location.
An established v1 boundary can be strict while a v2 boundary is discovered in
audit mode. The server-style example below uses only the supported local
surfaces, types_matching, source_surface, and forbidden_surfaces
fields:
contracts:
strict_versioned_contract_surface_isolation:
- id: orders-v1-isolation
name: orders-v1-isolation
surfaces:
- id: v1-dtos
types_matching:
namespace: Acme.Orders.Api.Contracts.V1
- id: v2-dtos
types_matching:
namespace: Acme.Orders.Api.Contracts.V2
- id: domain-types
types_matching:
namespace: Acme.Orders.Domain
- id: persistence-types
types_matching:
namespace: Acme.Orders.Persistence
source_surface: v1-dtos
forbidden_surfaces: [v2-dtos, domain-types, persistence-types]
reason: V1 DTOs must not expose a newer contract or domain/persistence implementation types.
audit_versioned_contract_surface_isolation:
- id: orders-v2-isolation-migration
name: audit-orders-v2-isolation-before-enforcement
surfaces:
- id: v1-dtos
types_matching:
namespace: Acme.Orders.Api.Contracts.V1
- id: v2-dtos
types_matching:
namespace: Acme.Orders.Api.Contracts.V2
- id: persistence-types
types_matching:
namespace: Acme.Orders.Persistence
source_surface: v2-dtos
forbidden_surfaces: [v1-dtos, persistence-types]
reason: Discover reverse-version and persistence leaks before enforcing v2 isolation.
The source and target surface IDs above are policy-local labels. For each
source root, the checker reuses the exposure family's recursive CLR traversal,
including nested generic arguments, tuple elements, arrays, wrappers, and
compiled metadata. A Task<Envelope<Acme.Orders.Api.Contracts.V2.Customer>>
visible from a v1 root therefore reaches the v2 Customer target. Distinct
paths and same-named types from different namespaces or assemblies remain
distinct findings.
The same pattern supports a library/runtime-editor split when the runtime surface must not expose editor implementation types:
contracts:
strict_versioned_contract_surface_isolation:
- id: game-runtime-isolation
name: game-runtime-isolation
surfaces:
- id: runtime-contracts
types_matching:
has_attribute: Acme.Game.Runtime.PublicApiContractAttribute
- id: editor-implementation
types_matching:
namespace: Acme.Game.Editor
source_surface: runtime-contracts
forbidden_surfaces: [editor-implementation]
reason: Runtime library contracts must not expose Unity editor implementation types.
audit_versioned_contract_surface_isolation:
- id: game-runtime-isolation-migration
name: audit-runtime-editor-isolation
surfaces:
- id: runtime-contracts
types_matching:
namespace: Acme.Game.Runtime.Contracts
- id: editor-implementation
types_matching:
namespace: UnityEditor
source_surface: runtime-contracts
forbidden_surfaces: [editor-implementation]
reason: Find runtime-to-editor exposure while the Unity boundary is being migrated.
PublicApiContractAttribute in this example is user-owned selector evidence
for the rule-local runtime-contracts group; it is not itself a reviewed
public-API membership declaration. Define reviewed membership separately with
public_api_surface.surface_selector when that boundary is needed. Either use
does not overwrite an existing ValueObject, Entity, Controller, or
Adapter primary semantic role. There is no built-in marker package or
Unity-specific magic preset.
Each rule has a non-blank id and name, one local non-empty surfaces
list, one source_surface, and one or more forbidden_surfaces. Every
surface has a unique non-blank id and a non-empty types_matching selector.
Surface references resolve only within the same rule. Blank, duplicate,
unknown, empty, unbounded, or self-referential declarations are invalid policy
configuration.
Selecting surfaces¶
types_matching reuses the bounded selector vocabulary used by other contract
families:
| Field | Matches |
|---|---|
name_suffix / name_prefix |
Simple type name |
namespace |
A namespace or one of its child namespaces |
layer |
A namespace resolved to a declared layer |
base_type |
A type whose base-type chain includes the named type |
implements_interface |
A type implementing the named interface |
has_attribute |
A type carrying the named full-name custom attribute |
role |
The type's existing winning semantic role |
All populated fields combine with AND semantics. The selector observes existing classification and metadata; it does not add a second role, invent tags, or introduce regex or free-form expressions. Surface IDs are policy labels, not CLR type identities.
The source surface selects exported visible roots. A forbidden surface selects target types from the complete exposure universe, including first-party non-exported implementation types when they occur in an exposure path. Thus a v1 contract can be protected from both v2 contracts and internal transport types.
What is inspected¶
For each source root, the checker follows recursively visible CLR signatures
and compiled contract metadata. It traverses nested generic arguments, tuple
elements, arrays, wrappers, and other metadata-supported signature shapes. For
example, exposing Task<Envelope<V2.Customer>> produces a finding for the
v2 Customer target when it matches a forbidden surface.
The checker emits one deterministic finding per distinct source/path/target occurrence. Findings retain the source surface, declaring type/member or metadata site, deterministic exposure path, and assembly-qualified target identity. Same-named types from different namespaces or assemblies remain distinct, as do distinct paths to the same target. Strict and audit findings reuse the existing contract-surface exposure diagnostic payload, canonical identity, suppression/ignore, baseline, Human, JSON, SARIF, and Testing projections.
Applicability and failures¶
Each effective rule contributes one required applicability record. A rule is unassessable, rather than clean, when required evidence is unavailable or unexpectedly empty, including:
- a referenced source or forbidden surface matches zero current types;
- the source surface has zero exported roots;
- required type or recursive exposure evidence is incomplete; or
- the target universe is unavailable.
These records carry deterministic reason and provenance through the normal governance lifecycle. Zero leak findings are a clean result only when the source and every referenced target surface are evaluable. Strict mode blocks on findings; audit mode reports the same normalized findings non-blockingly.
Scope¶
This family is static contract-surface isolation only. It does not:
- route runtime endpoints or negotiate API versions;
- execute serializers, payloads, or runtime schemas;
- decide semantic-version increments or compatibility policy; or
- perform binary/package compatibility analysis.
It also does not change public-API snapshots, semantic roles, runtime configuration, or the behavior of generic contract-surface exposure rules. It does not execute runtime serialization, route endpoints, negotiate API versions, or perform data-flow analysis; those are outside this static, reflection/metadata-based family.