Skip to content

fix(dreaming): drop heartbeat assistant responses from dream corpus#109403

Merged
steipete merged 16 commits into
openclaw:mainfrom
ekinnee:fix/dreaming-heartbeat-filter
Jul 17, 2026
Merged

fix(dreaming): drop heartbeat assistant responses from dream corpus#109403
steipete merged 16 commits into
openclaw:mainfrom
ekinnee:fix/dreaming-heartbeat-filter

Conversation

@ekinnee

@ekinnee ekinnee commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Related: #103720
Supersedes #104085.

What Problem This Solves

Fixes an issue where natural-language assistant responses to heartbeat polls could enter the Dreaming session corpus. The existing sanitizer removed the heartbeat user marker and exact heartbeat acknowledgments, but retained arbitrary assistant text generated during the same heartbeat turn.

Why This Change Was Made

Heartbeat turns now persist the existing internal-system provenance with a heartbeat source-tool marker. Session corpus construction uses that trusted runtime fact to exclude every assistant response in the heartbeat turn until the next user turn.

This does not add a new provenance kind, and user-authored heartbeat-like text cannot suppress an assistant response. Historical transcripts do not contain an authenticated heartbeat marker, so this change intentionally does not guess from text or delete existing corpus lines.

User Impact

New heartbeat responses no longer add low-value Dreaming memories. Normal user turns, media-only turns, and inter-session turns remain indexable. Existing Dreaming diaries and ingestion checkpoints are untouched.

Evidence

@openclaw-barnacle openclaw-barnacle Bot added extensions: memory-core Extension: memory-core commands Command implementations size: XL labels Jul 16, 2026
@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. merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. labels Jul 17, 2026
@clawsweeper

clawsweeper Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed July 17, 2026, 12:53 AM ET / 04:53 UTC.

Summary
The PR marks persisted heartbeat user turns with authenticated internal provenance and excludes their assistant responses from Dreaming session-corpus construction until the next user turn.

PR surface: Source +11, Tests +115. Total +126 across 4 files.

Reproducibility: yes. at source level: current corpus sanitization removes the heartbeat user marker and exact acknowledgment but does not associate arbitrary assistant replies with the heartbeat turn; the branch adds focused regression cases for that path. This review did not independently run a current-main live reproduction.

Review metrics: none identified.

Stored data model
Persistent data-model change detected: serialized state: packages/memory-host-sdk/src/host/session-files.test.ts, serialized state: packages/memory-host-sdk/src/host/session-files.ts, unknown-data-model-change: packages/memory-host-sdk/src/host/session-files.test.ts, unknown-data-model-change: packages/memory-host-sdk/src/host/session-files.ts. Confirm migration or upgrade compatibility proof before merge.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #103720
Summary: This PR is the candidate fix for the canonical issue's heartbeat-response contamination, while that issue also contains a distinct deleted-transcript ingestion problem.

Members:

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

Merge readiness
Overall: 🦪 silver shellfish
Proof: 🦪 silver shellfish
Patch quality: 🐚 platinum hermit
Result: blocked until real behavior proof from a real setup is added.

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

Rank-up moves:

  • [P1] Add redacted live output showing a natural-language heartbeat response excluded from the Dreaming corpus while the next normal user/assistant turn remains indexed.

Proof guidance:

  • [P1] Needs real behavior proof before merge: The current head cites focused tests, CI, and autoreview only; add redacted live transcript and resulting corpus output, then update the PR body to trigger re-review or ask a maintainer to comment @clawsweeper re-review.

Risk before merge

  • [P1] The filter deliberately suppresses every assistant message after an authenticated heartbeat marker until the next user turn; an incorrect persisted marker or turn-boundary assumption could omit valid content from future Dreaming memories, and the PR lacks live exact-head corpus proof of that boundary.

Maintainer options:

  1. Add live corpus proof (recommended)
    Capture redacted after-fix output from a real heartbeat session showing the persisted provenance, excluded heartbeat replies, and retained next user turn before merging.
  2. Accept test-only evidence
    Merge based on focused boundary tests and exact-head CI while explicitly accepting that the persisted runtime-to-corpus path was not demonstrated in a real setup.

Next step before merge

  • [P1] No automated code repair is identified; the remaining contributor action is to supply real exact-head behavior proof and let normal maintainer review and CI gate the PR.

Security
Cleared: The four-file diff adds no dependency, workflow, secret, permission, download, or new externally forgeable provenance surface; heartbeat classification remains tied to an internal runtime fact.

Review details

Best possible solution:

Keep the provenance-based forward filter, demonstrate one real heartbeat transcript whose natural-language assistant replies are absent from the generated corpus while the following normal turn remains present, then land it as the heartbeat-specific fix while leaving the deleted-transcript portion of the canonical issue separately tracked.

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

Yes at source level: current corpus sanitization removes the heartbeat user marker and exact acknowledgment but does not associate arbitrary assistant replies with the heartbeat turn; the branch adds focused regression cases for that path. This review did not independently run a current-main live reproduction.

Is this the best way to solve the issue?

Yes, the source-authenticated provenance marker is safer and narrower than matching user-visible heartbeat text or broad acknowledgment heuristics, and the turn-boundary tests cover the important sibling cases. The remaining gap is proof rather than an identified implementation defect.

AGENTS.md: found and applied where relevant.

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

Label changes

Label changes:

  • add rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🦪 silver shellfish and patch quality is 🐚 platinum hermit.
  • add status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: The current head cites focused tests, CI, and autoreview only; add redacted live transcript and resulting corpus output, then update the PR body to trigger re-review or ask a maintainer to comment @clawsweeper re-review.
  • remove proof: sufficient: Current real behavior proof status is mock_only, not sufficient.
  • remove rating: 🐚 platinum hermit: Current PR rating is rating: 🦪 silver shellfish, so this older rating label is no longer current.
  • remove status: 👀 ready for maintainer look: Current PR status label is status: 📣 needs proof.

Label justifications:

  • P2: The PR fixes bounded Dreaming memory contamination with limited blast radius rather than an urgent core-runtime outage.
  • merge-risk: 🚨 session-state: The new authenticated turn state determines which persisted assistant messages are retained or suppressed from future Dreaming memory.
  • rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🦪 silver shellfish and patch quality is 🐚 platinum hermit.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: The current head cites focused tests, CI, and autoreview only; add redacted live transcript and resulting corpus output, then update the PR body to trigger re-review or ask a maintainer to comment @clawsweeper re-review.
Evidence reviewed

PR surface:

Source +11, Tests +115. Total +126 across 4 files.

View PR surface stats
Area Files Added Removed Net
Source 2 19 8 +11
Tests 2 115 0 +115
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 4 134 8 +126

What I checked:

Likely related people:

  • steipete: Authored and force-pushed the consolidated current head that narrowed the earlier repair-heavy branch to provenance persistence and corpus filtering. (role: recent area contributor; confidence: high; commits: 5b095b159e47; files: src/auto-reply/reply/get-reply-run.ts, packages/memory-host-sdk/src/host/session-files.ts)
  • Patrick-Erichsen: Authored the merged Dreaming isolation and transcript-noise filtering work that established the relevant corpus-sanitization boundary. (role: introduced adjacent behavior; confidence: medium; commits: aca92b29065f; files: packages/memory-host-sdk/src/host/session-files.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.
Review history (1 earlier review cycle)
  • reviewed 2026-07-16T23:57:32.562Z sha a22b8bd :: needs maintainer review before merge. :: none

Erick Kinnee and others added 16 commits July 17, 2026 05:40
The dreaming ingestion pipeline filters heartbeat user messages
(containing [OpenClaw heartbeat poll]) via sanitizeSessionText, but the
paired assistant response is only dropped when it is the exact string
HEARTBEAT_OK. Local models frequently respond with natural-language
acknowledgments (e.g. "Heartbeat received. Main is active.") which pass
through the sanitizer unchanged and enter the dream corpus as
low-confidence (0.58) memory snippets.

Fix: track heartbeat user message drops in buildSessionEntry and skip
the immediately following assistant response. This cross-message coupling
is safe because the heartbeat prompt pattern is injected by the runtime
and cannot be spoofed by user input, unlike [cron:...] or
System (untrusted): ... patterns (see PR openclaw#70737).

Refs: openclaw#103720
…xt matching

Replace text-based heartbeat detection (isGeneratedHeartbeatPromptMessage)
with provenance-based authentication. The heartbeat turn now carries
provenance: { kind: "heartbeat" } when persisted to the transcript,
and buildSessionEntry checks for this provenance instead of matching
user-spoofable text content.

Changes:
- src/sessions/input-provenance.ts: Add "heartbeat" to
  INPUT_PROVENANCE_KIND_VALUES
- src/auto-reply/reply/get-reply-run.ts: Attach heartbeat provenance
  to user turn input when isHeartbeat is true
- packages/memory-host-sdk/src/host/session-files.ts: Check
  message.provenance.kind === "heartbeat" instead of text matching
- packages/memory-host-sdk/src/host/session-files.test.ts: Update
  heartbeat test to include provenance; add lookalike regression test

This addresses the ClawSweeper review finding [P1] about user-spoofable
text matching. The cross-message coupling is now authenticated by
runtime provenance, not user-typed content.

Refs: openclaw#103720
The findHeartbeatContaminatedCorpusLines function could not read
SQLite-backed session transcripts. When a corpus ref had no .jsonl
extension (SQLite logical path format), it appended .jsonl, read an
empty file, and silently skipped — leaving pre-fix heartbeat
contamination intact for SQLite users.

Fix:
- Add loadTranscriptLinesFromSqlite helper that reads events via
  the canonical loadTranscriptEventsSync API
- Serialize all events to preserve original event positions for
  corpus #L<n> line-number references
- Fall back to SQLite reader when filesystem read fails on a
  non-.jsonl corpus ref path

Test: add SQLite-backed session regression test that seeds a
session, writes corpus refs without .jsonl extension, and
verifies both audit detection and targeted repair.
…nd early return

P1: Remove clearScopedSessionIngestionState call after heartbeat line
removal. Clearing the cursor causes the next ingestion to re-process the
transcript from the beginning, duplicating normal corpus lines that were
deliberately retained.

P2: Remove early return after heartbeat cleanup so the self-ingestion
narrative content check and archiveDiary request still run.
…t ID

The session path in corpus refs is already in the format
'sessions/main/abc.jsonl' (includes agent subdirectory). The
previous code prepended source.agentId again, creating a path
like 'agents/main/sessions/main/abc.jsonl' — which silently
returned empty in production workspaces.

Fix: use path.basename to extract just the filename from the
session path, then construct the transcript path correctly.
…LOC ratchet

The dreaming-repair.ts file grew from 337 to 831 lines, exceeding the
500-line LOC ratchet limit. Split helper utilities into a new
dreaming-repair-utils.ts file:

- dreaming-repair.ts (365 lines): imports, types, public API functions
  (auditDreamingArtifacts, repairDreamingArtifacts)
- dreaming-repair-utils.ts (498 lines): all helper functions, constants,
  and internal types
…lear

When heartbeat contamination is found, archive the entire session-corpus directory and clear all SQLite checkpoints instead of doing targeted line-by-line rewrite. This ensures the cleaned corpus gets re-ingested under the new provenance-based forward filter.
@steipete
steipete force-pushed the fix/dreaming-heartbeat-filter branch from a22b8bd to 5b095b1 Compare July 17, 2026 04:48
@openclaw-barnacle openclaw-barnacle Bot added size: S and removed extensions: memory-core Extension: memory-core commands Command implementations size: XL labels Jul 17, 2026
@clawsweeper clawsweeper Bot added 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. and removed 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. labels Jul 17, 2026
@steipete
steipete merged commit 571f1dc into openclaw:main Jul 17, 2026
152 of 164 checks passed
@steipete

Copy link
Copy Markdown
Contributor

Merged via squash.

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 17, 2026
…penclaw#109403)

* fix(dreaming): drop heartbeat assistant responses from dream corpus

The dreaming ingestion pipeline filters heartbeat user messages
(containing [OpenClaw heartbeat poll]) via sanitizeSessionText, but the
paired assistant response is only dropped when it is the exact string
HEARTBEAT_OK. Local models frequently respond with natural-language
acknowledgments (e.g. "Heartbeat received. Main is active.") which pass
through the sanitizer unchanged and enter the dream corpus as
low-confidence (0.58) memory snippets.

Fix: track heartbeat user message drops in buildSessionEntry and skip
the immediately following assistant response. This cross-message coupling
is safe because the heartbeat prompt pattern is injected by the runtime
and cannot be spoofed by user input, unlike [cron:...] or
System (untrusted): ... patterns (see PR openclaw#70737).

Refs: openclaw#103720

* test(dreaming): add heartbeat assistant response filter test

* fix(dreaming): use provenance-based heartbeat detection instead of text matching

Replace text-based heartbeat detection (isGeneratedHeartbeatPromptMessage)
with provenance-based authentication. The heartbeat turn now carries
provenance: { kind: "heartbeat" } when persisted to the transcript,
and buildSessionEntry checks for this provenance instead of matching
user-spoofable text content.

Changes:
- src/sessions/input-provenance.ts: Add "heartbeat" to
  INPUT_PROVENANCE_KIND_VALUES
- src/auto-reply/reply/get-reply-run.ts: Attach heartbeat provenance
  to user turn input when isHeartbeat is true
- packages/memory-host-sdk/src/host/session-files.ts: Check
  message.provenance.kind === "heartbeat" instead of text matching
- packages/memory-host-sdk/src/host/session-files.test.ts: Update
  heartbeat test to include provenance; add lookalike regression test

This addresses the ClawSweeper review finding [P1] about user-spoofable
text matching. The cross-message coupling is now authenticated by
runtime provenance, not user-typed content.

Refs: openclaw#103720

* fix(dreaming): add targeted heartbeat-derived corpus repair

* fix(dreaming): fix TS return type for clearScopedLegacySessionIngestionJson catch

* fix(dreaming): match chunked SQLite seen-state keys by stored scope

* fix(dreaming): repair SQLite-backed session transcript lookup in doctor

The findHeartbeatContaminatedCorpusLines function could not read
SQLite-backed session transcripts. When a corpus ref had no .jsonl
extension (SQLite logical path format), it appended .jsonl, read an
empty file, and silently skipped — leaving pre-fix heartbeat
contamination intact for SQLite users.

Fix:
- Add loadTranscriptLinesFromSqlite helper that reads events via
  the canonical loadTranscriptEventsSync API
- Serialize all events to preserve original event positions for
  corpus #L<n> line-number references
- Fall back to SQLite reader when filesystem read fails on a
  non-.jsonl corpus ref path

Test: add SQLite-backed session regression test that seeds a
session, writes corpus refs without .jsonl extension, and
verifies both audit detection and targeted repair.

* fix(dreaming): address autoreview P1 and P2 for checkpoint clearing and early return

P1: Remove clearScopedSessionIngestionState call after heartbeat line
removal. Clearing the cursor causes the next ingestion to re-process the
transcript from the beginning, duplicating normal corpus lines that were
deliberately retained.

P2: Remove early return after heartbeat cleanup so the self-ingestion
narrative content check and archiveDiary request still run.

* fix(dreaming): resolve corpus ref session path without duplicate agent ID

The session path in corpus refs is already in the format
'sessions/main/abc.jsonl' (includes agent subdirectory). The
previous code prepended source.agentId again, creating a path
like 'agents/main/sessions/main/abc.jsonl' — which silently
returned empty in production workspaces.

Fix: use path.basename to extract just the filename from the
session path, then construct the transcript path correctly.

* refactor(dreaming): split repair utils into separate file to satisfy LOC ratchet

The dreaming-repair.ts file grew from 337 to 831 lines, exceeding the
500-line LOC ratchet limit. Split helper utilities into a new
dreaming-repair-utils.ts file:

- dreaming-repair.ts (365 lines): imports, types, public API functions
  (auditDreamingArtifacts, repairDreamingArtifacts)
- dreaming-repair-utils.ts (498 lines): all helper functions, constants,
  and internal types

* fix(ts): add missing type imports for return types in dreaming-repair.ts

* fix(deadcode): remove unused exports from dreaming-repair-utils.ts

* fix: resolve merge conflict marker in get-reply-run.ts

* fix: remove unnecessary export from INPUT_PROVENANCE_KIND_VALUES

* fix(dreaming): converge targeted repair path with wholesale archive+clear

When heartbeat contamination is found, archive the entire session-corpus directory and clear all SQLite checkpoints instead of doing targeted line-by-line rewrite. This ensures the cleaned corpus gets re-ingested under the new provenance-based forward filter.

* fix(dreaming): authenticate heartbeat transcript turns

Co-authored-by: Erick Kinnee <[email protected]>

---------

Co-authored-by: Erick Kinnee <[email protected]>
Co-authored-by: Erick Kinnee <[email protected]>
Co-authored-by: Peter Steinberger <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. P2 Normal backlog priority with limited blast radius. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. size: S status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants