Skip to content

fix(client,app): follow fork links for session roots - #807

Merged
dezren39 merged 3 commits into
mainfrom
queue/2916-784-fork-root-walks
Oct 29, 2026
Merged

dezren39 merged 3 commits into
mainfrom
queue/2916-784-fork-root-walks

Conversation

@dezren39

@dezren39 dezren39 commented Oct 29, 2026 •

Copy link
Copy Markdown

Summary

  • Follow fork.sessionID after parentID in the client family/root walk and app titlebar root resolution.
  • Cover a root → child → fork → grandchild chain, fork-link cycles, root family registration, and aggregate root cost.
  • Leave top-level session listing behavior unchanged.

Fixes #784

Verification

  • git diff --check passed.
  • Tests were not run here; per task instructions, run the client and app unit tests in CI/laptop, not the sandbox.
  • bun run check could not run because oxlint is unavailable; focused package typechecks could not run because tsgo is unavailable.
  • Production benchmark baseline/after comparison was not run in this environment; app AGENTS.md requires it for session/timeline changes.

View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-29T08:11:56.703757Z d34ae53 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@coderabbitai

coderabbitai Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 26 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 23b905a5-6a98-4aaf-bf67-6ede1c108654
Commits

Reviewing files that changed from the base of the PR and between f05429f and d34ae53.

Files selected for processing: 4
  • packages/app/src/shell/routes/session.test.ts
  • packages/app/src/shell/routes/session.ts
  • packages/client/src/solid/data.ts
  • packages/client/test/solid-data.test.ts
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 479ac432f0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

seen.add(current.parentID)
current = await get(current.parentID)
while (true) {
const parentID = current.parentID ?? current.fork?.sessionID

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Record the required session benchmark comparison

This changes session-root resolution in the app, but the commit's verification notes explicitly state that no production benchmark baseline or after-change comparison was run. The app's review requirements mandate that comparison for session or timeline changes. Record the baseline and compare this change against it before merging, including fork chains that now require additional ancestor reads.

AGENTS.md reference: packages/app/AGENTS.md:L4-L4

Useful? React with 👍 / 👎.

function resolveRoot(sessionID: string) {
let current = sessionID
let parentID = store.session.info[sessionID]?.parentID
let parentID = store.session.info[sessionID]?.parentID ?? store.session.info[sessionID]?.fork?.sessionID

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve descendant cost aggregation for fork sessions

When a fork has subagents, following its source here moves the fork and its descendants into the source root's family and removes the family entry keyed by the fork. However, session.cost() still aggregates using store.session.family[sessionID] for sessions without parentID, so calling it for the fork now falls back to only the fork's own cost and omits all subagent costs. Both the TUI sidebar and prompt footer call this accessor with the displayed session ID. Update cost aggregation alongside the family-root change and cover cost("ses_fork"); the added test only checks the source root's total.

Useful? React with 👍 / 👎.

function resolveRoot(sessionID: string) {
let current = sessionID
let parentID = store.session.info[sessionID]?.parentID
let parentID = store.session.info[sessionID]?.parentID ?? store.session.info[sessionID]?.fork?.sessionID

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep fork execution watermarks independently acknowledgeable

When a fork completes an execution, opening it now acknowledges the source session instead of the fork: createServerNotificationState in packages/app/src/shell/notifications/notification.tsx resolves session.root(sessionID) before reading the idle watermark and posting session.view. Forks still have their own idle/viewed watermarks, and that module's roots() includes forks because it filters only on parentID. Consequently, the fork remains unread in project.hasUnread() even after it is viewed, and project-level markViewed() also cannot clear it. The session-level unread accessor additionally reads the source's state rather than the fork's. Preserve independent fork acknowledgement when changing the family-root semantics.

Useful? React with 👍 / 👎.

function resolveRoot(sessionID: string) {
let current = sessionID
let parentID = store.session.info[sessionID]?.parentID
let parentID = store.session.info[sessionID]?.parentID ?? store.session.info[sessionID]?.fork?.sessionID

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve the fork as owner of its background operations

When viewing a fork that starts a shell or subagent, the app's background controls now inspect the source session instead. In packages/app/src/session/requests/model.ts, owner is derived from data.session.root(id) and passed to createSessionBackground; packages/app/src/session/requests/background.ts then reads only that owner's messages and filters live subagents by parentID === owner and shells by metadata.sessionID === owner. Following the fork link therefore hides the fork's own running operations and can show the source's operations instead, while the move-to-background action still targets the fork via params.id. Keep operation ownership separate from the newly expanded family root.

Useful? React with 👍 / 👎.

@github-actions

Copy link
Copy Markdown

The following comment was made by an LLM, it may be inaccurate:

@github-actions

Copy link
Copy Markdown

This PR doesn't fully meet our contributing guidelines and PR template.

What needs to be fixed:

  • PR description is missing required template sections. Please use the PR template.

Please edit this PR description to address the above within 72 hours, or it will be automatically closed.

If you believe this was flagged incorrectly, please let a maintainer know.

@github-actions

Copy link
Copy Markdown

Hey! Your PR title fix(client,app): follow fork links for session roots doesn't follow conventional commit format.

Please update it to start with one of:

  • feat: or feat(scope): new feature
  • fix: or fix(scope): bug fix
  • docs: or docs(scope): documentation changes
  • chore: or chore(scope): maintenance tasks
  • refactor: or refactor(scope): code refactoring
  • test: or test(scope): adding or updating tests

Where scope is the package name (e.g., app, desktop, opencode).

See CONTRIBUTING.md for details.

@dezren39

Copy link
Copy Markdown
Author

🤖 Agent note · salvage review (duplicate PR #809 closed; this PR is canonical for task 2916 / issue #784)

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 544ddede84

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

function resolveRoot(sessionID: string) {
let current = sessionID
let parentID = store.session.info[sessionID]?.parentID
let parentID = store.session.info[sessionID]?.parentID ?? store.session.info[sessionID]?.fork?.sessionID

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve fork navigation when normalizing TUI tabs

When a fork is opened with TUI tabs enabled, SessionTabsProvider now normalizes its tab ID to the source root (packages/tui/src/context/session-tabs.tsx, normalize and the route-registration effect). However, tab selection, index selection, and cycling navigate directly to that normalized ID, and no separate fork route is retained. Consequently, switching away from a fork and selecting its tab opens the source conversation instead; existing persisted fork tabs are also collapsed into the source tab. Keep the fork independently addressable in tabs, or retain its actual route separately from the expanded family ID.

Useful? React with 👍 / 👎.

function resolveRoot(sessionID: string) {
let current = sessionID
let parentID = store.session.info[sessionID]?.parentID
let parentID = store.session.info[sessionID]?.parentID ?? store.session.info[sessionID]?.fork?.sessionID

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep source requests from blocking input in a fork

If a source session has a pending permission or question and its fork is opened in the TUI, data.session.family(forkID) now includes the source and its other branches. In packages/tui/src/routes/session/index.tsx, descendantSessionIDs treats every other family member as a descendant of the viewed top-level session, then includes their requests in permissions() and forms(). Those requests set disabled() and replace the fork's composer with the source's request prompt, preventing the user from continuing the independent fork without answering a request belonging to another execution. Filter request aggregation by actual parentID descendants rather than the expanded affinity family.

Useful? React with 👍 / 👎.

@dezren39
dezren39 merged commit a6d7f78 into main Oct 29, 2026
23 of 25 checks passed
@dezren39
dezren39 deleted the queue/2916-784-fork-root-walks branch October 29, 2026 10:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(client,app): client-side session root walks ignore the fork link — a fork splits its family, cost and titlebar root from the source tree

1 participant