/** * Task #242 (B1) — Capability data-foundation 骨架接口。 * * **本任务**:只接口 + 一个最小实现(`writeRow` 真插表 / `readRows` 真查表), * 让 B7 可以平滑加 distill / reuse 路径。 * **B7**:真把 inner-loop run 蒸馏成 row(parameter-fingerprint / failure-mode / ...) * 并在 inner-loop search 步骤复用。 * * 注意:`capability_data_foundation` 表已在 B2 (#255) 建好;本模块直接 * 复用,不新建 schema。 */ import { and, desc, eq } from "drizzle-orm"; import { db, capabilityDataFoundation, type CapabilityDataFoundationRow, type InsertCapabilityDataFoundationRow, } from "@workspace/db"; import { newId } from "../../ids.js"; export interface DataFoundationRow { id: string; capabilityId: string; kind: string; payload: Record; sourceRunId: string | null; createdAt: Date; } export interface WriteRowInput { capabilityId: string; kind: string; payload: Record; sourceRunId?: string | null; } export interface ReadRowsQuery { capabilityId: string; kind?: string; /** Default 50, max 500. */ limit?: number; /** * **Anti-self-loop**: when set, EXCLUDE rows where capabilityId matches * the requester (B7 will set this when an inner-loop search step asks * for "what other capabilities have learned" without polluting itself). */ excludeSelf?: boolean; } function rowToOutput(r: CapabilityDataFoundationRow): DataFoundationRow { return { id: r.id, capabilityId: r.capabilityId, kind: r.kind, payload: (r.payload ?? {}) as Record, sourceRunId: r.sourceRunId, createdAt: r.createdAt, }; } export async function writeRow(input: WriteRowInput): Promise<{ id: string }> { if (!input.capabilityId) { throw new Error("data-foundation/writeRow: capabilityId required"); } if (!input.kind) { throw new Error("data-foundation/writeRow: kind required"); } const id = newId("cdf"); const row: InsertCapabilityDataFoundationRow = { id, capabilityId: input.capabilityId, kind: input.kind, payload: input.payload, sourceRunId: input.sourceRunId ?? null, }; await db.insert(capabilityDataFoundation).values(row); return { id }; } export async function readRows( query: ReadRowsQuery, ): Promise { const limit = Math.min(Math.max(query.limit ?? 50, 1), 500); const conds = []; if (query.excludeSelf) { // 反模式:excludeSelf + capabilityId 同时给:本意是"排除自己读自己的行" // 但 capabilityId 是必填,所以这里其实排除其它 capability 的行=空集。 // 真正的"排除调用方自己"在 B7 inner-loop search 里实现(用 NOT 等), // 本骨架只 throw 提示调用方语义不对。 throw new Error( "data-foundation/readRows: excludeSelf is reserved for B7 inner-loop search " + "and is not yet implemented; pass capabilityId only to read own rows.", ); } if (query.kind) { conds.push( and( eq(capabilityDataFoundation.capabilityId, query.capabilityId), eq(capabilityDataFoundation.kind, query.kind), ), ); } else { conds.push(eq(capabilityDataFoundation.capabilityId, query.capabilityId)); } const rows = await db .select() .from(capabilityDataFoundation) .where(conds[0]) .orderBy(desc(capabilityDataFoundation.createdAt)) .limit(limit); return rows.map(rowToOutput); }