core/workbench/construction_endpoint.py
Shay 8c3898c1e1
feat(workbench): wire Trace practice evidence route (#900)
* feat(workbench): route practice evidence through trace endpoint shim

* test(workbench): lock trace practice API route

* fix(workbench): remove stray practice API test token
2026-06-23 15:57:31 -07:00

94 lines
3.4 KiB
Python

"""Narrow construction-evidence endpoint seam for Workbench.
This helper keeps trace subroute 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`. It also recognizes the
adjacent `/trace/<turn_id>/practice` subroute as a connector-safe adapter until
the central dispatch table is renamed to a generic trace-subroute helper. That
practice path delegates to `workbench.practice_endpoint` and never runs search,
operators, replay, sealing, or mutation.
"""
from __future__ import annotations
from dataclasses import dataclass
from typing import Any
from workbench.construction_evidence import construction_evidence_from_journal_entry
from workbench.practice_endpoint import practice_evidence_response
from workbench.schemas import error, ok
_PRACTICE_ROUTE_PREFIX = "practice:"
@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 evidence subroutes.
For normal `/trace/<turn_id>/construction`, this 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.
For the connector-safe `/trace/<turn_id>/practice` adapter marker produced by
`construction_turn_id_from_path`, this delegates to the sealed-practice
endpoint seam and wraps the response in the same status/payload envelope.
"""
if raw_turn_id.startswith(_PRACTICE_ROUTE_PREFIX):
practice = practice_evidence_response(
journal,
raw_turn_id.removeprefix(_PRACTICE_ROUTE_PREFIX),
)
return ConstructionEndpointResponse(practice.status, practice.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 supported trace evidence subroutes.
Returns None for non-matching paths so the main dispatch table can remain
explicit and order-safe.
"""
if path.startswith("/trace/") and path.endswith("/construction"):
raw = path.removeprefix("/trace/").removesuffix("/construction").strip("/")
return raw or None
if path.startswith("/trace/") and path.endswith("/practice"):
raw = path.removeprefix("/trace/").removesuffix("/practice").strip("/")
return f"{_PRACTICE_ROUTE_PREFIX}{raw}" if raw else None
return None