Skip to content

feat: Add the override marker to the models and mark evaluations - #526

Draft
kinyoklion wants to merge 1 commit into
rlamb/overrides-python-filedata-reloaderfrom
rlamb/overrides-python-model-marker
Draft

kinyoklion wants to merge 1 commit into
rlamb/overrides-python-filedata-reloaderfrom
rlamb/overrides-python-model-marker

Conversation

@kinyoklion

Copy link
Copy Markdown
Member

Summary

This is the second step of the flag overrides port described by the OVERRIDE specification. It is based on the shared file data branch because the phases are stacked; retarget to feat/overrides once that branch merges.

The FeatureFlag and Segment models gain an override marker, is_override. The SDK represents stored entities with typed model classes distinct from their JSON form, so the marker is carried on the type and never in the JSON: to_json_dict(), the data kind encoder, and ModelEncoder produce the same output for a marked and an unmarked entity, and a persistent store never sees it. with_override_marker() returns a marked shallow copy that shares the definition and leaves the source unmarked, so a source can retain and resupply its entities. Only the SDK components that manage override entries (added in the next step) set the marker.

The evaluator marks an evaluation as override-affected when any definition it reads carries the marker: the evaluated flag, a prerequisite flag at any depth, or a segment consulted during clause matching. A read counts whether or not the segment matches, including through a negated clause and through a segment referenced by another segment's rule. A definition that cannot be resolved contributes nothing, because nothing was read. The marking propagates upward only: a prerequisite is an evaluation in its own right, its record reflects only the definitions its own subtree read, so an unaffected prerequisite inside a marked evaluation stays unmarked and a marked prerequisite does not mark a sibling. An evaluation that fails with an error reason, including a prerequisite cycle, is still marked when it read an override definition.

The marking is reported in two places that always agree: the overrideAffected indicator on the evaluation reason, present only when true so an unaffected reason serializes exactly as before, and EvalResult.override_affected, which the client and event processing read in the following steps. The AsyncEvaluator receives the same changes.

Tests cover each marking condition for both evaluators (flag off, fallthrough, and rule match; plain evaluations; malformed flag and prerequisite cycle errors; prerequisite records at depth one and two; unaffected sibling and downward non-propagation; segment reads with and without a match, negated, nested, and through a prerequisite; missing definitions; scalar and indicator agreement) and the model marker (default, marked copy, never serialized, shared definition).

@kinyoklion
kinyoklion force-pushed the rlamb/overrides-python-model-marker branch from 60d6f22 to 34322d8 Compare September 25, 2026 22:11
Adds an override marker to the FeatureFlag and Segment models. The marker is
an attribute of the model type, never part of the JSON representation, so
serializing an entity or writing it to a persistent store never carries it.
A marked shallow copy is produced with with_override_marker(), which leaves
the source entity unmarked and shares its definition.

The evaluator marks an evaluation as override-affected when any definition
it reads carries the marker: the evaluated flag, a prerequisite at any depth,
or a segment consulted during rule matching, including a segment that does
not match and a segment referenced by another segment. The marking propagates
upward only. A prerequisite's own record reflects the definitions its subtree
read, so an unaffected prerequisite inside a marked evaluation is not marked
and a marked prerequisite does not mark a sibling. An evaluation that fails
with an error reason is still marked when it read an override definition.

The marking appears on the evaluation reason as the overrideAffected
indicator, present only when true, and on EvalResult.override_affected for
the client and event processing. The async evaluator mirrors the sync one.
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-python-filedata-reloader branch from c478bd1 to 7e64242 Compare September 28, 2026 20:28
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-python-model-marker branch from 34322d8 to ee313c1 Compare September 28, 2026 20:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant