qa: hourly in-process heartbeat + run pruning
- runner: QA_HEARTBEAT_MIN (default 60) in-process timer runs a scheduled QA pass; one pulse ~90s after boot. Triage (LLM) skipped on clean scheduled runs to save tokens; only manual runs or runs with errors get a written summary. - qa-finish.php prunes to the 50 most recent runs. - systemd unit exposes QA_HEARTBEAT_MIN for tuning.
This commit is contained in:
parent
56c1926b81
commit
d548d8a8a8
2 changed files with 25 additions and 1 deletions
|
|
@ -796,7 +796,12 @@ async function runQaFlow(trigger) {
|
||||||
runId = JSON.parse(start.out || "{}").run_id || 0;
|
runId = JSON.parse(start.out || "{}").run_id || 0;
|
||||||
if (!runId) throw new Error("could not open a QA run");
|
if (!runId) throw new Error("could not open a QA run");
|
||||||
const { findings, counts } = await runQa();
|
const { findings, counts } = await runQa();
|
||||||
const summary = await qaTriage(findings, counts);
|
// Triage (an LLM call) only when it's worth it: a manual run, or a
|
||||||
|
// scheduled heartbeat that actually found errors. Clean heartbeats get a
|
||||||
|
// cheap templated summary — no tokens spent when the site is fine.
|
||||||
|
const summary = (trigger === "manual" || counts.error > 0)
|
||||||
|
? await qaTriage(findings, counts)
|
||||||
|
: `${counts.error} error(s) and ${counts.warning} warning(s) across ${counts.pages} pages.`;
|
||||||
const tmp = `/tmp/qa-${runId}.json`;
|
const tmp = `/tmp/qa-${runId}.json`;
|
||||||
writeFileSync(tmp, JSON.stringify(findings));
|
writeFileSync(tmp, JSON.stringify(findings));
|
||||||
await phpCli(["api/cli/qa-finish.php", `--run=${runId}`, "--status=done", `--summary=${summary}`, `--counts=${JSON.stringify(counts)}`, `--findings-file=${tmp}`]);
|
await phpCli(["api/cli/qa-finish.php", `--run=${runId}`, "--status=done", `--summary=${summary}`, `--counts=${JSON.stringify(counts)}`, `--findings-file=${tmp}`]);
|
||||||
|
|
@ -993,3 +998,15 @@ if (!existsSync(PREVIEW_DIR)) mkdirSync(PREVIEW_DIR, { recursive: true });
|
||||||
server.listen(PORT, "127.0.0.1", () => {
|
server.listen(PORT, "127.0.0.1", () => {
|
||||||
console.log(`[console] runner on 127.0.0.1:${PORT}, repo ${REPO}`);
|
console.log(`[console] runner on 127.0.0.1:${PORT}, repo ${REPO}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ---- QA heartbeat ------------------------------------------------------------
|
||||||
|
// The runner is long-lived, so the QA heartbeat is just an in-process timer —
|
||||||
|
// no cron, no extra units. Runs a scheduled QA pass every QA_HEARTBEAT_MIN
|
||||||
|
// minutes (0 disables), plus one pulse shortly after boot. runQaFlow self-guards
|
||||||
|
// against overlap and is read-only against the live site.
|
||||||
|
const QA_HEARTBEAT_MIN = Number(process.env.QA_HEARTBEAT_MIN || 60);
|
||||||
|
if (QA_HEARTBEAT_MIN > 0) {
|
||||||
|
setTimeout(() => runQaFlow("scheduled"), 90_000); // first pulse ~90s after boot
|
||||||
|
setInterval(() => runQaFlow("scheduled"), QA_HEARTBEAT_MIN * 60_000);
|
||||||
|
console.log(`[qa] heartbeat every ${QA_HEARTBEAT_MIN} min`);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -61,4 +61,11 @@ foreach ($findings as $f) {
|
||||||
$upd = $pdo->prepare('UPDATE cja_qa_runs SET status = ?, summary = ?, counts = ?, finished_at = NOW() WHERE run_id = ?');
|
$upd = $pdo->prepare('UPDATE cja_qa_runs SET status = ?, summary = ?, counts = ?, finished_at = NOW() WHERE run_id = ?');
|
||||||
$upd->execute([$status, $summary, $counts, $runId]);
|
$upd->execute([$status, $summary, $counts, $runId]);
|
||||||
|
|
||||||
|
// Prune to the 50 most recent runs (the heartbeat runs often).
|
||||||
|
$cut = (int) ($pdo->query('SELECT run_id FROM cja_qa_runs ORDER BY run_id DESC LIMIT 1 OFFSET 50')->fetchColumn() ?: 0);
|
||||||
|
if ($cut > 0) {
|
||||||
|
$pdo->prepare('DELETE FROM cja_qa_findings WHERE run_id < ?')->execute([$cut]);
|
||||||
|
$pdo->prepare('DELETE FROM cja_qa_runs WHERE run_id < ?')->execute([$cut]);
|
||||||
|
}
|
||||||
|
|
||||||
echo json_encode(['ok' => true, 'run_id' => $runId, 'findings' => $n]);
|
echo json_encode(['ok' => true, 'run_id' => $runId, 'findings' => $n]);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue