Skip to content

fix(desktop,ui): drop the orphaned remove-row from a task's move menu - #5739

Closed
ggbdpq wants to merge 1 commit into
apache:mainfrom
ggbdpq:fix/orphan-project-menu
Closed

ggbdpq wants to merge 1 commit into
apache:mainfrom
ggbdpq:fix/orphan-project-menu

Conversation

@ggbdpq

@ggbdpq ggbdpq commented Sep 26, 2026

Copy link
Copy Markdown
Contributor

Fixes #5725

Summary

  • Both guards read the question wrong. A task whose projectId no longer names any visible project scope still got a "Remove from project" row: the navigation provider's target builder pushed the projectId: null exit whenever session.projectId was truthy, and the ui list's filter kept that exit whenever currentProjectId was truthy. An id orphaned by a deleted, archived, or relocated project therefore rode the "Move to project" submenu as a lone remove entry on a task that already reads project-less.
  • The provider's builder (extracted into a pure, tested buildSessionMoveTargets in session-navigation-groups.ts) now offers the leave-row only while the pointed-at project is among the visible scopes.
  • The ui list's filter applies the same visibility check for whatever producer feeds it (inVisibleProject), so the submenu can never render the lone remove-row regardless of the source.

Verification

Check Result
buildSessionMoveTargets: in-project task → visible projects + leave row pass
buildSessionMoveTargets: orphaned projectId → no leave row pass
buildSessionMoveTargets: project-less task → no leave row pass
Existing session-navigation-move-to-project and session-history-project-drop suites pass
Renderer architecture check (plain) pass
biome format on touched files desktop/ui paths are formatter-excluded by biome.jsonc; no formatted-path files touched

Not verified locally: full CI on Linux, and the real-app repro (macOS report) — the fix is covered at the builder and filter layers instead.

AI use

Implemented with GLM-5.3-Flash (ZCode): the pure-builder extraction, both visibility guards, and the tests.

  • I have read the project's contributing guidelines
  • The commit message follows the repository convention
  • Tests cover the orphaned-id case at the builder layer

A task whose projectId no longer names any visible project scope still
got a 'Remove from project' row: both the navigation provider's target
builder and the ui list's filter read 'is this task in a project?' as
'is projectId truthy?', so an id orphaned by a deleted or archived
project rode the submenu as a lone remove entry on a task that already
reads project-less.

The provider's target builder (extracted to a pure, tested helper)
offers the leave-row only while the pointed-at project is among the
visible scopes, and the ui list's filter applies the same visibility
check for whatever producer feeds it.

Fixes apache#5725

Generated-by: GLM-5.3-Flash (ZCode)
@github-actions github-actions Bot added the effort/M Under 500 readable lines label Sep 26, 2026

@hqhq1025 hqhq1025 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This change removes the orphaned Remove from project row by deriving menu targets from available, unarchived scopes and checking membership again in the UI. I found one alias-membership regression (inline P2): a task can visibly belong to a project under an alias ID but lose the remove option. No schema migration is involved.

I checked the five-file diff, scope source, project alias resolution, menu/drop consumers, and the added builder tests. The current-head CI test and label checks passed; merge-tree and diff-check were clean against the latest main. I did not run a local Desktop build or manually reproduce the macOS UI case. The new tests cover canonical IDs, orphan IDs, and null IDs but not aliases. Human review is still needed before merging.

Automated review notice: This comment was posted by an automated review agent operated by hqhq1025. It is not an independent human review and does not replace one.

}));
if (
session.projectId &&
targets.some((target) => target.projectId === session.projectId)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[P2] Resolve project aliases before deciding whether this task can leave its project. A relink/merge records the absorbed ID in project.aliases (packages/storage/src/project-catalog.ts:700-704), and the rail explicitly maps that alias to the visible project (session-navigation-groups.ts:99-105). For a visible scope with id='new', aliases=['old'] and a session with projectId='old', the task is displayed inside that project, but this strict-ID check returns false and never supplies the null exit. The UI's separate strict-ID check in packages/ui/src/session-history-list.tsx:1473-1478 would also drop that exit even if the builder supplied it. With no other destination, the Move menu vanishes entirely, so the user cannot remove a task that is visibly in a project. Please use the same canonical/alias membership rule in both guards and add an alias regression.

@ggbdpq

ggbdpq commented Sep 26, 2026

Copy link
Copy Markdown
Contributor Author

Closing as superseded by #5726, which landed the same orphan guard through a dedicated sessionMoveTargets module: membership is resolved with findProjectByIdentity (which also matches historical aliases — stronger than this PR's id-equality check), the exit row is offered only while the owning Host still knows the project, and the ui list now maps the provider's targets directly, making the provider the single membership authority.

After merging current main this branch's remaining delta was nil. Good outcome for #5725 either way — thanks for reviewing.

@ggbdpq ggbdpq closed this Sep 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/M Under 500 readable lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Sidebar task menu shows "Remove from project" on tasks that have no project

2 participants