@revturbine/cli 0.16.1 → 0.16.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +10 -10
  2. package/dist/cli.js +607 -395
  3. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -18,8 +18,8 @@ import { z as z5 } from "zod";
18
18
  import { z as z4 } from "zod";
19
19
  import { z as z6 } from "zod";
20
20
  import { z as z7 } from "zod";
21
- import { z as z8 } from "zod";
22
21
  import { z as z9 } from "zod";
22
+ import { z as z8 } from "zod";
23
23
  import { z as z10 } from "zod";
24
24
  import { z as z11 } from "zod";
25
25
  import { z as z12 } from "zod";
@@ -51,7 +51,12 @@ var DataClassification = {
51
51
  Financial: { "x-revturbine-data-classification": "financial" },
52
52
  Unrestricted: { "x-revturbine-data-classification": "unrestricted" }
53
53
  };
54
+ var SCHEMA_EXPOSURE_META_KEY = "x-revturbine-schema-exposure";
54
55
  var READ_ONLY_META_KEY = "readOnly";
56
+ var DECISION_ONLY_META_KEY = "x-revturbine-decision-only";
57
+ var DecisionOnly = { [DECISION_ONLY_META_KEY]: true };
58
+ var ClientSafe = { [SCHEMA_EXPOSURE_META_KEY]: SchemaExposure.External };
59
+ var ServerOnly = { [SCHEMA_EXPOSURE_META_KEY]: SchemaExposure.Internal };
55
60
  function toWritableSchema(schema) {
56
61
  const writableShape = {};
57
62
  for (const [fieldName, fieldSchema] of Object.entries(schema.shape)) {
@@ -1334,6 +1339,9 @@ var PlacementPayloadSchema = IdField.merge(TimestampFields).merge(TenantIdField)
1334
1339
  max_per_period: z7.number().int().min(0).nullable().default(null).meta(Unrestricted4),
1335
1340
  max_period_unit: CapPeriodSchema.default("session").meta(Unrestricted4),
1336
1341
  cooldown_after_dismiss_days: z7.number().int().min(0).default(7).meta(Unrestricted4),
1342
+ // Per-payload remind-me-later (defer) window override, in minutes. Null =
1343
+ // inherit the tenant default (`remindLaterMinutes`, 60). Plan 167 REQ-6 / Q-3.
1344
+ remind_later_minutes: z7.number().int().min(0).nullable().default(null).meta(Unrestricted4),
1337
1345
  recommendation_strategy: z7.enum(["next_tier_up", "best_value", "custom"]).optional().default("next_tier_up").meta(Unrestricted4),
1338
1346
  recommendation_plan_override: z7.string().optional().meta(Unrestricted4)
1339
1347
  }).meta(
@@ -1532,256 +1540,72 @@ var placementPaths = {
1532
1540
  };
1533
1541
  var { Unrestricted: Unrestricted5, Pii } = DataClassification;
1534
1542
  var { Persisted: Persisted5, Transient: Transient5 } = SchemaPersistence;
1535
- var { External: External5 } = SchemaExposure;
1536
- var UserTrialStatusSchema = z8.object({
1537
- in_trial: z8.boolean().meta(Unrestricted5),
1538
- trial_type: z8.string().optional().meta(Unrestricted5),
1539
- plan_handle: z8.string().optional().meta(Unrestricted5),
1540
- // 'converted' reflects a server-side analytic-event transition
1541
- // (typically a Stripe webhook like `customer.subscription.created`
1542
- // or `invoice.payment_succeeded` against a trialing subscription)
1543
- // that the control plane processes to flip TrialInstance.status.
1544
- // The SDK reads this state from the decision-API response — it does
1545
- // NOT derive 'converted' client-side. trial_lifecycle.v1 rules
1546
- // matching on 'trial_converted' fire while the user's status carries
1547
- // this value, enabling conversion-celebration / receipt placements.
1548
- state: z8.enum(["active", "running_out", "expired", "converted", "none"]).optional().meta(Unrestricted5),
1549
- /**
1550
- * Trial limit model — mirrors the rule's `trial_limit_type`. The
1551
- * SDK uses this to decide which numeric fields below to surface;
1552
- * the placement-resolver gates `trial_ending(days_before_end)`
1553
- * only when `trial_limit_type === 'time'`.
1554
- */
1555
- trial_limit_type: z8.enum(["time", "usage"]).optional().meta(Unrestricted5),
1556
- /**
1557
- * Universal progress metric, 0..100. Computed by
1558
- * `deriveLocalTrialStatusFromInstance` from elapsed days
1559
- * (time-based) or consumed/limit (usage-based). Trial rule
1560
- * modules + placement-resolver supersession consume this field
1561
- * so they don't have to branch on the limit type.
1562
- */
1563
- progress_percent: z8.number().min(0).max(100).optional().meta(Unrestricted5),
1564
- // Time-based numeric fields. Populated when trial_limit_type='time'.
1565
- day_number: z8.number().int().min(0).optional().meta(Unrestricted5),
1566
- days_remaining: z8.number().int().min(0).optional().meta(Unrestricted5),
1567
- // Usage-based numeric fields. Populated when trial_limit_type='usage'.
1568
- usage_entitlement_handle: z8.string().optional().meta(Unrestricted5),
1569
- usage_consumed: z8.number().int().min(0).optional().meta(Unrestricted5),
1570
- usage_remaining: z8.number().int().min(0).optional().meta(Unrestricted5),
1571
- usage_limit: z8.number().int().min(0).optional().meta(Unrestricted5)
1572
- }).meta(
1573
- { id: "UserTrialStatus", "x-revturbine-schema-persistence": Transient5, "x-revturbine-schema-exposure": External5 }
1574
- );
1575
- var UserUsageEntrySchema = z8.object({
1576
- entitlement_handle: z8.string().min(1).meta(Unrestricted5),
1577
- unit: z8.string().min(1).meta(Unrestricted5),
1578
- amount: z8.number().min(0).meta(Unrestricted5),
1579
- limit: z8.number().min(0).optional().meta(Unrestricted5),
1580
- reset_date: z8.string().optional().meta(Unrestricted5)
1581
- }).meta(
1582
- { id: "UserUsageEntry", "x-revturbine-schema-persistence": Transient5, "x-revturbine-schema-exposure": External5 }
1583
- );
1584
- var UserPlanContextSchema = z8.object({
1585
- id: z8.string().min(1).meta(Unrestricted5),
1586
- name: z8.string().min(1).meta(Unrestricted5),
1587
- price: z8.string().optional().meta(Unrestricted5),
1588
- billing_period: z8.enum(["monthly", "annual", "none"]).optional().meta(Unrestricted5)
1589
- }).meta(
1590
- { id: "UserPlanContext", "x-revturbine-schema-persistence": Transient5, "x-revturbine-schema-exposure": External5 }
1591
- );
1592
- var UserInstanceContextSchema = z8.object({
1593
- product_instance_id: z8.string().min(1).meta(Unrestricted5),
1594
- user_id: z8.string().min(1).meta(Pii),
1595
- plan: UserPlanContextSchema.optional().meta(Unrestricted5),
1596
- /** Usage entries for this instance, keyed by entitlement handle. */
1597
- usage: z8.record(z8.string(), UserUsageEntrySchema).default({}).meta(Unrestricted5),
1598
- trial: UserTrialStatusSchema.optional().meta(Unrestricted5),
1599
- /** Entitlements resolved at this instance level, keyed by handle. */
1600
- entitlements: z8.record(z8.string(), z8.union([z8.boolean(), EntitlementGrantSchema])).default({}).meta(Unrestricted5)
1601
- }).meta(
1602
- {
1603
- id: "UserInstanceContext",
1604
- "x-revturbine-schema-persistence": Persisted5,
1605
- "x-revturbine-schema-exposure": External5
1606
- }
1607
- );
1608
- var UserContextSchema = IdField.merge(TenantIdField).merge(TimestampFields).extend({
1609
- user_id: z8.string().min(1).meta(Pii),
1610
- account_id: z8.string().min(1).nullable().optional().meta(Pii),
1611
- email: z8.string().email().nullable().optional().meta(Pii),
1612
- /** Optional email classification (e.g. `business`, `personal`). */
1613
- email_type: z8.string().optional().meta(Unrestricted5),
1614
- plan: UserPlanContextSchema.optional().meta(Unrestricted5),
1615
- /** Aggregate usage entries across all instances, keyed by handle. */
1616
- usage: z8.record(z8.string(), UserUsageEntrySchema).default({}).meta(Unrestricted5),
1617
- trial: UserTrialStatusSchema.optional().meta(Unrestricted5),
1618
- /**
1619
- * Billing-recovery signals (account-level). `payment_failed` reflects a
1620
- * hard payment failure (e.g. Stripe `invoice.payment_failed`);
1621
- * `payment_at_risk` an expiring / missing-backup payment method. They
1622
- * drive the Retention `qualifier` placement triggers
1623
- * (placement-studio-ui.md §3.7) — the SDK surfaces them onto the
1624
- * PlanProvider state the placement resolver reads. Omitted for users in
1625
- * good standing; a qualifier gate reads `=== true`, so an absent signal
1626
- * never fires the recovery placement.
1627
- */
1628
- payment_failed: z8.boolean().optional().meta(Unrestricted5),
1629
- payment_at_risk: z8.boolean().optional().meta(Unrestricted5),
1630
- /**
1631
- * The user's current tier per `capability_tier` entitlement, keyed by
1632
- * entitlement handle → tier handle (plan 138 TASK-4). Drives the
1633
- * `entitlement_gate.tier_threshold` placement trigger: the SDK surfaces
1634
- * this onto the EntitlementProvider state, and the resolver ranks the
1635
- * current tier against the entitlement's ordered ladder. Omitted for a
1636
- * user holding no tier (ranks below every threshold).
1637
- */
1638
- tiers: z8.record(z8.string(), z8.string()).optional().meta(Unrestricted5),
1639
- /** Account-level entitlements, keyed by handle. */
1640
- entitlements: z8.record(z8.string(), z8.union([z8.boolean(), EntitlementGrantSchema])).default({}).meta(Unrestricted5),
1641
- /** Per-instance breakdowns when the account has multiple product instances. */
1642
- instances: z8.array(UserInstanceContextSchema).optional().meta(Unrestricted5),
1643
- /** Customer-defined fields: role, app-level permissions, display prefs. */
1644
- custom: z8.record(z8.string(), z8.union([z8.string(), z8.number(), z8.boolean(), z8.null()])).default({}).meta(Pii),
1645
- /**
1646
- * Transient personalization token map.
1647
- *
1648
- * Holds SDK-derived tokens (plan_name, usage_current, etc.) merged with
1649
- * app-provided tokens. Not persisted to the backend — rebuilt on each
1650
- * SDK session from context + exported config.
1651
- */
1652
- personalization: z8.record(z8.string(), z8.union([z8.string(), z8.number()])).default({}).meta({ ...Unrestricted5, "x-revturbine-schema-persistence": Transient5 }),
1653
- // ── Derived-entitlement cache (plan 74 REQ-12/REQ-13) ──────────────
1654
- // `entitlements` above is the rule-evaluated projection — a CACHE, not
1655
- // source of truth. These stamps record what it was computed against so a
1656
- // read can detect staleness: recompute when the active config version
1657
- // moved on OR the context hash changed.
1658
- /** Active config version (activated change-set id / compiled-bundle stamp) the cache was computed against. */
1659
- derived_config_version: z8.string().nullable().optional().meta(Unrestricted5),
1660
- /** Deterministic `computeUserContextHash` of the inputs the cache was computed from (REQ-13 ETag value). */
1661
- context_hash: z8.string().nullable().optional().meta(Unrestricted5),
1662
- /** When the cached entitlements were last (re)computed. */
1663
- derived_computed_at: NullableDatetimeField.meta(Unrestricted5)
1664
- }).meta(
1665
- { id: "UserContext", "x-revturbine-schema-persistence": Persisted5, "x-revturbine-schema-exposure": External5 }
1666
- );
1667
- var userContextPaths = {
1668
- "/api/user-contexts": {
1669
- get: operation({
1670
- operationId: "listUserContexts",
1671
- requestParams: { query: ListQueryParamsSchema },
1672
- summary: "List user contexts",
1673
- tags: ["users"],
1674
- responses: { "200": { description: "User context list", content: { "application/json": { schema: ListEnvelope(UserContextSchema) } } } },
1675
- "x-revturbine-operation": { exposure: "internal", resource: "user-contexts", persistence: { table: "userContexts", mode: "list" } }
1676
- }),
1677
- // Upsert by (tenant_id, user_id): the SDK identify/setUserContext path
1678
- // writes the persisted context. The collection POST is the create-or-update
1679
- // entry point — the route resolves the existing row by (tenant_id, user_id)
1680
- // and applies an idempotent upsert (server-side, plan 74 TASK-4).
1681
- post: operation({
1682
- operationId: "createUserContext",
1683
- summary: "Create or upsert a user context",
1684
- tags: ["users"],
1685
- requestBody: { required: true, content: { "application/json": { schema: UserContextSchema } } },
1686
- responses: { "201": { description: "Created", content: { "application/json": { schema: UserContextSchema } } } },
1687
- "x-revturbine-operation": { exposure: "internal", resource: "user-contexts", persistence: { table: "userContexts", mode: "create" } }
1688
- })
1689
- },
1690
- "/api/user-contexts/{userContextId}": {
1691
- get: operation({
1692
- operationId: "getUserContext",
1693
- requestParams: { path: z8.object({ userContextId: z8.string() }) },
1694
- summary: "Get user context",
1695
- tags: ["users"],
1696
- responses: { "200": { description: "User context", content: { "application/json": { schema: UserContextSchema } } } },
1697
- "x-revturbine-operation": { exposure: "internal", resource: "user-contexts", persistence: { table: "userContexts", mode: "get" } }
1698
- }),
1699
- patch: operation({
1700
- operationId: "updateUserContext",
1701
- requestParams: { path: z8.object({ userContextId: z8.string() }) },
1702
- summary: "Update a user context",
1703
- tags: ["users"],
1704
- requestBody: { required: true, content: { "application/json": { schema: UserContextSchema.partial() } } },
1705
- responses: { "200": { description: "Updated", content: { "application/json": { schema: UserContextSchema } } } },
1706
- "x-revturbine-operation": { exposure: "internal", resource: "user-contexts", persistence: { table: "userContexts", mode: "update" } }
1707
- }),
1708
- // Delete path ships in v1 (plan 74 Q-1): a user context can be removed on
1709
- // request now; full by-email DSR tooling remains deferred (sdk.md §8).
1710
- delete: operation({
1711
- operationId: "deleteUserContext",
1712
- requestParams: { path: z8.object({ userContextId: z8.string() }) },
1713
- summary: "Delete a user context",
1714
- tags: ["users"],
1715
- responses: { "204": { description: "Deleted" } },
1716
- "x-revturbine-operation": { exposure: "internal", resource: "user-contexts", persistence: { table: "userContexts", mode: "delete" } }
1717
- })
1718
- }
1719
- };
1720
- var { Unrestricted: Unrestricted6, Pii: Pii2 } = DataClassification;
1721
- var { Persisted: Persisted6, Transient: Transient6 } = SchemaPersistence;
1722
1543
  var { Internal: Internal4 } = SchemaExposure;
1723
1544
  var CUSTOMER_OPERATIONS_FACETS = schemaFacets(SchemaContext.CustomerOperations, {
1724
1545
  sdkInput: false
1725
1546
  });
1726
- var IdentitySchema = z9.object({
1727
- external_id: z9.string().min(1).meta(Pii2),
1728
- traits: z9.record(z9.string(), z9.unknown()).default({}).meta(Pii2),
1729
- plan_id: z9.string().optional().meta(Unrestricted6)
1547
+ var IdentitySchema = z8.object({
1548
+ external_id: z8.string().min(1).meta(Pii),
1549
+ traits: z8.record(z8.string(), z8.unknown()).default({}).meta(Pii),
1550
+ plan_id: z8.string().optional().meta(Unrestricted5)
1730
1551
  }).meta(
1731
1552
  {
1732
1553
  id: "Identity",
1733
- "x-revturbine-schema-persistence": Persisted6,
1554
+ "x-revturbine-schema-persistence": Persisted5,
1734
1555
  "x-revturbine-schema-exposure": Internal4
1735
1556
  }
1736
1557
  );
1558
+ var BillingHealthStatusSchema = z8.enum(["payment_failed", "payment_method_missing"]);
1737
1559
  var CustomerSchema = IdField.merge(TimestampFields).merge(TenantIdField).extend({
1738
- external_id: z9.string().min(1).meta(Pii2),
1739
- identity: IdentitySchema.meta(Pii2),
1740
- stripe_customer_id: z9.string().optional().meta(Unrestricted6),
1741
- current_plan_id: z9.string().optional().meta(Unrestricted6),
1742
- subscription_status: z9.enum(["active", "past_due", "canceled", "trialing", "unpaid", "none"]).default("none").meta(Unrestricted6),
1743
- status: z9.enum(["active", "churned", "trial", "suspended"]).default("active").meta(Unrestricted6),
1744
- metadata: MetadataField.meta(Unrestricted6)
1560
+ external_id: z8.string().min(1).meta(Pii),
1561
+ identity: IdentitySchema.meta(Pii),
1562
+ stripe_customer_id: z8.string().optional().meta(Unrestricted5),
1563
+ current_plan_id: z8.string().optional().meta(Unrestricted5),
1564
+ subscription_status: z8.enum(["active", "past_due", "canceled", "trialing", "unpaid", "none"]).default("none").meta(Unrestricted5),
1565
+ status: z8.enum(["active", "churned", "trial", "suspended"]).default("active").meta(Unrestricted5),
1566
+ /** Active billing-health problems (plan 156); empty array = healthy. */
1567
+ billing_health_issues: z8.array(BillingHealthStatusSchema).default([]).meta(Unrestricted5),
1568
+ metadata: MetadataField.meta(Unrestricted5)
1745
1569
  }).meta(
1746
1570
  {
1747
1571
  id: "Customer",
1748
- "x-revturbine-schema-persistence": Persisted6,
1572
+ "x-revturbine-schema-persistence": Persisted5,
1749
1573
  "x-revturbine-schema-exposure": Internal4
1750
1574
  }
1751
1575
  );
1752
- var CustomerOverrideDurationSchema = z9.enum([
1576
+ var CustomerOverrideDurationSchema = z8.enum([
1753
1577
  "permanent",
1754
1578
  "for_duration"
1755
1579
  ]).meta(
1756
1580
  {
1757
1581
  id: "CustomerOverrideDuration",
1758
- "x-revturbine-schema-persistence": Transient6,
1582
+ "x-revturbine-schema-persistence": Transient5,
1759
1583
  "x-revturbine-schema-exposure": Internal4
1760
1584
  }
1761
1585
  );
1762
- var CustomerOverrideTypeSchema = z9.enum(["grant_plan", "grant_addon", "grant_entitlement"]).meta(
1763
- { id: "CustomerOverrideType", "x-revturbine-schema-persistence": Transient6, "x-revturbine-schema-exposure": Internal4 }
1586
+ var CustomerOverrideTypeSchema = z8.enum(["grant_plan", "grant_addon", "grant_entitlement"]).meta(
1587
+ { id: "CustomerOverrideType", "x-revturbine-schema-persistence": Transient5, "x-revturbine-schema-exposure": Internal4 }
1764
1588
  );
1765
- var CustomerOverrideStatusSchema = z9.enum(["active", "expired", "revoked"]).meta(
1766
- { id: "CustomerOverrideStatus", "x-revturbine-schema-persistence": Transient6, "x-revturbine-schema-exposure": Internal4 }
1589
+ var CustomerOverrideStatusSchema = z8.enum(["active", "expired", "revoked"]).meta(
1590
+ { id: "CustomerOverrideStatus", "x-revturbine-schema-persistence": Transient5, "x-revturbine-schema-exposure": Internal4 }
1767
1591
  );
1768
1592
  var CustomerOverrideSchema = IdField.merge(TimestampFields).merge(TenantIdField).merge(AnchorFields).merge(VersionFields).extend({
1769
- handle: HandleField.meta({ ...Unrestricted6, readOnly: true }),
1770
- customer_id: z9.string().min(1).meta(Pii2),
1771
- customer_id_list: z9.array(z9.string()).optional().meta(Pii2),
1772
- override_type: CustomerOverrideTypeSchema.meta(Unrestricted6),
1773
- target_id: z9.string().min(1).meta(Unrestricted6),
1774
- value: z9.record(z9.string(), z9.unknown()).default({}).meta(Unrestricted6),
1775
- duration_type: CustomerOverrideDurationSchema.default("permanent").meta(Unrestricted6),
1776
- duration_value: z9.string().optional().meta(Unrestricted6),
1777
- expiry_date: NullableDatetimeField.meta(Unrestricted6),
1778
- status: CustomerOverrideStatusSchema.default("active").meta(Unrestricted6),
1779
- reason: z9.string().max(500).optional().meta(Unrestricted6),
1780
- created_by: z9.string().optional().meta(Unrestricted6)
1593
+ handle: HandleField.meta({ ...Unrestricted5, readOnly: true }),
1594
+ customer_id: z8.string().min(1).meta(Pii),
1595
+ customer_id_list: z8.array(z8.string()).optional().meta(Pii),
1596
+ override_type: CustomerOverrideTypeSchema.meta(Unrestricted5),
1597
+ target_id: z8.string().min(1).meta(Unrestricted5),
1598
+ value: z8.record(z8.string(), z8.unknown()).default({}).meta(Unrestricted5),
1599
+ duration_type: CustomerOverrideDurationSchema.default("permanent").meta(Unrestricted5),
1600
+ duration_value: z8.string().optional().meta(Unrestricted5),
1601
+ expiry_date: NullableDatetimeField.meta(Unrestricted5),
1602
+ status: CustomerOverrideStatusSchema.default("active").meta(Unrestricted5),
1603
+ reason: z8.string().max(500).optional().meta(Unrestricted5),
1604
+ created_by: z8.string().optional().meta(Unrestricted5)
1781
1605
  }).meta(
1782
1606
  {
1783
1607
  id: "CustomerOverride",
1784
- "x-revturbine-schema-persistence": Persisted6,
1608
+ "x-revturbine-schema-persistence": Persisted5,
1785
1609
  "x-revturbine-schema-exposure": Internal4,
1786
1610
  ...CUSTOMER_OPERATIONS_FACETS,
1787
1611
  // KENT-REVIEW (plan 121): defaulted to Bucket 2 on (customer_id, override_type,
@@ -1805,7 +1629,7 @@ var customerPaths = {
1805
1629
  "/api/customers/{customerId}": {
1806
1630
  get: operation({
1807
1631
  operationId: "getCustomer",
1808
- requestParams: { path: z9.object({ customerId: z9.string() }) },
1632
+ requestParams: { path: z8.object({ customerId: z8.string() }) },
1809
1633
  summary: "Get customer by ID",
1810
1634
  tags: ["customers"],
1811
1635
  responses: { "200": { description: "Customer", content: { "application/json": { schema: CustomerSchema } } } },
@@ -1833,7 +1657,7 @@ var customerPaths = {
1833
1657
  "/api/customer-overrides/{overrideId}": {
1834
1658
  get: operation({
1835
1659
  operationId: "getCustomerOverride",
1836
- requestParams: { path: z9.object({ overrideId: z9.string() }) },
1660
+ requestParams: { path: z8.object({ overrideId: z8.string() }) },
1837
1661
  summary: "Get customer override",
1838
1662
  tags: ["customers"],
1839
1663
  responses: { "200": { description: "Override", content: { "application/json": { schema: CustomerOverrideSchema } } } },
@@ -1841,7 +1665,7 @@ var customerPaths = {
1841
1665
  }),
1842
1666
  patch: operation({
1843
1667
  operationId: "updateCustomerOverride",
1844
- requestParams: { path: z9.object({ overrideId: z9.string() }) },
1668
+ requestParams: { path: z8.object({ overrideId: z8.string() }) },
1845
1669
  summary: "Update customer override",
1846
1670
  tags: ["customers"],
1847
1671
  requestBody: { required: true, content: { "application/json": { schema: CustomerOverrideSchema.partial() } } },
@@ -1850,7 +1674,7 @@ var customerPaths = {
1850
1674
  }),
1851
1675
  delete: operation({
1852
1676
  operationId: "deleteCustomerOverride",
1853
- requestParams: { path: z9.object({ overrideId: z9.string() }) },
1677
+ requestParams: { path: z8.object({ overrideId: z8.string() }) },
1854
1678
  summary: "Delete customer override",
1855
1679
  tags: ["customers"],
1856
1680
  responses: { "204": { description: "Deleted" } },
@@ -1860,7 +1684,7 @@ var customerPaths = {
1860
1684
  "/api/customer-overrides/{overrideId}/revoke": {
1861
1685
  post: operation({
1862
1686
  operationId: "revokeCustomerOverride",
1863
- requestParams: { path: z9.object({ overrideId: z9.string() }) },
1687
+ requestParams: { path: z8.object({ overrideId: z8.string() }) },
1864
1688
  summary: "Revoke an active customer override",
1865
1689
  tags: ["customers"],
1866
1690
  responses: { "200": { description: "Revoked", content: { "application/json": { schema: CustomerOverrideSchema } } } },
@@ -1870,28 +1694,263 @@ var customerPaths = {
1870
1694
  "/api/customer-overrides/{overrideId}/duplicate": {
1871
1695
  post: operation({
1872
1696
  operationId: "duplicateCustomerOverride",
1873
- requestParams: { path: z9.object({ overrideId: z9.string() }) },
1697
+ requestParams: { path: z8.object({ overrideId: z8.string() }) },
1874
1698
  summary: "Duplicate customer override for another customer",
1875
1699
  tags: ["customers"],
1876
- requestBody: { required: true, content: { "application/json": { schema: z9.object({ customer_id: z9.string() }) } } },
1700
+ requestBody: { required: true, content: { "application/json": { schema: z8.object({ customer_id: z8.string() }) } } },
1877
1701
  responses: { "201": { description: "Duplicated", content: { "application/json": { schema: CustomerOverrideSchema } } } },
1878
1702
  "x-revturbine-operation": { exposure: "internal", resource: "customer-overrides", persistence: { table: "customerOverrides", mode: "create" } }
1879
1703
  })
1880
1704
  }
1881
1705
  };
1706
+ var { Unrestricted: Unrestricted6, Pii: Pii2, Financial: Financial2 } = DataClassification;
1707
+ var { Persisted: Persisted6, Transient: Transient6 } = SchemaPersistence;
1708
+ var { External: External5, Internal: Internal5 } = SchemaExposure;
1709
+ var UserTrialStatusSchema = z9.object({
1710
+ in_trial: z9.boolean().meta(Unrestricted6),
1711
+ trial_type: z9.string().optional().meta(Unrestricted6),
1712
+ plan_handle: z9.string().optional().meta(Unrestricted6),
1713
+ // 'converted' reflects a server-side analytic-event transition
1714
+ // (typically a Stripe webhook like `customer.subscription.created`
1715
+ // or `invoice.payment_succeeded` against a trialing subscription)
1716
+ // that the control plane processes to flip TrialInstance.status.
1717
+ // The SDK reads this state from the decision-API response — it does
1718
+ // NOT derive 'converted' client-side. trial_lifecycle.v1 rules
1719
+ // matching on 'trial_converted' fire while the user's status carries
1720
+ // this value, enabling conversion-celebration / receipt placements.
1721
+ state: z9.enum(["active", "running_out", "expired", "converted", "none"]).optional().meta(Unrestricted6),
1722
+ /**
1723
+ * Trial limit model — mirrors the rule's `trial_limit_type`. The
1724
+ * SDK uses this to decide which numeric fields below to surface;
1725
+ * the placement-resolver gates `trial_ending(days_before_end)`
1726
+ * only when `trial_limit_type === 'time'`.
1727
+ */
1728
+ trial_limit_type: z9.enum(["time", "usage"]).optional().meta(Unrestricted6),
1729
+ /**
1730
+ * Universal progress metric, 0..100. Computed by
1731
+ * `deriveLocalTrialStatusFromInstance` from elapsed days
1732
+ * (time-based) or consumed/limit (usage-based). Trial rule
1733
+ * modules + placement-resolver supersession consume this field
1734
+ * so they don't have to branch on the limit type.
1735
+ */
1736
+ progress_percent: z9.number().min(0).max(100).optional().meta(Unrestricted6),
1737
+ // Time-based numeric fields. Populated when trial_limit_type='time'.
1738
+ day_number: z9.number().int().min(0).optional().meta(Unrestricted6),
1739
+ days_remaining: z9.number().int().min(0).optional().meta(Unrestricted6),
1740
+ // Usage-based numeric fields. Populated when trial_limit_type='usage'.
1741
+ usage_entitlement_handle: z9.string().optional().meta(Unrestricted6),
1742
+ usage_consumed: z9.number().int().min(0).optional().meta(Unrestricted6),
1743
+ usage_remaining: z9.number().int().min(0).optional().meta(Unrestricted6),
1744
+ usage_limit: z9.number().int().min(0).optional().meta(Unrestricted6)
1745
+ }).meta(
1746
+ { id: "UserTrialStatus", "x-revturbine-schema-persistence": Transient6, "x-revturbine-schema-exposure": External5 }
1747
+ );
1748
+ var UserUsageEntrySchema = z9.object({
1749
+ entitlement_handle: z9.string().min(1).meta(Unrestricted6),
1750
+ unit: z9.string().min(1).meta(Unrestricted6),
1751
+ amount: z9.number().min(0).meta(Unrestricted6),
1752
+ limit: z9.number().min(0).optional().meta(Unrestricted6),
1753
+ reset_date: z9.string().optional().meta(Unrestricted6)
1754
+ }).meta(
1755
+ { id: "UserUsageEntry", "x-revturbine-schema-persistence": Transient6, "x-revturbine-schema-exposure": External5 }
1756
+ );
1757
+ var UserPlanContextSchema = z9.object({
1758
+ id: z9.string().min(1).meta(Unrestricted6),
1759
+ name: z9.string().min(1).meta(Unrestricted6),
1760
+ price: z9.string().optional().meta(Unrestricted6),
1761
+ billing_period: z9.enum(["monthly", "annual", "none"]).optional().meta(Unrestricted6)
1762
+ }).meta(
1763
+ { id: "UserPlanContext", "x-revturbine-schema-persistence": Transient6, "x-revturbine-schema-exposure": External5 }
1764
+ );
1765
+ var UserInstanceContextSchema = z9.object({
1766
+ product_instance_id: z9.string().min(1).meta(Unrestricted6),
1767
+ user_id: z9.string().min(1).meta(Pii2),
1768
+ plan: UserPlanContextSchema.optional().meta(Unrestricted6),
1769
+ /** Usage entries for this instance, keyed by entitlement handle. */
1770
+ usage: z9.record(z9.string(), UserUsageEntrySchema).default({}).meta(Unrestricted6),
1771
+ trial: UserTrialStatusSchema.optional().meta(Unrestricted6),
1772
+ /** Entitlements resolved at this instance level, keyed by handle. */
1773
+ entitlements: z9.record(z9.string(), z9.union([z9.boolean(), EntitlementGrantSchema])).default({}).meta(Unrestricted6)
1774
+ }).meta(
1775
+ {
1776
+ id: "UserInstanceContext",
1777
+ "x-revturbine-schema-persistence": Persisted6,
1778
+ "x-revturbine-schema-exposure": External5
1779
+ }
1780
+ );
1781
+ var UserContextSchema = IdField.merge(TenantIdField).merge(TimestampFields).extend({
1782
+ user_id: z9.string().min(1).meta(Pii2),
1783
+ account_id: z9.string().min(1).nullable().optional().meta(Pii2),
1784
+ email: z9.string().email().nullable().optional().meta(Pii2),
1785
+ /** Optional email classification (e.g. `business`, `personal`). */
1786
+ email_type: z9.string().optional().meta(Unrestricted6),
1787
+ plan: UserPlanContextSchema.optional().meta(Unrestricted6),
1788
+ /** Aggregate usage entries across all instances, keyed by handle. */
1789
+ usage: z9.record(z9.string(), UserUsageEntrySchema).default({}).meta(Unrestricted6),
1790
+ trial: UserTrialStatusSchema.optional().meta(Unrestricted6),
1791
+ /**
1792
+ * Billing-recovery signals (account-level). `payment_failed` reflects a
1793
+ * hard payment failure (e.g. Stripe `invoice.payment_failed`);
1794
+ * `payment_at_risk` an expiring / missing-backup payment method. They
1795
+ * drive the Retention `qualifier` placement triggers
1796
+ * (placement-studio-ui.md §3.7) — the SDK surfaces them onto the
1797
+ * PlanProvider state the placement resolver reads. Omitted for users in
1798
+ * good standing; a qualifier gate reads `=== true`, so an absent signal
1799
+ * never fires the recovery placement.
1800
+ */
1801
+ payment_failed: z9.boolean().optional().meta(Unrestricted6),
1802
+ payment_at_risk: z9.boolean().optional().meta(Unrestricted6),
1803
+ /**
1804
+ * The user's current tier per `capability_tier` entitlement, keyed by
1805
+ * entitlement handle → tier handle (plan 138 TASK-4). Drives the
1806
+ * `entitlement_gate.tier_threshold` placement trigger: the SDK surfaces
1807
+ * this onto the EntitlementProvider state, and the resolver ranks the
1808
+ * current tier against the entitlement's ordered ladder. Omitted for a
1809
+ * user holding no tier (ranks below every threshold).
1810
+ */
1811
+ tiers: z9.record(z9.string(), z9.string()).optional().meta(Unrestricted6),
1812
+ /** Account-level entitlements, keyed by handle. */
1813
+ entitlements: z9.record(z9.string(), z9.union([z9.boolean(), EntitlementGrantSchema])).default({}).meta(Unrestricted6),
1814
+ /** Per-instance breakdowns when the account has multiple product instances. */
1815
+ instances: z9.array(UserInstanceContextSchema).optional().meta(Unrestricted6),
1816
+ /** Customer-defined fields: role, app-level permissions, display prefs. */
1817
+ custom: z9.record(z9.string(), z9.union([z9.string(), z9.number(), z9.boolean(), z9.null()])).default({}).meta(Pii2),
1818
+ /**
1819
+ * Transient personalization token map.
1820
+ *
1821
+ * Holds SDK-derived tokens (plan_name, usage_current, etc.) merged with
1822
+ * app-provided tokens. Not persisted to the backend — rebuilt on each
1823
+ * SDK session from context + exported config.
1824
+ */
1825
+ personalization: z9.record(z9.string(), z9.union([z9.string(), z9.number()])).default({}).meta({ ...Unrestricted6, "x-revturbine-schema-persistence": Transient6 }),
1826
+ // ── Derived-entitlement cache (plan 74 REQ-12/REQ-13) ──────────────
1827
+ // `entitlements` above is the rule-evaluated projection — a CACHE, not
1828
+ // source of truth. These stamps record what it was computed against so a
1829
+ // read can detect staleness: recompute when the active config version
1830
+ // moved on OR the context hash changed.
1831
+ /** Active config version (activated change-set id / compiled-bundle stamp) the cache was computed against. */
1832
+ derived_config_version: z9.string().nullable().optional().meta(Unrestricted6),
1833
+ /** Deterministic `computeUserContextHash` of the inputs the cache was computed from (REQ-13 ETag value). */
1834
+ context_hash: z9.string().nullable().optional().meta(Unrestricted6),
1835
+ /** When the cached entitlements were last (re)computed. */
1836
+ derived_computed_at: NullableDatetimeField.meta(Unrestricted6)
1837
+ }).meta(
1838
+ { id: "UserContext", "x-revturbine-schema-persistence": Persisted6, "x-revturbine-schema-exposure": External5 }
1839
+ );
1840
+ var ClientContextTrialSchema = z9.object({
1841
+ status: z9.enum(["active", "running_out", "expired", "converted", "none"]).meta({ ...Unrestricted6, ...ClientSafe }),
1842
+ days_remaining: z9.number().int().min(0).optional().meta({ ...Unrestricted6, ...ClientSafe }),
1843
+ ends_at: z9.string().optional().meta({ ...Unrestricted6, ...ClientSafe })
1844
+ }).meta(
1845
+ { id: "ClientContextTrial", "x-revturbine-schema-persistence": Transient6, "x-revturbine-schema-exposure": Internal5 }
1846
+ );
1847
+ var ClientContextBillingSchema = z9.object({
1848
+ /** Coarse, browser-safe billing-health signal (the only billing field exposed). */
1849
+ health: z9.enum(["ok", "attention_required"]).optional().meta({ ...Unrestricted6, ...ClientSafe }),
1850
+ /** decision_only — drives retention placements server-side; never returned to the browser. */
1851
+ failed_payment_reason: z9.string().optional().meta({ ...Financial2, ...DecisionOnly }),
1852
+ /**
1853
+ * decision_only — the raw billing-health issue codes (plan 156). The coarse
1854
+ * `health` above is derived from whether this is non-empty; these raw codes
1855
+ * may drive server-side Playbook decisions but are stripped by the exposure
1856
+ * filter and never returned to the browser.
1857
+ */
1858
+ issues: z9.array(BillingHealthStatusSchema).optional().meta({ ...Financial2, ...DecisionOnly }),
1859
+ /** server_only — raw provider identifier; never exposed. */
1860
+ provider_subscription_id: z9.string().optional().meta({ ...Financial2, ...ServerOnly })
1861
+ }).meta(
1862
+ { id: "ClientContextBilling", "x-revturbine-schema-persistence": Transient6, "x-revturbine-schema-exposure": Internal5 }
1863
+ );
1864
+ var ClientContextCapabilitiesSchema = z9.object({
1865
+ can_upgrade: z9.boolean().optional().meta({ ...Unrestricted6, ...ClientSafe }),
1866
+ can_manage_billing: z9.boolean().optional().meta({ ...Unrestricted6, ...ClientSafe })
1867
+ }).meta(
1868
+ { id: "ClientContextCapabilities", "x-revturbine-schema-persistence": Transient6, "x-revturbine-schema-exposure": Internal5 }
1869
+ );
1870
+ var ClientContextPlanSchema = z9.object({
1871
+ /** The customer's current plan handle (`plans.unique_handle`). */
1872
+ handle: z9.string().min(1).optional().meta({ ...Unrestricted6, ...ClientSafe })
1873
+ }).meta(
1874
+ { id: "ClientContextPlan", "x-revturbine-schema-persistence": Transient6, "x-revturbine-schema-exposure": Internal5 }
1875
+ );
1876
+ var ClientContextSchema = z9.object({
1877
+ /** The end-user subject the client token was minted for (carried by the token, not the request). */
1878
+ subject: z9.string().min(1).meta({ ...Unrestricted6, ...ClientSafe }),
1879
+ /** Opaque version stamp of the underlying context snapshot. */
1880
+ context_version: z9.string().optional().meta({ ...Unrestricted6, ...ClientSafe }),
1881
+ trial: ClientContextTrialSchema.optional().meta({ ...Unrestricted6, ...ClientSafe }),
1882
+ billing: ClientContextBillingSchema.optional().meta({ ...Unrestricted6, ...ClientSafe }),
1883
+ plan: ClientContextPlanSchema.optional().meta({ ...Unrestricted6, ...ClientSafe }),
1884
+ capabilities: ClientContextCapabilitiesSchema.optional().meta({ ...Unrestricted6, ...ClientSafe })
1885
+ }).meta(
1886
+ { id: "ClientContext", "x-revturbine-schema-persistence": Transient6, "x-revturbine-schema-exposure": Internal5 }
1887
+ );
1888
+ var userContextPaths = {
1889
+ "/api/user-contexts": {
1890
+ get: operation({
1891
+ operationId: "listUserContexts",
1892
+ requestParams: { query: ListQueryParamsSchema },
1893
+ summary: "List user contexts",
1894
+ tags: ["users"],
1895
+ responses: { "200": { description: "User context list", content: { "application/json": { schema: ListEnvelope(UserContextSchema) } } } },
1896
+ "x-revturbine-operation": { exposure: "internal", resource: "user-contexts", persistence: { table: "userContexts", mode: "list" } }
1897
+ }),
1898
+ // Upsert by (tenant_id, user_id): the SDK identify/setUserContext path
1899
+ // writes the persisted context. The collection POST is the create-or-update
1900
+ // entry point — the route resolves the existing row by (tenant_id, user_id)
1901
+ // and applies an idempotent upsert (server-side, plan 74 TASK-4).
1902
+ post: operation({
1903
+ operationId: "createUserContext",
1904
+ summary: "Create or upsert a user context",
1905
+ tags: ["users"],
1906
+ requestBody: { required: true, content: { "application/json": { schema: UserContextSchema } } },
1907
+ responses: { "201": { description: "Created", content: { "application/json": { schema: UserContextSchema } } } },
1908
+ "x-revturbine-operation": { exposure: "internal", resource: "user-contexts", persistence: { table: "userContexts", mode: "create", uniqueBy: ["tenant_id", "user_id"] } }
1909
+ })
1910
+ },
1911
+ "/api/user-contexts/{userContextId}": {
1912
+ get: operation({
1913
+ operationId: "getUserContext",
1914
+ requestParams: { path: z9.object({ userContextId: z9.string() }) },
1915
+ summary: "Get user context",
1916
+ tags: ["users"],
1917
+ responses: { "200": { description: "User context", content: { "application/json": { schema: UserContextSchema } } } },
1918
+ "x-revturbine-operation": { exposure: "internal", resource: "user-contexts", persistence: { table: "userContexts", mode: "get" } }
1919
+ }),
1920
+ patch: operation({
1921
+ operationId: "updateUserContext",
1922
+ requestParams: { path: z9.object({ userContextId: z9.string() }) },
1923
+ summary: "Update a user context",
1924
+ tags: ["users"],
1925
+ requestBody: { required: true, content: { "application/json": { schema: UserContextSchema.partial() } } },
1926
+ responses: { "200": { description: "Updated", content: { "application/json": { schema: UserContextSchema } } } },
1927
+ "x-revturbine-operation": { exposure: "internal", resource: "user-contexts", persistence: { table: "userContexts", mode: "update" } }
1928
+ }),
1929
+ // Delete path ships in v1 (plan 74 Q-1): a user context can be removed on
1930
+ // request now; full by-email DSR tooling remains deferred (sdk.md §8).
1931
+ delete: operation({
1932
+ operationId: "deleteUserContext",
1933
+ requestParams: { path: z9.object({ userContextId: z9.string() }) },
1934
+ summary: "Delete a user context",
1935
+ tags: ["users"],
1936
+ responses: { "204": { description: "Deleted" } },
1937
+ "x-revturbine-operation": { exposure: "internal", resource: "user-contexts", persistence: { table: "userContexts", mode: "delete" } }
1938
+ })
1939
+ }
1940
+ };
1882
1941
  var { Unrestricted: Unrestricted7 } = DataClassification;
1883
1942
  var { Persisted: Persisted7, Transient: Transient7 } = SchemaPersistence;
1884
- var { Internal: Internal5 } = SchemaExposure;
1943
+ var { Internal: Internal6 } = SchemaExposure;
1885
1944
  var PLAYBOOK_SDK_FACETS4 = schemaFacets(SchemaContext.Playbook, { sdkInput: true });
1886
1945
  var PENDING_PLAYBOOK_SDK_FACETS = schemaFacets(SchemaContext.Playbook, {
1887
1946
  inConfig: false,
1888
1947
  sdkInput: true
1889
1948
  });
1890
1949
  var DimensionCategorySchema = z10.enum(["default", "custom"]).meta(
1891
- { id: "DimensionCategory", "x-revturbine-schema-persistence": Transient7, "x-revturbine-schema-exposure": Internal5 }
1950
+ { id: "DimensionCategory", "x-revturbine-schema-persistence": Transient7, "x-revturbine-schema-exposure": Internal6 }
1892
1951
  );
1893
1952
  var DimensionSourceTypeSchema = z10.enum(["system", "sdk_trait", "sdk_trait_enum", "cdp_property", "manual_list"]).meta(
1894
- { id: "DimensionSourceType", "x-revturbine-schema-persistence": Transient7, "x-revturbine-schema-exposure": Internal5 }
1953
+ { id: "DimensionSourceType", "x-revturbine-schema-persistence": Transient7, "x-revturbine-schema-exposure": Internal6 }
1895
1954
  );
1896
1955
  var SegmentDimensionSchema = IdField.merge(TimestampFields).merge(TenantIdField).merge(AnchorFields).merge(VersionFields).extend({
1897
1956
  anchor_id: z10.string().min(1).meta({ ...Unrestricted7, readOnly: true }),
@@ -1909,7 +1968,7 @@ var SegmentDimensionSchema = IdField.merge(TimestampFields).merge(TenantIdField)
1909
1968
  {
1910
1969
  id: "SegmentDimension",
1911
1970
  "x-revturbine-schema-persistence": Persisted7,
1912
- "x-revturbine-schema-exposure": Internal5,
1971
+ "x-revturbine-schema-exposure": Internal6,
1913
1972
  ...PENDING_PLAYBOOK_SDK_FACETS,
1914
1973
  ...namedIdentity()
1915
1974
  }
@@ -1927,7 +1986,7 @@ var SegmentValueSchema = IdField.merge(TimestampFields).merge(TenantIdField).mer
1927
1986
  {
1928
1987
  id: "SegmentValue",
1929
1988
  "x-revturbine-schema-persistence": Persisted7,
1930
- "x-revturbine-schema-exposure": Internal5,
1989
+ "x-revturbine-schema-exposure": Internal6,
1931
1990
  ...PENDING_PLAYBOOK_SDK_FACETS,
1932
1991
  ...namedIdentity()
1933
1992
  }
@@ -1946,7 +2005,7 @@ var SegmentSchema = IdField.merge(TimestampFields).merge(TenantIdField).merge(An
1946
2005
  {
1947
2006
  id: "Segment",
1948
2007
  "x-revturbine-schema-persistence": Persisted7,
1949
- "x-revturbine-schema-exposure": Internal5,
2008
+ "x-revturbine-schema-exposure": Internal6,
1950
2009
  ...PLAYBOOK_SDK_FACETS4,
1951
2010
  ...namedIdentity()
1952
2011
  }
@@ -2280,7 +2339,7 @@ var contentPaths = {
2280
2339
  };
2281
2340
  var { Unrestricted: Unrestricted9, Pii: Pii3 } = DataClassification;
2282
2341
  var { Persisted: Persisted9, Transient: Transient9 } = SchemaPersistence;
2283
- var { Internal: Internal6, External: External7 } = SchemaExposure;
2342
+ var { Internal: Internal7, External: External7 } = SchemaExposure;
2284
2343
  var BRANDING_FACETS = schemaFacets(SchemaContext.Branding, { sdkInput: false });
2285
2344
  var ThemeSchema = z12.object({
2286
2345
  id: z12.string().min(1).meta({ ...Unrestricted9, readOnly: true }),
@@ -2303,7 +2362,7 @@ var UiPreferenceSchema = IdField.merge(TimestampFields).extend({
2303
2362
  {
2304
2363
  id: "UiPreference",
2305
2364
  "x-revturbine-schema-persistence": Persisted9,
2306
- "x-revturbine-schema-exposure": Internal6
2365
+ "x-revturbine-schema-exposure": Internal7
2307
2366
  }
2308
2367
  );
2309
2368
  var uiPreferencePaths = {
@@ -2347,15 +2406,15 @@ var uiPreferencePaths = {
2347
2406
  })
2348
2407
  }
2349
2408
  };
2350
- var { Unrestricted: Unrestricted10, Financial: Financial2 } = DataClassification;
2409
+ var { Unrestricted: Unrestricted10, Financial: Financial3 } = DataClassification;
2351
2410
  var { Persisted: Persisted10, Transient: Transient10 } = SchemaPersistence;
2352
- var { External: External8, Internal: Internal7 } = SchemaExposure;
2411
+ var { External: External8, Internal: Internal8 } = SchemaExposure;
2353
2412
  var CohortMonthSchema = z13.object({
2354
2413
  month: z13.string().meta(Unrestricted10),
2355
2414
  cohort_size: z13.number().int().min(0).meta(Unrestricted10),
2356
2415
  retained: z13.number().int().min(0).meta(Unrestricted10),
2357
2416
  retention_rate: z13.number().min(0).max(1).meta(Unrestricted10),
2358
- revenue_cents: z13.number().int().min(0).meta(Financial2)
2417
+ revenue_cents: z13.number().int().min(0).meta(Financial3)
2359
2418
  }).meta(
2360
2419
  { id: "CohortMonth", "x-revturbine-schema-persistence": Transient10, "x-revturbine-schema-exposure": External8 }
2361
2420
  );
@@ -2376,26 +2435,26 @@ var PlacementPerformanceRowSchema = z13.object({
2376
2435
  conversions: z13.number().int().min(0).meta(Unrestricted10),
2377
2436
  ctr: z13.number().min(0).meta(Unrestricted10),
2378
2437
  conversion_rate: z13.number().min(0).meta(Unrestricted10),
2379
- revenue_cents: z13.number().int().min(0).meta(Financial2)
2438
+ revenue_cents: z13.number().int().min(0).meta(Financial3)
2380
2439
  }).meta(
2381
2440
  { id: "PlacementPerformanceRow", "x-revturbine-schema-persistence": Transient10, "x-revturbine-schema-exposure": External8 }
2382
2441
  );
2383
2442
  var RevenueMetricSchema = z13.object({
2384
2443
  date: z13.string().meta(Unrestricted10),
2385
- mrr_cents: z13.number().int().min(0).meta(Financial2),
2386
- arr_cents: z13.number().int().min(0).meta(Financial2),
2387
- new_mrr_cents: z13.number().int().min(0).meta(Financial2),
2388
- churned_mrr_cents: z13.number().int().min(0).meta(Financial2),
2389
- expansion_mrr_cents: z13.number().int().min(0).meta(Financial2),
2390
- net_new_mrr_cents: z13.number().int().meta(Financial2)
2444
+ mrr_cents: z13.number().int().min(0).meta(Financial3),
2445
+ arr_cents: z13.number().int().min(0).meta(Financial3),
2446
+ new_mrr_cents: z13.number().int().min(0).meta(Financial3),
2447
+ churned_mrr_cents: z13.number().int().min(0).meta(Financial3),
2448
+ expansion_mrr_cents: z13.number().int().min(0).meta(Financial3),
2449
+ net_new_mrr_cents: z13.number().int().meta(Financial3)
2391
2450
  }).meta(
2392
2451
  { id: "RevenueMetric", "x-revturbine-schema-persistence": Transient10, "x-revturbine-schema-exposure": External8 }
2393
2452
  );
2394
2453
  var KpiAggregateSchema = z13.object({
2395
2454
  metric_key: z13.string().min(1).meta(Unrestricted10),
2396
2455
  label: z13.string().meta(Unrestricted10),
2397
- current_value: z13.number().meta(Financial2),
2398
- previous_value: z13.number().nullable().default(null).meta(Financial2),
2456
+ current_value: z13.number().meta(Financial3),
2457
+ previous_value: z13.number().nullable().default(null).meta(Financial3),
2399
2458
  change_percent: z13.number().nullable().default(null).meta(Unrestricted10),
2400
2459
  period: z13.string().meta(Unrestricted10),
2401
2460
  unit: z13.enum(["count", "cents", "percent", "ratio", "seconds"]).default("count").meta(Unrestricted10)
@@ -2415,7 +2474,7 @@ var DriftReportSchema = IdField.merge(TimestampFields).merge(TenantIdField).exte
2415
2474
  resolved_at: NullableDatetimeField.meta(Unrestricted10),
2416
2475
  metadata: MetadataField.meta(Unrestricted10)
2417
2476
  }).meta(
2418
- { id: "DriftReport", "x-revturbine-schema-persistence": Persisted10, "x-revturbine-schema-exposure": Internal7 }
2477
+ { id: "DriftReport", "x-revturbine-schema-persistence": Persisted10, "x-revturbine-schema-exposure": Internal8 }
2419
2478
  );
2420
2479
  var AlertSchema = IdField.merge(TimestampFields).merge(TenantIdField).extend({
2421
2480
  alert_type: z13.enum(["usage_threshold", "revenue_drop", "conversion_drop", "experiment_significance", "drift_detected", "custom"]).meta(Unrestricted10),
@@ -2429,7 +2488,7 @@ var AlertSchema = IdField.merge(TimestampFields).merge(TenantIdField).extend({
2429
2488
  acknowledged_by: z13.string().nullable().default(null).meta(Unrestricted10),
2430
2489
  metadata: MetadataField.meta(Unrestricted10)
2431
2490
  }).meta(
2432
- { id: "Alert", "x-revturbine-schema-persistence": Persisted10, "x-revturbine-schema-exposure": Internal7 }
2491
+ { id: "Alert", "x-revturbine-schema-persistence": Persisted10, "x-revturbine-schema-exposure": Internal8 }
2433
2492
  );
2434
2493
  var analyticsPaths = {
2435
2494
  "/api/analytics/kpis": {
@@ -2525,7 +2584,7 @@ var analyticsPaths = {
2525
2584
  };
2526
2585
  var { Unrestricted: Unrestricted11, Pii: Pii4 } = DataClassification;
2527
2586
  var { Persisted: Persisted11, Transient: Transient11 } = SchemaPersistence;
2528
- var { Internal: Internal8, External: External9 } = SchemaExposure;
2587
+ var { Internal: Internal9, External: External9 } = SchemaExposure;
2529
2588
  var EventSourceSchema = z14.enum(["clickstream", "telemetry", "sdk", "workflow", "system"]).meta(
2530
2589
  {
2531
2590
  id: "EventSource",
@@ -2547,7 +2606,7 @@ var EventEnvelopeSchema = IdField.extend({
2547
2606
  {
2548
2607
  id: "EventEnvelope",
2549
2608
  "x-revturbine-schema-persistence": Transient11,
2550
- "x-revturbine-schema-exposure": Internal8
2609
+ "x-revturbine-schema-exposure": Internal9
2551
2610
  }
2552
2611
  );
2553
2612
  var IngestedEventSchema = EventEnvelopeSchema.extend({
@@ -2556,7 +2615,7 @@ var IngestedEventSchema = EventEnvelopeSchema.extend({
2556
2615
  {
2557
2616
  id: "IngestedEvent",
2558
2617
  "x-revturbine-schema-persistence": Persisted11,
2559
- "x-revturbine-schema-exposure": Internal8
2618
+ "x-revturbine-schema-exposure": Internal9
2560
2619
  }
2561
2620
  );
2562
2621
  var EventIngestBatchSchema = z14.array(
@@ -2577,7 +2636,7 @@ var EventIngestBatchSchema = z14.array(
2577
2636
  {
2578
2637
  id: "EventIngestBatch",
2579
2638
  "x-revturbine-schema-persistence": Transient11,
2580
- "x-revturbine-schema-exposure": Internal8
2639
+ "x-revturbine-schema-exposure": Internal9
2581
2640
  }
2582
2641
  );
2583
2642
  var TreatmentInteractionTypeSchema = z14.enum([
@@ -2606,6 +2665,12 @@ var TreatmentInteractionInputSchema = z14.object({
2606
2665
  payload_id: z14.string().min(1).optional().meta(Unrestricted11),
2607
2666
  interaction_type: TreatmentInteractionTypeSchema.meta(Unrestricted11),
2608
2667
  interaction_at: z14.string().datetime().optional().meta(Unrestricted11),
2668
+ /**
2669
+ * Caller-declared test traffic (plan 164) — mirrors `TrackEvent.test` so
2670
+ * the presentation/interaction feed (the dashboard denominators) carries
2671
+ * the same default-excluded, `include_test`-toggleable dimension.
2672
+ */
2673
+ test: z14.boolean().optional().meta(Unrestricted11),
2609
2674
  metadata: z14.record(z14.string(), z14.unknown()).optional().meta(Unrestricted11)
2610
2675
  }).meta(
2611
2676
  {
@@ -2754,21 +2819,21 @@ var EventSearchParamsSchema = z14.object({
2754
2819
  {
2755
2820
  id: "EventSearchParams",
2756
2821
  "x-revturbine-schema-persistence": Transient11,
2757
- "x-revturbine-schema-exposure": Internal8
2822
+ "x-revturbine-schema-exposure": Internal9
2758
2823
  }
2759
2824
  );
2760
2825
  var WebhookEventSourceSchema = z14.enum(["stripe", "apple", "google"]).meta(
2761
2826
  {
2762
2827
  id: "WebhookEventSource",
2763
2828
  "x-revturbine-schema-persistence": Transient11,
2764
- "x-revturbine-schema-exposure": Internal8
2829
+ "x-revturbine-schema-exposure": Internal9
2765
2830
  }
2766
2831
  );
2767
2832
  var WebhookEventStatusSchema = z14.enum(["processed", "failed", "skipped"]).meta(
2768
2833
  {
2769
2834
  id: "WebhookEventStatus",
2770
2835
  "x-revturbine-schema-persistence": Transient11,
2771
- "x-revturbine-schema-exposure": Internal8
2836
+ "x-revturbine-schema-exposure": Internal9
2772
2837
  }
2773
2838
  );
2774
2839
  var WebhookEventLogSchema = IdField.merge(TimestampFields).merge(TenantIdField).extend({
@@ -2783,7 +2848,7 @@ var WebhookEventLogSchema = IdField.merge(TimestampFields).merge(TenantIdField).
2783
2848
  {
2784
2849
  id: "WebhookEventLog",
2785
2850
  "x-revturbine-schema-persistence": Persisted11,
2786
- "x-revturbine-schema-exposure": Internal8
2851
+ "x-revturbine-schema-exposure": Internal9
2787
2852
  }
2788
2853
  );
2789
2854
  var EventIngestResponseSchema = z14.object({
@@ -2792,7 +2857,7 @@ var EventIngestResponseSchema = z14.object({
2792
2857
  {
2793
2858
  id: "null",
2794
2859
  "x-revturbine-schema-persistence": Transient11,
2795
- "x-revturbine-schema-exposure": Internal8
2860
+ "x-revturbine-schema-exposure": Internal9
2796
2861
  }
2797
2862
  );
2798
2863
  var MAX_TRACK_EVENTS_PER_BATCH = 500;
@@ -2837,7 +2902,16 @@ var TrackEventSchema = z14.object({
2837
2902
  /** Immutable Playbook version that produced the experience. The field that makes a past decision reproducible. */
2838
2903
  playbook_version: z14.string().nullable().optional().meta(Unrestricted11),
2839
2904
  /** Correlates every event caused by one decision — a join key, not a group-by. */
2840
- decision_id: z14.string().nullable().optional().meta(Unrestricted11)
2905
+ decision_id: z14.string().nullable().optional().meta(Unrestricted11),
2906
+ /**
2907
+ * Caller-declared test traffic (plan 164): set from the SDK's `test` init
2908
+ * option, stamped on every emitted event. Analytics pipes exclude
2909
+ * `test = true` rows from denominators/rollups by default, with an
2910
+ * `include_test` pipe parameter as the opt-in toggle. Distinct from the
2911
+ * placement "Test Mode" (plan 08c), which is a server-decided per-user
2912
+ * flag on DECISIONING responses — this one marks EMITTED events.
2913
+ */
2914
+ test: z14.boolean().optional().meta(Unrestricted11)
2841
2915
  }).meta(
2842
2916
  {
2843
2917
  id: "TrackEvent",
@@ -2855,7 +2929,7 @@ var TrackIngestBatchSchema = z14.object({
2855
2929
  }
2856
2930
  );
2857
2931
  var MAX_SDK_META_EVENTS_PER_BATCH = 10;
2858
- var SdkMetaEventTypeSchema = z14.enum(["sdk_init", "sdk_error", "sdk_validation_warning"]).meta(
2932
+ var SdkMetaEventTypeSchema = z14.enum(["sdk_init", "sdk_error", "sdk_validation_warning", "resolution_failure"]).meta(
2859
2933
  {
2860
2934
  id: "SdkMetaEventType",
2861
2935
  "x-revturbine-schema-persistence": Transient11,
@@ -2893,7 +2967,19 @@ var SdkMetaEventSchema = z14.object({
2893
2967
  // Present for sdk_init; config-shape counts only, no user context (REQ-6).
2894
2968
  config_shape: SdkConfigShapeSchema.optional().meta(Unrestricted11),
2895
2969
  // Short non-PII diagnostic for sdk_error / sdk_validation_warning.
2896
- message: z14.string().max(500).optional().meta(Unrestricted11)
2970
+ message: z14.string().max(500).optional().meta(Unrestricted11),
2971
+ // Diagnostic fields for `resolution_failure` (plan 144 TASK-20; absorbed
2972
+ // plan 124 REQ-5/AC-6 — its Q-1 allow-list, append-only). Every value is
2973
+ // an AUTHOR-DEFINED handle or a closed reason code — never user-supplied
2974
+ // free text; `message` above remains the only prose field and stays
2975
+ // length-capped. Emitted from the SDK's fallback/deny sites so a
2976
+ // placement that silently resolves to nothing becomes observable.
2977
+ reason: z14.string().min(1).max(64).optional().meta(Unrestricted11),
2978
+ placement_handle: z14.string().min(1).max(64).optional().meta(Unrestricted11),
2979
+ slot_handle: z14.string().min(1).max(64).optional().meta(Unrestricted11),
2980
+ surface: z14.string().min(1).max(64).optional().meta(Unrestricted11),
2981
+ plan_handle: z14.string().min(1).max(64).optional().meta(Unrestricted11),
2982
+ entitlement_handle: z14.string().min(1).max(64).optional().meta(Unrestricted11)
2897
2983
  }).meta(
2898
2984
  {
2899
2985
  id: "SdkMetaEvent",
@@ -2985,17 +3071,17 @@ var eventPaths = {
2985
3071
  };
2986
3072
  var { Unrestricted: Unrestricted12 } = DataClassification;
2987
3073
  var { Persisted: Persisted12, Transient: Transient12 } = SchemaPersistence;
2988
- var { Internal: Internal9 } = SchemaExposure;
3074
+ var { Internal: Internal10 } = SchemaExposure;
2989
3075
  var PLAYBOOK_SDK_FACETS6 = schemaFacets(SchemaContext.Playbook, { sdkInput: true });
2990
3076
  var PENDING_PLAYBOOK_SDK_FACETS2 = schemaFacets(SchemaContext.Playbook, {
2991
3077
  inConfig: false,
2992
3078
  sdkInput: true
2993
3079
  });
2994
3080
  var TrialStatusSchema = z15.enum(["not_started", "active", "expired", "converted", "cancelled"]).meta(
2995
- { id: "TrialStatus", "x-revturbine-schema-persistence": Transient12, "x-revturbine-schema-exposure": Internal9 }
3081
+ { id: "TrialStatus", "x-revturbine-schema-persistence": Transient12, "x-revturbine-schema-exposure": Internal10 }
2996
3082
  );
2997
3083
  var TrialLimitTypeSchema = z15.enum(["time", "usage"]).meta(
2998
- { id: "TrialLimitType", "x-revturbine-schema-persistence": Transient12, "x-revturbine-schema-exposure": Internal9 }
3084
+ { id: "TrialLimitType", "x-revturbine-schema-persistence": Transient12, "x-revturbine-schema-exposure": Internal10 }
2999
3085
  );
3000
3086
  var FreeTrialRuleCoreFieldsSchema = z15.object({
3001
3087
  name: NameField.meta(Unrestricted12),
@@ -3043,11 +3129,11 @@ var FreeTrialRuleCoreFieldsSchema = z15.object({
3043
3129
  var FreeTrialRuleSchema = IdField.merge(TimestampFields).merge(TenantIdField).merge(AnchorFields).merge(VersionFields).extend({
3044
3130
  anchor_id: z15.string().min(1).meta({ ...Unrestricted12, readOnly: true })
3045
3131
  }).merge(FreeTrialRuleCoreFieldsSchema).meta(
3046
- { id: "FreeTrialRule", "x-revturbine-schema-persistence": Persisted12, "x-revturbine-schema-exposure": Internal9, ...PLAYBOOK_SDK_FACETS6, ...namedIdentity() }
3132
+ { id: "FreeTrialRule", "x-revturbine-schema-persistence": Persisted12, "x-revturbine-schema-exposure": Internal10, ...PLAYBOOK_SDK_FACETS6, ...namedIdentity() }
3047
3133
  );
3048
3134
  var FreeTrialRuleAnchorSchema = makeAnchor("FreeTrialRuleAnchor");
3049
3135
  var ReverseTrialStartPolicySchema = z15.enum(["signup", "first_premium_access", "manual"]).meta(
3050
- { id: "ReverseTrialStartPolicy", "x-revturbine-schema-persistence": Transient12, "x-revturbine-schema-exposure": Internal9 }
3136
+ { id: "ReverseTrialStartPolicy", "x-revturbine-schema-persistence": Transient12, "x-revturbine-schema-exposure": Internal10 }
3051
3137
  );
3052
3138
  var ReverseTrialRuleCoreFieldsSchema = z15.object({
3053
3139
  name: NameField.meta(Unrestricted12),
@@ -3068,26 +3154,26 @@ var ReverseTrialRuleCoreFieldsSchema = z15.object({
3068
3154
  var ReverseTrialRuleSchema = IdField.merge(TimestampFields).merge(TenantIdField).merge(AnchorFields).merge(VersionFields).extend({
3069
3155
  anchor_id: z15.string().min(1).meta({ ...Unrestricted12, readOnly: true })
3070
3156
  }).merge(ReverseTrialRuleCoreFieldsSchema).meta(
3071
- { id: "ReverseTrialRule", "x-revturbine-schema-persistence": Persisted12, "x-revturbine-schema-exposure": Internal9, ...PLAYBOOK_SDK_FACETS6, ...namedIdentity() }
3157
+ { id: "ReverseTrialRule", "x-revturbine-schema-persistence": Persisted12, "x-revturbine-schema-exposure": Internal10, ...PLAYBOOK_SDK_FACETS6, ...namedIdentity() }
3072
3158
  );
3073
3159
  var ReverseTrialRuleAnchorSchema = makeAnchor("ReverseTrialRuleAnchor");
3074
3160
  var TrialLimitPolicySchema = z15.enum(["1_per_lifetime", "1_per_plan", "1_per_year", "unlimited"]).meta(
3075
- { id: "TrialLimitPolicy", "x-revturbine-schema-persistence": Transient12, "x-revturbine-schema-exposure": Internal9 }
3161
+ { id: "TrialLimitPolicy", "x-revturbine-schema-persistence": Transient12, "x-revturbine-schema-exposure": Internal10 }
3076
3162
  );
3077
3163
  var TrialEligibilityScopeSchema = z15.enum(["per_customer", "per_email_domain"]).meta(
3078
- { id: "TrialEligibilityScope", "x-revturbine-schema-persistence": Transient12, "x-revturbine-schema-exposure": Internal9 }
3164
+ { id: "TrialEligibilityScope", "x-revturbine-schema-persistence": Transient12, "x-revturbine-schema-exposure": Internal10 }
3079
3165
  );
3080
3166
  var FreeTrialSettingsSchema = IdField.merge(TimestampFields).merge(TenantIdField).extend({
3081
3167
  trial_limit_policy: TrialLimitPolicySchema.default("1_per_lifetime").meta(Unrestricted12),
3082
3168
  eligibility_scope: TrialEligibilityScopeSchema.default("per_customer").meta(Unrestricted12)
3083
3169
  }).meta(
3084
- { id: "FreeTrialSettings", "x-revturbine-schema-persistence": Persisted12, "x-revturbine-schema-exposure": Internal9, ...PENDING_PLAYBOOK_SDK_FACETS2 }
3170
+ { id: "FreeTrialSettings", "x-revturbine-schema-persistence": Persisted12, "x-revturbine-schema-exposure": Internal10, ...PENDING_PLAYBOOK_SDK_FACETS2 }
3085
3171
  );
3086
3172
  var ReverseTrialSettingsSchema = IdField.merge(TimestampFields).merge(TenantIdField).extend({
3087
3173
  trial_limit_policy: TrialLimitPolicySchema.default("1_per_lifetime").meta(Unrestricted12),
3088
3174
  eligibility_scope: TrialEligibilityScopeSchema.default("per_customer").meta(Unrestricted12)
3089
3175
  }).meta(
3090
- { id: "ReverseTrialSettings", "x-revturbine-schema-persistence": Persisted12, "x-revturbine-schema-exposure": Internal9, ...PENDING_PLAYBOOK_SDK_FACETS2 }
3176
+ { id: "ReverseTrialSettings", "x-revturbine-schema-persistence": Persisted12, "x-revturbine-schema-exposure": Internal10, ...PENDING_PLAYBOOK_SDK_FACETS2 }
3091
3177
  );
3092
3178
  var TrialInstanceSchema = IdField.merge(TimestampFields).merge(TenantIdField).extend({
3093
3179
  customer_id: z15.string().min(1).meta(Unrestricted12),
@@ -3127,7 +3213,7 @@ var TrialInstanceSchema = IdField.merge(TimestampFields).merge(TenantIdField).ex
3127
3213
  cancelled_at: NullableDatetimeField.meta(Unrestricted12),
3128
3214
  metadata: MetadataField.meta(Unrestricted12)
3129
3215
  }).meta(
3130
- { id: "TrialInstance", "x-revturbine-schema-persistence": Persisted12, "x-revturbine-schema-exposure": Internal9 }
3216
+ { id: "TrialInstance", "x-revturbine-schema-persistence": Persisted12, "x-revturbine-schema-exposure": Internal10 }
3131
3217
  );
3132
3218
  var trialPaths = {
3133
3219
  "/api/free-trial-rule-anchors": {
@@ -3364,16 +3450,16 @@ var trialPaths = {
3364
3450
  };
3365
3451
  var { Unrestricted: Unrestricted13 } = DataClassification;
3366
3452
  var { Persisted: Persisted13, Transient: Transient13 } = SchemaPersistence;
3367
- var { Internal: Internal10 } = SchemaExposure;
3453
+ var { Internal: Internal11 } = SchemaExposure;
3368
3454
  var PENDING_PLAYBOOK_SDK_FACETS3 = schemaFacets(SchemaContext.Playbook, {
3369
3455
  inConfig: false,
3370
3456
  sdkInput: true
3371
3457
  });
3372
3458
  var ExperimentStatusSchema = z16.enum(["draft", "ramping", "winning", "neutral", "needs_attention", "paused", "complete"]).meta(
3373
- { id: "ExperimentStatus", "x-revturbine-schema-persistence": Transient13, "x-revturbine-schema-exposure": Internal10 }
3459
+ { id: "ExperimentStatus", "x-revturbine-schema-persistence": Transient13, "x-revturbine-schema-exposure": Internal11 }
3374
3460
  );
3375
3461
  var ExperimentTypeSchema = z16.enum(["placement_ab", "entitlement_ab", "plan_ab", "pricing_ab", "custom"]).meta(
3376
- { id: "ExperimentType", "x-revturbine-schema-persistence": Transient13, "x-revturbine-schema-exposure": Internal10 }
3462
+ { id: "ExperimentType", "x-revturbine-schema-persistence": Transient13, "x-revturbine-schema-exposure": Internal11 }
3377
3463
  );
3378
3464
  var ExperimentVariantSchema = z16.object({
3379
3465
  variant_id: z16.string().min(1),
@@ -3382,7 +3468,7 @@ var ExperimentVariantSchema = z16.object({
3382
3468
  is_control: z16.boolean().default(false),
3383
3469
  config: z16.record(z16.string(), z16.unknown()).default({})
3384
3470
  }).meta(
3385
- { id: "ExperimentVariant", "x-revturbine-schema-persistence": Persisted13, "x-revturbine-schema-exposure": Internal10, ...PENDING_PLAYBOOK_SDK_FACETS3 }
3471
+ { id: "ExperimentVariant", "x-revturbine-schema-persistence": Persisted13, "x-revturbine-schema-exposure": Internal11, ...PENDING_PLAYBOOK_SDK_FACETS3 }
3386
3472
  );
3387
3473
  var ExperimentSchema = IdField.merge(TimestampFields).merge(TenantIdField).merge(AnchorFields).merge(VersionFields).extend({
3388
3474
  anchor_id: z16.string().min(1).meta({ ...Unrestricted13, readOnly: true }),
@@ -3406,7 +3492,7 @@ var ExperimentSchema = IdField.merge(TimestampFields).merge(TenantIdField).merge
3406
3492
  winning_variant_id: z16.string().nullable().default(null).meta(Unrestricted13),
3407
3493
  metadata: MetadataField.meta(Unrestricted13)
3408
3494
  }).meta(
3409
- { id: "Experiment", "x-revturbine-schema-persistence": Persisted13, "x-revturbine-schema-exposure": Internal10, ...PENDING_PLAYBOOK_SDK_FACETS3, ...namedIdentity() }
3495
+ { id: "Experiment", "x-revturbine-schema-persistence": Persisted13, "x-revturbine-schema-exposure": Internal11, ...PENDING_PLAYBOOK_SDK_FACETS3, ...namedIdentity() }
3410
3496
  );
3411
3497
  var ExperimentAnchorSchema = makeAnchor("ExperimentAnchor");
3412
3498
  var SuggestionSeveritySchema = SeveritySchema;
@@ -3422,7 +3508,7 @@ var OptimizationSuggestionSchema = IdField.merge(TimestampFields).merge(TenantId
3422
3508
  is_dismissed: z16.boolean().default(false).meta(Unrestricted13),
3423
3509
  metadata: MetadataField.meta(Unrestricted13)
3424
3510
  }).meta(
3425
- { id: "OptimizationSuggestion", "x-revturbine-schema-persistence": Persisted13, "x-revturbine-schema-exposure": Internal10 }
3511
+ { id: "OptimizationSuggestion", "x-revturbine-schema-persistence": Persisted13, "x-revturbine-schema-exposure": Internal11 }
3426
3512
  );
3427
3513
  var experimentPaths = {
3428
3514
  "/api/experiment-anchors": {
@@ -3535,15 +3621,15 @@ var experimentPaths = {
3535
3621
  })
3536
3622
  }
3537
3623
  };
3538
- var { Unrestricted: Unrestricted14, Financial: Financial3 } = DataClassification;
3624
+ var { Unrestricted: Unrestricted14, Financial: Financial4 } = DataClassification;
3539
3625
  var { Persisted: Persisted14, Transient: Transient14 } = SchemaPersistence;
3540
- var { Internal: Internal11 } = SchemaExposure;
3626
+ var { Internal: Internal12 } = SchemaExposure;
3541
3627
  var PLAYBOOK_SDK_FACETS7 = schemaFacets(SchemaContext.Playbook, { sdkInput: true });
3542
3628
  var PromotionStatusSchema = z17.enum(["draft", "scheduled", "live", "expired", "archived"]).meta(
3543
- { id: "PromotionStatus", "x-revturbine-schema-persistence": Transient14, "x-revturbine-schema-exposure": Internal11 }
3629
+ { id: "PromotionStatus", "x-revturbine-schema-persistence": Transient14, "x-revturbine-schema-exposure": Internal12 }
3544
3630
  );
3545
3631
  var DiscountTypeSchema = z17.enum(["percentage", "fixed_amount", "free_months"]).meta(
3546
- { id: "DiscountType", "x-revturbine-schema-persistence": Transient14, "x-revturbine-schema-exposure": Internal11 }
3632
+ { id: "DiscountType", "x-revturbine-schema-persistence": Transient14, "x-revturbine-schema-exposure": Internal12 }
3547
3633
  );
3548
3634
  var PromotionSchema = IdField.merge(TimestampFields).merge(TenantIdField).merge(AnchorFields).merge(VersionFields).extend({
3549
3635
  anchor_id: z17.string().min(1).meta({ ...Unrestricted14, readOnly: true }),
@@ -3552,8 +3638,8 @@ var PromotionSchema = IdField.merge(TimestampFields).merge(TenantIdField).merge(
3552
3638
  description: z17.string().max(1e3).optional().meta(Unrestricted14),
3553
3639
  rt_status: PromotionStatusSchema.default("draft").meta(Unrestricted14),
3554
3640
  discount_type: DiscountTypeSchema.meta(Unrestricted14),
3555
- discount_value: z17.number().min(0).meta(Financial3),
3556
- currency: z17.string().length(3).default("USD").meta(Financial3),
3641
+ discount_value: z17.number().min(0).meta(Financial4),
3642
+ currency: z17.string().length(3).default("USD").meta(Financial4),
3557
3643
  applicable_plan_ids: z17.array(z17.string()).default([]).meta(Unrestricted14),
3558
3644
  applicable_addon_ids: z17.array(z17.string()).default([]).meta(Unrestricted14),
3559
3645
  target_segment_ids: z17.array(z17.string()).default([]).meta(Unrestricted14),
@@ -3568,7 +3654,7 @@ var PromotionSchema = IdField.merge(TimestampFields).merge(TenantIdField).merge(
3568
3654
  auto_sync_stripe: z17.boolean().default(false).meta(Unrestricted14),
3569
3655
  metadata: MetadataField.meta(Unrestricted14)
3570
3656
  }).meta(
3571
- { id: "Promotion", "x-revturbine-schema-persistence": Persisted14, "x-revturbine-schema-exposure": Internal11, ...PLAYBOOK_SDK_FACETS7, ...namedIdentity() }
3657
+ { id: "Promotion", "x-revturbine-schema-persistence": Persisted14, "x-revturbine-schema-exposure": Internal12, ...PLAYBOOK_SDK_FACETS7, ...namedIdentity() }
3572
3658
  );
3573
3659
  var PromotionAnchorSchema = makeAnchor("PromotionAnchor");
3574
3660
  var promotionPaths = {
@@ -3653,7 +3739,7 @@ var promotionPaths = {
3653
3739
  };
3654
3740
  var { Unrestricted: Unrestricted15, Pii: Pii5 } = DataClassification;
3655
3741
  var { Persisted: Persisted15, Transient: Transient15 } = SchemaPersistence;
3656
- var { Internal: Internal12, External: External10 } = SchemaExposure;
3742
+ var { Internal: Internal13, External: External10 } = SchemaExposure;
3657
3743
  var PresentationRecordSchema = IdField.merge(TimestampFields).merge(TenantIdField).extend({
3658
3744
  customer_id: z18.string().min(1).meta(Pii5),
3659
3745
  placement_id: z18.string().min(1).meta(Unrestricted15),
@@ -3668,7 +3754,7 @@ var PresentationRecordSchema = IdField.merge(TimestampFields).merge(TenantIdFiel
3668
3754
  session_id: z18.string().optional().meta(Unrestricted15),
3669
3755
  metadata: MetadataField.meta(Unrestricted15)
3670
3756
  }).meta(
3671
- { id: "PresentationRecord", "x-revturbine-schema-persistence": Persisted15, "x-revturbine-schema-exposure": Internal12 }
3757
+ { id: "PresentationRecord", "x-revturbine-schema-persistence": Persisted15, "x-revturbine-schema-exposure": Internal13 }
3672
3758
  );
3673
3759
  var DecisionLogSchema = IdField.merge(TimestampFields).merge(TenantIdField).extend({
3674
3760
  customer_id: z18.string().min(1).meta(Pii5),
@@ -3684,7 +3770,7 @@ var DecisionLogSchema = IdField.merge(TimestampFields).merge(TenantIdField).exte
3684
3770
  variant_id: z18.string().nullable().default(null).meta(Unrestricted15),
3685
3771
  latency_ms: z18.number().min(0).optional().meta(Unrestricted15)
3686
3772
  }).meta(
3687
- { id: "DecisionLog", "x-revturbine-schema-persistence": Persisted15, "x-revturbine-schema-exposure": Internal12 }
3773
+ { id: "DecisionLog", "x-revturbine-schema-persistence": Persisted15, "x-revturbine-schema-exposure": Internal13 }
3688
3774
  );
3689
3775
  var EntitlementEvalLogSchema = IdField.merge(TimestampFields).merge(TenantIdField).extend({
3690
3776
  customer_id: z18.string().min(1).meta(Pii5),
@@ -3697,7 +3783,7 @@ var EntitlementEvalLogSchema = IdField.merge(TimestampFields).merge(TenantIdFiel
3697
3783
  source_id: z18.string().min(1).meta(Unrestricted15),
3698
3784
  latency_ms: z18.number().min(0).optional().meta(Unrestricted15)
3699
3785
  }).meta(
3700
- { id: "EntitlementEvalLog", "x-revturbine-schema-persistence": Persisted15, "x-revturbine-schema-exposure": Internal12 }
3786
+ { id: "EntitlementEvalLog", "x-revturbine-schema-persistence": Persisted15, "x-revturbine-schema-exposure": Internal13 }
3701
3787
  );
3702
3788
  var runtimePaths = {
3703
3789
  "/api/presentation-records": {
@@ -3733,7 +3819,7 @@ var runtimePaths = {
3733
3819
  };
3734
3820
  var { Unrestricted: Unrestricted16 } = DataClassification;
3735
3821
  var { Persisted: Persisted16, Transient: Transient16 } = SchemaPersistence;
3736
- var { Internal: Internal13, External: External11 } = SchemaExposure;
3822
+ var { Internal: Internal14, External: External11 } = SchemaExposure;
3737
3823
  var PLAYBOOK_SDK_FACETS8 = schemaFacets(SchemaContext.Playbook, { sdkInput: true });
3738
3824
  var PLAYBOOK_AUTHORING_FACETS2 = schemaFacets(SchemaContext.Playbook, { sdkInput: false });
3739
3825
  var PENDING_PLAYBOOK_FACETS4 = schemaFacets(SchemaContext.Playbook, {
@@ -3776,7 +3862,7 @@ var SeatTypeSchema = IdField.merge(TimestampFields).merge(TenantIdField).merge(A
3776
3862
  entitlement_ids: z19.array(z19.string()).default([]).meta(Unrestricted16),
3777
3863
  metadata: MetadataField.meta(Unrestricted16)
3778
3864
  }).meta(
3779
- { id: "SeatType", "x-revturbine-schema-persistence": Persisted16, "x-revturbine-schema-exposure": Internal13, ...PENDING_PLAYBOOK_FACETS4, ...namedIdentity() }
3865
+ { id: "SeatType", "x-revturbine-schema-persistence": Persisted16, "x-revturbine-schema-exposure": Internal14, ...PENDING_PLAYBOOK_FACETS4, ...namedIdentity() }
3780
3866
  );
3781
3867
  var SeatTypeAnchorSchema = makeAnchor("SeatTypeAnchor");
3782
3868
  var PersonalizationTokenSchema = IdField.merge(TimestampFields).merge(TenantIdField).merge(AnchorFields).merge(VersionFields).extend({
@@ -3791,10 +3877,10 @@ var PersonalizationTokenSchema = IdField.merge(TimestampFields).merge(TenantIdFi
3791
3877
  format: z19.enum(["string", "number", "currency", "percentage", "date"]).nullable().default(null).meta(Unrestricted16),
3792
3878
  metadata: MetadataField.meta(Unrestricted16)
3793
3879
  }).meta(
3794
- { id: "PersonalizationToken", "x-revturbine-schema-persistence": Persisted16, "x-revturbine-schema-exposure": Internal13, ...PENDING_PLAYBOOK_FACETS4, ...namedIdentity() }
3880
+ { id: "PersonalizationToken", "x-revturbine-schema-persistence": Persisted16, "x-revturbine-schema-exposure": Internal14, ...PENDING_PLAYBOOK_FACETS4, ...namedIdentity() }
3795
3881
  );
3796
3882
  var PersonalizationTokenAnchorSchema = makeAnchor("PersonalizationTokenAnchor");
3797
- var OnboardingStateSchema = z19.enum(["not_started", "started", "details_submitted", "charges_enabled", "activated", "deauthorized"]).meta({ id: "OnboardingState", "x-revturbine-schema-persistence": Transient16, "x-revturbine-schema-exposure": Internal13 });
3883
+ var OnboardingStateSchema = z19.enum(["not_started", "started", "details_submitted", "charges_enabled", "activated", "deauthorized"]).meta({ id: "OnboardingState", "x-revturbine-schema-persistence": Transient16, "x-revturbine-schema-exposure": Internal14 });
3798
3884
  var StripeIntegrationConfigSchema = IdField.merge(TimestampFields).merge(TenantIdField).merge(AnchorFields).merge(VersionFields).extend({
3799
3885
  handle: HandleField.meta({ ...Unrestricted16, readOnly: true }),
3800
3886
  stripe_account_id: z19.string().min(1).meta(Unrestricted16),
@@ -3820,7 +3906,7 @@ var StripeIntegrationConfigSchema = IdField.merge(TimestampFields).merge(TenantI
3820
3906
  last_sync_at: z19.string().optional().meta({ ...Unrestricted16, readOnly: true }),
3821
3907
  metadata: MetadataField.meta(Unrestricted16)
3822
3908
  }).meta(
3823
- { id: "StripeIntegrationConfig", "x-revturbine-schema-persistence": Persisted16, "x-revturbine-schema-exposure": Internal13, ...BILLING_FACETS2, ...mintedIdentity() }
3909
+ { id: "StripeIntegrationConfig", "x-revturbine-schema-persistence": Persisted16, "x-revturbine-schema-exposure": Internal14, ...BILLING_FACETS2, ...mintedIdentity() }
3824
3910
  );
3825
3911
  var BrandingConfigSchema = z19.object({
3826
3912
  theme: z19.record(z19.string(), z19.unknown()).optional().meta(Unrestricted16),
@@ -3845,10 +3931,10 @@ var MeteringConfigSchema = IdField.merge(TimestampFields).merge(TenantIdField).m
3845
3931
  is_active: z19.boolean().default(true).meta(Unrestricted16),
3846
3932
  metadata: MetadataField.meta(Unrestricted16)
3847
3933
  }).meta(
3848
- { id: "MeteringConfig", "x-revturbine-schema-persistence": Persisted16, "x-revturbine-schema-exposure": Internal13, ...METERING_FACETS, ...mintedIdentity() }
3934
+ { id: "MeteringConfig", "x-revturbine-schema-persistence": Persisted16, "x-revturbine-schema-exposure": Internal14, ...METERING_FACETS, ...mintedIdentity() }
3849
3935
  );
3850
3936
  var EnforcementActionSchema = z19.enum(["block", "warn", "downgrade", "throttle", "notify_admin", "custom"]).meta(
3851
- { id: "EnforcementAction", "x-revturbine-schema-persistence": Transient16, "x-revturbine-schema-exposure": Internal13 }
3937
+ { id: "EnforcementAction", "x-revturbine-schema-persistence": Transient16, "x-revturbine-schema-exposure": Internal14 }
3852
3938
  );
3853
3939
  var UsageEnforcementSettingsSchema = IdField.merge(TimestampFields).merge(TenantIdField).merge(AnchorFields).merge(VersionFields).extend({
3854
3940
  anchor_id: z19.string().min(1).meta({ ...Unrestricted16, readOnly: true }),
@@ -3862,7 +3948,7 @@ var UsageEnforcementSettingsSchema = IdField.merge(TimestampFields).merge(Tenant
3862
3948
  notification_channels: z19.array(z19.enum(["email", "in_app", "webhook"])).default(["in_app"]).meta(Unrestricted16),
3863
3949
  is_active: z19.boolean().default(true).meta(Unrestricted16)
3864
3950
  }).meta(
3865
- { id: "UsageEnforcementSettings", "x-revturbine-schema-persistence": Persisted16, "x-revturbine-schema-exposure": Internal13, ...PENDING_PLAYBOOK_SDK_FACETS4, ...mintedIdentity() }
3951
+ { id: "UsageEnforcementSettings", "x-revturbine-schema-persistence": Persisted16, "x-revturbine-schema-exposure": Internal14, ...PENDING_PLAYBOOK_SDK_FACETS4, ...mintedIdentity() }
3866
3952
  );
3867
3953
  var UsageEnforcementSettingsAnchorSchema = makeAnchor("UsageEnforcementSettingsAnchor");
3868
3954
  var PlacementSettingsCapRuleGroupItemSchema = z19.object({
@@ -3870,7 +3956,7 @@ var PlacementSettingsCapRuleGroupItemSchema = z19.object({
3870
3956
  id: z19.string().min(1).meta(Unrestricted16),
3871
3957
  label: z19.string().min(1).optional().meta(Unrestricted16)
3872
3958
  }).meta(
3873
- { id: "PlacementSettingsCapRuleGroupItem", "x-revturbine-schema-persistence": Transient16, "x-revturbine-schema-exposure": Internal13 }
3959
+ { id: "PlacementSettingsCapRuleGroupItem", "x-revturbine-schema-persistence": Transient16, "x-revturbine-schema-exposure": Internal14 }
3874
3960
  );
3875
3961
  var PlacementSettingsCapRuleSchema = z19.object({
3876
3962
  id: z19.string().min(1).meta(Unrestricted16),
@@ -3880,17 +3966,21 @@ var PlacementSettingsCapRuleSchema = z19.object({
3880
3966
  period: z19.enum(["session", "day", "week", "month"]).meta(Unrestricted16)
3881
3967
  }).meta(Unrestricted16)
3882
3968
  }).meta(
3883
- { id: "PlacementSettingsCapRule", "x-revturbine-schema-persistence": Transient16, "x-revturbine-schema-exposure": Internal13 }
3969
+ { id: "PlacementSettingsCapRule", "x-revturbine-schema-persistence": Transient16, "x-revturbine-schema-exposure": Internal14 }
3884
3970
  );
3885
3971
  var PlacementTestModeSchema = z19.enum(["off", "test_users", "all_traffic"]).meta(
3886
- { id: "PlacementTestMode", "x-revturbine-schema-persistence": Transient16, "x-revturbine-schema-exposure": Internal13 }
3972
+ { id: "PlacementTestMode", "x-revturbine-schema-persistence": Transient16, "x-revturbine-schema-exposure": Internal14 }
3887
3973
  );
3888
3974
  var PlacementSettingsCapStateSchema = z19.object({
3889
3975
  capRules: z19.array(PlacementSettingsCapRuleSchema).default([]).meta(Unrestricted16),
3890
3976
  sessionCooldownMinutes: z19.number().int().min(0).default(30).meta(Unrestricted16),
3977
+ // Tenant-level default remind-me-later (defer) window, in minutes. A
3978
+ // per-payload `remind_later_minutes` overrides it when set (plan 167 REQ-6,
3979
+ // Q-3). Rides in this global_frequency_cap jsonb wrapper — no column/`.fbs`.
3980
+ remindLaterMinutes: z19.number().int().min(0).default(60).meta(Unrestricted16),
3891
3981
  testMode: PlacementTestModeSchema.default("off").meta(Unrestricted16)
3892
3982
  }).meta(
3893
- { id: "PlacementSettingsCapState", "x-revturbine-schema-persistence": Transient16, "x-revturbine-schema-exposure": Internal13 }
3983
+ { id: "PlacementSettingsCapState", "x-revturbine-schema-persistence": Transient16, "x-revturbine-schema-exposure": Internal14 }
3894
3984
  );
3895
3985
  var PlacementSettingsSchema = IdField.merge(TimestampFields).merge(TenantIdField).merge(AnchorFields).merge(VersionFields).extend({
3896
3986
  anchor_id: z19.string().min(1).meta({ ...Unrestricted16, readOnly: true }),
@@ -3902,11 +3992,12 @@ var PlacementSettingsSchema = IdField.merge(TimestampFields).merge(TenantIdField
3902
3992
  global_frequency_cap_period: z19.enum(["hour", "day", "week", "month", "session"]).nullable().default(null).meta(Unrestricted16),
3903
3993
  suppress_for_paid: z19.boolean().default(false).meta(Unrestricted16),
3904
3994
  suppress_for_trial: z19.boolean().default(false).meta(Unrestricted16),
3905
- default_dismiss_cooldown_hours: z19.number().int().min(0).default(24).meta(Unrestricted16),
3995
+ // `default_dismiss_cooldown_hours` removed (plan 167 Q-2): the dismiss
3996
+ // cooldown is defined per-payload in days (`cooldown_after_dismiss_days`).
3906
3997
  allow_stacking: z19.boolean().default(false).meta(Unrestricted16),
3907
3998
  priority_collision_strategy: z19.enum(["highest_priority", "most_recent", "random"]).default("highest_priority").meta(Unrestricted16)
3908
3999
  }).meta(
3909
- { id: "PlacementSettings", "x-revturbine-schema-persistence": Persisted16, "x-revturbine-schema-exposure": Internal13, ...PENDING_PLAYBOOK_SDK_FACETS4, ...mintedIdentity() }
4000
+ { id: "PlacementSettings", "x-revturbine-schema-persistence": Persisted16, "x-revturbine-schema-exposure": Internal14, ...PENDING_PLAYBOOK_SDK_FACETS4, ...mintedIdentity() }
3910
4001
  );
3911
4002
  var PlacementSettingsAnchorSchema = makeAnchor("PlacementSettingsAnchor");
3912
4003
  var RevTurbineConfigSegmentsItemPredicatesItemSchema = z19.object({
@@ -4014,7 +4105,7 @@ var RevTurbineConfigPlacementSettingsItemSchema = z19.object({
4014
4105
  global_frequency_cap_period: z19.enum(["hour", "day", "week", "month", "session"]).nullable().default(null).meta(Unrestricted16),
4015
4106
  suppress_for_paid: z19.boolean().default(false).meta(Unrestricted16),
4016
4107
  suppress_for_trial: z19.boolean().default(false).meta(Unrestricted16),
4017
- default_dismiss_cooldown_hours: z19.number().int().min(0).nullable().default(null).meta(Unrestricted16),
4108
+ // `default_dismiss_cooldown_hours` removed (plan 167 Q-2).
4018
4109
  allow_stacking: z19.boolean().default(false).meta(Unrestricted16),
4019
4110
  priority_collision_strategy: z19.string().nullable().default(null).meta(Unrestricted16)
4020
4111
  }).meta(
@@ -4156,7 +4247,11 @@ var RevTurbineConfigUiPathActionTypeSchema = z19.enum([
4156
4247
  "verify_work_email",
4157
4248
  "update_payment_method",
4158
4249
  "enable_auto_renewal",
4159
- "manage_subscription"
4250
+ "manage_subscription",
4251
+ // Authored `snooze` resolves through to the SDK's remind-later path
4252
+ // (plan 167 windows); previously it fell through as an invalid type
4253
+ // (plan 174 TASK-6 / Q-5, spec-check F-65a).
4254
+ "snooze"
4160
4255
  ]).meta(
4161
4256
  { id: "RevTurbineConfigUiPathActionType", "x-revturbine-schema-persistence": Transient16, "x-revturbine-schema-exposure": External11 }
4162
4257
  );
@@ -4265,6 +4360,8 @@ var RevTurbineConfigStudioPayloadSchema = z19.object({
4265
4360
  caps: RevTurbineConfigStudioPayloadCapsSchema.optional().meta(Unrestricted16),
4266
4361
  // Optional slot targeting (spec §3.1.1): empty/absent = any compatible slot.
4267
4362
  surface_slot_ids: z19.array(z19.string()).optional().meta(Unrestricted16),
4363
+ // Per-payload remind-me-later override (minutes); absent = inherit tenant default (plan 167 Q-3).
4364
+ remind_later_minutes: z19.number().int().min(0).nullable().optional().meta(Unrestricted16),
4268
4365
  created_at: z19.string().optional().meta({ ...Unrestricted16, readOnly: true }),
4269
4366
  recommendation_strategy: z19.enum(["next_tier_up", "best_value", "custom"]).optional().default("next_tier_up").meta(Unrestricted16),
4270
4367
  recommendation_plan_override: z19.string().optional().meta(Unrestricted16)
@@ -4304,6 +4401,8 @@ var RevTurbineConfigPlacementPayloadItemSchema = z19.object({
4304
4401
  placement_id: z19.string().min(1).meta(Unrestricted16),
4305
4402
  target: RevTurbineConfigStudioPayloadTargetSchema.meta(Unrestricted16),
4306
4403
  caps: RevTurbineConfigStudioPayloadCapsSchema.optional().meta(Unrestricted16),
4404
+ // Per-payload remind-me-later override (minutes); absent = inherit tenant default (plan 167 Q-3).
4405
+ remind_later_minutes: z19.number().int().min(0).nullable().optional().meta(Unrestricted16),
4307
4406
  created_at: z19.string().meta({ ...Unrestricted16, readOnly: true }),
4308
4407
  updated_at: z19.string().datetime().optional().meta({ ...Unrestricted16, readOnly: true }),
4309
4408
  source_mode: z19.enum(["inline", "content_linked"]).meta(Unrestricted16),
@@ -4534,6 +4633,12 @@ var PlaybookSchema = z19.preprocess(normalizeConfigHeaderInput, PlaybookObjectSc
4534
4633
  "x-revturbine-schema-exposure": External11,
4535
4634
  ...PLAYBOOK_SDK_FACETS8
4536
4635
  });
4636
+ var PlaybookStrictSchema = z19.preprocess(normalizeConfigHeaderInput, PlaybookObjectSchema.strict()).meta({
4637
+ id: "PlaybookStrict",
4638
+ "x-revturbine-schema-persistence": Transient16,
4639
+ "x-revturbine-schema-exposure": External11,
4640
+ ...PLAYBOOK_SDK_FACETS8
4641
+ });
4537
4642
  var RevTurbineConfigSchema = PlaybookSchema;
4538
4643
  var configPaths = {
4539
4644
  "/api/seat-type-anchors": {
@@ -4773,9 +4878,9 @@ var configPaths = {
4773
4878
  };
4774
4879
  var { Unrestricted: Unrestricted17 } = DataClassification;
4775
4880
  var { Persisted: Persisted17 } = SchemaPersistence;
4776
- var { Internal: Internal14 } = SchemaExposure;
4881
+ var { Internal: Internal15 } = SchemaExposure;
4777
4882
  var ChangeLogActionSchema = z20.enum(["create", "update", "delete", "archive", "restore", "reorder", "duplicate", "sync", "publish"]).meta(
4778
- { id: "ChangeLogAction", "x-revturbine-schema-persistence": Persisted17, "x-revturbine-schema-exposure": Internal14 }
4883
+ { id: "ChangeLogAction", "x-revturbine-schema-persistence": Persisted17, "x-revturbine-schema-exposure": Internal15 }
4779
4884
  );
4780
4885
  var ChangeLogEntrySchema = IdField.merge(TimestampFields).merge(TenantIdField).extend({
4781
4886
  action: ChangeLogActionSchema.meta(Unrestricted17),
@@ -4791,7 +4896,7 @@ var ChangeLogEntrySchema = IdField.merge(TimestampFields).merge(TenantIdField).e
4791
4896
  summary: z20.string().max(1e3).optional().meta(Unrestricted17),
4792
4897
  metadata: MetadataField.meta(Unrestricted17)
4793
4898
  }).meta(
4794
- { id: "ChangeLogEntry", "x-revturbine-schema-persistence": Persisted17, "x-revturbine-schema-exposure": Internal14 }
4899
+ { id: "ChangeLogEntry", "x-revturbine-schema-persistence": Persisted17, "x-revturbine-schema-exposure": Internal15 }
4795
4900
  );
4796
4901
  var changelogPaths = {
4797
4902
  "/api/changelog": {
@@ -4817,9 +4922,9 @@ var changelogPaths = {
4817
4922
  };
4818
4923
  var { Unrestricted: Unrestricted18 } = DataClassification;
4819
4924
  var { Persisted: Persisted18, Transient: Transient17 } = SchemaPersistence;
4820
- var { Internal: Internal15 } = SchemaExposure;
4925
+ var { Internal: Internal16 } = SchemaExposure;
4821
4926
  var TenantStatusSchema = z21.enum(["active", "suspended", "archived"]).meta(
4822
- { id: "TenantStatus", "x-revturbine-schema-persistence": Transient17, "x-revturbine-schema-exposure": Internal15 }
4927
+ { id: "TenantStatus", "x-revturbine-schema-persistence": Transient17, "x-revturbine-schema-exposure": Internal16 }
4823
4928
  );
4824
4929
  var TenantSchema = IdField.merge(TimestampFields).extend({
4825
4930
  name: NameField.meta(Unrestricted18),
@@ -4827,7 +4932,7 @@ var TenantSchema = IdField.merge(TimestampFields).extend({
4827
4932
  status: TenantStatusSchema.default("active").meta(Unrestricted18),
4828
4933
  metadata: MetadataField.meta(Unrestricted18)
4829
4934
  }).meta(
4830
- { id: "Tenant", "x-revturbine-schema-persistence": Persisted18, "x-revturbine-schema-exposure": Internal15 }
4935
+ { id: "Tenant", "x-revturbine-schema-persistence": Persisted18, "x-revturbine-schema-exposure": Internal16 }
4831
4936
  );
4832
4937
  var tenantPaths = {
4833
4938
  "/api/tenants": {
@@ -4893,9 +4998,9 @@ var tenantPaths = {
4893
4998
  };
4894
4999
  var { Unrestricted: Unrestricted19 } = DataClassification;
4895
5000
  var { Persisted: Persisted19, Transient: Transient18 } = SchemaPersistence;
4896
- var { Internal: Internal16 } = SchemaExposure;
5001
+ var { Internal: Internal17 } = SchemaExposure;
4897
5002
  var EnvironmentStatusSchema = z22.enum(["active", "archived", "locked"]).meta(
4898
- { id: "EnvironmentStatus", "x-revturbine-schema-persistence": Transient18, "x-revturbine-schema-exposure": Internal16 }
5003
+ { id: "EnvironmentStatus", "x-revturbine-schema-persistence": Transient18, "x-revturbine-schema-exposure": Internal17 }
4899
5004
  );
4900
5005
  var EnvironmentSchema = IdField.merge(TimestampFields).merge(TenantIdField).extend({
4901
5006
  name: NameField.meta(Unrestricted19),
@@ -4914,7 +5019,7 @@ var EnvironmentSchema = IdField.merge(TimestampFields).merge(TenantIdField).exte
4914
5019
  created_by: z22.string().optional().meta(Unrestricted19),
4915
5020
  metadata: MetadataField.meta(Unrestricted19)
4916
5021
  }).meta(
4917
- { id: "Environment", "x-revturbine-schema-persistence": Persisted19, "x-revturbine-schema-exposure": Internal16 }
5022
+ { id: "Environment", "x-revturbine-schema-persistence": Persisted19, "x-revturbine-schema-exposure": Internal17 }
4918
5023
  );
4919
5024
  var EnvironmentPromotionRequestSchema = z22.object({
4920
5025
  source_environment_id: z22.string().min(1),
@@ -4922,7 +5027,7 @@ var EnvironmentPromotionRequestSchema = z22.object({
4922
5027
  playbook_version_ids: z22.array(z22.string()).optional(),
4923
5028
  strategy: z22.enum(["all_current", "selected_playbook_versions"]).default("all_current")
4924
5029
  }).meta(
4925
- { id: "EnvironmentPromotionRequest", "x-revturbine-schema-persistence": Transient18, "x-revturbine-schema-exposure": Internal16 }
5030
+ { id: "EnvironmentPromotionRequest", "x-revturbine-schema-persistence": Transient18, "x-revturbine-schema-exposure": Internal17 }
4926
5031
  );
4927
5032
  var environmentPaths = {
4928
5033
  "/api/environments": {
@@ -5101,7 +5206,7 @@ var ServerEvaluationPayloadSchema = z23.object({
5101
5206
  }).meta({ id: "ServerEvaluationPayload", "x-revturbine-schema-persistence": Transient19, "x-revturbine-schema-exposure": External12 });
5102
5207
  var { Unrestricted: Unrestricted21 } = DataClassification;
5103
5208
  var { Persisted: Persisted21, Transient: Transient20 } = SchemaPersistence;
5104
- var { Internal: Internal17 } = SchemaExposure;
5209
+ var { Internal: Internal18 } = SchemaExposure;
5105
5210
  var PlaybookVersionStatusSchema = z24.enum([
5106
5211
  "draft",
5107
5212
  "awaiting_approval",
@@ -5111,7 +5216,7 @@ var PlaybookVersionStatusSchema = z24.enum([
5111
5216
  "rejected",
5112
5217
  "archived"
5113
5218
  ]).meta(
5114
- { id: "PlaybookVersionStatus", "x-revturbine-schema-persistence": Transient20, "x-revturbine-schema-exposure": Internal17 }
5219
+ { id: "PlaybookVersionStatus", "x-revturbine-schema-persistence": Transient20, "x-revturbine-schema-exposure": Internal18 }
5115
5220
  );
5116
5221
  var PlaybookVersionSchema = IdField.merge(TimestampFields).merge(TenantIdField).extend({
5117
5222
  environment_id: z24.string().min(1).meta(Unrestricted21),
@@ -5148,7 +5253,7 @@ var PlaybookVersionSchema = IdField.merge(TimestampFields).merge(TenantIdField).
5148
5253
  bundle: z24.string().nullable().default(null).meta({ ...Unrestricted21, readOnly: true }),
5149
5254
  metadata: MetadataField.meta(Unrestricted21)
5150
5255
  }).meta(
5151
- { id: "PlaybookVersion", "x-revturbine-schema-persistence": Persisted21, "x-revturbine-schema-exposure": Internal17 }
5256
+ { id: "PlaybookVersion", "x-revturbine-schema-persistence": Persisted21, "x-revturbine-schema-exposure": Internal18 }
5152
5257
  );
5153
5258
  var PlaybookVersionEntrySummarySchema = z24.object({
5154
5259
  handle: z24.string().meta(Unrestricted21),
@@ -5157,7 +5262,7 @@ var PlaybookVersionEntrySummarySchema = z24.object({
5157
5262
  action: z24.enum(["create", "update", "delete"]).meta(Unrestricted21),
5158
5263
  has_conflict: z24.boolean().meta(Unrestricted21)
5159
5264
  }).meta(
5160
- { id: "PlaybookVersionEntrySummary", "x-revturbine-schema-persistence": Transient20, "x-revturbine-schema-exposure": Internal17 }
5265
+ { id: "PlaybookVersionEntrySummary", "x-revturbine-schema-persistence": Transient20, "x-revturbine-schema-exposure": Internal18 }
5161
5266
  );
5162
5267
  var PlaybookVersionDiffSchema = z24.object({
5163
5268
  playbook_version_id: z24.string().meta(Unrestricted21),
@@ -5166,7 +5271,7 @@ var PlaybookVersionDiffSchema = z24.object({
5166
5271
  total_conflicts: z24.number().int().min(0).meta(Unrestricted21),
5167
5272
  deployable: z24.boolean().meta(Unrestricted21)
5168
5273
  }).meta(
5169
- { id: "PlaybookVersionDiff", "x-revturbine-schema-persistence": Transient20, "x-revturbine-schema-exposure": Internal17 }
5274
+ { id: "PlaybookVersionDiff", "x-revturbine-schema-persistence": Transient20, "x-revturbine-schema-exposure": Internal18 }
5170
5275
  );
5171
5276
  var PlaybookVersionDeployResultSchema = z24.object({
5172
5277
  playbook_version_id: z24.string().meta(Unrestricted21),
@@ -5175,7 +5280,7 @@ var PlaybookVersionDeployResultSchema = z24.object({
5175
5280
  skipped_conflicts: z24.number().int().min(0).meta(Unrestricted21),
5176
5281
  deployed_at: z24.string().datetime().meta(Unrestricted21)
5177
5282
  }).meta(
5178
- { id: "PlaybookVersionDeployResult", "x-revturbine-schema-persistence": Transient20, "x-revturbine-schema-exposure": Internal17 }
5283
+ { id: "PlaybookVersionDeployResult", "x-revturbine-schema-persistence": Transient20, "x-revturbine-schema-exposure": Internal18 }
5179
5284
  );
5180
5285
  var playbookVersionPaths = {
5181
5286
  // ── Lifecycle transitions ────────────────────────────────────────────────
@@ -5302,8 +5407,8 @@ var playbookVersionPaths = {
5302
5407
  };
5303
5408
  var { Unrestricted: Unrestricted22, Pii: Pii7 } = DataClassification;
5304
5409
  var { Persisted: Persisted22 } = SchemaPersistence;
5305
- var { Internal: Internal18 } = SchemaExposure;
5306
- var ApiKeyStatusSchema = z25.enum(["active", "revoked", "rotating"]).meta({ id: "ApiKeyStatus", "x-revturbine-schema-persistence": Persisted22, "x-revturbine-schema-exposure": Internal18 });
5410
+ var { Internal: Internal19 } = SchemaExposure;
5411
+ var ApiKeyStatusSchema = z25.enum(["active", "revoked", "rotating"]).meta({ id: "ApiKeyStatus", "x-revturbine-schema-persistence": Persisted22, "x-revturbine-schema-exposure": Internal19 });
5307
5412
  var ApiKeySchema = IdField.merge(TimestampFields).merge(TenantIdField).extend({
5308
5413
  name: NameField.meta(Unrestricted22),
5309
5414
  key_hash: z25.string().min(1).meta({ ...Pii7, readOnly: true }),
@@ -5312,22 +5417,22 @@ var ApiKeySchema = IdField.merge(TimestampFields).merge(TenantIdField).extend({
5312
5417
  status: ApiKeyStatusSchema.default("active").meta(Unrestricted22),
5313
5418
  last_used_at: NullableDatetimeField.meta({ ...Unrestricted22, readOnly: true }),
5314
5419
  expires_at: NullableDatetimeField.meta(Unrestricted22)
5315
- }).meta({ id: "ApiKey", "x-revturbine-schema-persistence": Persisted22, "x-revturbine-schema-exposure": Internal18 });
5316
- var FlagValueTypeSchema = z25.enum(["boolean", "string", "number", "json"]).meta({ id: "FlagValueType", "x-revturbine-schema-persistence": Persisted22, "x-revturbine-schema-exposure": Internal18 });
5420
+ }).meta({ id: "ApiKey", "x-revturbine-schema-persistence": Persisted22, "x-revturbine-schema-exposure": Internal19 });
5421
+ var FlagValueTypeSchema = z25.enum(["boolean", "string", "number", "json"]).meta({ id: "FlagValueType", "x-revturbine-schema-persistence": Persisted22, "x-revturbine-schema-exposure": Internal19 });
5317
5422
  var FeatureFlagSchema = IdField.merge(TimestampFields).merge(TenantIdField).extend({
5318
5423
  key: z25.string().min(1).max(100).meta(Unrestricted22),
5319
5424
  value_type: FlagValueTypeSchema.default("boolean").meta(Unrestricted22),
5320
5425
  value: z25.string().max(4e3).default("false").meta(Unrestricted22),
5321
5426
  description: DescriptionField.meta(Unrestricted22),
5322
5427
  enabled: z25.boolean().default(true).meta(Unrestricted22)
5323
- }).meta({ id: "FeatureFlag", "x-revturbine-schema-persistence": Persisted22, "x-revturbine-schema-exposure": Internal18 });
5428
+ }).meta({ id: "FeatureFlag", "x-revturbine-schema-persistence": Persisted22, "x-revturbine-schema-exposure": Internal19 });
5324
5429
  var TenantConfigSchema = IdField.merge(TimestampFields).merge(TenantIdField).extend({
5325
5430
  workspace_name: z25.string().min(1).max(200).meta(Unrestricted22),
5326
5431
  support_email: z25.string().email().nullable().default(null).meta(Unrestricted22),
5327
5432
  timezone: z25.string().max(50).default("UTC").meta(Unrestricted22),
5328
5433
  default_currency: z25.string().length(3).default("USD").meta(Unrestricted22),
5329
5434
  logo_url: z25.string().url().nullable().default(null).meta(Unrestricted22)
5330
- }).meta({ id: "TenantConfig", "x-revturbine-schema-persistence": Persisted22, "x-revturbine-schema-exposure": Internal18 });
5435
+ }).meta({ id: "TenantConfig", "x-revturbine-schema-persistence": Persisted22, "x-revturbine-schema-exposure": Internal19 });
5331
5436
  var McpConfigSchema = IdField.merge(TimestampFields).merge(TenantIdField).extend({
5332
5437
  /** @deprecated Plan 28 ships a hosted MCP server at `/api/mcp/streamable-http`; no outbound server URL is needed. Column remains for backward compatibility. */
5333
5438
  server_url: z25.string().url().max(500).meta(Unrestricted22),
@@ -5336,14 +5441,14 @@ var McpConfigSchema = IdField.merge(TimestampFields).merge(TenantIdField).extend
5336
5441
  allow_write_actions: z25.boolean().default(false).meta(Unrestricted22),
5337
5442
  enabled_tools: z25.array(z25.string().max(100)).default([]).meta(Unrestricted22),
5338
5443
  enabled: z25.boolean().default(false).meta(Unrestricted22)
5339
- }).meta({ id: "McpConfig", "x-revturbine-schema-persistence": Persisted22, "x-revturbine-schema-exposure": Internal18 });
5444
+ }).meta({ id: "McpConfig", "x-revturbine-schema-persistence": Persisted22, "x-revturbine-schema-exposure": Internal19 });
5340
5445
  var OnboardingChecklistSchema = IdField.merge(TimestampFields).merge(TenantIdField).extend({
5341
5446
  step_key: z25.string().min(1).max(100).meta(Unrestricted22),
5342
5447
  label: z25.string().min(1).max(200).meta(Unrestricted22),
5343
5448
  done: z25.boolean().default(false).meta(Unrestricted22),
5344
5449
  completed_at: NullableDatetimeField.meta({ ...Unrestricted22, readOnly: true })
5345
- }).meta({ id: "OnboardingChecklist", "x-revturbine-schema-persistence": Persisted22, "x-revturbine-schema-exposure": Internal18 });
5346
- var AuditActorTypeSchema = z25.enum(["user", "agent", "system", "webhook"]).meta({ id: "AuditActorType", "x-revturbine-schema-persistence": Persisted22, "x-revturbine-schema-exposure": Internal18 });
5450
+ }).meta({ id: "OnboardingChecklist", "x-revturbine-schema-persistence": Persisted22, "x-revturbine-schema-exposure": Internal19 });
5451
+ var AuditActorTypeSchema = z25.enum(["user", "agent", "system", "webhook"]).meta({ id: "AuditActorType", "x-revturbine-schema-persistence": Persisted22, "x-revturbine-schema-exposure": Internal19 });
5347
5452
  var AuditEventSchema = IdField.merge(TenantIdField).extend({
5348
5453
  environment_id: z25.string().min(1).default("production").meta(Unrestricted22),
5349
5454
  actor_type: AuditActorTypeSchema.meta(Unrestricted22),
@@ -5353,15 +5458,15 @@ var AuditEventSchema = IdField.merge(TenantIdField).extend({
5353
5458
  object_id: z25.string().max(200).nullable().default(null).meta(Unrestricted22),
5354
5459
  payload: z25.record(z25.string(), z25.unknown()).nullable().default(null).meta(Unrestricted22),
5355
5460
  occurred_at: z25.string().datetime().meta({ ...Unrestricted22, readOnly: true })
5356
- }).meta({ id: "AuditEvent", "x-revturbine-schema-persistence": Persisted22, "x-revturbine-schema-exposure": Internal18 });
5357
- var PlacementTestUserIdentifierTypeSchema = z25.enum(["user_id", "account_id", "email"]).meta({ id: "PlacementTestUserIdentifierType", "x-revturbine-schema-persistence": Persisted22, "x-revturbine-schema-exposure": Internal18 });
5461
+ }).meta({ id: "AuditEvent", "x-revturbine-schema-persistence": Persisted22, "x-revturbine-schema-exposure": Internal19 });
5462
+ var PlacementTestUserIdentifierTypeSchema = z25.enum(["user_id", "account_id", "email"]).meta({ id: "PlacementTestUserIdentifierType", "x-revturbine-schema-persistence": Persisted22, "x-revturbine-schema-exposure": Internal19 });
5358
5463
  var PlacementTestUserSchema = IdField.merge(TimestampFields).merge(TenantIdField).merge(AnchorFields).merge(VersionFields).extend({
5359
5464
  handle: HandleField.meta({ ...Unrestricted22, readOnly: true }),
5360
5465
  identifier: z25.string().min(1).max(200).meta(Unrestricted22),
5361
5466
  identifier_type: PlacementTestUserIdentifierTypeSchema.default("user_id").meta(Unrestricted22),
5362
5467
  note: z25.string().max(500).nullable().default(null).meta(Unrestricted22),
5363
5468
  added_by: z25.string().meta(Unrestricted22)
5364
- }).meta({ id: "PlacementTestUser", "x-revturbine-schema-persistence": Persisted22, "x-revturbine-schema-exposure": Internal18, ...mintedIdentity() });
5469
+ }).meta({ id: "PlacementTestUser", "x-revturbine-schema-persistence": Persisted22, "x-revturbine-schema-exposure": Internal19, ...mintedIdentity() });
5365
5470
  var settingsPaths = {
5366
5471
  // ── API Keys ─────────────────────────────────────────────────────────────
5367
5472
  "/api/settings/api-keys": {
@@ -5547,8 +5652,8 @@ var settingsPaths = {
5547
5652
  };
5548
5653
  var { Unrestricted: Unrestricted23, Pii: Pii8 } = DataClassification;
5549
5654
  var { Persisted: Persisted23, Transient: Transient21 } = SchemaPersistence;
5550
- var { Internal: Internal19 } = SchemaExposure;
5551
- var UserRoleSchema = z26.enum(["user", "admin"]).meta({ id: "UserRole", "x-revturbine-schema-persistence": Persisted23, "x-revturbine-schema-exposure": Internal19 });
5655
+ var { Internal: Internal20 } = SchemaExposure;
5656
+ var UserRoleSchema = z26.enum(["user", "admin"]).meta({ id: "UserRole", "x-revturbine-schema-persistence": Persisted23, "x-revturbine-schema-exposure": Internal20 });
5552
5657
  var AuthUserSchema = IdField.merge(TimestampFields).extend({
5553
5658
  name: NameField.meta(Unrestricted23),
5554
5659
  email: z26.string().email().meta(Pii8),
@@ -5559,7 +5664,7 @@ var AuthUserSchema = IdField.merge(TimestampFields).extend({
5559
5664
  ban_reason: z26.string().nullable().default(null).meta({ ...Unrestricted23, readOnly: true }),
5560
5665
  ban_expires: NullableDatetimeField.meta({ ...Unrestricted23, readOnly: true }),
5561
5666
  two_factor_enabled: z26.boolean().default(false).meta({ ...Unrestricted23, readOnly: true })
5562
- }).meta({ id: "AuthUser", "x-revturbine-schema-persistence": Persisted23, "x-revturbine-schema-exposure": Internal19 });
5667
+ }).meta({ id: "AuthUser", "x-revturbine-schema-persistence": Persisted23, "x-revturbine-schema-exposure": Internal20 });
5563
5668
  var AuthSessionSchema = IdField.merge(TimestampFields).extend({
5564
5669
  expires_at: z26.string().datetime().meta(Unrestricted23),
5565
5670
  token: z26.string().min(1).meta({ ...Pii8, readOnly: true }),
@@ -5568,7 +5673,7 @@ var AuthSessionSchema = IdField.merge(TimestampFields).extend({
5568
5673
  user_id: z26.string().min(1).meta({ ...Unrestricted23, readOnly: true }),
5569
5674
  active_organization_id: z26.string().nullable().default(null).meta(Unrestricted23),
5570
5675
  impersonated_by: z26.string().nullable().default(null).meta({ ...Unrestricted23, readOnly: true })
5571
- }).meta({ id: "AuthSession", "x-revturbine-schema-persistence": Persisted23, "x-revturbine-schema-exposure": Internal19 });
5676
+ }).meta({ id: "AuthSession", "x-revturbine-schema-persistence": Persisted23, "x-revturbine-schema-exposure": Internal20 });
5572
5677
  var AuthAccountSchema = IdField.merge(TimestampFields).extend({
5573
5678
  account_id: z26.string().min(1).meta(Unrestricted23),
5574
5679
  provider_id: z26.string().min(1).meta(Unrestricted23),
@@ -5580,26 +5685,26 @@ var AuthAccountSchema = IdField.merge(TimestampFields).extend({
5580
5685
  refresh_token_expires_at: NullableDatetimeField.meta({ ...Unrestricted23, readOnly: true }),
5581
5686
  scope: z26.string().nullable().default(null).meta(Unrestricted23),
5582
5687
  password: z26.string().nullable().default(null).meta({ ...Pii8, readOnly: true })
5583
- }).meta({ id: "AuthAccount", "x-revturbine-schema-persistence": Persisted23, "x-revturbine-schema-exposure": Internal19 });
5688
+ }).meta({ id: "AuthAccount", "x-revturbine-schema-persistence": Persisted23, "x-revturbine-schema-exposure": Internal20 });
5584
5689
  var AuthVerificationSchema = IdField.merge(TimestampFields).extend({
5585
5690
  identifier: z26.string().min(1).meta(Pii8),
5586
5691
  value: z26.string().min(1).meta({ ...Pii8, readOnly: true }),
5587
5692
  expires_at: z26.string().datetime().meta(Unrestricted23)
5588
- }).meta({ id: "AuthVerification", "x-revturbine-schema-persistence": Persisted23, "x-revturbine-schema-exposure": Internal19 });
5693
+ }).meta({ id: "AuthVerification", "x-revturbine-schema-persistence": Persisted23, "x-revturbine-schema-exposure": Internal20 });
5589
5694
  var AuthTwoFactorSchema = IdField.extend({
5590
5695
  secret: z26.string().min(1).meta({ ...Pii8, readOnly: true }),
5591
5696
  backup_codes: z26.string().min(1).meta({ ...Pii8, readOnly: true }),
5592
5697
  user_id: z26.string().min(1).meta({ ...Unrestricted23, readOnly: true }),
5593
5698
  verified: z26.boolean().default(false).meta({ ...Unrestricted23, readOnly: true })
5594
- }).meta({ id: "AuthTwoFactor", "x-revturbine-schema-persistence": Persisted23, "x-revturbine-schema-exposure": Internal19 });
5699
+ }).meta({ id: "AuthTwoFactor", "x-revturbine-schema-persistence": Persisted23, "x-revturbine-schema-exposure": Internal20 });
5595
5700
  var AuthOrganizationSchema = IdField.extend({
5596
5701
  name: NameField.meta(Unrestricted23),
5597
5702
  slug: z26.string().min(1).max(100).nullable().default(null).meta(Unrestricted23),
5598
5703
  logo: z26.string().url().nullable().default(null).meta(Unrestricted23),
5599
5704
  created_at: z26.string().datetime().meta({ ...Unrestricted23, readOnly: true }),
5600
5705
  metadata: z26.string().nullable().default(null).meta(Unrestricted23)
5601
- }).meta({ id: "AuthOrganization", "x-revturbine-schema-persistence": Persisted23, "x-revturbine-schema-exposure": Internal19 });
5602
- var RoleSchema = z26.enum(["viewer", "collaborator", "approver", "admin"]).meta({ id: "Role", "x-revturbine-schema-persistence": Persisted23, "x-revturbine-schema-exposure": Internal19 });
5706
+ }).meta({ id: "AuthOrganization", "x-revturbine-schema-persistence": Persisted23, "x-revturbine-schema-exposure": Internal20 });
5707
+ var RoleSchema = z26.enum(["viewer", "collaborator", "approver", "admin"]).meta({ id: "Role", "x-revturbine-schema-persistence": Persisted23, "x-revturbine-schema-exposure": Internal20 });
5603
5708
  var PermissionResourceSchema = z26.enum([
5604
5709
  "tenant",
5605
5710
  "users",
@@ -5614,7 +5719,7 @@ var PermissionResourceSchema = z26.enum([
5614
5719
  "mcp",
5615
5720
  "api_tokens",
5616
5721
  "settings"
5617
- ]).meta({ id: "PermissionResource", "x-revturbine-schema-persistence": Transient21, "x-revturbine-schema-exposure": Internal19 });
5722
+ ]).meta({ id: "PermissionResource", "x-revturbine-schema-persistence": Transient21, "x-revturbine-schema-exposure": Internal20 });
5618
5723
  var PermissionActionSchema = z26.enum([
5619
5724
  "read",
5620
5725
  "create",
@@ -5624,11 +5729,11 @@ var PermissionActionSchema = z26.enum([
5624
5729
  "approve",
5625
5730
  "invite",
5626
5731
  "manage_roles"
5627
- ]).meta({ id: "PermissionAction", "x-revturbine-schema-persistence": Transient21, "x-revturbine-schema-exposure": Internal19 });
5732
+ ]).meta({ id: "PermissionAction", "x-revturbine-schema-persistence": Transient21, "x-revturbine-schema-exposure": Internal20 });
5628
5733
  var PermissionSchema = z26.object({
5629
5734
  resource: PermissionResourceSchema,
5630
5735
  action: PermissionActionSchema
5631
- }).meta({ id: "Permission", "x-revturbine-schema-persistence": Transient21, "x-revturbine-schema-exposure": Internal19 });
5736
+ }).meta({ id: "Permission", "x-revturbine-schema-persistence": Transient21, "x-revturbine-schema-exposure": Internal20 });
5632
5737
  var VIEWER_RESOURCES = [
5633
5738
  "tenant",
5634
5739
  "plans",
@@ -5697,14 +5802,14 @@ var ADMIN_PERMS = expand(ADMIN_FULL, ADMIN_ACTIONS);
5697
5802
  var SCOPE_VALUES = PermissionResourceSchema.options.flatMap(
5698
5803
  (resource) => PermissionActionSchema.options.map((action) => `${resource}:${action}`)
5699
5804
  );
5700
- var McpTokenScopeSchema = z26.enum(SCOPE_VALUES).meta({ id: "McpTokenScope", "x-revturbine-schema-persistence": Persisted23, "x-revturbine-schema-exposure": Internal19 });
5805
+ var McpTokenScopeSchema = z26.enum(SCOPE_VALUES).meta({ id: "McpTokenScope", "x-revturbine-schema-persistence": Persisted23, "x-revturbine-schema-exposure": Internal20 });
5701
5806
  var AuthMemberSchema = IdField.extend({
5702
5807
  organization_id: z26.string().min(1).meta({ ...Unrestricted23, readOnly: true }),
5703
5808
  user_id: z26.string().min(1).meta({ ...Unrestricted23, readOnly: true }),
5704
5809
  role: RoleSchema.default("viewer").meta(Unrestricted23),
5705
5810
  created_at: z26.string().datetime().meta({ ...Unrestricted23, readOnly: true })
5706
- }).meta({ id: "AuthMember", "x-revturbine-schema-persistence": Persisted23, "x-revturbine-schema-exposure": Internal19 });
5707
- var InvitationStatusSchema = z26.enum(["pending", "accepted", "rejected", "canceled", "expired"]).meta({ id: "InvitationStatus", "x-revturbine-schema-persistence": Persisted23, "x-revturbine-schema-exposure": Internal19 });
5811
+ }).meta({ id: "AuthMember", "x-revturbine-schema-persistence": Persisted23, "x-revturbine-schema-exposure": Internal20 });
5812
+ var InvitationStatusSchema = z26.enum(["pending", "accepted", "rejected", "canceled", "expired"]).meta({ id: "InvitationStatus", "x-revturbine-schema-persistence": Persisted23, "x-revturbine-schema-exposure": Internal20 });
5708
5813
  var AuthInvitationSchema = IdField.extend({
5709
5814
  organization_id: z26.string().min(1).meta({ ...Unrestricted23, readOnly: true }),
5710
5815
  email: z26.string().email().meta(Pii8),
@@ -5713,7 +5818,7 @@ var AuthInvitationSchema = IdField.extend({
5713
5818
  expires_at: z26.string().datetime().meta(Unrestricted23),
5714
5819
  created_at: z26.string().datetime().meta({ ...Unrestricted23, readOnly: true }),
5715
5820
  inviter_id: z26.string().min(1).meta({ ...Unrestricted23, readOnly: true })
5716
- }).meta({ id: "AuthInvitation", "x-revturbine-schema-persistence": Persisted23, "x-revturbine-schema-exposure": Internal19 });
5821
+ }).meta({ id: "AuthInvitation", "x-revturbine-schema-persistence": Persisted23, "x-revturbine-schema-exposure": Internal20 });
5717
5822
  var AuthPasskeySchema = IdField.extend({
5718
5823
  name: z26.string().max(200).nullable().default(null).meta(Unrestricted23),
5719
5824
  public_key: z26.string().min(1).meta({ ...Pii8, readOnly: true }),
@@ -5725,7 +5830,7 @@ var AuthPasskeySchema = IdField.extend({
5725
5830
  transports: z26.string().nullable().default(null).meta(Unrestricted23),
5726
5831
  created_at: z26.string().datetime().meta({ ...Unrestricted23, readOnly: true }),
5727
5832
  aaguid: z26.string().nullable().default(null).meta(Unrestricted23)
5728
- }).meta({ id: "AuthPasskey", "x-revturbine-schema-persistence": Persisted23, "x-revturbine-schema-exposure": Internal19 });
5833
+ }).meta({ id: "AuthPasskey", "x-revturbine-schema-persistence": Persisted23, "x-revturbine-schema-exposure": Internal20 });
5729
5834
  var AuthApiKeySchema = IdField.merge(TimestampFields).extend({
5730
5835
  config_id: z26.string().min(1).meta(Unrestricted23),
5731
5836
  name: z26.string().max(200).nullable().default(null).meta(Unrestricted23),
@@ -5746,7 +5851,7 @@ var AuthApiKeySchema = IdField.merge(TimestampFields).extend({
5746
5851
  expires_at: NullableDatetimeField.meta(Unrestricted23),
5747
5852
  permissions: z26.string().nullable().default(null).meta(Unrestricted23),
5748
5853
  metadata: z26.string().nullable().default(null).meta(Unrestricted23)
5749
- }).meta({ id: "AuthApiKey", "x-revturbine-schema-persistence": Persisted23, "x-revturbine-schema-exposure": Internal19 });
5854
+ }).meta({ id: "AuthApiKey", "x-revturbine-schema-persistence": Persisted23, "x-revturbine-schema-exposure": Internal20 });
5750
5855
  var AuthSsoProviderSchema = IdField.extend({
5751
5856
  issuer: z26.string().min(1).meta(Unrestricted23),
5752
5857
  oidc_config: z26.string().nullable().default(null).meta(Unrestricted23),
@@ -5755,7 +5860,7 @@ var AuthSsoProviderSchema = IdField.extend({
5755
5860
  provider_id: z26.string().min(1).meta(Unrestricted23),
5756
5861
  organization_id: z26.string().nullable().default(null).meta(Unrestricted23),
5757
5862
  domain: z26.string().min(1).meta(Unrestricted23)
5758
- }).meta({ id: "AuthSsoProvider", "x-revturbine-schema-persistence": Persisted23, "x-revturbine-schema-exposure": Internal19 });
5863
+ }).meta({ id: "AuthSsoProvider", "x-revturbine-schema-persistence": Persisted23, "x-revturbine-schema-exposure": Internal20 });
5759
5864
 
5760
5865
  // src/schema/validators.snapshot.mjs
5761
5866
  import { z as z27 } from "zod";
@@ -5827,11 +5932,12 @@ var CATALOG = {
5827
5932
  message: "Plan '{plan}' has no Stripe price linked.",
5828
5933
  specRef: "optimization-studio-ui.md \xA73.1; plans-entitlements-studio-ui.md \xA72.2"
5829
5934
  },
5830
- // entitlement-rule overlap. Origin: the `rule.overlap` check (plan 40).
5935
+ // entitlement-rule overlap. Origin: the `rule.overlap` check (plan 40);
5936
+ // scoped to same-entitlement pairs per §5.8 (plan 179 / devkit #597).
5831
5937
  "VAL-PLN-05": {
5832
5938
  id: "VAL-PLN-05",
5833
5939
  severity: "warning",
5834
- message: "These entitlement rules target the same plan and segment \u2014 only one will apply.",
5940
+ message: "These rules for the same entitlement target the same plan and segment \u2014 only one will apply.",
5835
5941
  specRef: "config-validation.md \xA75.3 (provisional \u2014 plan 73 Q-1)"
5836
5942
  },
5837
5943
  // multiple public variations. Origin: `*_variation.multiple_public`.
@@ -5840,6 +5946,27 @@ var CATALOG = {
5840
5946
  severity: "error_draft",
5841
5947
  message: "Only one public variation is allowed per billing period and segment.",
5842
5948
  specRef: "config-validation.md \xA75.3 (provisional \u2014 plan 73 Q-1)"
5949
+ },
5950
+ // more-than-two CTAs on a payload surface. Promoted from §5.8 (plan 174
5951
+ // TASK-7 / devkit #584): the decision output carries ONE cta_path (from
5952
+ // ctas[0]); ctas[1] contributes secondary_cta_label only; ctas[2..] are
5953
+ // dropped at resolution with no finding.
5954
+ "VAL-PLC-05": {
5955
+ id: "VAL-PLC-05",
5956
+ severity: "warning",
5957
+ message: "Payload '{payload}' authors {count} CTAs, but the decision output carries one primary path and one secondary label \u2014 the third and later CTAs are dropped at resolution.",
5958
+ specRef: "config-validation.md \xA75.8 (promoted \u2014 plan 174 TASK-7)"
5959
+ },
5960
+ // widest-possible-audience trial rule. Promoted from §5.8 (plan 174 TASK-7 /
5961
+ // devkit #584): plan and segment on a free trial rule NARROW, so null on
5962
+ // both means every user on every plan — legal and occasionally intended,
5963
+ // but indistinguishable from an unfinished rule after authoring. Reverse
5964
+ // trial rules can't hit this (premium_plan_id is required).
5965
+ "VAL-TRL-04": {
5966
+ id: "VAL-TRL-04",
5967
+ severity: "warning",
5968
+ message: "Trial rule '{rule}' targets every user on every plan (no plan, no segment). Confirm the widest possible audience is intended.",
5969
+ specRef: "config-validation.md \xA75.8 (promoted \u2014 plan 174 TASK-7)"
5843
5970
  }
5844
5971
  };
5845
5972
  function getCatalogEntry(id) {
@@ -5849,9 +5976,63 @@ function runSemanticRules(graph) {
5849
5976
  return [
5850
5977
  ...checkPlansHaveStripePrice(graph),
5851
5978
  ...checkRuleOverlaps(graph),
5852
- ...checkPublicVariationCollisions(graph)
5979
+ ...checkPublicVariationCollisions(graph),
5980
+ ...checkPayloadCtaOverflow(graph),
5981
+ ...checkTrialRuleWidestAudience(graph)
5853
5982
  ];
5854
5983
  }
5984
+ function surfaceCtas(surface) {
5985
+ if (!surface || typeof surface !== "object") return 0;
5986
+ const ctas = surface.ctas;
5987
+ return Array.isArray(ctas) ? ctas.length : 0;
5988
+ }
5989
+ function checkPayloadCtaOverflow(graph) {
5990
+ const findings = [];
5991
+ const flag = (payloadId, count, objectType) => {
5992
+ findings.push(
5993
+ finding("VAL-PLC-05", { object_type: objectType, object_id: payloadId, field: "ctas", studio: "placements" }, {
5994
+ message: `Payload '${payloadId}' authors ${count} CTAs, but the decision output carries one primary path and one secondary label \u2014 the third and later CTAs are dropped at resolution.`
5995
+ })
5996
+ );
5997
+ };
5998
+ for (const placement of graph.placements ?? []) {
5999
+ const payloads = Array.isArray(placement.payloads) ? placement.payloads : [];
6000
+ for (const payload of payloads) {
6001
+ const payloadId = String(payload.id ?? placement.id ?? "");
6002
+ const surfaces = Array.isArray(payload.surfaces) ? payload.surfaces : [];
6003
+ for (const surface of surfaces) {
6004
+ const count = surfaceCtas(surface);
6005
+ if (count > 2) flag(payloadId, count, "placement_payload");
6006
+ }
6007
+ }
6008
+ }
6009
+ for (const payload of graph.placement_payloads ?? []) {
6010
+ const payloadId = String(payload.payload_id ?? payload.id ?? "");
6011
+ const surfaces = Array.isArray(payload.surfaces) ? payload.surfaces : [];
6012
+ for (const surface of surfaces) {
6013
+ const count = surfaceCtas(surface);
6014
+ if (count > 2) flag(payloadId, count, "placement_payload");
6015
+ }
6016
+ }
6017
+ return findings;
6018
+ }
6019
+ function checkTrialRuleWidestAudience(graph) {
6020
+ const findings = [];
6021
+ for (const rule of graph.free_trial_rules ?? []) {
6022
+ const planId = rule.plan_id;
6023
+ const segmentId = rule.segment_id;
6024
+ const planUnset = planId === null || planId === void 0 || planId === "";
6025
+ const segmentUnset = segmentId === null || segmentId === void 0 || segmentId === "";
6026
+ if (!planUnset || !segmentUnset) continue;
6027
+ const ruleId = String(rule.handle ?? rule.id ?? "");
6028
+ findings.push(
6029
+ finding("VAL-TRL-04", { object_type: "free_trial_rule", object_id: ruleId, studio: "trials" }, {
6030
+ message: `Trial rule '${ruleId || "unnamed"}' targets every user on every plan (no plan, no segment). Confirm the widest possible audience is intended.`
6031
+ })
6032
+ );
6033
+ }
6034
+ return findings;
6035
+ }
5855
6036
  function finding(catalogId, target, opts = {}) {
5856
6037
  const entry = getCatalogEntry(catalogId);
5857
6038
  const severity = entry?.severity ?? "warning";
@@ -5915,6 +6096,7 @@ function checkRuleOverlaps(graph) {
5915
6096
  const sigs = [];
5916
6097
  for (const rule of rules) {
5917
6098
  const id = String(rule.handle ?? rule.id ?? "");
6099
+ const entitlementId = typeof rule.entitlement_id === "string" ? rule.entitlement_id : "";
5918
6100
  const targetIds = /* @__PURE__ */ new Set();
5919
6101
  if (Array.isArray(rule.targets)) {
5920
6102
  for (const t of rule.targets) {
@@ -5942,6 +6124,7 @@ function checkRuleOverlaps(graph) {
5942
6124
  sigs.push({
5943
6125
  rule,
5944
6126
  id,
6127
+ entitlementId,
5945
6128
  targetIds,
5946
6129
  segmentPairs,
5947
6130
  matchesAllSegments: segmentIds.length === 0
@@ -5952,6 +6135,7 @@ function checkRuleOverlaps(graph) {
5952
6135
  for (let j = i + 1; j < sigs.length; j++) {
5953
6136
  const a = sigs[i];
5954
6137
  const b = sigs[j];
6138
+ if (!a.entitlementId || a.entitlementId !== b.entitlementId) continue;
5955
6139
  let sharedTarget = false;
5956
6140
  for (const t of a.targetIds) {
5957
6141
  if (b.targetIds.has(t)) {
@@ -5990,7 +6174,7 @@ function checkRuleOverlaps(graph) {
5990
6174
  studio: "plans-entitlements"
5991
6175
  },
5992
6176
  {
5993
- message: `Entitlement rule '${name}' overlaps another rule on a shared target and segment \u2014 only one will apply.`,
6177
+ message: `Entitlement rule '${name}' overlaps another rule for the same entitlement on a shared target and segment \u2014 only one will apply.`,
5994
6178
  detail: "Where rules overlap, the most permissive value applies (plans-entitlements-studio-ui.md \xA72.3.2). Adjust the segment selection if this is unintended."
5995
6179
  }
5996
6180
  )
@@ -6055,8 +6239,12 @@ function messageForZodIssue(issue) {
6055
6239
  switch (issue.code) {
6056
6240
  case "invalid_type":
6057
6241
  return issue.input === void 0 ? `${field} is required.` : `${field} must be a ${issue.expected ?? "valid value"}.`;
6058
- case "invalid_value":
6059
- return `${field} must be one of the allowed values.`;
6242
+ case "invalid_value": {
6243
+ const values = (issue.values ?? []).filter(
6244
+ (v) => ["string", "number", "boolean"].includes(typeof v)
6245
+ );
6246
+ return values.length > 0 ? `${field} must be one of: ${values.map((v) => `'${String(v)}'`).join(", ")}.` : `${field} must be one of the allowed values.`;
6247
+ }
6060
6248
  case "invalid_format":
6061
6249
  return `${field} isn't in a valid format.`;
6062
6250
  case "too_small":
@@ -6116,7 +6304,12 @@ var DataClassification2 = {
6116
6304
  Financial: { "x-revturbine-data-classification": "financial" },
6117
6305
  Unrestricted: { "x-revturbine-data-classification": "unrestricted" }
6118
6306
  };
6307
+ var SCHEMA_EXPOSURE_META_KEY2 = "x-revturbine-schema-exposure";
6119
6308
  var READ_ONLY_META_KEY2 = "readOnly";
6309
+ var DECISION_ONLY_META_KEY2 = "x-revturbine-decision-only";
6310
+ var DecisionOnly2 = { [DECISION_ONLY_META_KEY2]: true };
6311
+ var ClientSafe2 = { [SCHEMA_EXPOSURE_META_KEY2]: SchemaExposure2.External };
6312
+ var ServerOnly2 = { [SCHEMA_EXPOSURE_META_KEY2]: SchemaExposure2.Internal };
6120
6313
  function toWritableSchema2(schema) {
6121
6314
  const writableShape = {};
6122
6315
  for (const [fieldName, fieldSchema] of Object.entries(schema.shape)) {
@@ -6140,7 +6333,7 @@ function toCreateSchema2(schema) {
6140
6333
  }
6141
6334
  var { Unrestricted: Unrestricted24 } = DataClassification2;
6142
6335
  var { Transient: Transient22, Persisted: Persisted24 } = SchemaPersistence2;
6143
- var { Internal: Internal20, External: External13 } = SchemaExposure2;
6336
+ var { Internal: Internal21, External: External13 } = SchemaExposure2;
6144
6337
  var PaginationParamsSchema2 = z32.object({
6145
6338
  page: z32.coerce.number().int().min(1).default(1).meta(Unrestricted24),
6146
6339
  per_page: z32.coerce.number().int().min(1).max(100).default(25).meta(Unrestricted24)
@@ -6176,7 +6369,7 @@ var TenantIdField2 = z32.object({
6176
6369
  {
6177
6370
  id: "null",
6178
6371
  "x-revturbine-schema-persistence": Transient22,
6179
- "x-revturbine-schema-exposure": Internal20
6372
+ "x-revturbine-schema-exposure": Internal21
6180
6373
  }
6181
6374
  );
6182
6375
  var BillingCadenceSchema2 = z32.enum([
@@ -6242,7 +6435,7 @@ var FeatureFlagValueSchema2 = z32.union([
6242
6435
  {
6243
6436
  id: "FeatureFlagValue",
6244
6437
  "x-revturbine-schema-persistence": Transient22,
6245
- "x-revturbine-schema-exposure": Internal20
6438
+ "x-revturbine-schema-exposure": Internal21
6246
6439
  }
6247
6440
  );
6248
6441
  var NameField2 = z32.string().min(1).max(200);
@@ -6253,7 +6446,7 @@ var ThresholdPercentField2 = z32.number().int().min(10).max(100).multipleOf(10);
6253
6446
  var NullableDatetimeField2 = z32.string().datetime().nullable().default(null);
6254
6447
  var AnchorFields2 = z32.object({
6255
6448
  environment_id: z32.string().min(1).default("production").meta({ ...Unrestricted24, readOnly: true })
6256
- }).meta({ id: "null", "x-revturbine-schema-persistence": Transient22, "x-revturbine-schema-exposure": Internal20 });
6449
+ }).meta({ id: "null", "x-revturbine-schema-persistence": Transient22, "x-revturbine-schema-exposure": Internal21 });
6257
6450
  var VersionFields2 = z32.object({
6258
6451
  // Which playbook version (the review/deploy unit — was `change_set_id`) staged
6259
6452
  // this ledger row; null once it is the deployed live row.
@@ -6266,7 +6459,7 @@ var VersionFields2 = z32.object({
6266
6459
  // so a stale draft is detected when live has moved past it.
6267
6460
  sequence: z32.number().int().min(1).default(1).meta({ ...Unrestricted24, readOnly: true }),
6268
6461
  base_sequence: z32.number().int().nullable().default(null).meta({ ...Unrestricted24, readOnly: true })
6269
- }).meta({ id: "null", "x-revturbine-schema-persistence": Transient22, "x-revturbine-schema-exposure": Internal20 });
6462
+ }).meta({ id: "null", "x-revturbine-schema-persistence": Transient22, "x-revturbine-schema-exposure": Internal21 });
6270
6463
  var AnchorBaseSchema2 = IdField2.merge(TimestampFields2).merge(TenantIdField2).merge(AnchorFields2).extend({
6271
6464
  handle: HandleField2.meta({ ...Unrestricted24, readOnly: true }),
6272
6465
  active: z32.boolean().default(true).meta({ ...Unrestricted24, readOnly: true })
@@ -6275,10 +6468,10 @@ function makeAnchor2(id) {
6275
6468
  return AnchorBaseSchema2.meta({
6276
6469
  id,
6277
6470
  "x-revturbine-schema-persistence": Persisted24,
6278
- "x-revturbine-schema-exposure": Internal20
6471
+ "x-revturbine-schema-exposure": Internal21
6279
6472
  });
6280
6473
  }
6281
- var SeveritySchema22 = z32.enum(["info", "warning", "critical"]).meta({ id: "Severity", "x-revturbine-schema-persistence": Transient22, "x-revturbine-schema-exposure": Internal20 });
6474
+ var SeveritySchema22 = z32.enum(["info", "warning", "critical"]).meta({ id: "Severity", "x-revturbine-schema-persistence": Transient22, "x-revturbine-schema-exposure": Internal21 });
6282
6475
  var StudioSurfaceTypeSchema2 = z32.enum([
6283
6476
  "button",
6284
6477
  "plans_page_ctas",
@@ -6299,7 +6492,7 @@ var StudioSurfaceTypeSchema2 = z32.enum([
6299
6492
  "cli",
6300
6493
  "agent_connector",
6301
6494
  "custom_in_app"
6302
- ]).meta({ id: "StudioSurfaceType", "x-revturbine-schema-persistence": Transient22, "x-revturbine-schema-exposure": Internal20 });
6495
+ ]).meta({ id: "StudioSurfaceType", "x-revturbine-schema-persistence": Transient22, "x-revturbine-schema-exposure": Internal21 });
6303
6496
  var CtaActionTypeSchema2 = z32.enum([
6304
6497
  "open_checkout",
6305
6498
  "view_plans",
@@ -7179,7 +7372,7 @@ var trialPaths2 = {
7179
7372
  })
7180
7373
  }
7181
7374
  };
7182
- var { Unrestricted: Unrestricted42, Financial: Financial4 } = DataClassification2;
7375
+ var { Unrestricted: Unrestricted42, Financial: Financial5 } = DataClassification2;
7183
7376
  var { Persisted: Persisted42, Transient: Transient42 } = SchemaPersistence2;
7184
7377
  var { External: External32 } = SchemaExposure2;
7185
7378
  var PLAYBOOK_SDK_FACETS32 = schemaFacets2(SchemaContext2.Playbook, { sdkInput: true });
@@ -7234,7 +7427,7 @@ var PlanVariationSchema2 = IdField2.merge(TimestampFields2).merge(TenantIdField2
7234
7427
  plan_id: z82.string().min(1).meta(Unrestricted42),
7235
7428
  billing_period: z82.enum(["monthly", "annual", "one_time", "custom"]).meta(Unrestricted42),
7236
7429
  segment_id: z82.string().nullable().default(null).meta(Unrestricted42),
7237
- price_amount: z82.number().min(0).meta(Financial4),
7430
+ price_amount: z82.number().min(0).meta(Financial5),
7238
7431
  pricing_model: PricingModelSchema2.meta(Unrestricted42),
7239
7432
  visibility: PlanVisibilitySchema2.default("public").meta(Unrestricted42),
7240
7433
  // Soft reference → StripePrice.stripe_price_id (the backend Stripe-price mirror).
@@ -7279,7 +7472,7 @@ var AddOnVariationSchema2 = IdField2.merge(TimestampFields2).merge(TenantIdField
7279
7472
  // expansion packages) bind here without coercion to 'custom'.
7280
7473
  billing_period: z82.enum(["monthly", "annual", "one_time", "custom"]).meta(Unrestricted42),
7281
7474
  segment_id: z82.string().nullable().default(null).meta(Unrestricted42),
7282
- price_amount: z82.number().min(0).meta(Financial4),
7475
+ price_amount: z82.number().min(0).meta(Financial5),
7283
7476
  pricing_model: PricingModelSchema2.meta(Unrestricted42),
7284
7477
  visibility: PlanVisibilitySchema2.default("public").meta(Unrestricted42),
7285
7478
  // Soft reference → StripePrice.stripe_price_id (the backend Stripe-price mirror).
@@ -7306,8 +7499,8 @@ var StripePriceSchema2 = IdField2.merge(TimestampFields2).merge(TenantIdField2).
7306
7499
  stripe_price_id: z82.string().min(1).meta(Unrestricted42),
7307
7500
  stripe_product_id: z82.string().min(1).meta(Unrestricted42),
7308
7501
  billing_period: StripePriceBillingPeriodSchema2.meta(Unrestricted42),
7309
- unit_amount_cents: z82.number().int().min(0).nullable().default(null).meta(Financial4),
7310
- currency: CurrencySchema2.meta(Financial4),
7502
+ unit_amount_cents: z82.number().int().min(0).nullable().default(null).meta(Financial5),
7503
+ currency: CurrencySchema2.meta(Financial5),
7311
7504
  pricing_model: PricingModelSchema2.meta(Unrestricted42),
7312
7505
  nickname: z82.string().nullable().default(null).meta(Unrestricted42),
7313
7506
  // Placeholder/seed price (demo or pre-integration tenants) vs a real
@@ -7790,6 +7983,10 @@ var PlacementTestModeSchema2 = z92.enum(["off", "test_users", "all_traffic"]).me
7790
7983
  var PlacementSettingsCapStateSchema2 = z92.object({
7791
7984
  capRules: z92.array(PlacementSettingsCapRuleSchema2).default([]).meta(Unrestricted52),
7792
7985
  sessionCooldownMinutes: z92.number().int().min(0).default(30).meta(Unrestricted52),
7986
+ // Tenant-level default remind-me-later (defer) window, in minutes. A
7987
+ // per-payload `remind_later_minutes` overrides it when set (plan 167 REQ-6,
7988
+ // Q-3). Rides in this global_frequency_cap jsonb wrapper — no column/`.fbs`.
7989
+ remindLaterMinutes: z92.number().int().min(0).default(60).meta(Unrestricted52),
7793
7990
  testMode: PlacementTestModeSchema2.default("off").meta(Unrestricted52)
7794
7991
  }).meta(
7795
7992
  { id: "PlacementSettingsCapState", "x-revturbine-schema-persistence": Transient52, "x-revturbine-schema-exposure": Internal42 }
@@ -7804,7 +8001,8 @@ var PlacementSettingsSchema2 = IdField2.merge(TimestampFields2).merge(TenantIdFi
7804
8001
  global_frequency_cap_period: z92.enum(["hour", "day", "week", "month", "session"]).nullable().default(null).meta(Unrestricted52),
7805
8002
  suppress_for_paid: z92.boolean().default(false).meta(Unrestricted52),
7806
8003
  suppress_for_trial: z92.boolean().default(false).meta(Unrestricted52),
7807
- default_dismiss_cooldown_hours: z92.number().int().min(0).default(24).meta(Unrestricted52),
8004
+ // `default_dismiss_cooldown_hours` removed (plan 167 Q-2): the dismiss
8005
+ // cooldown is defined per-payload in days (`cooldown_after_dismiss_days`).
7808
8006
  allow_stacking: z92.boolean().default(false).meta(Unrestricted52),
7809
8007
  priority_collision_strategy: z92.enum(["highest_priority", "most_recent", "random"]).default("highest_priority").meta(Unrestricted52)
7810
8008
  }).meta(
@@ -7916,7 +8114,7 @@ var RevTurbineConfigPlacementSettingsItemSchema2 = z92.object({
7916
8114
  global_frequency_cap_period: z92.enum(["hour", "day", "week", "month", "session"]).nullable().default(null).meta(Unrestricted52),
7917
8115
  suppress_for_paid: z92.boolean().default(false).meta(Unrestricted52),
7918
8116
  suppress_for_trial: z92.boolean().default(false).meta(Unrestricted52),
7919
- default_dismiss_cooldown_hours: z92.number().int().min(0).nullable().default(null).meta(Unrestricted52),
8117
+ // `default_dismiss_cooldown_hours` removed (plan 167 Q-2).
7920
8118
  allow_stacking: z92.boolean().default(false).meta(Unrestricted52),
7921
8119
  priority_collision_strategy: z92.string().nullable().default(null).meta(Unrestricted52)
7922
8120
  }).meta(
@@ -8058,7 +8256,11 @@ var RevTurbineConfigUiPathActionTypeSchema2 = z92.enum([
8058
8256
  "verify_work_email",
8059
8257
  "update_payment_method",
8060
8258
  "enable_auto_renewal",
8061
- "manage_subscription"
8259
+ "manage_subscription",
8260
+ // Authored `snooze` resolves through to the SDK's remind-later path
8261
+ // (plan 167 windows); previously it fell through as an invalid type
8262
+ // (plan 174 TASK-6 / Q-5, spec-check F-65a).
8263
+ "snooze"
8062
8264
  ]).meta(
8063
8265
  { id: "RevTurbineConfigUiPathActionType", "x-revturbine-schema-persistence": Transient52, "x-revturbine-schema-exposure": External42 }
8064
8266
  );
@@ -8167,6 +8369,8 @@ var RevTurbineConfigStudioPayloadSchema2 = z92.object({
8167
8369
  caps: RevTurbineConfigStudioPayloadCapsSchema2.optional().meta(Unrestricted52),
8168
8370
  // Optional slot targeting (spec §3.1.1): empty/absent = any compatible slot.
8169
8371
  surface_slot_ids: z92.array(z92.string()).optional().meta(Unrestricted52),
8372
+ // Per-payload remind-me-later override (minutes); absent = inherit tenant default (plan 167 Q-3).
8373
+ remind_later_minutes: z92.number().int().min(0).nullable().optional().meta(Unrestricted52),
8170
8374
  created_at: z92.string().optional().meta({ ...Unrestricted52, readOnly: true }),
8171
8375
  recommendation_strategy: z92.enum(["next_tier_up", "best_value", "custom"]).optional().default("next_tier_up").meta(Unrestricted52),
8172
8376
  recommendation_plan_override: z92.string().optional().meta(Unrestricted52)
@@ -8206,6 +8410,8 @@ var RevTurbineConfigPlacementPayloadItemSchema2 = z92.object({
8206
8410
  placement_id: z92.string().min(1).meta(Unrestricted52),
8207
8411
  target: RevTurbineConfigStudioPayloadTargetSchema2.meta(Unrestricted52),
8208
8412
  caps: RevTurbineConfigStudioPayloadCapsSchema2.optional().meta(Unrestricted52),
8413
+ // Per-payload remind-me-later override (minutes); absent = inherit tenant default (plan 167 Q-3).
8414
+ remind_later_minutes: z92.number().int().min(0).nullable().optional().meta(Unrestricted52),
8209
8415
  created_at: z92.string().meta({ ...Unrestricted52, readOnly: true }),
8210
8416
  updated_at: z92.string().datetime().optional().meta({ ...Unrestricted52, readOnly: true }),
8211
8417
  source_mode: z92.enum(["inline", "content_linked"]).meta(Unrestricted52),
@@ -8436,6 +8642,12 @@ var PlaybookSchema2 = z92.preprocess(normalizeConfigHeaderInput2, PlaybookObject
8436
8642
  "x-revturbine-schema-exposure": External42,
8437
8643
  ...PLAYBOOK_SDK_FACETS42
8438
8644
  });
8645
+ var PlaybookStrictSchema2 = z92.preprocess(normalizeConfigHeaderInput2, PlaybookObjectSchema2.strict()).meta({
8646
+ id: "PlaybookStrict",
8647
+ "x-revturbine-schema-persistence": Transient52,
8648
+ "x-revturbine-schema-exposure": External42,
8649
+ ...PLAYBOOK_SDK_FACETS42
8650
+ });
8439
8651
  var configPaths2 = {
8440
8652
  "/api/seat-type-anchors": {
8441
8653
  get: operation2({
@@ -8711,7 +8923,7 @@ function repairDeprecatedFields(config, schema = PlaybookObjectSchema2) {
8711
8923
  }
8712
8924
 
8713
8925
  // src/schema/version.ts
8714
- var SCHEMA_VERSION = "0.1.145";
8926
+ var SCHEMA_VERSION = "0.1.167";
8715
8927
 
8716
8928
  // src/lib/credentials.ts
8717
8929
  import { chmodSync, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "fs";
@@ -8967,7 +9179,7 @@ Account created. Enter the verification code emailed to ${opts.email}.`);
8967
9179
  }
8968
9180
 
8969
9181
  // src/lib/track.ts
8970
- var CLI_UNTRACKED_COMMANDS = /* @__PURE__ */ new Set(["login", "signup", "logout", "verify"]);
9182
+ var CLI_UNTRACKED_COMMANDS = /* @__PURE__ */ new Set(["login", "signup", "logout"]);
8971
9183
  function shouldTrackCommandExecution(name, hasUrl) {
8972
9184
  return hasUrl && !CLI_UNTRACKED_COMMANDS.has(name);
8973
9185
  }
@@ -10086,7 +10298,7 @@ Command groups:
10086
10298
  Keys ingest-keys create|list|revoke
10087
10299
 
10088
10300
  Version selectors (no defaults \u2014 a command that reads a config requires one):
10089
- <file> a local Config File (positional, or --file <path>)
10301
+ <file> a local Playbook file (positional, or --file <path>)
10090
10302
  --draft the tenant's single open draft (resolved automatically)
10091
10303
  --live the current live Release
10092
10304
  --release <id> a specific playbook version / Release
@@ -10097,13 +10309,13 @@ Common workflows:
10097
10309
 
10098
10310
  # Author, validate, and ship against the default instance (revturbine.com/app)
10099
10311
  revturbine login
10100
- revturbine download --live --save ./config.json
10101
- revturbine validate ./config.json
10102
- revturbine diff ./config.json --live
10103
- revturbine launch ./config.json
10312
+ revturbine download --live --save ./revturbine.playbook.json
10313
+ revturbine validate ./revturbine.playbook.json
10314
+ revturbine diff ./revturbine.playbook.json --live
10315
+ revturbine launch ./revturbine.playbook.json
10104
10316
 
10105
10317
  # Manual review flow (stage \u2192 inspect \u2192 launch)
10106
- revturbine upload ./config.json # stage as the open draft
10318
+ revturbine upload ./revturbine.playbook.json # stage as the open draft
10107
10319
  revturbine preview # inspect the open draft
10108
10320
  revturbine validate --draft # full server catalog
10109
10321
  revturbine launch --draft
@@ -10133,7 +10345,7 @@ program.hook("postAction", async (_thisCommand, actionCommand) => {
10133
10345
  command: actionCommand.name()
10134
10346
  });
10135
10347
  });
10136
- program.name("revturbine").description("Validate RevTurbine Config Files and ship them through the playbook-version lifecycle (draft \u2192 Release).").version(`${pkgVersion} (schema ${SCHEMA_VERSION})`, "-V, --version", "Print the revturbine and bundled schema versions").option(NO_LOCAL_FLAG, "Ignore the repo-pinned CLI and run this installation").showHelpAfterError().addHelpText("after", HELP_AFTER);
10348
+ program.name("revturbine").description("Validate RevTurbine Playbooks and ship them through the playbook-version lifecycle (draft \u2192 Release).").version(`${pkgVersion} (schema ${SCHEMA_VERSION})`, "-V, --version", "Print the revturbine and bundled schema versions").option(NO_LOCAL_FLAG, "Ignore the repo-pinned CLI and run this installation").showHelpAfterError().addHelpText("after", HELP_AFTER);
10137
10349
  function runInstall(manager, args, cwd) {
10138
10350
  return new Promise((resolve, reject) => {
10139
10351
  const child = spawn2(manager, args, { cwd, stdio: "inherit", shell: true });
@@ -10434,7 +10646,7 @@ program.command("download").description("Fetch a config version from the server.
10434
10646
  }
10435
10647
  }
10436
10648
  );
10437
- program.command("validate").description("Validate a Config File offline (schema), or the open draft against the full server catalog (--draft).").argument("[file...]", "Path(s) to a Config File (offline mode)").option("--draft", "Validate the open draft server-side (full catalog)").option("-u, --url <url>", "RevTurbine instance URL (used with --draft)", DEFAULT_URL).option("-t, --tenant-id <id>", "x-tenant-id (defaults to the stored token tenant)").action(async (files, opts) => {
10649
+ program.command("validate").description("Validate a Playbook file offline (schema), or the open draft against the full server catalog (--draft).").argument("[file...]", "Path(s) to a Playbook file (offline mode)").option("--draft", "Validate the open draft server-side (full catalog)").option("-u, --url <url>", "RevTurbine instance URL (used with --draft)", DEFAULT_URL).option("-t, --tenant-id <id>", "x-tenant-id (defaults to the stored token tenant)").action(async (files, opts) => {
10438
10650
  if (opts.draft && files.length > 0) fail(EXIT.USAGE, "validate takes either <file> or --draft, not both.");
10439
10651
  if (!opts.draft && files.length === 0) {
10440
10652
  throw new SelectorError("STATE_REQUIRED \u2014 validate needs a version: <file> (offline) or --draft (server catalog).");
@@ -10476,7 +10688,7 @@ program.command("validate").description("Validate a Config File offline (schema)
10476
10688
  }
10477
10689
  diag("\u2713 No blocking findings.");
10478
10690
  });
10479
- program.command("diff").description("Compare two config versions (first \u2192 second; a file vs --draft/--live/--release previews the launch \u2014 the server side is the base). Dry-run, no writes.").argument("[file...]", "Local Config File path(s)").option("--draft", "The tenant's open draft").option("--live", "The current live Release").option("--release <id>", "A specific playbook version / Release").option("-u, --url <url>", "RevTurbine instance URL", DEFAULT_URL).option("-t, --tenant-id <id>", "x-tenant-id (defaults to the stored token tenant)").action(async (files, opts) => {
10691
+ program.command("diff").description("Compare two config versions (first \u2192 second; a file vs --draft/--live/--release previews the launch \u2014 the server side is the base). Dry-run, no writes.").argument("[file...]", "Local Playbook file path(s)").option("--draft", "The tenant's open draft").option("--live", "The current live Release").option("--release <id>", "A specific playbook version / Release").option("-u, --url <url>", "RevTurbine instance URL", DEFAULT_URL).option("-t, --tenant-id <id>", "x-tenant-id (defaults to the stored token tenant)").action(async (files, opts) => {
10480
10692
  const sels = requireSelectors(opts, files, { count: 2, allowed: ["file", "draft", "live", "release"], command: "diff" });
10481
10693
  const ordered = orderDiffSelectors(sels);
10482
10694
  const needsServer = ordered.some((s) => s.kind !== "file");
@@ -10486,7 +10698,7 @@ program.command("diff").description("Compare two config versions (first \u2192 s
10486
10698
  process.stdout.write(`${formatDiff(diffExportedConfig(a, b))}
10487
10699
  `);
10488
10700
  });
10489
- program.command("show").description(`Render a summary view of a config version. <kind> \u2208 ${SHOW_KINDS.join(" | ")}.`).argument("<kind>", `One of: ${SHOW_KINDS.join(", ")}`).option("--file <path>", "A local Config File").option("--draft", "The tenant's open draft").option("--live", "The current live Release").option("--release <id>", "A specific playbook version / Release").option("-u, --url <url>", "RevTurbine instance URL", DEFAULT_URL).option("-t, --tenant-id <id>", "x-tenant-id (defaults to the stored token tenant)").option("--json", "Machine-readable output").action(
10701
+ program.command("show").description(`Render a summary view of a config version. <kind> \u2208 ${SHOW_KINDS.join(" | ")}.`).argument("<kind>", `One of: ${SHOW_KINDS.join(", ")}`).option("--file <path>", "A local Playbook file").option("--draft", "The tenant's open draft").option("--live", "The current live Release").option("--release <id>", "A specific playbook version / Release").option("-u, --url <url>", "RevTurbine instance URL", DEFAULT_URL).option("-t, --tenant-id <id>", "x-tenant-id (defaults to the stored token tenant)").option("--json", "Machine-readable output").action(
10490
10702
  async (kind, opts) => {
10491
10703
  if (!SHOW_KINDS.includes(kind)) {
10492
10704
  fail(EXIT.USAGE, `unknown kind '${kind}' \u2014 use one of: ${SHOW_KINDS.join(", ")}.`);
@@ -10501,7 +10713,7 @@ program.command("show").description(`Render a summary view of a config version.
10501
10713
  );
10502
10714
  var PRUNE_OPTION = ["--prune", "Confirm a convergent delete past the mass-deletion guard (removes entities absent from the file)"];
10503
10715
  var NO_PRUNE_OPTION = ["--no-prune", "Additive import \u2014 keep entities that are absent from the file (disables convergent delete)"];
10504
- program.command("upload").description("Stage a Config File as the open draft (POST /api/config/import).").argument("<config>", "Path to a Config File").option("-u, --url <url>", "RevTurbine instance URL", DEFAULT_URL).option("-t, --tenant-id <id>", "x-tenant-id (defaults to the stored token tenant)").option(...PRUNE_OPTION).option(...NO_PRUNE_OPTION).action(async (configFile2, opts) => {
10716
+ program.command("upload").description("Stage a Playbook file as the open draft (POST /api/config/import).").argument("<config>", "Path to a Playbook file").option("-u, --url <url>", "RevTurbine instance URL", DEFAULT_URL).option("-t, --tenant-id <id>", "x-tenant-id (defaults to the stored token tenant)").option(...PRUNE_OPTION).option(...NO_PRUNE_OPTION).action(async (configFile2, opts) => {
10505
10717
  const config = verifyConfig(configFile2);
10506
10718
  if (config === null) fail(EXIT.VALIDATION, `Fix the issues above in ${configFile2}, then re-run.`);
10507
10719
  const conn = connect(opts.url, uploadTenantFor(opts.url, config, opts.tenantId));
@@ -10522,7 +10734,7 @@ program.command("upload").description("Stage a Config File as the open draft (PO
10522
10734
  if (playbookVersionId) diagRaw(` playbook_version_id: ${playbookVersionId}`);
10523
10735
  diag("Launch it with `revturbine launch --draft`, or from the UI (Drafts & Releases).");
10524
10736
  });
10525
- program.command("launch").description("Take a config live as a new Release: validate (launch gate), then submit \u2192 approve \u2192 deploy. Synchronous.").argument("[file]", "Config File to upload and launch directly").option("--draft", "Launch the already-open draft").option("--force", "Launch past incomplete-but-valid findings (error_launch); structural errors (error_draft) still block").option("-u, --url <url>", "RevTurbine instance URL", DEFAULT_URL).option("-t, --tenant-id <id>", "x-tenant-id (defaults to the stored token tenant)").option(...PRUNE_OPTION).option(...NO_PRUNE_OPTION).option("--yes", "Accepted for parity with discard/restore; launch has no confirmation prompt").action(async (file, opts) => {
10737
+ program.command("launch").description("Take a config live as a new Release: validate (launch gate), then submit \u2192 approve \u2192 deploy. Synchronous.").argument("[file]", "Playbook file to upload and launch directly").option("--draft", "Launch the already-open draft").option("--force", "Launch past incomplete-but-valid findings (error_launch); structural errors (error_draft) still block").option("-u, --url <url>", "RevTurbine instance URL", DEFAULT_URL).option("-t, --tenant-id <id>", "x-tenant-id (defaults to the stored token tenant)").option(...PRUNE_OPTION).option(...NO_PRUNE_OPTION).option("--yes", "Accepted for parity with discard/restore; launch has no confirmation prompt").action(async (file, opts) => {
10526
10738
  const [sel] = requireSelectors(opts, file ? [file] : [], { count: 1, allowed: ["file", "draft"], command: "launch" });
10527
10739
  let conn;
10528
10740
  let playbookVersionId;
@@ -10666,7 +10878,7 @@ program.command("evaluate").description("Run placement/entitlement decisions for
10666
10878
  var generateCmd = program.command("generate").description("Code generation from a config version (see: generate types).");
10667
10879
  generateCmd.command("types").description(
10668
10880
  "Generate a TypeScript module of Playbook handles \u2014 entitlements (namespaced by type), plans, segments, surface-template ids, and ui-path action types \u2014 for type-safe call sites."
10669
- ).argument("[file...]", "Path to a Config File").option("--draft", "The tenant's open draft").option("--live", "The current live Release").option("--release <id>", "A specific playbook version / Release").option("-o, --out <path>", "Write the generated module to this path (parent dirs created); default stdout").option("--json", "Emit the handle map as JSON instead of TypeScript").option("-u, --url <url>", "RevTurbine instance URL", DEFAULT_URL).option("-t, --tenant-id <id>", "x-tenant-id (defaults to the stored token tenant)").action(
10881
+ ).argument("[file...]", "Path to a Playbook file").option("--draft", "The tenant's open draft").option("--live", "The current live Release").option("--release <id>", "A specific playbook version / Release").option("-o, --out <path>", "Write the generated module to this path (parent dirs created); default stdout").option("--json", "Emit the handle map as JSON instead of TypeScript").option("-u, --url <url>", "RevTurbine instance URL", DEFAULT_URL).option("-t, --tenant-id <id>", "x-tenant-id (defaults to the stored token tenant)").action(
10670
10882
  async (files, opts) => {
10671
10883
  const [sel] = requireSelectors(opts, files, {
10672
10884
  count: 1,
@@ -10736,23 +10948,23 @@ var COMMAND_EXAMPLES = {
10736
10948
  download: [
10737
10949
  "",
10738
10950
  "Examples:",
10739
- " revturbine download --live --save ./config.json The live config \u2192 file",
10951
+ " revturbine download --live --save ./revturbine.playbook.json The live config \u2192 file",
10740
10952
  " revturbine download --draft The open draft (rendered on demand) \u2192 stdout",
10741
10953
  " revturbine download --release cs_1a2b3c --format flatbuffer --save ./bundle.fb"
10742
10954
  ].join("\n"),
10743
10955
  validate: [
10744
10956
  "",
10745
10957
  "Examples:",
10746
- " revturbine validate ./config.json Offline schema validation (no network)",
10958
+ " revturbine validate ./revturbine.playbook.json Offline schema validation (no network)",
10747
10959
  " revturbine validate --draft Full server catalog against the open draft"
10748
10960
  ].join("\n"),
10749
10961
  diff: [
10750
10962
  "",
10751
10963
  "Examples:",
10752
- " revturbine diff ./config.json --live Local edits vs the live config",
10964
+ " revturbine diff ./revturbine.playbook.json --live Local edits vs the live config",
10753
10965
  " revturbine diff --live --draft What the open draft would change"
10754
10966
  ].join("\n"),
10755
- show: ["", "Examples:", " revturbine show plans --live", " revturbine show segments --file ./config.json"].join("\n"),
10967
+ show: ["", "Examples:", " revturbine show plans --live", " revturbine show segments --file ./revturbine.playbook.json"].join("\n"),
10756
10968
  generate: [
10757
10969
  "",
10758
10970
  "Examples:",
@@ -10764,11 +10976,11 @@ var COMMAND_EXAMPLES = {
10764
10976
  "entitlement type) and the `EntitlementHandle` union for can()/gate()",
10765
10977
  "call sites. Its header records the exact command to regenerate it."
10766
10978
  ].join("\n"),
10767
- upload: ["", "Example:", " revturbine upload ./config.json Stage as the open draft"].join("\n"),
10979
+ upload: ["", "Example:", " revturbine upload ./revturbine.playbook.json Stage as the open draft"].join("\n"),
10768
10980
  launch: [
10769
10981
  "",
10770
10982
  "Examples:",
10771
- " revturbine launch ./config.json Upload, gate, and go live",
10983
+ " revturbine launch ./revturbine.playbook.json Upload, gate, and go live",
10772
10984
  " revturbine launch --draft Launch the already-open draft"
10773
10985
  ].join("\n"),
10774
10986
  evaluate: [