Tasks: let folders be deleted from Manage - #1703
Conversation
|
🚨 SLOP COP 🚨 · I am SlopCop, and I am reviewing this pull request now. Plain English summary: This change lets a user delete a Tasks folder from the Manage view. I will check security, code quality, performance, architecture, and the user path. I will post the full results after those checks finish. |
| size="icon" | ||
| variant="ghost" | ||
| className="size-6 text-muted-foreground hover:text-destructive" | ||
| aria-label={`Delete folder ${folder.name}`} |
There was a problem hiding this comment.
🚨 slopcop/review — Blocking: this new user feature has no agent CLI path.
bb tasks folder still supports only create, list, and update. Therefore, bb tasks folder delete <id-or-name> returns unknown folder subcommand: delete. The repository contract requires each end-user feature to ship through the bb CLI. Please add the delete command, explicit destructive confirmation, help, README text, and CLI tests.
| // NULL moves its projects and subfolders to the top level. Nothing else is | ||
| // removed, so the confirmation names the move rather than warning about loss. | ||
| function describeDeleteImpact(folder: Folder): string { | ||
| const projectCount = (projects.data ?? []).filter( |
There was a problem hiding this comment.
🚨 slopcop/review — The dialog can report a false delete impact while projects load.
useFolders() and useProjects() load independently. Folder rows can appear before project data arrives, and projects.data ?? [] converts that state into zero projects. A user can then see “The folder is empty” even though the delete will move projects. A project-query failure causes the same result. Please wait for valid project data, or compute the authoritative impact on the server. Add a delayed or failed listProjects test.
There was a problem hiding this comment.
🚨 SLOP COP 🚨 · review
Plain English summary: This PR adds a delete control for Tasks folders in Manage.
Deleting a folder moves its direct projects and child folders to the top level. It does not delete tasks.
I found two issues:
- Blocking contract gap: The new user feature has no
bb tasks folder deletecommand. The repository requires a CLI path for every user feature. I left an inline comment. - Incorrect impact text: The folder list can load before the project list. The dialog can then say a folder is empty when it contains projects. I left an inline comment.
The security review found no issue. React escapes folder names, the contract validates IDs, and SQL uses bound values.
The performance review found no issue. The dialog performs one linear scan of projects and folders, then sends one RPC call.
The code correctly reuses the shared ConfirmDialog. I found no duplicate folder-delete UI that needs a new shared component.
A server impact query would remove the UI's copy of database behavior. It would also fix the load race with authoritative counts.
All GitHub CI checks passed. The focused local Manage test reached Vitest, but host contention prevented completion, so I stopped it.
I did not run the browser path because the focused local test did not finish. Please fix the two inline issues before merge.
The deleteFolder RPC, contract entry, store function, and CLI command all shipped, but the only folder surface in the app (Manage -> Folders) offered rename and re-parent, so a folder created from the New project dialog could never be removed without dropping to the CLI. Adds the trash control the Labels and Presets rows already have, behind the shared ConfirmDialog. The confirmation names the outcome rather than warning about loss: both folder foreign keys are ON DELETE SET NULL, so the folder's projects and subfolders move to the top level and no task is touched. Fixes get-bb#1701 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…n loaded data Review follow-ups for the Manage folder delete control: - Add `bb tasks folder delete <id-or-name> [--json]` with the same semantics as the dialog: the folder's projects and subfolders move to the top level and no tasks are deleted. The command resolves folders by id or name, reports what moved (ids in JSON, counts in text), and fails with `folder not found` for unknown folders. Help text and README list the new subcommand. - The Manage confirm dialog loaded folders and projects independently and turned a still-loading or failed project list into "The folder is empty". It now waits for both queries before describing the impact, shows a loading/error message instead, and disables confirm until then (ConfirmDialog gained a `confirmDisabled` prop). - Tests: CLI delete by name and id, unknown folder, missing positional, moved projects/subfolders and untouched task; dialog gating on a delayed and a failed listProjects. Co-Authored-By: Claude <noreply@anthropic.com>
94a557c to
551d7c5
Compare
|
Addressed both SlopCop findings (rebased onto current
|
|
🚨 SLOP COP 🚨 · I am the SlopCop. I am reviewing this change for security, code quality, performance, architecture, and end-to-end behavior. |
| // Deleting a folder only unfiles what it held (ON DELETE SET NULL moves | ||
| // its projects and subfolders to the top level), so report the move the | ||
| // same way the Manage dialog does instead of warning about loss. | ||
| const movedProjects = (await listProjects(domain)).filter( |
There was a problem hiding this comment.
🚨 slopcop/review — P2: The command computes move results before the delete and reads all projects.
Another client can change the folder between these reads and the delete. The JSON result can then contain incorrect moved IDs. This code also filters every project, although listProjects accepts folderId. Return the moved IDs from the delete transaction. Use indexed queries for the target folder.
| ), | ||
| ) | ||
| .folders.filter((entry) => entry.parentFolderId === folder.id); | ||
| const result = tasksRpcContract.deleteFolder.output.parse( |
There was a problem hiding this comment.
🚨 slopcop/review — P2: Both delete paths can report success when no row changes.
The RPC returns deleted: false when another client already removed the folder. The CLI still prints success and exits with code zero. The Manage dialog also closes without an error. Treat false as a not-found conflict. Refresh the UI and return a CLI error.
| // The folder and project queries load independently, so the impact text | ||
| // waits for both rather than treating a still-loading (or failed) project | ||
| // list as "no projects". | ||
| const impactReady = |
There was a problem hiding this comment.
🚨 slopcop/review — P2: A failed refresh can enable deletion with cached data.
useTasksQuery keeps same-scope data when a refresh fails. This condition ignores both query errors and can set impactReady to true. The dialog can show stale counts and enable deletion. Require both errors to be null. Add a test with cached data followed by a failed refresh.
| status Show plugin status | ||
| project create|list|show|update | ||
| folder create|list|update | ||
| folder create|list|update|delete |
There was a problem hiding this comment.
🚨 slopcop/review — P3: The registered folder command summary still omits delete.
This help now includes delete. However, the registered folder summary at line 1944 still lists only create, list, and update. Add delete there so all CLI help remains accurate.
There was a problem hiding this comment.
🚨 SLOP COP 🚨 · review
Plain English summary: This PR lets users delete Tasks folders from Manage and the CLI. The operation moves projects and subfolders to the top level. It keeps all tasks.
I found four issues:
- A failed refresh can leave cached data and still enable deletion. The dialog can show stale impact counts. Inline comment
- Both delete paths ignore
deleted: false. The UI can close without an error, and the CLI can report false success. Inline comment - The CLI reads all projects before deletion and computes moved IDs outside the delete transaction. Concurrent changes can make the result incorrect. Inline comment
- The registered folder command summary still omits
delete. Inline comment
I found no direct security defect. React escapes folder names, the contract checks IDs, and SQL uses bound values.
The change uses the shared ConfirmDialog. However, the UI and CLI duplicate the folder-impact policy. A server impact query and an authoritative delete result would remove this duplication. They would also permit indexed queries.
GitHub reports that all current CI checks pass. The Node compatibility smoke job did not run.
I did not start a local server or a browser test. This review environment cannot safely isolate code from the untrusted fork.
…d stale impact data as failures - deleteFolder (store, API contract, CLI, UI) now returns movedProjectIds and movedFolderIds read inside the delete transaction; the CLI no longer computes the move from a pre-delete project scan. - deleted: false is a failure: the CLI exits 1 with a folder-not-found message and the Manage dialog shows the alert and refetches instead of closing silently. - The Manage delete dialog withholds the impact and disables confirm when either the folders or projects query has an error, even if cached rows exist. - The registered folder command summary lists delete. Co-Authored-By: Claude <noreply@anthropic.com>
|
Pushed 98e95e1 addressing the second-round SlopCop findings:
|
Fixes #1701
Problem
deleteFolderships in the RPC contract, the API handler, the store, and the CLI — but no UI surface calls it. Manage → Folders rows offer rename and a parent select; the sidebar renders folders as collapse headers with no menu. A folder created from the New project dialog can only be removed via the CLI.Change
Adds the trash control the Labels and Presets rows already have, behind the shared
ConfirmDialog.The confirmation names the outcome instead of warning about loss, because deleting a folder is not destructive: both folder foreign keys are
ON DELETE SET NULL, so its projects and subfolders move to the top level and no task is touched.2 projects and 1 subfolder move to the top level. No tasks are deleted.The folder is empty.A failed delete surfaces in the section's existing
role="alert"rather than closing silently, matching the labels flow.Tests
Two tests in
views/manage/manage.test.tsx: the delete path (impact copy, thendeleteFoldercalled with the right id) and the failure path (error surfaces instead of a silent close).turbo run typecheck test --filter=bb-plugin-tasks: 35 test files pass, typecheck clean.Notes
No contract, CLI, or docs change — the surfaces already existed, only the app was missing one.