xytara 2.7.0 → 2.8.0

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.
package/RELEASE_NOTES.md CHANGED
@@ -1,3 +1,15 @@
1
+ # xytara 2.8.0 Release Notes
2
+
3
+ `xytara` 2.8.0 is the release-boundary hardening line for live-provider promotion discipline, treasury/operator public-claim safety, and pricing experiment guardrails.
4
+
5
+ Highlights:
6
+
7
+ - adds framework-provider promotion evidence gates so LangGraph/LangChain style claims stay reference-contract until endpoint, auth, health, latency, failure, and proof-fact evidence exists
8
+ - hardens pricing experiment planning and launch gates so optimization remains sample-maturity and operator-review gated
9
+ - hardens treasury public claim boundaries so public surfaces do not leak landing/custody/provider refs or promote readiness-only/internal rails as live
10
+ - hardens operator observability boundaries so read-only views cannot drift into mutation, settlement submission, fund movement, unsafe attention actions, or secret-bearing control behavior
11
+ - keeps the 2.7.0 clean-consumer packaging and release-smoke posture intact while making expansion claims more defensible
12
+
1
13
  # xytara 2.7.0 Release Notes
2
14
 
3
15
  `xytara` 2.7.0 is the expansion-closeout release line for package-hardening, clean-consumer release smoke testing, and disciplined adapter/product claim boundaries.
package/index.js CHANGED
@@ -35,6 +35,14 @@ const {
35
35
  buildAdapterPack,
36
36
  summarizeAdapterPack
37
37
  } = require("./lib/adapter_pack");
38
+ const {
39
+ buildFrameworkProviderPromotionPack,
40
+ summarizeFrameworkProviderPromotionPack
41
+ } = require("./lib/framework_provider_promotion");
42
+ const {
43
+ buildPricingExperimentPlan,
44
+ summarizePricingExperimentPlan
45
+ } = require("./lib/pricing_optimization_contract");
38
46
  const {
39
47
  buildPublishPlan,
40
48
  summarizePublishPlan
@@ -159,6 +167,10 @@ module.exports = {
159
167
  summarizeReleaseHistory,
160
168
  buildAdapterPack,
161
169
  summarizeAdapterPack,
170
+ buildFrameworkProviderPromotionPack,
171
+ summarizeFrameworkProviderPromotionPack,
172
+ buildPricingExperimentPlan,
173
+ summarizePricingExperimentPlan,
162
174
  buildPublishPlan,
163
175
  summarizePublishPlan,
164
176
  buildEcosystemEntryPack,
@@ -0,0 +1,235 @@
1
+ "use strict";
2
+
3
+ const packageJson = require("../package.json");
4
+
5
+ const FRAMEWORK_PROVIDER_CANDIDATES = Object.freeze([
6
+ Object.freeze({
7
+ framework_id: "langgraph",
8
+ adapter_id: "reference.framework.langgraph",
9
+ task_ref: "framework.langgraph.execute",
10
+ endpoint_env: "XYTARA_LANGGRAPH_PROVIDER_URL",
11
+ auth_env: "XYTARA_LANGGRAPH_PROVIDER_TOKEN",
12
+ health_path_env: "XYTARA_LANGGRAPH_PROVIDER_HEALTH_PATH"
13
+ }),
14
+ Object.freeze({
15
+ framework_id: "langchain",
16
+ adapter_id: "reference.framework.langchain",
17
+ task_ref: "framework.langchain.execute",
18
+ endpoint_env: "XYTARA_LANGCHAIN_PROVIDER_URL",
19
+ auth_env: "XYTARA_LANGCHAIN_PROVIDER_TOKEN",
20
+ health_path_env: "XYTARA_LANGCHAIN_PROVIDER_HEALTH_PATH"
21
+ })
22
+ ]);
23
+
24
+ const FRAMEWORK_PROVIDER_PROMOTION_REQUIREMENTS = Object.freeze([
25
+ "endpoint_configured",
26
+ "auth_configured",
27
+ "health_check_passes",
28
+ "latency_observed",
29
+ "failure_behavior_bounded",
30
+ "proof_fact_shape_preserved",
31
+ "operator_evidence_recorded"
32
+ ]);
33
+
34
+ const FRAMEWORK_PROVIDER_PROMOTION_REJECTION_CODES = Object.freeze([
35
+ "framework_not_registered",
36
+ "candidate_not_live_check_ready",
37
+ "adapter_id_mismatch",
38
+ "task_ref_mismatch",
39
+ "operator_evidence_missing",
40
+ "auth_boundary_evidence_missing",
41
+ "health_check_missing",
42
+ "health_check_failed",
43
+ "latency_measurement_missing",
44
+ "latency_budget_exceeded",
45
+ "failure_behavior_evidence_missing",
46
+ "proof_fact_shape_evidence_missing",
47
+ "secret_material_forbidden"
48
+ ]);
49
+
50
+ function normalizeString(value) {
51
+ return typeof value === "string" ? value.trim() : "";
52
+ }
53
+
54
+ function containsSecretMaterial(value) {
55
+ if (!value || typeof value !== "object") return false;
56
+ const secretishKey = /(secret|token|credential|password|private[_-]?key|authorization)/i;
57
+ const secretishValue = /(bearer\s+[a-z0-9._-]{8,}|sk_live_|npm_[a-z0-9]|ghp_[a-z0-9]|xox[baprs]-)/i;
58
+ const stack = [value];
59
+ while (stack.length > 0) {
60
+ const current = stack.pop();
61
+ if (!current || typeof current !== "object") continue;
62
+ for (const [key, entry] of Object.entries(current)) {
63
+ if (secretishKey.test(String(key))) return true;
64
+ if (typeof entry === "string" && secretishValue.test(entry)) return true;
65
+ if (entry && typeof entry === "object") stack.push(entry);
66
+ }
67
+ }
68
+ return false;
69
+ }
70
+
71
+ function boolFromEnv(value) {
72
+ const normalized = normalizeString(value).toLowerCase();
73
+ return ["1", "true", "yes", "on"].includes(normalized);
74
+ }
75
+
76
+ function summarizeCandidate(candidate, env = process.env) {
77
+ const endpoint = normalizeString(env[candidate.endpoint_env]);
78
+ const authToken = normalizeString(env[candidate.auth_env]);
79
+ const healthPath = normalizeString(env[candidate.health_path_env]) || "/health";
80
+ const endpointConfigured = Boolean(endpoint);
81
+ const authConfigured = Boolean(authToken);
82
+ const liveCheckReady = endpointConfigured && authConfigured;
83
+ return {
84
+ framework_id: candidate.framework_id,
85
+ adapter_id: candidate.adapter_id,
86
+ task_ref: candidate.task_ref,
87
+ state: liveCheckReady ? "live_check_ready" : "reference_contract_only",
88
+ endpoint_configured: endpointConfigured,
89
+ auth_configured: authConfigured,
90
+ health_path: healthPath,
91
+ endpoint_env: candidate.endpoint_env,
92
+ auth_env: candidate.auth_env,
93
+ health_path_env: candidate.health_path_env,
94
+ promotion_evidence_template: {
95
+ framework_id: candidate.framework_id,
96
+ adapter_id: candidate.adapter_id,
97
+ task_ref: candidate.task_ref,
98
+ operator_evidence_ref: "ops.framework_provider.<framework_id>.<date>",
99
+ auth_boundary_ref: "ops.framework_provider.auth_boundary.<framework_id>.<date>",
100
+ health_check: {
101
+ status_code: 200,
102
+ health_ok: true
103
+ },
104
+ latency_ms: 250,
105
+ latency_budget_ms: 2000,
106
+ failure_behavior_ref: "ops.framework_provider.failure_behavior.<framework_id>.<date>",
107
+ proof_fact_shape_ref: "ops.framework_provider.proof_facts.<framework_id>.<date>"
108
+ },
109
+ required_live_evidence: [
110
+ "real_external_endpoint",
111
+ "auth_or_access_boundary",
112
+ "health_check_response",
113
+ "bounded_latency_measurement",
114
+ "failure_behavior_observed",
115
+ "proof_fact_shape_preserved"
116
+ ],
117
+ claim_boundary: liveCheckReady
118
+ ? "may_run_live_promotion_check_but_must_not_claim_provider_live_until_check_evidence_is_recorded"
119
+ : "reference_adapter_only_no_live_external_provider_claim"
120
+ };
121
+ }
122
+
123
+ function buildFrameworkProviderPromotionPack(options = {}) {
124
+ const env = options.env || process.env;
125
+ const candidates = FRAMEWORK_PROVIDER_CANDIDATES.map((candidate) => summarizeCandidate(candidate, env));
126
+ const liveReadyCandidates = candidates.filter((candidate) => candidate.state === "live_check_ready");
127
+ const requireLive = boolFromEnv(env.XYTARA_FRAMEWORK_PROMOTION_REQUIRE_LIVE);
128
+ return {
129
+ ok: true,
130
+ product: packageJson.name,
131
+ category: "machine-commerce-framework-provider-promotion-pack",
132
+ pack_version: "xytara-framework-provider-promotion-pack-v1",
133
+ posture: "reference_framework_adapters_promote_only_with_external_endpoint_evidence",
134
+ promotion_state: liveReadyCandidates.length > 0 ? "live_check_ready" : "no_live_provider_evidence_configured",
135
+ require_live: requireLive,
136
+ framework_candidate_count: candidates.length,
137
+ live_check_ready_count: liveReadyCandidates.length,
138
+ promoted_provider_count: 0,
139
+ candidates,
140
+ promotion_requirements: FRAMEWORK_PROVIDER_PROMOTION_REQUIREMENTS.slice(),
141
+ deterministic_rejection_codes: FRAMEWORK_PROVIDER_PROMOTION_REJECTION_CODES.slice(),
142
+ strict_boundaries: [
143
+ "reference_framework_adapters_do_not_equal_live_provider_integrations",
144
+ "do_not_claim_langgraph_or_langchain_live_provider_without_endpoint_auth_health_and_latency_evidence",
145
+ "failed_or_missing_live_checks_keep_frameworks_in_reference_contract_state",
146
+ "live_provider_promotion_does_not_change_adapter_claims_for_unchecked_frameworks"
147
+ ],
148
+ linked_surfaces: {
149
+ framework_lane_ref: "/v1/frameworks",
150
+ framework_provider_promotion_ref: "/v1/framework-provider-promotion",
151
+ framework_provider_promotion_summary_ref: "/v1/framework-provider-promotion/summary",
152
+ adapter_depth_ref: "/v1/adapter-depth/summary",
153
+ integrations_ref: "/v1/integrations"
154
+ }
155
+ };
156
+ }
157
+
158
+ function validateFrameworkProviderPromotionEvidence(pack, evidence) {
159
+ const promotionPack = pack || {};
160
+ const evidencePacket = evidence || {};
161
+ const candidates = Array.isArray(promotionPack.candidates) ? promotionPack.candidates : [];
162
+ const frameworkId = normalizeString(evidencePacket.framework_id);
163
+ const candidate = candidates.find((entry) => entry && entry.framework_id === frameworkId) || null;
164
+ const healthCheck = evidencePacket.health_check || null;
165
+ const latencyValue = evidencePacket.latency_ms;
166
+ const latencyMs = Number(latencyValue);
167
+ const latencyBudgetMs = Number(evidencePacket.latency_budget_ms || 2000);
168
+ const rejectionCodes = [];
169
+
170
+ if (!candidate) {
171
+ rejectionCodes.push("framework_not_registered");
172
+ } else {
173
+ if (candidate.state !== "live_check_ready" || !candidate.endpoint_configured || !candidate.auth_configured) {
174
+ rejectionCodes.push("candidate_not_live_check_ready");
175
+ }
176
+ if (normalizeString(evidencePacket.adapter_id) && normalizeString(evidencePacket.adapter_id) !== candidate.adapter_id) {
177
+ rejectionCodes.push("adapter_id_mismatch");
178
+ }
179
+ if (normalizeString(evidencePacket.task_ref) && normalizeString(evidencePacket.task_ref) !== candidate.task_ref) {
180
+ rejectionCodes.push("task_ref_mismatch");
181
+ }
182
+ }
183
+ if (!normalizeString(evidencePacket.operator_evidence_ref)) rejectionCodes.push("operator_evidence_missing");
184
+ if (!normalizeString(evidencePacket.auth_boundary_ref)) rejectionCodes.push("auth_boundary_evidence_missing");
185
+ if (!healthCheck || typeof healthCheck !== "object") {
186
+ rejectionCodes.push("health_check_missing");
187
+ } else if (healthCheck.health_ok !== true || Number(healthCheck.status_code || 0) < 200 || Number(healthCheck.status_code || 0) >= 300) {
188
+ rejectionCodes.push("health_check_failed");
189
+ }
190
+ if (latencyValue === null || latencyValue === undefined || latencyValue === "" || !Number.isFinite(latencyMs) || latencyMs < 0) {
191
+ rejectionCodes.push("latency_measurement_missing");
192
+ } else if (Number.isFinite(latencyBudgetMs) && latencyBudgetMs > 0 && latencyMs > latencyBudgetMs) {
193
+ rejectionCodes.push("latency_budget_exceeded");
194
+ }
195
+ if (!normalizeString(evidencePacket.failure_behavior_ref)) rejectionCodes.push("failure_behavior_evidence_missing");
196
+ if (!normalizeString(evidencePacket.proof_fact_shape_ref)) rejectionCodes.push("proof_fact_shape_evidence_missing");
197
+ if (containsSecretMaterial(evidencePacket)) rejectionCodes.push("secret_material_forbidden");
198
+
199
+ return {
200
+ validation_version: "xytara-framework-provider-promotion-evidence-validation-v1",
201
+ promotion_allowed: rejectionCodes.length === 0,
202
+ framework_id: frameworkId || null,
203
+ rejection_codes: Array.from(new Set(rejectionCodes)),
204
+ required_requirements: FRAMEWORK_PROVIDER_PROMOTION_REQUIREMENTS.slice(),
205
+ boundary: "framework_provider_promotion_requires_endpoint_auth_health_latency_failure_and_proof_fact_evidence_without_secret_material"
206
+ };
207
+ }
208
+
209
+ function summarizeFrameworkProviderPromotionPack(options = {}) {
210
+ const pack = buildFrameworkProviderPromotionPack(options);
211
+ return {
212
+ ok: true,
213
+ product: pack.product,
214
+ category: "machine-commerce-framework-provider-promotion-summary",
215
+ summary_version: "xytara-framework-provider-promotion-summary-v1",
216
+ posture: pack.posture,
217
+ promotion_state: pack.promotion_state,
218
+ require_live: pack.require_live,
219
+ framework_candidate_count: pack.framework_candidate_count,
220
+ live_check_ready_count: pack.live_check_ready_count,
221
+ promoted_provider_count: pack.promoted_provider_count,
222
+ promotion_requirement_count: pack.promotion_requirements.length,
223
+ deterministic_rejection_code_count: pack.deterministic_rejection_codes.length,
224
+ boundary_count: pack.strict_boundaries.length,
225
+ linked_surfaces: pack.linked_surfaces
226
+ };
227
+ }
228
+
229
+ module.exports = {
230
+ FRAMEWORK_PROVIDER_CANDIDATES,
231
+ FRAMEWORK_PROVIDER_PROMOTION_REJECTION_CODES,
232
+ buildFrameworkProviderPromotionPack,
233
+ summarizeFrameworkProviderPromotionPack,
234
+ validateFrameworkProviderPromotionEvidence
235
+ };
@@ -5,12 +5,71 @@ const { buildEconomicsIntelligenceSummary } = require("./commerce_economics");
5
5
  const { buildPartnerIntelligencePack } = require("./partner_intelligence");
6
6
  const { buildRevenueSignalSummary } = require("./pricing_optimization_contract");
7
7
 
8
+ const OPERATOR_OBSERVABILITY_REJECTION_CODES = [
9
+ "boundary_missing",
10
+ "missing_linked_surface",
11
+ "mutation_surface_linked",
12
+ "settlement_submission_surface_linked",
13
+ "fund_movement_surface_linked",
14
+ "unsafe_attention_action",
15
+ "secret_material_forbidden",
16
+ "count_not_number"
17
+ ];
18
+
19
+ const OPERATOR_OBSERVABILITY_REQUIRED_SURFACES = [
20
+ "activity_ledger_ref",
21
+ "payment_ledger_ref",
22
+ "reconciliation_report_ref",
23
+ "deliveries_ref",
24
+ "settlement_ref",
25
+ "adapter_depth_ref",
26
+ "pricing_revenue_signal_ref",
27
+ "operator_intelligence_ref"
28
+ ];
29
+
8
30
  function valuesFromCollection(collection) {
9
31
  if (!collection) return [];
10
32
  if (collection instanceof Map) return Array.from(collection.values());
11
33
  return Array.isArray(collection) ? collection : [];
12
34
  }
13
35
 
36
+ function containsSecretMaterial(value) {
37
+ if (!value || typeof value !== "object") return false;
38
+ const secretishKey = /(secret|token|credential|password|private[_-]?key|authorization|api[_-]?key)/i;
39
+ const secretishValue = /(bearer\s+[a-z0-9._-]{8,}|sk_live_|npm_[a-z0-9]|ghp_[a-z0-9]|xox[baprs]-)/i;
40
+ const stack = [value];
41
+ while (stack.length > 0) {
42
+ const current = stack.pop();
43
+ if (!current || typeof current !== "object") continue;
44
+ for (const [key, entry] of Object.entries(current)) {
45
+ if (secretishKey.test(String(key))) return true;
46
+ if (typeof entry === "string" && secretishValue.test(entry)) return true;
47
+ if (entry && typeof entry === "object") stack.push(entry);
48
+ }
49
+ }
50
+ return false;
51
+ }
52
+
53
+ function isUnsafeLinkedSurface(value) {
54
+ const normalized = String(value || "").toLowerCase();
55
+ return /\/(submit|refund|grant|release|rotate|delete|mutate|write|webhook|checkout)(\/|$)|submission|fund-movement|fund_movement/.test(normalized);
56
+ }
57
+
58
+ function isSettlementSubmissionSurface(value) {
59
+ const normalized = String(value || "").toLowerCase();
60
+ return /settlement.*(submit|submission|broadcast|raw_tx)|\/submit(\/|$)/.test(normalized);
61
+ }
62
+
63
+ function isFundMovementSurface(value) {
64
+ const normalized = String(value || "").toLowerCase();
65
+ return /(fund|treasury|credit).*(release|transfer|withdraw|grant|refund|disburse)|external-credit-grants/.test(normalized);
66
+ }
67
+
68
+ function isUnsafeAttentionAction(action) {
69
+ const normalized = String(action || "").toLowerCase();
70
+ return /^(submit|grant|refund|release|rotate|delete|write|mutate|broadcast|withdraw|transfer|disburse)_/.test(normalized);
71
+ }
72
+
14
73
  function unwrapTransaction(entry) {
15
74
  if (entry && entry.transaction) return entry.transaction;
16
75
  return entry || null;
@@ -269,10 +328,52 @@ function buildOperatorObservabilityPack(state, input) {
269
328
  pricing_revenue_signal_ref: "/v1/pricing-optimization/revenue-signal-summary",
270
329
  operator_intelligence_ref: "/v1/operator-intelligence"
271
330
  },
331
+ deterministic_rejection_codes: OPERATOR_OBSERVABILITY_REJECTION_CODES.slice(),
272
332
  boundary: "read_only_operator_visibility_no_fund_movement_no_settlement_submission_no_secret_material"
273
333
  };
274
334
  }
275
335
 
336
+ function validateOperatorObservabilityBoundary(packInput) {
337
+ const pack = packInput || {};
338
+ const rejectionCodes = [];
339
+ const linkedSurfaces = pack.linked_surfaces || {};
340
+ const counts = pack.counts || {};
341
+ if (pack.boundary !== "read_only_operator_visibility_no_fund_movement_no_settlement_submission_no_secret_material") {
342
+ rejectionCodes.push("boundary_missing");
343
+ }
344
+ for (const surfaceName of OPERATOR_OBSERVABILITY_REQUIRED_SURFACES) {
345
+ if (!linkedSurfaces[surfaceName]) {
346
+ rejectionCodes.push("missing_linked_surface");
347
+ }
348
+ }
349
+ for (const surfaceRef of Object.values(linkedSurfaces)) {
350
+ if (isUnsafeLinkedSurface(surfaceRef)) rejectionCodes.push("mutation_surface_linked");
351
+ if (isSettlementSubmissionSurface(surfaceRef)) rejectionCodes.push("settlement_submission_surface_linked");
352
+ if (isFundMovementSurface(surfaceRef)) rejectionCodes.push("fund_movement_surface_linked");
353
+ }
354
+ const attentionQueue = Array.isArray(pack.attention_queue) ? pack.attention_queue : [];
355
+ for (const item of attentionQueue) {
356
+ if (item && isUnsafeAttentionAction(item.action)) {
357
+ rejectionCodes.push("unsafe_attention_action");
358
+ }
359
+ }
360
+ for (const [key, value] of Object.entries(counts)) {
361
+ if (key.endsWith("_count") && typeof value !== "number") {
362
+ rejectionCodes.push("count_not_number");
363
+ }
364
+ }
365
+ if (containsSecretMaterial(pack)) {
366
+ rejectionCodes.push("secret_material_forbidden");
367
+ }
368
+ return {
369
+ validation_version: "xytara-operator-observability-boundary-validation-v1",
370
+ observability_boundary_valid: rejectionCodes.length === 0,
371
+ rejection_codes: Array.from(new Set(rejectionCodes)),
372
+ deterministic_rejection_codes: OPERATOR_OBSERVABILITY_REJECTION_CODES.slice(),
373
+ boundary: "operator_observability_must_remain_read_only_without_secret_material_mutation_links_or_fund_movement_actions"
374
+ };
375
+ }
376
+
276
377
  function summarizeOperatorObservabilityPack(state, input) {
277
378
  const pack = buildOperatorObservabilityPack(state, input);
278
379
  return {
@@ -292,6 +393,7 @@ function summarizeOperatorObservabilityPack(state, input) {
292
393
  adapter_failure_count: pack.counts.adapter_failure_count,
293
394
  pricing_sample_maturity: pack.pricing_telemetry_summary.sample_maturity,
294
395
  attention_item_count: pack.attention_queue.length,
396
+ deterministic_rejection_code_count: pack.deterministic_rejection_codes.length,
295
397
  linked_surfaces: pack.linked_surfaces
296
398
  };
297
399
  }
@@ -300,5 +402,6 @@ module.exports = {
300
402
  buildOperatorIntelligencePack,
301
403
  summarizeOperatorIntelligencePack,
302
404
  buildOperatorObservabilityPack,
303
- summarizeOperatorObservabilityPack
405
+ summarizeOperatorObservabilityPack,
406
+ validateOperatorObservabilityBoundary
304
407
  };
@@ -11,6 +11,27 @@ const PRICING_BAND_UNITS = {
11
11
  trust_critical: 8
12
12
  };
13
13
 
14
+ const PRICING_EXPERIMENT_REJECTION_CODES = [
15
+ "pricing_sample_too_sparse",
16
+ "experiment_not_registered",
17
+ "experiment_not_allowed_now",
18
+ "operator_approval_missing",
19
+ "baseline_metrics_missing",
20
+ "rollback_trigger_thresholds_missing",
21
+ "public_checkout_copy_review_missing",
22
+ "post_experiment_review_window_missing",
23
+ "target_surface_mismatch"
24
+ ];
25
+
26
+ const PRICING_EXPERIMENT_LAUNCH_REQUIREMENTS = [
27
+ "operator_approval_recorded",
28
+ "sample_maturity_is_mature_live_signals",
29
+ "baseline_metrics_recorded_before_change",
30
+ "rollback_trigger_thresholds_recorded",
31
+ "public_checkout_copy_reviewed_for_accuracy",
32
+ "post_experiment_review_window_defined"
33
+ ];
34
+
14
35
  function normalizeAmount(value) {
15
36
  const amount = Number(value || 0);
16
37
  return Number.isFinite(amount) ? amount : 0;
@@ -466,6 +487,195 @@ function buildOptimizationRecommendations(state) {
466
487
  };
467
488
  }
468
489
 
490
+ function buildPricingExperimentPlan(state) {
491
+ const revenueSignals = buildRevenueSignalSummary(state);
492
+ const decisionGuardrail = buildPricingDecisionGuardrail(state);
493
+ const quoteCohorts = buildQuoteCohorts(state);
494
+ const sampleMaturity = revenueSignals.sample_maturity;
495
+ const experimentReady = decisionGuardrail.allow_price_change;
496
+ const candidateExperiments = [
497
+ {
498
+ experiment_id: "starter_api_pack_entry_price_test",
499
+ target_surface: "hosted_checkout_starter_api_pack",
500
+ hypothesis: "starter_entry_conversion_can_improve_without_degrading_refund_or_grant_success_rates",
501
+ required_segments: [
502
+ "first_purchase",
503
+ "hosted_checkout",
504
+ "starter_api_pack"
505
+ ],
506
+ primary_metrics: [
507
+ "checkout_paid_rate",
508
+ "external_credit_grant_success_rate",
509
+ "execution_completion_rate"
510
+ ],
511
+ rollback_triggers: [
512
+ "refund_or_chargeback_rate_increases",
513
+ "grant_failure_rate_increases",
514
+ "execution_completion_rate_decreases"
515
+ ],
516
+ allowed_now: experimentReady
517
+ },
518
+ {
519
+ experiment_id: "utility_minimum_floor_test",
520
+ target_surface: "utility_pricing_band_minimum_charge_floor",
521
+ hypothesis: "utility_quote_abandonment_can_be_reduced_without_training_users_to_expect_free_execution",
522
+ required_segments: [
523
+ "pricing_band.utility",
524
+ "quote_created",
525
+ "quote_accepted_or_expired"
526
+ ],
527
+ primary_metrics: [
528
+ "quote_conversion_rate",
529
+ "quote_abandonment_rate",
530
+ "revenue_capture_rate"
531
+ ],
532
+ rollback_triggers: [
533
+ "revenue_capture_rate_decreases",
534
+ "support_or_refund_signal_increases",
535
+ "paid_execution_quality_decreases"
536
+ ],
537
+ allowed_now: experimentReady
538
+ },
539
+ {
540
+ experiment_id: "trust_critical_premium_review",
541
+ target_surface: "trust_critical_premium_multiplier",
542
+ hypothesis: "high_consequence_work_may_support_higher_margin_when_conversion_is_already_strong",
543
+ required_segments: [
544
+ "pricing_band.trust_critical",
545
+ "converted_quotes",
546
+ "completed_transactions"
547
+ ],
548
+ primary_metrics: [
549
+ "trust_critical_conversion_rate",
550
+ "completed_paid_execution_count",
551
+ "refund_or_dispute_rate"
552
+ ],
553
+ rollback_triggers: [
554
+ "trust_critical_conversion_rate_decreases",
555
+ "refund_or_dispute_rate_increases",
556
+ "operator_quality_review_fails"
557
+ ],
558
+ allowed_now: experimentReady
559
+ }
560
+ ];
561
+ return {
562
+ experiment_plan_version: "xytara-pricing-experiment-plan-v1",
563
+ sample_maturity: sampleMaturity,
564
+ experiment_state: experimentReady ? "operator_reviewed_experiments_allowed" : "blocked_collect_more_live_data",
565
+ allow_experiment_launch: experimentReady,
566
+ required_minimum_live_sample: {
567
+ quote_count: 100,
568
+ payment_ledger_count: 20,
569
+ execution_transaction_count: 20,
570
+ refund_monitoring_required: true
571
+ },
572
+ current_evidence: {
573
+ counts: revenueSignals.counts,
574
+ rates: revenueSignals.rates,
575
+ pricing_band_cohorts: quoteCohorts.by_pricing_band
576
+ },
577
+ candidate_experiments: candidateExperiments,
578
+ launch_requirements: PRICING_EXPERIMENT_LAUNCH_REQUIREMENTS.slice(),
579
+ deterministic_rejection_codes: PRICING_EXPERIMENT_REJECTION_CODES.slice(),
580
+ launch_request_template: {
581
+ experiment_id: "starter_api_pack_entry_price_test",
582
+ target_surface: "hosted_checkout_starter_api_pack",
583
+ operator_approval_ref: "ops.pricing.approval.<date>",
584
+ baseline_metrics_ref: "ops.pricing.baseline.<date>",
585
+ rollback_trigger_thresholds: {
586
+ refund_or_chargeback_rate_increases: "operator_defined_threshold",
587
+ grant_failure_rate_increases: "operator_defined_threshold",
588
+ execution_completion_rate_decreases: "operator_defined_threshold"
589
+ },
590
+ public_checkout_copy_review_ref: "ops.pricing.copy_review.<date>",
591
+ post_experiment_review_window_days: 14
592
+ },
593
+ blocked_actions: experimentReady
594
+ ? []
595
+ : [
596
+ "start_price_ab_test_from_sparse_signals",
597
+ "change_public_package_prices_without_operator_approval",
598
+ "change_minimum_floors_without_baseline_metrics",
599
+ "change_auto_topup_thresholds_without_repeat_usage_data"
600
+ ],
601
+ allowed_actions: experimentReady
602
+ ? [
603
+ "prepare_small_operator_reviewed_price_experiment",
604
+ "record_baseline_and_rollback_thresholds",
605
+ "run_one_surface_at_a_time"
606
+ ]
607
+ : [
608
+ "collect_more_live_quote_payment_execution_and_refund_data",
609
+ "review_cohort_instrumentation",
610
+ "keep_current_public_prices_and_thresholds"
611
+ ],
612
+ boundary: "pricing_experiments_are_operator_reviewed_and_data_gated_not_automatic_price_changes"
613
+ };
614
+ }
615
+
616
+ function validatePricingExperimentLaunchRequest(plan, request) {
617
+ const pricingPlan = plan || {};
618
+ const launchRequest = request || {};
619
+ const candidates = Array.isArray(pricingPlan.candidate_experiments)
620
+ ? pricingPlan.candidate_experiments
621
+ : [];
622
+ const experimentId = launchRequest.experiment_id ? String(launchRequest.experiment_id) : "";
623
+ const candidate = candidates.find((entry) => entry && entry.experiment_id === experimentId) || null;
624
+ const rollbackThresholds = launchRequest.rollback_trigger_thresholds || {};
625
+ const missingRollbackThreshold = candidate
626
+ ? (Array.isArray(candidate.rollback_triggers) ? candidate.rollback_triggers : [])
627
+ .some((trigger) => !rollbackThresholds || !rollbackThresholds[trigger])
628
+ : true;
629
+ const reviewWindowDays = Number(launchRequest.post_experiment_review_window_days || 0);
630
+ const rejectionCodes = [];
631
+
632
+ if (pricingPlan.allow_experiment_launch !== true || pricingPlan.sample_maturity !== "mature_live_signals") {
633
+ rejectionCodes.push("pricing_sample_too_sparse");
634
+ }
635
+ if (!candidate) {
636
+ rejectionCodes.push("experiment_not_registered");
637
+ } else {
638
+ if (candidate.allowed_now !== true) rejectionCodes.push("experiment_not_allowed_now");
639
+ if (launchRequest.target_surface && launchRequest.target_surface !== candidate.target_surface) {
640
+ rejectionCodes.push("target_surface_mismatch");
641
+ }
642
+ }
643
+ if (!launchRequest.operator_approval_ref) rejectionCodes.push("operator_approval_missing");
644
+ if (!launchRequest.baseline_metrics_ref) rejectionCodes.push("baseline_metrics_missing");
645
+ if (missingRollbackThreshold) rejectionCodes.push("rollback_trigger_thresholds_missing");
646
+ if (!launchRequest.public_checkout_copy_review_ref) rejectionCodes.push("public_checkout_copy_review_missing");
647
+ if (!Number.isFinite(reviewWindowDays) || reviewWindowDays < 1) {
648
+ rejectionCodes.push("post_experiment_review_window_missing");
649
+ }
650
+
651
+ return {
652
+ validation_version: "xytara-pricing-experiment-launch-validation-v1",
653
+ launch_allowed: rejectionCodes.length === 0,
654
+ experiment_id: experimentId || null,
655
+ sample_maturity: pricingPlan.sample_maturity || null,
656
+ rejection_codes: Array.from(new Set(rejectionCodes)),
657
+ required_requirements: PRICING_EXPERIMENT_LAUNCH_REQUIREMENTS.slice(),
658
+ boundary: "pricing_launch_validation_requires_mature_samples_operator_approval_baseline_metrics_and_rollback_thresholds"
659
+ };
660
+ }
661
+
662
+ function summarizePricingExperimentPlan(state) {
663
+ const plan = buildPricingExperimentPlan(state);
664
+ return {
665
+ product: "xytara",
666
+ category: "machine-commerce-pricing-experiment-plan-summary",
667
+ summary_version: "xytara-pricing-experiment-plan-summary-v1",
668
+ sample_maturity: plan.sample_maturity,
669
+ experiment_state: plan.experiment_state,
670
+ allow_experiment_launch: plan.allow_experiment_launch,
671
+ candidate_experiment_count: plan.candidate_experiments.length,
672
+ blocked_action_count: plan.blocked_actions.length,
673
+ launch_requirement_count: plan.launch_requirements.length,
674
+ deterministic_rejection_code_count: plan.deterministic_rejection_codes.length,
675
+ boundary: plan.boundary
676
+ };
677
+ }
678
+
469
679
  function buildPricingOptimizationPack(state) {
470
680
  const minimumChargeFloors = buildMinimumChargeFloors();
471
681
  const starterPricing = buildStarterPricing();
@@ -476,6 +686,7 @@ function buildPricingOptimizationPack(state) {
476
686
  const revenueSignalSummary = buildRevenueSignalSummary(state);
477
687
  const pricingDecisionGuardrail = buildPricingDecisionGuardrail(state);
478
688
  const packageThresholdRationale = buildPackageThresholdRationale(state);
689
+ const pricingExperimentPlan = buildPricingExperimentPlan(state);
479
690
  return {
480
691
  product: "xytara",
481
692
  category: "machine-commerce-pricing-optimization-pack",
@@ -501,6 +712,7 @@ function buildPricingOptimizationPack(state) {
501
712
  revenue_signal_summary: revenueSignalSummary,
502
713
  pricing_decision_guardrail: pricingDecisionGuardrail,
503
714
  package_threshold_rationale: packageThresholdRationale,
715
+ pricing_experiment_plan: pricingExperimentPlan,
504
716
  optimization_recommendations: recommendations,
505
717
  data_dependent_tuning: [
506
718
  "conversion_test_starter_pack_pricing",
@@ -516,7 +728,9 @@ function buildPricingOptimizationPack(state) {
516
728
  quote_cohorts_ref: "/v1/pricing-optimization/quote-cohorts",
517
729
  recommendations_ref: "/v1/pricing-optimization/recommendations",
518
730
  revenue_signal_summary_ref: "/v1/pricing-optimization/revenue-signal-summary",
519
- decision_guardrail_ref: "/v1/pricing-optimization/decision-guardrail"
731
+ decision_guardrail_ref: "/v1/pricing-optimization/decision-guardrail",
732
+ experiment_plan_ref: "/v1/pricing-optimization/experiment-plan",
733
+ experiment_plan_summary_ref: "/v1/pricing-optimization/experiment-plan/summary"
520
734
  }
521
735
  };
522
736
  }
@@ -541,6 +755,8 @@ function summarizePricingOptimizationPack(state) {
541
755
  refund_count: pack.revenue_signal_summary.counts.refund_count,
542
756
  execution_transaction_count: pack.revenue_signal_summary.counts.execution_transaction_count,
543
757
  recommendation_count: pack.optimization_recommendations.recommendations.length,
758
+ candidate_experiment_count: pack.pricing_experiment_plan.candidate_experiments.length,
759
+ allow_experiment_launch: pack.pricing_experiment_plan.allow_experiment_launch,
544
760
  linked_surfaces: pack.supported_surfaces
545
761
  };
546
762
  }
@@ -552,5 +768,8 @@ module.exports = {
552
768
  buildQuoteCohorts,
553
769
  buildRevenueSignalSummary,
554
770
  buildPricingDecisionGuardrail,
555
- buildOptimizationRecommendations
771
+ buildOptimizationRecommendations,
772
+ buildPricingExperimentPlan,
773
+ summarizePricingExperimentPlan,
774
+ validatePricingExperimentLaunchRequest
556
775
  };