Files
member-console/internal/integration/contract.go
T
cgalo5758 3727ff31d8 Add entitlement set rule change ledger and preview flow
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.
2026-09-15 03:53:28 -05:00

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"
)