Skip to content

fix(logs): only report rotation when the log file actually shrank#74252

Open
BSG2000 wants to merge 3 commits into
openclaw:mainfrom
BSG2000:fix/log-tail-rotated-message
Open

fix(logs): only report rotation when the log file actually shrank#74252
BSG2000 wants to merge 3 commits into
openclaw:mainfrom
BSG2000:fix/log-tail-rotated-message

Conversation

@BSG2000

@BSG2000 BSG2000 commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

PR title: fix(logs): only report rotation when the log file actually shrank


Summary

  • Problem: openclaw logs --follow prints Log cursor reset (file rotated). to stderr whenever the gateway log grows faster than --max-bytes between polls, even though no rotation has occurred — the cursor is still valid and the tail was just truncated to fit the byte budget.
  • Why it matters: Operators watching busy gateways see false "file rotated" notices on every burst (e.g. during a noisy startup or while a chatty channel pumps logs). The accompanying Log tail truncated (increase --max-bytes). line is the correct signal; the rotation line muddies it and trains readers to ignore both.
  • What changed: In src/logging/log-tail.ts::readLogSlice, only set reset = true in the true rotation case (cursor > size — the file shrank below the previous cursor). The fast-growth branch (size - start > maxBytes) now sets truncated = true only, since the cursor it returned remains contiguous with what the caller saw last poll.
  • What did NOT change (scope boundary): The LogTailPayload shape, the rotation-case behavior, the Log tail truncated message, and the byte-budget arithmetic are all unchanged. No CLI strings, schema, or downstream consumers were modified — src/logging/diagnostic-support-export.ts and src/cli/logs-cli.ts continue to work with the same fields.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration (logging is shared infra)
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX (stderr notice text)
  • CI/CD / infra

Linked Issue/PR

Root Cause (if applicable)

  • Root cause: readLogSlice conflated two situations into a single reset flag:
    1. cursor > size — the previously-returned cursor is past EOF; the file was rotated/truncated, so the cursor is meaningless and we must restart from the new tail.
    2. size - start > maxBytes — the file grew faster than the per-poll byte budget; the cursor is still valid, we just can't read everything that was appended in one poll.
      Only case (1) is a real reset/rotation. Case (2) was incorrectly being flagged as "rotated" because both branches set reset = true.
  • Missing detection / guardrail: log-tail.test.ts only covered the redaction path; nothing pinned the meaning of reset vs truncated.
  • Contributing context: The CLI side (src/cli/logs-cli.ts:402) hard-codes Log cursor reset (file rotated). for payload.reset, so the conflation surfaced as a misleading user-visible string rather than a silent boolean.

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file: src/logging/log-tail.test.ts
  • Scenarios the test locks in:
    1. File grows by >> maxBytes between two polls with the same valid cursor → truncated: true, reset: false, lines returned.
    2. File shrinks below the previous cursor (real rotation) → reset: true.
  • Why this is the smallest reliable guardrail: the bug is at the readLogSlice boundary; both scenarios can be exercised end-to-end against a real temp log file using the existing setLoggerOverride test seam.
  • Existing test that already covers this (if any): none.
  • If no new test is added, why not: N/A.

User-visible / Behavior Changes

openclaw logs --follow (and JSON-mode --json) no longer emits the Log cursor reset (file rotated). notice during high-throughput writes. The existing Log tail truncated (increase --max-bytes). notice still fires in that case, which is the actionable signal. The notice still fires correctly when the log is genuinely rotated/truncated.

Diagram (if applicable)

N/A

Security Impact (required)

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? No
  • New/changed network calls? No
  • Command/tool execution surface changed? No
  • Data access scope changed? No
  • N/A

Repro + Verification

Environment

  • OS: Ubuntu 24.04 (WSL) / Windows 11 host
  • Runtime/container: Node 22 (pnpm dev)
  • Model/provider: N/A
  • Integration/channel (if any): N/A — gateway logs only
  • Relevant config (redacted): default loggerSettings

Steps (to reproduce the original misleading notice on main)

  1. Start a gateway with anything that writes a steady stream of log lines (e.g. a chatty channel or openclaw status --watch against a busy gateway).
  2. In another terminal: openclaw logs --follow --max-bytes 4000.
  3. Wait for a write burst that exceeds 4 KB between two polls.

Expected

Only Log tail truncated (increase --max-bytes). is printed to stderr while the burst is being caught up.

Actual (before this PR)

Both Log tail truncated (increase --max-bytes). and Log cursor reset (file rotated). are printed, even though the file was never rotated.

After this PR

Only the truthful Log tail truncated notice is printed during fast growth. The rotation notice still fires when the file is actually rotated/truncated (covered by the second new test).

Evidence

  • Failing test/log before + passing after (see new tests in src/logging/log-tail.test.ts)
  • Trace/log snippets (see "Repro + Verification")
  • Screenshot/recording
  • Perf numbers (if relevant)

Local verification:

# Targeted unit lane
pnpm test src/logging/log-tail.test.ts
# Expect: 3 tests pass (1 existing + 2 new)

Note: I was unable to run the full repository test sweep locally (the dev environment is on a WSL/Windows-mounted filesystem that pnpm refuses to install into with ERR_PNPM_EACCES). The fix is a 5-line local change inside readLogSlice with no schema, no public-API, and no other call-site impact, and the new tests exercise the exact two paths affected. CI in this repo runs the full lane.

Human Verification (required)

  • Verified scenarios: fast file growth between polls → no rotation message; real rotation (file shrunk) → rotation message still emitted.
  • Edge cases checked: initial poll with no cursor still uses start = max(0, size - maxBytes) and reports truncated only (unchanged); empty file still returns cursor: size, lines: [] (unchanged); cursor inside [start, size] window with size - start <= maxBytes is unchanged.
  • What I did NOT verify: live CLI output against a running daemon during a real burst (will rely on CI + reviewer spot-check).

Review Conversations

  • I will reply to or resolve every bot review conversation I address in this PR.
  • I will leave unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? Yes
  • Config/env changes? No
  • Migration needed? No
  • N/A

Risks and Mitigations

  • Risk: a downstream consumer relies on reset === true to mean "skipped some lines" rather than "the cursor is no longer meaningful".
    • Mitigation: I grepped the repo for payload.reset / LogTailPayload. The only consumers are src/cli/logs-cli.ts (renders the user-visible string this PR is correcting) and src/logging/diagnostic-support-export.ts::sanitizeLogTail (passes the boolean through verbatim into the support bundle). Both are display-only; neither uses reset to decide whether lines were skipped — they use the existing truncated flag for that.

AI/Vibe-Coded PRs Welcome! 🤖

  • AI-assisted PR. Drafted with help from Claude (Opus 4.7) via the GitHub Copilot CLI agent harness.
  • Degree of testing: lightly tested — new unit tests added; full repo lane not run locally (see note above).
  • Prompts/session logs: available on request.
  • I understand exactly what the code does (5-line semantic narrowing of when reset is asserted in readLogSlice).
  • codex review --base origin/main not run locally (no Codex access in this environment); happy to address Codex bot comments after the PR opens.
  • Will resolve bot review conversations after addressing them.

Real behavior proof

Behavior or issue addressed: readLogSlice in src/logging/log-tail.ts raised the reset flag (rendered by the CLI as Log cursor reset (file rotated).) whenever the log grew faster than --max-bytes between two polls, even though no rotation happened — the cursor was still valid and only the tail was byte-budget truncated. Operators watching a busy gateway saw false "file rotated" notices on every burst.

Real environment tested: Local Node v22 runtime (Ubuntu 24.04 / WSL) driving the actual shipped readConfiguredLogTail export from this branch through node --import tsx, pointed at a real on-disk log file via the public setLoggerOverride({ file }) seam. The harness writes, appends to, and rotates a genuine temp log file on disk and reads it back through the real code path — no fakes for the slice logic.

Exact steps or command run after this patch:

node --import tsx ./proof_logtail.ts
// proof_logtail.ts seeds a real temp log file, reads it once to get a cursor,
// appends ~400 lines (far beyond maxBytes=4000) to the SAME file (fast growth,
// no rotation), then truncates the file below the cursor (real rotation), reading
// the real readConfiguredLogTail payload at each step. Run against upstream/main and this branch.

Evidence after fix: Real console output captured from the command above, run first against the current upstream/main slice logic, then against this branch:

########## BEFORE PATCH (current upstream/main log-tail.ts) ##########
Poll 1 (seed) -> cursor: 54 reset: false truncated: false
Poll 2 (fast growth) -> reset: true truncated: true lines: 85
Poll 3 (real rotation) -> reset: true truncated: false lines: 1

########## AFTER PATCH (this branch log-tail.ts) ##########
Poll 1 (seed) -> cursor: 54 reset: false truncated: false
Poll 2 (fast growth) -> reset: false truncated: true lines: 85
Poll 3 (real rotation) -> reset: true truncated: false lines: 1

Observed result after fix: Before the change, Poll 2 (fast growth, same file) reported reset: true — a false rotation signal. After the change the identical real scenario reports reset: false truncated: true, while Poll 3 (the file genuinely shrinking below the cursor) still correctly reports reset: true. The misleading rotation notice is gone for fast growth and preserved for real rotation.

What was not tested: Did not drive the openclaw logs --follow CLI presentation layer against a live long-running daemon during an organic write burst; the proof targets readLogSlice where the flag is set. The CLI string mapping of reset is unchanged by this PR.

@greptile-apps

greptile-apps Bot commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a false-positive "file rotated" notice in openclaw logs --follow by removing reset = true from the fast-growth branch inside readLogSlice. The fix correctly narrows reset to the one case where it is semantically meaningful (cursor > size), and two new unit tests lock in both the fast-growth and genuine-rotation paths.

Confidence Score: 5/5

This PR is safe to merge — it is a minimal, well-reasoned semantic fix with no schema changes and new tests covering both affected paths.

The change is a 5-line narrowing of when reset is asserted; the LogTailPayload shape, all downstream consumers, and byte-budget arithmetic are untouched. Both the bug (false rotation report) and the regression guard (two new unit tests) are present and correct.

No files require special attention.

Reviews (1): Last reviewed commit: "fix(logs): only report rotation when the..." | Re-trigger Greptile

Re-review progress:

@clawsweeper

clawsweeper Bot commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed July 3, 2026, 4:15 PM ET / 20:15 UTC.

Summary
The branch narrows log-tail reset semantics, adds optional logs.tail.skippedBytes metadata, updates CLI and Control UI handling/tests, and refreshes generated Swift protocol output.

PR surface: Source +13, Tests +113, Other +4. Total +130 across 7 files.

Reproducibility: yes. Source inspection gives a high-confidence path: read once, append more than maxBytes without rotating, then read with the old cursor; current main sets reset: true, and the CLI maps that flag to the false rotation notice.

Review metrics: 1 noteworthy metric.

  • Gateway response fields: 1 optional result field added. logs.tail.skippedBytes changes the strict protocol schema and generated Swift model, so maintainers should notice the API contract before merge.

Stored data model
Persistent data-model change detected: serialized state: src/logging/log-tail.test.ts. Confirm migration or upgrade compatibility proof before merge.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🦞 diamond lobster
Patch quality: 🐚 platinum hermit
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • none.

Risk before merge

  • [P1] Merging makes optional logs.tail.skippedBytes part of the gateway response contract and generated Swift model; it is additive and validated, but green CI does not replace maintainer acceptance of that API surface.

Maintainer options:

  1. Accept additive skipped-window metadata (recommended)
    Approve skippedBytes as the optional logs.tail result field needed for Control UI re-anchoring, then merge after normal maintainer review.
  2. Split the protocol addition
    Ask for the branch to keep only the reset/truncation bug fix and move skippedBytes plus Control UI re-anchoring into a separate protocol-surface PR.

Next step before merge

  • [P2] A maintainer needs to accept or split the optional logs.tail.skippedBytes protocol response field; there is no narrow automated code repair left.

Security
Cleared: No concrete security or supply-chain concern was found; the diff changes log-tail state handling, tests, a TypeBox schema, UI consumption, and generated Swift output without new dependencies, secrets, permissions, CI, downloads, or execution paths.

Review details

Best possible solution:

Land the reset/truncation fix with skippedBytes if maintainers accept the additive logs.tail response contract; otherwise split the protocol/UI re-anchor work from the reset-only bug fix.

Do we have a high-confidence way to reproduce the issue?

Yes. Source inspection gives a high-confidence path: read once, append more than maxBytes without rotating, then read with the old cursor; current main sets reset: true, and the CLI maps that flag to the false rotation notice.

Is this the best way to solve the issue?

Yes, conditionally. Fixing readLogSlice is the right layer, and skippedBytes is a narrow additive signal for UI re-anchoring if maintainers accept the protocol addition.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 5361e5a0b455.

Label changes

Label justifications:

  • P2: The PR fixes misleading operator-facing log-follow feedback with limited blast radius across CLI, gateway protocol, and Control UI logs.
  • merge-risk: 🚨 compatibility: The diff adds optional logs.tail.skippedBytes response metadata and a generated Swift model member for protocol consumers.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The PR body and follow-up comments include after-fix terminal output from a real on-disk log-tail harness showing fast growth no longer reports reset while true shrink still does.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body and follow-up comments include after-fix terminal output from a real on-disk log-tail harness showing fast growth no longer reports reset while true shrink still does.
Evidence reviewed

PR surface:

Source +13, Tests +113, Other +4. Total +130 across 7 files.

View PR surface stats
Area Files Added Removed Net
Source 3 18 5 +13
Tests 3 113 0 +113
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 1 5 1 +4
Total 7 136 6 +130

What I checked:

  • Repository policy: Root policy was read fully and treats gateway protocol changes as additive-first compatibility-sensitive surfaces that need whole-path review. (AGENTS.md:73, 5361e5a0b455)
  • Current main bug: Current main still sets reset = true when a valid cursor is merely more than maxBytes behind, conflating fast growth with rotation. (src/logging/log-tail.ts:96, 5361e5a0b455)
  • CLI user-visible mapping: The CLI renders payload.truncated as the truncation notice and payload.reset as Log cursor reset (file rotated)., so the current source conflation reaches operators. (src/cli/logs-cli.ts:789, 5361e5a0b455)
  • Latest release behavior: Release v2026.6.11 contains the same fast-growth branch setting reset = true and has no skippedBytes result field. (src/logging/log-tail.ts:96, e085fa1a3ffd)
  • PR source fix: At PR head, fast growth keeps reset false, computes skippedBytes, and leaves the true shrink/rotation branch as the reset path. (src/logging/log-tail.ts:100, f00e27bffd26)
  • Protocol addition: The PR adds optional skippedBytes to the strict LogsTailResultSchema and refreshes the generated Swift LogsTailResult model. (packages/gateway-protocol/src/schema/logs-chat.ts:25, f00e27bffd26)

Likely related people:

  • steipete: Git history shows Peter Steinberger introduced the log-tail helper and local logs fallback that contain the reset/truncation behavior under review. (role: original logging surface contributor; confidence: high; commits: 306fe841f54b, 64fc3c068ddd; files: src/logging/log-tail.ts, src/cli/logs-cli.ts, src/gateway/server-methods/logs.ts)
  • joshavant: Recent current-main history on the Control UI logs controller includes focused logs payload-shape work before this PR's re-anchor change. (role: recent adjacent UI contributor; confidence: medium; commits: b658e5d35ccc; files: ui/src/ui/controllers/logs.ts)
  • anyech: The merged adjacent logs-follow PR changed nearby CLI follow-loop behavior and is useful routing context, though it does not supersede this reset/truncation fix. (role: adjacent logs-follow contributor; confidence: medium; commits: bd5bf4820ab3; files: src/cli/logs-cli.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@BSG2000

BSG2000 commented May 2, 2026

Copy link
Copy Markdown
Contributor Author

Addressed @clawsweeper [P3] in f729cbc: added an Unreleased > Fixes CHANGELOG.md entry crediting the contribution. No code change; pnpm vitest run src/logging/log-tail.test.ts → 3/3 green locally.

@BSG2000
BSG2000 force-pushed the fix/log-tail-rotated-message branch from f729cbc to f6298d9 Compare May 6, 2026 09:36
@openclaw-barnacle openclaw-barnacle Bot added the triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. label May 6, 2026
BSG2000 added a commit to BSG2000/openclaw that referenced this pull request May 7, 2026
…sage

Address review feedback on PR openclaw#74252 (P3 from clawsweeper): add
Unreleased > Fixes entry per CONTRIBUTING policy for user-facing fixes.
No code change; log-tail.test.ts: 3/3 green.

Co-authored-by: Copilot <[email protected]>
@BSG2000
BSG2000 force-pushed the fix/log-tail-rotated-message branch from 4868f27 to fa08a97 Compare May 7, 2026 06:42
@BSG2000

BSG2000 commented May 18, 2026

Copy link
Copy Markdown
Contributor Author

Comment for PR #74252 — fix(logs): only report rotation when the log file actually shrank

Real-behavior proof against the latest published release ([email protected], current on npm)

Repro script — pulls the compiled readLogSlice out of the published 2026.5.12 tarball, then drives it through a "file grew fast, no rotation" sequence:

mkdir -p /tmp/openclaw-512 && cd /tmp/openclaw-512
npm pack [email protected] >/dev/null && tar xzf openclaw-2026.5.12.tgz
python3 - <<'PY'
import re
src = open('package/dist/log-tail-ByVTdGQi.js').read()
m = re.search(r'async function readLogSlice\(.*?\n\}', src, re.S)
open('/tmp/lt_512.js', 'w').write(m.group(0))
PY
node --input-type=module -e "
import { readFileSync, writeFileSync, appendFileSync, unlinkSync } from 'node:fs';
import * as fsp from 'node:fs/promises';
import { tmpdir } from 'node:os'; import { join } from 'node:path';
const shim = 'const MAX_LIMIT=5e3;const MAX_BYTES=1e6;const clamp=(v,lo,hi)=>Math.max(lo,Math.min(hi,Number(v)||lo));';
const readLogSlice = new Function('fs', shim + readFileSync('/tmp/lt_512.js','utf8') + '\nreturn readLogSlice;')(fsp);
const file = join(tmpdir(), 'oc-repro.log');
writeFileSync(file, 'A'.repeat(200)+'\n');
const initial = await readLogSlice({ file, maxBytes: 1024, limit: 100 });
appendFileSync(file, 'B'.repeat(5000)+'\n');             // file grows; NOT rotated
const fast = await readLogSlice({ file, maxBytes: 512, limit: 100, cursor: initial.cursor });
console.log('fast-growth:', { truncated: fast.truncated, reset: fast.reset });
unlinkSync(file);
"

Output on a clean Ubuntu 24.04 / Node 22.22.0 host, against [email protected]:

fast-growth: { truncated: true, reset: true }

Expected: { truncated: true, reset: false } — the file was not rotated, it just grew faster than --max-bytes. With reset: true, src/cli/logs-cli.ts emits both notices to the user:

Log tail truncated (increase --max-bytes).
Log cursor reset (file rotated).

…even though no rotation occurred. After this PR's one-line removal of reset = true from the fast-growth branch, the same script returns { truncated: true, reset: false } and the false "file rotated" notice disappears, while the true cursor > size rotation branch still sets reset: true (covered by the second test in this PR).

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented May 18, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

Re-review progress:

@clawsweeper clawsweeper Bot added rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. labels May 18, 2026
@BSG2000
BSG2000 force-pushed the fix/log-tail-rotated-message branch from fa08a97 to adc1507 Compare May 18, 2026 11:02
@BSG2000

BSG2000 commented May 18, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto current main and merged in upstream changes; current HEAD is fd73644d6f. Only conflict was in CHANGELOG.md (a new top entry was added under ## Unreleased > ### Fixes upstream); resolved by keeping both entries. No code changes vs. previously-reviewed diff.

GitHub now reports mergeable=true. Real-behavior proof from the earlier comment still applies against the latest published [email protected].

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented May 18, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

Re-review progress:

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. and removed rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. labels May 18, 2026
@BSG2000

BSG2000 commented May 19, 2026

Copy link
Copy Markdown
Contributor Author

Follow-up: after-fix real behavior proof + UI gap contract response

1) After-fix proof (addresses "add redacted after-fix terminal output" rank-up move)

Same harness as the earlier comment, this PR's one-line removal of reset = true applied to the compiled readLogSlice extracted from [email protected]:

mkdir -p /tmp/openclaw-512 && cd /tmp/openclaw-512
npm pack [email protected] >/dev/null && tar xzf openclaw-2026.5.12.tgz
python3 - <<'PY'
import re
src = open('package/dist/log-tail-ByVTdGQi.js').read()
m = re.search(r'async function readLogSlice\(.*?\n\}', src, re.S)
open('/tmp/lt_orig.js', 'w').write(m.group(0))
# Same one-line removal this PR makes in the fast-growth `if (size - start > maxBytes) { ... }` block.
patched = re.sub(r'(if \(size - start > maxBytes\) \{\s*)reset = true;\s*', r'\1', m.group(0), count=1)
open('/tmp/lt_fixed.js', 'w').write(patched)
PY
node --input-type=module -e "
import { readFileSync, writeFileSync, appendFileSync, unlinkSync, existsSync } from 'node:fs';
import * as fsp from 'node:fs/promises';
import { tmpdir } from 'node:os'; import { join } from 'node:path';
const shim = 'const MAX_LIMIT=5e3;const MAX_BYTES=1e6;const clamp=(v,lo,hi)=>Math.max(lo,Math.min(hi,Number(v)||lo));';
async function run(label, path) {
  const readLogSlice = new Function('fs', shim + readFileSync(path,'utf8') + '\nreturn readLogSlice;')(fsp);
  // (a) fast-growth, NOT rotated
  const f1 = join(tmpdir(), 'oc-grow-' + label + '.log');
  if (existsSync(f1)) unlinkSync(f1);
  writeFileSync(f1, 'A'.repeat(200)+'\n');
  const i1 = await readLogSlice({ file: f1, maxBytes: 1024, limit: 100 });
  appendFileSync(f1, 'B'.repeat(5000)+'\n');
  const r1 = await readLogSlice({ file: f1, maxBytes: 512, limit: 100, cursor: i1.cursor });
  console.log(label + ' fast-growth (file NOT rotated):', { truncated: r1.truncated, reset: r1.reset });
  // (b) true rotation (file shrank)
  const f2 = join(tmpdir(), 'oc-rot-' + label + '.log');
  if (existsSync(f2)) unlinkSync(f2);
  writeFileSync(f2, 'X'.repeat(10000)+'\n');
  const i2 = await readLogSlice({ file: f2, maxBytes: 4096, limit: 100 });
  writeFileSync(f2, 'Y'.repeat(100)+'\n');
  const r2 = await readLogSlice({ file: f2, maxBytes: 4096, limit: 100, cursor: i2.cursor });
  console.log(label + ' true-rotation  (file shrank)   :', { truncated: r2.truncated, reset: r2.reset });
  unlinkSync(f1); unlinkSync(f2);
}
await run('before', '/tmp/lt_orig.js');
await run('after ', '/tmp/lt_fixed.js');
"

Output on a clean Ubuntu 24.04 / Node 22.22.0 host:

before fast-growth (file NOT rotated): { truncated: true, reset: true  }   ← wrong: emits the false "Log cursor reset (file rotated)." notice
before true-rotation  (file shrank)   : { truncated: false, reset: true  }
after  fast-growth (file NOT rotated): { truncated: true, reset: false }   ← correct: only the truthful "Log tail truncated (increase --max-bytes)." notice
after  true-rotation  (file shrank)   : { truncated: false, reset: true  } ← unchanged: real rotation still flagged via the upper `cursor > size` branch

So this PR removes the false rotation message on fast growth and preserves real rotation reporting on shrink. The second test in this PR pins that the cursor > size branch still sets reset: true.

2) Response to the "skipped fast-growth chunk appends across an omitted gap" concern

Worth being explicit about this — it deserves a clear answer rather than just an attempt to flip the rating:

  • The gap is intrinsic to polling-based tail with --max-bytes, not introduced by this PR. Whenever size - cursor > maxBytes, readLogSlice already moves start forward to size - maxBytes and returns truncated: true. The user-facing CLI (src/cli/logs-cli.ts:407,449) renders this as Log tail truncated (increase --max-bytes). — i.e. the omitted-gap signal is already part of the contract, it just doesn't masquerade as a rotation anymore.
  • Pre-PR behavior was strictly worse for the UI, not better. With the spurious reset: true the consumer would clear and re-anchor to the new window every fast-growth poll, which is exactly the "gap with no signal" outcome the rank-up move flags. Post-PR, the truncation flag carries the same "you missed some bytes" signal honestly; the rotation message is reserved for actual rotations.
  • If the Control UI wants finer rendering (e.g. a ... [N bytes skipped] ... separator instead of a banner), the contract change that unlocks it is exposing a skippedBytes (number) on the result — strictly additive, no consumer breaks. I'm happy to do that as a follow-up PR rather than scope-creep this one-line correctness fix; the current PR already lands the necessary primitive (truncated set, reset not).

Current HEAD: fd73644d6f (rebased + merged onto current main; only conflict was a non-overlapping CHANGELOG.md entry; no code changes vs. previously-reviewed diff).

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented May 19, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

Re-review progress:

@BSG2000

BSG2000 commented May 19, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

(Previous targeted re-review for this PR got cancelled by concurrent runs; the after-fix proof and UI-gap contract response are in the comment above.)

@clawsweeper

clawsweeper Bot commented May 19, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

Re-review progress:

@clawsweeper clawsweeper Bot added rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels May 19, 2026
@openclaw-barnacle openclaw-barnacle Bot added the gateway Gateway runtime label Jun 16, 2026
@BSG2000

BSG2000 commented Jun 16, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the remaining ClawSweeper contract blocker in 85e1470cc5.

What changed:

  • kept logs.tail.reset narrow for real cursor invalidation / file shrink / rotation
  • added additive skippedBytes metadata when fast growth forces the reader to advance from a still-valid cursor to the bounded tail window
  • updated the Control UI logs controller to re-anchor/replace entries when skippedBytes > 0, so it no longer appends across an omitted gap while still avoiding the misleading rotation notice
  • added log-tail and Control UI coverage for the skipped-window contract

Validation:

npm exec --yes [email protected] -- node scripts/run-vitest.mjs run src/logging/log-tail.test.ts src/cli/logs-cli.test.ts ui/src/ui/controllers/logs.node.test.ts
✓ 37 tests passed across 3 shards

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

Re-review progress:

@BSG2000

BSG2000 commented Jun 16, 2026

Copy link
Copy Markdown
Contributor Author

Follow-up pushed in e970325897 for the red type checks from the previous skippedBytes commit.

The new skippedBytes protocol field is additive/optional in the gateway schema, so the TypeScript LogTailPayload type now matches that compatibility contract (skippedBytes?: number). That fixes existing test fixtures and consumers that construct log-tail payloads without the new field.

Validation:

npm exec --yes [email protected] -- node scripts/run-vitest.mjs run src/logging/log-tail.test.ts src/cli/logs-cli.test.ts ui/src/ui/controllers/logs.node.test.ts
✓ 37 tests passed across 3 shards

npm exec --yes [email protected] -- run tsgo:core:test
npm exec --yes [email protected] -- run tsgo:test:ui
npm exec --yes [email protected] -- run tsgo:core
✓ all passed

@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label Jun 16, 2026
@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. labels Jun 17, 2026
@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. and removed rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. labels Jun 24, 2026
@BSG2000

BSG2000 commented Jun 26, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the remaining waiting on author blocker from the unstable checks-fast-bundled-protocol lane.

Root cause: the PR added logs.tail.skippedBytes, but the generated Swift protocol model had not been refreshed, so pnpm protocol:check found a diff.

Fix pushed in f00e27bffd2:

  • refreshed apps/shared/OpenClawKit/Sources/OpenClawProtocol/GatewayModels.swift so LogsTailResult includes skippedBytes.

Validation:

  • node scripts/run-vitest.mjs run --config test/vitest/vitest.bundled.config.ts
  • node --import tsx scripts/protocol-gen.ts
  • node --import tsx scripts/protocol-gen-swift.ts
  • verified the only generated diff was the missing Swift skippedBytes model update before committing it.

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@clawsweeper clawsweeper Bot added rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. rating: 🦞 diamond lobster Very strong PR readiness with only minor maintainer review expected. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. rating: 🦞 diamond lobster Very strong PR readiness with only minor maintainer review expected. labels Jun 26, 2026
A fast-growing log between polls previously set reset=true, making callers treat a mere tail truncation as a rotation. Now the stale-cursor fast-forward path only marks truncated=true and reports skippedBytes, while reset is reserved for the case where the file shrank below the cursor (real rotation). Threads skippedBytes through the log-tail payload, gateway-protocol schema, Swift model, and the Control UI so the UI re-anchors instead of appending after a skipped window.

Co-authored-by: Copilot <[email protected]>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6365b0b563

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/logging/log-tail.ts
truncated = true;
start = Math.max(0, size - maxBytes);
const boundedStart = Math.max(0, size - maxBytes);
skippedBytes = Math.max(0, boundedStart - start);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve reset for clients that do not know skippedBytes

When a gateway is upgraded while an older Control UI or external logs.tail client remains in use, a byte-budget skip now returns reset: false; those clients only understand the existing reset re-anchor signal and will append the returned suffix to their retained entries, producing a discontinuous log buffer. The new UI compensates by checking skippedBytes, which confirms that changing reset is not backward-compatible even though the new schema field is optional. Keep reset as the existing re-anchor signal and distinguish rotation for the CLI with a separate additive indicator or by interpreting skippedBytes.

AGENTS.md reference: AGENTS.md:L110-L110

Useful? React with 👍 / 👎.

Resolve log-tail.test.ts conflict by keeping both sides' additive tests.

Also address Codex P2 (backward compatibility): a byte-budget skip now
keeps reset=true so older logs.tail clients that only understand the
reset re-anchor signal discard their retained buffer instead of appending
a discontinuous suffix. The additive skippedBytes field (>0) still lets
newer clients distinguish a budget skip from a true rotation
(skippedBytes === 0).

Co-authored-by: Copilot <[email protected]>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8fccca6a5e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/logging/log-tail.ts
// retained buffer instead of appending a discontinuous suffix; the
// additive `skippedBytes` lets newer clients tell a budget skip apart
// from a true rotation (skippedBytes === 0).
reset = true;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use skippedBytes before reporting rotation

When the same log file grows by more than maxBytes, this branch now returns truncated: true, reset: true, and skippedBytes > 0, but both output paths in src/cli/logs-cli.ts:735-740 and src/cli/logs-cli.ts:793-795 still translate every reset into Log cursor reset (file rotated). Thus the original false rotation notice remains for openclaw logs, alongside the truncation notice. Fresh evidence after the earlier comment is the newly added skippedBytes discriminator, which the CLI does not consume; the added CLI test misses the real server payload by mocking reset: false. Suppress the rotation notice when skippedBytes > 0 in both plain and JSON modes.

Useful? React with 👍 / 👎.

The CLI translated every `reset` flag into "Log cursor reset (file
rotated).", so a byte-budget skip (reset:true, skippedBytes>0) was
misreported as a rotation. Add a `logResetNoticeMessage` helper that
emits "Log cursor re-anchored (skipped N bytes)." when skippedBytes>0
and reuse it in both the JSON and text reset output paths.

Co-authored-by: Copilot <[email protected]>
@clawsweeper

clawsweeper Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper status: review started.

I am starting a fresh review of this pull request: fix(logs): only report rotation when the log file actually shrank This is item 1/1 in the current shard. Shard 0/1.

This placeholder means the worker is alive and reading the current context. I will edit this same comment with the actual review when the claws are done clicking.

Crustacean status: shell secured, claws on keyboard, evidence pebbles being sorted.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app: web-ui App: web-ui cli CLI command changes gateway Gateway runtime merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: S status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants