Summary
The structured-output response models for LLM analysis, LLMFinding (src/skillspector/llm_analyzer_base.py) and MetaAnalyzerFinding (src/skillspector/nodes/meta_analyzer.py), express their numeric ranges with Pydantic ge/le (start_line >= 1, confidence in [0.0, 1.0]). Pydantic emits those as JSON-schema minimum / maximum. Some OpenAI-compatible structured-output and tool-calling endpoints validate the response schema and reject those keywords, so the LLM call fails before any finding is returned.
Impact
On an affected endpoint, every LLM-backed analyzer (semantic discovery and the meta-analyzer filter/enrich pass) fails the structured-output call, so --llm runs produce no LLM findings even though static analysis still works.
Proposed fix
Drop the ge/le Field bounds, convey the ranges in the field descriptions, and enforce them with runtime field_validators that clamp into range. The emitted schema then carries no minimum / maximum, while the in-code [0, 1] / >= 1 guarantee is unchanged.
Raised while splitting the bounded-reads PR (#19), where @rng1995 asked for this to land as its own change with rationale.
Summary
The structured-output response models for LLM analysis,
LLMFinding(src/skillspector/llm_analyzer_base.py) andMetaAnalyzerFinding(src/skillspector/nodes/meta_analyzer.py), express their numeric ranges with Pydanticge/le(start_line >= 1, confidence in [0.0, 1.0]). Pydantic emits those as JSON-schemaminimum/maximum. Some OpenAI-compatible structured-output and tool-calling endpoints validate the response schema and reject those keywords, so the LLM call fails before any finding is returned.Impact
On an affected endpoint, every LLM-backed analyzer (semantic discovery and the meta-analyzer filter/enrich pass) fails the structured-output call, so
--llmruns produce no LLM findings even though static analysis still works.Proposed fix
Drop the
ge/leField bounds, convey the ranges in the field descriptions, and enforce them with runtimefield_validators that clamp into range. The emitted schema then carries nominimum/maximum, while the in-code [0, 1] / >= 1 guarantee is unchanged.Raised while splitting the bounded-reads PR (#19), where @rng1995 asked for this to land as its own change with rationale.