Skip to content

refactor(organizations): emit organization.created and move signup grant to a billing subscriber#3957

Merged
PierreBrisorgueil merged 2 commits into
masterfrom
refactor/3952-organization-created-seam
Jul 16, 2026
Merged

refactor(organizations): emit organization.created and move signup grant to a billing subscriber#3957
PierreBrisorgueil merged 2 commits into
masterfrom
refactor/3952-organization-created-seam

Conversation

@PierreBrisorgueil

@PierreBrisorgueil PierreBrisorgueil commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Summary

  • What changed: organizations.crud.service.js::create and organizations.service.js::createOrganizationForUser no longer import BillingSignupGrantService directly. Both now emit an organization.created event through the module's existing event seam (modules/organizations/lib/events.js, which already carries organization.provisioned). Billing subscribes to organization.created in billing.init.js and issues the signup grant from there.
  • Why: organizations previously reached into billing from two call sites (the newer organizations.crud.service.js path added in fix(billing): credit signupGrant on the generic org-creation path #3949, and the older organizations.service.js path since feat(billing): credit signupGrant on org creation post-signup #3663), making the coupling bidirectional and breaking the module-removability guarantee — organizations should stay decoupled and optional from billing's perspective. The module already has a sanctioned seam for exactly this kind of fan-out (organization.provisionedbilling.init.js); this PR adds the creation-side counterpart instead of inventing a new pattern.
  • Related issues: Closes 🔧 Decouple signup grant from organization creation via a creation-side seam #3952

Scope

  • Module(s) impacted: organizations (emit event only, no behavior change to org creation), billing (new subscriber in billing.init.js that owns the grant call)
  • Cross-module impact: yes — billing now listens on the organizations event bus instead of organizations importing billing directly; the dependency direction flips to match the existing organization.provisioned precedent. No public API/route changes.
  • Risk level: low — grant issuance stays idempotent via refId and a grant failure still never blocks or rolls back org creation (same guarantee as before, just moved behind the event boundary).

Validation

  • npm run lint
  • npm test
  • Manual checks done (if applicable)

Guardrails check

  • No secrets or credentials introduced (.env*, secrets/**, keys, tokens)
  • No risky rename/move of core stack paths
  • Changes remain merge-friendly for downstream projects
  • Tests added or updated when behavior changed

Notes for reviewers

  • Security considerations: none — no new external surface, grant logic unchanged, just relocated behind the event seam.
  • Mergeability considerations: unit suite (2138) + integration suite (488) green locally. Async timing in the crud/service unit and integration tests was adapted to the existing waitForLedgerEntry poll pattern already used elsewhere in the billing tests, since the grant now fires from an event handler instead of an inline await.
  • Follow-up tasks (optional): none — this closes out both call sites (fix(billing): credit signupGrant on the generic org-creation path #3949's new path and the older feat(billing): credit signupGrant on org creation post-signup #3663 path) in one pass, per the issue scope.

https://claude.ai/code/session_01WfNC8bt1TgL4AsiYgCEGup

Summary by CodeRabbit

  • New Features

    • Organization creation now triggers an asynchronous signup credit of 500 credits for eligible new organizations.
    • Signup credit processing is isolated from organization creation, so billing issues do not prevent successful organization setup.
  • Documentation

    • Clarified the organization-created event, including its timing, payload, and processing behavior.
  • Bug Fixes

    • Added safeguards to prevent signup credit failures from interrupting organization creation.

…ant to a billing subscriber

Organizations previously imported BillingSignupGrantService directly from two
call sites (organizations.crud.service.js::create, added #3949, and the older
organizations.service.js::createOrganizationForUser, since #3663), making
billing<->organizations coupling bidirectional and breaking the module-
removability guarantee.

Adds a creation-side seam mirroring the existing organization.provisioned
event: both call sites now emit `organization.created` ({ orgId, planId })
on the organizations events singleton instead of calling billing directly.
billing.init.js subscribes next to the existing organization.provisioned
listener and credits the one-shot signupGrant, following the same
fire-and-forget / self-guarded pattern (listener errors swallowed+logged,
grant failure never rolls back org creation, refId idempotence unchanged).

Adapts organizations.integration.tests.js and billing.referral.integration.tests.js
to the new async-listener timing (poll for the ledger entry / wait for it to
settle before snapshotting balances), mirroring the existing waitForLedgerEntry
pattern already used for the referral grant listener.

Closes #3952

Claude-Session: https://claude.ai/code/session_01WfNC8bt1TgL4AsiYgCEGup
@PierreBrisorgueil PierreBrisorgueil added the Refactor Neither fixes a bug nor adds a feat label Jul 16, 2026
@PierreBrisorgueil PierreBrisorgueil self-assigned this Jul 16, 2026
@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@PierreBrisorgueil, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 50 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 63b5135a-4bc6-4dc9-b74d-380c7303ebf2

📥 Commits

Reviewing files that changed from the base of the PR and between 797bcf8 and fde171e.

📒 Files selected for processing (1)
  • modules/billing/tests/billing.referral.integration.tests.js

Walkthrough

Organization creation now emits a guarded organization.created event after durable creation. Billing subscribes to the event and asynchronously invokes grantOnSignup; tests cover event wiring, failure isolation, exactly-once emission, and asynchronous ledger synchronization.

Changes

Signup grant event integration

Layer / File(s) Summary
Creation event seam
modules/organizations/lib/events.js, modules/organizations/services/*.js
Organization creation flows emit organization.created with orgId and planId, replacing direct billing service calls and preserving failure isolation.
Creation event validation
modules/organizations/tests/*.unit.tests.js
Unit tests verify guarded emission, payloads, exactly-once behavior, convergence exclusions, and listener error handling.
Billing signup listener
modules/billing/billing.init.js, modules/billing/tests/billing.init.unit.tests.js
Billing registers the event listener and forwards payloads to BillingSignupGrantService.grantOnSignup, swallowing and logging failures.
Asynchronous grant validation
modules/organizations/tests/organizations.integration.tests.js, modules/billing/tests/billing.referral.integration.tests.js
Integration tests wait for signup-grant ledger entries before asserting balances and referral replay behavior.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant OrganizationsService
  participant organizationEvents
  participant billingInit
  participant BillingSignupGrantService
  OrganizationsService->>organizationEvents: emit organization.created
  organizationEvents->>billingInit: invoke registered listener
  billingInit->>BillingSignupGrantService: grantOnSignup with orgId and planId
Loading

Possibly related issues

  • Issue 3954 — Both changes touch the organization creation flow, but the provided summary indicates different behavior.

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: emitting organization.created and moving signup grants into billing.
Description check ✅ Passed The description follows the template well, covering summary, scope, validation, guardrails, and notes.
Linked Issues check ✅ Passed The changes match #3952 by adding organization.created, moving grant logic to billing, and preserving non-blocking idempotent grants.
Out of Scope Changes check ✅ Passed The changes appear aligned with the issue scope; the test and event-doc updates support the new event flow rather than adding unrelated behavior.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/3952-organization-created-seam

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Jul 16, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.71%. Comparing base (7c7c0bf) to head (fde171e).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #3957      +/-   ##
==========================================
+ Coverage   92.70%   92.71%   +0.01%     
==========================================
  Files         169      169              
  Lines        5563     5573      +10     
  Branches     1791     1792       +1     
==========================================
+ Hits         5157     5167      +10     
  Misses        326      326              
  Partials       80       80              
Flag Coverage Δ
integration 61.00% <41.66%> (-0.04%) ⬇️
unit 74.44% <100.00%> (+0.04%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.


Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 7c7c0bf...fde171e. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@PierreBrisorgueil
PierreBrisorgueil marked this pull request as ready for review July 16, 2026 14:04

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@modules/billing/tests/billing.referral.integration.tests.js`:
- Around line 106-111: Capture the return value of waitForLedgerEntry for
adminOrgId and assert it is not null before continuing; apply the same
capture-and-assert change to the waitForLedgerEntry call for refereeOrgId in
modules/billing/tests/billing.referral.integration.tests.js lines 106-111 and
174-177. This ensures both polled ledger entries are found and failures report
immediately.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0c33e206-e424-4922-9eb4-2a68cdaef65c

📥 Commits

Reviewing files that changed from the base of the PR and between 7c7c0bf and 797bcf8.

📒 Files selected for processing (10)
  • modules/billing/billing.init.js
  • modules/billing/tests/billing.init.unit.tests.js
  • modules/billing/tests/billing.referral.integration.tests.js
  • modules/organizations/lib/events.js
  • modules/organizations/services/organizations.crud.service.js
  • modules/organizations/services/organizations.service.js
  • modules/organizations/tests/organizations.crud.grant.unit.tests.js
  • modules/organizations/tests/organizations.emailVerification.policy.unit.tests.js
  • modules/organizations/tests/organizations.integration.tests.js
  • modules/organizations/tests/organizations.service.signup.unit.tests.js
💤 Files with no reviewable changes (1)
  • modules/organizations/tests/organizations.emailVerification.policy.unit.tests.js

Comment thread modules/billing/tests/billing.referral.integration.tests.js
…on test

CodeRabbit nit on PR #3957 — the admin and referee signup-grant polls in
billing.referral.integration.tests.js returned the ledger entry but never
asserted it, so a poll timeout would surface as a cryptic downstream balance
mismatch instead of a clear not-null failure at the source.
@PierreBrisorgueil
PierreBrisorgueil merged commit 2edb8e4 into master Jul 16, 2026
8 checks passed
@PierreBrisorgueil
PierreBrisorgueil deleted the refactor/3952-organization-created-seam branch July 16, 2026 14:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Refactor Neither fixes a bug nor adds a feat

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🔧 Decouple signup grant from organization creation via a creation-side seam

1 participant