fix(gsd): wire setLogBasePath into engine init to resurrect audit log (#2745)
* fix: wire setLogBasePath into engine init to resurrect audit log _auditBasePath was always null — setLogBasePath() existed but was never called from any production code path. Every logWarning/logError call hit the if (_auditBasePath) guard as false, so nothing was ever written to .gsd/audit-log.jsonl. Two independent fixes: 1. Remove _auditBasePath = null from _resetLogs() — the base path must survive unit resets, it's stable for process lifetime 2. Call setLogBasePath(base) after s.basePath = base in both the fresh- start path (bootstrapAutoSession) and the resume path (startAuto) Adds two tests verifying disk persistence and that _resetLogs doesn't kill the audit path. Fixes #2722 * refactor: clean up audit log tests and avoid redundant mkdirSync - Use makeTempDir/cleanup from test-utils.ts instead of inline mkdtempSync/rmSync - Add afterEach in audit describe block to reset _auditBasePath via setLogBasePath("") — prevents state bleed into subsequent tests since _resetLogs() no longer clears it - Drop four raw imports (mkdtempSync, rmSync, tmpdir — join was already used) - Guard mkdirSync in _push() with _auditDirEnsured flag — was calling mkdirSync on every log entry; now called once per base path * revert: remove _auditDirEnsured flag mkdirSync({ recursive: true }) on an existing dir is a cheap stat, not meaningful overhead on a low-frequency warn/error path. The flag added mutable state for no real gain.
This commit is contained in:
parent
913984c26e
commit
a436f06e2d
4 changed files with 47 additions and 2 deletions
|
|
@ -67,6 +67,7 @@ import {
|
|||
getDebugLogPath,
|
||||
} from "./debug-logger.js";
|
||||
import { parseUnitId } from "./unit-id.js";
|
||||
import { setLogBasePath } from "./workflow-logger.js";
|
||||
import type { AutoSession } from "./auto/session.js";
|
||||
import {
|
||||
existsSync,
|
||||
|
|
@ -461,6 +462,7 @@ export async function bootstrapAutoSession(
|
|||
s.verbose = verboseMode;
|
||||
s.cmdCtx = ctx;
|
||||
s.basePath = base;
|
||||
setLogBasePath(base);
|
||||
s.unitDispatchCount.clear();
|
||||
s.unitRecoveryCount.clear();
|
||||
s.lastBudgetAlertLevel = 0;
|
||||
|
|
|
|||
|
|
@ -114,6 +114,7 @@ import {
|
|||
formatCost,
|
||||
formatTokenCount,
|
||||
} from "./metrics.js";
|
||||
import { setLogBasePath } from "./workflow-logger.js";
|
||||
import { join } from "node:path";
|
||||
import { readFileSync, existsSync, mkdirSync, writeFileSync, unlinkSync } from "node:fs";
|
||||
import { atomicWriteSync } from "./atomic-write.js";
|
||||
|
|
@ -1102,6 +1103,7 @@ export async function startAuto(
|
|||
s.stepMode = requestedStepMode;
|
||||
s.cmdCtx = ctx;
|
||||
s.basePath = base;
|
||||
setLogBasePath(base);
|
||||
s.unitDispatchCount.clear();
|
||||
s.unitLifetimeDispatches.clear();
|
||||
if (!getLedger()) initMetrics(base);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
// GSD Extension — Workflow Logger Tests
|
||||
// Tests for the centralized warning/error accumulator.
|
||||
|
||||
import { describe, test, beforeEach } from "node:test";
|
||||
import { describe, test, beforeEach, afterEach } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { existsSync, readFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { makeTempDir, cleanup } from "./test-utils.ts";
|
||||
import {
|
||||
logWarning,
|
||||
logError,
|
||||
|
|
@ -14,6 +17,7 @@ import {
|
|||
hasAnyIssues,
|
||||
summarizeLogs,
|
||||
formatForNotification,
|
||||
setLogBasePath,
|
||||
_resetLogs,
|
||||
} from "../workflow-logger.ts";
|
||||
|
||||
|
|
@ -222,6 +226,44 @@ describe("workflow-logger", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("audit log persistence", () => {
|
||||
let dir: string;
|
||||
|
||||
beforeEach(() => {
|
||||
dir = makeTempDir("wl-audit-");
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
setLogBasePath("");
|
||||
cleanup(dir);
|
||||
});
|
||||
|
||||
test("writes entry to .gsd/audit-log.jsonl after setLogBasePath", () => {
|
||||
setLogBasePath(dir);
|
||||
logWarning("engine", "audit test entry");
|
||||
|
||||
const auditPath = join(dir, ".gsd", "audit-log.jsonl");
|
||||
assert.ok(existsSync(auditPath), "audit-log.jsonl should exist");
|
||||
const content = readFileSync(auditPath, "utf-8");
|
||||
const entry = JSON.parse(content.trim());
|
||||
assert.equal(entry.severity, "warn");
|
||||
assert.equal(entry.component, "engine");
|
||||
assert.equal(entry.message, "audit test entry");
|
||||
});
|
||||
|
||||
test("_resetLogs does not clear the audit base path", () => {
|
||||
setLogBasePath(dir);
|
||||
_resetLogs();
|
||||
logWarning("engine", "post-reset entry");
|
||||
|
||||
const auditPath = join(dir, ".gsd", "audit-log.jsonl");
|
||||
assert.ok(existsSync(auditPath), "audit-log.jsonl should exist after _resetLogs");
|
||||
const content = readFileSync(auditPath, "utf-8");
|
||||
const entry = JSON.parse(content.trim());
|
||||
assert.equal(entry.message, "post-reset entry");
|
||||
});
|
||||
});
|
||||
|
||||
describe("buffer limit", () => {
|
||||
test("caps at MAX_BUFFER entries, dropping oldest", () => {
|
||||
const OVER = 110;
|
||||
|
|
|
|||
|
|
@ -199,7 +199,6 @@ export function readAuditLog(basePath?: string): LogEntry[] {
|
|||
*/
|
||||
export function _resetLogs(): void {
|
||||
_buffer = [];
|
||||
_auditBasePath = null;
|
||||
}
|
||||
|
||||
// ─── Internal ───────────────────────────────────────────────────────────
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue