---
id: CAP-PFM-ELG-001
title: Author a readable eligibility rule
feature: FEAT-PFM-ELG-01
product_area: platform
module: eligibility
revision: 1
status: draft
product_intent: confirmed
implementation_evidence: observed
supersedes:
  - CAP-ELIG-DEF-001
  - CAP-ELIG-DEF-004
  - CAP-ELIG-DEF-005
  - CAP-ELIG-DEF-006
  - CAP-ELIG-DEF-008
  - CAP-ELIG-DEF-009
  - CAP-ELIG-DEF-010
  - CAP-ELIG-DEF-011
  - CAP-ELIG-DEF-015
  - CAP-ELIG-REG-013
source_sha:
  services-platform: ad3f6a6582843008ed0f5a17b131878d15829af1
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
intent_sources:
  - 'services-platform:docs/milestones/plans/PLAT-M11-plan.md#2. Scope'
  - 'services-platform:docs/milestones/plans/PLAT-M11-plan.md#A5'
  - 'services-platform:docs/milestones/plans/PLAT-M11-plan.md#D6'
  - 'services-platform:docs/proposals/eligibility/17-decision-register.md#D-11'
  - 'services-platform:docs/proposals/eligibility/17-decision-register.md#D-27'
purpose: >-
  Administrators need one understandable definition of who qualifies across the
  organization. Authoring turns application conditions into a rule that staff
  can review before releasing it.
outcomes:
  - Staff reuse a rule through its permanent readable key.
  - Validation identifies problems before the expression is relied on.
  - A sentence explains the authored expression.
concepts:
  - CON-PFM-ELG-001
actors:
  - ACT-PFM-023
  - ACT-PFM-024
surfaces:
  - console
  - api
preconditions: []
postconditions: []
permissions:
  - key: platform.eligibility.write
    where_checked: api
    scope: tenant
  - key: platform.eligibility.read
    where_checked: api
    scope: tenant
  - key: platform.eligibility.publish
    where_checked: service
    scope: tenant
rules:
  - id: BR-CAP-PFM-ELG-001-01
    proved_by: [ATS-PFM-ELG-001-01, TST-PFM-ELG-097, TST-PFM-ELG-410, TST-PFM-ELG-450]
    statement: >-
      A readable key identifies the rule from creation, uses lowercase letters
      and digits separated by single underscores, begins with a letter and
      contains no more than 63 characters, while creation refuses reserved route
      names and the system key prefix.
    kind: validation
    enforced_at:
      - api
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/eligibility/tests/integration/services/persistence/definitions.integration.spec.js#refuses
      an id that is not a configuration id, before anything is written
    origin: 'services-platform:docs/milestones/plans/PLAT-M11-plan.md#A5'
    examples:
      positive: member_in_good_standing
      negative: Member-In-Good-Standing
  - id: BR-CAP-PFM-ELG-001-02
    proved_by: [ATS-PFM-ELG-001-02, TST-PFM-ELG-411, TST-PFM-ELG-450]
    statement: >-
      Within a workspace each rule key is unique, while separate workspaces can
      choose the same key.
    kind: invariant
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/eligibility/tests/integration/services/persistence/definitions.integration.spec.js#refuses
      a second definition with the same id (key) in the tenant, naming it
    origin: 'services-platform:docs/milestones/plans/PLAT-M11-plan.md#A5'
  - id: BR-CAP-PFM-ELG-001-03
    proved_by: [ATS-PFM-ELG-001-03, TST-PFM-ELG-411, TST-PFM-ELG-450]
    statement: 'Rule keys are permanent, and an update that supplies a key is rejected.'
    kind: invariant
    enforced_at:
      - api
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/eligibility/tests/e2e/eligibility-definitions.e2e.spec.js#refuses
      an edit that names the key, which never changes, 400
    origin: 'services-platform:docs/milestones/plans/PLAT-M11-plan.md#A5'
  - id: BR-CAP-PFM-ELG-001-04
    proved_by: [ATS-PFM-ELG-001-04, ATS-PFM-ELG-001-17, ATS-PFM-ELG-001-18, ATS-PFM-ELG-003-06, ATS-PFM-ELG-004-13, TST-PFM-ELG-104, TST-PFM-ELG-411]
    statement: >-
      Saving retains a draft with validation problems and returns their codes
      and its readable summary, but errors prevent publication.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/eligibility/tests/e2e/eligibility-definitions.e2e.spec.js#answers
      the draft’s problem, saved rather than refused
    origin: >-
      services-platform:docs/milestones/plans/PLAT-M11-plan.md#3. How it works
      for the user
  - id: BR-CAP-PFM-ELG-001-05
    proved_by: [ATS-PFM-ELG-001-05, TST-PFM-ELG-101, TST-PFM-ELG-438]
    statement: >-
      Each rule has one replaceable draft; supplying an outdated draft version
      rejects a save, while omitting the version overwrites the current draft.
    kind: lifecycle
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/eligibility/tests/integration/services/model/authoring.integration.spec.js#replaces
      the draft in place on the next save, moving its version
    origin: >-
      services-platform:docs/milestones/plans/PLAT-M11-plan.md#Appendix A.
      Behaviours that stand as built unless Andrew objects
  - id: BR-CAP-PFM-ELG-001-06
    proved_by: [ATS-PFM-ELG-001-06, ATS-PFM-ELG-001-16, ATS-PFM-ELG-002-09, TST-PFM-ELG-438]
    statement: >-
      Creation without an expression leaves the rule without a version;
      supplying an expression creates its first draft version.
    kind: lifecycle
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/eligibility/tests/integration/services/model/authoring.integration.spec.js#creates
      a definition without a draft when the request carries no expression
    origin: none
  - id: BR-CAP-PFM-ELG-001-07
    proved_by: [ATS-PFM-ELG-001-07, ATS-PFM-ELG-001-19, ATS-PFM-ELG-006-09, TST-PFM-ELG-441]
    statement: >-
      Validation identifies unavailable rule references and cycles, unsupported
      facts, absent workspace custom fields and a primary subject whose kind
      conflicts with the rule.
    kind: validation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/eligibility/tests/integration/services/model/validate-definition.integration.spec.js#answers
      a reference to an archived definition as missing
    origin: none
  - id: BR-CAP-PFM-ELG-001-08
    proved_by: [ATS-PFM-ELG-001-08, TST-PFM-ELG-413, TST-PFM-ELG-480]
    statement: >-
      Parameter declarations require ordered minimum and maximum bounds and a
      default compatible with the declared type, bounds and available options.
    kind: validation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/eligibility/tests/e2e/eligibility-governance-repairs.e2e.spec.js#refuses
      min above max, naming the parameter
    origin: none
  - id: BR-CAP-PFM-ELG-001-09
    proved_by: [ATS-PFM-ELG-001-09, TST-PFM-ELG-106]
    statement: >-
      Rules allow at most 200 conditions and groups, depth eight, 50 children
      per group, 20 references and three reference levels; readable summaries
      stop at 4,000 characters.
    kind: validation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      react-console:acceptance/eligibility/playwright/journeys/definitions/ELIG-ACC-0061.spec.ts#ELIG-ACC-0061
    origin: none
  - id: BR-CAP-PFM-ELG-001-10
    proved_by: [ATS-PFM-ELG-001-10, TST-PFM-ELG-107, TST-PFM-ELG-440]
    statement: >-
      Readable summaries use available labels for roles, choices, organizations
      and custom fields, retaining the underlying reference when its label
      cannot be resolved.
    kind: calculation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/eligibility/tests/integration/services/model/role-names.integration.spec.js#says
      the roles by name in the summary of a validation
    origin: none
  - id: BR-CAP-PFM-ELG-001-11
    proved_by: [ATS-PFM-ELG-001-11, ATS-PFM-ELG-001-20, ATS-PFM-ELG-007-15, TST-PFM-ELG-102, TST-PFM-ELG-411, TST-PFM-ELG-438]
    statement: Sharing a previously private rule requires authority to publish rules.
    kind: permission
    enforced_at:
      - api
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/eligibility/tests/e2e/eligibility-definitions.e2e.spec.js#refuses
      a writer without the publish permission the promotion of a private
      definition, 403
    origin: 'services-platform:docs/milestones/plans/PLAT-M11-plan.md#D6'
  - id: BR-CAP-PFM-ELG-001-12
    proved_by: [ATS-PFM-ELG-001-12, TST-PFM-ELG-110, TST-PFM-ELG-438]
    statement: >-
      An unspecified duplication source uses the latest published expression,
      then the draft, or copies a bare rule; explicitly requesting an absent
      source is rejected, and copied expressions start as a workspace-owned
      first draft.
    kind: lifecycle
    enforced_at:
      - service
    must_prove: true
    provenance: observed
    source: >-
      services-platform:modules/eligibility/services/model/authoring.ts:duplicateDefinition
    origin: none
  - id: BR-CAP-PFM-ELG-001-13
    proved_by: [ATS-PFM-ELG-001-13, TST-PFM-ELG-111, TST-PFM-ELG-462, TST-PFM-ELG-471]
    statement: >-
      Character limits are 1,000 for a change note or portal message, 200 for a
      name and 2,000 for a description, with no more than 20 tags per rule.
    kind: validation
    enforced_at:
      - api
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/eligibility/tests/integration/services/persistence/versions.integration.spec.js#refuses
      a change note of 1001 astral characters naming the limit
    origin: none
  - id: BR-CAP-PFM-ELG-001-14
    proved_by: [ATS-PFM-ELG-001-14, TST-PFM-ELG-116, TST-PFM-ELG-438, TST-PFM-ELG-450]
    statement: An archived rule must be restored before another draft can be saved.
    kind: lifecycle
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/eligibility/tests/integration/services/model/authoring.integration.spec.js#refuses
      a draft of an archived definition
    origin: none
  - id: BR-CAP-PFM-ELG-001-15
    proved_by: [ATS-PFM-ELG-001-15, TST-PFM-ELG-410]
    statement: >-
      Creation and duplication accept the readable identity or its legacy key
      alias, which must agree when supplied together; absence or conflict is
      refused, and responses expose the same value under both names.
    kind: validation
    enforced_at:
      - api
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/eligibility/tests/e2e/eligibility-definition-key-alias.e2e.spec.js#refuses
      id and key that differ, 400, naming both
    origin: none
scenarios:
  - id: SC-CAP-PFM-ELG-001-01
    given: >-
      A director drafts a qualification expression containing an unknown
      condition.
    when: They save and attempt publication.
    then: >-
      The saved work retains its problem and the release fails with that problem
      identified.
    provenance: test
    source: >-
      services-platform:modules/eligibility/tests/e2e/eligibility-definitions.e2e.spec.js#refuses
      the publish while the problem exists, 400, naming it
  - id: SC-CAP-PFM-ELG-001-02
    given: A product-specific expression remains private.
    when: A writer without release authority attempts to share it.
    then: Sharing fails for that writer and succeeds for a publish-authorized user.
    provenance: test
    source: >-
      react-console:acceptance/eligibility/playwright/journeys/definitions/ELIG-ACC-0057.spec.ts#ELIG-ACC-0057
edge_cases:
  - id: SC-CAP-PFM-ELG-001-03
    given: Two colleagues have read the same draft.
    when: 'Both save, with the later request omitting its version.'
    then: >-
      The later request replaces the earlier draft without a warning under the
      approved current behavior.
    provenance: test
    source: >-
      react-console:acceptance/eligibility/playwright/journeys/definitions/ELIG-ACC-0056.spec.ts#ELIG-ACC-0056
constraints: []
depends_on: []
related:
  - CAP-PFM-ELG-002
  - CAP-PFM-ELG-003
  - CAP-PFM-ELG-004
  - CAP-PFM-ELG-006
  - CAP-PFM-ELG-007
journeys:
  - JNY-PFM-ELG-001
open_questions: []
evidence:
  - >-
    services-platform: docs/eligibility.md;
    docs/milestones/PLAT-M11-eligibility-rules.md and plans/PLAT-M11-plan.md;
    docs/proposals/eligibility/17-decision-register.md; aws/platform.service.ts
    (ELIGIBILITY_PERMISSIONS);
    modules/eligibility/aws/api/definitions.routes.ts, eligibility.schemas.ts,
    services/model/authoring.ts, validate-definition.ts, role-names.ts,
    option-labels.ts, rule-names.ts, services/persistence/definitions.ts,
    versions.ts, binding-fit.ts (declarationProblems), code-points.ts,
    db/eligibility-definitions.table.ts, eligibility-versions.table.ts,
    eligibility-dependencies.table.ts; rule-level source and test references
  - >-
    react-console: components/configuration/resources/eligibility-rules.tsx,
    components/eligibility/definition-editor.tsx,
    components/eligibility/builder/**, lib/eligibility/**;
    acceptance/eligibility/journeys/definitions/*.json and matching browser
    specifications; acceptance/eligibility/inventory/capabilities.json (area
    DEF).
controls: [TC-AVL-005, TC-AZ-001, TC-AZ-002, TC-AZ-003, TC-AZ-004, TC-IAM-001, TC-IAM-002, TC-IAM-004, TC-IAM-005, TC-LOG-004, TC-LOG-005, TC-PI-001, TC-PI-003, TC-PI-005, TC-TEN-001, TC-TEN-002, TC-TEN-003, TC-TEN-004, TC-VEN-001, TC-VEN-002, TC-VEN-003]
exercised_by: [FAS-PFM-ELG-01-01, FAS-PFM-ELG-01-03, FAS-PFM-ELG-01-04, FAS-PFM-ELG-01-05]
suites: [TST-PFM-ELG-097, TST-PFM-ELG-101, TST-PFM-ELG-102, TST-PFM-ELG-104, TST-PFM-ELG-106, TST-PFM-ELG-107, TST-PFM-ELG-110, TST-PFM-ELG-111, TST-PFM-ELG-116, TST-PFM-ELG-410, TST-PFM-ELG-411, TST-PFM-ELG-413, TST-PFM-ELG-438, TST-PFM-ELG-440, TST-PFM-ELG-441, TST-PFM-ELG-450, TST-PFM-ELG-462, TST-PFM-ELG-471, TST-PFM-ELG-480]
---
# Author a readable eligibility rule

## Purpose

Administrators need one understandable definition of who qualifies across the organization. Authoring turns application conditions into a rule that staff can review before releasing it.

## How It Works

The administrator starts in the configuration catalogue and creates a named rule about people or organizations. The builder supplies the available conditions and grouping choices. Its summary helps the author review the expression in familiar words. Saving preserves unfinished work and reports problems beside it. The author fixes those problems before requesting publication. Testing provides a separate way to inspect examples. Colleagues can work on drafts, but an unversioned save can replace another person’s work. Copies provide a starting point for a new rule. Private product rules need a publisher’s involvement before joining the shared library.

## Relationships

Authoring uses the application vocabulary and leads into trial evaluation and release review.
