Skip to content

feat: Mark evaluation events as override-affected - #366

Merged
kinyoklion merged 2 commits into
dotnet-server-sdk-v9from
rlamb/overrides-dotnet-events
Oct 29, 2026
Merged

kinyoklion merged 2 commits into
dotnet-server-sdk-v9from
rlamb/overrides-dotnet-events

Conversation

@kinyoklion

@kinyoklion kinyoklion commented Sep 25, 2026 •

Copy link
Copy Markdown
Member

Summary

Makes the server SDK hand the override-affected marking to the event processor, as defined by the OVERRIDE specification. EventProcessorTypes.EvaluationEvent gains an OverrideAffected property, EventFactory sets it from the evaluation result for every evaluation event and every prerequisite record, and DefaultEventProcessorWrapper passes it to the InternalSdk event processor, which then suppresses individual feature and debug events for the marked evaluation and counts it under a separate summary counter that carries the marker. The marking is carried whether or not the caller asked for reasons.

A wrong-type result keeps the marking: the replacement error reason and the event for that evaluation both report it, because the evaluation read the same definitions. The specification's test vectors now also assert the per-evaluation summary contribution, and new client tests cover the marked evaluation record, the prerequisite tree in which only one leaf is overridden, and the event output of a full flush, in which only the unaffected sibling prerequisite produces a feature event while all four flags have tracking on.

It depends on the InternalSdk events change, #365, released as LaunchDarkly.InternalSdk 3.12.0-alpha, which the server now references.

SDK-3248


Note

Overview
The server SDK now passes override-affected marking from evaluations into analytics: EvaluationEvent gains OverrideAffected, EventFactory sets it from the evaluation reason (including prerequisite and wrong-type default-value events), and DefaultEventProcessorWrapper forwards it to InternalSdk so marked evaluations skip individual feature/debug events and appear in summary counters with the marker—even when the caller did not request reasons.

The package reference bumps to LaunchDarkly.InternalSdk 3.12.0-alpha (depends on the InternalSdk events change). Override vector tests assert summaryOverrideAffected, and new LdClientOverrideEventsTest coverage exercises marking, prerequisite trees, wrong-type paths, flush output, and the default event processor wrapper.

Reviewed by Cursor Bugbot for commit 2b8016e. Bugbot is set up for automated code reviews on this repo. Configure here.

Opened from termag

@kinyoklion
kinyoklion force-pushed the rlamb/overrides-dotnet-events-internal branch from 9387b50 to 3aacd97 Compare September 28, 2026 20:39
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-dotnet-events branch from 86caaa5 to 9ddeb28 Compare September 28, 2026 20:39
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-dotnet-events-internal branch from 3aacd97 to 471a60c Compare October 1, 2026 23:43
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-dotnet-events branch from 9ddeb28 to 75135db Compare October 1, 2026 23:44
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-dotnet-events-internal branch from 471a60c to a750428 Compare October 3, 2026 01:34
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-dotnet-events branch from 75135db to 89329c5 Compare October 3, 2026 01:34
@kinyoklion

Copy link
Copy Markdown
Member Author

bugbot review

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

@kinyoklion
kinyoklion force-pushed the rlamb/overrides-dotnet-events-internal branch from a750428 to c9ce308 Compare October 3, 2026 01:52
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-dotnet-events branch from 89329c5 to 403d56e Compare October 3, 2026 01:52
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-dotnet-events-internal branch from c9ce308 to 18760a7 Compare October 5, 2026 21:55
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-dotnet-events branch from 403d56e to b085719 Compare October 5, 2026 21:55
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-dotnet-events-internal branch from 18760a7 to 3941b93 Compare October 5, 2026 22:12
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-dotnet-events branch from b085719 to 2b54c3e Compare October 5, 2026 22:12
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-dotnet-events-internal branch from 3941b93 to 17cf04b Compare October 5, 2026 22:28
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-dotnet-events branch from 2b54c3e to 4ec85dc Compare October 5, 2026 22:28
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-dotnet-events-internal branch from 17cf04b to fb8c7ac Compare October 5, 2026 23:56
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-dotnet-events branch from 4ec85dc to 368f8fa Compare October 5, 2026 23:56
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-dotnet-events-internal branch from fb8c7ac to e0080d0 Compare October 13, 2026 21:03
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-dotnet-events branch from 368f8fa to f12a5f6 Compare October 13, 2026 21:03
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-dotnet-events-internal branch from e0080d0 to 3469298 Compare October 13, 2026 22:13
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-dotnet-events branch from f12a5f6 to 6d2b453 Compare October 13, 2026 22:13
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-dotnet-events-internal branch from 3469298 to 1531cd9 Compare October 16, 2026 21:21
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-dotnet-events branch from 6d2b453 to 4e21dce Compare October 16, 2026 21:21
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-dotnet-events-internal branch from 1531cd9 to ac67f94 Compare October 23, 2026 23:50
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-dotnet-events branch from 4e21dce to 28e15b9 Compare October 23, 2026 23:50
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-dotnet-events-internal branch from ac67f94 to e4a339c Compare October 26, 2026 22:46
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-dotnet-events branch from 28e15b9 to 8bd83fe Compare October 26, 2026 22:46
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-dotnet-events-internal branch from e4a339c to e6beb28 Compare October 26, 2026 23:13
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-dotnet-events branch from 8bd83fe to d116173 Compare October 26, 2026 23:13
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-dotnet-events-internal branch from 693bae7 to 0da3d0f Compare October 28, 2026 20:38
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-dotnet-events branch from 511c88a to 6735a14 Compare October 28, 2026 20:38
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-dotnet-events-internal branch from 0da3d0f to 917b778 Compare October 28, 2026 21:04
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-dotnet-events branch from 6735a14 to 73a926e Compare October 28, 2026 21:04
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-dotnet-events-internal branch from 917b778 to 96a4008 Compare October 28, 2026 21:23
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-dotnet-events branch from 73a926e to 9de4329 Compare October 28, 2026 21:23
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-dotnet-events-internal branch from 96a4008 to 521a20b Compare October 28, 2026 21:53
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-dotnet-events branch from 9de4329 to e148541 Compare October 28, 2026 21:53
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-dotnet-events-internal branch from 521a20b to f7eafb9 Compare October 28, 2026 22:02
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-dotnet-events branch from e148541 to 69a4fcf Compare October 28, 2026 22:02
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-dotnet-events-internal branch from f7eafb9 to 52b82ae Compare October 28, 2026 22:35
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-dotnet-events branch from 69a4fcf to 9d0b734 Compare October 28, 2026 22:35
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-dotnet-events-internal branch from 52b82ae to 56e48ec Compare October 29, 2026 16:45
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-dotnet-events branch from 9d0b734 to 9259cff Compare October 29, 2026 16:45

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread pkgs/sdk/server/src/Internal/Events/DefaultEventProcessorWrapper.cs
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-dotnet-events branch from 9259cff to 19628f5 Compare October 29, 2026 20:23
Base automatically changed from rlamb/overrides-dotnet-events-internal to dotnet-server-sdk-v9 October 29, 2026 20:36
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-dotnet-events branch from 19628f5 to 7f9e390 Compare October 29, 2026 20:39
EventProcessorTypes.EvaluationEvent gains an OverrideAffected property.
The event factory sets it from the evaluation result for every evaluation
event and prerequisite record, whether or not the caller asked for
reasons, and the event processor wrapper passes it through, so a marked
evaluation produces no individual feature or debug event and is counted
under a separate summary counter. A wrong-type result keeps the marking
on its reason and its event. The OVERRIDE specification's test vectors
now also assert the per-evaluation summary contribution.
The server uses the override-affected event marking that InternalSdk
3.12.0-alpha adds.
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-dotnet-events branch from 7f9e390 to 2b8016e Compare October 29, 2026 20:53
@kinyoklion

Copy link
Copy Markdown
Member Author

bugbot review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 2b8016e. Configure here.

@kinyoklion
kinyoklion merged commit 7c873a0 into dotnet-server-sdk-v9 Oct 29, 2026
10 checks passed
@kinyoklion
kinyoklion deleted the rlamb/overrides-dotnet-events branch October 29, 2026 21:08
kinyoklion added a commit that referenced this pull request Oct 29, 2026
<!-- TERMAG_PR_BODY_BEGIN -->
## Summary

Adds the file-based override source, as defined by the OVERRIDE
specification, on top of the shared file loading code and the override
layer from the earlier changes in this series. The series targets
`dotnet-server-sdk-v9`, the branch that prepares the next major version.
The existing file data source keeps its current behavior: it does not
use the shared code, and nothing in this change touches it.

Configuration: `FileOverrides.Source()` returns a
`FileOverrideSourceBuilder` that is passed to
`DataSystemBuilder.Overrides(...)`. Its options are `FilePaths` (one or
more files, in precedence order, required), `DuplicateKeysHandling`
(`Fail`, the default, or `Ignore`, which keeps the first configured
file's entry), `ChangeDetection` (`Polling`, the default, or
`Watching`), `PollInterval` (one second by default and at minimum; a
lower value is raised to the minimum with a warning), and `Parser`,
which registers a parsing function for a file extension, for formats
other than JSON such as YAML. Building a source with no file paths, with
an option outside its enumeration, or with a YAML file for which no
parser is registered, throws, so the client constructor fails the same
way it does for other invalid component configuration. A path listed
more than once is logged at Warn and the repeats are ignored; the first
occurrence keeps its place in the order. Every public member carries the
note that flag overrides are currently experimental and subject to
change.

Behavior: the files use the file data document format (`flags`,
`flagValues`, `segments`); a `flagValues` entry expands to a flag that
is on and serves its single value by fallthrough, so it evaluates with
the `FALLTHROUGH` reason. The parser for a file is chosen by its
extension, and a file whose extension has no registered parser is parsed
as JSON. A flag or segment is listed under its key; a `key` property in
the definition is optional and must be equal to it, and a `version`
property is optional. The initial load runs inside the client
constructor. Change detection is in place before the initial load, so a
change made between the two is not missed. In watching mode a configured
path that is a link to a file in another directory is detected only
through the periodic directory check; polling, the default, follows the
link directly. A configured file that does not exist contributes no
overrides and produces no error log; creating it later adds its
overrides and deleting it removes them. A file that exists but cannot be
read or parsed, or a duplicate key under `Fail`, fails that whole
reload: the previously loaded overrides stay in effect, the failure is
logged, and the source retries after a bounded delay and on the next
change. Every applied change is logged at Info level with the overrides
in effect and what each configured file supplied (for example `Flag
overrides in effect: 2 flags, 1 segment (/etc/ld/a.json: 2 flags, 1
segment; /etc/ld/b.json: absent)`). Polling examines the files'
modification time, size, and existence once per interval; watching uses
file system notifications on each file's directory.

YAML: the SDK does not bundle a YAML parser, following the existing file
data source, so YAML files require the application to register a parser
for the `.yaml` or `.yml` extension through the `Parser` option. A YAML
file configured without one is reported when the client is created, not
on every reload. The specification allows the application to supply the
parser.

Tests cover the builder validation and defaults, including the YAML
check, every file behavior above in both change detection modes, the
Info log lines, and client end-to-end scenarios in which an operator
adds, changes, and removes an override on a running client that never
obtains LaunchDarkly data, and in which a YAML override file is read
with the registered parser.

It depends on the server events change, #366, which is merged into
`dotnet-server-sdk-v9`.

[SDK-3248](https://launchdarkly.atlassian.net/browse/SDK-3248)


[SDK-3248]:
https://launchdarkly.atlassian.net/browse/SDK-3248?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Overview**
> Adds the **built-in file override integration** so operators can force
flag/segment values from local files via
`DataSystemBuilder.Overrides(FileOverrides.Source()...)`, without
restarting the client.
> 
> The public surface is `FileOverrides`, `FileOverrideSourceBuilder`,
and `FileOverrideTypes` (duplicate-key policy, polling vs watching, poll
interval, optional per-extension parsers for YAML). The internal
`FileOverrideSource` merges multiple paths in order, loads synchronously
on startup, hot-reloads through shared file-loading helpers, pushes full
snapshots to the override layer, and logs what is in effect per file.
Failed reloads keep the last good snapshot; missing files are ignored
until they appear.
> 
> Docs on `DataSystemBuilder.Overrides` and `IOverrideSource` now point
at `FileOverrides`. Tests cover builder validation, reload/merge/error
behavior, and end-to-end `LdClient` evaluation (including
`OverrideAffected` and client-side `AllFlagsState` rules).
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
feaf592. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
<!-- TERMAG_PR_BODY_END -->

<!-- termag: box=i-0575d1c54853058d7 project=projects -->
<sub>Opened from termag</sub>
kinyoklion pushed a commit that referenced this pull request Oct 29, 2026
…ha.1 (#382)

🤖 I have created a release *beep* *boop*
---


##
[9.0.0-alpha.1](LaunchDarkly.ServerSdk-v9.0.0-alpha.0...LaunchDarkly.ServerSdk-v9.0.0-alpha.1)
(2026-10-29)


### Features

* Add a shared file data reloader, poller, and watcher
([#361](#361))
([4eed241](4eed241))
* Add the file-based override source
([#367](#367))
([b834131](b834131))
* Add the override marker to the flag and segment models and mark
evaluations
([#363](#363))
([21f666a](21f666a))
* Add the override store, overlay, source interfaces, and data system
wiring ([#364](#364))
([e1748a1](e1748a1))
* Mark evaluation events as override-affected
([#366](#366))
([7c873a0](7c873a0))
* Split summary counters by overrideAffected and suppress individual
events for marked evaluations
([#365](#365))
([c9b327b](c9b327b))
* Support the flag-overrides capability in the contract test service
([#368](#368))
([b80d6aa](b80d6aa))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Overview**
> This is a **Release Please** cut that publishes
**LaunchDarkly.ServerSdk `9.0.0-alpha.1`**. The diff only updates
release metadata: `.release-please-manifest.json`,
`LaunchDarkly.ServerSdk.csproj` `<Version>`, `PROVENANCE.md`
verification example, and a new **`9.0.0-alpha.1`** section in
`CHANGELOG.md`.
> 
> The changelog entry documents what ships in this alpha (already merged
elsewhere): **local flag overrides** via override store/overlay/source
wiring and a **file-based override source** with shared file
reloader/poller/watcher; **override markers** on flag/segment models and
evaluations; **analytics** changes that mark override-affected
evaluations, split summary counters by `overrideAffected`, and suppress
individual events when appropriate; plus **contract-test** support for
the flag-overrides capability.
> 
> Reviewers can treat this as a **versioning and release-notes sanity
check**—no application logic changes appear in the diff itself.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
81f0588. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
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.

2 participants