backfill subscriptions onto plan prices - #782
Closed
mitchelljkotler wants to merge 6 commits into
Closed
Conversation
mitchelljkotler
force-pushed
the
pricing-models
branch
from
August 27, 2026 13:03
df41037 to
8a26f70
Compare
mitchelljkotler
force-pushed
the
pricing-models-backfill
branch
from
August 27, 2026 13:03
6b6b8fe to
a1b18c9
Compare
mitchelljkotler
temporarily deployed
to
squarelet-pi-pricing-mo-7hpy4u
August 27, 2026 13:04
Inactive
mitchelljkotler
force-pushed
the
pricing-models-backfill
branch
from
August 27, 2026 20:09
a1b18c9 to
6ee5503
Compare
mitchelljkotler
temporarily deployed
to
squarelet-pi-pricing-mo-7hpy4u
August 27, 2026 20:10
Inactive
mitchelljkotler
temporarily deployed
to
squarelet-pi-pricing-mo-7hpy4u
August 27, 2026 21:02
Inactive
mitchelljkotler
temporarily deployed
to
squarelet-pi-pricing-mo-wawwsj
September 1, 2026 13:57
Inactive
mitchelljkotler
force-pushed
the
pricing-models
branch
from
September 1, 2026 15:56
cb0277d to
95c30e2
Compare
285 lines that repoint `plan` on every subscription in production, with no tests. The Stripe half needs none, but the half that decides what happens to each row is a pure function of local data and worth pinning: - is_billing separating standard from comped, which is what keeps years of admin-granted free access off a paid price - _preflight refusing to write when a (slug, is_billing) pair is unmapped, and writing nothing at all when it does refuse -- the deliberate absence of a log-and-skip path is the command's main safety property and nothing checked it held - DEFERRED_SLUGS genuinely left alone - per-user subscribers still billing excluded, and comped ones NOT That last one is the subtle case. The exclusion is per-user AND billing, so a comped organization -- per-user plan, no Stripe subscription -- must still be migrated. Its correctness rests on subscription_id being nullable and on both halves of a two-condition exclude; the naive `exclude(plan__price_per_user__gt=0)` strands every comped organization with a null plan_price, which then fails step 3c much later and much less legibly. Verified the test fails against that mutation. Targets are derived from LEGACY_PLAN_MAP rather than listed, so the fixture cannot drift from the map it is standing in for. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
_preflight promises to refuse the whole run unless every case is accounted for, but its collision scan only compared pending rows against each other. A per-user subscriber still billing is excluded from this step and yet still occupies (organization, plan), so an organization holding one of those plus a legacy comp that maps onto the same canonical plan got past the check and aborted mid-write on a bare IntegrityError -- precisely the outcome the check exists to prevent. The scan now also looks at rows outside `pending`. Extracted to its own method: _preflight was running three unrelated checks and had outgrown being read in one go. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The targets fixture built a plan at each canonical slug without checking whether the migration had already seeded one. Where it had, AutoSlugField quietly made `professional-2`, the command could not find its target prices, and every test that runs it failed on the pre-flight check -- including the two collision tests, which never reached the assertion they were making. Whether the seeded plans are present depends on whether a transactional test has flushed the database first, so this passed locally and failed on a clean one. The fixture now reuses a seeded plan when there is one. Verified in all three states: the file alone against a freshly migrated database, the full suite against that same database once flushed, and the full suite against a fresh one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Same treatment as the migration and consolidation command: no pointers to a document the reviewer does not have, and no step numbers that mean nothing outside it. Two of these had also gone stale. The command still described per-user subscribers as being split "at renewal", and reported them as "awaiting their renewal-time split" -- the design that was replaced by a single all-at-once decomposition. An operator reading that summary would have been told to expect something that is no longer going to happen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
mitchelljkotler
force-pushed
the
pricing-models-backfill
branch
from
September 1, 2026 15:57
bccbf33 to
26d6e04
Compare
mitchelljkotler
temporarily deployed
to
squarelet-pi-pricing-mo-wawwsj
September 1, 2026 15:57
Inactive
No organization holds it any more, so there is nothing left to have a pricing conversation about. With no subscriptions on the slug it never reaches `pending`, so removing it from DEFERRED_SLUGS changes nothing the command does today -- and if one did turn up, the pre-flight would refuse the whole run rather than guess at a target, which is the outcome we want. custom-crp and sunlight-premium-annual stay deferred; both still need a decision. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
mitchelljkotler
temporarily deployed
to
squarelet-pi-pricing-mo-wawwsj
September 1, 2026 18:11
Inactive
mitchelljkotler
marked this pull request as ready for review
September 1, 2026 18:12
mitchelljkotler
marked this pull request as draft
September 2, 2026 16:14
Member
Author
|
Do not review yet, this needs to be re-ordered |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This adds a command to backfill subscriptions into the new data model
Claude notes:
One command, no schema, no Stripe calls. The checks worth doing are the refusals — no --actor, unknown --actor, and running it before consolidate_stripe_products — because the command's whole safety story is that it stops rather than guessing.
Then spot-check the distinction it exists to make: a paying subscriber lands on the standard price, a comped organization lands on the comped price with granted_by filled in.