* feat(workbench): add construction endpoint handler seam * test(workbench): cover construction endpoint handler seam * feat(workbench-ui): add construction evidence endpoint helpers * test(workbench-ui): cover construction endpoint helpers
71 lines
2.2 KiB
Python
71 lines
2.2 KiB
Python
"""Narrow construction-evidence endpoint seam for Workbench.
|
|
|
|
This helper keeps the endpoint behavior testable without editing the large
|
|
`workbench.api` dispatch table from environments that cannot run local syntax and
|
|
route tests. The final API wiring is intentionally one line of dispatch:
|
|
|
|
return construction_evidence_response(self._journal, raw_turn_id)
|
|
|
|
The helper itself is read-only and projects only persisted construction evidence
|
|
through `construction_evidence_from_journal_entry`.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from typing import Any
|
|
|
|
from workbench.construction_evidence import construction_evidence_from_journal_entry
|
|
from workbench.schemas import error, ok
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class ConstructionEndpointResponse:
|
|
status: int
|
|
payload: dict[str, Any]
|
|
|
|
|
|
def construction_evidence_response(
|
|
journal: Any,
|
|
raw_turn_id: str,
|
|
) -> ConstructionEndpointResponse:
|
|
"""Return JSON-envelope payload for `/trace/<turn_id>/construction`.
|
|
|
|
The function is deliberately free of parsing/reconstruction side effects. It
|
|
reads a journal entry and projects construction evidence when already
|
|
persisted; legacy entries receive a typed `missing_evidence` data payload.
|
|
"""
|
|
|
|
try:
|
|
turn_id = int(raw_turn_id)
|
|
except ValueError:
|
|
return ConstructionEndpointResponse(
|
|
404,
|
|
error("not_found", f"trace construction not found: {raw_turn_id}"),
|
|
)
|
|
|
|
try:
|
|
entry = journal.get_entry(turn_id)
|
|
except FileNotFoundError:
|
|
return ConstructionEndpointResponse(
|
|
404,
|
|
error("not_found", f"trace construction not found: {turn_id}"),
|
|
)
|
|
|
|
return ConstructionEndpointResponse(
|
|
200,
|
|
ok(construction_evidence_from_journal_entry(entry)),
|
|
)
|
|
|
|
|
|
def construction_turn_id_from_path(path: str) -> str | None:
|
|
"""Extract raw turn id from `/trace/<turn_id>/construction`.
|
|
|
|
Returns None for non-matching paths so the main dispatch table can remain
|
|
explicit and order-safe.
|
|
"""
|
|
|
|
if not (path.startswith("/trace/") and path.endswith("/construction")):
|
|
return None
|
|
raw = path.removeprefix("/trace/").removesuffix("/construction").strip("/")
|
|
return raw or None
|