Skip to content

fix(session): skip runtime handoffs when deriving the auto title - #6012

Open
SparkofSpike wants to merge 3 commits into
Hmbown:mainfrom
SparkofSpike:codex/fix-title-runtime-envelope
Open

fix(session): skip runtime handoffs when deriving the auto title#6012
SparkofSpike wants to merge 3 commits into
Hmbown:mainfrom
SparkofSpike:codex/fix-title-runtime-envelope

Conversation

@SparkofSpike

Copy link
Copy Markdown
Contributor

Summary

Session titles auto-generated from the conversation were showing internal runtime envelopes such as <codewhale:runtime_event kind="operate_contract" ...> instead of the real prompt. Chat-template compatibility forces runtime-owned control traffic (sub-agent handoffs and completions, the Operate contract, restore checkpoints) through role = "user", and create_saved_session_with_id_and_mode derived the title from the first role == "user" message with only a <turn_meta> prefix stripped. In Operate/automation sessions whose transcript opens with an internal envelope, the envelope text became the title (truncated to 50 chars with ...), and once such a title was persisted the snapshot priority rules kept it pinned until a manual /rename. SavedSession::import_foreign carried the same stale logic, so importing such an exported session re-derived the envelope as its title.

Changes

  • fix(session): skip internal runtime handoffs during title derivation (session_manager.rs): the auto-title now comes from the first message that is both role = "user" and not is_internal_runtime_handoff(m), reusing the exact recognizer the session peek already uses (runtime_handoff.rs), so title generation and preview agree about what counts as conversation. When no real user message exists yet, the DEFAULT_SESSION_TITLE placeholder is kept and the existing snapshot healing logic promotes a real prompt title once one arrives.

  • fix(session): apply the same derivation on foreign import (session_manager.rs): the logic is extracted into a shared conversation_derived_title() used by both create_saved_session_with_id_and_mode and SavedSession::import_foreign, closing the import-side copy of the bug (review follow-up).

  • test(session): regression coverage (session_manager.rs tests): create_saved_session_skips_runtime_handoffs_when_deriving_title — an Operate-contract-first session titles from the real prompt that follows it; create_saved_session_with_only_runtime_traffic_keeps_placeholder_title — covers every handoff shape the peek filters (Operate contract, waiting/restored checkpoint, shell completion) and asserts the placeholder is kept; plus two import_foreign tests for the same guarantees on the import path. All fail on the old code.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Refactoring (no functional changes)
  • Tests

Testing

  • cargo fmt --all -- --check
  • cargo clippy --workspace --all-targets --all-features --locked (not run: full-workspace gate; the change is a shared title-derivation helper plus unit tests, and the crate compiles cleanly through the test build)
  • cargo test -p codewhale-tui --lib create_saved_session — 3 passed (regression tests + existing turn_meta title test)
  • cargo test -p codewhale-tui --lib "session_manager::" — 72 passed (incl. new import_foreign tests)
  • cargo test -p codewhale-tui --lib import_foreign — 2 passed
  • cargo test -p codewhale-tui --lib "import" (RUST_MIN_STACK=16MB) — 52 passed (contract import integration surface unaffected)
  • cargo test -p codewhale-tui --lib "runtime_handoff::" — 14 passed
  • cargo test -p codewhale-tui --lib "session_peek::" — 15 passed
  • cargo test --no-fail-fast -p codewhale-tui --lib (RUST_MIN_STACK=16MB) — 11651 passed; 9 failures verified unrelated on the unmodified baseline via git stash (missing sh/bash on this Windows host, live-network Anthropic SSE test, one rendering-surface baseline, one zai-stream thread test). One additional acp_server test overflows the default Windows test-thread stack and passes with RUST_MIN_STACK=16MB — also reproduced on the unmodified baseline.

Independent review: a reviewer sub-agent returned APPROVE-WITH-NITS; its single MAJOR finding (stale duplicate logic in SavedSession::import_foreign) was fixed in the follow-up commit above, and its suggested test-coverage extension (all peek-filtered handoff shapes) was folded into the runtime-traffic regression test.

Checklist

  • Updated docs or comments as needed (doc comment on the title-derivation lookup explaining why runtime handoffs are skipped and that the recognizer is shared with the session peek)
  • Added or updated tests where relevant (2 regression tests, both shown failing without the fix)
  • Verified TUI behavior manually if UI changes (title derivation is exercised by automatic snapshots in automation/headless flows; covered by the unit-level regression tests above)
  • Harvested/co-authored credit uses a GitHub numeric noreply address (N/A — no external contributor work is landed here)

Related Issues

No-Issue: auto-generated session titles leak <codewhale:runtime_...> internal envelopes when runtime control traffic is the first user-role message (reported in-session 2026-09-08; no matching GitHub issue or PR found at report time; branch codex/fix-title-runtime-envelope, commits eeee8b9 and 14147f6).

Chat-template compatibility forces runtime-owned control traffic (sub-agent handoffs, the Operate contract, restore checkpoints) through role = user. The auto-generated session title was derived from the first such message, so Operate/automation sessions whose transcript opened with an internal envelope got titles like '<codewhale:runtime_event kind=...' instead of the real prompt.

Reuse the runtime_handoff::is_internal_runtime_handoff recognizer the session peek already uses, so title derivation and preview agree about what counts as conversation. When no real user message exists yet the placeholder DEFAULT_SESSION_TITLE is kept.

Adds two regression tests: an Operate-contract-first session titles from the following real prompt, and a runtime-traffic-only session keeps the placeholder.
Review follow-up: SavedSession::import_foreign re-derived the title with the old first-role-user logic (no runtime-handoff filter and no <turn_meta> stripping), so importing a session whose transcript opened with an internal envelope re-introduced the exact title bug the main derivation path just fixed.

Extract conversation_derived_title() shared by both callers — it skips is_internal_runtime_handoff messages and strips a leading <turn_meta> envelope — so auto-snapshot, session creation, and foreign import all agree on what counts as a real user message.

Extends the runtime-traffic-only regression test to cover every handoff shape the session peek filters (Operate contract, waiting/restored checkpoint, shell completion) and adds two import_foreign regression tests.
macOS Test hit the 90-minute job timeout after all 14547 tests passed
(cold sccache on first run); npm wrapper smoke was killed by a runner
shutdown signal. Both are infrastructure failures, not test failures.
@SparkofSpike
SparkofSpike marked this pull request as ready for review September 8, 2026 17:08
@SparkofSpike

Copy link
Copy Markdown
Contributor Author

Ugh, I'm so confused by this CI. It's either a Test timeout or the Smoke failing. No idea what to do, but it doesn't seem like my problem. Just gonna wait for a manual merge.

@SparkofSpike
SparkofSpike requested a review from Hmbown as a code owner September 8, 2026 17:08
Hmbown pushed a commit that referenced this pull request Sep 9, 2026
… live UI

Harvested from PR #6012 by @SparkofSpike

Preserve the contributor's shared save/import title derivation and four
regression tests from eeee8b9 and 14147f6. Resolve the test-helper insertion
against current source without removing the existing session tests.

Use the existing user-turn classifier for prompt eligibility and share raw
prompt selection with the live title fallback. This also excludes generic
runtime and legacy non-authoritative metadata while retaining the first
image-only user boundary. Existing formatting, sanitization and rename
priority remain in place; previously persisted bad titles are not migrated.

Local validation:
- Exact source-fragment probe: 21 passed, 0 failed; rustc -D warnings exit 0.
- Baseline runtime-first preservation invariant: expected failure, exit 101.
- cargo fmt --all -- --check and git diff --check: exit 0.
- npm test and npm run check:web: each exit 1, missing package scripts.
- Production crate compilation/tests: not run in this lane; integrator owns
  the coordinated full-link window. Four maintainer regressions added.

Hosted PR-head CI is separate evidence: three OS test jobs passed; wrapper
binary build ended with exit 143 and runner shutdown, smoke skipped. That
failed gate is not a pass for this integrated donor.

Co-authored-by: Sh1Zuku <125943630+SparkofSpike@users.noreply.github.com>
Hmbown pushed a commit that referenced this pull request Sep 9, 2026
Record the adapted #6012 contribution and retain #6011/#6013 as open broader
requests. Describe local candidate dispatch ordering and request diagnostics
without claiming reporter reproduction or provider billing proof.

Validation: changelog sync and sync --check passed; git diff --check passed.
No new product tests for documentation. npm test and npm run check:web each
exit 1 because the Core root defines neither package script. Final candidate
product gates remain separate.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant