// @bun
var{defineProperty:J,getOwnPropertyNames:Y,getOwnPropertyDescriptor:Z}=Object,_=Object.prototype.hasOwnProperty;function $(b){return this[b]}var x=(b)=>{var j=(W??=new WeakMap).get(b),z;if(j)return j;if(j=J({},"__esModule",{value:!0}),b&&typeof b==="object"||typeof b==="function"){for(var A of Y(b))if(!_.call(j,A))J(j,A,{get:$.bind(b,A),enumerable:!(z=Z(b,A))||z.enumerable})}return W.set(b,j),j},W;var h=(b)=>b;function I(b,j){this[b]=h.bind(null,j)}var K=(b,j)=>{for(var z in j)J(b,z,{get:j[z],enumerable:!0,configurable:!0,set:I.bind(j,z)})};var O=(b,j)=>()=>(b&&(j=b(b=0)),j);var C={};K(C,{groupByToArray:()=>R,groupByMultiple:()=>T,groupBy:()=>H,getUniqueTags:()=>N,getUniqueOwners:()=>U,getUniqueDomains:()=>V,filterBy:()=>M,GroupingStrategies:()=>L});function M(b,j){return b.filter((z)=>{if(j.tags?.length){if(!j.tags.some((D)=>z.meta.tags?.includes(D)))return!1}if(j.owners?.length){if(!j.owners.some((D)=>z.meta.owners?.includes(D)))return!1}if(j.stability?.length){if(!j.stability.includes(z.meta.stability??"stable"))return!1}if(j.domain){if(L.byDomain(z)!==j.domain)return!1}if(j.keyPattern){let A=z.meta.key??z.meta.key??"",D=j.keyPattern.replace(/\*/g,".*").replace(/\?/g,".");if(!new RegExp(`^${D}$`,"i").test(A))return!1}return!0})}function H(b,j){let z=new Map;for(let A of b){let D=j(A),E=z.get(D);if(E)E.push(A);else z.set(D,[A])}return z}function R(b,j){let z=H(b,j);return Array.from(z.entries()).map(([A,D])=>({key:A,items:D}))}function T(b,j){let z=new Map;for(let A of b){let D=j(A);for(let E of D){let Q=z.get(E);if(Q)Q.push(A);else z.set(E,[A])}}return z}function N(b){let j=new Set;for(let z of b)for(let A of z.meta.tags??[])j.add(A);return Array.from(j).sort()}function U(b){let j=new Set;for(let z of b)for(let A of z.meta.owners??[])j.add(A);return Array.from(j).sort()}function V(b){let j=new Set;for(let z of b)j.add(L.byDomain(z));return Array.from(j).sort()}var L;var X=O(()=>{L={byTag:(b)=>b.meta.tags?.[0]??"untagged",byAllTags:(b)=>b.meta.tags?.length?b.meta.tags:["untagged"],byOwner:(b)=>b.meta.owners?.[0]??"unowned",byDomain:(b)=>{return(b.meta.key??b.meta.key??"").split(".")[0]??"default"},byStability:(b)=>b.meta.stability??"stable",byUrlPath:(b)=>(j)=>{if(!j.path)return"root";return j.path.split("/").filter(Boolean).slice(0,b).join("/")||"root"}}});X();import{compareVersions as q}from"compare-versions";var B=(b)=>`${b.meta.key}.v${b.meta.version}`;class P{contractType;items=new Map;constructor(b,j){this.contractType=b;if(j)j.forEach((z)=>this.register(z))}register(b){let j=B(b);if(this.items.has(j))throw Error(`Duplicate contract \`${this.contractType}\` ${j}`);return this.items.set(j,b),this}count(){return this.items.size}list(){return[...this.items.values()]}get(b,j){if(j!=null)return this.items.get(`${b}.v${j}`);let z;for(let[A,D]of this.items.entries()){if(!A.startsWith(`${b}.v`))continue;if(!z||q(D.meta.version,z.meta.version)>0)z=D}return z}has(b,j){return!!this.get(b,j)}filter(b){return M(this.list(),b)}listByTag(b){return this.list().filter((j)=>j.meta.tags?.includes(b))}listByOwner(b){return this.list().filter((j)=>j.meta.owners?.includes(b))}groupBy(b){return H(this.list(),b)}getUniqueTags(){return N(this.list())}}var G=(b)=>`${b.key}.v${b.version}`;class S extends P{constructor(b){super("experiment",b)}}function c(b){return G(b)}var k=[{id:"docs.tech.contracts.experiments",title:"ExperimentSpec & ExperimentEvaluator",summary:"Use experiments to test alternative workflows, data views, or themes with controlled allocations and measurable outcomes.",kind:"reference",visibility:"public",route:"/docs/tech/contracts/experiments",tags:["tech","contracts","experiments"],body:`# ExperimentSpec & ExperimentEvaluator

Use experiments to test alternative workflows, data views, or themes with controlled allocations and measurable outcomes.

- Types & registry: \`packages/libs/contracts/src/experiments/spec.ts\`
- Runtime evaluator: \`packages/libs/contracts/src/experiments/evaluator.ts\`
- CLI wizard/template: \`contractspec create experiment\`

## Structure

\`\`\`ts
export interface ExperimentSpec {
  meta: ExperimentMeta;
  controlVariant: string;
  variants: ExperimentVariant[];
  allocation: AllocationStrategy;
  successMetrics?: SuccessMetric[];
}
\`\`\`

- \`variants\`: define UI/behavior overrides (data views, workflows, themes, policies)
- \`allocation\`:
  - \`random\`: 50/50 or weighted via \`weight\`
  - \`sticky\`: deterministic hash on user/organization/session
  - \`targeted\`: rule-based (policy + expression + optional percentage)
- \`successMetrics\`: telemetry events + aggregation (count/avg/p95) to track outcomes

### Example

\`\`\`ts
export const OnboardingSplitFormExperiment: ExperimentSpec = {
  meta: {
    name: 'sigil.onboarding.split_form',
    version: '1.0.0',
    title: 'Split onboarding form',
    description: 'Compare single vs multi-step onboarding',
    domain: 'onboarding',
    owners: ['@team.onboarding'],
    tags: ['experiment'],
    stability: StabilityEnum.Experimental,
  },
  controlVariant: 'control',
  variants: [
    { id: 'control', name: 'Single-step form' },
    {
      id: 'multi_step',
      name: 'Multi-step form',
      overrides: [
        { type: 'workflow', target: 'sigil.onboarding.workflow.multi_step' },
      ],
    },
  ],
  allocation: {
    type: 'targeted',
    rules: [
      {
        variantId: 'multi_step',
        expression: "context.attributes?.segment === 'vip'",
      },
    ],
    fallback: 'random',
  },
  successMetrics: [
    {
      name: 'Completion rate',
      telemetryEvent: { name: 'sigil.telemetry.onboarding_completed', version: '1.0.0' },
      aggregation: 'count',
    },
  ],
};
\`\`\`

## Variant evaluation

\`\`\`ts
const evaluator = new ExperimentEvaluator({
  registry: experimentRegistry,
  policyChecker: (policyRef, context) =>
    policyEngine.decide({
      action: 'experiment_targeting',
      subject: { roles: context.flags },
      resource: { type: 'experiment', attributes: { name: context.experiment } },
      policies: [policyRef],
    }).effect === 'allow',
});

const assignment = await evaluator.chooseVariant({
  experiment: 'sigil.onboarding.split_form',
  userId: ctx.userId,
  organizationId: ctx.organizationId,
  attributes: ctx.attributes,
});

if (assignment) {
  // Apply overrides for the chosen variant
  applyExperimentOverrides(assignment.variant);
}
\`\`\`

- \`random\` uses deterministic hashing (\`salt\` optional) for stable splits
- \`sticky\` hashes a specified attribute (userId/orgId/sessionId)
- \`targeted\` evaluates rules in order; each rule can reference a PolicySpec and simple JS expressions (\`context\` input)

## Integrations

- **Feature modules**: \`FeatureModuleSpec.experiments\` references experiments owned by a module
- **DataViewSpec / WorkflowSpec**: \`experiments?: ExperimentRef[]\` indicate which experiments modify the spec
- **Telemetry**: success metrics reference \`TelemetrySpec\` events to ensure compliant tracking
- **Policy**: targeting rules call into \`PolicyEngine\` via the evaluator callback to respect privacy/security

## CLI workflow

\`\`\`
contractspec create experiment
\`\`\`

- Prompts for control/variants, allocation strategy, targeting rules, success metrics
- Outputs a typed \`ExperimentSpec\` file alongside your contracts

## Best practices

1. Keep experiments short-lived; increment \`meta.version\` when changing allocation or variants.
2. Always declare a control variant and ensure overrides are reversible.
3. Tie success metrics to privacy-reviewed telemetry events.
4. Use targeting rules sparingly; combine with PolicySpec to avoid exposing experiments to unauthorized users.
5. When an experiment wins, promote the variant to the canonical spec and retire the experiment.

`}];export{k as tech_contracts_experiments_DocBlocks,c as makeExperimentKey,S as ExperimentRegistry};
