[Spec] External Results API v1 — Pydantic schemas, spec doc, tests (BGSTM#299) - #304
Merged
bg-playground merged 2 commits intoMay 6, 2026
Conversation
…#299) Agent-Logs-Url: https://github.com/bg-playground/BGSTM/sessions/272b701a-fa8a-4a0c-82b8-acd89f93a16b Co-authored-by: bg-playground <259109604+bg-playground@users.noreply.github.com>
Copilot
AI
changed the title
[WIP] Draft v1 contract for External Results API
[Spec] External Results API v1 — Pydantic schemas, spec doc, tests (BGSTM#299)
May 6, 2026
bg-playground
marked this pull request as ready for review
May 6, 2026 21:09
bg-playground
deleted the
copilot/bgstm-299-draft-v1-contract-external-results-api
branch
May 6, 2026 21:09
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Drafts the v1 contract for the External Results API — the ingestion surface external test runners (Playwright first) use to report execution into BGSTM. Spec-only: no router, no DB migration, no auth implementation, no
main.pyedits.Schemas (
backend/app/schemas/external_results.py)Pydantic v2 types-only module covering the full lifecycle:
RunStatus·CaseOutcome·ArtifactKindSessionCreate/SessionResponse/SessionFinishCaseResultCreate/CaseResultResponse/CaseResultUpdateArtifactCreate/ArtifactResponseErrorResponseTwo validators enforced at schema level:
CaseResultResponseuses composition (not inheritance fromCaseResultCreate) to avoid exposing request validators in response models.Spec (
docs/specs/external_results_v1.md)Eight sections: overview + versioning model, auth (bearer token shape, scopes, error codes — forward-refs #296), all 7 endpoints with request/response JSON examples and error tables, idempotency rules (dedup by
external_id, SHA-256 for artifacts), session + outcome transition tables, unified error code registry, audit-log action taxonomy (forward-ref #297), and reference implementation pointer (bgstm-playwright-frameworks#3, #295).Nav + README
mkdocs.yml: addedSpecs → External Results v1nav entryREADME.md: added "📐 API Contracts & Specs" table near "Related projects"Tests (
backend/tests/test_external_results_schemas.py)27 tests — enum value coverage, both validators (happy + failure paths),
duration_msbounds, and fullmodel_dump_json→model_validate_jsonround-trips for every response model. No new dev dependencies.Original prompt
Goal
Draft the v1 contract for the External Results API — the surface that external test runners (Playwright first; Pytest/Cypress later) use to report execution into BGSTM. Spec only — no router wiring, no business logic, no DB migration.
This implements BGSTM#299 and unblocks parallel work on:
Parent epic: BGSTM#291.
Repository conventions to match
backend/app/api/. Existing schemas live inbackend/app/schemas/. Inspect a couple of these to match style:backend/app/api/auth.pybackend/app/api/test_cases.pybackend/app/api/traceability.pybackend/app/schemas/for Pydantic conventionsmkdocs.ymlat repo root or indocs/). Match existing doc page style and frontmatter.Tasks
1.
backend/app/schemas/external_results.py— Pydantic modelsCreate the module with types only — no FastAPI imports, no DB. The models must be importable and pass
pytest/mypy/ruffper the repo's existing config.Required models (use Pydantic v2 syntax —
model_config,Field(...), etc., matching the repo's existing style):RunStatus— Enum:started,passed,failed,skipped,abortedCaseOutcome— Enum:passed,failed,skipped,flakyArtifactKind— Enum:screenshot,trace,video,log,otherSessionCreate— payload forPOST /external-results/sessionrunner: str,project_id: UUID,git_sha: str | None,git_branch: str | None,ci_url: HttpUrl | None,metadata: dict[str, Any]model_configwith a JSON Schema exampleSessionResponse—id,status,started_at,finished_at, plus echoed metadata fieldsSessionFinish—status: RunStatus(onlypassed/failed/abortedallowed; enforce with a validator),summary: dict[str, Any]CaseResultCreate:session_id: UUIDtest_case_id: UUID | Noneexternal_id: str | Nonetitle: stroutcome: CaseOutcomeduration_ms: int = Field(ge=0)error_message: str | Nonerequirement_ids: list[UUID]test_case_idorexternal_idmust be set, else raiseValueErrorCaseResultResponse— extendsCaseResultCreatewithid,created_at,auto_registered: boolCaseResultUpdate— partial:outcome,duration_ms,error_messageall optionalArtifactCreate—case_result_id: UUID,kind: ArtifactKind,filename: str,content_type: str,size_bytes: int = Field(ge=0)ArtifactResponse— addsid,url: HttpUrl,created_atErrorResponse—code: str,message: str,details: dict[str, Any] | NoneAdd a top-of-file docstring linking to the spec doc and to BGSTM#291.
2.
docs/specs/external_results_v1.md— Canonical specCreate the canonical human-readable spec. Use whichever heading style and frontmatter the existing docs use. Required sections:
a. Overview
/api/v1/..., no breaking changes within v1)b. Authentication
Authorization: Bearer bgstm_runner_<opaque>c. Endpoints
For each endpoint, document: method, path, auth scope, request body, response body, all error codes (400/401/403/404/409/413/415/422/500), idempotency behavior, and a JSON example of request + success response.
Endpoints:
POST /api/v1/external-results/session(scopeexternal_results:write)PATCH /api/v1/external-results/session/{session_id}This pull request was created from Copilot chat.