Skip to content

fix: Discord follow-up replies avoid false stale snapshots#97642

Closed
scribe-dandelion-cult wants to merge 4 commits into
openclaw:mainfrom
karmaterminal:codeagent/96699-reply-init-skills-snapshot
Closed

fix: Discord follow-up replies avoid false stale snapshots#97642
scribe-dandelion-cult wants to merge 4 commits into
openclaw:mainfrom
karmaterminal:codeagent/96699-reply-init-skills-snapshot

Conversation

@scribe-dandelion-cult

@scribe-dandelion-cult scribe-dandelion-cult commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Summary

Fix false stale-snapshot conflicts during reply-session initialization when a persisted skillsSnapshot.promptRef is hydrated into runtime skillsSnapshot.prompt.

Fixes #96698.
Related to #96699.

What Problem This Solves

Discord/channel follow-up messages can fail with:

reply session initialization conflicted for agent:main:discord:channel:<channelId>

The reply-session initialization guard computes a revision by serializing the session entry. But session-store reads can represent the same logical entry in multiple shapes:

  • persisted/durable shape: skillsSnapshot: { skills, promptRef }
  • hydrated runtime shape: skillsSnapshot: { skills, prompt }
  • writer-cache shape: runtime-only fields such as skillsSnapshot.resolvedSkills

That means the optimistic guard can compare two equivalent session entries and falsely conclude that the entry changed concurrently.

Fix

Build reply-init revisions from the normalized persisted session-entry shape before comparing. This makes hydrated prompt, durable promptRef, and runtime-only resolvedSkills caches compare equivalently while keeping the optimistic guard intact for real concurrent entry mutations.

Evidence

The regression test recreates the exact mismatch:

  1. Save a session entry with a large skillsSnapshot.prompt.
  2. Verify it persists as skillsSnapshot.promptRef.
  3. Reload through the reply-init snapshot path, which hydrates the prompt.
  4. Add runtime-only skillsSnapshot.resolvedSkills to the hydrated writer entry.
  5. Attempt commitReplySessionInitialization.
  6. Before the fix: commit returns stale-snapshot.
  7. After the fix: commit succeeds.

Focused proof:

node scripts/run-vitest.mjs run src/config/sessions/session-accessor.test.ts -t 'skillsSnapshot'

Supplemental live output from Ronan after the rescue frond-build shows consecutive Discord message.action successes and zero reply-init conflict/stale-snapshot lines in the same copied journal window:

2026-06-28T21:21:32-07:00 ... [ws] ⇄ res ✓ message.action 346ms channel=discord ...
2026-06-28T21:21:39-07:00 ... [ws] ⇄ res ✓ message.action 208ms channel=discord ...
...
reply-init conflict/stale-snapshot count in window: 0

PR-scoped proof corpus:

Validation

node scripts/run-vitest.mjs run src/config/sessions/session-accessor.test.ts -t 'skillsSnapshot'
node scripts/run-vitest.mjs run src/config/sessions/session-accessor.test.ts
node scripts/run-vitest.mjs run src/config/sessions/store.skills-stripping.test.ts
node scripts/run-vitest.mjs run src/auto-reply/reply/session.test.ts
CI=true pnpm tsgo:core:test
node scripts/run-oxlint-shards.mjs --only=core --split-core

Full node scripts/test-projects.mjs was also run. It failed unrelated baseline/environment/catalog shards outside this session-store change; targeted session-store/reply-session coverage passed.

@openclaw-barnacle openclaw-barnacle Bot added size: S triage: needs-pr-context Candidate: external PR body lacks required problem context or evidence. labels Jun 29, 2026
@clawsweeper

clawsweeper Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed June 29, 2026, 12:45 AM ET / 04:45 UTC.

Summary
The PR changes reply-session initialization revision comparison to use normalized persisted session-entry shape and adds regression coverage for hydrated skill prompt blobs plus runtime-only resolved skill caches.

PR surface: Source +28, Tests +93. Total +121 across 2 files.

Reproducibility: yes. at source level: current main JSON-stringifies reply-session entries while storage can represent the same skillsSnapshot as durable promptRef, hydrated prompt, or runtime-only resolvedSkills. The inspected proof corpus also includes after-fix Discord logs, but I did not run a live Discord reproduction locally.

Review metrics: 1 noteworthy metric.

  • Discord proof window: 6 message.action successes, 0 conflict matches. The linked live log corpus exercises the affected Discord follow-up path and shows the stale-snapshot pattern absent after the fix.

Stored data model
Persistent data-model change detected: serialized state: src/config/sessions/session-accessor.test.ts, serialized state: src/config/sessions/session-accessor.ts, unknown-data-model-change: src/config/sessions/session-accessor.test.ts, unknown-data-model-change: src/config/sessions/session-accessor.ts. Confirm migration or upgrade compatibility proof before merge.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #96698
Summary: This PR is one candidate fix for the canonical Discord second-message false stale-snapshot issue; several open PRs overlap the same persisted-versus-hydrated skillsSnapshot revision identity root cause.

Members:

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

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:

  • [P2] Resolve the canonical branch choice against the newer same-root-cause PR before merge.

Mantis proof suggestion
A real Discord follow-up transcript would materially demonstrate the affected channel reply path if maintainers want fresh transport proof. A maintainer can ask Mantis to capture proof by posting this exact PR comment:

@openclaw-mantis discord thread attachment: verify two consecutive Discord messages in the same session both receive replies after a skillsSnapshot promptRef is persisted.

Risk before merge

  • [P1] A newer same-root-cause PR has stronger direct Discord before/after proof, but it was not a safe superseding landing path while live checks were unstable/failing.
  • [P1] The revision helper is part of the session-state guard for reply initialization, so the final branch must preserve true stale-snapshot rejection while ignoring only durable/runtime representation differences.

Maintainer options:

  1. Settle the canonical branch (recommended)
    Use this PR or the newer same-root-cause PR only after one branch has clean checks plus the final code, tests, and proof for the linked Discord bug.
  2. Land this PR if it remains cleaner
    Maintainers can choose this branch if they prefer its narrower session-accessor-only implementation and accept the linked log proof as sufficient.
  3. Close after a replacement is viable
    If the newer PR becomes the clean canonical landing path, close this PR as superseded with a link to that branch.

Next step before merge

  • [P2] The remaining action is maintainer canonical-branch selection across overlapping open fixes, not an automated code repair on this branch.

Security
Cleared: The diff changes session revision comparison and tests only; no dependency, workflow, secret, permission, or external code execution surface is introduced.

Review details

Best possible solution:

Land one canonical shared session-store fix after checks settle, then close the duplicate same-root-cause PRs and linked issue through that merged branch.

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

Yes at source level: current main JSON-stringifies reply-session entries while storage can represent the same skillsSnapshot as durable promptRef, hydrated prompt, or runtime-only resolvedSkills. The inspected proof corpus also includes after-fix Discord logs, but I did not run a live Discord reproduction locally.

Is this the best way to solve the issue?

Yes, this is a sound fix shape at the shared session-store boundary because it normalizes only revision comparison identity and keeps runtime hydration intact. The remaining issue is selecting one canonical green branch across overlapping open fixes.

AGENTS.md: found and applied where relevant.

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

Label changes

Label justifications:

  • P1: The PR targets a broken Discord follow-up workflow where false session-state conflicts can suppress real user replies.
  • merge-risk: 🚨 session-state: The patch changes the optimistic revision identity used to guard reply-session initialization writes over persisted session metadata.
  • merge-risk: 🚨 message-delivery: A wrong reply-session initialization comparison can continue blocking visible channel follow-up replies.
  • 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 (logs): The inspected proof corpus includes copied live Discord output showing consecutive message.action successes and zero reply-init conflict/stale-snapshot lines after the fix.
  • proof: sufficient: Contributor real behavior proof is sufficient. The inspected proof corpus includes copied live Discord output showing consecutive message.action successes and zero reply-init conflict/stale-snapshot lines after the fix.
Evidence reviewed

PR surface:

Source +28, Tests +93. Total +121 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 1 32 4 +28
Tests 1 94 1 +93
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 2 126 5 +121

What I checked:

Likely related people:

  • jalehman: GitHub commit metadata shows commit 95e37f8 added the guarded reply-session initialization accessor path that owns the stale-snapshot revision comparison. (role: introduced reply-init guard boundary; confidence: high; commits: 95e37f8e9517, 7e2b2d2987b2, b58e6e0734f2; files: src/config/sessions/session-accessor.ts, src/auto-reply/reply/session.ts)
  • steipete: GitHub history ties persisted skill prompt blob projection and hydration to commits including edd4c62 and d93394e, the storage behavior involved in the persisted-versus-hydrated mismatch. (role: adjacent promptRef persistence contributor; confidence: high; commits: edd4c62da1c2, d93394e29b1f, 0e3f7a82fd12; files: src/config/sessions/skill-prompt-blobs.ts, src/config/sessions/store-load.ts, src/config/sessions/store.ts)
  • obviyus: Merged commit d2da8c7 changed reply-session initialization serialization in the same conflict family, overlapping this runtime path without covering promptRef hydration identity. (role: recent adjacent session-initialization contributor; confidence: medium; commits: d2da8c79d9b8; files: src/config/sessions/session-accessor.ts, src/auto-reply/reply/session.ts, src/config/sessions/store.ts)
  • joshavant: They opened the newer same-root-cause PR with direct Discord repro/fix proof, so they are relevant for canonical-branch coordination. (role: same-root-cause follow-up owner; confidence: medium; commits: 0f68f1292cf7; files: src/config/sessions/session-accessor.ts, src/config/sessions/session-accessor.test.ts, src/config/sessions/store.ts)
  • vincentkoc: Recent commit 4d13055 repaired prompt blob fast-update behavior in the same persisted skillsSnapshot storage area. (role: adjacent prompt-blob repair contributor; confidence: medium; commits: 4d13055ca564; files: src/config/sessions/skill-prompt-blobs.ts, src/config/sessions/store.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 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. P1 High-priority user-facing bug, regression, or broken workflow. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. labels Jun 29, 2026
@scribe-dandelion-cult

scribe-dandelion-cult commented Jun 29, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review 794ee30

Updated PR-scoped proof corpus for this fix:

  • docs commit: karmaterminal/karmaterminal-openclaw-docs@5548616
  • index: PR-1130/PROOFS/reply-init-skills-snapshot-live/INDEX.json
  • manifest: PR-1130/PROOFS/reply-init-skills-snapshot-live/manifest.json
  • evidence: PR-1130/PROOFS/reply-init-skills-snapshot-live/frond-scribe/EVIDENCE.md
  • copied live output: PR-1130/PROOFS/reply-init-skills-snapshot-live/frond-scribe/after/ronan-post-p2-two-message-live-output.log

It includes the focused unit proof, P2 normalization gates for runtime-only skillsSnapshot.resolvedSkills, and Ronan copied live output showing consecutive Discord message.action successes plus zero reply-init conflict/stale-snapshot lines in the same window.

Strip runtime-only skillsSnapshot caches before deriving reply initialization revisions so hydrated writer entries match durable session JSON.

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

clawsweeper Bot commented Jun 29, 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.

@openclaw-barnacle openclaw-barnacle Bot removed the triage: needs-pr-context Candidate: external PR body lacks required problem context or evidence. label Jun 29, 2026
@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. labels Jun 29, 2026
Exercise runtime-only resolvedSkills on the cached writer-store entry so the reply-init revision guard proves the persisted-shape normalization path.

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

clawsweeper Bot commented Jun 29, 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 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. 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 Jun 29, 2026
@joshavant

Copy link
Copy Markdown
Contributor

Thanks @scribe-dandelion-cult for working through this same reply-session failure mode, and thanks @wiidz for the original report in #96698.

A canonical fix has now landed in #97657 via 75db48a. It addresses the same persisted-versus-hydrated skillsSnapshot revision mismatch, while preserving hydrated runtime session entries and limiting the normalization to the optimistic revision comparison.

Because #97657 has merged with focused regression coverage and live Discord before/after proof, this PR is now superseded. Closing in favor of the landed fix.

@joshavant joshavant closed this Jun 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. P1 High-priority user-facing bug, regression, or broken workflow. 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.

Discord second message fails: false stale-snapshot from skillsSnapshot hydration mismatch

2 participants