From c6d1ee55e6dc847b36c48cf63a27fdb6d307ea99 Mon Sep 17 00:00:00 2001 From: tegwick Date: Sun, 26 Apr 2026 02:56:52 +0200 Subject: [PATCH] deterministic input/output hints for interface capabilities --- .../candidate_graph/generator.py | 26 +++++++++++++++++-- tests/test_candidate_graph.py | 26 +++++++++++++++++++ 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/src/repo_registry/candidate_graph/generator.py b/src/repo_registry/candidate_graph/generator.py index 9d1a959..af4da2b 100644 --- a/src/repo_registry/candidate_graph/generator.py +++ b/src/repo_registry/candidate_graph/generator.py @@ -138,8 +138,8 @@ class CandidateGraphGenerator: return CandidateCapabilityDraft( name="Expose Repository Interface", description=self._interface_description(chunks), - inputs=[], - outputs=["callable interface"], + inputs=self._interface_inputs(interfaces), + outputs=self._interface_outputs(interfaces), confidence=self._interface_confidence( interfaces=interfaces, tests=tests, @@ -195,6 +195,28 @@ class CandidateGraphGenerator: return "API" return "interface" + def _interface_inputs(self, interfaces: list[ObservedFact]) -> list[str]: + feature_types = {self._feature_type(fact) for fact in interfaces} + inputs: list[str] = [] + if "API" in feature_types: + inputs.append("HTTP request") + if "CLI" in feature_types: + inputs.append("CLI arguments") + if not inputs: + inputs.append("caller input") + return inputs + + def _interface_outputs(self, interfaces: list[ObservedFact]) -> list[str]: + feature_types = {self._feature_type(fact) for fact in interfaces} + outputs: list[str] = [] + if "API" in feature_types: + outputs.append("HTTP response") + if "CLI" in feature_types: + outputs.append("command output") + if not outputs: + outputs.append("callable interface result") + return outputs + def _feature_name(self, fact: ObservedFact, chunks: list[ContentChunk]) -> str: route_name = self._route_feature_name(fact.value) if route_name: diff --git a/tests/test_candidate_graph.py b/tests/test_candidate_graph.py index 1cec574..a61a48f 100644 --- a/tests/test_candidate_graph.py +++ b/tests/test_candidate_graph.py @@ -55,6 +55,8 @@ def test_candidate_generator_builds_review_seed_from_observed_facts(): interface_capability = ability.capabilities[0] assert interface_capability.name == "Expose Repository Interface" assert interface_capability.confidence == 0.75 + assert interface_capability.inputs == ["HTTP request"] + assert interface_capability.outputs == ["HTTP response"] assert interface_capability.features[0].type == "API" assert interface_capability.features[0].name == "POST /classify" assert interface_capability.features[0].location == "app.py" @@ -180,5 +182,29 @@ def test_candidate_generator_names_cli_features_from_nearby_function(): graph = CandidateGraphGenerator().generate(repository, facts, chunks) feature = graph[0].capabilities[0].features[0] + capability = graph[0].capabilities[0] assert feature.type == "CLI" assert feature.name == "CLI command import_repositories" + assert capability.inputs == ["CLI arguments"] + assert capability.outputs == ["command output"] + + +def test_candidate_generator_uses_generic_io_for_unknown_interfaces(): + repository = Repository( + id=1, + name="UnknownInterface", + url="/tmp/unknown-interface", + description=None, + branch="main", + status="analyzed", + ) + facts = [ + fact(1, "documentation", "README", "README.md"), + fact(2, "interface", "possible surface", "src/plugin.py"), + ] + + graph = CandidateGraphGenerator().generate(repository, facts) + + capability = graph[0].capabilities[0] + assert capability.inputs == ["caller input"] + assert capability.outputs == ["callable interface result"]