Add bulk delete by entry type to the Brain UI - #929
Merged
selfcontained merged 2 commits intoAug 11, 2026
Conversation
The Brain explorer could delete one entry at a time, clear a collection, or
clear a project — but not "clear just the events", which is what you want when
one noisy job floods the event log and the objects and lists around it are
still worth keeping.
Each section header (Objects / Lists / Events) now clears that type in the
current scope: the selected collection, or every collection in the project when
the All pill is active.
Server:
- deleteEntriesOfType(repoRoot, type, scope) deletes one entry type in one
scope. Lists take their items with them via the existing foreign key cascade.
- deleteFromEntryTable is now the only author of a bulk WHERE clause, the table
lookup, and the blank-collection rule; deleteCollection and deleteProject
route through it too, so the three paths cannot disagree about scoping.
- toEntryTypeDeleteScope owns "collection XOR allCollections", called by both
the route and the store so untyped callers hit the same gate. A missing
collection is rejected rather than read as "everything" — the same reason
brain_delete_events requires one.
- DELETE /api/v1/brain/{objects,lists,events} keeps only HTTP-shaped rules:
rejecting array-valued query keys, requiring repoRoot, coercing
allCollections. mapBrainValidation turns store rejections into 400s, which
the pre-existing collections DELETE now shares.
Web:
- The confirmation quotes the scope's real totals, not the rows on screen: the
lists are capped at 100 and narrowed client-side by the filter box, so the
badge reads "0 / 6" while the dialog offers to delete all six. Totals come
from useBrainScopeTotals and are read live, so an agent writing entries while
the dialog is open moves the number rather than leaving it stale.
- A filter that hides every row of a type keeps the section mounted, otherwise
the one control that reaches the hidden rows disappears exactly when it is
needed.
- Bulk delete is not offered until totals load, rather than guessing a count and
then silently skipping the empty-collection redirect.
- CollapsibleSection stays a layout primitive: one headerAction slot, and
visibleCount/totalCount instead of a count whose meaning changed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>The backend is already covered by brain-routes.test.ts, which drives the real Fastify app against a real Postgres through app.inject — scoped and project-wide deletes, cascade, cross-repo isolation, and every malformed selector, in 3s with no browser. The two Playwright tests only added frontend wiring coverage for an area that changes rarely and was verified by hand. Keeps the "All" pill locator fix: the section headers now render "Delete all objects in ..." buttons, and getByRole matches accessible names by substring, so the pre-existing test needs exact: true to stay unambiguous. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Uh oh!
There was an error while loading. Please reload this page.
selfcontained
deleted the
agt_185c616cd5c1/build-brain-ui-bulk-delete-by-type
branch
August 11, 2026 00:55
This was referenced Aug 11, 2026
selfcontained added a commit
that referenced
this pull request
Aug 12, 2026
BrainCollectionView's bulk delete (#929) had no unit coverage and no E2E: brains-page.spec.ts only exercises the single-entry and clear-collection paths. The new tests mount the real card tree with the real use-brain queries and mutations (only the HTTP seam and the toaster are mocked) and pin the parts that are decided from unfiltered scope totals rather than the rows on screen — the scoped vs all-collections delete URLs, the count the confirmation may quote, singular/plural nouns, the collection-cleared redirect and its two guards, invalidation, the failure path, the filter match fields, and the 100-item cap note. Also fixes two harness defects that only showed up locally: - scripts/e2e-isolated.sh now installs the pinned Playwright chromium before provisioning anything. pnpm install does not fetch browser binaries, so a Playwright bump in the lockfile made every browser test fail at launch; CI has always installed it as its own step. - .prettierignore now covers apps/site/.astro. `pnpm run check` generates it, after which `pnpm run format` — and so `pnpm run ci` — failed on any machine that had run check. CI passes it only because its checkout is still fresh. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The Brain explorer could delete one entry at a time, clear a collection, or clear a project — but not "clear just the events", which is what you want when one noisy job floods the event log and the objects and lists around it are still worth keeping.
Each section header (Objects / Lists / Events) now clears that type in the current scope: the selected collection, or every collection in the project when the All pill is active.
Server
deleteEntriesOfType(repoRoot, type, scope)deletes one entry type in one scope. Lists take their items with them via the existingbrain_list_itemsforeign key cascade.deleteFromEntryTableis now the only author of a bulkWHEREclause, the table lookup, and the blank-collection rule.deleteCollectionanddeleteProjectroute through it too, so the three paths cannot disagree about scoping.toEntryTypeDeleteScopeowns "collection XOR allCollections", called by both the route and the store so untyped callers hit the same gate. A missingcollectionis rejected rather than read as "everything" — the same reasonbrain_delete_eventsrequires one (Add brain_delete_events MCP tool #928).DELETE /api/v1/brain/{objects,lists,events}keeps only HTTP-shaped rules: rejecting array-valued query keys, requiringrepoRoot, coercingallCollections.mapBrainValidationturns store rejections into 400s, which the pre-existing collections DELETE now shares.Web
0 / 6while the dialog offers to delete all six. Totals are read live, so an agent writing entries while the dialog is open moves the number rather than leaving it stale.CollapsibleSectionstays a layout primitive: oneheaderActionslot, andvisibleCount/totalCountinstead of acountwhose meaning changed.Reviews
Three persona reviews ran on this branch; all 10 items are resolved. They caught a filter-to-zero bug that hid the delete-all button, a stale-count snapshot that SSE invalidation made live rather than theoretical, a blank-collection divergence between the two delete paths, and a 500 introduced while fixing that last one.
Testing
pnpm run check,pnpm run finalize:webbrain-routes.test.tsdrives the real Fastify app against a real Postgres viaapp.inject, so the new endpoints are covered end to end without a browser.exact: truelocator fix, needed becausegetByRolematches accessible names by substring and the new "Delete all ..." buttons otherwise collide with the "All" collection pill.🤖 Generated with Claude Code