diff --git a/core-spec/behavior-layer.md b/core-spec/behavior-layer.md new file mode 100644 index 0000000..5e30636 --- /dev/null +++ b/core-spec/behavior-layer.md @@ -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": [] + } +``` + diff --git a/core-spec/behavior-layer.schema.json b/core-spec/behavior-layer.schema.json new file mode 100644 index 0000000..999eda1 --- /dev/null +++ b/core-spec/behavior-layer.schema.json @@ -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" } } + } + } + } +} + diff --git a/core-spec/osi-schema.json b/core-spec/osi-schema.json index 30210d1..c0b38bd 100644 --- a/core-spec/osi-schema.json +++ b/core-spec/osi-schema.json @@ -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": { + "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": { diff --git a/core-spec/spec.md b/core-spec/spec.md index 33da9bd..72efb85 100644 --- a/core-spec/spec.md +++ b/core-spec/spec.md @@ -1,6 +1,6 @@ # OSI - Core Metadata Specification -**Version:** 0.1.1 +**Version:** 0.1.2 ## Goals @@ -16,7 +16,8 @@ 4. [Relationships](#relationships) 5. [Fields](#fields) 6. [Metrics](#metrics) -7. [Examples](#examples) +7. [Behavior (Actions / Rules / Effects)](#behavior-actions--rules--effects) +8. [Examples](#examples) --- @@ -62,6 +63,7 @@ The top-level container that represents a complete semantic model, including dat | `datasets` | array | Yes | Collection of logical datasets (fact and dimension tables) | | `relationships` | array | No | Defines how logical datasets are connected | | `metrics` | array | No | Quantifiable measures defined as aggregate expessions on fields from logical datsets | +| `behavior` | object | No | Optional behavior layer for deterministic action planning and attribution | | `custom_extensions` | array | No | Vendor-specific attributes for extensibility | ### Example @@ -75,6 +77,11 @@ semantic_model: datasets: [] relationships: [] metrics: [] + behavior: + namespace: "SALES" + behavior_layer_version: "0.1" + actions: [] + rules: [] custom_extensions: - vendor_name: DBT data: '{"project_name": "tpcds_analytics", "models_path": "models/semantic"}' @@ -82,6 +89,75 @@ semantic_model: --- +## Behavior (Actions / Rules / Effects) + +Behavior is an optional, vendor-agnostic layer for **deterministic action planning** and **attribution** (what changed/why). + +This repository supports two equivalent placements: +1) **Preferred (first-class):** `semantic_model[].behavior` +2) **Backwards-compatible (legacy):** embed behavior-layer JSON under `custom_extensions` (e.g. `vendor_name: COMMON`) + +### Schema (semantic_model.behavior) + +| Field | Type | Required | Description | +|------|------|----------|-------------| +| `namespace` | string | Yes | Namespace/group for actions and rules (e.g. `SAP_P2P`) | +| `behavior_layer_version` | string | Yes | Version for behavior schema evolution | +| `actions` | array | No* | Preferred list of actions (`actions` or `action_types` required) | +| `action_types` | array | No* | Legacy alias of `actions` (for backwards compatibility) | +| `rules` | array | Yes | Declarative constraints/guards for planning and governance | +| `metadata` | object | No | Governance metadata (owner/tags/last_updated, etc.) | + +### Action Schema (high-level) + +Actions represent executable intents (API calls, workflows, tools). This spec intentionally does **not** assume SQL. + +| Field | Type | Required | Description | +|------|------|----------|-------------| +| `id` | string | Yes | Action id, e.g. `suppliers/block` | +| `title` | string | Yes | Human-friendly title | +| `kind` | string | No | `command` or `query` | +| `operation` | string | No | Free-form operation name (e.g. `block`, `unblock`, `analyze`) | +| `effects` | array | No | Optional machine-readable impact annotations | + +### Effects (impact annotations) + +Effects encode how an action changes datasets/fields for: +- plan validation (state transitions / prohibited follow-ups) +- attribution (what changed/why for a field or dataset) + +Minimal effect fields: +`entity` (dataset/field/metric/relationship), `mode` (read/write/derive), `selectors` (dataset + field_names), +optional `impact_type`, `transition`, `set_value`. + +### Example + +```yaml +semantic_model: + - name: sap_p2p + datasets: [] + behavior: + namespace: "SAP_P2P" + behavior_layer_version: "0.1" + actions: + - id: suppliers/block + title: Block supplier + kind: command + operation: block + entity_name: suppliers + effects: + - entity: field + mode: write + impact_type: state_transition + selectors: + dataset: suppliers + field_names: [status] + set_value: Blocked + rules: [] +``` + +--- + ## Datasets Logical datasets represent business entities or concepts (fact and dimension tables). They contain fields and define the structure of the data. diff --git a/core-spec/spec.yaml b/core-spec/spec.yaml index d27a9ba..bd56187 100644 --- a/core-spec/spec.yaml +++ b/core-spec/spec.yaml @@ -1,5 +1,5 @@ # OSI - Core Metadata Spec (YAML Schema) -version: 0.1.1 +version: 0.1.2 # # Goals: # - Standardization: Establish uniform language and structure for semantic model definitions @@ -59,6 +59,75 @@ semantic_model: - vendor_name: string # Must be one of the values from 'vendors' enum above data: string + # Optional: + # Behavior layer (actions/rules/effects) promoted to a first-class, vendor-agnostic structure. + # Intended for deterministic action planning and attribution (what changed/why). + # + # Backwards compatibility: + # - Tools MAY still embed behavior-layer JSON in custom_extensions (e.g. vendor_name: COMMON). + # - If both semantic_model.behavior and embedded behavior are provided, tools SHOULD define precedence + # (recommended: semantic_model.behavior takes precedence). + behavior: {} + +--- +# Behavior Schema (Optional) +# A vendor-agnostic behavior layer for deterministic action planning and attribution. +# This is intentionally separate from SQL/query generation: actions can map to APIs, workflows, or tools. +behavior: + # Required: Namespace for grouping actions/rules (e.g., "SAP_P2P", "CRM") + namespace: string + + # Required: Version of the behavior schema used by this model + behavior_layer_version: string + + # Optional: Metadata for governance/audit + metadata: {} + + # Preferred: List of actions supported by this semantic model + actions: [] + + # Legacy alias (compat): actions MAY also be provided as action_types + # New models SHOULD use actions. + action_types: [] + + # Optional: Declarative rules/constraints for planning and validation + rules: [] + +--- +# Action Schema (within behavior.actions) +actions: + - id: string # e.g. "suppliers/block" + title: string # Human-friendly title + description: string + kind: "command"|"query" + operation: string # free-form, e.g. "block"|"unblock"|"create"|"analyze" + entity_name: string # dataset name or conceptual entity + idempotency: "idempotent"|"non_idempotent"|"unknown" + applies_to: {} # optional selectors (dataset/field/metric/relationship) + io_schema: {} # optional input/output JSON schema for tool execution + effects: [] # optional: machine-readable impact annotations + tags: [] + synonyms: [] + +--- +# Effect Schema (within action.effects) +# Used for: +# - Planning: understand state transitions and prohibited/required follow-up actions +# - Attribution: explain "what changed/why" for fields/datasets +effects: + - entity: "dataset"|"field"|"metric"|"relationship" + mode: "read"|"write"|"derive" + impact_type: "state_transition"|"master_data_mutation"|"transactional_write"|"derived_metric_change"|"other" + selectors: + dataset: string + field_names: [] + transition: + from: string + to: string + set_value: string + confidence: "guaranteed"|"likely"|"unknown" + notes: string + --- # Logical Dataset Schema # Represents business entities or concepts (fact and dimension tables) diff --git a/examples/p2p_behavior_effects_minimal.yaml b/examples/p2p_behavior_effects_minimal.yaml new file mode 100644 index 0000000..1da4c6a --- /dev/null +++ b/examples/p2p_behavior_effects_minimal.yaml @@ -0,0 +1,119 @@ +# yaml-language-server: $schema=../core-spec/osi-schema.json +version: 0.1.2 + +semantic_model: + - name: sap_p2p_minimal_behavior + description: "Minimal example: first-class behavior/actions/effects for SAP P2P" + + datasets: + - name: suppliers + source: sap.p2p.suppliers + primary_key: [supplier_id] + description: "Supplier master data" + fields: + - name: supplier_id + expression: + dialects: + - dialect: ANSI_SQL + expression: supplier_id + description: "Supplier ID" + + - name: supplier_name + expression: + dialects: + - dialect: ANSI_SQL + expression: supplier_name + description: "Supplier name" + + - name: status + expression: + dialects: + - dialect: ANSI_SQL + expression: status + description: "Supplier lifecycle / blocking status (e.g., Active, Blocked)" + + behavior: + namespace: "SAP_P2P" + behavior_layer_version: "0.1" + metadata: + owner: "example" + tags: ["p2p", "behavior", "effects"] + + actions: + - id: suppliers/block + title: Block supplier + description: Block a supplier and prevent downstream procurement actions. + kind: command + operation: block + entity_name: suppliers + idempotency: idempotent + io_schema: + input_schema: + type: object + required: [supplier_id] + properties: + supplier_id: { type: string } + reason: { type: string } + output_schema: + type: object + properties: + supplier_id: { type: string } + status: { type: string } + effects: + - entity: field + mode: write + impact_type: state_transition + selectors: + dataset: suppliers + field_names: [status] + set_value: Blocked + confidence: guaranteed + notes: "After blocking, procurement actions should be denied by policy." + + - id: suppliers/unblock + title: Unblock supplier + description: Unblock a supplier. + kind: command + operation: unblock + entity_name: suppliers + idempotency: idempotent + io_schema: + input_schema: + type: object + required: [supplier_id] + properties: + supplier_id: { type: string } + comment: { type: string } + output_schema: + type: object + properties: + supplier_id: { type: string } + status: { type: string } + effects: + - entity: field + mode: write + impact_type: state_transition + selectors: + dataset: suppliers + field_names: [status] + transition: + from: Blocked + to: Active + confidence: likely + notes: "Exact target status may vary by ERP configuration." + + rules: + - id: sap_p2p/rule_blocked_vendor_transaction_prevention + title: Blocked supplier prevents procurement transactions + severity: error + when: + entity: dataset + selectors: + dataset: suppliers + field_names: [status] + constraint: + type: state_machine + rule: "If suppliers.status == 'Blocked' then deny procurement transactions." + message: "Supplier is blocked. Procurement transactions are not allowed." + remediation: "Run suppliers/unblock or contact an administrator to unblock the supplier." + diff --git a/examples/tpcds_semantic_model.yaml b/examples/tpcds_semantic_model.yaml index 98e956b..18441e5 100644 --- a/examples/tpcds_semantic_model.yaml +++ b/examples/tpcds_semantic_model.yaml @@ -4,7 +4,7 @@ # This example demonstrates the OSI Core Metadata Spec using the TPC-DS benchmark schema # TPC-DS is a decision support benchmark with a realistic retail business model -version: "0.1.1" +version: "0.1.2" semantic_model: - name: tpcds_retail_model