Skip to content

fix(ui): remove scope=project from session list requests - #572

Merged
pascalandr merged 4 commits into
NeuralNomadsAI:devfrom
JDis03:fix/session-list-directory-scope
Jul 8, 2026
Merged

pascalandr merged 4 commits into
NeuralNomadsAI:devfrom
JDis03:fix/session-list-directory-scope

Conversation

@JDis03

@JDis03 JDis03 commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Problem

PR #565 (commit e29c3a0) added scope=project to session list requests to avoid pagination limits. With OpenCode 1.17.x, the /session endpoint ignores the directory filter when scope=project is present and returns all sessions from the global SQLite database instead.

This makes the same session appear in every project tab. Deleting a session from one tab deletes it from the shared DB, affecting all of them.

Reproduction

# Pick any opencode workspace port
PORT=4096
curl -s -u "codenomad:$PASSWORD" \
  "http://127.0.0.1:$PORT/session?directory=/home/user/project-a&scope=project" \
  | jq 'length'
# Returns sessions from project-b too — should only return project-a's

curl -s -u "codenomad:$PASSWORD" \
  "http://127.0.0.1:$PORT/session?directory=/home/user/project-a" \
  | jq 'length'
# Correct: only project-a's sessions

Related: anomalyco/opencode#33113

Fix

Drop scope: "project" from buildProjectSessionListOptions(). The directory parameter alone correctly filters sessions to the requested folder on OpenCode 1.17.x. scope: "project" can be reintroduced once the backend respects directory alongside it.

Validation

  • directory=/test → 1 session (correct)
  • directory=/idm → 1 session (correct)
  • Same session no longer appears in multiple project tabs
  • Deleting a session in one project no longer affects others

OpenCode 1.17.10 has a bug in the /session endpoint: when scope=project
is passed, it ignores the directory filter and returns ALL sessions
across every project. This made the same session appear in every
project tab in CodeNomad — deleting a session from one tab deleted
it from all of them.

The scope=project parameter was added in upstream PR NeuralNomadsAI#565
(commit e29c3a0) to avoid pagination limits on the default endpoint.
With the directory filter ignored, that optimization becomes a
correctness bug, so drop scope=project until OpenCode fixes the
backend.

With directory alone, the endpoint correctly returns only the
sessions for the requested folder. Verified via curl on the running
process: directory=/test returns 1 session (Hola greeting),
directory=/idm returns 1 session (up).

Co-Authored-By: Claude <[email protected]>
@github-actions

Copy link
Copy Markdown
Contributor

PR builds are available as GitHub Actions artifacts:

https://github.com/NeuralNomadsAI/CodeNomad/actions/runs/28476660488

Artifacts expire in 7 days.
Artifacts:

  • pr-572-5f90c07932d62d0429e5c37d168a884686021f64-tauri-macos
  • pr-572-5f90c07932d62d0429e5c37d168a884686021f64-tauri-linux
  • pr-572-5f90c07932d62d0429e5c37d168a884686021f64-electron-macos
  • pr-572-5f90c07932d62d0429e5c37d168a884686021f64-tauri-windows
  • pr-572-5f90c07932d62d0429e5c37d168a884686021f64-tauri-macos-arm64
  • pr-572-5f90c07932d62d0429e5c37d168a884686021f64-electron-windows
  • pr-572-5f90c07932d62d0429e5c37d168a884686021f64-electron-linux

@pascalandr

Copy link
Copy Markdown
Contributor

@CodeNomadBot
I want you to perform the gatekeeper checks for latest PR state
check for regressions
check for better possible implementations
Be the PR gatekeeper
Be ruthless about code quality

@pascalandr
pascalandr self-requested a review July 7, 2026 18:18
@codenomadbot

codenomadbot Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Gatekeeper review for latest PR state.

I understand this PR as: remove scope: "project" from project session list requests because OpenCode 1.17.x ignores directory when that scope is present, causing cross-project session leakage. I reviewed the current diff, nearby call sites, and ran focused validation.

Findings:

  1. BLOCKING: the PR leaves a failing regression test behind.

packages/ui/src/stores/session-pagination.test.ts:11-16 still asserts that buildProjectSessionListOptions() returns scope: "project". The focused test fails exactly on that stale expectation:

npm exec tsx -- --test packages/ui/src/stores/session-pagination.test.ts
✖ builds a one-shot project-scoped request without pagination params
AssertionError: actual omitted scope, expected included scope: 'project'

This should be updated before merge so the test now protects the intended behavior: directory, optional search, and limit, with no scope, start, or cursor. The test name should also stop saying project-scoped request if that means the API scope parameter.

  1. SHOULD FIX: runtime logging now lies about the request being sent.

packages/ui/src/stores/session-api.ts:211 still logs:

scope: "project"

But fetchV2Sessions() now calls client.session.list(buildProjectSessionListOptions(...)), and that helper no longer sends scope. This is not a functional request regression, but it is bad operational hygiene: anyone debugging this exact bug from logs would think the broken parameter is still present. Either remove scope from the log payload or log the actual listOptions produced by buildProjectSessionListOptions().

Implementation assessment:

The production-side fix in packages/ui/src/stores/session-list-options.ts is the right minimal implementation. Removing the field from both ProjectSessionListOptions and the returned object is cleaner than conditionally gating on SDK/OpenCode versions, because the reported supported behavior is that directory alone filters correctly on OpenCode 1.17.x.

Regression risk:

The main behavioral tradeoff is returning to a directory-only request with limit: 1000. That matches the issue’s proposed fix and preserves the previous pagination-limit mitigation as much as possible without reintroducing the backend bug. I did not find another call site that re-adds scope: "project" to session list requests.

Validation performed:

  • git diff origin/dev...HEAD shows only packages/ui/src/stores/session-list-options.ts changed.
  • Searched for buildProjectSessionListOptions, scope: "project", and session list call sites.
  • Ran npm exec tsx -- --test packages/ui/src/stores/session-pagination.test.ts: fails due to stale test expectation above.
  • Ran npm run typecheck --workspace @codenomad/ui: failed in this checkout due missing dependency/type resolution noise such as Cannot find module 'solid-js' and many unrelated existing type errors, so it was not useful as PR-specific signal.

Verdict: do not merge yet. The request-construction fix is correct, but the PR needs at least the focused test updated, and preferably the stale log corrected, before it passes a ruthless quality gate.

--
Yours,
CodeNomadBot

Update buildProjectSessionListOptions test expectation to match the
new request shape (no scope field). Also drop the stale scope field
from the session-list log so the log reflects what is actually sent.

Refs PR NeuralNomadsAI#572
@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

PR builds are available as GitHub Actions artifacts:

https://github.com/NeuralNomadsAI/CodeNomad/actions/runs/28914698265

Artifacts expire in 7 days.
Artifacts:

  • pr-572-2f22aedf6b264f65579d29519b8b8100a1b17abd-tauri-macos
  • pr-572-2f22aedf6b264f65579d29519b8b8100a1b17abd-tauri-linux
  • pr-572-2f22aedf6b264f65579d29519b8b8100a1b17abd-tauri-windows
  • pr-572-2f22aedf6b264f65579d29519b8b8100a1b17abd-electron-macos
  • pr-572-2f22aedf6b264f65579d29519b8b8100a1b17abd-tauri-macos-arm64
  • pr-572-2f22aedf6b264f65579d29519b8b8100a1b17abd-electron-windows
  • pr-572-2f22aedf6b264f65579d29519b8b8100a1b17abd-electron-linux

@pascalandr

Copy link
Copy Markdown
Contributor

@CodeNomadBot
same review

@codenomadbot

codenomadbot Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Repeat gatekeeper review for latest PR state.

I understand same review as: re-run the strict PR gatekeeper pass after the follow-up commit, checking whether the prior blockers were fixed and whether the current implementation introduces regressions. I treated this as review-only.

Findings:

No blocking findings in the latest state.

What changed since the prior review:

  • packages/ui/src/stores/session-pagination.test.ts now expects no scope field and explicitly asserts "scope" in options is false.
  • The stale test name was updated from project-scoped to directory-scoped wording.
  • packages/ui/src/stores/session-api.ts:211 no longer logs scope: "project", so logs no longer contradict the actual request.

Implementation assessment:

The implementation remains the right minimal fix. buildProjectSessionListOptions() now returns only directory, optional search, and limit, avoiding the OpenCode 1.17.x backend behavior where scope=project causes directory to be ignored. I did not find another session-list call path under packages/ui/src/stores that re-adds scope: "project".

Validation performed:

npm exec tsx -- --test packages/ui/src/stores/session-pagination.test.ts

Result: passed, 3/3 tests.

npm run typecheck --workspace @codenomad/ui

Result: failed in this checkout due broad dependency/type-resolution failures, starting with missing zod, solid-js, @kobalte/core/dialog, solid-toast, and related JSX/runtime types. This does not appear PR-specific based on the scope of the errors, so I am not treating it as a blocker for this change.

Quality notes:

  • packages/ui/src/stores/session-api.ts is approximately 1002 lines, above the project’s file-size warning threshold. This PR only touches one log line there, so I do not recommend bundling a refactor into this fix.

Verdict: mergeable from this gatekeeper pass, assuming CI/build artifacts are otherwise green and the runtime validation in the PR description remains accurate.

--
Yours,
CodeNomadBot

@shantur

shantur commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

@pascalandr
I think this needs to be analysed. I had a requirement to add scope=project to make the sessions available from worktrees.

@JDis03

JDis03 commented Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

Analysis of the scope=project removal

@shantur — thanks for raising the worktree concern. I dug into our actual session data before pushing back and want to share what I found so we can decide this on evidence rather than speculation. I am happy to revise the approach if it conflicts with the worktree requirement.

What scope=project actually does in OpenCode 1.17.x

Looking at ~/.local/share/opencode/opencode.db, OpenCode groups sessions by project_id, and project_id is derived from project.worktree. The interesting mappings I see today:

Directory Project ID Notes
/home/dark/Project/idm f6d9b8cf… Single folder in this project
/home/dark/Project/codenomad e3625924… Single folder
/home/dark/Project/test global Non-git folder
/home/dark/Project/tiktok global Non-git folder, same as /test

Sessions in non-git folders are merged into a "global" project, so /test and /tiktok share the same project_id = global.

What the user-visible bug was

In a fresh CodeNomad tab for /test, the sidebar showed Hola greeting, Find Jellyfin IMDB lookup endpoint, and Automatizar publicaciones TikTok — the last one is /tiktok, not /test. Both folders are "global" in OpenCode, so scope=project returned both.

When I added directory=/home/dark/Project/test as well, OpenCode 1.17.9 on this build did not narrow the response. The user behavior I confirmed earlier (sidebar mixing sessions across folder tabs) lines up with that.

Worktree concern

I cannot reproduce a session-in-worktree scenario on this machine today, so I do not want to claim that scope=project is "safe to remove globally". The two paths I can think of:

  1. Sibling directories under the same git repo (the bc5afa1… project): SQLite shows memory-system and the now-removed hackDark both had project_id = bc5afa1…. This is the only place scope=project would surface additional sessions in the same project. They are not literal git worktrees though — hackDark was just a normal sibling that OpenCode matched onto the same project row. If the desired behavior is "show all sessions in the same git repo across sibling folders", we have lost that with this PR.
  2. Actual git worktree sessions (.codenomad/worktrees/foo) — I do not have a reproducible case here. The behavior the previous PR fix(ui): scope project session list requests #565 added would have supported them, but I do not know if they ever appeared correctly under scope=project either, given the broader backend bug.

Concrete questions before merge

@shantur, can you confirm one of:

  1. Which exact directory value does the missing worktree session have in OpenCode DB? (x-opencode-directory forwarded by CodeNomad, or the main repo path?)
  2. When the worktree session is missing, does adding scope=project to the current request actually return it, or does it still ignore directory?
  3. If the answer to (2) is "yes it does return it today on a fresh OpenCode 1.17.x", then a follow-up OpenCode bug to support both directory filtering AND worktree-session inclusion is the right path — and this PR is the right temporary fix until that lands.

Without (2) being true, keeping scope=project does not actually solve the original issue #518 either; it only papers over one form of cross-folder leakage with another.

My read

  • This PR fixes the reproducer I have (the one @pascalandr and I saw with Hola greeting/TikTok showing in /test).
  • The cost is potentially losing cross-sibling-folder visibility for project_id-grouped sessions, which may or may not include real worktree sessions depending on OpenCode version.
  • I think the right next step is OpenCode upstreaming support for {directory, scope=project} together, then re-adding scope=project here once that lands. Until then, removing it is strictly less buggy than keeping it on 1.17.x.

Happy to revise wording, add a test for the sibling-folder scenario, or split the change into two PRs if that helps. Just want to make sure the decision is based on the actual session data and not the original PR #565 description.

Restore scope=project on session list requests so worktree sessions remain visible through the OpenCode project-scoped endpoint, then filter the returned sessions client-side to the current root and known CodeNomad worktree directories. This keeps the original worktree-session requirement while avoiding unrelated cross-directory leakage when OpenCode ignores directory alongside project scope.

Add focused tests for project-scoped request construction, root/worktree filtering, and Windows path normalization.

Validation: npm exec tsx -- --test packages/ui/src/stores/session-pagination.test.ts; npm run typecheck --workspace @codenomad/ui; npm run build --workspace @codenomad/ui; npm run build:tauri.
@pascalandr

pascalandr commented Jul 8, 2026 •

Copy link
Copy Markdown
Contributor

@JDis03 To answer your analysis more directly: I agree with the distinction you made.

I pushed a comit to try to preserve both requirements:

  • keep scope=project so OpenCode still returns project/worktree sessions
  • then filter the returned sessions client-side to the current root directory plus known CodeNomad worktree directories

So this avoids the blanket removal of scope=project, while still preventing unrelated /test / /tiktok-style leakage when the backend ignores directory.

This should be a safer temporary client-side workaround until OpenCode supports { directory, scope=project } correctly upstream.

Validation passed: focused session pagination tests, UI typecheck, UI build, and Tauri release build/manual exe launch.

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

PR builds are available as GitHub Actions artifacts:

https://github.com/NeuralNomadsAI/CodeNomad/actions/runs/28954943119

Artifacts expire in 7 days.
Artifacts:

  • pr-572-ca2450bc94c7af4287053f3f28ef0ca6494e194e-electron-windows

@pascalandr

Copy link
Copy Markdown
Contributor

@CodeNomadBot

I want you to perform the gatekeeper checks for latest PR state

check for regressions
check for better possible implementations
Be the PR gatekeeper
Be ruthless about code quality

@codenomadbot

codenomadbot Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Gatekeeper review for latest PR state.

I understand the latest request as: re-run the ruthless quality gate after the PR changed direction from blanket scope=project removal to keeping scope=project and filtering returned sessions client-side to the current root plus known CodeNomad worktree directories. I treated this as review-only.

Findings:

No blocking findings in the latest state.

Implementation assessment:

The current approach is materially better than the previous blanket removal because it preserves the #565 requirement that project/worktree sessions remain discoverable while still mitigating the OpenCode 1.17.x bug where { directory, scope=project } leaks unrelated directories.

The key path is now:

  • packages/ui/src/stores/session-list-options.ts keeps scope: "project" in buildProjectSessionListOptions().
  • packages/ui/src/stores/session-api.ts:148-156 fetches the project-scoped result and filters it through filterProjectScopedSessions() using [options.directory, ...getWorktrees(instanceId).map(wt => wt.directory)].
  • packages/ui/src/stores/instances.ts:387-398 loads worktrees before the initial fetchSessions() call, so the main hydration path should have the allowed worktree directories available before filtering.

Regression checks:

  • I searched the store call paths for buildProjectSessionListOptions, client.session.list, scope: "project", and worktree loading.
  • The stale-log concern from the earlier review no longer applies because the request really is project-scoped again.
  • The prior stale-test blocker no longer applies because the tests now match the current intended behavior.

Validation performed:

npm exec tsx -- --test packages/ui/src/stores/session-pagination.test.ts

Result: passed, 5/5 tests.

npm run typecheck --workspace @codenomad/ui

Result: failed in this checkout because dependencies/types are broadly unresolved, starting with missing solid-js, @opencode-ai/sdk/v2, JSX runtime types, and related package declarations. This is not useful as PR-specific signal in the current bot workspace.

npm run build --workspace @codenomad/ui

Result: failed in this checkout because vite is not installed/resolvable. Again, not useful as PR-specific signal here.

Residual risks / quality notes:

  • filterProjectScopedSessions() intentionally keeps sessions with no directory (return !directory || allowed.has(directory)). That is a pragmatic compatibility fallback, but it also means directory-less sessions cannot be isolated by project tab and could still appear everywhere if OpenCode emits directory-less records. I am not blocking on this because the thread’s evidence and tests are directory-based, but this is the main remaining behavioral assumption.
  • Directory normalization is small and tested for Windows drive paths. There is a similar normalizer in opencode-workspace-matching.ts; consolidating that could reduce drift later, but doing so in this PR would be scope creep.
  • packages/ui/src/stores/session-api.ts is approximately 1013 lines, above the project’s file-size warning threshold. This PR touches a narrow path there, so I do not recommend bundling a refactor into this fix.

Verdict: mergeable from this gatekeeper pass, assuming CI artifacts/builds from a dependency-complete environment are green and the manual validation described in the thread remains accurate.

--
Yours,
CodeNomadBot

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

PR builds are available as GitHub Actions artifacts:

https://github.com/NeuralNomadsAI/CodeNomad/actions/runs/28955202802

Artifacts expire in 7 days.
Artifacts:

  • pr-572-512b6fac378b9960c9863118a384e8443e6c473a-tauri-linux
  • pr-572-512b6fac378b9960c9863118a384e8443e6c473a-tauri-windows
  • pr-572-512b6fac378b9960c9863118a384e8443e6c473a-tauri-macos
  • pr-572-512b6fac378b9960c9863118a384e8443e6c473a-tauri-macos-arm64
  • pr-572-512b6fac378b9960c9863118a384e8443e6c473a-electron-macos
  • pr-572-512b6fac378b9960c9863118a384e8443e6c473a-electron-windows
  • pr-572-512b6fac378b9960c9863118a384e8443e6c473a-electron-linux

@pascalandr
pascalandr merged commit 275770b into NeuralNomadsAI:dev Jul 8, 2026
11 checks passed
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.

3 participants