feat(FN-4218): complete Step 1 — define experiment session types
Fusion-Task-Id: FN-4218 Fusion-Task-Lineage: 0b50d7f4-5001-4eb6-9633-7b24b00097fd
This commit is contained in:
72
packages/core/src/__tests__/experiment-session-types.test.ts
Normal file
72
packages/core/src/__tests__/experiment-session-types.test.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
EXPERIMENT_METRIC_DIRECTIONS,
|
||||
EXPERIMENT_RECORD_TYPES,
|
||||
EXPERIMENT_RUN_OUTCOMES,
|
||||
EXPERIMENT_SESSION_STATUSES,
|
||||
isConfigRecord,
|
||||
isFinalizeRecord,
|
||||
isHookRecord,
|
||||
isRunRecord,
|
||||
type ExperimentSessionRecord,
|
||||
} from "../experiment-session-types.js";
|
||||
|
||||
describe("experiment-session-types", () => {
|
||||
it("defines constants with expected members and order", () => {
|
||||
expect(EXPERIMENT_SESSION_STATUSES).toEqual(["active", "finalizing", "finalized", "archived"]);
|
||||
expect(EXPERIMENT_METRIC_DIRECTIONS).toEqual(["maximize", "minimize"]);
|
||||
expect(EXPERIMENT_RECORD_TYPES).toEqual(["config", "run", "hook", "finalize"]);
|
||||
expect(EXPERIMENT_RUN_OUTCOMES).toEqual(["keep", "discard", "checks_failed", "errored", "pending"]);
|
||||
});
|
||||
|
||||
it("narrowing helpers identify record payload types", () => {
|
||||
const records: ExperimentSessionRecord[] = [
|
||||
{
|
||||
id: "EXPR-1",
|
||||
sessionId: "EXP-1",
|
||||
segment: 1,
|
||||
seq: 1,
|
||||
type: "config",
|
||||
payload: { metric: { name: "throughput", direction: "maximize" } },
|
||||
createdAt: new Date().toISOString(),
|
||||
},
|
||||
{
|
||||
id: "EXPR-2",
|
||||
sessionId: "EXP-1",
|
||||
segment: 1,
|
||||
seq: 2,
|
||||
type: "run",
|
||||
payload: { primaryMetric: 123, secondaryMetrics: [], status: "keep" },
|
||||
createdAt: new Date().toISOString(),
|
||||
},
|
||||
{
|
||||
id: "EXPR-3",
|
||||
sessionId: "EXP-1",
|
||||
segment: 1,
|
||||
seq: 3,
|
||||
type: "hook",
|
||||
payload: { hook: "before", exitCode: 0 },
|
||||
createdAt: new Date().toISOString(),
|
||||
},
|
||||
{
|
||||
id: "EXPR-4",
|
||||
sessionId: "EXP-1",
|
||||
segment: 1,
|
||||
seq: 4,
|
||||
type: "finalize",
|
||||
payload: { keptRunIds: ["EXPR-2"], discardedRunIds: [] },
|
||||
createdAt: new Date().toISOString(),
|
||||
},
|
||||
];
|
||||
|
||||
const [config, run, hook, finalize] = records;
|
||||
expect(isConfigRecord(config)).toBe(true);
|
||||
expect(isRunRecord(config)).toBe(false);
|
||||
expect(isRunRecord(run)).toBe(true);
|
||||
expect(run.payload.primaryMetric).toBe(123);
|
||||
expect(isHookRecord(hook)).toBe(true);
|
||||
expect(hook.payload.hook).toBe("before");
|
||||
expect(isFinalizeRecord(finalize)).toBe(true);
|
||||
expect(finalize.payload.keptRunIds).toEqual(["EXPR-2"]);
|
||||
});
|
||||
});
|
||||
189
packages/core/src/experiment-session-types.ts
Normal file
189
packages/core/src/experiment-session-types.ts
Normal file
@@ -0,0 +1,189 @@
|
||||
export const EXPERIMENT_SESSION_STATUSES = ["active", "finalizing", "finalized", "archived"] as const;
|
||||
export type ExperimentSessionStatus = typeof EXPERIMENT_SESSION_STATUSES[number];
|
||||
|
||||
export const EXPERIMENT_METRIC_DIRECTIONS = ["maximize", "minimize"] as const;
|
||||
export type ExperimentMetricDirection = typeof EXPERIMENT_METRIC_DIRECTIONS[number];
|
||||
|
||||
export interface ExperimentMetricDefinition {
|
||||
name: string;
|
||||
unit?: string;
|
||||
direction: ExperimentMetricDirection;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export const EXPERIMENT_RECORD_TYPES = ["config", "run", "hook", "finalize"] as const;
|
||||
export type ExperimentRecordType = typeof EXPERIMENT_RECORD_TYPES[number];
|
||||
|
||||
export const EXPERIMENT_RUN_OUTCOMES = ["keep", "discard", "checks_failed", "errored", "pending"] as const;
|
||||
export type ExperimentRunOutcome = typeof EXPERIMENT_RUN_OUTCOMES[number];
|
||||
|
||||
export interface ExperimentSecondaryMetric {
|
||||
name: string;
|
||||
value: number;
|
||||
unit?: string;
|
||||
}
|
||||
|
||||
export interface ExperimentRunRecordPayload {
|
||||
commit?: string;
|
||||
primaryMetric: number;
|
||||
secondaryMetrics: ExperimentSecondaryMetric[];
|
||||
status: ExperimentRunOutcome;
|
||||
description?: string;
|
||||
confidence?: number;
|
||||
asi?: Record<string, unknown>;
|
||||
durationMs?: number;
|
||||
}
|
||||
|
||||
export interface ExperimentConfigRecordPayload {
|
||||
metric: ExperimentMetricDefinition;
|
||||
maxIterations?: number;
|
||||
rules?: string;
|
||||
ideas?: string;
|
||||
workingDir?: string;
|
||||
}
|
||||
|
||||
export interface ExperimentHookRecordPayload {
|
||||
hook: "before" | "after";
|
||||
exitCode: number;
|
||||
durationMs?: number;
|
||||
stdout?: string;
|
||||
stderr?: string;
|
||||
steerMessage?: string;
|
||||
}
|
||||
|
||||
export interface ExperimentFinalizeRecordPayload {
|
||||
keptRunIds: string[];
|
||||
discardedRunIds: string[];
|
||||
branches?: Array<{ name: string; baseCommit?: string; tipCommit?: string }>;
|
||||
summary?: string;
|
||||
}
|
||||
|
||||
export interface ExperimentSessionRecordBase {
|
||||
id: string;
|
||||
sessionId: string;
|
||||
segment: number;
|
||||
seq: number;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export interface ExperimentConfigRecord extends ExperimentSessionRecordBase {
|
||||
type: "config";
|
||||
payload: ExperimentConfigRecordPayload;
|
||||
}
|
||||
|
||||
export interface ExperimentRunRecord extends ExperimentSessionRecordBase {
|
||||
type: "run";
|
||||
payload: ExperimentRunRecordPayload;
|
||||
}
|
||||
|
||||
export interface ExperimentHookRecord extends ExperimentSessionRecordBase {
|
||||
type: "hook";
|
||||
payload: ExperimentHookRecordPayload;
|
||||
}
|
||||
|
||||
export interface ExperimentFinalizeRecord extends ExperimentSessionRecordBase {
|
||||
type: "finalize";
|
||||
payload: ExperimentFinalizeRecordPayload;
|
||||
}
|
||||
|
||||
export type ExperimentSessionRecord =
|
||||
| ExperimentConfigRecord
|
||||
| ExperimentRunRecord
|
||||
| ExperimentHookRecord
|
||||
| ExperimentFinalizeRecord;
|
||||
|
||||
export function isRunRecord(record: ExperimentSessionRecord): record is ExperimentRunRecord {
|
||||
return record.type === "run";
|
||||
}
|
||||
|
||||
export function isConfigRecord(record: ExperimentSessionRecord): record is ExperimentConfigRecord {
|
||||
return record.type === "config";
|
||||
}
|
||||
|
||||
export function isHookRecord(record: ExperimentSessionRecord): record is ExperimentHookRecord {
|
||||
return record.type === "hook";
|
||||
}
|
||||
|
||||
export function isFinalizeRecord(record: ExperimentSessionRecord): record is ExperimentFinalizeRecord {
|
||||
return record.type === "finalize";
|
||||
}
|
||||
|
||||
export interface ExperimentSession {
|
||||
id: string;
|
||||
name: string;
|
||||
projectId?: string;
|
||||
status: ExperimentSessionStatus;
|
||||
metric: ExperimentMetricDefinition;
|
||||
currentSegment: number;
|
||||
maxIterations?: number;
|
||||
workingDir?: string;
|
||||
baselineRunId?: string;
|
||||
bestRunId?: string;
|
||||
keptRunIds: string[];
|
||||
tags: string[];
|
||||
metadata?: Record<string, unknown>;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
finalizedAt?: string;
|
||||
}
|
||||
|
||||
export interface ExperimentSessionCreateInput {
|
||||
name: string;
|
||||
projectId?: string;
|
||||
status?: ExperimentSessionStatus;
|
||||
metric: ExperimentMetricDefinition;
|
||||
currentSegment?: number;
|
||||
maxIterations?: number;
|
||||
workingDir?: string;
|
||||
baselineRunId?: string;
|
||||
bestRunId?: string;
|
||||
keptRunIds?: string[];
|
||||
tags?: string[];
|
||||
metadata?: Record<string, unknown>;
|
||||
finalizedAt?: string;
|
||||
}
|
||||
|
||||
export interface ExperimentSessionUpdateInput {
|
||||
name?: string;
|
||||
projectId?: string;
|
||||
status?: ExperimentSessionStatus;
|
||||
metric?: ExperimentMetricDefinition;
|
||||
currentSegment?: number;
|
||||
maxIterations?: number;
|
||||
workingDir?: string;
|
||||
baselineRunId?: string;
|
||||
bestRunId?: string;
|
||||
keptRunIds?: string[];
|
||||
tags?: string[];
|
||||
metadata?: Record<string, unknown>;
|
||||
finalizedAt?: string;
|
||||
}
|
||||
|
||||
export interface ExperimentSessionRecordAppendInput {
|
||||
segment?: number;
|
||||
type: ExperimentRecordType;
|
||||
payload:
|
||||
| ExperimentRunRecordPayload
|
||||
| ExperimentConfigRecordPayload
|
||||
| ExperimentHookRecordPayload
|
||||
| ExperimentFinalizeRecordPayload;
|
||||
}
|
||||
|
||||
export interface ExperimentSessionListOptions {
|
||||
status?: ExperimentSessionStatus;
|
||||
projectId?: string;
|
||||
tag?: string;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
search?: string;
|
||||
}
|
||||
|
||||
export interface ExperimentSessionStoreEvents {
|
||||
"session:created": [ExperimentSession];
|
||||
"session:updated": [ExperimentSession];
|
||||
"session:deleted": [string];
|
||||
"session:status_changed": [ExperimentSession];
|
||||
"session:finalized": [ExperimentSession];
|
||||
"record:appended": [ExperimentSessionRecord];
|
||||
"segment:reset": [{ sessionId: string; segment: number }];
|
||||
}
|
||||
Reference in New Issue
Block a user