Skip to content

fix: native no-op compaction leaves plugin observers waiting - #150920

Open
DonnieFi wants to merge 6 commits into
openclaw:mainfrom
DonnieFi:fix/81925-native-noop-after-compaction
Open

DonnieFi wants to merge 6 commits into
openclaw:mainfrom
DonnieFi:fix/81925-native-noop-after-compaction

Conversation

@DonnieFi

@DonnieFi DonnieFi commented Sep 17, 2026 •

Copy link
Copy Markdown
Contributor

Related: #81925

What Problem This Solves

Fixes plugin compaction observers timing out on a native no-op when the transcript has no real conversation messages. Also makes the bundled compaction-notifier notice truthful for that skip without mislabeling successful zero message-count rewrites.

User Impact

Heartbeat-only or tool-only sessions that skip summarization now emit after_compaction after before_compaction, so voice and plugin hooks receive a settled lifecycle instead of waiting indefinitely for a fake N→N skip. Chat surfaces that enable compaction-notifier say “Nothing to compact” only for an explicit native skip (compactionOutcome: "skipped"), not whenever compactedCount is 0.

Why This Change Was Made

Queued compaction already notifies observers on a successful compacted: false result (#118094). The direct session path still returned after before_compaction without the matching after event.

ClawSweeper follow-up: compactedCount === 0 is not a skip signal. Provider-side compaction can rewrite an assistant entry without shortening session.messages, so the notice must key off an unambiguous internal outcome instead of zero reduction.

Real Behavior Proof

  • HEAD: 61be59c97d68955a2f7dd1208c2ec11ffb9a3fb5
  • Claim: A heartbeat-only persisted transcript through production compactEmbeddedAgentSessionDirect natively skips summarization, fires exactly one installed after_compaction observer, leaves conversation message history and compaction accounting unchanged, and surfaces the bundled notifier text “Nothing to compact” (not “Context compacted”).
  • Exercised surface: compactEmbeddedAgentSessionDirect → executePreparedCompactionSession native no-conversation early return; real SessionManager + SQLite/session store transcript; local plugin after_compaction observer; bundled compaction-notifier via session:compact:after.
  • Environment: Linux worktree /mnt/adata/opt/openclaw-worktrees/81925-native-noop-after-compaction, branch fix/81925-native-noop-after-compaction, isolated createOpenClawTestState home/state (no live Gateway). Synthetic openai apiKey used only for model prep; no provider network call on the skip path.
  • Scenario:
    1. Create isolated OpenClaw state + session store entry agent:main:proof-noop-session.
    2. Persist one user message <b>HEARTBEAT_OK</b> through real SessionManager.open(...).appendMessage(...).
    3. Load a local plugin observer (/tmp/oc-150920-proof/observer-plugin/index.cjs) that appends one JSON line on after_compaction.
    4. Register bundled compaction-notifier on session:compact:before / session:compact:after.
    5. Call production compactEmbeddedAgentSessionDirect with accounting recorder attached.
    6. Compare persisted message events + in-memory messages; assert accounting empty and notice text.
  • Command:
OC_150920_WORKTREE=/mnt/adata/opt/openclaw-worktrees/81925-native-noop-after-compaction \
  node --import tsx /tmp/oc-150920-proof/run-proof.mjs
  • Relevant output (redacted):
[agent/embedded] [compaction] skipping — no real conversation messages (sessionKey=agent:main:proof-noop-session)
result: { ok: true, compacted: false, reason: "no real conversation messages" }
pluginAfterCount: 1
plugin-after.jsonl:
{"at":"2026-09-17T19:50:37.568Z","event":{"messageCount":1,"tokenCount":5,"compactedCount":0,"sessionFile":"agent:main:proof-noop-session"},"context":{"agentId":"main","sessionId":"proof-noop-session","sessionKey":"agent:main:proof-noop-session","workspaceDir":"[redacted]"}}
notices.after: "✅ Nothing to compact. Continuing from where I left off."
accountingReceipts: []
messageHistoryUnchanged: true
beforeMessageHash == afterMessageHash
EXIT:0
  • Artifact: /tmp/oc-150920-proof/result-final.json, /tmp/oc-150920-proof/plugin-after.jsonl, /tmp/oc-150920-proof/notices.json, /tmp/oc-150920-proof/run-proof-head.log (local; not committed).
  • Observed: Native skip completed; exactly one plugin after_compaction; conversation messages unchanged; accounting empty; notifier said “Nothing to compact”. Session prep may append incidental non-message transcript events (thinking_level_change, model-snapshot custom); those are outside the heartbeat conversation history under test.
  • Not tested: Live Gateway openclaw sessions compact RPC; Control UI / chat screenshot delivery of the notice; unfixed baseline replay on this host in the same script run.

Supporting unit coverage (not a substitute for the above): compaction-session-execution.noop-hooks.test.ts, compaction-notifier/handler.test.ts.

Separate Review Blockers

  • check-lint-core-2 / checks-fast-baseline-ratchets: addressed on this head (unused vi import removed; native skip extracted so compaction-session-execution.ts stays under the line-cap ratchet).
  • check-test-types-core-1: src/agents/embedded-agent-runner/run/payloads.test.ts missing finalMessageStart — not in this PR’s diff; shared baseline on main, not fake-greened here.

Evidence

Native skip path passes compactionOutcome: "skipped" into the internal session:compact:after event while preserving plugin compactedCount: 0. Successful after-hooks leave compactionOutcome unset.

ClawSweeper

  • Resolved prior P2: truthful notice for native no-op completion.
  • Resolved new P2: distinguish skipped compaction from zero message reduction via compactionOutcome: "skipped".
  • Real-setup observer proof with persisted transcript captured above for this head.

Sources

red added 2 commits September 17, 2026 10:24
Heartbeat-only transcripts fire before_compaction then skip. Observers
currently hang because after_compaction never arrives.
…mmarize

Queued compaction already notifies observers on a successful no-op. The
direct path returned after before_compaction without the matching after
event, so plugins synthesized a fake N to N skip while work was still
pending.
@clawsweeper

clawsweeper Bot commented Sep 17, 2026 •

Copy link
Copy Markdown
Contributor

🦞👀
ClawSweeper picked this up.

Pull request received. I will update this pull request when review starts.

ClawSweeper review complete

ClawSweeper finished reviewing this revision. The review result is being finalized.

View the workflow run.

@DonnieFi

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Sep 17, 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 exact_review_queue).
Result: when the review finishes, ClawSweeper will create the durable review comment if needed or update the existing comment in place.

Re-review progress:

@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: XS labels Sep 17, 2026
@clawsweeper clawsweeper Bot added P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. 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. labels Sep 17, 2026
@clawsweeper

clawsweeper Bot commented Sep 17, 2026 •

Copy link
Copy Markdown
Contributor

Codex review: blocked before merge. Reviewed October 6, 2026, 1:32 AM ET / 05:32 UTC (Revision 8).

ClawSweeper review

What this changes

The branch completes plugin observers when native compaction skips an empty conversation and gives the bundled notifier a distinct “Nothing to compact” message.

Merge readiness

⛔ Blocked before merge - 3 items remain

This PR remains useful: current main still omits completion for native no-conversation skips. Earlier notifier findings are resolved and real behavior proof is sufficient; native observer compatibility needs an explicit decision, and the branch has merge conflicts.

Priority: P2
Reviewed head: 61be59c97d68955a2f7dd1208c2ec11ffb9a3fb5
Owner decision: Required. See Decision needed.

Review scores

Measure Result What it means
Overall readiness 🐚 platinum hermit (4/6) A focused repair with sufficient production-path proof and no actionable correctness findings; compatibility acceptance and merge conflicts remain workflow blockers.
Proof confidence 🐚 platinum hermit (4/6) Sufficient (terminal): The exact-head production direct-compaction trace exercises real persisted session state and an installed plugin observer, showing one native skip completion, unchanged conversation history, empty accounting, and the correct notifier text. No stored-data contract changes.
Patch quality 🐚 platinum hermit (4/6) No actionable review findings were identified.

Verification

Check Result Evidence
Real behavior Verified Sufficient (terminal): The exact-head production direct-compaction trace exercises real persisted session state and an installed plugin observer, showing one native skip completion, unchanged conversation history, empty accounting, and the correct notifier text. No stored-data contract changes.
Evidence reviewed 8 items Introduced change: The pinned merge-base-to-head diff adds native skip completion, an internal-only skip discriminator, and notifier regression coverage. It does not add a public plugin payload field.
Current main still needs the repair: The native no-conversation branch still returns ok:true and compacted:false directly, without completing the previously emitted before hook. Inspection of the latest-release blob failed, so no release implementation claim is made.
Resolved notifier findings: The notifier checks compactionOutcome:"skipped", rather than compactedCount:0; the latter still reports successful compaction. Both cases have focused regression assertions.
Findings None None.
Security None None.

How this fits together

Compaction reduces session history before further model work. Its lifecycle hooks notify plugins and feed optional chat notices, including when an attempt finishes without rewriting history.

flowchart TD
  A[Session transcript] --> B[Native compaction]
  B --> C[Before observers]
  C --> D{Conversation to summarize?}
  D -->|No| E[Skip completion]
  D -->|Yes| F[History compaction]
  E --> G[After observers and chat notice]
  F --> G
Loading

Decision needed

Question Recommendation
Should successful native no-conversation skips emit after_compaction with the existing zero-count payload? Extend completion to native skips: Accept native completion callbacks with unchanged public payload fields and document that completion can occur without rewriting history.

Why: This changes callback behavior for third-party native observers; the merged acceptance expressly covered engine-owned attempts.

Before merge

  • Resolve merge risk (P1) - Native plugin observers will receive successful skip callbacks they previously never received. Consumers that assume every completion means rewritten history may perform unnecessary follow-up work; the recorded engine-only acceptance does not explicitly cover this extension.
  • Complete next step (P2) - Resolve the branch's merge conflicts against current main and refresh review of the resulting head.
  • Resolve maintainer decision - Resolve the maintainer decision shown above before merge.
Agent review details

Security

None.

PR surface

Source +90, Tests +202. Total +292 across 6 files.

View PR surface stats
Area Files Added Removed Net
Source 4 105 15 +90
Tests 2 202 0 +202
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 6 307 15 +292

Review metrics

Metric Value Why it matters
Production and regression growth production net +90 lines; tests +202 lines Most production growth extracts the shared prepared-hook adapter to keep the existing execution module within its line cap.

Merge-risk options

Maintainer options:

  1. Accept the native lifecycle extension (recommended)
    Explicitly accept native skip callbacks and document their unchanged-history semantics before landing.
  2. Pause the callback extension
    Keep the PR paused if native observer semantics must remain unchanged.

Technical review

Best possible solution:

Complete native skip lifecycles without mutation-only side effects, retain the truthful internal skip notice, and explicitly accept and document the native observer contract.

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

Yes, from source: a heartbeat-only transcript reaches the native no-conversation return after the before hook, with no matching completion on current main. This review did not execute target code.

Is this the best way to solve the issue?

Yes, technically: the patch uses the existing hook owner, preserves public payload fields and mutation-only side effects, and distinguishes skips from zero-reduction rewrites.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning medium; reviewed against d8612ec864bb.

Labels

Label changes:

No label changes.

Label justifications:

  • P2: This repairs a bounded compaction lifecycle gap that can leave plugin observers waiting.
  • merge-risk: 🚨 compatibility: Native observers gain callbacks for unchanged-history skips beyond the previously accepted engine-only behavior.
  • 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 (terminal): The exact-head production direct-compaction trace exercises real persisted session state and an installed plugin observer, showing one native skip completion, unchanged conversation history, empty accounting, and the correct notifier text. No stored-data contract changes.
  • proof: sufficient: Contributor real behavior proof is sufficient. The exact-head production direct-compaction trace exercises real persisted session state and an installed plugin observer, showing one native skip completion, unchanged conversation history, empty accounting, and the correct notifier text. No stored-data contract changes.

Evidence

What I checked:

Likely related people:

  • steipete: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)
  • peterolkhov: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)
  • fuller-stack-dev: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)

Rating scale

Score Internal tier Crab rank Meaning
6/6 S 🦀 challenger crab Exceptional readiness
5/6 A 🦞 diamond lobster Very strong readiness
4/6 B 🐚 platinum hermit Good normal PR; ordinary maintainer review
3/6 C 🦐 gold shrimp Useful, but confidence is limited
2/6 D 🦪 silver shellfish Proof or implementation needs work
1/6 F 🧂 unranked krab Not merge-ready
N/A NA 🌊 off-meta tidepool Rating does not apply

Overall follows the weaker of proof and patch quality.
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.

Workflow

  • 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.

History

Review history (7 earlier review cycles)
  • reviewed 2026-09-17T13:31:16.771Z sha 97d4bf3 :: needs real behavior proof before merge. :: none
  • reviewed 2026-09-17T13:38:49.656Z sha 97d4bf3 :: needs real behavior proof before merge. :: none
  • reviewed 2026-09-17T17:04:41.723Z sha 3848444 :: needs real behavior proof before merge. :: [P2] Make the bundled notice truthful for native compaction skips
  • reviewed 2026-09-17T18:26:42.715Z sha 7f59ca3 :: needs real behavior proof before merge. :: [P2] Distinguish skipped compaction from zero message reduction
  • reviewed 2026-09-17T18:37:52.914Z sha 22f369c :: needs real behavior proof before merge. :: none
  • reviewed 2026-09-17T20:00:26.034Z sha 61be59c :: blocked before merge. :: none
  • reviewed 2026-09-24T19:58:54.247Z sha 61be59c :: blocked before merge. :: none

@DonnieFi

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Sep 17, 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 exact_review_queue).
Result: when the review finishes, ClawSweeper will create the durable review comment if needed or update the existing comment in place.

Re-review progress:

Native no-op after_compaction already completes observers with
compactedCount: 0. The bundled notifier still claimed context was
compacted, so chat surfaces lied about a skip.
@DonnieFi

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Sep 17, 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 exact_review_queue).
Result: when the review finishes, ClawSweeper will create the durable review comment if needed or update the existing comment in place.

Re-review progress:

@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 Sep 17, 2026
Provider-side rewrites can finish with compactedCount 0 without skipping.
Key the bundled notice on compactionOutcome: skipped so a real rewrite
still reports Context compacted.
@DonnieFi

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Sep 17, 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 exact_review_queue).
Result: when the review finishes, ClawSweeper will create the durable review comment if needed or update the existing comment in place.

Extract the no-conversation skip completion helper so compaction-session-execution
stays within the line-cap ratchet, and remove an unused vitest import.
@DonnieFi

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Sep 17, 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 exact_review_queue).
Result: when the review finishes, ClawSweeper will create the durable review comment if needed or update the existing comment in place.

Re-review progress:

@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. labels Sep 17, 2026
@clawsweeper clawsweeper Bot added status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Sep 17, 2026
@fuller-stack-dev fuller-stack-dev self-assigned this Sep 18, 2026

This branch has not been deployed

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

Labels

agents Agent runtime and tooling 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. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: M 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