diff --git a/CHANGELOG.md b/CHANGELOG.md index a6fb38b5b..2297bad8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -92,6 +92,16 @@ `MappingProxyType[str, int]` (read-only) instead of `dict[str, int]`; callers that mutated the returned mapping must copy to a `dict` first. (#575, PR #657) +- Implemented UKO Layer 3 technology-specific vocabulary extensions for Python + (`uko-py:`), TypeScript (`uko-ts:`), Rust (`uko-rs:`), and Java (`uko-java:`). + Each vocabulary defines OWL classes, properties, Layer 2 dependencies, and + DetailLevelMap insertions per specification lines 44405-44420. Python inserts + 3 new depth levels (DECORATED_SIGNATURES, TYPE_STUBS, WITH_TESTS) producing a + 15-level effective map; TS/RS/Java extend at SIGNATURES level without new + insertions. Includes OWL/Turtle ontology files, ProvenanceInfo model with 2 + required fields (source_resource, source_path) and 3 defaulted fields + (source_range, valid_from, is_current), build_detail_level_map/resolve_detail_level + utilities, and full Behave BDD tests (78 scenarios, 200 steps). (#576) - Added `RepoIndexingService` for repository file indexing with incremental refresh, extension-based language detection, SHA-256 content hashing, and token estimation. Supports policy enforcement via include/exclude globs, @@ -219,9 +229,9 @@ bootstrap. Three scenarios: one failing TDD test reproducing bug #524 (no bootstrap called during init), and two regression tests verifying `bootstrap_builtin_types()` seeds correct data and `agents resource add git-checkout` succeeds. Includes Robot - Framework regression tests. (#553) + Framework regression tests. (Drive-by: corrected bug reference from #523 to #524.) (#553) - Added TDD-style Behave BDD tests for the built-in `fs-directory` resource type - bootstrap. Three scenarios: one failing TDD test reproducing bug #523 (no bootstrap + bootstrap. Three scenarios: one failing TDD test reproducing bug #524 (no bootstrap called during init), and two regression tests verifying `bootstrap_builtin_types()` seeds correct data and `agents resource add fs-directory` succeeds. Includes Robot Framework regression tests. (#537) diff --git a/benchmarks/bench_uko_layer3.py b/benchmarks/bench_uko_layer3.py new file mode 100644 index 000000000..d0d11cc40 --- /dev/null +++ b/benchmarks/bench_uko_layer3.py @@ -0,0 +1,143 @@ +"""ASV benchmarks for UKO Layer 3 technology vocabulary operations. + +Measures the performance of: +- build_detail_level_map with insertions (Python: 3 insertions, 15-level map) +- build_detail_level_map without insertions (TS/RS/Java: identity, 12-level map) +- resolve_detail_level lookups (direct hit, parent fallback) +- UKOVocabulary construction (frozen Pydantic model with nested tuples) +- ProvenanceInfo construction + +Based on specification.md Layer 3 Technology-Specific Specializations. +""" + +from __future__ import annotations + +import sys +from datetime import UTC, datetime +from pathlib import Path + +_SRC = str(Path(__file__).resolve().parents[1] / "src") +if _SRC not in sys.path: + sys.path.insert(0, _SRC) + +from cleveragents.acms.uko.layer3_java import JAVA_DETAIL_LEVELS +from cleveragents.acms.uko.layer3_py import PYTHON_DETAIL_LEVELS +from cleveragents.acms.uko.layer3_rs import RUST_DETAIL_LEVELS +from cleveragents.acms.uko.layer3_ts import TYPESCRIPT_DETAIL_LEVELS +from cleveragents.acms.uko.vocabulary import ( + OO_EFFECTIVE_LEVELS, + ProvenanceInfo, + UKOClass, + UKOVocabulary, + build_detail_level_map, + resolve_detail_level, +) + + +class BuildDetailLevelMapSuite: + """Benchmark build_detail_level_map for Layer 3 maps.""" + + timeout = 60 + + def setup(self) -> None: + """Prepare parent levels and insertions.""" + self.oo_levels = OO_EFFECTIVE_LEVELS + self.py_insertions: tuple[tuple[str, int], ...] = ( + ("DECORATED_SIGNATURES", 6), + ("TYPE_STUBS", 9), + ("WITH_TESTS", 12), + ) + self.empty_insertions: tuple[tuple[str, int], ...] = () + + def time_build_with_insertions(self) -> None: + """Build Python 15-level map (3 insertions into 12 parents).""" + build_detail_level_map(self.oo_levels, self.py_insertions) + + def time_build_identity(self) -> None: + """Build identity map (no insertions -- TS/RS/Java pattern).""" + build_detail_level_map(self.oo_levels, self.empty_insertions) + + def track_python_map_size(self) -> int: + """Track number of levels in the Python effective map.""" + return len(build_detail_level_map(self.oo_levels, self.py_insertions)) + + def track_identity_map_size(self) -> int: + """Track number of levels in the identity map.""" + return len(build_detail_level_map(self.oo_levels, self.empty_insertions)) + + +class ResolveDetailLevelSuite: + """Benchmark resolve_detail_level lookups.""" + + timeout = 60 + + def setup(self) -> None: + """Prepare maps for resolution.""" + self.py_levels = PYTHON_DETAIL_LEVELS + self.ts_levels = TYPESCRIPT_DETAIL_LEVELS + self.rs_levels = RUST_DETAIL_LEVELS + self.java_levels = JAVA_DETAIL_LEVELS + self.parent_levels = OO_EFFECTIVE_LEVELS + self.child_only_map: tuple[tuple[str, int], ...] = (("CHILD_ONLY", 0),) + + def time_resolve_first_level(self) -> None: + """Resolve MODULE_LISTING (first entry -- best case).""" + resolve_detail_level("MODULE_LISTING", self.py_levels) + + def time_resolve_last_level(self) -> None: + """Resolve WITH_TESTS (last entry -- worst case).""" + resolve_detail_level("WITH_TESTS", self.py_levels) + + def time_resolve_inserted_level(self) -> None: + """Resolve DECORATED_SIGNATURES (inserted level).""" + resolve_detail_level("DECORATED_SIGNATURES", self.py_levels) + + def time_resolve_with_parent_fallback(self) -> None: + """Resolve via parent fallback (name not in child map).""" + resolve_detail_level("MODULE_LISTING", self.child_only_map, self.parent_levels) + + def time_resolve_ts(self) -> None: + """Resolve SIGNATURES in TypeScript map.""" + resolve_detail_level("SIGNATURES", self.ts_levels) + + def time_resolve_rs(self) -> None: + """Resolve SIGNATURES in Rust map.""" + resolve_detail_level("SIGNATURES", self.rs_levels) + + def time_resolve_java(self) -> None: + """Resolve SIGNATURES in Java map.""" + resolve_detail_level("SIGNATURES", self.java_levels) + + +class VocabularyConstructionSuite: + """Benchmark UKOVocabulary and ProvenanceInfo construction.""" + + timeout = 60 + + def setup(self) -> None: + """Pre-compute a fixed datetime to avoid syscall noise.""" + self.fixed_datetime = datetime.now(tz=UTC) + + def time_provenance_info(self) -> None: + """Construct a ProvenanceInfo instance.""" + ProvenanceInfo( + source_resource="res-01", + source_path="src/main.py", + valid_from=self.fixed_datetime, + ) + + def time_uko_class(self) -> None: + """Construct a UKOClass instance.""" + UKOClass( + uri="https://example.com/Test", + label="Test", + subclass_of=("https://example.com/Parent",), + ) + + def time_uko_vocabulary(self) -> None: + """Construct a minimal UKOVocabulary instance.""" + UKOVocabulary( + namespace="https://example.com/test#", + prefix="test:", + label="Test Vocabulary", + ) diff --git a/docs/ontology/uko-java.ttl b/docs/ontology/uko-java.ttl new file mode 100644 index 000000000..e2a4234bb --- /dev/null +++ b/docs/ontology/uko-java.ttl @@ -0,0 +1,105 @@ +@prefix uko: . +@prefix uko-code: . +@prefix uko-oo: . +@prefix uko-java: . +@prefix rdf: . +@prefix rdfs: . +@prefix owl: . +@prefix xsd: . + + a owl:Ontology ; + rdfs:label "UKO Java Vocabulary" ; + rdfs:comment "Layer 3 technology vocabulary for Java." . + +# =========================================================================== +# UKO Layer 3: Java-specific vocabulary (uko-java:) +# +# Extends uko-oo: (Layer 2 OO paradigm) with Java-specific classes +# and properties. Extends at SIGNATURES level with package visibility, +# final/abstract/sealed modifiers, annotation lists, and checked +# exceptions. +# +# Based on docs/specification.md ~lines 44405-44420. +# =========================================================================== + +# NOTE: Properties with multiple rdfs:domain values use OWL intersection +# semantics (AND). This is intentional — the domain tuple serves as an +# informational hint for tooling, not a formal OWL restriction. +# See PR #660 review finding #18. + +# -- Classes -- + +uko-java:JavaClass a owl:Class ; + rdfs:subClassOf uko-oo:Class ; + rdfs:label "JavaClass" ; + rdfs:comment "A Java class definition — extends uko-oo:Class. Includes final/abstract/sealed modifiers and annotations." . + +uko-java:JavaInterface a owl:Class ; + rdfs:subClassOf uko-oo:Interface ; + rdfs:label "JavaInterface" ; + rdfs:comment "A Java interface — extends uko-oo:Interface. Includes sealed permits and default method support." . + +uko-java:JavaMethod a owl:Class ; + rdfs:subClassOf uko-oo:Method ; + rdfs:label "JavaMethod" ; + rdfs:comment "A Java method — extends uko-oo:Method. Includes annotations, checked exceptions, and modifiers." . + +uko-java:JavaAnnotation a owl:Class ; + rdfs:subClassOf uko-code:TypeDefinition ; + rdfs:label "JavaAnnotation" ; + rdfs:comment "A Java annotation entry (@Override, @Deprecated, custom). Records annotation name and parameters." . + +uko-java:JavaCheckedException a owl:Class ; + rdfs:subClassOf uko-code:TypeDefinition ; + rdfs:label "JavaCheckedException" ; + rdfs:comment "A checked exception declaration in a method's throws clause. Records the exception class and any conditions." . + +# -- Properties -- + +uko-java:packageVisibility a owl:DatatypeProperty ; + rdfs:domain uko-java:JavaClass , uko-java:JavaInterface , uko-java:JavaMethod ; + rdfs:range xsd:string ; + rdfs:label "packageVisibility" ; + rdfs:comment "Package-level visibility modifier: 'public', 'protected', 'package-private' (default), 'private'." . + +uko-java:isFinal a owl:DatatypeProperty ; + rdfs:domain uko-java:JavaClass , uko-java:JavaMethod ; + rdfs:range xsd:boolean ; + rdfs:label "isFinal" ; + rdfs:comment "Whether this class/method is marked 'final'." . + +uko-java:isAbstract a owl:DatatypeProperty ; + rdfs:domain uko-java:JavaClass , uko-java:JavaMethod ; + rdfs:range xsd:boolean ; + rdfs:label "isAbstract" ; + rdfs:comment "Whether this class/method is marked 'abstract'." . + +uko-java:isSealed a owl:DatatypeProperty ; + rdfs:domain uko-java:JavaClass , uko-java:JavaInterface ; + rdfs:range xsd:boolean ; + rdfs:label "isSealed" ; + rdfs:comment "Whether this class/interface is marked 'sealed'." . + +uko-java:hasAnnotation a owl:ObjectProperty ; + rdfs:domain uko-java:JavaClass , uko-java:JavaInterface , uko-java:JavaMethod ; + rdfs:range uko-java:JavaAnnotation ; + rdfs:label "hasAnnotation" ; + rdfs:comment "Links a class/method to its Java annotations." . + +uko-java:annotationName a owl:DatatypeProperty ; + rdfs:domain uko-java:JavaAnnotation ; + rdfs:range xsd:string ; + rdfs:label "annotationName" ; + rdfs:comment "The fully-qualified name of the annotation (e.g. 'java.lang.Override')." . + +uko-java:throwsException a owl:ObjectProperty ; + rdfs:domain uko-java:JavaMethod ; + rdfs:range uko-java:JavaCheckedException ; + rdfs:label "throwsException" ; + rdfs:comment "Links a method to its checked exception declarations." . + +uko-java:exceptionClass a owl:DatatypeProperty ; + rdfs:domain uko-java:JavaCheckedException ; + rdfs:range xsd:string ; + rdfs:label "exceptionClass" ; + rdfs:comment "Fully-qualified class name of the checked exception." . diff --git a/docs/ontology/uko-py.ttl b/docs/ontology/uko-py.ttl new file mode 100644 index 000000000..f98d825e5 --- /dev/null +++ b/docs/ontology/uko-py.ttl @@ -0,0 +1,89 @@ +@prefix uko: . +@prefix uko-code: . +@prefix uko-oo: . +@prefix uko-py: . +@prefix rdf: . +@prefix rdfs: . +@prefix owl: . +@prefix xsd: . + + a owl:Ontology ; + rdfs:label "UKO Python Vocabulary" ; + rdfs:comment "Layer 3 technology vocabulary for Python." . + +# =========================================================================== +# UKO Layer 3: Python-specific vocabulary (uko-py:) +# +# Extends uko-oo: (Layer 2 OO paradigm) with Python-specific classes +# and properties. DetailLevelMap insertions: +# - DECORATED_SIGNATURES (between SIGNATURES_WITH_DOCS and +# VISIBILITY_ANNOTATED) +# - TYPE_STUBS (between KEY_LOGIC and NEAR_COMPLETE) +# - WITH_TESTS (beyond FULL_SOURCE) +# +# Based on docs/specification.md ~lines 44405-44420, 25006-25026. +# =========================================================================== + +# NOTE: Properties with multiple rdfs:domain values use OWL intersection +# semantics (AND). This is intentional — the domain tuple serves as an +# informational hint for tooling, not a formal OWL restriction. +# See PR #660 review finding #18. + +# -- Classes -- + +uko-py:PythonModule a owl:Class ; + rdfs:subClassOf uko-code:Module ; + rdfs:label "PythonModule" ; + rdfs:comment "A Python module (.py file) — extends uko-code:Module." . + +uko-py:PythonClass a owl:Class ; + rdfs:subClassOf uko-oo:Class ; + rdfs:label "PythonClass" ; + rdfs:comment "A Python class definition — extends uko-oo:Class." . + +uko-py:PythonFunction a owl:Class ; + rdfs:subClassOf uko-oo:Method ; + rdfs:label "PythonFunction" ; + rdfs:comment "A Python function or method — extends uko-oo:Method. Includes decorators and type annotations." . + +uko-py:PythonDecorator a owl:Class ; + rdfs:subClassOf uko-code:TypeDefinition ; + rdfs:label "PythonDecorator" ; + rdfs:comment "A decorator chain entry (@property, @staticmethod, @classmethod, custom decorators)." . + +uko-py:PythonTypeStub a owl:Class ; + rdfs:subClassOf uko-code:TypeDefinition ; + rdfs:label "PythonTypeStub" ; + rdfs:comment "Type annotations from .pyi stub files or inline typing module usage." . + +# -- Properties -- + +uko-py:hasDecorator a owl:ObjectProperty ; + rdfs:domain uko-py:PythonFunction , uko-py:PythonClass ; + rdfs:range uko-py:PythonDecorator ; + rdfs:label "hasDecorator" ; + rdfs:comment "Links a function/class to its decorator chain entries." . + +uko-py:hasTypeStub a owl:ObjectProperty ; + rdfs:domain uko-py:PythonModule , uko-py:PythonClass , uko-py:PythonFunction ; + rdfs:range uko-py:PythonTypeStub ; + rdfs:label "hasTypeStub" ; + rdfs:comment "Links a module/class/function to its .pyi type stub." . + +uko-py:hasTest a owl:ObjectProperty ; + rdfs:domain uko-py:PythonFunction , uko-py:PythonClass ; + rdfs:range uko-py:PythonFunction ; + rdfs:label "hasTest" ; + rdfs:comment "Links a callable to its associated test cases (for WITH_TESTS depth level)." . + +uko-py:decoratorName a owl:DatatypeProperty ; + rdfs:domain uko-py:PythonDecorator ; + rdfs:range xsd:string ; + rdfs:label "decoratorName" ; + rdfs:comment "The string name of a decorator (e.g. 'property', 'staticmethod')." . + +uko-py:stubAnnotation a owl:DatatypeProperty ; + rdfs:domain uko-py:PythonTypeStub ; + rdfs:range xsd:string ; + rdfs:label "stubAnnotation" ; + rdfs:comment "Type annotation string from a .pyi stub file." . diff --git a/docs/ontology/uko-rs.ttl b/docs/ontology/uko-rs.ttl new file mode 100644 index 000000000..01af3b205 --- /dev/null +++ b/docs/ontology/uko-rs.ttl @@ -0,0 +1,105 @@ +@prefix uko: . +@prefix uko-code: . +@prefix uko-oo: . +@prefix uko-rs: . +@prefix rdf: . +@prefix rdfs: . +@prefix owl: . +@prefix xsd: . + + a owl:Ontology ; + rdfs:label "UKO Rust Vocabulary" ; + rdfs:comment "Layer 3 technology vocabulary for Rust." . + +# =========================================================================== +# UKO Layer 3: Rust-specific vocabulary (uko-rs:) +# +# Extends uko-oo: and uko-func: (Layer 2 paradigms) with Rust-specific +# classes and properties. Extends at SIGNATURES level with visibility +# modifiers, lifetime parameters, trait bounds, unsafe markers, +# #[derive] macros, and #[must_use] annotations. +# +# Based on docs/specification.md ~lines 44405-44420. +# =========================================================================== + +# NOTE: Properties with multiple rdfs:domain values use OWL intersection +# semantics (AND). This is intentional — the domain tuple serves as an +# informational hint for tooling, not a formal OWL restriction. +# See PR #660 review finding #18. + +# -- Classes -- + +uko-rs:RustStruct a owl:Class ; + rdfs:subClassOf uko-oo:Class ; + rdfs:label "RustStruct" ; + rdfs:comment "A Rust struct definition — extends uko-oo:Class. Includes derive macros, visibility, and lifetime parameters." . + +uko-rs:RustTrait a owl:Class ; + rdfs:subClassOf uko-oo:Interface ; + rdfs:label "RustTrait" ; + rdfs:comment "A Rust trait definition — extends uko-oo:Interface. Includes associated types, default implementations, and supertraits." . + +uko-rs:RustImpl a owl:Class ; + rdfs:subClassOf uko-code:TypeDefinition ; + rdfs:label "RustImpl" ; + rdfs:comment "A Rust impl block — either inherent or trait implementation. Extends uko-code:TypeDefinition." . + +uko-rs:RustFunction a owl:Class ; + rdfs:subClassOf uko-oo:Method ; + rdfs:label "RustFunction" ; + rdfs:comment "A Rust function or method — extends uko-oo:Method. Includes lifetime parameters, trait bounds, and unsafe markers." . + +uko-rs:RustDeriveAttribute a owl:Class ; + rdfs:subClassOf uko-code:TypeDefinition ; + rdfs:label "RustDeriveAttribute" ; + rdfs:comment "A #[derive(...)] macro attribute applied to a struct/enum. Records which traits are automatically derived." . + +# -- Properties -- + +uko-rs:visibility a owl:DatatypeProperty ; + rdfs:domain uko-rs:RustStruct , uko-rs:RustTrait , uko-rs:RustFunction ; + rdfs:range xsd:string ; + rdfs:label "visibility" ; + rdfs:comment "Visibility modifier: 'pub', 'pub(crate)', 'pub(super)', 'private' (default)." . + +uko-rs:lifetimeParameters a owl:DatatypeProperty ; + rdfs:domain uko-rs:RustStruct , uko-rs:RustImpl , uko-rs:RustFunction , uko-rs:RustTrait ; + rdfs:range xsd:string ; + rdfs:label "lifetimeParameters" ; + rdfs:comment "Lifetime parameter string (e.g. \"<'a, 'b>\")." . + +uko-rs:traitBounds a owl:DatatypeProperty ; + rdfs:domain uko-rs:RustStruct , uko-rs:RustFunction , uko-rs:RustImpl ; + rdfs:range xsd:string ; + rdfs:label "traitBounds" ; + rdfs:comment "Trait bound constraints (e.g. 'T: Clone + Send')." . + +uko-rs:isUnsafe a owl:DatatypeProperty ; + rdfs:domain uko-rs:RustFunction , uko-rs:RustImpl , uko-rs:RustTrait ; + rdfs:range xsd:boolean ; + rdfs:label "isUnsafe" ; + rdfs:comment "Whether this item is marked as 'unsafe'." . + +uko-rs:deriveTraits a owl:DatatypeProperty ; + rdfs:domain uko-rs:RustDeriveAttribute ; + rdfs:range xsd:string ; + rdfs:label "deriveTraits" ; + rdfs:comment "Comma-separated list of traits in #[derive(...)]. E.g. 'Clone, Debug, Serialize'." . + +uko-rs:mustUse a owl:DatatypeProperty ; + rdfs:domain uko-rs:RustStruct , uko-rs:RustFunction ; + rdfs:range xsd:boolean ; + rdfs:label "mustUse" ; + rdfs:comment "Whether this item carries a #[must_use] annotation." . + +uko-rs:hasDerive a owl:ObjectProperty ; + rdfs:domain uko-rs:RustStruct ; + rdfs:range uko-rs:RustDeriveAttribute ; + rdfs:label "hasDerive" ; + rdfs:comment "Links a struct to its #[derive] attribute." . + +uko-rs:implementsTrait a owl:ObjectProperty ; + rdfs:domain uko-rs:RustImpl ; + rdfs:range uko-rs:RustTrait ; + rdfs:label "implementsTrait" ; + rdfs:comment "Links an impl block to the trait it implements." . diff --git a/docs/ontology/uko-ts.ttl b/docs/ontology/uko-ts.ttl new file mode 100644 index 000000000..309a56a4b --- /dev/null +++ b/docs/ontology/uko-ts.ttl @@ -0,0 +1,88 @@ +@prefix uko: . +@prefix uko-code: . +@prefix uko-oo: . +@prefix uko-ts: . +@prefix rdf: . +@prefix rdfs: . +@prefix owl: . +@prefix xsd: . + + a owl:Ontology ; + rdfs:label "UKO TypeScript Vocabulary" ; + rdfs:comment "Layer 3 technology vocabulary for TypeScript." . + +# =========================================================================== +# UKO Layer 3: TypeScript-specific vocabulary (uko-ts:) +# +# Extends uko-oo: (Layer 2 OO paradigm) with TypeScript-specific classes +# and properties. Extends at SIGNATURES level with export/default +# export markers, declare ambient types, generic type parameters, +# mapped/conditional types. +# +# Based on docs/specification.md ~lines 44405-44420. +# =========================================================================== + +# NOTE: Properties with multiple rdfs:domain values use OWL intersection +# semantics (AND). This is intentional — the domain tuple serves as an +# informational hint for tooling, not a formal OWL restriction. +# See PR #660 review finding #18. + +# -- Classes -- + +uko-ts:TypeScriptModule a owl:Class ; + rdfs:subClassOf uko-code:Module ; + rdfs:label "TypeScriptModule" ; + rdfs:comment "A TypeScript module (.ts/.tsx file) — extends uko-code:Module." . + +uko-ts:TypeScriptClass a owl:Class ; + rdfs:subClassOf uko-oo:Class ; + rdfs:label "TypeScriptClass" ; + rdfs:comment "A TypeScript class definition — extends uko-oo:Class." . + +uko-ts:TypeScriptInterface a owl:Class ; + rdfs:subClassOf uko-oo:Interface ; + rdfs:label "TypeScriptInterface" ; + rdfs:comment "A TypeScript interface — extends uko-oo:Interface. Includes generic type parameters, mapped and conditional types." . + +uko-ts:TypeScriptFunction a owl:Class ; + rdfs:subClassOf uko-oo:Method ; + rdfs:label "TypeScriptFunction" ; + rdfs:comment "A TypeScript function or method — extends uko-oo:Method. Includes export markers and generic type parameters." . + +# -- Properties -- + +uko-ts:exportKind a owl:DatatypeProperty ; + rdfs:domain uko-ts:TypeScriptClass , uko-ts:TypeScriptInterface , uko-ts:TypeScriptFunction , uko-ts:TypeScriptModule ; + rdfs:range xsd:string ; + rdfs:label "exportKind" ; + rdfs:comment "Export marker: 'named', 'default', 'none'. Indicates how this symbol is exported from its module." . + +uko-ts:isAmbient a owl:DatatypeProperty ; + rdfs:domain uko-ts:TypeScriptClass , uko-ts:TypeScriptInterface , uko-ts:TypeScriptFunction ; + rdfs:range xsd:boolean ; + rdfs:label "isAmbient" ; + rdfs:comment "Whether this is a 'declare' ambient type definition (e.g. in a .d.ts file)." . + +uko-ts:genericParameters a owl:DatatypeProperty ; + rdfs:domain uko-ts:TypeScriptClass , uko-ts:TypeScriptInterface , uko-ts:TypeScriptFunction ; + rdfs:range xsd:string ; + rdfs:label "genericParameters" ; + rdfs:comment "Generic type parameter string (e.g. '')." . + +uko-ts:isMappedType a owl:DatatypeProperty ; + rdfs:domain uko-ts:TypeScriptInterface ; + rdfs:range xsd:boolean ; + rdfs:label "isMappedType" ; + rdfs:comment "Whether this interface defines a mapped type." . + +uko-ts:isConditionalType a owl:DatatypeProperty ; + rdfs:domain uko-ts:TypeScriptInterface ; + rdfs:range xsd:boolean ; + rdfs:label "isConditionalType" ; + rdfs:comment "Whether this interface/type alias uses conditional types." . + +uko-ts:implementsInterface a owl:ObjectProperty ; + rdfs:label "implementsInterface" ; + rdfs:comment "Links a TypeScript class to the interface(s) it implements." ; + rdfs:domain uko-ts:TypeScriptClass ; + rdfs:range uko-ts:TypeScriptInterface . diff --git a/docs/reference/uko_layer3_technology_vocabularies.md b/docs/reference/uko_layer3_technology_vocabularies.md new file mode 100644 index 000000000..b6ef2ffa2 --- /dev/null +++ b/docs/reference/uko_layer3_technology_vocabularies.md @@ -0,0 +1,173 @@ +# UKO Layer 3 Technology Vocabularies + +## Overview + +Layer 3 of the Unified Knowledge Ontology (UKO) extends Layer 2 paradigm +vocabularies (or Layer 1 directly) with technology-specific semantic +refinements for four programming languages: + +- **Python** (`uko-py:`) -- decorators, `.pyi` type stubs, test associations +- **TypeScript** (`uko-ts:`) -- export markers, ambient types, generics, mapped/conditional types +- **Rust** (`uko-rs:`) -- visibility, lifetimes, trait bounds, derive macros, unsafe markers +- **Java** (`uko-java:`) -- package visibility, modifiers, annotations, checked exceptions + +Each vocabulary defines OWL classes, properties, Layer 2 dependencies, and +optional DetailLevelMap insertions per `docs/specification.md` ~lines 44405-44420. + +## Namespace Prefixes + +| Prefix | IRI | Layer | +|--------|-----|-------| +| `uko-py:` | `https://cleveragents.ai/ontology/uko/py#` | 3 | +| `uko-ts:` | `https://cleveragents.ai/ontology/uko/ts#` | 3 | +| `uko-rs:` | `https://cleveragents.ai/ontology/uko/rs#` | 3 | +| `uko-java:` | `https://cleveragents.ai/ontology/uko/java#` | 3 | + +## Python Vocabulary (`uko-py:`) + +### Classes + +| Class | Superclass | Description | +|-------|-----------|-------------| +| `uko-py:PythonModule` | `uko-code:Module` | A Python module (`.py` file). | +| `uko-py:PythonClass` | `uko-oo:Class` | A Python class definition. | +| `uko-py:PythonFunction` | `uko-oo:Method` | A Python function or method. | +| `uko-py:PythonDecorator` | `uko-code:TypeDefinition` | A decorator chain entry. | +| `uko-py:PythonTypeStub` | `uko-code:TypeDefinition` | `.pyi` stub type annotation. | + +### Properties + +| Property | Domain | Range | Type | +|----------|--------|-------|------| +| `uko-py:hasDecorator` | PythonFunction, PythonClass | PythonDecorator | ObjectProperty | +| `uko-py:hasTypeStub` | PythonModule, PythonClass, PythonFunction | PythonTypeStub | ObjectProperty | +| `uko-py:hasTest` | PythonFunction, PythonClass | PythonFunction | ObjectProperty | +| `uko-py:decoratorName` | PythonDecorator | `xsd:string` | DatatypeProperty | +| `uko-py:stubAnnotation` | PythonTypeStub | `xsd:string` | DatatypeProperty | + +### DetailLevelMap Insertions + +Python inserts 3 levels into the OO parent map, producing a 15-level effective map: + +| Depth | Name | Description | +|-------|------|-------------| +| 7 | `DECORATED_SIGNATURES` | Decorator chains (`@property`, `@staticmethod`, custom). | +| 11 | `TYPE_STUBS` | Type annotations from `.pyi` stub files. | +| 14 | `WITH_TESTS` | Full source + associated test cases. | + +## TypeScript Vocabulary (`uko-ts:`) + +### Classes + +| Class | Superclass | Description | +|-------|-----------|-------------| +| `uko-ts:TypeScriptModule` | `uko-code:Module` | A TypeScript module (`.ts`/`.tsx`). | +| `uko-ts:TypeScriptClass` | `uko-oo:Class` | A TypeScript class. | +| `uko-ts:TypeScriptInterface` | `uko-oo:Interface` | A TypeScript interface (generics, mapped/conditional). | +| `uko-ts:TypeScriptFunction` | `uko-oo:Method` | A TypeScript function or method. | + +### Properties + +| Property | Domain | Range | Type | +|----------|--------|-------|------| +| `uko-ts:exportKind` | TypeScriptClass, TypeScriptInterface, TypeScriptFunction, TypeScriptModule | `xsd:string` | DatatypeProperty | +| `uko-ts:isAmbient` | TypeScriptClass, TypeScriptInterface, TypeScriptFunction | `xsd:boolean` | DatatypeProperty | +| `uko-ts:genericParameters` | TypeScriptClass, TypeScriptInterface, TypeScriptFunction | `xsd:string` | DatatypeProperty | +| `uko-ts:isMappedType` | TypeScriptInterface | `xsd:boolean` | DatatypeProperty | +| `uko-ts:isConditionalType` | TypeScriptInterface | `xsd:boolean` | DatatypeProperty | +| `uko-ts:implementsInterface` | TypeScriptClass | TypeScriptInterface | ObjectProperty | + +### DetailLevelMap Insertions + +TypeScript extends at the SIGNATURES level with no new depth insertions (12-level identity map). + +## Rust Vocabulary (`uko-rs:`) + +### Classes + +| Class | Superclass | Description | +|-------|-----------|-------------| +| `uko-rs:RustStruct` | `uko-oo:Class` | A Rust struct definition. | +| `uko-rs:RustTrait` | `uko-oo:Interface` | A Rust trait definition. | +| `uko-rs:RustImpl` | `uko-code:TypeDefinition` | A Rust impl block. | +| `uko-rs:RustFunction` | `uko-oo:Method` | A Rust function or method. | +| `uko-rs:RustDeriveAttribute` | `uko-code:TypeDefinition` | A `#[derive(...)]` macro attribute. | + +### Properties + +| Property | Domain | Range | Type | +|----------|--------|-------|------| +| `uko-rs:visibility` | RustStruct, RustTrait, RustFunction | `xsd:string` | DatatypeProperty | +| `uko-rs:lifetimeParameters` | RustStruct, RustImpl, RustFunction, RustTrait | `xsd:string` | DatatypeProperty | +| `uko-rs:traitBounds` | RustStruct, RustFunction, RustImpl | `xsd:string` | DatatypeProperty | +| `uko-rs:isUnsafe` | RustFunction, RustImpl, RustTrait | `xsd:boolean` | DatatypeProperty | +| `uko-rs:deriveTraits` | RustDeriveAttribute | `xsd:string` | DatatypeProperty | +| `uko-rs:mustUse` | RustStruct, RustFunction | `xsd:boolean` | DatatypeProperty | +| `uko-rs:hasDerive` | RustStruct | RustDeriveAttribute | ObjectProperty | +| `uko-rs:implementsTrait` | RustImpl | RustTrait | ObjectProperty | + +### DetailLevelMap Insertions + +Rust extends at the SIGNATURES level with no new depth insertions (12-level identity map). + +## Java Vocabulary (`uko-java:`) + +### Classes + +| Class | Superclass | Description | +|-------|-----------|-------------| +| `uko-java:JavaClass` | `uko-oo:Class` | A Java class definition. | +| `uko-java:JavaInterface` | `uko-oo:Interface` | A Java interface. | +| `uko-java:JavaMethod` | `uko-oo:Method` | A Java method. | +| `uko-java:JavaAnnotation` | `uko-code:TypeDefinition` | A Java annotation entry. | +| `uko-java:JavaCheckedException` | `uko-code:TypeDefinition` | A checked exception declaration. | + +### Properties + +| Property | Domain | Range | Type | +|----------|--------|-------|------| +| `uko-java:packageVisibility` | JavaClass, JavaInterface, JavaMethod | `xsd:string` | DatatypeProperty | +| `uko-java:isFinal` | JavaClass, JavaMethod | `xsd:boolean` | DatatypeProperty | +| `uko-java:isAbstract` | JavaClass, JavaMethod | `xsd:boolean` | DatatypeProperty | +| `uko-java:isSealed` | JavaClass, JavaInterface | `xsd:boolean` | DatatypeProperty | +| `uko-java:hasAnnotation` | JavaClass, JavaInterface, JavaMethod | JavaAnnotation | ObjectProperty | +| `uko-java:annotationName` | JavaAnnotation | `xsd:string` | DatatypeProperty | +| `uko-java:throwsException` | JavaMethod | JavaCheckedException | ObjectProperty | +| `uko-java:exceptionClass` | JavaCheckedException | `xsd:string` | DatatypeProperty | + +### DetailLevelMap Insertions + +Java extends at the SIGNATURES level with no new depth insertions (12-level identity map). + +## Shared Base Types + +All Layer 3 vocabularies use shared types from `vocabulary.py`: + +| Type | Description | +|------|-------------| +| `ProvenanceInfo` | Frozen Pydantic model with 2 required fields (`source_resource`, `source_path`) and 3 defaulted fields (`source_range`, `valid_from`, `is_current`). | +| `UKOClass` | Frozen OWL class definition (`uri`, `label`, `comment`, `subclass_of`, `provenance`). | +| `UKOProperty` | Frozen OWL property definition (`uri`, `label`, `comment`, `domain`, `range_uri`, `is_object_property`, `provenance`). | +| `Layer2Dependency` | Declared dependency on a Layer 1/2 URI (`uri`, `layer`, `prefix`, `description`). | +| `UKOVocabulary` | Container aggregating classes, properties, deps, inserted levels, and optional `provenance`. | + +## Utility Functions + +| Function | Description | +|----------|-------------| +| `build_detail_level_map(parent_levels, insertions)` | Merge parent + child insertions with consecutive integer re-numbering. | +| `resolve_detail_level(name, levels, parent_levels=None)` | Look up a named level in `levels`, falling back to `parent_levels`. | + +## Source Files + +| File | Description | +|------|-------------| +| `src/cleveragents/acms/uko/vocabulary.py` | Shared base types and utility functions. | +| `src/cleveragents/acms/uko/layer3_py.py` | Python vocabulary definitions. | +| `src/cleveragents/acms/uko/layer3_ts.py` | TypeScript vocabulary definitions. | +| `src/cleveragents/acms/uko/layer3_rs.py` | Rust vocabulary definitions. | +| `src/cleveragents/acms/uko/layer3_java.py` | Java vocabulary definitions. | +| `docs/ontology/uko-py.ttl` | Python OWL/Turtle ontology. | +| `docs/ontology/uko-ts.ttl` | TypeScript OWL/Turtle ontology. | +| `docs/ontology/uko-rs.ttl` | Rust OWL/Turtle ontology. | +| `docs/ontology/uko-java.ttl` | Java OWL/Turtle ontology. | diff --git a/features/acms/uko_layer3_vocabularies.feature b/features/acms/uko_layer3_vocabularies.feature new file mode 100644 index 000000000..17ace2424 --- /dev/null +++ b/features/acms/uko_layer3_vocabularies.feature @@ -0,0 +1,518 @@ +@acms @uko @layer3 +Feature: UKO Layer 3 Technology Vocabularies + As a developer + I want language-specific UKO vocabulary extensions for Python, TypeScript, Rust, and Java + So that the ACMS can represent technology-specific semantic refinements with correct DetailLevelMap resolution + + # =========================================================================== + # ProvenanceInfo — construction and validation + # =========================================================================== + + @provenance + Scenario: Create a ProvenanceInfo with all fields for uko_l3 + Given a provenance info with resource "res-01HXM7" and path "src/auth/manager.py" for uko_l3 + Then the provenance source_resource should be "res-01HXM7" for uko_l3 + And the provenance source_path should be "src/auth/manager.py" for uko_l3 + And the provenance is_current should be true for uko_l3 + And the provenance valid_from should be a UTC datetime for uko_l3 + + @provenance + Scenario: Create a ProvenanceInfo with source range for uko_l3 + Given a ranged provenance info with resource "res-01ABC" and path "lib/util.py" and range "15:1-87:0" for uko_l3 + Then the provenance source_range should be "15:1-87:0" for uko_l3 + + @provenance + Scenario: ProvenanceInfo rejects empty source_resource for uko_l3 + Then creating a provenance info with empty source_resource should raise ValueError for uko_l3 + + @provenance + Scenario: ProvenanceInfo rejects whitespace-only source_resource for uko_l3 + Then creating a provenance info with whitespace source_resource should raise ValueError for uko_l3 + + @provenance + Scenario: ProvenanceInfo rejects empty source_path for uko_l3 + Then creating a provenance info with empty source_path should raise ValueError for uko_l3 + + @provenance + Scenario: ProvenanceInfo rejects whitespace-only source_path for uko_l3 + Then creating a provenance info with whitespace source_path should raise ValueError for uko_l3 + + @provenance + Scenario: ProvenanceInfo is immutable for uko_l3 + Given a provenance info with resource "res-FROZEN" and path "frozen.py" for uko_l3 + Then modifying provenance is_current should raise an error for uko_l3 + + # =========================================================================== + # UKOClass — construction and validation + # =========================================================================== + + @uko_class + Scenario: Create a UKOClass with subclass_of for uko_l3 + Given a UKO class with uri "https://example.com/Test" and label "Test" for uko_l3 + Then the UKO class uri should be "https://example.com/Test" for uko_l3 + And the UKO class label should be "Test" for uko_l3 + And the UKO class subclass_of should be empty for uko_l3 + + @uko_class + Scenario: UKOClass rejects empty uri for uko_l3 + Then creating a UKO class with empty uri should raise ValueError for uko_l3 + + @uko_class + Scenario: UKOClass rejects whitespace-only label for uko_l3 + Then creating a UKO class with whitespace label should raise ValueError for uko_l3 + + @uko_class + Scenario: UKOClass is immutable for uko_l3 + Given a UKO class with uri "https://example.com/Frozen" and label "Frozen" for uko_l3 + Then modifying UKO class label should raise an error for uko_l3 + + @uko_class @provenance + Scenario: UKOClass carries provenance when provided for uko_l3 + Given a UKO class with provenance resource "res-42" and path "src/main.py" for uko_l3 + Then the UKO class provenance source_resource should be "res-42" for uko_l3 + And the UKO class provenance source_path should be "src/main.py" for uko_l3 + + @uko_class @provenance + Scenario: UKOClass provenance defaults to None for uko_l3 + Given a UKO class with uri "https://example.com/NoProv" and label "NoProv" for uko_l3 + Then the UKO class provenance should be None for uko_l3 + + # =========================================================================== + # UKOProperty — construction and validation + # =========================================================================== + + @uko_property + Scenario: Create a UKOProperty for uko_l3 + Given a UKO property with uri "https://example.com/hasFoo" and label "hasFoo" for uko_l3 + Then the UKO property uri should be "https://example.com/hasFoo" for uko_l3 + And the UKO property is_object_property should be true for uko_l3 + + @uko_property + Scenario: UKOProperty rejects empty uri for uko_l3 + Then creating a UKO property with empty uri should raise ValueError for uko_l3 + + # =========================================================================== + # Layer2Dependency — construction + # =========================================================================== + + @layer2_dep + Scenario: Create a Layer2Dependency for uko_l3 + Given a layer2 dependency with uri "https://cleveragents.ai/ontology/uko/oo#Class" and prefix "uko-oo:" for uko_l3 + Then the layer2 dependency layer should be 2 for uko_l3 + And the layer2 dependency prefix should be "uko-oo:" for uko_l3 + + # =========================================================================== + # UKOVocabulary — container for classes/properties/deps + # =========================================================================== + + @vocabulary + Scenario: Create a UKOVocabulary container for uko_l3 + Given a UKO vocabulary with namespace "https://example.com/test#" and prefix "test:" and label "Test" for uko_l3 + Then the vocabulary layer should be 3 for uko_l3 + And the vocabulary prefix should be "test:" for uko_l3 + + # =========================================================================== + # Python vocabulary (uko-py:) — classes and properties + # =========================================================================== + + @python_vocab + Scenario: Python vocabulary has correct namespace for uko_l3 + Then the Python vocabulary namespace should be "https://cleveragents.ai/ontology/uko/py#" for uko_l3 + And the Python vocabulary prefix should be "uko-py:" for uko_l3 + And the Python vocabulary layer should be 3 for uko_l3 + + @python_vocab + Scenario: Python vocabulary has 5 classes for uko_l3 + Then the Python vocabulary should have 5 classes for uko_l3 + And the Python vocabulary should contain class "PythonModule" for uko_l3 + And the Python vocabulary should contain class "PythonClass" for uko_l3 + And the Python vocabulary should contain class "PythonFunction" for uko_l3 + And the Python vocabulary should contain class "PythonDecorator" for uko_l3 + And the Python vocabulary should contain class "PythonTypeStub" for uko_l3 + + @python_vocab + Scenario: Python vocabulary has 5 properties for uko_l3 + Then the Python vocabulary should have 5 properties for uko_l3 + And the Python vocabulary should contain property "hasDecorator" for uko_l3 + And the Python vocabulary should contain property "hasTypeStub" for uko_l3 + And the Python vocabulary should contain property "hasTest" for uko_l3 + And the Python vocabulary should contain property "decoratorName" for uko_l3 + And the Python vocabulary should contain property "stubAnnotation" for uko_l3 + + @python_vocab + Scenario: Python vocabulary extends uko-oo: for uko_l3 + Then the Python vocabulary parent_prefixes should contain "uko-oo:" for uko_l3 + + @python_vocab + Scenario: PythonClass subclass_of includes uko-oo:Class for uko_l3 + Then the PythonClass subclass_of should contain "https://cleveragents.ai/ontology/uko/oo#Class" for uko_l3 + + @python_vocab + Scenario: Python vocabulary has 3 inserted levels for uko_l3 + Then the Python vocabulary should have 3 inserted levels for uko_l3 + And the Python vocabulary inserted levels should include "DECORATED_SIGNATURES" for uko_l3 + And the Python vocabulary inserted levels should include "TYPE_STUBS" for uko_l3 + And the Python vocabulary inserted levels should include "WITH_TESTS" for uko_l3 + + # =========================================================================== + # TypeScript vocabulary (uko-ts:) + # =========================================================================== + + @typescript_vocab + Scenario: TypeScript vocabulary has correct namespace for uko_l3 + Then the TypeScript vocabulary namespace should be "https://cleveragents.ai/ontology/uko/ts#" for uko_l3 + And the TypeScript vocabulary prefix should be "uko-ts:" for uko_l3 + And the TypeScript vocabulary layer should be 3 for uko_l3 + + @typescript_vocab + Scenario: TypeScript vocabulary has 4 classes for uko_l3 + Then the TypeScript vocabulary should have 4 classes for uko_l3 + And the TypeScript vocabulary should contain class "TypeScriptModule" for uko_l3 + And the TypeScript vocabulary should contain class "TypeScriptClass" for uko_l3 + And the TypeScript vocabulary should contain class "TypeScriptInterface" for uko_l3 + And the TypeScript vocabulary should contain class "TypeScriptFunction" for uko_l3 + + @typescript_vocab + Scenario: TypeScript vocabulary has 6 properties for uko_l3 + Then the TypeScript vocabulary should have 6 properties for uko_l3 + And the TypeScript vocabulary should contain property "exportKind" for uko_l3 + And the TypeScript vocabulary should contain property "isAmbient" for uko_l3 + And the TypeScript vocabulary should contain property "genericParameters" for uko_l3 + And the TypeScript vocabulary should contain property "isMappedType" for uko_l3 + And the TypeScript vocabulary should contain property "isConditionalType" for uko_l3 + And the TypeScript vocabulary should contain property "implementsInterface" for uko_l3 + + @typescript_vocab + Scenario: TypeScript vocabulary extends uko-oo: for uko_l3 + Then the TypeScript vocabulary parent_prefixes should contain "uko-oo:" for uko_l3 + + @typescript_vocab + Scenario: TypeScriptClass subclass_of includes uko-oo:Class for uko_l3 + Then the TypeScriptClass subclass_of should contain "https://cleveragents.ai/ontology/uko/oo#Class" for uko_l3 + + @typescript_vocab + Scenario: TypeScript vocabulary has 0 inserted levels for uko_l3 + Then the TypeScript vocabulary should have 0 inserted levels for uko_l3 + + # =========================================================================== + # Rust vocabulary (uko-rs:) + # =========================================================================== + + @rust_vocab + Scenario: Rust vocabulary has correct namespace for uko_l3 + Then the Rust vocabulary namespace should be "https://cleveragents.ai/ontology/uko/rs#" for uko_l3 + And the Rust vocabulary prefix should be "uko-rs:" for uko_l3 + And the Rust vocabulary layer should be 3 for uko_l3 + + @rust_vocab + Scenario: Rust vocabulary has 5 classes for uko_l3 + Then the Rust vocabulary should have 5 classes for uko_l3 + And the Rust vocabulary should contain class "RustStruct" for uko_l3 + And the Rust vocabulary should contain class "RustTrait" for uko_l3 + And the Rust vocabulary should contain class "RustImpl" for uko_l3 + And the Rust vocabulary should contain class "RustFunction" for uko_l3 + And the Rust vocabulary should contain class "RustDeriveAttribute" for uko_l3 + + @rust_vocab + Scenario: Rust vocabulary has 8 properties for uko_l3 + Then the Rust vocabulary should have 8 properties for uko_l3 + And the Rust vocabulary should contain property "visibility" for uko_l3 + And the Rust vocabulary should contain property "lifetimeParameters" for uko_l3 + And the Rust vocabulary should contain property "traitBounds" for uko_l3 + And the Rust vocabulary should contain property "isUnsafe" for uko_l3 + And the Rust vocabulary should contain property "deriveTraits" for uko_l3 + And the Rust vocabulary should contain property "mustUse" for uko_l3 + And the Rust vocabulary should contain property "hasDerive" for uko_l3 + And the Rust vocabulary should contain property "implementsTrait" for uko_l3 + + @rust_vocab + Scenario: Rust vocabulary extends uko-oo: for uko_l3 + Then the Rust vocabulary parent_prefixes should contain "uko-oo:" for uko_l3 + + @rust_vocab + Scenario: RustStruct subclass_of includes uko-oo:Class for uko_l3 + Then the RustStruct subclass_of should contain "https://cleveragents.ai/ontology/uko/oo#Class" for uko_l3 + + @rust_vocab + Scenario: Rust vocabulary has 0 inserted levels for uko_l3 + Then the Rust vocabulary should have 0 inserted levels for uko_l3 + + # =========================================================================== + # Java vocabulary (uko-java:) + # =========================================================================== + + @java_vocab + Scenario: Java vocabulary has correct namespace for uko_l3 + Then the Java vocabulary namespace should be "https://cleveragents.ai/ontology/uko/java#" for uko_l3 + And the Java vocabulary prefix should be "uko-java:" for uko_l3 + And the Java vocabulary layer should be 3 for uko_l3 + + @java_vocab + Scenario: Java vocabulary has 5 classes for uko_l3 + Then the Java vocabulary should have 5 classes for uko_l3 + And the Java vocabulary should contain class "JavaClass" for uko_l3 + And the Java vocabulary should contain class "JavaInterface" for uko_l3 + And the Java vocabulary should contain class "JavaMethod" for uko_l3 + And the Java vocabulary should contain class "JavaAnnotation" for uko_l3 + And the Java vocabulary should contain class "JavaCheckedException" for uko_l3 + + @java_vocab + Scenario: Java vocabulary has 8 properties for uko_l3 + Then the Java vocabulary should have 8 properties for uko_l3 + And the Java vocabulary should contain property "packageVisibility" for uko_l3 + And the Java vocabulary should contain property "isFinal" for uko_l3 + And the Java vocabulary should contain property "isAbstract" for uko_l3 + And the Java vocabulary should contain property "isSealed" for uko_l3 + And the Java vocabulary should contain property "hasAnnotation" for uko_l3 + And the Java vocabulary should contain property "annotationName" for uko_l3 + And the Java vocabulary should contain property "throwsException" for uko_l3 + And the Java vocabulary should contain property "exceptionClass" for uko_l3 + + @java_vocab + Scenario: Java vocabulary extends uko-oo: for uko_l3 + Then the Java vocabulary parent_prefixes should contain "uko-oo:" for uko_l3 + + @java_vocab + Scenario: JavaClass subclass_of includes uko-oo:Class for uko_l3 + Then the JavaClass subclass_of should contain "https://cleveragents.ai/ontology/uko/oo#Class" for uko_l3 + + @java_vocab + Scenario: Java vocabulary has 0 inserted levels for uko_l3 + Then the Java vocabulary should have 0 inserted levels for uko_l3 + + # =========================================================================== + # DetailLevelMap — build_detail_level_map + # =========================================================================== + + @detail_level_map + Scenario: build_detail_level_map with no insertions produces identity for uko_l3 + Given a parent level map with entries "A=0,B=1,C=2" for uko_l3 + When I build a detail level map with no insertions for uko_l3 + Then the resulting map should have 3 entries for uko_l3 + And the resulting map entry "A" should have depth 0 for uko_l3 + And the resulting map entry "B" should have depth 1 for uko_l3 + And the resulting map entry "C" should have depth 2 for uko_l3 + + @detail_level_map + Scenario: build_detail_level_map with one insertion shifts subsequent depths for uko_l3 + Given a parent level map with entries "A=0,B=1,C=2" for uko_l3 + When I build a detail level map with insertion "X=1" for uko_l3 + Then the resulting map should have 4 entries for uko_l3 + And the resulting map entry "A" should have depth 0 for uko_l3 + And the resulting map entry "B" should have depth 1 for uko_l3 + And the resulting map entry "X" should have depth 2 for uko_l3 + And the resulting map entry "C" should have depth 3 for uko_l3 + + @detail_level_map + Scenario: build_detail_level_map with multiple insertions for uko_l3 + Given a parent level map with entries "A=0,B=1,C=2,D=3" for uko_l3 + When I build a detail level map with insertions "X=1,Y=2" for uko_l3 + Then the resulting map should have 6 entries for uko_l3 + And the resulting map entry "A" should have depth 0 for uko_l3 + And the resulting map entry "B" should have depth 1 for uko_l3 + And the resulting map entry "X" should have depth 2 for uko_l3 + And the resulting map entry "C" should have depth 3 for uko_l3 + And the resulting map entry "Y" should have depth 4 for uko_l3 + And the resulting map entry "D" should have depth 5 for uko_l3 + + @detail_level_map + Scenario: build_detail_level_map rejects duplicate names for uko_l3 + Given a parent level map with entries "A=0,B=1" for uko_l3 + Then building a detail level map with duplicate insertion "A=0" should raise ValueError for uko_l3 + + # =========================================================================== + # Python effective DetailLevelMap — 4-layer resolution + # =========================================================================== + + @python_detail_map + Scenario: Python effective map has 15 levels for uko_l3 + Then the Python detail level map should have 15 entries for uko_l3 + + @python_detail_map + Scenario: Python effective map has correct depth for MODULE_LISTING for uko_l3 + Then the Python detail level "MODULE_LISTING" should resolve to depth 0 for uko_l3 + + @python_detail_map + Scenario: Python effective map has correct depth for DECORATED_SIGNATURES for uko_l3 + Then the Python detail level "DECORATED_SIGNATURES" should resolve to depth 7 for uko_l3 + + @python_detail_map + Scenario: Python effective map has correct depth for TYPE_STUBS for uko_l3 + Then the Python detail level "TYPE_STUBS" should resolve to depth 11 for uko_l3 + + @python_detail_map + Scenario: Python effective map has correct depth for WITH_TESTS for uko_l3 + Then the Python detail level "WITH_TESTS" should resolve to depth 14 for uko_l3 + + @python_detail_map + Scenario: Python effective map has correct depth for FULL_SOURCE for uko_l3 + Then the Python detail level "FULL_SOURCE" should resolve to depth 13 for uko_l3 + + @python_detail_map + Scenario: Python effective map has correct depth for SIGNATURES for uko_l3 + Then the Python detail level "SIGNATURES" should resolve to depth 5 for uko_l3 + + @python_detail_map + Scenario: Python effective map has correct depth for CLASS_HIERARCHY for uko_l3 + Then the Python detail level "CLASS_HIERARCHY" should resolve to depth 3 for uko_l3 + + @python_detail_map + Scenario: Python effective map has correct depth for VISIBILITY_ANNOTATED for uko_l3 + Then the Python detail level "VISIBILITY_ANNOTATED" should resolve to depth 8 for uko_l3 + + # =========================================================================== + # resolve_detail_level — chain resolution + # =========================================================================== + + @resolve_level + Scenario: resolve_detail_level finds name in current map for uko_l3 + Given a detail level map with entries "FOO=0,BAR=1" for uko_l3 + Then resolving "FOO" should return 0 for uko_l3 + And resolving "BAR" should return 1 for uko_l3 + + @resolve_level + Scenario: resolve_detail_level falls back to parent map for uko_l3 + Given a detail level map with entries "CHILD=5" for uko_l3 + And a parent detail level map with entries "PARENT=3" for uko_l3 + Then resolving "PARENT" with parent fallback should return 3 for uko_l3 + + @resolve_level + Scenario: resolve_detail_level raises for unknown name for uko_l3 + Given a detail level map with entries "KNOWN=1" for uko_l3 + Then resolving "UNKNOWN" should raise ValueError for uko_l3 + + @resolve_level + Scenario: resolve_detail_level raises for empty name for uko_l3 + Given a detail level map with entries "KNOWN=1" for uko_l3 + Then resolving empty string should raise ValueError for uko_l3 + + @resolve_level + Scenario: resolve_detail_level raises for whitespace-only name for uko_l3 + Given a detail level map with entries "KNOWN=1" for uko_l3 + Then resolving whitespace string should raise ValueError for uko_l3 + + @resolve_level + Scenario: resolve_detail_level raises when not found in child or parent for uko_l3 + Given a detail level map with entries "CHILD=5" for uko_l3 + And a parent detail level map with entries "PARENT=3" for uko_l3 + Then resolving "MISSING" with parent fallback should raise ValueError for uko_l3 + + # =========================================================================== + # 4-layer chain resolution end-to-end + # =========================================================================== + + @four_layer_chain + Scenario: 4-layer resolution Layer3 -> Layer2 -> Layer1 -> Layer0 for uko_l3 + Given a layer 0 map with "UNIVERSAL=0" for uko_l3 + And a layer 1 map with "MODULE_LISTING=0,SIGNATURES=4" inheriting layer 0 for uko_l3 + And a layer 2 map with "CLASS_HIERARCHY=3" inheriting layer 1 for uko_l3 + And a layer 3 map with "DECORATED_SIGNATURES=7" inheriting layer 2 for uko_l3 + Then resolving "DECORATED_SIGNATURES" through layer 3 should return 7 for uko_l3 + And resolving "CLASS_HIERARCHY" through layer 3 should return 3 for uko_l3 + And resolving "SIGNATURES" through layer 3 should return 4 for uko_l3 + And resolving "UNIVERSAL" through layer 3 should return 0 for uko_l3 + And resolving "NONEXISTENT" through layer 3 should raise ValueError for uko_l3 + + # =========================================================================== + # TypeScript / Rust / Java detail level maps + # =========================================================================== + + @ts_detail_map + Scenario: TypeScript effective map has 12 levels for uko_l3 + Then the TypeScript detail level map should have 12 entries for uko_l3 + + @ts_detail_map + Scenario: TypeScript SIGNATURES resolves to depth 5 for uko_l3 + Then the TypeScript detail level "SIGNATURES" should resolve to depth 5 for uko_l3 + + @ts_detail_map + Scenario: TypeScript MODULE_LISTING resolves to depth 0 for uko_l3 + Then the TypeScript detail level "MODULE_LISTING" should resolve to depth 0 for uko_l3 + + @ts_detail_map + Scenario: TypeScript CLASS_HIERARCHY resolves to depth 3 for uko_l3 + Then the TypeScript detail level "CLASS_HIERARCHY" should resolve to depth 3 for uko_l3 + + @ts_detail_map + Scenario: TypeScript FULL_SOURCE resolves to depth 11 for uko_l3 + Then the TypeScript detail level "FULL_SOURCE" should resolve to depth 11 for uko_l3 + + @rs_detail_map + Scenario: Rust effective map has 12 levels for uko_l3 + Then the Rust detail level map should have 12 entries for uko_l3 + + @rs_detail_map + Scenario: Rust SIGNATURES resolves to depth 5 for uko_l3 + Then the Rust detail level "SIGNATURES" should resolve to depth 5 for uko_l3 + + @rs_detail_map + Scenario: Rust MODULE_LISTING resolves to depth 0 for uko_l3 + Then the Rust detail level "MODULE_LISTING" should resolve to depth 0 for uko_l3 + + @rs_detail_map + Scenario: Rust CLASS_HIERARCHY resolves to depth 3 for uko_l3 + Then the Rust detail level "CLASS_HIERARCHY" should resolve to depth 3 for uko_l3 + + @rs_detail_map + Scenario: Rust FULL_SOURCE resolves to depth 11 for uko_l3 + Then the Rust detail level "FULL_SOURCE" should resolve to depth 11 for uko_l3 + + @java_detail_map + Scenario: Java effective map has 12 levels for uko_l3 + Then the Java detail level map should have 12 entries for uko_l3 + + @java_detail_map + Scenario: Java SIGNATURES resolves to depth 5 for uko_l3 + Then the Java detail level "SIGNATURES" should resolve to depth 5 for uko_l3 + + @java_detail_map + Scenario: Java MODULE_LISTING resolves to depth 0 for uko_l3 + Then the Java detail level "MODULE_LISTING" should resolve to depth 0 for uko_l3 + + @java_detail_map + Scenario: Java CLASS_HIERARCHY resolves to depth 3 for uko_l3 + Then the Java detail level "CLASS_HIERARCHY" should resolve to depth 3 for uko_l3 + + @java_detail_map + Scenario: Java FULL_SOURCE resolves to depth 11 for uko_l3 + Then the Java detail level "FULL_SOURCE" should resolve to depth 11 for uko_l3 + + # =========================================================================== + # Provenance contract — every vocabulary class has provenance fields + # =========================================================================== + + @provenance_contract + Scenario: ProvenanceInfo has all 5 required fields for uko_l3 + Then ProvenanceInfo should have field "source_resource" for uko_l3 + And ProvenanceInfo should have field "source_path" for uko_l3 + And ProvenanceInfo should have field "source_range" for uko_l3 + And ProvenanceInfo should have field "valid_from" for uko_l3 + And ProvenanceInfo should have field "is_current" for uko_l3 + + # =========================================================================== + # Layer 2 dependency tracking + # =========================================================================== + + @layer2_deps + Scenario: Python vocabulary declares Layer 2 dependencies for uko_l3 + Then the Python vocabulary should have at least 4 layer2 dependencies for uko_l3 + And the Python vocabulary layer2 dependencies should include uri containing "uko/oo#Class" for uko_l3 + + @layer2_deps + Scenario: Rust vocabulary declares both uko-oo: and uko-func: dependencies for uko_l3 + Then the Rust vocabulary layer2 dependencies should include prefix "uko-oo:" for uko_l3 + And the Rust vocabulary layer2 dependencies should include prefix "uko-func:" for uko_l3 + + # =========================================================================== + # Package-level imports + # =========================================================================== + + @imports + Scenario: All Layer 3 symbols are importable from acms.uko for uko_l3 + Then importing PythonVocabulary from acms.uko should succeed for uko_l3 + And importing TypeScriptVocabulary from acms.uko should succeed for uko_l3 + And importing RustVocabulary from acms.uko should succeed for uko_l3 + And importing JavaVocabulary from acms.uko should succeed for uko_l3 + And importing ProvenanceInfo from acms.uko should succeed for uko_l3 + And importing build_detail_level_map from acms.uko should succeed for uko_l3 + And importing resolve_detail_level from acms.uko should succeed for uko_l3 diff --git a/features/steps/uko_layer3_detail_map_steps.py b/features/steps/uko_layer3_detail_map_steps.py new file mode 100644 index 000000000..dc6b15f93 --- /dev/null +++ b/features/steps/uko_layer3_detail_map_steps.py @@ -0,0 +1,403 @@ +"""Step definitions for features/uko_layer3_vocabularies.feature (detail level map scenarios). + +Steps for UKO Layer 3 -- detail level maps and verification. +All steps use the suffix "for uko_l3" to avoid AmbiguousStep errors. +""" + +from __future__ import annotations + +from typing import Any + +from behave import given, then, when + +from cleveragents.acms.uko import ( + JAVA_VOCABULARY as _JAVA_VOCABULARY_import, +) +from cleveragents.acms.uko import ( + PYTHON_VOCABULARY as _PYTHON_VOCABULARY_import, +) +from cleveragents.acms.uko import ( + RUST_VOCABULARY as _RUST_VOCABULARY_import, +) +from cleveragents.acms.uko import ( + TYPESCRIPT_VOCABULARY as _TYPESCRIPT_VOCABULARY_import, +) +from cleveragents.acms.uko import ( + ProvenanceInfo as _ProvenanceInfo_import, +) +from cleveragents.acms.uko import ( + build_detail_level_map as _build_detail_level_map_import, +) +from cleveragents.acms.uko import ( + resolve_detail_level as _resolve_detail_level_import, +) +from cleveragents.acms.uko.layer3_java import JAVA_DETAIL_LEVELS +from cleveragents.acms.uko.layer3_py import ( + PYTHON_DETAIL_LEVELS, + PYTHON_VOCABULARY, +) +from cleveragents.acms.uko.layer3_rs import ( + RUST_DETAIL_LEVELS, + RUST_VOCABULARY, +) +from cleveragents.acms.uko.layer3_ts import TYPESCRIPT_DETAIL_LEVELS +from cleveragents.acms.uko.vocabulary import ( + ProvenanceInfo, + build_detail_level_map, + resolve_detail_level, +) + +__all__: list[str] = [] + + +# =========================================================================== +# Helpers +# =========================================================================== + + +def _parse_entries(entries_str: str) -> tuple[tuple[str, int], ...]: + """Parse "A=0,B=1,C=2" into tuple of (name, depth) pairs.""" + result: list[tuple[str, int]] = [] + for pair in entries_str.split(","): + name, val = pair.strip().split("=") + result.append((name.strip(), int(val.strip()))) + return tuple(result) + + +# =========================================================================== +# build_detail_level_map steps +# =========================================================================== + + +@given('a parent level map with entries "{entries}" for uko_l3') +def step_given_parent_map(context: Any, entries: str) -> None: + context.parent_levels = _parse_entries(entries) + + +@when("I build a detail level map with no insertions for uko_l3") +def step_when_build_no_insertions(context: Any) -> None: + context.result_map = build_detail_level_map(context.parent_levels, ()) + + +@when('I build a detail level map with insertion "{entry}" for uko_l3') +def step_when_build_one_insertion(context: Any, entry: str) -> None: + insertions = _parse_entries(entry) + context.result_map = build_detail_level_map(context.parent_levels, insertions) + + +@when('I build a detail level map with insertions "{entries}" for uko_l3') +def step_when_build_multiple_insertions(context: Any, entries: str) -> None: + insertions = _parse_entries(entries) + context.result_map = build_detail_level_map(context.parent_levels, insertions) + + +@then("the resulting map should have {count:d} entries for uko_l3") +def step_then_result_map_count(context: Any, count: int) -> None: + assert len(context.result_map) == count + + +@then('the resulting map entry "{name}" should have depth {depth:d} for uko_l3') +def step_then_result_map_entry(context: Any, name: str, depth: int) -> None: + level_dict = dict(context.result_map) + assert name in level_dict, f"{name!r} not in {level_dict}" + assert level_dict[name] == depth, f"Expected {name}={depth}, got {level_dict[name]}" + + +@then( + "building a detail level map with duplicate insertion " + '"{entry}" should raise ValueError for uko_l3' +) +def step_then_build_duplicate_raises(context: Any, entry: str) -> None: + insertions = _parse_entries(entry) + try: + build_detail_level_map(context.parent_levels, insertions) + msg = "Expected ValueError for duplicate names" + raise AssertionError(msg) + except ValueError: + pass + + +# =========================================================================== +# Python DetailLevelMap steps +# =========================================================================== + + +@then("the Python detail level map should have {count:d} entries for uko_l3") +def step_then_py_map_count(context: Any, count: int) -> None: + assert len(PYTHON_DETAIL_LEVELS) == count + + +@then('the Python detail level "{name}" should resolve to depth {depth:d} for uko_l3') +def step_then_py_level_depth(context: Any, name: str, depth: int) -> None: + resolved = resolve_detail_level(name, PYTHON_DETAIL_LEVELS) + assert resolved == depth, f"Expected {name}={depth}, got {resolved}" + + +# =========================================================================== +# resolve_detail_level steps +# =========================================================================== + + +@given('a detail level map with entries "{entries}" for uko_l3') +def step_given_detail_map(context: Any, entries: str) -> None: + context.detail_levels = _parse_entries(entries) + + +@given('a parent detail level map with entries "{entries}" for uko_l3') +def step_given_parent_detail_map(context: Any, entries: str) -> None: + context.parent_detail_levels = _parse_entries(entries) + + +@then('resolving "{name}" should return {depth:d} for uko_l3') +def step_then_resolve_name(context: Any, name: str, depth: int) -> None: + resolved = resolve_detail_level(name, context.detail_levels) + assert resolved == depth + + +@then('resolving "{name}" with parent fallback should return {depth:d} for uko_l3') +def step_then_resolve_with_parent(context: Any, name: str, depth: int) -> None: + resolved = resolve_detail_level( + name, context.detail_levels, context.parent_detail_levels + ) + assert resolved == depth + + +@then('resolving "{name}" with parent fallback should raise ValueError for uko_l3') +def step_then_resolve_with_parent_raises(context: Any, name: str) -> None: + try: + resolve_detail_level(name, context.detail_levels, context.parent_detail_levels) + msg = "Expected ValueError" + raise AssertionError(msg) + except ValueError: + pass + + +@then('resolving "{name}" should raise ValueError for uko_l3') +def step_then_resolve_raises(context: Any, name: str) -> None: + try: + resolve_detail_level(name, context.detail_levels) + msg = "Expected ValueError" + raise AssertionError(msg) + except ValueError: + pass + + +@then("resolving empty string should raise ValueError for uko_l3") +def step_then_resolve_empty_raises(context: Any) -> None: + try: + resolve_detail_level("", context.detail_levels) + msg = "Expected ValueError" + raise AssertionError(msg) + except ValueError: + pass + + +@then("resolving whitespace string should raise ValueError for uko_l3") +def step_then_resolve_whitespace_raises(context: Any) -> None: + try: + resolve_detail_level(" ", context.detail_levels) + msg = "Expected ValueError" + raise AssertionError(msg) + except ValueError: + pass + + +# =========================================================================== +# 4-layer chain resolution steps +# =========================================================================== + + +@given('a layer 0 map with "{entries}" for uko_l3') +def step_given_layer0_map(context: Any, entries: str) -> None: + context.layer0_map = _parse_entries(entries) + + +@given('a layer 1 map with "{entries}" inheriting layer 0 for uko_l3') +def step_given_layer1_map(context: Any, entries: str) -> None: + context.layer1_map = _parse_entries(entries) + + +@given('a layer 2 map with "{entries}" inheriting layer 1 for uko_l3') +def step_given_layer2_map(context: Any, entries: str) -> None: + context.layer2_map = _parse_entries(entries) + + +@given('a layer 3 map with "{entries}" inheriting layer 2 for uko_l3') +def step_given_layer3_map(context: Any, entries: str) -> None: + context.layer3_map = _parse_entries(entries) + + +def _resolve_through_chain( + name: str, + layer_maps: tuple[tuple[tuple[str, int], ...], ...], +) -> int: + """Resolve *name* through a chain of maps using ``resolve_detail_level``. + + Walks the chain from most-specific (index 0) to least-specific, + calling ``resolve_detail_level`` with the current map and the next + map as the parent fallback. The last map has no parent. + """ + for idx, current_map in enumerate(layer_maps): + parent = layer_maps[idx + 1] if idx + 1 < len(layer_maps) else None + try: + return resolve_detail_level(name, current_map, parent) + except ValueError: + # Not found in current + parent -- continue to next pair. + continue + raise ValueError(f"Unknown detail level: {name!r}") + + +@then('resolving "{name}" through layer 3 should return {depth:d} for uko_l3') +def step_then_resolve_through_chain(context: Any, name: str, depth: int) -> None: + chain = ( + context.layer3_map, + context.layer2_map, + context.layer1_map, + context.layer0_map, + ) + resolved = _resolve_through_chain(name, chain) + assert resolved == depth, f"Expected {name}={depth}, got {resolved}" + + +@then('resolving "{name}" through layer 3 should raise ValueError for uko_l3') +def step_then_resolve_chain_raises(context: Any, name: str) -> None: + chain = ( + context.layer3_map, + context.layer2_map, + context.layer1_map, + context.layer0_map, + ) + try: + _resolve_through_chain(name, chain) + msg = f"Expected ValueError for {name!r}" + raise AssertionError(msg) + except ValueError: + pass + + +# =========================================================================== +# TypeScript / Rust / Java DetailLevelMap steps +# =========================================================================== + + +@then("the TypeScript detail level map should have {count:d} entries for uko_l3") +def step_then_ts_map_count(context: Any, count: int) -> None: + assert len(TYPESCRIPT_DETAIL_LEVELS) == count + + +@then( + 'the TypeScript detail level "{name}" should resolve to depth {depth:d} for uko_l3' +) +def step_then_ts_level_depth(context: Any, name: str, depth: int) -> None: + resolved = resolve_detail_level(name, TYPESCRIPT_DETAIL_LEVELS) + assert resolved == depth + + +@then("the Rust detail level map should have {count:d} entries for uko_l3") +def step_then_rs_map_count(context: Any, count: int) -> None: + assert len(RUST_DETAIL_LEVELS) == count + + +@then('the Rust detail level "{name}" should resolve to depth {depth:d} for uko_l3') +def step_then_rs_level_depth(context: Any, name: str, depth: int) -> None: + resolved = resolve_detail_level(name, RUST_DETAIL_LEVELS) + assert resolved == depth + + +@then("the Java detail level map should have {count:d} entries for uko_l3") +def step_then_java_map_count(context: Any, count: int) -> None: + assert len(JAVA_DETAIL_LEVELS) == count + + +@then('the Java detail level "{name}" should resolve to depth {depth:d} for uko_l3') +def step_then_java_level_depth(context: Any, name: str, depth: int) -> None: + resolved = resolve_detail_level(name, JAVA_DETAIL_LEVELS) + assert resolved == depth + + +# =========================================================================== +# Provenance contract steps +# =========================================================================== + + +@then('ProvenanceInfo should have field "{field}" for uko_l3') +def step_then_provenance_has_field(context: Any, field: str) -> None: + assert hasattr(ProvenanceInfo, "model_fields"), ( + "ProvenanceInfo is not a Pydantic model" + ) + assert field in ProvenanceInfo.model_fields, ( + f"ProvenanceInfo missing field {field!r}" + ) + + +# =========================================================================== +# Layer 2 dependency steps +# =========================================================================== + + +@then( + "the Python vocabulary should have at least {count:d} " + "layer2 dependencies for uko_l3" +) +def step_then_py_layer2_dep_count(context: Any, count: int) -> None: + assert len(PYTHON_VOCABULARY.layer2_dependencies) >= count + + +@then( + "the Python vocabulary layer2 dependencies should include " + 'uri containing "{fragment}" for uko_l3' +) +def step_then_py_layer2_dep_uri(context: Any, fragment: str) -> None: + uris = tuple(d.uri for d in PYTHON_VOCABULARY.layer2_dependencies) + assert any(fragment in u for u in uris), ( + f"No dependency URI containing {fragment!r} in {uris}" + ) + + +@then( + "the Rust vocabulary layer2 dependencies should include " + 'prefix "{prefix}" for uko_l3' +) +def step_then_rs_layer2_dep_prefix(context: Any, prefix: str) -> None: + prefixes = tuple(d.prefix for d in RUST_VOCABULARY.layer2_dependencies) + assert prefix in prefixes, f"{prefix!r} not in {prefixes}" + + +# =========================================================================== +# Package import steps +# =========================================================================== + + +@then("importing PythonVocabulary from acms.uko should succeed for uko_l3") +def step_then_import_python_vocab(context: Any) -> None: + assert _PYTHON_VOCABULARY_import is not None + + +@then("importing TypeScriptVocabulary from acms.uko should succeed for uko_l3") +def step_then_import_ts_vocab(context: Any) -> None: + assert _TYPESCRIPT_VOCABULARY_import is not None + + +@then("importing RustVocabulary from acms.uko should succeed for uko_l3") +def step_then_import_rust_vocab(context: Any) -> None: + assert _RUST_VOCABULARY_import is not None + + +@then("importing JavaVocabulary from acms.uko should succeed for uko_l3") +def step_then_import_java_vocab(context: Any) -> None: + assert _JAVA_VOCABULARY_import is not None + + +@then("importing ProvenanceInfo from acms.uko should succeed for uko_l3") +def step_then_import_provenance(context: Any) -> None: + assert _ProvenanceInfo_import is not None + + +@then("importing build_detail_level_map from acms.uko should succeed for uko_l3") +def step_then_import_build_map(context: Any) -> None: + assert _build_detail_level_map_import is not None + + +@then("importing resolve_detail_level from acms.uko should succeed for uko_l3") +def step_then_import_resolve(context: Any) -> None: + assert _resolve_detail_level_import is not None diff --git a/features/steps/uko_layer3_vocabularies_steps.py b/features/steps/uko_layer3_vocabularies_steps.py new file mode 100644 index 000000000..27c6ca641 --- /dev/null +++ b/features/steps/uko_layer3_vocabularies_steps.py @@ -0,0 +1,528 @@ +"""Step definitions for UKO Layer 3 Technology Vocabularies -- base models and vocabs. + +All steps use the suffix "for uko_l3" to avoid AmbiguousStep errors. +""" + +from __future__ import annotations + +from datetime import UTC, datetime +from typing import Any + +from behave import given, then +from pydantic import ValidationError + +from cleveragents.acms.uko.layer3_java import JAVA_VOCABULARY, JavaClass +from cleveragents.acms.uko.layer3_py import ( + PYTHON_VOCABULARY, + PythonClass, +) +from cleveragents.acms.uko.layer3_rs import RUST_VOCABULARY, RustStruct +from cleveragents.acms.uko.layer3_ts import TYPESCRIPT_VOCABULARY, TypeScriptClass +from cleveragents.acms.uko.vocabulary import ( + Layer2Dependency, + ProvenanceInfo, + UKOClass, + UKOProperty, + UKOVocabulary, +) + +__all__: list[str] = [] + +# -- ProvenanceInfo steps -- + + +@given('a provenance info with resource "{resource}" and path "{path}" for uko_l3') +def step_given_provenance_info(context: Any, resource: str, path: str) -> None: + context.provenance = ProvenanceInfo( + source_resource=resource, + source_path=path, + ) + + +@given( + 'a ranged provenance info with resource "{resource}" and path ' + '"{path}" and range "{source_range}" for uko_l3' +) +def step_given_provenance_info_with_range( + context: Any, resource: str, path: str, source_range: str +) -> None: + context.provenance = ProvenanceInfo( + source_resource=resource, + source_path=path, + source_range=source_range, + ) + + +@then('the provenance source_resource should be "{expected}" for uko_l3') +def step_then_provenance_resource(context: Any, expected: str) -> None: + assert context.provenance.source_resource == expected + + +@then('the provenance source_path should be "{expected}" for uko_l3') +def step_then_provenance_path(context: Any, expected: str) -> None: + assert context.provenance.source_path == expected + + +@then("the provenance is_current should be true for uko_l3") +def step_then_provenance_is_current(context: Any) -> None: + assert context.provenance.is_current is True + + +@then("the provenance valid_from should be a UTC datetime for uko_l3") +def step_then_provenance_valid_from_utc(context: Any) -> None: + vf = context.provenance.valid_from + assert isinstance(vf, datetime) + assert vf.tzinfo is not None + assert vf.tzinfo == UTC + + +@then('the provenance source_range should be "{expected}" for uko_l3') +def step_then_provenance_source_range(context: Any, expected: str) -> None: + assert context.provenance.source_range == expected + + +@then( + "creating a provenance info with empty source_resource " + "should raise ValueError for uko_l3" +) +def step_then_provenance_empty_resource(context: Any) -> None: + try: + ProvenanceInfo(source_resource="", source_path="some.py") + msg = "Expected ValueError" + raise AssertionError(msg) + except ValueError: + pass + + +@then( + "creating a provenance info with whitespace source_resource " + "should raise ValueError for uko_l3" +) +def step_then_provenance_whitespace_resource(context: Any) -> None: + try: + ProvenanceInfo(source_resource=" ", source_path="some.py") + msg = "Expected ValueError" + raise AssertionError(msg) + except ValueError: + pass + + +@then( + "creating a provenance info with empty source_path " + "should raise ValueError for uko_l3" +) +def step_then_provenance_empty_path(context: Any) -> None: + try: + ProvenanceInfo(source_resource="res-01", source_path="") + msg = "Expected ValueError" + raise AssertionError(msg) + except ValueError: + pass + + +@then( + "creating a provenance info with whitespace source_path " + "should raise ValueError for uko_l3" +) +def step_then_provenance_whitespace_path(context: Any) -> None: + try: + ProvenanceInfo(source_resource="res-01", source_path=" ") + msg = "Expected ValueError" + raise AssertionError(msg) + except ValueError: + pass + + +@then("modifying provenance is_current should raise an error for uko_l3") +def step_then_provenance_immutable(context: Any) -> None: + try: + context.provenance.is_current = False + msg = "Expected immutability error" + raise AssertionError(msg) + except (TypeError, AttributeError, ValidationError): + pass + + +# -- UKOClass steps -- + + +@given('a UKO class with uri "{uri}" and label "{label}" for uko_l3') +def step_given_uko_class(context: Any, uri: str, label: str) -> None: + context.uko_class = UKOClass(uri=uri, label=label) + + +@then('the UKO class uri should be "{expected}" for uko_l3') +def step_then_uko_class_uri(context: Any, expected: str) -> None: + assert context.uko_class.uri == expected + + +@then('the UKO class label should be "{expected}" for uko_l3') +def step_then_uko_class_label(context: Any, expected: str) -> None: + assert context.uko_class.label == expected + + +@then("the UKO class subclass_of should be empty for uko_l3") +def step_then_uko_class_subclass_empty(context: Any) -> None: + assert context.uko_class.subclass_of == () + + +@then("creating a UKO class with empty uri should raise ValueError for uko_l3") +def step_then_uko_class_empty_uri(context: Any) -> None: + try: + UKOClass(uri="", label="X") + msg = "Expected ValueError" + raise AssertionError(msg) + except ValueError: + pass + + +@then("creating a UKO class with whitespace label should raise ValueError for uko_l3") +def step_then_uko_class_whitespace_label(context: Any) -> None: + try: + UKOClass(uri="https://example.com/X", label=" ") + msg = "Expected ValueError" + raise AssertionError(msg) + except ValueError: + pass + + +@then("modifying UKO class label should raise an error for uko_l3") +def step_then_uko_class_immutable(context: Any) -> None: + try: + context.uko_class.label = "mutated" + msg = "Expected immutability error" + raise AssertionError(msg) + except (TypeError, AttributeError, ValidationError): + pass + + +# -- UKOClass provenance steps -- + + +@given('a UKO class with provenance resource "{resource}" and path "{path}" for uko_l3') +def step_given_uko_class_with_provenance( + context: Any, resource: str, path: str +) -> None: + context.uko_class = UKOClass( + uri="https://example.com/WithProv", + label="WithProv", + provenance=ProvenanceInfo( + source_resource=resource, + source_path=path, + ), + ) + + +@then('the UKO class provenance source_resource should be "{expected}" for uko_l3') +def step_then_uko_class_prov_resource(context: Any, expected: str) -> None: + assert context.uko_class.provenance is not None + assert context.uko_class.provenance.source_resource == expected + + +@then('the UKO class provenance source_path should be "{expected}" for uko_l3') +def step_then_uko_class_prov_path(context: Any, expected: str) -> None: + assert context.uko_class.provenance is not None + assert context.uko_class.provenance.source_path == expected + + +@then("the UKO class provenance should be None for uko_l3") +def step_then_uko_class_prov_none(context: Any) -> None: + assert context.uko_class.provenance is None + + +# -- UKOProperty steps -- + + +@given('a UKO property with uri "{uri}" and label "{label}" for uko_l3') +def step_given_uko_property(context: Any, uri: str, label: str) -> None: + context.uko_property = UKOProperty(uri=uri, label=label) + + +@then('the UKO property uri should be "{expected}" for uko_l3') +def step_then_uko_property_uri(context: Any, expected: str) -> None: + assert context.uko_property.uri == expected + + +@then("the UKO property is_object_property should be true for uko_l3") +def step_then_uko_property_is_object(context: Any) -> None: + assert context.uko_property.is_object_property is True + + +@then("creating a UKO property with empty uri should raise ValueError for uko_l3") +def step_then_uko_property_empty_uri(context: Any) -> None: + try: + UKOProperty(uri="", label="X") + msg = "Expected ValueError" + raise AssertionError(msg) + except ValueError: + pass + + +# -- Layer2Dependency steps -- + + +@given('a layer2 dependency with uri "{uri}" and prefix "{prefix}" for uko_l3') +def step_given_layer2_dep(context: Any, uri: str, prefix: str) -> None: + context.layer2_dep = Layer2Dependency(uri=uri, prefix=prefix) + + +@then("the layer2 dependency layer should be {expected:d} for uko_l3") +def step_then_layer2_dep_layer(context: Any, expected: int) -> None: + assert context.layer2_dep.layer == expected + + +@then('the layer2 dependency prefix should be "{expected}" for uko_l3') +def step_then_layer2_dep_prefix(context: Any, expected: str) -> None: + assert context.layer2_dep.prefix == expected + + +# -- UKOVocabulary steps -- + + +@given( + 'a UKO vocabulary with namespace "{ns}" and prefix "{prefix}" ' + 'and label "{label}" for uko_l3' +) +def step_given_uko_vocab(context: Any, ns: str, prefix: str, label: str) -> None: + context.uko_vocab = UKOVocabulary(namespace=ns, prefix=prefix, label=label) + + +@then("the vocabulary layer should be {expected:d} for uko_l3") +def step_then_vocab_layer(context: Any, expected: int) -> None: + assert context.uko_vocab.layer == expected + + +@then('the vocabulary prefix should be "{expected}" for uko_l3') +def step_then_vocab_prefix(context: Any, expected: str) -> None: + assert context.uko_vocab.prefix == expected + + +# -- Python vocabulary steps -- + + +@then('the Python vocabulary namespace should be "{expected}" for uko_l3') +def step_then_py_namespace(context: Any, expected: str) -> None: + assert PYTHON_VOCABULARY.namespace == expected + + +@then('the Python vocabulary prefix should be "{expected}" for uko_l3') +def step_then_py_prefix(context: Any, expected: str) -> None: + assert PYTHON_VOCABULARY.prefix == expected + + +@then("the Python vocabulary layer should be {expected:d} for uko_l3") +def step_then_py_layer(context: Any, expected: int) -> None: + assert PYTHON_VOCABULARY.layer == expected + + +@then("the Python vocabulary should have {count:d} classes for uko_l3") +def step_then_py_class_count(context: Any, count: int) -> None: + assert len(PYTHON_VOCABULARY.classes) == count + + +@then('the Python vocabulary should contain class "{name}" for uko_l3') +def step_then_py_has_class(context: Any, name: str) -> None: + labels = tuple(c.label for c in PYTHON_VOCABULARY.classes) + assert name in labels, f"{name!r} not in {labels}" + + +@then("the Python vocabulary should have {count:d} properties for uko_l3") +def step_then_py_property_count(context: Any, count: int) -> None: + assert len(PYTHON_VOCABULARY.properties) == count + + +@then('the Python vocabulary should contain property "{name}" for uko_l3') +def step_then_py_has_property(context: Any, name: str) -> None: + labels = tuple(p.label for p in PYTHON_VOCABULARY.properties) + assert name in labels, f"{name!r} not in {labels}" + + +@then('the Python vocabulary parent_prefixes should contain "{prefix}" for uko_l3') +def step_then_py_parent_prefix(context: Any, prefix: str) -> None: + assert prefix in PYTHON_VOCABULARY.parent_prefixes + + +@then('the PythonClass subclass_of should contain "{uri}" for uko_l3') +def step_then_python_class_subclass(context: Any, uri: str) -> None: + assert uri in PythonClass.subclass_of, f"{uri!r} not in {PythonClass.subclass_of}" + + +@then("the Python vocabulary should have {count:d} inserted levels for uko_l3") +def step_then_py_inserted_count(context: Any, count: int) -> None: + assert len(PYTHON_VOCABULARY.inserted_levels) == count + + +@then('the Python vocabulary inserted levels should include "{name}" for uko_l3') +def step_then_py_inserted_name(context: Any, name: str) -> None: + names = tuple(n for n, _ in PYTHON_VOCABULARY.inserted_levels) + assert name in names, f"{name!r} not in {names}" + + +# -- TypeScript vocabulary steps -- + + +@then('the TypeScript vocabulary namespace should be "{expected}" for uko_l3') +def step_then_ts_namespace(context: Any, expected: str) -> None: + assert TYPESCRIPT_VOCABULARY.namespace == expected + + +@then('the TypeScript vocabulary prefix should be "{expected}" for uko_l3') +def step_then_ts_prefix(context: Any, expected: str) -> None: + assert TYPESCRIPT_VOCABULARY.prefix == expected + + +@then("the TypeScript vocabulary layer should be {expected:d} for uko_l3") +def step_then_ts_layer(context: Any, expected: int) -> None: + assert TYPESCRIPT_VOCABULARY.layer == expected + + +@then("the TypeScript vocabulary should have {count:d} classes for uko_l3") +def step_then_ts_class_count(context: Any, count: int) -> None: + assert len(TYPESCRIPT_VOCABULARY.classes) == count + + +@then('the TypeScript vocabulary should contain class "{name}" for uko_l3') +def step_then_ts_has_class(context: Any, name: str) -> None: + labels = tuple(c.label for c in TYPESCRIPT_VOCABULARY.classes) + assert name in labels, f"{name!r} not in {labels}" + + +@then("the TypeScript vocabulary should have {count:d} properties for uko_l3") +def step_then_ts_property_count(context: Any, count: int) -> None: + assert len(TYPESCRIPT_VOCABULARY.properties) == count + + +@then('the TypeScript vocabulary should contain property "{name}" for uko_l3') +def step_then_ts_has_property(context: Any, name: str) -> None: + labels = tuple(p.label for p in TYPESCRIPT_VOCABULARY.properties) + assert name in labels, f"{name!r} not in {labels}" + + +@then('the TypeScript vocabulary parent_prefixes should contain "{prefix}" for uko_l3') +def step_then_ts_parent_prefix(context: Any, prefix: str) -> None: + assert prefix in TYPESCRIPT_VOCABULARY.parent_prefixes + + +@then("the TypeScript vocabulary should have {count:d} inserted levels for uko_l3") +def step_then_ts_inserted_count(context: Any, count: int) -> None: + assert len(TYPESCRIPT_VOCABULARY.inserted_levels) == count + + +# -- Rust vocabulary steps -- + + +@then('the Rust vocabulary namespace should be "{expected}" for uko_l3') +def step_then_rs_namespace(context: Any, expected: str) -> None: + assert RUST_VOCABULARY.namespace == expected + + +@then('the Rust vocabulary prefix should be "{expected}" for uko_l3') +def step_then_rs_prefix(context: Any, expected: str) -> None: + assert RUST_VOCABULARY.prefix == expected + + +@then("the Rust vocabulary layer should be {expected:d} for uko_l3") +def step_then_rs_layer(context: Any, expected: int) -> None: + assert RUST_VOCABULARY.layer == expected + + +@then("the Rust vocabulary should have {count:d} classes for uko_l3") +def step_then_rs_class_count(context: Any, count: int) -> None: + assert len(RUST_VOCABULARY.classes) == count + + +@then('the Rust vocabulary should contain class "{name}" for uko_l3') +def step_then_rs_has_class(context: Any, name: str) -> None: + labels = tuple(c.label for c in RUST_VOCABULARY.classes) + assert name in labels, f"{name!r} not in {labels}" + + +@then("the Rust vocabulary should have {count:d} properties for uko_l3") +def step_then_rs_property_count(context: Any, count: int) -> None: + assert len(RUST_VOCABULARY.properties) == count + + +@then('the Rust vocabulary should contain property "{name}" for uko_l3') +def step_then_rs_has_property(context: Any, name: str) -> None: + labels = tuple(p.label for p in RUST_VOCABULARY.properties) + assert name in labels, f"{name!r} not in {labels}" + + +@then('the Rust vocabulary parent_prefixes should contain "{prefix}" for uko_l3') +def step_then_rs_parent_prefix(context: Any, prefix: str) -> None: + assert prefix in RUST_VOCABULARY.parent_prefixes + + +@then("the Rust vocabulary should have {count:d} inserted levels for uko_l3") +def step_then_rs_inserted_count(context: Any, count: int) -> None: + assert len(RUST_VOCABULARY.inserted_levels) == count + + +@then('the RustStruct subclass_of should contain "{uri}" for uko_l3') +def step_then_rs_struct_subclass(context: Any, uri: str) -> None: + assert uri in RustStruct.subclass_of, f"{uri!r} not in {RustStruct.subclass_of}" + + +# -- Java vocabulary steps -- + + +@then('the Java vocabulary namespace should be "{expected}" for uko_l3') +def step_then_java_namespace(context: Any, expected: str) -> None: + assert JAVA_VOCABULARY.namespace == expected + + +@then('the Java vocabulary prefix should be "{expected}" for uko_l3') +def step_then_java_prefix(context: Any, expected: str) -> None: + assert JAVA_VOCABULARY.prefix == expected + + +@then("the Java vocabulary layer should be {expected:d} for uko_l3") +def step_then_java_layer(context: Any, expected: int) -> None: + assert JAVA_VOCABULARY.layer == expected + + +@then("the Java vocabulary should have {count:d} classes for uko_l3") +def step_then_java_class_count(context: Any, count: int) -> None: + assert len(JAVA_VOCABULARY.classes) == count + + +@then('the Java vocabulary should contain class "{name}" for uko_l3') +def step_then_java_has_class(context: Any, name: str) -> None: + labels = tuple(c.label for c in JAVA_VOCABULARY.classes) + assert name in labels, f"{name!r} not in {labels}" + + +@then("the Java vocabulary should have {count:d} properties for uko_l3") +def step_then_java_property_count(context: Any, count: int) -> None: + assert len(JAVA_VOCABULARY.properties) == count + + +@then('the Java vocabulary should contain property "{name}" for uko_l3') +def step_then_java_has_property(context: Any, name: str) -> None: + labels = tuple(p.label for p in JAVA_VOCABULARY.properties) + assert name in labels, f"{name!r} not in {labels}" + + +@then('the Java vocabulary parent_prefixes should contain "{prefix}" for uko_l3') +def step_then_java_parent_prefix(context: Any, prefix: str) -> None: + assert prefix in JAVA_VOCABULARY.parent_prefixes + + +@then("the Java vocabulary should have {count:d} inserted levels for uko_l3") +def step_then_java_inserted_count(context: Any, count: int) -> None: + assert len(JAVA_VOCABULARY.inserted_levels) == count + + +@then('the JavaClass subclass_of should contain "{uri}" for uko_l3') +def step_then_java_class_subclass(context: Any, uri: str) -> None: + assert uri in JavaClass.subclass_of, f"{uri!r} not in {JavaClass.subclass_of}" + + +# -- TypeScript subclass_of step -- + + +@then('the TypeScriptClass subclass_of should contain "{uri}" for uko_l3') +def step_then_ts_class_subclass(context: Any, uri: str) -> None: + assert uri in TypeScriptClass.subclass_of, ( + f"{uri!r} not in {TypeScriptClass.subclass_of}" + ) diff --git a/robot/helper_uko_layer3_vocabularies.py b/robot/helper_uko_layer3_vocabularies.py new file mode 100644 index 000000000..3bb95922c --- /dev/null +++ b/robot/helper_uko_layer3_vocabularies.py @@ -0,0 +1,310 @@ +"""Robot Framework helper for UKO Layer 3 technology vocabulary tests. + +Subcommands: + vocab-py -- verify uko-py: vocabulary, print ``vocab-py-ok`` + vocab-ts -- verify uko-ts: vocabulary, print ``vocab-ts-ok`` + vocab-rs -- verify uko-rs: vocabulary, print ``vocab-rs-ok`` + vocab-java -- verify uko-java: vocabulary, print ``vocab-java-ok`` + detail-map-py -- verify Python 15-level map, print ``detail-map-py-ok`` + detail-map-ts -- verify TypeScript 12-level map, print ``detail-map-ts-ok`` + detail-map-rs -- verify Rust 12-level map, print ``detail-map-rs-ok`` + detail-map-java -- verify Java 12-level map, print ``detail-map-java-ok`` + provenance -- verify ProvenanceInfo contract, print ``provenance-ok`` +""" + +from __future__ import annotations + +import sys +import traceback +from collections.abc import Callable +from pathlib import Path + +_SRC = str(Path(__file__).resolve().parents[1] / "src") +if _SRC not in sys.path: + sys.path.insert(0, _SRC) + +from cleveragents.acms.uko.layer3_java import ( # noqa: E402 + JAVA_DETAIL_LEVELS, + JAVA_VOCABULARY, +) +from cleveragents.acms.uko.layer3_py import ( # noqa: E402 + PYTHON_DETAIL_LEVELS, + PYTHON_VOCABULARY, +) +from cleveragents.acms.uko.layer3_rs import ( # noqa: E402 + RUST_DETAIL_LEVELS, + RUST_VOCABULARY, +) +from cleveragents.acms.uko.layer3_ts import ( # noqa: E402 + TYPESCRIPT_DETAIL_LEVELS, + TYPESCRIPT_VOCABULARY, +) +from cleveragents.acms.uko.vocabulary import ( # noqa: E402 + ProvenanceInfo, + resolve_detail_level, +) + + +def _cmd_vocab_py() -> int: + """Verify uko-py: vocabulary has expected classes and properties.""" + try: + v = PYTHON_VOCABULARY + if v.prefix != "uko-py:": + print(f"vocab-py-fail: prefix {v.prefix!r}") + return 1 + if len(v.classes) != 5: + print(f"vocab-py-fail: expected 5 classes, got {len(v.classes)}") + return 1 + if len(v.properties) != 5: + print(f"vocab-py-fail: expected 5 properties, got {len(v.properties)}") + return 1 + expected = { + "PythonModule", + "PythonClass", + "PythonFunction", + "PythonDecorator", + "PythonTypeStub", + } + actual = {c.label for c in v.classes} + if actual != expected: + print(f"vocab-py-fail: classes {actual}") + return 1 + print(f"vocab-py-ok: {len(v.classes)} classes, {len(v.properties)} properties") + return 0 + except (ValueError, TypeError, AttributeError, ImportError) as exc: + traceback.print_exc() + print(f"vocab-py-fail: {exc}") + return 1 + + +def _cmd_vocab_ts() -> int: + """Verify uko-ts: vocabulary has expected classes.""" + try: + v = TYPESCRIPT_VOCABULARY + if v.prefix != "uko-ts:": + print(f"vocab-ts-fail: prefix {v.prefix!r}") + return 1 + if len(v.classes) != 4: + print(f"vocab-ts-fail: expected 4 classes, got {len(v.classes)}") + return 1 + if len(v.properties) != 6: + print(f"vocab-ts-fail: expected 6 properties, got {len(v.properties)}") + return 1 + print(f"vocab-ts-ok: {len(v.classes)} classes, {len(v.properties)} properties") + return 0 + except (ValueError, TypeError, AttributeError, ImportError) as exc: + traceback.print_exc() + print(f"vocab-ts-fail: {exc}") + return 1 + + +def _cmd_vocab_rs() -> int: + """Verify uko-rs: vocabulary has expected classes.""" + try: + v = RUST_VOCABULARY + if v.prefix != "uko-rs:": + print(f"vocab-rs-fail: prefix {v.prefix!r}") + return 1 + if len(v.classes) != 5: + print(f"vocab-rs-fail: expected 5 classes, got {len(v.classes)}") + return 1 + if len(v.properties) != 8: + print(f"vocab-rs-fail: expected 8 properties, got {len(v.properties)}") + return 1 + print(f"vocab-rs-ok: {len(v.classes)} classes, {len(v.properties)} properties") + return 0 + except (ValueError, TypeError, AttributeError, ImportError) as exc: + traceback.print_exc() + print(f"vocab-rs-fail: {exc}") + return 1 + + +def _cmd_vocab_java() -> int: + """Verify uko-java: vocabulary has expected classes.""" + try: + v = JAVA_VOCABULARY + if v.prefix != "uko-java:": + print(f"vocab-java-fail: prefix {v.prefix!r}") + return 1 + if len(v.classes) != 5: + print(f"vocab-java-fail: expected 5 classes, got {len(v.classes)}") + return 1 + if len(v.properties) != 8: + print(f"vocab-java-fail: expected 8 properties, got {len(v.properties)}") + return 1 + print( + f"vocab-java-ok: {len(v.classes)} classes, {len(v.properties)} properties" + ) + return 0 + except (ValueError, TypeError, AttributeError, ImportError) as exc: + traceback.print_exc() + print(f"vocab-java-fail: {exc}") + return 1 + + +def _cmd_detail_map_py() -> int: + """Verify Python 15-level map and key depth resolutions.""" + try: + levels = PYTHON_DETAIL_LEVELS + if len(levels) != 15: + print(f"detail-map-py-fail: expected 15 levels, got {len(levels)}") + return 1 + checks = { + "MODULE_LISTING": 0, + "DECORATED_SIGNATURES": 7, + "TYPE_STUBS": 11, + "WITH_TESTS": 14, + "FULL_SOURCE": 13, + } + for name, expected_depth in checks.items(): + actual = resolve_detail_level(name, levels) + if actual != expected_depth: + print( + f"detail-map-py-fail: {name} expected {expected_depth}, " + f"got {actual}" + ) + return 1 + print(f"detail-map-py-ok: {len(levels)} levels, all depths correct") + return 0 + except (ValueError, TypeError, AttributeError, ImportError) as exc: + traceback.print_exc() + print(f"detail-map-py-fail: {exc}") + return 1 + + +def _cmd_detail_map_ts() -> int: + """Verify TypeScript 12-level map and key depth resolutions.""" + try: + levels = TYPESCRIPT_DETAIL_LEVELS + if len(levels) != 12: + print(f"detail-map-ts-fail: expected 12 levels, got {len(levels)}") + return 1 + checks = { + "MODULE_LISTING": 0, + "SIGNATURES": 5, + "FULL_SOURCE": 11, + } + for name, expected_depth in checks.items(): + actual = resolve_detail_level(name, levels) + if actual != expected_depth: + print( + f"detail-map-ts-fail: {name} expected {expected_depth}, " + f"got {actual}" + ) + return 1 + print(f"detail-map-ts-ok: {len(levels)} levels, all depths correct") + return 0 + except (ValueError, TypeError, AttributeError, ImportError) as exc: + traceback.print_exc() + print(f"detail-map-ts-fail: {exc}") + return 1 + + +def _cmd_detail_map_rs() -> int: + """Verify Rust 12-level map and key depth resolutions.""" + try: + levels = RUST_DETAIL_LEVELS + if len(levels) != 12: + print(f"detail-map-rs-fail: expected 12 levels, got {len(levels)}") + return 1 + checks = { + "MODULE_LISTING": 0, + "SIGNATURES": 5, + "FULL_SOURCE": 11, + } + for name, expected_depth in checks.items(): + actual = resolve_detail_level(name, levels) + if actual != expected_depth: + print( + f"detail-map-rs-fail: {name} expected {expected_depth}, " + f"got {actual}" + ) + return 1 + print(f"detail-map-rs-ok: {len(levels)} levels, all depths correct") + return 0 + except (ValueError, TypeError, AttributeError, ImportError) as exc: + traceback.print_exc() + print(f"detail-map-rs-fail: {exc}") + return 1 + + +def _cmd_detail_map_java() -> int: + """Verify Java 12-level map and key depth resolutions.""" + try: + levels = JAVA_DETAIL_LEVELS + if len(levels) != 12: + print(f"detail-map-java-fail: expected 12 levels, got {len(levels)}") + return 1 + checks = { + "MODULE_LISTING": 0, + "SIGNATURES": 5, + "FULL_SOURCE": 11, + } + for name, expected_depth in checks.items(): + actual = resolve_detail_level(name, levels) + if actual != expected_depth: + print( + f"detail-map-java-fail: {name} expected {expected_depth}, " + f"got {actual}" + ) + return 1 + print(f"detail-map-java-ok: {len(levels)} levels, all depths correct") + return 0 + except (ValueError, TypeError, AttributeError, ImportError) as exc: + traceback.print_exc() + print(f"detail-map-java-fail: {exc}") + return 1 + + +def _cmd_provenance() -> int: + """Verify ProvenanceInfo has all 5 required fields.""" + try: + required = { + "source_resource", + "source_path", + "source_range", + "valid_from", + "is_current", + } + actual = set(ProvenanceInfo.model_fields.keys()) + missing = required - actual + if missing: + print(f"provenance-fail: missing fields {missing}") + return 1 + # Round-trip construction check + p = ProvenanceInfo(source_resource="res-01", source_path="test.py") + if not p.is_current: + print("provenance-fail: is_current should default to True") + return 1 + print(f"provenance-ok: {len(required)} required fields present") + return 0 + except (ValueError, TypeError, AttributeError, ImportError) as exc: + traceback.print_exc() + print(f"provenance-fail: {exc}") + return 1 + + +_DISPATCH: dict[str, Callable[[], int]] = { + "vocab-py": _cmd_vocab_py, + "vocab-ts": _cmd_vocab_ts, + "vocab-rs": _cmd_vocab_rs, + "vocab-java": _cmd_vocab_java, + "detail-map-py": _cmd_detail_map_py, + "detail-map-ts": _cmd_detail_map_ts, + "detail-map-rs": _cmd_detail_map_rs, + "detail-map-java": _cmd_detail_map_java, + "provenance": _cmd_provenance, +} + + +def main() -> None: + """Entry point for Robot Framework process execution.""" + if len(sys.argv) < 2 or sys.argv[1] not in _DISPATCH: + cmds = ", ".join(sorted(_DISPATCH)) + print(f"Usage: {sys.argv[0]} <{cmds}>") + sys.exit(2) + sys.exit(_DISPATCH[sys.argv[1]]()) + + +if __name__ == "__main__": + main() diff --git a/robot/uko_layer3_vocabularies.robot b/robot/uko_layer3_vocabularies.robot new file mode 100644 index 000000000..253f133d2 --- /dev/null +++ b/robot/uko_layer3_vocabularies.robot @@ -0,0 +1,49 @@ +*** Settings *** +Library Process +Suite Setup Log UKO Layer 3 Technology Vocabulary Integration Tests + +*** Test Cases *** +Verify Python Vocabulary + ${result}= Run Process python3 ${CURDIR}/helper_uko_layer3_vocabularies.py vocab-py + Should Be Equal As Integers ${result.rc} 0 + Should Contain ${result.stdout} vocab-py-ok + +Verify TypeScript Vocabulary + ${result}= Run Process python3 ${CURDIR}/helper_uko_layer3_vocabularies.py vocab-ts + Should Be Equal As Integers ${result.rc} 0 + Should Contain ${result.stdout} vocab-ts-ok + +Verify Rust Vocabulary + ${result}= Run Process python3 ${CURDIR}/helper_uko_layer3_vocabularies.py vocab-rs + Should Be Equal As Integers ${result.rc} 0 + Should Contain ${result.stdout} vocab-rs-ok + +Verify Java Vocabulary + ${result}= Run Process python3 ${CURDIR}/helper_uko_layer3_vocabularies.py vocab-java + Should Be Equal As Integers ${result.rc} 0 + Should Contain ${result.stdout} vocab-java-ok + +Verify Python Detail Level Map + ${result}= Run Process python3 ${CURDIR}/helper_uko_layer3_vocabularies.py detail-map-py + Should Be Equal As Integers ${result.rc} 0 + Should Contain ${result.stdout} detail-map-py-ok + +Verify TypeScript Detail Level Map + ${result}= Run Process python3 ${CURDIR}/helper_uko_layer3_vocabularies.py detail-map-ts + Should Be Equal As Integers ${result.rc} 0 + Should Contain ${result.stdout} detail-map-ts-ok + +Verify Rust Detail Level Map + ${result}= Run Process python3 ${CURDIR}/helper_uko_layer3_vocabularies.py detail-map-rs + Should Be Equal As Integers ${result.rc} 0 + Should Contain ${result.stdout} detail-map-rs-ok + +Verify Java Detail Level Map + ${result}= Run Process python3 ${CURDIR}/helper_uko_layer3_vocabularies.py detail-map-java + Should Be Equal As Integers ${result.rc} 0 + Should Contain ${result.stdout} detail-map-java-ok + +Verify Provenance Contract + ${result}= Run Process python3 ${CURDIR}/helper_uko_layer3_vocabularies.py provenance + Should Be Equal As Integers ${result.rc} 0 + Should Contain ${result.stdout} provenance-ok diff --git a/src/cleveragents/acms/__init__.py b/src/cleveragents/acms/__init__.py index be5b04e5c..7fe96a0d0 100644 --- a/src/cleveragents/acms/__init__.py +++ b/src/cleveragents/acms/__init__.py @@ -1,10 +1,11 @@ """ACMS (Advanced Context Management System) UKO vocabulary support. -Provides UKO Layer 2 paradigm vocabulary specializations and the -DetailLevelMap inheritance mechanism for resolving named detail levels -across the ontology hierarchy (Layer 2 -> Layer 1 -> Layer 0). +Provides UKO Layer 2 paradigm vocabulary specializations, Layer 3 +technology-specific vocabulary extensions, and the DetailLevelMap +inheritance mechanism for resolving named detail levels across the +ontology hierarchy (Layer 3 -> Layer 2 -> Layer 1 -> Layer 0). -Based on ``docs/specification.md`` ~lines 42333-42422. +Based on ``docs/specification.md`` ~lines 42333-42422, 44405-44420. """ from __future__ import annotations @@ -13,17 +14,52 @@ from cleveragents.acms import uko as _uko from cleveragents.acms.uko import ( CODE_DETAIL_LEVEL_MAP, FUNC_DETAIL_LEVEL_MAP, + JAVA_DETAIL_LEVELS, + JAVA_VOCABULARY, OO_DETAIL_LEVEL_MAP, PROC_DETAIL_LEVEL_MAP, + PYTHON_DETAIL_LEVELS, + PYTHON_VOCABULARY, + RUST_DETAIL_LEVELS, + RUST_VOCABULARY, + TYPESCRIPT_DETAIL_LEVELS, + TYPESCRIPT_VOCABULARY, DetailLevelMapBuilder, + DuplicateVocabularyError, + JavaAnnotation, + JavaCheckedException, + JavaClass, + JavaInterface, + JavaMethod, + Layer2Dependency, ParadigmVocabulary, + ProvenanceInfo, + PythonClass, + PythonDecorator, + PythonFunction, + PythonModule, + PythonTypeStub, + RustDeriveAttribute, + RustFunction, + RustImpl, + RustStruct, + RustTrait, + TypeScriptClass, + TypeScriptFunction, + TypeScriptInterface, + TypeScriptModule, + UKOClass, + UKOProperty, + UKOVocabulary, VocabularyClass, VocabularyProperty, VocabularyRegistry, + build_detail_level_map, build_effective_map, get_func_vocabulary, get_oo_vocabulary, get_proc_vocabulary, + resolve_detail_level, ) # Re-export everything published by the ``uko`` sub-package so the two diff --git a/src/cleveragents/acms/uko/__init__.py b/src/cleveragents/acms/uko/__init__.py index 57538597f..68544b22f 100644 --- a/src/cleveragents/acms/uko/__init__.py +++ b/src/cleveragents/acms/uko/__init__.py @@ -1,8 +1,23 @@ -"""UKO Layer 2 paradigm vocabulary specializations. +"""UKO vocabulary extensions for the ACMS. -Provides Object-Oriented (``uko-oo:``), Functional (``uko-func:``), and -Procedural (``uko-proc:``) vocabulary definitions with DetailLevelMap -inheritance that extends the Layer 1 ``uko-code:`` domain. +Provides Layer 2 paradigm vocabulary specializations (Object-Oriented, +Functional, Procedural) with DetailLevelMap inheritance, and Layer 3 +technology-specific vocabulary extensions that further specialize +Layer 2 paradigms with language-specific semantic refinements. + +Layer 2 modules: +- ``detail_level_maps`` -- CODE/OO/FUNC/PROC DetailLevelMaps, builder +- ``vocabularies`` -- ParadigmVocabulary, VocabularyClass/Property +- ``vocabulary_registry`` -- VocabularyRegistry, DuplicateVocabularyError + +Layer 3 modules: +- ``layer3_py`` -- Python (``uko-py:``) vocabulary and DetailLevelMap +- ``layer3_ts`` -- TypeScript (``uko-ts:``) vocabulary and DetailLevelMap +- ``layer3_rs`` -- Rust (``uko-rs:``) vocabulary and DetailLevelMap +- ``layer3_java`` -- Java (``uko-java:``) vocabulary and DetailLevelMap + +Shared types: +- ``vocabulary`` -- Base classes for UKO vocabulary definitions Namespace prefixes (per specification.md ~line 41900): @@ -11,8 +26,12 @@ Namespace prefixes (per specification.md ~line 41900): | ``uko-oo:`` | ``https://cleveragents.ai/ontology/uko/oo#`` | 2 | | ``uko-func:`` | ``https://cleveragents.ai/ontology/uko/func#`` | 2 | | ``uko-proc:`` | ``https://cleveragents.ai/ontology/uko/proc#`` | 2 | +| ``uko-py:`` | ``https://cleveragents.ai/ontology/uko/py#`` | 3 | +| ``uko-ts:`` | ``https://cleveragents.ai/ontology/uko/ts#`` | 3 | +| ``uko-rs:`` | ``https://cleveragents.ai/ontology/uko/rs#`` | 3 | +| ``uko-java:`` | ``https://cleveragents.ai/ontology/uko/java#`` | 3 | -Based on ``docs/specification.md`` ~lines 42333-42422. +Based on ``docs/specification.md`` ~lines 42333-42513. """ from __future__ import annotations @@ -25,6 +44,41 @@ from cleveragents.acms.uko.detail_level_maps import ( DetailLevelMapBuilder, build_effective_map, ) +from cleveragents.acms.uko.layer3_java import ( + JAVA_DETAIL_LEVELS, + JAVA_VOCABULARY, + JavaAnnotation, + JavaCheckedException, + JavaClass, + JavaInterface, + JavaMethod, +) +from cleveragents.acms.uko.layer3_py import ( + PYTHON_DETAIL_LEVELS, + PYTHON_VOCABULARY, + PythonClass, + PythonDecorator, + PythonFunction, + PythonModule, + PythonTypeStub, +) +from cleveragents.acms.uko.layer3_rs import ( + RUST_DETAIL_LEVELS, + RUST_VOCABULARY, + RustDeriveAttribute, + RustFunction, + RustImpl, + RustStruct, + RustTrait, +) +from cleveragents.acms.uko.layer3_ts import ( + TYPESCRIPT_DETAIL_LEVELS, + TYPESCRIPT_VOCABULARY, + TypeScriptClass, + TypeScriptFunction, + TypeScriptInterface, + TypeScriptModule, +) from cleveragents.acms.uko.vocabularies import ( ParadigmVocabulary, VocabularyClass, @@ -33,6 +87,15 @@ from cleveragents.acms.uko.vocabularies import ( get_oo_vocabulary, get_proc_vocabulary, ) +from cleveragents.acms.uko.vocabulary import ( + Layer2Dependency, + ProvenanceInfo, + UKOClass, + UKOProperty, + UKOVocabulary, + build_detail_level_map, + resolve_detail_level, +) from cleveragents.acms.uko.vocabulary_registry import ( DuplicateVocabularyError, VocabularyRegistry, @@ -41,16 +104,50 @@ from cleveragents.acms.uko.vocabulary_registry import ( __all__: list[str] = [ "CODE_DETAIL_LEVEL_MAP", "FUNC_DETAIL_LEVEL_MAP", + "JAVA_DETAIL_LEVELS", + "JAVA_VOCABULARY", "OO_DETAIL_LEVEL_MAP", "PROC_DETAIL_LEVEL_MAP", + "PYTHON_DETAIL_LEVELS", + "PYTHON_VOCABULARY", + "RUST_DETAIL_LEVELS", + "RUST_VOCABULARY", + "TYPESCRIPT_DETAIL_LEVELS", + "TYPESCRIPT_VOCABULARY", "DetailLevelMapBuilder", "DuplicateVocabularyError", + "JavaAnnotation", + "JavaCheckedException", + "JavaClass", + "JavaInterface", + "JavaMethod", + "Layer2Dependency", "ParadigmVocabulary", + "ProvenanceInfo", + "PythonClass", + "PythonDecorator", + "PythonFunction", + "PythonModule", + "PythonTypeStub", + "RustDeriveAttribute", + "RustFunction", + "RustImpl", + "RustStruct", + "RustTrait", + "TypeScriptClass", + "TypeScriptFunction", + "TypeScriptInterface", + "TypeScriptModule", + "UKOClass", + "UKOProperty", + "UKOVocabulary", "VocabularyClass", "VocabularyProperty", "VocabularyRegistry", + "build_detail_level_map", "build_effective_map", "get_func_vocabulary", "get_oo_vocabulary", "get_proc_vocabulary", + "resolve_detail_level", ] diff --git a/src/cleveragents/acms/uko/layer3_java.py b/src/cleveragents/acms/uko/layer3_java.py new file mode 100644 index 000000000..19e2728b7 --- /dev/null +++ b/src/cleveragents/acms/uko/layer3_java.py @@ -0,0 +1,283 @@ +"""UKO Layer 3: Java-specific vocabulary (``uko-java:``). + +Extends the Layer 2 Object-Oriented paradigm (``uko-oo:``) with +Java-specific semantic refinements: + +Classes: +- ``JavaClass`` -- Java class (extends ``uko-oo:Class``) +- ``JavaInterface`` -- Java interface (extends ``uko-oo:Interface``) +- ``JavaMethod`` -- Java method (extends ``uko-oo:Method``) +- ``JavaAnnotation`` -- Java annotation entry +- ``JavaCheckedException`` -- Checked exception declaration + +Java extends at the SIGNATURES level with: +- Package visibility +- ``final``/``abstract``/``sealed`` modifiers +- Annotation lists (``@Override``, ``@Deprecated``) +- Checked exceptions + +Based on ``docs/specification.md`` ~lines 44405-44420. +""" + +from __future__ import annotations + +from cleveragents.acms.uko.vocabulary import ( + OO_EFFECTIVE_LEVELS, + UKO_CODE_IRI, + UKO_OO_IRI, + Layer2Dependency, + UKOClass, + UKOProperty, + UKOVocabulary, + build_detail_level_map, +) + +__all__: list[str] = [ + "JAVA_DETAIL_LEVELS", + "JAVA_VOCABULARY", + "JavaAnnotation", + "JavaCheckedException", + "JavaClass", + "JavaInterface", + "JavaMethod", +] + +# --------------------------------------------------------------------------- +# Namespace constants +# --------------------------------------------------------------------------- + +UKO_JAVA_IRI = "https://cleveragents.ai/ontology/uko/java#" +UKO_JAVA_PREFIX = "uko-java:" + + +# --------------------------------------------------------------------------- +# OWL Class definitions +# --------------------------------------------------------------------------- + +JavaClass = UKOClass( + uri=f"{UKO_JAVA_IRI}JavaClass", + label="JavaClass", + comment=( + "A Java class definition — extends uko-oo:Class. " + "Includes final/abstract/sealed modifiers and annotations." + ), + subclass_of=(f"{UKO_OO_IRI}Class",), +) + +JavaInterface = UKOClass( + uri=f"{UKO_JAVA_IRI}JavaInterface", + label="JavaInterface", + comment=( + "A Java interface — extends uko-oo:Interface. " + "Includes sealed permits and default method support." + ), + subclass_of=(f"{UKO_OO_IRI}Interface",), +) + +JavaMethod = UKOClass( + uri=f"{UKO_JAVA_IRI}JavaMethod", + label="JavaMethod", + comment=( + "A Java method — extends uko-oo:Method. " + "Includes annotations, checked exceptions, and modifiers." + ), + subclass_of=(f"{UKO_OO_IRI}Method",), +) + +# NOTE: Models annotation *application* (@Override on a method), not +# annotation *definition* (@interface Override). TypeDefinition is a +# partial fit because Java annotations ARE types (@interface). +JavaAnnotation = UKOClass( + uri=f"{UKO_JAVA_IRI}JavaAnnotation", + label="JavaAnnotation", + comment=( + "A Java annotation entry (@Override, @Deprecated, custom). " + "Records annotation name and parameters." + ), + subclass_of=(f"{UKO_CODE_IRI}TypeDefinition",), +) + +# NOTE: subclasses uko-code:TypeDefinition as a pragmatic approximation. +# Throws-clause entries reference existing exception types, not define new ones. +# Pending uko-code:Declaration base class (see #576 review). +JavaCheckedException = UKOClass( + uri=f"{UKO_JAVA_IRI}JavaCheckedException", + label="JavaCheckedException", + comment=( + "A checked exception declaration in a method's throws clause. " + "Records the exception class and any conditions." + ), + subclass_of=(f"{UKO_CODE_IRI}TypeDefinition",), +) + + +# --------------------------------------------------------------------------- +# OWL Property definitions +# --------------------------------------------------------------------------- + +_java_package_visibility = UKOProperty( + uri=f"{UKO_JAVA_IRI}packageVisibility", + label="packageVisibility", + comment=( + "Package-level visibility modifier: 'public', 'protected', " + "'package-private' (default), 'private'." + ), + domain=( + f"{UKO_JAVA_IRI}JavaClass", + f"{UKO_JAVA_IRI}JavaInterface", + f"{UKO_JAVA_IRI}JavaMethod", + ), + range_uri="xsd:string", + is_object_property=False, +) + +_java_is_final = UKOProperty( + uri=f"{UKO_JAVA_IRI}isFinal", + label="isFinal", + comment="Whether this class/method is marked 'final'.", + domain=(f"{UKO_JAVA_IRI}JavaClass", f"{UKO_JAVA_IRI}JavaMethod"), + range_uri="xsd:boolean", + is_object_property=False, +) + +_java_is_abstract = UKOProperty( + uri=f"{UKO_JAVA_IRI}isAbstract", + label="isAbstract", + comment="Whether this class/method is marked 'abstract'.", + domain=(f"{UKO_JAVA_IRI}JavaClass", f"{UKO_JAVA_IRI}JavaMethod"), + range_uri="xsd:boolean", + is_object_property=False, +) + +_java_is_sealed = UKOProperty( + uri=f"{UKO_JAVA_IRI}isSealed", + label="isSealed", + comment="Whether this class/interface is marked 'sealed'.", + domain=(f"{UKO_JAVA_IRI}JavaClass", f"{UKO_JAVA_IRI}JavaInterface"), + range_uri="xsd:boolean", + is_object_property=False, +) + +_java_has_annotation = UKOProperty( + uri=f"{UKO_JAVA_IRI}hasAnnotation", + label="hasAnnotation", + comment="Links a class/method to its Java annotations.", + domain=( + f"{UKO_JAVA_IRI}JavaClass", + f"{UKO_JAVA_IRI}JavaInterface", + f"{UKO_JAVA_IRI}JavaMethod", + ), + range_uri=f"{UKO_JAVA_IRI}JavaAnnotation", + is_object_property=True, +) + +_java_annotation_name = UKOProperty( + uri=f"{UKO_JAVA_IRI}annotationName", + label="annotationName", + comment=("The fully-qualified name of the annotation (e.g. 'java.lang.Override')."), + domain=(f"{UKO_JAVA_IRI}JavaAnnotation",), + range_uri="xsd:string", + is_object_property=False, +) + +_java_throws_exception = UKOProperty( + uri=f"{UKO_JAVA_IRI}throwsException", + label="throwsException", + comment="Links a method to its checked exception declarations.", + domain=(f"{UKO_JAVA_IRI}JavaMethod",), + range_uri=f"{UKO_JAVA_IRI}JavaCheckedException", + is_object_property=True, +) + +_java_exception_class = UKOProperty( + uri=f"{UKO_JAVA_IRI}exceptionClass", + label="exceptionClass", + comment="Fully-qualified class name of the checked exception.", + domain=(f"{UKO_JAVA_IRI}JavaCheckedException",), + range_uri="xsd:string", + is_object_property=False, +) + + +# --------------------------------------------------------------------------- +# Layer 2 dependencies +# --------------------------------------------------------------------------- + +_JAVA_LAYER2_DEPS = ( + Layer2Dependency( + uri=f"{UKO_OO_IRI}Class", + prefix="uko-oo:", + description="OO class (JavaClass extends this).", + ), + Layer2Dependency( + uri=f"{UKO_OO_IRI}Interface", + prefix="uko-oo:", + description="OO interface (JavaInterface extends this).", + ), + Layer2Dependency( + uri=f"{UKO_OO_IRI}Method", + prefix="uko-oo:", + description="OO method (JavaMethod extends this).", + ), + Layer2Dependency( + uri=f"{UKO_CODE_IRI}TypeDefinition", + prefix="uko-code:", + layer=1, + description=( + "General type definition (JavaAnnotation and " + "JavaCheckedException extend this)." + ), + ), +) + + +# --------------------------------------------------------------------------- +# DetailLevelMap: extends uko-oo: at SIGNATURES level +# --------------------------------------------------------------------------- + +# Java extends at SIGNATURES level — enriches the content at +# SIGNATURES depth with package visibility, final/abstract/sealed, +# annotations, checked exceptions. No new depth levels are inserted. +_JAVA_INSERTIONS: tuple[tuple[str, int], ...] = () + +JAVA_DETAIL_LEVELS: tuple[tuple[str, int], ...] = build_detail_level_map( + OO_EFFECTIVE_LEVELS, _JAVA_INSERTIONS +) + + +# --------------------------------------------------------------------------- +# Vocabulary definition +# --------------------------------------------------------------------------- + +JAVA_VOCABULARY = UKOVocabulary( + namespace=UKO_JAVA_IRI, + prefix=UKO_JAVA_PREFIX, + layer=3, + label="Java Technology Vocabulary", + comment=( + "Layer 3 technology-specific vocabulary for Java. " + "Extends uko-oo: with package visibility, " + "final/abstract/sealed modifiers, annotation lists " + "(@Override, @Deprecated), and checked exceptions." + ), + parent_prefixes=("uko-oo:",), + classes=( + JavaClass, + JavaInterface, + JavaMethod, + JavaAnnotation, + JavaCheckedException, + ), + properties=( + _java_package_visibility, + _java_is_final, + _java_is_abstract, + _java_is_sealed, + _java_has_annotation, + _java_annotation_name, + _java_throws_exception, + _java_exception_class, + ), + layer2_dependencies=_JAVA_LAYER2_DEPS, + inserted_levels=(), +) diff --git a/src/cleveragents/acms/uko/layer3_py.py b/src/cleveragents/acms/uko/layer3_py.py new file mode 100644 index 000000000..321ce4f20 --- /dev/null +++ b/src/cleveragents/acms/uko/layer3_py.py @@ -0,0 +1,264 @@ +"""UKO Layer 3: Python-specific vocabulary (``uko-py:``). + +Extends the Layer 2 Object-Oriented paradigm (``uko-oo:``) with +Python-specific semantic refinements: + +Classes: +- ``PythonModule`` -- Python module (extends ``uko-code:Module``) +- ``PythonClass`` -- Python class (extends ``uko-oo:Class``) +- ``PythonFunction`` -- Python function/method (extends ``uko-oo:Method``) +- ``PythonDecorator`` -- Decorator chain entry +- ``PythonTypeStub`` -- ``.pyi`` stub type annotation + +DetailLevelMap insertions (spec lines 25006-25026): +- ``DECORATED_SIGNATURES`` at depth 7 (between SIGNATURES_WITH_DOCS + and VISIBILITY_ANNOTATED) +- ``TYPE_STUBS`` at depth 11 (between KEY_LOGIC and NEAR_COMPLETE) +- ``WITH_TESTS`` at depth 14 (beyond FULL_SOURCE) + +Based on ``docs/specification.md`` ~lines 44405-44420 and 25006-25026. +""" + +from __future__ import annotations + +from cleveragents.acms.uko.vocabulary import ( + OO_EFFECTIVE_LEVELS, + UKO_CODE_IRI, + UKO_OO_IRI, + Layer2Dependency, + UKOClass, + UKOProperty, + UKOVocabulary, + build_detail_level_map, +) + +__all__: list[str] = [ + "PYTHON_DETAIL_LEVELS", + "PYTHON_VOCABULARY", + "PythonClass", + "PythonDecorator", + "PythonFunction", + "PythonModule", + "PythonTypeStub", +] + +# --------------------------------------------------------------------------- +# Namespace constants +# --------------------------------------------------------------------------- + +UKO_PY_IRI = "https://cleveragents.ai/ontology/uko/py#" +UKO_PY_PREFIX = "uko-py:" + + +# --------------------------------------------------------------------------- +# OWL Class definitions +# --------------------------------------------------------------------------- + +PythonModule = UKOClass( + uri=f"{UKO_PY_IRI}PythonModule", + label="PythonModule", + comment="A Python module (.py file) — extends uko-code:Module.", + subclass_of=(f"{UKO_CODE_IRI}Module",), +) + +PythonClass = UKOClass( + uri=f"{UKO_PY_IRI}PythonClass", + label="PythonClass", + comment="A Python class definition — extends uko-oo:Class.", + subclass_of=(f"{UKO_OO_IRI}Class",), +) + +PythonFunction = UKOClass( + uri=f"{UKO_PY_IRI}PythonFunction", + label="PythonFunction", + comment=( + "A Python function or method — extends uko-oo:Method. " + "Includes decorators and type annotations." + ), + subclass_of=(f"{UKO_OO_IRI}Method",), +) + +# Intentionally parallel to JavaAnnotation; pending +# uko-code:Annotation base (see #576 review). +PythonDecorator = UKOClass( + uri=f"{UKO_PY_IRI}PythonDecorator", + label="PythonDecorator", + comment=( + "A decorator chain entry (@property, @staticmethod, " + "@classmethod, custom decorators)." + ), + subclass_of=(f"{UKO_CODE_IRI}TypeDefinition",), +) + +PythonTypeStub = UKOClass( + uri=f"{UKO_PY_IRI}PythonTypeStub", + label="PythonTypeStub", + comment=("Type annotations from .pyi stub files or inline typing module usage."), + subclass_of=(f"{UKO_CODE_IRI}TypeDefinition",), +) + + +# --------------------------------------------------------------------------- +# OWL Property definitions +# --------------------------------------------------------------------------- + +_py_has_decorator = UKOProperty( + uri=f"{UKO_PY_IRI}hasDecorator", + label="hasDecorator", + comment="Links a function/class to its decorator chain entries.", + domain=(f"{UKO_PY_IRI}PythonFunction", f"{UKO_PY_IRI}PythonClass"), + range_uri=f"{UKO_PY_IRI}PythonDecorator", + is_object_property=True, +) + +_py_has_type_stub = UKOProperty( + uri=f"{UKO_PY_IRI}hasTypeStub", + label="hasTypeStub", + comment="Links a module/class/function to its .pyi type stub.", + domain=( + f"{UKO_PY_IRI}PythonModule", + f"{UKO_PY_IRI}PythonClass", + f"{UKO_PY_IRI}PythonFunction", + ), + range_uri=f"{UKO_PY_IRI}PythonTypeStub", + is_object_property=True, +) + +_py_has_test = UKOProperty( + uri=f"{UKO_PY_IRI}hasTest", + label="hasTest", + comment=( + "Links a callable to its associated test cases (for WITH_TESTS depth level)." + ), + domain=(f"{UKO_PY_IRI}PythonFunction", f"{UKO_PY_IRI}PythonClass"), + range_uri=f"{UKO_PY_IRI}PythonFunction", + is_object_property=True, +) + +_py_decorator_name = UKOProperty( + uri=f"{UKO_PY_IRI}decoratorName", + label="decoratorName", + comment="The string name of a decorator (e.g. 'property', 'staticmethod').", + domain=(f"{UKO_PY_IRI}PythonDecorator",), + range_uri="xsd:string", + is_object_property=False, +) + +_py_stub_annotation = UKOProperty( + uri=f"{UKO_PY_IRI}stubAnnotation", + label="stubAnnotation", + comment="Type annotation string from a .pyi stub file.", + domain=(f"{UKO_PY_IRI}PythonTypeStub",), + range_uri="xsd:string", + is_object_property=False, +) + + +# --------------------------------------------------------------------------- +# Layer 2 dependencies +# --------------------------------------------------------------------------- + +_PY_LAYER2_DEPS = ( + Layer2Dependency( + uri=f"{UKO_OO_IRI}Class", + prefix="uko-oo:", + description="OO class (PythonClass extends this).", + ), + Layer2Dependency( + uri=f"{UKO_OO_IRI}Method", + prefix="uko-oo:", + description="OO method (PythonFunction extends this).", + ), + # Conceptual reference — no Layer 3 class directly extends this. + Layer2Dependency( + uri=f"{UKO_OO_IRI}Interface", + prefix="uko-oo:", + description="OO interface (referenced for protocol support).", + ), + # Conceptual reference — no Layer 3 class directly extends this. + Layer2Dependency( + uri=f"{UKO_OO_IRI}Attribute", + prefix="uko-oo:", + description="OO attribute (referenced for member listings).", + ), + Layer2Dependency( + uri=f"{UKO_CODE_IRI}Module", + prefix="uko-code:", + layer=1, + description="General code module (PythonModule extends this).", + ), + Layer2Dependency( + uri=f"{UKO_CODE_IRI}TypeDefinition", + prefix="uko-code:", + layer=1, + description="General type definition (PythonTypeStub extends this).", + ), +) + + +# --------------------------------------------------------------------------- +# DetailLevelMap: uko-oo: effective map (parent) then uko-py: insertions +# --------------------------------------------------------------------------- + +# Python-specific insertions (spec lines 25006-25026) +_PY_INSERTIONS: tuple[tuple[str, int], ...] = ( + # DECORATED_SIGNATURES: between SIGNATURES_WITH_DOCS(6) and + # VISIBILITY_ANNOTATED(7) — use 6 so it sorts after depth-6 parent + ("DECORATED_SIGNATURES", 6), + # TYPE_STUBS: between KEY_LOGIC(9) and NEAR_COMPLETE(10) + ("TYPE_STUBS", 9), + # WITH_TESTS: beyond FULL_SOURCE(11) + ("WITH_TESTS", 12), +) + +# Build the full Python effective map +PYTHON_DETAIL_LEVELS: tuple[tuple[str, int], ...] = build_detail_level_map( + OO_EFFECTIVE_LEVELS, _PY_INSERTIONS +) + + +# --------------------------------------------------------------------------- +# Vocabulary definition +# --------------------------------------------------------------------------- + +PYTHON_VOCABULARY = UKOVocabulary( + namespace=UKO_PY_IRI, + prefix=UKO_PY_PREFIX, + layer=3, + label="Python Technology Vocabulary", + comment=( + "Layer 3 technology-specific vocabulary for Python. " + "Extends uko-oo: with decorator chains, .pyi stub type " + "annotations, and associated test cases." + ), + parent_prefixes=("uko-oo:",), + classes=( + PythonModule, + PythonClass, + PythonFunction, + PythonDecorator, + PythonTypeStub, + ), + properties=( + _py_has_decorator, + _py_has_type_stub, + _py_has_test, + _py_decorator_name, + _py_stub_annotation, + ), + layer2_dependencies=_PY_LAYER2_DEPS, + inserted_levels=( + ( + "DECORATED_SIGNATURES", + "@property, @staticmethod, @classmethod, custom decorator chains.", + ), + ( + "TYPE_STUBS", + "Type annotations from .pyi stub files, typing module usage.", + ), + ( + "WITH_TESTS", + "Full source + associated test cases for each callable.", + ), + ), +) diff --git a/src/cleveragents/acms/uko/layer3_rs.py b/src/cleveragents/acms/uko/layer3_rs.py new file mode 100644 index 000000000..edf876465 --- /dev/null +++ b/src/cleveragents/acms/uko/layer3_rs.py @@ -0,0 +1,311 @@ +"""UKO Layer 3: Rust-specific vocabulary (``uko-rs:``). + +Extends Layer 2 Object-Oriented (``uko-oo:``) paradigm with +Rust-specific semantic refinements: + +Classes: +- ``RustStruct`` -- Rust struct (extends ``uko-oo:Class``) +- ``RustTrait`` -- Rust trait (extends ``uko-oo:Interface``) +- ``RustImpl`` -- Rust impl block +- ``RustFunction`` -- Rust function/method +- ``RustDeriveAttribute`` -- ``#[derive(...)]`` macro attribute + +Rust extends at the SIGNATURES level with: +- Visibility modifiers (``pub``, ``pub(crate)``) +- Lifetime parameters +- Trait bounds +- ``unsafe`` markers +- ``#[derive]`` macros +- ``#[must_use]`` annotations + +Based on ``docs/specification.md`` ~lines 44405-44420. +""" + +from __future__ import annotations + +from cleveragents.acms.uko.vocabulary import ( + OO_EFFECTIVE_LEVELS, + UKO_CODE_IRI, + UKO_FUNC_IRI, + UKO_OO_IRI, + Layer2Dependency, + UKOClass, + UKOProperty, + UKOVocabulary, + build_detail_level_map, +) + +__all__: list[str] = [ + "RUST_DETAIL_LEVELS", + "RUST_VOCABULARY", + "RustDeriveAttribute", + "RustFunction", + "RustImpl", + "RustStruct", + "RustTrait", +] + +# --------------------------------------------------------------------------- +# Namespace constants +# --------------------------------------------------------------------------- + +UKO_RS_IRI = "https://cleveragents.ai/ontology/uko/rs#" +UKO_RS_PREFIX = "uko-rs:" + + +# --------------------------------------------------------------------------- +# OWL Class definitions +# --------------------------------------------------------------------------- + +RustStruct = UKOClass( + uri=f"{UKO_RS_IRI}RustStruct", + label="RustStruct", + comment=( + "A Rust struct definition — extends uko-oo:Class. " + "Includes derive macros, visibility, and lifetime parameters." + ), + subclass_of=(f"{UKO_OO_IRI}Class",), +) + +RustTrait = UKOClass( + uri=f"{UKO_RS_IRI}RustTrait", + label="RustTrait", + comment=( + "A Rust trait definition — extends uko-oo:Interface. " + "Includes associated types, default implementations, and supertraits." + ), + subclass_of=(f"{UKO_OO_IRI}Interface",), +) + +# NOTE: subclasses uko-code:TypeDefinition as a pragmatic approximation. +# Impl blocks add methods to existing types, not define new types. +# Pending uko-code:CodeBlock base class (see #576 review). +RustImpl = UKOClass( + uri=f"{UKO_RS_IRI}RustImpl", + label="RustImpl", + comment=( + "A Rust impl block — either inherent or trait implementation. " + "Extends uko-code:TypeDefinition." + ), + subclass_of=(f"{UKO_CODE_IRI}TypeDefinition",), +) + +RustFunction = UKOClass( + uri=f"{UKO_RS_IRI}RustFunction", + label="RustFunction", + comment=( + "A Rust function or method — extends uko-oo:Method. " + "Includes lifetime parameters, trait bounds, and unsafe markers." + ), + subclass_of=(f"{UKO_OO_IRI}Method",), +) + +# NOTE: subclasses uko-code:TypeDefinition as a pragmatic approximation. +# Derive attributes are macro metadata, not type definitions. +# Pending uko-code:Annotation base class (see #576 review). +RustDeriveAttribute = UKOClass( + uri=f"{UKO_RS_IRI}RustDeriveAttribute", + label="RustDeriveAttribute", + comment=( + "A #[derive(...)] macro attribute applied to a struct/enum. " + "Records which traits are automatically derived." + ), + subclass_of=(f"{UKO_CODE_IRI}TypeDefinition",), +) + + +# --------------------------------------------------------------------------- +# OWL Property definitions +# --------------------------------------------------------------------------- + +_rs_visibility = UKOProperty( + uri=f"{UKO_RS_IRI}visibility", + label="visibility", + comment=( + "Visibility modifier: 'pub', 'pub(crate)', 'pub(super)', 'private' (default)." + ), + domain=( + f"{UKO_RS_IRI}RustStruct", + f"{UKO_RS_IRI}RustTrait", + f"{UKO_RS_IRI}RustFunction", + ), + range_uri="xsd:string", + is_object_property=False, +) + +_rs_lifetime_params = UKOProperty( + uri=f"{UKO_RS_IRI}lifetimeParameters", + label="lifetimeParameters", + comment="Lifetime parameter string (e.g. \"<'a, 'b>\").", + domain=( + f"{UKO_RS_IRI}RustStruct", + f"{UKO_RS_IRI}RustImpl", + f"{UKO_RS_IRI}RustFunction", + f"{UKO_RS_IRI}RustTrait", + ), + range_uri="xsd:string", + is_object_property=False, +) + +_rs_trait_bounds = UKOProperty( + uri=f"{UKO_RS_IRI}traitBounds", + label="traitBounds", + comment=("Trait bound constraints (e.g. 'T: Clone + Send')."), + domain=( + f"{UKO_RS_IRI}RustStruct", + f"{UKO_RS_IRI}RustFunction", + f"{UKO_RS_IRI}RustImpl", + ), + range_uri="xsd:string", + is_object_property=False, +) + +_rs_is_unsafe = UKOProperty( + uri=f"{UKO_RS_IRI}isUnsafe", + label="isUnsafe", + comment="Whether this item is marked as 'unsafe'.", + domain=( + f"{UKO_RS_IRI}RustFunction", + f"{UKO_RS_IRI}RustImpl", + f"{UKO_RS_IRI}RustTrait", + ), + range_uri="xsd:boolean", + is_object_property=False, +) + +_rs_derive_traits = UKOProperty( + uri=f"{UKO_RS_IRI}deriveTraits", + label="deriveTraits", + comment=( + "Comma-separated list of traits in #[derive(...)]. " + "E.g. 'Clone, Debug, Serialize'." + ), + domain=(f"{UKO_RS_IRI}RustDeriveAttribute",), + range_uri="xsd:string", + is_object_property=False, +) + +_rs_must_use = UKOProperty( + uri=f"{UKO_RS_IRI}mustUse", + label="mustUse", + comment="Whether this item carries a #[must_use] annotation.", + domain=( + f"{UKO_RS_IRI}RustStruct", + f"{UKO_RS_IRI}RustFunction", + ), + range_uri="xsd:boolean", + is_object_property=False, +) + +_rs_has_derive = UKOProperty( + uri=f"{UKO_RS_IRI}hasDerive", + label="hasDerive", + comment="Links a struct to its #[derive] attribute.", + domain=(f"{UKO_RS_IRI}RustStruct",), + range_uri=f"{UKO_RS_IRI}RustDeriveAttribute", + is_object_property=True, +) + +_rs_implements_trait = UKOProperty( + uri=f"{UKO_RS_IRI}implementsTrait", + label="implementsTrait", + comment="Links an impl block to the trait it implements.", + domain=(f"{UKO_RS_IRI}RustImpl",), + range_uri=f"{UKO_RS_IRI}RustTrait", + is_object_property=True, +) + + +# --------------------------------------------------------------------------- +# Layer 2 dependencies +# --------------------------------------------------------------------------- + +_RS_LAYER2_DEPS = ( + Layer2Dependency( + uri=f"{UKO_OO_IRI}Class", + prefix="uko-oo:", + description="OO class (RustStruct extends this).", + ), + Layer2Dependency( + uri=f"{UKO_OO_IRI}Interface", + prefix="uko-oo:", + description="OO interface (RustTrait extends this).", + ), + Layer2Dependency( + uri=f"{UKO_OO_IRI}Method", + prefix="uko-oo:", + description="OO method (RustFunction extends this).", + ), + # Conceptual reference — no Layer 3 class directly extends this. + Layer2Dependency( + uri=f"{UKO_FUNC_IRI}PureFunction", + prefix="uko-func:", + description=("Functional pure function (Rust supports functional patterns)."), + ), + # Conceptual reference — no Layer 3 class directly extends this. + Layer2Dependency( + uri=f"{UKO_FUNC_IRI}TypeClass", + prefix="uko-func:", + description=( + "Functional type class (Rust traits parallel Haskell typeclasses)." + ), + ), + Layer2Dependency( + uri=f"{UKO_CODE_IRI}TypeDefinition", + prefix="uko-code:", + layer=1, + description="General type definition (RustImpl, RustDeriveAttribute).", + ), +) + + +# --------------------------------------------------------------------------- +# DetailLevelMap: extends uko-oo: at SIGNATURES level +# --------------------------------------------------------------------------- + +# Rust extends at SIGNATURES level — enriches the content at +# SIGNATURES depth with visibility, lifetimes, trait bounds, unsafe, +# derive macros, must_use. No new depth levels are inserted. +_RS_INSERTIONS: tuple[tuple[str, int], ...] = () + +RUST_DETAIL_LEVELS: tuple[tuple[str, int], ...] = build_detail_level_map( + OO_EFFECTIVE_LEVELS, _RS_INSERTIONS +) + + +# --------------------------------------------------------------------------- +# Vocabulary definition +# --------------------------------------------------------------------------- + +RUST_VOCABULARY = UKOVocabulary( + namespace=UKO_RS_IRI, + prefix=UKO_RS_PREFIX, + layer=3, + label="Rust Technology Vocabulary", + comment=( + "Layer 3 technology-specific vocabulary for Rust. " + "Extends uko-oo: with visibility modifiers, " + "lifetime parameters, trait bounds, unsafe markers, " + "#[derive] macros, and #[must_use] annotations." + ), + parent_prefixes=("uko-oo:",), + classes=( + RustStruct, + RustTrait, + RustImpl, + RustFunction, + RustDeriveAttribute, + ), + properties=( + _rs_visibility, + _rs_lifetime_params, + _rs_trait_bounds, + _rs_is_unsafe, + _rs_derive_traits, + _rs_must_use, + _rs_has_derive, + _rs_implements_trait, + ), + layer2_dependencies=_RS_LAYER2_DEPS, + inserted_levels=(), +) diff --git a/src/cleveragents/acms/uko/layer3_ts.py b/src/cleveragents/acms/uko/layer3_ts.py new file mode 100644 index 000000000..27b047be1 --- /dev/null +++ b/src/cleveragents/acms/uko/layer3_ts.py @@ -0,0 +1,242 @@ +"""UKO Layer 3: TypeScript-specific vocabulary (``uko-ts:``). + +Extends the Layer 2 Object-Oriented paradigm (``uko-oo:``) with +TypeScript-specific semantic refinements: + +Classes: +- ``TypeScriptModule`` -- TypeScript module +- ``TypeScriptClass`` -- TypeScript class +- ``TypeScriptInterface`` -- TypeScript interface with generic params +- ``TypeScriptFunction`` -- TypeScript function/method + +TypeScript extends at the SIGNATURES level with: +- ``export``/``default export`` markers +- ``declare`` ambient types +- Generic type parameters +- Mapped/conditional types + +Based on ``docs/specification.md`` ~lines 44405-44420. +""" + +from __future__ import annotations + +from cleveragents.acms.uko.vocabulary import ( + OO_EFFECTIVE_LEVELS, + UKO_CODE_IRI, + UKO_OO_IRI, + Layer2Dependency, + UKOClass, + UKOProperty, + UKOVocabulary, + build_detail_level_map, +) + +__all__: list[str] = [ + "TYPESCRIPT_DETAIL_LEVELS", + "TYPESCRIPT_VOCABULARY", + "TypeScriptClass", + "TypeScriptFunction", + "TypeScriptInterface", + "TypeScriptModule", +] + +# --------------------------------------------------------------------------- +# Namespace constants +# --------------------------------------------------------------------------- + +UKO_TS_IRI = "https://cleveragents.ai/ontology/uko/ts#" +UKO_TS_PREFIX = "uko-ts:" + + +# --------------------------------------------------------------------------- +# OWL Class definitions +# --------------------------------------------------------------------------- + +TypeScriptModule = UKOClass( + uri=f"{UKO_TS_IRI}TypeScriptModule", + label="TypeScriptModule", + comment="A TypeScript module (.ts/.tsx file) — extends uko-code:Module.", + subclass_of=(f"{UKO_CODE_IRI}Module",), +) + +TypeScriptClass = UKOClass( + uri=f"{UKO_TS_IRI}TypeScriptClass", + label="TypeScriptClass", + comment="A TypeScript class definition — extends uko-oo:Class.", + subclass_of=(f"{UKO_OO_IRI}Class",), +) + +TypeScriptInterface = UKOClass( + uri=f"{UKO_TS_IRI}TypeScriptInterface", + label="TypeScriptInterface", + comment=( + "A TypeScript interface — extends uko-oo:Interface. " + "Includes generic type parameters, mapped and conditional types." + ), + subclass_of=(f"{UKO_OO_IRI}Interface",), +) + +TypeScriptFunction = UKOClass( + uri=f"{UKO_TS_IRI}TypeScriptFunction", + label="TypeScriptFunction", + comment=( + "A TypeScript function or method — extends uko-oo:Method. " + "Includes export markers and generic type parameters." + ), + subclass_of=(f"{UKO_OO_IRI}Method",), +) + + +# --------------------------------------------------------------------------- +# OWL Property definitions +# --------------------------------------------------------------------------- + +_ts_export_kind = UKOProperty( + uri=f"{UKO_TS_IRI}exportKind", + label="exportKind", + comment=( + "Export marker: 'named', 'default', 'none'. " + "Indicates how this symbol is exported from its module." + ), + domain=( + f"{UKO_TS_IRI}TypeScriptClass", + f"{UKO_TS_IRI}TypeScriptInterface", + f"{UKO_TS_IRI}TypeScriptFunction", + f"{UKO_TS_IRI}TypeScriptModule", + ), + range_uri="xsd:string", + is_object_property=False, +) + +_ts_is_ambient = UKOProperty( + uri=f"{UKO_TS_IRI}isAmbient", + label="isAmbient", + comment=( + "Whether this is a 'declare' ambient type definition (e.g. in a .d.ts file)." + ), + domain=( + f"{UKO_TS_IRI}TypeScriptClass", + f"{UKO_TS_IRI}TypeScriptInterface", + f"{UKO_TS_IRI}TypeScriptFunction", + ), + range_uri="xsd:boolean", + is_object_property=False, +) + +_ts_generic_params = UKOProperty( + uri=f"{UKO_TS_IRI}genericParameters", + label="genericParameters", + comment=("Generic type parameter string (e.g. '')."), + domain=( + f"{UKO_TS_IRI}TypeScriptClass", + f"{UKO_TS_IRI}TypeScriptInterface", + f"{UKO_TS_IRI}TypeScriptFunction", + ), + range_uri="xsd:string", + is_object_property=False, +) + +_ts_mapped_type = UKOProperty( + uri=f"{UKO_TS_IRI}isMappedType", + label="isMappedType", + comment="Whether this interface defines a mapped type.", + domain=(f"{UKO_TS_IRI}TypeScriptInterface",), + range_uri="xsd:boolean", + is_object_property=False, +) + +_ts_conditional_type = UKOProperty( + uri=f"{UKO_TS_IRI}isConditionalType", + label="isConditionalType", + comment="Whether this interface/type alias uses conditional types.", + domain=(f"{UKO_TS_IRI}TypeScriptInterface",), + range_uri="xsd:boolean", + is_object_property=False, +) + +_ts_implements_interface = UKOProperty( + uri=f"{UKO_TS_IRI}implementsInterface", + label="implementsInterface", + comment="Links a TypeScript class to the interface(s) it implements.", + domain=(f"{UKO_TS_IRI}TypeScriptClass",), + range_uri=f"{UKO_TS_IRI}TypeScriptInterface", + is_object_property=True, +) + + +# --------------------------------------------------------------------------- +# Layer 2 dependencies +# --------------------------------------------------------------------------- + +_TS_LAYER2_DEPS = ( + Layer2Dependency( + uri=f"{UKO_OO_IRI}Class", + prefix="uko-oo:", + description="OO class (TypeScriptClass extends this).", + ), + Layer2Dependency( + uri=f"{UKO_OO_IRI}Interface", + prefix="uko-oo:", + description="OO interface (TypeScriptInterface extends this).", + ), + Layer2Dependency( + uri=f"{UKO_OO_IRI}Method", + prefix="uko-oo:", + description="OO method (TypeScriptFunction extends this).", + ), + Layer2Dependency( + uri=f"{UKO_CODE_IRI}Module", + prefix="uko-code:", + layer=1, + description="General code module (TypeScriptModule extends this).", + ), +) + + +# --------------------------------------------------------------------------- +# DetailLevelMap: extends uko-oo: at SIGNATURES level +# --------------------------------------------------------------------------- + +# TypeScript extends at SIGNATURES level — enriches the content at +# SIGNATURES depth with export markers, declare ambient types, +# generics, mapped/conditional types. No new depth levels are +# inserted; the existing SIGNATURES level is enriched. +_TS_INSERTIONS: tuple[tuple[str, int], ...] = () + +TYPESCRIPT_DETAIL_LEVELS: tuple[tuple[str, int], ...] = build_detail_level_map( + OO_EFFECTIVE_LEVELS, _TS_INSERTIONS +) + + +# --------------------------------------------------------------------------- +# Vocabulary definition +# --------------------------------------------------------------------------- + +TYPESCRIPT_VOCABULARY = UKOVocabulary( + namespace=UKO_TS_IRI, + prefix=UKO_TS_PREFIX, + layer=3, + label="TypeScript Technology Vocabulary", + comment=( + "Layer 3 technology-specific vocabulary for TypeScript. " + "Extends uko-oo: with export/default export markers, declare " + "ambient types, generic type parameters, mapped and conditional types." + ), + parent_prefixes=("uko-oo:",), + classes=( + TypeScriptModule, + TypeScriptClass, + TypeScriptInterface, + TypeScriptFunction, + ), + properties=( + _ts_export_kind, + _ts_is_ambient, + _ts_generic_params, + _ts_mapped_type, + _ts_conditional_type, + _ts_implements_interface, + ), + layer2_dependencies=_TS_LAYER2_DEPS, + inserted_levels=(), +) diff --git a/src/cleveragents/acms/uko/vocabulary.py b/src/cleveragents/acms/uko/vocabulary.py new file mode 100644 index 000000000..f68fd36e3 --- /dev/null +++ b/src/cleveragents/acms/uko/vocabulary.py @@ -0,0 +1,426 @@ +"""Base UKO vocabulary types for Layer 3 technology extensions. + +Provides the foundational Pydantic models used by all Layer 3 +vocabulary modules (``layer3_py``, ``layer3_ts``, ``layer3_rs``, +``layer3_java``): + +- ``ProvenanceInfo`` -- Provenance contract (5 required fields) +- ``UKOClass`` -- OWL class definition +- ``UKOProperty`` -- OWL property definition +- ``Layer2Dependency`` -- Declared dependency on a Layer 2 URI +- ``UKOVocabulary`` -- Container for a vocabulary's classes, + properties, and layer-2 dependencies + +Utility functions: + +- ``build_detail_level_map`` -- Merge parent + child level insertions +- ``resolve_detail_level`` -- Walk the inheritance chain for a name + +Based on ``docs/specification.md`` ~lines 42501-42513 (provenance) +and ~lines 24923-24952 (DetailLevelMap). +""" + +from __future__ import annotations + +from datetime import UTC, datetime + +from pydantic import BaseModel, ConfigDict, Field, field_validator + +from cleveragents.acms.uko.detail_level_maps import OO_DETAIL_LEVEL_MAP +from cleveragents.acms.uko.vocabularies import ( + UKO_FUNC_IRI as UKO_FUNC_IRI, +) +from cleveragents.acms.uko.vocabularies import ( + UKO_OO_IRI as UKO_OO_IRI, +) +from cleveragents.acms.uko.vocabularies import ( + _validate_http_uri, +) + +__all__: list[str] = [ + "Layer2Dependency", + "ProvenanceInfo", + "UKOClass", + "UKOProperty", + "UKOVocabulary", + "build_detail_level_map", + "resolve_detail_level", +] + + +# --------------------------------------------------------------------------- +# Shared namespace constants (used by all Layer 3 modules) +# --------------------------------------------------------------------------- + +UKO_CODE_IRI = "https://cleveragents.ai/ontology/uko/code#" +"""Layer 1 general code domain namespace.""" + + +# --------------------------------------------------------------------------- +# Shared OO effective levels (parent map for all Layer 3 vocabularies) +# --------------------------------------------------------------------------- + +OO_EFFECTIVE_LEVELS: tuple[tuple[str, int], ...] = tuple( + (name, depth) + for name, depth in sorted(OO_DETAIL_LEVEL_MAP.levels.items(), key=lambda x: x[1]) +) +"""The uko-oo: effective map (spec lines 24991-25004). + +Used as the parent map for all Layer 3 technology vocabularies that +extend the Object-Oriented paradigm. +""" + + +# --------------------------------------------------------------------------- +# ProvenanceInfo +# --------------------------------------------------------------------------- + + +class ProvenanceInfo(BaseModel): + """Provenance contract for a UKO node. + + Every UKO node carries provenance back to the originating resource. + + Based on ``docs/specification.md`` ~lines 42501-42511. + + Attributes: + source_resource: URI or ULID of the originating resource. + source_path: File path within the resource (e.g. + ``"src/auth/manager.py"``). + source_range: Line range in ``"startLine:startCol-endLine:endCol"`` + format (e.g. ``"15:1-87:0"``). + valid_from: UTC timestamp when this node became valid. + is_current: Whether this is the current (non-superseded) version. + """ + + source_resource: str = Field( + ..., + min_length=1, + description="URI or ULID of the originating resource.", + ) + source_path: str = Field( + ..., + min_length=1, + description="File path within the resource.", + ) + source_range: str = Field( + default="", + description=( + 'Line range "startLine:startCol-endLine:endCol" (e.g. "15:1-87:0").' + ), + ) + valid_from: datetime = Field( + default_factory=lambda: datetime.now(tz=UTC), + description="UTC timestamp when this node became valid.", + ) + is_current: bool = Field( + default=True, + description="Whether this is the current version.", + ) + + @field_validator("source_resource", mode="before") + @classmethod + def _validate_source_resource(cls, value: str) -> str: + if isinstance(value, str) and not value.strip(): + raise ValueError("source_resource must not be whitespace-only.") + return value + + @field_validator("source_path", mode="before") + @classmethod + def _validate_source_path(cls, value: str) -> str: + if isinstance(value, str) and not value.strip(): + raise ValueError("source_path must not be whitespace-only.") + return value + + model_config = ConfigDict(frozen=True, str_strip_whitespace=True) + + +# --------------------------------------------------------------------------- +# UKOClass +# --------------------------------------------------------------------------- + + +class UKOClass(BaseModel): + """An OWL class definition within a UKO vocabulary. + + Attributes: + uri: Full IRI of the class (e.g. + ``"https://cleveragents.ai/ontology/uko/py#PythonClass"``). + label: Human-readable label. + comment: Description of the class. + subclass_of: Tuple of parent class URIs. + provenance: Optional provenance metadata for this node. + """ + + uri: str = Field(..., min_length=1, description="Full IRI of the class.") + label: str = Field(..., min_length=1, description="Human-readable label.") + comment: str = Field(default="", description="Description of the class.") + subclass_of: tuple[str, ...] = Field( + default=(), + description="Parent class URIs (rdfs:subClassOf).", + ) + provenance: ProvenanceInfo | None = Field( + default=None, description="Provenance metadata for this node." + ) + + @field_validator("uri", mode="before") + @classmethod + def _validate_uri(cls, value: str) -> str: + if isinstance(value, str) and not value.strip(): + raise ValueError("uri must not be whitespace-only.") + if isinstance(value, str) and value.strip(): + _validate_http_uri(value, field_name="uri") + return value + + @field_validator("label", mode="before") + @classmethod + def _validate_label(cls, value: str) -> str: + if isinstance(value, str) and not value.strip(): + raise ValueError("label must not be whitespace-only.") + return value + + model_config = ConfigDict(frozen=True, str_strip_whitespace=True) + + +# --------------------------------------------------------------------------- +# UKOProperty +# --------------------------------------------------------------------------- + + +class UKOProperty(BaseModel): + """An OWL property definition within a UKO vocabulary. + + Attributes: + uri: Full IRI of the property. + label: Human-readable label. + comment: Description of the property. + domain: Tuple of class URIs this property applies to. + range_uri: Object type or datatype URI. + is_object_property: True for owl:ObjectProperty, False for + owl:DatatypeProperty. + provenance: Optional provenance metadata for this node. + """ + + uri: str = Field(..., min_length=1, description="Full IRI of the property.") + label: str = Field(..., min_length=1, description="Human-readable label.") + comment: str = Field(default="", description="Description of the property.") + domain: tuple[str, ...] = Field( + default=(), description="Class URIs this property applies to." + ) + range_uri: str = Field(default="", description="Object type or datatype URI.") + is_object_property: bool = Field( + default=True, + description="True for owl:ObjectProperty, False for DatatypeProperty.", + ) + provenance: ProvenanceInfo | None = Field( + default=None, description="Provenance metadata for this node." + ) + + @field_validator("uri", mode="before") + @classmethod + def _validate_uri(cls, value: str) -> str: + if isinstance(value, str) and not value.strip(): + raise ValueError("uri must not be whitespace-only.") + if isinstance(value, str) and value.strip(): + _validate_http_uri(value, field_name="uri") + return value + + @field_validator("label", mode="before") + @classmethod + def _validate_label(cls, value: str) -> str: + if isinstance(value, str) and not value.strip(): + raise ValueError("label must not be whitespace-only.") + return value + + model_config = ConfigDict(frozen=True, str_strip_whitespace=True) + + +# --------------------------------------------------------------------------- +# Layer2Dependency +# --------------------------------------------------------------------------- + + +class Layer2Dependency(BaseModel): + """A declared dependency on a parent-layer URI. + + Documents which parent-layer classes/properties this Layer 3 + vocabulary references via ``rdfs:subClassOf`` or + ``rdfs:subPropertyOf``. Most dependencies target Layer 2 + (``uko-oo:``, ``uko-func:``, ``uko-proc:``), but some target + Layer 1 (``uko-code:``) when a class directly extends a Layer 1 + type (e.g. ``PythonModule`` extends ``uko-code:Module``). + + Attributes: + uri: The parent-layer URI depended upon. + layer: The UKO layer number (1-3). + prefix: The parent-layer prefix (e.g. ``"uko-oo:"``, + ``"uko-code:"``). + description: What this URI represents. + """ + + uri: str = Field(..., min_length=1, description="Parent-layer URI depended upon.") + layer: int = Field(default=2, ge=1, le=3, description="UKO layer number.") + prefix: str = Field( + ..., min_length=1, description='Parent-layer prefix (e.g. "uko-oo:").' + ) + description: str = Field(default="", description="What this URI represents.") + + model_config = ConfigDict(frozen=True, str_strip_whitespace=True) + + +# --------------------------------------------------------------------------- +# UKOVocabulary +# --------------------------------------------------------------------------- + + +class UKOVocabulary(BaseModel): + """Container for a UKO vocabulary's classes, properties, and deps. + + Each Layer 3 vocabulary module exposes a single ``UKOVocabulary`` + instance that describes the OWL/Turtle definitions, Layer 2 + dependencies, and DetailLevelMap insertions. + + Attributes: + namespace: Full IRI namespace for this vocabulary. + prefix: Short prefix (e.g. ``"uko-py:"``). + layer: UKO layer number (3 for technology-specific). + label: Human-readable name. + comment: Description. + parent_prefixes: Tuple of parent Layer 2 prefixes extended. + classes: Tuple of OWL class definitions. + properties: Tuple of OWL property definitions. + layer2_dependencies: Tuple of Layer 2 URIs depended on. + inserted_levels: Mapping of level name to insertion description. + provenance: Optional provenance metadata for this node. + """ + + namespace: str = Field(..., min_length=1, description="Full IRI namespace.") + prefix: str = Field(..., min_length=1, description='Short prefix (e.g. "uko-py:").') + layer: int = Field(default=3, ge=0, description="UKO layer number.") + label: str = Field(..., min_length=1, description="Human-readable name.") + comment: str = Field(default="", description="Description.") + parent_prefixes: tuple[str, ...] = Field( + default=(), + description="Parent Layer 2 prefixes extended.", + ) + classes: tuple[UKOClass, ...] = Field( + default=(), description="OWL class definitions." + ) + properties: tuple[UKOProperty, ...] = Field( + default=(), description="OWL property definitions." + ) + layer2_dependencies: tuple[Layer2Dependency, ...] = Field( + default=(), description="Layer 2 URIs depended on." + ) + inserted_levels: tuple[tuple[str, str], ...] = Field( + default=(), + description="Tuple of (level_name, description) for inserted levels.", + ) + provenance: ProvenanceInfo | None = Field( + default=None, description="Provenance metadata for this node." + ) + + model_config = ConfigDict(frozen=True, str_strip_whitespace=True) + + +# --------------------------------------------------------------------------- +# DetailLevelMap helpers +# --------------------------------------------------------------------------- + + +def build_detail_level_map( + parent_levels: tuple[tuple[str, int], ...], + insertions: tuple[tuple[str, int], ...], +) -> tuple[tuple[str, int], ...]: + """Merge parent levels with child insertions, re-numbering. + + Combines parent named levels with child-inserted levels and + produces a new consecutive integer-numbered map. Insertions + specify a position *after which* the new level is placed in + the ordering derived from sorting all entries by their original + integer. + + The algorithm: + + 1. Combine parent and insertion entries into one list. + 2. Sort by the original integer value (stable sort preserves + insertion order for ties — insertions at the same depth + appear after the parent entry at that depth). + 3. Re-number from 0 with consecutive integers. + + Args: + parent_levels: Tuple of ``(name, original_depth)`` from the + parent map. + insertions: Tuple of ``(name, original_depth)`` for new levels + to insert. The ``original_depth`` indicates the sort + position: the new level is placed after all existing + entries at depths <= this value and before entries at + depths > this value. + + Returns: + Tuple of ``(name, new_depth)`` with consecutive integers + starting at 0. + + Raises: + ValueError: If *parent_levels* or *insertions* contain + duplicate names. + """ + all_names: list[str] = [n for n, _ in parent_levels] + [n for n, _ in insertions] + seen: set[str] = set() + for name in all_names: + if name in seen: + raise ValueError(f"Duplicate level name: {name!r}") + seen.add(name) + + # Tag entries: parent entries get tag=0, insertions get tag=1 + # so insertions sort after same-depth parent entries. + tagged: list[tuple[int, int, str]] = [] + for name, depth in parent_levels: + tagged.append((depth, 0, name)) + for name, depth in insertions: + tagged.append((depth, 1, name)) + + tagged.sort(key=lambda t: (t[0], t[1])) + + return tuple((name, idx) for idx, (_, _, name) in enumerate(tagged)) + + +def resolve_detail_level( + name: str, + levels: tuple[tuple[str, int], ...], + parent_levels: tuple[tuple[str, int], ...] | None = None, +) -> int: + """Resolve a named detail level to its integer depth. + + Searches *levels* first, then *parent_levels* if provided. + Supports 2-level lookup (child + optional parent fallback). + + In practice, full 4-layer chain resolution (Layer 3 -> Layer 2 -> + Layer 1 -> Layer 0) is achieved at *build* time by + ``build_detail_level_map``, which flattens the hierarchy into a + single merged map. This function provides an additional parent + fallback for cases where un-merged maps are used. + + Args: + name: Named level to resolve (e.g. ``"DECORATED_SIGNATURES"``). + levels: Current map as tuple of ``(name, depth)`` pairs. + parent_levels: Optional parent map for fallback resolution. + + Returns: + Integer depth for the named level. + + Raises: + ValueError: If *name* is not found in *levels* or + *parent_levels*. + """ + name = name.strip() + if not name: + raise ValueError("Level name must not be empty or whitespace-only.") + for level_name, depth in levels: + if level_name == name: + return depth + if parent_levels is not None: + for level_name, depth in parent_levels: + if level_name == name: + return depth + raise ValueError(f"Unknown detail level: {name!r}")