From 25ac3bc36be1d5c8ecb4ff4def83070e1c4c02fd Mon Sep 17 00:00:00 2001 From: Josh Stewart Date: Mon, 21 Sep 2026 10:21:57 +0000 Subject: [PATCH 1/2] feat(validation-rules-client): add maxChangesInPeriod operator and CadenceValue Regenerated from the validation-rules-api spec that introduces cadence conditions (limit how often a context attribute may change within a period). Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_015pQAQfjedT9aw4USe85sY5 --- .../validation-rules-cadence-operator.md | 6 ++ clients/validation-rules-client/package.json | 2 +- .../validation-rules-client/src/openapi.d.ts | 65 ++++++++++++++++--- .../validation-rules-client/src/openapi.json | 61 +++++++++++++++-- 4 files changed, 118 insertions(+), 16 deletions(-) create mode 100644 .changeset/validation-rules-cadence-operator.md diff --git a/.changeset/validation-rules-cadence-operator.md b/.changeset/validation-rules-cadence-operator.md new file mode 100644 index 000000000..b6050fb72 --- /dev/null +++ b/.changeset/validation-rules-cadence-operator.md @@ -0,0 +1,6 @@ +--- +"@epilot/validation-rules-client": minor +"@epilot/sdk": minor +--- + +Refresh the Validation Rules API contract: adds the `maxChangesInPeriod` operator and the `CadenceValue` condition value that limits how often a context attribute may change within a period. diff --git a/clients/validation-rules-client/package.json b/clients/validation-rules-client/package.json index 02690c93c..eca6ddf3b 100644 --- a/clients/validation-rules-client/package.json +++ b/clients/validation-rules-client/package.json @@ -1,6 +1,6 @@ { "name": "@epilot/validation-rules-client", - "version": "1.6.0-rc.1", + "version": "1.7.0-rc.0", "description": "API Client for epilot Validation Rules API", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/clients/validation-rules-client/src/openapi.d.ts b/clients/validation-rules-client/src/openapi.d.ts index 74e342abb..6fb51094b 100644 --- a/clients/validation-rules-client/src/openapi.d.ts +++ b/clients/validation-rules-client/src/openapi.d.ts @@ -53,6 +53,33 @@ declare namespace Components { */ EnvironmentValue; } + /** + * The value of a `maxChangesInPeriod` condition: which context attribute's changes are counted + * and how many are allowed within the period ending at the evaluation moment. The rule stores + * the settings only; the consumer supplies the attribute's change timestamps (e.g. from the + * entity activity feed) at evaluation time. + * + * Rolling units (`days`, `weeks`, `months`) count back from now. Calendar units + * (`calendar_weeks`, `calendar_months`) start at the beginning of the current calendar week + * (Monday) or month, extended backwards by `period - 1` units. + * + */ + export interface CadenceValue { + source: "cadence"; + /** + * The context attribute whose changes are counted, as `.`, e.g. `contract.installment_amount`. + */ + path: string; // ^[a-zA-Z_][a-zA-Z0-9_]*(\.[a-zA-Z0-9_]+)*$ + /** + * How many changes may already fall inside the period before the condition fails. 0 never allows a change. + */ + max_changes: number; + /** + * Length of the period in `unit`s. + */ + period: number; + unit: "days" | "weeks" | "months" | "calendar_weeks" | "calendar_months"; + } /** * Declarative validation rule (schema version v2). Supports predefined comparison operators * over number, date and text inputs, with static, dynamic (context path), relative-date and @@ -96,10 +123,13 @@ declare namespace Components { * separators, sign and the decimal separator are not counted); maxDecimals limits how many * digits may follow the decimal separator. Both take a non-negative integer comparison value * and, like the other numeric operators, are also allowed on text rules. + * maxChangesInPeriod is allowed on every input type and requires a `cadence` value. It judges + * the change history of a context attribute rather than the input itself: the condition holds + * while fewer than `max_changes` changes of that attribute fall inside the period ending now. * */ Operator; - value: /* The comparison value of a condition - a scalar, a range of scalars, or nothing (unary operators). */ ConditionValue; + value: /* The comparison value of a condition - a scalar, a range of scalars, a cadence (maxChangesInPeriod), or nothing (unary operators). */ ConditionValue; /** * Message shown to the end user when this condition fails. */ @@ -122,9 +152,9 @@ declare namespace Components { allow_failure?: boolean; } /** - * The comparison value of a condition - a scalar, a range of scalars, or nothing (unary operators). + * The comparison value of a condition - a scalar, a range of scalars, a cadence (maxChangesInPeriod), or nothing (unary operators). */ - export type ConditionValue = /* The comparison value of a condition - a scalar, a range of scalars, or nothing (unary operators). */ /* A fixed comparison value. */ StaticValue | /** + export type ConditionValue = /* The comparison value of a condition - a scalar, a range of scalars, a cadence (maxChangesInPeriod), or nothing (unary operators). */ /* A fixed comparison value. */ StaticValue | /** * A dynamic comparison value resolved from runtime context, e.g. `contract.installment_amount` * or `previous_reading.value`. The first path segment must match the `name` of a declared * context requirement. @@ -156,10 +186,21 @@ declare namespace Components { * in public journeys the dependent conditions are skipped. * */ - ExternalValue | /* A lower and upper bound for range operators (between, dateBetween, lengthBetween). Bounds are inclusive. */ RangeValue | /* No comparison value - used by unary operators such as notInFuture / notInPast. */ NoValue; + ExternalValue | /* A lower and upper bound for range operators (between, dateBetween, lengthBetween). Bounds are inclusive. */ RangeValue | /** + * The value of a `maxChangesInPeriod` condition: which context attribute's changes are counted + * and how many are allowed within the period ending at the evaluation moment. The rule stores + * the settings only; the consumer supplies the attribute's change timestamps (e.g. from the + * entity activity feed) at evaluation time. + * + * Rolling units (`days`, `weeks`, `months`) count back from now. Calendar units + * (`calendar_weeks`, `calendar_months`) start at the beginning of the current calendar week + * (Monday) or month, extended backwards by `period - 1` units. + * + */ + CadenceValue | /* No comparison value - used by unary operators such as notInFuture / notInPast. */ NoValue; /** * An entity context source the rule needs at evaluation time, referenced by `context` - * value paths via the schema slug as their first segment (e.g. `contract.installment_amount`). + * and `cadence` value paths via the schema slug as their first segment (e.g. `contract.installment_amount`). * How the source is resolved (which entity instance) is decided by the consuming surface, * not by the rule. Meter reading comparisons use the meter/meter_counter entity schemas * (e.g. `meter_counter.current_consumption` for the previous reading value). @@ -241,7 +282,7 @@ declare namespace Components { */ contexts?: /** * An entity context source the rule needs at evaluation time, referenced by `context` - * value paths via the schema slug as their first segment (e.g. `contract.installment_amount`). + * and `cadence` value paths via the schema slug as their first segment (e.g. `contract.installment_amount`). * How the source is resolved (which entity instance) is decided by the consuming surface, * not by the rule. Meter reading comparisons use the meter/meter_counter entity schemas * (e.g. `meter_counter.current_consumption` for the previous reading value). @@ -581,9 +622,12 @@ declare namespace Components { * separators, sign and the decimal separator are not counted); maxDecimals limits how many * digits may follow the decimal separator. Both take a non-negative integer comparison value * and, like the other numeric operators, are also allowed on text rules. + * maxChangesInPeriod is allowed on every input type and requires a `cadence` value. It judges + * the change history of a context attribute rather than the input itself: the condition holds + * while fewer than `max_changes` changes of that attribute fall inside the period ending now. * */ - export type Operator = "equal" | "notEqual" | "greaterThan" | "greaterThanInclusive" | "lessThan" | "lessThanInclusive" | "between" | "dateBefore" | "dateOnOrBefore" | "dateAfter" | "dateOnOrAfter" | "dateBetween" | "notInFuture" | "notInPast" | "contains" | "doesNotContain" | "startsWith" | "endsWith" | "regexMatch" | "lengthBetween" | "maxDigits" | "maxDecimals"; + export type Operator = "equal" | "notEqual" | "greaterThan" | "greaterThanInclusive" | "lessThan" | "lessThanInclusive" | "between" | "dateBefore" | "dateOnOrBefore" | "dateAfter" | "dateOnOrAfter" | "dateBetween" | "notInFuture" | "notInPast" | "contains" | "doesNotContain" | "startsWith" | "endsWith" | "regexMatch" | "lengthBetween" | "maxDigits" | "maxDecimals" | "maxChangesInPeriod"; /** * Condition definition for a pattern-based validation rule (2 levels deep) */ @@ -915,7 +959,7 @@ declare namespace Components { */ contexts?: /** * An entity context source the rule needs at evaluation time, referenced by `context` - * value paths via the schema slug as their first segment (e.g. `contract.installment_amount`). + * and `cadence` value paths via the schema slug as their first segment (e.g. `contract.installment_amount`). * How the source is resolved (which entity instance) is decided by the consuming surface, * not by the rule. Meter reading comparisons use the meter/meter_counter entity schemas * (e.g. `meter_counter.current_consumption` for the previous reading value). @@ -1003,7 +1047,7 @@ declare namespace Components { */ contexts?: /** * An entity context source the rule needs at evaluation time, referenced by `context` - * value paths via the schema slug as their first segment (e.g. `contract.installment_amount`). + * and `cadence` value paths via the schema slug as their first segment (e.g. `contract.installment_amount`). * How the source is resolved (which entity instance) is decided by the consuming surface, * not by the rule. Meter reading comparisons use the meter/meter_counter entity schemas * (e.g. `meter_counter.current_consumption` for the previous reading value). @@ -1088,7 +1132,7 @@ declare namespace Components { */ contexts?: /** * An entity context source the rule needs at evaluation time, referenced by `context` - * value paths via the schema slug as their first segment (e.g. `contract.installment_amount`). + * and `cadence` value paths via the schema slug as their first segment (e.g. `contract.installment_amount`). * How the source is resolved (which entity instance) is decided by the consuming surface, * not by the rule. Meter reading comparisons use the meter/meter_counter entity schemas * (e.g. `meter_counter.current_consumption` for the previous reading value). @@ -1628,6 +1672,7 @@ export type Client = OpenAPIClient export type AppliesWhen = Components.Schemas.AppliesWhen; +export type CadenceValue = Components.Schemas.CadenceValue; export type ComparisonRuleType = Components.Schemas.ComparisonRuleType; export type Condition = Components.Schemas.Condition; export type ConditionValue = Components.Schemas.ConditionValue; diff --git a/clients/validation-rules-client/src/openapi.json b/clients/validation-rules-client/src/openapi.json index 56949f420..4c2612a6e 100644 --- a/clients/validation-rules-client/src/openapi.json +++ b/clients/validation-rules-client/src/openapi.json @@ -2071,7 +2071,7 @@ }, "Operator": { "type": "string", - "description": "Predefined comparison operator. Compatibility (enforced at write time):\n- number: equal, notEqual, greaterThan, greaterThanInclusive, lessThan, lessThanInclusive, between, regexMatch,\n maxDigits, maxDecimals\n- date: dateBefore, dateOnOrBefore, dateAfter, dateOnOrAfter, dateBetween, notInFuture, notInPast, regexMatch\n- text: equal, notEqual, contains, doesNotContain, startsWith, endsWith, regexMatch, lengthBetween,\n greaterThan, greaterThanInclusive, lessThan, lessThanInclusive, between, maxDigits, maxDecimals\nRange operators (between, dateBetween, lengthBetween) require a `range` value;\nunary operators (notInFuture, notInPast) require a `none` value; all others require a scalar value.\nregexMatch validates the raw input string's format and always takes a static string pattern.\nNumeric comparison operators on text rules parse the input as a number at evaluation time\n(free-text fields often hold numbers); unparsable input fails the condition.\nmaxDigits limits how many digits the written input may contain in total (grouping\nseparators, sign and the decimal separator are not counted); maxDecimals limits how many\ndigits may follow the decimal separator. Both take a non-negative integer comparison value\nand, like the other numeric operators, are also allowed on text rules.\n", + "description": "Predefined comparison operator. Compatibility (enforced at write time):\n- number: equal, notEqual, greaterThan, greaterThanInclusive, lessThan, lessThanInclusive, between, regexMatch,\n maxDigits, maxDecimals\n- date: dateBefore, dateOnOrBefore, dateAfter, dateOnOrAfter, dateBetween, notInFuture, notInPast, regexMatch\n- text: equal, notEqual, contains, doesNotContain, startsWith, endsWith, regexMatch, lengthBetween,\n greaterThan, greaterThanInclusive, lessThan, lessThanInclusive, between, maxDigits, maxDecimals\nRange operators (between, dateBetween, lengthBetween) require a `range` value;\nunary operators (notInFuture, notInPast) require a `none` value; all others require a scalar value.\nregexMatch validates the raw input string's format and always takes a static string pattern.\nNumeric comparison operators on text rules parse the input as a number at evaluation time\n(free-text fields often hold numbers); unparsable input fails the condition.\nmaxDigits limits how many digits the written input may contain in total (grouping\nseparators, sign and the decimal separator are not counted); maxDecimals limits how many\ndigits may follow the decimal separator. Both take a non-negative integer comparison value\nand, like the other numeric operators, are also allowed on text rules.\nmaxChangesInPeriod is allowed on every input type and requires a `cadence` value. It judges\nthe change history of a context attribute rather than the input itself: the condition holds\nwhile fewer than `max_changes` changes of that attribute fall inside the period ending now.\n", "enum": [ "equal", "notEqual", @@ -2094,11 +2094,12 @@ "regexMatch", "lengthBetween", "maxDigits", - "maxDecimals" + "maxDecimals", + "maxChangesInPeriod" ] }, "ConditionValue": { - "description": "The comparison value of a condition - a scalar, a range of scalars, or nothing (unary operators).", + "description": "The comparison value of a condition - a scalar, a range of scalars, a cadence (maxChangesInPeriod), or nothing (unary operators).", "oneOf": [ { "$ref": "#/components/schemas/StaticValue" @@ -2118,6 +2119,9 @@ { "$ref": "#/components/schemas/RangeValue" }, + { + "$ref": "#/components/schemas/CadenceValue" + }, { "$ref": "#/components/schemas/NoValue" } @@ -2313,7 +2317,7 @@ "up", "down" ], - "description": "Rounds the adjusted result to a whole number - `up` (ceiling) or `down` (floor).\nOmitted means no rounding. Used for bounds like \"round up(current \u00d7 0.9) to whole euros\".\n" + "description": "Rounds the adjusted result to a whole number - `up` (ceiling) or `down` (floor).\nOmitted means no rounding. Used for bounds like \"round up(current × 0.9) to whole euros\".\n" } } }, @@ -2396,6 +2400,53 @@ } } }, + "CadenceValue": { + "type": "object", + "description": "The value of a `maxChangesInPeriod` condition: which context attribute's changes are counted\nand how many are allowed within the period ending at the evaluation moment. The rule stores\nthe settings only; the consumer supplies the attribute's change timestamps (e.g. from the\nentity activity feed) at evaluation time.\n\nRolling units (`days`, `weeks`, `months`) count back from now. Calendar units\n(`calendar_weeks`, `calendar_months`) start at the beginning of the current calendar week\n(Monday) or month, extended backwards by `period - 1` units.\n", + "required": [ + "source", + "path", + "max_changes", + "period", + "unit" + ], + "additionalProperties": false, + "properties": { + "source": { + "type": "string", + "enum": [ + "cadence" + ] + }, + "path": { + "type": "string", + "minLength": 1, + "maxLength": 200, + "pattern": "^[a-zA-Z_][a-zA-Z0-9_]*(\\.[a-zA-Z0-9_]+)*$", + "description": "The context attribute whose changes are counted, as `.`, e.g. `contract.installment_amount`." + }, + "max_changes": { + "type": "integer", + "minimum": 0, + "description": "How many changes may already fall inside the period before the condition fails. 0 never allows a change." + }, + "period": { + "type": "integer", + "minimum": 1, + "description": "Length of the period in `unit`s." + }, + "unit": { + "type": "string", + "enum": [ + "days", + "weeks", + "months", + "calendar_weeks", + "calendar_months" + ] + } + } + }, "DocumentRuleType": { "type": "object", "description": "Declarative validation rule (schema version v2) for uploaded files. The rule declares how deep\nthe check goes (`check.level`) and the conditions the file must satisfy. Property conditions\n(file type, size, page count, image resolution, blank and password-protected detection) are\ndeterministic and available at every level; the `meetsCriteria` condition judges a free-text\nrequirement against the document's extracted content and is available from level `standard`.\n\nEvaluation semantics for content conditions differ from scalar rules: each condition resolves to\npass, fail or uncertain. A confident fail on a blocking condition fails the input; an uncertain\nresult is always reported as advisory and never fails the input, regardless of `allow_failure`.\nDocument rules carry no `contexts` yet.\n", @@ -2618,7 +2669,7 @@ }, "ContextRequirement": { "type": "object", - "description": "An entity context source the rule needs at evaluation time, referenced by `context`\nvalue paths via the schema slug as their first segment (e.g. `contract.installment_amount`).\nHow the source is resolved (which entity instance) is decided by the consuming surface,\nnot by the rule. Meter reading comparisons use the meter/meter_counter entity schemas\n(e.g. `meter_counter.current_consumption` for the previous reading value).\n", + "description": "An entity context source the rule needs at evaluation time, referenced by `context`\nand `cadence` value paths via the schema slug as their first segment (e.g. `contract.installment_amount`).\nHow the source is resolved (which entity instance) is decided by the consuming surface,\nnot by the rule. Meter reading comparisons use the meter/meter_counter entity schemas\n(e.g. `meter_counter.current_consumption` for the previous reading value).\n", "required": [ "schema" ], From da401e495bd137f727108ccf5c13393125b94bf2 Mon Sep 17 00:00:00 2001 From: Josh Stewart Date: Mon, 21 Sep 2026 14:23:17 +0000 Subject: [PATCH 2/2] refactor(validation-rules-client): cadence counts records via maxPerPeriod Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_015pQAQfjedT9aw4USe85sY5 --- .../validation-rules-cadence-operator.md | 2 +- .../validation-rules-client/src/openapi.d.ts | 71 ++++++++++++------- .../validation-rules-client/src/openapi.json | 26 ++++--- 3 files changed, 63 insertions(+), 36 deletions(-) diff --git a/.changeset/validation-rules-cadence-operator.md b/.changeset/validation-rules-cadence-operator.md index b6050fb72..4837ee3b5 100644 --- a/.changeset/validation-rules-cadence-operator.md +++ b/.changeset/validation-rules-cadence-operator.md @@ -3,4 +3,4 @@ "@epilot/sdk": minor --- -Refresh the Validation Rules API contract: adds the `maxChangesInPeriod` operator and the `CadenceValue` condition value that limits how often a context attribute may change within a period. +Refresh the Validation Rules API contract: adds the `maxPerPeriod` operator and the `CadenceValue` condition value that limits how often something may happen within a period, counting records of a bound context type (e.g. tickets). diff --git a/clients/validation-rules-client/src/openapi.d.ts b/clients/validation-rules-client/src/openapi.d.ts index 6fb51094b..bdabd407c 100644 --- a/clients/validation-rules-client/src/openapi.d.ts +++ b/clients/validation-rules-client/src/openapi.d.ts @@ -54,10 +54,15 @@ declare namespace Components { EnvironmentValue; } /** - * The value of a `maxChangesInPeriod` condition: which context attribute's changes are counted - * and how many are allowed within the period ending at the evaluation moment. The rule stores - * the settings only; the consumer supplies the attribute's change timestamps (e.g. from the - * entity activity feed) at evaluation time. + * The value of a `maxPerPeriod` condition: what is counted and how many are allowed within the + * period ending at the evaluation moment. The rule stores the settings only; the consumer resolves + * the counted records (e.g. the tickets bound to the rule's `ticket` context) and supplies their + * timestamps at evaluation time. + * + * `count` says what kind of thing is counted. Today only `records` exists: entities of the + * context type named by the first segment of `path`, dated by the attribute in the rest of the + * path (e.g. `ticket._created_at`). Further kinds may be added later without affecting existing + * rules. * * Rolling units (`days`, `weeks`, `months`) count back from now. Calendar units * (`calendar_weeks`, `calendar_months`) start at the beginning of the current calendar week @@ -67,13 +72,17 @@ declare namespace Components { export interface CadenceValue { source: "cadence"; /** - * The context attribute whose changes are counted, as `.`, e.g. `contract.installment_amount`. + * What is counted. `records` counts entities of the context type in `path`, dated by the attribute in `path`. + */ + count: "records"; + /** + * The counted records as `.`, e.g. `ticket._created_at`. */ path: string; // ^[a-zA-Z_][a-zA-Z0-9_]*(\.[a-zA-Z0-9_]+)*$ /** - * How many changes may already fall inside the period before the condition fails. 0 never allows a change. + * How many counted records may already fall inside the period before the condition fails. 0 never allows another one. */ - max_changes: number; + max: number; /** * Length of the period in `unit`s. */ @@ -123,13 +132,13 @@ declare namespace Components { * separators, sign and the decimal separator are not counted); maxDecimals limits how many * digits may follow the decimal separator. Both take a non-negative integer comparison value * and, like the other numeric operators, are also allowed on text rules. - * maxChangesInPeriod is allowed on every input type and requires a `cadence` value. It judges - * the change history of a context attribute rather than the input itself: the condition holds - * while fewer than `max_changes` changes of that attribute fall inside the period ending now. + * maxPerPeriod is allowed on every input type and requires a `cadence` value. It judges how often + * something happened rather than the input itself: the condition holds while fewer than `max` + * counted records of a context entity type fall inside the period ending now. * */ Operator; - value: /* The comparison value of a condition - a scalar, a range of scalars, a cadence (maxChangesInPeriod), or nothing (unary operators). */ ConditionValue; + value: /* The comparison value of a condition - a scalar, a range of scalars, a cadence (maxPerPeriod), or nothing (unary operators). */ ConditionValue; /** * Message shown to the end user when this condition fails. */ @@ -152,9 +161,9 @@ declare namespace Components { allow_failure?: boolean; } /** - * The comparison value of a condition - a scalar, a range of scalars, a cadence (maxChangesInPeriod), or nothing (unary operators). + * The comparison value of a condition - a scalar, a range of scalars, a cadence (maxPerPeriod), or nothing (unary operators). */ - export type ConditionValue = /* The comparison value of a condition - a scalar, a range of scalars, a cadence (maxChangesInPeriod), or nothing (unary operators). */ /* A fixed comparison value. */ StaticValue | /** + export type ConditionValue = /* The comparison value of a condition - a scalar, a range of scalars, a cadence (maxPerPeriod), or nothing (unary operators). */ /* A fixed comparison value. */ StaticValue | /** * A dynamic comparison value resolved from runtime context, e.g. `contract.installment_amount` * or `previous_reading.value`. The first path segment must match the `name` of a declared * context requirement. @@ -187,10 +196,15 @@ declare namespace Components { * */ ExternalValue | /* A lower and upper bound for range operators (between, dateBetween, lengthBetween). Bounds are inclusive. */ RangeValue | /** - * The value of a `maxChangesInPeriod` condition: which context attribute's changes are counted - * and how many are allowed within the period ending at the evaluation moment. The rule stores - * the settings only; the consumer supplies the attribute's change timestamps (e.g. from the - * entity activity feed) at evaluation time. + * The value of a `maxPerPeriod` condition: what is counted and how many are allowed within the + * period ending at the evaluation moment. The rule stores the settings only; the consumer resolves + * the counted records (e.g. the tickets bound to the rule's `ticket` context) and supplies their + * timestamps at evaluation time. + * + * `count` says what kind of thing is counted. Today only `records` exists: entities of the + * context type named by the first segment of `path`, dated by the attribute in the rest of the + * path (e.g. `ticket._created_at`). Further kinds may be added later without affecting existing + * rules. * * Rolling units (`days`, `weeks`, `months`) count back from now. Calendar units * (`calendar_weeks`, `calendar_months`) start at the beginning of the current calendar week @@ -200,7 +214,8 @@ declare namespace Components { CadenceValue | /* No comparison value - used by unary operators such as notInFuture / notInPast. */ NoValue; /** * An entity context source the rule needs at evaluation time, referenced by `context` - * and `cadence` value paths via the schema slug as their first segment (e.g. `contract.installment_amount`). + * value paths (e.g. `contract.installment_amount`) and `cadence` value paths (e.g. `ticket._created_at`) + * via the schema slug as their first segment. * How the source is resolved (which entity instance) is decided by the consuming surface, * not by the rule. Meter reading comparisons use the meter/meter_counter entity schemas * (e.g. `meter_counter.current_consumption` for the previous reading value). @@ -282,7 +297,8 @@ declare namespace Components { */ contexts?: /** * An entity context source the rule needs at evaluation time, referenced by `context` - * and `cadence` value paths via the schema slug as their first segment (e.g. `contract.installment_amount`). + * value paths (e.g. `contract.installment_amount`) and `cadence` value paths (e.g. `ticket._created_at`) + * via the schema slug as their first segment. * How the source is resolved (which entity instance) is decided by the consuming surface, * not by the rule. Meter reading comparisons use the meter/meter_counter entity schemas * (e.g. `meter_counter.current_consumption` for the previous reading value). @@ -622,12 +638,12 @@ declare namespace Components { * separators, sign and the decimal separator are not counted); maxDecimals limits how many * digits may follow the decimal separator. Both take a non-negative integer comparison value * and, like the other numeric operators, are also allowed on text rules. - * maxChangesInPeriod is allowed on every input type and requires a `cadence` value. It judges - * the change history of a context attribute rather than the input itself: the condition holds - * while fewer than `max_changes` changes of that attribute fall inside the period ending now. + * maxPerPeriod is allowed on every input type and requires a `cadence` value. It judges how often + * something happened rather than the input itself: the condition holds while fewer than `max` + * counted records of a context entity type fall inside the period ending now. * */ - export type Operator = "equal" | "notEqual" | "greaterThan" | "greaterThanInclusive" | "lessThan" | "lessThanInclusive" | "between" | "dateBefore" | "dateOnOrBefore" | "dateAfter" | "dateOnOrAfter" | "dateBetween" | "notInFuture" | "notInPast" | "contains" | "doesNotContain" | "startsWith" | "endsWith" | "regexMatch" | "lengthBetween" | "maxDigits" | "maxDecimals" | "maxChangesInPeriod"; + export type Operator = "equal" | "notEqual" | "greaterThan" | "greaterThanInclusive" | "lessThan" | "lessThanInclusive" | "between" | "dateBefore" | "dateOnOrBefore" | "dateAfter" | "dateOnOrAfter" | "dateBetween" | "notInFuture" | "notInPast" | "contains" | "doesNotContain" | "startsWith" | "endsWith" | "regexMatch" | "lengthBetween" | "maxDigits" | "maxDecimals" | "maxPerPeriod"; /** * Condition definition for a pattern-based validation rule (2 levels deep) */ @@ -959,7 +975,8 @@ declare namespace Components { */ contexts?: /** * An entity context source the rule needs at evaluation time, referenced by `context` - * and `cadence` value paths via the schema slug as their first segment (e.g. `contract.installment_amount`). + * value paths (e.g. `contract.installment_amount`) and `cadence` value paths (e.g. `ticket._created_at`) + * via the schema slug as their first segment. * How the source is resolved (which entity instance) is decided by the consuming surface, * not by the rule. Meter reading comparisons use the meter/meter_counter entity schemas * (e.g. `meter_counter.current_consumption` for the previous reading value). @@ -1047,7 +1064,8 @@ declare namespace Components { */ contexts?: /** * An entity context source the rule needs at evaluation time, referenced by `context` - * and `cadence` value paths via the schema slug as their first segment (e.g. `contract.installment_amount`). + * value paths (e.g. `contract.installment_amount`) and `cadence` value paths (e.g. `ticket._created_at`) + * via the schema slug as their first segment. * How the source is resolved (which entity instance) is decided by the consuming surface, * not by the rule. Meter reading comparisons use the meter/meter_counter entity schemas * (e.g. `meter_counter.current_consumption` for the previous reading value). @@ -1132,7 +1150,8 @@ declare namespace Components { */ contexts?: /** * An entity context source the rule needs at evaluation time, referenced by `context` - * and `cadence` value paths via the schema slug as their first segment (e.g. `contract.installment_amount`). + * value paths (e.g. `contract.installment_amount`) and `cadence` value paths (e.g. `ticket._created_at`) + * via the schema slug as their first segment. * How the source is resolved (which entity instance) is decided by the consuming surface, * not by the rule. Meter reading comparisons use the meter/meter_counter entity schemas * (e.g. `meter_counter.current_consumption` for the previous reading value). diff --git a/clients/validation-rules-client/src/openapi.json b/clients/validation-rules-client/src/openapi.json index 4c2612a6e..672923859 100644 --- a/clients/validation-rules-client/src/openapi.json +++ b/clients/validation-rules-client/src/openapi.json @@ -2071,7 +2071,7 @@ }, "Operator": { "type": "string", - "description": "Predefined comparison operator. Compatibility (enforced at write time):\n- number: equal, notEqual, greaterThan, greaterThanInclusive, lessThan, lessThanInclusive, between, regexMatch,\n maxDigits, maxDecimals\n- date: dateBefore, dateOnOrBefore, dateAfter, dateOnOrAfter, dateBetween, notInFuture, notInPast, regexMatch\n- text: equal, notEqual, contains, doesNotContain, startsWith, endsWith, regexMatch, lengthBetween,\n greaterThan, greaterThanInclusive, lessThan, lessThanInclusive, between, maxDigits, maxDecimals\nRange operators (between, dateBetween, lengthBetween) require a `range` value;\nunary operators (notInFuture, notInPast) require a `none` value; all others require a scalar value.\nregexMatch validates the raw input string's format and always takes a static string pattern.\nNumeric comparison operators on text rules parse the input as a number at evaluation time\n(free-text fields often hold numbers); unparsable input fails the condition.\nmaxDigits limits how many digits the written input may contain in total (grouping\nseparators, sign and the decimal separator are not counted); maxDecimals limits how many\ndigits may follow the decimal separator. Both take a non-negative integer comparison value\nand, like the other numeric operators, are also allowed on text rules.\nmaxChangesInPeriod is allowed on every input type and requires a `cadence` value. It judges\nthe change history of a context attribute rather than the input itself: the condition holds\nwhile fewer than `max_changes` changes of that attribute fall inside the period ending now.\n", + "description": "Predefined comparison operator. Compatibility (enforced at write time):\n- number: equal, notEqual, greaterThan, greaterThanInclusive, lessThan, lessThanInclusive, between, regexMatch,\n maxDigits, maxDecimals\n- date: dateBefore, dateOnOrBefore, dateAfter, dateOnOrAfter, dateBetween, notInFuture, notInPast, regexMatch\n- text: equal, notEqual, contains, doesNotContain, startsWith, endsWith, regexMatch, lengthBetween,\n greaterThan, greaterThanInclusive, lessThan, lessThanInclusive, between, maxDigits, maxDecimals\nRange operators (between, dateBetween, lengthBetween) require a `range` value;\nunary operators (notInFuture, notInPast) require a `none` value; all others require a scalar value.\nregexMatch validates the raw input string's format and always takes a static string pattern.\nNumeric comparison operators on text rules parse the input as a number at evaluation time\n(free-text fields often hold numbers); unparsable input fails the condition.\nmaxDigits limits how many digits the written input may contain in total (grouping\nseparators, sign and the decimal separator are not counted); maxDecimals limits how many\ndigits may follow the decimal separator. Both take a non-negative integer comparison value\nand, like the other numeric operators, are also allowed on text rules.\nmaxPerPeriod is allowed on every input type and requires a `cadence` value. It judges how often\nsomething happened rather than the input itself: the condition holds while fewer than `max`\ncounted records of a context entity type fall inside the period ending now.\n", "enum": [ "equal", "notEqual", @@ -2095,11 +2095,11 @@ "lengthBetween", "maxDigits", "maxDecimals", - "maxChangesInPeriod" + "maxPerPeriod" ] }, "ConditionValue": { - "description": "The comparison value of a condition - a scalar, a range of scalars, a cadence (maxChangesInPeriod), or nothing (unary operators).", + "description": "The comparison value of a condition - a scalar, a range of scalars, a cadence (maxPerPeriod), or nothing (unary operators).", "oneOf": [ { "$ref": "#/components/schemas/StaticValue" @@ -2402,11 +2402,12 @@ }, "CadenceValue": { "type": "object", - "description": "The value of a `maxChangesInPeriod` condition: which context attribute's changes are counted\nand how many are allowed within the period ending at the evaluation moment. The rule stores\nthe settings only; the consumer supplies the attribute's change timestamps (e.g. from the\nentity activity feed) at evaluation time.\n\nRolling units (`days`, `weeks`, `months`) count back from now. Calendar units\n(`calendar_weeks`, `calendar_months`) start at the beginning of the current calendar week\n(Monday) or month, extended backwards by `period - 1` units.\n", + "description": "The value of a `maxPerPeriod` condition: what is counted and how many are allowed within the\nperiod ending at the evaluation moment. The rule stores the settings only; the consumer resolves\nthe counted records (e.g. the tickets bound to the rule's `ticket` context) and supplies their\ntimestamps at evaluation time.\n\n`count` says what kind of thing is counted. Today only `records` exists: entities of the\ncontext type named by the first segment of `path`, dated by the attribute in the rest of the\npath (e.g. `ticket._created_at`). Further kinds may be added later without affecting existing\nrules.\n\nRolling units (`days`, `weeks`, `months`) count back from now. Calendar units\n(`calendar_weeks`, `calendar_months`) start at the beginning of the current calendar week\n(Monday) or month, extended backwards by `period - 1` units.\n", "required": [ "source", + "count", "path", - "max_changes", + "max", "period", "unit" ], @@ -2418,17 +2419,24 @@ "cadence" ] }, + "count": { + "type": "string", + "enum": [ + "records" + ], + "description": "What is counted. `records` counts entities of the context type in `path`, dated by the attribute in `path`." + }, "path": { "type": "string", "minLength": 1, "maxLength": 200, "pattern": "^[a-zA-Z_][a-zA-Z0-9_]*(\\.[a-zA-Z0-9_]+)*$", - "description": "The context attribute whose changes are counted, as `.`, e.g. `contract.installment_amount`." + "description": "The counted records as `.`, e.g. `ticket._created_at`." }, - "max_changes": { + "max": { "type": "integer", "minimum": 0, - "description": "How many changes may already fall inside the period before the condition fails. 0 never allows a change." + "description": "How many counted records may already fall inside the period before the condition fails. 0 never allows another one." }, "period": { "type": "integer", @@ -2669,7 +2677,7 @@ }, "ContextRequirement": { "type": "object", - "description": "An entity context source the rule needs at evaluation time, referenced by `context`\nand `cadence` value paths via the schema slug as their first segment (e.g. `contract.installment_amount`).\nHow the source is resolved (which entity instance) is decided by the consuming surface,\nnot by the rule. Meter reading comparisons use the meter/meter_counter entity schemas\n(e.g. `meter_counter.current_consumption` for the previous reading value).\n", + "description": "An entity context source the rule needs at evaluation time, referenced by `context`\nvalue paths (e.g. `contract.installment_amount`) and `cadence` value paths (e.g. `ticket._created_at`)\nvia the schema slug as their first segment.\nHow the source is resolved (which entity instance) is decided by the consuming surface,\nnot by the rule. Meter reading comparisons use the meter/meter_counter entity schemas\n(e.g. `meter_counter.current_consumption` for the previous reading value).\n", "required": [ "schema" ],