Skip to content

fix(notifications): match issue-watch repo names case-insensitively - #747

Merged
JSONbored merged 8 commits into
JSONbored:mainfrom
galuis116:fix/issue-watch-repo-casing
Jun 17, 2026
Merged

fix(notifications): match issue-watch repo names case-insensitively#747
JSONbored merged 8 commits into
JSONbored:mainfrom
galuis116:fix/issue-watch-repo-casing

Conversation

@galuis116

Copy link
Copy Markdown
Contributor

Summary

The issue-watch feature (#735, gittensory_watch_issues) stored and matched the watched repoFullName case-sensitively, while the rest of the codebase treats repo full names case-insensitively (getRepository resolves via lower(fullName) = lower(input); the registry sync was fixed for this in #604). The watch functions deliberately lowercase login and labels "for case-insensitive matching" — but not repoFullName.

Impact: the MCP tool stores the user's raw casing (schema is just z.string(), no canonicalization), but the webhook looks up watchers with GitHub's canonical payload.repository.full_name via an exact eq:

// store: repoFullName saved raw
await upsertIssueWatchSubscription(env, { login, repoFullName: input.repoFullName, labels });
// match: case-sensitive
.where(eq(issueWatchSubscriptions.repoFullName, repoFullName)) // canonical webhook casing

So a contributor who subscribes with non-canonical casing (e.g. jsonbored/gittensory — trivially common, since GitHub URLs are case-insensitive) gets a subscription the webhook can never match: they see it in list, but silently never receive any issue-watch notification. It also permitted duplicate rows for the same (login, repo) across casings (the unique index is case-sensitive). Fails closed — a correctness/usability bug, not a security issue.

Fix

Lowercase repoFullName on store and on both lookups (upsertIssueWatchSubscription, listIssueWatchersForRepo, deleteIssueWatchSubscription), mirroring the existing login/labels normalization in the same functions. Matching is now symmetric, and the UNIQUE(login, repo_full_name) index dedupes across casings since the stored value is always lowercase.

Tests

Added a regression test in issue-watch.test.ts: subscribing with Owner/Repo is found by the canonical owner/repo lookup and fires detectIssueWatchEvents; the stored/listed name is normalized; re-subscribing under another casing is idempotent; and unwatch is case-insensitive. (The existing CRUD test only ever used the already-lowercase owner/repo, so the asymmetry was invisible.)

Full coverage suite green locally (only the known CRLF-local gittensory-focus-manifest test fails locally; passes in CI).

Closes #746

issue-watch subscriptions stored and matched repoFullName case-sensitively
while the rest of the codebase treats repo names case-insensitively. A
contributor who subscribed via gittensory_watch_issues with non-canonical
casing (GitHub repo names are case-insensitive) was silently never matched
by the webhook's canonical repository.full_name lookup, so the feature
quietly delivered no notifications.

Lowercase repoFullName on store and on both lookups (mirroring the existing
login/label normalization), so matching is symmetric and the (login, repo)
unique index dedupes across casings.

Closes JSONbored#746
@ghost

ghost commented Jun 14, 2026

Copy link
Copy Markdown

Note

Gittensory Gate skipped

PR closed before full evaluation. No late first comment was created.

Signal Result Evidence Action
Gate result ⚠️ Skipped #747 is no longer open. No action.

💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →.

Checked by Gittensory, a quiet PR intelligence layer for OSS maintainers.

@ghost ghost added the gittensory:reviewed label Jun 14, 2026
@superagent-security

Copy link
Copy Markdown
Contributor

Superagent didn't find any vulnerabilities or security issues in this PR.

@ghost

ghost commented Jun 14, 2026

Copy link
Copy Markdown

gittensory · advisory review

Reviewed 2 changed file(s) — two independent AI reviewers.

Changed files: src/db/repositories.ts, test/unit/issue-watch.test.ts

Suggested action: 🛠️ Request changes. (reviewers split: request changes / merge) Address the suggestions below before merging.

Reviewer A · gpt-oss-120b — recommends 🛠️ request changes
The PR adds case‑insensitive handling for repository names in issue‑watch subscriptions by lower‑casing repoFullName on upsert, delete, and lookup, and adds a comprehensive test for the new behaviour.

Suggestions

  • Add a migration that normalises existing issueWatchSubscriptions.repoFullName values to lower‑case and updates any unique constraints accordingly to prevent duplicate rows for the same repo with different casing.
  • Consider adding a functional unique index (e.g., LOWER(repoFullName)) or adjusting the existing index to enforce case‑insensitivity at the DB level.
  • Add a test that verifies listIssueWatchersForRepo works when called with a differently‑cased repo name (e.g., "Owner/Repo").
  • Document the new case‑insensitive contract in the function JSDoc/comments for future maintainers.

Worth double-checking

  • Existing data may contain mixed‑case repo names, leading to duplicate subscription rows after this change.
  • Without a DB‑level case‑insensitive constraint, race conditions could create duplicate entries for the same repo.

Reviewer B · nemotron-3-120b-a12b — recommends ✅ merge
The PR makes issue-watch repo name matching case-insensitive by normalizing stored repoFullName to lowercase and using lowercase lookups. This aligns with GitHub's case-insensitive repo names and ensures webhook deliveries match stored subscriptions regardless of casing. The changes are consistent across upsert, delete, and list operations, and the added test covers the new behavior comprehensively.

No blocking issues spotted.

@JSONbored JSONbored added the gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. label Jun 16, 2026

@JSONbored JSONbored left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

PR #747 — fix(notifications): match issue-watch repo names case-insensitively
Author: galuis116 | CI: Fail
ACTION: REquest changes

Highlights:

  • Lowercases repoFullName on store and all three lookups (upsert/delete/listIssueWatchersForRepo), matching existing login/labels normalization (#604 convention).
  • Genuine correctness fix: webhooks deliver canonical repository.full_name, so non-canonical-cased subscriptions silently never matched. Fails closed.
  • Test proves store-normalization, canonical-casing delivery, idempotency across casings, and case-insensitive unwatch.

Notes / before merge:

  • mergeable_state: fail, please ensure passing CI (needs test coverage updates)

JSONbored and others added 3 commits June 16, 2026 15:10
…e admits fan-out

The main merge brought in JSONbored#742's visibility-aware fan-out gate, which only
fans out issue-watch events for a tracked, accessible repo. The
case-insensitivity regression test subscribed to a repo it never upserted,
so the gate (correctly) returned no events. Upsert it as a tracked PUBLIC
repo, matching the other detectIssueWatchEvents tests, so the case-insensitive
match is exercised end-to-end.
@codecov

codecov Bot commented Jun 16, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 96.73%. Comparing base (71bf2b0) to head (d798684).

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #747   +/-   ##
=======================================
  Coverage   96.72%   96.73%           
=======================================
  Files          94       94           
  Lines       13850    13851    +1     
  Branches     5052     5051    -1     
=======================================
+ Hits        13397    13399    +2     
  Misses         86       86           
+ Partials      367      366    -1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

codecov/patch flagged the upsert's ternary fallback as a partial branch: the
`: { login, repoFullName, labels }` arm is unreachable because the row always
exists immediately after the insert/onConflictDoUpdate. Mark it /* v8 ignore */
(the established convention for defensive type-safety fallbacks) so the diff is
fully covered.
@galuis116
galuis116 requested a review from JSONbored June 17, 2026 02:25

@JSONbored JSONbored left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

PR #747 — fix(notifications): match issue-watch repo names case-insensitively
Verdict: MERGE
Resolves issue #746: FULLY
CI: All green (test 1/2, lint, workers, ui, security, codecov). gittensory/Context neutral.

Highlights:

  • Exactly the prescribed fix: lowercases repoFullName on store (upsertIssueWatchSubscription) and on both lookups (listIssueWatchersForRepo, deleteIssueWatchSubscription), mirroring existing login/labels normalization; select-back where-clause corrected to the normalized value.
  • Restores the codebase-wide case-insensitive repo invariant (getRepository, #604) and lets UNIQUE(login, repo_full_name) dedupe across casings.
  • Strong regression test: store normalization, canonical-casing delivery via detectIssueWatchEvents, cross-casing idempotency (no dup row), case-insensitive unwatch — exercises every path the bug touched.

Concerns / required changes:

  • None.

Completeness vs issue: Implements the issue's recommended unconditional-lowercase fix on all three functions plus the exact regression test requested; fully resolves the silent-missed-notification and duplicate-row problems.

@dosubot dosubot Bot added the lgtm label Jun 17, 2026
@JSONbored
JSONbored merged commit bf451b0 into JSONbored:main Jun 17, 2026
17 checks passed
@github-project-automation github-project-automation Bot moved this from Todo to Done in gittensory - v1 roadmap Jun 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier.

Projects

No open projects
Status: Done

Development

Successfully merging this pull request may close these issues.

[Bug]: issue-watch subscriptions match repoFullName case-sensitively

2 participants