Skip to content

feat(notifications): event→subscription→delivery service + MCP badge feed (closes #536, advances #535) - #707

Merged
JSONbored merged 1 commit into
mainfrom
feat/notifications-service
Jun 14, 2026
Merged

feat(notifications): event→subscription→delivery service + MCP badge feed (closes #536, advances #535)#707
JSONbored merged 1 commit into
mainfrom
feat/notifications-service

Conversation

@JSONbored

Copy link
Copy Markdown
Owner

What

Closes the contribution loop's killer event (#535): when a reviewer requests changes on a miner's PR, the miner now has a notification to read. Builds the event → subscription → delivery pipeline on top of the existing changes-requested detector (src/notifications/events.ts, shipped in #609), delivered through the miner's primary surface — MCP.

Also closes #536 (the review webhook-payload field + GitHubReviewPayload already landed with #609; this PR consumes them end-to-end, satisfying that prerequisite).

Pipeline

  • D1 (migrations/0031_…): notification_subscriptions (per-channel opt-out; badge on by default) + notification_deliveries with UNIQUE(dedup_key, channel) as the idempotency guard — a duplicate webhook / queue retry produces exactly one delivery.
  • Queue: new notify-evaluate / notify-deliver job types. The webhook enqueues notify-evaluate per detected event; evaluate resolves the recipient's channels, writes one idempotent delivery row (rate-limited per recipient/window — bursts beyond the cap are recorded suppressed, never notified), and enqueues notify-deliver; deliver makes the badge row visible (pull-based).
  • Service (src/notifications/service.ts): channel resolution, public-safe changes-requested copy (via sanitizePublicComment), the badge feed builder (unread = delivered count), idempotent evaluate, deliver.

Miner surface (the product rule)

  • MCP tools gittensory_list_notifications + gittensory_mark_notifications_read. Both self-scoped via requireContributorAccess — a session can only read/clear its own login's notifications.
  • This is deliberate: the browser extension is maintainer-gated (minting an extension session requires a maintainer/owner/operator role — routes.ts:812), so it cannot reach plain miners. Per the product rule, miners reach Gittensory through their MCP harness. The literal extension-badge UI (for maintainers-who-are-also-contributors) belongs with the extension rebuild (feat(extension): rebuild as dual-audience overlay (WXT + React) #534/feat(notifications): extension-badge delivery channel #569).

Access boundary

No change to the API access-control surface. The only new reachable surface is the two MCP tools, both gated by the existing requireContributorAccess (own-login-only). All notification copy passes through sanitizePublicComment.

Scope

Badge channel only — the AC's first channel ("not gated behind #150/PWA"). Email (#570), the browser-extension badge UI (#534/#569), and the predicted-gate fix-list enrichment remain follow-ups on #535.

Tests

  • notifications-service.test.ts: channel resolution + mute, copy + reviewer fallback, feed (unread = delivered only), evaluate idempotency, rate-limit suppression, deliver no-op guards, repo round-trips, queue webhook→evaluate→deliver e2e + idempotency.
  • mcp-notifications.test.ts: tool success + scope (own vs. other login throws "authenticated GitHub login").
  • queue.test.ts: strengthened the existing changes-requested webhook test to assert the notify-evaluate enqueue.
  • 97% coverage gate green (Functions 97.06 / Branches 97.01); workers tests + git diff --check + ui:openapi:check all pass.

Closes #536. Advances #535 (badge-channel slice + MCP feed).

…feed (closes #536, advances #535)

Closes the contribution loop's killer event: when a reviewer requests changes
on a miner's PR, the miner now has a notification to read. Builds the
event→subscription→delivery pipeline on top of the existing changes-requested
detector (events.ts, #609), delivered through the miner's primary surface — MCP.

- D1: `notification_subscriptions` (per-channel opt-out; badge on by default) +
  `notification_deliveries` with UNIQUE(dedup_key, channel) as the idempotency
  guard, so a duplicate webhook / queue retry produces exactly one delivery.
- Queue: `notify-evaluate` / `notify-deliver` job types. The webhook enqueues
  notify-evaluate per detected event; evaluate resolves channels, writes one
  idempotent delivery row (rate-limited per recipient/window — bursts beyond the
  cap are recorded `suppressed`, never notified), and enqueues notify-deliver;
  deliver makes the badge row visible (pull-based).
- Notification service (`src/notifications/service.ts`): channel resolution,
  public-safe changes-requested copy (via sanitizePublicComment), the badge feed
  builder (unread = delivered count), idempotent evaluate, and deliver.
- MCP: `gittensory_list_notifications` + `gittensory_mark_notifications_read`,
  the miner's harness surface. Both self-scoped via requireContributorAccess —
  a session can only read/clear its OWN login's notifications.
- `DetectedNotificationEvent`/`NotificationEventType` moved to types.ts (canonical
  location) to avoid a types↔events circular import.

Scope: badge channel only (the AC's first channel, not gated behind #150/PWA).
The maintainer-gated browser-extension badge UI (#534/#569), email (#570), and
the predicted-gate fix-list enrichment remain follow-ups on #535.

Tests: service unit (channel resolution, copy, feed, idempotency, rate-limit,
mute, deliver), queue wiring (webhook→evaluate→deliver e2e + idempotency), MCP
tool scope (own vs. other login). 97% coverage gate green; workers tests pass.
@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 #707 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 11 changed file(s) — two independent AI reviewers.

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

Reviewer A · gpt-oss-120b — recommends ✅ merge
This PR introduces a full notifications subsystem—including DB schema, repository helpers, service logic, queue integration, MCP tools, and comprehensive tests—while respecting the public/private boundary and using sanitization for public copy. The changes compile cleanly and appear well‑tested.

Suggestions

  • Add a migration runner step in CI to ensure the new migration is applied during test environment setup.
  • Consider adding a unit test for the email channel (even if not yet functional) to guard future implementation.
  • Document the new MCP tools in the API reference so downstream users know about the required login parameter.

Worth double-checking

  • The new migration must be executed in production; forgetting to run it could cause runtime errors.
  • Rate‑limit logic assumes timestamps are ISO strings—ensure all callers provide correctly formatted values.

Reviewer B · mistral-small-3.1-24b-instruct — recommends 🛠️ request changes
This PR introduces a new notification system with event detection, subscription management, and delivery services. It includes comprehensive changes to the database schema, repository functions, notification service logic, and MCP server integration. The changes are well-structured and include thorough tests.

Suggestions

  • Consider adding more detailed comments to explain the purpose and functionality of new functions, especially in src/notifications/service.ts and src/db/repositories.ts.
  • Ensure that all new database migrations are documented and flagged in the PR description as required.
  • Review the sanitizePublicComment function to ensure it handles all edge cases and forbidden terms effectively.

Worth double-checking

  • Verify that the rate-limiting logic in evaluateNotificationEvent works as expected and does not suppress legitimate notifications.
  • Ensure that the new notification system does not introduce any security vulnerabilities, especially regarding user data and privacy.
  • Check that the new MCP tools (gittensory_list_notifications and gittensory_mark_notifications_read) are properly authenticated and scoped to the correct user.

@ghost ghost added the gittensory-review label Jun 14, 2026
@JSONbored
JSONbored merged commit 04f7fbc into main Jun 14, 2026
10 checks passed
@JSONbored
JSONbored deleted the feat/notifications-service branch June 14, 2026 10:34
@github-project-automation github-project-automation Bot moved this from Todo to Done in gittensory - v1 roadmap Jun 14, 2026
@github-actions github-actions Bot mentioned this pull request Jun 14, 2026
12 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

No open projects
Status: Done

Development

Successfully merging this pull request may close these issues.

feat(types): add review field to GitHubWebhookPayload

1 participant