From ab723a73a628289e75bae708ef83146899f3e620 Mon Sep 17 00:00:00 2001 From: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com> Date: Sun, 18 Oct 2026 13:34:41 -0400 Subject: [PATCH] fix: apply PLUK_LOG_MAX_BYTES cap even when log has fewer than keepLines lines Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com> --- changelog.d/fixed-283.md | 1 + src/run-dir.ts | 2 +- test/default-fallbacks.test.js | 9 ++++----- test/run-dir.test.js | 15 +++++++++++++++ 4 files changed, 21 insertions(+), 6 deletions(-) create mode 100644 changelog.d/fixed-283.md diff --git a/changelog.d/fixed-283.md b/changelog.d/fixed-283.md new file mode 100644 index 0000000..44186a2 --- /dev/null +++ b/changelog.d/fixed-283.md @@ -0,0 +1 @@ +- Log rotation now applies the `PLUK_LOG_MAX_BYTES` cap even when the log has fewer than `PLUK_LOG_KEEP_LINES` lines (#283). diff --git a/src/run-dir.ts b/src/run-dir.ts index 2ebc453..83d8912 100644 --- a/src/run-dir.ts +++ b/src/run-dir.ts @@ -175,7 +175,6 @@ export function rotateLogFileIfNeeded( const lines = snapshot.toString('utf-8').split('\n'); const hasTrailingNewline = lines.length > 0 && lines[lines.length - 1] === ''; const usableLines = hasTrailingNewline ? lines.slice(0, -1) : lines; - if (usableLines.length <= keepLines) return false; // A snapshot ending mid-line keeps that partial line unterminated so the // rest of it, copied from past the snapshot below, completes it. @@ -188,6 +187,7 @@ export function rotateLogFileIfNeeded( keptBytes -= Buffer.byteLength(kept[drop]!, 'utf-8') + 1; drop++; } + if (drop === 0 && kept.length === usableLines.length) return false; kept = kept.slice(drop); const rebuilt = Buffer.from(kept.join('\n') + (hasTrailingNewline ? '\n' : ''), 'utf-8'); diff --git a/test/default-fallbacks.test.js b/test/default-fallbacks.test.js index f1f070e..65d39b9 100644 --- a/test/default-fallbacks.test.js +++ b/test/default-fallbacks.test.js @@ -94,17 +94,16 @@ test('defaultRunDir falls back to pluk-user when process.getuid is unavailable', // --- rotateLogFileIfNeeded with too few lines to trim ----------------------------- -test('rotateLogFileIfNeeded leaves an oversized log alone when it has no more than keepLines lines', () => { +test('rotateLogFileIfNeeded trims an oversized log with fewer than keepLines lines to the byte cap', () => { const base = mkdtempSync(join(tmpdir(), 'pluk-fallbacks-')); try { const file = join(base, 'wide.jsonl'); - // Two lines, each far past maxBytes: over the byte threshold, but - // trimming to the last 5 lines would keep everything, so no rewrite. + // Two lines, each far past maxBytes: only the newest line is kept. const content = `{"pad":"${'x'.repeat(200)}"}\n{"pad":"${'y'.repeat(200)}"}\n`; writeFileSync(file, content); - assert.strictEqual(rotateLogFileIfNeeded(file, { maxBytes: 10, keepLines: 5 }), false); - assert.equal(readFileSync(file, 'utf-8'), content); + assert.strictEqual(rotateLogFileIfNeeded(file, { maxBytes: 10, keepLines: 5 }), true); + assert.equal(readFileSync(file, 'utf-8'), `{"pad":"${'y'.repeat(200)}"}\n`); } finally { rmSync(base, { recursive: true, force: true }); } diff --git a/test/run-dir.test.js b/test/run-dir.test.js index d5747b8..10e50cb 100644 --- a/test/run-dir.test.js +++ b/test/run-dir.test.js @@ -249,6 +249,21 @@ test('rotateLogFileIfNeeded trims an oversized log to the last N lines', () => { } }); +test('rotateLogFileIfNeeded applies the byte cap when the log has fewer than keepLines lines', () => { + const base = tempBase(); + try { + const file = join(base, 'few.jsonl'); + const lines = Array.from({ length: 6 }, (_, i) => `{"n":${i}}`); + writeFileSync(file, lines.join('\n') + '\n'); + + // Each line is 8 bytes with its newline; 20 bytes fits the last two. + assert.strictEqual(rotateLogFileIfNeeded(file, { maxBytes: 20, keepLines: 100 }), true); + assert.equal(readFileSync(file, 'utf-8'), lines.slice(-2).join('\n') + '\n'); + } finally { + rmSync(base, { recursive: true, force: true }); + } +}); + test('rotateLogFileIfNeeded is a no-op for a missing file', () => { const base = tempBase(); try {