Files
stack/apps/gateway/src/harness/harness-selection.repository.ts
T
jason.woltjeandClaude Opus 4.8 f16f206a0a
ci/woodpecker/pr/ci Pipeline failed
feat(gateway): expose generic harness catalog and selection
Add the Slice-Zero catalog and selection HTTP surfaces for P3 Task 3:
GET /api/harnesses, GET /api/harnesses/:harnessId/catalog,
GET+PUT /api/chat/preferences/selection. Scope is always server-derived
via scopeFromUser(CurrentUser); selection tuples are validated against the
live catalog with no fallback substitution and persisted in a transitional
owner-scoped in-memory store. HarnessModule is wired into AppModule.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01ESFAnh2t9HmLwng8oW95St
2026-08-11 19:56:11 -05:00

40 lines
1.5 KiB
TypeScript

import { Injectable } from '@nestjs/common';
import type { HarnessSelection } from '@mosaicstack/types';
import type { ActorTenantScope } from '../auth/session-scope.js';
/**
* TRANSITIONAL Slice-Zero selection store.
*
* This is an EXPLICITLY in-memory, owner-scoped store. It exists only so the
* Slice-Zero HTTP surfaces round-trip a selection; it is NOT durable and does not
* survive a process restart. Task Seven replaces it with the durable selection
* repository backed by the database.
*
* The store is keyed STRICTLY by the server-derived scope (userId + tenantId).
* There is no code path through which a client-supplied id chooses the key.
*/
@Injectable()
export class HarnessSelectionRepository {
private readonly byOwner = new Map<string, HarnessSelection>();
get(scope: ActorTenantScope): HarnessSelection | null {
return this.byOwner.get(ownerKey(scope)) ?? null;
}
set(scope: ActorTenantScope, selection: HarnessSelection): HarnessSelection {
// Store a normalized copy of exactly the tuple fields — nothing else is retained.
const stored: HarnessSelection = {
harnessId: selection.harnessId,
providerId: selection.providerId,
modelId: selection.modelId,
};
this.byOwner.set(ownerKey(scope), stored);
return stored;
}
}
/** Owner key from the server-derived scope only. NUL-separated to avoid collisions. */
function ownerKey(scope: ActorTenantScope): string {
return `${scope.userId}${scope.tenantId}`;
}