generated from coulomb/repo-seed
API contract hardening
This commit is contained in:
@@ -208,6 +208,8 @@ Search results include `match_type`, `matched_field`, `confidence`,
|
|||||||
source/evidence context when the match comes from implementation evidence.
|
source/evidence context when the match comes from implementation evidence.
|
||||||
The generated OpenAPI schema at `/openapi.json` and docs at `/docs` include
|
The generated OpenAPI schema at `/openapi.json` and docs at `/docs` include
|
||||||
typed response schemas and examples for the main agent-facing responses.
|
typed response schemas and examples for the main agent-facing responses.
|
||||||
|
The API compatibility policy is documented in `docs/api-contract.md`; stable
|
||||||
|
agent-facing paths are guarded by an OpenAPI contract snapshot test.
|
||||||
|
|
||||||
Discovery helpers are available for production-readiness workflows that compare
|
Discovery helpers are available for production-readiness workflows that compare
|
||||||
approved profiles, find simple capability gaps, or export a registry entry:
|
approved profiles, find simple capability gaps, or export a registry entry:
|
||||||
|
|||||||
34
docs/api-contract.md
Normal file
34
docs/api-contract.md
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# API Contract Policy
|
||||||
|
|
||||||
|
The public HTTP API is currently version `0.1.0`. Until a versioned path such as
|
||||||
|
`/api/v1` is introduced, compatibility is governed by the OpenAPI contract
|
||||||
|
published at `/openapi.json` and covered by the test suite.
|
||||||
|
|
||||||
|
Agent-facing endpoints are considered stable when their path, method, tag, and
|
||||||
|
success response model appear in the OpenAPI contract snapshot test. A change to
|
||||||
|
one of those fields is allowed, but it must update the snapshot test in the same
|
||||||
|
change so the break is explicit during review.
|
||||||
|
|
||||||
|
Compatible changes include:
|
||||||
|
|
||||||
|
- adding optional request fields with defaults
|
||||||
|
- adding response fields that clients may ignore
|
||||||
|
- adding new endpoints
|
||||||
|
- adding new examples or descriptions
|
||||||
|
|
||||||
|
Breaking changes include:
|
||||||
|
|
||||||
|
- removing or renaming an endpoint
|
||||||
|
- changing an endpoint method or tag
|
||||||
|
- changing the success response model
|
||||||
|
- removing or renaming a response field
|
||||||
|
- making an optional request field required
|
||||||
|
- changing the shape of common error responses
|
||||||
|
|
||||||
|
Common application errors use:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"detail": "repository 999 was not found"}
|
||||||
|
```
|
||||||
|
|
||||||
|
Validation errors remain FastAPI's standard `422` validation response.
|
||||||
@@ -40,6 +40,7 @@ from repo_registry.web_api.schemas import (
|
|||||||
ContentChunkResponse,
|
ContentChunkResponse,
|
||||||
EvidenceCreate,
|
EvidenceCreate,
|
||||||
EvidenceUpdate,
|
EvidenceUpdate,
|
||||||
|
ErrorResponse,
|
||||||
FeatureCreate,
|
FeatureCreate,
|
||||||
FeatureUpdate,
|
FeatureUpdate,
|
||||||
IdResponse,
|
IdResponse,
|
||||||
@@ -116,6 +117,10 @@ app = FastAPI(
|
|||||||
version="0.1.0",
|
version="0.1.0",
|
||||||
description=API_DESCRIPTION,
|
description=API_DESCRIPTION,
|
||||||
openapi_tags=OPENAPI_TAGS,
|
openapi_tags=OPENAPI_TAGS,
|
||||||
|
responses={
|
||||||
|
400: {"model": ErrorResponse, "description": "Bad request."},
|
||||||
|
404: {"model": ErrorResponse, "description": "Resource not found."},
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,19 @@ SOURCE_REFERENCE_EXAMPLE = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ErrorResponse(BaseModel):
|
||||||
|
detail: str
|
||||||
|
|
||||||
|
model_config = {
|
||||||
|
"json_schema_extra": {
|
||||||
|
"examples": [
|
||||||
|
{"detail": "repository 999 was not found"},
|
||||||
|
{"detail": "target candidate must be different from source"},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class RepositoryCreate(BaseModel):
|
class RepositoryCreate(BaseModel):
|
||||||
url: str
|
url: str
|
||||||
name: str | None = None
|
name: str | None = None
|
||||||
@@ -540,6 +553,61 @@ class AnalysisRunDiffResponse(BaseModel):
|
|||||||
candidates: AnalysisRunDiffSectionResponse
|
candidates: AnalysisRunDiffSectionResponse
|
||||||
approved_entries: AnalysisRunDiffSectionResponse
|
approved_entries: AnalysisRunDiffSectionResponse
|
||||||
|
|
||||||
|
model_config = {
|
||||||
|
"json_schema_extra": {
|
||||||
|
"examples": [
|
||||||
|
{
|
||||||
|
"repository": REPOSITORY_EXAMPLE,
|
||||||
|
"base_run": {**ANALYSIS_RUN_EXAMPLE, "id": 1},
|
||||||
|
"target_run": {**ANALYSIS_RUN_EXAMPLE, "id": 2},
|
||||||
|
"facts": {
|
||||||
|
"added": [
|
||||||
|
{
|
||||||
|
"change_type": "added",
|
||||||
|
"item_type": "fact",
|
||||||
|
"key": "api_route:src/routes/status.py:GET /status",
|
||||||
|
"target": {
|
||||||
|
"kind": "api_route",
|
||||||
|
"path": "src/routes/status.py",
|
||||||
|
"name": "GET /status",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"removed": [],
|
||||||
|
"changed": [],
|
||||||
|
"weakened": [],
|
||||||
|
},
|
||||||
|
"chunks": {
|
||||||
|
"added": [],
|
||||||
|
"removed": [],
|
||||||
|
"changed": [],
|
||||||
|
"weakened": [],
|
||||||
|
},
|
||||||
|
"candidates": {
|
||||||
|
"added": [],
|
||||||
|
"removed": [],
|
||||||
|
"changed": [],
|
||||||
|
"weakened": [
|
||||||
|
{
|
||||||
|
"change_type": "weakened",
|
||||||
|
"item_type": "capability",
|
||||||
|
"key": "classify incoming email",
|
||||||
|
"base": {"confidence": 0.9},
|
||||||
|
"target": {"confidence": 0.62},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"approved_entries": {
|
||||||
|
"added": [],
|
||||||
|
"removed": [],
|
||||||
|
"changed": [],
|
||||||
|
"weakened": [],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class EvidenceResponse(BaseModel):
|
class EvidenceResponse(BaseModel):
|
||||||
id: int
|
id: int
|
||||||
@@ -760,6 +828,48 @@ class RepositoryComparisonResponse(BaseModel):
|
|||||||
abilities: list[ComparedAbilityResponse]
|
abilities: list[ComparedAbilityResponse]
|
||||||
unique_capabilities: list[UniqueCapabilityResponse]
|
unique_capabilities: list[UniqueCapabilityResponse]
|
||||||
|
|
||||||
|
model_config = {
|
||||||
|
"json_schema_extra": {
|
||||||
|
"examples": [
|
||||||
|
{
|
||||||
|
"repositories": [
|
||||||
|
REPOSITORY_EXAMPLE,
|
||||||
|
{**REPOSITORY_EXAMPLE, "id": 2, "name": "InboxWorker"},
|
||||||
|
],
|
||||||
|
"abilities": [
|
||||||
|
{
|
||||||
|
"name": "Business Email Routing",
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"repository_id": 1,
|
||||||
|
"repository_name": "MailRouter",
|
||||||
|
"confidence": 0.92,
|
||||||
|
"confidence_label": "high",
|
||||||
|
"capabilities": [
|
||||||
|
{
|
||||||
|
"name": "Classify Incoming Email",
|
||||||
|
"confidence": 0.88,
|
||||||
|
"confidence_label": "high",
|
||||||
|
"evidence_count": 2,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"unique_capabilities": [
|
||||||
|
{
|
||||||
|
"repository_id": 1,
|
||||||
|
"repository_name": "MailRouter",
|
||||||
|
"ability_name": "Business Email Routing",
|
||||||
|
"capability_name": "Classify Incoming Email",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class CapabilityGapMatchResponse(BaseModel):
|
class CapabilityGapMatchResponse(BaseModel):
|
||||||
capability: str
|
capability: str
|
||||||
@@ -787,3 +897,37 @@ class CapabilityGapResponse(BaseModel):
|
|||||||
missing_capabilities: list[str]
|
missing_capabilities: list[str]
|
||||||
weakly_evidenced_capabilities: list[WeakCapabilityEvidenceResponse]
|
weakly_evidenced_capabilities: list[WeakCapabilityEvidenceResponse]
|
||||||
duplicate_capabilities: list[DuplicateCapabilityResponse]
|
duplicate_capabilities: list[DuplicateCapabilityResponse]
|
||||||
|
|
||||||
|
model_config = {
|
||||||
|
"json_schema_extra": {
|
||||||
|
"examples": [
|
||||||
|
{
|
||||||
|
"desired_ability": "Business Email Routing",
|
||||||
|
"matched_capabilities": [
|
||||||
|
{
|
||||||
|
"capability": "Classify Incoming Email",
|
||||||
|
"repositories": ["MailRouter"],
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"missing_capabilities": ["Route Email to Team"],
|
||||||
|
"weakly_evidenced_capabilities": [
|
||||||
|
{
|
||||||
|
"capability": "Classify Incoming Email",
|
||||||
|
"repository_id": 1,
|
||||||
|
"repository_name": "MailRouter",
|
||||||
|
"evidence_count": 0,
|
||||||
|
"strongest_evidence": None,
|
||||||
|
"confidence": 0.62,
|
||||||
|
"confidence_label": "medium",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"duplicate_capabilities": [
|
||||||
|
{
|
||||||
|
"capability": "Classify Incoming Email",
|
||||||
|
"repositories": ["MailRouter", "InboxWorker"],
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -52,6 +52,235 @@ def test_openapi_groups_agent_facing_endpoints():
|
|||||||
assert (
|
assert (
|
||||||
"/repos/{repository_id}/analysis-runs/{analysis_run_id}/changes/approve"
|
"/repos/{repository_id}/analysis-runs/{analysis_run_id}/changes/approve"
|
||||||
) in schema["paths"]
|
) in schema["paths"]
|
||||||
|
assert components["ErrorResponse"]["examples"][0]["detail"]
|
||||||
|
assert (
|
||||||
|
schema["paths"]["/repos/{repository_id}"]["get"]["responses"]["404"][
|
||||||
|
"content"
|
||||||
|
]["application/json"]["schema"]["$ref"]
|
||||||
|
).endswith("/ErrorResponse")
|
||||||
|
assert (
|
||||||
|
schema["paths"][
|
||||||
|
"/repos/{repository_id}/analysis-runs/{base_analysis_run_id}/diff/"
|
||||||
|
"{target_analysis_run_id}"
|
||||||
|
]["get"]["responses"]["200"]["content"]["application/json"]["schema"]["$ref"]
|
||||||
|
).endswith("/AnalysisRunDiffResponse")
|
||||||
|
assert (
|
||||||
|
components["RepositoryComparisonResponse"]["examples"][0][
|
||||||
|
"unique_capabilities"
|
||||||
|
][0]["capability_name"]
|
||||||
|
== "Classify Incoming Email"
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
components["CapabilityGapResponse"]["examples"][0]["missing_capabilities"][0]
|
||||||
|
== "Route Email to Team"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_openapi_contract_snapshot_for_stable_agent_paths():
|
||||||
|
client = TestClient(app)
|
||||||
|
|
||||||
|
schema = client.get("/openapi.json").json()
|
||||||
|
|
||||||
|
def success_schema(operation):
|
||||||
|
responses = operation["responses"]
|
||||||
|
status_code = "201" if "201" in responses else "200"
|
||||||
|
content = responses.get(status_code, {}).get("content", {})
|
||||||
|
if "application/json" in content:
|
||||||
|
response_schema = content["application/json"]["schema"]
|
||||||
|
if "$ref" in response_schema:
|
||||||
|
return response_schema["$ref"].split("/")[-1]
|
||||||
|
if response_schema.get("type") == "array":
|
||||||
|
items = response_schema["items"]
|
||||||
|
if "$ref" in items:
|
||||||
|
return f"list[{items['$ref'].split('/')[-1]}]"
|
||||||
|
return "list"
|
||||||
|
return response_schema.get("type")
|
||||||
|
if "application/x-yaml" in content:
|
||||||
|
return "application/x-yaml"
|
||||||
|
return None
|
||||||
|
|
||||||
|
stable_contract = {
|
||||||
|
path: {
|
||||||
|
method: {
|
||||||
|
"tags": operation["tags"],
|
||||||
|
"success_schema": success_schema(operation),
|
||||||
|
}
|
||||||
|
for method, operation in sorted(methods.items())
|
||||||
|
}
|
||||||
|
for path, methods in sorted(schema["paths"].items())
|
||||||
|
if not path.startswith("/ui")
|
||||||
|
}
|
||||||
|
|
||||||
|
assert stable_contract == {
|
||||||
|
"/abilities": {
|
||||||
|
"get": {"tags": ["search"], "success_schema": "list[AbilitySummaryResponse]"}
|
||||||
|
},
|
||||||
|
"/capabilities": {
|
||||||
|
"get": {
|
||||||
|
"tags": ["search"],
|
||||||
|
"success_schema": "list[CapabilitySummaryResponse]",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/capability-gaps": {
|
||||||
|
"post": {"tags": ["discovery"], "success_schema": "CapabilityGapResponse"}
|
||||||
|
},
|
||||||
|
"/health": {"get": {"tags": ["health"], "success_schema": "object"}},
|
||||||
|
"/repos": {
|
||||||
|
"get": {"tags": ["repositories"], "success_schema": "list[RepositoryResponse]"},
|
||||||
|
"post": {"tags": ["repositories"], "success_schema": "RepositoryResponse"},
|
||||||
|
},
|
||||||
|
"/repos/{repository_id}": {
|
||||||
|
"delete": {"tags": ["repositories"], "success_schema": None},
|
||||||
|
"get": {"tags": ["repositories"], "success_schema": "RepositoryResponse"},
|
||||||
|
"patch": {"tags": ["repositories"], "success_schema": "RepositoryResponse"},
|
||||||
|
},
|
||||||
|
"/repos/{repository_id}/abilities": {
|
||||||
|
"post": {"tags": ["registry"], "success_schema": "IdResponse"}
|
||||||
|
},
|
||||||
|
"/repos/{repository_id}/abilities/{ability_id}": {
|
||||||
|
"delete": {
|
||||||
|
"tags": ["registry"],
|
||||||
|
"success_schema": "RepositoryAbilityMapResponse",
|
||||||
|
},
|
||||||
|
"patch": {
|
||||||
|
"tags": ["registry"],
|
||||||
|
"success_schema": "RepositoryAbilityMapResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"/repos/{repository_id}/ability-map": {
|
||||||
|
"get": {"tags": ["registry"], "success_schema": "RepositoryAbilityMapResponse"}
|
||||||
|
},
|
||||||
|
"/repos/{repository_id}/analysis-runs": {
|
||||||
|
"get": {"tags": ["analysis"], "success_schema": "list[AnalysisRunResponse]"},
|
||||||
|
"post": {"tags": ["analysis"], "success_schema": "ScanSummaryResponse"},
|
||||||
|
},
|
||||||
|
"/repos/{repository_id}/analysis-runs/{analysis_run_id}": {
|
||||||
|
"get": {"tags": ["analysis"], "success_schema": "AnalysisRunResponse"}
|
||||||
|
},
|
||||||
|
"/repos/{repository_id}/analysis-runs/{analysis_run_id}/candidate-graph": {
|
||||||
|
"get": {"tags": ["review"], "success_schema": "CandidateGraphResponse"}
|
||||||
|
},
|
||||||
|
"/repos/{repository_id}/analysis-runs/{analysis_run_id}/candidate-graph/approve": {
|
||||||
|
"post": {
|
||||||
|
"tags": ["review"],
|
||||||
|
"success_schema": "RepositoryAbilityMapResponse",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/repos/{repository_id}/analysis-runs/{analysis_run_id}/changes/approve": {
|
||||||
|
"post": {
|
||||||
|
"tags": ["review"],
|
||||||
|
"success_schema": "RepositoryAbilityMapResponse",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/repos/{repository_id}/analysis-runs/{analysis_run_id}/content-chunks": {
|
||||||
|
"get": {"tags": ["analysis"], "success_schema": "list[ContentChunkResponse]"}
|
||||||
|
},
|
||||||
|
"/repos/{repository_id}/analysis-runs/{analysis_run_id}/review-decisions": {
|
||||||
|
"get": {"tags": ["review"], "success_schema": "list[ReviewDecisionResponse]"}
|
||||||
|
},
|
||||||
|
"/repos/{repository_id}/analysis-runs/{base_analysis_run_id}/diff/{target_analysis_run_id}": {
|
||||||
|
"get": {"tags": ["review"], "success_schema": "AnalysisRunDiffResponse"}
|
||||||
|
},
|
||||||
|
"/repos/{repository_id}/analysis-runs/{analysis_run_id}/candidate-abilities/{candidate_ability_id}": {
|
||||||
|
"patch": {"tags": ["review"], "success_schema": "CandidateGraphResponse"}
|
||||||
|
},
|
||||||
|
"/repos/{repository_id}/analysis-runs/{analysis_run_id}/candidate-abilities/{candidate_ability_id}/reject": {
|
||||||
|
"post": {"tags": ["review"], "success_schema": "CandidateGraphResponse"}
|
||||||
|
},
|
||||||
|
"/repos/{repository_id}/analysis-runs/{analysis_run_id}/candidate-abilities/{source_ability_id}/merge": {
|
||||||
|
"post": {"tags": ["review"], "success_schema": "CandidateGraphResponse"}
|
||||||
|
},
|
||||||
|
"/repos/{repository_id}/analysis-runs/{analysis_run_id}/candidate-capabilities/{candidate_capability_id}": {
|
||||||
|
"patch": {"tags": ["review"], "success_schema": "CandidateGraphResponse"}
|
||||||
|
},
|
||||||
|
"/repos/{repository_id}/analysis-runs/{analysis_run_id}/candidate-capabilities/{candidate_capability_id}/reject": {
|
||||||
|
"post": {"tags": ["review"], "success_schema": "CandidateGraphResponse"}
|
||||||
|
},
|
||||||
|
"/repos/{repository_id}/analysis-runs/{analysis_run_id}/candidate-capabilities/{candidate_capability_id}/relink": {
|
||||||
|
"post": {"tags": ["review"], "success_schema": "CandidateGraphResponse"}
|
||||||
|
},
|
||||||
|
"/repos/{repository_id}/analysis-runs/{analysis_run_id}/candidate-capabilities/{source_capability_id}/merge": {
|
||||||
|
"post": {"tags": ["review"], "success_schema": "CandidateGraphResponse"}
|
||||||
|
},
|
||||||
|
"/repos/{repository_id}/analysis-runs/{analysis_run_id}/candidate-evidence/{candidate_evidence_id}/reject": {
|
||||||
|
"post": {"tags": ["review"], "success_schema": "CandidateGraphResponse"}
|
||||||
|
},
|
||||||
|
"/repos/{repository_id}/analysis-runs/{analysis_run_id}/candidate-evidence/{candidate_evidence_id}/relink": {
|
||||||
|
"post": {"tags": ["review"], "success_schema": "CandidateGraphResponse"}
|
||||||
|
},
|
||||||
|
"/repos/{repository_id}/analysis-runs/{analysis_run_id}/candidate-evidence/{source_evidence_id}/merge": {
|
||||||
|
"post": {"tags": ["review"], "success_schema": "CandidateGraphResponse"}
|
||||||
|
},
|
||||||
|
"/repos/{repository_id}/analysis-runs/{analysis_run_id}/candidate-features/{candidate_feature_id}/reject": {
|
||||||
|
"post": {"tags": ["review"], "success_schema": "CandidateGraphResponse"}
|
||||||
|
},
|
||||||
|
"/repos/{repository_id}/analysis-runs/{analysis_run_id}/candidate-features/{candidate_feature_id}/relink": {
|
||||||
|
"post": {"tags": ["review"], "success_schema": "CandidateGraphResponse"}
|
||||||
|
},
|
||||||
|
"/repos/{repository_id}/analysis-runs/{analysis_run_id}/candidate-features/{source_feature_id}/merge": {
|
||||||
|
"post": {"tags": ["review"], "success_schema": "CandidateGraphResponse"}
|
||||||
|
},
|
||||||
|
"/repos/{repository_id}/capabilities": {
|
||||||
|
"post": {"tags": ["registry"], "success_schema": "IdResponse"}
|
||||||
|
},
|
||||||
|
"/repos/{repository_id}/capabilities/{capability_id}": {
|
||||||
|
"delete": {
|
||||||
|
"tags": ["registry"],
|
||||||
|
"success_schema": "RepositoryAbilityMapResponse",
|
||||||
|
},
|
||||||
|
"patch": {
|
||||||
|
"tags": ["registry"],
|
||||||
|
"success_schema": "RepositoryAbilityMapResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"/repos/{repository_id}/content-chunks": {
|
||||||
|
"get": {"tags": ["analysis"], "success_schema": "list[ContentChunkResponse]"}
|
||||||
|
},
|
||||||
|
"/repos/{repository_id}/evidence": {
|
||||||
|
"post": {"tags": ["registry"], "success_schema": "IdResponse"}
|
||||||
|
},
|
||||||
|
"/repos/{repository_id}/evidence/{evidence_id}": {
|
||||||
|
"delete": {
|
||||||
|
"tags": ["registry"],
|
||||||
|
"success_schema": "RepositoryAbilityMapResponse",
|
||||||
|
},
|
||||||
|
"patch": {
|
||||||
|
"tags": ["registry"],
|
||||||
|
"success_schema": "RepositoryAbilityMapResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"/repos/{repository_id}/export": {
|
||||||
|
"get": {"tags": ["discovery"], "success_schema": "application/x-yaml"}
|
||||||
|
},
|
||||||
|
"/repos/{repository_id}/features": {
|
||||||
|
"post": {"tags": ["registry"], "success_schema": "IdResponse"}
|
||||||
|
},
|
||||||
|
"/repos/{repository_id}/features/{feature_id}": {
|
||||||
|
"delete": {
|
||||||
|
"tags": ["registry"],
|
||||||
|
"success_schema": "RepositoryAbilityMapResponse",
|
||||||
|
},
|
||||||
|
"patch": {
|
||||||
|
"tags": ["registry"],
|
||||||
|
"success_schema": "RepositoryAbilityMapResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"/repos/{repository_id}/observed-facts": {
|
||||||
|
"get": {"tags": ["analysis"], "success_schema": "list[ObservedFactResponse]"}
|
||||||
|
},
|
||||||
|
"/repos/{repository_id}/review-decisions": {
|
||||||
|
"get": {"tags": ["review"], "success_schema": "list[ReviewDecisionResponse]"}
|
||||||
|
},
|
||||||
|
"/repository-comparisons": {
|
||||||
|
"get": {
|
||||||
|
"tags": ["discovery"],
|
||||||
|
"success_schema": "RepositoryComparisonResponse",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/search": {
|
||||||
|
"get": {"tags": ["search"], "success_schema": "list[SearchResultResponse]"}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def test_docs_endpoint_is_available():
|
def test_docs_endpoint_is_available():
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ restore guidance for SQLite deployments. Migration strategy notes for PostgreSQL
|
|||||||
|
|
||||||
```task
|
```task
|
||||||
id: RREG-WP-0002-T06
|
id: RREG-WP-0002-T06
|
||||||
status: todo
|
status: done
|
||||||
priority: low
|
priority: low
|
||||||
state_hub_task_id: "271a4fc4-d966-40ef-bc6f-a5fd1c445a16"
|
state_hub_task_id: "271a4fc4-d966-40ef-bc6f-a5fd1c445a16"
|
||||||
```
|
```
|
||||||
|
|||||||
Reference in New Issue
Block a user