from __future__ import annotations from dataclasses import asdict from html import escape from fastapi import APIRouter, Depends, Form from fastapi.responses import HTMLResponse, RedirectResponse from repo_registry.core.service import RegistryService from repo_registry.web_api.app import get_service router = APIRouter(include_in_schema=False) def page(title: str, body: str) -> HTMLResponse: return HTMLResponse( f""" {escape(title)} · Repository Ability Registry
Repository Ability Registry
{body}
""" ) @router.get("/ui") def repository_index(service: RegistryService = Depends(get_service)) -> HTMLResponse: repositories = service.list_repositories() rows = "\n".join( f""" {escape(repo.name)} {escape(repo.status)} {escape(repo.branch)} {escape(repo.url)} """ for repo in repositories ) body = f"""

Repositories

Register Repository

Registry

{rows or ''}
NameStatusBranchSource
No repositories yet.
""" return page("Repositories", body) @router.get("/ui/search") def search_page( q: str = "", status: str = "", language: str = "", framework: str = "", ability: str = "", capability: str = "", service: RegistryService = Depends(get_service), ) -> HTMLResponse: results = ( service.search( q, status=status or None, language=language or None, framework=framework or None, ability=ability or None, capability=capability or None, ) if q.strip() else [] ) rows = "\n".join( f""" {escape(result.repository_name)} {escape(result.match_type)} {escape(result.match_name)} {render_search_context(asdict(result))} {escape(result.matched_field)} {result.confidence:.2f} """ for result in results ) empty = ( 'No matches.' if q.strip() else 'Enter a need, capability, or repository name.' ) body = f"""

Search

Repositories
Clear
{rows or empty}
RepositoryMatchNameFieldConfidence
""" return page("Search", body) @router.post("/ui/repos") def create_repository_from_form( url: str = Form(...), branch: str = Form("main"), service: RegistryService = Depends(get_service), ) -> RedirectResponse: repository = service.register_repository( url=url, branch=branch or "main", ) return RedirectResponse(f"/ui/repos/{repository.id}", status_code=303) @router.get("/ui/repos/{repository_id}") def repository_detail( repository_id: int, service: RegistryService = Depends(get_service), ) -> HTMLResponse: repository = service.get_repository(repository_id) runs = service.list_analysis_runs(repository_id) ability_map = service.ability_map(repository_id) decisions = service.list_review_decisions(repository_id) facts = service.list_observed_facts(repository_id) languages = sorted({fact.name for fact in facts if fact.kind == "language"}) frameworks = sorted({fact.name for fact in facts if fact.kind == "framework"}) run_rows = "\n".join( f""" #{run.id} {escape(run.status)} {escape(run.started_at)} {escape(run.error_message or '')} """ for run in runs ) body = f"""

{escape(repository.name)}

Back

{escape(repository.description or '')}

{escape(repository.status)} {escape(repository.url)}

{render_repository_facts(languages, frameworks)}

Repository Metadata

Run Analysis

Analysis Runs

{run_rows or ''}
RunStatusStartedError
No runs yet.

Approved Ability Map

{render_ability_map(asdict(ability_map), repository_id)}

Manual Registry Entry

Add Ability

Add Capability

Add Feature

Add Evidence

Review Decisions

{render_review_decisions(decisions)}
""" return page(repository.name, body) @router.post("/ui/repos/{repository_id}/edit") def edit_repository_from_form( repository_id: int, name: str = Form(...), description: str = Form(""), branch: str = Form("main"), service: RegistryService = Depends(get_service), ) -> RedirectResponse: service.update_repository( repository_id, name=name, description=description, branch=branch or "main", ) return RedirectResponse(f"/ui/repos/{repository_id}", status_code=303) @router.post("/ui/repos/{repository_id}/abilities") def create_ability_from_form( repository_id: int, name: str = Form(...), description: str = Form(""), confidence: float = Form(1.0), service: RegistryService = Depends(get_service), ) -> RedirectResponse: service.add_ability( repository_id, name=name, description=description, confidence=confidence, ) return RedirectResponse(f"/ui/repos/{repository_id}", status_code=303) @router.post("/ui/repos/{repository_id}/capabilities") def create_capability_from_form( repository_id: int, ability_id: int = Form(...), name: str = Form(...), description: str = Form(""), inputs: str = Form(""), outputs: str = Form(""), confidence: float = Form(1.0), service: RegistryService = Depends(get_service), ) -> RedirectResponse: service.add_capability( repository_id, ability_id, name=name, description=description, inputs=split_csv(inputs), outputs=split_csv(outputs), confidence=confidence, ) return RedirectResponse(f"/ui/repos/{repository_id}", status_code=303) @router.post("/ui/repos/{repository_id}/features") def create_feature_from_form( repository_id: int, capability_id: int = Form(...), name: str = Form(...), type: str = Form(...), location: str = Form(""), confidence: float = Form(1.0), service: RegistryService = Depends(get_service), ) -> RedirectResponse: service.add_feature( repository_id, capability_id, name=name, type=type, location=location, confidence=confidence, ) return RedirectResponse(f"/ui/repos/{repository_id}", status_code=303) @router.post("/ui/repos/{repository_id}/evidence") def create_evidence_from_form( repository_id: int, capability_id: int = Form(...), type: str = Form(...), reference: str = Form(...), strength: str = Form("medium"), service: RegistryService = Depends(get_service), ) -> RedirectResponse: service.add_evidence( repository_id, capability_id, type=type, reference=reference, strength=strength, ) return RedirectResponse(f"/ui/repos/{repository_id}", status_code=303) @router.post("/ui/repos/{repository_id}/abilities/{ability_id}/edit") def edit_ability_from_form( repository_id: int, ability_id: int, name: str = Form(...), description: str = Form(""), confidence: float = Form(1.0), service: RegistryService = Depends(get_service), ) -> RedirectResponse: service.update_ability( repository_id, ability_id, name=name, description=description, confidence=confidence, ) return RedirectResponse(f"/ui/repos/{repository_id}", status_code=303) @router.post("/ui/repos/{repository_id}/abilities/{ability_id}/delete") def delete_ability_from_form( repository_id: int, ability_id: int, service: RegistryService = Depends(get_service), ) -> RedirectResponse: service.delete_ability(repository_id, ability_id) return RedirectResponse(f"/ui/repos/{repository_id}", status_code=303) @router.post("/ui/repos/{repository_id}/capabilities/{capability_id}/edit") def edit_capability_from_form( repository_id: int, capability_id: int, name: str = Form(...), description: str = Form(""), inputs: str = Form(""), outputs: str = Form(""), confidence: float = Form(1.0), service: RegistryService = Depends(get_service), ) -> RedirectResponse: service.update_capability( repository_id, capability_id, name=name, description=description, inputs=split_csv(inputs), outputs=split_csv(outputs), confidence=confidence, ) return RedirectResponse(f"/ui/repos/{repository_id}", status_code=303) @router.post("/ui/repos/{repository_id}/capabilities/{capability_id}/delete") def delete_capability_from_form( repository_id: int, capability_id: int, service: RegistryService = Depends(get_service), ) -> RedirectResponse: service.delete_capability(repository_id, capability_id) return RedirectResponse(f"/ui/repos/{repository_id}", status_code=303) @router.post("/ui/repos/{repository_id}/features/{feature_id}/edit") def edit_feature_from_form( repository_id: int, feature_id: int, name: str = Form(...), type: str = Form(...), location: str = Form(""), confidence: float = Form(1.0), service: RegistryService = Depends(get_service), ) -> RedirectResponse: service.update_feature( repository_id, feature_id, name=name, type=type, location=location, confidence=confidence, ) return RedirectResponse(f"/ui/repos/{repository_id}", status_code=303) @router.post("/ui/repos/{repository_id}/features/{feature_id}/delete") def delete_feature_from_form( repository_id: int, feature_id: int, service: RegistryService = Depends(get_service), ) -> RedirectResponse: service.delete_feature(repository_id, feature_id) return RedirectResponse(f"/ui/repos/{repository_id}", status_code=303) @router.post("/ui/repos/{repository_id}/evidence/{evidence_id}/edit") def edit_evidence_from_form( repository_id: int, evidence_id: int, type: str = Form(...), reference: str = Form(...), strength: str = Form("medium"), service: RegistryService = Depends(get_service), ) -> RedirectResponse: service.update_evidence( repository_id, evidence_id, type=type, reference=reference, strength=strength, ) return RedirectResponse(f"/ui/repos/{repository_id}", status_code=303) @router.post("/ui/repos/{repository_id}/evidence/{evidence_id}/delete") def delete_evidence_from_form( repository_id: int, evidence_id: int, service: RegistryService = Depends(get_service), ) -> RedirectResponse: service.delete_evidence(repository_id, evidence_id) return RedirectResponse(f"/ui/repos/{repository_id}", status_code=303) @router.post("/ui/repos/{repository_id}/analysis-runs") def create_analysis_run_from_form( repository_id: int, source_path: str = Form(""), service: RegistryService = Depends(get_service), ) -> RedirectResponse: summary = service.analyze_repository( repository_id, source_path=source_path or None, ) return RedirectResponse( f"/ui/repos/{repository_id}/analysis-runs/{summary.analysis_run.id}", status_code=303, ) @router.get("/ui/repos/{repository_id}/analysis-runs/{analysis_run_id}") def analysis_run_detail( repository_id: int, analysis_run_id: int, service: RegistryService = Depends(get_service), ) -> HTMLResponse: repository = service.get_repository(repository_id) candidate_graph = service.candidate_graph(repository_id, analysis_run_id) facts = service.list_observed_facts(repository_id, analysis_run_id) decisions = service.list_review_decisions(repository_id, analysis_run_id) fact_rows = "\n".join( f""" {escape(fact.kind)} {escape(fact.name)} {escape(fact.path)} {escape(fact.value)} """ for fact in facts ) body = f"""

{escape(repository.name)} · Run #{analysis_run_id}

Repository

Candidate Graph

{render_candidate_graph(asdict(candidate_graph), repository_id, analysis_run_id)}

Observed Facts

{fact_rows or ''}
KindNamePathValue
No observed facts.

Review Decisions

{render_review_decisions(decisions)}
""" return page(f"{repository.name} Run {analysis_run_id}", body) @router.post("/ui/repos/{repository_id}/analysis-runs/{analysis_run_id}/candidate-graph/approve") def approve_candidate_graph_from_form( repository_id: int, analysis_run_id: int, service: RegistryService = Depends(get_service), ) -> RedirectResponse: service.approve_candidate_graph( repository_id, analysis_run_id, notes="Approved from web UI", ) return RedirectResponse(f"/ui/repos/{repository_id}", status_code=303) @router.post( "/ui/repos/{repository_id}/analysis-runs/{analysis_run_id}" "/candidate-abilities/{candidate_ability_id}/reject" ) def reject_candidate_ability_from_form( repository_id: int, analysis_run_id: int, candidate_ability_id: int, service: RegistryService = Depends(get_service), ) -> RedirectResponse: service.reject_candidate_ability( repository_id, analysis_run_id, candidate_ability_id, notes="Rejected from web UI", ) return RedirectResponse( f"/ui/repos/{repository_id}/analysis-runs/{analysis_run_id}", status_code=303, ) @router.post( "/ui/repos/{repository_id}/analysis-runs/{analysis_run_id}" "/candidate-capabilities/{candidate_capability_id}/reject" ) def reject_candidate_capability_from_form( repository_id: int, analysis_run_id: int, candidate_capability_id: int, service: RegistryService = Depends(get_service), ) -> RedirectResponse: service.reject_candidate_capability( repository_id, analysis_run_id, candidate_capability_id, notes="Rejected from web UI", ) return RedirectResponse( f"/ui/repos/{repository_id}/analysis-runs/{analysis_run_id}", status_code=303, ) @router.post( "/ui/repos/{repository_id}/analysis-runs/{analysis_run_id}" "/candidate-features/{candidate_feature_id}/reject" ) def reject_candidate_feature_from_form( repository_id: int, analysis_run_id: int, candidate_feature_id: int, service: RegistryService = Depends(get_service), ) -> RedirectResponse: service.reject_candidate_feature( repository_id, analysis_run_id, candidate_feature_id, notes="Rejected from web UI", ) return RedirectResponse( f"/ui/repos/{repository_id}/analysis-runs/{analysis_run_id}", status_code=303, ) @router.post( "/ui/repos/{repository_id}/analysis-runs/{analysis_run_id}" "/candidate-evidence/{candidate_evidence_id}/reject" ) def reject_candidate_evidence_from_form( repository_id: int, analysis_run_id: int, candidate_evidence_id: int, service: RegistryService = Depends(get_service), ) -> RedirectResponse: service.reject_candidate_evidence( repository_id, analysis_run_id, candidate_evidence_id, notes="Rejected from web UI", ) return RedirectResponse( f"/ui/repos/{repository_id}/analysis-runs/{analysis_run_id}", status_code=303, ) @router.post( "/ui/repos/{repository_id}/analysis-runs/{analysis_run_id}" "/candidate-abilities/{candidate_ability_id}/edit" ) def edit_candidate_ability_from_form( repository_id: int, analysis_run_id: int, candidate_ability_id: int, name: str = Form(...), description: str = Form(""), confidence: float = Form(...), service: RegistryService = Depends(get_service), ) -> RedirectResponse: service.edit_candidate_ability( repository_id, analysis_run_id, candidate_ability_id, name=name, description=description, confidence=confidence, notes="Edited from web UI", ) return RedirectResponse( f"/ui/repos/{repository_id}/analysis-runs/{analysis_run_id}", status_code=303, ) @router.post( "/ui/repos/{repository_id}/analysis-runs/{analysis_run_id}" "/candidate-capabilities/{candidate_capability_id}/edit" ) def edit_candidate_capability_from_form( repository_id: int, analysis_run_id: int, candidate_capability_id: int, name: str = Form(...), description: str = Form(""), confidence: float = Form(...), service: RegistryService = Depends(get_service), ) -> RedirectResponse: service.edit_candidate_capability( repository_id, analysis_run_id, candidate_capability_id, name=name, description=description, confidence=confidence, notes="Edited from web UI", ) return RedirectResponse( f"/ui/repos/{repository_id}/analysis-runs/{analysis_run_id}", status_code=303, ) @router.post( "/ui/repos/{repository_id}/analysis-runs/{analysis_run_id}" "/candidate-capabilities/{candidate_capability_id}/relink" ) def relink_candidate_capability_from_form( repository_id: int, analysis_run_id: int, candidate_capability_id: int, target_ability_id: int = Form(...), service: RegistryService = Depends(get_service), ) -> RedirectResponse: service.relink_candidate_capability( repository_id, analysis_run_id, candidate_capability_id, target_ability_id=target_ability_id, notes="Relinked from web UI", ) return RedirectResponse( f"/ui/repos/{repository_id}/analysis-runs/{analysis_run_id}", status_code=303, ) @router.post( "/ui/repos/{repository_id}/analysis-runs/{analysis_run_id}" "/candidate-features/{candidate_feature_id}/relink" ) def relink_candidate_feature_from_form( repository_id: int, analysis_run_id: int, candidate_feature_id: int, target_capability_id: int = Form(...), service: RegistryService = Depends(get_service), ) -> RedirectResponse: service.relink_candidate_feature( repository_id, analysis_run_id, candidate_feature_id, target_capability_id=target_capability_id, notes="Relinked from web UI", ) return RedirectResponse( f"/ui/repos/{repository_id}/analysis-runs/{analysis_run_id}", status_code=303, ) @router.post( "/ui/repos/{repository_id}/analysis-runs/{analysis_run_id}" "/candidate-evidence/{candidate_evidence_id}/relink" ) def relink_candidate_evidence_from_form( repository_id: int, analysis_run_id: int, candidate_evidence_id: int, target_capability_id: int = Form(...), service: RegistryService = Depends(get_service), ) -> RedirectResponse: service.relink_candidate_evidence( repository_id, analysis_run_id, candidate_evidence_id, target_capability_id=target_capability_id, notes="Relinked from web UI", ) return RedirectResponse( f"/ui/repos/{repository_id}/analysis-runs/{analysis_run_id}", status_code=303, ) @router.post( "/ui/repos/{repository_id}/analysis-runs/{analysis_run_id}" "/candidate-abilities/{source_ability_id}/merge" ) def merge_candidate_ability_from_form( repository_id: int, analysis_run_id: int, source_ability_id: int, target_ability_id: int = Form(...), service: RegistryService = Depends(get_service), ) -> RedirectResponse: service.merge_candidate_ability( repository_id, analysis_run_id, source_ability_id, target_ability_id=target_ability_id, notes="Merged from web UI", ) return RedirectResponse( f"/ui/repos/{repository_id}/analysis-runs/{analysis_run_id}", status_code=303, ) @router.post( "/ui/repos/{repository_id}/analysis-runs/{analysis_run_id}" "/candidate-capabilities/{source_capability_id}/merge" ) def merge_candidate_capability_from_form( repository_id: int, analysis_run_id: int, source_capability_id: int, target_capability_id: int = Form(...), service: RegistryService = Depends(get_service), ) -> RedirectResponse: service.merge_candidate_capability( repository_id, analysis_run_id, source_capability_id, target_capability_id=target_capability_id, notes="Merged from web UI", ) return RedirectResponse( f"/ui/repos/{repository_id}/analysis-runs/{analysis_run_id}", status_code=303, ) @router.post( "/ui/repos/{repository_id}/analysis-runs/{analysis_run_id}" "/candidate-features/{source_feature_id}/merge" ) def merge_candidate_feature_from_form( repository_id: int, analysis_run_id: int, source_feature_id: int, target_feature_id: int = Form(...), service: RegistryService = Depends(get_service), ) -> RedirectResponse: service.merge_candidate_feature( repository_id, analysis_run_id, source_feature_id, target_feature_id=target_feature_id, notes="Merged from web UI", ) return RedirectResponse( f"/ui/repos/{repository_id}/analysis-runs/{analysis_run_id}", status_code=303, ) @router.post( "/ui/repos/{repository_id}/analysis-runs/{analysis_run_id}" "/candidate-evidence/{source_evidence_id}/merge" ) def merge_candidate_evidence_from_form( repository_id: int, analysis_run_id: int, source_evidence_id: int, target_evidence_id: int = Form(...), service: RegistryService = Depends(get_service), ) -> RedirectResponse: service.merge_candidate_evidence( repository_id, analysis_run_id, source_evidence_id, target_evidence_id=target_evidence_id, notes="Merged from web UI", ) return RedirectResponse( f"/ui/repos/{repository_id}/analysis-runs/{analysis_run_id}", status_code=303, ) def render_candidate_graph(graph: dict, repository_id: int, analysis_run_id: int) -> str: abilities = graph.get("abilities", []) if not abilities: return '

No candidates generated.

' items = [] for ability in abilities: capabilities = "".join( render_candidate_capability(capability, repository_id, analysis_run_id) for capability in ability["capabilities"] ) items.append( f"""
  • {escape(ability['name'])} ID {ability['id']} {escape(ability['status'])} {ability['confidence']:.2f} {render_candidate_ability_actions(ability, repository_id, analysis_run_id)}

    {escape(ability['description'])}

    {render_candidate_edit_form('candidate-abilities', ability, repository_id, analysis_run_id)} {render_candidate_merge_form('candidate-abilities', ability, repository_id, analysis_run_id, 'target_ability_id', 'Merge into ability ID')} {render_sources(ability['source_refs'])}
  • """ ) return f'
    ' def render_repository_facts(languages: list[str], frameworks: list[str]) -> str: if not languages and not frameworks: return "" language_pills = "".join( f'Language: {escape(language)}' for language in languages ) framework_pills = "".join( f'Framework: {escape(framework)}' for framework in frameworks ) return f'

    {language_pills}{framework_pills}

    ' def split_csv(value: str) -> list[str]: return [item.strip() for item in value.split(",") if item.strip()] def render_review_decisions(decisions: list) -> str: if not decisions: return '

    No review decisions yet.

    ' rows = "\n".join( f""" {escape(decision.action)} {escape(decision.created_at)} {escape(decision.notes)} """ for decision in decisions ) return f""" {rows}
    ActionCreatedNotes
    """ def render_candidate_ability_actions( ability: dict, repository_id: int, analysis_run_id: int, ) -> str: if ability["status"] != "candidate": return "" action = ( f"/ui/repos/{repository_id}/analysis-runs/{analysis_run_id}" f"/candidate-abilities/{ability['id']}/reject" ) return f"""
    """ def render_candidate_edit_form( collection: str, candidate: dict, repository_id: int, analysis_run_id: int, ) -> str: if candidate["status"] != "candidate": return "" action = ( f"/ui/repos/{repository_id}/analysis-runs/{analysis_run_id}" f"/{collection}/{candidate['id']}/edit" ) confidence = f"{candidate['confidence']:.2f}" return f"""
    """ def render_candidate_capability( capability: dict, repository_id: int, analysis_run_id: int, ) -> str: features = "".join( render_candidate_feature(feature, repository_id, analysis_run_id) for feature in capability["features"] ) evidence = "".join( render_candidate_evidence(item, repository_id, analysis_run_id) for item in capability["evidence"] ) return f"""
  • {escape(capability['name'])} ID {capability['id']} {escape(capability['status'])} {capability['confidence']:.2f} {render_candidate_reject_form('candidate-capabilities', capability, repository_id, analysis_run_id)}

    {escape(capability['description'])}

    {render_candidate_edit_form('candidate-capabilities', capability, repository_id, analysis_run_id)} {render_candidate_relink_form('candidate-capabilities', capability, repository_id, analysis_run_id, 'target_ability_id', 'Target ability ID')} {render_candidate_merge_form('candidate-capabilities', capability, repository_id, analysis_run_id, 'target_capability_id', 'Merge into capability ID')} {render_sources(capability['source_refs'])}

    Features

    Evidence

  • """ def render_candidate_feature( feature: dict, repository_id: int, analysis_run_id: int, ) -> str: return f"""
  • {escape(feature["name"])} ID {feature["id"]} {escape(feature["status"])} {escape(feature["type"])} {escape(feature["location"])} {render_candidate_reject_form('candidate-features', feature, repository_id, analysis_run_id)} {render_candidate_relink_form('candidate-features', feature, repository_id, analysis_run_id, 'target_capability_id', 'Target capability ID')} {render_candidate_merge_form('candidate-features', feature, repository_id, analysis_run_id, 'target_feature_id', 'Merge into feature ID')}
  • """ def render_candidate_evidence( evidence: dict, repository_id: int, analysis_run_id: int, ) -> str: return f"""
  • {escape(evidence["type"])} ID {evidence["id"]} {escape(evidence["status"])} {escape(evidence["strength"])} {escape(evidence["reference"])} {render_candidate_reject_form('candidate-evidence', evidence, repository_id, analysis_run_id)} {render_candidate_relink_form('candidate-evidence', evidence, repository_id, analysis_run_id, 'target_capability_id', 'Target capability ID')} {render_candidate_merge_form('candidate-evidence', evidence, repository_id, analysis_run_id, 'target_evidence_id', 'Merge into evidence ID')}
  • """ def render_candidate_reject_form( collection: str, candidate: dict, repository_id: int, analysis_run_id: int, ) -> str: if candidate["status"] != "candidate": return "" action = ( f"/ui/repos/{repository_id}/analysis-runs/{analysis_run_id}" f"/{collection}/{candidate['id']}/reject" ) return f"""
    """ def render_candidate_relink_form( collection: str, candidate: dict, repository_id: int, analysis_run_id: int, field_name: str, label: str, ) -> str: if candidate["status"] != "candidate": return "" action = ( f"/ui/repos/{repository_id}/analysis-runs/{analysis_run_id}" f"/{collection}/{candidate['id']}/relink" ) return f"""
    """ def render_candidate_merge_form( collection: str, candidate: dict, repository_id: int, analysis_run_id: int, field_name: str, label: str, ) -> str: if candidate["status"] != "candidate": return "" action = ( f"/ui/repos/{repository_id}/analysis-runs/{analysis_run_id}" f"/{collection}/{candidate['id']}/merge" ) return f"""
    """ def render_ability_map(ability_map: dict, repository_id: int) -> str: abilities = ability_map.get("abilities", []) if not abilities: return '

    No approved entries yet.

    ' items = [] for ability in abilities: capabilities = [] for capability in ability["capabilities"]: features = "".join( render_approved_feature(feature, repository_id) for feature in capability["features"] ) evidence = "".join( render_approved_evidence(item, repository_id) for item in capability["evidence"] ) capabilities.append( f"""
  • {escape(capability['name'])} ID {capability['id']}

    {escape(capability['description'])}

    {render_approved_capability_forms(capability, repository_id)}
  • """ ) items.append( f"""
  • {escape(ability['name'])} ID {ability['id']}

    {escape(ability['description'])}

    {render_approved_ability_forms(ability, repository_id)}
  • """ ) return f'
    ' def render_approved_ability_forms(ability: dict, repository_id: int) -> str: return f"""
    """ def render_approved_capability_forms(capability: dict, repository_id: int) -> str: inputs = ", ".join(capability["inputs"]) outputs = ", ".join(capability["outputs"]) return f"""
    """ def render_approved_feature(feature: dict, repository_id: int) -> str: return f"""
  • {escape(feature["name"])} {escape(feature["type"])} {escape(feature["location"])} {render_sources(feature.get("source_refs", []))}
  • """ def render_approved_evidence(evidence: dict, repository_id: int) -> str: return f"""
  • {escape(evidence["type"])} {escape(evidence["strength"])} {escape(evidence["reference"])} {render_sources(evidence.get("source_refs", []))}
  • """ def search_result_href(result: dict) -> str: href = f"/ui/repos/{result['repository_id']}" if result.get("capability_id"): return f"{href}#capability-{result['capability_id']}" if result.get("ability_id"): return f"{href}#ability-{result['ability_id']}" return href def render_sources(source_refs: list[dict]) -> str: if not source_refs: return "" sources = ", ".join( f'{escape(ref["kind"])}:{escape(source_ref_label(ref))}' for ref in source_refs[:5] ) if len(source_refs) > 5: sources += f' +{len(source_refs) - 5} more' return f"

    {sources}

    " def source_ref_label(ref: dict) -> str: label = ref["path"] or ref["name"] if ref.get("line"): label = f"{label}:{ref['line']}" return label def render_search_context(result: dict) -> str: details = [] if result.get("ability_name"): details.append(f"Ability: {escape(result['ability_name'])}") if result.get("capability_name"): details.append(f"Capability: {escape(result['capability_name'])}") if result.get("evidence_level"): details.append(f"Evidence: {escape(result['evidence_level'])}") if result.get("source_reference"): details.append(f"Source: {escape(result['source_reference'])}") if result.get("match_description"): details.append(escape(result["match_description"])) if not details: return "" return f'

    {" · ".join(details)}

    '