Skip to content

fix(api): scope compaction-summary banner to the compacted session (#6225)#6230

Merged
houko merged 4 commits into
mainfrom
fix/6225-compaction-summary-session-scope
Jun 19, 2026
Merged

fix(api): scope compaction-summary banner to the compacted session (#6225)#6230
houko merged 4 commits into
mainfrom
fix/6225-compaction-summary-session-scope

Conversation

@houko

@houko houko commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

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_summary lives in the agent-scoped canonical_sessions table — one row per agent (PRIMARY KEY agent_id, see crates/librefang-memory/src/migration.rs migrate_v5). It outlives any individual session.
  • The GET handler 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:
    let compacted_summary = if target_session_id == entry.session_id {
        ...canonical_context(agent_id, None, Some(0))   // per-AGENT row
    } else { None };
  • create_agent_session (crates/librefang-kernel/src/kernel/session_ops.rs:697update_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 field CanonicalSession.compacted_summary_session_id: Option<SessionId>, threaded through load_canonical_in_tx / save_canonical_in_tx.
    store_llm_summary now takes owning_session_id and stamps it; the legacy text-truncation compactor in append_canonical stamps the triggering session_id too.
  • crates/librefang-memory/src/substrate.rs — new compacted_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 passes Some(target_session_id) (the session actually being compacted) into store_llm_summary.
  • crates/librefang-api/src/routes/agents/sessions.rs — both GET branches (materialized + empty/active) now call compacted_summary_for_session(agent_id, <this session>) instead of the unconditional agent-scoped lookup.

Verification

Run inside the repo's librefang-rust-dev Docker 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):
    • 31 migration tests pass (incl. test_migration_creates_tables, idempotency, ladder checks).
    • 8 canonical tests pass, incl. test_canonical_backward_compat_legacy_blob (legacy row with no owner → None).
    • new test_store_llm_summary_records_owning_session_and_round_trips passes.
  • cargo test -p librefang-api --test agents_routes_integration session — 7/7 pass, including:
    • new 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 assert compacted_summary is 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_session still 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

…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.
@github-actions github-actions Bot added area/docs Documentation and guides area/kernel Core kernel (scheduling, RBAC, workflows) size/L 250-999 lines changed has-conflicts PR has merge conflicts that need resolution labels Jun 19, 2026
@houko
houko enabled auto-merge (squash) June 19, 2026 07:29
@github-actions github-actions Bot added ready-for-review PR is ready for maintainer review and removed has-conflicts PR has merge conflicts that need resolution labels Jun 19, 2026
@houko
houko merged commit 5ee00b9 into main Jun 19, 2026
32 checks passed
@houko
houko deleted the fix/6225-compaction-summary-session-scope branch June 19, 2026 08:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/docs Documentation and guides area/kernel Core kernel (scheduling, RBAC, workflows) ready-for-review PR is ready for maintainer review size/L 250-999 lines changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(api): compaction-summary banner leaks across sessions — agent-scoped canonical summary shown on a freshly created session

1 participant