---
id: CAP-MBR-MEM-006
title: Derive package grants and organizational child memberships
feature: FEAT-MBR-MEM-01
product_area: membership
module: memberships
revision: 1
status: draft
product_intent: confirmed
implementation_evidence: partially-observed
supersedes:
  - 'inventory:CAP-MBR-MEM-012'
  - 'inventory:CAP-MBR-MEM-017'
  - 'inventory:CAP-MBR-AFF-009'
  - 'inventory:CAP-MBR-AFF-010'
  - 'inventory:CAP-MBR-DAT-013'
  - 'inventory:CAP-MBR-CFG-016'
source_sha:
  services-membership: 5994c270e8a37a30f848e99ba5b5ee6b87c08893
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
intent_sources:
  - 'services-membership:README.md#2026-10-18 — Final acceptance'
  - 'services-membership:modules/memberships/services/grants/apply-grants.ts (the decision of 2026-10-17)'
purpose: >-
  A package can supply additional program coverage, while an organizational membership can provide dependent
  coverage to eligible people.
outcomes:
  - Package grants create additional periods for the same owner.
  - Eligible organizational contacts receive dependent membership periods.
  - Delivered CRM changes trigger recalculation of affected organizational coverage.
concepts:
  - CON-MBR-MEM-001
  - CON-MBR-MEM-002
actors:
  - ACT-MBR-007
  - ACT-MBR-012
surfaces:
  - api
  - job
permissions:
  - key: membership.memberships.create
    where_checked: api
    scope: tenant
  - key: membership.terms.create
    where_checked: api
    scope: tenant
rules:
  - id: BR-CAP-MBR-MEM-006-01
    statement: >-
      Committing a package term applies its non-self grants in the same transaction, creating a principal
      membership in each target program when needed and linking the granted term to its source.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: observed
    source: 'services-membership:modules/memberships/services/grants/apply-grants.ts:applyPackageGrants'
    origin: >-
      the decision of 2026-10-17 on package grants
      (services-membership:modules/memberships/services/grants/apply-grants.ts header)
  - id: BR-CAP-MBR-MEM-006-02
    statement: >-
      A same-as-primary grant uses the source dates; an own-rule grant computes its period without starting
      before the source, and package renewal runs the grant calculation again.
    kind: calculation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-membership:modules/memberships/tests/unit/grant-plan.spec.ts#plans a granted term by the
      grant’s own rule, never starting before the primary term
    origin: none
  - id: BR-CAP-MBR-MEM-006-03
    statement: >-
      A planned grant overlapping any effective term already held in the target membership is skipped and
      identifies the overlapping term.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-membership:modules/memberships/tests/unit/grant-plan.spec.ts#skips a grant the member already
      holds for those dates, naming the term
    origin: none
  - id: BR-CAP-MBR-MEM-006-04
    statement: >-
      Organizational flow-down selects contacts with currently effective configured roles and affiliations,
      then mirrors parent terms using the configured flow-down type or the parent type when none is specified.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: observed
    source: >-
      services-membership:modules/memberships/services/flowdown/organization-children.ts:syncOrganizationChildren
    origin: none
  - id: BR-CAP-MBR-MEM-006-05
    statement: >-
      When cascading is enabled, flow-down considers qualifying contacts in descendant organizations matching
      the configured types, through depth ten.
    kind: calculation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-membership:modules/memberships/tests/integration/services/organization-children.integration.spec.ts#cascades
      to the contacts of child organizations down to depth 10 and no further
    origin: none
  - id: BR-CAP-MBR-MEM-006-06
    statement: >-
      Assigned-seat derivation takes the first contacts from decided applications up to the package limit
      before excluding contacts already covered by flow-down.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: observed
    source: 'services-membership:modules/memberships/services/flowdown/organization-children.ts:wantedPeople'
    origin: none
  - id: BR-CAP-MBR-MEM-006-07
    statement: >-
      Recalculating an organizational parent updates dependent mirrors to follow corrected dates and term
      closure.
    kind: invariant
    enforced_at:
      - service
    must_prove: true
    provenance: observed
    source: >-
      services-membership:modules/memberships/services/flowdown/organization-children.ts:syncOrganizationChildren
    origin: none
  - id: BR-CAP-MBR-MEM-006-08
    statement: >-
      On loss of eligibility, a started child term ends on the handling day, limited by its existing end,
      while a future child term is cancelled.
    kind: lifecycle
    enforced_at:
      - service
    must_prove: true
    provenance: observed
    source: >-
      services-membership:modules/memberships/services/flowdown/organization-children.ts:syncOrganizationChildren
    origin: none
  - id: BR-CAP-MBR-MEM-006-09
    statement: >-
      The CRM change handler recalculates affected organizational memberships from current CRM rows, including
      the current ancestor chain, so duplicate deliveries do not rely on event ordering.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: observed
    source: 'services-membership:modules/memberships/services/flowdown/crm-change-events.ts:handleCrmChangeEvents'
    origin: none
  - id: BR-CAP-MBR-MEM-006-10
    statement: >-
      The product should recalculate the former organizational parent after a CRM reparenting so people lose
      coverage derived from the old hierarchy.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: unknown
    source: FND-MBR-MEM-019
    origin: none
  - id: BR-CAP-MBR-MEM-006-11
    statement: >-
      Organizational children receive program numbers, are excluded from billing, and accept term changes only
      through their principal parent.
    kind: policy
    enforced_at:
      - service
    must_prove: false
    provenance: observed
    source: >-
      services-membership:modules/memberships/services/flowdown/organization-children.ts:syncOrganizationChildren;
      modules/billing/services/planning/run-plan.ts:exclusionOf;
      modules/memberships/services/operations/staff-operations.ts:refuseChild
    origin: none
  - id: BR-CAP-MBR-MEM-006-12
    statement: An organization-owned membership must have principal origin.
    kind: invariant
    enforced_at:
      - database
    must_prove: false
    provenance: observed
    source: 'services-membership:modules/memberships/db/memberships.table.ts:memberships_organization_principal_check'
    origin: none
scenarios:
  - id: SC-CAP-MBR-MEM-006-01
    given: Acme has a corporate type configured for employee flow-down and cascading.
    when: Staff create its corporate membership with a term.
    then: Eligible contacts receive mirrored Employee terms through the permitted organizational depths.
    provenance: test
    source: >-
      services-membership:tests/acceptance/affiliations/MBR-ACC-0274.acceptance.ts#MBR-ACC-0274: Acme's
      corporate membership reaches its CEO, its engineers and the people of its subsidiaries ten levels down,
      each with a mirrored employee term, and follows the parent through correction, termination, suspension
      and restore
  - id: SC-CAP-MBR-MEM-006-02
    given: A member’s bundle includes a grant into a section program.
    when: The bundle receives a renewed term.
    then: The section receives its next grant unless existing effective coverage overlaps the planned period.
    provenance: test
    source: >-
      services-membership:tests/acceptance/memberships/MBR-ACC-0181.acceptance.ts#MBR-ACC-0181: A bundle
      package grants a section membership with the national one, renews it with each renewal, skips a grant
      the member already holds, ignores a grant into its own program, and the granted membership stands on its
      own
edge_cases: []
constraints:
  - Cascading flow-down searches at most ten descendant levels.
depends_on:
  - CAP-MBR-MEM-001
related:
  - CAP-MBR-MEM-003
  - CAP-MBR-MEM-004
  - CAP-MBR-MEM-012
journeys: []
open_questions: []
evidence:
  - repository: services-membership
    source_sha: 5994c270e8a37a30f848e99ba5b5ee6b87c08893
    paths:
      - modules/memberships/services/grants/grant-plan.ts
      - modules/memberships/services/grants/apply-grants.ts
      - modules/memberships/services/flowdown/organization-children.ts
      - modules/memberships/services/flowdown/crm-change-events.ts
      - modules/memberships/services/persistence/crm-people.ts
      - modules/memberships/aws/lambda/CRM_CHANGE_HANDLER.ts
      - modules/memberships/tests/unit/grant-plan.spec.ts
      - modules/memberships/tests/integration/services/organization-children.integration.spec.ts
      - tests/acceptance/affiliations
      - tests/acceptance/memberships
  - repository: react-console
    source_sha: baf6dae382d6f41dce976c7f848667ccc37e076f
    paths:
      - acceptance/membership/reports/defects/affiliations.md
      - acceptance/membership/reports/defects/data-operations.md
controls: [TC-AVL-002, TC-AVL-003, TC-AVL-004, TC-AZ-001, TC-AZ-002, TC-AZ-003, TC-AZ-004, TC-AZ-006, TC-CHG-001, TC-CHG-002, TC-CHG-003, TC-CHG-004, TC-CRY-001, TC-CRY-002, TC-CRY-003, TC-DAT-001, TC-DAT-002, TC-DAT-003, TC-DAT-007, TC-IAM-001, TC-IAM-002, TC-IAM-004, TC-IAM-005, TC-IAM-006, TC-LOG-002, TC-LOG-003, TC-LOG-004, TC-LOG-005, TC-MON-001, TC-MON-002, TC-MON-004, TC-PI-001, TC-PI-002, TC-PI-003, TC-PI-004, TC-PI-005, TC-PRV-001, TC-PRV-002, TC-SEC-001, TC-SEC-004, TC-TEN-001, TC-TEN-002, TC-TEN-003]
---

# Derive package grants and organizational child memberships

## Purpose

Staff use bundled packages and corporate memberships to provide coverage beyond the original period’s immediate purpose. The distinction matters: a package grant creates a term on a principal membership, while organizational flow-down and assigned seats create dependent memberships.

## How It Works

The membership write invokes the package’s grant plan and reports planned and skipped grants. Staff can inspect the additional program periods after the write. An organizational membership follows a different derivation, based on CRM relationships and contacts selected for assigned seats. Current and future parental periods are reconsidered against current eligibility; ended periods preserve the people they previously covered. Corrections and closures cause existing child mirrors to be reconsidered. CRM deliveries provide another trigger for the same derivation. Staff should inspect the seat limit alongside people already covered by flow-down, because the selection can consume a slot for someone already covered. The former-parent reparenting finding and CRM subscription dependency remain unresolved.

## Relationships

- Staff creation and term extension trigger grant application and organizational derivation.
- CRM Entities supplies the organizational hierarchy and effective contact roles.
- Application decisions supply the contacts used for assigned seats.
