5 Commits
Author SHA1 Message Date
Facundo MenzellaandClaude Opus 5.5 4a87bf64f3 other: send fallback_original_step_id and always send blob_ref on workflow events (#7878)
* Send fallback_original_step_id and always send blob_ref on workflow events

khepri publishes ~f fallback copies of screens for audience-targeted
experiments (WFL-626). Workflow events now carry the copy's
fallback_original_step_id and send blob_ref whenever the SDK has one, so
khepri resolves each step against its exact blob. Experiment params are
read from step metadata first, then param_values.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Trim workflow event comments

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Drop legacy blob_ref decoding for queued workflow events

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Revert unrelated Package.resolved change

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Rename experimentParam to experimentValueFromMetadataOrParams

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-07 11:07:10 +02:00
Facundo MenzellaandClaude Opus 5 02b4a0a850 other: echo step experiment params on workflow events (#7659)
* Parse workflow experiment enrollment from the config item combo_key

The workflows topic serves each workflow item with a combo_key that
encodes the experiment variants the user was bucketed into
(experiment_id=variant_key pairs joined by ;, or default). Parse it
into WorkflowDataResult.enrolledVariants on every read path so steps
can report experiment exposure in a follow-up. Workflows without an
experiment keep enrolledVariants nil.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Tighten combo_key parsing after review

Drop the redundant default sentinel guard (no pair parses out of it
anyway), keep the moved helpers private, pin that = inside a variant
key is kept, and trim the doc comments.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Echo the step's experiment params on workflow step events

Khepri bakes experiment_id and experiment_variant into the steps of the
enrolled variant (khepri #24946, one experiment per screen) and enrolls
the subscriber on the first step_started that echoes them. Read both
from the step's param_values and put them on every step event.

Drops the combo_key parsing from the previous commits: enrollment must
not be derived from what the config endpoint served, only from exposure.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Trim comments to the ones that carry information

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Reword the experiment param comments

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Plainer wording on the experiment param comments

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Address review: plainer param docs and wire coverage for close

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Delete isLastVariantStep from the SDK

Khepri computes it server side and never puts it in the workflow blob,
so the SDK has nothing to read or send.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Report the workflow blob ref on step events

Khepri needs the blob the client rendered to validate the experiment
params it reports, instead of trusting them. Carry the config item's
blob_ref from the workflows topic through to every step event.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Group the experiment fields and name the ref workflowBlobRef

Per the thread: the blob ref only means something as proof of an
experiment claim, so it travels with the experiment instead of riding
on every workflow event. SDK-level naming is workflowBlobRef so it
does not read as a bare config-layer concept; the wire keys stay
experiment_id, experiment_variant and blob_ref.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Drop comments that restate the code

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Require the workflow blob ref alongside the experiment params

A workflow can only be read through a blob, so a rendered step that has
experiment params always has a ref. Making it required means khepri
never receives a claim it cannot validate.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Carry the blob ref through the checkpoint presentation path

Checkpoint workflows build their context through WorkflowPreview, which
left the ref nil, so a checkpoint-presented experiment step reported
nothing once the ref became required. Thread it from the resolved
workflow. Injected dashboard previews still pass none.

ResolvedCheckpointWorkflow moves to its own file, unchanged, to keep the
resolver under the file length limit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Register ResolvedCheckpointWorkflow.swift in RevenueCat.xcodeproj

The legacy project enumerates sources by hand, so a file added outside
Xcode is invisible to it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Name the request property workflowBlobRef

Matches the Android rename; wire name stays blob_ref.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-10 15:30:46 +02:00
Cesar de la Vega c29512a1d5 Rename workflow event names to singular (#7099) 2026-07-21 17:18:07 +02:00
Facundo MenzellaandClaude Opus 4.8 d3b401a39e feat(workflows): add workflows_close abandonment event (#7040)
* feat(workflows): add workflows_close abandonment event

Fires a workflow-level workflows_close event when the user dismisses a
workflow without completing it (no purchase or successful restore). It does
not replace paywall_close and is not gated by screen_type, so abandonment on
non-paywall steps is captured too. Port of khepri #21505.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Fix workflow close completion tracking

* Mark workflow completion before purchase close

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 17:57:28 +02:00
Facundo MenzellaandClaude Sonnet 4.6 d54e876395 feat(workflows): add WorkflowEvent model and wire format serialization (#6858)
* feat(workflows): add WorkflowEvent model and wire format serialization

Port of https://github.com/RevenueCat/purchases-android/pull/3486

Adds WorkflowEvent (stepStarted/stepCompleted) as the iOS domain model
for workflow step lifecycle events. Adds FeatureEventsRequest.WorkflowEvent
as the Khepri-compatible wire format, wires it into the Feature routing
pipeline, and exposes screenType on WorkflowStep for local context.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: register new workflow event files in RevenueCat.xcodeproj

Adds WorkflowEvent.swift, FeatureEventsRequest+WorkflowEvent.swift,
WorkflowEventTests.swift, and WorkflowEventsRequestTests.swift to the
committed SPM-generated Xcode project so CI builds pick them up.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(workflows): address review feedback on WorkflowEvent wire format

- Rename `discriminator` to `type` in FeatureEventsRequest.WorkflowEvent (matches backend expectation and CustomPaywallEvent pattern)
- Add `version: Int = 1` to wire format, matching Android and CustomPaywallEvent schema versioning
- Add `experimentId`, `experimentVariant`, `isLastVariantStep` to WorkflowEvent.Data and wire format Properties (parity with Android/Khepri)
- Add `traceId` to WorkflowEvent.Data and wire format Properties (parity with Android)
- Update tests to cover renamed field, version, new experiment fields, and traceId in JSON shape

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(workflows): handle WorkflowEvent in toMap() and remove shadowing test extension

- Add WorkflowEvent case to FeatureEvent.toMap() so eventsListener receives structured data instead of the unknown fallback
- Remove redundant private extension in WorkflowEventTests that shadowed the @_spi(Internal) public creationData/data accessors

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(workflows): address review items - locale in wire format, toMap fields, screenType accessor

- Add localeIdentifier to WorkflowEvent.Data (captured at creation, always sent in wire format context)
- Change Context.locale from String? to String; wire format always includes locale
- Add trace_id, experiment_id, experiment_variant, is_last_variant_step to workflowEventMap()
- Add public stepScreenType accessor on WorkflowStep for SPI consumers
- Add tests for locale in wire format and all workflowEventMap() fields

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(workflows): include locale in workflowEventMap() for hybrid SDK consistency

Matches paywallMap(), customerCenterImpressionMap(), and customerCenterAnswerSubmittedMap()
which all include locale. Hybrid SDK consumers now receive locale for workflow events.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 06:43:11 +00:00