fix(notifications): match goal owner by canonical forms, not '%user%' substring#205
Conversation
… substring
The open-goals SessionStart banner filtered goals with
`owner LIKE '%${userName}%'`, which has two real failure modes
(the same ones CodeRabbit flagged on PR #203 for the context block):
1. Substring collision — `'%ali%'` matches `malice@…`, leaking
another user's goals into the banner.
2. Reverse-alias miss — a full-email userName never matches a row
whose owner is the short form.
The context-renderer already solved this with `listOpenGoals`
(exact full / exact short / `short@%` alias), but the banner was
never ported. Reuse that reader instead of duplicating a worse query;
the loose bidirectional `includes()` JS guard goes away with it.
Note: `listOpenGoals` also carries a `version = MAX(version)` filter,
but `version` is vestigial (always 1, see deeplake-fs.ts), so it does
NOT dedup the duplicate-row "N goals" double-count from Deeplake's
UPDATE-coalescing quirk. That needs a separate `DISTINCT ON (goal_id)`
fix in both readers — out of scope here.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThis PR refactors ChangesOpen Goals Canonical Owner Matching
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint skipped: no ESLint configuration detected in root package.json. To enable, add Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Coverage ReportScope: files changed in this PR. Enforced threshold: 90% per metric (per file via
File Coverage — 1 file changed
Generated for commit 531cfc2. |
Summary
The open-goals SessionStart banner (
src/notifications/sources/open-goals.ts) filtered goals withowner LIKE '%${userName}%'. That substring match has two real failure modes — the same ones CodeRabbit flagged on #203 for the SessionStart context block:'%ali%'matchesmalice@…, leaking another user's goals into the banner.userNamenever matches a row whoseowneris the short form.The context-renderer already fixed this with
listOpenGoals(exact full / exact short /short@%alias), but the banner was never ported and kept the worse query plus a loose bidirectionalincludes()JS guard.This PR makes the banner reuse
listOpenGoalsinstead of duplicating its own SQL. Net change is a deletion: the substring query and the JS guard both go away.Changes
open-goals.ts: drop theowner LIKE '%…%'SQL +includes()guard; calllistOpenGoals(sql => api.query(sql), goalsTable, userName, { limit: 25 }).notifications-open-goals-source.test.tsSQL-shape assertions to the canonical-forms query. 18/18 pass.Verified
Built the bundle, installed locally, and ran the actual SessionStart hook against the live API. It now issues:
exact owner match, no substring scan — rendered the banner correctly, exit 0.
While testing I confirmed the "N goals open" double-count is a separate bug that this PR does not fix.
listOpenGoalscarries aversion = MAX(version)filter, butversionis vestigial — always 1 (deeplake-fs.ts:459-461), so it never dedups. The duplicate rows come from Deeplake's UPDATE-coalescing quirk (deeplake-fs.ts:466-468): a single logical goal can have 2+ rows with the same(goal_id, version). A real fix needsDISTINCT ON (goal_id)/GROUP BY goal_idin both readers (open-goalsandcontext-renderer). Happy to do that in a follow-up.Test plan
vitest run tests/claude-code/notifications-open-goals-source.test.ts(18/18)tsc --noEmit --skipLibCheckSummary by CodeRabbit