test(guards): hold the whole regenerate route to its two model stages

The browser's abort for a forced regenerate waits three calls at the
response timeout, fewer than the five the background generator may make.
That is correct: the route runs its own pipeline, refuses an unparseable
reply or a screened payload instead of retrying, and never reaches the
background generator, so its longest run is the generation, the
grounding retry and the one reasoning repeat.

The guard only read two of the pipeline's files, so a retry stage added
anywhere else on the route would have lengthened the run past the
browser's abort unnoticed. It now sweeps the route and the whole
briefing-request folder for stages and for any call into the background
generator, and the advisor states why its count is the shorter one.

Refs #1274
This commit is contained in:
Marc-André Bombeck committed 2026-10-24 09:29:38 +02:00
1 parent 552510c654
commit 3d47c1e6b4
2 files changed
+27 -6

No files matched your search

+21 -6
View File
@@ -273,12 +273,27 @@ describe("outer bounds around model work allow every call they wait on", () => {
});
it("the on-demand regenerate makes no more calls in a row than the browser waits for", () => {
// The generation and its grounding retry, and the one repeat inside the
// client: FORCE_ADVISOR_MODEL_CALLS in the advisor hook.
expect([
...stagesIn("lib/insights/briefing-request/completion.ts"),
...stagesIn("lib/insights/briefing-request/grounding.ts"),
]).toEqual(["generate", "grounding-retry"]);
// The route's own pipeline: the generation and its grounding retry, and
// the one repeat inside the client (FORCE_ADVISOR_MODEL_CALLS in the
// advisor hook). An unparseable reply or a screened payload is refused,
// not retried, and the background generator is never reached from here,
// which is why this is shorter than COMPREHENSIVE_MODEL_CALLS. A new
// stage anywhere on the route means recounting the browser's abort.
const pipeline = [
"app/api/insights/generate/route.ts",
...walkSourceFiles(join(SRC, "lib/insights/briefing-request"), {
floor: 8,
})
.filter((f) => !f.includes("__tests__"))
.map((f) => `lib/insights/briefing-request/${f}`),
];
expect(pipeline.flatMap(stagesIn).sort()).toEqual([
"generate",
"grounding-retry",
]);
for (const rel of pipeline) {
expect(code(rel), rel).not.toMatch(/\bgenerateComprehensiveInsight\b/);
}
expect(code("components/insights/use-insights-advisor.ts")).toMatch(
/\bFORCE_ADVISOR_MODEL_CALLS\s*=\s*3\b/,
);
@@ -128,6 +128,12 @@ const ADVISOR_TIMEOUT_MS = 8_000;
* answered nothing is asked once more inside the first, so three calls at the
* per-call timeout, at least the briefing's own 180 s default each. Never
* shorter than the server: the abort is only for a request nobody answers.
*
* Fewer than `COMPREHENSIVE_MODEL_CALLS` on purpose: the route runs its own
* pipeline (`src/lib/insights/briefing-request/`), not the background
* generator, and answers an unparseable reply or a screened payload with a
* refusal instead of the generator's JSON retry and screen repair.
* `ai-call-timeout-guard.test.ts` holds that pipeline to these two stages.
*/
const FORCE_ADVISOR_MODEL_CALLS = 3;