Rule Cascade
LearnLevels and inheritance

Override a rule

Tighten, switch off or change an inherited rule, within its override policy.

A child changes an inherited rule under overrides.rules. It may set severity, enabled, acknowledgement and acceptance, and must give a reason. The rule's overridePolicy decides what is allowed: tighten-only (the default) only makes the rule stricter, open allows any change, and locked allows none.

Syntax

override an inherited rule
overrides:
  rules:
    - rule: <inherited rule id>
      set: { severity: warning, acknowledgement: required }
      reason: <why this level needs it>

Example

The outlet raises the note rule from info to a warning that needs an acknowledgement (a tightening), and switches off the open express check. The order is denied until the note warning is acknowledged.

rule-overrides.ruleset.yaml
ruleCascade: 1.0.0
kind: RuleSet
metadata: { id: learn.outlet, version: 1.0.0, title: "Outlet rules" }
scope:
  - { level: organization, id: learn }
  - { level: project, id: outlet }
extends:
  - { ruleset: learn.store-rules, version: "^1.0.0" }
overrides:
  rules:
    - rule: store.notes.recommended
      set: { severity: warning, acknowledgement: required }
      reason: The outlet warehouse needs a note on every order.
    - rule: store.express.check
      set: { enabled: false }
      reason: The outlet has no express delivery surcharge.
rules: []
tests:
  - name: the note is now a warning to acknowledge, and the express check is off
    entity: Order
    operation: create
    given:
      data: { quantity: 2, express: true, country: "PT" }
    expect:
      decision: deny
      findings:
        - { rule: store.notes.recommended, severity: warning, blocking: true }
  - name: acknowledging the note allows the order
    entity: Order
    operation: create
    given:
      data: { quantity: 2, express: true, country: "PT" }
      resolutions:
        - { rule: store.notes.recommended, type: acknowledge }
    expect:
      decision: allow
      findings:
        - { rule: store.notes.recommended, status: acknowledged }
store.ruleset.yaml
ruleCascade: 1.0.0
kind: RuleSet
metadata: { id: learn.store-rules, version: 1.0.0, title: "Store rules" }
scope:
  - { level: organization, id: learn }
entities:
  Order:
    schema: { $ref: "./learn.openapi.yaml#/components/schemas/Order" }
rules:
  - id: store.quantity.min
    kind: validation
    target: { entity: Order, field: /quantity }
    operations: [create]
    assert: { op: gte, args: [{ var: data.quantity }, 1] }
    severity: error
    finding: { code: LRN-ROV-001, message: store.quantityMin }
  - id: store.notes.recommended
    kind: validation
    target: { entity: Order, field: /notes }
    operations: [create]
    assert: { op: exists, args: [{ var: data.notes }] }
    severity: info
    finding: { code: LRN-ROV-002, message: store.notesRecommended }
  - id: store.express.check
    kind: validation
    target: { entity: Order, field: /express }
    operations: [create]
    overridePolicy: open
    assert: { op: ne, args: [{ var: data.express }, true] }
    severity: warning
    finding: { code: LRN-ROV-003, message: store.expressCheck }
  - id: store.country.required
    kind: validation
    target: { entity: Order, field: /country }
    operations: [create]
    overridePolicy: locked
    assert: { op: exists, args: [{ var: data.country }] }
    severity: error
    finding: { code: LRN-ROV-004, message: store.countryRequired }
messages:
  en:
    store.quantityMin: "Order at least one item."
    store.notesRecommended: "A note helps the warehouse."
    store.expressCheck: "Express delivery costs extra."
    store.countryRequired: "Choose a country."
request.json
{
  "entity": "Order",
  "operation": "create",
  "data": {
    "quantity": 2,
    "express": true,
    "country": "PT"
  }
}

Result, from the engine

Decisiondeny1 finding, server channel

  • LRN-ROV-002warningblockingA note helps the warehouse./notes
Try it YourselfOpens this ruleset and request in the playground. Nothing to install.

Common mistakes

  • Loosening a tighten-only rule: disabling it, lowering its severity, allowing an acceptance or removing a required acknowledgement. Below, the outlet lowers an error to a warning, so it does not load: RULE_LOOSENED.
  • Changing a locked rule (RULE_LOCKED) or a rule id that is not inherited (RULE_UNKNOWN).
rule-overrides-loosened.ruleset.yaml
ruleCascade: 1.0.0
kind: RuleSet
metadata: { id: learn.outlet, version: 1.0.0, title: "Outlet rules" }
scope:
  - { level: organization, id: learn }
  - { level: project, id: outlet }
extends:
  - { ruleset: learn.store-rules, version: "^1.0.0" }
overrides:
  rules:
    - rule: store.quantity.min
      set: { severity: warning }
      reason: Let empty orders through.
rules: []
tests:
  - name: zero items
    entity: Order
    operation: create
    given:
      data: { quantity: 0, country: "PT", notes: "x" }
    expect:
      decision: deny
      findings:
        - { rule: store.quantity.min, severity: error }
store.ruleset.yaml
ruleCascade: 1.0.0
kind: RuleSet
metadata: { id: learn.store-rules, version: 1.0.0, title: "Store rules" }
scope:
  - { level: organization, id: learn }
entities:
  Order:
    schema: { $ref: "./learn.openapi.yaml#/components/schemas/Order" }
rules:
  - id: store.quantity.min
    kind: validation
    target: { entity: Order, field: /quantity }
    operations: [create]
    assert: { op: gte, args: [{ var: data.quantity }, 1] }
    severity: error
    finding: { code: LRN-ROV-001, message: store.quantityMin }
  - id: store.notes.recommended
    kind: validation
    target: { entity: Order, field: /notes }
    operations: [create]
    assert: { op: exists, args: [{ var: data.notes }] }
    severity: info
    finding: { code: LRN-ROV-002, message: store.notesRecommended }
  - id: store.express.check
    kind: validation
    target: { entity: Order, field: /express }
    operations: [create]
    overridePolicy: open
    assert: { op: ne, args: [{ var: data.express }, true] }
    severity: warning
    finding: { code: LRN-ROV-003, message: store.expressCheck }
  - id: store.country.required
    kind: validation
    target: { entity: Order, field: /country }
    operations: [create]
    overridePolicy: locked
    assert: { op: exists, args: [{ var: data.country }] }
    severity: error
    finding: { code: LRN-ROV-004, message: store.countryRequired }
messages:
  en:
    store.quantityMin: "Order at least one item."
    store.notesRecommended: "A note helps the warehouse."
    store.expressCheck: "Express delivery costs extra."
    store.countryRequired: "Choose a country."
request.json
{
  "entity": "Order",
  "operation": "create",
  "data": {
    "quantity": 0,
    "country": "PT",
    "notes": "x"
  }
}

Result, from the engine

does not loadThe engine refuses the ruleset before it evaluates anything.

  • RULE_LOOSENED learn.outlet: rule store.quantity.min is tighten-only (line 11)
Try it YourselfOpens this ruleset and request in the playground. Nothing to install.

Exercise

Instead of switching the express check off, make it an error that a manager may accept without a reason. Test that an express order is denied, and allowed when a manager accepts it.

Hint

The express check is open, so its override may set severity: error and an acceptance with roles: [manager].

Show answer
rule-overrides.ruleset.yaml
ruleCascade: 1.0.0
kind: RuleSet
metadata: { id: learn.outlet, version: 1.0.0, title: "Outlet rules" }
scope:
  - { level: organization, id: learn }
  - { level: project, id: outlet }
extends:
  - { ruleset: learn.store-rules, version: "^1.0.0" }
overrides:
  rules:
    - rule: store.notes.recommended
      set: { severity: warning, acknowledgement: required }
      reason: The outlet warehouse needs a note on every order.
    - rule: store.express.check
      set:
        severity: error
        acceptance: { allowed: true, roles: [manager], justification: none }
      reason: Express delivery from the outlet needs a manager.
rules: []
tests:
  - name: express delivery is an error now
    entity: Order
    operation: create
    given:
      data: { quantity: 2, express: true, country: "PT", notes: "back door" }
    expect:
      decision: deny
      findings:
        - { rule: store.express.check, severity: error, blocking: true }
  - name: a manager accepts it
    entity: Order
    operation: create
    given:
      data: { quantity: 2, express: true, country: "PT", notes: "back door" }
      actor: { id: m-1, roles: [manager] }
      resolutions:
        - { rule: store.express.check, type: accept-risk }
    expect:
      decision: allow
      findings:
        - { rule: store.express.check, status: accepted }
store.ruleset.yaml
ruleCascade: 1.0.0
kind: RuleSet
metadata: { id: learn.store-rules, version: 1.0.0, title: "Store rules" }
scope:
  - { level: organization, id: learn }
entities:
  Order:
    schema: { $ref: "./learn.openapi.yaml#/components/schemas/Order" }
rules:
  - id: store.quantity.min
    kind: validation
    target: { entity: Order, field: /quantity }
    operations: [create]
    assert: { op: gte, args: [{ var: data.quantity }, 1] }
    severity: error
    finding: { code: LRN-ROV-001, message: store.quantityMin }
  - id: store.notes.recommended
    kind: validation
    target: { entity: Order, field: /notes }
    operations: [create]
    assert: { op: exists, args: [{ var: data.notes }] }
    severity: info
    finding: { code: LRN-ROV-002, message: store.notesRecommended }
  - id: store.express.check
    kind: validation
    target: { entity: Order, field: /express }
    operations: [create]
    overridePolicy: open
    assert: { op: ne, args: [{ var: data.express }, true] }
    severity: warning
    finding: { code: LRN-ROV-003, message: store.expressCheck }
  - id: store.country.required
    kind: validation
    target: { entity: Order, field: /country }
    operations: [create]
    overridePolicy: locked
    assert: { op: exists, args: [{ var: data.country }] }
    severity: error
    finding: { code: LRN-ROV-004, message: store.countryRequired }
messages:
  en:
    store.quantityMin: "Order at least one item."
    store.notesRecommended: "A note helps the warehouse."
    store.expressCheck: "Express delivery costs extra."
    store.countryRequired: "Choose a country."
request.json
{
  "entity": "Order",
  "operation": "create",
  "data": {
    "quantity": 2,
    "express": true,
    "country": "PT",
    "notes": "back door"
  }
}

Result, from the engine

Decisiondeny1 finding, server channel

  • LRN-ROV-003errorblockingExpress delivery costs extra./express
Course overview

On this page