Classify resource keys as boolean or numeric, adapt the operator form to the selected key, and derive rule types server-side to prevent wrong-shape rules.
8.2 KiB
entitlement-sets
Purpose
Defines the entitlement set abstraction that decouples capability specification from commercial packaging. Entitlement sets are named, reusable collections of entitlement rules that products and grants reference to declare what capabilities they confer.
Requirements
Requirement: Entitlement sets as named capability collections
The system SHALL maintain an entitlement_sets table where each row represents a named, reusable collection of entitlement rules. Each entitlement set SHALL have a set_id (UUID), name (VARCHAR(255)), description (TEXT), and is_active (BOOLEAN). Entitlement sets decouple capability specification from commercial packaging, but conferral SHALL flow exclusively through products: an entitlement set SHALL be reference-able only by a product's entitlement_set_id, including an internal wrap product minted solely to confer the set (see product-management, entitlement-conferral), and SHALL NOT be referenced directly by a grant. An entitlement set with is_active = false SHALL NOT be assignable to new products, but existing references SHALL remain valid.
Scenario: Create an entitlement set
- WHEN an operator creates an entitlement set with name "FedWiki Capabilities"
- THEN the system SHALL create an
entitlement_setsrecord withis_active = true - AND the set SHALL be available for reference by products, including internal wrap products minted solely to confer the set
Scenario: Deactivated set remains valid for existing references
- WHEN an entitlement set is deactivated (
is_active = false) - THEN existing products referencing that set SHALL continue to function
- AND existing pool provisions resolved to that set SHALL continue to materialize
Requirement: Entitlement set rules define capabilities within a set
The system SHALL maintain an entitlement_set_rules table where each row defines a single capability within an entitlement set. Each rule SHALL reference a set_id (FK to entitlement_sets) and have a rule_type discriminator. rule_type = 'limit' and rule_type = 'boolean' are supported; quota and credit remain unmaterialized. Each limit rule SHALL have a resource_key (FK to resource_keys), resource_value (BIGINT), resource_per_unit (BOOLEAN), and stacking_policy (one of additive, maximum, replace). Each boolean rule SHALL have a resource_key and NULL resource_value, resource_per_unit, and stacking_policy.
Scenario: Limit rule within a set
- WHEN an entitlement set has a limit rule with
resource_key = 'sites',resource_value = 5,resource_per_unit = false,stacking_policy = 'additive' - THEN any pool provision resolved to this set SHALL contribute 5 sites to the pool's numeric entitlements
Scenario: Per-unit limit rule within a set
- WHEN an entitlement set has a limit rule with
resource_key = 'sites',resource_value = 1,resource_per_unit = true,stacking_policy = 'additive' - AND a pool provision resolved to this set has
quantity = 3 - THEN the contributed value SHALL be 3 (1 x 3)
Scenario: Boolean rule within a set
- WHEN an entitlement set has a boolean rule with
resource_key = 'discourse_posting' - THEN any active pool provision resolved to this set SHALL cause the pool's
boolean_entitlementsrow for that key to begranted = true
Scenario: Rule type CHECK constraint
- WHEN a limit rule is inserted into
entitlement_set_rules - THEN
resource_keyandresource_valueSHALL NOT be NULL - AND
credit_amountandcredit_currencySHALL be NULL
Scenario: Boolean rule CHECK constraint
- WHEN a boolean rule is inserted into
entitlement_set_rules - THEN
resource_keySHALL NOT be NULL - AND
resource_value,stacking_policy,resource_per_unit,credit_amount, andcredit_currencySHALL be NULL
Requirement: Seed entitlement sets for existing products
The system SHALL seed entitlement sets via migration corresponding to each seed product:
- A "FedWiki Capabilities" set with a
limitrule forresource_key = 'sites',resource_value = 5,resource_per_unit = false,stacking_policy = 'additive'. - A "Site Credit Capabilities" set with a
limitrule forresource_key = 'sites',resource_value = 1,resource_per_unit = true,stacking_policy = 'additive'.
Scenario: Fresh database has seed entitlement sets
- WHEN migrations have run on a fresh database
- THEN the
entitlement_setstable SHALL contain a "FedWiki Capabilities" set withis_active = true - AND the
entitlement_setstable SHALL contain a "Site Credit Capabilities" set withis_active = true - AND each set SHALL have its corresponding
entitlement_set_rulesrows
Requirement: Every pool provision resolves to exactly one entitlement set
The system SHALL require that every pool_provisions record has a non-NULL entitlement_set_id (FK to entitlement_sets). The entitlement_set_id SHALL be resolved at provision-creation time and SHALL NOT change after creation. The materialization pipeline SHALL use pool_provisions.entitlement_set_id as the sole key for looking up rules. Entitlement sets decouple capability specification from commercial packaging, but conferral flows exclusively through products: a grant SHALL NOT reference an entitlement set directly, and pool_provisions.entitlement_set_id SHALL always be resolved from the conferring product's entitlement_set_id (see entitlement-conferral). Ad hoc conferral of a raw entitlement set, with no ordinary commercial product behind it, SHALL be accomplished by minting an internal wrap product whose entitlement_set_id names the set — never by a grant naming the set directly (see product-management, entitlement-conferral).
Scenario: Provision from a product-based grant
- WHEN a grant references a product with
entitlement_set_id = X - THEN the pool provision created for this grant SHALL have
entitlement_set_id = X
Scenario: Ad hoc set conferral is mediated by a wrap product
- WHEN an operator needs to confer a raw entitlement set that has no ordinary commercial product behind it
- THEN the operator SHALL mint an internal wrap product (
is_public = FALSE, published, priceless) whoseentitlement_set_idnames the set - AND the grant SHALL name that wrap product's
product_id - AND the pool provision created for the grant SHALL have
entitlement_set_idresolved from the wrap product, exactly as for any other product-backed conferral
Scenario: A grant SHALL NOT reference an entitlement set directly
- WHEN a grant is created
- THEN the grant SHALL name a
product_id - AND the grant SHALL NOT carry an
entitlement_set_idcolumn - AND no code path SHALL resolve a pool provision's
entitlement_set_idfrom a grant-supplied set reference rather than from the conferring product
Scenario: Provision entitlement_set_id is immutable after creation
- WHEN a pool provision exists with
entitlement_set_id = X - AND the referenced product's
entitlement_set_idis changed to Z - THEN the pool provision's
entitlement_set_idSHALL remain X
Requirement: Resource keys declare a kind
The system SHALL record a shape discriminator on core.resource_keys: kind VARCHAR(20) NOT NULL DEFAULT 'numeric' with CHECK (kind IN ('boolean', 'numeric')). boolean keys are conferred by boolean rules and consumed via boolean_entitlements; numeric keys are conferred by limit (and, when materialized, quota) rules and consumed via numeric_entitlements. Migrations and seeds that create resource-key rows SHALL declare kind explicitly.
Scenario: Boolean key is marked boolean
- WHEN migrations have run on a fresh database with the discourse stream enabled
- THEN the
core.resource_keysrow fordiscourse_postingSHALL havekind = 'boolean'
Scenario: Numeric key defaults to numeric
- WHEN migrations have run on a fresh database
- THEN the
core.resource_keysrow forfedwiki_sitesSHALL havekind = 'numeric'
Scenario: Invalid kind is rejected
- WHEN a row is inserted into
core.resource_keyswithkind = 'quota' - THEN the insert SHALL fail with a CHECK constraint violation