Filtering the memory list by project threw a Next.js server-side exception (Application error … Digest). Root cause: the filter built a raw Drizzle sql fragment interpolating a JS string[] into ANY(${accessibleIds}::uuid[]), which does not bind as a Postgres array literal — the same array-binding bug class fixed in #1 (commits 3019446, b3f7e60). The search path avoided it via the pg tag; the plain list path did not.
Fix
Resolve the typed project key to a single project id from the user's accessible set (owned ∪ shared; owned wins on key collision, matching project.identify and resolveProjectIdForKey in the search path), then filter with a parameterized eq(memories.projectId, resolvedId). Returns empty when the key matches no readable project. Removes the raw-SQL array interpolation entirely.
Makes list-view collision semantics consistent with the search view.
Feature
Replaces the plain 'Project key…' input with a type-ahead combobox (_project-combobox.tsx) populated with the user's accessible project keys; narrows as you type, selecting applies the filter immediately, free-typed keys still submit.
Hoists getAccessibleProjects to the page, reused for both the dropdown options and the list WHERE clause.
Verification
tsc typecheck clean; production next build succeeds.
Code review + security review run; one React-18 requestSubmit stale-value bug found and fixed (sync DOM value before submit). No access-control regression: combobox only receives keys the user can already read; filter narrows, never widens; injection surface eliminated.
## Problem
Filtering the memory list by project threw a Next.js server-side exception (`Application error … Digest`). Root cause: the filter built a raw Drizzle `sql` fragment interpolating a JS `string[]` into `ANY(${accessibleIds}::uuid[])`, which does not bind as a Postgres array literal — the same array-binding bug class fixed in #1 (commits 3019446, b3f7e60). The search path avoided it via the `pg` tag; the plain list path did not.
## Fix
- Resolve the typed project key to a single project id from the user's accessible set (owned ∪ shared; owned wins on key collision, matching `project.identify` and `resolveProjectIdForKey` in the search path), then filter with a parameterized `eq(memories.projectId, resolvedId)`. Returns empty when the key matches no readable project. Removes the raw-SQL array interpolation entirely.
- Makes list-view collision semantics consistent with the search view.
## Feature
- Replaces the plain 'Project key…' input with a type-ahead combobox (`_project-combobox.tsx`) populated with the user's accessible project keys; narrows as you type, selecting applies the filter immediately, free-typed keys still submit.
- Hoists `getAccessibleProjects` to the page, reused for both the dropdown options and the list WHERE clause.
## Verification
- `tsc` typecheck clean; production `next build` succeeds.
- Code review + security review run; one React-18 `requestSubmit` stale-value bug found and fixed (sync DOM value before submit). No access-control regression: combobox only receives keys the user can already read; filter narrows, never widens; injection surface eliminated.
The memory list page threw a Next.js server-side exception whenever a
project filter was applied. The filter built a raw Drizzle `sql` fragment
that interpolated a JS string[] into `ANY(${accessibleIds}::uuid[])`,
which doesn't bind as a Postgres array literal — the same array-binding
bug class already fixed in #1 (commits 3019446, b3f7e60) for
memory.list/snippet.list. The search path avoided it by using the `pg`
tag; the plain list path did not.
Fix: resolve the typed project key to a single project id from the
user's accessible set (owned ∪ shared, owned winning on key collision to
match project.identify and the search path), then filter with a plain
`eq(memories.projectId, resolvedId)` — fully parameterized, no raw array
interpolation. Returns empty when the key matches no readable project.
This also makes the list view's collision semantics consistent with the
search view.
Feature: replace the plain "Project key…" text input with a type-ahead
combobox (_project-combobox.tsx) populated with the user's accessible
project keys, narrowing as they type; selecting a suggestion applies the
filter immediately. Free-typed keys still submit. getAccessibleProjects
is hoisted to the page and reused for both the dropdown options and the
list WHERE clause (no extra query on the list path).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
jknapp
merged commit cb1bd31de6 into main2026-07-01 23:22:13 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Problem
Filtering the memory list by project threw a Next.js server-side exception (
Application error … Digest). Root cause: the filter built a raw Drizzlesqlfragment interpolating a JSstring[]intoANY(${accessibleIds}::uuid[]), which does not bind as a Postgres array literal — the same array-binding bug class fixed in #1 (commits3019446,b3f7e60). The search path avoided it via thepgtag; the plain list path did not.Fix
project.identifyandresolveProjectIdForKeyin the search path), then filter with a parameterizedeq(memories.projectId, resolvedId). Returns empty when the key matches no readable project. Removes the raw-SQL array interpolation entirely.Feature
_project-combobox.tsx) populated with the user's accessible project keys; narrows as you type, selecting applies the filter immediately, free-typed keys still submit.getAccessibleProjectsto the page, reused for both the dropdown options and the list WHERE clause.Verification
tsctypecheck clean; productionnext buildsucceeds.requestSubmitstale-value bug found and fixed (sync DOM value before submit). No access-control regression: combobox only receives keys the user can already read; filter narrows, never widens; injection surface eliminated.The memory list page threw a Next.js server-side exception whenever a project filter was applied. The filter built a raw Drizzle `sql` fragment that interpolated a JS string[] into `ANY(${accessibleIds}::uuid[])`, which doesn't bind as a Postgres array literal — the same array-binding bug class already fixed in #1 (commits3019446,b3f7e60) for memory.list/snippet.list. The search path avoided it by using the `pg` tag; the plain list path did not. Fix: resolve the typed project key to a single project id from the user's accessible set (owned ∪ shared, owned winning on key collision to match project.identify and the search path), then filter with a plain `eq(memories.projectId, resolvedId)` — fully parameterized, no raw array interpolation. Returns empty when the key matches no readable project. This also makes the list view's collision semantics consistent with the search view. Feature: replace the plain "Project key…" text input with a type-ahead combobox (_project-combobox.tsx) populated with the user's accessible project keys, narrowing as they type; selecting a suggestion applies the filter immediately. Free-typed keys still submit. getAccessibleProjects is hoisted to the page and reused for both the dropdown options and the list WHERE clause (no extra query on the list path). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>