Past tier changes are named by the ladder's current order, so a reorder or a tier removal rewrites what they say #211

Open
opened 2026-10-09 16:03:04 +00:00 by cgalo5758 · 0 comments
Owner

The question

Should a past tier change name the product it moved at the time, or whatever product sits at that rank on the ladder today?

A plan ladder is an ordered list of tiers, each one a product. A tier's rank is its place in that order, counting from 0. A tier change stores ranks, not products (from_rank and to_rank in core.pool_provision_transitions). Two pages look each rank up in the ladder as it is now: the organization page's Tier changes list (the tierLabel closure in loadOrgEnrollmentData, internal/server/operator_enrollment.go) and the landing page's Recent activity (tierNameResolver, internal/server/operator_activity_vocab.go). The Tier changes requirement in openspec/specs/plan-enrollment-administration/spec.md asks for exactly this lookup. When a rank no longer exists it shows "rank N", and a help tooltip says names follow the ladder's current shape. Three things follow from it:

  • A reorder renames history. An organization on a ladder of Basic then Pro upgrades from Basic to Pro. The ladder is then reordered to Pro then Basic, and the row now reads "Upgraded: Pro to Basic". Removing any tier but the last does the same to every tier above it, because the removal renumbers the rest. The ranks still exist, so nothing falls back to "rank N" and nothing marks the row as changed.
  • The two specs disagree on the fallback. On a ladder of Basic, Plus, Max, removing Max turns a row that named Max into "Started: rank 2" on both pages. Tier changes requires that text. The Recent activity requirement in openspec/specs/operator-panel-navigation/spec.md says tier events use the Tier changes wording and never show an integer rank, and Recent activity has no tooltip giving the caveat.
  • The end rows a removal writes name nothing. CommitPlanLadderTierRemoval (internal/server/operator_plan_ladders.go) deletes and renumbers the tiers before it ends the holders' positions. core.align_conferral_shape and core.end_conferral then look up the ended rank in the tiers table, find no row and write from_rank as NULL. Tier changes shows Ended with "—" in both From and To, and Recent activity shows "Ended: —".

What depends on the answer

  • Both specs, and what the Tier changes tooltip promises.
  • The name lookup the two pages will share once their per-row reads become batch reads (#10). It keeps whichever rule wins.
  • #179. Option 2 below needs the id that ties together the rows one act writes.

Options known so far

  1. Keep today's names and make them consistent. The two specs settle on one fallback, both pages carry the caveat, and a tier removal records the rank of the tier it ends. Smallest change, but editing the catalog still rewrites history.
  2. Name each side by the position the change placed. Every row written since transitions gained provision_ladder_id points at a position record in core.pool_provision_ladders, and that record's product_id is the product at the time. This names the To side of a Started, Resumed, Extended, Upgraded or Downgraded row, and the From side of an Ended row, including the end rows a removal writes. The From side of an upgrade or downgrade is the replaced position. Today the code reaches it only by pairing the row with that act's Ended row by pool, instant, actor and reason, and #179 wants that pairing gone. Rows from before the column keep rank names.
  3. Store the products on the change. The conferral functions write the from and to products (or position ids) next to the ranks when they write the row. Both sides name the product at the time, without pairing and without #179. Existing rows need a backfill or a fallback, and every writer changes.

Decided when

Both specs state one rule for naming a past change, with scenarios for a reorder, the removal of a middle tier and the end rows a removal writes, and they agree on what a side with no name shows.

## The question Should a past tier change name the product it moved at the time, or whatever product sits at that rank on the ladder today? A plan ladder is an ordered list of tiers, each one a product. A tier's rank is its place in that order, counting from 0. A tier change stores ranks, not products (`from_rank` and `to_rank` in `core.pool_provision_transitions`). Two pages look each rank up in the ladder as it is now: the organization page's Tier changes list (the `tierLabel` closure in `loadOrgEnrollmentData`, `internal/server/operator_enrollment.go`) and the landing page's Recent activity (`tierNameResolver`, `internal/server/operator_activity_vocab.go`). The Tier changes requirement in `openspec/specs/plan-enrollment-administration/spec.md` asks for exactly this lookup. When a rank no longer exists it shows "rank N", and a help tooltip says names follow the ladder's current shape. Three things follow from it: - **A reorder renames history.** An organization on a ladder of Basic then Pro upgrades from Basic to Pro. The ladder is then reordered to Pro then Basic, and the row now reads "Upgraded: Pro to Basic". Removing any tier but the last does the same to every tier above it, because the removal renumbers the rest. The ranks still exist, so nothing falls back to "rank N" and nothing marks the row as changed. - **The two specs disagree on the fallback.** On a ladder of Basic, Plus, Max, removing Max turns a row that named Max into "Started: rank 2" on both pages. Tier changes requires that text. The Recent activity requirement in `openspec/specs/operator-panel-navigation/spec.md` says tier events use the Tier changes wording and never show an integer rank, and Recent activity has no tooltip giving the caveat. - **The end rows a removal writes name nothing.** `CommitPlanLadderTierRemoval` (`internal/server/operator_plan_ladders.go`) deletes and renumbers the tiers before it ends the holders' positions. `core.align_conferral_shape` and `core.end_conferral` then look up the ended rank in the tiers table, find no row and write `from_rank` as NULL. Tier changes shows Ended with "—" in both From and To, and Recent activity shows "Ended: —". ## What depends on the answer - Both specs, and what the Tier changes tooltip promises. - The name lookup the two pages will share once their per-row reads become batch reads (#10). It keeps whichever rule wins. - #179. Option 2 below needs the id that ties together the rows one act writes. ## Options known so far 1. **Keep today's names and make them consistent.** The two specs settle on one fallback, both pages carry the caveat, and a tier removal records the rank of the tier it ends. Smallest change, but editing the catalog still rewrites history. 2. **Name each side by the position the change placed.** Every row written since transitions gained `provision_ladder_id` points at a position record in `core.pool_provision_ladders`, and that record's `product_id` is the product at the time. This names the To side of a Started, Resumed, Extended, Upgraded or Downgraded row, and the From side of an Ended row, including the end rows a removal writes. The From side of an upgrade or downgrade is the replaced position. Today the code reaches it only by pairing the row with that act's Ended row by pool, instant, actor and reason, and #179 wants that pairing gone. Rows from before the column keep rank names. 3. **Store the products on the change.** The conferral functions write the from and to products (or position ids) next to the ranks when they write the row. Both sides name the product at the time, without pairing and without #179. Existing rows need a backfill or a fallback, and every writer changes. ## Decided when Both specs state one rule for naming a past change, with scenarios for a reorder, the removal of a middle tier and the end rows a removal writes, and they agree on what a side with no name shows.
cgalo5758 added the
kind
design
area/entitlementsarea/operator-ui
labels 2026-10-09 16:03:04 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: wiki-cafe/member-console#211