MentionTypeDriftQuarantinePolicy

The shipped DriftQuarantinePolicy: quarantine a proposition when one of its entity mentions names a type the schema change made lossy. Lossy means the change can strand data that was already extracted:

  • the type was removed, so nothing describes those mentions any more;

  • the type kept its name and lost labels or whole properties;

  • a property's shape narrowed: its value type or reference target changed to something outside SAFE_TYPE_WIDENINGS, it flipped between holding a value and pointing at another type, or its cardinality shrank (a list collapsing to a single value, an optional becoming required). This covers a property that kept its name and one that was renamed under a declared alias; both carry a before and an after signature, and both are judged by the same rule.

Additive changes never trigger quarantine: new types, new labels, new properties, and cardinality moving the other way, since a single value becoming a list still holds everything it held before. The diff itself makes no judgement. MetamodelChange.PropertySignatureChanged states that age went from string to integer, and this policy decides that stranding is possible and pulls the affected propositions out of normal use until a person looks.

Declared renames

A rename is a declared fact about one type or property, so on its own it strands nothing. MetamodelChange.EntityTypeRenamed and MetamodelChange.PropertyRenamed are non-lossy per se, and MetamodelChange.EntityTypeAliasesChanged never quarantines at all: it says the declaration's list of former names moved, and no label, property or relationship went with it.

Whatever else moved on a renamed type is reported under the type's new name, and the data in the graph still carries the old one. So a mention type is matched against its own name plus every current type name that used to go by it, read off the newer version's whole declared alias map. Those accumulate, so a type renamed ABC declares {A, B} and a lossy change on C quarantines propositions mentioning A, B or C alike.

Matching reads the declaration rather than this diff's rename entries, so it holds when the rename and the loss land in different releases: a diff that only drops a property from a type renamed two stamps ago still reaches data written under the old name.

A removed type is resolved from the older version instead, since it has no entry on the newer side at all. Removing C after it had gone by {A, B} strands data under all three names, and the removal is matched under all three. The exception is a former name the newer version declares as a live type of its own: reusing a retired name is legal once its claimant is gone, and data under it is judged as that live type's.

A former name the declaration deliberately retires stops matching. Retiring is a statement that the schema no longer claims the name, and from then on data still carrying it is reported by the observed-side comparison as ordinary undeclared drift.

A rename's own propagation — the type's own label swapping, a referrer's signature pointing at the new name, a child's inherited label — folds into MetamodelChange.EntityTypeRenamed in the diff and never reaches this policy as loss.

Two spellings of one type name

A declared name can be fully qualified where the data is simple. A JVM-backed type is declared as com.example.Person, extraction records the mention as Person, and the graph writes Person as the label. Matching on the declared spelling alone would read a lossy change on com.example.Person as touching nothing at all, and a schema that dropped the type outright would leave every proposition it stranded looking healthy.

So every name this policy matches on — removed types, types that lost shape, declared former names — is registered under both spellings: the name as declared, and the label it writes onto a node (DeclaredSchema.ownLabelOf). This is the same cut the declared-vs-observed comparison makes, so the two halves of a drift check agree about which type is which. Two declared types in different packages share one label, and a graph can't tell them apart either, so a mention under that label is checked against both.

Matching a mention under the other spelling of its own type is ordinary matching, so it never shows up in the reason as a former name.

Value types

A changed value type counts as lossy in both directions except for the four promotions in SAFE_TYPE_WIDENINGS. Outside those, we know the declared type names moved and we don't know how the backend stored the values or whether the new type can read the old ones, and guessing wrong in the permissive direction leaves unreadable data looking healthy. A changed reference target is always lossy: it names a different entity type, which is not a promotion of anything.

Quarantining moves the proposition to PropositionStatus.QUARANTINED, annotates it under DiceMetadataKeys.QUARANTINE_REASON, and records the status it came from. All of that produces an immutable copy; the original is never mutated, and persisting the copies is the caller's job.

A proposition an earlier sweep already quarantined comes back as QuarantineDecision.AlreadyQuarantined, untouched, with its original reason preserved and outside the conforming bucket. That holds for any diff, an empty one included, because being already quarantined is a fact about the proposition — its status says so.

Pinned propositions

A pinned proposition a lossy change would otherwise catch is never flipped to STALE. Pinning is DICE's cross-cutting promise that a proposition resists reclamation, the same promise the decay collector, the sweep policy and contradiction resolution already honor, and quarantine is one more reclamation path that has to keep it. The match still gets reported, as QuarantineDecision.Protected, so an operator can see what the schema change would have caught without the proposition itself being touched. A proposition an earlier sweep already quarantined before it was pinned is unaffected by this: it still comes back as QuarantineDecision.AlreadyQuarantined, since idempotency is checked first.

Rename awareness and the widening allow-list are experimental: behavior may change before 1.0.

Constructors

Link copied to clipboard
constructor()

Types

Link copied to clipboard
object Companion

Functions

Link copied to clipboard
open override fun candidateMentionTypes(diff: <Error class: unknown class>): Set<String>

Every mention type name this policy could match under diff, which is what a bounded sweep asks its store for.

Link copied to clipboard
open override fun evaluate(diff: <Error class: unknown class>, propositions: Iterable<Proposition>): QuarantineResult

Evaluate every proposition against diff.