// The candidate-file dance: resume from the strongest, write through the gate.
import test from "node:test";
import assert from "node:assert/strict";
import { mkdtempSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { resolveModel, gatePromotion } from "../dist/gate.js";
import { checkpointPath, candidatePath, saveModel, loadModel } from "../dist/checkpoint.js";
import { modelRev, modelVersion } from "../dist/rev.js";
const model = (episodes) => ({ v: 1, game: "toy", episodes, updatedAt: 0, q: {} });
test("resolveModel: promoted beats candidate beats nothing", () => {
const dir = mkdtempSync(join(tmpdir(), "tk-gate-"));
assert.equal(resolveModel(dir, "toy"), null);
saveModel(candidatePath(dir, "toy"), model(10));
assert.equal(resolveModel(dir, "toy").episodes, 10, "candidate when nothing promoted");
saveModel(checkpointPath(dir, "toy"), model(99));
assert.equal(resolveModel(dir, "toy").episodes, 99, "promoted wins");
});
test("gatePromotion writes the right file and returns its path", () => {
const dir = mkdtempSync(join(tmpdir(), "tk-gate-"));
const cand = gatePromotion(dir, "toy", model(1), false);
assert.equal(cand, candidatePath(dir, "toy"));
assert.equal(loadModel(checkpointPath(dir, "toy")), null, "not promoted → no live file");
const live = gatePromotion(dir, "toy", model(2), true);
assert.equal(live, checkpointPath(dir, "toy"));
assert.equal(loadModel(live).episodes, 2);
});
test("a promoted model ships its weights rev, and promotion moves the version", () => {
const dir = mkdtempSync(join(tmpdir(), "tk-gate-"));
const before = model(10);
gatePromotion(dir, "toy", before, true);
const promoted = loadModel(checkpointPath(dir, "toy"));
assert.equal(promoted.rev, modelRev(before), "the gate wrote the rev of the weights it promoted");
assert.equal(modelVersion(promoted), `10@${modelRev(before)}`);
const trained = { ...model(20), q: { s0: [0, 1] } };
gatePromotion(dir, "toy", trained, true);
const next = loadModel(checkpointPath(dir, "toy"));
assert.notEqual(modelVersion(next), modelVersion(promoted), "new weights → a visibly new version");
assert.equal(modelVersion(next), `20@${modelRev(trained)}`);
});