// Run provenance: every materialized project is self-describing — filled // config, engine version, achieved scores — and survives a JSON round trip. import { describe, expect, test } from "bun:test"; import { JsonReader } from "../../src/json/JsonReader.ts"; import { JsonWriter } from "../../src/json/JsonWriter.ts"; import { runFromConfig } from "../../src/level-blocks/runConfig.ts"; import { STANDARD_CODES } from "../../src/level-blocks/standard.ts"; import { LEVELSET_VERSION } from "../../src/version.ts"; import { MON_JAN_5, SAT_JAN_10, makeAssignment, makeProject, makeResource, makeTask, } from "../level-core/fixtures.ts"; function project() { const t1 = makeTask({ uniqueId: 1, start: MON_JAN_5, finish: SAT_JAN_10 }); const t2 = makeTask({ uniqueId: 2, start: MON_JAN_5, finish: SAT_JAN_10 }); return makeProject([t1, t2], { resources: [makeResource(100, "Crew")], assignments: [makeAssignment(1, 100), makeAssignment(2, 100)], }); } function config() { return { formatVersion: 1, seed: 42, pipeline: { formatVersion: 1, blocks: [ { instanceId: "crew-cap", code: STANDARD_CODES.maxConcurrentResource, params: { resourceUniqueId: 100, max: 1 }, }, { instanceId: "makespan", code: STANDARD_CODES.makespan, params: {} }, { instanceId: "search", code: STANDARD_CODES.serialSGS, params: {} }, ], }, }; } describe("run provenance", () => { test("materialize embeds version, filled config, seed, and scores", async () => { const run = runFromConfig(project(), config()); const best = (await run.best())!; const leveled = run.materialize(best); const provenance = leveled.levelset!; expect(provenance.levelsetVersion).toBe(LEVELSET_VERSION); expect(provenance.search).toBe("serial-SGS"); expect(provenance.seed).toBe(42); expect(provenance.scores).toEqual({ makespan: best.makespan }); expect(provenance.generatedAt).toMatch(/^\d{4}-\d{2}-\d{2}T/); // The embedded config is the FILLED one — rerunning it reproduces the run. expect(provenance.runConfig).toEqual(run.config as never); const rerun = runFromConfig(project(), provenance.runConfig); const rebest = (await rerun.best())!; expect(rebest.tasks).toEqual(best.tasks); }); test("provenance survives a JSON write → read round trip", async () => { const run = runFromConfig(project(), config()); const leveled = run.materialize((await run.best())!); const json = new JsonWriter().write(leveled); const reread = new JsonReader().read(json); expect(reread.levelset).toEqual(leveled.levelset); }); test("unknown provenance keys survive the round trip (forward compatibility)", async () => { // A levelset block written by a newer version (or annotated app-side) // must not be silently truncated to this version's fields on re-save. const run = runFromConfig(project(), config()); const leveled = run.materialize((await run.best())!); const annotated = { ...leveled, levelset: { ...leveled.levelset!, futureField: { nested: true } }, }; const reread = new JsonReader().read(new JsonWriter().write(annotated)); expect(reread.levelset).toEqual(annotated.levelset); expect((reread.levelset as Record)["futureField"]).toEqual( { nested: true }, ); }); test("unknown nested sweep provenance keys survive the round trip", async () => { const run = runFromConfig(project(), config()); const leveled = run.materialize((await run.best())!); const annotated = { ...leveled, levelset: { ...leveled.levelset!, sweep: { combinations: 2, feasibleCells: 1, winner: { cap: 2 }, skipped: [], futureDiagnostic: { retained: true }, }, }, }; const reread = new JsonReader().read(new JsonWriter().write(annotated)); expect( (reread.levelset!.sweep as Record)["futureDiagnostic"], ).toEqual({ retained: true, }); }); test("plain projects have no levelset block and reads tolerate garbage", () => { const json = new JsonWriter().write(project()); const reread = new JsonReader().read(json); expect(reread.levelset).toBeUndefined(); const garbage = new JsonReader().read('{"levelset": {"nonsense": true}}'); expect(garbage.levelset).toBeUndefined(); }); });