feat(workspaces): status and list use cases

getWorkspaceStatus returns persisted { id, status } only (runner-driven
transitions arrive in story 07); listWorkspaces returns the
credential-free workspace array with a strict void input (optional on
the tRPC procedure so clients can call without args). Repository gains
listWorkspaces on interface, mock, and Payload impl; the contract suite
covers listing incl. the never-returns-credentials guarantee.
Controller span+capture wrapping is extracted into a shared
bindWorkspacesController helper used by both binders, trimming the
bind-production/bind-dev-seed clone family fallow was flagging.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016j8z4VHjedXDTjEDNg7qHK
This commit is contained in:
2026-07-12 22:27:44 +02:00
parent c990e1b871
commit eec402d49d
25 changed files with 743 additions and 92 deletions

View File

@@ -0,0 +1,43 @@
import {
withSpan,
withCapture,
type ITracer,
type ILogger,
} from "@repo/core-shared/instrumentation";
import { workspacesContainer } from "./container";
/**
* Binds a controller wrapped with the span + capture sandwich at bind time
* (withSpan outermost, per template convention). Shared by bind-production
* and bind-dev-seed so the wrapping stays identical in both modes.
*
* Idempotent: rebinding replaces the previous constant value.
*/
export function bindWorkspacesController<Args extends unknown[], R>(
tracer: ITracer,
logger: ILogger,
symbol: symbol,
name: string,
controller: (...args: Args) => Promise<R>,
): void {
if (workspacesContainer.isBound(symbol)) {
workspacesContainer.unbind(symbol);
}
workspacesContainer
.bind(symbol)
.toConstantValue(
withSpan(
tracer,
{ name: `workspaces.${name}`, op: "controller" },
withCapture(
logger,
{
feature: "workspaces",
layer: "controller",
name: `workspaces.${name}`,
},
controller,
),
),
);
}