Skip to content

Commit 4a9a556

Browse files
fix(config): reject zero-value resetArchiveRetention to prevent silent data loss (#104516)
* fix(config): reject zero-value resetArchiveRetention to prevent silent data loss * fix: add braces to guard clauses for curly lint rule * fix: normalize numeric values and cover pruneAfter fallback in migration * fix: reject zero-value pruneAfter at schema level to close fallback gap * fix: independently check and repair both retention fields in migration * fix: split doctor diagnostics into field-specific rules and messages * fix: remove unused local in migration test helper * fix: repair 5 lint errors (curly braces, zero-fraction, narrow value) * fix(doctor): rewrite zero resetArchiveRetention to false instead of deleting it Deleting a zero resetArchiveRetention caused the runtime to fall back to pruneAfter (default 30d), which contradicted the doctor output that claimed archives would be kept indefinitely. Only an explicit false value produces indefinite retention per the documented schema. - P1: Rewrite zero resetArchiveRetention to false in the doctor migration apply callback, matching the promised indefinite-retention behavior. - P3: Update the pruneAfter rule warning to describe eligible stale/non-preserved session entries instead of "all sessions". * fix(config): repair zero retention safely --------- Co-authored-by: Peter Steinberger <[email protected]> Co-authored-by: Peter Steinberger <[email protected]>
1 parent 067bc66 commit 4a9a556

8 files changed

Lines changed: 476 additions & 5 deletions

docs/gateway/config-agents.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1328,7 +1328,7 @@ See [Multi-Agent Sandbox & Tools](/tools/multi-agent-sandbox-tools) for preceden
13281328
- `maxEntries`: maximum number of SQLite session entries (default `500`). Runtime writes batch cleanup with a small high-water buffer for production-sized caps; `openclaw sessions cleanup --enforce` applies the cap immediately.
13291329
- Short-lived gateway model-run probe sessions use fixed `24h` retention, but cleanup is pressure-gated: it only removes stale strict model-run probe rows when session-entry maintenance/cap pressure is reached. Only strict explicit probe keys matching `agent:*:explicit:model-run-<uuid>` are eligible; normal direct, group, thread, cron, hook, heartbeat, ACP, and sub-agent sessions do not inherit this 24h retention. When model-run cleanup runs, it runs before the broader `pruneAfter` stale-entry cleanup and `maxEntries` cap.
13301330
- Legacy `rotateBytes` is rejected by the current schema; `openclaw doctor --fix` removes it from older configs.
1331-
- `resetArchiveRetention`: retention for `*.reset.<timestamp>` transcript archives. Defaults to `pruneAfter`; set `false` to disable.
1331+
- `resetArchiveRetention`: age-based retention for reset/deleted transcript archives. By default, archives remain until disk-budget eviction; set a duration to opt into wall-clock deletion, or `false` to disable it explicitly.
13321332
- `maxDiskBytes`: optional sessions-directory disk budget. In `warn` mode it logs warnings; in `enforce` mode it removes oldest artifacts/sessions first.
13331333
- `highWaterBytes`: optional target after budget cleanup. Defaults to `80%` of `maxDiskBytes`.
13341334
- **`writeLock`**: session transcript write-lock controls. Tune only when legitimate transcript prep, cleanup, compaction, or mirror work contends longer than the default policies.

src/commands/doctor-session-transcripts.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,8 +257,10 @@ describe("doctor session transcript repair", () => {
257257
},
258258
});
259259
const env = { ...process.env, OPENCLAW_STATE_DIR: root };
260+
const cfg = {};
260261

261262
await noteSessionTranscriptHealth({
263+
cfg,
262264
env,
263265
sessionDirs: [sessionsDir],
264266
sessionSqlite: true,
@@ -267,6 +269,7 @@ describe("doctor session transcript repair", () => {
267269

268270
expect(runDoctorSessionSqlite).toHaveBeenCalledWith({
269271
allAgents: true,
272+
cfg,
270273
env,
271274
mode: "import",
272275
});
@@ -301,8 +304,10 @@ describe("doctor session transcript repair", () => {
301304
},
302305
});
303306
const env = { ...process.env, OPENCLAW_STATE_DIR: root };
307+
const cfg = {};
304308

305309
await noteSessionTranscriptHealth({
310+
cfg,
306311
env,
307312
sessionDirs: [sessionsDir],
308313
sessionSqlite: true,
@@ -311,6 +316,7 @@ describe("doctor session transcript repair", () => {
311316

312317
expect(runDoctorSessionSqlite).toHaveBeenCalledWith({
313318
allAgents: true,
319+
cfg,
314320
env,
315321
mode: "dry-run",
316322
});

src/commands/doctor-session-transcripts.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
scanSessionTranscriptTree,
1717
selectSessionTranscriptTreePathNodes,
1818
} from "../config/sessions/transcript-tree.js";
19+
import type { OpenClawConfig } from "../config/types.openclaw.js";
1920
import type { HealthFinding, HealthRepairEffect } from "../flows/health-checks.js";
2021
import { shortenHomePath } from "../utils.js";
2122
import { withDoctorSqliteMaintenanceLock } from "./doctor-sqlite-maintenance-lock.js";
@@ -433,6 +434,7 @@ export function sessionTranscriptIssueToRepairEffect(
433434

434435
/** Scans session transcript files and reports or repairs legacy/broken transcript state. */
435436
export async function noteSessionTranscriptHealth(params?: {
437+
cfg?: OpenClawConfig;
436438
env?: NodeJS.ProcessEnv;
437439
sessionSqlite?: boolean;
438440
shouldRepair?: boolean;
@@ -484,13 +486,15 @@ export async function noteSessionTranscriptHealth(params?: {
484486

485487
if (params?.sessionDirs === undefined || params.sessionSqlite === true) {
486488
await noteSessionSqliteMigrationHealth({
489+
cfg: params?.cfg,
487490
env: params?.env ?? process.env,
488491
shouldRepair,
489492
});
490493
}
491494
}
492495

493496
async function noteSessionSqliteMigrationHealth(params: {
497+
cfg?: OpenClawConfig;
494498
env: NodeJS.ProcessEnv;
495499
shouldRepair: boolean;
496500
}): Promise<void> {
@@ -500,6 +504,7 @@ async function noteSessionSqliteMigrationHealth(params: {
500504
const runSessionSqlite = async () =>
501505
await runDoctorSessionSqlite({
502506
allAgents: true,
507+
...(params.cfg ? { cfg: params.cfg } : {}),
503508
env: params.env,
504509
mode: params.shouldRepair ? "import" : "dry-run",
505510
});

0 commit comments

Comments
 (0)