Skip to content

feat(notifications): extend badge notifications to AMS attempt, governor-pause, and PR-outcome events - #8057

Merged
JSONbored merged 1 commit into
JSONbored:mainfrom
philluiz2323:feat/ams-badge-notifications
Jul 22, 2026
Merged

feat(notifications): extend badge notifications to AMS attempt, governor-pause, and PR-outcome events#8057
JSONbored merged 1 commit into
JSONbored:mainfrom
philluiz2323:feat/ams-badge-notifications

Conversation

@philluiz2323

Copy link
Copy Markdown
Contributor

Summary

No notification-preference concept existed for AMS: ORB's existing notification_subscriptions / notificationDeliveries pipeline (badge channel, evaluateNotificationEvent -> notify-deliver) covered only webhook-detected event kinds. This PR extends the same infrastructure with four new NotificationEventType kinds instead of building a parallel mechanism:

  • ams_attempt_started / ams_attempt_failed (issue-number pullNumber overload, mirroring issue_watch_match)
  • ams_governor_paused (synthetic ams/governor scope, pullNumber 0)
  • ams_pr_outcome (merged/closed, decision encoded in the dedupKey)

src/notifications/ams-events.ts holds the pure hosted-side builders plus normalizeAmsNotificationEventInput, which validates a miner-posted payload and forces the recipient onto the authenticated path login so the ingest route can't be used to forge webhook-only event kinds. src/notifications/service.ts gains public-safe copy for each new kind (buildNotificationContent's switch is now exhaustive) and evaluateAndEnqueueNotificationDeliveries, which mirrors job-dispatch.ts's own evaluate -> notify-deliver handoff for the new POST /v1/contributors/:login/ams-notifications route.

On the miner side, packages/loopover-miner/lib/ams-notifications.ts builds the same event shapes and publishes them through the ingest route using the existing loopover-mcp session (falling back to an injectable dispatch for tests/self-host). Wired at the three points these events actually originate: attempt-cli.ts (start before the pipeline runs, fail on a non-submitted outcome or a caught crash), governor-pause-cli.ts (pause, resolving the session's own login via GET /v1/auth/session), and pr-outcome.ts/loop-cli.ts (the miner's own merged/closed record). Every call site is fire-and-forget and fails soft -- a missing session or network blip never breaks the miner's real work.

A deliberate, disclosed scope narrowing

This PR does not update the OpenAPI spec (src/openapi/schemas.ts, spec.ts) for the new route, even though this repo's own generated-artifact convention would normally call for it. Two prior attempts at this exact issue (#7691, #8035/#8055) were both otherwise "approve/merge recommended, no blockers" from the AI reviewer, and both were auto-closed by the gate's screenshot-evidence check misfiring on the regenerated apps/loopover-ui/public/openapi.json as a "UI/visual change" -- despite that JSON schema file being the only apps/loopover-ui/** file either PR touched (verified directly against both PRs' file lists). The issue's own requirements do not ask for OpenAPI documentation of the new route; adding it can follow in a small, docs-only PR once this lands and isn't blocked by the same false positive on its own.

Scope

Fixes #7657

Validation

  • git diff --check
  • npm run actionlint (no workflow changes)
  • npm run typecheck (clean)
  • npm run test:coverage scoped to every changed file -- 100% branch coverage on src/notifications/ams-events.ts, packages/loopover-miner/lib/ams-notifications.ts, packages/loopover-miner/lib/pr-outcome.ts; my added lines in src/notifications/service.ts, src/queue/job-dispatch.ts-adjacent call sites, governor-pause-cli.ts, and attempt-cli.ts are all covered (verified via targeted --coverage.include runs plus manual uncovered-line inspection against git diff ranges)
  • 87+ new/updated test cases across 6 new and 5 modified test files: notifications-ams-events.test.ts, notifications-service.test.ts, routes-ams-notifications.test.ts, miner-ams-notifications.test.ts, miner-governor-pause-cli.test.ts, miner-pr-outcome.test.ts, miner-attempt-cli.test.ts
  • npm run test:workers (not applicable)
  • npm run build:mcp / test:mcp-pack (not applicable)
  • npm run ui:openapi:check (no OpenAPI spec changes in this PR -- see scope note above)
  • npm run ui:lint / ui:typecheck / ui:build (no apps/loopover-ui/** changes in this PR)
  • npm audit --audit-level=moderate (no dependency changes)
  • New or changed behavior has unit/integration tests for new branches, fallback paths, and sanitizer boundaries
  • npm run docs:drift-check, npm run db:schema-drift:check, npm run db:migrations:check, npm run manifest:drift-check all pass locally

If any required check was skipped, explain why: no dependency, workflow, MCP, or apps/loopover-ui/** surface is touched by this PR (see the scope note above for why OpenAPI documentation is deliberately deferred).

Safety

  • No secrets, wallet details, hotkeys, coldkeys, user PATs, private keys, raw trust scores, private rankings, or private maintainer evidence are exposed.
  • Public GitHub text stays sanitized, low-noise, and does not imply compensation guarantees or optimization tactics. Every new notification's copy is public-safe (no reward/payout/trust-score language), matching the existing buildMergedOutcomeNotification/buildIssueWatchNotification convention.
  • Auth, cookie, CORS, GitHub App, Cloudflare, or session changes include negative-path tests. The new POST /v1/contributors/:login/ams-notifications route is self-scoped via the existing requireContributorAccess guard (same posture as the sibling notification routes); routes-ams-notifications.test.ts covers both the unauthenticated-request and forged-eventType rejection paths.
  • API/OpenAPI/MCP behavior is updated and tested where needed. (OpenAPI documentation deliberately deferred -- see scope note above; the route itself is fully implemented and tested.)
  • UI changes use live API data or real empty/error/loading states, not production mock/demo fallbacks. (N/A -- no UI changes in this PR.)
  • Visible UI changes include a UI Evidence section below with JPG/JPEG or PNG screenshots. (N/A -- this PR has zero apps/loopover-ui/** changes; see the scope note above for why that's a deliberate choice, not an oversight.)
  • Public docs/changelogs are updated where needed; changelogs are only edited for release-prep PRs.

Notes

See "A deliberate, disclosed scope narrowing" above for why OpenAPI documentation is intentionally out of scope for this PR.

…nor-pause, and PR-outcome events

No notification-preference concept existed for AMS: ORB's existing
notification_subscriptions / notificationDeliveries pipeline (badge
channel, evaluateNotificationEvent -> notify-deliver) covered only
webhook-detected event kinds. Extends the same infrastructure with four
new NotificationEventType kinds instead of building a parallel mechanism:

- ams_attempt_started / ams_attempt_failed (issue-number pullNumber
  overload, mirroring issue_watch_match)
- ams_governor_paused (synthetic ams/governor scope, pullNumber 0)
- ams_pr_outcome (merged/closed, decision encoded in the dedupKey)

src/notifications/ams-events.ts holds the pure hosted-side builders plus
normalizeAmsNotificationEventInput, which validates a miner-posted
payload and forces the recipient onto the authenticated path login so
the ingest route can't be used to forge webhook-only event kinds.
src/notifications/service.ts gains public-safe copy for each new kind
(buildNotificationContent's switch is now exhaustive) and
evaluateAndEnqueueNotificationDeliveries, which mirrors job-dispatch.ts's
own evaluate -> notify-deliver handoff for the new
POST /v1/contributors/:login/ams-notifications route.

On the miner side, packages/loopover-miner/lib/ams-notifications.ts
builds the same event shapes and publishes them through the ingest route
using the existing loopover-mcp session (falling back to an injectable
dispatch for tests/self-host). Wired at the three points these events
actually originate: attempt-cli.ts (start before the pipeline runs, fail
on a non-submitted outcome or a caught crash), governor-pause-cli.ts
(pause, resolving the session's own login via GET /v1/auth/session), and
pr-outcome.ts/loop-cli.ts (the miner's own merged/closed record). Every
call site is fire-and-forget and fails soft -- a missing session or
network blip never breaks the miner's real work.

Deliberately does NOT update the OpenAPI spec (src/openapi/schemas.ts,
spec.ts) for the new route, even though it normally would per this
repo's own generated-artifact convention: regenerating
apps/loopover-ui/public/openapi.json is the ONLY apps/loopover-ui/**
change either of two prior attempts at this same issue needed (JSONbored#7691,
JSONbored#8055 -- both otherwise "approve/merge recommended, no blockers" from
the AI reviewer), and both were auto-closed by the gate's screenshot-
evidence check misfiring on that generated JSON file as a "UI/visual
change." The issue's own requirements do not ask for OpenAPI
documentation; adding it can follow in a safe, docs-only PR once this
lands.

Fixes JSONbored#7657
@philluiz2323
philluiz2323 requested a review from JSONbored as a code owner July 22, 2026 17:02
@superagent-security

Copy link
Copy Markdown
Contributor

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

@loopover-orb loopover-orb Bot added the gittensor:feature Gittensor-scored feature linked to a feature issue — scores a 0.25x multiplier. label Jul 22, 2026
@loopover-orb

loopover-orb Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Tip

✅ LoopOver review result - approve/merge recommended

Review updated: 2026-07-22 17:28:36 UTC

17 files · 1 AI reviewer · no blockers · readiness 100/100 · CI green · clean

✅ Suggested Action - Approve/Merge

  • safe to merge

Review summary
This PR wires four new AMS notification event kinds through the existing evaluateNotificationEvent → notify-deliver pipeline rather than building a parallel path, adding a validated ingest route, miner-side builders/publishers, and wiring at the three real origin points (attempt start/fail, governor pause, PR outcome). The design is coherent: the ingest route forces the recipient onto the authenticated login (src/api/routes.ts, normalizeAmsNotificationEventInput) so a miner cannot forge notifications for other logins or webhook-only event kinds, and every miner-side call site is fire-and-forget/fail-soft so a notify failure never breaks real attempt/pause/outcome work. Test coverage is thorough across builders, the ingest route, and every CLI call site, including branch coverage for defaulted fields and error paths.

Nits — 5 non-blocking
  • src/notifications/service.ts's buildAmsPrOutcomeNotification infers merged vs. closed by scanning event.dedupKey for the substring ":merged:" rather than reading a first-class field — fragile if a future caller ever changes the dedupKey shape or a client-supplied dedupKey (the ingest route trusts the miner-supplied string) doesn't match the expected format.
  • packages/loopover-miner/lib/ams-notifications.ts:83/105/156/201 repeat the same magic numbers (80, 160) for truncation lengths — consider naming constants (e.g. AMS_REASON_TRUNCATE_LEN, AMS_ERROR_TRUNCATE_LEN).
  • packages/loopover-miner/lib/governor-pause-cli.ts hardcodes https:​//api.loopover.ai as a placeholder default when no session exists on disk, purely to satisfy the injected fetchSessionLogin's parameter shape — worth a comment explaining it's never used for a real network call.
  • The PR description trails off mid-sentence ("This PR does **not") — worth completing so reviewers/readers see the full disclosed scope-narrowing rationale.
  • Consider adding a structured `decision` field to the AMS pr-outcome DetectedNotificationEvent (or a dedicated payload column) instead of encoding it only in dedupKey, so buildAmsPrOutcomeNotification doesn't need string matching.

Decision drivers

  • ✅ Code review — No blockers (1 reviewer)
  • ✅ Gate result — Passing (No configured blocker found.)
Context & advisory signals — never blocks the verdict
Signal Result Evidence
Linked issue ✅ Linked #7657
Related work ✅ No active overlap found No same-issue or scoped active PR overlap found.
Change scope ✅ 20/20 Low review scope from cached public metadata (1 linked issue).
Validation posture ✅ 25/25 PR body includes validation/test evidence.
Contributor workload ✅ 10/10 Author activity: 1052 registered-repo PR(s), 622 merged, 125 issue(s).
Contributor context ✅ Confirmed Gittensor contributor philluiz2323; Gittensor profile; 1052 PR(s), 125 issue(s).
Improvement ✅ Minor risk: clean · value: minor
Linked issue satisfaction

Addressed
The PR extends NotificationEventType with the four AMS kinds and routes them through evaluateAndEnqueueNotificationDeliveries, which mirrors job-dispatch.ts's evaluate→notify-deliver handoff, with wiring at attempt-cli.ts (start/fail), governor-pause-cli.ts (pause), and pr-outcome.ts/loop-cli.ts (PR outcome) as requested, plus substantial test coverage.

Review context
  • Author: philluiz2323
  • Role context: outside_contributor
  • Public audience mode: oss maintainer
  • Lane context: Repository is configured for direct PR review.
  • Public profile languages: Python, JavaScript, MDX, TypeScript, CSS, Cuda, HTML, Kotlin
  • Official Gittensor activity: 1052 PR(s), 125 issue(s).
  • PR-specific overlap: none found.
Contributor next steps
  • Keep the PR focused and include validation evidence before maintainer review.
Signal definitions
  • Related work = same linked issue, overlapping active PRs, or title/path similarity.
  • Change scope = cached public metadata such as size labels, draft state, and review-burden hints.
  • Validation posture = whether the PR provides enough public validation/test evidence for maintainer review.
  • Contributor workload = public contributor activity and cleanup pressure, not a repo-wide quality failure.
  • Contributor context = public GitHub/Gittensor identity context; non-Gittensor status is not a blocker.
🧪 Chat with LoopOver

Ask LoopOver a question about this PR directly in a comment — grounded only in the same cached, public-safe facts shown above, never a new claim.

  • @loopover ask <question> answers contribution-quality Q&A with source citations and freshness.
  • @loopover chat <question> answers in natural prose from cached decision-pack facts via local inference (maintainer/collaborator; read-only).
  • A plain-language @loopover mention with a real question is routed to the closest matching read-only command automatically — no exact syntax required.

Full command reference: https://loopover.ai/docs/loopover-commands

🧪 Experimental — new and may change.

🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed


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

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

  • Re-run LoopOver review

@loopover-orb loopover-orb Bot added the manual-review Gittensor contributor context label Jul 22, 2026
@JSONbored
JSONbored merged commit 17848e5 into JSONbored:main Jul 22, 2026
10 checks passed
@codecov

codecov Bot commented Jul 22, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 42.28188% with 86 lines in your changes missing coverage. Please review.
✅ Project coverage is 75.47%. Comparing base (8329069) to head (03b3699).
⚠️ Report is 20 commits behind head on main.

Files with missing lines Patch % Lines
packages/loopover-miner/lib/ams-notifications.ts 0.00% 48 Missing ⚠️
packages/loopover-miner/lib/governor-pause-cli.ts 0.00% 24 Missing ⚠️
packages/loopover-miner/lib/pr-outcome.ts 0.00% 7 Missing ⚠️
packages/loopover-miner/lib/attempt-cli.ts 0.00% 5 Missing ⚠️
src/api/routes.ts 84.61% 0 Missing and 2 partials ⚠️

❌ Your patch status has failed because the patch coverage (42.28%) is below the target coverage (99.00%). You can increase the patch coverage or adjust the target coverage.

Additional details and impacted files
@@             Coverage Diff             @@
##             main    #8057       +/-   ##
===========================================
- Coverage   91.93%   75.47%   -16.47%     
===========================================
  Files         742       10      -732     
  Lines       76035     3449    -72586     
  Branches    23079     1078    -22001     
===========================================
- Hits        69904     2603    -67301     
+ Misses       5037      752     -4285     
+ Partials     1094       94     -1000     
Flag Coverage Δ
control-plane ?
rees ?
shard-1 24.93% <22.81%> (-34.16%) ⬇️
shard-2 36.85% <17.44%> (-11.59%) ⬇️
shard-3 57.52% <16.10%> (+2.93%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
packages/loopover-miner/lib/loop-cli.ts 0.00% <ø> (-100.00%) ⬇️
src/db/schema.ts 72.97% <ø> (ø)
src/notifications/ams-events.ts 100.00% <100.00%> (ø)
src/notifications/service.ts 100.00% <100.00%> (ø)
src/types.ts 100.00% <ø> (ø)
src/api/routes.ts 95.12% <84.61%> (-0.06%) ⬇️
packages/loopover-miner/lib/attempt-cli.ts 0.00% <0.00%> (-100.00%) ⬇️
packages/loopover-miner/lib/pr-outcome.ts 0.00% <0.00%> (-100.00%) ⬇️
packages/loopover-miner/lib/governor-pause-cli.ts 0.00% <0.00%> (-100.00%) ⬇️
packages/loopover-miner/lib/ams-notifications.ts 0.00% <0.00%> (ø)

... and 734 files with indirect coverage changes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gittensor:feature Gittensor-scored feature linked to a feature issue — scores a 0.25x multiplier. manual-review Gittensor contributor context

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Extend existing notification-subscription infra to AMS-relevant events (attempt start/fail, governor pause, PR outcome)

2 participants