Add an append-only ledger of entitlement set rule changes with per-pool effect rows, a preview-and-commit rule change flow, and an automatic drain that settles deferred recomputations. Rules gain a tier reduction policy, resource keys declare over-limit behavior, and the materializer now lowers limits when a rule stops applying. Add entitlement set rule change ledger and preview flow Add an append-only ledger of entitlement set rule changes with a preview-and-commit operator flow. Rule writes now go through an enclosed `core.commit_rule_change` function that files an act row and one obligation per carrying pool, with a drain workflow settling deferred recomputations. The preview dry-runs the materializer with a rule overlay and renders per-pool buckets, reduction-policy disclosures, and provider over-limit consequences. Materializing transactions take a shared advisory rendezvous that rule changes hold exclusively, enforced by a possession assertion. Add History and Entitlement changes surfaces, a rule-less warning on five product-selection surfaces, and a `tier_reduction_policy` column that gates FedWiki parking.
113 lines
4.9 KiB
Go
113 lines
4.9 KiB
Go
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Commercial
|
|
// SPDX-FileCopyrightText: 2025-2026 Christian Galo
|
|
|
|
package integration
|
|
|
|
// ProviderKind discriminates providers in the registry. The operator
|
|
// "Integrations" surface is exactly the KindProvisioning subset; a KindPayment
|
|
// provider (Stripe) is registered but never appears there.
|
|
type ProviderKind string
|
|
|
|
const (
|
|
KindPayment ProviderKind = "payment"
|
|
KindProvisioning ProviderKind = "provisioning"
|
|
KindNotification ProviderKind = "notification"
|
|
KindTax ProviderKind = "tax"
|
|
)
|
|
|
|
// Operation is a lifecycle verb a provider may support. The mutating verbs
|
|
// (OpCreate / OpSetStatus / OpDelete) are dispatched provider-side — the
|
|
// transport is per-provider (Temporal for FedWiki, core.outbox for
|
|
// Stripe), not mandated by the contract. The read class (OpList/OpDescribe)
|
|
// lets the platform enumerate a provider's instances to reconcile the observed
|
|
// status projection. OpSetStatus moves an instance between the provider's
|
|
// declared States; OpDelete is terminal and distinct from the state machine.
|
|
type Operation string
|
|
|
|
const (
|
|
OpCreate Operation = "create"
|
|
OpSetStatus Operation = "set_status"
|
|
OpDelete Operation = "delete"
|
|
OpList Operation = "list"
|
|
OpDescribe Operation = "describe"
|
|
)
|
|
|
|
// State is a lifecycle state a provider's instance can occupy. `active` is the
|
|
// implicit baseline every provisioning provider supports; a provider that
|
|
// declares OpSetStatus also declares the non-active states it supports, and
|
|
// set_status moves an instance between them. These constants are the known
|
|
// values, but the supported set is provider-declared (see Manifest.States).
|
|
type State string
|
|
|
|
const (
|
|
StateActive State = "active"
|
|
StateReadonly State = "readonly"
|
|
StateArchived State = "archived"
|
|
)
|
|
|
|
// Manifest is the capability declaration a provider contributes at boot. It is
|
|
// the single source of truth for a provider's kind, the lifecycle operations it
|
|
// supports, and the resource keys it owns. The registry persists it into
|
|
// core.providers / core.provider_operations and stamps
|
|
// core.resource_keys.provider for owned keys.
|
|
//
|
|
// Operational/commercial state (status, plan-ladder bindings, enablement) is NOT
|
|
// part of the manifest — that is DB-canonical and operator-editable, and a boot
|
|
// re-registration must not clobber it.
|
|
type Manifest struct {
|
|
Key string // ^[a-z0-9]+$ (no underscores); see validateManifest
|
|
Kind ProviderKind // one of the ProviderKind constants
|
|
DisplayName string // human label for the registry / operator surface
|
|
Operations []Operation // verbs this provider implements
|
|
States []State // lifecycle states it supports; requires OpSetStatus, `active` implicit
|
|
ResourceKeys []string // bare keys it owns; each MUST be "<key>_…"-prefixed
|
|
// OverLimit declares, per owned resource key, what the provider does with
|
|
// usage above a lowered limit and the one sentence a rule-change preview
|
|
// renders for it (Decision 147). A key with no declaration denies new use
|
|
// and nothing else. The registry stamps the declaration together with the
|
|
// key's provider, because the platform_keys_deny_new CHECK requires a
|
|
// provider on any key that is not deny_new.
|
|
OverLimit map[string]OverLimit
|
|
|
|
// OperatorSurfacePath is the operator admin-surface URL for this provider,
|
|
// rendered in the operator sidebar's Integration group. Empty for providers
|
|
// with no operator surface (e.g. payment providers).
|
|
OperatorSurfacePath string
|
|
|
|
// Description is an optional one-sentence lead for the provider's own
|
|
// page (its admin surface when it declares one, otherwise its settings
|
|
// page) — operator-panel-navigation: "A provider page leads with the
|
|
// integration's description". "" renders no lead override.
|
|
Description string
|
|
}
|
|
|
|
// ProviderSource is implemented by each provider package (e.g. fedwiki, stripe)
|
|
// and registered in boot order, parallel to db.MigrationSource. The returned
|
|
// Manifest is validated and persisted by RegisterProviders at boot.
|
|
type ProviderSource interface {
|
|
ProviderManifest() Manifest
|
|
}
|
|
|
|
// OverLimit is a provider's declaration for one resource key: the behavior
|
|
// usage above a lowered limit meets under a force_reduce rule, and the
|
|
// sentence the console renders verbatim wherever it discloses it.
|
|
type OverLimit struct {
|
|
Behavior OverLimitBehavior
|
|
Consequence string
|
|
}
|
|
|
|
// OverLimitBehavior is what a provider does with usage above a lowered limit
|
|
// when the governing rule's reduction policy is force_reduce.
|
|
type OverLimitBehavior string
|
|
|
|
const (
|
|
// OverLimitDenyNew refuses new use above the limit and touches nothing
|
|
// that exists; it is the default and the only value a platform key may
|
|
// carry.
|
|
OverLimitDenyNew OverLimitBehavior = "deny_new"
|
|
// OverLimitPark sets the excess aside, retained and recoverable.
|
|
OverLimitPark OverLimitBehavior = "park"
|
|
// OverLimitReclaim takes the excess back.
|
|
OverLimitReclaim OverLimitBehavior = "reclaim"
|
|
)
|