Feature: UKO Runtime — Provenance Tracking, Temporal Versioning, and ACMS Integration As a context pipeline consumer I need the UKO runtime to produce typed triples with provenance, support temporal versioning, and integrate with ACMS context strategies So that the knowledge graph is semantically rich and queryable across time # ================================================================= # UKO Indexer produces provenance-annotated triples # ================================================================= Scenario: UKO indexer produces typed triples with provenance metadata Given uko a PythonAnalyzer is registered And uko a UKOIndexer with all backends When uko I index a Python resource "01HQ8ZDRX50000000000000001" with content "class Foo:\n pass" Then uko the index result should have triple_count greater than 0 And uko the graph backend should contain a triple with predicate "uko:sourceResource" And uko the graph backend should contain a triple with predicate "uko:validFrom" And uko the graph backend should contain a triple with predicate "uko:isCurrent" Scenario: Provenance metadata has correct source_resource value Given uko a PythonAnalyzer is registered And uko a UKOIndexer with all backends When uko I index a Python resource "01HQ8ZDRX50000000000000002" with content "def bar(): pass" Then uko the graph backend should contain a triple with object "uko://resource/01HQ8ZDRX50000000000000002" Scenario: Provenance metadata marks triples as current Given uko a PythonAnalyzer is registered And uko a UKOIndexer with all backends When uko I index a Python resource "01HQ8ZDRX50000000000000003" with content "x = 1" Then uko the graph has a predicate-object pair "uko:isCurrent" "true" # ================================================================= # Four ontology layers are populated during indexing # ================================================================= Scenario: Indexing a Python file populates layer 0 (universal) Given uko a PythonAnalyzer is registered And uko a UKOIndexer with all backends When uko I index a Python resource "01HQ8ZDRX50000000000000004" with content "class Foo:\n pass" Then uko the graph has a predicate-object pair "uko:layer" "0" Scenario: Indexing a Python file populates layer 1 (domain) Given uko a PythonAnalyzer is registered And uko a UKOIndexer with all backends When uko I index a Python resource "01HQ8ZDRX50000000000000005" with content "class Foo:\n pass" Then uko the graph has a predicate-object pair "uko:layer" "1" Scenario: Indexing a Python file populates layer 3 (technology) Given uko a PythonAnalyzer is registered And uko a UKOIndexer with all backends When uko I index a Python resource "01HQ8ZDRX50000000000000006" with content "class Foo:\n pass" Then uko the graph has a predicate-object pair "uko:layer" "3" Scenario: Indexing a Python file populates layer 2 (paradigm) Given uko a PythonAnalyzer is registered And uko a UKOIndexer with all backends When uko I index a Python resource "01HQ8ZDRX50000000000000012" with content "class Foo:\n pass" Then uko the graph has a predicate-object pair "uko:layer" "2" # ================================================================= # Implicit relationship inference # ================================================================= Scenario: Inference engine produces sibling relationships for same-type subjects Given uko an inference engine And uko triples with two subjects of the same type "uko-py:Class" When uko I run inference Then uko the inferred triples should contain a "uko:implicitSiblingOf" relationship Scenario: Inference engine produces containment relationships Given uko an inference engine And uko triples with a parent subject "uko://code/module/foo" and child "uko://code/module/foo/bar" When uko I run inference Then uko the inferred triples should contain a "uko:implicitContains" relationship Scenario: Inference engine produces dependency relationships Given uko an inference engine And uko triples where subject "uko://code/module/a" references "uko://code/module/b" When uko I run inference Then uko the inferred triples should contain a "uko:implicitDependsOn" relationship Scenario: Inference engine returns empty list for empty input Given uko an inference engine When uko I run inference on empty triples Then uko the inferred triples should be empty Scenario: Indexer stores implicit relationship triples in graph backend Given uko a PythonAnalyzer is registered And uko a UKOIndexer with all backends When uko I index a Python resource "01HQ8ZDRX50000000000000007" with content "class Foo:\n pass\nclass Bar:\n pass" Then uko the graph backend should contain a triple with predicate "uko:implicitSiblingOf" # ================================================================= # UKO Query Interface — ACMS context strategy integration # ================================================================= Scenario: UKO query interface classifies a resource by layer Given uko a graph backend with triples for resource "01HQ8ZDRX50000000000000008" And uko the graph has a type triple "uko-py:Class" for the resource When uko I classify resource "01HQ8ZDRX50000000000000008" Then uko the classification result should have layer 3 Scenario: UKO query interface returns layer 0 for unclassified resource Given uko a graph backend with no triples When uko I classify resource "01HQ8ZDRX50000000000000009" Then uko the classification result should have layer 0 Scenario: UKO query interface returns primary type Given uko a graph backend with triples for resource "01HQ8ZDRX50000000000000010" And uko the graph has a type triple "uko-py:Class" for the resource When uko I classify resource "01HQ8ZDRX50000000000000010" Then uko the classification result should have primary_type "uko-py:Class" Scenario: UKO query interface rejects empty resource_id Given uko a graph backend with no triples Then uko classifying with empty resource_id should raise ValueError Scenario: UKO query interface rejects invalid graph_backend type Then uko creating UKOQueryInterface with invalid backend should raise TypeError Scenario: UKO query interface rejects empty project Then uko creating UKOQueryInterface with empty project should raise ValueError Scenario: UKO query interface get_resources_by_layer rejects invalid layer Given uko a graph backend with no triples Then uko get_resources_by_layer with layer 4 should raise ValueError # ================================================================= # UKO Graph Persistence # ================================================================= Scenario: UKO graph persistence saves and restores triples Given uko a graph backend with triples for resource "01HQ8ZDRX50000000000000011" And uko the graph has a type triple "uko-py:Class" for the resource When uko I save the graph state And uko I restore the graph state into a fresh backend Then uko the restored backend should have triples Scenario: UKO graph persistence returns 0 for empty graph Given uko a graph backend with no triples When uko I save the graph state Then uko the save result should be 0 Scenario: UKO graph persistence restore returns 0 when no data persisted Given uko a graph backend with no triples When uko I restore the graph state into a fresh backend Then uko the restore result should be 0 Scenario: UKO graph persistence rejects empty project Then uko creating UKOGraphPersistence with empty project should raise ValueError Scenario: UKO graph persistence rejects invalid graph_backend type Then uko creating UKOGraphPersistence with invalid backend should raise TypeError Scenario: InMemoryPersistenceBackend save and load round-trip Given uko an in-memory persistence backend When uko I save triples to the persistence backend for project "local/test" Then uko loading from the persistence backend should return the same triples Scenario: InMemoryPersistenceBackend returns empty list for unknown project Given uko an in-memory persistence backend Then uko loading from the persistence backend for unknown project should return empty list Scenario: InMemoryPersistenceBackend rejects empty project on save Given uko an in-memory persistence backend Then uko saving to persistence backend with empty project should raise ValueError Scenario: InMemoryPersistenceBackend rejects empty project on load Given uko an in-memory persistence backend Then uko loading from persistence backend with empty project should raise ValueError Scenario: JSONFilePersistenceBackend save and load round-trip Given uko a JSON file persistence backend in a temp directory When uko I save triples to the JSON file backend for project "local/test" Then uko loading from the JSON file backend should return the same triples Scenario: JSONFilePersistenceBackend returns empty list for unknown project Given uko a JSON file persistence backend in a temp directory Then uko loading from the JSON file backend for unknown project should return empty list Scenario: JSONFilePersistenceBackend rejects empty project on save Given uko a JSON file persistence backend in a temp directory Then uko saving to JSON file backend with empty project should raise ValueError Scenario: JSONFilePersistenceBackend rejects empty project on load Given uko a JSON file persistence backend in a temp directory Then uko loading from JSON file backend with empty project should raise ValueError # ================================================================= # Temporal versioning — revision chain # ================================================================= Scenario: Temporal service stores initial node Given uko a temporal backend And uko I store an initial node "uko-py:class/Foo_v1" for resource "RES001" Then uko the temporal backend should have node "uko-py:class/Foo_v1" And uko the node should be current Scenario: Temporal service creates a revision Given uko a temporal backend And uko I store an initial node "uko-py:class/Foo_v1" for resource "RES001" And uko I create a revision from "uko-py:class/Foo_v1" to "uko-py:class/Foo_v1_r2" Then uko the temporal backend should have node "uko-py:class/Foo_v1_r2" And uko node "uko-py:class/Foo_v1" should be historical And uko node "uko-py:class/Foo_v1_r2" should be current Scenario: Temporal service get_revision_chain returns full chain Given uko a temporal backend And uko I store an initial node "uko-py:class/Foo_v1" for resource "RES001" And uko I create a revision from "uko-py:class/Foo_v1" to "uko-py:class/Foo_v1_r2" When uko I get the revision chain for "uko-py:class/Foo_v1_r2" Then uko the revision chain should have depth 2 And uko the revision chain current_uri should be "uko-py:class/Foo_v1_r2" And uko the revision chain predecessors should contain "uko-py:class/Foo_v1" Scenario: Temporal service get_history returns all versions Given uko a temporal backend And uko I store an initial node "uko-py:class/Foo_v1" for resource "RES001" And uko I create a revision from "uko-py:class/Foo_v1" to "uko-py:class/Foo_v1_r2" When uko I get history for "uko-py:class/Foo_v1" with scope ALL Then uko the history should have 2 nodes Scenario: Temporal service get_current returns the current node Given uko a temporal backend And uko I store an initial node "uko-py:class/Foo_v1" for resource "RES001" And uko I create a revision from "uko-py:class/Foo_v1" to "uko-py:class/Foo_v1_r2" When uko I get current for "uko-py:class/Foo_v1" Then uko the current node should be "uko-py:class/Foo_v1_r2" Scenario: Temporal service mark_historical marks a node as historical Given uko a temporal backend And uko I store an initial node "uko-py:class/Foo_v1" for resource "RES001" When uko I mark "uko-py:class/Foo_v1" as historical Then uko node "uko-py:class/Foo_v1" should be historical # ================================================================= # Layer inference helper # ================================================================= Scenario: Layer inference returns 0 for universal UKO URI Then uko inferring layer for "uko:Resource" should return 0 Scenario: Layer inference returns 1 for domain UKO URI Then uko inferring layer for "uko-code:Module" should return 1 Scenario: Layer inference returns 2 for paradigm UKO URI Then uko inferring layer for "uko-oo:Class" should return 2 Scenario: Layer inference returns 3 for technology UKO URI Then uko inferring layer for "uko-py:Class" should return 3 Scenario: Layer inference returns 3 for full IRI technology URI Then uko inferring layer for "https://cleveragents.ai/ontology/uko/py#Class" should return 3 Scenario: Layer inference returns 0 for unknown URI Then uko inferring layer for "unknown:Foo" should return 0 # ================================================================= # ClassificationResult validation # ================================================================= Scenario: ClassificationResult rejects empty resource_id Then uko creating ClassificationResult with empty resource_id should raise ValueError Scenario: ClassificationResult rejects invalid layer Then uko creating ClassificationResult with layer 5 should raise ValueError Scenario: ClassificationResult accepts valid layer 0 Then uko creating ClassificationResult with layer 0 should succeed Scenario: ClassificationResult accepts valid layer 3 Then uko creating ClassificationResult with layer 3 should succeed