fix(api): scope compaction-summary banner to the compacted session (#6225)#6230
Merged
Conversation
…6225) The dashboard "Session summary (older messages compacted)" banner leaked across sessions: a freshly created session that was never compacted showed a previous unrelated conversation's summary. compacted_summary lives in the agent-scoped canonical_sessions row (one per agent_id) and outlives any individual session, but get_agent_session exposed it whenever the requested session was the agent's active one. create_agent_session makes a new session active without clearing the row, so the new session inherited the prior summary on message #1. Record which session owns the summary and gate the read on a match. A nullable compacted_summary_session_id column (schema v46, backward-compatible) is stamped by store_llm_summary with the compacted session; the GET handler surfaces the banner only when the requested session matches, via the new MemorySubstrate::compacted_summary_for_session. The summary is scoped, not lost. Closes #6225.
added 2 commits
June 19, 2026 16:04
…ummary-session-scope
…ummary-session-scope
houko
enabled auto-merge (squash)
June 19, 2026 07:29
…ummary-session-scope
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The dashboard "Session summary (older messages compacted)" banner (
CompactionSummaryBanner) appeared on a freshly created session that was never compacted, showing a previous unrelated conversation's summary.Root cause is a scope mismatch:
compacted_summarylives in the agent-scopedcanonical_sessionstable — one row per agent (PRIMARY KEY agent_id, seecrates/librefang-memory/src/migration.rsmigrate_v5). It outlives any individual session.get_agent_session(crates/librefang-api/src/routes/agents/sessions.rs) exposed that summary whenever the requested session was the agent's active session, with no check that this session's own history was ever compacted:create_agent_session(crates/librefang-kernel/src/kernel/session_ops.rs:697→update_session_id) makes the new session active but does not clear/scope the canonical row, so the fresh session inherited the prior summary and the banner rendered on message Bump docker/build-push-action from 6 to 7 #1.Fix — track the owning session and gate the read on a match
Chosen approach: record which session a canonical summary belongs to, then surface it only on that session.
This preserves the summary for the session that legitimately owns it (it is scoped, not cleared), and is robust regardless of how a session becomes active.
Changes:
crates/librefang-memory/src/migration.rs— schema v46:ALTER TABLE canonical_sessions ADD COLUMN compacted_summary_session_id TEXT(nullable).Backward-compatible: existing rows get
NULL, which the read path treats as "owned by no specific session" (banner hidden) until the next compaction stamps the owner.crates/librefang-memory/src/session.rs— new fieldCanonicalSession.compacted_summary_session_id: Option<SessionId>, threaded throughload_canonical_in_tx/save_canonical_in_tx.store_llm_summarynow takesowning_session_idand stamps it; the legacy text-truncation compactor inappend_canonicalstamps the triggeringsession_idtoo.crates/librefang-memory/src/substrate.rs— newcompacted_summary_for_session(agent_id, session_id)returning the summary only when its recorded owner matches.crates/librefang-kernel/src/kernel/agent_runtime.rs— the compactor passesSome(target_session_id)(the session actually being compacted) intostore_llm_summary.crates/librefang-api/src/routes/agents/sessions.rs— both GET branches (materialized + empty/active) now callcompacted_summary_for_session(agent_id, <this session>)instead of the unconditional agent-scoped lookup.Verification
Run inside the repo's
librefang-rust-devDocker image (per-worktree target volume), scoped to touched crates.cargo check -p librefang-api -p librefang-memory -p librefang-kernel --lib— Finished, clean.cargo clippy -p librefang-api -p librefang-memory -p librefang-kernel --all-targets -- -D warnings— Finished, zero warnings.cargo test -p librefang-memory --lib(migration + canonical):test_migration_creates_tables, idempotency, ladder checks).test_canonical_backward_compat_legacy_blob(legacy row with no owner →None).test_store_llm_summary_records_owning_session_and_round_tripspasses.cargo test -p librefang-api --test agents_routes_integration session— 7/7 pass, including:test_compacted_summary_does_not_leak_to_new_session— the regression: compact session A, confirm A shows the banner, POST a new session B via/api/agents/{id}/sessions(making it active), GET the active session and assertcompacted_summaryis null; assert the substrate still records A as owner.test_agent_session_returns_compacted_summary_after_force_compact(updated to stamp the active session) still passes — the legitimate case still shows the summary.test_agent_session_returns_null_summary_for_non_canonical_sessionstill passes.Live LLM verification not required: this is a memory/API wiring change with no LLM call-path involvement, fully covered by the integration test above.
Out-of-scope follow-ups
None.
Closes #6225