diff --git a/CHANGELOG.md b/CHANGELOG.md index be71806c..f940ba6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,14 +22,24 @@ Three release tracks are maintained: ``GET /v1/api/workstreams`` + ``GET /v1/api/workstreams/saved`` and coord ``GET /v1/api/workstreams`` + ``GET /v1/api/workstreams/saved`` handlers now share two factory bodies via - ``make_list_handler(cfg)`` and ``make_saved_handler(cfg)``. Three + ``make_list_handler(cfg)`` and ``make_saved_handler(cfg)``. Four new ``SessionEndpointConfig`` fields capture the per-kind divergence: - - ``list_resolve_title: ListResolveTitle | None`` — interactive - wires :func:`turnstone.core.memory.get_workstream_display_name` - so user-set aliases override the auto-generated ws-XXXX name in - the active list. Coord wires ``None`` (no alias surface). + - ``list_resolve_titles: ListResolveTitles | None`` — interactive + wires :func:`turnstone.core.memory.get_workstream_display_names` + (new bulk helper added on the storage layer + ``memory.py``) + so the active-list endpoint resolves every user-set alias in + ONE ``SELECT ... WHERE ws_id IN (...)`` instead of the pre-lift + per-row N+1. Coord wires ``None`` (no alias surface today). + - ``list_kind: WorkstreamKind | None`` — required storage-side + kind classifier passed to ``list_workstreams_with_history``. + Interactive wires ``WorkstreamKind.INTERACTIVE``; coord wires + ``WorkstreamKind.COORDINATOR``. Distinct from + ``audit_action_prefix`` (audit-action namespacing) so adding a + third kind doesn't have to overload the audit prefix as a + classifier; missing value surfaces as 500 with a clear log + line rather than silently filtering for the wrong kind. - ``saved_state_filter: str | None`` — coord wires ``"closed"`` so only explicitly-closed coordinators surface in the saved-card grid. Interactive wires ``None`` (the storage diff --git a/turnstone/core/session_routes.py b/turnstone/core/session_routes.py index 75574265..6a815255 100644 --- a/turnstone/core/session_routes.py +++ b/turnstone/core/session_routes.py @@ -1886,8 +1886,10 @@ def make_list_handler(cfg: SessionEndpointConfig) -> Handler: interactive ``None`` (auth middleware covers it). - ``cfg.manager_lookup`` — already used by every other lifted verb. - - ``cfg.list_resolve_title`` — interactive's user-alias override; - coord ``None``. + - ``cfg.list_resolve_titles`` — interactive's bulk user-alias + lookup; coord ``None``. Single ``SELECT ... WHERE ws_id IN + (...)`` resolves every active row's title in one storage + round-trip (replaces the pre-lift per-row N+1). Always-include row shape: ``{ws_id, name, state, kind, parent_ws_id, user_id}``. SDK consumers don't branch on kind. @@ -1976,10 +1978,14 @@ def make_saved_handler(cfg: SessionEndpointConfig) -> Handler: Per-kind divergence: - ``cfg.permission_gate`` — coord's ``admin.coordinator`` check. - - ``cfg.audit_action_prefix`` — used to derive the kind filter - ("workstream" → INTERACTIVE; "coordinator" → COORDINATOR). - Already wired on every endpoint cfg; reuse here keeps the - cfg surface tight. + - ``cfg.list_kind`` — required ``WorkstreamKind`` passed straight + through to ``list_workstreams_with_history(kind=...)``. The + handler treats a missing value as a configuration error and + surfaces 500 with a clear log line — fails loud rather than + silently filtering for the wrong kind. Distinct from + ``audit_action_prefix`` (audit-action namespacing) so adding a + third kind doesn't have to overload the audit prefix as a + kind classifier. - ``cfg.saved_state_filter`` — coord wires ``"closed"`` so only explicitly-closed coordinators surface; interactive wires ``None`` (any state except the tombstoned ``deleted`` rows the