Ctrl K
ComunidadGuías…

test/generation-status.test.js

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { test } from "node:test";
import assert from "node:assert/strict";
import { mkdtemp, mkdir, writeFile, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import path from "node:path";
import YAML from "yaml";

// generationStatus reads DECKS from src/paths.js at import time, so the env has
// to point at a scratch dir before the pipeline is imported.
const scratch = await mkdtemp(path.join(tmpdir(), "forge-genstatus-"));
process.env.FORGE_DECKS_DIR = scratch;
// Pinned for the same reason FORGE_DECKS_DIR is: finalize now records the
// deck's score, and an unpinned config dir writes that row into the real
// config/scores.jsonl — a test polluting the operator's history.
process.env.FORGE_CONFIG_DIR = process.env.FORGE_CONFIG_DIR ?? path.join(scratch, "config");

const { generationStatus } = await import("../src/ai/pipeline.js");

/**
 * The finalize watchdog's input.
 *
 * `DeckDetail` AUTO-RUNS finalize when this reports a deck as needing it — a
 * grounding pass, a coherence model call, image supply, a render and a preview.
 * So "does this deck need finalizing" is not a cosmetic question: getting it
 * wrong re-runs the whole post-write pass every time somebody opens the page.
 */

let n = 0;
async function deckDir({ plan = 4, written = 4, status = "ready", run = null, meta = true } = {}) {
  const slug = `deck-${n++}`;
  const dir = path.join(scratch, slug);
  await mkdir(dir, { recursive: true });
  if (plan != null) {
    await writeFile(path.join(dir, "plan.yaml"), YAML.stringify({
      title: "T", sections: ["A"],
      slides: Array.from({ length: plan }, () => ({ type: "bullets", section: 0, purpose: "p" })),
    }), "utf8");
  }
  if (written != null) {
    await writeFile(path.join(dir, "deck.yaml"), YAML.stringify({
      title: "T",
      slides: Array.from({ length: written }, () => ({ type: "bullets", headline: "H", bullets: ["a", "b", "c", "d"] })),
    }), "utf8");
  }
  if (meta) await writeFile(path.join(dir, "meta.yaml"), YAML.stringify({ slug, status }), "utf8");
  if (run) await writeFile(path.join(dir, ".run.json"), JSON.stringify(run), "utf8");
  return slug;
}

test("a finished deck does not ask to be finalised again", async () => {
  // The regression: `complete` is true of every deck that ever succeeded, so
  // reading it as "interrupted" made the watchdog re-run the post-write pass on
  // every page load of every finished deck.
  const slug = await deckDir({ plan: 4, written: 4, status: "ready" });
  const r = await generationStatus(slug);
  assert.equal(r.complete, true, "every plan slide is written");
  assert.equal(r.finalized, true);
  assert.equal(r.unfinalised, false, "finished is not the same as unfinalised");
  assert.equal(r.partial, false);
});

test("a deck written through but stranded before finalize still asks", async () => {
  // writeDeckContent sets "writing" and finalizeDeck flips it to "ready", so a
  // deck stuck at "writing" with every slide on disk is exactly what an
  // interrupted run leaves behind — the case the watchdog exists for.
  const slug = await deckDir({ plan: 4, written: 4, status: "writing" });
  const r = await generationStatus(slug);
  assert.equal(r.complete, true);
  assert.equal(r.finalized, false);
  assert.equal(r.unfinalised, true, "this is the state the watchdog is for");
});

test("a half-written deck is resumable, not finalisable", async () => {
  const slug = await deckDir({ plan: 5, written: 2, status: "writing", run: { total: 5, status: "writing" } });
  const r = await generationStatus(slug);
  assert.equal(r.partial, true);
  assert.equal(r.complete, false);
  assert.equal(r.unfinalised, false, "there is still content to write — resume, do not finalize");
  assert.equal(r.status, "writing");
});

test("a planned deck with nothing written is neither", async () => {
  const slug = await deckDir({ plan: 6, written: null, status: "planned" });
  const r = await generationStatus(slug);
  assert.equal(r.written, 0);
  assert.equal(r.complete, false);
  assert.equal(r.partial, false);
  assert.equal(r.unfinalised, false);
});

test("a deck with no meta is treated as unfinalised, the old behaviour", async () => {
  // Offering a finalize that was not needed costs a run; withholding one that
  // was leaves a deck the user cannot use. Without the signal, prefer the run.
  const slug = await deckDir({ plan: 4, written: 4, meta: false });
  const r = await generationStatus(slug);
  assert.equal(r.finalized, false);
  assert.equal(r.unfinalised, true);
});

test("the checkpoint's total wins over the plan's, and completeness follows it", async () => {
  // A resumed run's real total lives in .run.json; the plan may have been
  // trimmed since. Reading the plan instead would call a deck complete early.
  const slug = await deckDir({ plan: 4, written: 4, status: "writing", run: { total: 8, status: "writing" } });
  const r = await generationStatus(slug);
  assert.equal(r.total, 8);
  assert.equal(r.complete, false);
  assert.equal(r.partial, true);
});

test("a swept or re-rendered ready deck stays finalised", async () => {
  // sweepDensity and the critic branch both re-write meta.status = "ready";
  // none of them may push a deck back into the watchdog's path.
  const slug = await deckDir({ plan: 12, written: 12, status: "ready" });
  assert.equal((await generationStatus(slug)).unfinalised, false);
});

test.after(() => rm(scratch, { recursive: true, force: true }));