Repository navigation
Carry main into redesign/main: streak gap repairs, billed amount, free sub-habits and referral Pro days (#746) - #829
Conversation
* fix: preserve yearly streak gap repairs through recalculation * fix: isolate yearly streak repair history window (cherry picked from commit 90c5320)
* fix: expose discounted Stripe billed amount per period * fix: isolate optional recurring Stripe billing preview (cherry picked from commit 0460a54)
* feat: make sub-habits available to every plan (ORB-231) * test: track free sub-habits in migration model expectations * fix: preserve Pro slip alerts when creating free sub-habits (cherry picked from commit c9d22a6)
* feat: grant atomic referral Pro days (ORB-22) * fix: preserve referral transaction and billing context * fix: preserve bonus Pro access across provider changes (cherry picked from commit 7b4e9af)
There was a problem hiding this comment.
Important
The new billed-amount preview excludes the one-time referral coupons Orbit actually issues, so the discounted billing display remains incorrect for those subscribers.
Reviewed changes across all five carry commits into redesign/main, including the combined migrations, redesign-specific adaptations, tests, and paired billing consumer.
- Streak repairs: Extends streak and repair history to 732 days and slices schedule reads to retain annual predecessors through recalculation.
- Billing amounts: Appends an optional discounted-amount field, isolates Stripe previews from ordinary subscription reads, and preserves nullable provider-failure fallback.
- Free sub-habits: Removes creation plan gates across REST, Astra, bulk, duplication and MCP while retaining slip-alert entitlement, root ceilings and depth checks.
- Referral rewards: Adds atomic bonus Pro days, subscriber coupon delivery, independent bonus expiry, and mapped entitlement queries for background services.
- Combined schema: Carries three migrations and retains redesign migration/model assertions and both versioned OpenAPI documents.
Focused verification passed all 278 tests: 79 Infrastructure, 176 Application and 23 Domain. The full suite was not rerun; the worktree is clean. The optional cross-model check was unavailable because codex is not installed.
gpt-6.1-sol | 𝕏
(cherry picked from commit 377eca5)
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes since the prior Pullfrog review at 26b385e2, covering the redesign/main merge and billing fix through a80cf565.
- Fixed one-time coupon previews: Switched Stripe previews to
next, including applicable referral coupons while preserving subscription-line filtering, the unchanged plan price, and nullable fallback. - Expanded billing regressions: Covered
onceandforevercoupons, normal pricing after consumption, and exclusion of customer credit and debit balances. - Reconciled migration assertions: Retained eval-trace seed checks alongside referral columns, free sub-habits, and the combined migration inventory after the base merge.
All 86 focused billing and migration tests passed with zero failures or skips. The full suite was not rerun; the worktree is clean.
gpt-6.1-sol | 𝕏
|




Carries the four requested main fixes into
redesign/main, retaining the redesign API, Astra previews and revisions, strict tool schemas, v2 endpoints, and capability policies.Refs thomasluizon/orbit-tickets#746
Refs thomasluizon/orbit-tickets#483
Refs thomasluizon/orbit-tickets#385
Refs thomasluizon/orbit-tickets#386
Refs thomasluizon/orbit-tickets#237
Refs thomasluizon/orbit-tickets#28
Carried commits
Applied oldest first with
git cherry-pick -x --no-commit, then built and ran focused tests before each commit. Every resulting commit retains the full source commit trailer.90c5320d(#818)f7e7afb10460a545(#819)fadb7fa7c9d22a6e(#824)12ce3fae7b4e9af6(#823)8e7638b7No commits were skipped. Before applying them, inspected
origin/redesign/mainat84451e28, searched its commit messages for all four trailers, and ran a reversegit apply --checkfor each source patch; all four reverse checks exited 1. Content inspection also showed the changes missing:src/Orbit.Application/Common/AppConstants.cs:73had only the 365-day streak lookback.src/Orbit.Application/Subscriptions/SubscriptionDtos.cs:50defined the billing response without the appended billed amount.src/Orbit.Application/Common/PayGateService.cs:30still enforcedCanCreateSubHabitsthroughSubHabitsProOnly.src/Orbit.Domain/Entities/Referral.cs:8had no per-recipient reward choices or delivery timestamps, and the new referral migration files were absent.These line references describe the pre-carry base, not the changed head.
Conflict resolutions by redesign commit
Product-language explanations, #797 (
45548c59)src/Orbit.Application/Chat/Content/FeatureExplanations/streaks.md: retain prose without internal identifiers, change the window to 732 days, and describe the retained predecessor occurrence. Update the provenance toStreakRepairWindowDays.Notion API additions, #732 (
5d9ff03e)src/Orbit.Api/openapi.jsonsrc/Orbit.Api/openapi_v2.jsonThe billing schema insertion conflicted with the redesign's
AuthorizeRequestschema. Discard the conflicted generated output and regenerate both documents throughdotnet build Orbit.slnx. Both retain every pre-carry path and schema and append optional, nullablebilledAmountPerPeriod. The final build also regenerates the referral response schemas. Neither document was hand-merged.Atomic AI quota checks, #806 (
e40a2a69)src/Orbit.Application/Common/PayGateService.cs: remove onlyCanCreateSubHabits; keepTryConsumeAiMessage, refunds, smoke-account handling and Notion access. Do not restore the retired duplicateCanSendAiMessagecheck from main's surrounding context.src/Orbit.Domain/Interfaces/IPayGateService.cs: remove only the sub-habit gate contract and retain the redesign's atomic quota interface.tests/Orbit.Application.Tests/Common/PayGateServiceTests.cs: remove the five tests for the deleted gate, as upstream does, while retaining atomic quota, refund and smoke-account coverage. The main-side duplicate-check tests target the method already removed by test: close Astra coverage gaps and remove the duplicate quota check #806; the retained atomic-path tests cover that behavior.Strict schemas and combined model checks, #816 (
84451e28), with conversation assertions from #800 (4e7ddcb5)src/Orbit.Application/Chat/Tools/Implementations/CreateHabitTool.cs: remove the sub-habit gate while retaining the redesign'sBuildChildHabitssignature, optional-null normalization, preview checks and independent child scheduling. Apply main's slip-alert gate before any persistence.tests/Orbit.Infrastructure.Tests/Persistence/FeatureFlagAccountsMigrationTests.cs: resolve both carries' conflicts by retaining the redesign's schema-version, AI-security, conversation, proactive preference and migration inventory checks; add the free-sub-habit seed assertion and all five referral column assertions. The combined model has 51 operations relative to the existing baseline. Keep every migration in timestamp order and assert no pending model changes.Creation surface audit
CreateHabitCommand,CreateSubHabitCommand,BulkCreateHabitsCommandand duplication handlers. The existing versioned route declarations remain intact.CreateSubHabitCommand; their free and Pro cases now persist real children through the handlers. Test arguments include schema-derived optional nulls and pass through the sameOptionalToolArgumentsnormalization as the chat dispatcher.create_sub_habitretains its executor, audit and policy bridge. Remove the obsolete Pro requirement from its description and adapt its real tool-to-command test to the carried handler constructor.HabitsWriteand bulk creation toHabitsBulkWrite, with no plan requirement. It needs no policy change. Catalog, policy, strict-schema and MCP tests pass.AppFeatureFlags.sub_habits.PlanRequirementto null and removesAppConfigs.SubHabitsProOnly. The service/interface/config constant no longer exposes the obsolete gate. The redesign-only free/Pro explanation and drift assertions now match.GetHabitByIdQuery,GetHabitFullDetailQuery, strict schema conversion, pending-operation revision/refresh behavior and the v2 API surface are retained.Migrations
Keep the original migration and Designer files byte-for-byte:
20261020023828_AddReferralProDayRewards20261020030817_MakeSubHabitsFree20261020041243_SeparateBonusProEntitlementThe combined snapshot retains the redesign migrations, including
20261020023110_VersionPendingToolArgumentsand20261020033359_RestoreRequiredProactiveAstraPreference, while adding the referral fields and free-sub-habit seed. Original timestamps order the referral column addition before the sub-habit seed change, despite the requested cherry-pick order. These operations are independent; the bonus-entitlement data migration remains last. No migration was renamed or regenerated.From
src/Orbit.Api,env -u LANG dotnet ef migrations has-pending-model-changes --project ../Orbit.Infrastructure --startup-project . --no-buildexited 0 and reported: "No changes have been made to the model since the last migration." The design-time factory uses no live database. EF tools report the existing 10.0.12 versus runtime 10.0.13 version warning.Assumptions
7b4e9af6; rejected interpreting the order's singular migration reference as permission to omitSeparateBonusProEntitlement.BuildChildHabitssignature; rejected restoring main's older argument shape and losing the strict-schema work.Test evidence
Each coherent carry was built, tested and committed before broader verification. A separate follow-up commit updates the remaining redesign migration expectations and shared assertions, with no production changes. Focused passing runs: streak Application 134 and Infrastructure 48; billing Application 9 and Infrastructure 85; sub-habit Application 338 and Infrastructure 201; referral Domain 23, Application 208 and Infrastructure 134. All exited 0.
Regression observations from this run:
The unchanged
FeatureExplanationDriftTestspassed while the old Pro-only sub-habit claim was present:env -u LANG dotnet test tests/Orbit.Application.Tests --no-build --filter 'FullyQualifiedName~HabitScheduleServiceTests|FullyQualifiedName~GetStreakHistoryQueryHandlerTests|FullyQualifiedName~RepairStreakGapCommandValidatorTests|FullyQualifiedName~FeatureExplanationDriftTests'exited 0 with 134 passing tests. After updatingPaygate_ProUnlocksMatchEnforcedGatesfor the carried policy,env -u LANG dotnet test tests/Orbit.Application.Tests --no-build --filter FullyQualifiedName~FeatureExplanationDriftTestsexited 1 with two intended failures: the prose still listedSub-habitsas a Pro unlock. After fixing the prose, the 338-test Application filter passed, including both cases.The unchanged combined-model test detected both inventory mismatches: after the sub-habit carry,
env -u LANG dotnet test tests/Orbit.Infrastructure.Tests --no-build --filter FullyQualifiedName~FeatureFlagAccountsMigrationTestsexited 1, expecting 45 operations and finding 46. After the referral carry, the same test in the filter below expected 46 and found 51. Retaining all prior assertions and adding the carried assertions made the migration tests pass. No strengthened red test was needed because the existing assertion detected the mismatch.Before the referral carry,
env -u LANG dotnet test tests/Orbit.Infrastructure.Tests --no-build --filter 'FullyQualifiedName~ProactiveEngineServiceTests|FullyQualifiedName~NotionPollServiceTests.ScheduledTickIncludesPeopleWithTrialProAccess'passed 69 tests. After the carry and before adaptation,env -u LANG dotnet test tests/Orbit.Infrastructure.Tests --no-build --filter 'FullyQualifiedName~ProactiveEngineServiceTests.TrialIsEligible_FreeAndExpiredTrialAreNot|FullyQualifiedName~NotionPollServiceTests.ScheduledTickIncludesPeopleWithTrialProAccess|FullyQualifiedName~FeatureFlagAccountsMigrationTests'exited 1: both unchanged scheduling tests failed because EF could not translate the now-unmappedPlanandPlanExpiresAtmembers, alongside the model-count failure above.Added bonus-expiry boundary cases before correcting those two queries.
env -u LANG dotnet test tests/Orbit.Infrastructure.Tests --filter FullyQualifiedName~BonusProEligibilityexited 1 with all six cases failing on that translation error. After the query adaptation, the 134-test Infrastructure filter passed, including active, exactly expired and previously expired bonus accounts on both services.The first broad
env -u LANG dotnet test tests/Orbit.Infrastructure.Tests --no-build --no-restoreexited 1 with three existing migration assertions failing:ExpandHabitScheduleAndStartDateMigrationTests.TargetModel_PreservesRedesignWithConversationAndAiSecurityAdditionsexpected 16 operations and found 22;DailyAiDollarBudgetMigrationTests.DollarExpansion_PreservesLegacyWritersWithConversationAndArgumentVersionAdditionsexpected 8 and found 14;ConversationMigrationTests.Migration_ExpandsSchemaAndDisablesCollection_AndDownDropsDiagnosticDataexpected only the pending-argument-version column among the remaining additions. All now retain their prior schema checks and explicitly verify the five referral columns and free-sub-habit seed.env -u LANG dotnet test tests/Orbit.Infrastructure.Tests --filter 'FullyQualifiedName~MigrationTests|FullyQualifiedName~RollingMigrationGateTests'then exited 0 with 138 passing tests. Existing assertions caught these mismatches, so no separate strengthened red test was needed.Final verification on
26b385e2, with every process judged by its exit code:LANGunsetLC_ALL=en_US.UTF-8dotnet build Orbit.slnxdotnet test tests/Orbit.Domain.Tests --no-build --no-restoredotnet test tests/Orbit.Application.Tests --no-build --no-restoredotnet test tests/Orbit.Infrastructure.Tests --no-build --no-restoreThe two environment prefixes were
env -u LANGandLC_ALL=en_US.UTF-8. Both Infrastructure runs setORBIT_MIGRATION_GUARD_BASE=origin/redesign/main. Each locale passed all 11,579 tests with zero failures or skips, including migration and strict-schema tests. The final full Application and Infrastructure commands provide the passing results for every regression named above. Both builds regenerate both OpenAPI documents without leaving changes. All five commits passed the normal hooks. Mutation testing remains with CI; live release verification remains with the run.Build and suites passed with both
LANGunset andLC_ALL=en_US.UTF-8, each exiting 0.dotnet build Orbit.slnxThe unchanged pre-merge focused suite passed 20 tests. After merging, the stale eval-trace expectation failed with “expected 2, found 8.” After reconciliation, all 22 focused tests passed.
dotnet ef migrations has-pending-model-changesexited 0 and reported no changes. Commit hooks passed.The merge of api
redesign/main(84f433bd,dd8b07b6) is57585c1a. The Pullfrog P1 on the billed-amount preview was fixed on apimainfirst underthomasluizon/orbit-tickets#1581(orbit-api#830, merged as377eca51) and cherry-picked here with-xasa80cf565; on the carry,dotnet test tests/Orbit.Infrastructure.Tests --filter "FullyQualifiedName~StripeBilling"passed 70 of 70, exit 0.External interface evidence
The carried Stripe preview fields and error handling are supported by #819's pinned SDK source inspection: Stripe.net 52.4.2 source commit
f4f1c8ce704825d7d7f00b4c9d848a2d028757eb, covering preview options, invoice currency/lines, pagination, recurring parent details, subtotal and discount amounts. Reproduce from the package nuspec repository commit and the linked source declarations.The carried Hangfire job members, retry attributes and Stripe idempotency key are supported by #823's runtime reflection evidence. That evidence lists the actual installed
IBackgroundJobClient.Create,Job.Type,Job.Args,RequestOptions.IdempotencyKey,CouponService.CreateAsyncand retry attribute shapes. Queue return IDs are not used as failure signals. The redesign adaptations add no provider response reads or invented provider fixtures.Manual steps
main, before the paired billing and referral consumers, thomasluizon/orbit-tickets#386 and thomasluizon/orbit-tickets#62. In GitHub Actions >Release API, dispatch frommainwithenvironment=productionandbranch=main. Startup applies the included migrations. This worker does not perform the release.GET /api/subscriptions/billingfor a discounted Stripe subscription and verifybilledAmountPerPeriodalongside the unchanged plan price. Verify the nullable fallback for unavailable previews before the billing UI ships.GET /api/configand confirmfeatures.sub_habits.enabledis true andplanRequirementis null. Confirm ordinary free child creation persists through REST and Astra, free creation with slip alerts returnsPAY_GATEwithout persistence, and Pro creation retains alerts.DownforSeparateBonusProEntitlementfirst usingdotnet ef database update 20261020023828_AddReferralProDayRewardsagainst the production connection. Verify every bonus row has rawPlan = ProandPlanExpiresAtcovering its bonus expiry before starting the older binary.None for the owner. No new environment variables, secrets, dashboard settings, manual SQL or backfills are required for rollout. Live release checks remain the run's release verification steps.
None introduced by this merge.