Skip to content

Stop Stripe and Play renewals when account deletion is confirmed - #746

Merged
thomasluizon merged 7 commits into
mainfrom
thomasluizon/ticket-115-deletion-cancels-subscription
Oct 18, 2026
Merged

thomasluizon merged 7 commits into
mainfrom
thomasluizon/ticket-115-deletion-cancels-subscription

Conversation

@thomasluizon

@thomasluizon thomasluizon commented Oct 18, 2026 •

Copy link
Copy Markdown
Owner

Confirming account deletion now stops subscription renewal through a durable Hangfire job while preserving the paid period and the existing deletion schedule. Provider failures escape the job for retry and leave confirmation successful.

Closes thomasluizon/orbit-tickets#115. Implements ORB-109.

Intended approach

  • IBillingService and StripeBillingService update cancel_at_period_end in either direction with the existing retry and exception wrapper. They never immediately cancel the subscription.
  • IPlayBillingService and GooglePlayBillingService call Publisher v2 cancel. The job verifies the purchase first and skips inactive or unknown purchases. Paid entitlement is unchanged.
  • ConfirmAccountDeletionCommand saves deactivation, then queues CancelSubscriptionOnDeletionJob with primitive provider identifiers. Hangfire retries failures at 60 seconds, 300 seconds, then hourly. The job locks and reloads the current user, preventing delayed cancellation or restoration from reversing a later account action.
  • VerifyCodeCommand and the shared GoogleSignInFlow queue Stripe restoration after reactivation only while the paid period remains unexpired. The shared flow covers Google auth and OAuth callbacks. User.CancelDeactivation returns an expired subscription to the free plan without dropping identifiers that the deletion backstop may need. Play reactivation does not restore renewal from the server; the person must resume in the Play Store.
  • AccountDeletionService rechecks deletion eligibility under the same user row lock and checks both retained provider identifiers before deleting any owned data or removing the user. Failed provider calls retain the row for another scheduled attempt. Already stopped Stripe subscriptions do not receive another update.
  • Unit coverage follows the confirmation, job, domain, Google auth, and OAuth paths. The hard-delete tests drive the actual scheduled service with the existing SQLite unit-test factory, asserting provider cancellation before removal and row retention on failure.

No endpoint or DTO contract changes, migrations, dependency updates, or new configuration are required. The OpenAPI documents remain unchanged after the build.

External interface evidence

Verified the installed assemblies with ilspycmd and their accompanying XML documentation, rather than deriving new provider fields from mocks:

  • Stripe.net 52.4.2: Stripe.SubscriptionUpdateOptions.CancelAtPeriodEnd is a nullable bool serialized as cancel_at_period_end. Stripe.SubscriptionService.UpdateAsync(string, SubscriptionUpdateOptions, RequestOptions, CancellationToken) posts to /v1/subscriptions/{id}; CancelAsync instead sends DELETE. Stripe.Subscription exposes status and cancel_at_period_end; its status documentation includes canceled and incomplete_expired. The backstop uses those verified fields through the existing billing DTO. Provider tests assert update arguments, error wrapping, transient retries, and no immediate cancellation.
  • Android Publisher 1.77.0.4284: PurchasesResource.Subscriptionsv2Resource.Cancel(CancelSubscriptionPurchaseRequest, string packageName, string token) creates a POST to androidpublisher/v3/applications/{packageName}/purchases/subscriptionsv2/tokens/{token}:cancel. CancelSubscriptionPurchaseRequest.CancellationContext and CancellationContext.CancellationType are required. The Google API reference confirms USER_REQUESTED_STOP_RENEWALS and an empty success response. The live Publisher discovery schema was also read: schemas.CancellationContext.properties.cancellationType.enum contains CANCELLATION_TYPE_UNSPECIFIED, USER_REQUESTED_STOP_RENEWALS, and DEVELOPER_REQUESTED_STOP_PAYMENTS. A unit test captures the actual SDK request and asserts its method, path, and serialized context, with an empty success body.
  • Hangfire.Core 1.8.25: Hangfire.Common.Job exposes Type, Method, and Args, which the queue tests inspect. AutomaticRetryAttribute repeats the last configured delay after exhausting its delay array, so hourly retries continue instead of abandoning billing cancellation after the default attempt limit.

Reproduce the installed-source checks with ilspycmd -t <type> <assembly> using these package-relative assemblies:

stripe.net/52.4.2/lib/net9.0/Stripe.net.dll
  Stripe.SubscriptionUpdateOptions
  Stripe.SubscriptionService
  Stripe.Subscription
google.apis.androidpublisher.v3/1.77.0.4284/lib/net6.0/Google.Apis.AndroidPublisher.v3.dll
  Google.Apis.AndroidPublisher.v3.PurchasesResource
  Google.Apis.AndroidPublisher.v3.Data.CancelSubscriptionPurchaseRequest
  Google.Apis.AndroidPublisher.v3.Data.CancellationContext
hangfire.core/1.8.25/lib/netstandard2.0/Hangfire.Core.dll
  Hangfire.Common.Job
  Hangfire.AutomaticRetryAttribute

Assumptions

  • Preserve the checked-out handler's existing 30-day grace cap, rejecting the older ticket excerpt that omitted the cap. The existing boundary tests remain unchanged in their scheduling assertions.
  • Restore Google and OAuth accounts in their shared GoogleSignInFlow, rejecting edits to obsolete direct CancelDeactivation callsites. The OAuth regression test drives controller, command handlers, and the shared flow together.
  • Supply the required Play USER_REQUESTED_STOP_RENEWALS context, rejecting the ticket's empty request body. This follows the decision that the person can resume in the Play Store; the API does not attempt a server-side restoration.
  • Reconcile delayed jobs against the current account state under a row lock, rejecting unconditional replay of stale cancel or restore arguments.
  • Used seven days overdue instead of a persisted attempt counter, avoiding a migration.
  • Used 26 retries with the existing delays instead of indefinite retries.

Retained the existing tested seven-day billing handoff. The snapshot merged without conflicts, so regeneration was unnecessary.

Test evidence

Before implementation, the unchanged suite passed with the defect present:

dotnet test tests/Orbit.Application.Tests --filter FullyQualifiedName~ConfirmAccountDeletionCommandHandlerTests --verbosity quiet
15 passed, 0 failed.

Strengthened Handle_ProPlanWithinGraceCap_SchedulesDeletionSevenDaysAfterPlanExpiry to require one queued billing job. With production code still unchanged:

dotnet test tests/Orbit.Application.Tests --filter FullyQualifiedName~Handle_ProPlanWithinGraceCap_SchedulesDeletionSevenDaysAfterPlanExpiry --verbosity quiet
0 passed, 1 failed.

The detailed rerun with --no-build --logger 'console;verbosity=normal' reported ReceivedCallsException: expected one Create(any Job, any IState) call, received none. After implementation, the same strengthened test passed in the focused confirmation suite. Additional confirmation cases assert the provider-specific job arguments, preserved expiry, unchanged grace math, no queue work for free/trial users, and successful confirmation followed by a retryable provider failure.

The restoration expiry test also passed for ordinary subscriptions before strengthening it with lifetime access. The strengthened ExecuteAsync_Restoration_OnlyRestoresBeforeExpiry initially failed for expired: true, lifetime: true; after checking the actual paid-period end, all four combinations pass.

The first broader run exposed missing job-client wiring in the existing concurrency fixture and eager billing resolution on free-account hard deletion. Both were corrected, and targeted reruns passed: 52 application cases and 28 deletion-service cases, including the existing deletion, marketing opt-out, and concurrency tests.

Final validation:

dotnet build Orbit.slnx --verbosity quiet
Exit 0, 0 errors. Existing package/API warnings remain.

dotnet test tests/Orbit.Domain.Tests --no-build --verbosity quiet
Exit 0: 675 passed, 0 failed.

dotnet test tests/Orbit.Application.Tests --no-build --verbosity quiet
Exit 0: 5519 passed, 0 failed.

dotnet test tests/Orbit.Infrastructure.Tests --no-build --verbosity quiet
Exit 0: 3372 passed, 0 failed.

dotnet test tests/Orbit.Analyzers.Tests --no-build --verbosity quiet
Exit 0: 32 passed, 0 failed.

All 9598 tests passed. Focused confirmation/job/reactivation/orphan-webhook coverage also passed (50 application cases), as did focused provider/deletion/OAuth coverage (51 infrastructure cases). Source search found one Users.Remove path, guarded by the cancellation backstop; free accounts skip billing dependency resolution entirely. No new bare narration comments were introduced. Formatting, dash, timeless-text, root-allowlist, and suppression-allowlist hooks passed. No server, browser, live database, or mutation testing was used.

Exact regression commands:

dotnet test tests/Orbit.Application.Tests --filter FullyQualifiedName~CancelSubscriptionOnDeletionJobTests
dotnet test tests/Orbit.Infrastructure.Tests --filter 'FullyQualifiedName~AccountDeletionServiceTests|FullyQualifiedName~StripeBillingServiceTests|FullyQualifiedName~GooglePlayBillingServiceTests'
Suite Unchanged tests, defective head Strengthened tests, defective head Fixed
Application focused 19 passed 4 failed 21 passed
Infrastructure focused 47 passed 16 failed 73 passed

Finding 1 regressions include GetSubscriptionDetailsAsync_MissingSubscription_ReturnsNull, VerifyAsync_MissingPurchase_ReturnsNull, and RunAsync_DuePaidAccount_CancelsBeforeRemovingAndRetainsRowOnFailure. Finding 2’s ExecuteAsync_ProviderFailure_PropagatesForDurableRetry failed because retries were 2147483647 instead of bounded.

The additional null-queue regression failed before its guard was added, then passed.

Final checks, all exit 0:

  • dotnet build Orbit.slnx: zero errors.
  • dotnet test tests/Orbit.Domain.Tests: 675 passed.
  • dotnet test tests/Orbit.Application.Tests: 5,521 passed.
  • dotnet test tests/Orbit.Infrastructure.Tests: 3,398 passed.
  • All commit hooks passed. Generated OpenAPI files were unchanged.

External members were verified against package-pinned source: Stripe exception members and error code mapping, Google HTTP status, and Hangfire retry behavior, nullable creation result, and job arguments. Missing-record semantics match Stripe’s codes and Play’s HTTP errors.

All required commands exited 0:

  • dotnet build: zero errors.
  • git diff --exit-code -- src/Orbit.Api/openapi.json: no drift.
  • dotnet test tests/Orbit.Domain.Tests: 730 passed.
  • dotnet test tests/Orbit.Application.Tests: 5,547 passed.
  • dotnet test tests/Orbit.Infrastructure.Tests: 3,553 passed.
  • dotnet test tests/Orbit.Analyzers.Tests: 32 passed.

Regression proof using dotnet test tests/Orbit.Infrastructure.Tests --filter FullyQualifiedName~AccountDeletionServiceTests.RunAsync_DuePaidAccount_CancelsBeforeRemovingAndRetainsRowOnFailure:

  • Original assertions, with storage registered: 15 passed with the incorrect uploads-first order.
  • Strengthened assertions: five failed because upload deletion was attempted before billing failure retained the account.
  • After correcting the order, dotnet test tests/Orbit.Infrastructure.Tests --no-build --filter FullyQualifiedName~AccountDeletionService: all 42 passed.

Manual steps

None. No new configuration, secrets, migrations or backfills are required.

None introduced by this merge.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

This PR can restart customer-canceled Stripe renewals and leave linked Play subscriptions renewing after deletion. The three billing issues below should be addressed before merging.

Reviewed changes: Reviewed the account-deletion cancellation flow, provider adapters, reactivation paths, hard-delete backstop, and associated tests across all 24 changed files.

  • Durable cancellation: Confirmation queues provider work that reconciles current account state under a row lock and retries provider failures.
  • Provider operations: Stripe updates period-end cancellation, while Play uses Publisher v2 user-requested cancellation without revoking the paid period.
  • Reactivation: Email and Google/OAuth sign-in queue Stripe restoration for unexpired paid periods, and expired non-lifetime plans return to Free.
  • Deletion backstop: Scheduled deletion checks retained provider identifiers before removing the account and retains the row on provider failure.
  • Verification: All 100 focused application and infrastructure unit tests passed; the scenarios identified below are not covered by those assertions.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using gpt-6.1-sol | 𝕏

Comment thread src/Orbit.Application/Auth/Jobs/CancelSubscriptionOnDeletionJob.cs Outdated
Comment thread src/Orbit.Application/Auth/Jobs/CancelSubscriptionOnDeletionJob.cs Outdated
Comment thread src/Orbit.Application/Auth/Jobs/CancelSubscriptionOnDeletionJob.cs Outdated

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

No new High or Critical issues found in this commit. The existing blocking billing threads remain unresolved, so this review does not approve the PR.

Reviewed changes: Reviewed 452fdc96 against the previous review at cbd4b0e1, with the full PR diff as context.

  • Bounded cancellation retries to 26 attempts, retaining exhausted work in Hangfire's failed state for manual recovery.
  • Added erasure handoff after seven days overdue, persisting provider identifiers before deletion and refusing erasure when enqueueing fails.
  • Enabled post-erasure cancellation using retained job arguments without permitting restoration for a missing user, and attempted both providers independently.
  • Handled missing provider records and timeouts, treating confirmed missing records as completed work while preserving retryable provider failures and caller cancellation.
  • Verified focused regression coverage with 39 application and 73 infrastructure unit tests passing.

Pullfrog  | Fix it ➔ | View workflow run | Using gpt-6.1-sol | 𝕏

pullfrog[bot]
pullfrog Bot previously approved these changes Oct 18, 2026

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new High or Critical issues found.

Reviewed changes: Reviewed 9d225cf1 since the prior Pullfrog review at 452fdc96, with the full PR diff as context. All three resolved findings have fix explanations supported by the current implementation and regression tests.

  • Persisted renewal ownership: Added guarded domain state and an EF migration so reactivation restores only Stripe renewal actually stopped by account deletion, preserving independent cancellation intent.
  • Separated Play renewability: Added CanRenew independently of entitlement, allowing paused and on-hold auto-renewing purchases to be canceled without granting paid access.
  • Reconciled both providers: Queued every retained provider identifier regardless of entitlement ownership and committed successful cancellation ownership before retrying another provider's failure, including the hard-delete backstop.
  • Extracted Google notifications: Moved existing welcome-email and referral effects into the registered GoogleSignInNotifications service without changing their behavior.
  • Verified regressions: Passed all 275 focused unit tests: 92 Application, 16 Domain, and 167 Infrastructure, covering billing adapters, persistence/reload, deletion, retries, and email/Google/OAuth reactivation. The full suite was not rerun locally.

Pullfrog  | View workflow run | Using gpt-6.1-sol | 𝕏

pullfrog[bot]
pullfrog Bot previously approved these changes Oct 18, 2026

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new High or Critical issues found.

Reviewed changes: Reviewed the merge at 489e9289 since the prior Pullfrog review at 9d225cf1, with the full PR diff as context.

  • Preserved erasure ordering: Integrated main's upload cleanup after successful billing reconciliation or durable cancellation handoff, keeping uploads intact when billing or enqueueing fails.
  • Strengthened regression coverage: Added a populated object store to the paid-account deletion cases and asserted that retained accounts keep their uploads while completed erasure removes them.
  • Verified combined paths: Passed all 45 focused deletion-service and cancellation-persistence unit tests, including storage retries, provider failures, and post-erasure cancellation. The full suite was not rerun locally.

Pullfrog  | View workflow run | Using gpt-6.1-sol | 𝕏

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new High or Critical issues found.

Reviewed changes: Reviewed the merge at 0ff557f7 since the prior Pullfrog review at 489e9289, with the full PR diff as context.

  • Preserved transactional restoration: Retained Stripe restoration after successful email and Google/OAuth reactivation while integrating main's transactional user provisioning and session creation.
  • Integrated durable signup work: Preserved signup-job dispatch alongside renewal restoration, replacing the earlier Google notification extraction with main's shared durable signup flow.
  • Strengthened merged assertions: Distinguished restoration jobs from signup jobs across email, Google token, Google code, and OAuth callback tests, and retained migration-model checks for both renewal-ownership columns.
  • Verified combined behavior: Passed all 277 focused unit tests: 100 Application and 177 Infrastructure, covering reactivation, signup dispatch and concurrency, migration composition, deletion, and cancellation persistence. The full suite was not rerun locally.

Pullfrog  | View workflow run | Using gpt-6.1-sol | 𝕏

@sonarqubecloud

Copy link
Copy Markdown

@thomasluizon
thomasluizon merged commit 85d8824 into main Oct 18, 2026
25 checks passed
@thomasluizon
thomasluizon deleted the thomasluizon/ticket-115-deletion-cancels-subscription branch October 18, 2026 20:02
thomasluizon added a commit that referenced this pull request Oct 18, 2026
* fix: pin analyzer test Pkcs dependency to patched version (#728)

(cherry picked from commit 61b0090)

* fix: shard pull request domain mutation testing (#729)

(cherry picked from commit bcfd81e)

* Promote the API redesign with production compatibility checks (#723)

* Bring the API redesign tree onto main for #1413

* Preserve calendar connection detection for shipped clients

* Prove API and migrated schema compatibility with shipped clients

* Trim whitespace in the archived main schema proof

* Require the production health response to report the released commit

* Bind initial approval previews to stored pending operations

* fix: return the bulk skip refresh outcome and split the undo skip query

SonarCloud failed the quality gate on two reliability findings in code this pull request brings to main:
the bulk skip conflict check read a flag only a lambda wrote (S2583), and the undo skip check loaded
Tags and Goals in one query (S8733). The transaction now returns whether every habit refreshed, and the
undo skip check splits its query. A new case covers the concurrent change conflict; it failed with the
refresh outcome forced to true and passes with the fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 5526c9b)

* Add per-account feature flag targeting and admin controls (#724)

* feat: resolve feature flags per account

* feat: administer account feature flags through MCP and REST

* test: exclude feature flag targets from rate limit fixture

* fix: reconcile account flag tests and migration metadata with redesign

(cherry picked from commit 1b886bd)

* Add header API versioning for v1 and v2 (#733)

* Add header API versioning with v1 compatibility

* Verify versioned OpenAPI and browser header compatibility

* Preserve skip undo path contract with optional version header

(cherry picked from commit ac8b34a)

* fix: expose the config response in OpenAPI (#734)

(cherry picked from commit a9d8dd5)

* Route production redesign accounts through Cloudflare (#735)

* Add cookie-based routing for the production redesign web service

* Document web routing rollout, permissions, and rollback checks

* Fix web routing coverage and disable infra install scripts

* chore: regenerate the v2 OpenAPI document on main

orbit-api#733 added openapi_v2.json and orbit-api#734 typed the /api/config
response in parallel, so main carries a v2 document without the typed config
schema and the Build check's stale-document step fails on every merge with
main. dotnet build src/Orbit.Api regenerates it; openapi.json is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 9c64770)

* Fix habit edit gating, end dates, and reminder updates (#736)

* fix: validate habit updates with the resulting time range

* fix: preserve end dates when creating root habits

* fix: gate slip alert activation and preserve unchanged reminders

* fix: compare effective reminder rules across stored formats

(cherry picked from commit 7472203)

* Reduce mutation and guard CI jobs (#737)

* Select mutation shards from changed projects

* Consolidate guards and filter artifact generation

* Skip project documentation in mutation selection

* Validate mutation base refs and restrict Git PATH

* Read mutation shard changes from standard input

(cherry picked from commit b802f9e)

* test: strengthen habit and goal ownership coverage (#739)

(cherry picked from commit 0e32628)

* test: verify checkout session user metadata (#740)

(cherry picked from commit da2f53a)

* fix: protect OAuth code delivery with Turnstile (#741)

(cherry picked from commit 1149606)

* fix: enforce chat interaction scope before loading context (#743)

(cherry picked from commit 98faf4e)

* fix: erase accountability pairs during account deletion and reset (#745)

* fix: erase accountability pairs during account deletion and reset

* test: preserve migration compatibility checks after accountability cleanup

(cherry picked from commit a9e5fb9)

* Fix goal sync dates and deadline reminder counts (#747)

* fix: compare streak sync dates in the user timezone

* fix: show actual days remaining in goal deadline reminders

* test: configure timezone for streak scheduler fixtures

(cherry picked from commit 0e63842)

* Manage staging landing routing in Terraform (#742)

(cherry picked from commit abdd738)

* Restrict Web Push endpoints to supported push services (#1503) (#751)

Web Push subscriptions accept only HTTPS endpoints on the FCM, Mozilla, Apple and WNS push hosts, and the push HTTP client follows no redirects. Replaces orbit-api#748 as one commit so no commit carries the high-entropy sample fixture.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit d9f847a)

* Erase account uploads before permanent deletion (#1507) (#750)

* fix: erase uploaded objects before completing account deletion

* fix: refuse upload reads after permanent account deletion

* fix: grant uploads bucket deletion permissions and verify provider wiring

* test: align storage startup checks with erasure requirements

* fix: retire deleted Supabase upload storage

* test: configure upload erasure providers in deletion fixtures

Share the existing object-store fake across account deletion and scheduler fixtures after merging main. Preserve accountability assertions and production provider requirements for thomasluizon/orbit-tickets#1507.

(cherry picked from commit fa33a94)

* fix: limit anonymous auth and waitlist by email and source IP (ORB-108) (#749)

* fix: enforce source IP ceilings alongside anonymous email limits

* test: cover both rate limit dimensions before bot verification

* fix: isolate BFF email rate limits by verified client IP

* fix: generate and persist BFF forwarding secrets

(cherry picked from commit de0f42f)

* Limit access tokens to fifteen minutes (#738)

* fix(auth): limit access tokens to fifteen minutes

* fix(auth): align managed JWT lifetime and prove logout rejection

(cherry picked from commit ade7016)

* fix(auth): equalize account provisioning work (ORB-96) (#744)

* test(auth): expose registration dependent sign-in work

* fix(auth): perform uniform registration work before sign-in responses

* test(auth): cover expired codes and tracked upsert conflicts

* fix(auth): isolate signup retries and restore provisioning guards

* fix(auth): recover signup work after dispatch failures

* fix(auth): roll back failed signup referrals before retry

(cherry picked from commit 1bd0d26)

* fix: expose redesign controllers in both API versions

Apply the carried version declarations to the Notion and proactive controllers and regenerate the v2 contract. Refs thomasluizon/orbit-tickets#746.

* fix: honor account flags in proactive engine

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
thomasluizon added a commit that referenced this pull request Oct 19, 2026
…, slip contract and the Supabase sign-in removal (#768)

* Stop Stripe and Play renewals when account deletion is confirmed (#746)

* fix: stop subscription renewal when account deletion is confirmed

* fix: restrict renewal restoration to an unexpired paid period

* fix: keep free account deletion independent of billing services

* fix: bound billing cancellation delays during account erasure

* fix: preserve renewal intent during deletion reconciliation

(cherry picked from commit 85d8824)

* docs: name Render web services in version floor preconditions (#755)

(cherry picked from commit 21615aa)

* fix(infra): use Render for production database connections (#754)

(cherry picked from commit e2909b6)

* test(habits): cover end date reactivation allowance through handler (#759)

(cherry picked from commit 16203d9)

* test(mcp): verify Pro access for OAuth and API keys (#760)

(cherry picked from commit 0a90f67)

* Cover calendar auto-sync scheduling and import reconciliation (ORB-199) (#761)

* test(calendar): cover auto-sync scheduler eligibility and batching

* test(calendar): cover import suggestion reconciliation filters

* test(calendar): isolate free plan eligibility from expiry

(cherry picked from commit 664c756)

* Clarify controller action cross-references in the agent catalog (#762)

(cherry picked from commit d9cda52)

* refactor(calendar): share legacy match key for sync and suggestions (#763)

(cherry picked from commit 8e754c4)

* Contract habit log slip classification schema (#718) (#764)

(cherry picked from commit b0d3ff4)

* fix(calendar): gate recurring event reconciliation (#765)

(cherry picked from commit c900dba)

* Gate rolling schema contractions behind deployment barriers (#766)

* feat: require deployment barriers for rolling schema contractions

* fix: keep quoted SQL comment markers from hiding contractions

* test: record the deployed habit log contract barrier

* ci: run the rolling contract gate only when its inputs change

The gate builds the infrastructure test project, which costs minutes on every
pull request. It now runs only when a migration, the barrier file or the gate
itself changes; the step before it already reads the same diff.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: require barriers for unclassified ALTER TABLE actions

* test: isolate barrier unit coverage from checkout history

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 626c525)

* fix: require a session claim on access tokens (#767)

(cherry picked from commit 517ed1b)

* Deprecate legacy Google auth routes (#1528, part 1) (#753)

* Deprecate legacy Google auth routes for #1528

* Explain deprecated Google auth routes and annotate legacy tests

(cherry picked from commit 21f810f)

* Remove deleted Supabase sign-in path (#1528) (#752)

* Remove deleted Supabase sign-in path (#1528)

* Remove unused access-token validation catalog entries (#1528)

(cherry picked from commit 141a4a2)

* fix: bind carried schema barrier to the deployed redesign

Record the observed staging deployment and its drained predecessors without changing the migration gate. Regenerate the tracked gating matrix for the combined source. Refs thomasluizon/orbit-tickets#746.

* fix: retain deletion cancellation ownership across provider retries (#770)

* fix: persist deletion cancellation ownership before provider updates

* test: cover deletion intent commit and account changes

* fix: identify deletion-owned Stripe cancellations

Reconcile provider metadata before restoring renewal or retaining ownership, reject open cancellation transactions, and log skipped erasure rechecks.

Refs thomasluizon/orbit-tickets#1547

(cherry picked from commit a7d1cf2)

* test: drop the deprecation assertions the legacy Google route removal supersedes

The base merge brought #769's assertions that POST /api/Auth/google and
POST /api/Auth/operations/google stay available and deprecated. This carry
removes both routes (main's #1528 part 2), so those assertions no longer
have a subject.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
thomasluizon added a commit that referenced this pull request Oct 19, 2026
…d cancellations and the Play renewal notice (#746) (#778)

* Deprecate AI memory HTTP contracts for ORB-229 (part 1) (#771)

(cherry picked from commit f8abd02)

* Deprecate challenge and accountability routes for ORB-212 (#772)

(cherry picked from commit 572b8de)

* Deprecate social API routes and required profile fields (ORB-233 part 1) (#773)

(cherry picked from commit 9c414c7)

* fix: exclude retired notifications from inbox counts (#774)

(cherry picked from commit f5df366)

* fix: retain owed cancellations after account erasure (#775)

* fix: retain provider cancellation obligations after account erasure

* fix: preserve erasure for accounts without billing obligations

* fix: claim owed cancellations before provider calls

* fix: alert owed cancellation exhaustion only after a failed attempt

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit c369535)

* fix: notify returning Play subscribers about renewal (#776)

* fix: explain Play renewal recovery on account reactivation

* test: register reactivation service in sign-in retry fixture

* fix: verify Play renewal before reactivation notice

(cherry picked from commit 96023f9)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
thomasluizon added a commit that referenced this pull request Oct 19, 2026
…nd accountability removal (#746) (#785)

* ORB-202: request 3D Secure for subscription checkout (#780)

* fix(billing): request 3D Secure for subscription checkout

* fix(billing): key checkout retries by the 3D Secure policy

A checkout request with the new card authentication option must not reuse
an idempotency key that a request without it created, because Stripe
rejects changed parameters for a retained key.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 46e3aac)

* Remove challenge and accountability endpoints (ORB-212) (#779)

* feat: remove challenge and accountability endpoints (ORB-212)

* fix: exclude removed achievements from earned totals (ORB-212)

(cherry picked from commit c75ab1b)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
thomasluizon added a commit that referenced this pull request Oct 19, 2026
…grounding (#746) (#788)

* Remove AI memory runtime surfaces (ORB-229) (#783)

* Remove AI memory runtime surfaces (ORB-229)

* Preserve profile memory compatibility and finish removal cleanup (ORB-229)

(cherry picked from commit 8e91cf6)

* feat(chat): ground Astra replies in current-turn tool facts (#784)

* feat(chat): ground Astra text in current-turn tool results

* test(chat): cover grounding modes and current-turn provenance

* fix(chat): check localized numbers and exclude numeric identifiers

* test(chat): align habit prompt coverage with tool grounding

* fix(chat): ground schedule answers in canonical day facts

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit f5446e1)

* test: list the redesign-only export sections in the carried export assertion

The AI memory removal (8e91cf6) asserts the full export property set as main
has it; redesign/main also exports proactiveItems, notionConnection and
notionTasks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
thomasluizon added a commit that referenced this pull request Oct 19, 2026
…EADME (#746) (#790)

* Remove social runtime surfaces (ORB-233) (#787)

* Remove social runtime surfaces (ORB-233)

* Finish social signup assertions and reference cleanup

* ci: exclude the retained public profile shape from coverage

The record is never built after the social removal; it stays only so the
profile response schema keeps its shape for old clients until #685, and the
committed OpenAPI snapshot holds that shape.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 7c79309)

* docs: point the grounding mode switch at the Render database (#789)

Production data lives on Render Postgres; the Supabase project is deleted.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 3330b8c)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
thomasluizon added a commit that referenced this pull request Oct 19, 2026
…uppression cleanup (#746) (#793)

* feat: expand habit schedule model (ORB-221, step 1 of 3) (#792)

* test: characterize habit schedules before expand step (ORB-221)

* feat: expand habit schedule and immutable start date (ORB-221)

* test: include habit expansion in schema preservation guard

* fix: address habit schedule review (thomasluizon/orbit-tickets#227)

* fix: preserve FrequencyUnit OpenAPI contract (ORB-221)

(cherry picked from commit 7a708b5)

* Clear handwritten suppressions and audit retained exemptions (ORB-224) (#791)

* fix: recognize UTC timestamp sinks without date exemptions

* fix: guard generated logging arguments and remove blanket disables

* refactor: remove catalog suppression scaffolding without changing metadata

* fix: forward cancellation and remove obsolete library suppressions

* chore: record every suppression verdict and reject duplicate reasons

* fix: address suppression audit review for ticket 230

* fix: resolve suppression cleanup CI findings

Refs thomasluizon/orbit-tickets#230

* test: prove OAuth authorize query validation parity

(cherry picked from commit 05ab55f)
thomasluizon added a commit that referenced this pull request Oct 20, 2026
… and bulk reparenting (#746) (#827)

* fix: expose persisted habit recurrence origins (#820)

(cherry picked from commit 6e4a3a5)

* feat: support bounded habit log date ranges (#822)

(cherry picked from commit 906f9e4)

* Add bulk habit reparenting (#821)

* refactor: share habit hierarchy validation helpers

* feat: move selected habits under a parent atomically

* fix: register bulk parent action in the capability catalog

(cherry picked from commit f67708f)
thomasluizon added a commit that referenced this pull request Oct 20, 2026
thomasluizon added a commit that referenced this pull request Oct 20, 2026
…e sub-habits and referral Pro days (#746) (#829)

* fix: preserve yearly streak gap repairs through recalculation (#818)

* 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 (#819)

* fix: expose discounted Stripe billed amount per period

* fix: isolate optional recurring Stripe billing preview

(cherry picked from commit 0460a54)

* Make sub-habits free across every creation path (ORB-231) (#824)

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

* Grant atomic referral Pro days (ORB-22) (#823)

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

* test: retain combined migration assertions after main carry

* fix: include one-time coupons in billed amount preview (#830)

(cherry picked from commit 377eca5)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant