Skip to content

fix #97625: Qwen3:14b compaction fails with Already compacted#99136

Merged
openclaw-clownfish[bot] merged 1 commit into
openclaw:mainfrom
mushuiyu886:feat/issue-97625
Jul 6, 2026
Merged

fix #97625: Qwen3:14b compaction fails with Already compacted#99136
openclaw-clownfish[bot] merged 1 commit into
openclaw:mainfrom
mushuiyu886:feat/issue-97625

Conversation

@mushuiyu886

Copy link
Copy Markdown
Contributor

Closes #97625

Summary

  • Treat Already compacted / already_compacted CLI turn compaction outcomes as safe no-ops instead of fatal pre-turn failures.
  • Apply the same benign no-op classification to the context-engine throw path and non-compacting result path.
  • Add regression coverage for the reported ollama/qwen3:14b failure message.

What Problem This Solves

Users running local Ollama-backed CLI sessions can hit a fatal turn abort after the runtime has already compacted the transcript. In the reported Qwen3:14b workflow, this surfaced as CLI transcript compaction failed for ollama/qwen3:14b: Already compacted before the MCP tool result could reach the user, while another Ollama model completed the same flow.

Why This Change Was Made

OpenClaw already classifies Already compacted as already_compacted_recently, and adjacent manual /compact plus memory-flush preflight paths already treat that class as a skipped compaction. The CLI turn compaction lifecycle only skipped below_threshold, so the same harmless already-compacted state became a fatal failureReason for CLI-backed turns.

User Impact

A CLI-backed agent turn can now continue when the selected context engine reports that the transcript was already compacted. Real compaction failures still fail closed, so timeouts, guard failures, provider errors, and unclassified compaction errors remain visible instead of being hidden.

Why it matters / User impact

The failure happens on the path that decides whether a user turn may proceed after transcript persistence. When it fails closed on a harmless already-compacted transcript, the user loses the expected assistant/tool response even though the transcript source of truth is already in a compacted state. This is especially visible for small-context local models such as ollama/qwen3:14b, where compaction can happen before the MCP tool workflow completes.

Maintainer-ready checklist

  • Why it matters / User impact: The failure happens on the path that decides whether a user turn may proceed after transcript persistence. When it fails closed on a harmless already-compacted transcript, the user loses the expected assistant/tool response even though the transcript source of truth is already in a compacted state.
  • What did NOT change: This patch only changes CLI compaction no-op classification. It does not change MCP execution, Ollama routing, model catalog/config behavior, prompt assembly, transcript rewriting, context-engine plugin registration, or the lazy compaction-count reconcile persistence path.
  • Architecture / source-of-truth check: The model-consumed source-of-truth is the active session transcript plus the context-engine compaction result for that transcript. Already compacted means the compact boundary found the current branch already ends at a compaction state, so the correct lifecycle contract is to skip without mutating transcript contents, session state, or compaction counters.
  • Scenario locked in: A CLI-backed session above its prompt-reserve budget selects ollama/qwen3:14b, reaches context-engine compaction, and receives Already compacted; the lifecycle must return the existing session entry instead of throwing CLI transcript compaction failed.
  • Why this is the smallest reliable guardrail: The regression checks exactly the decision boundary that failed, verifies the compact boundary is reached once, and verifies no maintenance or compaction-count write happens for the no-op. That prevents both the reported abort and an over-broad false success.

Scope boundary

This patch is limited to the CLI turn compaction lifecycle in src/agents/command/cli-compaction.ts and its regression coverage. It does not change MCP execution, Ollama routing, model catalog/config behavior, prompt assembly, transcript rewriting, context-engine plugin registration, or the lazy compaction-count reconcile persistence path.

Source-of-truth / architecture boundary

The model-consumed source of truth is the active session transcript and the context-engine compaction result for that transcript. Already compacted means the compact boundary found the current branch already ends at a compaction state, so there is no additional transcript rewrite to perform. The fix changes only the decision that maps that known terminal no-op reason into turn control flow; it does not mutate transcript contents, session store entries, or compaction counters for the no-op path.

Related PR scan

The issue evidence scan found no related open PRs for the public-contract search terms or direct issue linkage. The config.plugins.slots.contextEngine reference appears only in the local proof command to select a registered context-engine slot; this patch does not add or modify any public config/schema/API/protocol surface.

Root Cause

  • Root cause: compactCliTranscript converted every thrown context-engine compact error into failureReason, and runCliTurnCompactionLifecycle throws on that failureReason. Although classifyCompactionReason() already maps Already compacted to already_compacted_recently, the CLI lifecycle only treated below_threshold as a non-fatal no-op.
  • Why this is root-cause fix: The patch classifies the thrown or returned compaction reason before deciding whether to fail the turn. Only the already-known benign no-op class is added to the CLI skip path, matching adjacent compaction preflight semantics without adding provider-specific Qwen/Ollama handling.
  • Not changed: The lazy compaction-count reconcile path is not changed. A focused handler test passed locally and did not reproduce the reported reconcile is not a function warning, which also appeared in the reporter's successful Llama3.1 run and was not the fatal abort path.

Real behavior proof

  • Behavior or issue addressed: CLI turn compaction should not abort a turn when the context-engine compact boundary reports Already compacted for ollama/qwen3:14b.
  • Real environment tested: Linux source checkout running the actual runCliTurnCompactionLifecycle production path with a registered public context-engine slot selected through config.plugins.slots.contextEngine. The slot's compact boundary throws the reporter's exact Already compacted message for ollama/qwen3:14b; no direct helper call bypasses the lifecycle.
  • Exact steps or command run after this patch:
pnpm --dir /media/vdb/code/ai/aispace/openclaw-worktrees/issue-97625 exec node --import tsx -e "import fs from 'node:fs/promises'; import os from 'node:os'; import path from 'node:path'; import { CURRENT_SESSION_VERSION } from 'openclaw/plugin-sdk/agent-sessions'; import { registerContextEngine } from './src/context-engine/registry.ts'; import { runCliTurnCompactionLifecycle } from './src/agents/command/cli-compaction.ts'; const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'oc-97625-slot-proof-')); try { const engineId = 'already-compacted-proof'; const compactCalls = []; registerContextEngine(engineId, () => ({ info: { id: engineId, name: 'Already Compacted Proof Engine' }, async ingest() { return { ingested: false }; }, async assemble(params) { return { messages: params.messages, estimatedTokens: 0 }; }, async compact(params) { compactCalls.push({ force: params.force, currentTokenCount: params.currentTokenCount, compactionTarget: params.compactionTarget }); throw new Error('Already compacted'); } })); const sessionKey = 'agent:main:qwen-proof'; const sessionId = 'session-qwen-proof'; const sessionFile = path.join(dir, 'session.jsonl'); const storePath = path.join(dir, 'sessions.json'); await fs.writeFile(sessionFile, [JSON.stringify({ type: 'session', version: CURRENT_SESSION_VERSION, id: sessionId, timestamp: new Date(0).toISOString(), cwd: dir }), JSON.stringify({ type: 'message', message: { role: 'user', content: 'old ask', timestamp: 1 } }), JSON.stringify({ type: 'message', message: { role: 'assistant', content: [{ type: 'text', text: 'old answer' }], timestamp: 2 } }), ''].join('\n'), 'utf-8'); const sessionEntry = { sessionId, updatedAt: Date.now(), sessionFile, contextTokens: 1000, totalTokens: 950, totalTokensFresh: true }; const sessionStore = { [sessionKey]: sessionEntry }; await fs.writeFile(storePath, JSON.stringify(sessionStore, null, 2), 'utf-8'); const updatedEntry = await runCliTurnCompactionLifecycle({ cfg: { plugins: { slots: { contextEngine: engineId } } }, sessionId, sessionKey, sessionEntry, sessionStore, storePath, sessionAgentId: 'main', workspaceDir: dir, agentDir: dir, provider: 'ollama', model: 'qwen3:14b' }); console.log(JSON.stringify({ completedWithoutThrow: true, sameEntryReturned: updatedEntry === sessionEntry, compactCalls, persistedCompactionCount: sessionStore[sessionKey]?.compactionCount ?? null }, null, 2)); } finally { await fs.rm(dir, { recursive: true, force: true }); }"
  • Evidence after fix:
[context-engine] Context engine "already-compacted-proof" owner=public-sdk failed during compact: Already compacted; quarantining it for this process and falling back to default engine "legacy".
[agents/cli-compaction] CLI transcript compaction skipped for ollama/qwen3:14b: Already compacted
{
  "completedWithoutThrow": true,
  "sameEntryReturned": true,
  "compactCalls": [
    {
      "force": true,
      "currentTokenCount": 950,
      "compactionTarget": "budget"
    }
  ],
  "persistedCompactionCount": null
}
  • Observed result after fix: The CLI turn compaction lifecycle reaches the configured context-engine compact boundary, receives the exact Already compacted failure text, logs it as skipped for ollama/qwen3:14b, and returns the existing session entry without throwing or recording a new compaction.
  • What was not tested: I did not run a live Ollama qwen3:14b model, the reporter's Python MCP stdio server, or the exact jarvis__expand tool workflow in this environment. The reported late reconcile TypeError was checked through src/agents/embedded-agent-subscribe.handlers.compaction.test.ts but was not reproduced locally, so this patch does not change the lazy reconcile import/persistence path.

Regression Test Plan

  • Target test file: src/agents/command/cli-compaction.test.ts
  • Regression scenario: A CLI-backed session is above its prompt-reserve budget, selects ollama/qwen3:14b, reaches context-engine compaction, and receives the reported Already compacted reason. Before this patch, the lifecycle threw CLI transcript compaction failed for ollama/qwen3:14b: Already compacted; after this patch, it returns the existing session entry as a no-op.
  • Test guardrail rationale: The regression asserts that the compact boundary is reached exactly once, maintenance is not invoked, and no compaction record is written. That guards against both the reported fatal abort and an over-broad fix that would incorrectly count a no-op as a successful new compaction.
pnpm --dir /media/vdb/code/ai/aispace/openclaw-worktrees/issue-97625 test src/agents/command/cli-compaction.test.ts
Test Files  1 passed (1)
Tests  20 passed (20)
pnpm --dir /media/vdb/code/ai/aispace/openclaw-worktrees/issue-97625 test src/agents/embedded-agent-subscribe.handlers.compaction.test.ts
Test Files  1 passed (1)
Tests  11 passed (11)
pnpm --dir /media/vdb/code/ai/aispace/openclaw-worktrees/issue-97625 run format:check -- src/agents/command/cli-compaction.ts src/agents/command/cli-compaction.test.ts
All matched files use the correct format.
pnpm --dir /media/vdb/code/ai/aispace/openclaw-worktrees/issue-97625 run tsgo:test:src
PASS, exit code 0.

Merge risk

  • Risk labels considered: impact:session-state, impact:message-loss, impact:auth-provider, merge-risk:agent-runtime, merge-risk:session-state.
  • Risk explanation: The change affects whether a CLI compaction failure aborts a user turn. Over-broad skipping could hide real compaction failures and allow a too-large prompt to continue.
  • Why acceptable: The skip is limited to shared classifier output for below_threshold and already_compacted_recently. Existing timeout/provider/guard/unknown failures still produce failureReason, and the regression asserts that no maintenance or compaction-count write is performed for the already-compacted no-op.

Patch quality notes

  • Fix classification: Root-cause CLI compaction lifecycle fix with focused regression coverage.
  • Maintainer-ready confidence: High for the source-level CLI compaction lifecycle and the exact fatal reason mapping; live Ollama/Qwen3 MCP validation remains the proof gap.
  • Patch quality notes: The patch changes only the compaction reason classification boundary and one focused test file. It does not add model-specific handling, config knobs, or unrelated compaction policy changes.

@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: S labels Jul 2, 2026
@clawsweeper

clawsweeper Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed July 2, 2026, 11:40 AM ET / 15:40 UTC.

Summary
The PR treats already-compacted CLI/context-engine compaction outcomes as benign no-ops and adds focused regression coverage in src/agents/command/cli-compaction.test.ts.

PR surface: Source +6, Tests +68. Total +74 across 2 files.

Reproducibility: yes. at source level, but not as a live current-main Ollama run. Current main classifies Already compacted but the CLI lifecycle only skips below-threshold reasons before throwing, matching the reporter's fatal path.

Review metrics: none identified.

Stored data model
Persistent data-model change detected: serialized state: src/agents/command/cli-compaction.test.ts. Confirm migration or upgrade compatibility proof before merge.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #97625
Summary: This PR is the direct candidate fix for the linked Qwen3/Ollama CLI compaction abort; broader recently-compacted overflow recovery remains adjacent but distinct.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🐚 platinum hermit
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

Maintainer options:

  1. Decide the mitigation before merge
    Land the narrow CLI compaction lifecycle fix after required CI remains clean, while keeping broader recently-compacted overflow recovery under Compaction dead-end: recently-compacted session with overflow blocks permanently #98982.
  2. Pause or close
    Do not merge this PR until maintainers decide whether the risk is worth taking.

Next step before merge

  • No ClawSweeper repair lane is needed; the remaining action is normal maintainer and CI review on the contributor PR.

Security
Cleared: The diff only changes agent compaction reason handling and a focused test; it does not touch dependencies, secrets, workflows, package resolution, or other supply-chain surfaces.

Review details

Best possible solution:

Land the narrow CLI compaction lifecycle fix after required CI remains clean, while keeping broader recently-compacted overflow recovery under #98982.

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

Yes at source level, but not as a live current-main Ollama run. Current main classifies Already compacted but the CLI lifecycle only skips below-threshold reasons before throwing, matching the reporter's fatal path.

Is this the best way to solve the issue?

Yes, this is the best narrow fix. It reuses the existing compaction reason classifier at the CLI lifecycle decision boundary instead of adding provider-specific Qwen/Ollama handling or a new config knob.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 3020f7817eb3.

Label changes

Label justifications:

  • P2: The PR fixes a user-visible CLI-backed agent turn abort with limited surface area and a model workaround reported in the linked issue.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🐚 platinum hermit and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (live_output): The PR body includes after-fix terminal output from a Linux source checkout exercising the production runCliTurnCompactionLifecycle path with a registered context-engine slot throwing the exact Already compacted message.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes after-fix terminal output from a Linux source checkout exercising the production runCliTurnCompactionLifecycle path with a registered context-engine slot throwing the exact Already compacted message.
Evidence reviewed

PR surface:

Source +6, Tests +68. Total +74 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 1 14 8 +6
Tests 1 68 0 +68
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 2 82 8 +74

What I checked:

Likely related people:

  • frankekn: Authored the merged below-target CLI compaction no-op behavior in fix(agents): skip below-target CLI compaction failures #88319, directly adjacent to the invariant this PR extends. (role: adjacent CLI compaction behavior contributor; confidence: high; commits: 049c69b697b9, 4ca3773a0dfe; files: src/agents/command/cli-compaction.ts, src/agents/command/cli-compaction.test.ts)
  • obviyus: Merged the adjacent CLI compaction no-op PR and current-main blame in this shallow checkout points to Ayaan Zaidi carrying the central compaction files forward. (role: recent area contributor and merger; confidence: medium; commits: 15c151181791, 2643c8cbfb9a; files: src/agents/command/cli-compaction.ts, src/agents/embedded-agent-runner/compact-reasons.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.

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. 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. P2 Normal backlog priority with limited blast radius. labels Jul 2, 2026
@openclaw-clownfish
openclaw-clownfish Bot merged commit 806397f into openclaw:main Jul 6, 2026
142 of 150 checks passed
vincentkoc added a commit to zhangqueping/openclaw that referenced this pull request Jul 6, 2026
* origin/main: (1287 commits)
  fix(android): block loopback canvas navigation (openclaw#99874)
  fix(hooks): suppress unhandled stdout/stderr stream errors in gmail watcher (openclaw#100519)
  fix(memory-core): guard qmd mcporter JSON.parse against non-JSON stdout (openclaw#98381)
  fix(build): fall back to tsx for build TypeScript scripts (openclaw#91262)
  feat(skills): suggest saving detected reusable workflows by default (openclaw#95477) (openclaw#100692)
  docs(changelog): remove generated release-note entries
  feat(telegram): offer BotFather web app flow in setup help and docs (openclaw#100540)
  fix(ios): unify Talk and Settings row typography on one branded detail row (openclaw#100515)
  feat(gateway): add persisted crash-loop breaker and fatal-config exit contract
  refactor(macos): lock and unify PortGuardian tunnel record persistence so concurrent app instances cannot lose orphan records (openclaw#100601)
  fix: stop reconnecting on protocol mismatch (openclaw#98414)
  fix(maint): reuse recent hosted gates after rebase (openclaw#100663)
  fix(ui): reopen web terminals without stale content (openclaw#100665)
  fix(browser): diagnose empty WSL2 Chrome replies (openclaw#100590)
  fix(ios): chat snaps back to bottom when scrolling to top via status-bar tap (openclaw#100502)
  Treat already-compacted CLI compaction as no-op (openclaw#99136)
  docs(changelog): remove direct main fix entry
  fix(feishu): strip internal tool-trace banners from outbound text (openclaw#98705)
  fix(message): thread --limit through to CLI formatter and surface provider pagination hints (openclaw#99089)
  fix(voyage): close response body stream when batch output JSONL parsing throws (openclaw#98840)
  ...

# Conflicts:
#	extensions/memory-wiki/package.json
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 6, 2026
giodl73-repo pushed a commit to giodl73-repo/openclaw that referenced this pull request Jul 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. 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.

[Bug]: Qwen3:14b fails with CLI transcript compaction failed: Already compacted; identical MCP tool succeeds with Llama3.1:8b

1 participant