---
id: WF-PFM-ELG-021
title: Save a product-specific rule from the product card
trigger: >-
  The product card creates and saves a private qualification rule, changes a
  shared choice or removes a rule.
product_area: platform
module: eligibility
revision: 1
status: draft
design_intent: proposed
implementation_evidence: observed
inference: []
source_sha:
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
  services-platform: ad3f6a6582843008ed0f5a17b131878d15829af1
implements:
  - CAP-PFM-ELG-007
  - CAP-PFM-ELG-001
  - CAP-PFM-ELG-004
moves:
  - STATE-PFM-ELG-002
  - STATE-PFM-ELG-003
crosses:
  - SVC-PFM-ELG-eligibility
idempotency: >-
  A stopped first save can locate its own rule and resume; new creation tries up
  to five key candidates. Each service call remains a separate operation.
concurrency: >-
  Browser orchestration uses separate transactions without a lock spanning all
  calls or both editors of the product.
hops:
  - 'n': 1
    actor_or_service: Eligibility card (browser)
    action: Create an application-owned private definition from the product context.
    anchor: 'react-console:lib/eligibility/consumer-rule-actions.ts:savePrivateRule'
    seam: sync
    on_failure: Display creation refusal and stop further calls.
    left_inconsistent: 'No later draft, release or binding is attempted.'
    observer: The pricing manager sees the card error.
    provenance: observed
    source: 'react-console:lib/eligibility/consumer-rule-actions.ts:savePrivateRule'
  - 'n': 2
    actor_or_service: Eligibility card (browser)
    action: Create a draft when needed and save the expression.
    anchor: 'react-console:lib/eligibility/api.ts:saveDefinition'
    seam: sync
    on_failure: Display draft refusal.
    left_inconsistent: >-
      A previously created rule can remain without a release; an existing bound
      product can retain its older release.
    observer: The manager sees the saved or failed stage.
    provenance: observed
    source: >-
      services-platform:modules/eligibility/services/model/authoring.ts:saveDefinitionDraft
  - 'n': 3
    actor_or_service: Eligibility card (browser)
    action: >-
      Publish the draft without simulation, effective-period choice or change
      note.
    anchor: >-
      services-platform:modules/eligibility/services/governance/publish.ts:publishDefinition
    seam: sync
    on_failure: Validation or missing publishing authority refuses the call.
    left_inconsistent: >-
      A saved draft can remain; the card blocks missing save authority before
      submission.
    observer: The pricing manager sees the blocker or service error.
    provenance: observed
    source: 'react-console:lib/eligibility/consumer-rule-actions.ts:savePrivateRule'
  - 'n': 4
    actor_or_service: Eligibility card (browser)
    action: Bind the product to follow the private release when not already bound.
    anchor: >-
      services-platform:modules/eligibility/services/bindings/consumer-bindings.ts:bindDefinition
    seam: sync
    on_failure: Display binding refusal.
    left_inconsistent: A released private rule can remain unused until a later successful bind.
    observer: The manager receives the card error.
    provenance: observed
    source: >-
      services-platform:modules/eligibility/services/bindings/consumer-bindings.ts:bindDefinition
  - 'n': 5
    actor_or_service: 'Eligibility card (browser), changing to another shared rule'
    action: >-
      When changing a shared choice, delete its existing use before binding the
      replacement.
    anchor: 'react-console:lib/eligibility/consumer-rule-actions.ts:swapShared'
    seam: sync
    on_failure: >-
      Explain that removal succeeded but replacement failed; do not restore the
      old use.
    left_inconsistent: >-
      The product can remain without a qualification rule, subject to the
      consumer’s actual enforcement integration.
    observer: The card message is the immediate warning.
    provenance: observed
    source: 'react-console:lib/eligibility/consumer-rule-actions.ts:swapShared'
  - 'n': 6
    actor_or_service: 'Eligibility card (browser), removing a private rule'
    action: >-
      Remove the use and attempt private-rule archive if retire authority is
      available.
    anchor: 'react-console:lib/eligibility/consumer-rule-actions.ts:removePrivateRule'
    seam: sync
    on_failure: Archive errors are ignored after removal.
    left_inconsistent: An unused private definition can remain outside the normal shared list.
    observer: There is no dedicated archive-failure notification.
    provenance: observed
    source: 'react-console:lib/eligibility/consumer-rule-actions.ts:removePrivateRule'
branches:
  - >-
    Private removal uses confirmation, while shared-use removal and change do
    not.
  - The atomic replacement choice remains an open product question.
waits: []
evidence:
  - >-
    react-console at baf6dae: lib/eligibility/consumer-rule-actions.ts, api.ts;
    components/eligibility/embeds/eligibility-card.tsx, private-rule-editor.tsx,
    use-consumer-binding.ts.
  - >-
    services-platform at ad3f6a6: services/model/authoring.ts;
    services/governance/publish.ts; services/bindings/consumer-bindings.ts;
    docs/milestones/plans/PLAT-M11-plan.md (D6, D7).
controls: [TC-AVL-004, TC-AVL-005, TC-AZ-001, TC-AZ-002, TC-AZ-003, TC-AZ-004, TC-DAT-004, TC-IAM-001, TC-IAM-002, TC-IAM-004, TC-IAM-005, TC-IAM-006, TC-LOG-001, TC-LOG-002, TC-LOG-003, TC-LOG-004, TC-LOG-005, TC-MON-001, TC-MON-002, TC-PI-001, TC-PI-002, TC-PI-003, TC-PI-004, TC-PI-005, TC-TEN-001, TC-TEN-002, TC-TEN-003, TC-TEN-004, TC-VEN-001, TC-VEN-002, TC-VEN-003]
---
# Save a product-specific rule from the product card

## What It Does

The product card creates and saves a private qualification rule, changes a shared choice or removes a rule.

## The Flow

```mermaid
flowchart TD
  Choice[Product card action] --> Save[Create or find private definition]
  Save --> Draft[Save draft]
  Draft --> Publish[Publish release]
  Publish --> Bind[Bind if needed]
  Choice --> Swap[Remove old shared use]
  Swap --> New[Bind replacement]
  Choice --> Remove[Unbind private rule]
  Remove --> Archive[Attempt permitted archive]
```

Create an application-owned private definition from the product context. Create a draft when needed and save the expression. Publish the draft without simulation, effective-period choice or change note. Bind the product to follow the private release when not already bound. When changing a shared choice, delete its existing use before binding the replacement. Remove the use and attempt private-rule archive if retire authority is available.

## When Something Fails

Display creation refusal and stop further calls. No later draft, release or binding is attempted. Display draft refusal. A previously created rule can remain without a release; an existing bound product can retain its older release. Validation or missing publishing authority refuses the call. A saved draft can remain; the card blocks missing save authority before submission. Display binding refusal. A released private rule can remain unused until a later successful bind. Explain that removal succeeded but replacement failed; do not restore the old use. The product can remain without a qualification rule, subject to the consumer’s actual enforcement integration. Archive errors are ignored after removal. An unused private definition can remain outside the normal shared list.

## Open Design Questions

Private removal uses confirmation, while shared-use removal and change do not. The atomic replacement choice remains an open product question.
