first review workflow slice

This commit is contained in:
2026-04-25 22:46:22 +02:00
parent 519b7726e7
commit 8f94c38309
7 changed files with 198 additions and 0 deletions

View File

@@ -103,6 +103,67 @@ class RegistryService:
def candidate_graph(self, repository_id: int, analysis_run_id: int) -> CandidateGraph:
return self.store.get_candidate_graph(repository_id, analysis_run_id)
def approve_candidate_graph(
self,
repository_id: int,
analysis_run_id: int,
*,
notes: str = "",
) -> RepositoryAbilityMap:
graph = self.store.get_candidate_graph(repository_id, analysis_run_id)
pending_abilities = [
ability for ability in graph.abilities if ability.status == "candidate"
]
for ability in pending_abilities:
approved_ability_id = self.store.create_ability(
repository_id,
name=ability.name,
description=ability.description,
confidence=ability.confidence,
)
for capability in ability.capabilities:
approved_capability_id = self.store.create_capability(
repository_id,
approved_ability_id,
name=capability.name,
description=capability.description,
inputs=capability.inputs,
outputs=capability.outputs,
confidence=capability.confidence,
)
for feature in capability.features:
self.store.create_feature(
repository_id,
approved_capability_id,
name=feature.name,
type=feature.type,
location=feature.location,
confidence=feature.confidence,
)
for evidence in capability.evidence:
self.store.create_evidence(
repository_id,
approved_capability_id,
type=evidence.type,
reference=evidence.reference,
strength=evidence.strength,
)
if pending_abilities:
self.store.mark_candidate_graph_status(
repository_id,
analysis_run_id,
"approved",
)
self.store.create_review_decision(
repository_id,
analysis_run_id,
action="approve_candidate_graph",
notes=notes,
)
self.store.update_repository_status(repository_id, "indexed")
return self.store.get_ability_map(repository_id)
def add_ability(
self,
repository_id: int,

View File

@@ -361,6 +361,47 @@ class RegistryStore:
abilities=abilities,
)
def mark_candidate_graph_status(
self,
repository_id: int,
analysis_run_id: int,
status: str,
) -> None:
with self.connect() as connection:
for table in (
"candidate_abilities",
"candidate_capabilities",
"candidate_features",
"candidate_evidence",
):
connection.execute(
f"""
UPDATE {table}
SET status = ?
WHERE repository_id = ? AND analysis_run_id = ?
""",
(status, repository_id, analysis_run_id),
)
def create_review_decision(
self,
repository_id: int,
analysis_run_id: int,
*,
action: str,
notes: str = "",
) -> int:
with self.connect() as connection:
cursor = connection.execute(
"""
INSERT INTO review_decisions
(repository_id, analysis_run_id, action, notes)
VALUES (?, ?, ?, ?)
""",
(repository_id, analysis_run_id, action, notes),
)
return int(cursor.lastrowid)
def fail_analysis_run(
self,
repository_id: int,

View File

@@ -69,6 +69,10 @@ class AnalysisRunCreate(BaseModel):
source_path: str | None = None
class CandidateGraphApproval(BaseModel):
notes: str = ""
app = FastAPI(title="Repository Ability Registry", version="0.1.0")
@@ -161,6 +165,25 @@ def get_candidate_graph(
raise HTTPException(status_code=404, detail=str(exc)) from exc
@app.post("/repos/{repository_id}/analysis-runs/{analysis_run_id}/candidate-graph/approve")
def approve_candidate_graph(
repository_id: int,
analysis_run_id: int,
payload: CandidateGraphApproval,
service: RegistryService = Depends(get_service),
) -> dict[str, object]:
try:
return asdict(
service.approve_candidate_graph(
repository_id,
analysis_run_id,
notes=payload.notes,
)
)
except NotFoundError as exc:
raise HTTPException(status_code=404, detail=str(exc)) from exc
@app.post("/repos/{repository_id}/abilities", status_code=201)
def create_ability(
repository_id: int,