-
Notifications
You must be signed in to change notification settings - Fork 82
feat(core-spec): add semantic_model.behavior with actions/effects #105
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| # OSI Extension: Behavior Layer (Actions, Rules, Effects) | ||
|
|
||
| **Applies to:** OSI Core v0.1.2 | ||
| **Goal:** Provide a vendor-agnostic behavior layer for deterministic action planning and attribution (what changed/why), without breaking OSI Core compatibility. | ||
|
|
||
| ## 1) Placement & Compatibility | ||
|
|
||
| This extension supports two equivalent placements: | ||
|
|
||
| 1. **Preferred (first-class):** `semantic_model[].behavior` | ||
| 2. **Legacy (embedded):** behavior JSON embedded in `dataset.custom_extensions[].data` (as a JSON string) | ||
|
|
||
| Backwards compatibility guidance: | ||
| - New models SHOULD use `semantic_model.behavior`. | ||
| - Tools MAY continue to support legacy embedded behavior for older models. | ||
| - If both are present, tools SHOULD define precedence (recommended: first-class `semantic_model.behavior` wins). | ||
|
|
||
| ## 2) Top-level object: Behavior | ||
|
|
||
| Minimal structure: | ||
|
|
||
| | Field | Type | Required | Notes | | ||
| |---|---|---:|---| | ||
| | `namespace` | string | Yes | Grouping namespace (e.g. `SAP_P2P`) | | ||
| | `behavior_layer_version` | string | Yes | Schema evolution version | | ||
| | `actions` | array | No* | Preferred list of actions (`actions` or `action_types` must exist) | | ||
| | `action_types` | array | No* | Legacy alias of `actions` | | ||
| | `rules` | array | Yes | Constraints/guards for planning and governance | | ||
| | `metadata` | object | No | Optional governance metadata | | ||
|
|
||
| ## 3) Actions | ||
|
|
||
| Actions describe what a tool/agent can do (API calls, workflows, scripts). OSI does not assume SQL. | ||
|
|
||
| Recommended action fields: | ||
| - `id` (stable identifier, e.g. `suppliers/block`) | ||
| - `title`, `description` | ||
| - `kind`: `command` or `query` | ||
| - `operation`: free-form operation name (e.g. `block`, `unblock`, `analyze`) | ||
| - `entity_name`: dataset name or conceptual entity | ||
| - `io_schema`: optional input/output JSON schema | ||
| - `effects`: optional machine-readable impact annotations | ||
|
|
||
| ## 4) Effects (impact annotations) | ||
|
|
||
| Effects encode how an action reads/writes/derives datasets/fields. | ||
|
|
||
| Minimal effect fields: | ||
| - `entity`: dataset / field / metric / relationship | ||
| - `mode`: read / write / derive | ||
| - `selectors`: `{ dataset, field_names[] }` (or other selectors) | ||
| - optional: `impact_type`, `transition`, `set_value`, `confidence`, `notes` | ||
|
|
||
| Typical uses: | ||
| - Deterministic planning: validate state transitions and required prerequisites | ||
| - Attribution: explain “what changed/why” for a field or dataset | ||
|
|
||
| ## 5) Legacy embedded example (dataset.custom_extensions) | ||
|
|
||
| ```yaml | ||
| datasets: | ||
| - name: suppliers | ||
| source: sap.p2p.suppliers | ||
| custom_extensions: | ||
| - vendor_name: COMMON | ||
| data: | | ||
| { | ||
| "namespace": "SAP_P2P", | ||
| "behavior_layer_version": "0.1", | ||
| "action_types": [], | ||
| "rules": [] | ||
| } | ||
| ``` | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| { | ||
| "$schema": "https://json-schema.org/draft/2020-12/schema", | ||
| "$id": "https://github.com/open-semantic-interchange/OSI/core-spec/behavior-layer.schema.json", | ||
| "title": "OSI Behavior Layer (First-class)", | ||
| "description": "Schema for semantic_model.behavior (actions/rules/effects). Supports legacy alias action_types.", | ||
| "type": "object", | ||
| "additionalProperties": true, | ||
| "required": ["namespace", "behavior_layer_version", "rules"], | ||
| "anyOf": [ | ||
| { "required": ["actions"] }, | ||
| { "required": ["action_types"] } | ||
| ], | ||
| "properties": { | ||
| "namespace": { "type": "string", "minLength": 1 }, | ||
| "behavior_layer_version": { "type": "string", "minLength": 1 }, | ||
| "metadata": { "type": "object", "additionalProperties": true }, | ||
| "actions": { | ||
| "type": "array", | ||
| "description": "Preferred: list of actions supported by this semantic model. (Alias of legacy action_types)", | ||
| "items": { "$ref": "#/$defs/actionType" } | ||
| }, | ||
| "action_types": { | ||
| "type": "array", | ||
| "description": "Legacy alias of actions. New models SHOULD use actions.", | ||
| "items": { "$ref": "#/$defs/actionType" } | ||
| }, | ||
| "rules": { | ||
| "type": "array", | ||
| "items": { "$ref": "#/$defs/rule" } | ||
| } | ||
| }, | ||
| "$defs": { | ||
| "actionType": { | ||
| "type": "object", | ||
| "additionalProperties": true, | ||
| "required": ["id", "title"], | ||
| "properties": { | ||
| "id": { "type": "string", "minLength": 1 }, | ||
| "title": { "type": "string", "minLength": 1 }, | ||
| "description": { "type": "string" }, | ||
| "kind": { "type": "string", "enum": ["command", "query"] }, | ||
| "operation": { "type": "string", "description": "Free-form operation name." }, | ||
| "aggregate": { "type": "string" }, | ||
| "entity_name": { "type": "string" }, | ||
| "idempotency": { "type": "string", "enum": ["idempotent", "non_idempotent", "unknown"] }, | ||
| "applies_to": { "type": "object", "additionalProperties": true }, | ||
| "io_schema": { "type": "object", "additionalProperties": true }, | ||
| "effects": { | ||
| "type": "array", | ||
| "description": "Optional: machine-readable impact annotations (what this action reads/writes/derives).", | ||
| "items": { "$ref": "#/$defs/effect" } | ||
| }, | ||
| "examples": { "type": "array", "items": { "type": "string" } }, | ||
| "tool_hint": { "type": "object", "additionalProperties": true }, | ||
| "tags": { "type": "array", "items": { "type": "string" } }, | ||
| "synonyms": { "type": "array", "items": { "type": "string" } }, | ||
| "deprecated": { "type": "boolean" }, | ||
| "version": { "type": "string" } | ||
| } | ||
| }, | ||
| "effect": { | ||
| "type": "object", | ||
| "additionalProperties": true, | ||
| "required": ["entity", "mode"], | ||
| "properties": { | ||
| "entity": { "type": "string", "enum": ["dataset", "field", "metric", "relationship"] }, | ||
| "mode": { "type": "string", "enum": ["read", "write", "derive"] }, | ||
| "impact_type": { "type": "string" }, | ||
| "selectors": { "type": "object", "additionalProperties": true }, | ||
| "transition": { "type": "object", "additionalProperties": true }, | ||
| "set_value": { "type": "string" }, | ||
| "confidence": { "type": "string", "enum": ["guaranteed", "likely", "unknown"] }, | ||
| "notes": { "type": "string" }, | ||
| "tags": { "type": "array", "items": { "type": "string" } } | ||
| } | ||
| }, | ||
| "rule": { | ||
| "type": "object", | ||
| "additionalProperties": true, | ||
| "required": ["id", "title", "severity", "when", "constraint", "message"], | ||
| "properties": { | ||
| "id": { "type": "string", "minLength": 1 }, | ||
| "title": { "type": "string", "minLength": 1 }, | ||
| "description": { "type": "string" }, | ||
| "severity": { "type": "string", "enum": ["error", "warn", "info"] }, | ||
| "when": { "type": "object", "additionalProperties": true }, | ||
| "if": { "type": "object", "additionalProperties": true }, | ||
| "constraint": { "type": "object", "additionalProperties": true }, | ||
| "message": { "type": "string", "minLength": 1 }, | ||
| "remediation": { "type": "string" }, | ||
| "references": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, | ||
| "tags": { "type": "array", "items": { "type": "string" } } | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,7 +7,7 @@ | |
| "properties": { | ||
| "version": { | ||
| "type": "string", | ||
| "const": "0.1.1", | ||
| "const": "0.1.2", | ||
| "description": "OSI specification version" | ||
| }, | ||
| "dialects": { | ||
|
|
@@ -92,6 +92,89 @@ | |
| "required": ["vendor_name", "data"], | ||
| "additionalProperties": false | ||
| }, | ||
| "Behavior": { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm a bit confused here: it seems we have two behavior type definition:
I see already some differences before the two definitions. Why not having I guess that having two schemas that must kept in sync manually will drift. |
||
| "type": "object", | ||
| "description": "Optional behavior layer for deterministic action planning and attribution (actions/rules/effects).", | ||
| "additionalProperties": true, | ||
| "required": ["namespace", "behavior_layer_version", "rules"], | ||
| "anyOf": [ | ||
| { "required": ["actions"] }, | ||
| { "required": ["action_types"] } | ||
| ], | ||
| "properties": { | ||
| "namespace": { "type": "string", "minLength": 1 }, | ||
| "behavior_layer_version": { "type": "string", "minLength": 1 }, | ||
| "metadata": { "type": "object", "additionalProperties": true }, | ||
| "actions": { | ||
| "type": "array", | ||
| "items": { "$ref": "#/$defs/BehaviorAction" } | ||
| }, | ||
| "action_types": { | ||
| "type": "array", | ||
| "description": "Legacy alias of actions.", | ||
| "items": { "$ref": "#/$defs/BehaviorAction" } | ||
| }, | ||
| "rules": { | ||
| "type": "array", | ||
| "items": { "$ref": "#/$defs/BehaviorRule" } | ||
| } | ||
| } | ||
| }, | ||
| "BehaviorAction": { | ||
| "type": "object", | ||
| "additionalProperties": true, | ||
| "required": ["id", "title"], | ||
| "properties": { | ||
| "id": { "type": "string", "minLength": 1 }, | ||
| "title": { "type": "string", "minLength": 1 }, | ||
| "description": { "type": "string" }, | ||
| "kind": { "type": "string", "enum": ["command", "query"] }, | ||
| "operation": { "type": "string" }, | ||
| "entity_name": { "type": "string" }, | ||
| "idempotency": { "type": "string", "enum": ["idempotent", "non_idempotent", "unknown"] }, | ||
| "applies_to": { "type": "object", "additionalProperties": true }, | ||
| "io_schema": { "type": "object", "additionalProperties": true }, | ||
| "effects": { | ||
| "type": "array", | ||
| "items": { "$ref": "#/$defs/BehaviorEffect" } | ||
| }, | ||
| "tags": { "type": "array", "items": { "type": "string" } }, | ||
| "synonyms": { "type": "array", "items": { "type": "string" } } | ||
| } | ||
| }, | ||
| "BehaviorEffect": { | ||
| "type": "object", | ||
| "additionalProperties": true, | ||
| "required": ["entity", "mode"], | ||
| "properties": { | ||
| "entity": { "type": "string", "enum": ["dataset", "field", "metric", "relationship"] }, | ||
| "mode": { "type": "string", "enum": ["read", "write", "derive"] }, | ||
| "impact_type": { "type": "string" }, | ||
| "selectors": { "type": "object", "additionalProperties": true }, | ||
| "transition": { "type": "object", "additionalProperties": true }, | ||
| "set_value": { "type": "string" }, | ||
| "confidence": { "type": "string", "enum": ["guaranteed", "likely", "unknown"] }, | ||
| "notes": { "type": "string" } | ||
| } | ||
| }, | ||
| "BehaviorRule": { | ||
| "type": "object", | ||
| "description": "Declarative rule/constraint for planning and governance.", | ||
| "additionalProperties": true, | ||
| "required": ["id", "title", "severity", "when", "constraint", "message"], | ||
| "properties": { | ||
| "id": { "type": "string", "minLength": 1 }, | ||
| "title": { "type": "string", "minLength": 1 }, | ||
| "description": { "type": "string" }, | ||
| "severity": { "type": "string", "enum": ["error", "warn", "info"] }, | ||
| "when": { "type": "object", "additionalProperties": true }, | ||
| "if": { "type": "object", "additionalProperties": true }, | ||
| "constraint": { "type": "object", "additionalProperties": true }, | ||
| "message": { "type": "string", "minLength": 1 }, | ||
| "remediation": { "type": "string" }, | ||
| "tags": { "type": "array", "items": { "type": "string" } } | ||
| } | ||
| }, | ||
| "DialectExpression": { | ||
| "type": "object", | ||
| "description": "Expression in a specific dialect", | ||
|
|
@@ -330,6 +413,9 @@ | |
| }, | ||
| "description": "Quantifiable measures spanning datasets" | ||
| }, | ||
| "behavior": { | ||
| "$ref": "#/$defs/Behavior" | ||
| }, | ||
| "custom_extensions": { | ||
| "type": "array", | ||
| "items": { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
impact_typeis a string here, but an implicit enum inspec.yaml.