Skip to content

[AI-FSSDK] [FSSDK-12813] Normalize decision event campaign_id, variation_id, and entity_id#401

Open
jaeopt wants to merge 5 commits into
masterfrom
ai/jaeopt/FSSDK-12813-holdout-event
Open

[AI-FSSDK] [FSSDK-12813] Normalize decision event campaign_id, variation_id, and entity_id#401
jaeopt wants to merge 5 commits into
masterfrom
ai/jaeopt/FSSDK-12813-holdout-event

Conversation

@jaeopt

@jaeopt jaeopt commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Normalize three ID fields on outgoing decision events — decisions[].campaign_id, decisions[].variation_id, and impression events[].entity_id — so the wire payload is byte-equivalent across SDKs for every decision type (experiment, feature test, rollout, holdout). The normalization path is silent and never drops, defers, or fails event dispatch.

Per the updated FSSDK-12813 spec, campaign_id and entity_id accept any non-empty string (numeric or opaque, e.g. "default-12345", "layer_abc"); the fallback to experiment_id fires only when the value is empty string, nil, or missing. variation_id keeps the stricter numeric-string-only contract — empty/whitespace/non-numeric values normalize to nil. Non-string types are out of scope.

Changes

  • Added a shared Helpers::EventIdValidator module with two predicates: non_empty_string? (used for campaign_id and entity_id, accepts any non-empty string including opaque IDs) and numeric_string? (used for variation_id, requires decimal digits only). The normalization helpers apply the spec's substitution rules: campaign_id falls back to experiment_id only when empty/nil; invalid variation_id becomes nil.
  • Routed every impression decision through the new normalization helper in EventFactory#create_impression_event_visitor so the rule applies uniformly to experiment, feature test, rollout, and holdout decisions with no per-type branching.
  • Guaranteed that the impression events[].entity_id reuses the same normalized campaign_id byte-for-byte (impression-only; conversion event entity_id is unchanged).
  • Added unit coverage for both predicates and the normalizers (including opaque/whitespace passthrough for campaign_id and nil-fallback for variation_id) and integration coverage on the EventFactory wire payload covering nil, empty, whitespace, non-string, non-numeric-placeholder, and opaque-prefixed (e.g. default-12345) inputs, plus a holdout scenario.
  • Updated a pre-existing wire-payload assertion in spec/project_spec.rb to expect variation_id: nil (was '') for the rollout-no-match path, per the variation_id normalization rule (FR-011).

Jira Ticket

FSSDK-12813

jaeopt added 5 commits June 25, 2026 08:32
…ing per updated spec

Per updated FSSDK-12813 spec, campaign_id and entity_id now accept any
non-empty string (numeric or opaque, e.g. "default-12345", "layer_abc").
The fallback to experiment_id fires ONLY when the value is empty string,
nil, or missing. variation_id retains the stricter numeric-string-only
contract — empty/whitespace/non-numeric values still normalize to nil.

Non-string types remain out of scope per spec.

Changes:
- EventIdValidator: introduce non_empty_string? predicate; use it for
  campaign_id normalization while keeping numeric_string? for variation_id.
- Unit tests: add non_empty_string? coverage; update normalize_campaign_id
  tests so opaque/whitespace inputs pass through (per relaxed FR-001).
- EventFactory integration tests: flip non-numeric/whitespace campaign_id
  cases from fallback assertions to passthrough assertions.
- project_spec.rb: update a pre-existing wire-payload assertion to expect
  variation_id: nil (was '') per the variation_id normalization.
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