Skip to content

fix: preserve labeled session titles during heartbeat polls#46209

Closed
JFWaskin wants to merge 2 commits into
openclaw:mainfrom
JFWaskin:codex/heartbeat-title-stability-v2
Closed

fix: preserve labeled session titles during heartbeat polls#46209
JFWaskin wants to merge 2 commits into
openclaw:mainfrom
JFWaskin:codex/heartbeat-title-stability-v2

Conversation

@JFWaskin

Copy link
Copy Markdown

Summary

  • extract session title derivation into a small standalone helper with focused coverage
  • prefer persisted session labels before transcript-derived titles so heartbeat turns do not rename labeled sessions to heartbeat
  • add precedence coverage so displayName and subject stay ahead of label-based fallbacks

Testing

  • ./node_modules/.bin/vitest run src/gateway/session-title.test.ts

Closes #42495.

@openclaw-barnacle openclaw-barnacle Bot added gateway Gateway runtime size: M labels Mar 14, 2026
@greptile-apps

greptile-apps Bot commented Mar 14, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a bug where labeled sessions were being renamed to "heartbeat" during heartbeat polls by inserting entry.label and entry.origin?.label checks into the title-derivation priority chain ahead of firstUserMessage. The refactoring also extracts deriveSessionTitle (and its private helpers) from session-utils.ts into a new focused module (session-title.ts) with a dedicated test file.

  • session-title.ts — new standalone module; extends the old fallback chain with entry.label and entry.origin?.label precedence levels between subject and firstUserMessage.
  • session-title.test.ts — new focused test file covering all priority levels including the new label cases.
  • session-utils.ts — removes the now-relocated helpers and re-exports deriveSessionTitle from session-title.ts to maintain backwards compatibility for existing callers.
  • Note: The legacy describe("deriveSessionTitle") block in session-utils.test.ts (lines 504–597) was not removed. Its test at line 528 ("uses first user message when displayName and subject missing") is technically still passing but is now an incomplete description of the preconditions, and two unique edge-case tests from that block are absent from the new dedicated file.

Confidence Score: 4/5

  • Safe to merge; the bug fix is targeted and correct, with no breaking changes to the public API.
  • The logic change is small, well-targeted, and directly addresses the reported issue. The re-export in session-utils.ts preserves backwards compatibility for all existing callers. The only concern is that the legacy deriveSessionTitle tests in session-utils.test.ts were not updated or removed, leaving a stale test description and minor test duplication.
  • src/gateway/session-utils.test.ts — the legacy describe("deriveSessionTitle") block should either be removed (migrating its two unique cases to session-title.test.ts) or have its test descriptions updated to reflect the new label-precedence behavior.

Comments Outside Diff (1)

  1. src/gateway/session-utils.test.ts, line 528-534 (link)

    Stale test description no longer accurate

    Now that label and origin.label are checked before firstUserMessage, this test's description — "uses first user message when displayName and subject missing" — is incomplete. Technically the test still passes (the entry has neither label nor origin.label), but the description is misleading for anyone reading the test suite.

    Additionally, the whole describe("deriveSessionTitle") block in this file (lines 504–597) is now partially duplicated by the new session-title.test.ts. The old block is also missing coverage for the new label-precedence behavior, and two of its extra cases ("truncates at word boundary when possible" and "falls back to sessionId prefix without date when updatedAt missing") are absent from the new file. Consider either removing this block entirely and migrating those two unique cases into session-title.test.ts, or updating the description here to reflect the full preconditions:

Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/gateway/session-utils.test.ts
Line: 528-534

Comment:
**Stale test description no longer accurate**

Now that `label` and `origin.label` are checked before `firstUserMessage`, this test's description — `"uses first user message when displayName and subject missing"` — is incomplete. Technically the test still passes (the entry has neither `label` nor `origin.label`), but the description is misleading for anyone reading the test suite.

Additionally, the whole `describe("deriveSessionTitle")` block in this file (lines 504–597) is now partially duplicated by the new `session-title.test.ts`. The old block is also missing coverage for the new label-precedence behavior, and two of its extra cases (`"truncates at word boundary when possible"` and `"falls back to sessionId prefix without date when updatedAt missing"`) are absent from the new file. Consider either removing this block entirely and migrating those two unique cases into `session-title.test.ts`, or updating the description here to reflect the full preconditions:

```suggestion
  test("uses first user message when displayName, subject, and labels are all missing", () => {
```

How can I resolve this? If you propose a fix, please make it concise.

Last reviewed commit: 2c0be25

JFWaskin pushed a commit to JFWaskin/openclaw that referenced this pull request Mar 23, 2026
…ates

- Add 'truncates at word boundary' and 'updatedAt missing' tests to session-title.test.ts
- Remove duplicate deriveSessionTitle test block from session-utils.test.ts
- Add minimal compatibility test verifying session-utils re-export

Addresses review feedback on PR openclaw#46209.
@vincentkoc vincentkoc added the triage: refactor-only Candidate: refactor/cleanup-only PR without maintainer context. label Apr 26, 2026
@clawsweeper

clawsweeper Bot commented Apr 26, 2026

Copy link
Copy Markdown
Contributor

Closing this as implemented after Codex automated review.

PR #46209 is redundant on current main. The linked bug in #42495 is already solved by the shipped session-state path: heartbeat/cron/exec system-event turns reuse persisted session metadata, skip synthetic system-event origin derivation, and Control UI session rows resolve visible names from persisted displayName/label/origin.label before transcript-derived titles. The PR's helper extraction is now refactor-only, and related PR #42584 plus issue #46116 were already reviewed as implemented by the same current behavior.

Best possible solution:

Close this PR as already implemented. Keep the shipped system-event metadata preservation path; any remaining desire to split deriveSessionTitle into its own helper should be a separate maintainer-owned refactor, not a bug-fix PR for #42495.

What I checked:

  • System-event turns are classified before session mutation: initSessionState treats heartbeat, cron-event, and exec-event as automated system events, so they do not follow normal user-interaction session binding and continuity mutation paths. (src/auto-reply/reply/session.ts:237, 6cd047e7c270)
  • Heartbeat/system events reuse persisted route, label, and display name: For system events, session initialization reads route fields from baseEntry, persists label: persistedLabel ?? baseEntry?.label and displayName: persistedDisplayName ?? baseEntry?.displayName, and passes skipSystemEventOrigin: isSystemEvent into metadata derivation. (src/auto-reply/reply/session.ts:560, 6cd047e7c270)
  • Synthetic heartbeat origin is skipped: deriveSessionOrigin returns undefined when skipSystemEventOrigin is set for heartbeat, cron-event, or exec-event, preventing From: "heartbeat" / provider metadata from replacing the existing origin. (src/config/sessions/metadata.ts:54, 6cd047e7c270)
  • Session rows already prefer persisted labels for visible names: Gateway session rows compute displayName from entry.displayName, then group metadata, then entry.label, then origin.label; the Control UI chat selector resolves label before displayName, so preserved labels remain visible without the PR's deriveSessionTitle refactor. (src/gateway/session-utils.ts:1239, 6cd047e7c270)
  • Regression coverage exists on main: The regression test preserves the existing user route when a heartbeat targets a different chat on the shared session asserts a heartbeat-shaped update keeps the existing Feishu route and origin instead of replacing them with heartbeat metadata. (src/auto-reply/reply/session.test.ts:3396, 6cd047e7c270)
  • Shipped release contains the implementation: The latest release tag v2026.4.24 resolves to cbcfdf62c7297bda66009ea7476f053c3e9addab and contains the same system-event metadata preservation path plus the session-row entry.label ?? originLabel fallback. (src/auto-reply/reply/session.ts:648, cbcfdf62c729)

So I’m closing this as already implemented rather than keeping a duplicate issue open.

Codex Review notes: model gpt-5.5, reasoning high; reviewed against 6cd047e7c270; fix evidence: release v2026.4.24, commit cbcfdf62c729.

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

Labels

gateway Gateway runtime size: M triage: refactor-only Candidate: refactor/cleanup-only PR without maintainer context.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Heartbeat polls overwrite session displayName, breaking control UI session selector

2 participants