Skip to content

[fix] Session lists only show rows they can open - #5993

Merged
mmabrouk merged 1 commit into
release/v0.112.1from
fix/web-session-openability
Aug 12, 2026
Merged

[fix] Session lists only show rows they can open#5993
mmabrouk merged 1 commit into
release/v0.112.1from
fix/web-session-openability

Conversation

@mmabrouk

@mmabrouk mmabrouk commented Aug 12, 2026

Copy link
Copy Markdown
Member

Context

This is the top PR of the untitled-sessions stack. It sits on #5991 and on the runner PR #5992.

What the user sees. The session lists show rows that they cannot open. Two cases:

  • A headless session with no references renders normally, and the click does nothing.
  • A session with only a variant reference navigates to /apps/<variant-id>/playground, which is not a real route.

Why it happens. The rule for showing a row and the rule for opening it disagreed. A row was shown if it had references, or a name, or a last message, or a trigger. But opening it needed an agent id taken from the references. The sidebar already dropped such rows. The sessions page and the home cards did not.

Changes

The open target follows the key

sessionAgentId now prefers the reference element whose key is "workflow". A keyed family with no workflow element resolves to null, so the code no longer guesses. The old "first UUID wins" fallback stays, but only for old rows that carry no key at all.

The wire key is parsed as an open string, not as a fixed set of values. If the backend adds a new family name later, such a row reads as "cannot open". That is safer than falling back to the guess and its dead route.

One rule for what each group shows

sessionGroupRows(group, rows) in sessionListPolicy.ts now decides this in one place.

  • The main list keeps only rows that are started and openable.
  • The pinned group shows everything.
  • The waiting group shows everything.

Pinned rows stay, because a pin that silently disappears reads as data loss. Waiting rows stay, because hiding one would ask the user to approve a tool call they cannot see.

Automations keep their existing exemption. A scheduled run has a row before its first turn lands, and that row must still show.

No copy changed anywhere. An unopenable pinned row renders inert, exactly as it does today.

Known divergence, and it predates this PR. The sidebar drops unopenable rows in every group, pinned included. This PR leaves the sidebar alone.

This PR also carries the documentation for the stack's wire changes. That is the sessions-takeover architecture page, the runner interface page, and two superseded design notes. It also carries the investigation workspace docs/design/untitled-sessions-investigation/, which holds the findings, the plan and the handoff.

Tests

  • 63 @agenta/sessions tests, 974 @agenta/entities tests and 396 app-level tests pass.
  • tsc --noEmit is clean. Scoped lint changed nothing.
  • New unit tests cover five cases: the key preference, a keyed family with no workflow resolving to null, an unknown key still counting as keyed, the main-list filtering, and the pinned and waiting exemptions.

What to QA

  • Sessions page: click each visible row. Every one opens its conversation. The old untitled headless rows have no references, so they no longer appear in the main list.
  • Pin a session. Use a legacy broken one too, if you have one. The pin stays visible. A broken pinned row is inert but present.
  • Automations view: scheduled-run sessions still appear. That includes ones with no turn yet.
  • Deploy the full stack, then run a test_run from a builder agent. The new session appears with the prompt text as its title. It opens on the right agent's playground.
  • Regression: the playground rail behaves as before. It never used references.

@dosubot dosubot Bot added the size:XL This PR changes 500-999 lines, ignoring generated files. label Aug 12, 2026
@vercel

vercel Bot commented Aug 12, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agenta-documentation Error Error Aug 12, 2026 7:56pm

Request Review

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • Improved Session Handling

    • Sessions now retain workflow references and titles more reliably, including headless sessions.
    • Session lists hide entries that cannot be opened while preserving pinned and waiting sessions.
    • Sessions can be opened using stream-level references before their first turn is added.
    • Workflow references are prioritized consistently, with improved support for legacy sessions.
  • Documentation

    • Updated guidance on session attribution, workflow references, heartbeats, and session opening behavior.
    • Added investigation findings, implementation plans, and handoff documentation for untitled sessions.
  • Tests

    • Added coverage for workflow reference selection, session openability, filtering, and legacy compatibility.

Walkthrough

The PR documents workflow-reference propagation and session persistence. It adds keyed session-reference types, centralizes agent resolution, filters unopenable sessions, updates list hooks, and adds unit tests.

Changes

Session attribution and openability

Layer / File(s) Summary
Attribution and persistence contracts
docs/design/agent-workflows/interfaces/cross-service/service-to-agent-runner.md, docs/design/agent-workflows/projects/build-kit-tools-cleanup/research.md, docs/design/agent-workflows/projects/sessions-takeover/architecture.md, docs/design/agenta-sessions-ux/plan.md
The documentation defines keyed workflow references, stream-level persistence, fill-once heartbeat proposals, session naming, and workflow-based session targeting.
Untitled-session investigation and delivery plan
docs/design/untitled-sessions-investigation/*
The investigation, plan, and handoff documents describe session-title and reference gaps, implementation lanes, validation results, and remaining delivery tasks.
Keyed reference schema and agent resolution
web/packages/agenta-entities/src/session/core/schema.ts, web/packages/agenta-entities/src/session/index.ts, web/packages/agenta-sessions/src/row/*, web/oss/src/components/AgentChatSlice/assets/sessionOpenTarget.*
Session references accept optional keys for workflow families. sessionAgentId selects workflow ownership, preserves legacy unkeyed fallback, and supplies ownership to session view models and open-target resolution.
Openability policy and list integration
web/packages/agenta-sessions/src/state/*, web/packages/agenta-sessions/tests/unit/sessionListPolicy.test.ts
Shared list policy functions filter main-group rows by started state and openability while retaining automation, pinned, and waiting rows according to their group rules. Tests cover reference handling, ordering, automation retention, and group filtering.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

  • Agenta-AI/agenta#5991: Extends the keyed workflow-reference and session-stream attribution model into frontend resolution and filtering.
  • Agenta-AI/agenta#5903: Also updates service-to-agent-runner.md for runContext workflow identity propagation.
  • Agenta-AI/agenta#5944: Modifies session-list policy and hooks for session-row filtering.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 60.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly states that session lists now show only rows users can open, matching the primary change.
Description check ✅ Passed The description explains the unopenable-row problem, the filtering changes, reference handling, tests, and QA scope.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/web-session-openability

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Railway Preview Environment

Status Destroyed (PR closed)

Updated at 2026-08-12T20:10:37.430Z

@mmabrouk mmabrouk left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inline notes to explain the intent behind each part of this change. They are explanations for the reviewer, not change requests.

if (workflow?.id) return workflow.id
if (usable.some((ref) => Boolean(ref.key))) return null
return usable[0]?.id ?? null
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This decides where a row opens, and it is the only place that decides it now.

The references arrive as a flat list, one element per member of the workflow family, each naming its member in key. Only the workflow element holds an app id. A variant or revision id sends the playground to a route that does not exist. So the code prefers the workflow element.

const usable = (row.references ?? []).filter((ref) => ref.id && isValidUUID(ref.id))
const workflow = usable.find((ref) => ref.key === "workflow")
if (workflow?.id) return workflow.id
if (usable.some((ref) => Boolean(ref.key))) return null

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is why a keyed row without a workflow element returns null instead of an id.

If any element carries a key, the writer labelled the family. A missing workflow element then means the row genuinely has no app id, and returning the first uuid would send the user to a dead route. Rendering the row inert is the correct outcome.

Note that ANY non-empty key counts as keyed, not only the three we act on. A key this code does not recognise still proves the writer labelled the family. Treating it as unkeyed would drop through to the old guess.

const workflow = usable.find((ref) => ref.key === "workflow")
if (workflow?.id) return workflow.id
if (usable.some((ref) => Boolean(ref.key))) return null
return usable[0]?.id ?? null

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The old heuristic survives, but only for rows that carry no key at all. Those rows were written before the runner stamped the family, and the first uuid is the only thing on offer for them. Removing this line would hide history that opens correctly today.

// Which family member this id is. Absent on rows written before the runner stamped it; open
// string, see `SessionReferenceKey`. `.catch(undefined)` keeps a non-string from failing the
// whole page's parse.
key: z.string().nullish().catch(undefined),

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

key is parsed as an open string, not as an enum of the three known values.

The backend stores reference keys permissively, so a future family name can reach the frontend. If this schema narrowed an unrecognised key to undefined, the element would read as unkeyed. The row would then fall back to the first-uuid guess and its dead route, which is what this stack is removing.

.catch(undefined) keeps a non-string value from failing the whole page's parse.

export const sessionGroupRows = <T extends SessionStream>(
group: "main" | "pinned" | "waiting",
rows: readonly T[],
): T[] => (group === "main" ? openableSessions(startedSessions(rows)) : [...rows])

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One function now decides what each group shows.

main is browsing, so it hides chats nobody started and rows with nowhere to open. pinned and waiting apply neither rule. Both hold rows someone asked for by name, where a missing row reads as a fault rather than as tidiness.

Concretely: a pin that silently disappears reads as data loss, and hiding an approval-gated row would ask its owner to approve a tool call they cannot see. An inert row is the lesser harm in both cases.


/** The automation carve-out, on the same two signals `isStartedSession` accepts. */
const hasAutomationIdentity = (row: SessionStream): boolean =>
isAutomationSession(row) || Boolean(row.trigger)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The automation carve-out, on the same two signals isStartedSession already accepts.

A trigger row IS its schedule. It has a name, an identity and its own menu before its first turn ever lands. Filtering it out on openability would blank the automations list.

*/
export function sessionOpenTarget(row: SessionStream): PendingSessionOpen | null {
const appId = row.references?.find((ref) => ref.id && isValidUUID(ref.id))?.id
const appId = sessionAgentId(row)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This used to run its own "first valid uuid" search. It now calls sessionAgentId, so the list row and the open target cannot disagree about which reference is the agent.

@mmabrouk

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@mmabrouk
mmabrouk force-pushed the fix/runner-typed-session-references branch from 4075881 to 3439b9e Compare August 12, 2026 19:36
@mmabrouk
mmabrouk force-pushed the fix/web-session-openability branch from 182bc36 to ef48d73 Compare August 12, 2026 19:36

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 27515e13-0aef-4319-b4c2-c141ad98264c

📥 Commits

Reviewing files that changed from the base of the PR and between 4075881 and 182bc36.

📒 Files selected for processing (20)
  • docs/design/agent-workflows/interfaces/cross-service/service-to-agent-runner.md
  • docs/design/agent-workflows/projects/build-kit-tools-cleanup/research.md
  • docs/design/agent-workflows/projects/sessions-takeover/architecture.md
  • docs/design/agenta-sessions-ux/plan.md
  • docs/design/untitled-sessions-investigation/HANDOFF.md
  • docs/design/untitled-sessions-investigation/findings.md
  • docs/design/untitled-sessions-investigation/plan.md
  • web/oss/src/components/AgentChatSlice/assets/sessionOpenTarget.test.ts
  • web/oss/src/components/AgentChatSlice/assets/sessionOpenTarget.ts
  • web/packages/agenta-entities/src/session/core/schema.ts
  • web/packages/agenta-entities/src/session/index.ts
  • web/packages/agenta-sessions/src/row/index.ts
  • web/packages/agenta-sessions/src/row/sessionAgent.ts
  • web/packages/agenta-sessions/src/row/viewModel.ts
  • web/packages/agenta-sessions/src/state/index.ts
  • web/packages/agenta-sessions/src/state/sessionListPolicy.ts
  • web/packages/agenta-sessions/src/state/useSessionCardList.ts
  • web/packages/agenta-sessions/src/state/useSessionsList.ts
  • web/packages/agenta-sessions/tests/unit/sessionAgent.test.ts
  • web/packages/agenta-sessions/tests/unit/sessionListPolicy.test.ts

Comment thread docs/design/agent-workflows/projects/sessions-takeover/architecture.md Outdated
Comment thread docs/design/agenta-sessions-ux/plan.md Outdated
Comment thread docs/design/untitled-sessions-investigation/findings.md Outdated
Comment thread docs/design/untitled-sessions-investigation/HANDOFF.md Outdated
Comment thread docs/design/untitled-sessions-investigation/HANDOFF.md Outdated
Comment thread docs/design/untitled-sessions-investigation/plan.md Outdated
Comment thread docs/design/untitled-sessions-investigation/plan.md Outdated

@mmabrouk mmabrouk left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@mmabrouk mmabrouk added the lgtm This PR has been approved by a maintainer label Aug 12, 2026
A session row was shown if it had references OR a name OR a last message, but was openable only when an agent id could be derived from its references, so headless sessions rendered and swallowed the click (or navigated to /apps/<variant-id>/playground, a dead route, when the first UUID was a variant). sessionAgentId now prefers the reference with key=workflow, treats any keyed family without one as unopenable instead of guessing, and keeps the first-UUID fallback only for legacy untagged rows. The main lists on the sessions page and home cards drop unopenable rows like the sidebar always did; pinned and waiting groups stay visible (a vanishing pin reads as data loss, and a hidden waiting row would ask users to approve a tool call they cannot see); automations keep their carve-out. Includes the docs sync for the new wire fields and the untitled-sessions investigation workspace.
@mmabrouk
mmabrouk force-pushed the fix/runner-typed-session-references branch from 3439b9e to 06477eb Compare August 12, 2026 19:56
@mmabrouk
mmabrouk force-pushed the fix/web-session-openability branch from ef48d73 to e83347e Compare August 12, 2026 19:56
@mmabrouk
mmabrouk changed the base branch from fix/runner-typed-session-references to release/v0.112.1 August 12, 2026 20:10
@mmabrouk
mmabrouk merged commit 698d2c1 into release/v0.112.1 Aug 12, 2026
65 of 69 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug frontend lgtm This PR has been approved by a maintainer size:XL This PR changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant