Files
cleveragents-core/features/uko_analyzers.feature
T
freemo d990fc1b41
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 15s
CI / build (pull_request) Successful in 16s
CI / quality (pull_request) Successful in 19s
CI / typecheck (pull_request) Successful in 37s
CI / security (pull_request) Successful in 37s
CI / unit_tests (pull_request) Successful in 2m30s
CI / integration_tests (pull_request) Successful in 2m57s
CI / docker (pull_request) Successful in 38s
CI / coverage (pull_request) Successful in 4m22s
CI / lint (push) Successful in 12s
CI / build (push) Successful in 15s
CI / quality (push) Successful in 16s
CI / security (push) Successful in 33s
CI / typecheck (push) Successful in 33s
CI / benchmark-regression (push) Has been skipped
CI / unit_tests (push) Successful in 2m8s
CI / docker (push) Successful in 39s
CI / integration_tests (push) Successful in 3m9s
CI / coverage (push) Successful in 4m35s
CI / benchmark-publish (push) Successful in 15m41s
CI / benchmark-regression (pull_request) Successful in 28m58s
feat(uko): add analyzer plugin framework and initial domain analyzers
Implemented the analyzer plugin framework with AnalyzerProtocol,
AnalyzerRegistry for registration/discovery by file extension,
PythonAnalyzer (AST-based extraction of modules, classes, functions,
imports, docstrings), and MarkdownAnalyzer (section, code block, and
link extraction). Both analyzers produce well-formed UKO triples with
proper URI schemes.

ISSUES CLOSED: #551
2026-03-05 19:20:39 +00:00

239 lines
11 KiB
Gherkin

Feature: UKO Analyzer Plugin Framework
As a developer
I want pluggable domain analyzers that produce UKO triples from resources
So that the ACMS can index code and documents into the UKO knowledge graph
# ---- UKOTriple model ----
Scenario: Create a valid UKOTriple with object_uri
Given a UKOTriple with subject "uko://code/module/foo" predicate "rdf:type" and object_uri "uko-code:Module"
Then the triple subject_uri should be "uko://code/module/foo"
And the triple predicate should be "rdf:type"
And the triple object_uri should be "uko-code:Module"
And the triple confidence should be 1.0
Scenario: Create a UKOTriple with object_value
Given a UKOTriple with subject "uko://code/class/foo/Bar" predicate "rdfs:label" and object_value "Bar"
Then the triple object_value should be "Bar"
Scenario: UKOTriple rejects empty subject_uri
Then creating a UKOTriple with empty subject_uri should raise ValueError
Scenario: UKOTriple rejects empty predicate
Then creating a UKOTriple with empty predicate should raise ValueError
Scenario: UKOTriple rejects confidence above 1.0
Then creating a UKOTriple with confidence 1.5 should raise ValueError
Scenario: UKOTriple rejects confidence below 0.0
Then creating a UKOTriple with confidence -0.1 should raise ValueError
Scenario: UKOTriple is immutable
Given a UKOTriple with subject "uko://a" predicate "rdf:type" and object_uri "uko:B"
Then modifying the triple confidence should raise an error
# ---- AnalyzerRegistry ----
Scenario: Register and look up an analyzer by extension
Given an AnalyzerRegistry with a PythonAnalyzer registered
When I look up the analyzer for extension ".py"
Then the analyzer domain should be "python"
Scenario: Registry returns None for unregistered extension
Given an empty AnalyzerRegistry
When I look up the analyzer for extension ".rs"
Then the lookup result should be None
Scenario: Registry rejects non-protocol objects
Given an empty AnalyzerRegistry
Then registering a plain object should raise TypeError
Scenario: Registry rejects analyzer with no extensions
Given an empty AnalyzerRegistry
Then registering an analyzer with no extensions should raise ValueError
Scenario: Registry lists all extensions
Given an AnalyzerRegistry with both Python and Markdown analyzers
Then the registry should list extensions including ".py" and ".md"
Scenario: Registry counts analyzers
Given an AnalyzerRegistry with both Python and Markdown analyzers
Then the registry length should be 2
Scenario: Registry first-registered-wins for duplicate extensions
Given an AnalyzerRegistry with a PythonAnalyzer registered
When I register another analyzer for ".py"
And I look up the analyzer for extension ".py"
Then the analyzer domain should be "python"
# ---- PythonAnalyzer ----
Scenario: PythonAnalyzer satisfies AnalyzerProtocol
Given a PythonAnalyzer instance
Then it should satisfy the AnalyzerProtocol
Scenario: PythonAnalyzer supports .py and .pyi extensions
Given a PythonAnalyzer instance
Then its supported extensions should include ".py" and ".pyi"
Scenario: PythonAnalyzer extracts module declaration
Given a PythonAnalyzer instance
When I analyze Python content "x = 1\n" with resource URI "src/foo.py"
Then the triples should contain a triple with predicate "rdf:type" and object_uri "uko-code:Module"
Scenario: PythonAnalyzer extracts class definition
Given a PythonAnalyzer instance
When I analyze Python content with a class "MyClass"
Then the triples should contain a triple with predicate "rdf:type" and object_uri "uko-py:Class"
And the triples should contain a triple with predicate "rdfs:label" and object_value "MyClass"
Scenario: PythonAnalyzer extracts class containment
Given a PythonAnalyzer instance
When I analyze Python content with a class "MyClass"
Then the triples should contain a triple with predicate "uko:contains" linking module to class
Scenario: PythonAnalyzer extracts function definition
Given a PythonAnalyzer instance
When I analyze Python content with a function "do_work"
Then the triples should contain a triple with predicate "rdf:type" and object_uri "uko-py:Function"
And the triples should contain a triple with predicate "rdfs:label" and object_value "do_work"
Scenario: PythonAnalyzer extracts method inside class
Given a PythonAnalyzer instance
When I analyze Python content with class "Cls" and method "run"
Then the triples should contain a method triple with predicate "rdfs:label" and object_value "run"
Scenario: PythonAnalyzer extracts import statement
Given a PythonAnalyzer instance
When I analyze Python content with import "os"
Then the triples should contain a triple with predicate "uko:references" referencing "os"
Scenario: PythonAnalyzer extracts from-import statement
Given a PythonAnalyzer instance
When I analyze Python content with from-import "pathlib"
Then the triples should contain a triple with predicate "uko:references" referencing "pathlib"
Scenario: PythonAnalyzer extracts module docstring
Given a PythonAnalyzer instance
When I analyze Python content with docstring "Module docs."
Then the triples should contain a triple with predicate "uko-doc:hasDocstring" and object_value "Module docs."
Scenario: PythonAnalyzer extracts class docstring
Given a PythonAnalyzer instance
When I analyze Python content with class docstring
Then the triples should contain a docstring triple for the class
Scenario: PythonAnalyzer handles syntax errors gracefully
Given a PythonAnalyzer instance
When I analyze malformed Python content
Then the analyzer result should be an empty triple list
Scenario: PythonAnalyzer rejects empty content
Given a PythonAnalyzer instance
Then analyzing empty content should raise ValueError
Scenario: PythonAnalyzer rejects empty resource_uri
Given a PythonAnalyzer instance
Then analyzing with empty resource_uri should raise ValueError
Scenario: PythonAnalyzer extracts base class inheritance
Given a PythonAnalyzer instance
When I analyze Python content with a class inheriting from "BaseClass"
Then the triples should contain a triple with predicate "uko-py:inheritsFrom" and object_value "BaseClass"
# ---- MarkdownAnalyzer ----
Scenario: MarkdownAnalyzer satisfies AnalyzerProtocol
Given a MarkdownAnalyzer instance
Then it should satisfy the AnalyzerProtocol
Scenario: MarkdownAnalyzer supports .md and .markdown extensions
Given a MarkdownAnalyzer instance
Then its supported extensions should include ".md" and ".markdown"
Scenario: MarkdownAnalyzer extracts document declaration
Given a MarkdownAnalyzer instance
When I analyze Markdown content "# Hello\nWorld\n" with resource URI "docs/readme.md"
Then the triples should contain a triple with predicate "rdf:type" and object_uri "uko-doc:Document"
Scenario: MarkdownAnalyzer extracts headings as sections
Given a MarkdownAnalyzer instance
When I analyze Markdown content with heading "Introduction"
Then the triples should contain a triple with predicate "rdf:type" and object_uri "uko-doc:Section"
And the triples should contain a triple with predicate "uko-doc:headingText" and object_value "Introduction"
And the triples should contain a triple with predicate "uko-doc:headingLevel" and object_value "1"
Scenario: MarkdownAnalyzer extracts nested heading hierarchy
Given a MarkdownAnalyzer instance
When I analyze Markdown with nested headings
Then the triples should contain section containment for nested sections
Scenario: MarkdownAnalyzer extracts fenced code blocks
Given a MarkdownAnalyzer instance
When I analyze Markdown content with a Python code block
Then the triples should contain a triple with predicate "rdf:type" and object_uri "uko-code:CodeBlock"
And the triples should contain a triple with predicate "uko-code:language" and object_value "python"
Scenario: MarkdownAnalyzer extracts links
Given a MarkdownAnalyzer instance
When I analyze Markdown content with a link to "https://example.com"
Then the triples should contain a triple with predicate "uko:references" and object_value "https://example.com"
Scenario: MarkdownAnalyzer rejects empty content
Given a MarkdownAnalyzer instance
Then analyzing empty markdown content should raise ValueError
Scenario: MarkdownAnalyzer rejects empty resource_uri
Given a MarkdownAnalyzer instance
Then analyzing markdown with empty resource_uri should raise ValueError
Scenario: MarkdownAnalyzer handles bare code fences
Given a MarkdownAnalyzer instance
When I analyze Markdown content with a bare code fence
Then the triples should contain a code block triple
# ---- Coverage gap: AnalyzerRegistry.get_for_extension("") ----
Scenario: Registry returns None for empty extension
Given an AnalyzerRegistry with a PythonAnalyzer registered
When I look up the analyzer for an empty extension
Then the lookup result should be None
Scenario: Registry list_analyzers returns all registered analyzers
Given an AnalyzerRegistry with both Python and Markdown analyzers
Then the registry list_analyzers should return 2 analyzers
# ---- Coverage gap: PythonAnalyzer function docstring ----
Scenario: PythonAnalyzer extracts function docstring
Given a PythonAnalyzer instance
When I analyze Python content with a function with docstring
Then the triples should contain a triple with predicate "uko-doc:hasDocstring" for a function
# ---- Coverage gap: PythonAnalyzer method docstring ----
Scenario: PythonAnalyzer extracts method docstring
Given a PythonAnalyzer instance
When I analyze Python content with class and method with docstring
Then the triples should contain a triple with predicate "uko-doc:hasDocstring" for a method
# ---- Coverage gap: PythonAnalyzer dotted base class (ast.Attribute) ----
Scenario: PythonAnalyzer extracts dotted base class inheritance
Given a PythonAnalyzer instance
When I analyze Python content with a class inheriting from "collections.abc.Mapping"
Then the triples should contain a triple with predicate "uko-py:inheritsFrom" and object_value "collections.abc.Mapping"
# ---- Coverage gap: MarkdownAnalyzer domain property ----
Scenario: MarkdownAnalyzer domain is "markdown"
Given a MarkdownAnalyzer instance
Then the analyzer domain property should be "markdown"
# ---- Coverage gap: MarkdownAnalyzer sibling heading pops stack ----
Scenario: MarkdownAnalyzer handles sibling headings at same level
Given a MarkdownAnalyzer instance
When I analyze Markdown with sibling headings at the same level
Then each sibling section should be contained by the document