Something went wrong. Try again.
A functional toolkit for authoring resource-leveling algorithms — compose constraint and scoring blocks, enumerate feasible schedules
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436// parseConf — read a `.conf` (TOML 1.0) pipeline configuration into a raw// RunConfig. The `.conf` grammar is a text projection of RunConfig: JSON stays// canonical, this is the human-authoring surface. Every framework section is// strict (an unknown section or a typo'd reserved key is an error, never// silently ignored), and every failure mode gets a coded ConfIssue.//// parseConf returns the *raw* config (block params not yet expanded through// their schemas, no levelset version stamp) — callers compose it with// fillRunConfig / runFromConfig. With a registry it additionally runs// validateGraph and the shared referential checks (checkRunConfigRefs), so a// weights/objective/collector reference that doesn't resolve fails here too.
import { z } from "zod";import { parse as parseToml, TomlError } from "smol-toml";
import { validateGraph } from "../level-blocks/graph.ts";import type { BlockDef, BlockRegistry } from "../level-blocks/registry.ts";import { checkRunConfigRefs, runConfigSchema, validateSweepConfig, type RunConfig,} from "../level-blocks/runConfig.ts";
import type { ConfIssue } from "./issues.ts";
export type ParseConfResult = | { ok: true; config: RunConfig; issues: ConfIssue[] } | { ok: false; issues: ConfIssue[] };
export interface ParseConfOptions { readonly registry?: BlockRegistry | undefined;}
/** Keys peeled off a `[blocks.<id>]` table as instance fields rather than * params. A block whose schema declares a param of one of these names can't * round-trip through conf (the reserved reading always wins) — E305. */export const RESERVED_BLOCK_KEYS = [ "code", "enabled", "provenance", "shadow",] as const;
const ALLOWED_SECTIONS = new Set([ "levelset", "resolve", "pipeline", "blocks", "weights", "objective", "sweep",]);
const LEVELSET_KEYS = new Set(["format", "version", "seed"]);const PIPELINE_KEYS = new Set(["systemCode"]);
// Named-property views over the loosely-typed TOML tables — so the known// sections read as `doc.levelset`, not index-signature access.interface ConfDoc { levelset?: unknown; resolve?: unknown; pipeline?: unknown; blocks?: unknown; weights?: unknown; objective?: unknown; sweep?: unknown; [key: string]: unknown;}interface LevelsetTbl { format?: unknown; version?: unknown; seed?: unknown; [key: string]: unknown;}interface PipelineTbl { systemCode?: unknown; [key: string]: unknown;}interface AssembledConfig { formatVersion: 1; levelset?: unknown; seed?: unknown; resolve?: unknown; pipeline?: unknown; weights?: unknown; objective?: unknown; sweep?: unknown;}interface BlockInstanceDraft { instanceId: string; code: unknown; params: unknown; provenance?: unknown; shadow?: unknown; enabled?: unknown;}
function isPlainObject(value: unknown): value is Record<string, unknown> { return ( typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date) );}
/** Render an arbitrary TOML value for an issue message. Scalars print bare; * a table or array prints as JSON rather than "[object Object]". */function describeValue(value: unknown): string { if (typeof value === "string") return value; return JSON.stringify(value) ?? String(value);}
/** The block schema's top-level property names — the conf param names — used * to detect reserved-key collisions. */export function blockParamNames(def: BlockDef): string[] { try { const js = z.toJSONSchema(def.block.schema.input, { io: "input" }) as { properties?: Record<string, unknown>; }; return Object.keys(js.properties ?? {}); } catch { return []; }}
/** Reserved conf keys a block's params collide with (empty when clean). */export function reservedParamCollisions(def: BlockDef): string[] { const params = new Set(blockParamNames(def)); return RESERVED_BLOCK_KEYS.filter((k) => params.has(k));}
function unknownSectionMessage(key: string): string { if (key === "search") { return `[search] is not a conf section — search is a block; add it under [blocks.<id>] with code = "levelset.SerialSGS.v1"`; } if (key === "profiles") { return `[profiles] is not a conf section — schedule profiles are runtime memoization, not config state`; } return `Unknown top-level section [${key}] — the conf grammar allows only levelset, resolve, pipeline, blocks, weights, objective, sweep`;}
/** Deep-scan for TOML datetime literals (E304) and non-finite numbers (E306), * which valid TOML admits but a config forbids. Reports each with its path. */function scanValues(value: unknown, path: string, issues: ConfIssue[]): void { if (value instanceof Date) { issues.push({ code: "LEVELSET_E304", path, message: `${path}: TOML datetime literals are not accepted — quote the date as an ISO string (e.g. "2026-01-01")`, }); return; } if (typeof value === "number" && !Number.isFinite(value)) { issues.push({ code: "LEVELSET_E306", path, message: `${path}: non-finite number (${String(value)}) — configs must be finite; model "unbounded" as an absent optional param`, }); return; } if (Array.isArray(value)) { value.forEach((v, i) => scanValues(v, `${path}[${String(i)}]`, issues)); return; } if (isPlainObject(value)) { for (const [k, v] of Object.entries(value)) scanValues(v, path === "" ? k : `${path}.${k}`, issues); }}
/** Translate a zod issue path (over the assembled RunConfig) into a * conf-oriented dotted path, so a param error reads `blocks.<id>.max` * rather than `pipeline.blocks.0.params.max`. */function confPath( path: readonly PropertyKey[], blocks: readonly { instanceId: string }[],): string { const seg = path.map(String); if (seg[0] === "pipeline" && seg[1] === "blocks") { const idx = Number(seg[2]); const id = blocks[idx]?.instanceId ?? seg[2]; const rest = seg.slice(seg[3] === "params" ? 4 : 3); return ["blocks", id, ...rest].join("."); } if (seg[0] === "formatVersion") return "levelset.format"; if (seg[0] === "seed") return "levelset.seed"; if (seg[0] === "levelset") return "levelset.version"; return seg.join(".");}
/** * Parse `.conf` text into a raw RunConfig. Returns `{ ok: true, config }` when * the text is a well-formed config, else `{ ok: false, issues }`. Pass a * registry to additionally validate the graph and its scorer references. */export function parseConf( text: string, opts?: ParseConfOptions,): ParseConfResult { let doc: ConfDoc; try { doc = parseToml(text); } catch (err) { if (err instanceof TomlError) { return { ok: false, issues: [ { code: "LEVELSET_E301", message: `TOML syntax error: ${err.message.split("\n")[0] ?? err.message}`, pos: { line: err.line, column: err.column }, }, ], }; } throw err; }
// Datetime / non-finite scan first: these are otherwise-valid TOML values a // config can't hold, and reporting them here beats a confusing downstream // shape error. const valueIssues: ConfIssue[] = []; scanValues(doc, "", valueIssues); if (valueIssues.length > 0) return { ok: false, issues: valueIssues };
const issues: ConfIssue[] = [];
// Strict top level: unknown sections are errors. for (const key of Object.keys(doc)) { if (!ALLOWED_SECTIONS.has(key)) { issues.push({ code: "LEVELSET_E302", path: key, message: unknownSectionMessage(key), }); } }
// [levelset] — required, `format = 1` required and authoritative for both // runConfig.formatVersion and pipeline.formatVersion. const levelsetTbl: LevelsetTbl | undefined = isPlainObject(doc.levelset) ? doc.levelset : undefined; if (!levelsetTbl || levelsetTbl.format === undefined) { issues.push({ code: "LEVELSET_E303", path: "levelset.format", message: `[levelset] format is required — add \`[levelset]\` with \`format = 1\``, }); } else { if (levelsetTbl.format !== 1) { issues.push({ code: "LEVELSET_E303", path: "levelset.format", message: `Unsupported [levelset] format ${describeValue(levelsetTbl.format)} — this engine supports format 1`, }); } for (const key of Object.keys(levelsetTbl)) { if (!LEVELSET_KEYS.has(key)) { issues.push({ code: "LEVELSET_E302", path: `levelset.${key}`, message: `Unknown key [levelset].${key} — allowed: format, version, seed`, }); } } }
// Assemble a RunConfig. formatVersion is forced to its only legal value (1) // so the `format` marker's own validation (E303) is the sole authority and // zod never double-reports it. const assembled: AssembledConfig = { formatVersion: 1 }; if (levelsetTbl?.version !== undefined) assembled.levelset = levelsetTbl.version; if (levelsetTbl?.seed !== undefined) assembled.seed = levelsetTbl.seed; if (doc.resolve !== undefined) assembled.resolve = doc.resolve;
// [pipeline] carries only systemCode; formatVersion comes from [levelset], // blocks from [blocks.*]. const pipelineTbl: PipelineTbl | undefined = isPlainObject(doc.pipeline) ? doc.pipeline : undefined; if (pipelineTbl) { for (const key of Object.keys(pipelineTbl)) { if (!PIPELINE_KEYS.has(key)) { issues.push({ code: "LEVELSET_E302", path: `pipeline.${key}`, message: `Unknown key [pipeline].${key} — a block goes under [blocks.<id>], not [pipeline]`, }); } } }
// [blocks.<id>] tables, in document order. const blocks: BlockInstanceDraft[] = []; const blocksTbl = isPlainObject(doc.blocks) ? doc.blocks : undefined; if (blocksTbl) { for (const [instanceId, raw] of Object.entries(blocksTbl)) { if (!isPlainObject(raw)) { issues.push({ code: "LEVELSET_E302", instanceId, path: `blocks.${instanceId}`, message: `[blocks.${instanceId}] must be a table`, }); continue; } const { code, enabled, provenance, shadow, ...params } = raw; if (code === undefined) { issues.push({ code: "LEVELSET_E302", instanceId, path: `blocks.${instanceId}.code`, message: `[blocks.${instanceId}] is missing the required \`code\` (its registry block code)`, }); continue; } const instance: BlockInstanceDraft = { instanceId, code, params }; if (provenance !== undefined) instance.provenance = provenance; if (shadow !== undefined) instance.shadow = shadow; if (enabled !== undefined) instance.enabled = enabled; blocks.push(instance);
// E305: a registry-resolved block whose schema declares a reserved-named // param — the value the user gave was silently peeled as an instance // field, so the param is unreachable. if (typeof code === "string") { const def = opts?.registry?.get(code); if (def) { for (const collision of reservedParamCollisions(def)) { issues.push({ code: "LEVELSET_E305", instanceId, path: `blocks.${instanceId}.${collision}`, message: `block "${instanceId}" (${code}) declares a param "${collision}" that collides with the reserved conf key "${collision}" — it cannot be set through the conf format`, }); } } } } } assembled.pipeline = { formatVersion: 1, ...(pipelineTbl?.systemCode !== undefined ? { systemCode: pipelineTbl.systemCode } : {}), blocks, };
// [weights] — number, or the string sentinel "report" ⇄ null (report-only). if (doc.weights !== undefined) { const wTbl = isPlainObject(doc.weights) ? doc.weights : undefined; if (!wTbl) { issues.push({ code: "LEVELSET_E302", path: "weights", message: `[weights] must be a table`, }); } else { const weights: Record<string, number | null> = {}; for (const [name, value] of Object.entries(wTbl)) { if (value === "report") weights[name] = null; else if (typeof value === "number") weights[name] = value; else { issues.push({ code: "LEVELSET_E302", path: `weights.${name}`, message: `weights.${name} must be a number or the string "report" (report-only)`, }); } } assembled.weights = weights; } }
if (doc.objective !== undefined) assembled.objective = doc.objective; if (doc.sweep !== undefined) assembled.sweep = doc.sweep;
// Shape validation. formatVersion is pre-forced; everything else is checked // here (types, strict resolve/objective sections, required fields). const parsed = runConfigSchema.safeParse(assembled); if (!parsed.success) { for (const issue of parsed.error.issues) { const path = confPath(issue.path, blocks); issues.push({ code: "LEVELSET_E302", ...(path !== "" ? { path } : {}), message: `${path}: ${issue.message}`, }); } }
if (issues.length > 0) return { ok: false, issues };
const config = parsed.success ? parsed.data : (assembled as RunConfig);
// Registry validation (E308): graph shape + cardinality + scorer references. if (opts?.registry) { const validated = validateGraph(opts.registry, config.pipeline); if (!validated.ok) { return { ok: false, issues: validated.issues.map((i) => graphIssueToConf(i)), }; } const refIssues = checkRunConfigRefs(config, opts.registry, validated); if (refIssues.length > 0) { return { ok: false, issues: refIssues.map((i) => graphIssueToConf(i)) }; } const sweepIssues = validateSweepConfig(config, opts.registry, validated); if (sweepIssues.length > 0) { return { ok: false, issues: sweepIssues.map((i) => graphIssueToConf(i)) }; } }
return { ok: true, config, issues: [] };}
function graphIssueToConf(issue: { instanceId?: string; path?: (string | number)[]; message: string;}): ConfIssue { return { code: "LEVELSET_E308", message: issue.message, ...(issue.instanceId !== undefined ? { instanceId: issue.instanceId } : {}), ...(issue.path !== undefined ? { path: issue.path.map(String).join(".") } : {}), };}