Skip to content

feat(tier-client): emit entitlement.tier_changed event (best-effort, opt-in) - #1822

Open
dzehnder wants to merge 1 commit into
mainfrom
feat/entitlement-tier-changed-event
Open

feat(tier-client): emit entitlement.tier_changed event (best-effort, opt-in)#1822
dzehnder wants to merge 1 commit into
mainfrom
feat/entitlement-tier-changed-event

Conversation

@dzehnder

Copy link
Copy Markdown
Contributor

What

Introduces an entitlement.tier_changed domain event, emitted from the single choke point where an entitlement tier changes — TierClient.createEntitlement(tier).

Event

  • Name / constant: ENTITLEMENT_TIER_CHANGED = 'entitlement.tier_changed' (exported from the package; typed EntitlementTierChangedEvent in index.d.ts).
  • Emitted on an actual change only:
    • Fresh create → { from: null, to: tier }
    • Existing entitlement tier transition (setTier+save) → { from: prevTier, to: tier }
    • No event when the tier is unchanged, when an existing PAID entitlement is left as-is, or when only a site enrollment is added.
  • Payload:
    {
      "type": "entitlement.tier_changed",
      "entitlementId": "",
      "organizationId": "",
      "productCode": "LLMO",
      "siteId": "…|null",        // null for org-only scope
      "enrollmentId": "…|null",  // null when no enrollment is in scope
      "from": "FREE_TRIAL|null", // null on a fresh create
      "to": "PAID",
      "occurredAt": "2026-07-17T00:00:00.000Z"
    }

Design: opt-in, best-effort, non-breaking

Uses the ecosystem's idiomatic publish path — the shared SQS helper on the request context (context.sqs, provided by sqsWrapper in every SpaceCat Lambda) plus an ENTITLEMENT_EVENTS_QUEUE_URL env var. No new SDK dependency, no factory/constructor signature change — additions only.

  • Opt-in: a no-op unless the context provides both context.sqs and context.env.ENTITLEMENT_EVENTS_QUEUE_URL. The 6+ existing consumers configure neither, so they are entirely unaffected.
  • Best-effort: a publish failure is caught and logged (log.warn) — it never propagates out of createEntitlement/save.

I considered a pluggable injected-publisher, but the shared context.sqs helper is the established convention for domain-event publishing across SpaceCat services and requires zero new infra from callers, so it wins here.

Target-architecture groundwork

This writes the event down as the target architecture (per the review recommendation): the eventual replacement for the current endpoint/reaction approach. There is no live consumer yet — today's provisioning is handled by the api-service endpoint + the fulfillment worker. Intended future consumers: prompt-suggestion schedule provisioning on →PAID, and a PAID→trial teardown. See the ADR: docs/adr/0002-entitlement-tier-changed-event.md.

Files changed

  • packages/spacecat-shared-tier-client/src/events.js (new) — ENTITLEMENT_TIER_CHANGED + queue-url key constants
  • packages/spacecat-shared-tier-client/src/tier-client.js#emitTierChangedEvent + wiring at the create/transition points
  • packages/spacecat-shared-tier-client/src/index.js / index.d.ts — export constants + typed payload/context
  • packages/spacecat-shared-tier-client/test/tier-client.test.js — emission tests
  • packages/spacecat-shared-tier-client/README.md, docs/adr/0002-entitlement-tier-changed-event.md — docs

Tests / lint

  • npm test -w packages/spacecat-shared-tier-client88 passing, coverage 100% lines/statements/branches/functions.
  • npm run lint -w packages/spacecat-shared-tier-client → clean.

🤖 Generated with Claude Code

…opt-in)

Emit an `entitlement.tier_changed` domain event from the single choke point
where a tier changes (`TierClient.createEntitlement`): on a fresh create
(`from: null`) and on a real tier transition (`from: prevTier`). No event on an
unchanged tier, an untouched PAID entitlement, or an enrollment-only add.

Emission is opt-in and best-effort so the 6+ existing consumers are unaffected:
it is a no-op unless the context provides both `context.sqs` (the shared SQS
helper) and an `ENTITLEMENT_EVENTS_QUEUE_URL` env var, and a publish failure is
logged and swallowed — it never fails createEntitlement/save. No new dependency,
no factory/constructor signature change; additions only.

This is target-architecture groundwork (ADR-0002): there is no live consumer
yet — current provisioning uses the api-service endpoint + fulfillment worker.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.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.

1 participant