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.
Extract ExtendGrantTx, RevokeGrantTx, and ExpireGrantTx into
internal/entitlements so the operator handlers, expiry activity, and
demo seed all run the same conferral path. The seed plays a five-grant
history on Carlos's org; pin-timestamps now uses dense-rank offsets
and transition list ordering ties break by transition_id.
Introduce a commercial license option alongside AGPL-3.0-only, require a
CLA for contributors, and document the terms in COMMERCIAL.md and
NOTICE. Add a script to stamp SPDX headers on Go files and apply it
across the tree.
Rebuild every operator, member, and integration template on the shared
page-anatomy parts, empty the lint allowlist, and move the confirm modal
into both shells. Replace back links with location trails and make row
identifiers the primary link. Make screen captures deterministic by
resetting the app database from a pinned demo snapshot. Send the logout
id_token_hint only while valid.
Move settled investigation records into
`status/explorations/<topic>-<date>/` with README indexes,
promote durable conventions to `docs/`, and add
`docs/identifiers.md` plus upstream Doc 46 person-name
provenance documents. Update cross-references and archive
resolved issues.
Replace the entity slugs on organizations, workspaces, resource pools,
and
plan ladders with nullable `key` columns and add keys to products,
prices,
and entitlement sets. Rename `providers.slug` to `provider` and add
partial
unique indexes for system and org role names.
Assign invoice numbers per billing account from a gapless transactional
counter; Stripe's number moves to the invoice mapping as an external
reference.
Seeds, fixtures, and the operator lookup address rows by key, and the
returning-login resync no longer blanks a display name when the IdP
sends
no `name` claim.
Configure the personal org-type default ladder and floor vacant pools so
the seeded dataset survives grant revocation. Let the modal mechanics
test
fall back to plan-ladder action triggers in full-suite order.
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.
Replace product-kind branching and direct position writes with enclosed
database functions driven by structural product shape.
Migrate grant and provision data, unify operator issuance, update
subscription and expiry flows, and add migration and integration proofs.
Squash the pre-production migration history into fresh core, fedwiki,
and stripe baselines and reduce the canonical source list to those
three streams.
Update sqlc configs, generated queries, raw SQL, tests, and docs while
keeping provider tables schema-qualified.
BREAKING: existing local database volumes must be wiped because goose
version history restarts from the new baselines.
Fix the rank-collision UX bug at the source.
- Append-at-end: CreatePlanLadderTier INSERT uses COALESCE(MAX(rank)+1, 0);
handler drops the rank input and template shows a drag-to-position hint.
- Renumber-on-delete: DeletePlanLadderTier renumbers remaining tiers inline
so ranks stay contiguous after deletion.
- Confirm dialog copy is now honest: deleting the top tier explicitly
promotes the next tier to the new-org default.
- Friendly errors: map duplicate-product and rank-collision constraints
to 422 field errors via web.FieldErrorsFromDB.
- All fixtures updated for append-at-end order.
Closes: openspec/changes/ladder-tier-append
Add a `seed-demo` subcommand (cmd/seed_demo.go + internal/demoseed/)
that inserts a fixed reference catalog into the member-console DB so
the operator panel has rows to walk through for UX research. Catalog:
6 demo-* products (4 by product_type + 2 extra plans), 1 plan ladder
with 3 tiers, 1 entitlement set with 2 rules (limit + boolean), and 1
grant on Alice's personal org (once she's logged in).
Person rows for bob/carlos/diana are seeded via provisioning.AutoProvision
keyed on the pinned Keycloak UUIDs — no longer fragile now that
seed-keycloak.sh's partialImport switch preserves the pinned id. Alice
is intentionally NOT pre-seeded so the lazy-creation OIDC flow stays
exercised on every fresh stack.
Idempotency: list+filter by `name` for catalog rows, by `oidc_subject`
for persons. Re-running is a no-op for created rows; warns + skips
the grant if alice hasn't logged in yet.
Host-side invocation only — run `./test/seed-demo.sh` after
bootstrap-stack.sh. Mirrors how member-console itself runs on the
host (config + secrets live under test/).
Two OpenSpec changes folded in: 2026-05-10-member-console-demo-seeder
(the seeder) and 2026-05-11-demo-seeder-persons (the persons follow-up
unlocked by the Keycloak fix).